Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/linville/wireless-2.6.26

This commit is contained in:
David S. Miller 2008-03-11 19:17:18 -07:00
commit ba73d4c84a
104 changed files with 11004 additions and 3568 deletions

View file

@ -11,7 +11,8 @@ DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml videobook.xml \
procfs-guide.xml writing_usb_driver.xml networking.xml \
kernel-api.xml filesystems.xml lsm.xml usb.xml \
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
mac80211.xml
###
# The build process is as follows (targets):

View file

@ -0,0 +1,335 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
<book id="mac80211-developers-guide">
<bookinfo>
<title>The mac80211 subsystem for kernel developers</title>
<authorgroup>
<author>
<firstname>Johannes</firstname>
<surname>Berg</surname>
<affiliation>
<address><email>johannes@sipsolutions.net</email></address>
</affiliation>
</author>
</authorgroup>
<copyright>
<year>2007</year>
<year>2008</year>
<holder>Johannes Berg</holder>
</copyright>
<legalnotice>
<para>
This documentation is free software; you can redistribute
it and/or modify it under the terms of the GNU General Public
License version 2 as published by the Free Software Foundation.
</para>
<para>
This documentation 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.
</para>
<para>
You should have received a copy of the GNU General Public
License along with this documentation; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
</para>
<para>
For more details see the file COPYING in the source
distribution of Linux.
</para>
</legalnotice>
<abstract>
!Pinclude/net/mac80211.h Introduction
!Pinclude/net/mac80211.h Warning
</abstract>
</bookinfo>
<toc></toc>
<!--
Generally, this document shall be ordered by increasing complexity.
It is important to note that readers should be able to read only
the first few sections to get a working driver and only advanced
usage should require reading the full document.
-->
<part>
<title>The basic mac80211 driver interface</title>
<partintro>
<para>
You should read and understand the information contained
within this part of the book while implementing a driver.
In some chapters, advanced usage is noted, that may be
skipped at first.
</para>
<para>
This part of the book only covers station and monitor mode
functionality, additional information required to implement
the other modes is covered in the second part of the book.
</para>
</partintro>
<chapter id="basics">
<title>Basic hardware handling</title>
<para>TBD</para>
<para>
This chapter shall contain information on getting a hw
struct allocated and registered with mac80211.
</para>
<para>
Since it is required to allocate rates/modes before registering
a hw struct, this chapter shall also contain information on setting
up the rate/mode structs.
</para>
<para>
Additionally, some discussion about the callbacks and
the general programming model should be in here, including
the definition of ieee80211_ops which will be referred to
a lot.
</para>
<para>
Finally, a discussion of hardware capabilities should be done
with references to other parts of the book.
</para>
<!-- intentionally multiple !F lines to get proper order -->
!Finclude/net/mac80211.h ieee80211_hw
!Finclude/net/mac80211.h ieee80211_hw_flags
!Finclude/net/mac80211.h SET_IEEE80211_DEV
!Finclude/net/mac80211.h SET_IEEE80211_PERM_ADDR
!Finclude/net/mac80211.h ieee80211_ops
!Finclude/net/mac80211.h ieee80211_alloc_hw
!Finclude/net/mac80211.h ieee80211_register_hw
!Finclude/net/mac80211.h ieee80211_get_tx_led_name
!Finclude/net/mac80211.h ieee80211_get_rx_led_name
!Finclude/net/mac80211.h ieee80211_get_assoc_led_name
!Finclude/net/mac80211.h ieee80211_get_radio_led_name
!Finclude/net/mac80211.h ieee80211_unregister_hw
!Finclude/net/mac80211.h ieee80211_free_hw
</chapter>
<chapter id="phy-handling">
<title>PHY configuration</title>
<para>TBD</para>
<para>
This chapter should describe PHY handling including
start/stop callbacks and the various structures used.
</para>
!Finclude/net/mac80211.h ieee80211_conf
!Finclude/net/mac80211.h ieee80211_conf_flags
</chapter>
<chapter id="iface-handling">
<title>Virtual interfaces</title>
<para>TBD</para>
<para>
This chapter should describe virtual interface basics
that are relevant to the driver (VLANs, MGMT etc are not.)
It should explain the use of the add_iface/remove_iface
callbacks as well as the interface configuration callbacks.
</para>
<para>Things related to AP mode should be discussed there.</para>
<para>
Things related to supporting multiple interfaces should be
in the appropriate chapter, a BIG FAT note should be here about
this though and the recommendation to allow only a single
interface in STA mode at first!
</para>
!Finclude/net/mac80211.h ieee80211_if_types
!Finclude/net/mac80211.h ieee80211_if_init_conf
!Finclude/net/mac80211.h ieee80211_if_conf
</chapter>
<chapter id="rx-tx">
<title>Receive and transmit processing</title>
<sect1>
<title>what should be here</title>
<para>TBD</para>
<para>
This should describe the receive and transmit
paths in mac80211/the drivers as well as
transmit status handling.
</para>
</sect1>
<sect1>
<title>Frame format</title>
!Pinclude/net/mac80211.h Frame format
</sect1>
<sect1>
<title>Alignment issues</title>
<para>TBD</para>
</sect1>
<sect1>
<title>Calling into mac80211 from interrupts</title>
!Pinclude/net/mac80211.h Calling mac80211 from interrupts
</sect1>
<sect1>
<title>functions/definitions</title>
!Finclude/net/mac80211.h ieee80211_rx_status
!Finclude/net/mac80211.h mac80211_rx_flags
!Finclude/net/mac80211.h ieee80211_tx_control
!Finclude/net/mac80211.h ieee80211_tx_status_flags
!Finclude/net/mac80211.h ieee80211_rx
!Finclude/net/mac80211.h ieee80211_rx_irqsafe
!Finclude/net/mac80211.h ieee80211_tx_status
!Finclude/net/mac80211.h ieee80211_tx_status_irqsafe
!Finclude/net/mac80211.h ieee80211_rts_get
!Finclude/net/mac80211.h ieee80211_rts_duration
!Finclude/net/mac80211.h ieee80211_ctstoself_get
!Finclude/net/mac80211.h ieee80211_ctstoself_duration
!Finclude/net/mac80211.h ieee80211_generic_frame_duration
!Finclude/net/mac80211.h ieee80211_get_hdrlen_from_skb
!Finclude/net/mac80211.h ieee80211_get_hdrlen
!Finclude/net/mac80211.h ieee80211_wake_queue
!Finclude/net/mac80211.h ieee80211_stop_queue
!Finclude/net/mac80211.h ieee80211_start_queues
!Finclude/net/mac80211.h ieee80211_stop_queues
!Finclude/net/mac80211.h ieee80211_wake_queues
</sect1>
</chapter>
<chapter id="filters">
<title>Frame filtering</title>
!Pinclude/net/mac80211.h Frame filtering
!Finclude/net/mac80211.h ieee80211_filter_flags
</chapter>
</part>
<part id="advanced">
<title>Advanced driver interface</title>
<partintro>
<para>
Information contained within this part of the book is
of interest only for advanced interaction of mac80211
with drivers to exploit more hardware capabilities and
improve performance.
</para>
</partintro>
<chapter id="hardware-crypto-offload">
<title>Hardware crypto acceleration</title>
!Pinclude/net/mac80211.h Hardware crypto acceleration
<!-- intentionally multiple !F lines to get proper order -->
!Finclude/net/mac80211.h set_key_cmd
!Finclude/net/mac80211.h ieee80211_key_conf
!Finclude/net/mac80211.h ieee80211_key_alg
!Finclude/net/mac80211.h ieee80211_key_flags
</chapter>
<chapter id="qos">
<title>Multiple queues and QoS support</title>
<para>TBD</para>
!Finclude/net/mac80211.h ieee80211_tx_queue_params
!Finclude/net/mac80211.h ieee80211_tx_queue_stats_data
!Finclude/net/mac80211.h ieee80211_tx_queue
</chapter>
<chapter id="AP">
<title>Access point mode support</title>
<para>TBD</para>
<para>Some parts of the if_conf should be discussed here instead</para>
<para>
Insert notes about VLAN interfaces with hw crypto here or
in the hw crypto chapter.
</para>
!Finclude/net/mac80211.h ieee80211_get_buffered_bc
!Finclude/net/mac80211.h ieee80211_beacon_get
</chapter>
<chapter id="multi-iface">
<title>Supporting multiple virtual interfaces</title>
<para>TBD</para>
<para>
Note: WDS with identical MAC address should almost always be OK
</para>
<para>
Insert notes about having multiple virtual interfaces with
different MAC addresses here, note which configurations are
supported by mac80211, add notes about supporting hw crypto
with it.
</para>
</chapter>
<chapter id="hardware-scan-offload">
<title>Hardware scan offload</title>
<para>TBD</para>
!Finclude/net/mac80211.h ieee80211_scan_completed
</chapter>
</part>
<part id="rate-control">
<title>Rate control interface</title>
<partintro>
<para>TBD</para>
<para>
This part of the book describes the rate control algorithm
interface and how it relates to mac80211 and drivers.
</para>
</partintro>
<chapter id="dummy">
<title>dummy chapter</title>
<para>TBD</para>
</chapter>
</part>
<part id="internal">
<title>Internals</title>
<partintro>
<para>TBD</para>
<para>
This part of the book describes mac80211 internals.
</para>
</partintro>
<chapter id="key-handling">
<title>Key handling</title>
<sect1>
<title>Key handling basics</title>
!Pnet/mac80211/key.c Key handling basics
</sect1>
<sect1>
<title>MORE TBD</title>
<para>TBD</para>
</sect1>
</chapter>
<chapter id="rx-processing">
<title>Receive processing</title>
<para>TBD</para>
</chapter>
<chapter id="tx-processing">
<title>Transmit processing</title>
<para>TBD</para>
</chapter>
<chapter id="sta-info">
<title>Station info handling</title>
<sect1>
<title>Programming information</title>
!Fnet/mac80211/sta_info.h sta_info
!Fnet/mac80211/sta_info.h ieee80211_sta_info_flags
</sect1>
<sect1>
<title>STA information lifetime rules</title>
!Pnet/mac80211/sta_info.c STA information lifetime rules
</sect1>
</chapter>
<chapter id="synchronisation">
<title>Synchronisation</title>
<para>TBD</para>
<para>Locking, lots of RCU</para>
</chapter>
</part>
</book>

View file

@ -140,7 +140,8 @@ enum ath5k_radio {
AR5K_RF5110 = 0,
AR5K_RF5111 = 1,
AR5K_RF5112 = 2,
AR5K_RF5413 = 3,
AR5K_RF2413 = 3,
AR5K_RF5413 = 4,
};
/*
@ -168,12 +169,15 @@ struct ath5k_srev_name {
#define AR5K_SREV_VER_AR5212 0x50
#define AR5K_SREV_VER_AR5213 0x55
#define AR5K_SREV_VER_AR5213A 0x59
#define AR5K_SREV_VER_AR2424 0xa0
#define AR5K_SREV_VER_AR5424 0xa3
#define AR5K_SREV_VER_AR2413 0x78
#define AR5K_SREV_VER_AR2414 0x79
#define AR5K_SREV_VER_AR2424 0xa0 /* PCI-E */
#define AR5K_SREV_VER_AR5424 0xa3 /* PCI-E */
#define AR5K_SREV_VER_AR5413 0xa4
#define AR5K_SREV_VER_AR5414 0xa5
#define AR5K_SREV_VER_AR5416 0xc0 /* ? */
#define AR5K_SREV_VER_AR5418 0xca
#define AR5K_SREV_VER_AR5416 0xc0 /* PCI-E */
#define AR5K_SREV_VER_AR5418 0xca /* PCI-E */
#define AR5K_SREV_VER_AR2425 0xe2 /* PCI-E */
#define AR5K_SREV_RAD_5110 0x00
#define AR5K_SREV_RAD_5111 0x10
@ -183,8 +187,9 @@ struct ath5k_srev_name {
#define AR5K_SREV_RAD_5112A 0x35
#define AR5K_SREV_RAD_2112 0x40
#define AR5K_SREV_RAD_2112A 0x45
#define AR5K_SREV_RAD_SC0 0x56 /* Found on 2413/2414 */
#define AR5K_SREV_RAD_SC1 0x63 /* Found on 5413/5414 */
#define AR5K_SREV_RAD_SC2 0xa2 /* Found on 2424/5424 */
#define AR5K_SREV_RAD_SC2 0xa2 /* Found on 2424-5/5424 */
#define AR5K_SREV_RAD_5133 0xc0 /* MIMO found on 5418 */
/* IEEE defs */
@ -268,12 +273,13 @@ enum ath5k_driver_mode {
#define SHPREAMBLE_FLAG(_ix) \
(HAS_SHPREAMBLE(_ix) ? AR5K_SET_SHORT_PREAMBLE : 0)
/****************\
TX DEFINITIONS
\****************/
/*
* Tx Descriptor
* TX Status
*/
struct ath5k_tx_status {
u16 ts_seqnum;
@ -421,7 +427,7 @@ enum ath5k_dmasize {
\****************/
/*
* Rx Descriptor
* RX Status
*/
struct ath5k_rx_status {
u16 rs_datalen;
@ -452,8 +458,6 @@ struct ath5k_mib_stats {
};
/**************************\
BEACON TIMERS DEFINITIONS
\**************************/
@ -495,29 +499,23 @@ struct ath5k_beacon_state {
#define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10)
/********************\
COMMON DEFINITIONS
\********************/
/*
* Atheros descriptor
* Atheros hardware descriptor
* This is read and written to by the hardware
*/
struct ath5k_desc {
u32 ds_link;
u32 ds_data;
u32 ds_ctl0;
u32 ds_ctl1;
u32 ds_hw[4];
u32 ds_link; /* physical address of the next descriptor */
u32 ds_data; /* physical address of data buffer (skb) */
union {
struct ath5k_rx_status rx;
struct ath5k_tx_status tx;
} ds_us;
#define ds_rxstat ds_us.rx
#define ds_txstat ds_us.tx
struct ath5k_hw_5210_tx_desc ds_tx5210;
struct ath5k_hw_5212_tx_desc ds_tx5212;
struct ath5k_hw_all_rx_desc ds_rx;
} ud;
} __packed;
#define AR5K_RXDESC_INTREQ 0x0020
@ -961,6 +959,7 @@ struct ath5k_hw {
u16 ah_phy_revision;
u16 ah_radio_5ghz_revision;
u16 ah_radio_2ghz_revision;
u32 ah_phy_spending;
enum ath5k_version ah_version;
enum ath5k_radio ah_radio;
@ -1036,8 +1035,10 @@ struct ath5k_hw {
int (*ah_setup_xtx_desc)(struct ath5k_hw *, struct ath5k_desc *,
unsigned int, unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int);
int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *);
int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *);
int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *,
struct ath5k_tx_status *);
int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *,
struct ath5k_rx_status *);
};
/*

View file

@ -118,6 +118,8 @@ static struct ath5k_srev_name srev_names[] = {
{ "5212", AR5K_VERSION_VER, AR5K_SREV_VER_AR5212 },
{ "5213", AR5K_VERSION_VER, AR5K_SREV_VER_AR5213 },
{ "5213A", AR5K_VERSION_VER, AR5K_SREV_VER_AR5213A },
{ "2413", AR5K_VERSION_VER, AR5K_SREV_VER_AR2413 },
{ "2414", AR5K_VERSION_VER, AR5K_SREV_VER_AR2414 },
{ "2424", AR5K_VERSION_VER, AR5K_SREV_VER_AR2424 },
{ "5424", AR5K_VERSION_VER, AR5K_SREV_VER_AR5424 },
{ "5413", AR5K_VERSION_VER, AR5K_SREV_VER_AR5413 },
@ -132,6 +134,7 @@ static struct ath5k_srev_name srev_names[] = {
{ "5112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A },
{ "2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112 },
{ "2112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A },
{ "SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC0 },
{ "SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC1 },
{ "SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC2 },
{ "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 },
@ -280,7 +283,8 @@ static int ath5k_rx_start(struct ath5k_softc *sc);
static void ath5k_rx_stop(struct ath5k_softc *sc);
static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
struct ath5k_desc *ds,
struct sk_buff *skb);
struct sk_buff *skb,
struct ath5k_rx_status *rs);
static void ath5k_tasklet_rx(unsigned long data);
/* Tx handling */
static void ath5k_tx_processq(struct ath5k_softc *sc,
@ -1560,8 +1564,7 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
*/
spin_lock_bh(&txq->lock);
list_for_each_entry_safe(bf, bf0, &txq->q, list) {
ath5k_debug_printtxbuf(sc, bf, !sc->ah->ah_proc_tx_desc(sc->ah,
bf->desc));
ath5k_debug_printtxbuf(sc, bf);
ath5k_txbuf_free(sc, bf);
@ -1686,20 +1689,20 @@ ath5k_rx_stop(struct ath5k_softc *sc)
static unsigned int
ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
struct sk_buff *skb)
struct sk_buff *skb, struct ath5k_rx_status *rs)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int keyix, hlen = ieee80211_get_hdrlen_from_skb(skb);
if (!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) &&
ds->ds_rxstat.rs_keyix != AR5K_RXKEYIX_INVALID)
if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
rs->rs_keyix != AR5K_RXKEYIX_INVALID)
return RX_FLAG_DECRYPTED;
/* Apparently when a default key is used to decrypt the packet
the hw does not set the index used to decrypt. In such cases
get the index from the packet. */
if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) &&
!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) &&
!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
skb->len >= hlen + 4) {
keyix = skb->data[hlen + 3] >> 6;
@ -1712,8 +1715,10 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
static void
ath5k_check_ibss_hw_merge(struct ath5k_softc *sc, struct sk_buff *skb)
ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
struct ieee80211_rx_status *rxs)
{
u64 tsf, bc_tstamp;
u32 hw_tu;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
@ -1724,16 +1729,45 @@ ath5k_check_ibss_hw_merge(struct ath5k_softc *sc, struct sk_buff *skb)
le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) {
/*
* Received an IBSS beacon with the same BSSID. Hardware might
* have updated the TSF, check if we need to update timers.
* Received an IBSS beacon with the same BSSID. Hardware *must*
* have updated the local TSF. We have to work around various
* hardware bugs, though...
*/
hw_tu = TSF_TO_TU(ath5k_hw_get_tsf64(sc->ah));
if (hw_tu >= sc->nexttbtt) {
ath5k_beacon_update_timers(sc,
le64_to_cpu(mgmt->u.beacon.timestamp));
tsf = ath5k_hw_get_tsf64(sc->ah);
bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
hw_tu = TSF_TO_TU(tsf);
ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
"beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
bc_tstamp, rxs->mactime,
(rxs->mactime - bc_tstamp), tsf);
/*
* Sometimes the HW will give us a wrong tstamp in the rx
* status, causing the timestamp extension to go wrong.
* (This seems to happen especially with beacon frames bigger
* than 78 byte (incl. FCS))
* But we know that the receive timestamp must be later than the
* timestamp of the beacon since HW must have synced to that.
*
* NOTE: here we assume mactime to be after the frame was
* received, not like mac80211 which defines it at the start.
*/
if (bc_tstamp > rxs->mactime) {
ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
"detected HW merge from received beacon\n");
"fixing mactime from %llx to %llx\n",
rxs->mactime, tsf);
rxs->mactime = tsf;
}
/*
* Local TSF might have moved higher than our beacon timers,
* in that case we have to update them to continue sending
* beacons. This also takes care of synchronizing beacon sending
* times with other stations.
*/
if (hw_tu >= sc->nexttbtt)
ath5k_beacon_update_timers(sc, bc_tstamp);
}
}
@ -1742,12 +1776,11 @@ static void
ath5k_tasklet_rx(unsigned long data)
{
struct ieee80211_rx_status rxs = {};
struct ath5k_rx_status rs = {};
struct sk_buff *skb;
struct ath5k_softc *sc = (void *)data;
struct ath5k_buf *bf;
struct ath5k_desc *ds;
u16 len;
u8 stat;
int ret;
int hdrlen;
int pad;
@ -1770,7 +1803,7 @@ ath5k_tasklet_rx(unsigned long data)
if (unlikely(ds->ds_link == bf->daddr)) /* this is the end */
break;
ret = sc->ah->ah_proc_rx_desc(sc->ah, ds);
ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs);
if (unlikely(ret == -EINPROGRESS))
break;
else if (unlikely(ret)) {
@ -1779,16 +1812,15 @@ ath5k_tasklet_rx(unsigned long data)
return;
}
if (unlikely(ds->ds_rxstat.rs_more)) {
if (unlikely(rs.rs_more)) {
ATH5K_WARN(sc, "unsupported jumbo\n");
goto next;
}
stat = ds->ds_rxstat.rs_status;
if (unlikely(stat)) {
if (stat & AR5K_RXERR_PHY)
if (unlikely(rs.rs_status)) {
if (rs.rs_status & AR5K_RXERR_PHY)
goto next;
if (stat & AR5K_RXERR_DECRYPT) {
if (rs.rs_status & AR5K_RXERR_DECRYPT) {
/*
* Decrypt error. If the error occurred
* because there was no hardware key, then
@ -1799,30 +1831,29 @@ ath5k_tasklet_rx(unsigned long data)
*
* XXX do key cache faulting
*/
if (ds->ds_rxstat.rs_keyix ==
AR5K_RXKEYIX_INVALID &&
!(stat & AR5K_RXERR_CRC))
if (rs.rs_keyix == AR5K_RXKEYIX_INVALID &&
!(rs.rs_status & AR5K_RXERR_CRC))
goto accept;
}
if (stat & AR5K_RXERR_MIC) {
if (rs.rs_status & AR5K_RXERR_MIC) {
rxs.flag |= RX_FLAG_MMIC_ERROR;
goto accept;
}
/* let crypto-error packets fall through in MNTR */
if ((stat & ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) ||
if ((rs.rs_status &
~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) ||
sc->opmode != IEEE80211_IF_TYPE_MNTR)
goto next;
}
accept:
len = ds->ds_rxstat.rs_datalen;
pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, len,
PCI_DMA_FROMDEVICE);
pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr,
rs.rs_datalen, PCI_DMA_FROMDEVICE);
pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
PCI_DMA_FROMDEVICE);
bf->skb = NULL;
skb_put(skb, len);
skb_put(skb, rs.rs_datalen);
/*
* the hardware adds a padding to 4 byte boundaries between
@ -1844,8 +1875,19 @@ accept:
* 15bit only. that means TSF extension has to be done within
* 32768usec (about 32ms). it might be necessary to move this to
* the interrupt handler, like it is done in madwifi.
*
* Unfortunately we don't know when the hardware takes the rx
* timestamp (beginning of phy frame, data frame, end of rx?).
* The only thing we know is that it is hardware specific...
* On AR5213 it seems the rx timestamp is at the end of the
* frame, but i'm not sure.
*
* NOTE: mac80211 defines mactime at the beginning of the first
* data symbol. Since we don't have any time references it's
* impossible to comply to that. This affects IBSS merge only
* right now, so it's not too bad...
*/
rxs.mactime = ath5k_extend_tsf(sc->ah, ds->ds_rxstat.rs_tstamp);
rxs.mactime = ath5k_extend_tsf(sc->ah, rs.rs_tstamp);
rxs.flag |= RX_FLAG_TSFT;
rxs.freq = sc->curchan->center_freq;
@ -1859,26 +1901,25 @@ accept:
/* noise floor in dBm, from the last noise calibration */
rxs.noise = sc->ah->ah_noise_floor;
/* signal level in dBm */
rxs.ssi = rxs.noise + ds->ds_rxstat.rs_rssi;
rxs.ssi = rxs.noise + rs.rs_rssi;
/*
* "signal" is actually displayed as Link Quality by iwconfig
* we provide a percentage based on rssi (assuming max rssi 64)
*/
rxs.signal = ds->ds_rxstat.rs_rssi * 100 / 64;
rxs.signal = rs.rs_rssi * 100 / 64;
rxs.antenna = ds->ds_rxstat.rs_antenna;
rxs.rate_idx = ath5k_hw_to_driver_rix(sc,
ds->ds_rxstat.rs_rate);
rxs.flag |= ath5k_rx_decrypted(sc, ds, skb);
rxs.antenna = rs.rs_antenna;
rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate);
rxs.flag |= ath5k_rx_decrypted(sc, ds, skb, &rs);
ath5k_debug_dump_skb(sc, skb, "RX ", 0);
/* check beacons in IBSS mode */
if (sc->opmode == IEEE80211_IF_TYPE_IBSS)
ath5k_check_ibss_hw_merge(sc, skb);
ath5k_check_ibss_tsf(sc, skb, &rxs);
__ieee80211_rx(sc->hw, skb, &rxs);
sc->led_rxrate = ds->ds_rxstat.rs_rate;
sc->led_rxrate = rs.rs_rate;
ath5k_led_event(sc, ATH_LED_RX);
next:
list_move_tail(&bf->list, &sc->rxbuf);
@ -1897,6 +1938,7 @@ static void
ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
{
struct ieee80211_tx_status txs = {};
struct ath5k_tx_status ts = {};
struct ath5k_buf *bf, *bf0;
struct ath5k_desc *ds;
struct sk_buff *skb;
@ -1909,7 +1951,7 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
/* TODO only one segment */
pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr,
sc->desc_len, PCI_DMA_FROMDEVICE);
ret = sc->ah->ah_proc_tx_desc(sc->ah, ds);
ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
if (unlikely(ret == -EINPROGRESS))
break;
else if (unlikely(ret)) {
@ -1924,17 +1966,16 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
PCI_DMA_TODEVICE);
txs.control = bf->ctl;
txs.retry_count = ds->ds_txstat.ts_shortretry +
ds->ds_txstat.ts_longretry / 6;
if (unlikely(ds->ds_txstat.ts_status)) {
txs.retry_count = ts.ts_shortretry + ts.ts_longretry / 6;
if (unlikely(ts.ts_status)) {
sc->ll_stats.dot11ACKFailureCount++;
if (ds->ds_txstat.ts_status & AR5K_TXERR_XRETRY)
if (ts.ts_status & AR5K_TXERR_XRETRY)
txs.excessive_retries = 1;
else if (ds->ds_txstat.ts_status & AR5K_TXERR_FILT)
else if (ts.ts_status & AR5K_TXERR_FILT)
txs.flags |= IEEE80211_TX_STATUS_TX_FILTERED;
} else {
txs.flags |= IEEE80211_TX_STATUS_ACK;
txs.ack_signal = ds->ds_txstat.ts_rssi;
txs.ack_signal = ts.ts_rssi;
}
ieee80211_tx_status(sc->hw, skb, &txs);
@ -2108,7 +2149,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
* beacon timer registers.
*
* This is called in a variety of situations, e.g. when a beacon is received,
* when a HW merge has been detected, but also when an new IBSS is created or
* when a TSF update has been detected, but also when an new IBSS is created or
* when we otherwise know we have to update the timers, but we keep it in this
* function to have it all together in one place.
*/
@ -2208,7 +2249,7 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
* another AP to associate with.
*
* In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
* interrupts to detect HW merges only.
* interrupts to detect TSF updates only.
*
* AP mode is missing.
*/
@ -2228,7 +2269,7 @@ ath5k_beacon_config(struct ath5k_softc *sc)
* hardware send the beacons automatically. We have to load it
* only once here.
* We use the SWBA interrupt only to keep track of the beacon
* timers in order to detect HW merges (automatic TSF updates).
* timers in order to detect automatic TSF updates.
*/
ath5k_beaconq_config(sc);
@ -2441,8 +2482,8 @@ ath5k_intr(int irq, void *dev_id)
*
* In IBSS mode we use this interrupt just to
* keep track of the next TBTT (target beacon
* transmission time) in order to detect hardware
* merges (TSF updates).
* transmission time) in order to detect wether
* automatic TSF updates happened.
*/
if (sc->opmode == IEEE80211_IF_TYPE_IBSS) {
/* XXX: only if VEOL suppported */

View file

@ -200,7 +200,8 @@ static ssize_t read_file_tsf(struct file *file, char __user *user_buf,
{
struct ath5k_softc *sc = file->private_data;
char buf[100];
snprintf(buf, sizeof(buf), "0x%016llx\n", ath5k_hw_get_tsf64(sc->ah));
snprintf(buf, sizeof(buf), "0x%016llx\n",
(unsigned long long)ath5k_hw_get_tsf64(sc->ah));
return simple_read_from_buffer(user_buf, count, ppos, buf, 19);
}
@ -271,7 +272,8 @@ static ssize_t read_file_beacon(struct file *file, char __user *user_buf,
tsf = ath5k_hw_get_tsf64(sc->ah);
len += snprintf(buf+len, sizeof(buf)-len,
"TSF\t\t0x%016llx\tTU: %08x\n", tsf, TSF_TO_TU(tsf));
"TSF\t\t0x%016llx\tTU: %08x\n",
(unsigned long long)tsf, TSF_TO_TU(tsf));
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@ -497,15 +499,18 @@ ath5k_debug_dump_bands(struct ath5k_softc *sc)
}
static inline void
ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done)
ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done,
struct ath5k_rx_status *rs)
{
struct ath5k_desc *ds = bf->desc;
struct ath5k_hw_all_rx_desc *rd = &ds->ud.ds_rx;
printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n",
ds, (unsigned long long)bf->daddr,
ds->ds_link, ds->ds_data, ds->ds_ctl0, ds->ds_ctl1,
ds->ds_hw[0], ds->ds_hw[1],
!done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!');
ds->ds_link, ds->ds_data,
rd->rx_ctl.rx_control_0, rd->rx_ctl.rx_control_1,
rd->u.rx_stat.rx_status_0, rd->u.rx_stat.rx_status_0,
!done ? ' ' : (rs->rs_status == 0) ? '*' : '!');
}
void
@ -513,6 +518,7 @@ ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
{
struct ath5k_desc *ds;
struct ath5k_buf *bf;
struct ath5k_rx_status rs = {};
int status;
if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
@ -524,9 +530,9 @@ ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
spin_lock_bh(&sc->rxbuflock);
list_for_each_entry(bf, &sc->rxbuf, list) {
ds = bf->desc;
status = ah->ah_proc_rx_desc(ah, ds);
status = ah->ah_proc_rx_desc(ah, ds, &rs);
if (!status)
ath5k_debug_printrxbuf(bf, status == 0);
ath5k_debug_printrxbuf(bf, status == 0, &rs);
}
spin_unlock_bh(&sc->rxbuflock);
}
@ -550,19 +556,24 @@ ath5k_debug_dump_skb(struct ath5k_softc *sc,
}
void
ath5k_debug_printtxbuf(struct ath5k_softc *sc,
struct ath5k_buf *bf, int done)
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf)
{
struct ath5k_desc *ds = bf->desc;
struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212;
struct ath5k_tx_status ts = {};
int done;
if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
return;
done = sc->ah->ah_proc_tx_desc(sc->ah, bf->desc, &ts);
printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x "
"%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link,
ds->ds_data, ds->ds_ctl0, ds->ds_ctl1,
ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
!done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!');
ds->ds_data, td->tx_ctl.tx_control_0, td->tx_ctl.tx_control_1,
td->tx_ctl.tx_control_2, td->tx_ctl.tx_control_3,
td->tx_stat.tx_status_0, td->tx_stat.tx_status_1,
done ? ' ' : (ts.ts_status == 0) ? '*' : '!');
}
#endif /* ifdef CONFIG_ATH5K_DEBUG */

View file

@ -160,8 +160,7 @@ ath5k_debug_dump_skb(struct ath5k_softc *sc,
struct sk_buff *skb, const char *prefix, int tx);
void
ath5k_debug_printtxbuf(struct ath5k_softc *sc,
struct ath5k_buf *bf, int done);
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf);
#else /* no debugging */
@ -199,8 +198,7 @@ ath5k_debug_dump_skb(struct ath5k_softc *sc,
struct sk_buff *skb, const char *prefix, int tx) {}
static inline void
ath5k_debug_printtxbuf(struct ath5k_softc *sc,
struct ath5k_buf *bf, int done) {}
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf) {}
#endif /* ifdef CONFIG_ATH5K_DEBUG */

View file

@ -48,14 +48,18 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
static int ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
unsigned int);
static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *, struct ath5k_desc *);
static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *, struct ath5k_desc *,
struct ath5k_tx_status *);
static int ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int);
static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *, struct ath5k_desc *);
static int ath5k_hw_proc_new_rx_status(struct ath5k_hw *, struct ath5k_desc *);
static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *, struct ath5k_desc *);
static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *, struct ath5k_desc *,
struct ath5k_tx_status *);
static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *, struct ath5k_desc *,
struct ath5k_rx_status *);
static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *, struct ath5k_desc *,
struct ath5k_rx_status *);
static int ath5k_hw_get_capabilities(struct ath5k_hw *);
static int ath5k_eeprom_init(struct ath5k_hw *);
@ -174,9 +178,9 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
}
if (ah->ah_version == AR5K_AR5212)
ah->ah_proc_rx_desc = ath5k_hw_proc_new_rx_status;
ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status;
else if (ah->ah_version <= AR5K_AR5211)
ah->ah_proc_rx_desc = ath5k_hw_proc_old_rx_status;
ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status;
/* Bring device out of sleep and reset it's units */
ret = ath5k_hw_nic_wakeup(ah, AR5K_INIT_MODE, true);
@ -208,7 +212,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
/* Identify single chip solutions */
if((srev <= AR5K_SREV_VER_AR5414) &&
(srev >= AR5K_SREV_VER_AR2424)) {
(srev >= AR5K_SREV_VER_AR2413)) {
ah->ah_single_chip = true;
} else {
ah->ah_single_chip = false;
@ -223,10 +227,33 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
ah->ah_radio = AR5K_RF5110;
} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112) {
ah->ah_radio = AR5K_RF5111;
} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC1) {
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5111;
} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC0) {
ah->ah_radio = AR5K_RF5112;
if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) {
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112;
} else {
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
}
} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC1) {
ah->ah_radio = AR5K_RF2413;
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
} else {
ah->ah_radio = AR5K_RF5413;
if (ah->ah_mac_srev <= AR5K_SREV_VER_AR5424 &&
ah->ah_mac_srev >= AR5K_SREV_VER_AR2424)
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5424;
else if (ah->ah_mac_srev >= AR5K_SREV_VER_AR2425)
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112;
else
ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
}
ah->ah_phy = AR5K_PHY(0);
@ -277,7 +304,8 @@ err:
*/
static int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
{
u32 turbo, mode, clock;
struct pci_dev *pdev = ah->ah_sc->pdev;
u32 turbo, mode, clock, bus_flags;
int ret;
turbo = 0;
@ -354,9 +382,15 @@ static int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
AR5K_PHY_TURBO);
}
/* ...reset chipset and PCI device */
if (ah->ah_single_chip == false && ath5k_hw_nic_reset(ah,
AR5K_RESET_CTL_CHIP | AR5K_RESET_CTL_PCI)) {
/* reseting PCI on PCI-E cards results card to hang
* and always return 0xffff... so we ingore that flag
* for PCI-E cards */
bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI;
/* Reset chipset */
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_BASEBAND | bus_flags);
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip + PCI\n");
return -EIO;
}
@ -565,7 +599,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
struct ieee80211_channel *channel, bool change_channel)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
u32 data, s_seq, s_ant, s_led[3];
struct pci_dev *pdev = ah->ah_sc->pdev;
u32 data, s_seq, s_ant, s_led[3], dma_size;
unsigned int i, mode, freq, ee_mode, ant[2];
int ret;
@ -617,7 +652,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
if (ah->ah_version != AR5K_AR5210) {
if (ah->ah_radio != AR5K_RF5111 &&
ah->ah_radio != AR5K_RF5112 &&
ah->ah_radio != AR5K_RF5413) {
ah->ah_radio != AR5K_RF5413 &&
ah->ah_radio != AR5K_RF2413) {
ATH5K_ERR(ah->ah_sc,
"invalid phy radio: %u\n", ah->ah_radio);
return -EINVAL;
@ -692,15 +728,26 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
/*
* Write some more initial register settings
*/
if (ah->ah_version > AR5K_AR5211){ /* found on 5213+ */
if (ah->ah_version == AR5K_AR5212) {
ath5k_hw_reg_write(ah, 0x0002a002, AR5K_PHY(11));
if (channel->hw_value == CHANNEL_G)
ath5k_hw_reg_write(ah, 0x00f80d80, AR5K_PHY(83)); /* 0x00fc0ec0 */
if (ah->ah_mac_srev < AR5K_SREV_VER_AR2413)
ath5k_hw_reg_write(ah, 0x00f80d80,
AR5K_PHY(83));
else if (ah->ah_mac_srev < AR5K_SREV_VER_AR2424)
ath5k_hw_reg_write(ah, 0x00380140,
AR5K_PHY(83));
else if (ah->ah_mac_srev < AR5K_SREV_VER_AR2425)
ath5k_hw_reg_write(ah, 0x00fc0ec0,
AR5K_PHY(83));
else /* 2425 */
ath5k_hw_reg_write(ah, 0x00fc0fc0,
AR5K_PHY(83));
else
ath5k_hw_reg_write(ah, 0x00000000, AR5K_PHY(83));
ath5k_hw_reg_write(ah, 0x00000000,
AR5K_PHY(83));
ath5k_hw_reg_write(ah, 0x000001b5, 0xa228); /* 0x000009b5 */
ath5k_hw_reg_write(ah, 0x000009b5, 0xa228);
ath5k_hw_reg_write(ah, 0x0000000f, 0x8060);
ath5k_hw_reg_write(ah, 0x00000000, 0xa254);
@ -876,13 +923,24 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
/*
* Set Rx/Tx DMA Configuration
*(passing dma size not available on 5210)
*
* Set maximum DMA size (512) except for PCI-E cards since
* it causes rx overruns and tx errors (tested on 5424 but since
* rx overruns also occur on 5416/5418 with madwifi we set 128
* for all PCI-E cards to be safe).
*
* In dumps this is 128 for allchips.
*
* XXX: need to check 5210 for this
* TODO: Check out tx triger level, it's always 64 on dumps but I
* guess we can tweak it and see how it goes ;-)
*/
dma_size = (pdev->is_pcie) ? AR5K_DMASIZE_128B : AR5K_DMASIZE_512B;
if (ah->ah_version != AR5K_AR5210) {
AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_SDMAMR,
AR5K_DMASIZE_512B | AR5K_TXCFG_DMASIZE);
AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_SDMAMW,
AR5K_DMASIZE_512B);
AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
AR5K_TXCFG_SDMAMR, dma_size);
AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
AR5K_RXCFG_SDMAMW, dma_size);
}
/*
@ -972,6 +1030,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
/*
* Set the 32MHz reference clock on 5212 phy clock sleep register
*
* TODO: Find out how to switch to external 32Khz clock to save power
*/
if (ah->ah_version == AR5K_AR5212) {
ath5k_hw_reg_write(ah, AR5K_PHY_SCR_32MHZ, AR5K_PHY_SCR);
@ -979,9 +1039,15 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ, AR5K_PHY_SCAL);
ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK);
ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY);
ath5k_hw_reg_write(ah, ah->ah_radio == AR5K_RF5111 ?
AR5K_PHY_SPENDING_RF5111 : AR5K_PHY_SPENDING_RF5112,
AR5K_PHY_SPENDING);
ath5k_hw_reg_write(ah, ah->ah_phy_spending, AR5K_PHY_SPENDING);
}
if (ah->ah_version == AR5K_AR5212) {
ath5k_hw_reg_write(ah, 0x000100aa, 0x8118);
ath5k_hw_reg_write(ah, 0x00003210, 0x811c);
ath5k_hw_reg_write(ah, 0x00000052, 0x8108);
if (ah->ah_mac_srev >= AR5K_SREV_VER_AR2413)
ath5k_hw_reg_write(ah, 0x00000004, 0x8120);
}
/*
@ -2228,8 +2294,8 @@ void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id)
* Set simple BSSID mask on 5212
*/
if (ah->ah_version == AR5K_AR5212) {
ath5k_hw_reg_write(ah, 0xfffffff, AR5K_BSS_IDM0);
ath5k_hw_reg_write(ah, 0xfffffff, AR5K_BSS_IDM1);
ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM0);
ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM1);
}
/*
@ -2374,6 +2440,8 @@ void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
/* TODO: ANI Support */
}
/*
@ -2383,6 +2451,8 @@ void ath5k_hw_stop_pcu_recv(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
/* TODO: ANI Support */
}
/*
@ -3456,10 +3526,10 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int rtscts_rate, unsigned int rtscts_duration)
{
u32 frame_type;
struct ath5k_hw_2w_tx_desc *tx_desc;
struct ath5k_hw_2w_tx_ctl *tx_ctl;
unsigned int frame_len;
tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
/*
* Validate input
@ -3478,12 +3548,8 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
return -EINVAL;
}
/* Clear status descriptor */
memset(desc->ds_hw, 0, sizeof(struct ath5k_hw_tx_status));
/* Initialize control descriptor */
tx_desc->tx_control_0 = 0;
tx_desc->tx_control_1 = 0;
/* Clear descriptor */
memset(&desc->ud.ds_tx5210, 0, sizeof(struct ath5k_hw_5210_tx_desc));
/* Setup control descriptor */
@ -3495,7 +3561,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
tx_desc->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
tx_ctl->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
/* Verify and set buffer length */
@ -3506,7 +3572,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN)
return -EINVAL;
tx_desc->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
tx_ctl->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
/*
* Verify and set header length
@ -3515,7 +3581,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
if (ah->ah_version == AR5K_AR5210) {
if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN)
return -EINVAL;
tx_desc->tx_control_0 |=
tx_ctl->tx_control_0 |=
AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN);
}
@ -3531,19 +3597,19 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
frame_type = type /*<< 2 ?*/;
}
tx_desc->tx_control_0 |=
tx_ctl->tx_control_0 |=
AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) |
AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
} else {
tx_desc->tx_control_0 |=
tx_ctl->tx_control_0 |=
AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) |
AR5K_REG_SM(antenna_mode, AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT);
tx_desc->tx_control_1 |=
tx_ctl->tx_control_1 |=
AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE);
}
#define _TX_FLAGS(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) \
tx_desc->tx_control_##_c |= \
tx_ctl->tx_control_##_c |= \
AR5K_2W_TX_DESC_CTL##_c##_##_flag
_TX_FLAGS(0, CLRDMASK);
@ -3558,9 +3624,9 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
* WEP crap
*/
if (key_index != AR5K_TXKEYIX_INVALID) {
tx_desc->tx_control_0 |=
tx_ctl->tx_control_0 |=
AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
tx_desc->tx_control_1 |=
tx_ctl->tx_control_1 |=
AR5K_REG_SM(key_index,
AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
}
@ -3570,7 +3636,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
*/
if ((ah->ah_version == AR5K_AR5210) &&
(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
tx_desc->tx_control_1 |= rtscts_duration &
tx_ctl->tx_control_1 |= rtscts_duration &
AR5K_2W_TX_DESC_CTL1_RTS_DURATION;
return 0;
@ -3586,13 +3652,11 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate,
unsigned int rtscts_duration)
{
struct ath5k_hw_4w_tx_desc *tx_desc;
struct ath5k_hw_tx_status *tx_status;
struct ath5k_hw_4w_tx_ctl *tx_ctl;
unsigned int frame_len;
ATH5K_TRACE(ah->ah_sc);
tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2];
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
/*
* Validate input
@ -3611,14 +3675,8 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
return -EINVAL;
}
/* Clear status descriptor */
memset(tx_status, 0, sizeof(struct ath5k_hw_tx_status));
/* Initialize control descriptor */
tx_desc->tx_control_0 = 0;
tx_desc->tx_control_1 = 0;
tx_desc->tx_control_2 = 0;
tx_desc->tx_control_3 = 0;
/* Clear descriptor */
memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
/* Setup control descriptor */
@ -3630,7 +3688,7 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
tx_desc->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
tx_ctl->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
/* Verify and set buffer length */
@ -3641,20 +3699,20 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN)
return -EINVAL;
tx_desc->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
tx_ctl->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
tx_desc->tx_control_0 |=
tx_ctl->tx_control_0 |=
AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
tx_desc->tx_control_1 |= AR5K_REG_SM(type,
tx_ctl->tx_control_1 |= AR5K_REG_SM(type,
AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
tx_desc->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
tx_ctl->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
tx_desc->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
tx_ctl->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
#define _TX_FLAGS(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) \
tx_desc->tx_control_##_c |= \
tx_ctl->tx_control_##_c |= \
AR5K_4W_TX_DESC_CTL##_c##_##_flag
_TX_FLAGS(0, CLRDMASK);
@ -3670,8 +3728,8 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
* WEP crap
*/
if (key_index != AR5K_TXKEYIX_INVALID) {
tx_desc->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
tx_desc->tx_control_1 |= AR5K_REG_SM(key_index,
tx_ctl->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
tx_ctl->tx_control_1 |= AR5K_REG_SM(key_index,
AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
}
@ -3682,9 +3740,9 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
if ((flags & AR5K_TXDESC_RTSENA) &&
(flags & AR5K_TXDESC_CTSENA))
return -EINVAL;
tx_desc->tx_control_2 |= rtscts_duration &
tx_ctl->tx_control_2 |= rtscts_duration &
AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
tx_desc->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
tx_ctl->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
}
@ -3699,7 +3757,7 @@ ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2,
unsigned int tx_rate3, u_int tx_tries3)
{
struct ath5k_hw_4w_tx_desc *tx_desc;
struct ath5k_hw_4w_tx_ctl *tx_ctl;
/*
* Rates can be 0 as long as the retry count is 0 too.
@ -3716,14 +3774,14 @@ ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
}
if (ah->ah_version == AR5K_AR5212) {
tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
#define _XTX_TRIES(_n) \
if (tx_tries##_n) { \
tx_desc->tx_control_2 |= \
tx_ctl->tx_control_2 |= \
AR5K_REG_SM(tx_tries##_n, \
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n); \
tx_desc->tx_control_3 |= \
tx_ctl->tx_control_3 |= \
AR5K_REG_SM(tx_rate##_n, \
AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n); \
}
@ -3744,13 +3802,15 @@ ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
* Proccess the tx status descriptor on 5210/5211
*/
static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc)
struct ath5k_desc *desc, struct ath5k_tx_status *ts)
{
struct ath5k_hw_2w_tx_ctl *tx_ctl;
struct ath5k_hw_tx_status *tx_status;
struct ath5k_hw_2w_tx_desc *tx_desc;
tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[0];
ATH5K_TRACE(ah->ah_sc);
tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
tx_status = &desc->ud.ds_tx5210.tx_stat;
/* No frame has been send or error */
if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
@ -3759,32 +3819,32 @@ static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
/*
* Get descriptor status
*/
desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
/*TODO: desc->ds_us.tx.ts_virtcol + test*/
desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
/*TODO: ts->ts_virtcol + test*/
ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_SEQ_NUM);
desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
desc->ds_us.tx.ts_antenna = 1;
desc->ds_us.tx.ts_status = 0;
desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_0,
ts->ts_antenna = 1;
ts->ts_status = 0;
ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_0,
AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
if (tx_status->tx_status_0 &
AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY;
ts->ts_status |= AR5K_TXERR_XRETRY;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO;
ts->ts_status |= AR5K_TXERR_FIFO;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT;
ts->ts_status |= AR5K_TXERR_FILT;
}
return 0;
@ -3794,14 +3854,15 @@ static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
* Proccess a tx descriptor on 5212
*/
static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc)
struct ath5k_desc *desc, struct ath5k_tx_status *ts)
{
struct ath5k_hw_4w_tx_ctl *tx_ctl;
struct ath5k_hw_tx_status *tx_status;
struct ath5k_hw_4w_tx_desc *tx_desc;
ATH5K_TRACE(ah->ah_sc);
tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2];
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
tx_status = &desc->ud.ds_tx5212.tx_stat;
/* No frame has been send or error */
if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
@ -3810,42 +3871,42 @@ static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
/*
* Get descriptor status
*/
desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_SEQ_NUM);
desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
desc->ds_us.tx.ts_antenna = (tx_status->tx_status_1 &
ts->ts_antenna = (tx_status->tx_status_1 &
AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
desc->ds_us.tx.ts_status = 0;
ts->ts_status = 0;
switch (AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) {
case 0:
desc->ds_us.tx.ts_rate = tx_desc->tx_control_3 &
ts->ts_rate = tx_ctl->tx_control_3 &
AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
break;
case 1:
desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE1);
desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
break;
case 2:
desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
break;
case 3:
desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3);
break;
}
@ -3853,13 +3914,13 @@ static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
if (tx_status->tx_status_0 &
AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY;
ts->ts_status |= AR5K_TXERR_XRETRY;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO;
ts->ts_status |= AR5K_TXERR_FIFO;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT;
ts->ts_status |= AR5K_TXERR_FILT;
}
return 0;
@ -3875,31 +3936,27 @@ static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
u32 size, unsigned int flags)
{
struct ath5k_rx_desc *rx_desc;
struct ath5k_hw_rx_ctl *rx_ctl;
ATH5K_TRACE(ah->ah_sc);
rx_desc = (struct ath5k_rx_desc *)&desc->ds_ctl0;
rx_ctl = &desc->ud.ds_rx.rx_ctl;
/*
*Clear ds_hw
* Clear the descriptor
* If we don't clean the status descriptor,
* while scanning we get too many results,
* most of them virtual, after some secs
* of scanning system hangs. M.F.
*/
memset(desc->ds_hw, 0, sizeof(desc->ds_hw));
/*Initialize rx descriptor*/
rx_desc->rx_control_0 = 0;
rx_desc->rx_control_1 = 0;
memset(&desc->ud.ds_rx, 0, sizeof(struct ath5k_hw_all_rx_desc));
/* Setup descriptor */
rx_desc->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
if (unlikely(rx_desc->rx_control_1 != size))
rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
if (unlikely(rx_ctl->rx_control_1 != size))
return -EINVAL;
if (flags & AR5K_RXDESC_INTREQ)
rx_desc->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
return 0;
}
@ -3907,67 +3964,68 @@ int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
/*
* Proccess the rx status descriptor on 5210/5211
*/
static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc)
static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc, struct ath5k_rx_status *rs)
{
struct ath5k_hw_old_rx_status *rx_status;
struct ath5k_hw_rx_status *rx_status;
rx_status = (struct ath5k_hw_old_rx_status *)&desc->ds_hw[0];
rx_status = &desc->ud.ds_rx.u.rx_stat;
/* No frame received / not ready */
if (unlikely((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_DONE)
if (unlikely((rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_DONE)
== 0))
return -EINPROGRESS;
/*
* Frame receive status
*/
desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 &
AR5K_OLD_RX_DESC_STATUS0_DATA_LEN;
desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL);
desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE);
desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 &
AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA;
desc->ds_us.rx.rs_more = rx_status->rx_status_0 &
AR5K_OLD_RX_DESC_STATUS0_MORE;
desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
desc->ds_us.rx.rs_status = 0;
rs->rs_datalen = rx_status->rx_status_0 &
AR5K_5210_RX_DESC_STATUS0_DATA_LEN;
rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL);
rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE);
rs->rs_antenna = rx_status->rx_status_0 &
AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA;
rs->rs_more = rx_status->rx_status_0 &
AR5K_5210_RX_DESC_STATUS0_MORE;
/* TODO: this timestamp is 13 bit, later on we assume 15 bit */
rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
rs->rs_status = 0;
/*
* Key table status
*/
if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID)
desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX);
if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID)
rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_KEY_INDEX);
else
desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID;
rs->rs_keyix = AR5K_RXKEYIX_INVALID;
/*
* Receive/descriptor errors
*/
if ((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK)
== 0) {
if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR)
desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC;
if ((rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) {
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_CRC;
if (rx_status->rx_status_1 &
AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN)
desc->ds_us.rx.rs_status |= AR5K_RXERR_FIFO;
AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN)
rs->rs_status |= AR5K_RXERR_FIFO;
if (rx_status->rx_status_1 &
AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR) {
desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY;
desc->ds_us.rx.rs_phyerr =
AR5K_REG_MS(rx_status->rx_status_1,
AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR);
AR5K_5210_RX_DESC_STATUS1_PHY_ERROR) {
rs->rs_status |= AR5K_RXERR_PHY;
rs->rs_phyerr = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_PHY_ERROR);
}
if (rx_status->rx_status_1 &
AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT;
AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_DECRYPT;
}
return 0;
@ -3976,71 +4034,72 @@ static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *ah,
/*
* Proccess the rx status descriptor on 5212
*/
static int ath5k_hw_proc_new_rx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc)
static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc, struct ath5k_rx_status *rs)
{
struct ath5k_hw_new_rx_status *rx_status;
struct ath5k_hw_rx_status *rx_status;
struct ath5k_hw_rx_error *rx_err;
ATH5K_TRACE(ah->ah_sc);
rx_status = (struct ath5k_hw_new_rx_status *)&desc->ds_hw[0];
rx_status = &desc->ud.ds_rx.u.rx_stat;
/* Overlay on error */
rx_err = (struct ath5k_hw_rx_error *)&desc->ds_hw[0];
rx_err = &desc->ud.ds_rx.u.rx_err;
/* No frame received / not ready */
if (unlikely((rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_DONE)
if (unlikely((rx_status->rx_status_1 & AR5K_5212_RX_DESC_STATUS1_DONE)
== 0))
return -EINPROGRESS;
/*
* Frame receive status
*/
desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 &
AR5K_NEW_RX_DESC_STATUS0_DATA_LEN;
desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL);
desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE);
desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 &
AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA;
desc->ds_us.rx.rs_more = rx_status->rx_status_0 &
AR5K_NEW_RX_DESC_STATUS0_MORE;
desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
desc->ds_us.rx.rs_status = 0;
rs->rs_datalen = rx_status->rx_status_0 &
AR5K_5212_RX_DESC_STATUS0_DATA_LEN;
rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL);
rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE);
rs->rs_antenna = rx_status->rx_status_0 &
AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA;
rs->rs_more = rx_status->rx_status_0 &
AR5K_5212_RX_DESC_STATUS0_MORE;
rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
rs->rs_status = 0;
/*
* Key table status
*/
if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID)
desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX);
if (rx_status->rx_status_1 & AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID)
rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5212_RX_DESC_STATUS1_KEY_INDEX);
else
desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID;
rs->rs_keyix = AR5K_RXKEYIX_INVALID;
/*
* Receive/descriptor errors
*/
if ((rx_status->rx_status_1 &
AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) {
if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR)
desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC;
AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) {
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_CRC;
if (rx_status->rx_status_1 &
AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR) {
desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY;
desc->ds_us.rx.rs_phyerr =
AR5K_REG_MS(rx_err->rx_error_1,
AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) {
rs->rs_status |= AR5K_RXERR_PHY;
rs->rs_phyerr = AR5K_REG_MS(rx_err->rx_error_1,
AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
}
if (rx_status->rx_status_1 &
AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT;
AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_DECRYPT;
if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR)
desc->ds_us.rx.rs_status |= AR5K_RXERR_MIC;
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_MIC_ERROR)
rs->rs_status |= AR5K_RXERR_MIC;
}
return 0;

View file

@ -173,7 +173,10 @@ struct ath5k_eeprom_info {
* (rX: reserved fields possibily used by future versions of the ar5k chipset)
*/
struct ath5k_rx_desc {
/*
* common hardware RX control descriptor
*/
struct ath5k_hw_rx_ctl {
u32 rx_control_0; /* RX control word 0 */
#define AR5K_DESC_RX_CTL0 0x00000000
@ -185,69 +188,63 @@ struct ath5k_rx_desc {
} __packed;
/*
* 5210/5211 rx status descriptor
* common hardware RX status descriptor
* 5210/11 and 5212 differ only in the flags defined below
*/
struct ath5k_hw_old_rx_status {
struct ath5k_hw_rx_status {
u32 rx_status_0; /* RX status word 0 */
#define AR5K_OLD_RX_DESC_STATUS0_DATA_LEN 0x00000fff
#define AR5K_OLD_RX_DESC_STATUS0_MORE 0x00001000
#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000
#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000
#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19
#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA 0x38000000
#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 27
u32 rx_status_1; /* RX status word 1 */
#define AR5K_OLD_RX_DESC_STATUS1_DONE 0x00000001
#define AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002
#define AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR 0x00000004
#define AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN 0x00000008
#define AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010
#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR 0x000000e0
#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR_S 5
#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100
#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX 0x00007e00
#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000
#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15
#define AR5K_OLD_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000
} __packed;
/* 5210/5211 */
#define AR5K_5210_RX_DESC_STATUS0_DATA_LEN 0x00000fff
#define AR5K_5210_RX_DESC_STATUS0_MORE 0x00001000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA 0x38000000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 27
#define AR5K_5210_RX_DESC_STATUS1_DONE 0x00000001
#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002
#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR 0x00000004
#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN 0x00000008
#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR 0x000000e0
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S 5
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15
#define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000
/* 5212 */
#define AR5K_5212_RX_DESC_STATUS0_DATA_LEN 0x00000fff
#define AR5K_5212_RX_DESC_STATUS0_MORE 0x00001000
#define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28
#define AR5K_5212_RX_DESC_STATUS1_DONE 0x00000001
#define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002
#define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR 0x00000004
#define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008
#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR 0x00000010
#define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR 0x00000020
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000
#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16
#define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000
/*
* 5212 rx status descriptor
* common hardware RX error descriptor
*/
struct ath5k_hw_new_rx_status {
u32 rx_status_0; /* RX status word 0 */
#define AR5K_NEW_RX_DESC_STATUS0_DATA_LEN 0x00000fff
#define AR5K_NEW_RX_DESC_STATUS0_MORE 0x00001000
#define AR5K_NEW_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000
#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000
#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000
#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20
#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000
#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28
u32 rx_status_1; /* RX status word 1 */
#define AR5K_NEW_RX_DESC_STATUS1_DONE 0x00000001
#define AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002
#define AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR 0x00000004
#define AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008
#define AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR 0x00000010
#define AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR 0x00000020
#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100
#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00
#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000
#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16
#define AR5K_NEW_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000
} __packed;
struct ath5k_hw_rx_error {
u32 rx_error_0; /* RX error word 0 */
@ -268,7 +265,10 @@ struct ath5k_hw_rx_error {
#define AR5K_DESC_RX_PHY_ERROR_SERVICE 0xc0
#define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR 0xe0
struct ath5k_hw_2w_tx_desc {
/*
* 5210/5211 hardware 2-word TX control descriptor
*/
struct ath5k_hw_2w_tx_ctl {
u32 tx_control_0; /* TX control word 0 */
#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN 0x00000fff
@ -314,9 +314,9 @@ struct ath5k_hw_2w_tx_desc {
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 0x10
/*
* 5212 4-word tx control descriptor
* 5212 hardware 4-word TX control descriptor
*/
struct ath5k_hw_4w_tx_desc {
struct ath5k_hw_4w_tx_ctl {
u32 tx_control_0; /* TX control word 0 */
#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN 0x00000fff
@ -374,7 +374,7 @@ struct ath5k_hw_4w_tx_desc {
} __packed;
/*
* Common tx status descriptor
* Common TX status descriptor
*/
struct ath5k_hw_tx_status {
u32 tx_status_0; /* TX status word 0 */
@ -414,6 +414,34 @@ struct ath5k_hw_tx_status {
} __packed;
/*
* 5210/5211 hardware TX descriptor
*/
struct ath5k_hw_5210_tx_desc {
struct ath5k_hw_2w_tx_ctl tx_ctl;
struct ath5k_hw_tx_status tx_stat;
} __packed;
/*
* 5212 hardware TX descriptor
*/
struct ath5k_hw_5212_tx_desc {
struct ath5k_hw_4w_tx_ctl tx_ctl;
struct ath5k_hw_tx_status tx_stat;
} __packed;
/*
* common hardware RX descriptor
*/
struct ath5k_hw_all_rx_desc {
struct ath5k_hw_rx_ctl rx_ctl;
union {
struct ath5k_hw_rx_status rx_stat;
struct ath5k_hw_rx_error rx_err;
} u;
} __packed;
/*
* AR5K REGISTER ACCESS
*/

View file

@ -678,8 +678,8 @@ static const struct ath5k_ini ar5212_ini[] = {
{ AR5K_PHY(644), 0x00806333 },
{ AR5K_PHY(645), 0x00106c10 },
{ AR5K_PHY(646), 0x009c4060 },
/*{ AR5K_PHY(647), 0x1483800a },*/ /* Old value */
{ AR5K_PHY(647), 0x1483800a },
/* { AR5K_PHY(648), 0x018830c6 },*/ /* 2413 */
{ AR5K_PHY(648), 0x01831061 },
{ AR5K_PHY(649), 0x00000400 },
/*{ AR5K_PHY(650), 0x000001b5 },*/
@ -1081,6 +1081,207 @@ static const struct ath5k_ini_mode rf5413_ini_mode_end[] = {
{ 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0 } },
};
/* Initial mode-specific settings for RF2413/2414 (Written after ar5212_ini) */
/* XXX: No dumps for turbog yet, so turbog is the same with g here with some
* minor tweaking based on dumps from other chips */
static const struct ath5k_ini_mode rf2413_ini_mode_end[] = {
{ AR5K_TXCFG,
/* b g gTurbo */
{ 0x00000015, 0x00000015, 0x00000015 } },
{ AR5K_USEC_5211,
{ 0x04e01395, 0x12e013ab, 0x098813cf } },
{ AR5K_PHY(10),
{ 0x05020000, 0x0a020001, 0x0a020001 } },
{ AR5K_PHY(13),
{ 0x00000e00, 0x00000e00, 0x00000e00 } },
{ AR5K_PHY(14),
{ 0x0000000a, 0x0000000a, 0x0000000a } },
{ AR5K_PHY(18),
{ 0x001a6a64, 0x001a6a64, 0x001a6a64 } },
{ AR5K_PHY(20),
{ 0x0de8b0da, 0x0c98b0da, 0x0c98b0da } },
{ AR5K_PHY_SIG,
{ 0x7ee80d2e, 0x7ec80d2e, 0x7ec80d2e } },
{ AR5K_PHY_AGCCOARSE,
{ 0x3137665e, 0x3139605e, 0x3139605e } },
{ AR5K_PHY(27),
{ 0x050cb081, 0x050cb081, 0x050cb081 } },
{ AR5K_PHY_RX_DELAY,
{ 0x0000044c, 0x00000898, 0x000007d0 } },
{ AR5K_PHY_FRAME_CTL_5211,
{ 0xf7b80d00, 0xf7b81000, 0xf7b81000 } },
{ AR5K_PHY_CCKTXCTL,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ AR5K_PHY(642),
{ 0xd03e6788, 0xd03e6788, 0xd03e6788 } },
{ AR5K_PHY_GAIN_2GHZ,
{ 0x0042c140, 0x0042c140, 0x0042c140 } },
{ 0xa21c,
{ 0x1863800a, 0x1883800a, 0x1883800a } },
{ AR5K_DCU_FP,
{ 0x000003e0, 0x000003e0, 0x000003e0 } },
{ 0x8060,
{ 0x0000000f, 0x0000000f, 0x0000000f } },
{ 0x8118,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x811c,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x8120,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x8124,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x8128,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x812c,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x8130,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x8134,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x8138,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x813c,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0x8140,
{ 0x800000a8, 0x800000a8, 0x800000a8 } },
{ 0x8144,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ AR5K_PHY_AGC,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ AR5K_PHY(11),
{ 0x0000a000, 0x0000a000, 0x0000a000 } },
{ AR5K_PHY(15),
{ 0x00200400, 0x00200400, 0x00200400 } },
{ AR5K_PHY(19),
{ 0x1284233c, 0x1284233c, 0x1284233c } },
{ AR5K_PHY_SCR,
{ 0x0000001f, 0x0000001f, 0x0000001f } },
{ AR5K_PHY_SLMT,
{ 0x00000080, 0x00000080, 0x00000080 } },
{ AR5K_PHY_SCAL,
{ 0x0000000e, 0x0000000e, 0x0000000e } },
{ AR5K_PHY(86),
{ 0x000000ff, 0x000000ff, 0x000000ff } },
{ AR5K_PHY(96),
{ 0x00000000, 0x00000000, 0x00000000 } },
{ AR5K_PHY(97),
{ 0x02800000, 0x02800000, 0x02800000 } },
{ AR5K_PHY(104),
{ 0x00000000, 0x00000000, 0x00000000 } },
{ AR5K_PHY(120),
{ 0x00000000, 0x00000000, 0x00000000 } },
{ AR5K_PHY(121),
{ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa } },
{ AR5K_PHY(122),
{ 0x3c466478, 0x3c466478, 0x3c466478 } },
{ AR5K_PHY(123),
{ 0x000000aa, 0x000000aa, 0x000000aa } },
{ AR5K_PHY_SCLOCK,
{ 0x0000000c, 0x0000000c, 0x0000000c } },
{ AR5K_PHY_SDELAY,
{ 0x000000ff, 0x000000ff, 0x000000ff } },
{ AR5K_PHY_SPENDING,
{ 0x00000014, 0x00000014, 0x00000014 } },
{ 0xa228,
{ 0x000009b5, 0x000009b5, 0x000009b5 } },
{ 0xa23c,
{ 0x93c889af, 0x93c889af, 0x93c889af } },
{ 0xa24c,
{ 0x00000001, 0x00000001, 0x00000001 } },
{ 0xa250,
{ 0x0000a000, 0x0000a000, 0x0000a000 } },
{ 0xa254,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0xa258,
{ 0x0cc75380, 0x0cc75380, 0x0cc75380 } },
{ 0xa25c,
{ 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01 } },
{ 0xa260,
{ 0x5f690f01, 0x5f690f01, 0x5f690f01 } },
{ 0xa264,
{ 0x00418a11, 0x00418a11, 0x00418a11 } },
{ 0xa268,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0xa26c,
{ 0x0c30c16a, 0x0c30c16a, 0x0c30c16a } },
{ 0xa270,
{ 0x00820820, 0x00820820, 0x00820820 } },
{ 0xa274,
{ 0x001b7caa, 0x001b7caa, 0x001b7caa } },
{ 0xa278,
{ 0x1ce739ce, 0x1ce739ce, 0x1ce739ce } },
{ 0xa27c,
{ 0x051701ce, 0x051701ce, 0x051701ce } },
{ 0xa300,
{ 0x18010000, 0x18010000, 0x18010000 } },
{ 0xa304,
{ 0x30032602, 0x30032602, 0x30032602 } },
{ 0xa308,
{ 0x48073e06, 0x48073e06, 0x48073e06 } },
{ 0xa30c,
{ 0x560b4c0a, 0x560b4c0a, 0x560b4c0a } },
{ 0xa310,
{ 0x641a600f, 0x641a600f, 0x641a600f } },
{ 0xa314,
{ 0x784f6e1b, 0x784f6e1b, 0x784f6e1b } },
{ 0xa318,
{ 0x868f7c5a, 0x868f7c5a, 0x868f7c5a } },
{ 0xa31c,
{ 0x8ecf865b, 0x8ecf865b, 0x8ecf865b } },
{ 0xa320,
{ 0x9d4f970f, 0x9d4f970f, 0x9d4f970f } },
{ 0xa324,
{ 0xa5cfa18f, 0xa5cfa18f, 0xa5cfa18f } },
{ 0xa328,
{ 0xb55faf1f, 0xb55faf1f, 0xb55faf1f } },
{ 0xa32c,
{ 0xbddfb99f, 0xbddfb99f, 0xbddfb99f } },
{ 0xa330,
{ 0xcd7fc73f, 0xcd7fc73f, 0xcd7fc73f } },
{ 0xa334,
{ 0xd5ffd1bf, 0xd5ffd1bf, 0xd5ffd1bf } },
{ 0xa338,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0xa33c,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0xa340,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0xa344,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 0xa348,
{ 0x3fffffff, 0x3fffffff, 0x3fffffff } },
{ 0xa34c,
{ 0x3fffffff, 0x3fffffff, 0x3fffffff } },
{ 0xa350,
{ 0x3fffffff, 0x3fffffff, 0x3fffffff } },
{ 0xa354,
{ 0x0003ffff, 0x0003ffff, 0x0003ffff } },
{ 0xa358,
{ 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f } },
{ 0xa35c,
{ 0x066c420f, 0x066c420f, 0x066c420f } },
{ 0xa360,
{ 0x0f282207, 0x0f282207, 0x0f282207 } },
{ 0xa364,
{ 0x17601685, 0x17601685, 0x17601685 } },
{ 0xa368,
{ 0x1f801104, 0x1f801104, 0x1f801104 } },
{ 0xa36c,
{ 0x37a00c03, 0x37a00c03, 0x37a00c03 } },
{ 0xa370,
{ 0x3fc40883, 0x3fc40883, 0x3fc40883 } },
{ 0xa374,
{ 0x57c00803, 0x57c00803, 0x57c00803 } },
{ 0xa378,
{ 0x5fd80682, 0x5fd80682, 0x5fd80682 } },
{ 0xa37c,
{ 0x7fe00482, 0x7fe00482, 0x7fe00482 } },
{ 0xa380,
{ 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba } },
{ 0xa384,
{ 0xf3307ff0, 0xf3307ff0, 0xf3307ff0 } },
};
/*
* Initial BaseBand Gain settings for RF5111/5112 (AR5210 comes with
* RF5110 only so initial BB Gain settings are included in AR5K_AR5210_INI)
@ -1290,29 +1491,57 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel)
/* Second set of mode-specific settings */
if (ah->ah_radio == AR5K_RF5111){
ath5k_hw_ini_mode_registers(ah,
ARRAY_SIZE(ar5212_rf5111_ini_mode_end),
ar5212_rf5111_ini_mode_end, mode);
/* Baseband gain table */
ath5k_hw_ini_registers(ah,
ARRAY_SIZE(rf5111_ini_bbgain),
rf5111_ini_bbgain, change_channel);
} else if (ah->ah_radio == AR5K_RF5112){
ath5k_hw_ini_mode_registers(ah,
ARRAY_SIZE(ar5212_rf5112_ini_mode_end),
ar5212_rf5112_ini_mode_end, mode);
/* Baseband gain table */
ath5k_hw_ini_registers(ah,
ARRAY_SIZE(rf5112_ini_bbgain),
rf5112_ini_bbgain, change_channel);
} else if (ah->ah_radio == AR5K_RF5413){
ath5k_hw_ini_mode_registers(ah,
ARRAY_SIZE(rf5413_ini_mode_end),
rf5413_ini_mode_end, mode);
ath5k_hw_ini_registers(ah,
ARRAY_SIZE(rf5112_ini_bbgain),
rf5112_ini_bbgain, change_channel);
} else if (ah->ah_radio == AR5K_RF2413) {
if (mode < 2) {
ATH5K_ERR(ah->ah_sc,
"unsupported channel mode: %d\n", mode);
return -EINVAL;
}
mode = mode - 2;
/* Override a setting from ar5212_ini */
ath5k_hw_reg_write(ah, 0x018830c6, AR5K_PHY(648));
ath5k_hw_ini_mode_registers(ah,
ARRAY_SIZE(rf2413_ini_mode_end),
rf2413_ini_mode_end, mode);
/* Baseband gain table */
ath5k_hw_ini_registers(ah,
ARRAY_SIZE(rf5112_ini_bbgain),
rf5112_ini_bbgain, change_channel);
}
/* For AR5211 */
} else if (ah->ah_version == AR5K_AR5211) {

View file

@ -666,6 +666,75 @@ static const struct ath5k_ini_rf rfregs_5413[] = {
{ 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
};
/* RF2413/2414 mode-specific init registers */
static const struct ath5k_ini_rf rfregs_2413[] = {
{ 1, AR5K_RF_BUFFER_CONTROL_4,
{ 0x00000020, 0x00000020, 0x00000020 } },
{ 2, AR5K_RF_BUFFER_CONTROL_3,
{ 0x02001408, 0x02001408, 0x02001408 } },
{ 3, AR5K_RF_BUFFER_CONTROL_6,
{ 0x00e020c0, 0x00e020c0, 0x00e020c0 } },
{ 6, AR5K_RF_BUFFER,
{ 0xf0000000, 0xf0000000, 0xf0000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x03000000, 0x03000000, 0x03000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x40400000, 0x40400000, 0x40400000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x65050000, 0x65050000, 0x65050000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00420000, 0x00420000, 0x00420000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00b50000, 0x00b50000, 0x00b50000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00030000, 0x00030000, 0x00030000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00f70000, 0x00f70000, 0x00f70000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x009d0000, 0x009d0000, 0x009d0000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00220000, 0x00220000, 0x00220000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x04220000, 0x04220000, 0x04220000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00230018, 0x00230018, 0x00230018 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00280050, 0x00280050, 0x00280050 } },
{ 6, AR5K_RF_BUFFER,
{ 0x005000c3, 0x005000c3, 0x005000c3 } },
{ 6, AR5K_RF_BUFFER,
{ 0x0004007f, 0x0004007f, 0x0004007f } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000458, 0x00000458, 0x00000458 } },
{ 6, AR5K_RF_BUFFER,
{ 0x00000000, 0x00000000, 0x00000000 } },
{ 6, AR5K_RF_BUFFER,
{ 0x0000c000, 0x0000c000, 0x0000c000 } },
{ 6, AR5K_RF_BUFFER_CONTROL_5,
{ 0x00400230, 0x00400230, 0x00400230 } },
{ 7, AR5K_RF_BUFFER,
{ 0x00006400, 0x00006400, 0x00006400 } },
{ 7, AR5K_RF_BUFFER,
{ 0x00000800, 0x00000800, 0x00000800 } },
{ 7, AR5K_RF_BUFFER_CONTROL_2,
{ 0x0000000e, 0x0000000e, 0x0000000e } },
};
/* Initial RF Gain settings for RF5112 */
static const struct ath5k_ini_rfgain rfgain_5112[] = {
@ -805,6 +874,74 @@ static const struct ath5k_ini_rfgain rfgain_5413[] = {
{ AR5K_RF_GAIN(63), { 0x000000f9, 0x000000f9 } },
};
/* Initial RF Gain settings for RF2413 */
static const struct ath5k_ini_rfgain rfgain_2413[] = {
{ AR5K_RF_GAIN(0), { 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000181 } },
{ AR5K_RF_GAIN(4), { 0x000001c1 } },
{ AR5K_RF_GAIN(5), { 0x00000001 } },
{ AR5K_RF_GAIN(6), { 0x00000041 } },
{ AR5K_RF_GAIN(7), { 0x00000081 } },
{ AR5K_RF_GAIN(8), { 0x00000168 } },
{ AR5K_RF_GAIN(9), { 0x000001a8 } },
{ AR5K_RF_GAIN(10), { 0x000001e8 } },
{ AR5K_RF_GAIN(11), { 0x00000028 } },
{ AR5K_RF_GAIN(12), { 0x00000068 } },
{ AR5K_RF_GAIN(13), { 0x00000189 } },
{ AR5K_RF_GAIN(14), { 0x000001c9 } },
{ AR5K_RF_GAIN(15), { 0x00000009 } },
{ AR5K_RF_GAIN(16), { 0x00000049 } },
{ AR5K_RF_GAIN(17), { 0x00000089 } },
{ AR5K_RF_GAIN(18), { 0x00000190 } },
{ AR5K_RF_GAIN(19), { 0x000001d0 } },
{ AR5K_RF_GAIN(20), { 0x00000010 } },
{ AR5K_RF_GAIN(21), { 0x00000050 } },
{ AR5K_RF_GAIN(22), { 0x00000090 } },
{ AR5K_RF_GAIN(23), { 0x00000191 } },
{ AR5K_RF_GAIN(24), { 0x000001d1 } },
{ AR5K_RF_GAIN(25), { 0x00000011 } },
{ AR5K_RF_GAIN(26), { 0x00000051 } },
{ AR5K_RF_GAIN(27), { 0x00000091 } },
{ AR5K_RF_GAIN(28), { 0x00000178 } },
{ AR5K_RF_GAIN(29), { 0x000001b8 } },
{ AR5K_RF_GAIN(30), { 0x000001f8 } },
{ AR5K_RF_GAIN(31), { 0x00000038 } },
{ AR5K_RF_GAIN(32), { 0x00000078 } },
{ AR5K_RF_GAIN(33), { 0x00000199 } },
{ AR5K_RF_GAIN(34), { 0x000001d9 } },
{ AR5K_RF_GAIN(35), { 0x00000019 } },
{ AR5K_RF_GAIN(36), { 0x00000059 } },
{ AR5K_RF_GAIN(37), { 0x00000099 } },
{ AR5K_RF_GAIN(38), { 0x000000d9 } },
{ AR5K_RF_GAIN(39), { 0x000000f9 } },
{ AR5K_RF_GAIN(40), { 0x000000f9 } },
{ AR5K_RF_GAIN(41), { 0x000000f9 } },
{ AR5K_RF_GAIN(42), { 0x000000f9 } },
{ AR5K_RF_GAIN(43), { 0x000000f9 } },
{ AR5K_RF_GAIN(44), { 0x000000f9 } },
{ AR5K_RF_GAIN(45), { 0x000000f9 } },
{ AR5K_RF_GAIN(46), { 0x000000f9 } },
{ AR5K_RF_GAIN(47), { 0x000000f9 } },
{ AR5K_RF_GAIN(48), { 0x000000f9 } },
{ AR5K_RF_GAIN(49), { 0x000000f9 } },
{ AR5K_RF_GAIN(50), { 0x000000f9 } },
{ AR5K_RF_GAIN(51), { 0x000000f9 } },
{ AR5K_RF_GAIN(52), { 0x000000f9 } },
{ AR5K_RF_GAIN(53), { 0x000000f9 } },
{ AR5K_RF_GAIN(54), { 0x000000f9 } },
{ AR5K_RF_GAIN(55), { 0x000000f9 } },
{ AR5K_RF_GAIN(56), { 0x000000f9 } },
{ AR5K_RF_GAIN(57), { 0x000000f9 } },
{ AR5K_RF_GAIN(58), { 0x000000f9 } },
{ AR5K_RF_GAIN(59), { 0x000000f9 } },
{ AR5K_RF_GAIN(60), { 0x000000f9 } },
{ AR5K_RF_GAIN(61), { 0x000000f9 } },
{ AR5K_RF_GAIN(62), { 0x000000f9 } },
{ AR5K_RF_GAIN(63), { 0x000000f9 } },
};
static const struct ath5k_gain_opt rfgain_opt_5112 = {
1,
8,
@ -955,7 +1092,6 @@ static s32 ath5k_hw_rfregs_gain_adjust(struct ath5k_hw *ah)
go = &rfgain_opt_5111;
break;
case AR5K_RF5112:
case AR5K_RF5413: /* ??? */
go = &rfgain_opt_5112;
break;
default:
@ -1226,8 +1362,21 @@ static int ath5k_hw_rf5413_rfregs(struct ath5k_hw *ah,
rf = ah->ah_rf_banks;
rf_ini = rfregs_5413;
rf_size = ARRAY_SIZE(rfregs_5413);
if (ah->ah_radio == AR5K_RF5413) {
rf_ini = rfregs_5413;
rf_size = ARRAY_SIZE(rfregs_5413);
} else if (ah->ah_radio == AR5K_RF2413) {
rf_ini = rfregs_2413;
rf_size = ARRAY_SIZE(rfregs_2413);
if (mode < 2) {
ATH5K_ERR(ah->ah_sc,
"invalid channel mode: %i\n", mode);
return -EINVAL;
}
mode = mode - 2;
} else {
return -EINVAL;
}
/* Copy values to modify them */
for (i = 0; i < rf_size; i++) {
@ -1286,6 +1435,10 @@ int ath5k_hw_rfregs(struct ath5k_hw *ah, struct ieee80211_channel *channel,
ah->ah_rf_banks_size = sizeof(rfregs_5413);
func = ath5k_hw_rf5413_rfregs;
break;
case AR5K_RF2413:
ah->ah_rf_banks_size = sizeof(rfregs_2413);
func = ath5k_hw_rf5413_rfregs;
break;
default:
return -EINVAL;
}
@ -1324,6 +1477,11 @@ int ath5k_hw_rfgain(struct ath5k_hw *ah, unsigned int freq)
ath5k_rfg = rfgain_5413;
size = ARRAY_SIZE(rfgain_5413);
break;
case AR5K_RF2413:
ath5k_rfg = rfgain_2413;
size = ARRAY_SIZE(rfgain_2413);
freq = 0; /* only 2Ghz */
break;
default:
return -EINVAL;
}
@ -1398,7 +1556,6 @@ int ath5k_hw_set_rfgain_opt(struct ath5k_hw *ah)
ah->ah_gain.g_active = 1;
break;
case AR5K_RF5112:
case AR5K_RF5413: /* ??? */
ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default;
ah->ah_gain.g_step =
&rfgain_opt_5112.go_step[ah->ah_gain.g_step_idx];
@ -2019,6 +2176,15 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
return -EINVAL;
}
/*
* RF2413 for some reason can't
* transmit anything if we call
* this funtion, so we skip it
* until we fix txpower.
*/
if (ah->ah_radio == AR5K_RF2413)
return 0;
/* Reset TX power values */
memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
ah->ah_txpower.txp_tpc = tpc;

View file

@ -1923,7 +1923,9 @@ after DFS is enabled */
#define AR5K_PHY_SDELAY_32MHZ 0x000000ff
#define AR5K_PHY_SPENDING 0x99f8
#define AR5K_PHY_SPENDING_RF5111 0x00000018
#define AR5K_PHY_SPENDING_RF5112 0x00000014
#define AR5K_PHY_SPENDING_RF5112 0x00000014 /* <- i 've only seen this on 2425 dumps ! */
#define AR5K_PHY_SPENDING_RF5112A 0x0000000e /* but since i only have 5112A-based chips */
#define AR5K_PHY_SPENDING_RF5424 0x00000012 /* to test it might be also for old 5112. */
/*
* Misc PHY/radio registers [5110 - 5111]

View file

@ -99,6 +99,8 @@
#define B43_MMIO_TSF_2 0x636 /* core rev < 3 only */
#define B43_MMIO_TSF_3 0x638 /* core rev < 3 only */
#define B43_MMIO_RNG 0x65A
#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */
#define B43_MMIO_IFSCTL_USE_EDCF 0x0004
#define B43_MMIO_POWERUP_DELAY 0x6A8
/* SPROM boardflags_lo values */
@ -587,15 +589,13 @@ struct b43_phy {
/* Data structures for DMA transmission, per 80211 core. */
struct b43_dma {
struct b43_dmaring *tx_ring0;
struct b43_dmaring *tx_ring1;
struct b43_dmaring *tx_ring2;
struct b43_dmaring *tx_ring3;
struct b43_dmaring *tx_ring4;
struct b43_dmaring *tx_ring5;
struct b43_dmaring *tx_ring_AC_BK; /* Background */
struct b43_dmaring *tx_ring_AC_BE; /* Best Effort */
struct b43_dmaring *tx_ring_AC_VI; /* Video */
struct b43_dmaring *tx_ring_AC_VO; /* Voice */
struct b43_dmaring *tx_ring_mcast; /* Multicast */
struct b43_dmaring *rx_ring0;
struct b43_dmaring *rx_ring3; /* only available on core.rev < 5 */
struct b43_dmaring *rx_ring;
};
/* Context information for a noise calculation (Link Quality). */
@ -621,6 +621,35 @@ struct b43_key {
u8 algorithm;
};
/* SHM offsets to the QOS data structures for the 4 different queues. */
#define B43_QOS_PARAMS(queue) (B43_SHM_SH_EDCFQ + \
(B43_NR_QOSPARAMS * sizeof(u16) * (queue)))
#define B43_QOS_BACKGROUND B43_QOS_PARAMS(0)
#define B43_QOS_BESTEFFORT B43_QOS_PARAMS(1)
#define B43_QOS_VIDEO B43_QOS_PARAMS(2)
#define B43_QOS_VOICE B43_QOS_PARAMS(3)
/* QOS parameter hardware data structure offsets. */
#define B43_NR_QOSPARAMS 22
enum {
B43_QOSPARAM_TXOP = 0,
B43_QOSPARAM_CWMIN,
B43_QOSPARAM_CWMAX,
B43_QOSPARAM_CWCUR,
B43_QOSPARAM_AIFS,
B43_QOSPARAM_BSLOTS,
B43_QOSPARAM_REGGAP,
B43_QOSPARAM_STATUS,
};
/* QOS parameters for a queue. */
struct b43_qos_params {
/* The QOS parameters */
struct ieee80211_tx_queue_params p;
/* Does this need to get uploaded to hardware? */
bool need_hw_update;
};
struct b43_wldev;
/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */
@ -673,6 +702,12 @@ struct b43_wl {
struct sk_buff *current_beacon;
bool beacon0_uploaded;
bool beacon1_uploaded;
/* The current QOS parameters for the 4 queues.
* This is protected by the irq_lock. */
struct b43_qos_params qos_params[4];
/* Workqueue for updating QOS parameters in hardware. */
struct work_struct qos_update_work;
};
/* In-memory representation of a cached microcode file. */

View file

@ -38,6 +38,7 @@
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <asm/div64.h>
/* 32bit DMA ops. */
@ -291,52 +292,6 @@ static inline int request_slot(struct b43_dmaring *ring)
return slot;
}
/* Mac80211-queue to b43-ring mapping */
static struct b43_dmaring *priority_to_txring(struct b43_wldev *dev,
int queue_priority)
{
struct b43_dmaring *ring;
/*FIXME: For now we always run on TX-ring-1 */
return dev->dma.tx_ring1;
/* 0 = highest priority */
switch (queue_priority) {
default:
B43_WARN_ON(1);
/* fallthrough */
case 0:
ring = dev->dma.tx_ring3;
break;
case 1:
ring = dev->dma.tx_ring2;
break;
case 2:
ring = dev->dma.tx_ring1;
break;
case 3:
ring = dev->dma.tx_ring0;
break;
}
return ring;
}
/* b43-ring to mac80211-queue mapping */
static inline int txring_to_priority(struct b43_dmaring *ring)
{
static const u8 idx_to_prio[] = { 3, 2, 1, 0, };
unsigned int index;
/*FIXME: have only one queue, for now */
return 0;
index = ring->index;
if (B43_WARN_ON(index >= ARRAY_SIZE(idx_to_prio)))
index = 0;
return idx_to_prio[index];
}
static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx)
{
static const u16 map64[] = {
@ -924,16 +879,52 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
goto out;
}
#define divide(a, b) ({ \
typeof(a) __a = a; \
do_div(__a, b); \
__a; \
})
#define modulo(a, b) ({ \
typeof(a) __a = a; \
do_div(__a, b); \
})
/* Main cleanup function. */
static void b43_destroy_dmaring(struct b43_dmaring *ring)
static void b43_destroy_dmaring(struct b43_dmaring *ring,
const char *ringname)
{
if (!ring)
return;
b43dbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots: %d/%d\n",
(unsigned int)(ring->type),
ring->mmio_base,
(ring->tx) ? "TX" : "RX", ring->max_used_slots, ring->nr_slots);
#ifdef CONFIG_B43_DEBUG
{
/* Print some statistics. */
u64 failed_packets = ring->nr_failed_tx_packets;
u64 succeed_packets = ring->nr_succeed_tx_packets;
u64 nr_packets = failed_packets + succeed_packets;
u64 permille_failed = 0, average_tries = 0;
if (nr_packets)
permille_failed = divide(failed_packets * 1000, nr_packets);
if (nr_packets)
average_tries = divide(ring->nr_total_packet_tries * 100, nr_packets);
b43dbg(ring->dev->wl, "DMA-%u %s: "
"Used slots %d/%d, Failed frames %llu/%llu = %llu.%01llu%%, "
"Average tries %llu.%02llu\n",
(unsigned int)(ring->type), ringname,
ring->max_used_slots,
ring->nr_slots,
(unsigned long long)failed_packets,
(unsigned long long)nr_packets,
(unsigned long long)divide(permille_failed, 10),
(unsigned long long)modulo(permille_failed, 10),
(unsigned long long)divide(average_tries, 100),
(unsigned long long)modulo(average_tries, 100));
}
#endif /* DEBUG */
/* Device IRQs are disabled prior entering this function,
* so no need to take care of concurrency with rx handler stuff.
*/
@ -946,33 +937,26 @@ static void b43_destroy_dmaring(struct b43_dmaring *ring)
kfree(ring);
}
#define destroy_ring(dma, ring) do { \
b43_destroy_dmaring((dma)->ring, __stringify(ring)); \
(dma)->ring = NULL; \
} while (0)
void b43_dma_free(struct b43_wldev *dev)
{
struct b43_dma *dma = &dev->dma;
b43_destroy_dmaring(dma->rx_ring3);
dma->rx_ring3 = NULL;
b43_destroy_dmaring(dma->rx_ring0);
dma->rx_ring0 = NULL;
b43_destroy_dmaring(dma->tx_ring5);
dma->tx_ring5 = NULL;
b43_destroy_dmaring(dma->tx_ring4);
dma->tx_ring4 = NULL;
b43_destroy_dmaring(dma->tx_ring3);
dma->tx_ring3 = NULL;
b43_destroy_dmaring(dma->tx_ring2);
dma->tx_ring2 = NULL;
b43_destroy_dmaring(dma->tx_ring1);
dma->tx_ring1 = NULL;
b43_destroy_dmaring(dma->tx_ring0);
dma->tx_ring0 = NULL;
destroy_ring(dma, rx_ring);
destroy_ring(dma, tx_ring_AC_BK);
destroy_ring(dma, tx_ring_AC_BE);
destroy_ring(dma, tx_ring_AC_VI);
destroy_ring(dma, tx_ring_AC_VO);
destroy_ring(dma, tx_ring_mcast);
}
int b43_dma_init(struct b43_wldev *dev)
{
struct b43_dma *dma = &dev->dma;
struct b43_dmaring *ring;
int err;
u64 dmamask;
enum b43_dmatype type;
@ -1002,83 +986,57 @@ int b43_dma_init(struct b43_wldev *dev)
err = -ENOMEM;
/* setup TX DMA channels. */
ring = b43_setup_dmaring(dev, 0, 1, type);
if (!ring)
dma->tx_ring_AC_BK = b43_setup_dmaring(dev, 0, 1, type);
if (!dma->tx_ring_AC_BK)
goto out;
dma->tx_ring0 = ring;
ring = b43_setup_dmaring(dev, 1, 1, type);
if (!ring)
goto err_destroy_tx0;
dma->tx_ring1 = ring;
dma->tx_ring_AC_BE = b43_setup_dmaring(dev, 1, 1, type);
if (!dma->tx_ring_AC_BE)
goto err_destroy_bk;
ring = b43_setup_dmaring(dev, 2, 1, type);
if (!ring)
goto err_destroy_tx1;
dma->tx_ring2 = ring;
dma->tx_ring_AC_VI = b43_setup_dmaring(dev, 2, 1, type);
if (!dma->tx_ring_AC_VI)
goto err_destroy_be;
ring = b43_setup_dmaring(dev, 3, 1, type);
if (!ring)
goto err_destroy_tx2;
dma->tx_ring3 = ring;
dma->tx_ring_AC_VO = b43_setup_dmaring(dev, 3, 1, type);
if (!dma->tx_ring_AC_VO)
goto err_destroy_vi;
ring = b43_setup_dmaring(dev, 4, 1, type);
if (!ring)
goto err_destroy_tx3;
dma->tx_ring4 = ring;
dma->tx_ring_mcast = b43_setup_dmaring(dev, 4, 1, type);
if (!dma->tx_ring_mcast)
goto err_destroy_vo;
ring = b43_setup_dmaring(dev, 5, 1, type);
if (!ring)
goto err_destroy_tx4;
dma->tx_ring5 = ring;
/* setup RX DMA channel. */
dma->rx_ring = b43_setup_dmaring(dev, 0, 0, type);
if (!dma->rx_ring)
goto err_destroy_mcast;
/* setup RX DMA channels. */
ring = b43_setup_dmaring(dev, 0, 0, type);
if (!ring)
goto err_destroy_tx5;
dma->rx_ring0 = ring;
if (dev->dev->id.revision < 5) {
ring = b43_setup_dmaring(dev, 3, 0, type);
if (!ring)
goto err_destroy_rx0;
dma->rx_ring3 = ring;
}
/* No support for the TX status DMA ring. */
B43_WARN_ON(dev->dev->id.revision < 5);
b43dbg(dev->wl, "%u-bit DMA initialized\n",
(unsigned int)type);
err = 0;
out:
out:
return err;
err_destroy_rx0:
b43_destroy_dmaring(dma->rx_ring0);
dma->rx_ring0 = NULL;
err_destroy_tx5:
b43_destroy_dmaring(dma->tx_ring5);
dma->tx_ring5 = NULL;
err_destroy_tx4:
b43_destroy_dmaring(dma->tx_ring4);
dma->tx_ring4 = NULL;
err_destroy_tx3:
b43_destroy_dmaring(dma->tx_ring3);
dma->tx_ring3 = NULL;
err_destroy_tx2:
b43_destroy_dmaring(dma->tx_ring2);
dma->tx_ring2 = NULL;
err_destroy_tx1:
b43_destroy_dmaring(dma->tx_ring1);
dma->tx_ring1 = NULL;
err_destroy_tx0:
b43_destroy_dmaring(dma->tx_ring0);
dma->tx_ring0 = NULL;
goto out;
err_destroy_mcast:
destroy_ring(dma, tx_ring_mcast);
err_destroy_vo:
destroy_ring(dma, tx_ring_AC_VO);
err_destroy_vi:
destroy_ring(dma, tx_ring_AC_VI);
err_destroy_be:
destroy_ring(dma, tx_ring_AC_BE);
err_destroy_bk:
destroy_ring(dma, tx_ring_AC_BK);
return err;
}
/* Generate a cookie for the TX header. */
static u16 generate_cookie(struct b43_dmaring *ring, int slot)
{
u16 cookie = 0x1000;
u16 cookie;
/* Use the upper 4 bits of the cookie as
* DMA controller ID and store the slot number
@ -1088,30 +1046,9 @@ static u16 generate_cookie(struct b43_dmaring *ring, int slot)
* It can also not be 0xFFFF because that is special
* for multicast frames.
*/
switch (ring->index) {
case 0:
cookie = 0x1000;
break;
case 1:
cookie = 0x2000;
break;
case 2:
cookie = 0x3000;
break;
case 3:
cookie = 0x4000;
break;
case 4:
cookie = 0x5000;
break;
case 5:
cookie = 0x6000;
break;
default:
B43_WARN_ON(1);
}
cookie = (((u16)ring->index + 1) << 12);
B43_WARN_ON(slot & ~0x0FFF);
cookie |= (u16) slot;
cookie |= (u16)slot;
return cookie;
}
@ -1125,22 +1062,19 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot)
switch (cookie & 0xF000) {
case 0x1000:
ring = dma->tx_ring0;
ring = dma->tx_ring_AC_BK;
break;
case 0x2000:
ring = dma->tx_ring1;
ring = dma->tx_ring_AC_BE;
break;
case 0x3000:
ring = dma->tx_ring2;
ring = dma->tx_ring_AC_VI;
break;
case 0x4000:
ring = dma->tx_ring3;
ring = dma->tx_ring_AC_VO;
break;
case 0x5000:
ring = dma->tx_ring4;
break;
case 0x6000:
ring = dma->tx_ring5;
ring = dma->tx_ring_mcast;
break;
default:
B43_WARN_ON(1);
@ -1272,6 +1206,37 @@ static inline int should_inject_overflow(struct b43_dmaring *ring)
return 0;
}
/* Static mapping of mac80211's queues (priorities) to b43 DMA rings. */
static struct b43_dmaring * select_ring_by_priority(struct b43_wldev *dev,
u8 queue_prio)
{
struct b43_dmaring *ring;
if (b43_modparam_qos) {
/* 0 = highest priority */
switch (queue_prio) {
default:
B43_WARN_ON(1);
/* fallthrough */
case 0:
ring = dev->dma.tx_ring_AC_VO;
break;
case 1:
ring = dev->dma.tx_ring_AC_VI;
break;
case 2:
ring = dev->dma.tx_ring_AC_BE;
break;
case 3:
ring = dev->dma.tx_ring_AC_BK;
break;
}
} else
ring = dev->dma.tx_ring_AC_BE;
return ring;
}
int b43_dma_tx(struct b43_wldev *dev,
struct sk_buff *skb, struct ieee80211_tx_control *ctl)
{
@ -1288,13 +1253,13 @@ int b43_dma_tx(struct b43_wldev *dev,
hdr = (struct ieee80211_hdr *)skb->data;
if (ctl->flags & IEEE80211_TXCTL_SEND_AFTER_DTIM) {
/* The multicast ring will be sent after the DTIM */
ring = dev->dma.tx_ring4;
ring = dev->dma.tx_ring_mcast;
/* Set the more-data bit. Ucode will clear it on
* the last frame for us. */
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
} else {
/* Decide by priority where to put this frame. */
ring = priority_to_txring(dev, ctl->queue);
ring = select_ring_by_priority(dev, ctl->queue);
}
spin_lock_irqsave(&ring->lock, flags);
@ -1309,6 +1274,11 @@ int b43_dma_tx(struct b43_wldev *dev,
* That would be a mac80211 bug. */
B43_WARN_ON(ring->stopped);
/* Assign the queue number to the ring (if not already done before)
* so TX status handling can use it. The queue to ring mapping is
* static, so we don't need to store it per frame. */
ring->queue_prio = ctl->queue;
err = dma_tx_fragment(ring, skb, ctl);
if (unlikely(err == -ENOKEY)) {
/* Drop this packet, as we don't have the encryption key
@ -1325,7 +1295,7 @@ int b43_dma_tx(struct b43_wldev *dev,
if ((free_slots(ring) < SLOTS_PER_PACKET) ||
should_inject_overflow(ring)) {
/* This TX ring is full. */
ieee80211_stop_queue(dev->wl->hw, txring_to_priority(ring));
ieee80211_stop_queue(dev->wl->hw, ctl->queue);
ring->stopped = 1;
if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
@ -1337,6 +1307,38 @@ out_unlock:
return err;
}
static void b43_fill_txstatus_report(struct b43_dmaring *ring,
struct ieee80211_tx_status *report,
const struct b43_txstatus *status)
{
bool frame_failed = 0;
if (status->acked) {
/* The frame was ACKed. */
report->flags |= IEEE80211_TX_STATUS_ACK;
} else {
/* The frame was not ACKed... */
if (!(report->control.flags & IEEE80211_TXCTL_NO_ACK)) {
/* ...but we expected an ACK. */
frame_failed = 1;
report->excessive_retries = 1;
}
}
if (status->frame_count == 0) {
/* The frame was not transmitted at all. */
report->retry_count = 0;
} else {
report->retry_count = status->frame_count - 1;
#ifdef CONFIG_B43_DEBUG
if (frame_failed)
ring->nr_failed_tx_packets++;
else
ring->nr_succeed_tx_packets++;
ring->nr_total_packet_tries += status->frame_count;
#endif /* DEBUG */
}
}
void b43_dma_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status)
{
@ -1371,18 +1373,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
* status of the transmission.
* Some fields of txstat are already filled in dma_tx().
*/
if (status->acked) {
meta->txstat.flags |= IEEE80211_TX_STATUS_ACK;
} else {
if (!(meta->txstat.control.flags
& IEEE80211_TXCTL_NO_ACK))
meta->txstat.excessive_retries = 1;
}
if (status->frame_count == 0) {
/* The frame was not transmitted at all. */
meta->txstat.retry_count = 0;
} else
meta->txstat.retry_count = status->frame_count - 1;
b43_fill_txstatus_report(ring, &(meta->txstat), status);
ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb,
&(meta->txstat));
/* skb is freed by ieee80211_tx_status_irqsafe() */
@ -1404,7 +1395,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
dev->stats.last_tx = jiffies;
if (ring->stopped) {
B43_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET);
ieee80211_wake_queue(dev->wl->hw, txring_to_priority(ring));
ieee80211_wake_queue(dev->wl->hw, ring->queue_prio);
ring->stopped = 0;
if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index);
@ -1425,7 +1416,7 @@ void b43_dma_get_tx_stats(struct b43_wldev *dev,
for (i = 0; i < nr_queues; i++) {
data = &(stats->data[i]);
ring = priority_to_txring(dev, i);
ring = select_ring_by_priority(dev, i);
spin_lock_irqsave(&ring->lock, flags);
data->len = ring->used_slots / SLOTS_PER_PACKET;
@ -1451,25 +1442,6 @@ static void dma_rx(struct b43_dmaring *ring, int *slot)
sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);
skb = meta->skb;
if (ring->index == 3) {
/* We received an xmit status. */
struct b43_hwtxstatus *hw = (struct b43_hwtxstatus *)skb->data;
int i = 0;
while (hw->cookie == 0) {
if (i > 100)
break;
i++;
udelay(2);
barrier();
}
b43_handle_hwtxstatus(ring->dev, hw);
/* recycle the descriptor buffer. */
sync_descbuffer_for_device(ring, meta->dmaaddr,
ring->rx_buffersize);
return;
}
rxhdr = (struct b43_rxhdr_fw4 *)skb->data;
len = le16_to_cpu(rxhdr->frame_len);
if (len == 0) {
@ -1526,7 +1498,7 @@ static void dma_rx(struct b43_dmaring *ring, int *slot)
skb_pull(skb, ring->frameoffset);
b43_rx(ring->dev, skb, rxhdr);
drop:
drop:
return;
}
@ -1572,21 +1544,19 @@ static void b43_dma_tx_resume_ring(struct b43_dmaring *ring)
void b43_dma_tx_suspend(struct b43_wldev *dev)
{
b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
b43_dma_tx_suspend_ring(dev->dma.tx_ring0);
b43_dma_tx_suspend_ring(dev->dma.tx_ring1);
b43_dma_tx_suspend_ring(dev->dma.tx_ring2);
b43_dma_tx_suspend_ring(dev->dma.tx_ring3);
b43_dma_tx_suspend_ring(dev->dma.tx_ring4);
b43_dma_tx_suspend_ring(dev->dma.tx_ring5);
b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BK);
b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BE);
b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VI);
b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VO);
b43_dma_tx_suspend_ring(dev->dma.tx_ring_mcast);
}
void b43_dma_tx_resume(struct b43_wldev *dev)
{
b43_dma_tx_resume_ring(dev->dma.tx_ring5);
b43_dma_tx_resume_ring(dev->dma.tx_ring4);
b43_dma_tx_resume_ring(dev->dma.tx_ring3);
b43_dma_tx_resume_ring(dev->dma.tx_ring2);
b43_dma_tx_resume_ring(dev->dma.tx_ring1);
b43_dma_tx_resume_ring(dev->dma.tx_ring0);
b43_dma_tx_resume_ring(dev->dma.tx_ring_mcast);
b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VO);
b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VI);
b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BE);
b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK);
b43_power_saving_ctl_bits(dev, 0);
}

View file

@ -245,6 +245,9 @@ struct b43_dmaring {
enum b43_dmatype type;
/* Boolean. Is this ring stopped at ieee80211 level? */
bool stopped;
/* The QOS priority assigned to this ring. Only used for TX rings.
* This is the mac80211 "queue" value. */
u8 queue_prio;
/* Lock, only used for TX. */
spinlock_t lock;
struct b43_wldev *dev;
@ -253,7 +256,13 @@ struct b43_dmaring {
int max_used_slots;
/* Last time we injected a ring overflow. */
unsigned long last_injected_overflow;
#endif /* CONFIG_B43_DEBUG */
/* Statistics: Number of successfully transmitted packets */
u64 nr_succeed_tx_packets;
/* Statistics: Number of failed TX packets */
u64 nr_failed_tx_packets;
/* Statistics: Total number of TX plus all retries. */
u64 nr_total_packet_tries;
#endif /* CONFIG_B43_DEBUG */
};
static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset)

View file

@ -78,6 +78,11 @@ static int modparam_nohwcrypt;
module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
int b43_modparam_qos = 1;
module_param_named(qos, b43_modparam_qos, int, 0444);
MODULE_PARM_DESC(qos, "Enable QOS support (default on)");
static const struct ssb_device_id b43_ssb_tbl[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5),
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6),
@ -1589,11 +1594,10 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev)
/* Check the DMA reason registers for received data. */
if (dma_reason[0] & B43_DMAIRQ_RX_DONE)
b43_dma_rx(dev->dma.rx_ring0);
if (dma_reason[3] & B43_DMAIRQ_RX_DONE)
b43_dma_rx(dev->dma.rx_ring3);
b43_dma_rx(dev->dma.rx_ring);
B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[3] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[4] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[5] & B43_DMAIRQ_RX_DONE);
@ -2708,10 +2712,178 @@ out:
return NETDEV_TX_OK;
}
/* Locking: wl->irq_lock */
static void b43_qos_params_upload(struct b43_wldev *dev,
const struct ieee80211_tx_queue_params *p,
u16 shm_offset)
{
u16 params[B43_NR_QOSPARAMS];
int cw_min, cw_max, aifs, bslots, tmp;
unsigned int i;
const u16 aCWmin = 0x0001;
const u16 aCWmax = 0x03FF;
/* Calculate the default values for the parameters, if needed. */
switch (shm_offset) {
case B43_QOS_VOICE:
aifs = (p->aifs == -1) ? 2 : p->aifs;
cw_min = (p->cw_min == 0) ? ((aCWmin + 1) / 4 - 1) : p->cw_min;
cw_max = (p->cw_max == 0) ? ((aCWmin + 1) / 2 - 1) : p->cw_max;
break;
case B43_QOS_VIDEO:
aifs = (p->aifs == -1) ? 2 : p->aifs;
cw_min = (p->cw_min == 0) ? ((aCWmin + 1) / 2 - 1) : p->cw_min;
cw_max = (p->cw_max == 0) ? aCWmin : p->cw_max;
break;
case B43_QOS_BESTEFFORT:
aifs = (p->aifs == -1) ? 3 : p->aifs;
cw_min = (p->cw_min == 0) ? aCWmin : p->cw_min;
cw_max = (p->cw_max == 0) ? aCWmax : p->cw_max;
break;
case B43_QOS_BACKGROUND:
aifs = (p->aifs == -1) ? 7 : p->aifs;
cw_min = (p->cw_min == 0) ? aCWmin : p->cw_min;
cw_max = (p->cw_max == 0) ? aCWmax : p->cw_max;
break;
default:
B43_WARN_ON(1);
return;
}
if (cw_min <= 0)
cw_min = aCWmin;
if (cw_max <= 0)
cw_max = aCWmin;
bslots = b43_read16(dev, B43_MMIO_RNG) % cw_min;
memset(&params, 0, sizeof(params));
params[B43_QOSPARAM_TXOP] = p->txop * 32;
params[B43_QOSPARAM_CWMIN] = cw_min;
params[B43_QOSPARAM_CWMAX] = cw_max;
params[B43_QOSPARAM_CWCUR] = cw_min;
params[B43_QOSPARAM_AIFS] = aifs;
params[B43_QOSPARAM_BSLOTS] = bslots;
params[B43_QOSPARAM_REGGAP] = bslots + aifs;
for (i = 0; i < ARRAY_SIZE(params); i++) {
if (i == B43_QOSPARAM_STATUS) {
tmp = b43_shm_read16(dev, B43_SHM_SHARED,
shm_offset + (i * 2));
/* Mark the parameters as updated. */
tmp |= 0x100;
b43_shm_write16(dev, B43_SHM_SHARED,
shm_offset + (i * 2),
tmp);
} else {
b43_shm_write16(dev, B43_SHM_SHARED,
shm_offset + (i * 2),
params[i]);
}
}
}
/* Update the QOS parameters in hardware. */
static void b43_qos_update(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
struct b43_qos_params *params;
unsigned long flags;
unsigned int i;
/* Mapping of mac80211 queues to b43 SHM offsets. */
static const u16 qos_shm_offsets[] = {
[0] = B43_QOS_VOICE,
[1] = B43_QOS_VIDEO,
[2] = B43_QOS_BESTEFFORT,
[3] = B43_QOS_BACKGROUND,
};
BUILD_BUG_ON(ARRAY_SIZE(qos_shm_offsets) != ARRAY_SIZE(wl->qos_params));
b43_mac_suspend(dev);
spin_lock_irqsave(&wl->irq_lock, flags);
for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
params = &(wl->qos_params[i]);
if (params->need_hw_update) {
b43_qos_params_upload(dev, &(params->p),
qos_shm_offsets[i]);
params->need_hw_update = 0;
}
}
spin_unlock_irqrestore(&wl->irq_lock, flags);
b43_mac_enable(dev);
}
static void b43_qos_clear(struct b43_wl *wl)
{
struct b43_qos_params *params;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
params = &(wl->qos_params[i]);
memset(&(params->p), 0, sizeof(params->p));
params->p.aifs = -1;
params->need_hw_update = 1;
}
}
/* Initialize the core's QOS capabilities */
static void b43_qos_init(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
unsigned int i;
/* Upload the current QOS parameters. */
for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++)
wl->qos_params[i].need_hw_update = 1;
b43_qos_update(dev);
/* Enable QOS support. */
b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
b43_write16(dev, B43_MMIO_IFSCTL,
b43_read16(dev, B43_MMIO_IFSCTL)
| B43_MMIO_IFSCTL_USE_EDCF);
}
static void b43_qos_update_work(struct work_struct *work)
{
struct b43_wl *wl = container_of(work, struct b43_wl, qos_update_work);
struct b43_wldev *dev;
mutex_lock(&wl->mutex);
dev = wl->current_dev;
if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED)))
b43_qos_update(dev);
mutex_unlock(&wl->mutex);
}
static int b43_op_conf_tx(struct ieee80211_hw *hw,
int queue,
int _queue,
const struct ieee80211_tx_queue_params *params)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
unsigned long flags;
unsigned int queue = (unsigned int)_queue;
struct b43_qos_params *p;
if (queue >= ARRAY_SIZE(wl->qos_params)) {
/* Queue not available or don't support setting
* params on this queue. Return success to not
* confuse mac80211. */
return 0;
}
spin_lock_irqsave(&wl->irq_lock, flags);
p = &(wl->qos_params[queue]);
memcpy(&(p->p), params, sizeof(p->p));
p->need_hw_update = 1;
spin_unlock_irqrestore(&wl->irq_lock, flags);
queue_work(hw->workqueue, &wl->qos_update_work);
return 0;
}
@ -3732,6 +3904,7 @@ static int b43_op_start(struct ieee80211_hw *hw)
memset(wl->mac_addr, 0, ETH_ALEN);
wl->filter_flags = 0;
wl->radiotap_enabled = 0;
b43_qos_clear(wl);
/* First register RFkill.
* LEDs that are registered later depend on it. */
@ -3773,6 +3946,7 @@ static void b43_op_stop(struct ieee80211_hw *hw)
struct b43_wldev *dev = wl->current_dev;
b43_rfkill_exit(dev);
cancel_work_sync(&(wl->qos_update_work));
mutex_lock(&wl->mutex);
if (b43_status(dev) >= B43_STAT_STARTED)
@ -3835,6 +4009,16 @@ static int b43_op_ibss_beacon_update(struct ieee80211_hw *hw,
return 0;
}
static void b43_op_sta_notify(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum sta_notify_cmd notify_cmd,
const u8 *addr)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
B43_WARN_ON(!vif || wl->vif != vif);
}
static const struct ieee80211_ops b43_hw_ops = {
.tx = b43_op_tx,
.conf_tx = b43_op_conf_tx,
@ -3851,6 +4035,7 @@ static const struct ieee80211_ops b43_hw_ops = {
.set_retry_limit = b43_op_set_retry_limit,
.set_tim = b43_op_beacon_set_tim,
.beacon_update = b43_op_ibss_beacon_update,
.sta_notify = b43_op_sta_notify,
};
/* Hard-reset the chip. Do not call this directly.
@ -4122,7 +4307,7 @@ static int b43_wireless_init(struct ssb_device *dev)
hw->max_signal = 100;
hw->max_rssi = -110;
hw->max_noise = -110;
hw->queues = 1; /* FIXME: hardware has more queues */
hw->queues = b43_modparam_qos ? 4 : 1;
SET_IEEE80211_DEV(hw, dev->dev);
if (is_valid_ether_addr(sprom->et1mac))
SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac);
@ -4138,6 +4323,7 @@ static int b43_wireless_init(struct ssb_device *dev)
spin_lock_init(&wl->shm_lock);
mutex_init(&wl->mutex);
INIT_LIST_HEAD(&wl->devlist);
INIT_WORK(&wl->qos_update_work, b43_qos_update_work);
ssb_set_devtypedata(dev, wl);
b43info(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id);

View file

@ -38,6 +38,10 @@
/* Magic helper macro to pad structures. Ignore those above. It's magic. */
#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes))
extern int b43_modparam_qos;
/* Lightweight function to convert a frequency (in Mhz) to a channel number. */
static inline u8 b43_freq_to_channel_5ghz(int freq)
{

View file

@ -705,30 +705,3 @@ void b43_tx_resume(struct b43_wldev *dev)
{
b43_dma_tx_resume(dev);
}
#if 0
static void upload_qos_parms(struct b43_wldev *dev,
const u16 * parms, u16 offset)
{
int i;
for (i = 0; i < B43_NR_QOSPARMS; i++) {
b43_shm_write16(dev, B43_SHM_SHARED,
offset + (i * 2), parms[i]);
}
}
#endif
/* Initialize the QoS parameters */
void b43_qos_init(struct b43_wldev *dev)
{
/* FIXME: This function must probably be called from the mac80211
* config callback. */
return;
b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
//FIXME kill magic
b43_write16(dev, 0x688, b43_read16(dev, 0x688) | 0x4);
/*TODO: We might need some stack support here to get the values. */
}

View file

@ -302,18 +302,6 @@ void b43_handle_hwtxstatus(struct b43_wldev *dev,
void b43_tx_suspend(struct b43_wldev *dev);
void b43_tx_resume(struct b43_wldev *dev);
#define B43_NR_QOSPARMS 22
enum {
B43_QOSPARM_TXOP = 0,
B43_QOSPARM_CWMIN,
B43_QOSPARM_CWMAX,
B43_QOSPARM_CWCUR,
B43_QOSPARM_AIFS,
B43_QOSPARM_BSLOTS,
B43_QOSPARM_REGGAP,
B43_QOSPARM_STATUS,
};
void b43_qos_init(struct b43_wldev *dev);
/* Helper functions for converting the key-table index from "firmware-format"
* to "raw-format" and back. The firmware API changed for this at some revision.

View file

@ -1,7 +1,12 @@
config IWLCORE
tristate "Intel Wireless Wifi Core"
depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
config IWL4965
tristate "Intel Wireless WiFi 4965AGN"
depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
select FW_LOADER
select IWLCORE
---help---
Select to build the driver supporting the:

View file

@ -1,3 +1,6 @@
obj-$(CONFIG_IWLCORE) += iwlcore.o
iwlcore-objs = iwl-core.o
obj-$(CONFIG_IWL3945) += iwl3945.o
iwl3945-objs = iwl3945-base.o iwl-3945.o iwl-3945-rs.o

View file

@ -666,26 +666,26 @@ struct iwl3945_rx_frame_hdr {
u8 payload[0];
} __attribute__ ((packed));
#define RX_RES_STATUS_NO_CRC32_ERROR __constant_cpu_to_le32(1 << 0)
#define RX_RES_STATUS_NO_RXE_OVERFLOW __constant_cpu_to_le32(1 << 1)
#define RX_RES_STATUS_NO_CRC32_ERROR __constant_cpu_to_le32(1 << 0)
#define RX_RES_STATUS_NO_RXE_OVERFLOW __constant_cpu_to_le32(1 << 1)
#define RX_RES_PHY_FLAGS_BAND_24_MSK __constant_cpu_to_le16(1 << 0)
#define RX_RES_PHY_FLAGS_MOD_CCK_MSK __constant_cpu_to_le16(1 << 1)
#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK __constant_cpu_to_le16(1 << 2)
#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK __constant_cpu_to_le16(1 << 3)
#define RX_RES_PHY_FLAGS_ANTENNA_MSK __constant_cpu_to_le16(0xf0)
#define RX_RES_PHY_FLAGS_BAND_24_MSK __constant_cpu_to_le16(1 << 0)
#define RX_RES_PHY_FLAGS_MOD_CCK_MSK __constant_cpu_to_le16(1 << 1)
#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK __constant_cpu_to_le16(1 << 2)
#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK __constant_cpu_to_le16(1 << 3)
#define RX_RES_PHY_FLAGS_ANTENNA_MSK __constant_cpu_to_le16(0xf0)
#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8)
#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8)
#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8)
#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8)
#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8)
#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8)
#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11)
#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11)
#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11)
#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11)
#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11)
#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11)
#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11)
#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11)
#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11)
#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11)
struct iwl3945_rx_frame_end {
__le32 status;

View file

@ -0,0 +1,80 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
* Contact Information:
* Tomas Winkler <tomas.winkler@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __iwl_3945_dev_h__
#define __iwl_3945_dev_h__
#define IWL_PCI_DEVICE(dev, subdev, cfg) \
.vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = (subdev), \
.driver_data = (kernel_ulong_t)&(cfg)
#define IWL_SKU_G 0x1
#define IWL_SKU_A 0x2
struct iwl_3945_cfg {
const char *name;
const char *fw_name;
unsigned int sku;
};
#endif /* __iwl_dev_h__ */

View file

@ -40,6 +40,15 @@ do { if (iwl3945_debug_level & (level)) \
do { if ((iwl3945_debug_level & (level)) && net_ratelimit()) \
printk(KERN_ERR DRV_NAME": %c %s " fmt, \
in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
static inline void iwl3945_print_hex_dump(int level, void *p, u32 len)
{
if (!(iwl3945_debug_level & level))
return;
print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
p, len, 1);
}
#else
static inline void IWL_DEBUG(int level, const char *fmt, ...)
{
@ -47,7 +56,12 @@ static inline void IWL_DEBUG(int level, const char *fmt, ...)
static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
{
}
#endif /* CONFIG_IWL3945_DEBUG */
static inline void iwl3945_print_hex_dump(int level, void *p, u32 len)
{
}
#endif /* CONFIG_IWL3945_DEBUG */
/*
* To use the debug system;
@ -143,6 +157,7 @@ static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
IWL_DEBUG_LIMIT(IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
#define IWL_DEBUG_HT(f, a...) IWL_DEBUG(IWL_DL_HT, f, ## a)
#define IWL_DEBUG_STATS(f, a...) IWL_DEBUG(IWL_DL_STATS, f, ## a)
#define IWL_DEBUG_STATS_LIMIT(f, a...) IWL_DEBUG_LIMIT(IWL_DL_STATS, f, ## a)
#define IWL_DEBUG_TX_REPLY(f, a...) IWL_DEBUG(IWL_DL_TX_REPLY, f, ## a)
#define IWL_DEBUG_QOS(f, a...) IWL_DEBUG(IWL_DL_QOS, f, ## a)
#define IWL_DEBUG_RADIO(f, a...) IWL_DEBUG(IWL_DL_RADIO, f, ## a)

View file

@ -321,180 +321,6 @@ struct iwl3945_eeprom {
#define PCI_REG_WUM8 0x0E8
#define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT (0x80000000)
/*=== CSR (control and status registers) ===*/
#define CSR_BASE (0x000)
#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */
#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */
#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */
#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */
#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/
#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */
#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
#define CSR_GP_CNTRL (CSR_BASE+0x024)
/*
* Hardware revision info
* Bit fields:
* 31-8: Reserved
* 7-4: Type of device: 0x0 = 4965, 0xd = 3945
* 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D
* 1-0: "Dash" value, as in A-1, etc.
*/
#define CSR_HW_REV (CSR_BASE+0x028)
/* EEPROM reads */
#define CSR_EEPROM_REG (CSR_BASE+0x02c)
#define CSR_EEPROM_GP (CSR_BASE+0x030)
#define CSR_GP_UCODE (CSR_BASE+0x044)
#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054)
#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058)
#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
/* Analog phase-lock-loop configuration (3945 only)
* Set bit 24. */
#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c)
/* Bits for CSR_HW_IF_CONFIG_REG */
#define CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MB (0x00000100)
#define CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MM (0x00000200)
#define CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC (0x00000400)
#define CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE (0x00000800)
#define CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000)
#define CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000)
#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
* acknowledged (reset) by host writing "1" to flagged bits. */
#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */
#define CSR_INT_BIT_DNLD (1 << 28) /* uCode Download */
#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */
#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */
#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */
#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */
#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */
#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses, 3945 */
#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */
#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */
#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \
CSR_INT_BIT_HW_ERR | \
CSR_INT_BIT_FH_TX | \
CSR_INT_BIT_SW_ERR | \
CSR_INT_BIT_RF_KILL | \
CSR_INT_BIT_SW_RX | \
CSR_INT_BIT_WAKEUP | \
CSR_INT_BIT_ALIVE)
/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */
#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */
#define CSR_FH_INT_BIT_RX_CHNL2 (1 << 18) /* Rx channel 2 (3945 only) */
#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */
#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */
#define CSR_FH_INT_BIT_TX_CHNL6 (1 << 6) /* Tx channel 6 (3945 only) */
#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */
#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */
#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \
CSR_FH_INT_BIT_RX_CHNL2 | \
CSR_FH_INT_BIT_RX_CHNL1 | \
CSR_FH_INT_BIT_RX_CHNL0)
#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL6 | \
CSR_FH_INT_BIT_TX_CHNL1 | \
CSR_FH_INT_BIT_TX_CHNL0)
/* RESET */
#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001)
#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002)
#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080)
#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100)
#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200)
/* GP (general purpose) CONTROL */
#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001)
#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004)
#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008)
#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010)
#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001)
#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000)
#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000)
#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000)
/* EEPROM REG */
#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001)
#define CSR_EEPROM_REG_BIT_CMD (0x00000002)
/* EEPROM GP */
#define CSR_EEPROM_GP_VALID_MSK (0x00000006)
#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000)
#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180)
/* UCODE DRV GP */
#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001)
#define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008)
/* GPIO */
#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200)
#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000)
#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC CSR_GPIO_IN_BIT_AUX_POWER
/* GI Chicken Bits */
#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000)
#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000)
/* CSR_ANA_PLL_CFG */
#define CSR_ANA_PLL_CFG_SH (0x00880300)
/*=== HBUS (Host-side Bus) ===*/
#define HBUS_BASE (0x400)
/*
* Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
* structures, error log, event log, verifying uCode load).
* First write to address register, then read from or write to data register
* to complete the job. Once the address register is set up, accesses to
* data registers auto-increment the address by one dword.
* Bit usage for address registers (read or write):
* 0-31: memory address within device
*/
#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c)
#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010)
#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018)
#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c)
/*
* Registers for accessing device's internal peripheral registers
* (e.g. SCD, BSM, etc.). First write to address register,
* then read from or write to data register to complete the job.
* Bit usage for address registers (read or write):
* 0-15: register address (offset) within device
* 24-25: (# bytes - 1) to read or write (e.g. 3 for dword)
*/
#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044)
#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048)
#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c)
#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050)
/*
* Per-Tx-queue write pointer (index, really!) (3945 and 4965).
* Indicates index to next TFD that driver will fill (1 past latest filled).
* Bit usage:
* 0-7: queue write index
* 11-8: queue selector
*/
#define HBUS_TARG_WRPTR (HBUS_BASE+0x060)
/* SCD (3945 Tx Frame Scheduler) */
#define SCD_BASE (CSR_BASE + 0x2E00)

View file

@ -158,9 +158,9 @@ static void iwl3945_clear_window(struct iwl3945_rate_scale_data *window)
{
window->data = 0;
window->success_counter = 0;
window->success_ratio = IWL_INVALID_VALUE;
window->success_ratio = -1;
window->counter = 0;
window->average_tpt = IWL_INVALID_VALUE;
window->average_tpt = IWL_INV_TPT;
window->stamp = 0;
}
@ -459,22 +459,23 @@ static void rs_tx_status(void *priv_rate,
struct iwl3945_rs_sta *rs_sta;
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
IWL_DEBUG_RATE("enter\n");
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
retries = tx_resp->retry_count;
/* FIXME : this is wrong */
first_index = &sband->bitrates[0] - tx_resp->control.tx_rate;
first_index = tx_resp->control.tx_rate->hw_value;
if ((first_index < 0) || (first_index >= IWL_RATE_COUNT)) {
IWL_DEBUG_RATE("leave: Rate out of bounds: %d\n", first_index);
return;
}
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
rcu_read_unlock();
IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
return;
}
@ -547,7 +548,7 @@ static void rs_tx_status(void *priv_rate,
spin_unlock_irqrestore(&rs_sta->lock, flags);
sta_info_put(sta);
rcu_read_unlock();
IWL_DEBUG_RATE("leave\n");
@ -633,7 +634,7 @@ static u16 iwl3945_get_adjacent_rate(struct iwl3945_rs_sta *rs_sta,
*
*/
static void rs_get_rate(void *priv_rate, struct net_device *dev,
struct ieee80211_supported_band *band,
struct ieee80211_supported_band *sband,
struct sk_buff *skb,
struct rate_selection *sel)
{
@ -643,9 +644,9 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
int index;
struct iwl3945_rs_sta *rs_sta;
struct iwl3945_rate_scale_data *window = NULL;
int current_tpt = IWL_INVALID_VALUE;
int low_tpt = IWL_INVALID_VALUE;
int high_tpt = IWL_INVALID_VALUE;
int current_tpt = IWL_INV_TPT;
int low_tpt = IWL_INV_TPT;
int high_tpt = IWL_INV_TPT;
u32 fail_count;
s8 scale_action = 0;
unsigned long flags;
@ -658,6 +659,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
IWL_DEBUG_RATE("enter\n");
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
/* Send management frames and broadcast/multicast data using lowest
@ -667,16 +670,15 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
is_multicast_ether_addr(hdr->addr1) ||
!sta || !sta->rate_ctrl_priv) {
IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
sel->rate = rate_lowest(local, band, sta);
if (sta)
sta_info_put(sta);
sel->rate = rate_lowest(local, sband, sta);
rcu_read_unlock();
return;
}
rate_mask = sta->supp_rates[band->band];
rate_mask = sta->supp_rates[sband->band];
index = min(sta->last_txrate_idx & 0xffff, IWL_RATE_COUNT - 1);
if (priv->band == IEEE80211_BAND_5GHZ)
if (sband->band == IEEE80211_BAND_5GHZ)
rate_mask = rate_mask << IWL_FIRST_OFDM_RATE;
rs_sta = (void *)sta->rate_ctrl_priv;
@ -708,7 +710,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
if (((fail_count <= IWL_RATE_MIN_FAILURE_TH) &&
(window->success_counter < IWL_RATE_MIN_SUCCESS_TH))) {
window->average_tpt = IWL_INVALID_VALUE;
window->average_tpt = IWL_INV_TPT;
spin_unlock_irqrestore(&rs_sta->lock, flags);
IWL_DEBUG_RATE("Invalid average_tpt on rate %d: "
@ -727,7 +729,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
current_tpt = window->average_tpt;
high_low = iwl3945_get_adjacent_rate(rs_sta, index, rate_mask,
band->band);
sband->band);
low = high_low & 0xff;
high = (high_low >> 8) & 0xff;
@ -744,19 +746,16 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) {
IWL_DEBUG_RATE("decrease rate because of low success_ratio\n");
scale_action = -1;
} else if ((low_tpt == IWL_INVALID_VALUE) &&
(high_tpt == IWL_INVALID_VALUE))
} else if ((low_tpt == IWL_INV_TPT) && (high_tpt == IWL_INV_TPT))
scale_action = 1;
else if ((low_tpt != IWL_INVALID_VALUE) &&
(high_tpt != IWL_INVALID_VALUE)
&& (low_tpt < current_tpt)
&& (high_tpt < current_tpt)) {
else if ((low_tpt != IWL_INV_TPT) && (high_tpt != IWL_INV_TPT) &&
(low_tpt < current_tpt) && (high_tpt < current_tpt)) {
IWL_DEBUG_RATE("No action -- low [%d] & high [%d] < "
"current_tpt [%d]\n",
low_tpt, high_tpt, current_tpt);
scale_action = 0;
} else {
if (high_tpt != IWL_INVALID_VALUE) {
if (high_tpt != IWL_INV_TPT) {
if (high_tpt > current_tpt)
scale_action = 1;
else {
@ -764,7 +763,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
("decrease rate because of high tpt\n");
scale_action = -1;
}
} else if (low_tpt != IWL_INVALID_VALUE) {
} else if (low_tpt != IWL_INV_TPT) {
if (low_tpt > current_tpt) {
IWL_DEBUG_RATE
("decrease rate because of low tpt\n");
@ -806,16 +805,16 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
out:
sta->last_txrate_idx = index;
if (priv->band == IEEE80211_BAND_5GHZ)
if (sband->band == IEEE80211_BAND_5GHZ)
sta->txrate_idx = sta->last_txrate_idx - IWL_FIRST_OFDM_RATE;
else
sta->txrate_idx = sta->last_txrate_idx;
sta_info_put(sta);
rcu_read_unlock();
IWL_DEBUG_RATE("leave: %d\n", index);
sel->rate = &priv->ieee_rates[index];
sel->rate = &sband->bitrates[sta->txrate_idx];
}
static struct rate_control_ops rs_ops = {
@ -843,13 +842,15 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
unsigned long now = jiffies;
u32 max_time = 0;
rcu_read_lock();
sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta) {
sta_info_put(sta);
if (sta)
IWL_DEBUG_RATE("leave - no private rate data!\n");
} else
else
IWL_DEBUG_RATE("leave - no station!\n");
rcu_read_unlock();
return sprintf(buf, "station %d not found\n", sta_id);
}
@ -890,7 +891,7 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
i = j;
}
spin_unlock_irqrestore(&rs_sta->lock, flags);
sta_info_put(sta);
rcu_read_unlock();
/* Display the average rate of all samples taken.
*
@ -927,11 +928,12 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
return;
}
rcu_read_lock();
sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
IWL_DEBUG_RATE("leave - no private rate data!\n");
rcu_read_unlock();
return;
}
@ -958,7 +960,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
break;
}
sta_info_put(sta);
rcu_read_unlock();
spin_unlock_irqrestore(&rs_sta->lock, flags);
rssi = priv->last_rx_rssi;

View file

@ -36,8 +36,8 @@ struct iwl3945_rate_info {
u8 next_rs; /* next rate used in rs algo */
u8 prev_rs_tgg; /* previous rate used in TGG rs algo */
u8 next_rs_tgg; /* next rate used in TGG rs algo */
u8 table_rs_index; /* index in rate scale table cmd */
u8 prev_table_rs; /* prev in rate table cmd */
u8 table_rs_index; /* index in rate scale table cmd */
u8 prev_table_rs; /* prev in rate table cmd */
};
/*
@ -159,7 +159,7 @@ enum {
#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
#define IWL_INVALID_VALUE -1
#define IWL_INV_TPT -1
#define IWL_MIN_RSSI_VAL -100
#define IWL_MAX_RSSI_VAL 0

View file

@ -39,6 +39,7 @@
#include <asm/unaligned.h>
#include <net/mac80211.h>
#include "iwl-3945-core.h"
#include "iwl-3945.h"
#include "iwl-helpers.h"
#include "iwl-3945-rs.h"
@ -183,6 +184,16 @@ void iwl3945_disable_events(struct iwl3945_priv *priv)
}
static int iwl3945_hwrate_to_plcp_idx(u8 plcp)
{
int idx;
for (idx = 0; idx < IWL_RATE_COUNT; idx++)
if (iwl3945_rates[idx].plcp == plcp)
return idx;
return -1;
}
/**
* iwl3945_get_antenna_flags - Get antenna flags for RXON command
* @priv: eeprom and antenna fields are used to determine antenna flags
@ -216,14 +227,126 @@ __le32 iwl3945_get_antenna_flags(const struct iwl3945_priv *priv)
return 0; /* "diversity" is default if error */
}
#ifdef CONFIG_IWL3945_DEBUG
#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
static const char *iwl3945_get_tx_fail_reason(u32 status)
{
switch (status & TX_STATUS_MSK) {
case TX_STATUS_SUCCESS:
return "SUCCESS";
TX_STATUS_ENTRY(SHORT_LIMIT);
TX_STATUS_ENTRY(LONG_LIMIT);
TX_STATUS_ENTRY(FIFO_UNDERRUN);
TX_STATUS_ENTRY(MGMNT_ABORT);
TX_STATUS_ENTRY(NEXT_FRAG);
TX_STATUS_ENTRY(LIFE_EXPIRE);
TX_STATUS_ENTRY(DEST_PS);
TX_STATUS_ENTRY(ABORTED);
TX_STATUS_ENTRY(BT_RETRY);
TX_STATUS_ENTRY(STA_INVALID);
TX_STATUS_ENTRY(FRAG_DROPPED);
TX_STATUS_ENTRY(TID_DISABLE);
TX_STATUS_ENTRY(FRAME_FLUSHED);
TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL);
TX_STATUS_ENTRY(TX_LOCKED);
TX_STATUS_ENTRY(NO_BEACON_ON_RADAR);
}
return "UNKNOWN";
}
#else
static inline const char *iwl3945_get_tx_fail_reason(u32 status)
{
return "";
}
#endif
/**
* iwl3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd
*
* When FW advances 'R' index, all entries between old and new 'R' index
* need to be reclaimed. As result, some free space forms. If there is
* enough free space (> low mark), wake the stack that feeds us.
*/
static void iwl3945_tx_queue_reclaim(struct iwl3945_priv *priv,
int txq_id, int index)
{
struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
struct iwl3945_queue *q = &txq->q;
struct iwl3945_tx_info *tx_info;
BUG_ON(txq_id == IWL_CMD_QUEUE_NUM);
for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index;
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
tx_info = &txq->txb[txq->q.read_ptr];
ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb[0],
&tx_info->status);
tx_info->skb[0] = NULL;
iwl3945_hw_txq_free_tfd(priv, txq);
}
if (iwl3945_queue_space(q) > q->low_mark && (txq_id >= 0) &&
(txq_id != IWL_CMD_QUEUE_NUM) &&
priv->mac80211_registered)
ieee80211_wake_queue(priv->hw, txq_id);
}
/**
* iwl3945_rx_reply_tx - Handle Tx response
*/
static void iwl3945_rx_reply_tx(struct iwl3945_priv *priv,
struct iwl3945_rx_mem_buffer *rxb)
{
struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
int txq_id = SEQ_TO_QUEUE(sequence);
int index = SEQ_TO_INDEX(sequence);
struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
struct ieee80211_tx_status *tx_status;
struct iwl3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
u32 status = le32_to_cpu(tx_resp->status);
int rate_idx;
if ((index >= txq->q.n_bd) || (iwl3945_x2_queue_used(&txq->q, index) == 0)) {
IWL_ERROR("Read index for DMA queue txq_id (%d) index %d "
"is out of range [0-%d] %d %d\n", txq_id,
index, txq->q.n_bd, txq->q.write_ptr,
txq->q.read_ptr);
return;
}
tx_status = &(txq->txb[txq->q.read_ptr].status);
tx_status->retry_count = tx_resp->failure_frame;
/* tx_status->rts_retry_count = tx_resp->failure_rts; */
tx_status->flags = ((status & TX_STATUS_MSK) == TX_STATUS_SUCCESS) ?
IEEE80211_TX_STATUS_ACK : 0;
IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n",
txq_id, iwl3945_get_tx_fail_reason(status), status,
tx_resp->rate, tx_resp->failure_frame);
rate_idx = iwl3945_hwrate_to_plcp_idx(tx_resp->rate);
tx_status->control.tx_rate = &priv->ieee_rates[rate_idx];
IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
iwl3945_tx_queue_reclaim(priv, txq_id, index);
if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
IWL_ERROR("TODO: Implement Tx ABORT REQUIRED!!!\n");
}
/*****************************************************************************
*
* Intel PRO/Wireless 3945ABG/BG Network Connection
*
* RX handler implementations
*
* Used by iwl-base.c
*
*****************************************************************************/
void iwl3945_hw_rx_statistics(struct iwl3945_priv *priv, struct iwl3945_rx_mem_buffer *rxb)
@ -238,6 +361,156 @@ void iwl3945_hw_rx_statistics(struct iwl3945_priv *priv, struct iwl3945_rx_mem_b
priv->last_statistics_time = jiffies;
}
/******************************************************************************
*
* Misc. internal state and helper functions
*
******************************************************************************/
#ifdef CONFIG_IWL3945_DEBUG
/**
* iwl3945_report_frame - dump frame to syslog during debug sessions
*
* You may hack this function to show different aspects of received frames,
* including selective frame dumps.
* group100 parameter selects whether to show 1 out of 100 good frames.
*/
static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv,
struct iwl3945_rx_packet *pkt,
struct ieee80211_hdr *header, int group100)
{
u32 to_us;
u32 print_summary = 0;
u32 print_dump = 0; /* set to 1 to dump all frames' contents */
u32 hundred = 0;
u32 dataframe = 0;
u16 fc;
u16 seq_ctl;
u16 channel;
u16 phy_flags;
u16 length;
u16 status;
u16 bcn_tmr;
u32 tsf_low;
u64 tsf;
u8 rssi;
u8 agc;
u16 sig_avg;
u16 noise_diff;
struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
u8 *data = IWL_RX_DATA(pkt);
/* MAC header */
fc = le16_to_cpu(header->frame_control);
seq_ctl = le16_to_cpu(header->seq_ctrl);
/* metadata */
channel = le16_to_cpu(rx_hdr->channel);
phy_flags = le16_to_cpu(rx_hdr->phy_flags);
length = le16_to_cpu(rx_hdr->len);
/* end-of-frame status and timestamp */
status = le32_to_cpu(rx_end->status);
bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
tsf = le64_to_cpu(rx_end->timestamp);
/* signal statistics */
rssi = rx_stats->rssi;
agc = rx_stats->agc;
sig_avg = le16_to_cpu(rx_stats->sig_avg);
noise_diff = le16_to_cpu(rx_stats->noise_diff);
to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
/* if data frame is to us and all is good,
* (optionally) print summary for only 1 out of every 100 */
if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
dataframe = 1;
if (!group100)
print_summary = 1; /* print each frame */
else if (priv->framecnt_to_us < 100) {
priv->framecnt_to_us++;
print_summary = 0;
} else {
priv->framecnt_to_us = 0;
print_summary = 1;
hundred = 1;
}
} else {
/* print summary for all other frames */
print_summary = 1;
}
if (print_summary) {
char *title;
u32 rate;
if (hundred)
title = "100Frames";
else if (fc & IEEE80211_FCTL_RETRY)
title = "Retry";
else if (ieee80211_is_assoc_response(fc))
title = "AscRsp";
else if (ieee80211_is_reassoc_response(fc))
title = "RasRsp";
else if (ieee80211_is_probe_response(fc)) {
title = "PrbRsp";
print_dump = 1; /* dump frame contents */
} else if (ieee80211_is_beacon(fc)) {
title = "Beacon";
print_dump = 1; /* dump frame contents */
} else if (ieee80211_is_atim(fc))
title = "ATIM";
else if (ieee80211_is_auth(fc))
title = "Auth";
else if (ieee80211_is_deauth(fc))
title = "DeAuth";
else if (ieee80211_is_disassoc(fc))
title = "DisAssoc";
else
title = "Frame";
rate = iwl3945_hwrate_to_plcp_idx(rx_hdr->rate);
if (rate == -1)
rate = 0;
else
rate = iwl3945_rates[rate].ieee / 2;
/* print frame summary.
* MAC addresses show just the last byte (for brevity),
* but you can hack it to show more, if you'd like to. */
if (dataframe)
IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
"len=%u, rssi=%d, chnl=%d, rate=%u, \n",
title, fc, header->addr1[5],
length, rssi, channel, rate);
else {
/* src/dst addresses assume managed mode */
IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
"src=0x%02x, rssi=%u, tim=%lu usec, "
"phy=0x%02x, chnl=%d\n",
title, fc, header->addr1[5],
header->addr3[5], rssi,
tsf_low - priv->scan_start_tsf,
phy_flags, channel);
}
}
if (print_dump)
iwl3945_print_hex_dump(IWL_DL_RX, data, length);
}
#else
static inline void iwl3945_dbg_report_frame(struct iwl3945_priv *priv,
struct iwl3945_rx_packet *pkt,
struct ieee80211_hdr *header, int group100)
{
}
#endif
static void iwl3945_add_radiotap(struct iwl3945_priv *priv,
struct sk_buff *skb,
struct iwl3945_rx_frame_hdr *rx_hdr,
@ -376,24 +649,28 @@ static void iwl3945_handle_data_packet(struct iwl3945_priv *priv, int is_data,
static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
struct iwl3945_rx_mem_buffer *rxb)
{
struct ieee80211_hdr *header;
struct ieee80211_rx_status rx_status;
struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
struct ieee80211_hdr *header;
int snr;
u16 rx_stats_sig_avg = le16_to_cpu(rx_stats->sig_avg);
u16 rx_stats_noise_diff = le16_to_cpu(rx_stats->noise_diff);
struct ieee80211_rx_status stats = {
.mactime = le64_to_cpu(rx_end->timestamp),
.freq = ieee80211chan2mhz(le16_to_cpu(rx_hdr->channel)),
.band = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ,
.antenna = 0,
.rate_idx = iwl3945_rate_index_from_plcp(rx_hdr->rate),
.flag = 0,
};
u8 network_packet;
int snr;
rx_status.antenna = 0;
rx_status.flag = 0;
rx_status.mactime = le64_to_cpu(rx_end->timestamp);
rx_status.freq = ieee80211chan2mhz(le16_to_cpu(rx_hdr->channel));
rx_status.band = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
rx_status.rate_idx = iwl3945_hwrate_to_plcp_idx(rx_hdr->rate);
if (rx_status.band == IEEE80211_BAND_5GHZ)
rx_status.rate_idx -= IWL_FIRST_OFDM_RATE;
if ((unlikely(rx_stats->phy_count > 20))) {
IWL_DEBUG_DROP
@ -409,12 +686,12 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
}
if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
iwl3945_handle_data_packet(priv, 1, rxb, &stats);
iwl3945_handle_data_packet(priv, 1, rxb, &rx_status);
return;
}
/* Convert 3945's rssi indicator to dBm */
stats.ssi = rx_stats->rssi - IWL_RSSI_OFFSET;
rx_status.ssi = rx_stats->rssi - IWL_RSSI_OFFSET;
/* Set default noise value to -127 */
if (priv->last_rx_noise == 0)
@ -430,50 +707,47 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
* signal-to-noise ratio (SNR) is (sig_avg / noise_diff).
* Convert linear SNR to dB SNR, then subtract that from rssi dBm
* to obtain noise level in dBm.
* Calculate stats.signal (quality indicator in %) based on SNR. */
* Calculate rx_status.signal (quality indicator in %) based on SNR. */
if (rx_stats_noise_diff) {
snr = rx_stats_sig_avg / rx_stats_noise_diff;
stats.noise = stats.ssi - iwl3945_calc_db_from_ratio(snr);
stats.signal = iwl3945_calc_sig_qual(stats.ssi, stats.noise);
rx_status.noise = rx_status.ssi -
iwl3945_calc_db_from_ratio(snr);
rx_status.signal = iwl3945_calc_sig_qual(rx_status.ssi,
rx_status.noise);
/* If noise info not available, calculate signal quality indicator (%)
* using just the dBm signal level. */
} else {
stats.noise = priv->last_rx_noise;
stats.signal = iwl3945_calc_sig_qual(stats.ssi, 0);
rx_status.noise = priv->last_rx_noise;
rx_status.signal = iwl3945_calc_sig_qual(rx_status.ssi, 0);
}
IWL_DEBUG_STATS("Rssi %d noise %d qual %d sig_avg %d noise_diff %d\n",
stats.ssi, stats.noise, stats.signal,
rx_status.ssi, rx_status.noise, rx_status.signal,
rx_stats_sig_avg, rx_stats_noise_diff);
/* can be covered by iwl3945_report_frame() in most cases */
/* IWL_DEBUG_RX("RX status: 0x%08X\n", rx_end->status); */
header = (struct ieee80211_hdr *)IWL_RX_DATA(pkt);
network_packet = iwl3945_is_network_packet(priv, header);
#ifdef CONFIG_IWL3945_DEBUG
if (iwl3945_debug_level & IWL_DL_STATS && net_ratelimit())
IWL_DEBUG_STATS
("[%c] %d RSSI: %d Signal: %u, Noise: %u, Rate: %u\n",
network_packet ? '*' : ' ',
le16_to_cpu(rx_hdr->channel),
stats.ssi, stats.ssi,
stats.ssi, stats.rate_idx);
IWL_DEBUG_STATS_LIMIT("[%c] %d RSSI:%d Signal:%u, Noise:%u, Rate:%u\n",
network_packet ? '*' : ' ',
le16_to_cpu(rx_hdr->channel),
rx_status.ssi, rx_status.ssi,
rx_status.ssi, rx_status.rate_idx);
#ifdef CONFIG_IWL3945_DEBUG
if (iwl3945_debug_level & (IWL_DL_RX))
/* Set "1" to report good data frames in groups of 100 */
iwl3945_report_frame(priv, pkt, header, 1);
iwl3945_dbg_report_frame(priv, pkt, header, 1);
#endif
if (network_packet) {
priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp);
priv->last_tsf = le64_to_cpu(rx_end->timestamp);
priv->last_rx_rssi = stats.ssi;
priv->last_rx_noise = stats.noise;
priv->last_rx_rssi = rx_status.ssi;
priv->last_rx_noise = rx_status.noise;
}
switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) {
@ -560,7 +834,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
}
}
iwl3945_handle_data_packet(priv, 0, rxb, &stats);
iwl3945_handle_data_packet(priv, 0, rxb, &rx_status);
break;
case IEEE80211_FTYPE_CTL:
@ -577,7 +851,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
print_mac(mac2, header->addr2),
print_mac(mac3, header->addr3));
else
iwl3945_handle_data_packet(priv, 1, rxb, &stats);
iwl3945_handle_data_packet(priv, 1, rxb, &rx_status);
break;
}
}
@ -993,19 +1267,19 @@ int iwl3945_hw_nic_init(struct iwl3945_priv *priv)
if (rev_id & PCI_CFG_REV_ID_BIT_RTP)
IWL_DEBUG_INFO("RTP type \n");
else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) {
IWL_DEBUG_INFO("ALM-MB type\n");
IWL_DEBUG_INFO("3945 RADIO-MB type\n");
iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MB);
CSR39_HW_IF_CONFIG_REG_BIT_3945_MB);
} else {
IWL_DEBUG_INFO("ALM-MM type\n");
IWL_DEBUG_INFO("3945 RADIO-MM type\n");
iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MM);
CSR39_HW_IF_CONFIG_REG_BIT_3945_MM);
}
if (EEPROM_SKU_CAP_OP_MODE_MRC == priv->eeprom.sku_cap) {
IWL_DEBUG_INFO("SKU OP mode is mrc\n");
iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC);
CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC);
} else
IWL_DEBUG_INFO("SKU OP mode is basic\n");
@ -1013,24 +1287,24 @@ int iwl3945_hw_nic_init(struct iwl3945_priv *priv)
IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
priv->eeprom.board_revision);
iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
} else {
IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
priv->eeprom.board_revision);
iwl3945_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
}
if (priv->eeprom.almgor_m_version <= 1) {
iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A);
CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A);
IWL_DEBUG_INFO("Card M type A version is 0x%X\n",
priv->eeprom.almgor_m_version);
} else {
IWL_DEBUG_INFO("Card M type B version is 0x%X\n",
priv->eeprom.almgor_m_version);
iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B);
CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B);
}
spin_unlock_irqrestore(&priv->lock, flags);
@ -2348,6 +2622,7 @@ unsigned int iwl3945_hw_get_beacon_cmd(struct iwl3945_priv *priv,
void iwl3945_hw_rx_handler_setup(struct iwl3945_priv *priv)
{
priv->rx_handlers[REPLY_TX] = iwl3945_rx_reply_tx;
priv->rx_handlers[REPLY_3945_RX] = iwl3945_rx_reply_rx;
}
@ -2362,9 +2637,25 @@ void iwl3945_hw_cancel_deferred_work(struct iwl3945_priv *priv)
cancel_delayed_work(&priv->thermal_periodic);
}
static struct iwl_3945_cfg iwl3945_bg_cfg = {
.name = "3945BG",
.fw_name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode",
.sku = IWL_SKU_G,
};
static struct iwl_3945_cfg iwl3945_abg_cfg = {
.name = "3945ABG",
.fw_name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode",
.sku = IWL_SKU_A|IWL_SKU_G,
};
struct pci_device_id iwl3945_hw_card_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4222)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4227)},
{IWL_PCI_DEVICE(0x4222, 0x1005, iwl3945_bg_cfg)},
{IWL_PCI_DEVICE(0x4222, 0x1034, iwl3945_bg_cfg)},
{IWL_PCI_DEVICE(0x4222, 0x1044, iwl3945_bg_cfg)},
{IWL_PCI_DEVICE(0x4227, 0x1014, iwl3945_bg_cfg)},
{IWL_PCI_DEVICE(0x4222, PCI_ANY_ID, iwl3945_abg_cfg)},
{IWL_PCI_DEVICE(0x4227, PCI_ANY_ID, iwl3945_abg_cfg)},
{0}
};

View file

@ -40,10 +40,17 @@
extern struct pci_device_id iwl3945_hw_card_ids[];
#define DRV_NAME "iwl3945"
#include "iwl-3945-hw.h"
#include "iwl-csr.h"
#include "iwl-prph.h"
#include "iwl-3945-hw.h"
#include "iwl-3945-debug.h"
/* Change firmware file name, using "-" and incrementing number,
* *only* when uCode interface or architecture changes so that it
* is not compatible with earlier drivers.
* This number will also appear in << 8 position of 1st dword of uCode file */
#define IWL3945_UCODE_API "-1"
/* Default noise level to report when noise measurement is not available.
* This may be because we're:
* 1) Not associated (4965, no beacon statistics being sent to driver)
@ -109,6 +116,9 @@ struct iwl3945_queue {
* space less than this */
} __attribute__ ((packed));
int iwl3945_queue_space(const struct iwl3945_queue *q);
int iwl3945_x2_queue_used(const struct iwl3945_queue *q, int i);
#define MAX_NUM_OF_TBS (20)
/* One for each TFD */
@ -558,16 +568,6 @@ extern int iwl3945_is_network_packet(struct iwl3945_priv *priv,
struct ieee80211_hdr *header);
extern int iwl3945_power_init_handle(struct iwl3945_priv *priv);
extern int iwl3945_eeprom_init(struct iwl3945_priv *priv);
#ifdef CONFIG_IWL3945_DEBUG
extern void iwl3945_report_frame(struct iwl3945_priv *priv,
struct iwl3945_rx_packet *pkt,
struct ieee80211_hdr *header, int group100);
#else
static inline void iwl3945_report_frame(struct iwl3945_priv *priv,
struct iwl3945_rx_packet *pkt,
struct ieee80211_hdr *header,
int group100) {}
#endif
extern void iwl3945_handle_data_packet_monitor(struct iwl3945_priv *priv,
struct iwl3945_rx_mem_buffer *rxb,
void *data, short len,
@ -691,6 +691,7 @@ struct iwl3945_priv {
struct ieee80211_hw *hw;
struct ieee80211_channel *ieee_channels;
struct ieee80211_rate *ieee_rates;
struct iwl_3945_cfg *cfg; /* device configuration */
/* temporary frame storage list */
struct list_head free_frames;
@ -800,7 +801,6 @@ struct iwl3945_priv {
struct iwl3945_tx_queue txq[IWL_MAX_NUM_QUEUES];
unsigned long status;
u32 config;
int last_rx_rssi; /* From Rx packet statisitics */
int last_rx_noise; /* From beacon statistics */
@ -830,7 +830,6 @@ struct iwl3945_priv {
int is_open;
u8 mac80211_registered;
int is_abg;
u32 notif_missed_beacons;
@ -950,16 +949,6 @@ static inline int is_channel_ibss(const struct iwl3945_channel_info *ch)
return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0;
}
static inline int iwl3945_rate_index_from_plcp(int plcp)
{
int i;
for (i = 0; i < IWL_RATE_COUNT; i++)
if (iwl3945_rates[i].plcp == plcp)
return i;
return -1;
}
extern const struct iwl3945_channel_info *iwl3945_get_channel_info(
const struct iwl3945_priv *priv, enum ieee80211_band band, u16 channel);

View file

@ -875,26 +875,26 @@ struct iwl4965_rx_frame_hdr {
u8 payload[0];
} __attribute__ ((packed));
#define RX_RES_STATUS_NO_CRC32_ERROR __constant_cpu_to_le32(1 << 0)
#define RX_RES_STATUS_NO_RXE_OVERFLOW __constant_cpu_to_le32(1 << 1)
#define RX_RES_STATUS_NO_CRC32_ERROR __constant_cpu_to_le32(1 << 0)
#define RX_RES_STATUS_NO_RXE_OVERFLOW __constant_cpu_to_le32(1 << 1)
#define RX_RES_PHY_FLAGS_BAND_24_MSK __constant_cpu_to_le16(1 << 0)
#define RX_RES_PHY_FLAGS_MOD_CCK_MSK __constant_cpu_to_le16(1 << 1)
#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK __constant_cpu_to_le16(1 << 2)
#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK __constant_cpu_to_le16(1 << 3)
#define RX_RES_PHY_FLAGS_ANTENNA_MSK __constant_cpu_to_le16(0xf0)
#define RX_RES_PHY_FLAGS_BAND_24_MSK __constant_cpu_to_le16(1 << 0)
#define RX_RES_PHY_FLAGS_MOD_CCK_MSK __constant_cpu_to_le16(1 << 1)
#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK __constant_cpu_to_le16(1 << 2)
#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK __constant_cpu_to_le16(1 << 3)
#define RX_RES_PHY_FLAGS_ANTENNA_MSK __constant_cpu_to_le16(0xf0)
#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8)
#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8)
#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8)
#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8)
#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8)
#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8)
#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11)
#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11)
#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11)
#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11)
#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11)
#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11)
#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11)
#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11)
#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11)
#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11)
struct iwl4965_rx_frame_end {
__le32 status;

View file

@ -40,15 +40,30 @@ do { if (iwl4965_debug_level & (level)) \
do { if ((iwl4965_debug_level & (level)) && net_ratelimit()) \
printk(KERN_ERR DRV_NAME": %c %s " fmt, \
in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
static inline void iwl4965_print_hex_dump(int level, void *p, u32 len)
{
if (!(iwl4965_debug_level & level))
return;
print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
p, len, 1);
}
#else
static inline void IWL_DEBUG(int level, const char *fmt, ...)
{
}
static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
{
}
static inline void iwl4965_print_hex_dump(int level, void *p, u32 len)
{
}
#endif /* CONFIG_IWL4965_DEBUG */
/*
* To use the debug system;
*
@ -143,6 +158,7 @@ static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
IWL_DEBUG_LIMIT(IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
#define IWL_DEBUG_HT(f, a...) IWL_DEBUG(IWL_DL_HT, f, ## a)
#define IWL_DEBUG_STATS(f, a...) IWL_DEBUG(IWL_DL_STATS, f, ## a)
#define IWL_DEBUG_STATS_LIMIT(f, a...) IWL_DEBUG_LIMIT(IWL_DL_STATS, f, ## a)
#define IWL_DEBUG_TX_REPLY(f, a...) IWL_DEBUG(IWL_DL_TX_REPLY, f, ## a)
#define IWL_DEBUG_QOS(f, a...) IWL_DEBUG(IWL_DL_QOS, f, ## a)
#define IWL_DEBUG_RADIO(f, a...) IWL_DEBUG(IWL_DL_RADIO, f, ## a)

View file

@ -410,181 +410,6 @@ struct iwl4965_eeprom {
#define PCI_REG_WUM8 0x0E8
#define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT (0x80000000)
/*=== CSR (control and status registers) ===*/
#define CSR_BASE (0x000)
#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */
#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */
#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */
#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */
#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/
#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */
#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
#define CSR_GP_CNTRL (CSR_BASE+0x024)
/*
* Hardware revision info
* Bit fields:
* 31-8: Reserved
* 7-4: Type of device: 0x0 = 4965, 0xd = 3945
* 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D
* 1-0: "Dash" value, as in A-1, etc.
*
* NOTE: Revision step affects calculation of CCK txpower for 4965.
*/
#define CSR_HW_REV (CSR_BASE+0x028)
/* EEPROM reads */
#define CSR_EEPROM_REG (CSR_BASE+0x02c)
#define CSR_EEPROM_GP (CSR_BASE+0x030)
#define CSR_GP_UCODE (CSR_BASE+0x044)
#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054)
#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058)
#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
/*
* Indicates hardware rev, to determine CCK backoff for txpower calculation.
* Bit fields:
* 3-2: 0 = A, 1 = B, 2 = C, 3 = D step
*/
#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C)
/* Hardware interface configuration bits */
#define CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R (0x00000010)
#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x00000C00)
#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100)
#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200)
#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
* acknowledged (reset) by host writing "1" to flagged bits. */
#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */
#define CSR_INT_BIT_DNLD (1 << 28) /* uCode Download */
#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */
#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */
#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */
#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */
#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */
#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses, 3945 */
#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */
#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */
#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \
CSR_INT_BIT_HW_ERR | \
CSR_INT_BIT_FH_TX | \
CSR_INT_BIT_SW_ERR | \
CSR_INT_BIT_RF_KILL | \
CSR_INT_BIT_SW_RX | \
CSR_INT_BIT_WAKEUP | \
CSR_INT_BIT_ALIVE)
/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */
#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */
#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */
#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */
#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */
#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */
#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \
CSR_FH_INT_BIT_RX_CHNL1 | \
CSR_FH_INT_BIT_RX_CHNL0)
#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \
CSR_FH_INT_BIT_TX_CHNL0)
/* RESET */
#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001)
#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002)
#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080)
#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100)
#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200)
/* GP (general purpose) CONTROL */
#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001)
#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004)
#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008)
#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010)
#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001)
#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000)
#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000)
#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000)
/* EEPROM REG */
#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001)
#define CSR_EEPROM_REG_BIT_CMD (0x00000002)
/* EEPROM GP */
#define CSR_EEPROM_GP_VALID_MSK (0x00000006)
#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000)
#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180)
/* UCODE DRV GP */
#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001)
#define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008)
/* GPIO */
#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200)
#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000)
#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC CSR_GPIO_IN_BIT_AUX_POWER
/* GI Chicken Bits */
#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000)
#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000)
/*=== HBUS (Host-side Bus) ===*/
#define HBUS_BASE (0x400)
/*
* Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
* structures, error log, event log, verifying uCode load).
* First write to address register, then read from or write to data register
* to complete the job. Once the address register is set up, accesses to
* data registers auto-increment the address by one dword.
* Bit usage for address registers (read or write):
* 0-31: memory address within device
*/
#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c)
#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010)
#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018)
#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c)
/*
* Registers for accessing device's internal peripheral registers
* (e.g. SCD, BSM, etc.). First write to address register,
* then read from or write to data register to complete the job.
* Bit usage for address registers (read or write):
* 0-15: register address (offset) within device
* 24-25: (# bytes - 1) to read or write (e.g. 3 for dword)
*/
#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044)
#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048)
#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c)
#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050)
/*
* Per-Tx-queue write pointer (index, really!) (3945 and 4965).
* Driver sets this to indicate index to next TFD that driver will fill
* (1 past latest filled).
* Bit usage:
* 0-7: queue write index (0-255)
* 11-8: queue selector (0-15)
*/
#define HBUS_TARG_WRPTR (HBUS_BASE+0x060)
#define HBUS_TARG_MBX_C (HBUS_BASE+0x030)
#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004)
#define TFD_QUEUE_SIZE_MAX (256)
#define IWL_NUM_SCAN_RATES (2)

View file

@ -570,7 +570,7 @@ static int rs_get_tbl_info_from_mcs(const struct iwl4965_rate *mcs_rate,
int index;
u32 ant_msk;
index = iwl4965_rate_index_from_plcp(mcs_rate->rate_n_flags);
index = iwl4965_hwrate_to_plcp_idx(mcs_rate->rate_n_flags);
if (index == IWL_RATE_INVALID) {
*rate_idx = -1;
@ -823,6 +823,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct iwl4965_priv *priv = (struct iwl4965_priv *)priv_rate;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_hw *hw = local_to_hw(local);
struct iwl4965_rate_scale_data *window = NULL;
struct iwl4965_rate_scale_data *search_win = NULL;
struct iwl4965_rate tx_mcs;
@ -847,23 +848,22 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
if (retries > 15)
retries = 15;
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
return;
}
if (!sta || !sta->rate_ctrl_priv)
goto out;
lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv;
if (!priv->lq_mngr.lq_ready)
return;
goto out;
if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
!lq_sta->ibss_sta_added)
return;
goto out;
table = &lq_sta->lq;
active_index = lq_sta->active_tbl;
@ -884,17 +884,6 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
search_win = (struct iwl4965_rate_scale_data *)
&(search_tbl->win[0]);
tx_mcs.rate_n_flags = tx_resp->control.tx_rate->hw_value;
rs_get_tbl_info_from_mcs(&tx_mcs, priv->band,
&tbl_type, &rs_index);
if ((rs_index < 0) || (rs_index >= IWL_RATE_COUNT)) {
IWL_DEBUG_RATE("bad rate index at: %d rate 0x%X\n",
rs_index, tx_mcs.rate_n_flags);
sta_info_put(sta);
return;
}
/*
* Ignore this Tx frame response if its initial rate doesn't match
* that of latest Link Quality command. There may be stragglers
@ -903,14 +892,29 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
* to check "search" mode, or a prior "search" mode after we've moved
* to a new "search" mode (which might become the new "active" mode).
*/
if (retries &&
(tx_mcs.rate_n_flags !=
le32_to_cpu(table->rs_table[0].rate_n_flags))) {
IWL_DEBUG_RATE("initial rate does not match 0x%x 0x%x\n",
tx_mcs.rate_n_flags,
le32_to_cpu(table->rs_table[0].rate_n_flags));
sta_info_put(sta);
return;
tx_mcs.rate_n_flags = le32_to_cpu(table->rs_table[0].rate_n_flags);
rs_get_tbl_info_from_mcs(&tx_mcs, priv->band, &tbl_type, &rs_index);
if (priv->band == IEEE80211_BAND_5GHZ)
rs_index -= IWL_FIRST_OFDM_RATE;
if ((tx_resp->control.tx_rate == NULL) ||
(tbl_type.is_SGI ^
!!(tx_resp->control.flags & IEEE80211_TXCTL_SHORT_GI)) ||
(tbl_type.is_fat ^
!!(tx_resp->control.flags & IEEE80211_TXCTL_40_MHZ_WIDTH)) ||
(tbl_type.is_dup ^
!!(tx_resp->control.flags & IEEE80211_TXCTL_DUP_DATA)) ||
(tbl_type.antenna_type ^
tx_resp->control.antenna_sel_tx) ||
(!!(tx_mcs.rate_n_flags & RATE_MCS_HT_MSK) ^
!!(tx_resp->control.flags & IEEE80211_TXCTL_OFDM_HT)) ||
(!!(tx_mcs.rate_n_flags & RATE_MCS_GF_MSK) ^
!!(tx_resp->control.flags & IEEE80211_TXCTL_GREEN_FIELD)) ||
(hw->wiphy->bands[priv->band]->bitrates[rs_index].bitrate !=
tx_resp->control.tx_rate->bitrate)) {
IWL_DEBUG_RATE("initial rate does not match 0x%x\n",
tx_mcs.rate_n_flags);
goto out;
}
/* Update frame history window with "failure" for each Tx retry. */
@ -959,14 +963,8 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
* if Tx was successful first try, use original rate,
* else look up the rate that was, finally, successful.
*/
if (!tx_resp->retry_count)
tx_mcs.rate_n_flags = tx_resp->control.tx_rate->hw_value;
else
tx_mcs.rate_n_flags =
le32_to_cpu(table->rs_table[index].rate_n_flags);
rs_get_tbl_info_from_mcs(&tx_mcs, priv->band,
&tbl_type, &rs_index);
tx_mcs.rate_n_flags = le32_to_cpu(table->rs_table[index].rate_n_flags);
rs_get_tbl_info_from_mcs(&tx_mcs, priv->band, &tbl_type, &rs_index);
/* Update frame history window with "success" if Tx got ACKed ... */
if (tx_resp->flags & IEEE80211_TX_STATUS_ACK)
@ -1025,7 +1023,8 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
/* See if there's a better rate or modulation mode to try. */
rs_rate_scale_perform(priv, dev, hdr, sta);
sta_info_put(sta);
out:
rcu_read_unlock();
return;
}
@ -1921,7 +1920,7 @@ static void rs_rate_scale_perform(struct iwl4965_priv *priv,
tbl = &(lq_sta->lq_info[active_tbl]);
/* Revert to "active" rate and throughput info */
index = iwl4965_rate_index_from_plcp(
index = iwl4965_hwrate_to_plcp_idx(
tbl->current_rate.rate_n_flags);
current_tpt = lq_sta->last_tpt;
@ -2077,7 +2076,7 @@ static void rs_rate_scale_perform(struct iwl4965_priv *priv,
rs_rate_scale_clear_window(&(tbl->win[i]));
/* Use new "search" start rate */
index = iwl4965_rate_index_from_plcp(
index = iwl4965_hwrate_to_plcp_idx(
tbl->current_rate.rate_n_flags);
IWL_DEBUG_HT("Switch current mcs: %X index: %d\n",
@ -2219,6 +2218,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n");
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
/* Send management frames and broadcast/multicast data using lowest
@ -2227,9 +2228,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
!sta || !sta->rate_ctrl_priv) {
sel->rate = rate_lowest(local, sband, sta);
if (sta)
sta_info_put(sta);
return;
goto out;
}
lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv;
@ -2256,14 +2255,15 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
goto done;
}
done:
done:
if ((i < 0) || (i > IWL_RATE_COUNT)) {
sel->rate = rate_lowest(local, sband, sta);
return;
goto out;
}
sta_info_put(sta);
sel->rate = &priv->ieee_rates[i];
out:
rcu_read_unlock();
}
static void *rs_alloc_sta(void *priv, gfp_t gfp)
@ -2735,13 +2735,15 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
u32 max_time = 0;
u8 lq_type, antenna;
rcu_read_lock();
sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta) {
sta_info_put(sta);
if (sta)
IWL_DEBUG_RATE("leave - no private rate data!\n");
} else
else
IWL_DEBUG_RATE("leave - no station!\n");
rcu_read_unlock();
return sprintf(buf, "station %d not found\n", sta_id);
}
@ -2808,7 +2810,7 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
"active_search %d rate index %d\n", lq_type, antenna,
lq_sta->search_better_tbl, sta->last_txrate_idx);
sta_info_put(sta);
rcu_read_unlock();
return cnt;
}

View file

@ -259,7 +259,7 @@ static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index)
return rate;
}
extern int iwl4965_rate_index_from_plcp(int plcp);
extern int iwl4965_hwrate_to_plcp_idx(u32 rate_n_flags);
/**
* iwl4965_fill_rs_info - Fill an output text buffer with the rate representation

View file

@ -38,6 +38,7 @@
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
#include "iwl-core.h"
#include "iwl-4965.h"
#include "iwl-helpers.h"
@ -122,6 +123,64 @@ static u8 is_single_stream(struct iwl4965_priv *priv)
return 0;
}
int iwl4965_hwrate_to_plcp_idx(u32 rate_n_flags)
{
int idx = 0;
/* 4965 HT rate format */
if (rate_n_flags & RATE_MCS_HT_MSK) {
idx = (rate_n_flags & 0xff);
if (idx >= IWL_RATE_MIMO_6M_PLCP)
idx = idx - IWL_RATE_MIMO_6M_PLCP;
idx += IWL_FIRST_OFDM_RATE;
/* skip 9M not supported in ht*/
if (idx >= IWL_RATE_9M_INDEX)
idx += 1;
if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
return idx;
/* 4965 legacy rate format, search for match in table */
} else {
for (idx = 0; idx < ARRAY_SIZE(iwl4965_rates); idx++)
if (iwl4965_rates[idx].plcp == (rate_n_flags & 0xFF))
return idx;
}
return -1;
}
/**
* translate ucode response to mac80211 tx status control values
*/
void iwl4965_hwrate_to_tx_control(struct iwl4965_priv *priv, u32 rate_n_flags,
struct ieee80211_tx_control *control)
{
int rate_index;
control->antenna_sel_tx =
((rate_n_flags & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_A_POS);
if (rate_n_flags & RATE_MCS_HT_MSK)
control->flags |= IEEE80211_TXCTL_OFDM_HT;
if (rate_n_flags & RATE_MCS_GF_MSK)
control->flags |= IEEE80211_TXCTL_GREEN_FIELD;
if (rate_n_flags & RATE_MCS_FAT_MSK)
control->flags |= IEEE80211_TXCTL_40_MHZ_WIDTH;
if (rate_n_flags & RATE_MCS_DUP_MSK)
control->flags |= IEEE80211_TXCTL_DUP_DATA;
if (rate_n_flags & RATE_MCS_SGI_MSK)
control->flags |= IEEE80211_TXCTL_SHORT_GI;
/* since iwl4965_hwrate_to_plcp_idx is band indifferent, we always use
* IEEE80211_BAND_2GHZ band as it contains all the rates */
rate_index = iwl4965_hwrate_to_plcp_idx(rate_n_flags);
if (rate_index == -1)
control->tx_rate = NULL;
else
control->tx_rate =
&priv->bands[IEEE80211_BAND_2GHZ].bitrates[rate_index];
}
/*
* Determine how many receiver/antenna chains to use.
* More provides better reception via diversity. Fewer saves power.
@ -546,9 +605,9 @@ int iwl4965_hw_nic_init(struct iwl4965_priv *priv)
/* set CSR_HW_CONFIG_REG for uCode use */
iwl4965_set_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R |
CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
CSR49_HW_IF_CONFIG_REG_BIT_4965_R |
CSR49_HW_IF_CONFIG_REG_BIT_RADIO_SI |
CSR49_HW_IF_CONFIG_REG_BIT_MAC_SI);
rc = iwl4965_grab_nic_access(priv);
if (rc < 0) {
@ -3523,6 +3582,160 @@ static void iwl4965_update_ps_mode(struct iwl4965_priv *priv, u16 ps_bit, u8 *ad
}
}
}
#ifdef CONFIG_IWL4965_DEBUG
/**
* iwl4965_dbg_report_frame - dump frame to syslog during debug sessions
*
* You may hack this function to show different aspects of received frames,
* including selective frame dumps.
* group100 parameter selects whether to show 1 out of 100 good frames.
*
* TODO: This was originally written for 3945, need to audit for
* proper operation with 4965.
*/
static void iwl4965_dbg_report_frame(struct iwl4965_priv *priv,
struct iwl4965_rx_packet *pkt,
struct ieee80211_hdr *header, int group100)
{
u32 to_us;
u32 print_summary = 0;
u32 print_dump = 0; /* set to 1 to dump all frames' contents */
u32 hundred = 0;
u32 dataframe = 0;
u16 fc;
u16 seq_ctl;
u16 channel;
u16 phy_flags;
int rate_sym;
u16 length;
u16 status;
u16 bcn_tmr;
u32 tsf_low;
u64 tsf;
u8 rssi;
u8 agc;
u16 sig_avg;
u16 noise_diff;
struct iwl4965_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
struct iwl4965_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
struct iwl4965_rx_frame_end *rx_end = IWL_RX_END(pkt);
u8 *data = IWL_RX_DATA(pkt);
if (likely(!(iwl4965_debug_level & IWL_DL_RX)))
return;
/* MAC header */
fc = le16_to_cpu(header->frame_control);
seq_ctl = le16_to_cpu(header->seq_ctrl);
/* metadata */
channel = le16_to_cpu(rx_hdr->channel);
phy_flags = le16_to_cpu(rx_hdr->phy_flags);
rate_sym = rx_hdr->rate;
length = le16_to_cpu(rx_hdr->len);
/* end-of-frame status and timestamp */
status = le32_to_cpu(rx_end->status);
bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
tsf = le64_to_cpu(rx_end->timestamp);
/* signal statistics */
rssi = rx_stats->rssi;
agc = rx_stats->agc;
sig_avg = le16_to_cpu(rx_stats->sig_avg);
noise_diff = le16_to_cpu(rx_stats->noise_diff);
to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
/* if data frame is to us and all is good,
* (optionally) print summary for only 1 out of every 100 */
if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
dataframe = 1;
if (!group100)
print_summary = 1; /* print each frame */
else if (priv->framecnt_to_us < 100) {
priv->framecnt_to_us++;
print_summary = 0;
} else {
priv->framecnt_to_us = 0;
print_summary = 1;
hundred = 1;
}
} else {
/* print summary for all other frames */
print_summary = 1;
}
if (print_summary) {
char *title;
int rate_idx;
u32 bitrate;
if (hundred)
title = "100Frames";
else if (fc & IEEE80211_FCTL_RETRY)
title = "Retry";
else if (ieee80211_is_assoc_response(fc))
title = "AscRsp";
else if (ieee80211_is_reassoc_response(fc))
title = "RasRsp";
else if (ieee80211_is_probe_response(fc)) {
title = "PrbRsp";
print_dump = 1; /* dump frame contents */
} else if (ieee80211_is_beacon(fc)) {
title = "Beacon";
print_dump = 1; /* dump frame contents */
} else if (ieee80211_is_atim(fc))
title = "ATIM";
else if (ieee80211_is_auth(fc))
title = "Auth";
else if (ieee80211_is_deauth(fc))
title = "DeAuth";
else if (ieee80211_is_disassoc(fc))
title = "DisAssoc";
else
title = "Frame";
rate_idx = iwl4965_hwrate_to_plcp_idx(rate_sym);
if (unlikely(rate_idx == -1))
bitrate = 0;
else
bitrate = iwl4965_rates[rate_idx].ieee / 2;
/* print frame summary.
* MAC addresses show just the last byte (for brevity),
* but you can hack it to show more, if you'd like to. */
if (dataframe)
IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
"len=%u, rssi=%d, chnl=%d, rate=%u, \n",
title, fc, header->addr1[5],
length, rssi, channel, bitrate);
else {
/* src/dst addresses assume managed mode */
IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
"src=0x%02x, rssi=%u, tim=%lu usec, "
"phy=0x%02x, chnl=%d\n",
title, fc, header->addr1[5],
header->addr3[5], rssi,
tsf_low - priv->scan_start_tsf,
phy_flags, channel);
}
}
if (print_dump)
iwl4965_print_hex_dump(IWL_DL_RX, data, length);
}
#else
static inline void iwl4965_dbg_report_frame(struct iwl4965_priv *priv,
struct iwl4965_rx_packet *pkt,
struct ieee80211_hdr *header,
int group100)
{
}
#endif
#define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
@ -3531,6 +3744,8 @@ static void iwl4965_update_ps_mode(struct iwl4965_priv *priv, u16 ps_bit, u8 *ad
static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
struct iwl4965_rx_mem_buffer *rxb)
{
struct ieee80211_hdr *header;
struct ieee80211_rx_status rx_status;
struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data;
/* Use phy data (Rx signal strength, etc.) contained within
* this rx packet for legacy frames,
@ -3541,27 +3756,29 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
(struct iwl4965_rx_phy_res *)&priv->last_phy_res[1];
__le32 *rx_end;
unsigned int len = 0;
struct ieee80211_hdr *header;
u16 fc;
struct ieee80211_rx_status stats = {
.mactime = le64_to_cpu(rx_start->timestamp),
.freq = ieee80211chan2mhz(le16_to_cpu(rx_start->channel)),
.band =
(rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ,
.antenna = 0,
.rate_idx = iwl4965_rate_index_from_plcp(
le32_to_cpu(rx_start->rate_n_flags)),
.flag = 0,
};
u8 network_packet;
rx_status.mactime = le64_to_cpu(rx_start->timestamp);
rx_status.freq = ieee80211chan2mhz(le16_to_cpu(rx_start->channel));
rx_status.band = (rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
rx_status.rate_idx = iwl4965_hwrate_to_plcp_idx(
le32_to_cpu(rx_start->rate_n_flags));
if (rx_status.band == IEEE80211_BAND_5GHZ)
rx_status.rate_idx -= IWL_FIRST_OFDM_RATE;
rx_status.antenna = 0;
rx_status.flag = 0;
if ((unlikely(rx_start->cfg_phy_cnt > 20))) {
IWL_DEBUG_DROP
("dsp size out of range [0,20]: "
"%d/n", rx_start->cfg_phy_cnt);
return;
}
if (!include_phy) {
if (priv->last_phy_res[0])
rx_start = (struct iwl4965_rx_phy_res *)
@ -3580,7 +3797,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
+ rx_start->cfg_phy_cnt);
len = le16_to_cpu(rx_start->byte_count);
rx_end = (__le32 *) (pkt->u.raw + rx_start->cfg_phy_cnt +
rx_end = (__le32 *)(pkt->u.raw + rx_start->cfg_phy_cnt +
sizeof(struct iwl4965_rx_phy_res) + len);
} else {
struct iwl4965_rx_mpdu_res_start *amsdu =
@ -3603,7 +3820,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
priv->ucode_beacon_time = le32_to_cpu(rx_start->beacon_time_stamp);
/* Find max signal strength (dBm) among 3 antenna/receiver chains */
stats.ssi = iwl4965_calc_rssi(rx_start);
rx_status.ssi = iwl4965_calc_rssi(rx_start);
/* Meaningful noise values are available only from beacon statistics,
* which are gathered only when associated, and indicate noise
@ -3611,32 +3828,29 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
* Ignore these noise values while scanning (other channels) */
if (iwl4965_is_associated(priv) &&
!test_bit(STATUS_SCANNING, &priv->status)) {
stats.noise = priv->last_rx_noise;
stats.signal = iwl4965_calc_sig_qual(stats.ssi, stats.noise);
rx_status.noise = priv->last_rx_noise;
rx_status.signal = iwl4965_calc_sig_qual(rx_status.ssi,
rx_status.noise);
} else {
stats.noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
stats.signal = iwl4965_calc_sig_qual(stats.ssi, 0);
rx_status.noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
rx_status.signal = iwl4965_calc_sig_qual(rx_status.ssi, 0);
}
/* Reset beacon noise level if not associated. */
if (!iwl4965_is_associated(priv))
priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
#ifdef CONFIG_IWL4965_DEBUG
/* TODO: Parts of iwl4965_report_frame are broken for 4965 */
if (iwl4965_debug_level & (IWL_DL_RX))
/* Set "1" to report good data frames in groups of 100 */
iwl4965_report_frame(priv, pkt, header, 1);
/* Set "1" to report good data frames in groups of 100 */
/* FIXME: need to optimze the call: */
iwl4965_dbg_report_frame(priv, pkt, header, 1);
if (iwl4965_debug_level & (IWL_DL_RX | IWL_DL_STATS))
IWL_DEBUG_RX("Rssi %d, noise %d, qual %d, TSF %lu\n",
stats.ssi, stats.noise, stats.signal,
(long unsigned int)le64_to_cpu(rx_start->timestamp));
#endif
IWL_DEBUG_STATS_LIMIT("Rssi %d, noise %d, qual %d, TSF %llu\n",
rx_status.ssi, rx_status.noise, rx_status.signal,
rx_status.mactime);
network_packet = iwl4965_is_network_packet(priv, header);
if (network_packet) {
priv->last_rx_rssi = stats.ssi;
priv->last_rx_rssi = rx_status.ssi;
priv->last_beacon_time = priv->ucode_beacon_time;
priv->last_tsf = le64_to_cpu(rx_start->timestamp);
}
@ -3739,7 +3953,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
return;
}
}
iwl4965_handle_data_packet(priv, 0, include_phy, rxb, &stats);
iwl4965_handle_data_packet(priv, 0, include_phy, rxb, &rx_status);
break;
case IEEE80211_FTYPE_CTL:
@ -3748,7 +3962,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
case IEEE80211_STYPE_BACK_REQ:
IWL_DEBUG_HT("IEEE80211_STYPE_BACK_REQ arrived\n");
iwl4965_handle_data_packet(priv, 0, include_phy,
rxb, &stats);
rxb, &rx_status);
break;
default:
break;
@ -3778,7 +3992,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
print_mac(mac3, header->addr3));
else
iwl4965_handle_data_packet(priv, 1, include_phy, rxb,
&stats);
&rx_status);
break;
}
default:
@ -3900,11 +4114,10 @@ static int iwl4965_tx_status_reply_compressed_ba(struct iwl4965_priv *priv,
tx_status->flags |= IEEE80211_TX_STATUS_AMPDU;
tx_status->ampdu_ack_map = successes;
tx_status->ampdu_ack_len = agg->frame_count;
/* FIXME Wrong rate
tx_status->control.tx_rate = agg->rate_n_flags;
*/
iwl4965_hwrate_to_tx_control(priv, agg->rate_n_flags,
&tx_status->control);
IWL_DEBUG_TX_REPLY("Bitmap %llx\n", bitmap);
IWL_DEBUG_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap);
return 0;
}
@ -3925,16 +4138,23 @@ static void iwl4965_tx_queue_stop_scheduler(struct iwl4965_priv *priv,
/**
* txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
* priv->lock must be held by the caller
*/
static int iwl4965_tx_queue_agg_disable(struct iwl4965_priv *priv, u16 txq_id,
u16 ssn_idx, u8 tx_fifo)
{
int ret = 0;
if (IWL_BACK_QUEUE_FIRST_ID > txq_id) {
IWL_WARNING("queue number too small: %d, must be > %d\n",
txq_id, IWL_BACK_QUEUE_FIRST_ID);
return -EINVAL;
}
ret = iwl4965_grab_nic_access(priv);
if (ret)
return ret;
iwl4965_tx_queue_stop_scheduler(priv, txq_id);
iwl4965_clear_bits_prph(priv, KDR_SCD_QUEUECHAIN_SEL, (1 << txq_id));
@ -3948,6 +4168,8 @@ static int iwl4965_tx_queue_agg_disable(struct iwl4965_priv *priv, u16 txq_id,
iwl4965_txq_ctx_deactivate(priv, txq_id);
iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
iwl4965_release_nic_access(priv);
return 0;
}
@ -4040,12 +4262,12 @@ static void iwl4965_rx_reply_compressed_ba(struct iwl4965_priv *priv,
"%d, scd_ssn = %d\n",
ba_resp->tid,
ba_resp->seq_ctl,
ba_resp->bitmap,
(unsigned long long)ba_resp->bitmap,
ba_resp->scd_flow,
ba_resp->scd_ssn);
IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx \n",
agg->start_idx,
agg->bitmap);
(unsigned long long)agg->bitmap);
/* Update driver's record of ACK vs. not for each frame in window */
iwl4965_tx_status_reply_compressed_ba(priv, agg, ba_resp);
@ -4232,7 +4454,7 @@ static u8 iwl4965_is_channel_extension(struct iwl4965_priv *priv,
if (!is_channel_valid(ch_info))
return 0;
if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO)
if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE)
return 0;
if ((ch_info->fat_extension_channel == extension_chan_offset) ||
@ -4249,7 +4471,7 @@ static u8 iwl4965_is_fat_tx_allowed(struct iwl4965_priv *priv,
if ((!iwl_ht_conf->is_ht) ||
(iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) ||
(iwl_ht_conf->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO))
(iwl_ht_conf->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE))
return 0;
if (sta_ht_inf) {
@ -4294,9 +4516,7 @@ void iwl4965_set_rxon_ht(struct iwl4965_priv *priv, struct iwl_ht_info *ht_info)
case IWL_EXT_CHANNEL_OFFSET_BELOW:
rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
break;
case IWL_EXT_CHANNEL_OFFSET_AUTO:
rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
break;
case IWL_EXT_CHANNEL_OFFSET_NONE:
default:
rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
break;
@ -4419,7 +4639,7 @@ static int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da,
int tx_fifo;
int txq_id;
int ssn = -1;
int rc = 0;
int ret = 0;
unsigned long flags;
struct iwl4965_tid_data *tid_data;
DECLARE_MAC_BUF(mac);
@ -4452,12 +4672,12 @@ static int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da,
spin_unlock_irqrestore(&priv->sta_lock, flags);
*start_seq_num = ssn;
rc = iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo,
sta_id, tid, ssn);
if (rc)
return rc;
ret = iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo,
sta_id, tid, ssn);
if (ret)
return ret;
rc = 0;
ret = 0;
if (tid_data->tfds_in_queue == 0) {
printk(KERN_ERR "HW queue is empty\n");
tid_data->agg.state = IWL_AGG_ON;
@ -4467,7 +4687,7 @@ static int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da,
tid_data->tfds_in_queue);
tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
}
return rc;
return ret;
}
static int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da,
@ -4477,7 +4697,7 @@ static int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da,
struct iwl4965_priv *priv = hw->priv;
int tx_fifo_id, txq_id, sta_id, ssn = -1;
struct iwl4965_tid_data *tid_data;
int rc, write_ptr, read_ptr;
int ret, write_ptr, read_ptr;
unsigned long flags;
DECLARE_MAC_BUF(mac);
@ -4517,17 +4737,11 @@ static int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da,
priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
spin_lock_irqsave(&priv->lock, flags);
rc = iwl4965_grab_nic_access(priv);
if (rc) {
spin_unlock_irqrestore(&priv->lock, flags);
return rc;
}
rc = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id);
iwl4965_release_nic_access(priv);
ret = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id);
spin_unlock_irqrestore(&priv->lock, flags);
if (rc)
return rc;
if (ret)
return ret;
ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, da, tid);
@ -4610,9 +4824,15 @@ void iwl4965_hw_cancel_deferred_work(struct iwl4965_priv *priv)
cancel_delayed_work(&priv->init_alive_start);
}
static struct iwl_cfg iwl4965_agn_cfg = {
.name = "4965AGN",
.fw_name = "iwlwifi-4965" IWL4965_UCODE_API ".ucode",
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
};
struct pci_device_id iwl4965_hw_card_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4229)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4230)},
{IWL_PCI_DEVICE(0x4229, PCI_ANY_ID, iwl4965_agn_cfg)},
{IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)},
{0}
};

View file

@ -41,9 +41,17 @@ extern struct pci_device_id iwl4965_hw_card_ids[];
#define DRV_NAME "iwl4965"
#include "iwl-4965-hw.h"
#include "iwl-csr.h"
#include "iwl-prph.h"
#include "iwl-4965-debug.h"
/* Change firmware file name, using "-" and incrementing number,
* *only* when uCode interface or architecture changes so that it
* is not compatible with earlier drivers.
* This number will also appear in << 8 position of 1st dword of uCode file */
#define IWL4965_UCODE_API "-1"
/* Default noise level to report when noise measurement is not available.
* This may be because we're:
* 1) Not associated (4965, no beacon statistics being sent to driver)
@ -633,16 +641,6 @@ extern int iwl4965_is_network_packet(struct iwl4965_priv *priv,
struct ieee80211_hdr *header);
extern int iwl4965_power_init_handle(struct iwl4965_priv *priv);
extern int iwl4965_eeprom_init(struct iwl4965_priv *priv);
#ifdef CONFIG_IWL4965_DEBUG
extern void iwl4965_report_frame(struct iwl4965_priv *priv,
struct iwl4965_rx_packet *pkt,
struct ieee80211_hdr *header, int group100);
#else
static inline void iwl4965_report_frame(struct iwl4965_priv *priv,
struct iwl4965_rx_packet *pkt,
struct ieee80211_hdr *header,
int group100) {}
#endif
extern void iwl4965_handle_data_packet_monitor(struct iwl4965_priv *priv,
struct iwl4965_rx_mem_buffer *rxb,
void *data, short len,
@ -767,6 +765,9 @@ extern int iwl4965_set_fat_chan_info(struct iwl4965_priv *priv,
const struct iwl4965_eeprom_channel *eeprom_ch,
u8 fat_extension_channel);
extern void iwl4965_rf_kill_ct_config(struct iwl4965_priv *priv);
extern void iwl4965_hwrate_to_tx_control(struct iwl4965_priv *priv,
u32 rate_n_flags,
struct ieee80211_tx_control *control);
#ifdef CONFIG_IWL4965_HT
void iwl4965_init_ht_hw_capab(struct ieee80211_ht_info *ht_info,
@ -808,11 +809,10 @@ struct iwl4965_kw {
#define IWL_OPERATION_MODE_MIXED 2
#define IWL_OPERATION_MODE_20MHZ 3
#define IWL_EXT_CHANNEL_OFFSET_AUTO 0
#define IWL_EXT_CHANNEL_OFFSET_ABOVE 1
#define IWL_EXT_CHANNEL_OFFSET_ 2
#define IWL_EXT_CHANNEL_OFFSET_BELOW 3
#define IWL_EXT_CHANNEL_OFFSET_MAX 4
#define IWL_EXT_CHANNEL_OFFSET_NONE 0
#define IWL_EXT_CHANNEL_OFFSET_ABOVE 1
#define IWL_EXT_CHANNEL_OFFSET_RESERVE1 2
#define IWL_EXT_CHANNEL_OFFSET_BELOW 3
#define NRG_NUM_PREV_STAT_L 20
#define NUM_RX_CHAINS (3)
@ -974,6 +974,7 @@ struct iwl4965_priv {
struct ieee80211_hw *hw;
struct ieee80211_channel *ieee_channels;
struct ieee80211_rate *ieee_rates;
struct iwl_cfg *cfg;
/* temporary frame storage list */
struct list_head free_frames;
@ -1104,7 +1105,6 @@ struct iwl4965_priv {
u32 scd_base_addr; /* scheduler sram base address */
unsigned long status;
u32 config;
int last_rx_rssi; /* From Rx packet statisitics */
int last_rx_noise; /* From beacon statistics */
@ -1134,7 +1134,6 @@ struct iwl4965_priv {
int is_open;
u8 mac80211_registered;
int is_abg;
u32 notif_missed_beacons;

View file

@ -0,0 +1,47 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
* Contact Information:
* Tomas Winkler <tomas.winkler@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include "iwl-4965-debug.h"
#include "iwl-core.h"
MODULE_DESCRIPTION("iwl core");
MODULE_VERSION(IWLWIFI_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL/BSD");
#ifdef CONFIG_IWL4965_DEBUG
u32 iwl4965_debug_level;
EXPORT_SYMBOL(iwl4965_debug_level);
#endif

View file

@ -0,0 +1,84 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
* Contact Information:
* Tomas Winkler <tomas.winkler@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __iwl_core_h__
#define __iwl_core_h__
#define IWLWIFI_VERSION "1.2.26k"
#define DRV_COPYRIGHT "Copyright(c) 2003-2008 Intel Corporation"
#define IWL_PCI_DEVICE(dev, subdev, cfg) \
.vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = (subdev), \
.driver_data = (kernel_ulong_t)&(cfg)
#define IWL_SKU_G 0x1
#define IWL_SKU_A 0x2
#define IWL_SKU_N 0x8
struct iwl_cfg {
const char *name;
const char *fw_name;
unsigned int sku;
};
#endif /* __iwl_core_h__ */

View file

@ -0,0 +1,259 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
* Contact Information:
* James P. Ketrenos <ipw2100-admin@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
/*=== CSR (control and status registers) ===*/
#define CSR_BASE (0x000)
#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */
#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */
#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */
#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */
#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/
#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */
#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
#define CSR_GP_CNTRL (CSR_BASE+0x024)
/*
* Hardware revision info
* Bit fields:
* 31-8: Reserved
* 7-4: Type of device: 0x0 = 4965, 0xd = 3945
* 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D
* 1-0: "Dash" value, as in A-1, etc.
*
* NOTE: Revision step affects calculation of CCK txpower for 4965.
*/
#define CSR_HW_REV (CSR_BASE+0x028)
/* EEPROM reads */
#define CSR_EEPROM_REG (CSR_BASE+0x02c)
#define CSR_EEPROM_GP (CSR_BASE+0x030)
#define CSR_GP_UCODE (CSR_BASE+0x044)
#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054)
#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058)
#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
/* Analog phase-lock-loop configuration (3945 only)
* Set bit 24. */
#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c)
/*
* Indicates hardware rev, to determine CCK backoff for txpower calculation.
* Bit fields:
* 3-2: 0 = A, 1 = B, 2 = C, 3 = D step
*/
#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C)
/* Bits for CSR_HW_IF_CONFIG_REG */
#define CSR49_HW_IF_CONFIG_REG_BIT_4965_R (0x00000010)
#define CSR49_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x00000C00)
#define CSR49_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100)
#define CSR49_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200)
#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MB (0x00000100)
#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MM (0x00000200)
#define CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC (0x00000400)
#define CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE (0x00000800)
#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000)
#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000)
#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
* acknowledged (reset) by host writing "1" to flagged bits. */
#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */
#define CSR_INT_BIT_DNLD (1 << 28) /* uCode Download */
#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */
#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */
#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */
#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */
#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */
#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses, 3945 */
#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */
#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */
#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \
CSR_INT_BIT_HW_ERR | \
CSR_INT_BIT_FH_TX | \
CSR_INT_BIT_SW_ERR | \
CSR_INT_BIT_RF_KILL | \
CSR_INT_BIT_SW_RX | \
CSR_INT_BIT_WAKEUP | \
CSR_INT_BIT_ALIVE)
/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */
#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */
#define CSR39_FH_INT_BIT_RX_CHNL2 (1 << 18) /* Rx channel 2 (3945 only) */
#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */
#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */
#define CSR39_FH_INT_BIT_TX_CHNL6 (1 << 6) /* Tx channel 6 (3945 only) */
#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */
#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */
#define CSR39_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \
CSR39_FH_INT_BIT_RX_CHNL2 | \
CSR_FH_INT_BIT_RX_CHNL1 | \
CSR_FH_INT_BIT_RX_CHNL0)
#define CSR39_FH_INT_TX_MASK (CSR39_FH_INT_BIT_TX_CHNL6 | \
CSR_FH_INT_BIT_TX_CHNL1 | \
CSR_FH_INT_BIT_TX_CHNL0)
#define CSR49_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \
CSR_FH_INT_BIT_RX_CHNL1 | \
CSR_FH_INT_BIT_RX_CHNL0)
#define CSR49_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \
CSR_FH_INT_BIT_TX_CHNL0)
/* RESET */
#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001)
#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002)
#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080)
#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100)
#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200)
/* GP (general purpose) CONTROL */
#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001)
#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004)
#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008)
#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010)
#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001)
#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000)
#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000)
#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000)
/* EEPROM REG */
#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001)
#define CSR_EEPROM_REG_BIT_CMD (0x00000002)
/* EEPROM GP */
#define CSR_EEPROM_GP_VALID_MSK (0x00000006)
#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000)
#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180)
/* UCODE DRV GP */
#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001)
#define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008)
/* GPIO */
#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200)
#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000)
#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC CSR_GPIO_IN_BIT_AUX_POWER
/* GI Chicken Bits */
#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000)
#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000)
/*=== HBUS (Host-side Bus) ===*/
#define HBUS_BASE (0x400)
/*
* Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
* structures, error log, event log, verifying uCode load).
* First write to address register, then read from or write to data register
* to complete the job. Once the address register is set up, accesses to
* data registers auto-increment the address by one dword.
* Bit usage for address registers (read or write):
* 0-31: memory address within device
*/
#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c)
#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010)
#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018)
#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c)
/*
* Registers for accessing device's internal peripheral registers
* (e.g. SCD, BSM, etc.). First write to address register,
* then read from or write to data register to complete the job.
* Bit usage for address registers (read or write):
* 0-15: register address (offset) within device
* 24-25: (# bytes - 1) to read or write (e.g. 3 for dword)
*/
#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044)
#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048)
#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c)
#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050)
/*
* Per-Tx-queue write pointer (index, really!) (3945 and 4965).
* Indicates index to next TFD that driver will fill (1 past latest filled).
* Bit usage:
* 0-7: queue write index
* 11-8: queue selector
*/
#define HBUS_TARG_WRPTR (HBUS_BASE+0x060)
#define HBUS_TARG_MBX_C (HBUS_BASE+0x030)
#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004)

View file

@ -0,0 +1,205 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
* Contact Information:
* Tomas Winkler <tomas.winkler@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <net/mac80211.h>
#include "iwl-4965-commands.h"
#include "iwl-4965.h"
#include "iwl-core.h"
#include "iwl-4965-debug.h"
#include "iwl-eeprom.h"
#include "iwl-4965-io.h"
/******************************************************************************
*
* EEPROM related functions
*
******************************************************************************/
int iwlcore_eeprom_verify_signature(struct iwl4965_priv *priv)
{
u32 gp = iwl4965_read32(priv, CSR_EEPROM_GP);
if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) {
IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp);
return -ENOENT;
}
return 0;
}
EXPORT_SYMBOL(iwlcore_eeprom_verify_signature);
/*
* The device's EEPROM semaphore prevents conflicts between driver and uCode
* when accessing the EEPROM; each access is a series of pulses to/from the
* EEPROM chip, not a single event, so even reads could conflict if they
* weren't arbitrated by the semaphore.
*/
int iwlcore_eeprom_acquire_semaphore(struct iwl4965_priv *priv)
{
u16 count;
int ret;
for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) {
/* Request semaphore */
iwl4965_set_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
/* See if we got it */
ret = iwl4965_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
EEPROM_SEM_TIMEOUT);
if (ret >= 0) {
IWL_DEBUG_IO("Acquired semaphore after %d tries.\n",
count+1);
return ret;
}
}
return ret;
}
EXPORT_SYMBOL(iwlcore_eeprom_acquire_semaphore);
void iwlcore_eeprom_release_semaphore(struct iwl4965_priv *priv)
{
iwl4965_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
}
EXPORT_SYMBOL(iwlcore_eeprom_release_semaphore);
/**
* iwl_eeprom_init - read EEPROM contents
*
* Load the EEPROM contents from adapter into priv->eeprom
*
* NOTE: This routine uses the non-debug IO access functions.
*/
int iwl_eeprom_init(struct iwl4965_priv *priv)
{
u16 *e = (u16 *)&priv->eeprom;
u32 gp = iwl4965_read32(priv, CSR_EEPROM_GP);
u32 r;
int sz = sizeof(priv->eeprom);
int ret;
int i;
u16 addr;
/* The EEPROM structure has several padding buffers within it
* and when adding new EEPROM maps is subject to programmer errors
* which may be very difficult to identify without explicitly
* checking the resulting size of the eeprom map. */
BUILD_BUG_ON(sizeof(priv->eeprom) != IWL_EEPROM_IMAGE_SIZE);
if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) {
IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp);
return -ENOENT;
}
/* Make sure driver (instead of uCode) is allowed to read EEPROM */
ret = priv->cfg->ops->lib->eeprom_ops.acquire_semaphore(priv);
if (ret < 0) {
IWL_ERROR("Failed to acquire EEPROM semaphore.\n");
return -ENOENT;
}
/* eeprom is an array of 16bit values */
for (addr = 0; addr < sz; addr += sizeof(u16)) {
_iwl4965_write32(priv, CSR_EEPROM_REG, addr << 1);
_iwl4965_clear_bit(priv, CSR_EEPROM_REG, CSR_EEPROM_REG_BIT_CMD);
for (i = 0; i < IWL_EEPROM_ACCESS_TIMEOUT;
i += IWL_EEPROM_ACCESS_DELAY) {
r = _iwl4965_read_direct32(priv, CSR_EEPROM_REG);
if (r & CSR_EEPROM_REG_READ_VALID_MSK)
break;
udelay(IWL_EEPROM_ACCESS_DELAY);
}
if (!(r & CSR_EEPROM_REG_READ_VALID_MSK)) {
IWL_ERROR("Time out reading EEPROM[%d]", addr);
ret = -ETIMEDOUT;
goto done;
}
e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
}
ret = 0;
done:
priv->cfg->ops->lib->eeprom_ops.release_semaphore(priv);
return ret;
}
EXPORT_SYMBOL(iwl_eeprom_init);
void iwl_eeprom_get_mac(const struct iwl4965_priv *priv, u8 *mac)
{
memcpy(mac, priv->eeprom.mac_address, 6);
}
EXPORT_SYMBOL(iwl_eeprom_get_mac);

View file

@ -0,0 +1,399 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
* Contact Information:
* Tomas Winkler <tomas.winkler@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef __iwl_eeprom_h__
#define __iwl_eeprom_h__
struct iwl4965_priv;
/*
* EEPROM access time values:
*
* Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG,
* then clearing (with subsequent read/modify/write) CSR_EEPROM_REG bit
* CSR_EEPROM_REG_BIT_CMD (0x2).
* Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1).
* When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec.
* Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG.
*/
#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */
#define IWL_EEPROM_ACCESS_DELAY 10 /* uSec */
#define IWL_EEPROM_SEM_TIMEOUT 10 /* milliseconds */
#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */
/*
* Regulatory channel usage flags in EEPROM struct iwl4965_eeprom_channel.flags.
*
* IBSS and/or AP operation is allowed *only* on those channels with
* (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because
* RADAR detection is not supported by the 4965 driver, but is a
* requirement for establishing a new network for legal operation on channels
* requiring RADAR detection or restricting ACTIVE scanning.
*
* NOTE: "WIDE" flag does not indicate anything about "FAT" 40 MHz channels.
* It only indicates that 20 MHz channel use is supported; FAT channel
* usage is indicated by a separate set of regulatory flags for each
* FAT channel pair.
*
* NOTE: Using a channel inappropriately will result in a uCode error!
*/
#define IWL_NUM_TX_CALIB_GROUPS 5
enum {
EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */
EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */
/* Bit 2 Reserved */
EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */
EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */
EEPROM_CHANNEL_WIDE = (1 << 5), /* 20 MHz channel okay */
EEPROM_CHANNEL_NARROW = (1 << 6), /* 10 MHz channel (not used) */
EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */
};
/* SKU Capabilities */
#define EEPROM_SKU_CAP_SW_RF_KILL_ENABLE (1 << 0)
#define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1)
/* *regulatory* channel data format in eeprom, one for each channel.
* There are separate entries for FAT (40 MHz) vs. normal (20 MHz) channels. */
struct iwl4965_eeprom_channel {
u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */
s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */
} __attribute__ ((packed));
/* 4965 has two radio transmitters (and 3 radio receivers) */
#define EEPROM_TX_POWER_TX_CHAINS (2)
/* 4965 has room for up to 8 sets of txpower calibration data */
#define EEPROM_TX_POWER_BANDS (8)
/* 4965 factory calibration measures txpower gain settings for
* each of 3 target output levels */
#define EEPROM_TX_POWER_MEASUREMENTS (3)
#define EEPROM_4965_TX_POWER_VERSION (2)
/* 4965 driver does not work with txpower calibration version < 5.
* Look for this in calib_version member of struct iwl4965_eeprom. */
#define EEPROM_TX_POWER_VERSION_NEW (5)
/*
* 4965 factory calibration data for one txpower level, on one channel,
* measured on one of the 2 tx chains (radio transmitter and associated
* antenna). EEPROM contains:
*
* 1) Temperature (degrees Celsius) of device when measurement was made.
*
* 2) Gain table index used to achieve the target measurement power.
* This refers to the "well-known" gain tables (see iwl-4965-hw.h).
*
* 3) Actual measured output power, in half-dBm ("34" = 17 dBm).
*
* 4) RF power amplifier detector level measurement (not used).
*/
struct iwl4965_eeprom_calib_measure {
u8 temperature; /* Device temperature (Celsius) */
u8 gain_idx; /* Index into gain table */
u8 actual_pow; /* Measured RF output power, half-dBm */
s8 pa_det; /* Power amp detector level (not used) */
} __attribute__ ((packed));
/*
* 4965 measurement set for one channel. EEPROM contains:
*
* 1) Channel number measured
*
* 2) Measurements for each of 3 power levels for each of 2 radio transmitters
* (a.k.a. "tx chains") (6 measurements altogether)
*/
struct iwl4965_eeprom_calib_ch_info {
u8 ch_num;
struct iwl4965_eeprom_calib_measure
measurements[EEPROM_TX_POWER_TX_CHAINS]
[EEPROM_TX_POWER_MEASUREMENTS];
} __attribute__ ((packed));
/*
* 4965 txpower subband info.
*
* For each frequency subband, EEPROM contains the following:
*
* 1) First and last channels within range of the subband. "0" values
* indicate that this sample set is not being used.
*
* 2) Sample measurement sets for 2 channels close to the range endpoints.
*/
struct iwl4965_eeprom_calib_subband_info {
u8 ch_from; /* channel number of lowest channel in subband */
u8 ch_to; /* channel number of highest channel in subband */
struct iwl4965_eeprom_calib_ch_info ch1;
struct iwl4965_eeprom_calib_ch_info ch2;
} __attribute__ ((packed));
/*
* 4965 txpower calibration info. EEPROM contains:
*
* 1) Factory-measured saturation power levels (maximum levels at which
* tx power amplifier can output a signal without too much distortion).
* There is one level for 2.4 GHz band and one for 5 GHz band. These
* values apply to all channels within each of the bands.
*
* 2) Factory-measured power supply voltage level. This is assumed to be
* constant (i.e. same value applies to all channels/bands) while the
* factory measurements are being made.
*
* 3) Up to 8 sets of factory-measured txpower calibration values.
* These are for different frequency ranges, since txpower gain
* characteristics of the analog radio circuitry vary with frequency.
*
* Not all sets need to be filled with data;
* struct iwl4965_eeprom_calib_subband_info contains range of channels
* (0 if unused) for each set of data.
*/
struct iwl4965_eeprom_calib_info {
u8 saturation_power24; /* half-dBm (e.g. "34" = 17 dBm) */
u8 saturation_power52; /* half-dBm */
s16 voltage; /* signed */
struct iwl4965_eeprom_calib_subband_info
band_info[EEPROM_TX_POWER_BANDS];
} __attribute__ ((packed));
/*
* 4965 EEPROM map
*/
struct iwl4965_eeprom {
u8 reserved0[16];
#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */
u16 device_id; /* abs.ofs: 16 */
u8 reserved1[2];
#define EEPROM_PMC (2*0x0A) /* 2 bytes */
u16 pmc; /* abs.ofs: 20 */
u8 reserved2[20];
#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */
u8 mac_address[6]; /* abs.ofs: 42 */
u8 reserved3[58];
#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */
u16 board_revision; /* abs.ofs: 106 */
u8 reserved4[11];
#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */
u8 board_pba_number[9]; /* abs.ofs: 119 */
u8 reserved5[8];
#define EEPROM_VERSION (2*0x44) /* 2 bytes */
u16 version; /* abs.ofs: 136 */
#define EEPROM_SKU_CAP (2*0x45) /* 1 bytes */
u8 sku_cap; /* abs.ofs: 138 */
#define EEPROM_LEDS_MODE (2*0x45+1) /* 1 bytes */
u8 leds_mode; /* abs.ofs: 139 */
#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */
u16 oem_mode;
#define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */
u16 wowlan_mode; /* abs.ofs: 142 */
#define EEPROM_LEDS_TIME_INTERVAL (2*0x48) /* 2 bytes */
u16 leds_time_interval; /* abs.ofs: 144 */
#define EEPROM_LEDS_OFF_TIME (2*0x49) /* 1 bytes */
u8 leds_off_time; /* abs.ofs: 146 */
#define EEPROM_LEDS_ON_TIME (2*0x49+1) /* 1 bytes */
u8 leds_on_time; /* abs.ofs: 147 */
#define EEPROM_ALMGOR_M_VERSION (2*0x4A) /* 1 bytes */
u8 almgor_m_version; /* abs.ofs: 148 */
#define EEPROM_ANTENNA_SWITCH_TYPE (2*0x4A+1) /* 1 bytes */
u8 antenna_switch_type; /* abs.ofs: 149 */
u8 reserved6[8];
#define EEPROM_4965_BOARD_REVISION (2*0x4F) /* 2 bytes */
u16 board_revision_4965; /* abs.ofs: 158 */
u8 reserved7[13];
#define EEPROM_4965_BOARD_PBA (2*0x56+1) /* 9 bytes */
u8 board_pba_number_4965[9]; /* abs.ofs: 173 */
u8 reserved8[10];
#define EEPROM_REGULATORY_SKU_ID (2*0x60) /* 4 bytes */
u8 sku_id[4]; /* abs.ofs: 192 */
/*
* Per-channel regulatory data.
*
* Each channel that *might* be supported by 3945 or 4965 has a fixed location
* in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory
* txpower (MSB).
*
* Entries immediately below are for 20 MHz channel width. FAT (40 MHz)
* channels (only for 4965, not supported by 3945) appear later in the EEPROM.
*
* 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
*/
#define EEPROM_REGULATORY_BAND_1 (2*0x62) /* 2 bytes */
u16 band_1_count; /* abs.ofs: 196 */
#define EEPROM_REGULATORY_BAND_1_CHANNELS (2*0x63) /* 28 bytes */
struct iwl4965_eeprom_channel band_1_channels[14]; /* abs.ofs: 196 */
/*
* 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196,
* 5.0 GHz channels 7, 8, 11, 12, 16
* (4915-5080MHz) (none of these is ever supported)
*/
#define EEPROM_REGULATORY_BAND_2 (2*0x71) /* 2 bytes */
u16 band_2_count; /* abs.ofs: 226 */
#define EEPROM_REGULATORY_BAND_2_CHANNELS (2*0x72) /* 26 bytes */
struct iwl4965_eeprom_channel band_2_channels[13]; /* abs.ofs: 228 */
/*
* 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
* (5170-5320MHz)
*/
#define EEPROM_REGULATORY_BAND_3 (2*0x7F) /* 2 bytes */
u16 band_3_count; /* abs.ofs: 254 */
#define EEPROM_REGULATORY_BAND_3_CHANNELS (2*0x80) /* 24 bytes */
struct iwl4965_eeprom_channel band_3_channels[12]; /* abs.ofs: 256 */
/*
* 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
* (5500-5700MHz)
*/
#define EEPROM_REGULATORY_BAND_4 (2*0x8C) /* 2 bytes */
u16 band_4_count; /* abs.ofs: 280 */
#define EEPROM_REGULATORY_BAND_4_CHANNELS (2*0x8D) /* 22 bytes */
struct iwl4965_eeprom_channel band_4_channels[11]; /* abs.ofs: 282 */
/*
* 5.7 GHz channels 145, 149, 153, 157, 161, 165
* (5725-5825MHz)
*/
#define EEPROM_REGULATORY_BAND_5 (2*0x98) /* 2 bytes */
u16 band_5_count; /* abs.ofs: 304 */
#define EEPROM_REGULATORY_BAND_5_CHANNELS (2*0x99) /* 12 bytes */
struct iwl4965_eeprom_channel band_5_channels[6]; /* abs.ofs: 306 */
u8 reserved10[2];
/*
* 2.4 GHz FAT channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11)
*
* The channel listed is the center of the lower 20 MHz half of the channel.
* The overall center frequency is actually 2 channels (10 MHz) above that,
* and the upper half of each FAT channel is centered 4 channels (20 MHz) away
* from the lower half; e.g. the upper half of FAT channel 1 is channel 5,
* and the overall FAT channel width centers on channel 3.
*
* NOTE: The RXON command uses 20 MHz channel numbers to specify the
* control channel to which to tune. RXON also specifies whether the
* control channel is the upper or lower half of a FAT channel.
*
* NOTE: 4965 does not support FAT channels on 2.4 GHz.
*/
#define EEPROM_REGULATORY_BAND_24_FAT_CHANNELS (2*0xA0) /* 14 bytes */
struct iwl4965_eeprom_channel band_24_channels[7]; /* abs.ofs: 320 */
u8 reserved11[2];
/*
* 5.2 GHz FAT channels 36 (40), 44 (48), 52 (56), 60 (64),
* 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161)
*/
#define EEPROM_REGULATORY_BAND_52_FAT_CHANNELS (2*0xA8) /* 22 bytes */
struct iwl4965_eeprom_channel band_52_channels[11]; /* abs.ofs: 336 */
u8 reserved12[6];
/*
* 4965 driver requires txpower calibration format version 5 or greater.
* Driver does not work with txpower calibration version < 5.
* This value is simply a 16-bit number, no major/minor versions here.
*/
#define EEPROM_CALIB_VERSION_OFFSET (2*0xB6) /* 2 bytes */
u16 calib_version; /* abs.ofs: 364 */
u8 reserved13[2];
u8 reserved14[96]; /* abs.ofs: 368 */
/*
* 4965 Txpower calibration data.
*/
#define EEPROM_IWL_CALIB_TXPOWER_OFFSET (2*0xE8) /* 48 bytes */
struct iwl4965_eeprom_calib_info calib_info; /* abs.ofs: 464 */
u8 reserved16[140]; /* fill out to full 1024 byte block */
} __attribute__ ((packed));
#define IWL_EEPROM_IMAGE_SIZE 1024
/* End of EEPROM */
struct iwl_eeprom_ops {
int (*verify_signature) (struct iwl4965_priv *priv);
int (*acquire_semaphore) (struct iwl4965_priv *priv);
void (*release_semaphore) (struct iwl4965_priv *priv);
};
void iwl_eeprom_get_mac(const struct iwl4965_priv *priv, u8 *mac);
int iwl_eeprom_init(struct iwl4965_priv *priv);
int iwlcore_eeprom_verify_signature(struct iwl4965_priv *priv);
int iwlcore_eeprom_acquire_semaphore(struct iwl4965_priv *priv);
void iwlcore_eeprom_release_semaphore(struct iwl4965_priv *priv);
#endif /* __iwl_eeprom_h__ */

View file

@ -254,6 +254,26 @@ static inline u8 iwl_get_dma_hi_address(dma_addr_t addr)
return sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0;
}
/**
* iwl_queue_inc_wrap - increment queue index, wrap back to beginning
* @index -- current index
* @n_bd -- total number of entries in queue (must be power of 2)
*/
static inline int iwl_queue_inc_wrap(int index, int n_bd)
{
return ++index & (n_bd - 1);
}
/**
* iwl_queue_dec_wrap - decrement queue index, wrap back to end
* @index -- current index
* @n_bd -- total number of entries in queue (must be power of 2)
*/
static inline int iwl_queue_dec_wrap(int index, int n_bd)
{
return --index & (n_bd - 1);
}
/* TODO: Move fw_desc functions to iwl-pci.ko */
static inline void iwl_free_fw_desc(struct pci_dev *pci_dev,
struct fw_desc *desc)

View file

@ -46,6 +46,7 @@
#include <asm/div64.h>
#include "iwl-3945-core.h"
#include "iwl-3945.h"
#include "iwl-helpers.h"
@ -95,11 +96,6 @@ int iwl3945_param_queues_num = IWL_MAX_NUM_QUEUES; /* def: 8 Tx queues */
#define DRV_COPYRIGHT "Copyright(c) 2003-2007 Intel Corporation"
#define DRV_VERSION IWLWIFI_VERSION
/* Change firmware file name, using "-" and incrementing number,
* *only* when uCode interface or architecture changes so that it
* is not compatible with earlier drivers.
* This number will also appear in << 8 position of 1st dword of uCode file */
#define IWL3945_UCODE_API "-1"
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
@ -162,17 +158,6 @@ static const char *iwl3945_escape_essid(const char *essid, u8 essid_len)
return escaped;
}
static void iwl3945_print_hex_dump(int level, void *p, u32 len)
{
#ifdef CONFIG_IWL3945_DEBUG
if (!(iwl3945_debug_level & level))
return;
print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
p, len, 1);
#endif
}
/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
* DMA services
*
@ -198,7 +183,7 @@ static void iwl3945_print_hex_dump(int level, void *p, u32 len)
* (#0-3) for data tx via EDCA. An additional 2 HCCA queues are unused.
***************************************************/
static int iwl3945_queue_space(const struct iwl3945_queue *q)
int iwl3945_queue_space(const struct iwl3945_queue *q)
{
int s = q->read_ptr - q->write_ptr;
@ -214,33 +199,14 @@ static int iwl3945_queue_space(const struct iwl3945_queue *q)
return s;
}
/**
* iwl3945_queue_inc_wrap - increment queue index, wrap back to beginning
* @index -- current index
* @n_bd -- total number of entries in queue (must be power of 2)
*/
static inline int iwl3945_queue_inc_wrap(int index, int n_bd)
{
return ++index & (n_bd - 1);
}
/**
* iwl3945_queue_dec_wrap - increment queue index, wrap back to end
* @index -- current index
* @n_bd -- total number of entries in queue (must be power of 2)
*/
static inline int iwl3945_queue_dec_wrap(int index, int n_bd)
{
return --index & (n_bd - 1);
}
static inline int x2_queue_used(const struct iwl3945_queue *q, int i)
int iwl3945_x2_queue_used(const struct iwl3945_queue *q, int i)
{
return q->write_ptr > q->read_ptr ?
(i >= q->read_ptr && i < q->write_ptr) :
!(i < q->read_ptr && i >= q->write_ptr);
}
static inline u8 get_cmd_index(struct iwl3945_queue *q, u32 index, int is_huge)
{
/* This is for scan command, the big buffer at end of command array */
@ -261,8 +227,8 @@ static int iwl3945_queue_init(struct iwl3945_priv *priv, struct iwl3945_queue *q
q->n_window = slots_num;
q->id = id;
/* count must be power-of-two size, otherwise iwl3945_queue_inc_wrap
* and iwl3945_queue_dec_wrap are broken. */
/* count must be power-of-two size, otherwise iwl_queue_inc_wrap
* and iwl_queue_dec_wrap are broken. */
BUG_ON(!is_power_of_2(count));
/* slots_num must be power-of-two size, otherwise
@ -362,7 +328,7 @@ int iwl3945_tx_queue_init(struct iwl3945_priv *priv,
txq->need_update = 0;
/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
* iwl3945_queue_inc_wrap and iwl3945_queue_dec_wrap are broken. */
* iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
/* Initialize queue high/low-water, head/tail indexes */
@ -393,7 +359,7 @@ void iwl3945_tx_queue_free(struct iwl3945_priv *priv, struct iwl3945_tx_queue *t
/* first, empty all BD's */
for (; q->write_ptr != q->read_ptr;
q->read_ptr = iwl3945_queue_inc_wrap(q->read_ptr, q->n_bd))
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd))
iwl3945_hw_txq_free_tfd(priv, txq);
len = sizeof(struct iwl3945_cmd) * q->n_window;
@ -732,7 +698,7 @@ static int iwl3945_enqueue_hcmd(struct iwl3945_priv *priv, struct iwl3945_host_c
txq->need_update = 1;
/* Increment and update queue's write index */
q->write_ptr = iwl3945_queue_inc_wrap(q->write_ptr, q->n_bd);
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
ret = iwl3945_tx_queue_update_write_ptr(priv, txq);
spin_unlock_irqrestore(&priv->hcmd_lock, flags);
@ -1630,151 +1596,6 @@ int iwl3945_eeprom_init(struct iwl3945_priv *priv)
return 0;
}
/******************************************************************************
*
* Misc. internal state and helper functions
*
******************************************************************************/
#ifdef CONFIG_IWL3945_DEBUG
/**
* iwl3945_report_frame - dump frame to syslog during debug sessions
*
* You may hack this function to show different aspects of received frames,
* including selective frame dumps.
* group100 parameter selects whether to show 1 out of 100 good frames.
*/
void iwl3945_report_frame(struct iwl3945_priv *priv,
struct iwl3945_rx_packet *pkt,
struct ieee80211_hdr *header, int group100)
{
u32 to_us;
u32 print_summary = 0;
u32 print_dump = 0; /* set to 1 to dump all frames' contents */
u32 hundred = 0;
u32 dataframe = 0;
u16 fc;
u16 seq_ctl;
u16 channel;
u16 phy_flags;
int rate_sym;
u16 length;
u16 status;
u16 bcn_tmr;
u32 tsf_low;
u64 tsf;
u8 rssi;
u8 agc;
u16 sig_avg;
u16 noise_diff;
struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
u8 *data = IWL_RX_DATA(pkt);
/* MAC header */
fc = le16_to_cpu(header->frame_control);
seq_ctl = le16_to_cpu(header->seq_ctrl);
/* metadata */
channel = le16_to_cpu(rx_hdr->channel);
phy_flags = le16_to_cpu(rx_hdr->phy_flags);
rate_sym = rx_hdr->rate;
length = le16_to_cpu(rx_hdr->len);
/* end-of-frame status and timestamp */
status = le32_to_cpu(rx_end->status);
bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
tsf = le64_to_cpu(rx_end->timestamp);
/* signal statistics */
rssi = rx_stats->rssi;
agc = rx_stats->agc;
sig_avg = le16_to_cpu(rx_stats->sig_avg);
noise_diff = le16_to_cpu(rx_stats->noise_diff);
to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
/* if data frame is to us and all is good,
* (optionally) print summary for only 1 out of every 100 */
if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
dataframe = 1;
if (!group100)
print_summary = 1; /* print each frame */
else if (priv->framecnt_to_us < 100) {
priv->framecnt_to_us++;
print_summary = 0;
} else {
priv->framecnt_to_us = 0;
print_summary = 1;
hundred = 1;
}
} else {
/* print summary for all other frames */
print_summary = 1;
}
if (print_summary) {
char *title;
u32 rate;
if (hundred)
title = "100Frames";
else if (fc & IEEE80211_FCTL_RETRY)
title = "Retry";
else if (ieee80211_is_assoc_response(fc))
title = "AscRsp";
else if (ieee80211_is_reassoc_response(fc))
title = "RasRsp";
else if (ieee80211_is_probe_response(fc)) {
title = "PrbRsp";
print_dump = 1; /* dump frame contents */
} else if (ieee80211_is_beacon(fc)) {
title = "Beacon";
print_dump = 1; /* dump frame contents */
} else if (ieee80211_is_atim(fc))
title = "ATIM";
else if (ieee80211_is_auth(fc))
title = "Auth";
else if (ieee80211_is_deauth(fc))
title = "DeAuth";
else if (ieee80211_is_disassoc(fc))
title = "DisAssoc";
else
title = "Frame";
rate = iwl3945_rate_index_from_plcp(rate_sym);
if (rate == -1)
rate = 0;
else
rate = iwl3945_rates[rate].ieee / 2;
/* print frame summary.
* MAC addresses show just the last byte (for brevity),
* but you can hack it to show more, if you'd like to. */
if (dataframe)
IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
"len=%u, rssi=%d, chnl=%d, rate=%u, \n",
title, fc, header->addr1[5],
length, rssi, channel, rate);
else {
/* src/dst addresses assume managed mode */
IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
"src=0x%02x, rssi=%u, tim=%lu usec, "
"phy=0x%02x, chnl=%d\n",
title, fc, header->addr1[5],
header->addr3[5], rssi,
tsf_low - priv->scan_start_tsf,
phy_flags, channel);
}
}
if (print_dump)
iwl3945_print_hex_dump(IWL_DL_RX, data, length);
}
#endif
static void iwl3945_unset_hw_setting(struct iwl3945_priv *priv)
{
if (priv->hw_setting.shared_virt)
@ -2242,34 +2063,6 @@ int iwl3945_is_network_packet(struct iwl3945_priv *priv, struct ieee80211_hdr *h
return 1;
}
#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
static const char *iwl3945_get_tx_fail_reason(u32 status)
{
switch (status & TX_STATUS_MSK) {
case TX_STATUS_SUCCESS:
return "SUCCESS";
TX_STATUS_ENTRY(SHORT_LIMIT);
TX_STATUS_ENTRY(LONG_LIMIT);
TX_STATUS_ENTRY(FIFO_UNDERRUN);
TX_STATUS_ENTRY(MGMNT_ABORT);
TX_STATUS_ENTRY(NEXT_FRAG);
TX_STATUS_ENTRY(LIFE_EXPIRE);
TX_STATUS_ENTRY(DEST_PS);
TX_STATUS_ENTRY(ABORTED);
TX_STATUS_ENTRY(BT_RETRY);
TX_STATUS_ENTRY(STA_INVALID);
TX_STATUS_ENTRY(FRAG_DROPPED);
TX_STATUS_ENTRY(TID_DISABLE);
TX_STATUS_ENTRY(FRAME_FLUSHED);
TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL);
TX_STATUS_ENTRY(TX_LOCKED);
TX_STATUS_ENTRY(NO_BEACON_ON_RADAR);
}
return "UNKNOWN";
}
/**
* iwl3945_scan_cancel - Cancel any currently executing HW scan
*
@ -2957,7 +2750,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv,
ieee80211_get_hdrlen(fc));
/* Tell device the write index *just past* this latest filled TFD */
q->write_ptr = iwl3945_queue_inc_wrap(q->write_ptr, q->n_bd);
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
rc = iwl3945_tx_queue_update_write_ptr(priv, txq);
spin_unlock_irqrestore(&priv->lock, flags);
@ -3317,125 +3110,6 @@ static int iwl3945_get_measurement(struct iwl3945_priv *priv,
}
#endif
static void iwl3945_txstatus_to_ieee(struct iwl3945_priv *priv,
struct iwl3945_tx_info *tx_sta)
{
tx_sta->status.ack_signal = 0;
tx_sta->status.excessive_retries = 0;
tx_sta->status.queue_length = 0;
tx_sta->status.queue_number = 0;
if (in_interrupt())
ieee80211_tx_status_irqsafe(priv->hw,
tx_sta->skb[0], &(tx_sta->status));
else
ieee80211_tx_status(priv->hw,
tx_sta->skb[0], &(tx_sta->status));
tx_sta->skb[0] = NULL;
}
/**
* iwl3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd
*
* When FW advances 'R' index, all entries between old and new 'R' index
* need to be reclaimed. As result, some free space forms. If there is
* enough free space (> low mark), wake the stack that feeds us.
*/
static int iwl3945_tx_queue_reclaim(struct iwl3945_priv *priv, int txq_id, int index)
{
struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
struct iwl3945_queue *q = &txq->q;
int nfreed = 0;
if ((index >= q->n_bd) || (x2_queue_used(q, index) == 0)) {
IWL_ERROR("Read index for DMA queue txq id (%d), index %d, "
"is out of range [0-%d] %d %d.\n", txq_id,
index, q->n_bd, q->write_ptr, q->read_ptr);
return 0;
}
for (index = iwl3945_queue_inc_wrap(index, q->n_bd);
q->read_ptr != index;
q->read_ptr = iwl3945_queue_inc_wrap(q->read_ptr, q->n_bd)) {
if (txq_id != IWL_CMD_QUEUE_NUM) {
iwl3945_txstatus_to_ieee(priv,
&(txq->txb[txq->q.read_ptr]));
iwl3945_hw_txq_free_tfd(priv, txq);
} else if (nfreed > 1) {
IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index,
q->write_ptr, q->read_ptr);
queue_work(priv->workqueue, &priv->restart);
}
nfreed++;
}
if (iwl3945_queue_space(q) > q->low_mark && (txq_id >= 0) &&
(txq_id != IWL_CMD_QUEUE_NUM) &&
priv->mac80211_registered)
ieee80211_wake_queue(priv->hw, txq_id);
return nfreed;
}
static int iwl3945_is_tx_success(u32 status)
{
return (status & 0xFF) == 0x1;
}
/******************************************************************************
*
* Generic RX handler implementations
*
******************************************************************************/
/**
* iwl3945_rx_reply_tx - Handle Tx response
*/
static void iwl3945_rx_reply_tx(struct iwl3945_priv *priv,
struct iwl3945_rx_mem_buffer *rxb)
{
struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
int txq_id = SEQ_TO_QUEUE(sequence);
int index = SEQ_TO_INDEX(sequence);
struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
struct ieee80211_tx_status *tx_status;
struct iwl3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
u32 status = le32_to_cpu(tx_resp->status);
if ((index >= txq->q.n_bd) || (x2_queue_used(&txq->q, index) == 0)) {
IWL_ERROR("Read index for DMA queue txq_id (%d) index %d "
"is out of range [0-%d] %d %d\n", txq_id,
index, txq->q.n_bd, txq->q.write_ptr,
txq->q.read_ptr);
return;
}
tx_status = &(txq->txb[txq->q.read_ptr].status);
tx_status->retry_count = tx_resp->failure_frame;
tx_status->queue_number = status;
tx_status->queue_length = tx_resp->bt_kill_count;
tx_status->queue_length |= tx_resp->failure_rts;
tx_status->flags =
iwl3945_is_tx_success(status) ? IEEE80211_TX_STATUS_ACK : 0;
IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n",
txq_id, iwl3945_get_tx_fail_reason(status), status,
tx_resp->rate, tx_resp->failure_frame);
IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
if (index != -1)
iwl3945_tx_queue_reclaim(priv, txq_id, index);
if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
IWL_ERROR("TODO: Implement Tx ABORT REQUIRED!!!\n");
}
static void iwl3945_rx_reply_alive(struct iwl3945_priv *priv,
struct iwl3945_rx_mem_buffer *rxb)
{
@ -3782,12 +3456,43 @@ static void iwl3945_setup_rx_handlers(struct iwl3945_priv *priv)
priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] =
iwl3945_rx_scan_complete_notif;
priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl3945_rx_card_state_notif;
priv->rx_handlers[REPLY_TX] = iwl3945_rx_reply_tx;
/* Set up hardware specific Rx handlers */
iwl3945_hw_rx_handler_setup(priv);
}
/**
* iwl3945_cmd_queue_reclaim - Reclaim CMD queue entries
* When FW advances 'R' index, all entries between old and new 'R' index
* need to be reclaimed.
*/
static void iwl3945_cmd_queue_reclaim(struct iwl3945_priv *priv,
int txq_id, int index)
{
struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
struct iwl3945_queue *q = &txq->q;
int nfreed = 0;
if ((index >= q->n_bd) || (iwl3945_x2_queue_used(q, index) == 0)) {
IWL_ERROR("Read index for DMA queue txq id (%d), index %d, "
"is out of range [0-%d] %d %d.\n", txq_id,
index, q->n_bd, q->write_ptr, q->read_ptr);
return;
}
for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index;
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
if (nfreed > 1) {
IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index,
q->write_ptr, q->read_ptr);
queue_work(priv->workqueue, &priv->restart);
break;
}
nfreed++;
}
}
/**
* iwl3945_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
* @rxb: Rx buffer to reclaim
@ -3807,12 +3512,6 @@ static void iwl3945_tx_cmd_complete(struct iwl3945_priv *priv,
int cmd_index;
struct iwl3945_cmd *cmd;
/* If a Tx command is being handled and it isn't in the actual
* command queue then there a command routing bug has been introduced
* in the queue management code. */
if (txq_id != IWL_CMD_QUEUE_NUM)
IWL_ERROR("Error wrong command queue %d command id 0x%X\n",
txq_id, pkt->hdr.cmd);
BUG_ON(txq_id != IWL_CMD_QUEUE_NUM);
cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge);
@ -3826,7 +3525,7 @@ static void iwl3945_tx_cmd_complete(struct iwl3945_priv *priv,
!cmd->meta.u.callback(priv, cmd, rxb->skb))
rxb->skb = NULL;
iwl3945_tx_queue_reclaim(priv, txq_id, index);
iwl3945_cmd_queue_reclaim(priv, txq_id, index);
if (!(cmd->meta.flags & CMD_ASYNC)) {
clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
@ -4506,8 +4205,7 @@ static void iwl3945_dump_nic_error_log(struct iwl3945_priv *priv)
if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
IWL_ERROR("Start IWL Error Log Dump:\n");
IWL_ERROR("Status: 0x%08lX, Config: %08X count: %d\n",
priv->status, priv->config, count);
IWL_ERROR("Status: 0x%08lX, count: %d\n", priv->status, count);
}
IWL_ERROR("Desc Time asrtPC blink2 "
@ -4727,9 +4425,9 @@ static void iwl3945_irq_tasklet(struct iwl3945_priv *priv)
* atomic, make sure that inta covers all the interrupts that
* we've discovered, even if FH interrupt came in just after
* reading CSR_INT. */
if (inta_fh & CSR_FH_INT_RX_MASK)
if (inta_fh & CSR39_FH_INT_RX_MASK)
inta |= CSR_INT_BIT_FH_RX;
if (inta_fh & CSR_FH_INT_TX_MASK)
if (inta_fh & CSR39_FH_INT_TX_MASK)
inta |= CSR_INT_BIT_FH_TX;
/* Now service all interrupt bits discovered above. */
@ -5119,11 +4817,12 @@ static int iwl3945_init_channel_map(struct iwl3945_priv *priv)
ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
ch_info->min_power = 0;
IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x"
IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x"
" %ddBm): Ad-Hoc %ssupported\n",
ch_info->channel,
is_channel_a_band(ch_info) ?
"5.2" : "2.4",
CHECK_AND_PRINT(VALID),
CHECK_AND_PRINT(IBSS),
CHECK_AND_PRINT(ACTIVE),
CHECK_AND_PRINT(RADAR),
@ -5333,7 +5032,7 @@ static void iwl3945_init_hw_rates(struct iwl3945_priv *priv,
static int iwl3945_init_geos(struct iwl3945_priv *priv)
{
struct iwl3945_channel_info *ch;
struct ieee80211_supported_band *band;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *channels;
struct ieee80211_channel *geo_ch;
struct ieee80211_rate *rates;
@ -5351,7 +5050,7 @@ static int iwl3945_init_geos(struct iwl3945_priv *priv)
if (!channels)
return -ENOMEM;
rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_MAX_RATES + 1)),
rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_RATE_COUNT + 1)),
GFP_KERNEL);
if (!rates) {
kfree(channels);
@ -5359,38 +5058,38 @@ static int iwl3945_init_geos(struct iwl3945_priv *priv)
}
/* 5.2GHz channels start after the 2.4GHz channels */
band = &priv->bands[IEEE80211_BAND_5GHZ];
band->channels = &channels[ARRAY_SIZE(iwl3945_eeprom_band_1)];
band->bitrates = &rates[4];
band->n_bitrates = 8; /* just OFDM */
sband = &priv->bands[IEEE80211_BAND_5GHZ];
sband->channels = &channels[ARRAY_SIZE(iwl3945_eeprom_band_1)];
/* just OFDM */
sband->bitrates = &rates[IWL_FIRST_OFDM_RATE];
sband->n_bitrates = IWL_RATE_COUNT - IWL_FIRST_OFDM_RATE;
band = &priv->bands[IEEE80211_BAND_2GHZ];
band->channels = channels;
band->bitrates = rates;
band->n_bitrates = 12; /* OFDM & CCK */
sband = &priv->bands[IEEE80211_BAND_2GHZ];
sband->channels = channels;
/* OFDM & CCK */
sband->bitrates = rates;
sband->n_bitrates = IWL_RATE_COUNT;
priv->ieee_channels = channels;
priv->ieee_rates = rates;
iwl3945_init_hw_rates(priv, rates);
for (i = 0, geo_ch = channels; i < priv->channel_count; i++) {
for (i = 0; i < priv->channel_count; i++) {
ch = &priv->channel_info[i];
if (!is_channel_valid(ch)) {
IWL_DEBUG_INFO("Channel %d [%sGHz] is restricted -- "
"skipping.\n",
ch->channel, is_channel_a_band(ch) ?
"5.2" : "2.4");
/* FIXME: might be removed if scan is OK*/
if (!is_channel_valid(ch))
continue;
}
if (is_channel_a_band(ch))
geo_ch = &priv->bands[IEEE80211_BAND_5GHZ].channels[priv->bands[IEEE80211_BAND_5GHZ].n_channels++];
sband = &priv->bands[IEEE80211_BAND_5GHZ];
else
geo_ch = &priv->bands[IEEE80211_BAND_2GHZ].channels[priv->bands[IEEE80211_BAND_2GHZ].n_channels++];
sband = &priv->bands[IEEE80211_BAND_2GHZ];
geo_ch->center_freq = ieee80211chan2mhz(ch->channel);
geo_ch = &sband->channels[sband->n_channels++];
geo_ch->center_freq = ieee80211_channel_to_frequency(ch->channel);
geo_ch->max_power = ch->max_power_avg;
geo_ch->max_antenna_gain = 0xff;
geo_ch->hw_value = ch->channel;
@ -5408,16 +5107,28 @@ static int iwl3945_init_geos(struct iwl3945_priv *priv)
if (ch->max_power_avg > priv->max_channel_txpower_limit)
priv->max_channel_txpower_limit =
ch->max_power_avg;
} else
} else {
geo_ch->flags |= IEEE80211_CHAN_DISABLED;
}
/* Save flags for reg domain usage */
geo_ch->orig_flags = geo_ch->flags;
IWL_DEBUG_INFO("Channel %d Freq=%d[%sGHz] %s flag=0%X\n",
ch->channel, geo_ch->center_freq,
is_channel_a_band(ch) ? "5.2" : "2.4",
geo_ch->flags & IEEE80211_CHAN_DISABLED ?
"restricted" : "valid",
geo_ch->flags);
}
if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && priv->is_abg) {
if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) &&
priv->cfg->sku & IWL_SKU_A) {
printk(KERN_INFO DRV_NAME
": Incorrectly detected BG card as ABG. Please send "
"your PCI ID 0x%04X:0x%04X to maintainer.\n",
priv->pci_dev->device, priv->pci_dev->subsystem_device);
priv->is_abg = 0;
priv->cfg->sku &= ~IWL_SKU_A;
}
printk(KERN_INFO DRV_NAME
@ -5764,7 +5475,7 @@ static int iwl3945_read_ucode(struct iwl3945_priv *priv)
int ret = 0;
const struct firmware *ucode_raw;
/* firmware file name contains uCode/driver compatibility version */
const char *name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode";
const char *name = priv->cfg->fw_name;
u8 *src;
size_t len;
u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
@ -7152,6 +6863,12 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
if (conf == NULL)
return -EIO;
if (priv->vif != vif) {
IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
mutex_unlock(&priv->mutex);
return 0;
}
/* XXX: this MUST use conf->mac_addr */
if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) &&
@ -7176,17 +6893,6 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) &&
!(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) {
*/
if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
IWL_DEBUG_MAC80211("leave - scanning\n");
mutex_unlock(&priv->mutex);
return 0;
}
if (priv->vif != vif) {
IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
mutex_unlock(&priv->mutex);
return 0;
}
if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
if (!conf->bssid) {
@ -7884,31 +7590,6 @@ static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR,
show_measurement, store_measurement);
#endif /* CONFIG_IWL3945_SPECTRUM_MEASUREMENT */
static ssize_t show_rate(struct device *d,
struct device_attribute *attr, char *buf)
{
struct iwl3945_priv *priv = dev_get_drvdata(d);
unsigned long flags;
int i;
spin_lock_irqsave(&priv->sta_lock, flags);
if (priv->iw_mode == IEEE80211_IF_TYPE_STA)
i = priv->stations[IWL_AP_ID].current_rate.s.rate;
else
i = priv->stations[IWL_STA_ID].current_rate.s.rate;
spin_unlock_irqrestore(&priv->sta_lock, flags);
i = iwl3945_rate_index_from_plcp(i);
if (i == -1)
return sprintf(buf, "0\n");
return sprintf(buf, "%d%s\n",
(iwl3945_rates[i].ieee >> 1),
(iwl3945_rates[i].ieee & 0x1) ? ".5" : "");
}
static DEVICE_ATTR(rate, S_IRUSR, show_rate, NULL);
static ssize_t store_retry_rate(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
@ -8199,7 +7880,6 @@ static struct attribute *iwl3945_sysfs_entries[] = {
&dev_attr_measurement.attr,
#endif
&dev_attr_power_level.attr,
&dev_attr_rate.attr,
&dev_attr_retry_rate.attr,
&dev_attr_rf_kill.attr,
&dev_attr_rs_window.attr,
@ -8238,9 +7918,9 @@ static struct ieee80211_ops iwl3945_hw_ops = {
static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int err = 0;
u32 pci_id;
struct iwl3945_priv *priv;
struct ieee80211_hw *hw;
struct iwl_3945_cfg *cfg = (struct iwl_3945_cfg *)(ent->driver_data);
int i;
DECLARE_MAC_BUF(mac);
@ -8276,6 +7956,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
priv->hw = hw;
priv->pci_dev = pdev;
priv->cfg = cfg;
/* Select antenna (may be helpful if only one antenna is connected) */
priv->antenna = (enum iwl3945_antenna)iwl3945_param_antenna;
@ -8365,32 +8046,8 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
priv->iw_mode = IEEE80211_IF_TYPE_STA;
pci_id =
(priv->pci_dev->device << 16) | priv->pci_dev->subsystem_device;
switch (pci_id) {
case 0x42221005: /* 0x4222 0x8086 0x1005 is BG SKU */
case 0x42221034: /* 0x4222 0x8086 0x1034 is BG SKU */
case 0x42271014: /* 0x4227 0x8086 0x1014 is BG SKU */
case 0x42221044: /* 0x4222 0x8086 0x1044 is BG SKU */
priv->is_abg = 0;
break;
/*
* Rest are assumed ABG SKU -- if this is not the
* case then the card will get the wrong 'Detected'
* line in the kernel log however the code that
* initializes the GEO table will detect no A-band
* channels and remove the is_abg mask.
*/
default:
priv->is_abg = 1;
break;
}
printk(KERN_INFO DRV_NAME
": Detected Intel PRO/Wireless 3945%sBG Network Connection\n",
priv->is_abg ? "A" : "");
": Detected Intel Wireless WiFi Link %s\n", priv->cfg->name);
/* Device-specific setup */
if (iwl3945_hw_set_hw_setting(priv)) {

View file

@ -45,13 +45,10 @@
#include <asm/div64.h>
#include "iwl-core.h"
#include "iwl-4965.h"
#include "iwl-helpers.h"
#ifdef CONFIG_IWL4965_DEBUG
u32 iwl4965_debug_level;
#endif
static int iwl4965_tx_queue_update_write_ptr(struct iwl4965_priv *priv,
struct iwl4965_tx_queue *txq);
@ -90,15 +87,8 @@ int iwl4965_param_amsdu_size_8K; /* def: enable 8K amsdu size */
#define VS
#endif
#define IWLWIFI_VERSION "1.2.26k" VD VS
#define DRV_COPYRIGHT "Copyright(c) 2003-2007 Intel Corporation"
#define DRV_VERSION IWLWIFI_VERSION
#define DRV_VERSION IWLWIFI_VERSION VD VS
/* Change firmware file name, using "-" and incrementing number,
* *only* when uCode interface or architecture changes so that it
* is not compatible with earlier drivers.
* This number will also appear in << 8 position of 1st dword of uCode file */
#define IWL4965_UCODE_API "-1"
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
@ -161,17 +151,6 @@ static const char *iwl4965_escape_essid(const char *essid, u8 essid_len)
return escaped;
}
static void iwl4965_print_hex_dump(int level, void *p, u32 len)
{
#ifdef CONFIG_IWL4965_DEBUG
if (!(iwl4965_debug_level & level))
return;
print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
p, len, 1);
#endif
}
/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
* DMA services
*
@ -215,25 +194,6 @@ int iwl4965_queue_space(const struct iwl4965_queue *q)
return s;
}
/**
* iwl4965_queue_inc_wrap - increment queue index, wrap back to beginning
* @index -- current index
* @n_bd -- total number of entries in queue (must be power of 2)
*/
static inline int iwl4965_queue_inc_wrap(int index, int n_bd)
{
return ++index & (n_bd - 1);
}
/**
* iwl4965_queue_dec_wrap - decrement queue index, wrap back to end
* @index -- current index
* @n_bd -- total number of entries in queue (must be power of 2)
*/
static inline int iwl4965_queue_dec_wrap(int index, int n_bd)
{
return --index & (n_bd - 1);
}
static inline int x2_queue_used(const struct iwl4965_queue *q, int i)
{
@ -262,8 +222,8 @@ static int iwl4965_queue_init(struct iwl4965_priv *priv, struct iwl4965_queue *q
q->n_window = slots_num;
q->id = id;
/* count must be power-of-two size, otherwise iwl4965_queue_inc_wrap
* and iwl4965_queue_dec_wrap are broken. */
/* count must be power-of-two size, otherwise iwl_queue_inc_wrap
* and iwl_queue_dec_wrap are broken. */
BUG_ON(!is_power_of_2(count));
/* slots_num must be power-of-two size, otherwise
@ -363,7 +323,7 @@ int iwl4965_tx_queue_init(struct iwl4965_priv *priv,
txq->need_update = 0;
/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
* iwl4965_queue_inc_wrap and iwl4965_queue_dec_wrap are broken. */
* iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
/* Initialize queue's high/low-water marks, and head/tail indexes */
@ -394,7 +354,7 @@ void iwl4965_tx_queue_free(struct iwl4965_priv *priv, struct iwl4965_tx_queue *t
/* first, empty all BD's */
for (; q->write_ptr != q->read_ptr;
q->read_ptr = iwl4965_queue_inc_wrap(q->read_ptr, q->n_bd))
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd))
iwl4965_hw_txq_free_tfd(priv, txq);
len = sizeof(struct iwl4965_cmd) * q->n_window;
@ -735,7 +695,7 @@ static int iwl4965_enqueue_hcmd(struct iwl4965_priv *priv, struct iwl4965_host_c
ret = iwl4965_tx_queue_update_wr_ptr(priv, txq, 0);
/* Increment and update queue's write index */
q->write_ptr = iwl4965_queue_inc_wrap(q->write_ptr, q->n_bd);
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
iwl4965_tx_queue_update_write_ptr(priv, txq);
spin_unlock_irqrestore(&priv->hcmd_lock, flags);
@ -1551,34 +1511,6 @@ unsigned int iwl4965_fill_beacon_frame(struct iwl4965_priv *priv,
return priv->ibss_beacon->len;
}
int iwl4965_rate_index_from_plcp(int plcp)
{
int i = 0;
/* 4965 HT rate format */
if (plcp & RATE_MCS_HT_MSK) {
i = (plcp & 0xff);
if (i >= IWL_RATE_MIMO_6M_PLCP)
i = i - IWL_RATE_MIMO_6M_PLCP;
i += IWL_FIRST_OFDM_RATE;
/* skip 9M not supported in ht*/
if (i >= IWL_RATE_9M_INDEX)
i += 1;
if ((i >= IWL_FIRST_OFDM_RATE) &&
(i <= IWL_LAST_OFDM_RATE))
return i;
/* 4965 legacy rate format, search for match in table */
} else {
for (i = 0; i < ARRAY_SIZE(iwl4965_rates); i++)
if (iwl4965_rates[i].plcp == (plcp &0xFF))
return i;
}
return -1;
}
static u8 iwl4965_rate_get_lowest_plcp(int rate_mask)
{
u8 i;
@ -1712,148 +1644,6 @@ done:
* Misc. internal state and helper functions
*
******************************************************************************/
#ifdef CONFIG_IWL4965_DEBUG
/**
* iwl4965_report_frame - dump frame to syslog during debug sessions
*
* You may hack this function to show different aspects of received frames,
* including selective frame dumps.
* group100 parameter selects whether to show 1 out of 100 good frames.
*
* TODO: This was originally written for 3945, need to audit for
* proper operation with 4965.
*/
void iwl4965_report_frame(struct iwl4965_priv *priv,
struct iwl4965_rx_packet *pkt,
struct ieee80211_hdr *header, int group100)
{
u32 to_us;
u32 print_summary = 0;
u32 print_dump = 0; /* set to 1 to dump all frames' contents */
u32 hundred = 0;
u32 dataframe = 0;
u16 fc;
u16 seq_ctl;
u16 channel;
u16 phy_flags;
int rate_sym;
u16 length;
u16 status;
u16 bcn_tmr;
u32 tsf_low;
u64 tsf;
u8 rssi;
u8 agc;
u16 sig_avg;
u16 noise_diff;
struct iwl4965_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
struct iwl4965_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
struct iwl4965_rx_frame_end *rx_end = IWL_RX_END(pkt);
u8 *data = IWL_RX_DATA(pkt);
/* MAC header */
fc = le16_to_cpu(header->frame_control);
seq_ctl = le16_to_cpu(header->seq_ctrl);
/* metadata */
channel = le16_to_cpu(rx_hdr->channel);
phy_flags = le16_to_cpu(rx_hdr->phy_flags);
rate_sym = rx_hdr->rate;
length = le16_to_cpu(rx_hdr->len);
/* end-of-frame status and timestamp */
status = le32_to_cpu(rx_end->status);
bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
tsf = le64_to_cpu(rx_end->timestamp);
/* signal statistics */
rssi = rx_stats->rssi;
agc = rx_stats->agc;
sig_avg = le16_to_cpu(rx_stats->sig_avg);
noise_diff = le16_to_cpu(rx_stats->noise_diff);
to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
/* if data frame is to us and all is good,
* (optionally) print summary for only 1 out of every 100 */
if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
dataframe = 1;
if (!group100)
print_summary = 1; /* print each frame */
else if (priv->framecnt_to_us < 100) {
priv->framecnt_to_us++;
print_summary = 0;
} else {
priv->framecnt_to_us = 0;
print_summary = 1;
hundred = 1;
}
} else {
/* print summary for all other frames */
print_summary = 1;
}
if (print_summary) {
char *title;
u32 rate;
if (hundred)
title = "100Frames";
else if (fc & IEEE80211_FCTL_RETRY)
title = "Retry";
else if (ieee80211_is_assoc_response(fc))
title = "AscRsp";
else if (ieee80211_is_reassoc_response(fc))
title = "RasRsp";
else if (ieee80211_is_probe_response(fc)) {
title = "PrbRsp";
print_dump = 1; /* dump frame contents */
} else if (ieee80211_is_beacon(fc)) {
title = "Beacon";
print_dump = 1; /* dump frame contents */
} else if (ieee80211_is_atim(fc))
title = "ATIM";
else if (ieee80211_is_auth(fc))
title = "Auth";
else if (ieee80211_is_deauth(fc))
title = "DeAuth";
else if (ieee80211_is_disassoc(fc))
title = "DisAssoc";
else
title = "Frame";
rate = iwl4965_rate_index_from_plcp(rate_sym);
if (rate == -1)
rate = 0;
else
rate = iwl4965_rates[rate].ieee / 2;
/* print frame summary.
* MAC addresses show just the last byte (for brevity),
* but you can hack it to show more, if you'd like to. */
if (dataframe)
IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
"len=%u, rssi=%d, chnl=%d, rate=%u, \n",
title, fc, header->addr1[5],
length, rssi, channel, rate);
else {
/* src/dst addresses assume managed mode */
IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
"src=0x%02x, rssi=%u, tim=%lu usec, "
"phy=0x%02x, chnl=%d\n",
title, fc, header->addr1[5],
header->addr3[5], rssi,
tsf_low - priv->scan_start_tsf,
phy_flags, channel);
}
}
if (print_dump)
iwl4965_print_hex_dump(IWL_DL_RX, data, length);
}
#endif
static void iwl4965_unset_hw_setting(struct iwl4965_priv *priv)
{
@ -3088,7 +2878,7 @@ static int iwl4965_tx_skb(struct iwl4965_priv *priv,
iwl4965_tx_queue_update_wr_ptr(priv, txq, len);
/* Tell device the write index *just past* this latest filled TFD */
q->write_ptr = iwl4965_queue_inc_wrap(q->write_ptr, q->n_bd);
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
rc = iwl4965_tx_queue_update_write_ptr(priv, txq);
spin_unlock_irqrestore(&priv->lock, flags);
@ -3482,9 +3272,9 @@ int iwl4965_tx_queue_reclaim(struct iwl4965_priv *priv, int txq_id, int index)
return 0;
}
for (index = iwl4965_queue_inc_wrap(index, q->n_bd);
for (index = iwl_queue_inc_wrap(index, q->n_bd);
q->read_ptr != index;
q->read_ptr = iwl4965_queue_inc_wrap(q->read_ptr, q->n_bd)) {
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
if (txq_id != IWL_CMD_QUEUE_NUM) {
iwl4965_txstatus_to_ieee(priv,
&(txq->txb[txq->q.read_ptr]));
@ -3591,9 +3381,9 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv,
tx_status->control.flags &= ~IEEE80211_TXCTL_AMPDU;
tx_status->flags = iwl4965_is_tx_success(status)?
IEEE80211_TX_STATUS_ACK : 0;
/* FIXME Wrong Rate
tx_status->control.tx_rate =
iwl4965_hw_get_rate_n_flags(tx_resp->rate_n_flags); */
iwl4965_hwrate_to_tx_control(priv,
le32_to_cpu(tx_resp->rate_n_flags),
&tx_status->control);
/* FIXME: code repetition end */
IWL_DEBUG_TX_REPLY("1 Frame 0x%x failure :%d\n",
@ -3729,7 +3519,7 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv,
if (txq->q.read_ptr != (scd_ssn & 0xff)) {
int freed;
index = iwl4965_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn "
"%d index %d\n", scd_ssn , index);
freed = iwl4965_tx_queue_reclaim(priv, txq_id, index);
@ -3750,9 +3540,10 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv,
tx_status->queue_number = status;
tx_status->queue_length = tx_resp->bt_kill_count;
tx_status->queue_length |= tx_resp->failure_rts;
tx_status->flags =
iwl4965_is_tx_success(status) ? IEEE80211_TX_STATUS_ACK : 0;
iwl4965_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
&tx_status->control);
IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) rate_n_flags 0x%x "
"retries %d\n", txq_id, iwl4965_get_tx_fail_reason(status),
@ -4886,8 +4677,7 @@ static void iwl4965_dump_nic_error_log(struct iwl4965_priv *priv)
if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
IWL_ERROR("Start IWL Error Log Dump:\n");
IWL_ERROR("Status: 0x%08lX, Config: %08X count: %d\n",
priv->status, priv->config, count);
IWL_ERROR("Status: 0x%08lX, count: %d\n", priv->status, count);
}
desc = iwl4965_read_targ_mem(priv, base + 1 * sizeof(u32));
@ -5099,9 +4889,9 @@ static void iwl4965_irq_tasklet(struct iwl4965_priv *priv)
* atomic, make sure that inta covers all the interrupts that
* we've discovered, even if FH interrupt came in just after
* reading CSR_INT. */
if (inta_fh & CSR_FH_INT_RX_MASK)
if (inta_fh & CSR49_FH_INT_RX_MASK)
inta |= CSR_INT_BIT_FH_RX;
if (inta_fh & CSR_FH_INT_TX_MASK)
if (inta_fh & CSR49_FH_INT_TX_MASK)
inta |= CSR_INT_BIT_FH_TX;
/* Now service all interrupt bits discovered above. */
@ -5502,11 +5292,12 @@ static int iwl4965_init_channel_map(struct iwl4965_priv *priv)
ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
ch_info->min_power = 0;
IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x"
IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x"
" %ddBm): Ad-Hoc %ssupported\n",
ch_info->channel,
is_channel_a_band(ch_info) ?
"5.2" : "2.4",
CHECK_AND_PRINT(VALID),
CHECK_AND_PRINT(IBSS),
CHECK_AND_PRINT(ACTIVE),
CHECK_AND_PRINT(RADAR),
@ -5749,7 +5540,7 @@ static void iwl4965_init_hw_rates(struct iwl4965_priv *priv,
static int iwl4965_init_geos(struct iwl4965_priv *priv)
{
struct iwl4965_channel_info *ch;
struct ieee80211_supported_band *band;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *channels;
struct ieee80211_channel *geo_ch;
struct ieee80211_rate *rates;
@ -5767,7 +5558,7 @@ static int iwl4965_init_geos(struct iwl4965_priv *priv)
if (!channels)
return -ENOMEM;
rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_MAX_RATES + 1)),
rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_RATE_COUNT + 1)),
GFP_KERNEL);
if (!rates) {
kfree(channels);
@ -5775,42 +5566,42 @@ static int iwl4965_init_geos(struct iwl4965_priv *priv)
}
/* 5.2GHz channels start after the 2.4GHz channels */
band = &priv->bands[IEEE80211_BAND_5GHZ];
band->channels = &channels[ARRAY_SIZE(iwl4965_eeprom_band_1)];
band->bitrates = &rates[4];
band->n_bitrates = 8; /* just OFDM */
sband = &priv->bands[IEEE80211_BAND_5GHZ];
sband->channels = &channels[ARRAY_SIZE(iwl4965_eeprom_band_1)];
/* just OFDM */
sband->bitrates = &rates[IWL_FIRST_OFDM_RATE];
sband->n_bitrates = IWL_RATE_COUNT - IWL_FIRST_OFDM_RATE;
iwl4965_init_ht_hw_capab(&band->ht_info, IEEE80211_BAND_5GHZ);
iwl4965_init_ht_hw_capab(&sband->ht_info, IEEE80211_BAND_5GHZ);
band = &priv->bands[IEEE80211_BAND_2GHZ];
band->channels = channels;
band->bitrates = rates;
band->n_bitrates = 12; /* OFDM & CCK */
sband = &priv->bands[IEEE80211_BAND_2GHZ];
sband->channels = channels;
/* OFDM & CCK */
sband->bitrates = rates;
sband->n_bitrates = IWL_RATE_COUNT;
iwl4965_init_ht_hw_capab(&band->ht_info, IEEE80211_BAND_2GHZ);
iwl4965_init_ht_hw_capab(&sband->ht_info, IEEE80211_BAND_2GHZ);
priv->ieee_channels = channels;
priv->ieee_rates = rates;
iwl4965_init_hw_rates(priv, rates);
for (i = 0, geo_ch = channels; i < priv->channel_count; i++) {
for (i = 0; i < priv->channel_count; i++) {
ch = &priv->channel_info[i];
if (!is_channel_valid(ch)) {
IWL_DEBUG_INFO("Channel %d [%sGHz] is restricted -- "
"skipping.\n",
ch->channel, is_channel_a_band(ch) ?
"5.2" : "2.4");
/* FIXME: might be removed if scan is OK */
if (!is_channel_valid(ch))
continue;
}
if (is_channel_a_band(ch)) {
geo_ch = &priv->bands[IEEE80211_BAND_5GHZ].channels[priv->bands[IEEE80211_BAND_5GHZ].n_channels++];
} else
geo_ch = &priv->bands[IEEE80211_BAND_2GHZ].channels[priv->bands[IEEE80211_BAND_2GHZ].n_channels++];
if (is_channel_a_band(ch))
sband = &priv->bands[IEEE80211_BAND_5GHZ];
else
sband = &priv->bands[IEEE80211_BAND_2GHZ];
geo_ch->center_freq = ieee80211chan2mhz(ch->channel);
geo_ch = &sband->channels[sband->n_channels++];
geo_ch->center_freq = ieee80211_channel_to_frequency(ch->channel);
geo_ch->max_power = ch->max_power_avg;
geo_ch->max_antenna_gain = 0xff;
geo_ch->hw_value = ch->channel;
@ -5828,16 +5619,28 @@ static int iwl4965_init_geos(struct iwl4965_priv *priv)
if (ch->max_power_avg > priv->max_channel_txpower_limit)
priv->max_channel_txpower_limit =
ch->max_power_avg;
} else
} else {
geo_ch->flags |= IEEE80211_CHAN_DISABLED;
}
/* Save flags for reg domain usage */
geo_ch->orig_flags = geo_ch->flags;
IWL_DEBUG_INFO("Channel %d Freq=%d[%sGHz] %s flag=0%X\n",
ch->channel, geo_ch->center_freq,
is_channel_a_band(ch) ? "5.2" : "2.4",
geo_ch->flags & IEEE80211_CHAN_DISABLED ?
"restricted" : "valid",
geo_ch->flags);
}
if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && priv->is_abg) {
if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) &&
priv->cfg->sku & IWL_SKU_A) {
printk(KERN_INFO DRV_NAME
": Incorrectly detected BG card as ABG. Please send "
"your PCI ID 0x%04X:0x%04X to maintainer.\n",
priv->pci_dev->device, priv->pci_dev->subsystem_device);
priv->is_abg = 0;
priv->cfg->sku &= ~IWL_SKU_A;
}
printk(KERN_INFO DRV_NAME
@ -6186,7 +5989,7 @@ static int iwl4965_read_ucode(struct iwl4965_priv *priv)
struct iwl4965_ucode *ucode;
int ret;
const struct firmware *ucode_raw;
const char *name = "iwlwifi-4965" IWL4965_UCODE_API ".ucode";
const char *name = priv->cfg->fw_name;
u8 *src;
size_t len;
u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
@ -7596,6 +7399,12 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
if (conf == NULL)
return -EIO;
if (priv->vif != vif) {
IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
mutex_unlock(&priv->mutex);
return 0;
}
if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) &&
(!conf->beacon || !conf->ssid_len)) {
IWL_DEBUG_MAC80211
@ -7618,17 +7427,6 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) &&
!(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) {
*/
if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
IWL_DEBUG_MAC80211("leave - scanning\n");
mutex_unlock(&priv->mutex);
return 0;
}
if (priv->vif != vif) {
IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
mutex_unlock(&priv->mutex);
return 0;
}
if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
if (!conf->bssid) {
@ -8127,15 +7925,21 @@ static void iwl4965_ht_info_fill(struct ieee80211_conf *conf,
iwl_conf->is_green_field = !!(ht_conf->cap & IEEE80211_HT_CAP_GRN_FLD);
iwl_conf->max_amsdu_size =
!!(ht_conf->cap & IEEE80211_HT_CAP_MAX_AMSDU);
iwl_conf->supported_chan_width =
!!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH);
iwl_conf->extension_chan_offset =
ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET;
/* If no above or below channel supplied disable FAT channel */
if (iwl_conf->extension_chan_offset != IWL_EXT_CHANNEL_OFFSET_ABOVE &&
iwl_conf->extension_chan_offset != IWL_EXT_CHANNEL_OFFSET_BELOW)
iwl_conf->supported_chan_width = 0;
iwl_conf->tx_mimo_ps_mode =
(u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2);
memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16);
iwl_conf->control_channel = ht_bss_conf->primary_channel;
iwl_conf->extension_chan_offset =
ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET;
iwl_conf->tx_chan_width =
!!(ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_WIDTH);
iwl_conf->ht_protection =
@ -8776,6 +8580,7 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
int err = 0;
struct iwl4965_priv *priv;
struct ieee80211_hw *hw;
struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
int i;
DECLARE_MAC_BUF(mac);
@ -8809,6 +8614,7 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
IWL_DEBUG_INFO("*** LOAD DRIVER ***\n");
priv = hw->priv;
priv->hw = hw;
priv->cfg = cfg;
priv->pci_dev = pdev;
priv->antenna = (enum iwl4965_antenna)iwl4965_param_antenna;
@ -8911,8 +8717,9 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
/* Choose which receivers/antennas to use */
iwl4965_set_rxon_chain(priv);
printk(KERN_INFO DRV_NAME
": Detected Intel Wireless WiFi Link 4965AGN\n");
": Detected Intel Wireless WiFi Link %s\n", priv->cfg->name);
/* Device-specific setup */
if (iwl4965_hw_set_hw_setting(priv)) {

View file

@ -349,11 +349,7 @@ static int assoc_helper_wpa_keys(struct lbs_private *priv,
if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
ret = lbs_prepare_and_send_command(priv,
CMD_802_11_KEY_MATERIAL,
CMD_ACT_SET,
CMD_OPTION_WAITFORRSP,
0, assoc_req);
ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
assoc_req->flags = flags;
}
@ -363,11 +359,7 @@ static int assoc_helper_wpa_keys(struct lbs_private *priv,
if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
ret = lbs_prepare_and_send_command(priv,
CMD_802_11_KEY_MATERIAL,
CMD_ACT_SET,
CMD_OPTION_WAITFORRSP,
0, assoc_req);
ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
assoc_req->flags = flags;
}

View file

@ -338,75 +338,103 @@ int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
return ret;
}
static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset,
struct enc_key * pkey)
static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam,
struct enc_key *key)
{
lbs_deb_enter(LBS_DEB_CMD);
if (pkey->flags & KEY_INFO_WPA_ENABLED) {
pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
}
if (pkey->flags & KEY_INFO_WPA_UNICAST) {
pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
}
if (pkey->flags & KEY_INFO_WPA_MCAST) {
pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
}
if (key->flags & KEY_INFO_WPA_ENABLED)
keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
if (key->flags & KEY_INFO_WPA_UNICAST)
keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
if (key->flags & KEY_INFO_WPA_MCAST)
keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
pkeyparamset->keytypeid = cpu_to_le16(pkey->type);
pkeyparamset->keylen = cpu_to_le16(pkey->len);
memcpy(pkeyparamset->key, pkey->key, pkey->len);
pkeyparamset->length = cpu_to_le16( sizeof(pkeyparamset->keytypeid)
+ sizeof(pkeyparamset->keyinfo)
+ sizeof(pkeyparamset->keylen)
+ sizeof(pkeyparamset->key));
keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
keyparam->keytypeid = cpu_to_le16(key->type);
keyparam->keylen = cpu_to_le16(key->len);
memcpy(keyparam->key, key->key, key->len);
/* Length field doesn't include the {type,length} header */
keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4);
lbs_deb_leave(LBS_DEB_CMD);
}
static int lbs_cmd_802_11_key_material(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action,
u32 cmd_oid, void *pdata_buf)
int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
struct assoc_request *assoc)
{
struct cmd_ds_802_11_key_material *pkeymaterial =
&cmd->params.keymaterial;
struct assoc_request * assoc_req = pdata_buf;
struct cmd_ds_802_11_key_material cmd;
int ret = 0;
int index = 0;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_KEY_MATERIAL);
pkeymaterial->action = cpu_to_le16(cmd_action);
cmd.action = cpu_to_le16(cmd_action);
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
if (cmd_action == CMD_ACT_GET) {
cmd->size = cpu_to_le16(S_DS_GEN + sizeof (pkeymaterial->action));
ret = 0;
goto done;
cmd.hdr.size = cpu_to_le16(S_DS_GEN + 2);
} else {
memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet));
if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) {
set_one_wpa_key(&cmd.keyParamSet[index],
&assoc->wpa_unicast_key);
index++;
}
if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) {
set_one_wpa_key(&cmd.keyParamSet[index],
&assoc->wpa_mcast_key);
index++;
}
/* The common header and as many keys as we included */
cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd),
keyParamSet[index]));
}
ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
/* Copy the returned key to driver private data */
if (!ret && cmd_action == CMD_ACT_GET) {
void *buf_ptr = cmd.keyParamSet;
void *resp_end = &(&cmd)[1];
while (buf_ptr < resp_end) {
struct MrvlIEtype_keyParamSet *keyparam = buf_ptr;
struct enc_key *key;
uint16_t param_set_len = le16_to_cpu(keyparam->length);
uint16_t key_len = le16_to_cpu(keyparam->keylen);
uint16_t key_flags = le16_to_cpu(keyparam->keyinfo);
uint16_t key_type = le16_to_cpu(keyparam->keytypeid);
void *end;
end = (void *)keyparam + sizeof(keyparam->type)
+ sizeof(keyparam->length) + param_set_len;
/* Make sure we don't access past the end of the IEs */
if (end > resp_end)
break;
if (key_flags & KEY_INFO_WPA_UNICAST)
key = &priv->wpa_unicast_key;
else if (key_flags & KEY_INFO_WPA_MCAST)
key = &priv->wpa_mcast_key;
else
break;
/* Copy returned key into driver */
memset(key, 0, sizeof(struct enc_key));
if (key_len > sizeof(key->key))
break;
key->type = key_type;
key->flags = key_flags;
key->len = key_len;
memcpy(key->key, keyparam->key, key->len);
buf_ptr = end + 1;
}
}
memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet));
if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
set_one_wpa_key(&pkeymaterial->keyParamSet[index],
&assoc_req->wpa_unicast_key);
index++;
}
if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
set_one_wpa_key(&pkeymaterial->keyParamSet[index],
&assoc_req->wpa_mcast_key);
index++;
}
cmd->size = cpu_to_le16( S_DS_GEN
+ sizeof (pkeymaterial->action)
+ (index * sizeof(struct MrvlIEtype_keyParamSet)));
ret = 0;
done:
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
@ -1354,10 +1382,6 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
ret = lbs_cmd_802_11_ps_mode(priv, cmdptr, cmd_action);
break;
case CMD_802_11_SCAN:
ret = lbs_cmd_80211_scan(priv, cmdptr, pdata_buf);
break;
case CMD_MAC_CONTROL:
ret = lbs_cmd_mac_control(priv, cmdptr);
break;
@ -1435,11 +1459,6 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
ret = lbs_cmd_80211_ad_hoc_stop(priv, cmdptr);
break;
case CMD_802_11_KEY_MATERIAL:
ret = lbs_cmd_802_11_key_material(priv, cmdptr, cmd_action,
cmd_oid, pdata_buf);
break;
case CMD_802_11_PAIRWISE_TSC:
break;
case CMD_802_11_GROUP_TSC:

View file

@ -57,5 +57,7 @@ int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
struct assoc_request *assoc);
int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
uint16_t *enable);
int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
struct assoc_request *assoc);
#endif /* _LBS_CMD_H */

View file

@ -204,61 +204,6 @@ static int lbs_ret_802_11_snmp_mib(struct lbs_private *priv,
return 0;
}
static int lbs_ret_802_11_key_material(struct lbs_private *priv,
struct cmd_ds_command *resp)
{
struct cmd_ds_802_11_key_material *pkeymaterial =
&resp->params.keymaterial;
u16 action = le16_to_cpu(pkeymaterial->action);
lbs_deb_enter(LBS_DEB_CMD);
/* Copy the returned key to driver private data */
if (action == CMD_ACT_GET) {
u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet;
u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size));
while (buf_ptr < resp_end) {
struct MrvlIEtype_keyParamSet * pkeyparamset =
(struct MrvlIEtype_keyParamSet *) buf_ptr;
struct enc_key * pkey;
u16 param_set_len = le16_to_cpu(pkeyparamset->length);
u16 key_len = le16_to_cpu(pkeyparamset->keylen);
u16 key_flags = le16_to_cpu(pkeyparamset->keyinfo);
u16 key_type = le16_to_cpu(pkeyparamset->keytypeid);
u8 * end;
end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type)
+ sizeof (pkeyparamset->length)
+ param_set_len;
/* Make sure we don't access past the end of the IEs */
if (end > resp_end)
break;
if (key_flags & KEY_INFO_WPA_UNICAST)
pkey = &priv->wpa_unicast_key;
else if (key_flags & KEY_INFO_WPA_MCAST)
pkey = &priv->wpa_mcast_key;
else
break;
/* Copy returned key into driver */
memset(pkey, 0, sizeof(struct enc_key));
if (key_len > sizeof(pkey->key))
break;
pkey->type = key_type;
pkey->flags = key_flags;
pkey->len = key_len;
memcpy(pkey->key, pkeyparamset->key, pkey->len);
buf_ptr = end + 1;
}
}
lbs_deb_enter(LBS_DEB_CMD);
return 0;
}
static int lbs_ret_802_11_mac_address(struct lbs_private *priv,
struct cmd_ds_command *resp)
{
@ -407,10 +352,6 @@ static inline int handle_cmd_response(struct lbs_private *priv,
ret = lbs_ret_reg_access(priv, respcmd, resp);
break;
case CMD_RET(CMD_802_11_SCAN):
ret = lbs_ret_80211_scan(priv, resp);
break;
case CMD_RET(CMD_802_11_GET_LOG):
ret = lbs_ret_get_log(priv, resp);
break;
@ -475,10 +416,6 @@ static inline int handle_cmd_response(struct lbs_private *priv,
ret = lbs_ret_80211_ad_hoc_stop(priv, resp);
break;
case CMD_RET(CMD_802_11_KEY_MATERIAL):
ret = lbs_ret_802_11_key_material(priv, resp);
break;
case CMD_RET(CMD_802_11_EEPROM_ACCESS):
ret = lbs_ret_802_11_eeprom_access(priv, resp);
break;

View file

@ -174,9 +174,11 @@ struct cmd_ds_802_11_subscribe_event {
* Define data structure for CMD_802_11_SCAN
*/
struct cmd_ds_802_11_scan {
u8 bsstype;
u8 bssid[ETH_ALEN];
u8 tlvbuffer[1];
struct cmd_header hdr;
uint8_t bsstype;
uint8_t bssid[ETH_ALEN];
uint8_t tlvbuffer[0];
#if 0
mrvlietypes_ssidparamset_t ssidParamSet;
mrvlietypes_chanlistparamset_t ChanListParamSet;
@ -185,9 +187,11 @@ struct cmd_ds_802_11_scan {
};
struct cmd_ds_802_11_scan_rsp {
struct cmd_header hdr;
__le16 bssdescriptsize;
u8 nr_sets;
u8 bssdesc_and_tlvbuffer[1];
uint8_t nr_sets;
uint8_t bssdesc_and_tlvbuffer[0];
};
struct cmd_ds_802_11_get_log {
@ -572,6 +576,8 @@ struct cmd_ds_host_sleep {
} __attribute__ ((packed));
struct cmd_ds_802_11_key_material {
struct cmd_header hdr;
__le16 action;
struct MrvlIEtype_keyParamSet keyParamSet[2];
} __attribute__ ((packed));
@ -689,8 +695,6 @@ struct cmd_ds_command {
/* command Body */
union {
struct cmd_ds_802_11_ps_mode psmode;
struct cmd_ds_802_11_scan scan;
struct cmd_ds_802_11_scan_rsp scanresp;
struct cmd_ds_mac_control macctrl;
struct cmd_ds_802_11_associate associate;
struct cmd_ds_802_11_deauthenticate deauth;
@ -712,7 +716,6 @@ struct cmd_ds_command {
struct cmd_ds_802_11_rssi_rsp rssirsp;
struct cmd_ds_802_11_disassociate dassociate;
struct cmd_ds_802_11_mac_address macadd;
struct cmd_ds_802_11_key_material keymaterial;
struct cmd_ds_mac_reg_access macreg;
struct cmd_ds_bbp_reg_access bbpreg;
struct cmd_ds_rf_reg_access rfreg;

File diff suppressed because it is too large Load diff

View file

@ -17,56 +17,15 @@
*/
#define LBS_IOCTL_USER_SCAN_CHAN_MAX 50
//! Infrastructure BSS scan type in lbs_scan_cmd_config
//! Infrastructure BSS scan type in cmd_ds_802_11_scan
#define LBS_SCAN_BSS_TYPE_BSS 1
//! Adhoc BSS scan type in lbs_scan_cmd_config
//! Adhoc BSS scan type in cmd_ds_802_11_scan
#define LBS_SCAN_BSS_TYPE_IBSS 2
//! Adhoc or Infrastructure BSS scan type in lbs_scan_cmd_config, no filter
//! Adhoc or Infrastructure BSS scan type in cmd_ds_802_11_scan, no filter
#define LBS_SCAN_BSS_TYPE_ANY 3
/**
* @brief Structure used internally in the wlan driver to configure a scan.
*
* Sent to the command processing module to configure the firmware
* scan command prepared by lbs_cmd_80211_scan.
*
* @sa lbs_scan_networks
*
*/
struct lbs_scan_cmd_config {
/**
* @brief BSS type to be sent in the firmware command
*
* Field can be used to restrict the types of networks returned in the
* scan. valid settings are:
*
* - LBS_SCAN_BSS_TYPE_BSS (infrastructure)
* - LBS_SCAN_BSS_TYPE_IBSS (adhoc)
* - LBS_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure)
*/
u8 bsstype;
/**
* @brief Specific BSSID used to filter scan results in the firmware
*/
u8 bssid[ETH_ALEN];
/**
* @brief length of TLVs sent in command starting at tlvBuffer
*/
int tlvbufferlen;
/**
* @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command
*
* @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t
* @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t
*/
u8 tlvbuffer[1]; //!< SSID TLV(s) and ChanList TLVs are stored here
};
/**
* @brief IOCTL channel sub-structure sent in lbs_ioctl_user_scan_cfg
*
@ -179,13 +138,6 @@ int lbs_find_best_network_ssid(struct lbs_private *priv, u8 *out_ssid,
int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid,
u8 ssid_len, u8 clear_ssid);
int lbs_cmd_80211_scan(struct lbs_private *priv,
struct cmd_ds_command *cmd,
void *pdata_buf);
int lbs_ret_80211_scan(struct lbs_private *priv,
struct cmd_ds_command *resp);
int lbs_scan_networks(struct lbs_private *priv,
const struct lbs_ioctl_user_scan_cfg *puserscanin,
int full_scan);

View file

@ -239,4 +239,17 @@ struct mrvlietypes_ledgpio {
struct led_pin ledpin[1];
} __attribute__ ((packed));
struct led_bhv {
uint8_t firmwarestate;
uint8_t led;
uint8_t ledstate;
uint8_t ledarg;
} __attribute__ ((packed));
struct mrvlietypes_ledbhv {
struct mrvlietypesheader header;
struct led_bhv ledbhv[1];
} __attribute__ ((packed));
#endif

View file

@ -809,6 +809,7 @@ static int hw_init_hmac(struct zd_chip *chip)
{ CR_AFTER_PNP, 0x1 },
{ CR_WEP_PROTECT, 0x114 },
{ CR_IFS_VALUE, IFS_VALUE_DEFAULT },
{ CR_CAM_MODE, MODE_AP_WDS},
};
ZD_ASSERT(mutex_is_locked(&chip->mutex));

View file

@ -489,6 +489,7 @@ enum {
#define CR_RX_OFFSET CTL_REG(0x065c)
#define CR_BCN_LENGTH CTL_REG(0x0664)
#define CR_PHY_DELAY CTL_REG(0x066C)
#define CR_BCN_FIFO CTL_REG(0x0670)
#define CR_SNIFFER_ON CTL_REG(0x0674)
@ -545,6 +546,8 @@ enum {
#define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \
RX_FILTER_CFEND | RX_FILTER_CFACK)
#define BCN_MODE_IBSS 0x2000000
/* Monitor mode sets filter to 0xfffff */
#define CR_ACK_TIMEOUT_EXT CTL_REG(0x0690)
@ -578,6 +581,11 @@ enum {
/* CAM: Continuous Access Mode (power management) */
#define CR_CAM_MODE CTL_REG(0x0700)
#define MODE_IBSS 0x0
#define MODE_AP 0x1
#define MODE_STA 0x2
#define MODE_AP_WDS 0x3
#define CR_CAM_ROLL_TB_LOW CTL_REG(0x0704)
#define CR_CAM_ROLL_TB_HIGH CTL_REG(0x0708)
#define CR_CAM_ADDRESS CTL_REG(0x070C)

View file

@ -475,6 +475,46 @@ static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
/* FIXME: Management frame? */
}
void zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon)
{
struct zd_mac *mac = zd_hw_mac(hw);
u32 tmp, j = 0;
/* 4 more bytes for tail CRC */
u32 full_len = beacon->len + 4;
zd_iowrite32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, 0);
zd_ioread32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, &tmp);
while (tmp & 0x2) {
zd_ioread32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, &tmp);
if ((++j % 100) == 0) {
printk(KERN_ERR "CR_BCN_FIFO_SEMAPHORE not ready\n");
if (j >= 500) {
printk(KERN_ERR "Giving up beacon config.\n");
return;
}
}
msleep(1);
}
zd_iowrite32(&mac->chip, CR_BCN_FIFO, full_len - 1);
if (zd_chip_is_zd1211b(&mac->chip))
zd_iowrite32(&mac->chip, CR_BCN_LENGTH, full_len - 1);
for (j = 0 ; j < beacon->len; j++)
zd_iowrite32(&mac->chip, CR_BCN_FIFO,
*((u8 *)(beacon->data + j)));
for (j = 0; j < 4; j++)
zd_iowrite32(&mac->chip, CR_BCN_FIFO, 0x0);
zd_iowrite32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, 1);
/* 802.11b/g 2.4G CCK 1Mb
* 802.11a, not yet implemented, uses different values (see GPL vendor
* driver)
*/
zd_iowrite32(&mac->chip, CR_BCN_PLCP_CFG, 0x00000400 |
(full_len << 19));
}
static int fill_ctrlset(struct zd_mac *mac,
struct sk_buff *skb,
struct ieee80211_tx_control *control)
@ -709,6 +749,7 @@ static int zd_op_add_interface(struct ieee80211_hw *hw,
switch (conf->type) {
case IEEE80211_IF_TYPE_MNTR:
case IEEE80211_IF_TYPE_MESH_POINT:
case IEEE80211_IF_TYPE_STA:
mac->type = conf->type;
break;
@ -738,15 +779,43 @@ static int zd_op_config_interface(struct ieee80211_hw *hw,
struct ieee80211_if_conf *conf)
{
struct zd_mac *mac = zd_hw_mac(hw);
int associated;
if (mac->type == IEEE80211_IF_TYPE_MESH_POINT) {
associated = true;
if (conf->beacon) {
zd_mac_config_beacon(hw, conf->beacon);
kfree_skb(conf->beacon);
zd_set_beacon_interval(&mac->chip, BCN_MODE_IBSS |
hw->conf.beacon_int);
}
} else
associated = is_valid_ether_addr(conf->bssid);
spin_lock_irq(&mac->lock);
mac->associated = is_valid_ether_addr(conf->bssid);
mac->associated = associated;
spin_unlock_irq(&mac->lock);
/* TODO: do hardware bssid filtering */
return 0;
}
void zd_process_intr(struct work_struct *work)
{
u16 int_status;
struct zd_mac *mac = container_of(work, struct zd_mac, process_intr);
int_status = le16_to_cpu(*(u16 *)(mac->intr_buffer+4));
if (int_status & INT_CFG_NEXT_BCN) {
if (net_ratelimit())
dev_dbg_f(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");
} else
dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n");
zd_chip_enable_hwint(&mac->chip);
}
static void set_multicast_hash_handler(struct work_struct *work)
{
struct zd_mac *mac =
@ -912,7 +981,8 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS;
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE;
hw->max_rssi = 100;
hw->max_signal = 100;
@ -926,6 +996,7 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
INIT_WORK(&mac->set_rx_filter_work, set_rx_filter_handler);
INIT_WORK(&mac->process_intr, zd_process_intr);
SET_IEEE80211_DEV(hw, &intf->dev);
return hw;

View file

@ -172,12 +172,15 @@ struct zd_tx_skb_control_block {
struct zd_mac {
struct zd_chip chip;
spinlock_t lock;
spinlock_t intr_lock;
struct ieee80211_hw *hw;
struct housekeeping housekeeping;
struct work_struct set_multicast_hash_work;
struct work_struct set_rts_cts_work;
struct work_struct set_rx_filter_work;
struct work_struct process_intr;
struct zd_mc_hash multicast_hash;
u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
u8 regdomain;
u8 default_regdomain;
int type;

View file

@ -97,6 +97,7 @@ MODULE_DEVICE_TABLE(usb, usb_ids);
#define FW_ZD1211B_PREFIX "zd1211/zd1211b_"
/* USB device initialization */
static void int_urb_complete(struct urb *urb);
static int request_fw_file(
const struct firmware **fw, const char *name, struct device *device)
@ -336,11 +337,18 @@ static inline void handle_regs_int(struct urb *urb)
struct zd_usb *usb = urb->context;
struct zd_usb_interrupt *intr = &usb->intr;
int len;
u16 int_num;
ZD_ASSERT(in_interrupt());
spin_lock(&intr->lock);
if (intr->read_regs_enabled) {
int_num = le16_to_cpu(*(u16 *)(urb->transfer_buffer+2));
if (int_num == CR_INTERRUPT) {
struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context));
memcpy(&mac->intr_buffer, urb->transfer_buffer,
USB_MAX_EP_INT_BUFFER);
schedule_work(&mac->process_intr);
} else if (intr->read_regs_enabled) {
intr->read_regs.length = len = urb->actual_length;
if (len > sizeof(intr->read_regs.buffer))
@ -351,7 +359,6 @@ static inline void handle_regs_int(struct urb *urb)
goto out;
}
dev_dbg_f(urb_dev(urb), "regs interrupt ignored\n");
out:
spin_unlock(&intr->lock);
}

View file

@ -125,4 +125,13 @@ config SSB_DRIVER_EXTIF
If unsure, say N
config SSB_DRIVER_GIGE
bool "SSB Broadcom Gigabit Ethernet driver"
depends on SSB_PCIHOST_POSSIBLE && SSB_EMBEDDED && MIPS
help
Driver for the Sonics Silicon Backplane attached
Broadcom Gigabit Ethernet.
If unsure, say N
endmenu

View file

@ -11,6 +11,7 @@ ssb-y += driver_chipcommon.o
ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o
ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o
ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o
ssb-$(CONFIG_SSB_DRIVER_GIGE) += driver_gige.o
# b43 pci-ssb-bridge driver
# Not strictly a part of SSB, but kept here for convenience

294
drivers/ssb/driver_gige.c Normal file
View file

@ -0,0 +1,294 @@
/*
* Sonics Silicon Backplane
* Broadcom Gigabit Ethernet core driver
*
* Copyright 2008, Broadcom Corporation
* Copyright 2008, Michael Buesch <mb@bu3sch.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_driver_gige.h>
#include <linux/pci.h>
#include <linux/pci_regs.h>
/*
MODULE_DESCRIPTION("SSB Broadcom Gigabit Ethernet driver");
MODULE_AUTHOR("Michael Buesch");
MODULE_LICENSE("GPL");
*/
static const struct ssb_device_id ssb_gige_tbl[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV),
SSB_DEVTABLE_END
};
/* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */
static inline u8 gige_read8(struct ssb_gige *dev, u16 offset)
{
return ssb_read8(dev->dev, offset);
}
static inline u16 gige_read16(struct ssb_gige *dev, u16 offset)
{
return ssb_read16(dev->dev, offset);
}
static inline u32 gige_read32(struct ssb_gige *dev, u16 offset)
{
return ssb_read32(dev->dev, offset);
}
static inline void gige_write8(struct ssb_gige *dev,
u16 offset, u8 value)
{
ssb_write8(dev->dev, offset, value);
}
static inline void gige_write16(struct ssb_gige *dev,
u16 offset, u16 value)
{
ssb_write16(dev->dev, offset, value);
}
static inline void gige_write32(struct ssb_gige *dev,
u16 offset, u32 value)
{
ssb_write32(dev->dev, offset, value);
}
static inline
u8 gige_pcicfg_read8(struct ssb_gige *dev, unsigned int offset)
{
BUG_ON(offset >= 256);
return gige_read8(dev, SSB_GIGE_PCICFG + offset);
}
static inline
u16 gige_pcicfg_read16(struct ssb_gige *dev, unsigned int offset)
{
BUG_ON(offset >= 256);
return gige_read16(dev, SSB_GIGE_PCICFG + offset);
}
static inline
u32 gige_pcicfg_read32(struct ssb_gige *dev, unsigned int offset)
{
BUG_ON(offset >= 256);
return gige_read32(dev, SSB_GIGE_PCICFG + offset);
}
static inline
void gige_pcicfg_write8(struct ssb_gige *dev,
unsigned int offset, u8 value)
{
BUG_ON(offset >= 256);
gige_write8(dev, SSB_GIGE_PCICFG + offset, value);
}
static inline
void gige_pcicfg_write16(struct ssb_gige *dev,
unsigned int offset, u16 value)
{
BUG_ON(offset >= 256);
gige_write16(dev, SSB_GIGE_PCICFG + offset, value);
}
static inline
void gige_pcicfg_write32(struct ssb_gige *dev,
unsigned int offset, u32 value)
{
BUG_ON(offset >= 256);
gige_write32(dev, SSB_GIGE_PCICFG + offset, value);
}
static int ssb_gige_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 *val)
{
struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops);
unsigned long flags;
if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0))
return PCIBIOS_DEVICE_NOT_FOUND;
if (reg >= 256)
return PCIBIOS_DEVICE_NOT_FOUND;
spin_lock_irqsave(&dev->lock, flags);
switch (size) {
case 1:
*val = gige_pcicfg_read8(dev, reg);
break;
case 2:
*val = gige_pcicfg_read16(dev, reg);
break;
case 4:
*val = gige_pcicfg_read32(dev, reg);
break;
default:
WARN_ON(1);
}
spin_unlock_irqrestore(&dev->lock, flags);
return PCIBIOS_SUCCESSFUL;
}
static int ssb_gige_pci_write_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 val)
{
struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops);
unsigned long flags;
if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0))
return PCIBIOS_DEVICE_NOT_FOUND;
if (reg >= 256)
return PCIBIOS_DEVICE_NOT_FOUND;
spin_lock_irqsave(&dev->lock, flags);
switch (size) {
case 1:
gige_pcicfg_write8(dev, reg, val);
break;
case 2:
gige_pcicfg_write16(dev, reg, val);
break;
case 4:
gige_pcicfg_write32(dev, reg, val);
break;
default:
WARN_ON(1);
}
spin_unlock_irqrestore(&dev->lock, flags);
return PCIBIOS_SUCCESSFUL;
}
static int ssb_gige_probe(struct ssb_device *sdev, const struct ssb_device_id *id)
{
struct ssb_gige *dev;
u32 base, tmslow, tmshigh;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->dev = sdev;
spin_lock_init(&dev->lock);
dev->pci_controller.pci_ops = &dev->pci_ops;
dev->pci_controller.io_resource = &dev->io_resource;
dev->pci_controller.mem_resource = &dev->mem_resource;
dev->pci_controller.io_map_base = 0x800;
dev->pci_ops.read = ssb_gige_pci_read_config;
dev->pci_ops.write = ssb_gige_pci_write_config;
dev->io_resource.name = SSB_GIGE_IO_RES_NAME;
dev->io_resource.start = 0x800;
dev->io_resource.end = 0x8FF;
dev->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED;
if (!ssb_device_is_enabled(sdev))
ssb_device_enable(sdev, 0);
/* Setup BAR0. This is a 64k MMIO region. */
base = ssb_admatch_base(ssb_read32(sdev, SSB_ADMATCH1));
gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_0, base);
gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_1, 0);
dev->mem_resource.name = SSB_GIGE_MEM_RES_NAME;
dev->mem_resource.start = base;
dev->mem_resource.end = base + 0x10000 - 1;
dev->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
/* Enable the memory region. */
gige_pcicfg_write16(dev, PCI_COMMAND,
gige_pcicfg_read16(dev, PCI_COMMAND)
| PCI_COMMAND_MEMORY);
/* Write flushing is controlled by the Flush Status Control register.
* We want to flush every register write with a timeout and we want
* to disable the IRQ mask while flushing to avoid concurrency.
* Note that automatic write flushing does _not_ work from
* an IRQ handler. The driver must flush manually by reading a register.
*/
gige_write32(dev, SSB_GIGE_SHIM_FLUSHSTAT, 0x00000068);
/* Check if we have an RGMII or GMII PHY-bus.
* On RGMII do not bypass the DLLs */
tmslow = ssb_read32(sdev, SSB_TMSLOW);
tmshigh = ssb_read32(sdev, SSB_TMSHIGH);
if (tmshigh & SSB_GIGE_TMSHIGH_RGMII) {
tmslow &= ~SSB_GIGE_TMSLOW_TXBYPASS;
tmslow &= ~SSB_GIGE_TMSLOW_RXBYPASS;
dev->has_rgmii = 1;
} else {
tmslow |= SSB_GIGE_TMSLOW_TXBYPASS;
tmslow |= SSB_GIGE_TMSLOW_RXBYPASS;
dev->has_rgmii = 0;
}
tmslow |= SSB_GIGE_TMSLOW_DLLEN;
ssb_write32(sdev, SSB_TMSLOW, tmslow);
ssb_set_drvdata(sdev, dev);
register_pci_controller(&dev->pci_controller);
return 0;
}
bool pdev_is_ssb_gige_core(struct pci_dev *pdev)
{
if (!pdev->resource[0].name)
return 0;
return (strcmp(pdev->resource[0].name, SSB_GIGE_MEM_RES_NAME) == 0);
}
EXPORT_SYMBOL(pdev_is_ssb_gige_core);
int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
struct pci_dev *pdev)
{
struct ssb_gige *dev = ssb_get_drvdata(sdev);
struct resource *res;
if (pdev->bus->ops != &dev->pci_ops) {
/* The PCI device is not on this SSB GigE bridge device. */
return -ENODEV;
}
/* Fixup the PCI resources. */
res = &(pdev->resource[0]);
res->flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
res->name = dev->mem_resource.name;
res->start = dev->mem_resource.start;
res->end = dev->mem_resource.end;
/* Fixup interrupt lines. */
pdev->irq = ssb_mips_irq(sdev) + 2;
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq);
return 0;
}
int ssb_gige_map_irq(struct ssb_device *sdev,
const struct pci_dev *pdev)
{
struct ssb_gige *dev = ssb_get_drvdata(sdev);
if (pdev->bus->ops != &dev->pci_ops) {
/* The PCI device is not on this SSB GigE bridge device. */
return -ENODEV;
}
return ssb_mips_irq(sdev) + 2;
}
static struct ssb_driver ssb_gige_driver = {
.name = "BCM-GigE",
.id_table = ssb_gige_tbl,
.probe = ssb_gige_probe,
};
int ssb_gige_init(void)
{
return ssb_driver_register(&ssb_gige_driver);
}

View file

@ -209,6 +209,7 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
/* fallthrough */
case SSB_DEV_PCI:
case SSB_DEV_ETHERNET:
case SSB_DEV_ETHERNET_GBIT:
case SSB_DEV_80211:
case SSB_DEV_USB20_HOST:
/* These devices get their own IRQ line if available, the rest goes on IRQ0 */

View file

@ -60,77 +60,6 @@ static DEFINE_SPINLOCK(cfgspace_lock);
/* Core to access the external PCI config space. Can only have one. */
static struct ssb_pcicore *extpci_core;
static u32 ssb_pcicore_pcibus_iobase = 0x100;
static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA;
int pcibios_plat_dev_init(struct pci_dev *d)
{
struct resource *res;
int pos, size;
u32 *base;
ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
pci_name(d));
/* Fix up resource bases */
for (pos = 0; pos < 6; pos++) {
res = &d->resource[pos];
if (res->flags & IORESOURCE_IO)
base = &ssb_pcicore_pcibus_iobase;
else
base = &ssb_pcicore_pcibus_membase;
res->flags |= IORESOURCE_PCI_FIXED;
if (res->end) {
size = res->end - res->start + 1;
if (*base & (size - 1))
*base = (*base + size) & ~(size - 1);
res->start = *base;
res->end = res->start + size - 1;
*base += size;
pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start);
}
/* Fix up PCI bridge BAR0 only */
if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0)
break;
}
/* Fix up interrupt lines */
d->irq = ssb_mips_irq(extpci_core->dev) + 2;
pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
return 0;
}
static void __init ssb_fixup_pcibridge(struct pci_dev *dev)
{
u8 lat;
if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
return;
ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev));
/* Enable PCI bridge bus mastering and memory space */
pci_set_master(dev);
if (pcibios_enable_device(dev, ~0) < 0) {
ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
return;
}
/* Enable PCI bridge BAR1 prefetch and burst */
pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
/* Make sure our latency is high enough to handle the devices behind us */
lat = 168;
ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n",
pci_name(dev), lat);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_fixup_pcibridge);
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return ssb_mips_irq(extpci_core->dev) + 2;
}
static u32 get_cfgspace_addr(struct ssb_pcicore *pc,
unsigned int bus, unsigned int dev,
@ -320,6 +249,95 @@ static struct pci_controller ssb_pcicore_controller = {
.mem_offset = 0x24000000,
};
static u32 ssb_pcicore_pcibus_iobase = 0x100;
static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA;
/* This function is called when doing a pci_enable_device().
* We must first check if the device is a device on the PCI-core bridge. */
int ssb_pcicore_plat_dev_init(struct pci_dev *d)
{
struct resource *res;
int pos, size;
u32 *base;
if (d->bus->ops != &ssb_pcicore_pciops) {
/* This is not a device on the PCI-core bridge. */
return -ENODEV;
}
ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
pci_name(d));
/* Fix up resource bases */
for (pos = 0; pos < 6; pos++) {
res = &d->resource[pos];
if (res->flags & IORESOURCE_IO)
base = &ssb_pcicore_pcibus_iobase;
else
base = &ssb_pcicore_pcibus_membase;
res->flags |= IORESOURCE_PCI_FIXED;
if (res->end) {
size = res->end - res->start + 1;
if (*base & (size - 1))
*base = (*base + size) & ~(size - 1);
res->start = *base;
res->end = res->start + size - 1;
*base += size;
pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start);
}
/* Fix up PCI bridge BAR0 only */
if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0)
break;
}
/* Fix up interrupt lines */
d->irq = ssb_mips_irq(extpci_core->dev) + 2;
pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
return 0;
}
/* Early PCI fixup for a device on the PCI-core bridge. */
static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)
{
u8 lat;
if (dev->bus->ops != &ssb_pcicore_pciops) {
/* This is not a device on the PCI-core bridge. */
return;
}
if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
return;
ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev));
/* Enable PCI bridge bus mastering and memory space */
pci_set_master(dev);
if (pcibios_enable_device(dev, ~0) < 0) {
ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
return;
}
/* Enable PCI bridge BAR1 prefetch and burst */
pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
/* Make sure our latency is high enough to handle the devices behind us */
lat = 168;
ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n",
pci_name(dev), lat);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge);
/* PCI device IRQ mapping. */
int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
if (dev->bus->ops != &ssb_pcicore_pciops) {
/* This is not a device on the PCI-core bridge. */
return -ENODEV;
}
return ssb_mips_irq(extpci_core->dev) + 2;
}
static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
{
u32 val;

View file

@ -10,6 +10,9 @@
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_embedded.h>
#include <linux/ssb/ssb_driver_pci.h>
#include <linux/ssb/ssb_driver_gige.h>
#include <linux/pci.h>
#include "ssb_private.h"
@ -130,3 +133,90 @@ u32 ssb_gpio_polarity(struct ssb_bus *bus, u32 mask, u32 value)
return res;
}
EXPORT_SYMBOL(ssb_gpio_polarity);
#ifdef CONFIG_SSB_DRIVER_GIGE
static int gige_pci_init_callback(struct ssb_bus *bus, unsigned long data)
{
struct pci_dev *pdev = (struct pci_dev *)data;
struct ssb_device *dev;
unsigned int i;
int res;
for (i = 0; i < bus->nr_devices; i++) {
dev = &(bus->devices[i]);
if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT)
continue;
if (!dev->dev ||
!dev->dev->driver ||
!device_is_registered(dev->dev))
continue;
res = ssb_gige_pcibios_plat_dev_init(dev, pdev);
if (res >= 0)
return res;
}
return -ENODEV;
}
#endif /* CONFIG_SSB_DRIVER_GIGE */
int ssb_pcibios_plat_dev_init(struct pci_dev *dev)
{
int err;
err = ssb_pcicore_plat_dev_init(dev);
if (!err)
return 0;
#ifdef CONFIG_SSB_DRIVER_GIGE
err = ssb_for_each_bus_call((unsigned long)dev, gige_pci_init_callback);
if (err >= 0)
return err;
#endif
/* This is not a PCI device on any SSB device. */
return -ENODEV;
}
#ifdef CONFIG_SSB_DRIVER_GIGE
static int gige_map_irq_callback(struct ssb_bus *bus, unsigned long data)
{
const struct pci_dev *pdev = (const struct pci_dev *)data;
struct ssb_device *dev;
unsigned int i;
int res;
for (i = 0; i < bus->nr_devices; i++) {
dev = &(bus->devices[i]);
if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT)
continue;
if (!dev->dev ||
!dev->dev->driver ||
!device_is_registered(dev->dev))
continue;
res = ssb_gige_map_irq(dev, pdev);
if (res >= 0)
return res;
}
return -ENODEV;
}
#endif /* CONFIG_SSB_DRIVER_GIGE */
int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int res;
/* Check if this PCI device is a device on a SSB bus or device
* and return the IRQ number for it. */
res = ssb_pcicore_pcibios_map_irq(dev, slot, pin);
if (res >= 0)
return res;
#ifdef CONFIG_SSB_DRIVER_GIGE
res = ssb_for_each_bus_call((unsigned long)dev, gige_map_irq_callback);
if (res >= 0)
return res;
#endif
/* This is not a PCI device on any SSB device. */
return -ENODEV;
}

View file

@ -14,6 +14,7 @@
#include <linux/io.h>
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_regs.h>
#include <linux/ssb/ssb_driver_gige.h>
#include <linux/dma-mapping.h>
#include <linux/pci.h>
@ -68,6 +69,25 @@ found:
}
#endif /* CONFIG_SSB_PCIHOST */
int ssb_for_each_bus_call(unsigned long data,
int (*func)(struct ssb_bus *bus, unsigned long data))
{
struct ssb_bus *bus;
int res;
ssb_buses_lock();
list_for_each_entry(bus, &buses, list) {
res = func(bus, data);
if (res >= 0) {
ssb_buses_unlock();
return res;
}
}
ssb_buses_unlock();
return -ENODEV;
}
static struct ssb_device *ssb_device_get(struct ssb_device *dev)
{
if (dev)
@ -1171,7 +1191,14 @@ static int __init ssb_modinit(void)
err = b43_pci_ssb_bridge_init();
if (err) {
ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge "
"initialization failed");
"initialization failed\n");
/* don't fail SSB init because of this */
err = 0;
}
err = ssb_gige_init();
if (err) {
ssb_printk(KERN_ERR "SSB Broadcom Gigabit Ethernet "
"driver initialization failed\n");
/* don't fail SSB init because of this */
err = 0;
}
@ -1185,6 +1212,7 @@ fs_initcall(ssb_modinit);
static void __exit ssb_modexit(void)
{
ssb_gige_exit();
b43_pci_ssb_bridge_exit();
bus_unregister(&ssb_bustype);
}

View file

@ -118,6 +118,8 @@ extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m);
extern int ssb_devices_freeze(struct ssb_bus *bus);
extern int ssb_devices_thaw(struct ssb_bus *bus);
extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
int ssb_for_each_bus_call(unsigned long data,
int (*func)(struct ssb_bus *bus, unsigned long data));
/* b43_pci_bridge.c */
#ifdef CONFIG_SSB_B43_PCI_BRIDGE

View file

@ -97,6 +97,7 @@
#define IEEE80211_MAX_FRAME_LEN 2352
#define IEEE80211_MAX_SSID_LEN 32
#define IEEE80211_MAX_MESH_ID_LEN 32
struct ieee80211_hdr {
__le16 frame_control;
@ -109,6 +110,16 @@ struct ieee80211_hdr {
} __attribute__ ((packed));
struct ieee80211s_hdr {
u8 flags;
u8 ttl;
u8 seqnum[3];
u8 eaddr1[6];
u8 eaddr2[6];
u8 eaddr3[6];
} __attribute__ ((packed));
struct ieee80211_mgmt {
__le16 frame_control;
__le16 duration;
@ -206,6 +217,23 @@ struct ieee80211_mgmt {
__le16 params;
__le16 reason_code;
} __attribute__((packed)) delba;
struct{
u8 action_code;
/* capab_info for open and confirm,
* reason for close
*/
__le16 aux;
/* Followed in plink_confirm by status
* code, AID and supported rates,
* and directly by supported rates in
* plink_open and plink_close
*/
u8 variable[0];
} __attribute__((packed)) plink_action;
struct{
u8 action_code;
u8 variable[0];
} __attribute__((packed)) mesh_action;
} u;
} __attribute__ ((packed)) action;
} u;
@ -437,6 +465,13 @@ enum ieee80211_eid {
WLAN_EID_TS_DELAY = 43,
WLAN_EID_TCLAS_PROCESSING = 44,
WLAN_EID_QOS_CAPA = 46,
/* 802.11s */
WLAN_EID_MESH_CONFIG = 36, /* Pending IEEE 802.11 ANA approval */
WLAN_EID_MESH_ID = 37, /* Pending IEEE 802.11 ANA approval */
WLAN_EID_PEER_LINK = 40, /* Pending IEEE 802.11 ANA approval */
WLAN_EID_PREQ = 53, /* Pending IEEE 802.11 ANA approval */
WLAN_EID_PREP = 54, /* Pending IEEE 802.11 ANA approval */
WLAN_EID_PERR = 55, /* Pending IEEE 802.11 ANA approval */
/* 802.11h */
WLAN_EID_PWR_CONSTRAINT = 32,
WLAN_EID_PWR_CAPABILITY = 33,

View file

@ -78,6 +78,18 @@
* or, if no MAC address given, all stations, on the interface identified
* by %NL80211_ATTR_IFINDEX.
*
* @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
* %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
* %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
* the interface identified by %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
* or, if no MAC address given, all mesh paths, on the interface identified
* by %NL80211_ATTR_IFINDEX.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@ -112,6 +124,11 @@ enum nl80211_commands {
/* add commands here */
NL80211_CMD_GET_MPATH,
NL80211_CMD_SET_MPATH,
NL80211_CMD_NEW_MPATH,
NL80211_CMD_DEL_MPATH,
/* used to define NL80211_CMD_MAX below */
__NL80211_CMD_AFTER_LAST,
NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
@ -157,13 +174,21 @@ enum nl80211_commands {
* restriction (at most %NL80211_MAX_SUPP_RATES).
* @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
* to, or the AP interface the station was originally added to to.
* @NL80211_ATTR_STA_STATS: statistics for a station, part of station info
* @NL80211_ATTR_STA_INFO: information about a station, part of station info
* given for %NL80211_CMD_GET_STATION, nested attribute containing
* info as possible, see &enum nl80211_sta_stats.
* info as possible, see &enum nl80211_sta_info.
*
* @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
* consisting of a nested array.
*
* @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
* @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link.
* @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
* @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
* info given for %NL80211_CMD_GET_MPATH, nested attribute described at
* &enum nl80211_mpath_info.
*
*
* @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
* &enum nl80211_mntr_flags.
*
@ -199,7 +224,7 @@ enum nl80211_attrs {
NL80211_ATTR_STA_LISTEN_INTERVAL,
NL80211_ATTR_STA_SUPPORTED_RATES,
NL80211_ATTR_STA_VLAN,
NL80211_ATTR_STA_STATS,
NL80211_ATTR_STA_INFO,
NL80211_ATTR_WIPHY_BANDS,
@ -207,6 +232,11 @@ enum nl80211_attrs {
/* add attributes here, update the policy in nl80211.c */
NL80211_ATTR_MESH_ID,
NL80211_ATTR_STA_PLINK_ACTION,
NL80211_ATTR_MPATH_NEXT_HOP,
NL80211_ATTR_MPATH_INFO,
__NL80211_ATTR_AFTER_LAST,
NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
};
@ -223,6 +253,7 @@ enum nl80211_attrs {
* @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
* @NL80211_IFTYPE_WDS: wireless distribution interface
* @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
* @NL80211_IFTYPE_MESH_POINT: mesh point
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
* @__NL80211_IFTYPE_AFTER_LAST: internal use
*
@ -238,6 +269,7 @@ enum nl80211_iftype {
NL80211_IFTYPE_AP_VLAN,
NL80211_IFTYPE_WDS,
NL80211_IFTYPE_MONITOR,
NL80211_IFTYPE_MESH_POINT,
/* keep last */
__NL80211_IFTYPE_AFTER_LAST,
@ -267,27 +299,78 @@ enum nl80211_sta_flags {
};
/**
* enum nl80211_sta_stats - station statistics
* enum nl80211_sta_info - station information
*
* These attribute types are used with %NL80211_ATTR_STA_STATS
* These attribute types are used with %NL80211_ATTR_STA_INFO
* when getting information about a station.
*
* @__NL80211_STA_STAT_INVALID: attribute number 0 is reserved
* @NL80211_STA_STAT_INACTIVE_TIME: time since last activity (u32, msecs)
* @NL80211_STA_STAT_RX_BYTES: total received bytes (u32, from this station)
* @NL80211_STA_STAT_TX_BYTES: total transmitted bytes (u32, to this station)
* @__NL80211_STA_STAT_AFTER_LAST: internal
* @NL80211_STA_STAT_MAX: highest possible station stats attribute
* @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
* @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
* @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
* @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
enum nl80211_sta_stats {
__NL80211_STA_STAT_INVALID,
NL80211_STA_STAT_INACTIVE_TIME,
NL80211_STA_STAT_RX_BYTES,
NL80211_STA_STAT_TX_BYTES,
enum nl80211_sta_info {
__NL80211_STA_INFO_INVALID,
NL80211_STA_INFO_INACTIVE_TIME,
NL80211_STA_INFO_RX_BYTES,
NL80211_STA_INFO_TX_BYTES,
NL80211_STA_INFO_LLID,
NL80211_STA_INFO_PLID,
NL80211_STA_INFO_PLINK_STATE,
/* keep last */
__NL80211_STA_STAT_AFTER_LAST,
NL80211_STA_STAT_MAX = __NL80211_STA_STAT_AFTER_LAST - 1
__NL80211_STA_INFO_AFTER_LAST,
NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
};
/**
* enum nl80211_mpath_flags - nl80211 mesh path flags
*
* @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
* @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running
* @NL80211_MPATH_FLAG_DSN_VALID: the mesh path contains a valid DSN
* @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set
* @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded
*/
enum nl80211_mpath_flags {
NL80211_MPATH_FLAG_ACTIVE = 1<<0,
NL80211_MPATH_FLAG_RESOLVING = 1<<1,
NL80211_MPATH_FLAG_DSN_VALID = 1<<2,
NL80211_MPATH_FLAG_FIXED = 1<<3,
NL80211_MPATH_FLAG_RESOLVED = 1<<4,
};
/**
* enum nl80211_mpath_info - mesh path information
*
* These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting
* information about a mesh path.
*
* @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
* @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination
* @NL80211_ATTR_MPATH_DSN: destination sequence number
* @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path
* @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now
* @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in
* &enum nl80211_mpath_flags;
* @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
* @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries
*/
enum nl80211_mpath_info {
__NL80211_MPATH_INFO_INVALID,
NL80211_MPATH_INFO_FRAME_QLEN,
NL80211_MPATH_INFO_DSN,
NL80211_MPATH_INFO_METRIC,
NL80211_MPATH_INFO_EXPTIME,
NL80211_MPATH_INFO_FLAGS,
NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
NL80211_MPATH_INFO_DISCOVERY_RETRIES,
/* keep last */
__NL80211_MPATH_INFO_AFTER_LAST,
NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
};
/**

View file

@ -422,5 +422,12 @@ extern int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl);
extern u32 ssb_admatch_base(u32 adm);
extern u32 ssb_admatch_size(u32 adm);
/* PCI device mapping and fixup routines.
* Called from the architecture pcibios init code.
* These are only available on SSB_EMBEDDED configurations. */
#ifdef CONFIG_SSB_EMBEDDED
int ssb_pcibios_plat_dev_init(struct pci_dev *dev);
int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
#endif /* CONFIG_SSB_EMBEDDED */
#endif /* LINUX_SSB_H_ */

View file

@ -0,0 +1,174 @@
#ifndef LINUX_SSB_DRIVER_GIGE_H_
#define LINUX_SSB_DRIVER_GIGE_H_
#include <linux/ssb/ssb.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#ifdef CONFIG_SSB_DRIVER_GIGE
#define SSB_GIGE_PCIIO 0x0000 /* PCI I/O Registers (1024 bytes) */
#define SSB_GIGE_RESERVED 0x0400 /* Reserved (1024 bytes) */
#define SSB_GIGE_PCICFG 0x0800 /* PCI config space (256 bytes) */
#define SSB_GIGE_SHIM_FLUSHSTAT 0x0C00 /* PCI to OCP: Flush status control (32bit) */
#define SSB_GIGE_SHIM_FLUSHRDA 0x0C04 /* PCI to OCP: Flush read address (32bit) */
#define SSB_GIGE_SHIM_FLUSHTO 0x0C08 /* PCI to OCP: Flush timeout counter (32bit) */
#define SSB_GIGE_SHIM_BARRIER 0x0C0C /* PCI to OCP: Barrier register (32bit) */
#define SSB_GIGE_SHIM_MAOCPSI 0x0C10 /* PCI to OCP: MaocpSI Control (32bit) */
#define SSB_GIGE_SHIM_SIOCPMA 0x0C14 /* PCI to OCP: SiocpMa Control (32bit) */
/* TM Status High flags */
#define SSB_GIGE_TMSHIGH_RGMII 0x00010000 /* Have an RGMII PHY-bus */
/* TM Status Low flags */
#define SSB_GIGE_TMSLOW_TXBYPASS 0x00080000 /* TX bypass (no delay) */
#define SSB_GIGE_TMSLOW_RXBYPASS 0x00100000 /* RX bypass (no delay) */
#define SSB_GIGE_TMSLOW_DLLEN 0x01000000 /* Enable DLL controls */
/* Boardflags (low) */
#define SSB_GIGE_BFL_ROBOSWITCH 0x0010
#define SSB_GIGE_MEM_RES_NAME "SSB Broadcom 47xx GigE memory"
#define SSB_GIGE_IO_RES_NAME "SSB Broadcom 47xx GigE I/O"
struct ssb_gige {
struct ssb_device *dev;
spinlock_t lock;
/* True, if the device has an RGMII bus.
* False, if the device has a GMII bus. */
bool has_rgmii;
/* The PCI controller device. */
struct pci_controller pci_controller;
struct pci_ops pci_ops;
struct resource mem_resource;
struct resource io_resource;
};
/* Check whether a PCI device is a SSB Gigabit Ethernet core. */
extern bool pdev_is_ssb_gige_core(struct pci_dev *pdev);
/* Convert a pci_dev pointer to a ssb_gige pointer. */
static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev)
{
if (!pdev_is_ssb_gige_core(pdev))
return NULL;
return container_of(pdev->bus->ops, struct ssb_gige, pci_ops);
}
/* Returns whether the PHY is connected by an RGMII bus. */
static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev)
{
struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
return (dev ? dev->has_rgmii : 0);
}
/* Returns whether we have a Roboswitch. */
static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev)
{
struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
if (dev)
return !!(dev->dev->bus->sprom.boardflags_lo &
SSB_GIGE_BFL_ROBOSWITCH);
return 0;
}
/* Returns whether we can only do one DMA at once. */
static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev)
{
struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
if (dev)
return ((dev->dev->bus->chip_id == 0x4785) &&
(dev->dev->bus->chip_rev < 2));
return 0;
}
/* Returns whether we must flush posted writes. */
static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev)
{
struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
if (dev)
return (dev->dev->bus->chip_id == 0x4785);
return 0;
}
extern char * nvram_get(const char *name);
/* Get the device MAC address */
static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr)
{
#ifdef CONFIG_BCM947XX
char *res = nvram_get("et0macaddr");
if (res)
memcpy(macaddr, res, 6);
#endif
}
extern int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
struct pci_dev *pdev);
extern int ssb_gige_map_irq(struct ssb_device *sdev,
const struct pci_dev *pdev);
/* The GigE driver is not a standalone module, because we don't have support
* for unregistering the driver. So we could not unload the module anyway. */
extern int ssb_gige_init(void);
static inline void ssb_gige_exit(void)
{
/* Currently we can not unregister the GigE driver,
* because we can not unregister the PCI bridge. */
BUG();
}
#else /* CONFIG_SSB_DRIVER_GIGE */
/* Gigabit Ethernet driver disabled */
static inline int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
struct pci_dev *pdev)
{
return -ENOSYS;
}
static inline int ssb_gige_map_irq(struct ssb_device *sdev,
const struct pci_dev *pdev)
{
return -ENOSYS;
}
static inline int ssb_gige_init(void)
{
return 0;
}
static inline void ssb_gige_exit(void)
{
}
static inline bool pdev_is_ssb_gige_core(struct pci_dev *pdev)
{
return 0;
}
static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev)
{
return NULL;
}
static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev)
{
return 0;
}
static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev)
{
return 0;
}
static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev)
{
return 0;
}
static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev)
{
return 0;
}
#endif /* CONFIG_SSB_DRIVER_GIGE */
#endif /* LINUX_SSB_DRIVER_GIGE_H_ */

View file

@ -1,6 +1,11 @@
#ifndef LINUX_SSB_PCICORE_H_
#define LINUX_SSB_PCICORE_H_
#include <linux/types.h>
struct pci_dev;
#ifdef CONFIG_SSB_DRIVER_PCICORE
/* PCI core registers. */
@ -88,6 +93,9 @@ extern void ssb_pcicore_init(struct ssb_pcicore *pc);
extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
struct ssb_device *dev);
int ssb_pcicore_plat_dev_init(struct pci_dev *d);
int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
#else /* CONFIG_SSB_DRIVER_PCICORE */
@ -107,5 +115,16 @@ int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
return 0;
}
static inline
int ssb_pcicore_plat_dev_init(struct pci_dev *d)
{
return -ENODEV;
}
static inline
int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return -ENODEV;
}
#endif /* CONFIG_SSB_DRIVER_PCICORE */
#endif /* LINUX_SSB_PCICORE_H_ */

View file

@ -455,6 +455,7 @@
#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */
#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */
#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */
#define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */
/* Statistics flags (bitmask in updated) */
#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */

View file

@ -12,6 +12,16 @@
* Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
*/
/**
* struct vif_params - describes virtual interface parameters
* @mesh_id: mesh ID to use
* @mesh_id_len: length of the mesh ID
*/
struct vif_params {
u8 *mesh_id;
int mesh_id_len;
};
/* Radiotap header iteration
* implemented in net/wireless/radiotap.c
* docs in Documentation/networking/radiotap-headers.txt
@ -108,6 +118,19 @@ enum station_flags {
STATION_FLAG_WME = 1<<NL80211_STA_FLAG_WME,
};
/**
* enum plink_action - actions to perform in mesh peers
*
* @PLINK_ACTION_INVALID: action 0 is reserved
* @PLINK_ACTION_OPEN: start mesh peer link establishment
* @PLINK_ACTION_BLOCL: block traffic from this mesh peer
*/
enum plink_actions {
PLINK_ACTION_INVALID,
PLINK_ACTION_OPEN,
PLINK_ACTION_BLOCK,
};
/**
* struct station_parameters - station parameters
*
@ -128,39 +151,52 @@ struct station_parameters {
int listen_interval;
u16 aid;
u8 supported_rates_len;
u8 plink_action;
};
/**
* enum station_stats_flags - station statistics flags
* enum station_info_flags - station information flags
*
* Used by the driver to indicate which info in &struct station_stats
* it has filled in during get_station().
* Used by the driver to indicate which info in &struct station_info
* it has filled in during get_station() or dump_station().
*
* @STATION_STAT_INACTIVE_TIME: @inactive_time filled
* @STATION_STAT_RX_BYTES: @rx_bytes filled
* @STATION_STAT_TX_BYTES: @tx_bytes filled
* @STATION_INFO_INACTIVE_TIME: @inactive_time filled
* @STATION_INFO_RX_BYTES: @rx_bytes filled
* @STATION_INFO_TX_BYTES: @tx_bytes filled
* @STATION_INFO_LLID: @llid filled
* @STATION_INFO_PLID: @plid filled
* @STATION_INFO_PLINK_STATE: @plink_state filled
*/
enum station_stats_flags {
STATION_STAT_INACTIVE_TIME = 1<<0,
STATION_STAT_RX_BYTES = 1<<1,
STATION_STAT_TX_BYTES = 1<<2,
enum station_info_flags {
STATION_INFO_INACTIVE_TIME = 1<<0,
STATION_INFO_RX_BYTES = 1<<1,
STATION_INFO_TX_BYTES = 1<<2,
STATION_INFO_LLID = 1<<3,
STATION_INFO_PLID = 1<<4,
STATION_INFO_PLINK_STATE = 1<<5,
};
/**
* struct station_stats - station statistics
* struct station_info - station information
*
* Station information filled by driver for get_station().
* Station information filled by driver for get_station() and dump_station.
*
* @filled: bitflag of flags from &enum station_stats_flags
* @filled: bitflag of flags from &enum station_info_flags
* @inactive_time: time since last station activity (tx/rx) in milliseconds
* @rx_bytes: bytes received from this station
* @tx_bytes: bytes transmitted to this station
* @llid: mesh local link id
* @plid: mesh peer link id
* @plink_state: mesh peer link state
*/
struct station_stats {
struct station_info {
u32 filled;
u32 inactive_time;
u32 rx_bytes;
u32 tx_bytes;
u16 llid;
u16 plid;
u8 plink_state;
};
/**
@ -183,6 +219,56 @@ enum monitor_flags {
MONITOR_FLAG_COOK_FRAMES = 1<<NL80211_MNTR_FLAG_COOK_FRAMES,
};
/**
* enum mpath_info_flags - mesh path information flags
*
* Used by the driver to indicate which info in &struct mpath_info it has filled
* in during get_station() or dump_station().
*
* MPATH_INFO_FRAME_QLEN: @frame_qlen filled
* MPATH_INFO_DSN: @dsn filled
* MPATH_INFO_METRIC: @metric filled
* MPATH_INFO_EXPTIME: @exptime filled
* MPATH_INFO_DISCOVERY_TIMEOUT: @discovery_timeout filled
* MPATH_INFO_DISCOVERY_RETRIES: @discovery_retries filled
* MPATH_INFO_FLAGS: @flags filled
*/
enum mpath_info_flags {
MPATH_INFO_FRAME_QLEN = BIT(0),
MPATH_INFO_DSN = BIT(1),
MPATH_INFO_METRIC = BIT(2),
MPATH_INFO_EXPTIME = BIT(3),
MPATH_INFO_DISCOVERY_TIMEOUT = BIT(4),
MPATH_INFO_DISCOVERY_RETRIES = BIT(5),
MPATH_INFO_FLAGS = BIT(6),
};
/**
* struct mpath_info - mesh path information
*
* Mesh path information filled by driver for get_mpath() and dump_mpath().
*
* @filled: bitfield of flags from &enum mpath_info_flags
* @frame_qlen: number of queued frames for this destination
* @dsn: destination sequence number
* @metric: metric (cost) of this mesh path
* @exptime: expiration time for the mesh path from now, in msecs
* @flags: mesh path flags
* @discovery_timeout: total mesh path discovery timeout, in msecs
* @discovery_retries: mesh path discovery retries
*/
struct mpath_info {
u32 filled;
u32 frame_qlen;
u32 dsn;
u32 metric;
u32 exptime;
u32 discovery_timeout;
u8 discovery_retries;
u8 flags;
};
/* from net/wireless.h */
struct wiphy;
@ -230,13 +316,17 @@ struct wiphy;
* @del_station: Remove a station; @mac may be NULL to remove all stations.
*
* @change_station: Modify a given station.
*
* @set_mesh_cfg: set mesh parameters (by now, just mesh id)
*/
struct cfg80211_ops {
int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
enum nl80211_iftype type, u32 *flags);
enum nl80211_iftype type, u32 *flags,
struct vif_params *params);
int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex);
int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex,
enum nl80211_iftype type, u32 *flags);
enum nl80211_iftype type, u32 *flags,
struct vif_params *params);
int (*add_key)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, u8 *mac_addr,
@ -264,7 +354,22 @@ struct cfg80211_ops {
int (*change_station)(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_parameters *params);
int (*get_station)(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_stats *stats);
u8 *mac, struct station_info *sinfo);
int (*dump_station)(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *mac, struct station_info *sinfo);
int (*add_mpath)(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop);
int (*del_mpath)(struct wiphy *wiphy, struct net_device *dev,
u8 *dst);
int (*change_mpath)(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop);
int (*get_mpath)(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop,
struct mpath_info *pinfo);
int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *dst, u8 *next_hop,
struct mpath_info *pinfo);
};
#endif /* __NET_CFG80211_H */

View file

@ -205,6 +205,62 @@ struct ieee80211_bss_conf {
bool use_short_preamble;
};
/**
* enum mac80211_tx_control_flags - flags to describe Tx configuration for
* the Tx frame
*
* These flags are used with the @flags member of &ieee80211_tx_control
*
* @IEEE80211_TXCTL_REQ_TX_STATUS: request TX status callback for this frame.
* @IEEE80211_TXCTL_DO_NOT_ENCRYPT: send this frame without encryption;
* e.g., for EAPOL frame
* @IEEE80211_TXCTL_USE_RTS_CTS: use RTS-CTS before sending frame
* @IEEE80211_TXCTL_USE_CTS_PROTECT: use CTS protection for the frame (e.g.,
* for combined 802.11g / 802.11b networks)
* @IEEE80211_TXCTL_NO_ACK: tell the low level not to wait for an ack
* @IEEE80211_TXCTL_RATE_CTRL_PROBE
* @EEE80211_TXCTL_CLEAR_PS_FILT: clear powersave filter
* for destination station
* @IEEE80211_TXCTL_REQUEUE:
* @IEEE80211_TXCTL_FIRST_FRAGMENT: this is a first fragment of the frame
* @IEEE80211_TXCTL_LONG_RETRY_LIMIT: this frame should be send using the
* through set_retry_limit configured long
* retry value
* @IEEE80211_TXCTL_EAPOL_FRAME: internal to mac80211
* @IEEE80211_TXCTL_SEND_AFTER_DTIM: send this frame after DTIM beacon
* @IEEE80211_TXCTL_AMPDU: this frame should be sent as part of an A-MPDU
* @IEEE80211_TXCTL_OFDM_HT: this frame can be sent in HT OFDM rates. number
* of streams when this flag is on can be extracted
* from antenna_sel_tx, so if 1 antenna is marked
* use SISO, 2 antennas marked use MIMO, n antennas
* marked use MIMO_n.
* @IEEE80211_TXCTL_GREEN_FIELD: use green field protection for this frame
* @IEEE80211_TXCTL_40_MHZ_WIDTH: send this frame using 40 Mhz channel width
* @IEEE80211_TXCTL_DUP_DATA: duplicate data frame on both 20 Mhz channels
* @IEEE80211_TXCTL_SHORT_GI: send this frame using short guard interval
*/
enum mac80211_tx_control_flags {
IEEE80211_TXCTL_REQ_TX_STATUS = (1<<0),
IEEE80211_TXCTL_DO_NOT_ENCRYPT = (1<<1),
IEEE80211_TXCTL_USE_RTS_CTS = (1<<2),
IEEE80211_TXCTL_USE_CTS_PROTECT = (1<<3),
IEEE80211_TXCTL_NO_ACK = (1<<4),
IEEE80211_TXCTL_RATE_CTRL_PROBE = (1<<5),
IEEE80211_TXCTL_CLEAR_PS_FILT = (1<<6),
IEEE80211_TXCTL_REQUEUE = (1<<7),
IEEE80211_TXCTL_FIRST_FRAGMENT = (1<<8),
IEEE80211_TXCTL_SHORT_PREAMBLE = (1<<9),
IEEE80211_TXCTL_LONG_RETRY_LIMIT = (1<<10),
IEEE80211_TXCTL_EAPOL_FRAME = (1<<11),
IEEE80211_TXCTL_SEND_AFTER_DTIM = (1<<12),
IEEE80211_TXCTL_AMPDU = (1<<13),
IEEE80211_TXCTL_OFDM_HT = (1<<14),
IEEE80211_TXCTL_GREEN_FIELD = (1<<15),
IEEE80211_TXCTL_40_MHZ_WIDTH = (1<<16),
IEEE80211_TXCTL_DUP_DATA = (1<<17),
IEEE80211_TXCTL_SHORT_GI = (1<<18),
};
/* Transmit control fields. This data structure is passed to low-level driver
* with each TX frame. The low-level driver is responsible for configuring
* the hardware to use given values (depending on what is supported). */
@ -219,42 +275,14 @@ struct ieee80211_tx_control {
/* retry rate for the last retries */
struct ieee80211_rate *alt_retry_rate;
#define IEEE80211_TXCTL_REQ_TX_STATUS (1<<0)/* request TX status callback for
* this frame */
#define IEEE80211_TXCTL_DO_NOT_ENCRYPT (1<<1) /* send this frame without
* encryption; e.g., for EAPOL
* frames */
#define IEEE80211_TXCTL_USE_RTS_CTS (1<<2) /* use RTS-CTS before sending
* frame */
#define IEEE80211_TXCTL_USE_CTS_PROTECT (1<<3) /* use CTS protection for the
* frame (e.g., for combined
* 802.11g / 802.11b networks) */
#define IEEE80211_TXCTL_NO_ACK (1<<4) /* tell the low level not to
* wait for an ack */
#define IEEE80211_TXCTL_RATE_CTRL_PROBE (1<<5)
#define IEEE80211_TXCTL_CLEAR_PS_FILT (1<<6) /* clear powersave filter
* for destination station */
#define IEEE80211_TXCTL_REQUEUE (1<<7)
#define IEEE80211_TXCTL_FIRST_FRAGMENT (1<<8) /* this is a first fragment of
* the frame */
#define IEEE80211_TXCTL_SHORT_PREAMBLE (1<<9)
#define IEEE80211_TXCTL_LONG_RETRY_LIMIT (1<<10) /* this frame should be send
* using the through
* set_retry_limit configured
* long retry value */
#define IEEE80211_TXCTL_EAPOL_FRAME (1<<11) /* internal to mac80211 */
#define IEEE80211_TXCTL_SEND_AFTER_DTIM (1<<12) /* send this frame after DTIM
* beacon */
#define IEEE80211_TXCTL_AMPDU (1<<13) /* this frame should be sent
* as part of an A-MPDU */
u32 flags; /* tx control flags defined
* above */
u32 flags; /* tx control flags defined above */
u8 key_idx; /* keyidx from hw->set_key(), undefined if
* IEEE80211_TXCTL_DO_NOT_ENCRYPT is set */
u8 retry_limit; /* 1 = only first attempt, 2 = one retry, ..
* This could be used when set_retry_limit
* is not implemented by the driver */
u8 antenna_sel_tx; /* 0 = default/diversity, 1 = Ant0, 2 = Ant1 */
u8 antenna_sel_tx; /* 0 = default/diversity, otherwise bit
* position represents antenna number used */
u8 icv_len; /* length of the ICV/MIC field in octets */
u8 iv_len; /* length of the IV field in octets */
u8 queue; /* hardware queue to use for this frame;
@ -407,7 +435,6 @@ enum ieee80211_conf_flags {
* @channel: the channel to tune to
*/
struct ieee80211_conf {
unsigned int regulatory_domain;
int radio_enabled;
int beacon_int;
@ -437,12 +464,14 @@ struct ieee80211_conf {
* @IEEE80211_IF_TYPE_WDS: interface in WDS mode.
* @IEEE80211_IF_TYPE_VLAN: VLAN interface bound to an AP, drivers
* will never see this type.
* @IEEE80211_IF_TYPE_MESH_POINT: 802.11s mesh point
*/
enum ieee80211_if_types {
IEEE80211_IF_TYPE_INVALID,
IEEE80211_IF_TYPE_AP,
IEEE80211_IF_TYPE_STA,
IEEE80211_IF_TYPE_IBSS,
IEEE80211_IF_TYPE_MESH_POINT,
IEEE80211_IF_TYPE_MNTR,
IEEE80211_IF_TYPE_WDS,
IEEE80211_IF_TYPE_VLAN,
@ -464,6 +493,14 @@ struct ieee80211_vif {
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};
static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
{
#ifdef CONFIG_MAC80211_MESH
return vif->type == IEEE80211_IF_TYPE_MESH_POINT;
#endif
return false;
}
/**
* struct ieee80211_if_init_conf - initial configuration of an interface
*
@ -1087,8 +1124,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
/**
* ieee80211_register_hw - Register hardware device
*
* You must call this function before any other functions
* except ieee80211_register_hwmode.
* You must call this function before any other functions in
* mac80211. Note that before a hardware can be registered, you
* need to fill the contained wiphy's information.
*
* @hw: the device to register as returned by ieee80211_alloc_hw()
*/

View file

@ -81,6 +81,15 @@ config MAC80211_RC_SIMPLE
Say N unless you know what you are doing.
endmenu
config MAC80211_MESH
bool "Enable mac80211 mesh networking (pre-802.11s) support"
depends on MAC80211 && EXPERIMENTAL
---help---
This options enables support of Draft 802.11s mesh networking.
The implementation is based on Draft 1.08 of the Mesh Networking
amendment. For more information visit http://o11s.org/.
config MAC80211_LEDS
bool "Enable LED triggers"
depends on MAC80211 && LEDS_TRIGGERS
@ -166,3 +175,10 @@ config MAC80211_VERBOSE_PS_DEBUG
---help---
Say Y here to print out verbose powersave
mode debug messages.
config MAC80211_VERBOSE_MPL_DEBUG
bool "Verbose mesh peer link debugging"
depends on MAC80211_DEBUG && MAC80211_MESH
---help---
Say Y here to print out verbose mesh peer link
debug messages.

View file

@ -36,6 +36,12 @@ mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
debugfs_netdev.o \
debugfs_key.o
mac80211-$(CONFIG_MAC80211_MESH) += \
mesh.o \
mesh_pathtbl.o \
mesh_plink.o \
mesh_hwmp.o
# Build rate control algorithm(s)
CFLAGS_rc80211_simple.o += -DRC80211_SIMPLE_COMPILE

View file

@ -15,6 +15,7 @@
#include "ieee80211_i.h"
#include "cfg.h"
#include "ieee80211_rate.h"
#include "mesh.h"
static enum ieee80211_if_types
nl80211_type_to_mac80211_type(enum nl80211_iftype type)
@ -28,13 +29,18 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type)
return IEEE80211_IF_TYPE_STA;
case NL80211_IFTYPE_MONITOR:
return IEEE80211_IF_TYPE_MNTR;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
return IEEE80211_IF_TYPE_MESH_POINT;
#endif
default:
return IEEE80211_IF_TYPE_INVALID;
}
}
static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
enum nl80211_iftype type, u32 *flags)
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
enum ieee80211_if_types itype;
@ -49,7 +55,7 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
if (itype == IEEE80211_IF_TYPE_INVALID)
return -EINVAL;
err = ieee80211_if_add(local->mdev, name, &dev, itype);
err = ieee80211_if_add(local->mdev, name, &dev, itype, params);
if (err || itype != IEEE80211_IF_TYPE_MNTR || !flags)
return err;
@ -78,7 +84,8 @@ static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
}
static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
enum nl80211_iftype type, u32 *flags)
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct net_device *dev;
@ -108,6 +115,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
ieee80211_if_reinit(dev);
ieee80211_if_set_type(dev, itype);
if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len)
ieee80211_if_sta_set_mesh_id(&sdata->u.sta,
params->mesh_id_len,
params->mesh_id);
if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags)
return 0;
@ -122,7 +134,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta = NULL;
enum ieee80211_key_alg alg;
int ret;
struct ieee80211_key *key;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@ -156,12 +167,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
ieee80211_key_link(key, sdata, sta);
ret = 0;
if (sta)
sta_info_put(sta);
return ret;
return 0;
}
static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
@ -170,7 +176,6 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
int ret;
struct ieee80211_key *key;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@ -181,21 +186,18 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
ret = 0;
if (sta->key) {
key = sta->key;
ieee80211_key_free(key);
ieee80211_key_free(sta->key);
WARN_ON(sta->key);
} else
ret = -ENOENT;
sta_info_put(sta);
return ret;
}
if (!sdata->keys[key_idx])
return -ENOENT;
key = sdata->keys[key_idx];
ieee80211_key_free(key);
ieee80211_key_free(sdata->keys[key_idx]);
WARN_ON(sdata->keys[key_idx]);
return 0;
@ -278,8 +280,6 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
err = 0;
out:
if (sta)
sta_info_put(sta);
return err;
}
@ -295,29 +295,73 @@ static int ieee80211_config_default_key(struct wiphy *wiphy,
return 0;
}
static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_stats *stats)
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
STATION_INFO_TX_BYTES;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->rx_bytes = sta->rx_bytes;
sinfo->tx_bytes = sta->tx_bytes;
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
sinfo->filled |= STATION_INFO_LLID |
STATION_INFO_PLID |
STATION_INFO_PLINK_STATE;
sinfo->llid = le16_to_cpu(sta->llid);
sinfo->plid = le16_to_cpu(sta->plid);
sinfo->plink_state = sta->plink_state;
#endif
}
}
static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *mac, struct station_info *sinfo)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
int ret = -ENOENT;
sta = sta_info_get(local, mac);
if (!sta)
return -ENOENT;
rcu_read_lock();
sta = sta_info_get_by_idx(local, idx, dev);
if (sta) {
ret = 0;
memcpy(mac, sta->addr, ETH_ALEN);
sta_set_sinfo(sta, sinfo);
}
rcu_read_unlock();
return ret;
}
static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_info *sinfo)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
int ret = -ENOENT;
rcu_read_lock();
/* XXX: verify sta->dev == dev */
stats->filled = STATION_STAT_INACTIVE_TIME |
STATION_STAT_RX_BYTES |
STATION_STAT_TX_BYTES;
sta = sta_info_get(local, mac);
if (sta) {
ret = 0;
sta_set_sinfo(sta, sinfo);
}
stats->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
stats->rx_bytes = sta->rx_bytes;
stats->tx_bytes = sta->tx_bytes;
rcu_read_unlock();
sta_info_put(sta);
return 0;
return ret;
}
/*
@ -510,8 +554,8 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */
skb->dev = sta->dev;
skb->protocol = eth_type_trans(skb, sta->dev);
skb->dev = sta->sdata->dev;
skb->protocol = eth_type_trans(skb, sta->sdata->dev);
memset(skb->cb, 0, sizeof(skb->cb));
netif_rx(skb);
}
@ -523,6 +567,13 @@ static void sta_apply_parameters(struct ieee80211_local *local,
u32 rates;
int i, j;
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
/*
* FIXME: updating the flags is racy when this function is
* called from ieee80211_change_station(), this will
* be resolved in a future patch.
*/
if (params->station_flags & STATION_FLAG_CHANGED) {
sta->flags &= ~WLAN_STA_AUTHORIZED;
@ -538,6 +589,13 @@ static void sta_apply_parameters(struct ieee80211_local *local,
sta->flags |= WLAN_STA_WME;
}
/*
* FIXME: updating the following information is racy when this
* function is called from ieee80211_change_station().
* However, all this information should be static so
* maybe we should just reject attemps to change it.
*/
if (params->aid) {
sta->aid = params->aid;
if (sta->aid > IEEE80211_MAX_AID)
@ -560,6 +618,17 @@ static void sta_apply_parameters(struct ieee80211_local *local,
}
sta->supp_rates[local->oper_channel->band] = rates;
}
if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
switch (params->plink_action) {
case PLINK_ACTION_OPEN:
mesh_plink_open(sta);
break;
case PLINK_ACTION_BLOCK:
mesh_plink_block(sta);
break;
}
}
}
static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
@ -568,6 +637,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
int err;
/* Prevent a race with changing the rate control algorithm */
if (!netif_running(dev))
@ -582,14 +652,15 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
} else
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
sta = sta_info_add(local, dev, mac, GFP_KERNEL);
if (IS_ERR(sta))
return PTR_ERR(sta);
if (compare_ether_addr(mac, dev->dev_addr) == 0)
return -EINVAL;
sta->dev = sdata->dev;
if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
sdata->vif.type == IEEE80211_IF_TYPE_AP)
ieee80211_send_layer2_update(sta);
if (is_multicast_ether_addr(mac))
return -EINVAL;
sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
if (!sta)
return -ENOMEM;
sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
@ -597,7 +668,20 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
rate_control_rate_init(sta, local);
sta_info_put(sta);
rcu_read_lock();
err = sta_info_insert(sta);
if (err) {
sta_info_destroy(sta);
rcu_read_unlock();
return err;
}
if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
sdata->vif.type == IEEE80211_IF_TYPE_AP)
ieee80211_send_layer2_update(sta);
rcu_read_unlock();
return 0;
}
@ -605,7 +689,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
if (mac) {
@ -614,10 +699,14 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
return -ENOENT;
sta_info_free(sta);
sta_info_put(sta);
sta_info_unlink(&sta);
if (sta) {
synchronize_rcu();
sta_info_destroy(sta);
}
} else
sta_info_flush(local, dev);
sta_info_flush(local, sdata);
return 0;
}
@ -636,24 +725,191 @@ static int ieee80211_change_station(struct wiphy *wiphy,
if (!sta)
return -ENOENT;
if (params->vlan && params->vlan != sta->dev) {
if (params->vlan && params->vlan != sta->sdata->dev) {
vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
vlansdata->vif.type != IEEE80211_IF_TYPE_AP)
return -EINVAL;
sta->dev = params->vlan;
sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
ieee80211_send_layer2_update(sta);
}
sta_apply_parameters(local, sta, params);
sta_info_put(sta);
return 0;
}
#ifdef CONFIG_MAC80211_MESH
static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath;
struct sta_info *sta;
int err;
if (!netif_running(dev))
return -ENETDOWN;
if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
sta = sta_info_get(local, next_hop);
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
err = mesh_path_add(dst, dev);
if (err) {
rcu_read_unlock();
return err;
}
mpath = mesh_path_lookup(dst, dev);
if (!mpath) {
rcu_read_unlock();
return -ENXIO;
}
mesh_path_fix_nexthop(mpath, sta);
rcu_read_unlock();
return 0;
}
static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
u8 *dst)
{
if (dst)
return mesh_path_del(dst, dev);
mesh_path_flush(dev);
return 0;
}
static int ieee80211_change_mpath(struct wiphy *wiphy,
struct net_device *dev,
u8 *dst, u8 *next_hop)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath;
struct sta_info *sta;
if (!netif_running(dev))
return -ENETDOWN;
if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
sta = sta_info_get(local, next_hop);
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
mpath = mesh_path_lookup(dst, dev);
if (!mpath) {
rcu_read_unlock();
return -ENOENT;
}
mesh_path_fix_nexthop(mpath, sta);
rcu_read_unlock();
return 0;
}
static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
struct mpath_info *pinfo)
{
if (mpath->next_hop)
memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN);
else
memset(next_hop, 0, ETH_ALEN);
pinfo->filled = MPATH_INFO_FRAME_QLEN |
MPATH_INFO_DSN |
MPATH_INFO_METRIC |
MPATH_INFO_EXPTIME |
MPATH_INFO_DISCOVERY_TIMEOUT |
MPATH_INFO_DISCOVERY_RETRIES |
MPATH_INFO_FLAGS;
pinfo->frame_qlen = mpath->frame_queue.qlen;
pinfo->dsn = mpath->dsn;
pinfo->metric = mpath->metric;
if (time_before(jiffies, mpath->exp_time))
pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
pinfo->discovery_timeout =
jiffies_to_msecs(mpath->discovery_timeout);
pinfo->discovery_retries = mpath->discovery_retries;
pinfo->flags = 0;
if (mpath->flags & MESH_PATH_ACTIVE)
pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
if (mpath->flags & MESH_PATH_RESOLVING)
pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
if (mpath->flags & MESH_PATH_DSN_VALID)
pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID;
if (mpath->flags & MESH_PATH_FIXED)
pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
if (mpath->flags & MESH_PATH_RESOLVING)
pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
pinfo->flags = mpath->flags;
}
static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath;
if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
mpath = mesh_path_lookup(dst, dev);
if (!mpath) {
rcu_read_unlock();
return -ENOENT;
}
memcpy(dst, mpath->dst, ETH_ALEN);
mpath_set_pinfo(mpath, next_hop, pinfo);
rcu_read_unlock();
return 0;
}
static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *dst, u8 *next_hop,
struct mpath_info *pinfo)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath;
if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
mpath = mesh_path_lookup_by_idx(idx, dev);
if (!mpath) {
rcu_read_unlock();
return -ENOENT;
}
memcpy(dst, mpath->dst, ETH_ALEN);
mpath_set_pinfo(mpath, next_hop, pinfo);
rcu_read_unlock();
return 0;
}
#endif
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@ -669,4 +925,12 @@ struct cfg80211_ops mac80211_config_ops = {
.del_station = ieee80211_del_station,
.change_station = ieee80211_change_station,
.get_station = ieee80211_get_station,
.dump_station = ieee80211_dump_station,
#ifdef CONFIG_MAC80211_MESH
.add_mpath = ieee80211_add_mpath,
.del_mpath = ieee80211_del_mpath,
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
#endif
};

View file

@ -39,6 +39,29 @@ static ssize_t ieee80211_if_read(
return ret;
}
#ifdef CONFIG_MAC80211_MESH
static ssize_t ieee80211_if_write(
struct ieee80211_sub_if_data *sdata,
char const __user *userbuf,
size_t count, loff_t *ppos,
int (*format)(struct ieee80211_sub_if_data *, char *))
{
char buf[10];
int buf_size;
memset(buf, 0x00, sizeof(buf));
buf_size = min(count, (sizeof(buf)-1));
read_lock(&dev_base_lock);
if (copy_from_user(buf, userbuf, buf_size))
goto endwrite;
if (sdata->dev->reg_state == NETREG_REGISTERED)
(*format)(sdata, buf);
endwrite:
read_unlock(&dev_base_lock);
return count;
}
#endif
#define IEEE80211_IF_FMT(name, field, format_string) \
static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
@ -46,6 +69,19 @@ static ssize_t ieee80211_if_fmt_##name( \
{ \
return scnprintf(buf, buflen, format_string, sdata->field); \
}
#define IEEE80211_IF_WFMT(name, field, type) \
static int ieee80211_if_wfmt_##name( \
struct ieee80211_sub_if_data *sdata, char *buf) \
{ \
unsigned long tmp; \
char *endp; \
\
tmp = simple_strtoul(buf, &endp, 0); \
if ((endp == buf) || ((type)tmp != tmp)) \
return -EINVAL; \
sdata->field = tmp; \
return 0; \
}
#define IEEE80211_IF_FMT_DEC(name, field) \
IEEE80211_IF_FMT(name, field, "%d\n")
#define IEEE80211_IF_FMT_HEX(name, field) \
@ -88,6 +124,34 @@ static const struct file_operations name##_ops = { \
IEEE80211_IF_FMT_##format(name, field) \
__IEEE80211_IF_FILE(name)
#define __IEEE80211_IF_WFILE(name) \
static ssize_t ieee80211_if_read_##name(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
return ieee80211_if_read(file->private_data, \
userbuf, count, ppos, \
ieee80211_if_fmt_##name); \
} \
static ssize_t ieee80211_if_write_##name(struct file *file, \
const char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
return ieee80211_if_write(file->private_data, \
userbuf, count, ppos, \
ieee80211_if_wfmt_##name); \
} \
static const struct file_operations name##_ops = { \
.read = ieee80211_if_read_##name, \
.write = ieee80211_if_write_##name, \
.open = mac80211_open_file_generic, \
}
#define IEEE80211_IF_WFILE(name, field, format, type) \
IEEE80211_IF_FMT_##format(name, field) \
IEEE80211_IF_WFMT(name, field, type) \
__IEEE80211_IF_WFILE(name)
/* common attributes */
IEEE80211_IF_FILE(channel_use, channel_use, DEC);
IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
@ -106,6 +170,7 @@ IEEE80211_IF_FILE(assoc_tries, u.sta.assoc_tries, DEC);
IEEE80211_IF_FILE(auth_algs, u.sta.auth_algs, HEX);
IEEE80211_IF_FILE(auth_alg, u.sta.auth_alg, DEC);
IEEE80211_IF_FILE(auth_transaction, u.sta.auth_transaction, DEC);
IEEE80211_IF_FILE(num_beacons_sta, u.sta.num_beacons, DEC);
static ssize_t ieee80211_if_fmt_flags(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@ -139,6 +204,42 @@ __IEEE80211_IF_FILE(num_buffered_multicast);
/* WDS attributes */
IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
#ifdef CONFIG_MAC80211_MESH
/* Mesh stats attributes */
IEEE80211_IF_FILE(fwded_frames, u.sta.mshstats.fwded_frames, DEC);
IEEE80211_IF_FILE(dropped_frames_ttl, u.sta.mshstats.dropped_frames_ttl, DEC);
IEEE80211_IF_FILE(dropped_frames_no_route,
u.sta.mshstats.dropped_frames_no_route, DEC);
IEEE80211_IF_FILE(estab_plinks, u.sta.mshstats.estab_plinks, ATOMIC);
/* Mesh parameters */
IEEE80211_IF_WFILE(dot11MeshMaxRetries,
u.sta.mshcfg.dot11MeshMaxRetries, DEC, u8);
IEEE80211_IF_WFILE(dot11MeshRetryTimeout,
u.sta.mshcfg.dot11MeshRetryTimeout, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshConfirmTimeout,
u.sta.mshcfg.dot11MeshConfirmTimeout, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshHoldingTimeout,
u.sta.mshcfg.dot11MeshHoldingTimeout, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshTTL, u.sta.mshcfg.dot11MeshTTL, DEC, u8);
IEEE80211_IF_WFILE(auto_open_plinks, u.sta.mshcfg.auto_open_plinks, DEC, bool);
IEEE80211_IF_WFILE(dot11MeshMaxPeerLinks,
u.sta.mshcfg.dot11MeshMaxPeerLinks, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshHWMPactivePathTimeout,
u.sta.mshcfg.dot11MeshHWMPactivePathTimeout, DEC, u32);
IEEE80211_IF_WFILE(dot11MeshHWMPpreqMinInterval,
u.sta.mshcfg.dot11MeshHWMPpreqMinInterval, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshHWMPnetDiameterTraversalTime,
u.sta.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshHWMPmaxPREQretries,
u.sta.mshcfg.dot11MeshHWMPmaxPREQretries, DEC, u8);
IEEE80211_IF_WFILE(path_refresh_time,
u.sta.mshcfg.path_refresh_time, DEC, u32);
IEEE80211_IF_WFILE(min_discovery_timeout,
u.sta.mshcfg.min_discovery_timeout, DEC, u16);
#endif
#define DEBUGFS_ADD(name, type)\
sdata->debugfs.type.name = debugfs_create_file(#name, 0444,\
sdata->debugfsdir, sdata, &name##_ops);
@ -161,6 +262,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(auth_alg, sta);
DEBUGFS_ADD(auth_transaction, sta);
DEBUGFS_ADD(flags, sta);
DEBUGFS_ADD(num_beacons_sta, sta);
}
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
@ -192,12 +294,57 @@ static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
{
}
#ifdef CONFIG_MAC80211_MESH
#define MESHSTATS_ADD(name)\
sdata->mesh_stats.name = debugfs_create_file(#name, 0444,\
sdata->mesh_stats_dir, sdata, &name##_ops);
static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
{
sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats",
sdata->debugfsdir);
MESHSTATS_ADD(fwded_frames);
MESHSTATS_ADD(dropped_frames_ttl);
MESHSTATS_ADD(dropped_frames_no_route);
MESHSTATS_ADD(estab_plinks);
}
#define MESHPARAMS_ADD(name)\
sdata->mesh_config.name = debugfs_create_file(#name, 0644,\
sdata->mesh_config_dir, sdata, &name##_ops);
static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
{
sdata->mesh_config_dir = debugfs_create_dir("mesh_config",
sdata->debugfsdir);
MESHPARAMS_ADD(dot11MeshMaxRetries);
MESHPARAMS_ADD(dot11MeshRetryTimeout);
MESHPARAMS_ADD(dot11MeshConfirmTimeout);
MESHPARAMS_ADD(dot11MeshHoldingTimeout);
MESHPARAMS_ADD(dot11MeshTTL);
MESHPARAMS_ADD(auto_open_plinks);
MESHPARAMS_ADD(dot11MeshMaxPeerLinks);
MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout);
MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval);
MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime);
MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries);
MESHPARAMS_ADD(path_refresh_time);
MESHPARAMS_ADD(min_discovery_timeout);
}
#endif
static void add_files(struct ieee80211_sub_if_data *sdata)
{
if (!sdata->debugfsdir)
return;
switch (sdata->vif.type) {
case IEEE80211_IF_TYPE_MESH_POINT:
#ifdef CONFIG_MAC80211_MESH
add_mesh_stats(sdata);
add_mesh_config(sdata);
#endif
/* fall through */
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
add_sta_files(sdata);
@ -243,6 +390,7 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_DEL(auth_alg, sta);
DEBUGFS_DEL(auth_transaction, sta);
DEBUGFS_DEL(flags, sta);
DEBUGFS_DEL(num_beacons_sta, sta);
}
static void del_ap_files(struct ieee80211_sub_if_data *sdata)
@ -274,12 +422,61 @@ static void del_monitor_files(struct ieee80211_sub_if_data *sdata)
{
}
#ifdef CONFIG_MAC80211_MESH
#define MESHSTATS_DEL(name) \
do { \
debugfs_remove(sdata->mesh_stats.name); \
sdata->mesh_stats.name = NULL; \
} while (0)
static void del_mesh_stats(struct ieee80211_sub_if_data *sdata)
{
MESHSTATS_DEL(fwded_frames);
MESHSTATS_DEL(dropped_frames_ttl);
MESHSTATS_DEL(dropped_frames_no_route);
MESHSTATS_DEL(estab_plinks);
debugfs_remove(sdata->mesh_stats_dir);
sdata->mesh_stats_dir = NULL;
}
#define MESHPARAMS_DEL(name) \
do { \
debugfs_remove(sdata->mesh_config.name); \
sdata->mesh_config.name = NULL; \
} while (0)
static void del_mesh_config(struct ieee80211_sub_if_data *sdata)
{
MESHPARAMS_DEL(dot11MeshMaxRetries);
MESHPARAMS_DEL(dot11MeshRetryTimeout);
MESHPARAMS_DEL(dot11MeshConfirmTimeout);
MESHPARAMS_DEL(dot11MeshHoldingTimeout);
MESHPARAMS_DEL(dot11MeshTTL);
MESHPARAMS_DEL(auto_open_plinks);
MESHPARAMS_DEL(dot11MeshMaxPeerLinks);
MESHPARAMS_DEL(dot11MeshHWMPactivePathTimeout);
MESHPARAMS_DEL(dot11MeshHWMPpreqMinInterval);
MESHPARAMS_DEL(dot11MeshHWMPnetDiameterTraversalTime);
MESHPARAMS_DEL(dot11MeshHWMPmaxPREQretries);
MESHPARAMS_DEL(path_refresh_time);
MESHPARAMS_DEL(min_discovery_timeout);
debugfs_remove(sdata->mesh_config_dir);
sdata->mesh_config_dir = NULL;
}
#endif
static void del_files(struct ieee80211_sub_if_data *sdata, int type)
{
if (!sdata->debugfsdir)
return;
switch (type) {
case IEEE80211_IF_TYPE_MESH_POINT:
#ifdef CONFIG_MAC80211_MESH
del_mesh_stats(sdata);
del_mesh_config(sdata);
#endif
/* fall through */
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
del_sta_files(sdata);

View file

@ -51,7 +51,7 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_OPS(name)
STA_FILE(aid, aid, D);
STA_FILE(dev, dev->name, S);
STA_FILE(dev, sdata->dev->name, S);
STA_FILE(rx_packets, rx_packets, LU);
STA_FILE(tx_packets, tx_packets, LU);
STA_FILE(rx_bytes, rx_bytes, LU);
@ -67,7 +67,7 @@ STA_FILE(last_rssi, last_rssi, D);
STA_FILE(last_signal, last_signal, D);
STA_FILE(last_noise, last_noise, D);
STA_FILE(channel_use, channel_use, D);
STA_FILE(wep_weak_iv_count, wep_weak_iv_count, D);
STA_FILE(wep_weak_iv_count, wep_weak_iv_count, LU);
static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
@ -200,7 +200,7 @@ static ssize_t sta_agg_status_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct sta_info *sta = file->private_data;
struct net_device *dev = sta->dev;
struct net_device *dev = sta->sdata->dev;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_hw *hw = &local->hw;
u8 *da = sta->addr;

View file

@ -1,6 +1,8 @@
#ifndef __MAC80211_DEBUGFS_STA_H
#define __MAC80211_DEBUGFS_STA_H
#include "sta_info.h"
#ifdef CONFIG_MAC80211_DEBUGFS
void ieee80211_sta_debugfs_add(struct sta_info *sta);
void ieee80211_sta_debugfs_remove(struct sta_info *sta);

View file

@ -26,6 +26,7 @@
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
#include "mesh.h"
#include "wep.h"
#include "wme.h"
#include "aes_ccm.h"
@ -138,9 +139,15 @@ static void ieee80211_master_set_multicast_list(struct net_device *dev)
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
int meshhdrlen;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
meshhdrlen = (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) ? 5 : 0;
/* FIX: what would be proper limits for MTU?
* This interface uses 802.3 frames. */
if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) {
if (new_mtu < 256 ||
new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) {
printk(KERN_WARNING "%s: invalid MTU %d\n",
dev->name, new_mtu);
return -EINVAL;
@ -176,6 +183,7 @@ static int ieee80211_open(struct net_device *dev)
struct ieee80211_if_init_conf conf;
int res;
bool need_hw_reconfig = 0;
struct sta_info *sta;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@ -249,6 +257,20 @@ static int ieee80211_open(struct net_device *dev)
case IEEE80211_IF_TYPE_WDS:
if (is_zero_ether_addr(sdata->u.wds.remote_addr))
return -ENOLINK;
/* Create STA entry for the WDS peer */
sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
GFP_KERNEL);
if (!sta)
return -ENOMEM;
sta->flags |= WLAN_STA_AUTHORIZED;
res = sta_info_insert(sta);
if (res) {
sta_info_destroy(sta);
return res;
}
break;
case IEEE80211_IF_TYPE_VLAN:
if (!sdata->u.vlan.ap)
@ -258,6 +280,7 @@ static int ieee80211_open(struct net_device *dev)
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_MNTR:
case IEEE80211_IF_TYPE_IBSS:
case IEEE80211_IF_TYPE_MESH_POINT:
/* no special treatment */
break;
case IEEE80211_IF_TYPE_INVALID:
@ -359,24 +382,51 @@ static int ieee80211_open(struct net_device *dev)
static int ieee80211_stop(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
int i;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
/*
* Stop TX on this interface first.
*/
netif_stop_queue(dev);
list_for_each_entry(sta, &local->sta_list, list) {
if (sta->dev == dev)
/*
* Now delete all active aggregation sessions.
*/
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sta->sdata == sdata)
for (i = 0; i < STA_TID_NUM; i++)
ieee80211_sta_stop_rx_ba_session(sta->dev,
ieee80211_sta_stop_rx_ba_session(sdata->dev,
sta->addr, i,
WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_LEAVE_QBSS);
}
netif_stop_queue(dev);
rcu_read_unlock();
/*
* Remove all stations associated with this interface.
*
* This must be done before calling ops->remove_interface()
* because otherwise we can later invoke ops->sta_notify()
* whenever the STAs are removed, and that invalidates driver
* assumptions about always getting a vif pointer that is valid
* (because if we remove a STA after ops->remove_interface()
* the driver will have removed the vif info already!)
*
* We could relax this and only unlink the stations from the
* hash table and list but keep them on a per-sdata list that
* will be inserted back again when the interface is brought
* up again, but I don't currently see a use case for that,
* except with WDS which gets a STA entry created when it is
* brought up.
*/
sta_info_flush(local, sdata);
/*
* Don't count this interface for promisc/allmulti while it
@ -440,6 +490,7 @@ static int ieee80211_stop(struct net_device *dev)
ieee80211_configure_filter(local);
netif_tx_unlock_bh(local->mdev);
break;
case IEEE80211_IF_TYPE_MESH_POINT:
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
sdata->u.sta.state = IEEE80211_DISABLED;
@ -511,9 +562,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
printk(KERN_DEBUG "Could not find the station\n");
rcu_read_unlock();
return -ENOENT;
}
@ -553,7 +607,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
spin_unlock_bh(&local->mdev->queue_lock);
goto start_ba_exit;
}
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
/* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
* call back right away, it must see that the flow has begun */
@ -590,7 +644,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
sta->ampdu_mlme.dialog_token_allocator;
sta->ampdu_mlme.tid_tx[tid].ssn = start_seq_num;
ieee80211_send_addba_request(sta->dev, ra, tid,
ieee80211_send_addba_request(sta->sdata->dev, ra, tid,
sta->ampdu_mlme.tid_tx[tid].dialog_token,
sta->ampdu_mlme.tid_tx[tid].ssn,
0x40, 5000);
@ -603,7 +657,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
start_ba_exit:
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
@ -626,9 +680,12 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta)
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
/* check if the TID is in aggregation */
state = &sta->ampdu_mlme.tid_tx[tid].state;
@ -662,7 +719,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
stop_BA_exit:
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
@ -680,8 +737,10 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
return;
}
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
rcu_read_unlock();
printk(KERN_DEBUG "Could not find station: %s\n",
print_mac(mac, ra));
return;
@ -694,7 +753,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
*state);
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
return;
}
@ -707,7 +766,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
}
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
@ -728,10 +787,12 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
printk(KERN_DEBUG "Stop a BA session requested on DA %s tid %d\n",
print_mac(mac, ra), tid);
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
printk(KERN_DEBUG "Could not find station: %s\n",
print_mac(mac, ra));
rcu_read_unlock();
return;
}
state = &sta->ampdu_mlme.tid_tx[tid].state;
@ -739,13 +800,13 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
sta_info_put(sta);
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
rcu_read_unlock();
return;
}
if (*state & HT_AGG_STATE_INITIATOR_MSK)
ieee80211_send_delba(sta->dev, ra, tid,
ieee80211_send_delba(sta->sdata->dev, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
agg_queue = sta->tid_to_tx_q[tid];
@ -766,7 +827,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
sta->ampdu_mlme.tid_tx[tid].addba_req_num = 0;
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
@ -867,44 +928,6 @@ void ieee80211_if_setup(struct net_device *dev)
dev->destructor = ieee80211_if_free;
}
/* WDS specialties */
int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta;
DECLARE_MAC_BUF(mac);
if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
return 0;
/* Create STA entry for the new peer */
sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
if (IS_ERR(sta))
return PTR_ERR(sta);
sta->flags |= WLAN_STA_AUTHORIZED;
sta_info_put(sta);
/* Remove STA entry for the old peer */
sta = sta_info_get(local, sdata->u.wds.remote_addr);
if (sta) {
sta_info_free(sta);
sta_info_put(sta);
} else {
printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
"peer %s\n",
dev->name, print_mac(mac, sdata->u.wds.remote_addr));
}
/* Update WDS link data */
memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
return 0;
}
/* everything else */
static int __ieee80211_if_config(struct net_device *dev,
@ -925,6 +948,9 @@ static int __ieee80211_if_config(struct net_device *dev,
conf.bssid = sdata->u.sta.bssid;
conf.ssid = sdata->u.sta.ssid;
conf.ssid_len = sdata->u.sta.ssid_len;
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
conf.beacon = beacon;
ieee80211_start_mesh(dev);
} else if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
conf.ssid = sdata->u.ap.ssid;
conf.ssid_len = sdata->u.ap.ssid_len;
@ -937,6 +963,11 @@ static int __ieee80211_if_config(struct net_device *dev,
int ieee80211_if_config(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT &&
(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
return ieee80211_if_config_beacon(dev);
return __ieee80211_if_config(dev, NULL, NULL);
}
@ -1311,6 +1342,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
return;
}
rcu_read_lock();
if (status->excessive_retries) {
struct sta_info *sta;
sta = sta_info_get(local, hdr->addr1);
@ -1324,10 +1357,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
status->flags |= IEEE80211_TX_STATUS_TX_FILTERED;
ieee80211_handle_filtered_frame(local, sta,
skb, status);
sta_info_put(sta);
rcu_read_unlock();
return;
}
sta_info_put(sta);
}
}
@ -1337,12 +1369,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
if (sta) {
ieee80211_handle_filtered_frame(local, sta, skb,
status);
sta_info_put(sta);
rcu_read_unlock();
return;
}
} else
rate_control_tx_status(local->mdev, skb, status);
rcu_read_unlock();
ieee80211_led_tx(local, 0);
/* SNMP counters
@ -1662,7 +1696,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* add one default STA interface */
result = ieee80211_if_add(local->mdev, "wlan%d", NULL,
IEEE80211_IF_TYPE_STA);
IEEE80211_IF_TYPE_STA, NULL);
if (result)
printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
wiphy_name(local->hw.wiphy));
@ -1801,6 +1835,9 @@ static void __exit ieee80211_exit(void)
rc80211_simple_exit();
rc80211_pid_exit();
if (mesh_allocated)
ieee80211s_stop();
ieee80211_wme_unregister();
ieee80211_debugfs_netdev_exit();
}

View file

@ -90,6 +90,11 @@ struct ieee80211_sta_bss {
size_t wmm_ie_len;
u8 *ht_ie;
size_t ht_ie_len;
#ifdef CONFIG_MAC80211_MESH
u8 *mesh_id;
size_t mesh_id_len;
u8 *mesh_cfg;
#endif
#define IEEE80211_MAX_SUPP_RATES 32
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
size_t supp_rates_len;
@ -107,32 +112,43 @@ struct ieee80211_sta_bss {
u8 erp_value;
};
static inline u8 *bss_mesh_cfg(struct ieee80211_sta_bss *bss)
{
#ifdef CONFIG_MAC80211_MESH
return bss->mesh_cfg;
#endif
return NULL;
}
static inline u8 *bss_mesh_id(struct ieee80211_sta_bss *bss)
{
#ifdef CONFIG_MAC80211_MESH
return bss->mesh_id;
#endif
return NULL;
}
static inline u8 bss_mesh_id_len(struct ieee80211_sta_bss *bss)
{
#ifdef CONFIG_MAC80211_MESH
return bss->mesh_id_len;
#endif
return 0;
}
typedef unsigned __bitwise__ ieee80211_tx_result;
#define TX_CONTINUE ((__force ieee80211_tx_result) 0u)
#define TX_DROP ((__force ieee80211_tx_result) 1u)
#define TX_QUEUED ((__force ieee80211_tx_result) 2u)
typedef unsigned __bitwise__ ieee80211_rx_result;
#define RX_CONTINUE ((__force ieee80211_rx_result) 0u)
#define RX_DROP_UNUSABLE ((__force ieee80211_rx_result) 1u)
#define RX_DROP_MONITOR ((__force ieee80211_rx_result) 2u)
#define RX_QUEUED ((__force ieee80211_rx_result) 3u)
#define IEEE80211_TX_FRAGMENTED BIT(0)
#define IEEE80211_TX_UNICAST BIT(1)
#define IEEE80211_TX_PS_BUFFERED BIT(2)
#define IEEE80211_TX_PROBE_LAST_FRAG BIT(3)
#define IEEE80211_TX_INJECTED BIT(4)
/* flags used in struct ieee80211_txrx_data.flags */
/* whether the MSDU was fragmented */
#define IEEE80211_TXRXD_FRAGMENTED BIT(0)
#define IEEE80211_TXRXD_TXUNICAST BIT(1)
#define IEEE80211_TXRXD_TXPS_BUFFERED BIT(2)
#define IEEE80211_TXRXD_TXPROBE_LAST_FRAG BIT(3)
#define IEEE80211_TXRXD_RXIN_SCAN BIT(4)
/* frame is destined to interface currently processed (incl. multicast frames) */
#define IEEE80211_TXRXD_RXRA_MATCH BIT(5)
#define IEEE80211_TXRXD_TX_INJECTED BIT(6)
#define IEEE80211_TXRXD_RX_AMSDU BIT(7)
#define IEEE80211_TXRXD_RX_CMNTR_REPORTED BIT(8)
struct ieee80211_txrx_data {
struct ieee80211_tx_data {
struct sk_buff *skb;
struct net_device *dev;
struct ieee80211_local *local;
@ -141,31 +157,52 @@ struct ieee80211_txrx_data {
u16 fc, ethertype;
struct ieee80211_key *key;
unsigned int flags;
union {
struct {
struct ieee80211_tx_control *control;
struct ieee80211_channel *channel;
struct ieee80211_rate *rate;
/* use this rate (if set) for last fragment; rate can
* be set to lower rate for the first fragments, e.g.,
* when using CTS protection with IEEE 802.11g. */
struct ieee80211_rate *last_frag_rate;
/* Extra fragments (in addition to the first fragment
* in skb) */
int num_extra_frag;
struct sk_buff **extra_frag;
} tx;
struct {
struct ieee80211_rx_status *status;
struct ieee80211_rate *rate;
int sent_ps_buffered;
int queue;
int load;
u32 tkip_iv32;
u16 tkip_iv16;
} rx;
} u;
struct ieee80211_tx_control *control;
struct ieee80211_channel *channel;
struct ieee80211_rate *rate;
/* use this rate (if set) for last fragment; rate can
* be set to lower rate for the first fragments, e.g.,
* when using CTS protection with IEEE 802.11g. */
struct ieee80211_rate *last_frag_rate;
/* Extra fragments (in addition to the first fragment
* in skb) */
int num_extra_frag;
struct sk_buff **extra_frag;
};
typedef unsigned __bitwise__ ieee80211_rx_result;
#define RX_CONTINUE ((__force ieee80211_rx_result) 0u)
#define RX_DROP_UNUSABLE ((__force ieee80211_rx_result) 1u)
#define RX_DROP_MONITOR ((__force ieee80211_rx_result) 2u)
#define RX_QUEUED ((__force ieee80211_rx_result) 3u)
#define IEEE80211_RX_IN_SCAN BIT(0)
/* frame is destined to interface currently processed (incl. multicast frames) */
#define IEEE80211_RX_RA_MATCH BIT(1)
#define IEEE80211_RX_AMSDU BIT(2)
#define IEEE80211_RX_CMNTR_REPORTED BIT(3)
#define IEEE80211_RX_FRAGMENTED BIT(4)
struct ieee80211_rx_data {
struct sk_buff *skb;
struct net_device *dev;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
u16 fc, ethertype;
struct ieee80211_key *key;
unsigned int flags;
struct ieee80211_rx_status *status;
struct ieee80211_rate *rate;
int sent_ps_buffered;
int queue;
int load;
u32 tkip_iv32;
u16 tkip_iv16;
};
/* flags used in struct ieee80211_tx_packet_data.flags */
@ -227,6 +264,41 @@ struct ieee80211_if_vlan {
struct list_head list;
};
struct mesh_stats {
__u32 fwded_frames; /* Mesh forwarded frames */
__u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/
__u32 dropped_frames_no_route; /* Not transmitted, no route found */
atomic_t estab_plinks;
};
#define PREQ_Q_F_START 0x1
#define PREQ_Q_F_REFRESH 0x2
struct mesh_preq_queue {
struct list_head list;
u8 dst[ETH_ALEN];
u8 flags;
};
struct mesh_config {
/* Timeouts in ms */
/* Mesh plink management parameters */
u16 dot11MeshRetryTimeout;
u16 dot11MeshConfirmTimeout;
u16 dot11MeshHoldingTimeout;
u16 dot11MeshMaxPeerLinks;
u8 dot11MeshMaxRetries;
u8 dot11MeshTTL;
bool auto_open_plinks;
/* HWMP parameters */
u32 dot11MeshHWMPactivePathTimeout;
u16 dot11MeshHWMPpreqMinInterval;
u16 dot11MeshHWMPnetDiameterTraversalTime;
u8 dot11MeshHWMPmaxPREQretries;
u32 path_refresh_time;
u16 min_discovery_timeout;
};
/* flags used in struct ieee80211_if_sta.flags */
#define IEEE80211_STA_SSID_SET BIT(0)
#define IEEE80211_STA_BSSID_SET BIT(1)
@ -245,7 +317,8 @@ struct ieee80211_if_sta {
enum {
IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED,
IEEE80211_MESH_UP
} state;
struct timer_list timer;
struct work_struct work;
@ -254,6 +327,34 @@ struct ieee80211_if_sta {
size_t ssid_len;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
#ifdef CONFIG_MAC80211_MESH
struct timer_list mesh_path_timer;
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
bool accepting_plinks;
size_t mesh_id_len;
/* Active Path Selection Protocol Identifier */
u8 mesh_pp_id[4];
/* Active Path Selection Metric Identifier */
u8 mesh_pm_id[4];
/* Congestion Control Mode Identifier */
u8 mesh_cc_id[4];
/* Local mesh Destination Sequence Number */
u32 dsn;
/* Last used PREQ ID */
u32 preq_id;
atomic_t mpaths;
/* Timestamp of last DSN update */
unsigned long last_dsn_update;
/* Timestamp of last DSN sent */
unsigned long last_preq;
struct mesh_rmc *rmc;
spinlock_t mesh_preq_queue_lock;
struct mesh_preq_queue preq_queue;
int preq_queue_len;
struct mesh_stats mshstats;
struct mesh_config mshcfg;
u8 mesh_seqnum[3];
#endif
u16 aid;
u16 ap_capab, capab;
u8 *extra_ie; /* to be added to the end of AssocReq */
@ -286,8 +387,25 @@ struct ieee80211_if_sta {
u32 supp_rates_bits[IEEE80211_NUM_BANDS];
int wmm_last_param_set;
int num_beacons; /* number of TXed beacon frames by this STA */
};
static inline void ieee80211_if_sta_set_mesh_id(struct ieee80211_if_sta *ifsta,
u8 mesh_id_len, u8 *mesh_id)
{
#ifdef CONFIG_MAC80211_MESH
ifsta->mesh_id_len = mesh_id_len;
memcpy(ifsta->mesh_id, mesh_id, mesh_id_len);
#endif
}
#ifdef CONFIG_MAC80211_MESH
#define IEEE80211_IFSTA_MESH_CTR_INC(sta, name) \
do { (sta)->mshstats.name++; } while (0)
#else
#define IEEE80211_IFSTA_MESH_CTR_INC(sta, name) \
do { } while (0)
#endif
/* flags used in struct ieee80211_sub_if_data.flags */
#define IEEE80211_SDATA_ALLMULTI BIT(0)
@ -365,6 +483,7 @@ struct ieee80211_sub_if_data {
struct dentry *auth_alg;
struct dentry *auth_transaction;
struct dentry *flags;
struct dentry *num_beacons_sta;
} sta;
struct {
struct dentry *channel_use;
@ -390,6 +509,35 @@ struct ieee80211_sub_if_data {
} monitor;
struct dentry *default_key;
} debugfs;
#ifdef CONFIG_MAC80211_MESH
struct dentry *mesh_stats_dir;
struct {
struct dentry *fwded_frames;
struct dentry *dropped_frames_ttl;
struct dentry *dropped_frames_no_route;
struct dentry *estab_plinks;
struct timer_list mesh_path_timer;
} mesh_stats;
struct dentry *mesh_config_dir;
struct {
struct dentry *dot11MeshRetryTimeout;
struct dentry *dot11MeshConfirmTimeout;
struct dentry *dot11MeshHoldingTimeout;
struct dentry *dot11MeshMaxRetries;
struct dentry *dot11MeshTTL;
struct dentry *auto_open_plinks;
struct dentry *dot11MeshMaxPeerLinks;
struct dentry *dot11MeshHWMPactivePathTimeout;
struct dentry *dot11MeshHWMPpreqMinInterval;
struct dentry *dot11MeshHWMPnetDiameterTraversalTime;
struct dentry *dot11MeshHWMPmaxPREQretries;
struct dentry *path_refresh_time;
struct dentry *min_discovery_timeout;
} mesh_config;
#endif
#endif
/* must be last, dynamically sized area in this! */
struct ieee80211_vif vif;
@ -426,6 +574,7 @@ struct ieee80211_local {
unsigned int filter_flags; /* FIF_* */
struct iw_statistics wstats;
u8 wstats_flags;
bool tim_in_locked_section; /* see ieee80211_beacon_get() */
int tx_headroom; /* required headroom for hardware/radiotap */
enum {
@ -443,9 +592,15 @@ struct ieee80211_local {
struct sk_buff_head skb_queue;
struct sk_buff_head skb_queue_unreliable;
/* Station data structures */
rwlock_t sta_lock; /* protects STA data structures */
int num_sta; /* number of stations in sta_list */
/* Station data */
/*
* The lock only protects the list, hash, timer and counter
* against manipulation, reads are done in RCU. Additionally,
* the lock protects each BSS's TIM bitmap and a few items
* in a STA info structure.
*/
spinlock_t sta_lock;
unsigned long num_sta;
struct list_head sta_list;
struct sta_info *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;
@ -617,6 +772,57 @@ struct ieee80211_ra_tid {
u16 tid;
};
/* Parsed Information Elements */
struct ieee802_11_elems {
/* pointers to IEs */
u8 *ssid;
u8 *supp_rates;
u8 *fh_params;
u8 *ds_params;
u8 *cf_params;
u8 *tim;
u8 *ibss_params;
u8 *challenge;
u8 *wpa;
u8 *rsn;
u8 *erp_info;
u8 *ext_supp_rates;
u8 *wmm_info;
u8 *wmm_param;
u8 *ht_cap_elem;
u8 *ht_info_elem;
u8 *mesh_config;
u8 *mesh_id;
u8 *peer_link;
u8 *preq;
u8 *prep;
u8 *perr;
/* length of them, respectively */
u8 ssid_len;
u8 supp_rates_len;
u8 fh_params_len;
u8 ds_params_len;
u8 cf_params_len;
u8 tim_len;
u8 ibss_params_len;
u8 challenge_len;
u8 wpa_len;
u8 rsn_len;
u8 erp_info_len;
u8 ext_supp_rates_len;
u8 wmm_info_len;
u8 wmm_param_len;
u8 ht_cap_elem_len;
u8 ht_info_elem_len;
u8 mesh_config_len;
u8 mesh_id_len;
u8 peer_link_len;
u8 preq_len;
u8 prep_len;
u8 perr_len;
};
static inline struct ieee80211_local *hw_to_local(
struct ieee80211_hw *hw)
{
@ -651,8 +857,7 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
int ieee80211_hw_config(struct ieee80211_local *local);
int ieee80211_if_config(struct net_device *dev);
int ieee80211_if_config_beacon(struct net_device *dev);
void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx);
int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
void ieee80211_if_setup(struct net_device *dev);
int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht,
struct ieee80211_ht_info *req_ht_cap,
@ -686,6 +891,7 @@ int ieee80211_set_compression(struct ieee80211_local *local,
struct net_device *dev, struct sta_info *sta);
int ieee80211_set_freq(struct ieee80211_local *local, int freq);
/* ieee80211_sta.c */
#define IEEE80211_FC(type, stype) cpu_to_le16(type | stype)
void ieee80211_sta_timer(unsigned long data);
void ieee80211_sta_work(struct work_struct *work);
void ieee80211_sta_scan_work(struct work_struct *work);
@ -726,9 +932,25 @@ void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *da,
u16 tid, u16 initiator, u16 reason);
void sta_rx_agg_session_timer_expired(unsigned long data);
void sta_addba_resp_timer_expired(unsigned long data);
u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
enum ieee80211_band band);
void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb,
int encrypt);
void ieee802_11_parse_elems(u8 *start, size_t len,
struct ieee802_11_elems *elems);
#ifdef CONFIG_MAC80211_MESH
void ieee80211_start_mesh(struct net_device *dev);
#else
static inline void ieee80211_start_mesh(struct net_device *dev)
{}
#endif
/* ieee80211_iface.c */
int ieee80211_if_add(struct net_device *dev, const char *name,
struct net_device **new_dev, int type);
struct net_device **new_dev, int type,
struct vif_params *params);
void ieee80211_if_set_type(struct net_device *dev, int type);
void ieee80211_if_reinit(struct net_device *dev);
void __ieee80211_if_del(struct ieee80211_local *local,

View file

@ -15,6 +15,7 @@
#include "ieee80211_i.h"
#include "sta_info.h"
#include "debugfs_netdev.h"
#include "mesh.h"
void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
{
@ -39,7 +40,8 @@ static void ieee80211_if_sdata_deinit(struct ieee80211_sub_if_data *sdata)
/* Must be called with rtnl lock held. */
int ieee80211_if_add(struct net_device *dev, const char *name,
struct net_device **new_dev, int type)
struct net_device **new_dev, int type,
struct vif_params *params)
{
struct net_device *ndev;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
@ -78,6 +80,12 @@ int ieee80211_if_add(struct net_device *dev, const char *name,
ieee80211_debugfs_add_netdev(sdata);
ieee80211_if_set_type(ndev, type);
if (ieee80211_vif_is_mesh(&sdata->vif) &&
params && params->mesh_id_len)
ieee80211_if_sta_set_mesh_id(&sdata->u.sta,
params->mesh_id_len,
params->mesh_id);
/* we're under RTNL so all this is fine */
if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) {
__ieee80211_if_del(local, sdata);
@ -134,6 +142,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
sdata->bss = &sdata->u.ap;
INIT_LIST_HEAD(&sdata->u.ap.vlans);
break;
case IEEE80211_IF_TYPE_MESH_POINT:
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS: {
struct ieee80211_sub_if_data *msdata;
@ -155,6 +164,9 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev);
sdata->bss = &msdata->u.ap;
if (ieee80211_vif_is_mesh(&sdata->vif))
ieee80211_mesh_init_sdata(sdata);
break;
}
case IEEE80211_IF_TYPE_MNTR:
@ -175,8 +187,8 @@ void ieee80211_if_reinit(struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta;
struct sk_buff *skb;
int flushed;
ASSERT_RTNL();
@ -184,6 +196,10 @@ void ieee80211_if_reinit(struct net_device *dev)
ieee80211_if_sdata_deinit(sdata);
/* Need to handle mesh specially to allow eliding the function call */
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_rmc_free(dev);
switch (sdata->vif.type) {
case IEEE80211_IF_TYPE_INVALID:
/* cannot happen */
@ -224,17 +240,9 @@ void ieee80211_if_reinit(struct net_device *dev)
break;
}
case IEEE80211_IF_TYPE_WDS:
sta = sta_info_get(local, sdata->u.wds.remote_addr);
if (sta) {
sta_info_free(sta);
sta_info_put(sta);
} else {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Someone had deleted my STA "
"entry for the WDS link\n", dev->name);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
}
/* nothing to do */
break;
case IEEE80211_IF_TYPE_MESH_POINT:
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
kfree(sdata->u.sta.extra_ie);
@ -257,8 +265,8 @@ void ieee80211_if_reinit(struct net_device *dev)
break;
}
/* remove all STAs that are bound to this virtual interface */
sta_info_flush(local, dev);
flushed = sta_info_flush(local, sdata);
WARN_ON(flushed);
memset(&sdata->u, 0, sizeof(sdata->u));
ieee80211_if_sdata_init(sdata);

View file

@ -33,8 +33,7 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
size_t key_len)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
int ret;
struct sta_info *sta = NULL;
struct sta_info *sta;
struct ieee80211_key *key;
struct ieee80211_sub_if_data *sdata;
@ -51,24 +50,23 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
key = sdata->keys[idx];
} else {
sta = sta_info_get(local, sta_addr);
if (!sta) {
ret = -ENOENT;
key = NULL;
goto err_out;
}
if (!sta)
return -ENOENT;
key = sta->key;
}
if (!key)
ret = -ENOENT;
else
ret = 0;
return -ENOENT;
ieee80211_key_free(key);
return 0;
} else {
key = ieee80211_key_alloc(alg, idx, key_len, _key);
if (!key)
return -ENOMEM;
sta = NULL;
if (!is_broadcast_ether_addr(sta_addr)) {
set_tx_key = 0;
/*
@ -78,14 +76,14 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
* work around this.
*/
if (idx != 0 && alg != ALG_WEP) {
ret = -EINVAL;
goto err_out;
ieee80211_key_free(key);
return -EINVAL;
}
sta = sta_info_get(local, sta_addr);
if (!sta) {
ret = -ENOENT;
goto err_out;
ieee80211_key_free(key);
return -ENOENT;
}
}
@ -93,18 +91,9 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
if (set_tx_key || (!sta && !sdata->default_key && key))
ieee80211_set_default_key(sdata, idx);
/* don't free key later */
key = NULL;
ret = 0;
}
err_out:
if (sta)
sta_info_put(sta);
ieee80211_key_free(key);
return ret;
return 0;
}
static int ieee80211_ioctl_siwgenie(struct net_device *dev,
@ -479,10 +468,20 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
ieee80211_sta_req_auth(dev, &sdata->u.sta);
return 0;
} else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
ETH_ALEN) == 0)
return 0;
return ieee80211_if_update_wds(dev, (u8 *) &ap_addr->sa_data);
/*
* If it is necessary to update the WDS peer address
* while the interface is running, then we need to do
* more work here, namely if it is running we need to
* add a new and remove the old STA entry, this is
* normally handled by _open() and _stop().
*/
if (netif_running(dev))
return -EBUSY;
memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
ETH_ALEN);
return 0;
}
return -EOPNOTSUPP;
@ -525,6 +524,7 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev,
if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT &&
sdata->vif.type != IEEE80211_IF_TYPE_AP)
return -EOPNOTSUPP;
@ -624,7 +624,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
else
rate->value = 0;
rate->value *= 100000;
sta_info_put(sta);
return 0;
}
@ -999,7 +999,6 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
wstats->qual.qual = sta->last_signal;
wstats->qual.noise = sta->last_noise;
wstats->qual.updated = local->wstats_flags;
sta_info_put(sta);
}
return wstats;
}

View file

@ -170,9 +170,12 @@ void rate_control_get_rate(struct net_device *dev,
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sta_info *sta = sta_info_get(local, hdr->addr1);
struct sta_info *sta;
int i;
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
memset(sel, 0, sizeof(struct rate_selection));
ref->ops->get_rate(ref->priv, dev, sband, skb, sel);
@ -190,8 +193,7 @@ void rate_control_get_rate(struct net_device *dev,
}
}
if (sta)
sta_info_put(sta);
rcu_read_unlock();
}
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)

View file

@ -14,6 +14,7 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <linux/kref.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "sta_info.h"

View file

@ -24,6 +24,7 @@
#include <linux/wireless.h>
#include <linux/random.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <net/iw_handler.h>
#include <asm/types.h>
@ -31,12 +32,14 @@
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
#include "ieee80211_led.h"
#include "mesh.h"
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
#define IEEE80211_AUTH_MAX_TRIES 3
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
#define IEEE80211_ASSOC_MAX_TRIES 3
#define IEEE80211_MONITORING_INTERVAL (2 * HZ)
#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)
#define IEEE80211_PROBE_INTERVAL (60 * HZ)
#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
#define IEEE80211_SCAN_INTERVAL (2 * HZ)
@ -49,6 +52,7 @@
#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
@ -87,46 +91,8 @@ static int ieee80211_sta_config_auth(struct net_device *dev,
struct ieee80211_if_sta *ifsta);
/* Parsed Information Elements */
struct ieee802_11_elems {
/* pointers to IEs */
u8 *ssid;
u8 *supp_rates;
u8 *fh_params;
u8 *ds_params;
u8 *cf_params;
u8 *tim;
u8 *ibss_params;
u8 *challenge;
u8 *wpa;
u8 *rsn;
u8 *erp_info;
u8 *ext_supp_rates;
u8 *wmm_info;
u8 *wmm_param;
u8 *ht_cap_elem;
u8 *ht_info_elem;
/* length of them, respectively */
u8 ssid_len;
u8 supp_rates_len;
u8 fh_params_len;
u8 ds_params_len;
u8 cf_params_len;
u8 tim_len;
u8 ibss_params_len;
u8 challenge_len;
u8 wpa_len;
u8 rsn_len;
u8 erp_info_len;
u8 ext_supp_rates_len;
u8 wmm_info_len;
u8 wmm_param_len;
u8 ht_cap_elem_len;
u8 ht_info_elem_len;
};
static void ieee802_11_parse_elems(u8 *start, size_t len,
struct ieee802_11_elems *elems)
void ieee802_11_parse_elems(u8 *start, size_t len,
struct ieee802_11_elems *elems)
{
size_t left = len;
u8 *pos = start;
@ -215,6 +181,30 @@ static void ieee802_11_parse_elems(u8 *start, size_t len,
elems->ht_info_elem = pos;
elems->ht_info_elem_len = elen;
break;
case WLAN_EID_MESH_ID:
elems->mesh_id = pos;
elems->mesh_id_len = elen;
break;
case WLAN_EID_MESH_CONFIG:
elems->mesh_config = pos;
elems->mesh_config_len = elen;
break;
case WLAN_EID_PEER_LINK:
elems->peer_link = pos;
elems->peer_link_len = elen;
break;
case WLAN_EID_PREQ:
elems->preq = pos;
elems->preq_len = elen;
break;
case WLAN_EID_PREP:
elems->prep = pos;
elems->prep_len = elen;
break;
case WLAN_EID_PERR:
elems->perr = pos;
elems->perr_len = elen;
break;
default:
break;
}
@ -501,8 +491,8 @@ static void ieee80211_set_disassoc(struct net_device *dev,
ieee80211_set_associated(dev, ifsta, 0);
}
static void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb,
int encrypt)
void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb,
int encrypt)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_tx_packet_data *pkt_data;
@ -856,6 +846,8 @@ static void ieee80211_associated(struct net_device *dev,
ifsta->state = IEEE80211_ASSOCIATED;
rcu_read_lock();
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
@ -871,7 +863,7 @@ static void ieee80211_associated(struct net_device *dev,
"range\n",
dev->name, print_mac(mac, ifsta->bssid));
disassoc = 1;
sta_info_free(sta);
sta_info_unlink(&sta);
} else
ieee80211_send_probe_req(dev, ifsta->bssid,
local->scan_ssid,
@ -887,8 +879,17 @@ static void ieee80211_associated(struct net_device *dev,
ifsta->ssid_len);
}
}
sta_info_put(sta);
}
rcu_read_unlock();
if (disassoc && sta) {
synchronize_rcu();
rtnl_lock();
sta_info_destroy(sta);
rtnl_unlock();
}
if (disassoc) {
ifsta->state = IEEE80211_DISABLED;
ieee80211_set_associated(dev, ifsta, 0);
@ -1114,9 +1115,13 @@ static void ieee80211_sta_process_addba_request(struct net_device *dev,
int ret = -EOPNOTSUPP;
DECLARE_MAC_BUF(mac);
rcu_read_lock();
sta = sta_info_get(local, mgmt->sa);
if (!sta)
if (!sta) {
rcu_read_unlock();
return;
}
/* extract session parameters from addba request frame */
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
@ -1208,9 +1213,9 @@ end:
spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
end_no_lock:
ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token,
status, 1, buf_size, timeout);
sta_info_put(sta);
ieee80211_send_addba_resp(sta->sdata->dev, sta->addr, tid,
dialog_token, status, 1, buf_size, timeout);
rcu_read_unlock();
}
static void ieee80211_sta_process_addba_resp(struct net_device *dev,
@ -1224,9 +1229,13 @@ static void ieee80211_sta_process_addba_resp(struct net_device *dev,
u16 tid;
u8 *state;
rcu_read_lock();
sta = sta_info_get(local, mgmt->sa);
if (!sta)
if (!sta) {
rcu_read_unlock();
return;
}
capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
@ -1241,7 +1250,7 @@ static void ieee80211_sta_process_addba_resp(struct net_device *dev,
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
sta_info_put(sta);
rcu_read_unlock();
return;
}
@ -1255,7 +1264,7 @@ static void ieee80211_sta_process_addba_resp(struct net_device *dev,
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
printk(KERN_DEBUG "state not HT_ADDBA_REQUESTED_MSK:"
"%d\n", *state);
sta_info_put(sta);
rcu_read_unlock();
return;
}
@ -1282,7 +1291,7 @@ static void ieee80211_sta_process_addba_resp(struct net_device *dev,
ieee80211_stop_tx_ba_session(hw, sta->addr, tid,
WLAN_BACK_INITIATOR);
}
sta_info_put(sta);
rcu_read_unlock();
}
void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid,
@ -1337,16 +1346,20 @@ void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid,
struct sta_info *sta;
int ret, i;
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta)
if (!sta) {
rcu_read_unlock();
return;
}
/* check if TID is in operational state */
spin_lock_bh(&sta->ampdu_mlme.ampdu_rx);
if (sta->ampdu_mlme.tid_rx[tid].state
!= HT_AGG_STATE_OPERATIONAL) {
spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
sta_info_put(sta);
rcu_read_unlock();
return;
}
sta->ampdu_mlme.tid_rx[tid].state =
@ -1385,7 +1398,7 @@ void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid,
kfree(sta->ampdu_mlme.tid_rx[tid].reorder_buf);
sta->ampdu_mlme.tid_rx[tid].state = HT_AGG_STATE_IDLE;
sta_info_put(sta);
rcu_read_unlock();
}
@ -1398,9 +1411,13 @@ static void ieee80211_sta_process_delba(struct net_device *dev,
u16 initiator;
DECLARE_MAC_BUF(mac);
rcu_read_lock();
sta = sta_info_get(local, mgmt->sa);
if (!sta)
if (!sta) {
rcu_read_unlock();
return;
}
params = le16_to_cpu(mgmt->u.action.u.delba.params);
tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
@ -1425,7 +1442,7 @@ static void ieee80211_sta_process_delba(struct net_device *dev,
ieee80211_stop_tx_ba_session(&local->hw, sta->addr, tid,
WLAN_BACK_RECIPIENT);
}
sta_info_put(sta);
rcu_read_unlock();
}
/*
@ -1437,7 +1454,7 @@ void sta_addba_resp_timer_expired(unsigned long data)
{
/* not an elegant detour, but there is no choice as the timer passes
* only one argument, and both sta_info and TID are needed, so init
* flow in sta_info_add gives the TID as data, while the timer_to_id
* flow in sta_info_create gives the TID as data, while the timer_to_id
* array gives the sta through container_of */
u16 tid = *(int *)data;
struct sta_info *temp_sta = container_of((void *)data,
@ -1448,9 +1465,13 @@ void sta_addba_resp_timer_expired(unsigned long data)
struct sta_info *sta;
u8 *state;
rcu_read_lock();
sta = sta_info_get(local, temp_sta->addr);
if (!sta)
if (!sta) {
rcu_read_unlock();
return;
}
state = &sta->ampdu_mlme.tid_tx[tid].state;
/* check if the TID waits for addBA response */
@ -1472,7 +1493,7 @@ void sta_addba_resp_timer_expired(unsigned long data)
WLAN_BACK_INITIATOR);
timer_expired_exit:
sta_info_put(sta);
rcu_read_unlock();
}
/*
@ -1484,7 +1505,7 @@ void sta_rx_agg_session_timer_expired(unsigned long data)
{
/* not an elegant detour, but there is no choice as the timer passes
* only one argument, and verious sta_info are needed here, so init
* flow in sta_info_add gives the TID as data, while the timer_to_id
* flow in sta_info_create gives the TID as data, while the timer_to_id
* array gives the sta through container_of */
u8 *ptid = (u8 *)data;
u8 *timer_to_id = ptid - *ptid;
@ -1492,8 +1513,8 @@ void sta_rx_agg_session_timer_expired(unsigned long data)
timer_to_tid[0]);
printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, (u16)*ptid,
WLAN_BACK_TIMER,
ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr,
(u16)*ptid, WLAN_BACK_TIMER,
WLAN_REASON_QSTA_TIMEOUT);
}
@ -1802,14 +1823,19 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (ifsta->assocresp_ies)
memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len);
rcu_read_lock();
/* Add STA entry for the AP */
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
struct ieee80211_sta_bss *bss;
sta = sta_info_add(local, dev, ifsta->bssid, GFP_KERNEL);
if (IS_ERR(sta)) {
printk(KERN_DEBUG "%s: failed to add STA entry for the"
" AP (error %ld)\n", dev->name, PTR_ERR(sta));
int err;
sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC);
if (!sta) {
printk(KERN_DEBUG "%s: failed to alloc STA entry for"
" the AP\n", dev->name);
rcu_read_unlock();
return;
}
bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
@ -1821,9 +1847,27 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
sta->last_noise = bss->noise;
ieee80211_rx_bss_put(dev, bss);
}
err = sta_info_insert(sta);
if (err) {
printk(KERN_DEBUG "%s: failed to insert STA entry for"
" the AP (error %d)\n", dev->name, err);
sta_info_destroy(sta);
rcu_read_unlock();
return;
}
}
sta->dev = dev;
/*
* FIXME: Do we really need to update the sta_info's information here?
* We already know about the AP (we found it in our list) so it
* should already be filled with the right info, no?
* As is stands, all this is racy because typically we assume
* the information that is filled in here (except flags) doesn't
* change while a STA structure is alive. As such, it should move
* to between the sta_info_alloc() and sta_info_insert() above.
*/
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP |
WLAN_STA_AUTHORIZED;
@ -1886,16 +1930,16 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (elems.wmm_param && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
sta->flags |= WLAN_STA_WME;
rcu_read_unlock();
ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param,
elems.wmm_param_len);
}
} else
rcu_read_unlock();
/* set AID, ieee80211_set_associated() will tell the driver */
bss_conf->aid = aid;
ieee80211_set_associated(dev, ifsta, 1);
sta_info_put(sta);
ieee80211_associated(dev, ifsta);
}
@ -1905,8 +1949,16 @@ static void __ieee80211_rx_bss_hash_add(struct net_device *dev,
struct ieee80211_sta_bss *bss)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
bss->hnext = local->sta_bss_hash[STA_HASH(bss->bssid)];
local->sta_bss_hash[STA_HASH(bss->bssid)] = bss;
u8 hash_idx;
if (bss_mesh_cfg(bss))
hash_idx = mesh_id_hash(bss_mesh_id(bss),
bss_mesh_id_len(bss));
else
hash_idx = STA_HASH(bss->bssid);
bss->hnext = local->sta_bss_hash[hash_idx];
local->sta_bss_hash[hash_idx] = bss;
}
@ -1959,7 +2011,6 @@ ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid, int freq,
return bss;
}
static struct ieee80211_sta_bss *
ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int freq,
u8 *ssid, u8 ssid_len)
@ -1970,7 +2021,8 @@ ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int freq,
spin_lock_bh(&local->sta_bss_lock);
bss = local->sta_bss_hash[STA_HASH(bssid)];
while (bss) {
if (!memcmp(bss->bssid, bssid, ETH_ALEN) &&
if (!bss_mesh_cfg(bss) &&
!memcmp(bss->bssid, bssid, ETH_ALEN) &&
bss->freq == freq &&
bss->ssid_len == ssid_len &&
(ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) {
@ -1983,6 +2035,72 @@ ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int freq,
return bss;
}
#ifdef CONFIG_MAC80211_MESH
static struct ieee80211_sta_bss *
ieee80211_rx_mesh_bss_get(struct net_device *dev, u8 *mesh_id, int mesh_id_len,
u8 *mesh_cfg, int freq)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sta_bss *bss;
spin_lock_bh(&local->sta_bss_lock);
bss = local->sta_bss_hash[mesh_id_hash(mesh_id, mesh_id_len)];
while (bss) {
if (bss_mesh_cfg(bss) &&
!memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) &&
bss->freq == freq &&
mesh_id_len == bss->mesh_id_len &&
(mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id,
mesh_id_len))) {
atomic_inc(&bss->users);
break;
}
bss = bss->hnext;
}
spin_unlock_bh(&local->sta_bss_lock);
return bss;
}
static struct ieee80211_sta_bss *
ieee80211_rx_mesh_bss_add(struct net_device *dev, u8 *mesh_id, int mesh_id_len,
u8 *mesh_cfg, int freq)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sta_bss *bss;
bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
if (!bss)
return NULL;
bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC);
if (!bss->mesh_cfg) {
kfree(bss);
return NULL;
}
if (mesh_id_len && mesh_id_len <= IEEE80211_MAX_MESH_ID_LEN) {
bss->mesh_id = kmalloc(mesh_id_len, GFP_ATOMIC);
if (!bss->mesh_id) {
kfree(bss->mesh_cfg);
kfree(bss);
return NULL;
}
memcpy(bss->mesh_id, mesh_id, mesh_id_len);
}
atomic_inc(&bss->users);
atomic_inc(&bss->users);
memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN);
bss->mesh_id_len = mesh_id_len;
bss->freq = freq;
spin_lock_bh(&local->sta_bss_lock);
/* TODO: order by RSSI? */
list_add_tail(&bss->list, &local->sta_bss_list);
__ieee80211_rx_bss_hash_add(dev, bss);
spin_unlock_bh(&local->sta_bss_lock);
return bss;
}
#endif
static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss)
{
@ -1990,6 +2108,8 @@ static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss)
kfree(bss->rsn_ie);
kfree(bss->wmm_ie);
kfree(bss->ht_ie);
kfree(bss_mesh_id(bss));
kfree(bss_mesh_cfg(bss));
kfree(bss);
}
@ -2185,6 +2305,42 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
return res;
}
u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
enum ieee80211_band band)
{
struct ieee80211_supported_band *sband;
struct ieee80211_rate *bitrates;
size_t num_rates;
u64 supp_rates;
int i, j;
sband = local->hw.wiphy->bands[band];
if (!sband) {
WARN_ON(1);
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
}
bitrates = sband->bitrates;
num_rates = sband->n_bitrates;
supp_rates = 0;
for (i = 0; i < elems->supp_rates_len +
elems->ext_supp_rates_len; i++) {
u8 rate = 0;
int own_rate;
if (i < elems->supp_rates_len)
rate = elems->supp_rates[i];
else if (elems->ext_supp_rates)
rate = elems->ext_supp_rates
[i - elems->supp_rates_len];
own_rate = 5 * (rate & 0x7f);
for (j = 0; j < num_rates; j++)
if (bitrates[j].bitrate == own_rate)
supp_rates |= BIT(j);
}
return supp_rates;
}
static void ieee80211_rx_bss_info(struct net_device *dev,
struct ieee80211_mgmt *mgmt,
@ -2219,41 +2375,23 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
if (ieee80211_vif_is_mesh(&sdata->vif) && elems.mesh_id &&
elems.mesh_config && mesh_matches_local(&elems, dev)) {
u64 rates = ieee80211_sta_get_rates(local, &elems,
rx_status->band);
mesh_neighbour_update(mgmt->sa, rates, dev,
mesh_peer_accepts_plinks(&elems, dev));
}
rcu_read_lock();
if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates &&
memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 &&
(sta = sta_info_get(local, mgmt->sa))) {
struct ieee80211_supported_band *sband;
struct ieee80211_rate *bitrates;
size_t num_rates;
u64 supp_rates, prev_rates;
int i, j;
sband = local->hw.wiphy->bands[rx_status->band];
if (!sband) {
WARN_ON(1);
sband = local->hw.wiphy->bands[
local->hw.conf.channel->band];
}
bitrates = sband->bitrates;
num_rates = sband->n_bitrates;
supp_rates = 0;
for (i = 0; i < elems.supp_rates_len +
elems.ext_supp_rates_len; i++) {
u8 rate = 0;
int own_rate;
if (i < elems.supp_rates_len)
rate = elems.supp_rates[i];
else if (elems.ext_supp_rates)
rate = elems.ext_supp_rates
[i - elems.supp_rates_len];
own_rate = 5 * (rate & 0x7f);
for (j = 0; j < num_rates; j++)
if (bitrates[j].bitrate == own_rate)
supp_rates |= BIT(j);
}
u64 prev_rates;
u64 supp_rates = ieee80211_sta_get_rates(local, &elems,
rx_status->band);
prev_rates = sta->supp_rates[rx_status->band];
sta->supp_rates[rx_status->band] &= supp_rates;
@ -2273,22 +2411,32 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
(unsigned long long) supp_rates,
(unsigned long long) sta->supp_rates[rx_status->band]);
}
sta_info_put(sta);
}
if (!elems.ssid)
return;
rcu_read_unlock();
if (elems.ds_params && elems.ds_params_len == 1)
freq = ieee80211_channel_to_frequency(elems.ds_params[0]);
else
freq = rx_status->freq;
bss = ieee80211_rx_bss_get(dev, mgmt->bssid, freq,
elems.ssid, elems.ssid_len);
if (!bss) {
bss = ieee80211_rx_bss_add(dev, mgmt->bssid, freq,
#ifdef CONFIG_MAC80211_MESH
if (elems.mesh_config)
bss = ieee80211_rx_mesh_bss_get(dev, elems.mesh_id,
elems.mesh_id_len, elems.mesh_config, freq);
else
#endif
bss = ieee80211_rx_bss_get(dev, mgmt->bssid, freq,
elems.ssid, elems.ssid_len);
if (!bss) {
#ifdef CONFIG_MAC80211_MESH
if (elems.mesh_config)
bss = ieee80211_rx_mesh_bss_add(dev, elems.mesh_id,
elems.mesh_id_len, elems.mesh_config, freq);
else
#endif
bss = ieee80211_rx_bss_add(dev, mgmt->bssid, freq,
elems.ssid, elems.ssid_len);
if (!bss)
return;
} else {
@ -2615,8 +2763,11 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
static void ieee80211_rx_mgmt_action(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
size_t len,
struct ieee80211_rx_status *rx_status)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (len < IEEE80211_MIN_ACTION_SIZE)
return;
@ -2648,7 +2799,18 @@ static void ieee80211_rx_mgmt_action(struct net_device *dev,
break;
}
break;
case PLINK_CATEGORY:
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_rx_plink_frame(dev, mgmt, len, rx_status);
break;
case MESH_PATH_SEL_CATEGORY:
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_rx_path_sel_frame(dev, mgmt, len);
break;
default:
if (net_ratelimit())
printk(KERN_DEBUG "%s: Rx unknown action frame - "
"category=%d\n", dev->name, mgmt->u.action.category);
break;
}
}
@ -2675,13 +2837,13 @@ void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
case IEEE80211_STYPE_PROBE_REQ:
case IEEE80211_STYPE_PROBE_RESP:
case IEEE80211_STYPE_BEACON:
case IEEE80211_STYPE_ACTION:
memcpy(skb->cb, rx_status, sizeof(*rx_status));
case IEEE80211_STYPE_AUTH:
case IEEE80211_STYPE_ASSOC_RESP:
case IEEE80211_STYPE_REASSOC_RESP:
case IEEE80211_STYPE_DEAUTH:
case IEEE80211_STYPE_DISASSOC:
case IEEE80211_STYPE_ACTION:
skb_queue_tail(&ifsta->skb_queue, skb);
queue_work(local->hw.workqueue, &ifsta->work);
return;
@ -2740,7 +2902,7 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_ACTION:
ieee80211_rx_mgmt_action(dev, ifsta, mgmt, skb->len);
ieee80211_rx_mgmt_action(dev, ifsta, mgmt, skb->len, rx_status);
break;
}
@ -2789,45 +2951,50 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
int active = 0;
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
read_lock_bh(&local->sta_lock);
list_for_each_entry(sta, &local->sta_list, list) {
if (sta->dev == dev &&
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sta->sdata == sdata &&
time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
jiffies)) {
active++;
break;
}
}
read_unlock_bh(&local->sta_lock);
rcu_read_unlock();
return active;
}
static void ieee80211_sta_expire(struct net_device *dev)
static void ieee80211_sta_expire(struct net_device *dev, unsigned long exp_time)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta, *tmp;
LIST_HEAD(tmp_list);
DECLARE_MAC_BUF(mac);
unsigned long flags;
write_lock_bh(&local->sta_lock);
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
if (time_after(jiffies, sta->last_rx +
IEEE80211_IBSS_INACTIVITY_LIMIT)) {
if (time_after(jiffies, sta->last_rx + exp_time)) {
printk(KERN_DEBUG "%s: expiring inactive STA %s\n",
dev->name, print_mac(mac, sta->addr));
__sta_info_get(sta);
sta_info_remove(sta);
list_add(&sta->list, &tmp_list);
sta_info_unlink(&sta);
if (sta)
list_add(&sta->list, &tmp_list);
}
write_unlock_bh(&local->sta_lock);
spin_unlock_irqrestore(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
sta_info_free(sta);
sta_info_put(sta);
}
synchronize_rcu();
rtnl_lock();
list_for_each_entry_safe(sta, tmp, &tmp_list, list)
sta_info_destroy(sta);
rtnl_unlock();
}
@ -2836,7 +3003,7 @@ static void ieee80211_sta_merge_ibss(struct net_device *dev,
{
mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
ieee80211_sta_expire(dev);
ieee80211_sta_expire(dev, IEEE80211_IBSS_INACTIVITY_LIMIT);
if (ieee80211_sta_active_ibss(dev))
return;
@ -2846,6 +3013,36 @@ static void ieee80211_sta_merge_ibss(struct net_device *dev,
}
#ifdef CONFIG_MAC80211_MESH
static void ieee80211_mesh_housekeeping(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
bool free_plinks;
ieee80211_sta_expire(dev, IEEE80211_MESH_PEER_INACTIVITY_LIMIT);
mesh_path_expire(dev);
free_plinks = mesh_plink_availables(sdata);
if (free_plinks != sdata->u.sta.accepting_plinks)
ieee80211_if_config_beacon(dev);
mod_timer(&ifsta->timer, jiffies +
IEEE80211_MESH_HOUSEKEEPING_INTERVAL);
}
void ieee80211_start_mesh(struct net_device *dev)
{
struct ieee80211_if_sta *ifsta;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
ifsta = &sdata->u.sta;
ifsta->state = IEEE80211_MESH_UP;
ieee80211_sta_timer((unsigned long)sdata);
}
#endif
void ieee80211_sta_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
@ -2857,7 +3054,6 @@ void ieee80211_sta_timer(unsigned long data)
queue_work(local->hw.workqueue, &ifsta->work);
}
void ieee80211_sta_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
@ -2874,7 +3070,8 @@ void ieee80211_sta_work(struct work_struct *work)
return;
if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
sdata->vif.type != IEEE80211_IF_TYPE_IBSS) {
sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT) {
printk(KERN_DEBUG "%s: ieee80211_sta_work: non-STA interface "
"(type=%d)\n", dev->name, sdata->vif.type);
return;
@ -2884,6 +3081,13 @@ void ieee80211_sta_work(struct work_struct *work)
while ((skb = skb_dequeue(&ifsta->skb_queue)))
ieee80211_sta_rx_queued_mgmt(dev, skb);
#ifdef CONFIG_MAC80211_MESH
if (ifsta->preq_queue_len &&
time_after(jiffies,
ifsta->last_preq + msecs_to_jiffies(ifsta->mshcfg.dot11MeshHWMPpreqMinInterval)))
mesh_path_start_discovery(dev);
#endif
if (ifsta->state != IEEE80211_AUTHENTICATE &&
ifsta->state != IEEE80211_ASSOCIATE &&
test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
@ -2919,6 +3123,11 @@ void ieee80211_sta_work(struct work_struct *work)
case IEEE80211_IBSS_JOINED:
ieee80211_sta_merge_ibss(dev, ifsta);
break;
#ifdef CONFIG_MAC80211_MESH
case IEEE80211_MESH_UP:
ieee80211_mesh_housekeeping(dev, ifsta);
break;
#endif
default:
printk(KERN_DEBUG "ieee80211_sta_work: Unknown state %d\n",
ifsta->state);
@ -3123,7 +3332,7 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
sband = local->hw.wiphy->bands[bss->band];
if (local->hw.conf.beacon_int == 0)
local->hw.conf.beacon_int = 100;
local->hw.conf.beacon_int = 10000;
bss->beacon_int = local->hw.conf.beacon_int;
bss->last_update = jiffies;
bss->capability = WLAN_CAPABILITY_IBSS;
@ -3367,6 +3576,13 @@ static void ieee80211_send_nullfunc(struct ieee80211_local *local,
}
static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
{
if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
ieee80211_vif_is_mesh(&sdata->vif))
ieee80211_sta_timer((unsigned long)sdata);
}
void ieee80211_scan_completed(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
@ -3380,6 +3596,12 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
if (local->sta_hw_scanning) {
local->sta_hw_scanning = 0;
/* Restart STA timer for HW scan case */
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list)
ieee80211_restart_sta_timer(sdata);
rcu_read_unlock();
goto done;
}
@ -3406,11 +3628,12 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
if (sdata->dev == local->mdev)
continue;
if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)
ieee80211_send_nullfunc(local, sdata, 0);
ieee80211_sta_timer((unsigned long)sdata);
}
/* Tell AP we're back */
if (sdata->vif.type == IEEE80211_IF_TYPE_STA &&
sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)
ieee80211_send_nullfunc(local, sdata, 0);
ieee80211_restart_sta_timer(sdata);
netif_wake_queue(sdata->dev);
}
@ -3654,15 +3877,25 @@ ieee80211_sta_scan_result(struct net_device *dev,
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWESSID;
iwe.u.data.length = bss->ssid_len;
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
bss->ssid);
if (bss_mesh_cfg(bss)) {
iwe.u.data.length = bss_mesh_id_len(bss);
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
bss_mesh_id(bss));
} else {
iwe.u.data.length = bss->ssid_len;
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
bss->ssid);
}
if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
|| bss_mesh_cfg(bss)) {
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWMODE;
if (bss->capability & WLAN_CAPABILITY_ESS)
if (bss_mesh_cfg(bss))
iwe.u.mode = IW_MODE_MESH;
else if (bss->capability & WLAN_CAPABILITY_ESS)
iwe.u.mode = IW_MODE_MASTER;
else
iwe.u.mode = IW_MODE_ADHOC;
@ -3751,6 +3984,45 @@ ieee80211_sta_scan_result(struct net_device *dev,
}
}
if (bss_mesh_cfg(bss)) {
char *buf;
u8 *cfg = bss_mesh_cfg(bss);
buf = kmalloc(50, GFP_ATOMIC);
if (buf) {
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM;
sprintf(buf, "Mesh network (version %d)", cfg[0]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(current_ev, end_buf,
&iwe, buf);
sprintf(buf, "Path Selection Protocol ID: "
"0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
cfg[4]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(current_ev, end_buf,
&iwe, buf);
sprintf(buf, "Path Selection Metric ID: "
"0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
cfg[8]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(current_ev, end_buf,
&iwe, buf);
sprintf(buf, "Congestion Control Mode ID: "
"0x%02X%02X%02X%02X", cfg[9], cfg[10],
cfg[11], cfg[12]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(current_ev, end_buf,
&iwe, buf);
sprintf(buf, "Channel Precedence: "
"0x%02X%02X%02X%02X", cfg[13], cfg[14],
cfg[15], cfg[16]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(current_ev, end_buf,
&iwe, buf);
kfree(buf);
}
}
return current_ev;
}
@ -3819,8 +4091,8 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name);
sta = sta_info_add(local, dev, addr, GFP_ATOMIC);
if (IS_ERR(sta))
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
if (!sta)
return NULL;
sta->flags |= WLAN_STA_AUTHORIZED;
@ -3830,7 +4102,12 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
rate_control_rate_init(sta, local);
return sta; /* caller will call sta_info_put() */
if (sta_info_insert(sta)) {
sta_info_destroy(sta);
return NULL;
}
return sta;
}

View file

@ -20,8 +20,8 @@
#include "aes_ccm.h"
/*
* Key handling basics
/**
* DOC: Key handling basics
*
* Key handling in mac80211 is done based on per-interface (sub_if_data)
* keys and per-station keys. Since each station belongs to an interface,
@ -174,6 +174,9 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
{
int idx, defkey;
if (new)
list_add(&new->list, &sdata->key_list);
if (sta) {
rcu_assign_pointer(sta->key, new);
} else {
@ -190,9 +193,6 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
ieee80211_set_default_key(sdata, -1);
rcu_assign_pointer(sdata->keys[idx], new);
if (new)
list_add(&new->list, &sdata->key_list);
if (defkey && new)
ieee80211_set_default_key(sdata, new->conf.keyidx);
}
@ -240,14 +240,17 @@ void ieee80211_key_link(struct ieee80211_key *key,
if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
struct sta_info *ap;
rcu_read_lock();
/* same here, the AP could be using QoS */
ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
if (ap) {
if (ap->flags & WLAN_STA_WME)
key->conf.flags |=
IEEE80211_KEY_FLAG_WMM_STA;
sta_info_put(ap);
}
rcu_read_unlock();
}
}
@ -290,6 +293,9 @@ void ieee80211_key_free(struct ieee80211_key *key)
__ieee80211_key_replace(key->sdata, key->sta,
key, NULL);
/*
* Do NOT remove this without looking at sta_info_destroy()
*/
synchronize_rcu();
/*

449
net/mac80211/mesh.c Normal file
View file

@ -0,0 +1,449 @@
/*
* Copyright (c) 2008 open80211s Ltd.
* Authors: Luis Carlos Cobo <luisca@cozybit.com>
* Javier Cardona <javier@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "ieee80211_i.h"
#include "mesh.h"
#define PP_OFFSET 1 /* Path Selection Protocol */
#define PM_OFFSET 5 /* Path Selection Metric */
#define CC_OFFSET 9 /* Congestion Control Mode */
#define CAPAB_OFFSET 17
#define ACCEPT_PLINKS 0x80
int mesh_allocated;
static struct kmem_cache *rm_cache;
void ieee80211s_init(void)
{
mesh_pathtbl_init();
mesh_allocated = 1;
rm_cache = kmem_cache_create("mesh_rmc", sizeof(struct rmc_entry),
0, 0, NULL);
}
void ieee80211s_stop(void)
{
mesh_pathtbl_unregister();
kmem_cache_destroy(rm_cache);
}
/**
* mesh_matches_local - check if the config of a mesh point matches ours
*
* @ie: information elements of a management frame from the mesh peer
* @dev: local mesh interface
*
* This function checks if the mesh configuration of a mesh point matches the
* local mesh configuration, i.e. if both nodes belong to the same mesh network.
*/
bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *sta = &sdata->u.sta;
/*
* As support for each feature is added, check for matching
* - On mesh config capabilities
* - Power Save Support En
* - Sync support enabled
* - Sync support active
* - Sync support required from peer
* - MDA enabled
* - Power management control on fc
*/
if (sta->mesh_id_len == ie->mesh_id_len &&
memcmp(sta->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
memcmp(sta->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 &&
memcmp(sta->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 &&
memcmp(sta->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0)
return true;
return false;
}
/**
* mesh_peer_accepts_plinks - check if an mp is willing to establish peer links
*
* @ie: information elements of a management frame from the mesh peer
* @dev: local mesh interface
*/
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie,
struct net_device *dev)
{
return (*(ie->mesh_config + CAPAB_OFFSET) & ACCEPT_PLINKS) != 0;
}
/**
* mesh_accept_plinks_update: update accepting_plink in local mesh beacons
*
* @sdata: mesh interface in which mesh beacons are going to be updated
*/
void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
{
bool free_plinks;
/* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0,
* the mesh interface might be able to establish plinks with peers that
* are already on the table but are not on PLINK_ESTAB state. However,
* in general the mesh interface is not accepting peer link requests
* from new peers, and that must be reflected in the beacon
*/
free_plinks = mesh_plink_availables(sdata);
if (free_plinks != sdata->u.sta.accepting_plinks)
ieee80211_sta_timer((unsigned long) sdata);
}
void mesh_ids_set_default(struct ieee80211_if_sta *sta)
{
u8 def_id[4] = {0x00, 0x0F, 0xAC, 0xff};
memcpy(sta->mesh_pp_id, def_id, 4);
memcpy(sta->mesh_pm_id, def_id, 4);
memcpy(sta->mesh_cc_id, def_id, 4);
}
int mesh_rmc_init(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
int i;
sdata->u.sta.rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL);
if (!sdata->u.sta.rmc)
return -ENOMEM;
sdata->u.sta.rmc->idx_mask = RMC_BUCKETS - 1;
for (i = 0; i < RMC_BUCKETS; i++)
INIT_LIST_HEAD(&sdata->u.sta.rmc->bucket[i].list);
return 0;
}
void mesh_rmc_free(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_rmc *rmc = sdata->u.sta.rmc;
struct rmc_entry *p, *n;
int i;
if (!sdata->u.sta.rmc)
return;
for (i = 0; i < RMC_BUCKETS; i++)
list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) {
list_del(&p->list);
kmem_cache_free(rm_cache, p);
}
kfree(rmc);
sdata->u.sta.rmc = NULL;
}
/**
* mesh_rmc_check - Check frame in recent multicast cache and add if absent.
*
* @sa: source address
* @mesh_hdr: mesh_header
*
* Returns: 0 if the frame is not in the cache, nonzero otherwise.
*
* Checks using the source address and the mesh sequence number if we have
* received this frame lately. If the frame is not in the cache, it is added to
* it.
*/
int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_rmc *rmc = sdata->u.sta.rmc;
u32 seqnum = 0;
int entries = 0;
u8 idx;
struct rmc_entry *p, *n;
/* Don't care about endianness since only match matters */
memcpy(&seqnum, mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
idx = mesh_hdr->seqnum[0] & rmc->idx_mask;
list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) {
++entries;
if (time_after(jiffies, p->exp_time) ||
(entries == RMC_QUEUE_MAX_LEN)) {
list_del(&p->list);
kmem_cache_free(rm_cache, p);
--entries;
} else if ((seqnum == p->seqnum)
&& (memcmp(sa, p->sa, ETH_ALEN) == 0))
return -1;
}
p = kmem_cache_alloc(rm_cache, GFP_ATOMIC);
if (!p) {
printk(KERN_DEBUG "o11s: could not allocate RMC entry\n");
return 0;
}
p->seqnum = seqnum;
p->exp_time = jiffies + RMC_TIMEOUT;
memcpy(p->sa, sa, ETH_ALEN);
list_add(&p->list, &rmc->bucket[idx].list);
return 0;
}
void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_supported_band *sband;
u8 *pos;
int len, i, rate;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
len = sband->n_bitrates;
if (len > 8)
len = 8;
pos = skb_put(skb, len + 2);
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = len;
for (i = 0; i < len; i++) {
rate = sband->bitrates[i].bitrate;
*pos++ = (u8) (rate / 5);
}
if (sband->n_bitrates > len) {
pos = skb_put(skb, sband->n_bitrates - len + 2);
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = sband->n_bitrates - len;
for (i = len; i < sband->n_bitrates; i++) {
rate = sband->bitrates[i].bitrate;
*pos++ = (u8) (rate / 5);
}
}
pos = skb_put(skb, 2 + sdata->u.sta.mesh_id_len);
*pos++ = WLAN_EID_MESH_ID;
*pos++ = sdata->u.sta.mesh_id_len;
if (sdata->u.sta.mesh_id_len)
memcpy(pos, sdata->u.sta.mesh_id, sdata->u.sta.mesh_id_len);
pos = skb_put(skb, 21);
*pos++ = WLAN_EID_MESH_CONFIG;
*pos++ = MESH_CFG_LEN;
/* Version */
*pos++ = 1;
/* Active path selection protocol ID */
memcpy(pos, sdata->u.sta.mesh_pp_id, 4);
pos += 4;
/* Active path selection metric ID */
memcpy(pos, sdata->u.sta.mesh_pm_id, 4);
pos += 4;
/* Congestion control mode identifier */
memcpy(pos, sdata->u.sta.mesh_cc_id, 4);
pos += 4;
/* Channel precedence:
* Not running simple channel unification protocol
*/
memset(pos, 0x00, 4);
pos += 4;
/* Mesh capability */
sdata->u.sta.accepting_plinks = mesh_plink_availables(sdata);
*pos++ = sdata->u.sta.accepting_plinks ? ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
return;
}
u32 mesh_table_hash(u8 *addr, struct net_device *dev, struct mesh_table *tbl)
{
/* Use last four bytes of hw addr and interface index as hash index */
return jhash_2words(*(u32 *)(addr+2), dev->ifindex, tbl->hash_rnd)
& tbl->hash_mask;
}
u8 mesh_id_hash(u8 *mesh_id, int mesh_id_len)
{
if (!mesh_id_len)
return 1;
else if (mesh_id_len == 1)
return (u8) mesh_id[0];
else
return (u8) (mesh_id[0] + 2 * mesh_id[1]);
}
struct mesh_table *mesh_table_alloc(int size_order)
{
int i;
struct mesh_table *newtbl;
newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL);
if (!newtbl)
return NULL;
newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) *
(1 << size_order), GFP_KERNEL);
if (!newtbl->hash_buckets) {
kfree(newtbl);
return NULL;
}
newtbl->hashwlock = kmalloc(sizeof(spinlock_t) *
(1 << size_order), GFP_KERNEL);
if (!newtbl->hashwlock) {
kfree(newtbl->hash_buckets);
kfree(newtbl);
return NULL;
}
newtbl->size_order = size_order;
newtbl->hash_mask = (1 << size_order) - 1;
atomic_set(&newtbl->entries, 0);
get_random_bytes(&newtbl->hash_rnd,
sizeof(newtbl->hash_rnd));
for (i = 0; i <= newtbl->hash_mask; i++)
spin_lock_init(&newtbl->hashwlock[i]);
return newtbl;
}
void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
{
struct hlist_head *mesh_hash;
struct hlist_node *p, *q;
int i;
mesh_hash = tbl->hash_buckets;
for (i = 0; i <= tbl->hash_mask; i++) {
spin_lock(&tbl->hashwlock[i]);
hlist_for_each_safe(p, q, &mesh_hash[i]) {
tbl->free_node(p, free_leafs);
atomic_dec(&tbl->entries);
}
spin_unlock(&tbl->hashwlock[i]);
}
kfree(tbl->hash_buckets);
kfree(tbl->hashwlock);
kfree(tbl);
}
static void ieee80211_mesh_path_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct ieee80211_local *local = wdev_priv(&sdata->wdev);
queue_work(local->hw.workqueue, &ifsta->work);
}
struct mesh_table *mesh_table_grow(struct mesh_table *tbl)
{
struct mesh_table *newtbl;
struct hlist_head *oldhash;
struct hlist_node *p;
int err = 0;
int i;
if (atomic_read(&tbl->entries)
< tbl->mean_chain_len * (tbl->hash_mask + 1)) {
err = -EPERM;
goto endgrow;
}
newtbl = mesh_table_alloc(tbl->size_order + 1);
if (!newtbl) {
err = -ENOMEM;
goto endgrow;
}
newtbl->free_node = tbl->free_node;
newtbl->mean_chain_len = tbl->mean_chain_len;
newtbl->copy_node = tbl->copy_node;
atomic_set(&newtbl->entries, atomic_read(&tbl->entries));
oldhash = tbl->hash_buckets;
for (i = 0; i <= tbl->hash_mask; i++)
hlist_for_each(p, &oldhash[i])
tbl->copy_node(p, newtbl);
endgrow:
if (err)
return NULL;
else
return newtbl;
}
/**
* ieee80211_new_mesh_header - create a new mesh header
* @meshhdr: uninitialized mesh header
* @sdata: mesh interface to be used
*
* Return the header length.
*/
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
struct ieee80211_sub_if_data *sdata)
{
meshhdr->flags = 0;
meshhdr->ttl = sdata->u.sta.mshcfg.dot11MeshTTL;
meshhdr->seqnum[0] = sdata->u.sta.mesh_seqnum[0]++;
meshhdr->seqnum[1] = sdata->u.sta.mesh_seqnum[1];
meshhdr->seqnum[2] = sdata->u.sta.mesh_seqnum[2];
if (sdata->u.sta.mesh_seqnum[0] == 0) {
sdata->u.sta.mesh_seqnum[1]++;
if (sdata->u.sta.mesh_seqnum[1] == 0)
sdata->u.sta.mesh_seqnum[2]++;
}
return 5;
}
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
ifsta->mshcfg.dot11MeshRetryTimeout = MESH_RET_T;
ifsta->mshcfg.dot11MeshConfirmTimeout = MESH_CONF_T;
ifsta->mshcfg.dot11MeshHoldingTimeout = MESH_HOLD_T;
ifsta->mshcfg.dot11MeshMaxRetries = MESH_MAX_RETR;
ifsta->mshcfg.dot11MeshTTL = MESH_TTL;
ifsta->mshcfg.auto_open_plinks = true;
ifsta->mshcfg.dot11MeshMaxPeerLinks =
MESH_MAX_ESTAB_PLINKS;
ifsta->mshcfg.dot11MeshHWMPactivePathTimeout =
MESH_PATH_TIMEOUT;
ifsta->mshcfg.dot11MeshHWMPpreqMinInterval =
MESH_PREQ_MIN_INT;
ifsta->mshcfg.dot11MeshHWMPnetDiameterTraversalTime =
MESH_DIAM_TRAVERSAL_TIME;
ifsta->mshcfg.dot11MeshHWMPmaxPREQretries =
MESH_MAX_PREQ_RETRIES;
ifsta->mshcfg.path_refresh_time =
MESH_PATH_REFRESH_TIME;
ifsta->mshcfg.min_discovery_timeout =
MESH_MIN_DISCOVERY_TIMEOUT;
ifsta->accepting_plinks = true;
ifsta->preq_id = 0;
ifsta->dsn = 0;
atomic_set(&ifsta->mpaths, 0);
mesh_rmc_init(sdata->dev);
ifsta->last_preq = jiffies;
/* Allocate all mesh structures when creating the first mesh interface. */
if (!mesh_allocated)
ieee80211s_init();
mesh_ids_set_default(ifsta);
setup_timer(&ifsta->mesh_path_timer,
ieee80211_mesh_path_timer,
(unsigned long) sdata);
INIT_LIST_HEAD(&ifsta->preq_queue.list);
spin_lock_init(&ifsta->mesh_preq_queue_lock);
}

290
net/mac80211/mesh.h Normal file
View file

@ -0,0 +1,290 @@
/*
* Copyright (c) 2008 open80211s Ltd.
* Authors: Luis Carlos Cobo <luisca@cozybit.com>
* Javier Cardona <javier@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef IEEE80211S_H
#define IEEE80211S_H
#include <linux/types.h>
#include <linux/jhash.h>
#include "ieee80211_i.h"
/* Data structures */
/**
* enum mesh_path_flags - mac80211 mesh path flags
*
*
*
* @MESH_PATH_ACTIVE: the mesh path is can be used for forwarding
* @MESH_PATH_RESOLVED: the discovery process is running for this mesh path
* @MESH_PATH_DSN_VALID: the mesh path contains a valid destination sequence
* number
* @MESH_PATH_FIXED: the mesh path has been manually set and should not be
* modified
* @MESH_PATH_RESOLVED: the mesh path can has been resolved
*
* MESH_PATH_RESOLVED and MESH_PATH_DELETE are used by the mesh path timer to
* decide when to stop or cancel the mesh path discovery.
*/
enum mesh_path_flags {
MESH_PATH_ACTIVE = BIT(0),
MESH_PATH_RESOLVING = BIT(1),
MESH_PATH_DSN_VALID = BIT(2),
MESH_PATH_FIXED = BIT(3),
MESH_PATH_RESOLVED = BIT(4),
};
/**
* struct mesh_path - mac80211 mesh path structure
*
* @dst: mesh path destination mac address
* @dev: mesh path device
* @next_hop: mesh neighbor to which frames for this destination will be
* forwarded
* @timer: mesh path discovery timer
* @frame_queue: pending queue for frames sent to this destination while the
* path is unresolved
* @dsn: destination sequence number of the destination
* @metric: current metric to this destination
* @hop_count: hops to destination
* @exp_time: in jiffies, when the path will expire or when it expired
* @discovery_timeout: timeout (lapse in jiffies) used for the last discovery
* retry
* @discovery_retries: number of discovery retries
* @flags: mesh path flags, as specified on &enum mesh_path_flags
* @state_lock: mesh pat state lock
*
*
* The combination of dst and dev is unique in the mesh path table. Since the
* next_hop STA is only protected by RCU as well, deleting the STA must also
* remove/substitute the mesh_path structure and wait until that is no longer
* reachable before destroying the STA completely.
*/
struct mesh_path {
u8 dst[ETH_ALEN];
struct net_device *dev;
struct sta_info *next_hop;
struct timer_list timer;
struct sk_buff_head frame_queue;
struct rcu_head rcu;
u32 dsn;
u32 metric;
u8 hop_count;
unsigned long exp_time;
u32 discovery_timeout;
u8 discovery_retries;
enum mesh_path_flags flags;
spinlock_t state_lock;
};
/**
* struct mesh_table
*
* @hash_buckets: array of hash buckets of the table
* @hashwlock: array of locks to protect write operations, one per bucket
* @hash_mask: 2^size_order - 1, used to compute hash idx
* @hash_rnd: random value used for hash computations
* @entries: number of entries in the table
* @free_node: function to free nodes of the table
* @copy_node: fuction to copy nodes of the table
* @size_order: determines size of the table, there will be 2^size_order hash
* buckets
* @mean_chain_len: maximum average length for the hash buckets' list, if it is
* reached, the table will grow
*/
struct mesh_table {
/* Number of buckets will be 2^N */
struct hlist_head *hash_buckets;
spinlock_t *hashwlock; /* One per bucket, for add/del */
unsigned int hash_mask; /* (2^size_order) - 1 */
__u32 hash_rnd; /* Used for hash generation */
atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */
void (*free_node) (struct hlist_node *p, bool free_leafs);
void (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
int size_order;
int mean_chain_len;
};
/* Recent multicast cache */
/* RMC_BUCKETS must be a power of 2, maximum 256 */
#define RMC_BUCKETS 256
#define RMC_QUEUE_MAX_LEN 4
#define RMC_TIMEOUT (3 * HZ)
/**
* struct rmc_entry - entry in the Recent Multicast Cache
*
* @seqnum: mesh sequence number of the frame
* @exp_time: expiration time of the entry, in jiffies
* @sa: source address of the frame
*
* The Recent Multicast Cache keeps track of the latest multicast frames that
* have been received by a mesh interface and discards received multicast frames
* that are found in the cache.
*/
struct rmc_entry {
struct list_head list;
u32 seqnum;
unsigned long exp_time;
u8 sa[ETH_ALEN];
};
struct mesh_rmc {
struct rmc_entry bucket[RMC_BUCKETS];
u8 idx_mask;
};
/* Mesh IEs constants */
#define MESH_CFG_LEN 19
/*
* MESH_CFG_COMP_LEN Includes:
* - Active path selection protocol ID.
* - Active path selection metric ID.
* - Congestion control mode identifier.
* - Channel precedence.
* Does not include mesh capabilities, which may vary across nodes in the same
* mesh
*/
#define MESH_CFG_CMP_LEN 17
/* Default values, timeouts in ms */
#define MESH_TTL 5
#define MESH_MAX_RETR 3
#define MESH_RET_T 100
#define MESH_CONF_T 100
#define MESH_HOLD_T 100
#define MESH_PATH_TIMEOUT 5000
/* Minimum interval between two consecutive PREQs originated by the same
* interface
*/
#define MESH_PREQ_MIN_INT 10
#define MESH_DIAM_TRAVERSAL_TIME 50
/* Paths will be refreshed if they are closer than PATH_REFRESH_TIME to their
* expiration
*/
#define MESH_PATH_REFRESH_TIME 1000
#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
#define MESH_MAX_PREQ_RETRIES 4
#define MESH_PATH_EXPIRE (600 * HZ)
/* Default maximum number of established plinks per interface */
#define MESH_MAX_ESTAB_PLINKS 32
/* Default maximum number of plinks per interface */
#define MESH_MAX_PLINKS 256
/* Maximum number of paths per interface */
#define MESH_MAX_MPATHS 1024
/* Pending ANA approval */
#define PLINK_CATEGORY 30
#define MESH_PATH_SEL_CATEGORY 32
/* Mesh Header Flags */
#define IEEE80211S_FLAGS_AE 0x3
/* Public interfaces */
/* Various */
u8 mesh_id_hash(u8 *mesh_id, int mesh_id_len);
int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
struct ieee80211_sub_if_data *sdata);
int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
struct net_device *dev);
bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev);
void mesh_ids_set_default(struct ieee80211_if_sta *sta);
void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev);
void mesh_rmc_free(struct net_device *dev);
int mesh_rmc_init(struct net_device *dev);
void ieee80211s_init(void);
void ieee80211s_stop(void);
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
/* Mesh paths */
int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb,
struct net_device *dev);
void mesh_path_start_discovery(struct net_device *dev);
struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev);
struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev);
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
void mesh_path_expire(struct net_device *dev);
void mesh_path_flush(struct net_device *dev);
void mesh_rx_path_sel_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
size_t len);
int mesh_path_add(u8 *dst, struct net_device *dev);
/* Mesh plinks */
void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
bool add);
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie,
struct net_device *dev);
void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
void mesh_plink_broken(struct sta_info *sta);
void mesh_plink_deactivate(struct sta_info *sta);
int mesh_plink_open(struct sta_info *sta);
int mesh_plink_close(struct sta_info *sta);
void mesh_plink_block(struct sta_info *sta);
void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
size_t len, struct ieee80211_rx_status *rx_status);
/* Private interfaces */
/* Mesh tables */
struct mesh_table *mesh_table_alloc(int size_order);
void mesh_table_free(struct mesh_table *tbl, bool free_leafs);
struct mesh_table *mesh_table_grow(struct mesh_table *tbl);
u32 mesh_table_hash(u8 *addr, struct net_device *dev, struct mesh_table *tbl);
/* Mesh paths */
int mesh_path_error_tx(u8 *dest, __le32 dest_dsn, u8 *ra,
struct net_device *dev);
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);
void mesh_path_flush_pending(struct mesh_path *mpath);
void mesh_path_tx_pending(struct mesh_path *mpath);
int mesh_pathtbl_init(void);
void mesh_pathtbl_unregister(void);
int mesh_path_del(u8 *addr, struct net_device *dev);
void mesh_path_timer(unsigned long data);
void mesh_path_flush_by_nexthop(struct sta_info *sta);
void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev);
#ifdef CONFIG_MAC80211_MESH
extern int mesh_allocated;
static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
{
return sdata->u.sta.mshcfg.dot11MeshMaxPeerLinks -
atomic_read(&sdata->u.sta.mshstats.estab_plinks);
}
static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata)
{
return (min_t(long, mesh_plink_free_count(sdata),
MESH_MAX_PLINKS - sdata->local->num_sta)) > 0;
}
static inline void mesh_path_activate(struct mesh_path *mpath)
{
mpath->flags |= MESH_PATH_ACTIVE | MESH_PATH_RESOLVED;
}
#define for_each_mesh_entry(x, p, node, i) \
for (i = 0; i <= x->hash_mask; i++) \
hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list)
#else
#define mesh_allocated 0
#endif
#define MESH_PREQ(skb) (skb->cb + 30)
#endif /* IEEE80211S_H */

857
net/mac80211/mesh_hwmp.c Normal file
View file

@ -0,0 +1,857 @@
/*
* Copyright (c) 2008 open80211s Ltd.
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <asm/unaligned.h>
#include "mesh.h"
#define IEEE80211_FC(type, stype) cpu_to_le16(type | stype)
#define TEST_FRAME_LEN 8192
#define MAX_METRIC 0xffffffff
#define ARITH_SHIFT 8
/* Number of frames buffered per destination for unresolved destinations */
#define MESH_FRAME_QUEUE_LEN 10
#define MAX_PREQ_QUEUE_LEN 64
/* Destination only */
#define MP_F_DO 0x1
/* Reply and forward */
#define MP_F_RF 0x2
static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
{
if (ae)
offset += 6;
return le32_to_cpu(get_unaligned((__le32 *) (preq_elem + offset)));
}
/* HWMP IE processing macros */
#define AE_F (1<<6)
#define AE_F_SET(x) (*x & AE_F)
#define PREQ_IE_FLAGS(x) (*(x))
#define PREQ_IE_HOPCOUNT(x) (*(x + 1))
#define PREQ_IE_TTL(x) (*(x + 2))
#define PREQ_IE_PREQ_ID(x) u32_field_get(x, 3, 0)
#define PREQ_IE_ORIG_ADDR(x) (x + 7)
#define PREQ_IE_ORIG_DSN(x) u32_field_get(x, 13, 0);
#define PREQ_IE_LIFETIME(x) u32_field_get(x, 17, AE_F_SET(x));
#define PREQ_IE_METRIC(x) u32_field_get(x, 21, AE_F_SET(x));
#define PREQ_IE_DST_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26))
#define PREQ_IE_DST_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27)
#define PREQ_IE_DST_DSN(x) u32_field_get(x, 33, AE_F_SET(x));
#define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x)
#define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x)
#define PREP_IE_TTL(x) PREQ_IE_TTL(x)
#define PREP_IE_ORIG_ADDR(x) (x + 3)
#define PREP_IE_ORIG_DSN(x) u32_field_get(x, 9, 0);
#define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x));
#define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x));
#define PREP_IE_DST_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21)
#define PREP_IE_DST_DSN(x) u32_field_get(x, 27, AE_F_SET(x));
#define PERR_IE_DST_ADDR(x) (x + 2)
#define PERR_IE_DST_DSN(x) u32_field_get(x, 8, 0);
#define TU_TO_EXP_TIME(x) (jiffies + msecs_to_jiffies(x * 1024 / 1000))
#define MSEC_TO_TU(x) (x*1000/1024)
#define DSN_GT(x, y) ((long) (y) - (long) (x) < 0)
#define DSN_LT(x, y) ((long) (x) - (long) (y) < 0)
#define net_traversal_jiffies(s) \
msecs_to_jiffies(s->u.sta.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
#define default_lifetime(s) \
MSEC_TO_TU(s->u.sta.mshcfg.dot11MeshHWMPactivePathTimeout)
#define min_preq_int_jiff(s) \
(msecs_to_jiffies(s->u.sta.mshcfg.dot11MeshHWMPpreqMinInterval))
#define max_preq_retries(s) (s->u.sta.mshcfg.dot11MeshHWMPmaxPREQretries)
#define disc_timeout_jiff(s) \
msecs_to_jiffies(sdata->u.sta.mshcfg.min_discovery_timeout)
enum mpath_frame_type {
MPATH_PREQ = 0,
MPATH_PREP,
MPATH_PERR
};
static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
u8 *orig_addr, __le32 orig_dsn, u8 dst_flags, u8 *dst,
__le32 dst_dsn, u8 *da, u8 hop_count, u8 ttl, __le32 lifetime,
__le32 metric, __le32 preq_id, struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
struct ieee80211_mgmt *mgmt;
u8 *pos;
int ie_len;
if (!skb)
return -1;
skb_reserve(skb, local->hw.extra_tx_headroom);
/* 25 is the size of the common mgmt part (24) plus the size of the
* common action part (1)
*/
mgmt = (struct ieee80211_mgmt *)
skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action));
memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action));
mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
mgmt->u.action.u.mesh_action.action_code = action;
switch (action) {
case MPATH_PREQ:
ie_len = 37;
pos = skb_put(skb, 2 + ie_len);
*pos++ = WLAN_EID_PREQ;
break;
case MPATH_PREP:
ie_len = 31;
pos = skb_put(skb, 2 + ie_len);
*pos++ = WLAN_EID_PREP;
break;
default:
kfree(skb);
return -ENOTSUPP;
break;
}
*pos++ = ie_len;
*pos++ = flags;
*pos++ = hop_count;
*pos++ = ttl;
if (action == MPATH_PREQ) {
memcpy(pos, &preq_id, 4);
pos += 4;
}
memcpy(pos, orig_addr, ETH_ALEN);
pos += ETH_ALEN;
memcpy(pos, &orig_dsn, 4);
pos += 4;
memcpy(pos, &lifetime, 4);
pos += 4;
memcpy(pos, &metric, 4);
pos += 4;
if (action == MPATH_PREQ) {
/* destination count */
*pos++ = 1;
*pos++ = dst_flags;
}
memcpy(pos, dst, ETH_ALEN);
pos += ETH_ALEN;
memcpy(pos, &dst_dsn, 4);
ieee80211_sta_tx(dev, skb, 0);
return 0;
}
/**
* mesh_send_path error - Sends a PERR mesh management frame
*
* @dst: broken destination
* @dst_dsn: dsn of the broken destination
* @ra: node this frame is addressed to
*/
int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
struct ieee80211_mgmt *mgmt;
u8 *pos;
int ie_len;
if (!skb)
return -1;
skb_reserve(skb, local->hw.extra_tx_headroom);
/* 25 is the size of the common mgmt part (24) plus the size of the
* common action part (1)
*/
mgmt = (struct ieee80211_mgmt *)
skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action));
memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action));
mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, ra, ETH_ALEN);
memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
mgmt->u.action.u.mesh_action.action_code = MPATH_PERR;
ie_len = 12;
pos = skb_put(skb, 2 + ie_len);
*pos++ = WLAN_EID_PERR;
*pos++ = ie_len;
/* mode flags, reserved */
*pos++ = 0;
/* number of destinations */
*pos++ = 1;
memcpy(pos, dst, ETH_ALEN);
pos += ETH_ALEN;
memcpy(pos, &dst_dsn, 4);
ieee80211_sta_tx(dev, skb, 0);
return 0;
}
static u32 airtime_link_metric_get(struct ieee80211_local *local,
struct sta_info *sta)
{
struct ieee80211_supported_band *sband;
/* This should be adjusted for each device */
int device_constant = 1 << ARITH_SHIFT;
int test_frame_len = TEST_FRAME_LEN << ARITH_SHIFT;
int s_unit = 1 << ARITH_SHIFT;
int rate, err;
u32 tx_time, estimated_retx;
u64 result;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
if (sta->fail_avg >= 100)
return MAX_METRIC;
err = (sta->fail_avg << ARITH_SHIFT) / 100;
/* bitrate is in units of 100 Kbps, while we need rate in units of
* 1Mbps. This will be corrected on tx_time computation.
*/
rate = sband->bitrates[sta->txrate_idx].bitrate;
tx_time = (device_constant + 10 * test_frame_len / rate);
estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err));
result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT) ;
return (u32)result;
}
/**
* hwmp_route_info_get - Update routing info to originator and transmitter
*
* @dev: local mesh interface
* @mgmt: mesh management frame
* @hwmp_ie: hwmp information element (PREP or PREQ)
*
* This function updates the path routing information to the originator and the
* transmitter of a HWMP PREQ or PREP fram.
*
* Returns: metric to frame originator or 0 if the frame should not be further
* processed
*
* Notes: this function is the only place (besides user-provided info) where
* path routing information is updated.
*/
static u32 hwmp_route_info_get(struct net_device *dev,
struct ieee80211_mgmt *mgmt,
u8 *hwmp_ie)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct mesh_path *mpath;
struct sta_info *sta;
bool fresh_info;
u8 *orig_addr, *ta;
u32 orig_dsn, orig_metric;
unsigned long orig_lifetime, exp_time;
u32 last_hop_metric, new_metric;
bool process = true;
u8 action = mgmt->u.action.u.mesh_action.action_code;
rcu_read_lock();
sta = sta_info_get(local, mgmt->sa);
if (!sta) {
rcu_read_unlock();
return 0;
}
last_hop_metric = airtime_link_metric_get(local, sta);
/* Update and check originator routing info */
fresh_info = true;
switch (action) {
case MPATH_PREQ:
orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie);
orig_dsn = PREQ_IE_ORIG_DSN(hwmp_ie);
orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie);
orig_metric = PREQ_IE_METRIC(hwmp_ie);
break;
case MPATH_PREP:
/* Originator here refers to the MP that was the destination in
* the Path Request. The draft refers to that MP as the
* destination address, even though usually it is the origin of
* the PREP frame. We divert from the nomenclature in the draft
* so that we can easily use a single function to gather path
* information from both PREQ and PREP frames.
*/
orig_addr = PREP_IE_ORIG_ADDR(hwmp_ie);
orig_dsn = PREP_IE_ORIG_DSN(hwmp_ie);
orig_lifetime = PREP_IE_LIFETIME(hwmp_ie);
orig_metric = PREP_IE_METRIC(hwmp_ie);
break;
default:
rcu_read_unlock();
return 0;
}
new_metric = orig_metric + last_hop_metric;
if (new_metric < orig_metric)
new_metric = MAX_METRIC;
exp_time = TU_TO_EXP_TIME(orig_lifetime);
if (memcmp(orig_addr, dev->dev_addr, ETH_ALEN) == 0) {
/* This MP is the originator, we are not interested in this
* frame, except for updating transmitter's path info.
*/
process = false;
fresh_info = false;
} else {
mpath = mesh_path_lookup(orig_addr, dev);
if (mpath) {
spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_FIXED)
fresh_info = false;
else if ((mpath->flags & MESH_PATH_ACTIVE) &&
(mpath->flags & MESH_PATH_DSN_VALID)) {
if (DSN_GT(mpath->dsn, orig_dsn) ||
(mpath->dsn == orig_dsn &&
action == MPATH_PREQ &&
new_metric > mpath->metric)) {
process = false;
fresh_info = false;
}
}
} else {
mesh_path_add(orig_addr, dev);
mpath = mesh_path_lookup(orig_addr, dev);
if (!mpath) {
rcu_read_unlock();
return 0;
}
spin_lock_bh(&mpath->state_lock);
}
if (fresh_info) {
mesh_path_assign_nexthop(mpath, sta);
mpath->flags |= MESH_PATH_DSN_VALID;
mpath->metric = new_metric;
mpath->dsn = orig_dsn;
mpath->exp_time = time_after(mpath->exp_time, exp_time)
? mpath->exp_time : exp_time;
mesh_path_activate(mpath);
spin_unlock_bh(&mpath->state_lock);
mesh_path_tx_pending(mpath);
/* draft says preq_id should be saved to, but there does
* not seem to be any use for it, skipping by now
*/
} else
spin_unlock_bh(&mpath->state_lock);
}
/* Update and check transmitter routing info */
ta = mgmt->sa;
if (memcmp(orig_addr, ta, ETH_ALEN) == 0)
fresh_info = false;
else {
fresh_info = true;
mpath = mesh_path_lookup(ta, dev);
if (mpath) {
spin_lock_bh(&mpath->state_lock);
if ((mpath->flags & MESH_PATH_FIXED) ||
((mpath->flags & MESH_PATH_ACTIVE) &&
(last_hop_metric > mpath->metric)))
fresh_info = false;
} else {
mesh_path_add(ta, dev);
mpath = mesh_path_lookup(ta, dev);
if (!mpath) {
rcu_read_unlock();
return 0;
}
spin_lock_bh(&mpath->state_lock);
}
if (fresh_info) {
mesh_path_assign_nexthop(mpath, sta);
mpath->flags &= ~MESH_PATH_DSN_VALID;
mpath->metric = last_hop_metric;
mpath->exp_time = time_after(mpath->exp_time, exp_time)
? mpath->exp_time : exp_time;
mesh_path_activate(mpath);
spin_unlock_bh(&mpath->state_lock);
mesh_path_tx_pending(mpath);
} else
spin_unlock_bh(&mpath->state_lock);
}
rcu_read_unlock();
return process ? new_metric : 0;
}
static void hwmp_preq_frame_process(struct net_device *dev,
struct ieee80211_mgmt *mgmt,
u8 *preq_elem, u32 metric) {
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct mesh_path *mpath;
u8 *dst_addr, *orig_addr;
u8 dst_flags, ttl;
u32 orig_dsn, dst_dsn, lifetime;
bool reply = false;
bool forward = true;
/* Update destination DSN, if present */
dst_addr = PREQ_IE_DST_ADDR(preq_elem);
orig_addr = PREQ_IE_ORIG_ADDR(preq_elem);
dst_dsn = PREQ_IE_DST_DSN(preq_elem);
orig_dsn = PREQ_IE_ORIG_DSN(preq_elem);
dst_flags = PREQ_IE_DST_F(preq_elem);
if (memcmp(dst_addr, dev->dev_addr, ETH_ALEN) == 0) {
forward = false;
reply = true;
metric = 0;
if (time_after(jiffies, ifsta->last_dsn_update +
net_traversal_jiffies(sdata)) ||
time_before(jiffies, ifsta->last_dsn_update)) {
dst_dsn = ++ifsta->dsn;
ifsta->last_dsn_update = jiffies;
}
} else {
rcu_read_lock();
mpath = mesh_path_lookup(dst_addr, dev);
if (mpath) {
if ((!(mpath->flags & MESH_PATH_DSN_VALID)) ||
DSN_LT(mpath->dsn, dst_dsn)) {
mpath->dsn = dst_dsn;
mpath->flags &= MESH_PATH_DSN_VALID;
} else if ((!(dst_flags & MP_F_DO)) &&
(mpath->flags & MESH_PATH_ACTIVE)) {
reply = true;
metric = mpath->metric;
dst_dsn = mpath->dsn;
if (dst_flags & MP_F_RF)
dst_flags |= MP_F_DO;
else
forward = false;
}
}
rcu_read_unlock();
}
if (reply) {
lifetime = PREQ_IE_LIFETIME(preq_elem);
ttl = ifsta->mshcfg.dot11MeshTTL;
if (ttl != 0)
mesh_path_sel_frame_tx(MPATH_PREP, 0, dst_addr,
cpu_to_le32(dst_dsn), 0, orig_addr,
cpu_to_le32(orig_dsn), mgmt->sa, 0, ttl,
cpu_to_le32(lifetime), cpu_to_le32(metric),
0, dev);
else
ifsta->mshstats.dropped_frames_ttl++;
}
if (forward) {
u32 preq_id;
u8 hopcount, flags;
ttl = PREQ_IE_TTL(preq_elem);
lifetime = PREQ_IE_LIFETIME(preq_elem);
if (ttl <= 1) {
ifsta->mshstats.dropped_frames_ttl++;
return;
}
--ttl;
flags = PREQ_IE_FLAGS(preq_elem);
preq_id = PREQ_IE_PREQ_ID(preq_elem);
hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1;
mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
cpu_to_le32(orig_dsn), dst_flags, dst_addr,
cpu_to_le32(dst_dsn), dev->broadcast,
hopcount, ttl, cpu_to_le32(lifetime),
cpu_to_le32(metric), cpu_to_le32(preq_id),
dev);
ifsta->mshstats.fwded_frames++;
}
}
static void hwmp_prep_frame_process(struct net_device *dev,
struct ieee80211_mgmt *mgmt,
u8 *prep_elem, u32 metric)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath;
u8 *dst_addr, *orig_addr;
u8 ttl, hopcount, flags;
u8 next_hop[ETH_ALEN];
u32 dst_dsn, orig_dsn, lifetime;
/* Note that we divert from the draft nomenclature and denominate
* destination to what the draft refers to as origininator. So in this
* function destnation refers to the final destination of the PREP,
* which corresponds with the originator of the PREQ which this PREP
* replies
*/
dst_addr = PREP_IE_DST_ADDR(prep_elem);
if (memcmp(dst_addr, dev->dev_addr, ETH_ALEN) == 0)
/* destination, no forwarding required */
return;
ttl = PREP_IE_TTL(prep_elem);
if (ttl <= 1) {
sdata->u.sta.mshstats.dropped_frames_ttl++;
return;
}
rcu_read_lock();
mpath = mesh_path_lookup(dst_addr, dev);
if (mpath)
spin_lock_bh(&mpath->state_lock);
else
goto fail;
if (!(mpath->flags & MESH_PATH_ACTIVE)) {
spin_unlock_bh(&mpath->state_lock);
goto fail;
}
memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN);
spin_unlock_bh(&mpath->state_lock);
--ttl;
flags = PREP_IE_FLAGS(prep_elem);
lifetime = PREP_IE_LIFETIME(prep_elem);
hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1;
orig_addr = PREP_IE_ORIG_ADDR(prep_elem);
dst_dsn = PREP_IE_DST_DSN(prep_elem);
orig_dsn = PREP_IE_ORIG_DSN(prep_elem);
mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr,
cpu_to_le32(orig_dsn), 0, dst_addr,
cpu_to_le32(dst_dsn), mpath->next_hop->addr, hopcount, ttl,
cpu_to_le32(lifetime), cpu_to_le32(metric),
0, dev);
rcu_read_unlock();
sdata->u.sta.mshstats.fwded_frames++;
return;
fail:
rcu_read_unlock();
sdata->u.sta.mshstats.dropped_frames_no_route++;
return;
}
static void hwmp_perr_frame_process(struct net_device *dev,
struct ieee80211_mgmt *mgmt, u8 *perr_elem)
{
struct mesh_path *mpath;
u8 *ta, *dst_addr;
u32 dst_dsn;
ta = mgmt->sa;
dst_addr = PERR_IE_DST_ADDR(perr_elem);
dst_dsn = PERR_IE_DST_DSN(perr_elem);
rcu_read_lock();
mpath = mesh_path_lookup(dst_addr, dev);
if (mpath) {
spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_ACTIVE &&
memcmp(ta, mpath->next_hop->addr, ETH_ALEN) == 0 &&
(!(mpath->flags & MESH_PATH_DSN_VALID) ||
DSN_GT(dst_dsn, mpath->dsn))) {
mpath->flags &= ~MESH_PATH_ACTIVE;
mpath->dsn = dst_dsn;
spin_unlock_bh(&mpath->state_lock);
mesh_path_error_tx(dst_addr, cpu_to_le32(dst_dsn),
dev->broadcast, dev);
} else
spin_unlock_bh(&mpath->state_lock);
}
rcu_read_unlock();
}
void mesh_rx_path_sel_frame(struct net_device *dev,
struct ieee80211_mgmt *mgmt,
size_t len)
{
struct ieee802_11_elems elems;
size_t baselen;
u32 last_hop_metric;
baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
len - baselen, &elems);
switch (mgmt->u.action.u.mesh_action.action_code) {
case MPATH_PREQ:
if (!elems.preq || elems.preq_len != 37)
/* Right now we support just 1 destination and no AE */
return;
last_hop_metric = hwmp_route_info_get(dev, mgmt, elems.preq);
if (!last_hop_metric)
return;
hwmp_preq_frame_process(dev, mgmt, elems.preq, last_hop_metric);
break;
case MPATH_PREP:
if (!elems.prep || elems.prep_len != 31)
/* Right now we support no AE */
return;
last_hop_metric = hwmp_route_info_get(dev, mgmt, elems.prep);
if (!last_hop_metric)
return;
hwmp_prep_frame_process(dev, mgmt, elems.prep, last_hop_metric);
break;
case MPATH_PERR:
if (!elems.perr || elems.perr_len != 12)
/* Right now we support only one destination per PERR */
return;
hwmp_perr_frame_process(dev, mgmt, elems.perr);
default:
return;
}
}
/**
* mesh_queue_preq - queue a PREQ to a given destination
*
* @mpath: mesh path to discover
* @flags: special attributes of the PREQ to be sent
*
* Locking: the function must be called from within a rcu read lock block.
*
*/
static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
{
struct ieee80211_sub_if_data *sdata =
IEEE80211_DEV_TO_SUB_IF(mpath->dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct mesh_preq_queue *preq_node;
preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_KERNEL);
if (!preq_node) {
printk(KERN_DEBUG "Mesh HWMP: could not allocate PREQ node\n");
return;
}
spin_lock(&ifsta->mesh_preq_queue_lock);
if (ifsta->preq_queue_len == MAX_PREQ_QUEUE_LEN) {
spin_unlock(&ifsta->mesh_preq_queue_lock);
kfree(preq_node);
if (printk_ratelimit())
printk(KERN_DEBUG "Mesh HWMP: PREQ node queue full\n");
return;
}
memcpy(preq_node->dst, mpath->dst, ETH_ALEN);
preq_node->flags = flags;
list_add_tail(&preq_node->list, &ifsta->preq_queue.list);
++ifsta->preq_queue_len;
spin_unlock(&ifsta->mesh_preq_queue_lock);
if (time_after(jiffies, ifsta->last_preq + min_preq_int_jiff(sdata)))
queue_work(sdata->local->hw.workqueue, &ifsta->work);
else if (time_before(jiffies, ifsta->last_preq)) {
/* avoid long wait if did not send preqs for a long time
* and jiffies wrapped around
*/
ifsta->last_preq = jiffies - min_preq_int_jiff(sdata) - 1;
queue_work(sdata->local->hw.workqueue, &ifsta->work);
} else
mod_timer(&ifsta->mesh_path_timer, ifsta->last_preq +
min_preq_int_jiff(sdata));
}
/**
* mesh_path_start_discovery - launch a path discovery from the PREQ queue
*
* @dev: local mesh interface
*/
void mesh_path_start_discovery(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata =
IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct mesh_preq_queue *preq_node;
struct mesh_path *mpath;
u8 ttl, dst_flags;
u32 lifetime;
spin_lock(&ifsta->mesh_preq_queue_lock);
if (!ifsta->preq_queue_len ||
time_before(jiffies, ifsta->last_preq +
min_preq_int_jiff(sdata))) {
spin_unlock(&ifsta->mesh_preq_queue_lock);
return;
}
preq_node = list_first_entry(&ifsta->preq_queue.list,
struct mesh_preq_queue, list);
list_del(&preq_node->list);
--ifsta->preq_queue_len;
spin_unlock(&ifsta->mesh_preq_queue_lock);
rcu_read_lock();
mpath = mesh_path_lookup(preq_node->dst, dev);
if (!mpath)
goto enddiscovery;
spin_lock_bh(&mpath->state_lock);
if (preq_node->flags & PREQ_Q_F_START) {
if (mpath->flags & MESH_PATH_RESOLVING) {
spin_unlock_bh(&mpath->state_lock);
goto enddiscovery;
} else {
mpath->flags &= ~MESH_PATH_RESOLVED;
mpath->flags |= MESH_PATH_RESOLVING;
mpath->discovery_retries = 0;
mpath->discovery_timeout = disc_timeout_jiff(sdata);
}
} else if (!(mpath->flags & MESH_PATH_RESOLVING) ||
mpath->flags & MESH_PATH_RESOLVED) {
mpath->flags &= ~MESH_PATH_RESOLVING;
spin_unlock_bh(&mpath->state_lock);
goto enddiscovery;
}
ifsta->last_preq = jiffies;
if (time_after(jiffies, ifsta->last_dsn_update +
net_traversal_jiffies(sdata)) ||
time_before(jiffies, ifsta->last_dsn_update)) {
++ifsta->dsn;
sdata->u.sta.last_dsn_update = jiffies;
}
lifetime = default_lifetime(sdata);
ttl = sdata->u.sta.mshcfg.dot11MeshTTL;
if (ttl == 0) {
sdata->u.sta.mshstats.dropped_frames_ttl++;
spin_unlock_bh(&mpath->state_lock);
goto enddiscovery;
}
if (preq_node->flags & PREQ_Q_F_REFRESH)
dst_flags = MP_F_DO;
else
dst_flags = MP_F_RF;
spin_unlock_bh(&mpath->state_lock);
mesh_path_sel_frame_tx(MPATH_PREQ, 0, dev->dev_addr,
cpu_to_le32(ifsta->dsn), dst_flags, mpath->dst,
cpu_to_le32(mpath->dsn), dev->broadcast, 0,
ttl, cpu_to_le32(lifetime), 0,
cpu_to_le32(ifsta->preq_id++), dev);
mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout);
enddiscovery:
rcu_read_unlock();
kfree(preq_node);
}
/**
* ieee80211s_lookup_nexthop - put the appropriate next hop on a mesh frame
*
* @next_hop: output argument for next hop address
* @skb: frame to be sent
* @dev: network device the frame will be sent through
*
* Returns: 0 if the next hop was found. Nonzero otherwise. If no next hop is
* found, the function will start a path discovery and queue the frame so it is
* sent when the path is resolved. This means the caller must not free the skb
* in this case.
*/
int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb,
struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sk_buff *skb_to_free = NULL;
struct mesh_path *mpath;
int err = 0;
rcu_read_lock();
mpath = mesh_path_lookup(skb->data, dev);
if (!mpath) {
mesh_path_add(skb->data, dev);
mpath = mesh_path_lookup(skb->data, dev);
if (!mpath) {
dev_kfree_skb(skb);
sdata->u.sta.mshstats.dropped_frames_no_route++;
err = -ENOSPC;
goto endlookup;
}
}
if (mpath->flags & MESH_PATH_ACTIVE) {
if (time_after(jiffies, mpath->exp_time -
msecs_to_jiffies(sdata->u.sta.mshcfg.path_refresh_time))
&& skb->pkt_type != PACKET_OTHERHOST
&& !(mpath->flags & MESH_PATH_RESOLVING)
&& !(mpath->flags & MESH_PATH_FIXED)) {
mesh_queue_preq(mpath,
PREQ_Q_F_START | PREQ_Q_F_REFRESH);
}
memcpy(next_hop, mpath->next_hop->addr,
ETH_ALEN);
} else {
if (!(mpath->flags & MESH_PATH_RESOLVING)) {
/* Start discovery only if it is not running yet */
mesh_queue_preq(mpath, PREQ_Q_F_START);
}
if (skb_queue_len(&mpath->frame_queue) >=
MESH_FRAME_QUEUE_LEN) {
skb_to_free = mpath->frame_queue.next;
skb_unlink(skb_to_free, &mpath->frame_queue);
}
skb_queue_tail(&mpath->frame_queue, skb);
if (skb_to_free)
mesh_path_discard_frame(skb_to_free, dev);
err = -ENOENT;
}
endlookup:
rcu_read_unlock();
return err;
}
void mesh_path_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata;
struct mesh_path *mpath;
rcu_read_lock();
mpath = (struct mesh_path *) data;
mpath = rcu_dereference(mpath);
if (!mpath)
goto endmpathtimer;
spin_lock_bh(&mpath->state_lock);
sdata = IEEE80211_DEV_TO_SUB_IF(mpath->dev);
if (mpath->flags & MESH_PATH_RESOLVED ||
(!(mpath->flags & MESH_PATH_RESOLVING)))
mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED);
else if (mpath->discovery_retries < max_preq_retries(sdata)) {
++mpath->discovery_retries;
mpath->discovery_timeout *= 2;
mesh_queue_preq(mpath, 0);
} else {
mpath->flags = 0;
mpath->exp_time = jiffies;
mesh_path_flush_pending(mpath);
}
spin_unlock_bh(&mpath->state_lock);
endmpathtimer:
rcu_read_unlock();
}

516
net/mac80211/mesh_pathtbl.c Normal file
View file

@ -0,0 +1,516 @@
/*
* Copyright (c) 2008 open80211s Ltd.
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/etherdevice.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "mesh.h"
/* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */
#define INIT_PATHS_SIZE_ORDER 2
/* Keep the mean chain length below this constant */
#define MEAN_CHAIN_LEN 2
#define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \
time_after(jiffies, mpath->exp_time) && \
!(mpath->flags & MESH_PATH_FIXED))
struct mpath_node {
struct hlist_node list;
struct rcu_head rcu;
/* This indirection allows two different tables to point to the same
* mesh_path structure, useful when resizing
*/
struct mesh_path *mpath;
};
static struct mesh_table *mesh_paths;
/* This lock will have the grow table function as writer and add / delete nodes
* as readers. When reading the table (i.e. doing lookups) we are well protected
* by RCU
*/
static DEFINE_RWLOCK(pathtbl_resize_lock);
/**
*
* mesh_path_assign_nexthop - update mesh path next hop
*
* @mpath: mesh path to update
* @sta: next hop to assign
*
* Locking: mpath->state_lock must be held when calling this function
*/
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
{
rcu_assign_pointer(mpath->next_hop, sta);
}
/**
* mesh_path_lookup - look up a path in the mesh path table
* @dst: hardware address (ETH_ALEN length) of destination
* @dev: local interface
*
* Returns: pointer to the mesh path structure, or NULL if not found
*
* Locking: must be called within a read rcu section.
*/
struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev)
{
struct mesh_path *mpath;
struct hlist_node *n;
struct hlist_head *bucket;
struct mesh_table *tbl;
struct mpath_node *node;
tbl = rcu_dereference(mesh_paths);
bucket = &tbl->hash_buckets[mesh_table_hash(dst, dev, tbl)];
hlist_for_each_entry_rcu(node, n, bucket, list) {
mpath = node->mpath;
if (mpath->dev == dev &&
memcmp(dst, mpath->dst, ETH_ALEN) == 0) {
if (MPATH_EXPIRED(mpath)) {
spin_lock_bh(&mpath->state_lock);
if (MPATH_EXPIRED(mpath))
mpath->flags &= ~MESH_PATH_ACTIVE;
spin_unlock_bh(&mpath->state_lock);
}
return mpath;
}
}
return NULL;
}
/**
* mesh_path_lookup_by_idx - look up a path in the mesh path table by its index
* @idx: index
* @dev: local interface, or NULL for all entries
*
* Returns: pointer to the mesh path structure, or NULL if not found.
*
* Locking: must be called within a read rcu section.
*/
struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev)
{
struct mpath_node *node;
struct hlist_node *p;
int i;
int j = 0;
for_each_mesh_entry(mesh_paths, p, node, i) {
if (dev && node->mpath->dev != dev)
continue;
if (j++ == idx) {
if (MPATH_EXPIRED(node->mpath)) {
spin_lock_bh(&node->mpath->state_lock);
if (MPATH_EXPIRED(node->mpath))
node->mpath->flags &= ~MESH_PATH_ACTIVE;
spin_unlock_bh(&node->mpath->state_lock);
}
return node->mpath;
}
}
return NULL;
}
/**
* mesh_path_add - allocate and add a new path to the mesh path table
* @addr: destination address of the path (ETH_ALEN length)
* @dev: local interface
*
* Returns: 0 on sucess
*
* State: the initial state of the new path is set to 0
*/
int mesh_path_add(u8 *dst, struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath, *new_mpath;
struct mpath_node *node, *new_node;
struct hlist_head *bucket;
struct hlist_node *n;
int grow = 0;
int err = 0;
u32 hash_idx;
if (memcmp(dst, dev->dev_addr, ETH_ALEN) == 0)
/* never add ourselves as neighbours */
return -ENOTSUPP;
if (is_multicast_ether_addr(dst))
return -ENOTSUPP;
if (atomic_add_unless(&sdata->u.sta.mpaths, 1, MESH_MAX_MPATHS) == 0)
return -ENOSPC;
read_lock(&pathtbl_resize_lock);
new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
if (!new_mpath) {
atomic_dec(&sdata->u.sta.mpaths);
err = -ENOMEM;
goto endadd2;
}
memcpy(new_mpath->dst, dst, ETH_ALEN);
new_mpath->dev = dev;
new_mpath->flags = 0;
skb_queue_head_init(&new_mpath->frame_queue);
new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
new_node->mpath = new_mpath;
new_mpath->timer.data = (unsigned long) new_mpath;
new_mpath->timer.function = mesh_path_timer;
new_mpath->exp_time = jiffies;
spin_lock_init(&new_mpath->state_lock);
init_timer(&new_mpath->timer);
hash_idx = mesh_table_hash(dst, dev, mesh_paths);
bucket = &mesh_paths->hash_buckets[hash_idx];
spin_lock(&mesh_paths->hashwlock[hash_idx]);
hlist_for_each_entry(node, n, bucket, list) {
mpath = node->mpath;
if (mpath->dev == dev && memcmp(dst, mpath->dst, ETH_ALEN)
== 0) {
err = -EEXIST;
atomic_dec(&sdata->u.sta.mpaths);
kfree(new_node);
kfree(new_mpath);
goto endadd;
}
}
hlist_add_head_rcu(&new_node->list, bucket);
if (atomic_inc_return(&mesh_paths->entries) >=
mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1))
grow = 1;
endadd:
spin_unlock(&mesh_paths->hashwlock[hash_idx]);
endadd2:
read_unlock(&pathtbl_resize_lock);
if (!err && grow) {
struct mesh_table *oldtbl, *newtbl;
write_lock(&pathtbl_resize_lock);
oldtbl = mesh_paths;
newtbl = mesh_table_grow(mesh_paths);
if (!newtbl) {
write_unlock(&pathtbl_resize_lock);
return -ENOMEM;
}
rcu_assign_pointer(mesh_paths, newtbl);
synchronize_rcu();
mesh_table_free(oldtbl, false);
write_unlock(&pathtbl_resize_lock);
}
return err;
}
/**
* mesh_plink_broken - deactivates paths and sends perr when a link breaks
*
* @sta: broken peer link
*
* This function must be called from the rate control algorithm if enough
* delivery errors suggest that a peer link is no longer usable.
*/
void mesh_plink_broken(struct sta_info *sta)
{
struct mesh_path *mpath;
struct mpath_node *node;
struct hlist_node *p;
struct net_device *dev = sta->sdata->dev;
int i;
rcu_read_lock();
for_each_mesh_entry(mesh_paths, p, node, i) {
mpath = node->mpath;
spin_lock_bh(&mpath->state_lock);
if (mpath->next_hop == sta &&
mpath->flags & MESH_PATH_ACTIVE &&
!(mpath->flags & MESH_PATH_FIXED)) {
mpath->flags &= ~MESH_PATH_ACTIVE;
++mpath->dsn;
spin_unlock_bh(&mpath->state_lock);
mesh_path_error_tx(mpath->dst,
cpu_to_le32(mpath->dsn),
dev->broadcast, dev);
} else
spin_unlock_bh(&mpath->state_lock);
}
rcu_read_unlock();
}
EXPORT_SYMBOL(mesh_plink_broken);
/**
* mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches
*
* @sta - mesh peer to match
*
* RCU notes: this function is called when a mesh plink transitions from
* PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that
* allows path creation. This will happen before the sta can be freed (because
* sta_info_destroy() calls this) so any reader in a rcu read block will be
* protected against the plink disappearing.
*/
void mesh_path_flush_by_nexthop(struct sta_info *sta)
{
struct mesh_path *mpath;
struct mpath_node *node;
struct hlist_node *p;
int i;
for_each_mesh_entry(mesh_paths, p, node, i) {
mpath = node->mpath;
if (mpath->next_hop == sta)
mesh_path_del(mpath->dst, mpath->dev);
}
}
void mesh_path_flush(struct net_device *dev)
{
struct mesh_path *mpath;
struct mpath_node *node;
struct hlist_node *p;
int i;
for_each_mesh_entry(mesh_paths, p, node, i) {
mpath = node->mpath;
if (mpath->dev == dev)
mesh_path_del(mpath->dst, mpath->dev);
}
}
static void mesh_path_node_reclaim(struct rcu_head *rp)
{
struct mpath_node *node = container_of(rp, struct mpath_node, rcu);
struct ieee80211_sub_if_data *sdata =
IEEE80211_DEV_TO_SUB_IF(node->mpath->dev);
del_timer_sync(&node->mpath->timer);
atomic_dec(&sdata->u.sta.mpaths);
kfree(node->mpath);
kfree(node);
}
/**
* mesh_path_del - delete a mesh path from the table
*
* @addr: dst address (ETH_ALEN length)
* @dev: local interface
*
* Returns: 0 if succesful
*/
int mesh_path_del(u8 *addr, struct net_device *dev)
{
struct mesh_path *mpath;
struct mpath_node *node;
struct hlist_head *bucket;
struct hlist_node *n;
int hash_idx;
int err = 0;
read_lock(&pathtbl_resize_lock);
hash_idx = mesh_table_hash(addr, dev, mesh_paths);
bucket = &mesh_paths->hash_buckets[hash_idx];
spin_lock(&mesh_paths->hashwlock[hash_idx]);
hlist_for_each_entry(node, n, bucket, list) {
mpath = node->mpath;
if (mpath->dev == dev &&
memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
spin_lock_bh(&mpath->state_lock);
mpath->flags |= MESH_PATH_RESOLVING;
hlist_del_rcu(&node->list);
call_rcu(&node->rcu, mesh_path_node_reclaim);
atomic_dec(&mesh_paths->entries);
spin_unlock_bh(&mpath->state_lock);
goto enddel;
}
}
err = -ENXIO;
enddel:
spin_unlock(&mesh_paths->hashwlock[hash_idx]);
read_unlock(&pathtbl_resize_lock);
return err;
}
/**
* mesh_path_tx_pending - sends pending frames in a mesh path queue
*
* @mpath: mesh path to activate
*
* Locking: the state_lock of the mpath structure must NOT be held when calling
* this function.
*/
void mesh_path_tx_pending(struct mesh_path *mpath)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&mpath->frame_queue)) &&
(mpath->flags & MESH_PATH_ACTIVE))
dev_queue_xmit(skb);
}
/**
* mesh_path_discard_frame - discard a frame whose path could not be resolved
*
* @skb: frame to discard
* @dev: network device the frame was to be sent through
*
* If the frame was beign forwarded from another MP, a PERR frame will be sent
* to the precursor.
*
* Locking: the function must me called within a rcu_read_lock region
*/
void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath;
u32 dsn = 0;
if (skb->pkt_type == PACKET_OTHERHOST) {
struct ieee80211s_hdr *prev_meshhdr;
int mshhdrlen;
u8 *ra, *da;
prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb);
mshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr);
da = skb->data;
ra = MESH_PREQ(skb);
mpath = mesh_path_lookup(da, dev);
if (mpath)
dsn = ++mpath->dsn;
mesh_path_error_tx(skb->data, cpu_to_le32(dsn), ra, dev);
}
kfree_skb(skb);
sdata->u.sta.mshstats.dropped_frames_no_route++;
}
/**
* mesh_path_flush_pending - free the pending queue of a mesh path
*
* @mpath: mesh path whose queue has to be freed
*
* Locking: the function must me called withing a rcu_read_lock region
*/
void mesh_path_flush_pending(struct mesh_path *mpath)
{
struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb;
sdata = IEEE80211_DEV_TO_SUB_IF(mpath->dev);
while ((skb = skb_dequeue(&mpath->frame_queue)) &&
(mpath->flags & MESH_PATH_ACTIVE))
mesh_path_discard_frame(skb, mpath->dev);
}
/**
* mesh_path_fix_nexthop - force a specific next hop for a mesh path
*
* @mpath: the mesh path to modify
* @next_hop: the next hop to force
*
* Locking: this function must be called holding mpath->state_lock
*/
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop)
{
spin_lock_bh(&mpath->state_lock);
mesh_path_assign_nexthop(mpath, next_hop);
mpath->dsn = 0xffff;
mpath->metric = 0;
mpath->hop_count = 0;
mpath->exp_time = 0;
mpath->flags |= MESH_PATH_FIXED;
mesh_path_activate(mpath);
spin_unlock_bh(&mpath->state_lock);
mesh_path_tx_pending(mpath);
}
static void mesh_path_node_free(struct hlist_node *p, bool free_leafs)
{
struct mesh_path *mpath;
struct mpath_node *node = hlist_entry(p, struct mpath_node, list);
mpath = node->mpath;
hlist_del_rcu(p);
synchronize_rcu();
if (free_leafs)
kfree(mpath);
kfree(node);
}
static void mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
{
struct mesh_path *mpath;
struct mpath_node *node, *new_node;
u32 hash_idx;
node = hlist_entry(p, struct mpath_node, list);
mpath = node->mpath;
new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
new_node->mpath = mpath;
hash_idx = mesh_table_hash(mpath->dst, mpath->dev, newtbl);
hlist_add_head(&new_node->list,
&newtbl->hash_buckets[hash_idx]);
}
int mesh_pathtbl_init(void)
{
mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
mesh_paths->free_node = &mesh_path_node_free;
mesh_paths->copy_node = &mesh_path_node_copy;
mesh_paths->mean_chain_len = MEAN_CHAIN_LEN;
if (!mesh_paths)
return -ENOMEM;
return 0;
}
void mesh_path_expire(struct net_device *dev)
{
struct mesh_path *mpath;
struct mpath_node *node;
struct hlist_node *p;
int i;
read_lock(&pathtbl_resize_lock);
for_each_mesh_entry(mesh_paths, p, node, i) {
if (node->mpath->dev != dev)
continue;
mpath = node->mpath;
spin_lock_bh(&mpath->state_lock);
if ((!(mpath->flags & MESH_PATH_RESOLVING)) &&
(!(mpath->flags & MESH_PATH_FIXED)) &&
time_after(jiffies,
mpath->exp_time + MESH_PATH_EXPIRE)) {
spin_unlock_bh(&mpath->state_lock);
mesh_path_del(mpath->dst, mpath->dev);
} else
spin_unlock_bh(&mpath->state_lock);
}
read_unlock(&pathtbl_resize_lock);
}
void mesh_pathtbl_unregister(void)
{
mesh_table_free(mesh_paths, true);
}

761
net/mac80211/mesh_plink.c Normal file
View file

@ -0,0 +1,761 @@
/*
* Copyright (c) 2008 open80211s Ltd.
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/random.h>
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
#include "mesh.h"
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
#define mpl_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args)
#else
#define mpl_dbg(fmt, args...) do { (void)(0); } while (0)
#endif
#define IEEE80211_FC(type, stype) cpu_to_le16(type | stype)
#define PLINK_GET_FRAME_SUBTYPE(p) (p)
#define PLINK_GET_LLID(p) (p + 1)
#define PLINK_GET_PLID(p) (p + 3)
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000))
/* Peer link cancel reasons, all subject to ANA approval */
#define MESH_LINK_CANCELLED 2
#define MESH_MAX_NEIGHBORS 3
#define MESH_CAPABILITY_POLICY_VIOLATION 4
#define MESH_CLOSE_RCVD 5
#define MESH_MAX_RETRIES 6
#define MESH_CONFIRM_TIMEOUT 7
#define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS 8
#define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE 9
#define MESH_SECURITY_FAILED_VERIFICATION 10
#define dot11MeshMaxRetries(s) (s->u.sta.mshcfg.dot11MeshMaxRetries)
#define dot11MeshRetryTimeout(s) (s->u.sta.mshcfg.dot11MeshRetryTimeout)
#define dot11MeshConfirmTimeout(s) (s->u.sta.mshcfg.dot11MeshConfirmTimeout)
#define dot11MeshHoldingTimeout(s) (s->u.sta.mshcfg.dot11MeshHoldingTimeout)
#define dot11MeshMaxPeerLinks(s) (s->u.sta.mshcfg.dot11MeshMaxPeerLinks)
enum plink_frame_type {
PLINK_OPEN = 0,
PLINK_CONFIRM,
PLINK_CLOSE
};
enum plink_event {
PLINK_UNDEFINED,
OPN_ACPT,
OPN_RJCT,
OPN_IGNR,
CNF_ACPT,
CNF_RJCT,
CNF_IGNR,
CLS_ACPT,
CLS_IGNR
};
static inline
void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
{
atomic_inc(&sdata->u.sta.mshstats.estab_plinks);
mesh_accept_plinks_update(sdata);
}
static inline
void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
{
atomic_dec(&sdata->u.sta.mshstats.estab_plinks);
mesh_accept_plinks_update(sdata);
}
/**
* mesh_plink_fsm_restart - restart a mesh peer link finite state machine
*
* @sta: mes peer link to restart
*
* Locking: this function must be called holding sta->plink_lock
*/
static inline void mesh_plink_fsm_restart(struct sta_info *sta)
{
sta->plink_state = PLINK_LISTEN;
sta->llid = sta->plid = sta->reason = 0;
sta->plink_retries = 0;
}
static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
u8 *hw_addr, u64 rates)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
if (local->num_sta >= MESH_MAX_PLINKS)
return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_ATOMIC);
if (!sta)
return NULL;
sta->flags |= WLAN_STA_AUTHORIZED;
sta->supp_rates[local->hw.conf.channel->band] = rates;
return sta;
}
/**
* mesh_plink_deactivate - deactivate mesh peer link
*
* @sta: mesh peer link to deactivate
*
* All mesh paths with this peer as next hop will be flushed
*
* Locking: the caller must hold sta->plink_lock
*/
static void __mesh_plink_deactivate(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
if (sta->plink_state == PLINK_ESTAB)
mesh_plink_dec_estab_count(sdata);
sta->plink_state = PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);
}
/**
* __mesh_plink_deactivate - deactivate mesh peer link
*
* @sta: mesh peer link to deactivate
*
* All mesh paths with this peer as next hop will be flushed
*/
void mesh_plink_deactivate(struct sta_info *sta)
{
spin_lock_bh(&sta->plink_lock);
__mesh_plink_deactivate(sta);
spin_unlock_bh(&sta->plink_lock);
}
static int mesh_plink_frame_tx(struct net_device *dev,
enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid,
__le16 reason) {
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
struct ieee80211_mgmt *mgmt;
bool include_plid = false;
u8 *pos;
int ie_len;
if (!skb)
return -1;
skb_reserve(skb, local->hw.extra_tx_headroom);
/* 25 is the size of the common mgmt part (24) plus the size of the
* common action part (1)
*/
mgmt = (struct ieee80211_mgmt *)
skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action));
memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action));
mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = PLINK_CATEGORY;
mgmt->u.action.u.plink_action.action_code = action;
if (action == PLINK_CLOSE)
mgmt->u.action.u.plink_action.aux = reason;
else {
mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0);
if (action == PLINK_CONFIRM) {
pos = skb_put(skb, 4);
/* two-byte status code followed by two-byte AID */
memset(pos, 0, 4);
}
mesh_mgmt_ies_add(skb, dev);
}
/* Add Peer Link Management element */
switch (action) {
case PLINK_OPEN:
ie_len = 3;
break;
case PLINK_CONFIRM:
ie_len = 5;
include_plid = true;
break;
case PLINK_CLOSE:
default:
if (!plid)
ie_len = 5;
else {
ie_len = 7;
include_plid = true;
}
break;
}
pos = skb_put(skb, 2 + ie_len);
*pos++ = WLAN_EID_PEER_LINK;
*pos++ = ie_len;
*pos++ = action;
memcpy(pos, &llid, 2);
if (include_plid) {
pos += 2;
memcpy(pos, &plid, 2);
}
if (action == PLINK_CLOSE) {
pos += 2;
memcpy(pos, &reason, 2);
}
ieee80211_sta_tx(dev, skb, 0);
return 0;
}
void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
bool peer_accepting_plinks)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
rcu_read_lock();
sta = sta_info_get(local, hw_addr);
if (!sta) {
sta = mesh_plink_alloc(sdata, hw_addr, rates);
if (!sta) {
rcu_read_unlock();
return;
}
if (sta_info_insert(sta)) {
sta_info_destroy(sta);
rcu_read_unlock();
return;
}
}
sta->last_rx = jiffies;
sta->supp_rates[local->hw.conf.channel->band] = rates;
if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN &&
sdata->u.sta.accepting_plinks &&
sdata->u.sta.mshcfg.auto_open_plinks)
mesh_plink_open(sta);
rcu_read_unlock();
}
static void mesh_plink_timer(unsigned long data)
{
struct sta_info *sta;
__le16 llid, plid, reason;
struct net_device *dev = NULL;
struct ieee80211_sub_if_data *sdata;
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF(mac);
#endif
/*
* This STA is valid because sta_info_destroy() will
* del_timer_sync() this timer after having made sure
* it cannot be readded (by deleting the plink.)
*/
sta = (struct sta_info *) data;
spin_lock_bh(&sta->plink_lock);
if (sta->ignore_plink_timer) {
sta->ignore_plink_timer = false;
spin_unlock_bh(&sta->plink_lock);
return;
}
mpl_dbg("Mesh plink timer for %s fired on state %d\n",
print_mac(mac, sta->addr), sta->plink_state);
reason = 0;
llid = sta->llid;
plid = sta->plid;
sdata = sta->sdata;
dev = sdata->dev;
switch (sta->plink_state) {
case PLINK_OPN_RCVD:
case PLINK_OPN_SNT:
/* retry timer */
if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
u32 rand;
mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n",
print_mac(mac, sta->addr),
sta->plink_retries, sta->plink_timeout);
get_random_bytes(&rand, sizeof(u32));
sta->plink_timeout = sta->plink_timeout +
rand % sta->plink_timeout;
++sta->plink_retries;
mod_plink_timer(sta, sta->plink_timeout);
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid,
0, 0);
break;
}
reason = cpu_to_le16(MESH_MAX_RETRIES);
/* fall through on else */
case PLINK_CNF_RCVD:
/* confirm timer */
if (!reason)
reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT);
sta->plink_state = PLINK_HOLDING;
mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid,
reason);
break;
case PLINK_HOLDING:
/* holding timer */
del_timer(&sta->plink_timer);
mesh_plink_fsm_restart(sta);
spin_unlock_bh(&sta->plink_lock);
break;
default:
spin_unlock_bh(&sta->plink_lock);
break;
}
}
static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
{
sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
sta->plink_timer.data = (unsigned long) sta;
sta->plink_timer.function = mesh_plink_timer;
sta->plink_timeout = timeout;
add_timer(&sta->plink_timer);
}
int mesh_plink_open(struct sta_info *sta)
{
__le16 llid;
struct ieee80211_sub_if_data *sdata = sta->sdata;
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF(mac);
#endif
spin_lock_bh(&sta->plink_lock);
get_random_bytes(&llid, 2);
sta->llid = llid;
if (sta->plink_state != PLINK_LISTEN) {
spin_unlock_bh(&sta->plink_lock);
return -EBUSY;
}
sta->plink_state = PLINK_OPN_SNT;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
spin_unlock_bh(&sta->plink_lock);
mpl_dbg("Mesh plink: starting establishment with %s\n",
print_mac(mac, sta->addr));
return mesh_plink_frame_tx(sdata->dev, PLINK_OPEN,
sta->addr, llid, 0, 0);
}
void mesh_plink_block(struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF(mac);
#endif
spin_lock_bh(&sta->plink_lock);
__mesh_plink_deactivate(sta);
sta->plink_state = PLINK_BLOCKED;
spin_unlock_bh(&sta->plink_lock);
}
int mesh_plink_close(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
__le16 llid, plid, reason;
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF(mac);
#endif
mpl_dbg("Mesh plink: closing link with %s\n",
print_mac(mac, sta->addr));
spin_lock_bh(&sta->plink_lock);
sta->reason = cpu_to_le16(MESH_LINK_CANCELLED);
reason = sta->reason;
if (sta->plink_state == PLINK_LISTEN ||
sta->plink_state == PLINK_BLOCKED) {
mesh_plink_fsm_restart(sta);
spin_unlock_bh(&sta->plink_lock);
return 0;
} else if (sta->plink_state == PLINK_ESTAB) {
__mesh_plink_deactivate(sta);
/* The timer should not be running */
mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
} else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)))
sta->ignore_plink_timer = true;
sta->plink_state = PLINK_HOLDING;
llid = sta->llid;
plid = sta->plid;
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(sta->sdata->dev, PLINK_CLOSE, sta->addr, llid,
plid, reason);
return 0;
}
void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
size_t len, struct ieee80211_rx_status *rx_status)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee802_11_elems elems;
struct sta_info *sta;
enum plink_event event;
enum plink_frame_type ftype;
size_t baselen;
u8 ie_len;
u8 *baseaddr;
__le16 plid, llid, reason;
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF(mac);
#endif
if (is_multicast_ether_addr(mgmt->da)) {
mpl_dbg("Mesh plink: ignore frame from multicast address");
return;
}
baseaddr = mgmt->u.action.u.plink_action.variable;
baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt;
if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) {
baseaddr += 4;
baselen -= 4;
}
ieee802_11_parse_elems(baseaddr, len - baselen, &elems);
if (!elems.peer_link) {
mpl_dbg("Mesh plink: missing necessary peer link ie\n");
return;
}
ftype = *((u8 *)PLINK_GET_FRAME_SUBTYPE(elems.peer_link));
ie_len = elems.peer_link_len;
if ((ftype == PLINK_OPEN && ie_len != 3) ||
(ftype == PLINK_CONFIRM && ie_len != 5) ||
(ftype == PLINK_CLOSE && ie_len != 5 && ie_len != 7)) {
mpl_dbg("Mesh plink: incorrect plink ie length\n");
return;
}
if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) {
mpl_dbg("Mesh plink: missing necessary ie\n");
return;
}
/* Note the lines below are correct, the llid in the frame is the plid
* from the point of view of this host.
*/
memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2);
if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7))
memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2);
rcu_read_lock();
sta = sta_info_get(local, mgmt->sa);
if (!sta && ftype != PLINK_OPEN) {
mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
rcu_read_unlock();
return;
}
if (sta && sta->plink_state == PLINK_BLOCKED) {
rcu_read_unlock();
return;
}
/* Now we will figure out the appropriate event... */
event = PLINK_UNDEFINED;
if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, dev))) {
switch (ftype) {
case PLINK_OPEN:
event = OPN_RJCT;
break;
case PLINK_CONFIRM:
event = CNF_RJCT;
break;
case PLINK_CLOSE:
/* avoid warning */
break;
}
spin_lock_bh(&sta->plink_lock);
} else if (!sta) {
/* ftype == PLINK_OPEN */
u64 rates;
if (!mesh_plink_free_count(sdata)) {
mpl_dbg("Mesh plink error: no more free plinks\n");
rcu_read_unlock();
return;
}
rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
if (!sta) {
mpl_dbg("Mesh plink error: plink table full\n");
rcu_read_unlock();
return;
}
if (sta_info_insert(sta)) {
sta_info_destroy(sta);
rcu_read_unlock();
return;
}
event = OPN_ACPT;
spin_lock_bh(&sta->plink_lock);
} else {
spin_lock_bh(&sta->plink_lock);
switch (ftype) {
case PLINK_OPEN:
if (!mesh_plink_free_count(sdata) ||
(sta->plid && sta->plid != plid))
event = OPN_IGNR;
else
event = OPN_ACPT;
break;
case PLINK_CONFIRM:
if (!mesh_plink_free_count(sdata) ||
(sta->llid != llid || sta->plid != plid))
event = CNF_IGNR;
else
event = CNF_ACPT;
break;
case PLINK_CLOSE:
if (sta->plink_state == PLINK_ESTAB)
/* Do not check for llid or plid. This does not
* follow the standard but since multiple plinks
* per sta are not supported, it is necessary in
* order to avoid a livelock when MP A sees an
* establish peer link to MP B but MP B does not
* see it. This can be caused by a timeout in
* B's peer link establishment or B beign
* restarted.
*/
event = CLS_ACPT;
else if (sta->plid != plid)
event = CLS_IGNR;
else if (ie_len == 7 && sta->llid != llid)
event = CLS_IGNR;
else
event = CLS_ACPT;
break;
default:
mpl_dbg("Mesh plink: unknown frame subtype\n");
spin_unlock_bh(&sta->plink_lock);
rcu_read_unlock();
return;
}
}
mpl_dbg("Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d\n",
print_mac(mac, mgmt->sa), sta->plink_state,
le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
event);
reason = 0;
switch (sta->plink_state) {
/* spin_unlock as soon as state is updated at each case */
case PLINK_LISTEN:
switch (event) {
case CLS_ACPT:
mesh_plink_fsm_restart(sta);
spin_unlock_bh(&sta->plink_lock);
break;
case OPN_ACPT:
sta->plink_state = PLINK_OPN_RCVD;
sta->plid = plid;
get_random_bytes(&llid, 2);
sta->llid = llid;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid,
0, 0);
mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr,
llid, plid, 0);
break;
default:
spin_unlock_bh(&sta->plink_lock);
break;
}
break;
case PLINK_OPN_SNT:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
case CLS_ACPT:
if (!reason)
reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason;
sta->plink_state = PLINK_HOLDING;
if (!mod_plink_timer(sta,
dot11MeshHoldingTimeout(sdata)))
sta->ignore_plink_timer = true;
llid = sta->llid;
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
plid, reason);
break;
case OPN_ACPT:
/* retry timer is left untouched */
sta->plink_state = PLINK_OPN_RCVD;
sta->plid = plid;
llid = sta->llid;
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
plid, 0);
break;
case CNF_ACPT:
sta->plink_state = PLINK_CNF_RCVD;
if (!mod_plink_timer(sta,
dot11MeshConfirmTimeout(sdata)))
sta->ignore_plink_timer = true;
spin_unlock_bh(&sta->plink_lock);
break;
default:
spin_unlock_bh(&sta->plink_lock);
break;
}
break;
case PLINK_OPN_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
case CLS_ACPT:
if (!reason)
reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason;
sta->plink_state = PLINK_HOLDING;
if (!mod_plink_timer(sta,
dot11MeshHoldingTimeout(sdata)))
sta->ignore_plink_timer = true;
llid = sta->llid;
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
plid, reason);
break;
case OPN_ACPT:
llid = sta->llid;
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
plid, 0);
break;
case CNF_ACPT:
del_timer(&sta->plink_timer);
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->plink_lock);
mpl_dbg("Mesh plink with %s ESTABLISHED\n",
print_mac(mac, sta->addr));
break;
default:
spin_unlock_bh(&sta->plink_lock);
break;
}
break;
case PLINK_CNF_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
case CLS_ACPT:
if (!reason)
reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason;
sta->plink_state = PLINK_HOLDING;
if (!mod_plink_timer(sta,
dot11MeshHoldingTimeout(sdata)))
sta->ignore_plink_timer = true;
llid = sta->llid;
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
plid, reason);
break;
case OPN_ACPT:
del_timer(&sta->plink_timer);
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->plink_lock);
mpl_dbg("Mesh plink with %s ESTABLISHED\n",
print_mac(mac, sta->addr));
mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
plid, 0);
break;
default:
spin_unlock_bh(&sta->plink_lock);
break;
}
break;
case PLINK_ESTAB:
switch (event) {
case CLS_ACPT:
reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason;
__mesh_plink_deactivate(sta);
sta->plink_state = PLINK_HOLDING;
llid = sta->llid;
mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
plid, reason);
break;
case OPN_ACPT:
llid = sta->llid;
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
plid, 0);
break;
default:
spin_unlock_bh(&sta->plink_lock);
break;
}
break;
case PLINK_HOLDING:
switch (event) {
case CLS_ACPT:
if (del_timer(&sta->plink_timer))
sta->ignore_plink_timer = 1;
mesh_plink_fsm_restart(sta);
spin_unlock_bh(&sta->plink_lock);
break;
case OPN_ACPT:
case CNF_ACPT:
case OPN_RJCT:
case CNF_RJCT:
llid = sta->llid;
reason = sta->reason;
spin_unlock_bh(&sta->plink_lock);
mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
plid, reason);
break;
default:
spin_unlock_bh(&sta->plink_lock);
}
break;
default:
/* should not get here, PLINK_BLOCKED is dealt with at the
* beggining of the function
*/
spin_unlock_bh(&sta->plink_lock);
break;
}
rcu_read_unlock();
}

View file

@ -15,7 +15,7 @@
#include <linux/debugfs.h>
#include <net/mac80211.h>
#include "ieee80211_rate.h"
#include "mesh.h"
#include "rc80211_pid.h"
@ -77,7 +77,7 @@ static void rate_control_pid_adjust_rate(struct ieee80211_local *local,
int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
int cur = sta->txrate_idx;
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
band = sband->band;
n_bitrates = sband->n_bitrates;
@ -148,6 +148,9 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
struct ieee80211_local *local,
struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_MESH
struct ieee80211_sub_if_data *sdata = sta->sdata;
#endif
struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv;
struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
struct ieee80211_supported_band *sband;
@ -178,7 +181,14 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
pf = spinfo->last_pf;
else {
pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
#ifdef CONFIG_MAC80211_MESH
if (pf == 100 &&
sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
mesh_plink_broken(sta);
#endif
pf <<= RC_PID_ARITH_SHIFT;
sta->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9)
>> RC_PID_ARITH_SHIFT;
}
spinfo->tx_num_xmit = 0;
@ -239,23 +249,25 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
unsigned long period;
struct ieee80211_supported_band *sband;
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
if (!sta)
return;
goto unlock;
/* Don't update the state if we're not controlling the rate. */
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
sta->txrate_idx = sdata->bss->max_ratectrl_rateidx;
return;
goto unlock;
}
/* Ignore all frames that were sent with a different rate than the rate
* we currently advise mac80211 to use. */
if (status->control.tx_rate != &sband->bitrates[sta->txrate_idx])
goto ignore;
goto unlock;
spinfo = sta->rate_ctrl_priv;
spinfo->tx_num_xmit++;
@ -293,8 +305,8 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
if (time_after(jiffies, spinfo->last_sample + period))
rate_control_pid_sample(pinfo, local, sta);
ignore:
sta_info_put(sta);
unlock:
rcu_read_unlock();
}
static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
@ -309,6 +321,8 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
int rateidx;
u16 fc;
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
/* Send management frames and broadcast/multicast data using lowest
@ -317,8 +331,7 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
is_multicast_ether_addr(hdr->addr1) || !sta) {
sel->rate = rate_lowest(local, sband, sta);
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return;
}
@ -334,7 +347,7 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
sta->last_txrate_idx = rateidx;
sta_info_put(sta);
rcu_read_unlock();
sel->rate = &sband->bitrates[rateidx];
@ -357,6 +370,7 @@ static void rate_control_pid_rate_init(void *priv, void *priv_sta,
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
sta->txrate_idx = rate_lowest_index(local, sband, sta);
sta->fail_avg = 0;
}
static void *rate_control_pid_alloc(struct ieee80211_local *local)

View file

@ -40,7 +40,7 @@ static void rate_control_rate_inc(struct ieee80211_local *local,
int i = sta->txrate_idx;
int maxrate;
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
/* forced unicast rate - do not change STA rate */
return;
@ -70,7 +70,7 @@ static void rate_control_rate_dec(struct ieee80211_local *local,
struct ieee80211_supported_band *sband;
int i = sta->txrate_idx;
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
/* forced unicast rate - do not change STA rate */
return;
@ -118,10 +118,12 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev,
struct sta_info *sta;
struct sta_rate_control *srctrl;
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (!sta)
return;
goto unlock;
srctrl = sta->rate_ctrl_priv;
srctrl->tx_num_xmit++;
@ -191,7 +193,8 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev,
}
}
sta_info_put(sta);
unlock:
rcu_read_unlock();
}
@ -208,6 +211,8 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev,
int rateidx;
u16 fc;
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
/* Send management frames and broadcast/multicast data using lowest
@ -216,8 +221,7 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev,
if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
is_multicast_ether_addr(hdr->addr1) || !sta) {
sel->rate = rate_lowest(local, sband, sta);
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return;
}
@ -233,7 +237,7 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev,
sta->last_txrate_idx = rateidx;
sta_info_put(sta);
rcu_read_unlock();
sel->rate = &sband->bitrates[rateidx];
}

View file

@ -20,6 +20,7 @@
#include "ieee80211_i.h"
#include "ieee80211_led.h"
#include "mesh.h"
#include "wep.h"
#include "wpa.h"
#include "tkip.h"
@ -250,7 +251,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
}
static void ieee80211_parse_qos(struct ieee80211_txrx_data *rx)
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
u8 *data = rx->skb->data;
int tid;
@ -261,9 +262,9 @@ static void ieee80211_parse_qos(struct ieee80211_txrx_data *rx)
/* frame has qos control */
tid = qc[0] & QOS_CONTROL_TID_MASK;
if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
rx->flags |= IEEE80211_TXRXD_RX_AMSDU;
rx->flags |= IEEE80211_RX_AMSDU;
else
rx->flags &= ~IEEE80211_TXRXD_RX_AMSDU;
rx->flags &= ~IEEE80211_RX_AMSDU;
} else {
if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
/* Separate TID for management frames */
@ -279,13 +280,13 @@ static void ieee80211_parse_qos(struct ieee80211_txrx_data *rx)
if (rx->sta)
I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
rx->u.rx.queue = tid;
rx->queue = tid;
/* Set skb->priority to 1d tag if highest order bit of TID is not set.
* For now, set skb->priority to 0 for other cases. */
rx->skb->priority = (tid > 7) ? 0 : tid;
}
static void ieee80211_verify_ip_alignment(struct ieee80211_txrx_data *rx)
static void ieee80211_verify_ip_alignment(struct ieee80211_rx_data *rx)
{
#ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
int hdrlen;
@ -313,7 +314,7 @@ static void ieee80211_verify_ip_alignment(struct ieee80211_txrx_data *rx)
* to move the 802.11 header further back in that case.
*/
hdrlen = ieee80211_get_hdrlen(rx->fc);
if (rx->flags & IEEE80211_TXRXD_RX_AMSDU)
if (rx->flags & IEEE80211_RX_AMSDU)
hdrlen += ETH_HLEN;
WARN_ON_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3);
#endif
@ -356,32 +357,32 @@ static u32 ieee80211_rx_load_stats(struct ieee80211_local *local,
/* rx handlers */
static ieee80211_rx_result
ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_if_stats(struct ieee80211_rx_data *rx)
{
if (rx->sta)
rx->sta->channel_use_raw += rx->u.rx.load;
rx->sdata->channel_use_raw += rx->u.rx.load;
rx->sta->channel_use_raw += rx->load;
rx->sdata->channel_use_raw += rx->load;
return RX_CONTINUE;
}
static ieee80211_rx_result
ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
struct sk_buff *skb = rx->skb;
if (unlikely(local->sta_hw_scanning))
return ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
return ieee80211_sta_rx_scan(rx->dev, skb, rx->status);
if (unlikely(local->sta_sw_scanning)) {
/* drop all the other packets during a software scan anyway */
if (ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status)
if (ieee80211_sta_rx_scan(rx->dev, skb, rx->status)
!= RX_QUEUED)
dev_kfree_skb(skb);
return RX_QUEUED;
}
if (unlikely(rx->flags & IEEE80211_TXRXD_RXIN_SCAN)) {
if (unlikely(rx->flags & IEEE80211_RX_IN_SCAN)) {
/* scanning finished during invoking of handlers */
I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
return RX_DROP_UNUSABLE;
@ -391,23 +392,75 @@ ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
}
static ieee80211_rx_result
ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
int hdrlen = ieee80211_get_hdrlen(rx->fc);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
#define msh_h_get(h, l) ((struct ieee80211s_hdr *) ((u8 *)h + l))
if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
if (!((rx->fc & IEEE80211_FCTL_FROMDS) &&
(rx->fc & IEEE80211_FCTL_TODS)))
return RX_DROP_MONITOR;
if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
return RX_DROP_MONITOR;
}
/* If there is not an established peer link and this is not a peer link
* establisment frame, beacon or probe, drop the frame.
*/
if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) {
struct ieee80211_mgmt *mgmt;
if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
return RX_DROP_MONITOR;
switch (rx->fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_ACTION:
mgmt = (struct ieee80211_mgmt *)hdr;
if (mgmt->u.action.category != PLINK_CATEGORY)
return RX_DROP_MONITOR;
/* fall through on else */
case IEEE80211_STYPE_PROBE_REQ:
case IEEE80211_STYPE_PROBE_RESP:
case IEEE80211_STYPE_BEACON:
return RX_CONTINUE;
break;
default:
return RX_DROP_MONITOR;
}
} else if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
is_broadcast_ether_addr(hdr->addr1) &&
mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->dev))
return RX_DROP_MONITOR;
#undef msh_h_get
return RX_CONTINUE;
}
static ieee80211_rx_result
ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *) rx->skb->data;
/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
rx->sta->last_seq_ctrl[rx->queue] ==
hdr->seq_ctrl)) {
if (rx->flags & IEEE80211_TXRXD_RXRA_MATCH) {
if (rx->flags & IEEE80211_RX_RA_MATCH) {
rx->local->dot11FrameDuplicateCount++;
rx->sta->num_duplicates++;
}
return RX_DROP_MONITOR;
} else
rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
rx->sta->last_seq_ctrl[rx->queue] = hdr->seq_ctrl;
}
if (unlikely(rx->skb->len < 16)) {
@ -423,6 +476,10 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
* deauth/disassoc frames when needed. In addition, hostapd is
* responsible for filtering on both auth and assoc states.
*/
if (ieee80211_vif_is_mesh(&rx->sdata->vif))
return ieee80211_rx_mesh_check(rx);
if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
(rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
@ -431,7 +488,7 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
!(rx->fc & IEEE80211_FCTL_TODS) &&
(rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
|| !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
|| !(rx->flags & IEEE80211_RX_RA_MATCH)) {
/* Drop IBSS frames and frames for other hosts
* silently. */
return RX_DROP_MONITOR;
@ -445,7 +502,7 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
static ieee80211_rx_result
ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
int keyidx;
@ -486,7 +543,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
* No point in finding a key and decrypting if the frame is neither
* addressed to us nor a multicast frame.
*/
if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
if (rx->sta)
@ -504,8 +561,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
* we somehow allow the driver to tell us which key
* the hardware used if this flag is set?
*/
if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED))
if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
(rx->status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
hdrlen = ieee80211_get_hdrlen(rx->fc);
@ -546,8 +603,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
/* Check for weak IVs if possible */
if (rx->sta && rx->key->conf.alg == ALG_WEP &&
((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
(!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED) ||
!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) &&
(!(rx->status->flag & RX_FLAG_IV_STRIPPED) ||
!(rx->status->flag & RX_FLAG_DECRYPTED)) &&
ieee80211_wep_is_weak_iv(rx->skb, rx->key))
rx->sta->wep_weak_iv_count++;
@ -564,7 +621,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
}
/* either the frame has been decrypted or will be dropped */
rx->u.rx.status->flag |= RX_FLAG_DECRYPTED;
rx->status->flag |= RX_FLAG_DECRYPTED;
return result;
}
@ -574,7 +631,7 @@ static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
struct ieee80211_sub_if_data *sdata;
DECLARE_MAC_BUF(mac);
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
if (sdata->bss)
atomic_inc(&sdata->bss->num_sta_ps);
@ -595,7 +652,7 @@ static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
struct ieee80211_tx_packet_data *pkt_data;
DECLARE_MAC_BUF(mac);
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
if (sdata->bss)
atomic_dec(&sdata->bss->num_sta_ps);
@ -634,7 +691,7 @@ static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
}
static ieee80211_rx_result
ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
{
struct sta_info *sta = rx->sta;
struct net_device *dev = rx->dev;
@ -657,24 +714,26 @@ ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
/* Update last_rx only for unicast frames in order to prevent
* the Probe Request frames (the only broadcast frames from a
* STA in infrastructure mode) from keeping a connection alive.
* Mesh beacons will update last_rx when if they are found to
* match the current local configuration when processed.
*/
sta->last_rx = jiffies;
}
if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
sta->rx_fragments++;
sta->rx_bytes += rx->skb->len;
sta->last_rssi = rx->u.rx.status->ssi;
sta->last_signal = rx->u.rx.status->signal;
sta->last_noise = rx->u.rx.status->noise;
sta->last_rssi = rx->status->ssi;
sta->last_signal = rx->status->signal;
sta->last_noise = rx->status->noise;
if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
/* Change STA power saving mode only in the end of a frame
* exchange sequence */
if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
rx->sent_ps_buffered += ap_sta_ps_end(dev, sta);
else if (!(sta->flags & WLAN_STA_PS) &&
(rx->fc & IEEE80211_FCTL_PM))
ap_sta_ps_start(dev, sta);
@ -779,7 +838,7 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
}
static ieee80211_rx_result
ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr;
u16 sc;
@ -805,14 +864,14 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
if (frag == 0) {
/* This is the first fragment of a new frame. */
entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
rx->u.rx.queue, &(rx->skb));
rx->queue, &(rx->skb));
if (rx->key && rx->key->conf.alg == ALG_CCMP &&
(rx->fc & IEEE80211_FCTL_PROTECTED)) {
/* Store CCMP PN so that we can verify that the next
* fragment has a sequential PN value. */
entry->ccmp = 1;
memcpy(entry->last_pn,
rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
rx->key->u.ccmp.rx_pn[rx->queue],
CCMP_PN_LEN);
}
return RX_QUEUED;
@ -822,7 +881,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
* fragment cache. Add this fragment to the end of the pending entry.
*/
entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
rx->u.rx.queue, hdr);
rx->queue, hdr);
if (!entry) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
return RX_DROP_MONITOR;
@ -841,7 +900,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
if (pn[i])
break;
}
rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
rpn = rx->key->u.ccmp.rx_pn[rx->queue];
if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: defrag: CCMP PN not "
@ -882,7 +941,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
}
/* Complete frame has been reassembled - process it now */
rx->flags |= IEEE80211_TXRXD_FRAGMENTED;
rx->flags |= IEEE80211_RX_FRAGMENTED;
out:
if (rx->sta)
@ -895,7 +954,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
}
static ieee80211_rx_result
ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct sk_buff *skb;
@ -905,7 +964,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
if (likely(!rx->sta ||
(rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)))
!(rx->flags & IEEE80211_RX_RA_MATCH)))
return RX_CONTINUE;
if ((sdata->vif.type != IEEE80211_IF_TYPE_AP) &&
@ -949,7 +1008,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
if (no_pending_pkts)
sta_info_clear_tim_bit(rx->sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
} else if (!rx->u.rx.sent_ps_buffered) {
} else if (!rx->sent_ps_buffered) {
/*
* FIXME: This can be the result of a race condition between
* us expiring a frame and the station polling for it.
@ -970,7 +1029,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
}
static ieee80211_rx_result
ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
{
u16 fc = rx->fc;
u8 *data = rx->skb->data;
@ -990,7 +1049,7 @@ ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
}
static int
ieee80211_802_1x_port_control(struct ieee80211_txrx_data *rx)
ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
{
if (unlikely(!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED))) {
#ifdef CONFIG_MAC80211_DEBUG
@ -1005,13 +1064,13 @@ ieee80211_802_1x_port_control(struct ieee80211_txrx_data *rx)
}
static int
ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx)
ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx)
{
/*
* Pass through unencrypted frames if the hardware has
* decrypted them already.
*/
if (rx->u.rx.status->flag & RX_FLAG_DECRYPTED)
if (rx->status->flag & RX_FLAG_DECRYPTED)
return 0;
/* Drop unencrypted frames if key is set. */
@ -1028,7 +1087,7 @@ ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx)
}
static int
ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
@ -1050,6 +1109,21 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
hdrlen = ieee80211_get_hdrlen(fc);
if (ieee80211_vif_is_mesh(&sdata->vif)) {
int meshhdrlen = ieee80211_get_mesh_hdrlen(
(struct ieee80211s_hdr *) (skb->data + hdrlen));
/* Copy on cb:
* - mesh header: to be used for mesh forwarding
* decision. It will also be used as mesh header template at
* tx.c:ieee80211_subif_start_xmit() if interface
* type is mesh and skb->pkt_type == PACKET_OTHERHOST
* - ta: to be used if a RERR needs to be sent.
*/
memcpy(skb->cb, skb->data + hdrlen, meshhdrlen);
memcpy(MESH_PREQ(skb), hdr->addr2, ETH_ALEN);
hdrlen += meshhdrlen;
}
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
* header
* IEEE 802.11 address fields:
@ -1083,9 +1157,10 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
memcpy(dst, hdr->addr3, ETH_ALEN);
memcpy(src, hdr->addr4, ETH_ALEN);
if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS)) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS &&
sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
"frame (RA=%s TA=%s DA=%s SA=%s)\n",
rx->dev->name,
print_mac(mac, hdr->addr1),
@ -1160,7 +1235,7 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
/*
* requires that rx->skb is a frame with ethernet header
*/
static bool ieee80211_frame_allowed(struct ieee80211_txrx_data *rx)
static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx)
{
static const u8 pae_group_addr[ETH_ALEN]
= { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
@ -1186,7 +1261,7 @@ static bool ieee80211_frame_allowed(struct ieee80211_txrx_data *rx)
* requires that rx->skb is a frame with ethernet header
*/
static void
ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
struct ieee80211_local *local = rx->local;
@ -1200,7 +1275,7 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
if (local->bridge_packets && (sdata->vif.type == IEEE80211_IF_TYPE_AP ||
sdata->vif.type == IEEE80211_IF_TYPE_VLAN) &&
(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
(rx->flags & IEEE80211_RX_RA_MATCH)) {
if (is_multicast_ether_addr(ehdr->h_dest)) {
/*
* send multicast frames both to higher layers in
@ -1212,7 +1287,7 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
"multicast frame\n", dev->name);
} else {
dsta = sta_info_get(local, skb->data);
if (dsta && dsta->dev == dev) {
if (dsta && dsta->sdata->dev == dev) {
/*
* The destination station is associated to
* this AP (in this VLAN), so send the frame
@ -1222,8 +1297,38 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
xmit_skb = skb;
skb = NULL;
}
if (dsta)
sta_info_put(dsta);
}
}
/* Mesh forwarding */
if (ieee80211_vif_is_mesh(&sdata->vif)) {
u8 *mesh_ttl = &((struct ieee80211s_hdr *)skb->cb)->ttl;
(*mesh_ttl)--;
if (is_multicast_ether_addr(skb->data)) {
if (*mesh_ttl > 0) {
xmit_skb = skb_copy(skb, GFP_ATOMIC);
if (!xmit_skb && net_ratelimit())
printk(KERN_DEBUG "%s: failed to clone "
"multicast frame\n", dev->name);
else
xmit_skb->pkt_type = PACKET_OTHERHOST;
} else
IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
dropped_frames_ttl);
} else if (skb->pkt_type != PACKET_OTHERHOST &&
compare_ether_addr(dev->dev_addr, skb->data) != 0) {
if (*mesh_ttl == 0) {
IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
dropped_frames_ttl);
dev_kfree_skb(skb);
skb = NULL;
} else {
xmit_skb = skb;
xmit_skb->pkt_type = PACKET_OTHERHOST;
if (!(dev->flags & IFF_PROMISC))
skb = NULL;
}
}
}
@ -1244,7 +1349,7 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
}
static ieee80211_rx_result
ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
struct ieee80211_local *local = rx->local;
@ -1264,7 +1369,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
return RX_DROP_MONITOR;
if (!(rx->flags & IEEE80211_TXRXD_RX_AMSDU))
if (!(rx->flags & IEEE80211_RX_AMSDU))
return RX_CONTINUE;
err = ieee80211_data_to_8023(rx);
@ -1361,7 +1466,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
}
static ieee80211_rx_result
ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
u16 fc;
@ -1392,7 +1497,7 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
}
static ieee80211_rx_result
ieee80211_rx_h_ctrl(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
struct ieee80211_hw *hw = &local->hw;
@ -1435,18 +1540,19 @@ ieee80211_rx_h_ctrl(struct ieee80211_txrx_data *rx)
}
static ieee80211_rx_result
ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
{
struct ieee80211_sub_if_data *sdata;
if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_MONITOR;
sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
sdata->vif.type == IEEE80211_IF_TYPE_IBSS) &&
sdata->vif.type == IEEE80211_IF_TYPE_IBSS ||
sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) &&
!(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->status);
else
return RX_DROP_MONITOR;
@ -1455,7 +1561,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
static void ieee80211_rx_michael_mic_report(struct net_device *dev,
struct ieee80211_hdr *hdr,
struct ieee80211_txrx_data *rx)
struct ieee80211_rx_data *rx)
{
int keyidx, hdrlen;
DECLARE_MAC_BUF(mac);
@ -1525,7 +1631,8 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
rx->skb = NULL;
}
static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
/* TODO: use IEEE80211_RX_FRAGMENTED */
static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local = rx->local;
@ -1538,9 +1645,9 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
} __attribute__ ((packed)) *rthdr;
struct sk_buff *skb = rx->skb, *skb2;
struct net_device *prev_dev = NULL;
struct ieee80211_rx_status *status = rx->u.rx.status;
struct ieee80211_rx_status *status = rx->status;
if (rx->flags & IEEE80211_TXRXD_RX_CMNTR_REPORTED)
if (rx->flags & IEEE80211_RX_CMNTR_REPORTED)
goto out_free_skb;
if (skb_headroom(skb) < sizeof(*rthdr) &&
@ -1555,7 +1662,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
(1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL));
rthdr->rate = rx->u.rx.rate->bitrate / 5;
rthdr->rate = rx->rate->bitrate / 5;
rthdr->chan_freq = cpu_to_le16(status->freq);
if (status->band == IEEE80211_BAND_5GHZ)
@ -1598,14 +1705,14 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
} else
goto out_free_skb;
rx->flags |= IEEE80211_TXRXD_RX_CMNTR_REPORTED;
rx->flags |= IEEE80211_RX_CMNTR_REPORTED;
return;
out_free_skb:
dev_kfree_skb(skb);
}
typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_txrx_data *);
typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_rx_data *);
static ieee80211_rx_handler ieee80211_rx_handlers[] =
{
ieee80211_rx_h_if_stats,
@ -1629,7 +1736,7 @@ static ieee80211_rx_handler ieee80211_rx_handlers[] =
};
static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
struct ieee80211_txrx_data *rx,
struct ieee80211_rx_data *rx,
struct sk_buff *skb)
{
ieee80211_rx_handler *handler;
@ -1672,7 +1779,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
/* main receive path */
static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
u8 *bssid, struct ieee80211_txrx_data *rx,
u8 *bssid, struct ieee80211_rx_data *rx,
struct ieee80211_hdr *hdr)
{
int multicast = is_multicast_ether_addr(hdr->addr1);
@ -1682,15 +1789,15 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
if (!bssid)
return 0;
if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!multicast &&
compare_ether_addr(sdata->dev->dev_addr,
hdr->addr1) != 0) {
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
}
break;
case IEEE80211_IF_TYPE_IBSS:
@ -1700,19 +1807,29 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
(rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
return 1;
else if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!multicast &&
compare_ether_addr(sdata->dev->dev_addr,
hdr->addr1) != 0) {
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!rx->sta)
rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
bssid, hdr->addr2);
break;
case IEEE80211_IF_TYPE_MESH_POINT:
if (!multicast &&
compare_ether_addr(sdata->dev->dev_addr,
hdr->addr1) != 0) {
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
}
break;
case IEEE80211_IF_TYPE_VLAN:
case IEEE80211_IF_TYPE_AP:
if (!bssid) {
@ -1721,12 +1838,12 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
return 0;
} else if (!ieee80211_bssid_match(bssid,
sdata->dev->dev_addr)) {
if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
}
if (sdata->dev == sdata->local->mdev &&
!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
!(rx->flags & IEEE80211_RX_IN_SCAN))
/* do not receive anything via
* master device when not scanning */
return 0;
@ -1763,7 +1880,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr;
struct ieee80211_txrx_data rx;
struct ieee80211_rx_data rx;
u16 type;
int prepares;
struct ieee80211_sub_if_data *prev = NULL;
@ -1775,9 +1892,9 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
rx.skb = skb;
rx.local = local;
rx.u.rx.status = status;
rx.u.rx.load = load;
rx.u.rx.rate = rate;
rx.status = status;
rx.load = load;
rx.rate = rate;
rx.fc = le16_to_cpu(hdr->frame_control);
type = rx.fc & IEEE80211_FCTL_FTYPE;
@ -1786,17 +1903,17 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
rx.sta = sta_info_get(local, hdr->addr2);
if (rx.sta) {
rx.dev = rx.sta->dev;
rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
rx.sdata = rx.sta->sdata;
rx.dev = rx.sta->sdata->dev;
}
if ((status->flag & RX_FLAG_MMIC_ERROR)) {
ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
goto end;
return;
}
if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
rx.flags |= IEEE80211_RX_IN_SCAN;
ieee80211_parse_qos(&rx);
ieee80211_verify_ip_alignment(&rx);
@ -1811,7 +1928,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
continue;
bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
rx.flags |= IEEE80211_RX_RA_MATCH;
prepares = prepare_for_handlers(sdata, bssid, &rx, hdr);
if (!prepares)
@ -1851,10 +1968,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
ieee80211_invoke_rx_handlers(prev, &rx, skb);
} else
dev_kfree_skb(skb);
end:
if (rx.sta)
sta_info_put(rx.sta);
}
#define SEQ_MODULO 0x1000
@ -2031,7 +2144,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
/* if this mpdu is fragmented - terminate rx aggregation session */
sc = le16_to_cpu(hdr->seq_ctrl);
if (sc & IEEE80211_SCTL_FRAG) {
ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr,
ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr,
tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
ret = 1;
goto end_reorder;
@ -2041,9 +2154,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
mpdu_seq_num, 0);
end_reorder:
if (sta)
sta_info_put(sta);
end_reorder:
return ret;
}

View file

@ -15,21 +15,52 @@
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/timer.h>
#include <linux/rtnetlink.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
#include "sta_info.h"
#include "debugfs_sta.h"
#include "mesh.h"
/* Caller must hold local->sta_lock */
static void sta_info_hash_add(struct ieee80211_local *local,
struct sta_info *sta)
{
sta->hnext = local->sta_hash[STA_HASH(sta->addr)];
local->sta_hash[STA_HASH(sta->addr)] = sta;
}
/**
* DOC: STA information lifetime rules
*
* STA info structures (&struct sta_info) are managed in a hash table
* for faster lookup and a list for iteration. They are managed using
* RCU, i.e. access to the list and hash table is protected by RCU.
*
* Upon allocating a STA info structure with sta_info_alloc(), the caller owns
* that structure. It must then either destroy it using sta_info_destroy()
* (which is pretty useless) or insert it into the hash table using
* sta_info_insert() which demotes the reference from ownership to a regular
* RCU-protected reference; if the function is called without protection by an
* RCU critical section the reference is instantly invalidated.
*
* Because there are debugfs entries for each station, and adding those
* must be able to sleep, it is also possible to "pin" a station entry,
* that means it can be removed from the hash table but not be freed.
* See the comment in __sta_info_unlink() for more information.
*
* In order to remove a STA info structure, the caller needs to first
* unlink it (sta_info_unlink()) from the list and hash tables and
* then wait for an RCU synchronisation before it can be freed. Due to
* the pinning and the possibility of multiple callers trying to remove
* the same STA info at the same time, sta_info_unlink() can clear the
* STA info pointer it is passed to indicate that the STA info is owned
* by somebody else now.
*
* If sta_info_unlink() did not clear the pointer then the caller owns
* the STA info structure now and is responsible of destroying it with
* a call to sta_info_destroy(), not before RCU synchronisation, of
* course. Note that sta_info_destroy() must be protected by the RTNL.
*
* In all other cases, there is no concept of ownership on a STA entry,
* each structure is owned by the global hash table/list until it is
* removed. All users of the structure need to be RCU protected so that
* the structure won't be freed before they are done using it.
*/
/* Caller must hold local->sta_lock */
static int sta_info_hash_del(struct ieee80211_local *local,
@ -41,109 +72,152 @@ static int sta_info_hash_del(struct ieee80211_local *local,
if (!s)
return -ENOENT;
if (s == sta) {
local->sta_hash[STA_HASH(sta->addr)] = s->hnext;
rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)],
s->hnext);
return 0;
}
while (s->hnext && s->hnext != sta)
s = s->hnext;
if (s->hnext) {
s->hnext = sta->hnext;
rcu_assign_pointer(s->hnext, sta->hnext);
return 0;
}
return -ENOENT;
}
/* must hold local->sta_lock */
/* protected by RCU */
static struct sta_info *__sta_info_find(struct ieee80211_local *local,
u8 *addr)
{
struct sta_info *sta;
sta = local->sta_hash[STA_HASH(addr)];
sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]);
while (sta) {
if (compare_ether_addr(sta->addr, addr) == 0)
break;
sta = sta->hnext;
sta = rcu_dereference(sta->hnext);
}
return sta;
}
struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
{
struct sta_info *sta;
read_lock_bh(&local->sta_lock);
sta = __sta_info_find(local, addr);
if (sta)
__sta_info_get(sta);
read_unlock_bh(&local->sta_lock);
return sta;
return __sta_info_find(local, addr);
}
EXPORT_SYMBOL(sta_info_get);
static void sta_info_release(struct kref *kref)
struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
struct net_device *dev)
{
struct sta_info *sta;
int i = 0;
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (dev && dev != sta->sdata->dev)
continue;
if (i < idx) {
++i;
continue;
}
return sta;
}
return NULL;
}
void sta_info_destroy(struct sta_info *sta)
{
struct sta_info *sta = container_of(kref, struct sta_info, kref);
struct ieee80211_local *local = sta->local;
struct sk_buff *skb;
int i;
DECLARE_MAC_BUF(mbuf);
if (!sta)
return;
ASSERT_RTNL();
might_sleep();
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
mesh_plink_deactivate(sta);
#endif
/*
* NOTE: This will call synchronize_rcu() internally to
* make sure no key references can be in use. We rely on
* that here for the mesh code!
*/
ieee80211_key_free(sta->key);
WARN_ON(sta->key);
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
del_timer_sync(&sta->plink_timer);
#endif
/* free sta structure; it has already been removed from
* hash table etc. external structures. Make sure that all
* buffered frames are release (one might have been added
* after sta_info_free() was called). */
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
local->total_ps_buffered--;
dev_kfree_skb_any(skb);
}
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
dev_kfree_skb_any(skb);
}
for (i = 0; i < STA_TID_NUM; i++) {
del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer);
del_timer_sync(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer);
}
rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
rate_control_put(sta->rate_ctrl);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Destroyed STA %s\n",
wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
kfree(sta);
}
void sta_info_put(struct sta_info *sta)
/* Caller must hold local->sta_lock */
static void sta_info_hash_add(struct ieee80211_local *local,
struct sta_info *sta)
{
kref_put(&sta->kref, sta_info_release);
sta->hnext = local->sta_hash[STA_HASH(sta->addr)];
rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)], sta);
}
EXPORT_SYMBOL(sta_info_put);
struct sta_info *sta_info_add(struct ieee80211_local *local,
struct net_device *dev, u8 *addr, gfp_t gfp)
struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
u8 *addr, gfp_t gfp)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int i;
DECLARE_MAC_BUF(mac);
DECLARE_MAC_BUF(mbuf);
sta = kzalloc(sizeof(*sta), gfp);
if (!sta)
return ERR_PTR(-ENOMEM);
kref_init(&sta->kref);
sta->rate_ctrl = rate_control_get(local->rate_ctrl);
sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp);
if (!sta->rate_ctrl_priv) {
rate_control_put(sta->rate_ctrl);
kfree(sta);
return ERR_PTR(-ENOMEM);
}
return NULL;
memcpy(sta->addr, addr, ETH_ALEN);
sta->local = local;
sta->dev = dev;
sta->sdata = sdata;
sta->rate_ctrl = rate_control_get(local->rate_ctrl);
sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
gfp);
if (!sta->rate_ctrl_priv) {
rate_control_put(sta->rate_ctrl);
kfree(sta);
return NULL;
}
spin_lock_init(&sta->ampdu_mlme.ampdu_rx);
spin_lock_init(&sta->ampdu_mlme.ampdu_tx);
for (i = 0; i < STA_TID_NUM; i++) {
@ -168,35 +242,68 @@ struct sta_info *sta_info_add(struct ieee80211_local *local,
}
skb_queue_head_init(&sta->ps_tx_buf);
skb_queue_head_init(&sta->tx_filtered);
write_lock_bh(&local->sta_lock);
/* mark sta as used (by caller) */
__sta_info_get(sta);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Allocated STA %s\n",
wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
#ifdef CONFIG_MAC80211_MESH
sta->plink_state = PLINK_LISTEN;
spin_lock_init(&sta->plink_lock);
init_timer(&sta->plink_timer);
#endif
return sta;
}
int sta_info_insert(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
unsigned long flags;
DECLARE_MAC_BUF(mac);
/*
* Can't be a WARN_ON because it can be triggered through a race:
* something inserts a STA (on one CPU) without holding the RTNL
* and another CPU turns off the net device.
*/
if (unlikely(!netif_running(sdata->dev)))
return -ENETDOWN;
if (WARN_ON(compare_ether_addr(sta->addr, sdata->dev->dev_addr) == 0))
return -EINVAL;
if (WARN_ON(is_multicast_ether_addr(sta->addr)))
return -EINVAL;
spin_lock_irqsave(&local->sta_lock, flags);
/* check if STA exists already */
if (__sta_info_find(local, addr)) {
write_unlock_bh(&local->sta_lock);
sta_info_put(sta);
return ERR_PTR(-EEXIST);
if (__sta_info_find(local, sta->addr)) {
spin_unlock_irqrestore(&local->sta_lock, flags);
return -EEXIST;
}
list_add(&sta->list, &local->sta_list);
local->num_sta++;
sta_info_hash_add(local, sta);
if (local->ops->sta_notify) {
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
/* notify driver */
if (local->ops->sta_notify) {
if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
sdata = sdata->u.vlan.ap;
local->ops->sta_notify(local_to_hw(local), &sdata->vif,
STA_NOTIFY_ADD, addr);
STA_NOTIFY_ADD, sta->addr);
}
write_unlock_bh(&local->sta_lock);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Added STA %s\n",
wiphy_name(local->hw.wiphy), print_mac(mac, addr));
printk(KERN_DEBUG "%s: Inserted STA %s\n",
wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
spin_unlock_irqrestore(&local->sta_lock, flags);
#ifdef CONFIG_MAC80211_DEBUGFS
/* debugfs entry adding might sleep, so schedule process
* context task for adding entry for STAs that do not yet
@ -204,7 +311,10 @@ struct sta_info *sta_info_add(struct ieee80211_local *local,
queue_work(local->hw.workqueue, &local->sta_debugfs_add);
#endif
return sta;
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_accept_plinks_update(sdata);
return 0;
}
static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
@ -230,19 +340,20 @@ static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss,
{
if (bss)
__bss_tim_set(bss, sta->aid);
if (sta->local->ops->set_tim)
if (sta->local->ops->set_tim) {
sta->local->tim_in_locked_section = true;
sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 1);
sta->local->tim_in_locked_section = false;
}
}
void sta_info_set_tim_bit(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata;
unsigned long flags;
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
read_lock_bh(&sta->local->sta_lock);
__sta_info_set_tim_bit(sdata->bss, sta);
read_unlock_bh(&sta->local->sta_lock);
spin_lock_irqsave(&sta->local->sta_lock, flags);
__sta_info_set_tim_bit(sta->sdata->bss, sta);
spin_unlock_irqrestore(&sta->local->sta_lock, flags);
}
static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
@ -250,88 +361,135 @@ static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
{
if (bss)
__bss_tim_clear(bss, sta->aid);
if (sta->local->ops->set_tim)
if (sta->local->ops->set_tim) {
sta->local->tim_in_locked_section = true;
sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 0);
sta->local->tim_in_locked_section = false;
}
}
void sta_info_clear_tim_bit(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata;
unsigned long flags;
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
read_lock_bh(&sta->local->sta_lock);
__sta_info_clear_tim_bit(sdata->bss, sta);
read_unlock_bh(&sta->local->sta_lock);
spin_lock_irqsave(&sta->local->sta_lock, flags);
__sta_info_clear_tim_bit(sta->sdata->bss, sta);
spin_unlock_irqrestore(&sta->local->sta_lock, flags);
}
/* Caller must hold local->sta_lock */
void sta_info_remove(struct sta_info *sta)
/*
* See comment in __sta_info_unlink,
* caller must hold local->sta_lock.
*/
static void __sta_info_pin(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata;
WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_NORMAL);
sta->pin_status = STA_INFO_PIN_STAT_PINNED;
}
/* don't do anything if we've been removed already */
if (sta_info_hash_del(local, sta))
/*
* See comment in __sta_info_unlink, returns sta if it
* needs to be destroyed.
*/
static struct sta_info *__sta_info_unpin(struct sta_info *sta)
{
struct sta_info *ret = NULL;
unsigned long flags;
spin_lock_irqsave(&sta->local->sta_lock, flags);
WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_DESTROY &&
sta->pin_status != STA_INFO_PIN_STAT_PINNED);
if (sta->pin_status == STA_INFO_PIN_STAT_DESTROY)
ret = sta;
sta->pin_status = STA_INFO_PIN_STAT_NORMAL;
spin_unlock_irqrestore(&sta->local->sta_lock, flags);
return ret;
}
static void __sta_info_unlink(struct sta_info **sta)
{
struct ieee80211_local *local = (*sta)->local;
struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
DECLARE_MAC_BUF(mbuf);
#endif
/*
* pull caller's reference if we're already gone.
*/
if (sta_info_hash_del(local, *sta)) {
*sta = NULL;
return;
}
list_del(&sta->list);
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
if (sta->flags & WLAN_STA_PS) {
sta->flags &= ~WLAN_STA_PS;
/*
* Also pull caller's reference if the STA is pinned by the
* task that is adding the debugfs entries. In that case, we
* leave the STA "to be freed".
*
* The rules are not trivial, but not too complex either:
* (1) pin_status is only modified under the sta_lock
* (2) sta_info_debugfs_add_work() will set the status
* to PINNED when it found an item that needs a new
* debugfs directory created. In that case, that item
* must not be freed although all *RCU* users are done
* with it. Hence, we tell the caller of _unlink()
* that the item is already gone (as can happen when
* two tasks try to unlink/destroy at the same time)
* (3) We set the pin_status to DESTROY here when we
* find such an item.
* (4) sta_info_debugfs_add_work() will reset the pin_status
* from PINNED to NORMAL when it is done with the item,
* but will check for DESTROY before resetting it in
* which case it will free the item.
*/
if ((*sta)->pin_status == STA_INFO_PIN_STAT_PINNED) {
(*sta)->pin_status = STA_INFO_PIN_STAT_DESTROY;
*sta = NULL;
return;
}
list_del(&(*sta)->list);
if ((*sta)->flags & WLAN_STA_PS) {
(*sta)->flags &= ~WLAN_STA_PS;
if (sdata->bss)
atomic_dec(&sdata->bss->num_sta_ps);
__sta_info_clear_tim_bit(sdata->bss, sta);
__sta_info_clear_tim_bit(sdata->bss, *sta);
}
local->num_sta--;
}
void sta_info_free(struct sta_info *sta)
{
struct sk_buff *skb;
struct ieee80211_local *local = sta->local;
DECLARE_MAC_BUF(mac);
might_sleep();
write_lock_bh(&local->sta_lock);
sta_info_remove(sta);
write_unlock_bh(&local->sta_lock);
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
local->total_ps_buffered--;
dev_kfree_skb(skb);
}
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
dev_kfree_skb(skb);
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Removed STA %s\n",
wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
ieee80211_key_free(sta->key);
WARN_ON(sta->key);
if (local->ops->sta_notify) {
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
sdata = sdata->u.vlan.ap;
local->ops->sta_notify(local_to_hw(local), &sdata->vif,
STA_NOTIFY_REMOVE, sta->addr);
STA_NOTIFY_REMOVE, (*sta)->addr);
}
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
if (ieee80211_vif_is_mesh(&sdata->vif)) {
mesh_accept_plinks_update(sdata);
#ifdef CONFIG_MAC80211_MESH
del_timer(&(*sta)->plink_timer);
#endif
}
sta_info_put(sta);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Removed STA %s\n",
wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
}
void sta_info_unlink(struct sta_info **sta)
{
struct ieee80211_local *local = (*sta)->local;
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
__sta_info_unlink(sta);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
static inline int sta_info_buffer_expired(struct ieee80211_local *local,
struct sta_info *sta,
@ -377,7 +535,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
if (!skb)
break;
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
local->total_ps_buffered--;
printk(KERN_DEBUG "Buffered frame expired (STA "
"%s)\n", print_mac(mac, sta->addr));
@ -394,13 +552,10 @@ static void sta_info_cleanup(unsigned long data)
struct ieee80211_local *local = (struct ieee80211_local *) data;
struct sta_info *sta;
read_lock_bh(&local->sta_lock);
list_for_each_entry(sta, &local->sta_list, list) {
__sta_info_get(sta);
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list)
sta_info_cleanup_expire_buffered(local, sta);
sta_info_put(sta);
}
read_unlock_bh(&local->sta_lock);
rcu_read_unlock();
local->sta_cleanup.expires =
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
@ -408,37 +563,45 @@ static void sta_info_cleanup(unsigned long data)
}
#ifdef CONFIG_MAC80211_DEBUGFS
static void sta_info_debugfs_add_task(struct work_struct *work)
static void sta_info_debugfs_add_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, sta_debugfs_add);
struct sta_info *sta, *tmp;
unsigned long flags;
while (1) {
sta = NULL;
read_lock_bh(&local->sta_lock);
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry(tmp, &local->sta_list, list) {
if (!tmp->debugfs.dir) {
sta = tmp;
__sta_info_get(sta);
__sta_info_pin(sta);
break;
}
}
read_unlock_bh(&local->sta_lock);
spin_unlock_irqrestore(&local->sta_lock, flags);
if (!sta)
break;
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
sta_info_put(sta);
sta = __sta_info_unpin(sta);
if (sta) {
synchronize_rcu();
sta_info_destroy(sta);
}
}
}
#endif
void sta_info_init(struct ieee80211_local *local)
{
rwlock_init(&local->sta_lock);
spin_lock_init(&local->sta_lock);
INIT_LIST_HEAD(&local->sta_list);
setup_timer(&local->sta_cleanup, sta_info_cleanup,
@ -447,7 +610,7 @@ void sta_info_init(struct ieee80211_local *local)
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
#ifdef CONFIG_MAC80211_DEBUGFS
INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_task);
INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_work);
#endif
}
@ -465,25 +628,38 @@ void sta_info_stop(struct ieee80211_local *local)
/**
* sta_info_flush - flush matching STA entries from the STA table
*
* Returns the number of removed STA entries.
*
* @local: local interface data
* @dev: matching rule for the net device (sta->dev) or %NULL to match all STAs
* @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
*/
void sta_info_flush(struct ieee80211_local *local, struct net_device *dev)
int sta_info_flush(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta, *tmp;
LIST_HEAD(tmp_list);
int ret = 0;
unsigned long flags;
write_lock_bh(&local->sta_lock);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
if (!dev || dev == sta->dev) {
__sta_info_get(sta);
sta_info_remove(sta);
list_add_tail(&sta->list, &tmp_list);
might_sleep();
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
if (!sdata || sdata == sta->sdata) {
__sta_info_unlink(&sta);
if (sta) {
list_add_tail(&sta->list, &tmp_list);
ret++;
}
}
write_unlock_bh(&local->sta_lock);
list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
sta_info_free(sta);
sta_info_put(sta);
}
spin_unlock_irqrestore(&local->sta_lock, flags);
synchronize_rcu();
list_for_each_entry_safe(sta, tmp, &tmp_list, list)
sta_info_destroy(sta);
return ret;
}

View file

@ -12,7 +12,6 @@
#include <linux/list.h>
#include <linux/types.h>
#include <linux/if_ether.h>
#include <linux/kref.h>
#include "ieee80211_key.h"
/**
@ -107,6 +106,29 @@ struct tid_ampdu_rx {
struct timer_list session_timer;
};
/**
* enum plink_state - state of a mesh peer link finite state machine
*
* @PLINK_LISTEN: initial state, considered the implicit state of non existant
* mesh peer links
* @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer
* @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer
* @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh
* peer
* @PLINK_ESTAB: mesh peer link is established
* @PLINK_HOLDING: mesh peer link is being closed or cancelled
* @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded
*/
enum plink_state {
PLINK_LISTEN,
PLINK_OPN_SNT,
PLINK_OPN_RCVD,
PLINK_CNF_RCVD,
PLINK_ESTAB,
PLINK_HOLDING,
PLINK_BLOCKED
};
/**
* struct sta_ampdu_mlme - STA aggregation information.
*
@ -124,75 +146,132 @@ struct sta_ampdu_mlme {
u8 dialog_token_allocator;
};
/* see __sta_info_unlink */
#define STA_INFO_PIN_STAT_NORMAL 0
#define STA_INFO_PIN_STAT_PINNED 1
#define STA_INFO_PIN_STAT_DESTROY 2
/**
* struct sta_info - STA information
*
* This structure collects information about a station that
* mac80211 is communicating with.
*
* @list: global linked list entry
* @hnext: hash table linked list pointer
* @local: pointer to the global information
* @addr: MAC address of this STA
* @aid: STA's unique AID (1..2007, 0 = not assigned yet),
* only used in AP (and IBSS?) mode
* @flags: STA flags, see &enum ieee80211_sta_info_flags
* @ps_tx_buf: buffer of frames to transmit to this station
* when it leaves power saving state
* @tx_filtered: buffer of frames we already tried to transmit
* but were filtered by hardware due to STA having entered
* power saving state
* @rx_packets: Number of MSDUs received from this STA
* @rx_bytes: Number of bytes received from this STA
* @supp_rates: Bitmap of supported rates (per band)
* @ht_info: HT capabilities of this STA
*/
struct sta_info {
struct kref kref;
/* General information, mostly static */
struct list_head list;
struct sta_info *hnext; /* next entry in hash table list */
struct sta_info *hnext;
struct ieee80211_local *local;
u8 addr[ETH_ALEN];
u16 aid; /* STA's unique AID (1..2007), 0 = not yet assigned */
u32 flags; /* WLAN_STA_ */
struct sk_buff_head ps_tx_buf; /* buffer of TX frames for station in
* power saving state */
struct sk_buff_head tx_filtered; /* buffer of TX frames that were
* already given to low-level driver,
* but were filtered */
unsigned long rx_packets, tx_packets; /* number of RX/TX MSDUs */
unsigned long rx_bytes, tx_bytes;
unsigned long tx_retry_failed, tx_retry_count;
unsigned long tx_filtered_count;
unsigned int wep_weak_iv_count; /* number of RX frames with weak IV */
unsigned long last_rx;
/* bitmap of supported rates per band */
u64 supp_rates[IEEE80211_NUM_BANDS];
int txrate_idx;
/* last rates used to send a frame to this STA */
int last_txrate_idx, last_nonerp_txrate_idx;
struct net_device *dev; /* which net device is this station associated
* to */
struct ieee80211_sub_if_data *sdata;
struct ieee80211_key *key;
u32 tx_num_consecutive_failures;
u32 tx_num_mpdu_ok;
u32 tx_num_mpdu_fail;
struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv;
struct ieee80211_ht_info ht_info;
u64 supp_rates[IEEE80211_NUM_BANDS];
u8 addr[ETH_ALEN];
u16 aid;
u16 listen_interval;
/* last received seq/frag number from this STA (per RX queue) */
__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
/*
* for use by the internal lifetime management,
* see __sta_info_unlink
*/
u8 pin_status;
/* frequently updated information, needs locking? */
u32 flags;
/*
* STA powersave frame queues, no more than the internal
* locking required.
*/
struct sk_buff_head ps_tx_buf;
struct sk_buff_head tx_filtered;
/* Updated from RX path only, no locking requirements */
unsigned long rx_packets, rx_bytes;
unsigned long wep_weak_iv_count;
unsigned long last_rx;
unsigned long num_duplicates; /* number of duplicate frames received
* from this STA */
unsigned long tx_fragments; /* number of transmitted MPDUs */
unsigned long rx_fragments; /* number of received MPDUs */
unsigned long rx_dropped; /* number of dropped MPDUs from this STA */
int last_rssi; /* RSSI of last received frame from this STA */
int last_signal; /* signal of last received frame from this STA */
int last_noise; /* noise of last received frame from this STA */
/* last received seq/frag number from this STA (per RX queue) */
__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
#endif
/* Updated from TX status path only, no locking requirements */
unsigned long tx_filtered_count;
unsigned long tx_retry_failed, tx_retry_count;
/* TODO: update in generic code not rate control? */
u32 tx_num_consecutive_failures;
u32 tx_num_mpdu_ok;
u32 tx_num_mpdu_fail;
/* moving percentage of failed MSDUs */
unsigned int fail_avg;
/* Updated from TX path only, no locking requirements */
unsigned long tx_packets; /* number of RX/TX MSDUs */
unsigned long tx_bytes;
unsigned long tx_fragments; /* number of transmitted MPDUs */
int txrate_idx;
int last_txrate_idx;
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES];
#endif
/* Debug counters, no locking doesn't matter */
int channel_use;
int channel_use_raw;
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES];
#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */
u16 listen_interval;
struct ieee80211_ht_info ht_info; /* 802.11n HT capabilities
of this STA */
/*
* Aggregation information, comes with own locking.
*/
struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[STA_TID_NUM]; /* convert timer id to tid */
u8 timer_to_tid[STA_TID_NUM]; /* identity mapping to ID timers */
u8 tid_to_tx_q[STA_TID_NUM]; /* map tid to tx queue */
#ifdef CONFIG_MAC80211_MESH
/*
* Mesh peer link attributes
* TODO: move to a sub-structure that is referenced with pointer?
*/
__le16 llid; /* Local link ID */
__le16 plid; /* Peer link ID */
__le16 reason; /* Cancel reason on PLINK_HOLDING state */
u8 plink_retries; /* Retries in establishment */
bool ignore_plink_timer;
enum plink_state plink_state;
u32 plink_timeout;
struct timer_list plink_timer;
spinlock_t plink_lock; /* For peer_state reads / updates and other
updates in the structure. Ensures robust
transitions for the peerlink FSM */
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
struct sta_info_debugfsdentries {
struct dentry *dir;
@ -209,6 +288,14 @@ struct sta_info {
#endif
};
static inline enum plink_state sta_plink_state(struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_MESH
return sta->plink_state;
#endif
return PLINK_LISTEN;
}
/* Maximum number of concurrently registered stations */
#define MAX_STA_COUNT 2007
@ -228,23 +315,44 @@ struct sta_info {
*/
#define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
static inline void __sta_info_get(struct sta_info *sta)
{
kref_get(&sta->kref);
}
struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr);
void sta_info_put(struct sta_info *sta);
struct sta_info *sta_info_add(struct ieee80211_local *local,
struct net_device *dev, u8 *addr, gfp_t gfp);
void sta_info_remove(struct sta_info *sta);
void sta_info_free(struct sta_info *sta);
void sta_info_init(struct ieee80211_local *local);
int sta_info_start(struct ieee80211_local *local);
void sta_info_stop(struct ieee80211_local *local);
void sta_info_flush(struct ieee80211_local *local, struct net_device *dev);
/*
* Get a STA info, must have be under RCU read lock.
*/
struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr);
/*
* Get STA info by index, BROKEN!
*/
struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
struct net_device *dev);
/*
* Create a new STA info, caller owns returned structure
* until sta_info_insert().
*/
struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
u8 *addr, gfp_t gfp);
/*
* Insert STA info into hash table/list, returns zero or a
* -EEXIST if (if the same MAC address is already present).
*
* Calling this without RCU protection makes the caller
* relinquish its reference to @sta.
*/
int sta_info_insert(struct sta_info *sta);
/*
* Unlink a STA info from the hash table/list.
* This can NULL the STA pointer if somebody else
* has already unlinked it.
*/
void sta_info_unlink(struct sta_info **sta);
void sta_info_destroy(struct sta_info *sta);
void sta_info_set_tim_bit(struct sta_info *sta);
void sta_info_clear_tim_bit(struct sta_info *sta);
void sta_info_init(struct ieee80211_local *local);
int sta_info_start(struct ieee80211_local *local);
void sta_info_stop(struct ieee80211_local *local);
int sta_info_flush(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
#endif /* STA_INFO_H */

View file

@ -26,6 +26,7 @@
#include "ieee80211_i.h"
#include "ieee80211_led.h"
#include "mesh.h"
#include "wep.h"
#include "wpa.h"
#include "wme.h"
@ -86,11 +87,11 @@ static inline void ieee80211_dump_frame(const char *ifname, const char *title,
}
#endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr,
static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
int next_frag_len)
{
int rate, mrate, erp, dur, i;
struct ieee80211_rate *txrate = tx->u.tx.rate;
struct ieee80211_rate *txrate = tx->rate;
struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband;
@ -233,7 +234,7 @@ static int inline is_ieee80211_device(struct net_device *dev,
/* tx handlers */
static ieee80211_tx_result
ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
{
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct sk_buff *skb = tx->skb;
@ -241,7 +242,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
u32 sta_flags;
if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED))
if (unlikely(tx->flags & IEEE80211_TX_INJECTED))
return TX_CONTINUE;
if (unlikely(tx->local->sta_sw_scanning) &&
@ -249,12 +250,15 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
(tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
return TX_DROP;
if (tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED)
if (tx->sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
return TX_CONTINUE;
if (tx->flags & IEEE80211_TX_PS_BUFFERED)
return TX_CONTINUE;
sta_flags = tx->sta ? tx->sta->flags : 0;
if (likely(tx->flags & IEEE80211_TXRXD_TXUNICAST)) {
if (likely(tx->flags & IEEE80211_TX_UNICAST)) {
if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
tx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
(tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
@ -284,7 +288,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
}
static ieee80211_tx_result
ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
@ -323,10 +327,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
}
total += skb_queue_len(&ap->ps_bc_buf);
}
rcu_read_unlock();
read_lock_bh(&local->sta_lock);
list_for_each_entry(sta, &local->sta_list, list) {
list_for_each_entry_rcu(sta, &local->sta_list, list) {
skb = skb_dequeue(&sta->ps_tx_buf);
if (skb) {
purged++;
@ -334,7 +336,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
}
total += skb_queue_len(&sta->ps_tx_buf);
}
read_unlock_bh(&local->sta_lock);
rcu_read_unlock();
local->total_ps_buffered = total;
printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
@ -342,7 +345,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
}
static ieee80211_tx_result
ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
{
/*
* broadcast/multicast frame
@ -379,13 +382,13 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
}
/* buffered in hardware */
tx->u.tx.control->flags |= IEEE80211_TXCTL_SEND_AFTER_DTIM;
tx->control->flags |= IEEE80211_TXCTL_SEND_AFTER_DTIM;
return TX_CONTINUE;
}
static ieee80211_tx_result
ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
{
struct sta_info *sta = tx->sta;
DECLARE_MAC_BUF(mac);
@ -439,32 +442,32 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
}
static ieee80211_tx_result
ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
{
if (unlikely(tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED))
if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
return TX_CONTINUE;
if (tx->flags & IEEE80211_TXRXD_TXUNICAST)
if (tx->flags & IEEE80211_TX_UNICAST)
return ieee80211_tx_h_unicast_ps_buf(tx);
else
return ieee80211_tx_h_multicast_ps_buf(tx);
}
static ieee80211_tx_result
ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
{
struct ieee80211_key *key;
u16 fc = tx->fc;
if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
if (unlikely(tx->control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
tx->key = NULL;
else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
tx->key = key;
else if ((key = rcu_dereference(tx->sdata->default_key)))
tx->key = key;
else if (tx->sdata->drop_unencrypted &&
!(tx->u.tx.control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
!(tx->flags & IEEE80211_TXRXD_TX_INJECTED)) {
!(tx->control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
!(tx->flags & IEEE80211_TX_INJECTED)) {
I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
return TX_DROP;
} else
@ -493,13 +496,13 @@ ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
}
if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
tx->u.tx.control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
tx->control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
return TX_CONTINUE;
}
static ieee80211_tx_result
ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
size_t hdrlen, per_fragm, num_fragm, payload_len, left;
@ -509,7 +512,7 @@ ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
u8 *pos;
int frag_threshold = tx->local->fragmentation_threshold;
if (!(tx->flags & IEEE80211_TXRXD_FRAGMENTED))
if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
return TX_CONTINUE;
first = tx->skb;
@ -561,8 +564,8 @@ ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
}
skb_trim(first, hdrlen + per_fragm);
tx->u.tx.num_extra_frag = num_fragm - 1;
tx->u.tx.extra_frag = frags;
tx->num_extra_frag = num_fragm - 1;
tx->extra_frag = frags;
return TX_CONTINUE;
@ -579,7 +582,7 @@ ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
}
static ieee80211_tx_result
ieee80211_tx_h_encrypt(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
{
if (!tx->key)
return TX_CONTINUE;
@ -599,56 +602,56 @@ ieee80211_tx_h_encrypt(struct ieee80211_txrx_data *tx)
}
static ieee80211_tx_result
ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
{
struct rate_selection rsel;
struct ieee80211_supported_band *sband;
sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
if (likely(!tx->u.tx.rate)) {
if (likely(!tx->rate)) {
rate_control_get_rate(tx->dev, sband, tx->skb, &rsel);
tx->u.tx.rate = rsel.rate;
tx->rate = rsel.rate;
if (unlikely(rsel.probe)) {
tx->u.tx.control->flags |=
tx->control->flags |=
IEEE80211_TXCTL_RATE_CTRL_PROBE;
tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
tx->u.tx.control->alt_retry_rate = tx->u.tx.rate;
tx->u.tx.rate = rsel.probe;
tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
tx->control->alt_retry_rate = tx->rate;
tx->rate = rsel.probe;
} else
tx->u.tx.control->alt_retry_rate = NULL;
tx->control->alt_retry_rate = NULL;
if (!tx->u.tx.rate)
if (!tx->rate)
return TX_DROP;
} else
tx->u.tx.control->alt_retry_rate = NULL;
tx->control->alt_retry_rate = NULL;
if (tx->sdata->bss_conf.use_cts_prot &&
(tx->flags & IEEE80211_TXRXD_FRAGMENTED) && rsel.nonerp) {
tx->u.tx.last_frag_rate = tx->u.tx.rate;
(tx->flags & IEEE80211_TX_FRAGMENTED) && rsel.nonerp) {
tx->last_frag_rate = tx->rate;
if (rsel.probe)
tx->flags &= ~IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
else
tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
tx->u.tx.rate = rsel.nonerp;
tx->u.tx.control->tx_rate = rsel.nonerp;
tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
tx->rate = rsel.nonerp;
tx->control->tx_rate = rsel.nonerp;
tx->control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
} else {
tx->u.tx.last_frag_rate = tx->u.tx.rate;
tx->u.tx.control->tx_rate = tx->u.tx.rate;
tx->last_frag_rate = tx->rate;
tx->control->tx_rate = tx->rate;
}
tx->u.tx.control->tx_rate = tx->u.tx.rate;
tx->control->tx_rate = tx->rate;
return TX_CONTINUE;
}
static ieee80211_tx_result
ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
u16 fc = le16_to_cpu(hdr->frame_control);
u16 dur;
struct ieee80211_tx_control *control = tx->u.tx.control;
struct ieee80211_tx_control *control = tx->control;
if (!control->retry_limit) {
if (!is_multicast_ether_addr(hdr->addr1)) {
@ -670,7 +673,7 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
}
}
if (tx->flags & IEEE80211_TXRXD_FRAGMENTED) {
if (tx->flags & IEEE80211_TX_FRAGMENTED) {
/* Do not use multiple retry rates when sending fragmented
* frames.
* TODO: The last fragment could still use multiple retry
@ -682,8 +685,8 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
* there are associated non-ERP stations and RTS/CTS is not configured
* for the frame. */
if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
(tx->u.tx.rate->flags & IEEE80211_RATE_ERP_G) &&
(tx->flags & IEEE80211_TXRXD_TXUNICAST) &&
(tx->rate->flags & IEEE80211_RATE_ERP_G) &&
(tx->flags & IEEE80211_TX_UNICAST) &&
tx->sdata->bss_conf.use_cts_prot &&
!(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
@ -692,18 +695,18 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
* short preambles at the selected rate and short preambles are
* available on the network at the current point in time. */
if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
(tx->u.tx.rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
(tx->rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
tx->sdata->bss_conf.use_short_preamble &&
(!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
tx->u.tx.control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
tx->control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
}
/* Setup duration field for the first fragment of the frame. Duration
* for remaining fragments will be updated when they are being sent
* to low-level driver in ieee80211_tx(). */
dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
(tx->flags & IEEE80211_TXRXD_FRAGMENTED) ?
tx->u.tx.extra_frag[0]->len : 0);
(tx->flags & IEEE80211_TX_FRAGMENTED) ?
tx->extra_frag[0]->len : 0);
hdr->duration_id = cpu_to_le16(dur);
if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
@ -719,7 +722,7 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
control->alt_retry_rate = NULL;
/* Use min(data rate, max base rate) as CTS/RTS rate */
rate = tx->u.tx.rate;
rate = tx->rate;
baserate = NULL;
for (idx = 0; idx < sband->n_bitrates; idx++) {
@ -741,12 +744,12 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
tx->sta->tx_packets++;
tx->sta->tx_fragments++;
tx->sta->tx_bytes += tx->skb->len;
if (tx->u.tx.extra_frag) {
if (tx->extra_frag) {
int i;
tx->sta->tx_fragments += tx->u.tx.num_extra_frag;
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
tx->sta->tx_fragments += tx->num_extra_frag;
for (i = 0; i < tx->num_extra_frag; i++) {
tx->sta->tx_bytes +=
tx->u.tx.extra_frag[i]->len;
tx->extra_frag[i]->len;
}
}
}
@ -755,13 +758,13 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
}
static ieee80211_tx_result
ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
ieee80211_tx_h_load_stats(struct ieee80211_tx_data *tx)
{
struct ieee80211_local *local = tx->local;
struct sk_buff *skb = tx->skb;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u32 load = 0, hdrtime;
struct ieee80211_rate *rate = tx->u.tx.rate;
struct ieee80211_rate *rate = tx->rate;
/* TODO: this could be part of tx_status handling, so that the number
* of retries would be known; TX rate should in that case be stored
@ -772,8 +775,8 @@ ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
* 1 usec = 1/8 * (1080 / 10) = 13.5 */
if (tx->u.tx.channel->band == IEEE80211_BAND_5GHZ ||
(tx->u.tx.channel->band == IEEE80211_BAND_2GHZ &&
if (tx->channel->band == IEEE80211_BAND_5GHZ ||
(tx->channel->band == IEEE80211_BAND_2GHZ &&
rate->flags & IEEE80211_RATE_ERP_G))
hdrtime = CHAN_UTIL_HDR_SHORT;
else
@ -783,20 +786,20 @@ ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
if (!is_multicast_ether_addr(hdr->addr1))
load += hdrtime;
if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
if (tx->control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
load += 2 * hdrtime;
else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
else if (tx->control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
load += hdrtime;
/* TODO: optimise again */
load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
if (tx->u.tx.extra_frag) {
if (tx->extra_frag) {
int i;
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
for (i = 0; i < tx->num_extra_frag; i++) {
load += 2 * hdrtime;
load += tx->u.tx.extra_frag[i]->len *
tx->u.tx.rate->bitrate;
load += tx->extra_frag[i]->len *
tx->rate->bitrate;
}
}
@ -811,7 +814,7 @@ ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
}
typedef ieee80211_tx_result (*ieee80211_tx_handler)(struct ieee80211_txrx_data *);
typedef ieee80211_tx_result (*ieee80211_tx_handler)(struct ieee80211_tx_data *);
static ieee80211_tx_handler ieee80211_tx_handlers[] =
{
ieee80211_tx_h_check_assoc,
@ -834,7 +837,7 @@ static ieee80211_tx_handler ieee80211_tx_handlers[] =
* with Radiotap Header -- only called for monitor mode interface
*/
static ieee80211_tx_result
__ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
__ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
struct sk_buff *skb)
{
/*
@ -850,13 +853,13 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_supported_band *sband;
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
struct ieee80211_tx_control *control = tx->u.tx.control;
struct ieee80211_tx_control *control = tx->control;
sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
tx->flags |= IEEE80211_TXRXD_TX_INJECTED;
tx->flags &= ~IEEE80211_TXRXD_FRAGMENTED;
tx->flags |= IEEE80211_TX_INJECTED;
tx->flags &= ~IEEE80211_TX_FRAGMENTED;
/*
* for every radiotap entry that is present
@ -892,7 +895,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
r = &sband->bitrates[i];
if (r->bitrate == target_rate) {
tx->u.tx.rate = r;
tx->rate = r;
break;
}
}
@ -930,7 +933,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
control->flags &=
~IEEE80211_TXCTL_DO_NOT_ENCRYPT;
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG)
tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
tx->flags |= IEEE80211_TX_FRAGMENTED;
break;
/*
@ -961,7 +964,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
* initialises @tx
*/
static ieee80211_tx_result
__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
__ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
struct sk_buff *skb,
struct net_device *dev,
struct ieee80211_tx_control *control)
@ -977,12 +980,12 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
tx->dev = dev; /* use original interface */
tx->local = local;
tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
tx->u.tx.control = control;
tx->control = control;
/*
* Set this flag (used below to indicate "automatic fragmentation"),
* it will be cleared/left by radiotap as desired.
*/
tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
tx->flags |= IEEE80211_TX_FRAGMENTED;
/* process and remove the injection radiotap header */
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@ -1003,20 +1006,20 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
tx->fc = le16_to_cpu(hdr->frame_control);
if (is_multicast_ether_addr(hdr->addr1)) {
tx->flags &= ~IEEE80211_TXRXD_TXUNICAST;
tx->flags &= ~IEEE80211_TX_UNICAST;
control->flags |= IEEE80211_TXCTL_NO_ACK;
} else {
tx->flags |= IEEE80211_TXRXD_TXUNICAST;
tx->flags |= IEEE80211_TX_UNICAST;
control->flags &= ~IEEE80211_TXCTL_NO_ACK;
}
if (tx->flags & IEEE80211_TXRXD_FRAGMENTED) {
if ((tx->flags & IEEE80211_TXRXD_TXUNICAST) &&
if (tx->flags & IEEE80211_TX_FRAGMENTED) {
if ((tx->flags & IEEE80211_TX_UNICAST) &&
skb->len + FCS_LEN > local->fragmentation_threshold &&
!local->ops->set_frag_threshold)
tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
tx->flags |= IEEE80211_TX_FRAGMENTED;
else
tx->flags &= ~IEEE80211_TXRXD_FRAGMENTED;
tx->flags &= ~IEEE80211_TX_FRAGMENTED;
}
if (!tx->sta)
@ -1039,7 +1042,7 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
/*
* NB: @tx is uninitialised when passed in here
*/
static int ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
static int ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
struct sk_buff *skb,
struct net_device *mdev,
struct ieee80211_tx_control *control)
@ -1062,9 +1065,9 @@ static int ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
}
static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_txrx_data *tx)
struct ieee80211_tx_data *tx)
{
struct ieee80211_tx_control *control = tx->u.tx.control;
struct ieee80211_tx_control *control = tx->control;
int ret, i;
if (!ieee80211_qdisc_installed(local->mdev) &&
@ -1081,20 +1084,20 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
local->mdev->trans_start = jiffies;
ieee80211_led_tx(local, 1);
}
if (tx->u.tx.extra_frag) {
if (tx->extra_frag) {
control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
IEEE80211_TXCTL_USE_CTS_PROTECT |
IEEE80211_TXCTL_CLEAR_PS_FILT |
IEEE80211_TXCTL_FIRST_FRAGMENT);
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
if (!tx->u.tx.extra_frag[i])
for (i = 0; i < tx->num_extra_frag; i++) {
if (!tx->extra_frag[i])
continue;
if (__ieee80211_queue_stopped(local, control->queue))
return IEEE80211_TX_FRAG_AGAIN;
if (i == tx->u.tx.num_extra_frag) {
control->tx_rate = tx->u.tx.last_frag_rate;
if (i == tx->num_extra_frag) {
control->tx_rate = tx->last_frag_rate;
if (tx->flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG)
if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
control->flags |=
IEEE80211_TXCTL_RATE_CTRL_PROBE;
else
@ -1104,18 +1107,18 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
"TX to low-level driver",
tx->u.tx.extra_frag[i]);
tx->extra_frag[i]);
ret = local->ops->tx(local_to_hw(local),
tx->u.tx.extra_frag[i],
tx->extra_frag[i],
control);
if (ret)
return IEEE80211_TX_FRAG_AGAIN;
local->mdev->trans_start = jiffies;
ieee80211_led_tx(local, 1);
tx->u.tx.extra_frag[i] = NULL;
tx->extra_frag[i] = NULL;
}
kfree(tx->u.tx.extra_frag);
tx->u.tx.extra_frag = NULL;
kfree(tx->extra_frag);
tx->extra_frag = NULL;
}
return IEEE80211_TX_OK;
}
@ -1126,7 +1129,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
ieee80211_tx_handler *handler;
struct ieee80211_txrx_data tx;
struct ieee80211_tx_data tx;
ieee80211_tx_result res = TX_DROP, res_prepare;
int ret, i;
@ -1137,22 +1140,19 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
return 0;
}
rcu_read_lock();
/* initialises tx */
res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
if (res_prepare == TX_DROP) {
dev_kfree_skb(skb);
rcu_read_unlock();
return 0;
}
/*
* key references are protected using RCU and this requires that
* we are in a read-site RCU section during receive processing
*/
rcu_read_lock();
sta = tx.sta;
tx.u.tx.channel = local->hw.conf.channel;
tx.channel = local->hw.conf.channel;
for (handler = ieee80211_tx_handlers; *handler != NULL;
handler++) {
@ -1163,9 +1163,6 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
skb = tx.skb; /* handlers are allowed to change skb */
if (sta)
sta_info_put(sta);
if (unlikely(res == TX_DROP)) {
I802_DEBUG_INC(local->tx_handlers_drop);
goto drop;
@ -1177,18 +1174,18 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
return 0;
}
if (tx.u.tx.extra_frag) {
for (i = 0; i < tx.u.tx.num_extra_frag; i++) {
if (tx.extra_frag) {
for (i = 0; i < tx.num_extra_frag; i++) {
int next_len, dur;
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *)
tx.u.tx.extra_frag[i]->data;
tx.extra_frag[i]->data;
if (i + 1 < tx.u.tx.num_extra_frag) {
next_len = tx.u.tx.extra_frag[i + 1]->len;
if (i + 1 < tx.num_extra_frag) {
next_len = tx.extra_frag[i + 1]->len;
} else {
next_len = 0;
tx.u.tx.rate = tx.u.tx.last_frag_rate;
tx.rate = tx.last_frag_rate;
}
dur = ieee80211_duration(&tx, 0, next_len);
hdr->duration_id = cpu_to_le16(dur);
@ -1223,11 +1220,11 @@ retry:
memcpy(&store->control, control,
sizeof(struct ieee80211_tx_control));
store->skb = skb;
store->extra_frag = tx.u.tx.extra_frag;
store->num_extra_frag = tx.u.tx.num_extra_frag;
store->last_frag_rate = tx.u.tx.last_frag_rate;
store->extra_frag = tx.extra_frag;
store->num_extra_frag = tx.num_extra_frag;
store->last_frag_rate = tx.last_frag_rate;
store->last_frag_rate_ctrl_probe =
!!(tx.flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG);
!!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
}
rcu_read_unlock();
return 0;
@ -1235,10 +1232,10 @@ retry:
drop:
if (skb)
dev_kfree_skb(skb);
for (i = 0; i < tx.u.tx.num_extra_frag; i++)
if (tx.u.tx.extra_frag[i])
dev_kfree_skb(tx.u.tx.extra_frag[i]);
kfree(tx.u.tx.extra_frag);
for (i = 0; i < tx.num_extra_frag; i++)
if (tx.extra_frag[i])
dev_kfree_skb(tx.extra_frag[i]);
kfree(tx.extra_frag);
rcu_read_unlock();
return 0;
}
@ -1384,8 +1381,9 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
struct ieee80211_tx_packet_data *pkt_data;
struct ieee80211_sub_if_data *sdata;
int ret = 1, head_need;
u16 ethertype, hdrlen, fc;
u16 ethertype, hdrlen, meshhdrlen = 0, fc;
struct ieee80211_hdr hdr;
struct ieee80211s_hdr mesh_hdr;
const u8 *encaps_data;
int encaps_len, skip_header_bytes;
int nh_pos, h_pos;
@ -1427,6 +1425,37 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 30;
break;
#ifdef CONFIG_MAC80211_MESH
case IEEE80211_IF_TYPE_MESH_POINT:
fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
/* RA TA DA SA */
if (is_multicast_ether_addr(skb->data))
memcpy(hdr.addr1, skb->data, ETH_ALEN);
else if (mesh_nexthop_lookup(hdr.addr1, skb, dev))
return 0;
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
memcpy(hdr.addr3, skb->data, ETH_ALEN);
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
if (skb->pkt_type == PACKET_OTHERHOST) {
/* Forwarded frame, keep mesh ttl and seqnum */
struct ieee80211s_hdr *prev_meshhdr;
prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb);
meshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr);
memcpy(&mesh_hdr, prev_meshhdr, meshhdrlen);
sdata->u.sta.mshstats.fwded_frames++;
} else {
if (!sdata->u.sta.mshcfg.dot11MeshTTL) {
/* Do not send frames with mesh_ttl == 0 */
sdata->u.sta.mshstats.dropped_frames_ttl++;
ret = 0;
goto fail;
}
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
sdata);
}
hdrlen = 30;
break;
#endif
case IEEE80211_IF_TYPE_STA:
fc |= IEEE80211_FCTL_TODS;
/* BSSID SA DA */
@ -1453,11 +1482,11 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
* in AP mode)
*/
if (!is_multicast_ether_addr(hdr.addr1)) {
rcu_read_lock();
sta = sta_info_get(local, hdr.addr1);
if (sta) {
if (sta)
sta_flags = sta->flags;
sta_info_put(sta);
}
rcu_read_unlock();
}
/* receiver is QoS enabled, use a QoS type frame */
@ -1471,8 +1500,8 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
* EAPOL frames from the local station.
*/
if (unlikely(!is_multicast_ether_addr(hdr.addr1) &&
!(sta_flags & WLAN_STA_AUTHORIZED) &&
!(ethertype == ETH_P_PAE &&
!(sta_flags & WLAN_STA_AUTHORIZED) &&
!(ethertype == ETH_P_PAE &&
compare_ether_addr(dev->dev_addr,
skb->data + ETH_ALEN) == 0))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@ -1525,7 +1554,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
* build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
* alloc_skb() (net/core/skbuff.c)
*/
head_need = hdrlen + encaps_len + local->tx_headroom;
head_need = hdrlen + encaps_len + meshhdrlen + local->tx_headroom;
head_need -= skb_headroom(skb);
/* We are going to modify skb data, so make a copy of it if happens to
@ -1559,6 +1588,12 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
h_pos += encaps_len;
}
if (meshhdrlen > 0) {
memcpy(skb_push(skb, meshhdrlen), &mesh_hdr, meshhdrlen);
nh_pos += meshhdrlen;
h_pos += meshhdrlen;
}
if (fc & IEEE80211_STYPE_QOS_DATA) {
__le16 *qos_control;
@ -1628,7 +1663,7 @@ void ieee80211_tx_pending(unsigned long data)
struct ieee80211_local *local = (struct ieee80211_local *)data;
struct net_device *dev = local->mdev;
struct ieee80211_tx_stored_packet *store;
struct ieee80211_txrx_data tx;
struct ieee80211_tx_data tx;
int i, ret, reschedule = 0;
netif_tx_lock_bh(dev);
@ -1640,13 +1675,13 @@ void ieee80211_tx_pending(unsigned long data)
continue;
}
store = &local->pending_packet[i];
tx.u.tx.control = &store->control;
tx.u.tx.extra_frag = store->extra_frag;
tx.u.tx.num_extra_frag = store->num_extra_frag;
tx.u.tx.last_frag_rate = store->last_frag_rate;
tx.control = &store->control;
tx.extra_frag = store->extra_frag;
tx.num_extra_frag = store->num_extra_frag;
tx.last_frag_rate = store->last_frag_rate;
tx.flags = 0;
if (store->last_frag_rate_ctrl_probe)
tx.flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
tx.flags |= IEEE80211_TX_PROBE_LAST_FRAG;
ret = __ieee80211_tx(local, store->skb, &tx);
if (ret) {
if (ret == IEEE80211_TX_FRAG_AGAIN)
@ -1680,7 +1715,6 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
/* Generate bitmap for TIM only if there are any STAs in power save
* mode. */
read_lock_bh(&local->sta_lock);
if (atomic_read(&bss->num_sta_ps) > 0)
/* in the hope that this is faster than
* checking byte-for-byte */
@ -1731,7 +1765,6 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
*pos++ = aid0; /* Bitmap control */
*pos++ = 0; /* Part Virt Bitmap */
}
read_unlock_bh(&local->sta_lock);
}
struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
@ -1746,6 +1779,10 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
struct rate_selection rsel;
struct beacon_data *beacon;
struct ieee80211_supported_band *sband;
struct ieee80211_mgmt *mgmt;
int *num_beacons;
bool err = true;
u8 *pos;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
@ -1753,11 +1790,84 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
sdata = vif_to_sdata(vif);
bdev = sdata->dev;
ap = &sdata->u.ap;
beacon = rcu_dereference(ap->beacon);
if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
ap = &sdata->u.ap;
beacon = rcu_dereference(ap->beacon);
if (ap && beacon) {
/*
* headroom, head length,
* tail length and maximum TIM length
*/
skb = dev_alloc_skb(local->tx_headroom +
beacon->head_len +
beacon->tail_len + 256);
if (!skb)
goto out;
if (!ap || sdata->vif.type != IEEE80211_IF_TYPE_AP || !beacon) {
skb_reserve(skb, local->tx_headroom);
memcpy(skb_put(skb, beacon->head_len), beacon->head,
beacon->head_len);
ieee80211_include_sequence(sdata,
(struct ieee80211_hdr *)skb->data);
/*
* Not very nice, but we want to allow the driver to call
* ieee80211_beacon_get() as a response to the set_tim()
* callback. That, however, is already invoked under the
* sta_lock to guarantee consistent and race-free update
* of the tim bitmap in mac80211 and the driver.
*/
if (local->tim_in_locked_section) {
ieee80211_beacon_add_tim(local, ap, skb, beacon);
} else {
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
ieee80211_beacon_add_tim(local, ap, skb, beacon);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
if (beacon->tail)
memcpy(skb_put(skb, beacon->tail_len),
beacon->tail, beacon->tail_len);
num_beacons = &ap->num_beacons;
err = false;
}
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
/* headroom, head length, tail length and maximum TIM length */
skb = dev_alloc_skb(local->tx_headroom + 400);
if (!skb)
goto out;
skb_reserve(skb, local->hw.extra_tx_headroom);
mgmt = (struct ieee80211_mgmt *)
skb_put(skb, 24 + sizeof(mgmt->u.beacon));
memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
IEEE80211_STYPE_BEACON);
memset(mgmt->da, 0xff, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.beacon.beacon_int =
cpu_to_le16(local->hw.conf.beacon_int);
mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */
pos = skb_put(skb, 2);
*pos++ = WLAN_EID_SSID;
*pos++ = 0x0;
mesh_mgmt_ies_add(skb, sdata->dev);
num_beacons = &sdata->u.sta.num_beacons;
err = false;
}
if (err) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "no beacon data avail for %s\n",
@ -1767,24 +1877,6 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
goto out;
}
/* headroom, head length, tail length and maximum TIM length */
skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
beacon->tail_len + 256);
if (!skb)
goto out;
skb_reserve(skb, local->tx_headroom);
memcpy(skb_put(skb, beacon->head_len), beacon->head,
beacon->head_len);
ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
ieee80211_beacon_add_tim(local, ap, skb, beacon);
if (beacon->tail)
memcpy(skb_put(skb, beacon->tail_len), beacon->tail,
beacon->tail_len);
if (control) {
rate_control_get_rate(local->mdev, sband, skb, &rsel);
if (!rsel.rate) {
@ -1808,10 +1900,8 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
control->retry_limit = 1;
control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT;
}
ap->num_beacons++;
out:
(*num_beacons)++;
out:
rcu_read_unlock();
return skb;
}
@ -1859,7 +1949,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
struct sk_buff *skb;
struct sta_info *sta;
ieee80211_tx_handler *handler;
struct ieee80211_txrx_data tx;
struct ieee80211_tx_data tx;
ieee80211_tx_result res = TX_DROP;
struct net_device *bdev;
struct ieee80211_sub_if_data *sdata;
@ -1881,7 +1971,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
rcu_read_unlock();
return NULL;
}
rcu_read_unlock();
if (bss->dtim_count != 0)
return NULL; /* send buffered bc/mc only after DTIM beacon */
@ -1907,8 +1996,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
dev_kfree_skb_any(skb);
}
sta = tx.sta;
tx.flags |= IEEE80211_TXRXD_TXPS_BUFFERED;
tx.u.tx.channel = local->hw.conf.channel;
tx.flags |= IEEE80211_TX_PS_BUFFERED;
tx.channel = local->hw.conf.channel;
for (handler = ieee80211_tx_handlers; *handler != NULL; handler++) {
res = (*handler)(&tx);
@ -1926,8 +2015,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
skb = NULL;
}
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return skb;
}

View file

@ -26,6 +26,7 @@
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
#include "mesh.h"
#include "wme.h"
/* privid for wiphys to determine whether they belong to us or not */
@ -146,17 +147,35 @@ int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
}
EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx)
int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
{
int ae = meshhdr->flags & IEEE80211S_FLAGS_AE;
/* 7.1.3.5a.2 */
switch (ae) {
case 0:
return 5;
case 1:
return 11;
case 2:
return 17;
case 3:
return 23;
default:
return 5;
}
}
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
if (tx->u.tx.extra_frag) {
if (tx->extra_frag) {
struct ieee80211_hdr *fhdr;
int i;
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
for (i = 0; i < tx->num_extra_frag; i++) {
fhdr = (struct ieee80211_hdr *)
tx->u.tx.extra_frag[i]->data;
tx->extra_frag[i]->data;
fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
}
}
@ -382,6 +401,7 @@ void ieee80211_iterate_active_interfaces(
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
case IEEE80211_IF_TYPE_WDS:
case IEEE80211_IF_TYPE_MESH_POINT:
break;
}
if (sdata->dev == local->mdev)

View file

@ -306,14 +306,14 @@ u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
}
ieee80211_rx_result
ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx)
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
{
if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))
return RX_CONTINUE;
if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) {
if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
#ifdef CONFIG_MAC80211_DEBUG
if (net_ratelimit())
@ -322,7 +322,7 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx)
#endif /* CONFIG_MAC80211_DEBUG */
return RX_DROP_UNUSABLE;
}
} else if (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED)) {
} else if (!(rx->status->flag & RX_FLAG_IV_STRIPPED)) {
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
/* remove ICV */
skb_trim(rx->skb, rx->skb->len - 4);
@ -331,13 +331,13 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx)
return RX_CONTINUE;
}
static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
{
if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
return -1;
} else {
tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
tx->control->key_idx = tx->key->conf.hw_key_idx;
if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) {
if (!ieee80211_wep_add_iv(tx->local, skb, tx->key))
return -1;
@ -347,21 +347,21 @@ static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
}
ieee80211_tx_result
ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx)
ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
{
tx->u.tx.control->iv_len = WEP_IV_LEN;
tx->u.tx.control->icv_len = WEP_ICV_LEN;
ieee80211_tx_set_iswep(tx);
tx->control->iv_len = WEP_IV_LEN;
tx->control->icv_len = WEP_ICV_LEN;
ieee80211_tx_set_protected(tx);
if (wep_encrypt_skb(tx, tx->skb) < 0) {
I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
return TX_DROP;
}
if (tx->u.tx.extra_frag) {
if (tx->extra_frag) {
int i;
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) {
for (i = 0; i < tx->num_extra_frag; i++) {
if (wep_encrypt_skb(tx, tx->extra_frag[i]) < 0) {
I802_DEBUG_INC(tx->local->
tx_handlers_drop_wep);
return TX_DROP;

View file

@ -29,8 +29,8 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
ieee80211_rx_result
ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx);
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx);
ieee80211_tx_result
ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx);
ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx);
#endif /* WEP_H */

Some files were not shown because too many files have changed in this diff Show more