Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/linville/wireless-2.6.26
This commit is contained in:
commit
ba73d4c84a
|
@ -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):
|
||||
|
|
|
@ -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>
|
|
@ -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 *);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(¶ms, 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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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. */
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__ */
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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__ */
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
@ -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__ */
|
|
@ -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)
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
|
@ -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_ */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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()
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue