mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-18 15:44:02 +08:00
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem
Conflicts: drivers/net/wireless/libertas/if_sdio.c
This commit is contained in:
commit
e569aa78ba
496
Documentation/DocBook/80211.tmpl
Normal file
496
Documentation/DocBook/80211.tmpl
Normal file
@ -0,0 +1,496 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE set PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
|
||||
<set>
|
||||
<setinfo>
|
||||
<title>The 802.11 subsystems – for kernel developers</title>
|
||||
<subtitle>
|
||||
Explaining wireless 802.11 networking in the Linux kernel
|
||||
</subtitle>
|
||||
|
||||
<copyright>
|
||||
<year>2007-2009</year>
|
||||
<holder>Johannes Berg</holder>
|
||||
</copyright>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Johannes</firstname>
|
||||
<surname>Berg</surname>
|
||||
<affiliation>
|
||||
<address><email>johannes@sipsolutions.net</email></address>
|
||||
</affiliation>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<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>
|
||||
<para>
|
||||
These books attempt to give a description of the
|
||||
various subsystems that play a role in 802.11 wireless
|
||||
networking in Linux. Since these books are for kernel
|
||||
developers they attempts to document the structures
|
||||
and functions used in the kernel as well as giving a
|
||||
higher-level overview.
|
||||
</para>
|
||||
<para>
|
||||
The reader is expected to be familiar with the 802.11
|
||||
standard as published by the IEEE in 802.11-2007 (or
|
||||
possibly later versions). References to this standard
|
||||
will be given as "802.11-2007 8.1.5".
|
||||
</para>
|
||||
</abstract>
|
||||
</setinfo>
|
||||
<book id="cfg80211-developers-guide">
|
||||
!Ainclude/net/cfg80211.h
|
||||
<bookinfo>
|
||||
<title>The cfg80211 subsystem</title>
|
||||
|
||||
<abstract>
|
||||
!Pinclude/net/cfg80211.h Introduction
|
||||
</abstract>
|
||||
</bookinfo>
|
||||
<chapter>
|
||||
<title>Device registration</title>
|
||||
!Pinclude/net/cfg80211.h Device registration
|
||||
!Finclude/net/cfg80211.h ieee80211_band
|
||||
!Finclude/net/cfg80211.h ieee80211_channel_flags
|
||||
!Finclude/net/cfg80211.h ieee80211_channel
|
||||
!Finclude/net/cfg80211.h ieee80211_rate_flags
|
||||
!Finclude/net/cfg80211.h ieee80211_rate
|
||||
!Finclude/net/cfg80211.h ieee80211_sta_ht_cap
|
||||
!Finclude/net/cfg80211.h ieee80211_supported_band
|
||||
!Finclude/net/cfg80211.h cfg80211_signal_type
|
||||
!Finclude/net/cfg80211.h wiphy_params_flags
|
||||
!Finclude/net/cfg80211.h wiphy_flags
|
||||
!Finclude/net/cfg80211.h wiphy
|
||||
!Finclude/net/cfg80211.h wireless_dev
|
||||
!Finclude/net/cfg80211.h wiphy_new
|
||||
!Finclude/net/cfg80211.h wiphy_register
|
||||
!Finclude/net/cfg80211.h wiphy_unregister
|
||||
!Finclude/net/cfg80211.h wiphy_free
|
||||
|
||||
!Finclude/net/cfg80211.h wiphy_name
|
||||
!Finclude/net/cfg80211.h wiphy_dev
|
||||
!Finclude/net/cfg80211.h wiphy_priv
|
||||
!Finclude/net/cfg80211.h priv_to_wiphy
|
||||
!Finclude/net/cfg80211.h set_wiphy_dev
|
||||
!Finclude/net/cfg80211.h wdev_priv
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Actions and configuration</title>
|
||||
!Pinclude/net/cfg80211.h Actions and configuration
|
||||
!Finclude/net/cfg80211.h cfg80211_ops
|
||||
!Finclude/net/cfg80211.h vif_params
|
||||
!Finclude/net/cfg80211.h key_params
|
||||
!Finclude/net/cfg80211.h survey_info_flags
|
||||
!Finclude/net/cfg80211.h survey_info
|
||||
!Finclude/net/cfg80211.h beacon_parameters
|
||||
!Finclude/net/cfg80211.h plink_actions
|
||||
!Finclude/net/cfg80211.h station_parameters
|
||||
!Finclude/net/cfg80211.h station_info_flags
|
||||
!Finclude/net/cfg80211.h rate_info_flags
|
||||
!Finclude/net/cfg80211.h rate_info
|
||||
!Finclude/net/cfg80211.h station_info
|
||||
!Finclude/net/cfg80211.h monitor_flags
|
||||
!Finclude/net/cfg80211.h mpath_info_flags
|
||||
!Finclude/net/cfg80211.h mpath_info
|
||||
!Finclude/net/cfg80211.h bss_parameters
|
||||
!Finclude/net/cfg80211.h ieee80211_txq_params
|
||||
!Finclude/net/cfg80211.h cfg80211_crypto_settings
|
||||
!Finclude/net/cfg80211.h cfg80211_auth_request
|
||||
!Finclude/net/cfg80211.h cfg80211_assoc_request
|
||||
!Finclude/net/cfg80211.h cfg80211_deauth_request
|
||||
!Finclude/net/cfg80211.h cfg80211_disassoc_request
|
||||
!Finclude/net/cfg80211.h cfg80211_ibss_params
|
||||
!Finclude/net/cfg80211.h cfg80211_connect_params
|
||||
!Finclude/net/cfg80211.h cfg80211_pmksa
|
||||
!Finclude/net/cfg80211.h cfg80211_send_rx_auth
|
||||
!Finclude/net/cfg80211.h cfg80211_send_auth_timeout
|
||||
!Finclude/net/cfg80211.h __cfg80211_auth_canceled
|
||||
!Finclude/net/cfg80211.h cfg80211_send_rx_assoc
|
||||
!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout
|
||||
!Finclude/net/cfg80211.h cfg80211_send_deauth
|
||||
!Finclude/net/cfg80211.h __cfg80211_send_deauth
|
||||
!Finclude/net/cfg80211.h cfg80211_send_disassoc
|
||||
!Finclude/net/cfg80211.h __cfg80211_send_disassoc
|
||||
!Finclude/net/cfg80211.h cfg80211_ibss_joined
|
||||
!Finclude/net/cfg80211.h cfg80211_connect_result
|
||||
!Finclude/net/cfg80211.h cfg80211_roamed
|
||||
!Finclude/net/cfg80211.h cfg80211_disconnected
|
||||
!Finclude/net/cfg80211.h cfg80211_ready_on_channel
|
||||
!Finclude/net/cfg80211.h cfg80211_remain_on_channel_expired
|
||||
!Finclude/net/cfg80211.h cfg80211_new_sta
|
||||
!Finclude/net/cfg80211.h cfg80211_rx_mgmt
|
||||
!Finclude/net/cfg80211.h cfg80211_mgmt_tx_status
|
||||
!Finclude/net/cfg80211.h cfg80211_cqm_rssi_notify
|
||||
!Finclude/net/cfg80211.h cfg80211_michael_mic_failure
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Scanning and BSS list handling</title>
|
||||
!Pinclude/net/cfg80211.h Scanning and BSS list handling
|
||||
!Finclude/net/cfg80211.h cfg80211_ssid
|
||||
!Finclude/net/cfg80211.h cfg80211_scan_request
|
||||
!Finclude/net/cfg80211.h cfg80211_scan_done
|
||||
!Finclude/net/cfg80211.h cfg80211_bss
|
||||
!Finclude/net/cfg80211.h cfg80211_inform_bss_frame
|
||||
!Finclude/net/cfg80211.h cfg80211_inform_bss
|
||||
!Finclude/net/cfg80211.h cfg80211_unlink_bss
|
||||
!Finclude/net/cfg80211.h cfg80211_find_ie
|
||||
!Finclude/net/cfg80211.h ieee80211_bss_get_ie
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Utility functions</title>
|
||||
!Pinclude/net/cfg80211.h Utility functions
|
||||
!Finclude/net/cfg80211.h ieee80211_channel_to_frequency
|
||||
!Finclude/net/cfg80211.h ieee80211_frequency_to_channel
|
||||
!Finclude/net/cfg80211.h ieee80211_get_channel
|
||||
!Finclude/net/cfg80211.h ieee80211_get_response_rate
|
||||
!Finclude/net/cfg80211.h ieee80211_hdrlen
|
||||
!Finclude/net/cfg80211.h ieee80211_get_hdrlen_from_skb
|
||||
!Finclude/net/cfg80211.h ieee80211_radiotap_iterator
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Data path helpers</title>
|
||||
!Pinclude/net/cfg80211.h Data path helpers
|
||||
!Finclude/net/cfg80211.h ieee80211_data_to_8023
|
||||
!Finclude/net/cfg80211.h ieee80211_data_from_8023
|
||||
!Finclude/net/cfg80211.h ieee80211_amsdu_to_8023s
|
||||
!Finclude/net/cfg80211.h cfg80211_classify8021d
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Regulatory enforcement infrastructure</title>
|
||||
!Pinclude/net/cfg80211.h Regulatory enforcement infrastructure
|
||||
!Finclude/net/cfg80211.h regulatory_hint
|
||||
!Finclude/net/cfg80211.h wiphy_apply_custom_regulatory
|
||||
!Finclude/net/cfg80211.h freq_reg_info
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>RFkill integration</title>
|
||||
!Pinclude/net/cfg80211.h RFkill integration
|
||||
!Finclude/net/cfg80211.h wiphy_rfkill_set_hw_state
|
||||
!Finclude/net/cfg80211.h wiphy_rfkill_start_polling
|
||||
!Finclude/net/cfg80211.h wiphy_rfkill_stop_polling
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Test mode</title>
|
||||
!Pinclude/net/cfg80211.h Test mode
|
||||
!Finclude/net/cfg80211.h cfg80211_testmode_alloc_reply_skb
|
||||
!Finclude/net/cfg80211.h cfg80211_testmode_reply
|
||||
!Finclude/net/cfg80211.h cfg80211_testmode_alloc_event_skb
|
||||
!Finclude/net/cfg80211.h cfg80211_testmode_event
|
||||
</chapter>
|
||||
</book>
|
||||
<book id="mac80211-developers-guide">
|
||||
<bookinfo>
|
||||
<title>The mac80211 subsystem</title>
|
||||
<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_vif
|
||||
</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>Packet alignment</title>
|
||||
!Pnet/mac80211/rx.c Packet alignment
|
||||
</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_info
|
||||
!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_wake_queue
|
||||
!Finclude/net/mac80211.h ieee80211_stop_queue
|
||||
!Finclude/net/mac80211.h ieee80211_wake_queues
|
||||
!Finclude/net/mac80211.h ieee80211_stop_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_flags
|
||||
</chapter>
|
||||
|
||||
<chapter id="powersave">
|
||||
<title>Powersave support</title>
|
||||
!Pinclude/net/mac80211.h Powersave support
|
||||
</chapter>
|
||||
|
||||
<chapter id="beacon-filter">
|
||||
<title>Beacon filter support</title>
|
||||
!Pinclude/net/mac80211.h Beacon filter support
|
||||
!Finclude/net/mac80211.h ieee80211_beacon_loss
|
||||
</chapter>
|
||||
|
||||
<chapter id="qos">
|
||||
<title>Multiple queues and QoS support</title>
|
||||
<para>TBD</para>
|
||||
!Finclude/net/mac80211.h ieee80211_tx_queue_params
|
||||
</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>
|
||||
</set>
|
@ -12,7 +12,7 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
|
||||
kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
|
||||
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
|
||||
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
|
||||
mac80211.xml debugobjects.xml sh.xml regulator.xml \
|
||||
80211.xml debugobjects.xml sh.xml regulator.xml \
|
||||
alsa-driver-api.xml writing-an-alsa-driver.xml \
|
||||
tracepoint.xml media.xml drm.xml
|
||||
|
||||
|
@ -1,337 +0,0 @@
|
||||
<?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-2009</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_vif
|
||||
</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>Packet alignment</title>
|
||||
!Pnet/mac80211/rx.c Packet alignment
|
||||
</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_info
|
||||
!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_wake_queue
|
||||
!Finclude/net/mac80211.h ieee80211_stop_queue
|
||||
!Finclude/net/mac80211.h ieee80211_wake_queues
|
||||
!Finclude/net/mac80211.h ieee80211_stop_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="powersave">
|
||||
<title>Powersave support</title>
|
||||
!Pinclude/net/mac80211.h Powersave support
|
||||
</chapter>
|
||||
|
||||
<chapter id="beacon-filter">
|
||||
<title>Beacon filter support</title>
|
||||
!Pinclude/net/mac80211.h Beacon filter support
|
||||
!Finclude/net/mac80211.h ieee80211_beacon_loss
|
||||
</chapter>
|
||||
|
||||
<chapter id="qos">
|
||||
<title>Multiple queues and QoS support</title>
|
||||
<para>TBD</para>
|
||||
!Finclude/net/mac80211.h ieee80211_tx_queue_params
|
||||
</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>
|
@ -2061,11 +2061,12 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
|
||||
int i;
|
||||
|
||||
at76_dbg(DBG_MAC80211, "%s(): cmd %d key->alg %d key->keyidx %d "
|
||||
at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d "
|
||||
"key->keylen %d",
|
||||
__func__, cmd, key->alg, key->keyidx, key->keylen);
|
||||
__func__, cmd, key->cipher, key->keyidx, key->keylen);
|
||||
|
||||
if (key->alg != ALG_WEP)
|
||||
if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) &&
|
||||
(key->cipher != WLAN_CIPHER_SUITE_WEP104))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
key->hw_key_idx = key->keyidx;
|
||||
|
@ -1190,14 +1190,13 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
|
||||
if (info->control.hw_key) {
|
||||
icv = info->control.hw_key->icv_len;
|
||||
|
||||
switch (info->control.hw_key->alg) {
|
||||
case ALG_WEP:
|
||||
switch (info->control.hw_key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
keytype = AR9170_TX_MAC_ENCR_RC4;
|
||||
break;
|
||||
case ALG_TKIP:
|
||||
keytype = AR9170_TX_MAC_ENCR_RC4;
|
||||
break;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
keytype = AR9170_TX_MAC_ENCR_AES;
|
||||
break;
|
||||
default:
|
||||
@ -1778,17 +1777,17 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
if ((!ar->vif) || (ar->disable_offload))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
if (key->keylen == WLAN_KEY_LEN_WEP40)
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
ktype = AR9170_ENC_ALG_WEP64;
|
||||
else
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
ktype = AR9170_ENC_ALG_WEP128;
|
||||
break;
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
ktype = AR9170_ENC_ALG_TKIP;
|
||||
break;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
ktype = AR9170_ENC_ALG_AESCCMP;
|
||||
break;
|
||||
default:
|
||||
@ -1827,7 +1826,7 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (key->alg == ALG_TKIP) {
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
|
||||
err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL,
|
||||
ktype, 1, key->key + 16, 16);
|
||||
if (err)
|
||||
@ -1864,7 +1863,7 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (key->alg == ALG_TKIP) {
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
|
||||
err = ar9170_upload_key(ar, key->hw_key_idx,
|
||||
NULL,
|
||||
AR9170_ENC_ALG_NONE, 1,
|
||||
|
@ -552,9 +552,9 @@ ath5k_ani_mib_intr(struct ath5k_hw *ah)
|
||||
if (ah->ah_sc->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
|
||||
return;
|
||||
|
||||
/* if one of the errors triggered, we can get a superfluous second
|
||||
* interrupt, even though we have already reset the register. the
|
||||
* function detects that so we can return early */
|
||||
/* If one of the errors triggered, we can get a superfluous second
|
||||
* interrupt, even though we have already reset the register. The
|
||||
* function detects that so we can return early. */
|
||||
if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0)
|
||||
return;
|
||||
|
||||
|
@ -175,7 +175,7 @@
|
||||
#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0
|
||||
#define AR5K_TUNE_RADAR_ALERT false
|
||||
#define AR5K_TUNE_MIN_TX_FIFO_THRES 1
|
||||
#define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_LEN / 64) + 1)
|
||||
#define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_FRAME_LEN / 64) + 1)
|
||||
#define AR5K_TUNE_REGISTER_TIMEOUT 20000
|
||||
/* Register for RSSI threshold has a mask of 0xff, so 255 seems to
|
||||
* be the max value. */
|
||||
@ -343,9 +343,6 @@ struct ath5k_srev_name {
|
||||
#define AR5K_SREV_PHY_5413 0x61
|
||||
#define AR5K_SREV_PHY_2425 0x70
|
||||
|
||||
/* IEEE defs */
|
||||
#define IEEE80211_MAX_LEN 2500
|
||||
|
||||
/* TODO add support to mac80211 for vendor-specific rates and modes */
|
||||
|
||||
/*
|
||||
@ -1190,7 +1187,7 @@ extern int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype opmode);
|
||||
void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class);
|
||||
/* BSSID Functions */
|
||||
int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac);
|
||||
void ath5k_hw_set_associd(struct ath5k_hw *ah);
|
||||
void ath5k_hw_set_bssid(struct ath5k_hw *ah);
|
||||
void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask);
|
||||
/* Receive start/stop functions */
|
||||
void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah);
|
||||
|
@ -144,7 +144,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
/* Bring device out of sleep and reset it's units */
|
||||
/* Bring device out of sleep and reset its units */
|
||||
ret = ath5k_hw_nic_wakeup(ah, 0, true);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
@ -158,7 +158,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
|
||||
CHANNEL_5GHZ);
|
||||
ah->ah_phy = AR5K_PHY(0);
|
||||
|
||||
/* Try to identify radio chip based on it's srev */
|
||||
/* Try to identify radio chip based on its srev */
|
||||
switch (ah->ah_radio_5ghz_revision & 0xf0) {
|
||||
case AR5K_SREV_RAD_5111:
|
||||
ah->ah_radio = AR5K_RF5111;
|
||||
@ -329,7 +329,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
|
||||
|
||||
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
|
||||
memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN);
|
||||
ath5k_hw_set_associd(ah);
|
||||
ath5k_hw_set_bssid(ah);
|
||||
ath5k_hw_set_opmode(ah, sc->opmode);
|
||||
|
||||
ath5k_hw_rfgain_opt_init(ah);
|
||||
|
@ -612,7 +612,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/*If we passed the test malloc a ath5k_hw struct*/
|
||||
/* If we passed the test, malloc an ath5k_hw struct */
|
||||
sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
|
||||
if (!sc->ah) {
|
||||
ret = -ENOMEM;
|
||||
@ -786,8 +786,8 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
|
||||
/*
|
||||
* Check if the MAC has multi-rate retry support.
|
||||
* We do this by trying to setup a fake extended
|
||||
* descriptor. MAC's that don't have support will
|
||||
* return false w/o doing anything. MAC's that do
|
||||
* descriptor. MACs that don't have support will
|
||||
* return false w/o doing anything. MACs that do
|
||||
* support it will return true w/o doing anything.
|
||||
*/
|
||||
ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
|
||||
@ -827,7 +827,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
|
||||
/*
|
||||
* Allocate hardware transmit queues: one queue for
|
||||
* beacon frames and one data queue for each QoS
|
||||
* priority. Note that hw functions handle reseting
|
||||
* priority. Note that hw functions handle resetting
|
||||
* these queues at the needed time.
|
||||
*/
|
||||
ret = ath5k_beaconq_setup(ah);
|
||||
@ -909,7 +909,7 @@ ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
|
||||
/*
|
||||
* NB: the order of these is important:
|
||||
* o call the 802.11 layer before detaching ath5k_hw to
|
||||
* insure callbacks into the driver to delete global
|
||||
* ensure callbacks into the driver to delete global
|
||||
* key cache entries can be handled
|
||||
* o reclaim the tx queue data structures after calling
|
||||
* the 802.11 layer as we'll get called back to reclaim
|
||||
@ -1514,7 +1514,7 @@ ath5k_txq_setup(struct ath5k_softc *sc,
|
||||
/*
|
||||
* Enable interrupts only for EOL and DESC conditions.
|
||||
* We mark tx descriptors to receive a DESC interrupt
|
||||
* when a tx queue gets deep; otherwise waiting for the
|
||||
* when a tx queue gets deep; otherwise we wait for the
|
||||
* EOL to reap descriptors. Note that this is done to
|
||||
* reduce interrupt load and this only defers reaping
|
||||
* descriptors, never transmitting frames. Aside from
|
||||
@ -1709,7 +1709,7 @@ ath5k_rx_start(struct ath5k_softc *sc)
|
||||
struct ath5k_buf *bf;
|
||||
int ret;
|
||||
|
||||
common->rx_bufsize = roundup(IEEE80211_MAX_LEN, common->cachelsz);
|
||||
common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
|
||||
|
||||
ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
|
||||
common->cachelsz, common->rx_bufsize);
|
||||
@ -1859,7 +1859,7 @@ ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute padding position. skb must contains an IEEE 802.11 frame
|
||||
* Compute padding position. skb must contain an IEEE 802.11 frame
|
||||
*/
|
||||
static int ath5k_common_padpos(struct sk_buff *skb)
|
||||
{
|
||||
@ -1878,10 +1878,9 @@ static int ath5k_common_padpos(struct sk_buff *skb)
|
||||
}
|
||||
|
||||
/*
|
||||
* This function expects a 802.11 frame and returns the number of
|
||||
* bytes added, or -1 if we don't have enought header room.
|
||||
* This function expects an 802.11 frame and returns the number of
|
||||
* bytes added, or -1 if we don't have enough header room.
|
||||
*/
|
||||
|
||||
static int ath5k_add_padding(struct sk_buff *skb)
|
||||
{
|
||||
int padpos = ath5k_common_padpos(skb);
|
||||
@ -1901,10 +1900,18 @@ static int ath5k_add_padding(struct sk_buff *skb)
|
||||
}
|
||||
|
||||
/*
|
||||
* This function expects a 802.11 frame and returns the number of
|
||||
* bytes removed
|
||||
* The MAC header is padded to have 32-bit boundary if the
|
||||
* packet payload is non-zero. The general calculation for
|
||||
* padsize would take into account odd header lengths:
|
||||
* padsize = 4 - (hdrlen & 3); however, since only
|
||||
* even-length headers are used, padding can only be 0 or 2
|
||||
* bytes and we can optimize this a bit. We must not try to
|
||||
* remove padding from short control frames that do not have a
|
||||
* payload.
|
||||
*
|
||||
* This function expects an 802.11 frame and returns the number of
|
||||
* bytes removed.
|
||||
*/
|
||||
|
||||
static int ath5k_remove_padding(struct sk_buff *skb)
|
||||
{
|
||||
int padpos = ath5k_common_padpos(skb);
|
||||
@ -1925,14 +1932,6 @@ ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
|
||||
{
|
||||
struct ieee80211_rx_status *rxs;
|
||||
|
||||
/* The MAC header is padded to have 32-bit boundary if the
|
||||
* packet payload is non-zero. The general calculation for
|
||||
* padsize would take into account odd header lengths:
|
||||
* padsize = (4 - hdrlen % 4) % 4; However, since only
|
||||
* even-length headers are used, padding can only be 0 or 2
|
||||
* bytes and we can optimize this a bit. In addition, we must
|
||||
* not try to remove padding from short control frames that do
|
||||
* not have payload. */
|
||||
ath5k_remove_padding(skb);
|
||||
|
||||
rxs = IEEE80211_SKB_RXCB(skb);
|
||||
@ -2036,9 +2035,8 @@ ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* let crypto-error packets fall through in MNTR */
|
||||
if ((rs->rs_status & ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) ||
|
||||
sc->opmode != NL80211_IFTYPE_MONITOR)
|
||||
/* reject any frames with non-crypto errors */
|
||||
if (rs->rs_status & ~(AR5K_RXERR_DECRYPT))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2281,10 +2279,11 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
|
||||
* default antenna which is supposed to be an omni.
|
||||
*
|
||||
* Note2: On sectored scenarios it's possible to have
|
||||
* multiple antennas (1omni -the default- and 14 sectors)
|
||||
* so if we choose to actually support this mode we need
|
||||
* to allow user to set how many antennas we have and tweak
|
||||
* the code below to send beacons on all of them.
|
||||
* multiple antennas (1 omni -- the default -- and 14
|
||||
* sectors), so if we choose to actually support this
|
||||
* mode, we need to allow the user to set how many antennas
|
||||
* we have and tweak the code below to send beacons
|
||||
* on all of them.
|
||||
*/
|
||||
if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP)
|
||||
antenna = sc->bsent & 4 ? 2 : 1;
|
||||
@ -2326,14 +2325,13 @@ ath5k_beacon_send(struct ath5k_softc *sc)
|
||||
|
||||
ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
|
||||
|
||||
if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION ||
|
||||
sc->opmode == NL80211_IFTYPE_MONITOR)) {
|
||||
if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION)) {
|
||||
ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Check if the previous beacon has gone out. If
|
||||
* not don't don't try to post another, skip this
|
||||
* not, don't don't try to post another: skip this
|
||||
* period and wait for the next. Missed beacons
|
||||
* indicate a problem and should not occur. If we
|
||||
* miss too many consecutive beacons reset the device.
|
||||
@ -2901,12 +2899,9 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
|
||||
|
||||
ath5k_debug_dump_skb(sc, skb, "TX ", 1);
|
||||
|
||||
if (sc->opmode == NL80211_IFTYPE_MONITOR)
|
||||
ATH5K_DBG(sc, ATH5K_DEBUG_XMIT, "tx in monitor (scan?)\n");
|
||||
|
||||
/*
|
||||
* the hardware expects the header padded to 4 byte boundaries
|
||||
* if this is not the case we add the padding after the header
|
||||
* The hardware expects the header padded to 4 byte boundaries.
|
||||
* If this is not the case, we add the padding after the header.
|
||||
*/
|
||||
padsize = ath5k_add_padding(skb);
|
||||
if (padsize < 0) {
|
||||
@ -3049,7 +3044,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
sc->opmode = vif->type;
|
||||
break;
|
||||
default:
|
||||
@ -3233,9 +3227,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
|
||||
rfilt |= AR5K_RX_FILTER_PHYERR;
|
||||
|
||||
/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
|
||||
* and probes for any BSSID, this needs testing */
|
||||
* and probes for any BSSID */
|
||||
if (*new_flags & FIF_BCN_PRBRESP_PROMISC)
|
||||
rfilt |= AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ;
|
||||
rfilt |= AR5K_RX_FILTER_BEACON;
|
||||
|
||||
/* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not
|
||||
* set we should only pass on control frames for this
|
||||
@ -3251,7 +3245,6 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
|
||||
|
||||
switch (sc->opmode) {
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
rfilt |= AR5K_RX_FILTER_CONTROL |
|
||||
AR5K_RX_FILTER_BEACON |
|
||||
AR5K_RX_FILTER_PROBEREQ |
|
||||
@ -3274,7 +3267,7 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
|
||||
|
||||
/* Set multicast bits */
|
||||
ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
|
||||
/* Set the cached hw filter flags, this will alter actually
|
||||
/* Set the cached hw filter flags, this will later actually
|
||||
* be set in HW */
|
||||
sc->filter_flags = rfilt;
|
||||
|
||||
@ -3297,11 +3290,12 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
if (sc->opmode == NL80211_IFTYPE_AP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
case ALG_TKIP:
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
break;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
if (sc->ah->ah_aes_support)
|
||||
break;
|
||||
|
||||
@ -3475,7 +3469,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
|
||||
/* Cache for later use during resets */
|
||||
memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
|
||||
common->curaid = 0;
|
||||
ath5k_hw_set_associd(ah);
|
||||
ath5k_hw_set_bssid(ah);
|
||||
mmiowb();
|
||||
}
|
||||
|
||||
@ -3493,7 +3487,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
|
||||
"Bss Info ASSOC %d, bssid: %pM\n",
|
||||
bss_conf->aid, common->curbssid);
|
||||
common->curaid = bss_conf->aid;
|
||||
ath5k_hw_set_associd(ah);
|
||||
ath5k_hw_set_bssid(ah);
|
||||
/* Once ANI is available you would start it here */
|
||||
}
|
||||
}
|
||||
|
@ -377,11 +377,11 @@ int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr)
|
||||
*
|
||||
* This function increases/decreases the tx trigger level for the tx fifo
|
||||
* buffer (aka FIFO threshold) that is used to indicate when PCU flushes
|
||||
* the buffer and transmits it's data. Lowering this results sending small
|
||||
* the buffer and transmits its data. Lowering this results sending small
|
||||
* frames more quickly but can lead to tx underruns, raising it a lot can
|
||||
* result other problems (i think bmiss is related). Right now we start with
|
||||
* the lowest possible (64Bytes) and if we get tx underrun we increase it using
|
||||
* the increase flag. Returns -EIO if we have have reached maximum/minimum.
|
||||
* the increase flag. Returns -EIO if we have reached maximum/minimum.
|
||||
*
|
||||
* XXX: Link this with tx DMA size ?
|
||||
* XXX: Use it to save interrupts ?
|
||||
|
@ -661,7 +661,7 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
|
||||
* (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC
|
||||
* steps that match with the power values we read from eeprom. On
|
||||
* older eeprom versions (< 3.2) these steps are equaly spaced at
|
||||
* 10% of the pcdac curve -until the curve reaches it's maximum-
|
||||
* 10% of the pcdac curve -until the curve reaches its maximum-
|
||||
* (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
|
||||
* these 11 steps are spaced in a different way. This function returns
|
||||
* the pcdac steps based on eeprom version and curve min/max so that we
|
||||
@ -1113,7 +1113,7 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
|
||||
*/
|
||||
|
||||
/* For RF2413 power calibration data doesn't start on a fixed location and
|
||||
* if a mode is not supported, it's section is missing -not zeroed-.
|
||||
* if a mode is not supported, its section is missing -not zeroed-.
|
||||
* So we need to calculate the starting offset for each section by using
|
||||
* these two functions */
|
||||
|
||||
|
@ -308,27 +308,26 @@ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac)
|
||||
}
|
||||
|
||||
/**
|
||||
* ath5k_hw_set_associd - Set BSSID for association
|
||||
* ath5k_hw_set_bssid - Set current BSSID on hw
|
||||
*
|
||||
* @ah: The &struct ath5k_hw
|
||||
* @bssid: BSSID
|
||||
* @assoc_id: Assoc id
|
||||
*
|
||||
* Sets the BSSID which trigers the "SME Join" operation
|
||||
* Sets the current BSSID and BSSID mask we have from the
|
||||
* common struct into the hardware
|
||||
*/
|
||||
void ath5k_hw_set_associd(struct ath5k_hw *ah)
|
||||
void ath5k_hw_set_bssid(struct ath5k_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath5k_hw_common(ah);
|
||||
u16 tim_offset = 0;
|
||||
|
||||
/*
|
||||
* Set simple BSSID mask on 5212
|
||||
* Set BSSID mask on 5212
|
||||
*/
|
||||
if (ah->ah_version == AR5K_AR5212)
|
||||
ath_hw_setbssidmask(common);
|
||||
|
||||
/*
|
||||
* Set BSSID which triggers the "SME Join" operation
|
||||
* Set BSSID
|
||||
*/
|
||||
ath5k_hw_reg_write(ah,
|
||||
get_unaligned_le32(common->curbssid),
|
||||
@ -695,21 +694,18 @@ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry)
|
||||
static
|
||||
int ath5k_keycache_type(const struct ieee80211_key_conf *key)
|
||||
{
|
||||
switch (key->alg) {
|
||||
case ALG_TKIP:
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
return AR5K_KEYTABLE_TYPE_TKIP;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
return AR5K_KEYTABLE_TYPE_CCM;
|
||||
case ALG_WEP:
|
||||
if (key->keylen == WLAN_KEY_LEN_WEP40)
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
return AR5K_KEYTABLE_TYPE_40;
|
||||
else if (key->keylen == WLAN_KEY_LEN_WEP104)
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
return AR5K_KEYTABLE_TYPE_104;
|
||||
return -EINVAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -728,7 +724,7 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
|
||||
bool is_tkip;
|
||||
const u8 *key_ptr;
|
||||
|
||||
is_tkip = (key->alg == ALG_TKIP);
|
||||
is_tkip = (key->cipher == WLAN_CIPHER_SUITE_TKIP);
|
||||
|
||||
/*
|
||||
* key->keylen comes in from mac80211 in bytes.
|
||||
|
@ -115,7 +115,7 @@ static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah,
|
||||
\**********************/
|
||||
|
||||
/*
|
||||
* This code is used to optimize rf gain on different environments
|
||||
* This code is used to optimize RF gain on different environments
|
||||
* (temperature mostly) based on feedback from a power detector.
|
||||
*
|
||||
* It's only used on RF5111 and RF5112, later RF chips seem to have
|
||||
@ -302,7 +302,7 @@ static bool ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah)
|
||||
}
|
||||
|
||||
/* Perform gain_F adjustment by choosing the right set
|
||||
* of parameters from rf gain optimization ladder */
|
||||
* of parameters from RF gain optimization ladder */
|
||||
static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah)
|
||||
{
|
||||
const struct ath5k_gain_opt *go;
|
||||
@ -367,7 +367,7 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Main callback for thermal rf gain calibration engine
|
||||
/* Main callback for thermal RF gain calibration engine
|
||||
* Check for a new gain reading and schedule an adjustment
|
||||
* if needed.
|
||||
*
|
||||
@ -433,7 +433,7 @@ done:
|
||||
return ah->ah_gain.g_state;
|
||||
}
|
||||
|
||||
/* Write initial rf gain table to set the RF sensitivity
|
||||
/* Write initial RF gain table to set the RF sensitivity
|
||||
* this one works on all RF chips and has nothing to do
|
||||
* with gain_F calibration */
|
||||
int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq)
|
||||
@ -496,7 +496,7 @@ int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq)
|
||||
|
||||
|
||||
/*
|
||||
* Setup RF registers by writing rf buffer on hw
|
||||
* Setup RF registers by writing RF buffer on hw
|
||||
*/
|
||||
int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
|
||||
unsigned int mode)
|
||||
@ -571,7 +571,7 @@ int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If it's the first time we set rf buffer, allocate
|
||||
/* If it's the first time we set RF buffer, allocate
|
||||
* ah->ah_rf_banks based on ah->ah_rf_banks_size
|
||||
* we set above */
|
||||
if (ah->ah_rf_banks == NULL) {
|
||||
@ -3035,9 +3035,6 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
|
||||
/* Limit max power if we have a CTL available */
|
||||
ath5k_get_max_ctl_power(ah, channel);
|
||||
|
||||
/* FIXME: Tx power limit for this regdomain
|
||||
* XXX: Mac80211/CRDA will do that anyway ? */
|
||||
|
||||
/* FIXME: Antenna reduction stuff */
|
||||
|
||||
/* FIXME: Limit power on turbo modes */
|
||||
|
@ -1911,7 +1911,7 @@
|
||||
#define AR5K_PHY_TURBO 0x9804 /* Register Address */
|
||||
#define AR5K_PHY_TURBO_MODE 0x00000001 /* Enable turbo mode */
|
||||
#define AR5K_PHY_TURBO_SHORT 0x00000002 /* Set short symbols to turbo mode */
|
||||
#define AR5K_PHY_TURBO_MIMO 0x00000004 /* Set turbo for mimo mimo */
|
||||
#define AR5K_PHY_TURBO_MIMO 0x00000004 /* Set turbo for mimo */
|
||||
|
||||
/*
|
||||
* PHY agility command register
|
||||
|
@ -959,7 +959,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
|
||||
AR5K_QUEUE_DCU_SEQNUM(0));
|
||||
}
|
||||
|
||||
/* TSF accelerates on AR5211 durring reset
|
||||
/* TSF accelerates on AR5211 during reset
|
||||
* As a workaround save it here and restore
|
||||
* it later so that it's back in time after
|
||||
* reset. This way it'll get re-synced on the
|
||||
@ -1080,7 +1080,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
|
||||
return ret;
|
||||
|
||||
/* Spur info is available only from EEPROM versions
|
||||
* bigger than 5.3 but but the EEPOM routines will use
|
||||
* greater than 5.3, but the EEPROM routines will use
|
||||
* static values for older versions */
|
||||
if (ah->ah_mac_srev >= AR5K_SREV_AR5424)
|
||||
ath5k_hw_set_spur_mitigation_filter(ah,
|
||||
@ -1160,7 +1160,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
|
||||
*/
|
||||
|
||||
/* Restore bssid and bssid mask */
|
||||
ath5k_hw_set_associd(ah);
|
||||
ath5k_hw_set_bssid(ah);
|
||||
|
||||
/* Set PCU config */
|
||||
ath5k_hw_set_opmode(ah, op_mode);
|
||||
@ -1173,11 +1173,11 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
|
||||
/* Set RSSI/BRSSI thresholds
|
||||
*
|
||||
* Note: If we decide to set this value
|
||||
* dynamicaly, have in mind that when AR5K_RSSI_THR
|
||||
* register is read it might return 0x40 if we haven't
|
||||
* wrote anything to it plus BMISS RSSI threshold is zeroed.
|
||||
* dynamically, keep in mind that when AR5K_RSSI_THR
|
||||
* register is read, it might return 0x40 if we haven't
|
||||
* written anything to it. Also, BMISS RSSI threshold is zeroed.
|
||||
* So doing a save/restore procedure here isn't the right
|
||||
* choice. Instead store it on ath5k_hw */
|
||||
* choice. Instead, store it in ath5k_hw */
|
||||
ath5k_hw_reg_write(ah, (AR5K_TUNE_RSSI_THRES |
|
||||
AR5K_TUNE_BMISS_THRES <<
|
||||
AR5K_RSSI_THR_BMISS_S),
|
||||
@ -1235,7 +1235,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
|
||||
|
||||
/*
|
||||
* Perform ADC test to see if baseband is ready
|
||||
* Set tx hold and check adc test register
|
||||
* Set TX hold and check ADC test register
|
||||
*/
|
||||
phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1);
|
||||
ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1);
|
||||
@ -1254,15 +1254,15 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
|
||||
*
|
||||
* This method is used to calibrate some static offsets
|
||||
* used together with on-the fly I/Q calibration (the
|
||||
* one performed via ath5k_hw_phy_calibrate), that doesn't
|
||||
* one performed via ath5k_hw_phy_calibrate), which doesn't
|
||||
* interrupt rx path.
|
||||
*
|
||||
* While rx path is re-routed to the power detector we also
|
||||
* start a noise floor calibration, to measure the
|
||||
* start a noise floor calibration to measure the
|
||||
* card's noise floor (the noise we measure when we are not
|
||||
* transmiting or receiving anything).
|
||||
* transmitting or receiving anything).
|
||||
*
|
||||
* If we are in a noisy environment AGC calibration may time
|
||||
* If we are in a noisy environment, AGC calibration may time
|
||||
* out and/or noise floor calibration might timeout.
|
||||
*/
|
||||
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
|
||||
|
@ -28,7 +28,7 @@
|
||||
* that holds all the settings we need. After we've sent the
|
||||
* data packet, we write on another special register to notify hw
|
||||
* to apply the settings. This is done so that control registers
|
||||
* can be dynamicaly programmed during operation and the settings
|
||||
* can be dynamically programmed during operation and the settings
|
||||
* are applied faster on the hw.
|
||||
*
|
||||
* We call each data packet an "RF Bank" and all the data we write
|
||||
|
@ -46,6 +46,7 @@ ath9k_htc-y += htc_hst.o \
|
||||
htc_drv_txrx.o \
|
||||
htc_drv_main.o \
|
||||
htc_drv_beacon.o \
|
||||
htc_drv_init.o
|
||||
htc_drv_init.o \
|
||||
htc_drv_gpio.o
|
||||
|
||||
obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o
|
||||
|
@ -423,6 +423,7 @@ int ath_beaconq_config(struct ath_softc *sc);
|
||||
#define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */
|
||||
#define ATH_ANI_POLLINTERVAL_OLD 100 /* 100 ms */
|
||||
#define ATH_ANI_POLLINTERVAL_NEW 1000 /* 1000 ms */
|
||||
#define ATH_LONG_CALINTERVAL_INT 1000 /* 1000 ms */
|
||||
#define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */
|
||||
#define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */
|
||||
|
||||
@ -436,14 +437,6 @@ void ath_ani_calibrate(unsigned long data);
|
||||
/* BTCOEX */
|
||||
/**********/
|
||||
|
||||
/* Defines the BT AR_BT_COEX_WGHT used */
|
||||
enum ath_stomp_type {
|
||||
ATH_BTCOEX_NO_STOMP,
|
||||
ATH_BTCOEX_STOMP_ALL,
|
||||
ATH_BTCOEX_STOMP_LOW,
|
||||
ATH_BTCOEX_STOMP_NONE
|
||||
};
|
||||
|
||||
struct ath_btcoex {
|
||||
bool hw_timer_enabled;
|
||||
spinlock_t btcoex_lock;
|
||||
|
@ -359,11 +359,12 @@ void ath_beacon_tasklet(unsigned long data)
|
||||
sc->beacon.bmisscnt++;
|
||||
|
||||
if (sc->beacon.bmisscnt < BSTUCK_THRESH) {
|
||||
ath_print(common, ATH_DBG_BEACON,
|
||||
ath_print(common, ATH_DBG_BSTUCK,
|
||||
"missed %u consecutive beacons\n",
|
||||
sc->beacon.bmisscnt);
|
||||
ath9k_hw_bstuck_nfcal(ah);
|
||||
} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
|
||||
ath_print(common, ATH_DBG_BEACON,
|
||||
ath_print(common, ATH_DBG_BSTUCK,
|
||||
"beacon is officially stuck\n");
|
||||
sc->sc_flags |= SC_OP_TSF_RESET;
|
||||
ath_reset(sc, false);
|
||||
@ -373,7 +374,7 @@ void ath_beacon_tasklet(unsigned long data)
|
||||
}
|
||||
|
||||
if (sc->beacon.bmisscnt != 0) {
|
||||
ath_print(common, ATH_DBG_BEACON,
|
||||
ath_print(common, ATH_DBG_BSTUCK,
|
||||
"resume beacon xmit after %u misses\n",
|
||||
sc->beacon.bmisscnt);
|
||||
sc->beacon.bmisscnt = 0;
|
||||
|
@ -168,6 +168,7 @@ EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight);
|
||||
static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* Program coex mode and weight registers to
|
||||
@ -177,6 +178,12 @@ static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah)
|
||||
REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_hw->bt_coex_weights);
|
||||
REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_hw->bt_coex_mode2);
|
||||
|
||||
if (AR_SREV_9271(ah)) {
|
||||
val = REG_READ(ah, 0x50040);
|
||||
val &= 0xFFFFFEFF;
|
||||
REG_WRITE(ah, 0x50040, val);
|
||||
}
|
||||
|
||||
REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
|
||||
REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0);
|
||||
|
||||
|
@ -19,8 +19,7 @@
|
||||
|
||||
/* Common calibration code */
|
||||
|
||||
/* We can tune this as we go by monitoring really low values */
|
||||
#define ATH9K_NF_TOO_LOW -60
|
||||
#define ATH9K_NF_TOO_HIGH -60
|
||||
|
||||
static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
|
||||
{
|
||||
@ -45,11 +44,39 @@ static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
|
||||
return nfval;
|
||||
}
|
||||
|
||||
static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
|
||||
static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah,
|
||||
struct ath9k_channel *chan)
|
||||
{
|
||||
struct ath_nf_limits *limit;
|
||||
|
||||
if (!chan || IS_CHAN_2GHZ(chan))
|
||||
limit = &ah->nf_2g;
|
||||
else
|
||||
limit = &ah->nf_5g;
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
|
||||
struct ath9k_channel *chan)
|
||||
{
|
||||
return ath9k_hw_get_nf_limits(ah, chan)->nominal;
|
||||
}
|
||||
|
||||
|
||||
static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
|
||||
struct ath9k_hw_cal_data *cal,
|
||||
int16_t *nfarray)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath_nf_limits *limit;
|
||||
struct ath9k_nfcal_hist *h;
|
||||
bool high_nf_mid = false;
|
||||
int i;
|
||||
|
||||
h = cal->nfCalHist;
|
||||
limit = ath9k_hw_get_nf_limits(ah, ah->curchan);
|
||||
|
||||
for (i = 0; i < NUM_NF_READINGS; i++) {
|
||||
h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
|
||||
|
||||
@ -63,9 +90,41 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
|
||||
h[i].privNF =
|
||||
ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
|
||||
}
|
||||
|
||||
if (!h[i].privNF)
|
||||
continue;
|
||||
|
||||
if (h[i].privNF > limit->max) {
|
||||
high_nf_mid = true;
|
||||
|
||||
ath_print(common, ATH_DBG_CALIBRATE,
|
||||
"NFmid[%d] (%d) > MAX (%d), %s\n",
|
||||
i, h[i].privNF, limit->max,
|
||||
(cal->nfcal_interference ?
|
||||
"not corrected (due to interference)" :
|
||||
"correcting to MAX"));
|
||||
|
||||
/*
|
||||
* Normally we limit the average noise floor by the
|
||||
* hardware specific maximum here. However if we have
|
||||
* encountered stuck beacons because of interference,
|
||||
* we bypass this limit here in order to better deal
|
||||
* with our environment.
|
||||
*/
|
||||
if (!cal->nfcal_interference)
|
||||
h[i].privNF = limit->max;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the noise floor seems normal for all chains, assume that
|
||||
* there is no significant interference in the environment anymore.
|
||||
* Re-enable the enforcement of the NF maximum again.
|
||||
*/
|
||||
if (!high_nf_mid)
|
||||
cal->nfcal_interference = false;
|
||||
}
|
||||
|
||||
static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah,
|
||||
enum ieee80211_band band,
|
||||
int16_t *nft)
|
||||
@ -104,19 +163,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
|
||||
ah->cal_samples = 0;
|
||||
}
|
||||
|
||||
static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
|
||||
struct ath9k_channel *chan)
|
||||
{
|
||||
struct ath_nf_limits *limit;
|
||||
|
||||
if (!chan || IS_CHAN_2GHZ(chan))
|
||||
limit = &ah->nf_2g;
|
||||
else
|
||||
limit = &ah->nf_5g;
|
||||
|
||||
return limit->nominal;
|
||||
}
|
||||
|
||||
/* This is done for the currently configured channel */
|
||||
bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
|
||||
{
|
||||
@ -277,10 +323,10 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
|
||||
"NF calibrated [%s] [chain %d] is %d\n",
|
||||
(i >= 3 ? "ext" : "ctl"), i % 3, nf[i]);
|
||||
|
||||
if (nf[i] > limit->max) {
|
||||
if (nf[i] > ATH9K_NF_TOO_HIGH) {
|
||||
ath_print(common, ATH_DBG_CALIBRATE,
|
||||
"NF[%d] (%d) > MAX (%d), correcting to MAX",
|
||||
i, nf[i], limit->max);
|
||||
i, nf[i], ATH9K_NF_TOO_HIGH);
|
||||
nf[i] = limit->max;
|
||||
} else if (nf[i] < limit->min) {
|
||||
ath_print(common, ATH_DBG_CALIBRATE,
|
||||
@ -326,7 +372,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
|
||||
h = caldata->nfCalHist;
|
||||
caldata->nfcal_pending = false;
|
||||
ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
|
||||
ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray);
|
||||
caldata->rawNoiseFloor = h[0].privNF;
|
||||
return true;
|
||||
}
|
||||
@ -361,3 +407,28 @@ s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
return ah->caldata->rawNoiseFloor;
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_getchan_noise);
|
||||
|
||||
void ath9k_hw_bstuck_nfcal(struct ath_hw *ah)
|
||||
{
|
||||
struct ath9k_hw_cal_data *caldata = ah->caldata;
|
||||
|
||||
if (unlikely(!caldata))
|
||||
return;
|
||||
|
||||
/*
|
||||
* If beacons are stuck, the most likely cause is interference.
|
||||
* Triggering a noise floor calibration at this point helps the
|
||||
* hardware adapt to a noisy environment much faster.
|
||||
* To ensure that we recover from stuck beacons quickly, let
|
||||
* the baseband update the internal NF value itself, similar to
|
||||
* what is being done after a full reset.
|
||||
*/
|
||||
if (!caldata->nfcal_pending)
|
||||
ath9k_hw_start_nfcal(ah, true);
|
||||
else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF))
|
||||
ath9k_hw_getnf(ah, ah->curchan);
|
||||
|
||||
caldata->nfcal_interference = true;
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal);
|
||||
|
||||
|
@ -113,6 +113,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
|
||||
bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan);
|
||||
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
|
||||
struct ath9k_channel *chan);
|
||||
void ath9k_hw_bstuck_nfcal(struct ath_hw *ah);
|
||||
s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
|
||||
void ath9k_hw_reset_calibration(struct ath_hw *ah,
|
||||
struct ath9k_cal_list *currCal);
|
||||
|
@ -46,12 +46,17 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
|
||||
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
if (tx_info->control.hw_key) {
|
||||
if (tx_info->control.hw_key->alg == ALG_WEP)
|
||||
switch (tx_info->control.hw_key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
return ATH9K_KEY_TYPE_WEP;
|
||||
else if (tx_info->control.hw_key->alg == ALG_TKIP)
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
return ATH9K_KEY_TYPE_TKIP;
|
||||
else if (tx_info->control.hw_key->alg == ALG_CCMP)
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
return ATH9K_KEY_TYPE_AES;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ATH9K_KEY_TYPE_CLEAR;
|
||||
@ -212,11 +217,11 @@ static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
|
||||
}
|
||||
|
||||
static int ath_reserve_key_cache_slot(struct ath_common *common,
|
||||
enum ieee80211_key_alg alg)
|
||||
u32 cipher)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (alg == ALG_TKIP)
|
||||
if (cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
return ath_reserve_key_cache_slot_tkip(common);
|
||||
|
||||
/* First, try to find slots that would not be available for TKIP. */
|
||||
@ -293,14 +298,15 @@ int ath9k_cmn_key_config(struct ath_common *common,
|
||||
|
||||
memset(&hk, 0, sizeof(hk));
|
||||
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
hk.kv_type = ATH9K_CIPHER_WEP;
|
||||
break;
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
hk.kv_type = ATH9K_CIPHER_TKIP;
|
||||
break;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
hk.kv_type = ATH9K_CIPHER_AES_CCM;
|
||||
break;
|
||||
default:
|
||||
@ -316,7 +322,7 @@ int ath9k_cmn_key_config(struct ath_common *common,
|
||||
memcpy(gmac, vif->addr, ETH_ALEN);
|
||||
gmac[0] |= 0x01;
|
||||
mac = gmac;
|
||||
idx = ath_reserve_key_cache_slot(common, key->alg);
|
||||
idx = ath_reserve_key_cache_slot(common, key->cipher);
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
if (!sta) {
|
||||
@ -326,7 +332,7 @@ int ath9k_cmn_key_config(struct ath_common *common,
|
||||
memcpy(gmac, sta->addr, ETH_ALEN);
|
||||
gmac[0] |= 0x01;
|
||||
mac = gmac;
|
||||
idx = ath_reserve_key_cache_slot(common, key->alg);
|
||||
idx = ath_reserve_key_cache_slot(common, key->cipher);
|
||||
break;
|
||||
default:
|
||||
idx = key->keyidx;
|
||||
@ -348,13 +354,13 @@ int ath9k_cmn_key_config(struct ath_common *common,
|
||||
return -EOPNOTSUPP;
|
||||
mac = sta->addr;
|
||||
|
||||
idx = ath_reserve_key_cache_slot(common, key->alg);
|
||||
idx = ath_reserve_key_cache_slot(common, key->cipher);
|
||||
}
|
||||
|
||||
if (idx < 0)
|
||||
return -ENOSPC; /* no free key cache entries */
|
||||
|
||||
if (key->alg == ALG_TKIP)
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
|
||||
vif->type == NL80211_IFTYPE_AP);
|
||||
else
|
||||
@ -364,7 +370,7 @@ int ath9k_cmn_key_config(struct ath_common *common,
|
||||
return -EIO;
|
||||
|
||||
set_bit(idx, common->keymap);
|
||||
if (key->alg == ALG_TKIP) {
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
|
||||
set_bit(idx + 64, common->keymap);
|
||||
if (common->splitmic) {
|
||||
set_bit(idx + 32, common->keymap);
|
||||
@ -389,7 +395,7 @@ void ath9k_cmn_key_delete(struct ath_common *common,
|
||||
return;
|
||||
|
||||
clear_bit(key->hw_key_idx, common->keymap);
|
||||
if (key->alg != ALG_TKIP)
|
||||
if (key->cipher != WLAN_CIPHER_SUITE_TKIP)
|
||||
return;
|
||||
|
||||
clear_bit(key->hw_key_idx + 64, common->keymap);
|
||||
@ -414,6 +420,37 @@ int ath9k_cmn_count_streams(unsigned int chainmask, int max)
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_count_streams);
|
||||
|
||||
/*
|
||||
* Configures appropriate weight based on stomp type.
|
||||
*/
|
||||
void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
|
||||
enum ath_stomp_type stomp_type)
|
||||
{
|
||||
struct ath_hw *ah = common->ah;
|
||||
|
||||
switch (stomp_type) {
|
||||
case ATH_BTCOEX_STOMP_ALL:
|
||||
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
|
||||
AR_STOMP_ALL_WLAN_WGHT);
|
||||
break;
|
||||
case ATH_BTCOEX_STOMP_LOW:
|
||||
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
|
||||
AR_STOMP_LOW_WLAN_WGHT);
|
||||
break;
|
||||
case ATH_BTCOEX_STOMP_NONE:
|
||||
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
|
||||
AR_STOMP_NONE_WLAN_WGHT);
|
||||
break;
|
||||
default:
|
||||
ath_print(common, ATH_DBG_BTCOEX,
|
||||
"Invalid Stomptype\n");
|
||||
break;
|
||||
}
|
||||
|
||||
ath9k_hw_btcoex_enable(ah);
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_btcoex_bt_stomp);
|
||||
|
||||
static int __init ath9k_cmn_init(void)
|
||||
{
|
||||
return 0;
|
||||
|
@ -52,6 +52,14 @@
|
||||
#define ATH_EP_RND(x, mul) \
|
||||
((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
|
||||
|
||||
/* Defines the BT AR_BT_COEX_WGHT used */
|
||||
enum ath_stomp_type {
|
||||
ATH_BTCOEX_NO_STOMP,
|
||||
ATH_BTCOEX_STOMP_ALL,
|
||||
ATH_BTCOEX_STOMP_LOW,
|
||||
ATH_BTCOEX_STOMP_NONE
|
||||
};
|
||||
|
||||
int ath9k_cmn_padpos(__le16 frame_control);
|
||||
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
|
||||
void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw,
|
||||
@ -65,3 +73,5 @@ int ath9k_cmn_key_config(struct ath_common *common,
|
||||
void ath9k_cmn_key_delete(struct ath_common *common,
|
||||
struct ieee80211_key_conf *key);
|
||||
int ath9k_cmn_count_streams(unsigned int chainmask, int max);
|
||||
void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
|
||||
enum ath_stomp_type stomp_type);
|
||||
|
@ -251,36 +251,6 @@ static void ath_detect_bt_priority(struct ath_softc *sc)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures appropriate weight based on stomp type.
|
||||
*/
|
||||
static void ath9k_btcoex_bt_stomp(struct ath_softc *sc,
|
||||
enum ath_stomp_type stomp_type)
|
||||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
|
||||
switch (stomp_type) {
|
||||
case ATH_BTCOEX_STOMP_ALL:
|
||||
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
|
||||
AR_STOMP_ALL_WLAN_WGHT);
|
||||
break;
|
||||
case ATH_BTCOEX_STOMP_LOW:
|
||||
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
|
||||
AR_STOMP_LOW_WLAN_WGHT);
|
||||
break;
|
||||
case ATH_BTCOEX_STOMP_NONE:
|
||||
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
|
||||
AR_STOMP_NONE_WLAN_WGHT);
|
||||
break;
|
||||
default:
|
||||
ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
|
||||
"Invalid Stomptype\n");
|
||||
break;
|
||||
}
|
||||
|
||||
ath9k_hw_btcoex_enable(ah);
|
||||
}
|
||||
|
||||
static void ath9k_gen_timer_start(struct ath_hw *ah,
|
||||
struct ath_gen_timer *timer,
|
||||
u32 timer_next,
|
||||
@ -319,6 +289,7 @@ static void ath_btcoex_period_timer(unsigned long data)
|
||||
struct ath_softc *sc = (struct ath_softc *) data;
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_btcoex *btcoex = &sc->btcoex;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
u32 timer_period;
|
||||
bool is_btscan;
|
||||
|
||||
@ -328,7 +299,7 @@ static void ath_btcoex_period_timer(unsigned long data)
|
||||
|
||||
spin_lock_bh(&btcoex->btcoex_lock);
|
||||
|
||||
ath9k_btcoex_bt_stomp(sc, is_btscan ? ATH_BTCOEX_STOMP_ALL :
|
||||
ath9k_cmn_btcoex_bt_stomp(common, is_btscan ? ATH_BTCOEX_STOMP_ALL :
|
||||
btcoex->bt_stomp_type);
|
||||
|
||||
spin_unlock_bh(&btcoex->btcoex_lock);
|
||||
@ -359,17 +330,18 @@ static void ath_btcoex_no_stomp_timer(void *arg)
|
||||
struct ath_softc *sc = (struct ath_softc *)arg;
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_btcoex *btcoex = &sc->btcoex;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
|
||||
|
||||
ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
|
||||
ath_print(common, ATH_DBG_BTCOEX,
|
||||
"no stomp timer running\n");
|
||||
|
||||
spin_lock_bh(&btcoex->btcoex_lock);
|
||||
|
||||
if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
|
||||
ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE);
|
||||
ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_NONE);
|
||||
else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
|
||||
ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW);
|
||||
ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_LOW);
|
||||
|
||||
spin_unlock_bh(&btcoex->btcoex_lock);
|
||||
}
|
||||
|
@ -920,7 +920,8 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,
|
||||
}
|
||||
|
||||
ret = ath9k_htc_hw_init(hif_dev->htc_handle,
|
||||
&hif_dev->udev->dev, hif_dev->device_id);
|
||||
&hif_dev->udev->dev, hif_dev->device_id,
|
||||
hif_dev->udev->product);
|
||||
if (ret) {
|
||||
ret = -EINVAL;
|
||||
goto err_htc_hw_init;
|
||||
|
@ -316,6 +316,19 @@ struct htc_beacon_config {
|
||||
u8 dtim_count;
|
||||
};
|
||||
|
||||
struct ath_btcoex {
|
||||
u32 bt_priority_cnt;
|
||||
unsigned long bt_priority_time;
|
||||
int bt_stomp_type; /* Types of BT stomping */
|
||||
u32 btcoex_no_stomp;
|
||||
u32 btcoex_period;
|
||||
u32 btscan_no_stomp;
|
||||
};
|
||||
|
||||
void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv);
|
||||
void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv);
|
||||
void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv);
|
||||
|
||||
#define OP_INVALID BIT(0)
|
||||
#define OP_SCANNING BIT(1)
|
||||
#define OP_FULL_RESET BIT(2)
|
||||
@ -327,6 +340,8 @@ struct htc_beacon_config {
|
||||
#define OP_ENABLE_BEACON BIT(8)
|
||||
#define OP_LED_DEINIT BIT(9)
|
||||
#define OP_UNPLUGGED BIT(10)
|
||||
#define OP_BT_PRIORITY_DETECTED BIT(11)
|
||||
#define OP_BT_SCAN BIT(12)
|
||||
|
||||
struct ath9k_htc_priv {
|
||||
struct device *dev;
|
||||
@ -391,6 +406,9 @@ struct ath9k_htc_priv {
|
||||
int cabq;
|
||||
int hwq_map[WME_NUM_AC];
|
||||
|
||||
struct ath_btcoex btcoex;
|
||||
struct delayed_work coex_period_work;
|
||||
struct delayed_work duty_cycle_work;
|
||||
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
|
||||
struct ath9k_debug debug;
|
||||
#endif
|
||||
@ -443,7 +461,7 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv);
|
||||
void ath9k_deinit_leds(struct ath9k_htc_priv *priv);
|
||||
|
||||
int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
|
||||
u16 devid);
|
||||
u16 devid, char *product);
|
||||
void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug);
|
||||
#ifdef CONFIG_PM
|
||||
int ath9k_htc_resume(struct htc_target *htc_handle);
|
||||
|
134
drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
Normal file
134
drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
Normal file
@ -0,0 +1,134 @@
|
||||
#include "htc.h"
|
||||
|
||||
/******************/
|
||||
/* BTCOEX */
|
||||
/******************/
|
||||
|
||||
/*
|
||||
* Detects if there is any priority bt traffic
|
||||
*/
|
||||
static void ath_detect_bt_priority(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
struct ath_btcoex *btcoex = &priv->btcoex;
|
||||
struct ath_hw *ah = priv->ah;
|
||||
|
||||
if (ath9k_hw_gpio_get(ah, ah->btcoex_hw.btpriority_gpio))
|
||||
btcoex->bt_priority_cnt++;
|
||||
|
||||
if (time_after(jiffies, btcoex->bt_priority_time +
|
||||
msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
|
||||
priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN);
|
||||
/* Detect if colocated bt started scanning */
|
||||
if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
|
||||
ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
|
||||
"BT scan detected");
|
||||
priv->op_flags |= (OP_BT_SCAN |
|
||||
OP_BT_PRIORITY_DETECTED);
|
||||
} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
|
||||
ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
|
||||
"BT priority traffic detected");
|
||||
priv->op_flags |= OP_BT_PRIORITY_DETECTED;
|
||||
}
|
||||
|
||||
btcoex->bt_priority_cnt = 0;
|
||||
btcoex->bt_priority_time = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the master bt coex work which runs for every
|
||||
* 45ms, bt traffic will be given priority during 55% of this
|
||||
* period while wlan gets remaining 45%
|
||||
*/
|
||||
static void ath_btcoex_period_work(struct work_struct *work)
|
||||
{
|
||||
struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
|
||||
coex_period_work.work);
|
||||
struct ath_btcoex *btcoex = &priv->btcoex;
|
||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||
u32 timer_period;
|
||||
bool is_btscan;
|
||||
int ret;
|
||||
u8 cmd_rsp, aggr;
|
||||
|
||||
ath_detect_bt_priority(priv);
|
||||
|
||||
is_btscan = !!(priv->op_flags & OP_BT_SCAN);
|
||||
|
||||
aggr = priv->op_flags & OP_BT_PRIORITY_DETECTED;
|
||||
|
||||
WMI_CMD_BUF(WMI_AGGR_LIMIT_CMD, &aggr);
|
||||
|
||||
ath9k_cmn_btcoex_bt_stomp(common, is_btscan ? ATH_BTCOEX_STOMP_ALL :
|
||||
btcoex->bt_stomp_type);
|
||||
|
||||
timer_period = is_btscan ? btcoex->btscan_no_stomp :
|
||||
btcoex->btcoex_no_stomp;
|
||||
ieee80211_queue_delayed_work(priv->hw, &priv->duty_cycle_work,
|
||||
msecs_to_jiffies(timer_period));
|
||||
ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work,
|
||||
msecs_to_jiffies(btcoex->btcoex_period));
|
||||
}
|
||||
|
||||
/*
|
||||
* Work to time slice between wlan and bt traffic and
|
||||
* configure weight registers
|
||||
*/
|
||||
static void ath_btcoex_duty_cycle_work(struct work_struct *work)
|
||||
{
|
||||
struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
|
||||
duty_cycle_work.work);
|
||||
struct ath_hw *ah = priv->ah;
|
||||
struct ath_btcoex *btcoex = &priv->btcoex;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
bool is_btscan = priv->op_flags & OP_BT_SCAN;
|
||||
|
||||
ath_print(common, ATH_DBG_BTCOEX,
|
||||
"time slice work for bt and wlan\n");
|
||||
|
||||
if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
|
||||
ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_NONE);
|
||||
else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
|
||||
ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_LOW);
|
||||
}
|
||||
|
||||
void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
struct ath_btcoex *btcoex = &priv->btcoex;
|
||||
|
||||
btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD;
|
||||
btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
|
||||
btcoex->btcoex_period / 100;
|
||||
btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
|
||||
btcoex->btcoex_period / 100;
|
||||
INIT_DELAYED_WORK(&priv->coex_period_work, ath_btcoex_period_work);
|
||||
INIT_DELAYED_WORK(&priv->duty_cycle_work, ath_btcoex_duty_cycle_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* (Re)start btcoex work
|
||||
*/
|
||||
|
||||
void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
struct ath_btcoex *btcoex = &priv->btcoex;
|
||||
struct ath_hw *ah = priv->ah;
|
||||
|
||||
ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
|
||||
"Starting btcoex work");
|
||||
|
||||
btcoex->bt_priority_cnt = 0;
|
||||
btcoex->bt_priority_time = jiffies;
|
||||
priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN);
|
||||
ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Cancel btcoex and bt duty cycle work.
|
||||
*/
|
||||
void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
cancel_delayed_work_sync(&priv->coex_period_work);
|
||||
cancel_delayed_work_sync(&priv->duty_cycle_work);
|
||||
}
|
@ -41,6 +41,8 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
|
||||
.max_power = 20, \
|
||||
}
|
||||
|
||||
#define ATH_HTC_BTCOEX_PRODUCT_ID "wb193"
|
||||
|
||||
static struct ieee80211_channel ath9k_2ghz_channels[] = {
|
||||
CHAN2G(2412, 0), /* Channel 1 */
|
||||
CHAN2G(2417, 1), /* Channel 2 */
|
||||
@ -605,7 +607,31 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv)
|
||||
priv->ah->opmode = NL80211_IFTYPE_STATION;
|
||||
}
|
||||
|
||||
static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid)
|
||||
static void ath9k_init_btcoex(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
int qnum;
|
||||
|
||||
switch (priv->ah->btcoex_hw.scheme) {
|
||||
case ATH_BTCOEX_CFG_NONE:
|
||||
break;
|
||||
case ATH_BTCOEX_CFG_3WIRE:
|
||||
priv->ah->btcoex_hw.btactive_gpio = 7;
|
||||
priv->ah->btcoex_hw.btpriority_gpio = 6;
|
||||
priv->ah->btcoex_hw.wlanactive_gpio = 8;
|
||||
priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
|
||||
ath9k_hw_btcoex_init_3wire(priv->ah);
|
||||
ath_htc_init_btcoex_work(priv);
|
||||
qnum = priv->hwq_map[WME_AC_BE];
|
||||
ath9k_hw_init_btcoex_hw(priv->ah, qnum);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int ath9k_init_priv(struct ath9k_htc_priv *priv,
|
||||
u16 devid, char *product)
|
||||
{
|
||||
struct ath_hw *ah = NULL;
|
||||
struct ath_common *common;
|
||||
@ -672,6 +698,11 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid)
|
||||
ath9k_init_channels_rates(priv);
|
||||
ath9k_init_misc(priv);
|
||||
|
||||
if (product && strncmp(product, ATH_HTC_BTCOEX_PRODUCT_ID, 5) == 0) {
|
||||
ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_3WIRE;
|
||||
ath9k_init_btcoex(priv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_queues:
|
||||
@ -734,7 +765,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
|
||||
SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
|
||||
}
|
||||
|
||||
static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid)
|
||||
static int ath9k_init_device(struct ath9k_htc_priv *priv,
|
||||
u16 devid, char *product)
|
||||
{
|
||||
struct ieee80211_hw *hw = priv->hw;
|
||||
struct ath_common *common;
|
||||
@ -743,7 +775,7 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid)
|
||||
struct ath_regulatory *reg;
|
||||
|
||||
/* Bring up device */
|
||||
error = ath9k_init_priv(priv, devid);
|
||||
error = ath9k_init_priv(priv, devid, product);
|
||||
if (error != 0)
|
||||
goto err_init;
|
||||
|
||||
@ -801,7 +833,7 @@ err_init:
|
||||
}
|
||||
|
||||
int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
|
||||
u16 devid)
|
||||
u16 devid, char *product)
|
||||
{
|
||||
struct ieee80211_hw *hw;
|
||||
struct ath9k_htc_priv *priv;
|
||||
@ -835,7 +867,7 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
|
||||
/* The device may have been unplugged earlier. */
|
||||
priv->op_flags &= ~OP_UNPLUGGED;
|
||||
|
||||
ret = ath9k_init_device(priv, devid);
|
||||
ret = ath9k_init_device(priv, devid, product);
|
||||
if (ret)
|
||||
goto err_init;
|
||||
|
||||
|
@ -1210,6 +1210,12 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
|
||||
|
||||
ieee80211_wake_queues(hw);
|
||||
|
||||
if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
|
||||
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
|
||||
AR_STOMP_LOW_WLAN_WGHT);
|
||||
ath9k_hw_btcoex_enable(ah);
|
||||
ath_htc_resume_btcoex_work(priv);
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
return ret;
|
||||
@ -1254,6 +1260,12 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
|
||||
"Monitor interface removed\n");
|
||||
}
|
||||
|
||||
if (ah->btcoex_hw.enabled) {
|
||||
ath9k_hw_btcoex_disable(ah);
|
||||
if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
|
||||
ath_htc_cancel_btcoex_work(priv);
|
||||
}
|
||||
|
||||
ath9k_hw_phy_disable(ah);
|
||||
ath9k_hw_disable(ah);
|
||||
ath9k_hw_configpcipowersave(ah, 1, 1);
|
||||
@ -1585,9 +1597,10 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw,
|
||||
key->hw_key_idx = ret;
|
||||
/* push IV and Michael MIC generation to stack */
|
||||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
|
||||
if (key->alg == ALG_TKIP)
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
|
||||
if (priv->ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
|
||||
if (priv->ah->sw_mgmt_crypto &&
|
||||
key->cipher == WLAN_CIPHER_SUITE_CCMP)
|
||||
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
|
||||
ret = 0;
|
||||
}
|
||||
|
@ -462,9 +462,9 @@ void ath9k_htc_hw_free(struct htc_target *htc)
|
||||
}
|
||||
|
||||
int ath9k_htc_hw_init(struct htc_target *target,
|
||||
struct device *dev, u16 devid)
|
||||
struct device *dev, u16 devid, char *product)
|
||||
{
|
||||
if (ath9k_htc_probe_device(target, dev, devid)) {
|
||||
if (ath9k_htc_probe_device(target, dev, devid, product)) {
|
||||
printk(KERN_ERR "Failed to initialize the device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ struct htc_target *ath9k_htc_hw_alloc(void *hif_handle,
|
||||
struct device *dev);
|
||||
void ath9k_htc_hw_free(struct htc_target *htc);
|
||||
int ath9k_htc_hw_init(struct htc_target *target,
|
||||
struct device *dev, u16 devid);
|
||||
struct device *dev, u16 devid, char *product);
|
||||
void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug);
|
||||
|
||||
#endif /* HTC_HST_H */
|
||||
|
@ -355,6 +355,7 @@ struct ath9k_hw_cal_data {
|
||||
int16_t rawNoiseFloor;
|
||||
bool paprd_done;
|
||||
bool nfcal_pending;
|
||||
bool nfcal_interference;
|
||||
u16 small_signal_gain[AR9300_MAX_CHAINS];
|
||||
u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ];
|
||||
struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
|
||||
|
@ -226,9 +226,10 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
|
||||
caldata = &aphy->caldata;
|
||||
|
||||
ath_print(common, ATH_DBG_CONFIG,
|
||||
"(%u MHz) -> (%u MHz), conf_is_ht40: %d\n",
|
||||
"(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n",
|
||||
sc->sc_ah->curchan->channel,
|
||||
channel->center_freq, conf_is_ht40(conf));
|
||||
channel->center_freq, conf_is_ht40(conf),
|
||||
fastcc);
|
||||
|
||||
spin_lock_bh(&sc->sc_resetlock);
|
||||
|
||||
@ -395,7 +396,12 @@ void ath_ani_calibrate(unsigned long data)
|
||||
bool shortcal = false;
|
||||
bool aniflag = false;
|
||||
unsigned int timestamp = jiffies_to_msecs(jiffies);
|
||||
u32 cal_interval, short_cal_interval;
|
||||
u32 cal_interval, short_cal_interval, long_cal_interval;
|
||||
|
||||
if (ah->caldata && ah->caldata->nfcal_interference)
|
||||
long_cal_interval = ATH_LONG_CALINTERVAL_INT;
|
||||
else
|
||||
long_cal_interval = ATH_LONG_CALINTERVAL;
|
||||
|
||||
short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
|
||||
ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
|
||||
@ -407,7 +413,7 @@ void ath_ani_calibrate(unsigned long data)
|
||||
ath9k_ps_wakeup(sc);
|
||||
|
||||
/* Long calibration runs independently of short calibration. */
|
||||
if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
|
||||
if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
|
||||
longcal = true;
|
||||
ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
|
||||
common->ani.longcal_timer = timestamp;
|
||||
@ -1776,9 +1782,10 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
|
||||
key->hw_key_idx = ret;
|
||||
/* push IV and Michael MIC generation to stack */
|
||||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
|
||||
if (key->alg == ALG_TKIP)
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
|
||||
if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
|
||||
if (sc->sc_ah->sw_mgmt_crypto &&
|
||||
key->cipher == WLAN_CIPHER_SUITE_CCMP)
|
||||
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
|
||||
ret = 0;
|
||||
}
|
||||
|
@ -85,6 +85,8 @@ static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
|
||||
return "WMI_TGT_DETACH_CMDID";
|
||||
case WMI_TGT_TXQ_ENABLE_CMDID:
|
||||
return "WMI_TGT_TXQ_ENABLE_CMDID";
|
||||
case WMI_AGGR_LIMIT_CMD:
|
||||
return "WMI_AGGR_LIMIT_CMD";
|
||||
}
|
||||
|
||||
return "Bogus";
|
||||
|
@ -71,6 +71,7 @@ enum wmi_cmd_id {
|
||||
WMI_TX_AGGR_ENABLE_CMDID,
|
||||
WMI_TGT_DETACH_CMDID,
|
||||
WMI_TGT_TXQ_ENABLE_CMDID,
|
||||
WMI_AGGR_LIMIT_CMD = 0x0026,
|
||||
};
|
||||
|
||||
enum wmi_event_id {
|
||||
|
@ -1407,22 +1407,6 @@ static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
|
||||
return htype;
|
||||
}
|
||||
|
||||
static int get_hw_crypto_keytype(struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
if (tx_info->control.hw_key) {
|
||||
if (tx_info->control.hw_key->alg == ALG_WEP)
|
||||
return ATH9K_KEY_TYPE_WEP;
|
||||
else if (tx_info->control.hw_key->alg == ALG_TKIP)
|
||||
return ATH9K_KEY_TYPE_TKIP;
|
||||
else if (tx_info->control.hw_key->alg == ALG_CCMP)
|
||||
return ATH9K_KEY_TYPE_AES;
|
||||
}
|
||||
|
||||
return ATH9K_KEY_TYPE_CLEAR;
|
||||
}
|
||||
|
||||
static void assign_aggr_tid_seqno(struct sk_buff *skb,
|
||||
struct ath_buf *bf)
|
||||
{
|
||||
@ -1661,7 +1645,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
|
||||
bf->bf_state.bfs_paprd_timestamp = jiffies;
|
||||
bf->bf_flags = setup_tx_flags(skb, use_ldpc);
|
||||
|
||||
bf->bf_keytype = get_hw_crypto_keytype(skb);
|
||||
bf->bf_keytype = ath9k_cmn_get_hw_crypto_keytype(skb);
|
||||
if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) {
|
||||
bf->bf_frmlen += tx_info->control.hw_key->icv_len;
|
||||
bf->bf_keyix = tx_info->control.hw_key->hw_key_idx;
|
||||
|
@ -36,6 +36,7 @@
|
||||
* @ATH_DBG_PS: power save processing
|
||||
* @ATH_DBG_HWTIMER: hardware timer handling
|
||||
* @ATH_DBG_BTCOEX: bluetooth coexistance
|
||||
* @ATH_DBG_BSTUCK: stuck beacons
|
||||
* @ATH_DBG_ANY: enable all debugging
|
||||
*
|
||||
* The debug level is used to control the amount and type of debugging output
|
||||
@ -60,6 +61,7 @@ enum ATH_DEBUG {
|
||||
ATH_DBG_HWTIMER = 0x00001000,
|
||||
ATH_DBG_BTCOEX = 0x00002000,
|
||||
ATH_DBG_WMI = 0x00004000,
|
||||
ATH_DBG_BSTUCK = 0x00008000,
|
||||
ATH_DBG_ANY = 0xffffffff
|
||||
};
|
||||
|
||||
|
@ -2280,6 +2280,7 @@ out:
|
||||
|
||||
static int b43_upload_microcode(struct b43_wldev *dev)
|
||||
{
|
||||
struct wiphy *wiphy = dev->wl->hw->wiphy;
|
||||
const size_t hdr_len = sizeof(struct b43_fw_header);
|
||||
const __be32 *data;
|
||||
unsigned int i, len;
|
||||
@ -2405,6 +2406,10 @@ static int b43_upload_microcode(struct b43_wldev *dev)
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u",
|
||||
dev->fw.rev, dev->fw.patch);
|
||||
wiphy->hw_version = dev->dev->id.coreid;
|
||||
|
||||
if (b43_is_old_txhdr_format(dev)) {
|
||||
/* We're over the deadline, but we keep support for old fw
|
||||
* until it turns out to be in major conflict with something new. */
|
||||
@ -3754,17 +3759,17 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
}
|
||||
|
||||
err = -EINVAL;
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
if (key->keylen == WLAN_KEY_LEN_WEP40)
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
algorithm = B43_SEC_ALGO_WEP40;
|
||||
else
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
algorithm = B43_SEC_ALGO_WEP104;
|
||||
break;
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
algorithm = B43_SEC_ALGO_TKIP;
|
||||
break;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
algorithm = B43_SEC_ALGO_AES;
|
||||
break;
|
||||
default:
|
||||
@ -4250,6 +4255,10 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
|
||||
B43_WARN_ON(dev && b43_status(dev) > B43_STAT_INITIALIZED);
|
||||
if (!dev || b43_status(dev) != B43_STAT_INITIALIZED)
|
||||
return;
|
||||
|
||||
/* Unregister HW RNG driver */
|
||||
b43_rng_exit(dev->wl);
|
||||
|
||||
b43_set_status(dev, B43_STAT_UNINIT);
|
||||
|
||||
/* Stop the microcode PSM. */
|
||||
@ -4379,6 +4388,9 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
|
||||
|
||||
b43_set_status(dev, B43_STAT_INITIALIZED);
|
||||
|
||||
/* Register HW RNG driver */
|
||||
b43_rng_init(dev->wl);
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
@ -4984,7 +4996,6 @@ static int b43_probe(struct ssb_device *dev, const struct ssb_device_id *id)
|
||||
if (err)
|
||||
goto err_one_core_detach;
|
||||
b43_leds_register(wl->current_dev);
|
||||
b43_rng_init(wl);
|
||||
}
|
||||
|
||||
out:
|
||||
@ -5020,7 +5031,6 @@ static void b43_remove(struct ssb_device *dev)
|
||||
b43_one_core_detach(dev);
|
||||
|
||||
if (list_empty(&wl->devlist)) {
|
||||
b43_rng_exit(wl);
|
||||
b43_leds_unregister(wl);
|
||||
/* Last core on the chip unregistered.
|
||||
* We can destroy common struct b43_wl.
|
||||
|
@ -893,7 +893,7 @@ static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev)
|
||||
}
|
||||
|
||||
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */
|
||||
static void b43_nphy_gain_crtl_workarounds(struct b43_wldev *dev)
|
||||
static void b43_nphy_gain_ctrl_workarounds(struct b43_wldev *dev)
|
||||
{
|
||||
struct b43_phy_n *nphy = dev->phy.n;
|
||||
u8 i, j;
|
||||
@ -1094,11 +1094,12 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
|
||||
b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7);
|
||||
b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7);
|
||||
|
||||
b43_nphy_gain_crtl_workarounds(dev);
|
||||
b43_nphy_gain_ctrl_workarounds(dev);
|
||||
|
||||
if (dev->phy.rev < 2) {
|
||||
if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2)
|
||||
; /*TODO: b43_mhf(dev, 2, 0x0010, 0x0010, 3);*/
|
||||
b43_hf_write(dev, b43_hf_read(dev) |
|
||||
B43_HF_MLADVW);
|
||||
} else if (dev->phy.rev == 2) {
|
||||
b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0);
|
||||
b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0);
|
||||
@ -3073,6 +3074,55 @@ static int b43_nphy_cal_rx_iq(struct b43_wldev *dev,
|
||||
return b43_nphy_rev2_cal_rx_iq(dev, target, type, debug);
|
||||
}
|
||||
|
||||
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MacPhyClkSet */
|
||||
static void b43_nphy_mac_phy_clock_set(struct b43_wldev *dev, bool on)
|
||||
{
|
||||
u32 tmslow = ssb_read32(dev->dev, SSB_TMSLOW);
|
||||
if (on)
|
||||
tmslow |= SSB_TMSLOW_PHYCLK;
|
||||
else
|
||||
tmslow &= ~SSB_TMSLOW_PHYCLK;
|
||||
ssb_write32(dev->dev, SSB_TMSLOW, tmslow);
|
||||
}
|
||||
|
||||
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCoreSetState */
|
||||
static void b43_nphy_set_rx_core_state(struct b43_wldev *dev, u8 mask)
|
||||
{
|
||||
struct b43_phy *phy = &dev->phy;
|
||||
struct b43_phy_n *nphy = phy->n;
|
||||
u16 buf[16];
|
||||
|
||||
if (0 /* FIXME clk */)
|
||||
return;
|
||||
|
||||
b43_mac_suspend(dev);
|
||||
|
||||
if (nphy->hang_avoid)
|
||||
b43_nphy_stay_in_carrier_search(dev, true);
|
||||
|
||||
b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXEN,
|
||||
(mask & 0x3) << B43_NPHY_RFSEQCA_RXEN_SHIFT);
|
||||
|
||||
if (mask & 0x3 != 0x3) {
|
||||
b43_phy_write(dev, B43_NPHY_HPANT_SWTHRES, 1);
|
||||
if (dev->phy.rev >= 3) {
|
||||
/* TODO */
|
||||
}
|
||||
} else {
|
||||
b43_phy_write(dev, B43_NPHY_HPANT_SWTHRES, 0x1E);
|
||||
if (dev->phy.rev >= 3) {
|
||||
/* TODO */
|
||||
}
|
||||
}
|
||||
|
||||
b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
|
||||
|
||||
if (nphy->hang_avoid)
|
||||
b43_nphy_stay_in_carrier_search(dev, false);
|
||||
|
||||
b43_mac_enable(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init N-PHY
|
||||
* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N
|
||||
@ -3173,7 +3223,7 @@ int b43_phy_initn(struct b43_wldev *dev)
|
||||
b43_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA);
|
||||
b43_nphy_bmac_clock_fgc(dev, 0);
|
||||
|
||||
/* TODO N PHY MAC PHY Clock Set with argument 1 */
|
||||
b43_nphy_mac_phy_clock_set(dev, true);
|
||||
|
||||
b43_nphy_pa_override(dev, false);
|
||||
b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
|
||||
@ -3199,7 +3249,7 @@ int b43_phy_initn(struct b43_wldev *dev)
|
||||
}
|
||||
|
||||
if (nphy->phyrxchain != 3)
|
||||
;/* TODO N PHY RX Core Set State with phyrxchain as argument */
|
||||
b43_nphy_set_rx_core_state(dev, nphy->phyrxchain);
|
||||
if (nphy->mphase_cal_phase_id > 0)
|
||||
;/* TODO PHY Periodic Calibration Multi-Phase Restart */
|
||||
|
||||
|
@ -1623,6 +1623,7 @@ error:
|
||||
|
||||
static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
|
||||
{
|
||||
struct wiphy *wiphy = dev->wl->hw->wiphy;
|
||||
const size_t hdr_len = sizeof(struct b43legacy_fw_header);
|
||||
const __be32 *data;
|
||||
unsigned int i;
|
||||
@ -1732,6 +1733,10 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
|
||||
dev->fw.rev = fwrev;
|
||||
dev->fw.patch = fwpatch;
|
||||
|
||||
snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u",
|
||||
dev->fw.rev, dev->fw.patch);
|
||||
wiphy->hw_version = dev->dev->id.coreid;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
@ -1696,7 +1696,7 @@ static int prism2_request_scan(struct net_device *dev)
|
||||
hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE,
|
||||
HFA384X_ROAMING_FIRMWARE);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* !PRISM2_NO_STATION_MODES */
|
||||
|
@ -3056,9 +3056,9 @@ static void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
|
||||
|
||||
packet = list_entry(element, struct ipw2100_tx_packet, list);
|
||||
|
||||
IPW_DEBUG_TX("using TBD at virt=%p, phys=%p\n",
|
||||
IPW_DEBUG_TX("using TBD at virt=%p, phys=%04X\n",
|
||||
&txq->drv[txq->next],
|
||||
(void *)(txq->nic + txq->next *
|
||||
(u32) (txq->nic + txq->next *
|
||||
sizeof(struct ipw2100_bd)));
|
||||
|
||||
packet->index = txq->next;
|
||||
|
@ -3,6 +3,9 @@ config IWLWIFI
|
||||
depends on PCI && MAC80211
|
||||
select FW_LOADER
|
||||
|
||||
menu "Debugging Options"
|
||||
depends on IWLWIFI
|
||||
|
||||
config IWLWIFI_DEBUG
|
||||
bool "Enable full debugging output in iwlagn and iwl3945 drivers"
|
||||
depends on IWLWIFI
|
||||
@ -36,6 +39,12 @@ config IWLWIFI_DEBUGFS
|
||||
is a low-impact option that allows getting insight into the
|
||||
driver's state at runtime.
|
||||
|
||||
config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
|
||||
bool "Experimental uCode support"
|
||||
depends on IWLWIFI && IWLWIFI_DEBUG
|
||||
---help---
|
||||
Enable use of experimental ucode for testing and debugging.
|
||||
|
||||
config IWLWIFI_DEVICE_TRACING
|
||||
bool "iwlwifi device access tracing"
|
||||
depends on IWLWIFI
|
||||
@ -53,6 +62,7 @@ config IWLWIFI_DEVICE_TRACING
|
||||
|
||||
If unsure, say Y so we can help you better when problems
|
||||
occur.
|
||||
endmenu
|
||||
|
||||
config IWLAGN
|
||||
tristate "Intel Wireless WiFi Next Gen AGN (iwlagn)"
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_IWLAGN) += iwlagn.o
|
||||
iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o iwl-agn-ict.o
|
||||
iwlagn-objs += iwl-agn-ucode.o iwl-agn-hcmd.o iwl-agn-tx.o
|
||||
iwlagn-objs += iwl-agn-lib.o iwl-agn-rx.o iwl-agn-calib.o
|
||||
iwlagn-objs += iwl-agn-tt.o
|
||||
iwlagn-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-agn-debugfs.o
|
||||
|
||||
iwlagn-$(CONFIG_IWL4965) += iwl-4965.o
|
||||
|
@ -229,6 +229,11 @@ static struct iwl_lib_ops iwl1000_lib = {
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
.txfifo_flush = iwlagn_txfifo_flush,
|
||||
.dev_txfifo_flush = iwlagn_dev_txfifo_flush,
|
||||
.tt_ops = {
|
||||
.lower_power_detection = iwl_tt_is_low_power_state,
|
||||
.tt_power_mode = iwl_tt_current_power_mode,
|
||||
.ct_kill_check = iwl_check_for_ct_kill,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iwl_ops iwl1000_ops = {
|
||||
|
@ -1470,7 +1470,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv,
|
||||
|
||||
cmd.band = band;
|
||||
cmd.expect_beacon = 0;
|
||||
ch = ieee80211_frequency_to_channel(ch_switch->channel->center_freq);
|
||||
ch = ch_switch->channel->hw_value;
|
||||
cmd.channel = cpu_to_le16(ch);
|
||||
cmd.rxon_flags = priv->staging_rxon.flags;
|
||||
cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
|
||||
|
@ -291,7 +291,7 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
|
||||
};
|
||||
|
||||
cmd.band = priv->band == IEEE80211_BAND_2GHZ;
|
||||
ch = ieee80211_frequency_to_channel(ch_switch->channel->center_freq);
|
||||
ch = ch_switch->channel->hw_value;
|
||||
IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
|
||||
priv->active_rxon.channel, ch);
|
||||
cmd.channel = cpu_to_le16(ch);
|
||||
@ -405,6 +405,11 @@ static struct iwl_lib_ops iwl5000_lib = {
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
.txfifo_flush = iwlagn_txfifo_flush,
|
||||
.dev_txfifo_flush = iwlagn_dev_txfifo_flush,
|
||||
.tt_ops = {
|
||||
.lower_power_detection = iwl_tt_is_low_power_state,
|
||||
.tt_power_mode = iwl_tt_current_power_mode,
|
||||
.ct_kill_check = iwl_check_for_ct_kill,
|
||||
}
|
||||
};
|
||||
|
||||
static struct iwl_lib_ops iwl5150_lib = {
|
||||
@ -470,6 +475,11 @@ static struct iwl_lib_ops iwl5150_lib = {
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
.txfifo_flush = iwlagn_txfifo_flush,
|
||||
.dev_txfifo_flush = iwlagn_dev_txfifo_flush,
|
||||
.tt_ops = {
|
||||
.lower_power_detection = iwl_tt_is_low_power_state,
|
||||
.tt_power_mode = iwl_tt_current_power_mode,
|
||||
.ct_kill_check = iwl_check_for_ct_kill,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iwl_ops iwl5000_ops = {
|
||||
|
@ -214,7 +214,7 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
|
||||
};
|
||||
|
||||
cmd.band = priv->band == IEEE80211_BAND_2GHZ;
|
||||
ch = ieee80211_frequency_to_channel(ch_switch->channel->center_freq);
|
||||
ch = ch_switch->channel->hw_value;
|
||||
IWL_DEBUG_11H(priv, "channel switch from %u to %u\n",
|
||||
priv->active_rxon.channel, ch);
|
||||
cmd.channel = cpu_to_le16(ch);
|
||||
@ -330,6 +330,11 @@ static struct iwl_lib_ops iwl6000_lib = {
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
.txfifo_flush = iwlagn_txfifo_flush,
|
||||
.dev_txfifo_flush = iwlagn_dev_txfifo_flush,
|
||||
.tt_ops = {
|
||||
.lower_power_detection = iwl_tt_is_low_power_state,
|
||||
.tt_power_mode = iwl_tt_current_power_mode,
|
||||
.ct_kill_check = iwl_check_for_ct_kill,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iwl_ops iwl6000_ops = {
|
||||
|
@ -235,13 +235,13 @@ static int iwlagn_calc_rssi(struct iwl_priv *priv,
|
||||
/* data from PHY/DSP regarding signal strength, etc.,
|
||||
* contents are always there, not configurable by host
|
||||
*/
|
||||
struct iwl5000_non_cfg_phy *ncphy =
|
||||
(struct iwl5000_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
|
||||
struct iwlagn_non_cfg_phy *ncphy =
|
||||
(struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
|
||||
u32 val, rssi_a, rssi_b, rssi_c, max_rssi;
|
||||
u8 agc;
|
||||
|
||||
val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_AGC_IDX]);
|
||||
agc = (val & IWL50_OFDM_AGC_MSK) >> IWL50_OFDM_AGC_BIT_POS;
|
||||
val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]);
|
||||
agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS;
|
||||
|
||||
/* Find max rssi among 3 possible receivers.
|
||||
* These values are measured by the digital signal processor (DSP).
|
||||
@ -249,11 +249,14 @@ static int iwlagn_calc_rssi(struct iwl_priv *priv,
|
||||
* if the radio's automatic gain control (AGC) is working right.
|
||||
* AGC value (see below) will provide the "interesting" info.
|
||||
*/
|
||||
val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_RSSI_AB_IDX]);
|
||||
rssi_a = (val & IWL50_OFDM_RSSI_A_MSK) >> IWL50_OFDM_RSSI_A_BIT_POS;
|
||||
rssi_b = (val & IWL50_OFDM_RSSI_B_MSK) >> IWL50_OFDM_RSSI_B_BIT_POS;
|
||||
val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_RSSI_C_IDX]);
|
||||
rssi_c = (val & IWL50_OFDM_RSSI_C_MSK) >> IWL50_OFDM_RSSI_C_BIT_POS;
|
||||
val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]);
|
||||
rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >>
|
||||
IWLAGN_OFDM_RSSI_A_BIT_POS;
|
||||
rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >>
|
||||
IWLAGN_OFDM_RSSI_B_BIT_POS;
|
||||
val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]);
|
||||
rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >>
|
||||
IWLAGN_OFDM_RSSI_C_BIT_POS;
|
||||
|
||||
max_rssi = max_t(u32, rssi_a, rssi_b);
|
||||
max_rssi = max_t(u32, max_rssi, rssi_c);
|
||||
|
@ -1098,7 +1098,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
|
||||
if (chan->band != band)
|
||||
continue;
|
||||
|
||||
channel = ieee80211_frequency_to_channel(chan->center_freq);
|
||||
channel = chan->hw_value;
|
||||
scan_ch->channel = cpu_to_le16(channel);
|
||||
|
||||
ch_info = iwl_get_channel_info(priv, band, channel);
|
||||
|
@ -82,6 +82,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
|
||||
struct iwl_lq_sta *lq_sta);
|
||||
static void rs_fill_link_cmd(struct iwl_priv *priv,
|
||||
struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
|
||||
static void rs_stay_in_table(struct iwl_lq_sta *lq_sta);
|
||||
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
@ -502,6 +503,7 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
|
||||
u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
|
||||
u8 mcs;
|
||||
|
||||
memset(tbl, 0, sizeof(struct iwl_scale_tbl_info));
|
||||
*rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
|
||||
|
||||
if (*rate_idx == IWL_RATE_INVALID) {
|
||||
@ -848,7 +850,20 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
||||
other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
|
||||
} else {
|
||||
IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n");
|
||||
return;
|
||||
tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
|
||||
IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n",
|
||||
tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI);
|
||||
tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
|
||||
IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n",
|
||||
tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI);
|
||||
IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n",
|
||||
tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI);
|
||||
/*
|
||||
* no matching table found, let's by-pass the data collection
|
||||
* and continue to perform rate scale to find the rate table
|
||||
*/
|
||||
rs_stay_in_table(lq_sta);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -909,7 +924,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
||||
}
|
||||
/* The last TX rate is cached in lq_sta; it's set in if/else above */
|
||||
lq_sta->last_rate_n_flags = tx_rate;
|
||||
|
||||
done:
|
||||
/* See if there's a better rate or modulation mode to try. */
|
||||
if (sta && sta->supp_rates[sband->band])
|
||||
rs_rate_scale_perform(priv, skb, sta, lq_sta);
|
||||
@ -1265,7 +1280,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
|
||||
struct iwl_rate_scale_data *window = &(tbl->win[index]);
|
||||
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
|
||||
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
|
||||
u8 start_action = tbl->action;
|
||||
u8 start_action;
|
||||
u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
|
||||
u8 tx_chains_num = priv->hw_params.tx_chains_num;
|
||||
int ret = 0;
|
||||
@ -1277,6 +1292,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
|
||||
else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE &&
|
||||
tbl->action > IWL_LEGACY_SWITCH_SISO)
|
||||
tbl->action = IWL_LEGACY_SWITCH_SISO;
|
||||
start_action = tbl->action;
|
||||
for (; ;) {
|
||||
lq_sta->action_counter++;
|
||||
switch (tbl->action) {
|
||||
@ -1403,7 +1419,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
|
||||
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
|
||||
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
|
||||
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
|
||||
u8 start_action = tbl->action;
|
||||
u8 start_action;
|
||||
u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
|
||||
u8 tx_chains_num = priv->hw_params.tx_chains_num;
|
||||
u8 update_search_tbl_counter = 0;
|
||||
@ -1414,6 +1430,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
|
||||
/* stay in SISO */
|
||||
tbl->action = IWL_SISO_SWITCH_ANTENNA1;
|
||||
}
|
||||
start_action = tbl->action;
|
||||
for (;;) {
|
||||
lq_sta->action_counter++;
|
||||
switch (tbl->action) {
|
||||
@ -1541,7 +1558,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv,
|
||||
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
|
||||
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
|
||||
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
|
||||
u8 start_action = tbl->action;
|
||||
u8 start_action;
|
||||
u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
|
||||
u8 tx_chains_num = priv->hw_params.tx_chains_num;
|
||||
u8 update_search_tbl_counter = 0;
|
||||
@ -1553,6 +1570,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv,
|
||||
/* switch in SISO */
|
||||
tbl->action = IWL_MIMO2_SWITCH_SISO_A;
|
||||
}
|
||||
start_action = tbl->action;
|
||||
for (;;) {
|
||||
lq_sta->action_counter++;
|
||||
switch (tbl->action) {
|
||||
@ -1682,7 +1700,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv,
|
||||
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
|
||||
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
|
||||
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
|
||||
u8 start_action = tbl->action;
|
||||
u8 start_action;
|
||||
u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
|
||||
u8 tx_chains_num = priv->hw_params.tx_chains_num;
|
||||
int ret;
|
||||
@ -1694,6 +1712,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv,
|
||||
/* switch in SISO */
|
||||
tbl->action = IWL_MIMO3_SWITCH_SISO_A;
|
||||
}
|
||||
start_action = tbl->action;
|
||||
for (;;) {
|
||||
lq_sta->action_counter++;
|
||||
switch (tbl->action) {
|
||||
@ -2594,7 +2613,6 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
|
||||
rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
|
||||
|
||||
/* Interpret new_rate (rate_n_flags) */
|
||||
memset(&tbl_type, 0, sizeof(tbl_type));
|
||||
rs_get_tbl_info_from_mcs(new_rate, lq_sta->band,
|
||||
&tbl_type, &rate_idx);
|
||||
|
||||
@ -2694,8 +2712,18 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
|
||||
|
||||
lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
|
||||
lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
|
||||
|
||||
lq_cmd->agg_params.agg_time_limit =
|
||||
cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
|
||||
/*
|
||||
* overwrite if needed, pass aggregation time limit
|
||||
* to uCode in uSec
|
||||
*/
|
||||
if (priv && priv->cfg->agg_time_limit &&
|
||||
priv->cfg->agg_time_limit >= LINK_QUAL_AGG_TIME_LIMIT_MIN &&
|
||||
priv->cfg->agg_time_limit <= LINK_QUAL_AGG_TIME_LIMIT_MAX)
|
||||
lq_cmd->agg_params.agg_time_limit =
|
||||
cpu_to_le16(priv->cfg->agg_time_limit);
|
||||
}
|
||||
|
||||
static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
||||
|
696
drivers/net/wireless/iwlwifi/iwl-agn-tt.c
Normal file
696
drivers/net/wireless/iwlwifi/iwl-agn-tt.c
Normal file
@ -0,0 +1,696 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright(c) 2007 - 2010 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Portions of this file are derived from the ipw3945 project, as well
|
||||
* as portions of the ieee80211 subsystem header files.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Contact Information:
|
||||
* Intel Linux Wireless <ilw@linux.intel.com>
|
||||
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <net/mac80211.h>
|
||||
|
||||
#include "iwl-eeprom.h"
|
||||
#include "iwl-dev.h"
|
||||
#include "iwl-core.h"
|
||||
#include "iwl-io.h"
|
||||
#include "iwl-commands.h"
|
||||
#include "iwl-debug.h"
|
||||
#include "iwl-agn-tt.h"
|
||||
|
||||
/* default Thermal Throttling transaction table
|
||||
* Current state | Throttling Down | Throttling Up
|
||||
*=============================================================================
|
||||
* Condition Nxt State Condition Nxt State Condition Nxt State
|
||||
*-----------------------------------------------------------------------------
|
||||
* IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
|
||||
* IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
|
||||
* IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
|
||||
* IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
|
||||
*=============================================================================
|
||||
*/
|
||||
static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = {
|
||||
{IWL_TI_0, IWL_ABSOLUTE_ZERO, 104},
|
||||
{IWL_TI_1, 105, CT_KILL_THRESHOLD - 1},
|
||||
{IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
|
||||
};
|
||||
static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = {
|
||||
{IWL_TI_0, IWL_ABSOLUTE_ZERO, 95},
|
||||
{IWL_TI_2, 110, CT_KILL_THRESHOLD - 1},
|
||||
{IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
|
||||
};
|
||||
static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = {
|
||||
{IWL_TI_1, IWL_ABSOLUTE_ZERO, 100},
|
||||
{IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX},
|
||||
{IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
|
||||
};
|
||||
static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = {
|
||||
{IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD},
|
||||
{IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX},
|
||||
{IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}
|
||||
};
|
||||
|
||||
/* Advance Thermal Throttling default restriction table */
|
||||
static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = {
|
||||
{IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true },
|
||||
{IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true },
|
||||
{IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false },
|
||||
{IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false }
|
||||
};
|
||||
|
||||
bool iwl_tt_is_low_power_state(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
if (tt->state >= IWL_TI_1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
u8 iwl_tt_current_power_mode(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
return tt->tt_power_mode;
|
||||
}
|
||||
|
||||
bool iwl_ht_enabled(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
struct iwl_tt_restriction *restriction;
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
return true;
|
||||
restriction = tt->restriction + tt->state;
|
||||
return restriction->is_ht;
|
||||
}
|
||||
|
||||
static bool iwl_within_ct_kill_margin(struct iwl_priv *priv)
|
||||
{
|
||||
s32 temp = priv->temperature; /* degrees CELSIUS except specified */
|
||||
bool within_margin = false;
|
||||
|
||||
if (priv->cfg->temperature_kelvin)
|
||||
temp = KELVIN_TO_CELSIUS(priv->temperature);
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
|
||||
CT_KILL_THRESHOLD_LEGACY) ? true : false;
|
||||
else
|
||||
within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
|
||||
CT_KILL_THRESHOLD) ? true : false;
|
||||
return within_margin;
|
||||
}
|
||||
|
||||
bool iwl_check_for_ct_kill(struct iwl_priv *priv)
|
||||
{
|
||||
bool is_ct_kill = false;
|
||||
|
||||
if (iwl_within_ct_kill_margin(priv)) {
|
||||
iwl_tt_enter_ct_kill(priv);
|
||||
is_ct_kill = true;
|
||||
}
|
||||
return is_ct_kill;
|
||||
}
|
||||
|
||||
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
struct iwl_tt_restriction *restriction;
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
return IWL_ANT_OK_MULTI;
|
||||
restriction = tt->restriction + tt->state;
|
||||
return restriction->tx_stream;
|
||||
}
|
||||
|
||||
enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
struct iwl_tt_restriction *restriction;
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
return IWL_ANT_OK_MULTI;
|
||||
restriction = tt->restriction + tt->state;
|
||||
return restriction->rx_stream;
|
||||
}
|
||||
|
||||
#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */
|
||||
#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */
|
||||
|
||||
/*
|
||||
* toggle the bit to wake up uCode and check the temperature
|
||||
* if the temperature is below CT, uCode will stay awake and send card
|
||||
* state notification with CT_KILL bit clear to inform Thermal Throttling
|
||||
* Management to change state. Otherwise, uCode will go back to sleep
|
||||
* without doing anything, driver should continue the 5 seconds timer
|
||||
* to wake up uCode for temperature check until temperature drop below CT
|
||||
*/
|
||||
static void iwl_tt_check_exit_ct_kill(unsigned long data)
|
||||
{
|
||||
struct iwl_priv *priv = (struct iwl_priv *)data;
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
unsigned long flags;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
if (tt->state == IWL_TI_CT_KILL) {
|
||||
if (priv->thermal_throttle.ct_kill_toggle) {
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
priv->thermal_throttle.ct_kill_toggle = false;
|
||||
} else {
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
priv->thermal_throttle.ct_kill_toggle = true;
|
||||
}
|
||||
iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
||||
spin_lock_irqsave(&priv->reg_lock, flags);
|
||||
if (!iwl_grab_nic_access(priv))
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, flags);
|
||||
|
||||
/* Reschedule the ct_kill timer to occur in
|
||||
* CT_KILL_EXIT_DURATION seconds to ensure we get a
|
||||
* thermal update */
|
||||
IWL_DEBUG_POWER(priv, "schedule ct_kill exit timer\n");
|
||||
mod_timer(&priv->thermal_throttle.ct_kill_exit_tm,
|
||||
jiffies + CT_KILL_EXIT_DURATION * HZ);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
|
||||
bool stop)
|
||||
{
|
||||
if (stop) {
|
||||
IWL_DEBUG_POWER(priv, "Stop all queues\n");
|
||||
if (priv->mac80211_registered)
|
||||
ieee80211_stop_queues(priv->hw);
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Schedule 5 seconds CT_KILL Timer\n");
|
||||
mod_timer(&priv->thermal_throttle.ct_kill_exit_tm,
|
||||
jiffies + CT_KILL_EXIT_DURATION * HZ);
|
||||
} else {
|
||||
IWL_DEBUG_POWER(priv, "Wake all queues\n");
|
||||
if (priv->mac80211_registered)
|
||||
ieee80211_wake_queues(priv->hw);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_tt_ready_for_ct_kill(unsigned long data)
|
||||
{
|
||||
struct iwl_priv *priv = (struct iwl_priv *)data;
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
/* temperature timer expired, ready to go into CT_KILL state */
|
||||
if (tt->state != IWL_TI_CT_KILL) {
|
||||
IWL_DEBUG_POWER(priv, "entering CT_KILL state when "
|
||||
"temperature timer expired\n");
|
||||
tt->state = IWL_TI_CT_KILL;
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
iwl_perform_ct_kill_task(priv, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_prepare_ct_kill_task(struct iwl_priv *priv)
|
||||
{
|
||||
IWL_DEBUG_POWER(priv, "Prepare to enter IWL_TI_CT_KILL\n");
|
||||
/* make request to retrieve statistics information */
|
||||
iwl_send_statistics_request(priv, CMD_SYNC, false);
|
||||
/* Reschedule the ct_kill wait timer */
|
||||
mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
|
||||
jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION));
|
||||
}
|
||||
|
||||
#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY)
|
||||
#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100)
|
||||
#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90)
|
||||
|
||||
/*
|
||||
* Legacy thermal throttling
|
||||
* 1) Avoid NIC destruction due to high temperatures
|
||||
* Chip will identify dangerously high temperatures that can
|
||||
* harm the device and will power down
|
||||
* 2) Avoid the NIC power down due to high temperature
|
||||
* Throttle early enough to lower the power consumption before
|
||||
* drastic steps are needed
|
||||
*/
|
||||
static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
enum iwl_tt_state old_state;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if ((tt->tt_previous_temp) &&
|
||||
(temp > tt->tt_previous_temp) &&
|
||||
((temp - tt->tt_previous_temp) >
|
||||
IWL_TT_INCREASE_MARGIN)) {
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Temperature increase %d degree Celsius\n",
|
||||
(temp - tt->tt_previous_temp));
|
||||
}
|
||||
#endif
|
||||
old_state = tt->state;
|
||||
/* in Celsius */
|
||||
if (temp >= IWL_MINIMAL_POWER_THRESHOLD)
|
||||
tt->state = IWL_TI_CT_KILL;
|
||||
else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2)
|
||||
tt->state = IWL_TI_2;
|
||||
else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1)
|
||||
tt->state = IWL_TI_1;
|
||||
else
|
||||
tt->state = IWL_TI_0;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
tt->tt_previous_temp = temp;
|
||||
#endif
|
||||
/* stop ct_kill_waiting_tm timer */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
|
||||
if (tt->state != old_state) {
|
||||
switch (tt->state) {
|
||||
case IWL_TI_0:
|
||||
/*
|
||||
* When the system is ready to go back to IWL_TI_0
|
||||
* we only have to call iwl_power_update_mode() to
|
||||
* do so.
|
||||
*/
|
||||
break;
|
||||
case IWL_TI_1:
|
||||
tt->tt_power_mode = IWL_POWER_INDEX_3;
|
||||
break;
|
||||
case IWL_TI_2:
|
||||
tt->tt_power_mode = IWL_POWER_INDEX_4;
|
||||
break;
|
||||
default:
|
||||
tt->tt_power_mode = IWL_POWER_INDEX_5;
|
||||
break;
|
||||
}
|
||||
mutex_lock(&priv->mutex);
|
||||
if (old_state == IWL_TI_CT_KILL)
|
||||
clear_bit(STATUS_CT_KILL, &priv->status);
|
||||
if (tt->state != IWL_TI_CT_KILL &&
|
||||
iwl_power_update_mode(priv, true)) {
|
||||
/* TT state not updated
|
||||
* try again during next temperature read
|
||||
*/
|
||||
if (old_state == IWL_TI_CT_KILL)
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
tt->state = old_state;
|
||||
IWL_ERR(priv, "Cannot update power mode, "
|
||||
"TT state not updated\n");
|
||||
} else {
|
||||
if (tt->state == IWL_TI_CT_KILL) {
|
||||
if (force) {
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
iwl_perform_ct_kill_task(priv, true);
|
||||
} else {
|
||||
iwl_prepare_ct_kill_task(priv);
|
||||
tt->state = old_state;
|
||||
}
|
||||
} else if (old_state == IWL_TI_CT_KILL &&
|
||||
tt->state != IWL_TI_CT_KILL)
|
||||
iwl_perform_ct_kill_task(priv, false);
|
||||
IWL_DEBUG_POWER(priv, "Temperature state changed %u\n",
|
||||
tt->state);
|
||||
IWL_DEBUG_POWER(priv, "Power Index change to %u\n",
|
||||
tt->tt_power_mode);
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Advance thermal throttling
|
||||
* 1) Avoid NIC destruction due to high temperatures
|
||||
* Chip will identify dangerously high temperatures that can
|
||||
* harm the device and will power down
|
||||
* 2) Avoid the NIC power down due to high temperature
|
||||
* Throttle early enough to lower the power consumption before
|
||||
* drastic steps are needed
|
||||
* Actions include relaxing the power down sleep thresholds and
|
||||
* decreasing the number of TX streams
|
||||
* 3) Avoid throughput performance impact as much as possible
|
||||
*
|
||||
*=============================================================================
|
||||
* Condition Nxt State Condition Nxt State Condition Nxt State
|
||||
*-----------------------------------------------------------------------------
|
||||
* IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
|
||||
* IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
|
||||
* IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
|
||||
* IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
|
||||
*=============================================================================
|
||||
*/
|
||||
static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
int i;
|
||||
bool changed = false;
|
||||
enum iwl_tt_state old_state;
|
||||
struct iwl_tt_trans *transaction;
|
||||
|
||||
old_state = tt->state;
|
||||
for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) {
|
||||
/* based on the current TT state,
|
||||
* find the curresponding transaction table
|
||||
* each table has (IWL_TI_STATE_MAX - 1) entries
|
||||
* tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1))
|
||||
* will advance to the correct table.
|
||||
* then based on the current temperature
|
||||
* find the next state need to transaction to
|
||||
* go through all the possible (IWL_TI_STATE_MAX - 1) entries
|
||||
* in the current table to see if transaction is needed
|
||||
*/
|
||||
transaction = tt->transaction +
|
||||
((old_state * (IWL_TI_STATE_MAX - 1)) + i);
|
||||
if (temp >= transaction->tt_low &&
|
||||
temp <= transaction->tt_high) {
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if ((tt->tt_previous_temp) &&
|
||||
(temp > tt->tt_previous_temp) &&
|
||||
((temp - tt->tt_previous_temp) >
|
||||
IWL_TT_INCREASE_MARGIN)) {
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Temperature increase %d "
|
||||
"degree Celsius\n",
|
||||
(temp - tt->tt_previous_temp));
|
||||
}
|
||||
tt->tt_previous_temp = temp;
|
||||
#endif
|
||||
if (old_state !=
|
||||
transaction->next_state) {
|
||||
changed = true;
|
||||
tt->state =
|
||||
transaction->next_state;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* stop ct_kill_waiting_tm timer */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
|
||||
if (changed) {
|
||||
struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
|
||||
|
||||
if (tt->state >= IWL_TI_1) {
|
||||
/* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */
|
||||
tt->tt_power_mode = IWL_POWER_INDEX_5;
|
||||
if (!iwl_ht_enabled(priv))
|
||||
/* disable HT */
|
||||
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
|
||||
RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
|
||||
RXON_FLG_HT40_PROT_MSK |
|
||||
RXON_FLG_HT_PROT_MSK);
|
||||
else {
|
||||
/* check HT capability and set
|
||||
* according to the system HT capability
|
||||
* in case get disabled before */
|
||||
iwl_set_rxon_ht(priv, &priv->current_ht_config);
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* restore system power setting -- it will be
|
||||
* recalculated automatically.
|
||||
*/
|
||||
|
||||
/* check HT capability and set
|
||||
* according to the system HT capability
|
||||
* in case get disabled before */
|
||||
iwl_set_rxon_ht(priv, &priv->current_ht_config);
|
||||
}
|
||||
mutex_lock(&priv->mutex);
|
||||
if (old_state == IWL_TI_CT_KILL)
|
||||
clear_bit(STATUS_CT_KILL, &priv->status);
|
||||
if (tt->state != IWL_TI_CT_KILL &&
|
||||
iwl_power_update_mode(priv, true)) {
|
||||
/* TT state not updated
|
||||
* try again during next temperature read
|
||||
*/
|
||||
IWL_ERR(priv, "Cannot update power mode, "
|
||||
"TT state not updated\n");
|
||||
if (old_state == IWL_TI_CT_KILL)
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
tt->state = old_state;
|
||||
} else {
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Thermal Throttling to new state: %u\n",
|
||||
tt->state);
|
||||
if (old_state != IWL_TI_CT_KILL &&
|
||||
tt->state == IWL_TI_CT_KILL) {
|
||||
if (force) {
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Enter IWL_TI_CT_KILL\n");
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
iwl_perform_ct_kill_task(priv, true);
|
||||
} else {
|
||||
iwl_prepare_ct_kill_task(priv);
|
||||
tt->state = old_state;
|
||||
}
|
||||
} else if (old_state == IWL_TI_CT_KILL &&
|
||||
tt->state != IWL_TI_CT_KILL) {
|
||||
IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n");
|
||||
iwl_perform_ct_kill_task(priv, false);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* Card State Notification indicated reach critical temperature
|
||||
* if PSP not enable, no Thermal Throttling function will be performed
|
||||
* just set the GP1 bit to acknowledge the event
|
||||
* otherwise, go into IWL_TI_CT_KILL state
|
||||
* since Card State Notification will not provide any temperature reading
|
||||
* for Legacy mode
|
||||
* so just pass the CT_KILL temperature to iwl_legacy_tt_handler()
|
||||
* for advance mode
|
||||
* pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state
|
||||
*/
|
||||
static void iwl_bg_ct_enter(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter);
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
if (!iwl_is_ready(priv))
|
||||
return;
|
||||
|
||||
if (tt->state != IWL_TI_CT_KILL) {
|
||||
IWL_ERR(priv, "Device reached critical temperature "
|
||||
"- ucode going to sleep!\n");
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
iwl_legacy_tt_handler(priv,
|
||||
IWL_MINIMAL_POWER_THRESHOLD,
|
||||
true);
|
||||
else
|
||||
iwl_advance_tt_handler(priv,
|
||||
CT_KILL_THRESHOLD + 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Card State Notification indicated out of critical temperature
|
||||
* since Card State Notification will not provide any temperature reading
|
||||
* so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature
|
||||
* to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state
|
||||
*/
|
||||
static void iwl_bg_ct_exit(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit);
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
if (!iwl_is_ready(priv))
|
||||
return;
|
||||
|
||||
/* stop ct_kill_exit_tm timer */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
|
||||
|
||||
if (tt->state == IWL_TI_CT_KILL) {
|
||||
IWL_ERR(priv,
|
||||
"Device temperature below critical"
|
||||
"- ucode awake!\n");
|
||||
/*
|
||||
* exit from CT_KILL state
|
||||
* reset the current temperature reading
|
||||
*/
|
||||
priv->temperature = 0;
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
iwl_legacy_tt_handler(priv,
|
||||
IWL_REDUCED_PERFORMANCE_THRESHOLD_2,
|
||||
true);
|
||||
else
|
||||
iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void iwl_tt_enter_ct_kill(struct iwl_priv *priv)
|
||||
{
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_POWER(priv, "Queueing critical temperature enter.\n");
|
||||
queue_work(priv->workqueue, &priv->ct_enter);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_enter_ct_kill);
|
||||
|
||||
void iwl_tt_exit_ct_kill(struct iwl_priv *priv)
|
||||
{
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_POWER(priv, "Queueing critical temperature exit.\n");
|
||||
queue_work(priv->workqueue, &priv->ct_exit);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_exit_ct_kill);
|
||||
|
||||
static void iwl_bg_tt_work(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work);
|
||||
s32 temp = priv->temperature; /* degrees CELSIUS except specified */
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
if (priv->cfg->temperature_kelvin)
|
||||
temp = KELVIN_TO_CELSIUS(priv->temperature);
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
iwl_legacy_tt_handler(priv, temp, false);
|
||||
else
|
||||
iwl_advance_tt_handler(priv, temp, false);
|
||||
}
|
||||
|
||||
void iwl_tt_handler(struct iwl_priv *priv)
|
||||
{
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_POWER(priv, "Queueing thermal throttling work.\n");
|
||||
queue_work(priv->workqueue, &priv->tt_work);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_handler);
|
||||
|
||||
/* Thermal throttling initialization
|
||||
* For advance thermal throttling:
|
||||
* Initialize Thermal Index and temperature threshold table
|
||||
* Initialize thermal throttling restriction table
|
||||
*/
|
||||
void iwl_tt_initialize(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1);
|
||||
struct iwl_tt_trans *transaction;
|
||||
|
||||
IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling\n");
|
||||
|
||||
memset(tt, 0, sizeof(struct iwl_tt_mgmt));
|
||||
|
||||
tt->state = IWL_TI_0;
|
||||
init_timer(&priv->thermal_throttle.ct_kill_exit_tm);
|
||||
priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv;
|
||||
priv->thermal_throttle.ct_kill_exit_tm.function =
|
||||
iwl_tt_check_exit_ct_kill;
|
||||
init_timer(&priv->thermal_throttle.ct_kill_waiting_tm);
|
||||
priv->thermal_throttle.ct_kill_waiting_tm.data =
|
||||
(unsigned long)priv;
|
||||
priv->thermal_throttle.ct_kill_waiting_tm.function =
|
||||
iwl_tt_ready_for_ct_kill;
|
||||
/* setup deferred ct kill work */
|
||||
INIT_WORK(&priv->tt_work, iwl_bg_tt_work);
|
||||
INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
|
||||
INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
|
||||
|
||||
if (priv->cfg->adv_thermal_throttle) {
|
||||
IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n");
|
||||
tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) *
|
||||
IWL_TI_STATE_MAX, GFP_KERNEL);
|
||||
tt->transaction = kzalloc(sizeof(struct iwl_tt_trans) *
|
||||
IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1),
|
||||
GFP_KERNEL);
|
||||
if (!tt->restriction || !tt->transaction) {
|
||||
IWL_ERR(priv, "Fallback to Legacy Throttling\n");
|
||||
priv->thermal_throttle.advanced_tt = false;
|
||||
kfree(tt->restriction);
|
||||
tt->restriction = NULL;
|
||||
kfree(tt->transaction);
|
||||
tt->transaction = NULL;
|
||||
} else {
|
||||
transaction = tt->transaction +
|
||||
(IWL_TI_0 * (IWL_TI_STATE_MAX - 1));
|
||||
memcpy(transaction, &tt_range_0[0], size);
|
||||
transaction = tt->transaction +
|
||||
(IWL_TI_1 * (IWL_TI_STATE_MAX - 1));
|
||||
memcpy(transaction, &tt_range_1[0], size);
|
||||
transaction = tt->transaction +
|
||||
(IWL_TI_2 * (IWL_TI_STATE_MAX - 1));
|
||||
memcpy(transaction, &tt_range_2[0], size);
|
||||
transaction = tt->transaction +
|
||||
(IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1));
|
||||
memcpy(transaction, &tt_range_3[0], size);
|
||||
size = sizeof(struct iwl_tt_restriction) *
|
||||
IWL_TI_STATE_MAX;
|
||||
memcpy(tt->restriction,
|
||||
&restriction_range[0], size);
|
||||
priv->thermal_throttle.advanced_tt = true;
|
||||
}
|
||||
} else {
|
||||
IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n");
|
||||
priv->thermal_throttle.advanced_tt = false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_initialize);
|
||||
|
||||
/* cleanup thermal throttling management related memory and timer */
|
||||
void iwl_tt_exit(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
/* stop ct_kill_exit_tm timer if activated */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
|
||||
/* stop ct_kill_waiting_tm timer if activated */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
|
||||
cancel_work_sync(&priv->tt_work);
|
||||
cancel_work_sync(&priv->ct_enter);
|
||||
cancel_work_sync(&priv->ct_exit);
|
||||
|
||||
if (priv->thermal_throttle.advanced_tt) {
|
||||
/* free advance thermal throttling memory */
|
||||
kfree(tt->restriction);
|
||||
tt->restriction = NULL;
|
||||
kfree(tt->transaction);
|
||||
tt->transaction = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_exit);
|
129
drivers/net/wireless/iwlwifi/iwl-agn-tt.h
Normal file
129
drivers/net/wireless/iwlwifi/iwl-agn-tt.h
Normal file
@ -0,0 +1,129 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright(c) 2007 - 2010 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Portions of this file are derived from the ipw3945 project, as well
|
||||
* as portions of the ieee80211 subsystem header files.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Contact Information:
|
||||
* Intel Linux Wireless <ilw@linux.intel.com>
|
||||
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
||||
*****************************************************************************/
|
||||
#ifndef __iwl_tt_setting_h__
|
||||
#define __iwl_tt_setting_h__
|
||||
|
||||
#include "iwl-commands.h"
|
||||
|
||||
#define IWL_ABSOLUTE_ZERO 0
|
||||
#define IWL_ABSOLUTE_MAX 0xFFFFFFFF
|
||||
#define IWL_TT_INCREASE_MARGIN 5
|
||||
#define IWL_TT_CT_KILL_MARGIN 3
|
||||
|
||||
enum iwl_antenna_ok {
|
||||
IWL_ANT_OK_NONE,
|
||||
IWL_ANT_OK_SINGLE,
|
||||
IWL_ANT_OK_MULTI,
|
||||
};
|
||||
|
||||
/* Thermal Throttling State Machine states */
|
||||
enum iwl_tt_state {
|
||||
IWL_TI_0, /* normal temperature, system power state */
|
||||
IWL_TI_1, /* high temperature detect, low power state */
|
||||
IWL_TI_2, /* higher temperature detected, lower power state */
|
||||
IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */
|
||||
IWL_TI_STATE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tt_restriction - Thermal Throttling restriction table
|
||||
* @tx_stream: number of tx stream allowed
|
||||
* @is_ht: ht enable/disable
|
||||
* @rx_stream: number of rx stream allowed
|
||||
*
|
||||
* This table is used by advance thermal throttling management
|
||||
* based on the current thermal throttling state, and determines
|
||||
* the number of tx/rx streams and the status of HT operation.
|
||||
*/
|
||||
struct iwl_tt_restriction {
|
||||
enum iwl_antenna_ok tx_stream;
|
||||
enum iwl_antenna_ok rx_stream;
|
||||
bool is_ht;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tt_trans - Thermal Throttling transaction table
|
||||
* @next_state: next thermal throttling mode
|
||||
* @tt_low: low temperature threshold to change state
|
||||
* @tt_high: high temperature threshold to change state
|
||||
*
|
||||
* This is used by the advanced thermal throttling algorithm
|
||||
* to determine the next thermal state to go based on the
|
||||
* current temperature.
|
||||
*/
|
||||
struct iwl_tt_trans {
|
||||
enum iwl_tt_state next_state;
|
||||
u32 tt_low;
|
||||
u32 tt_high;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tt_mgnt - Thermal Throttling Management structure
|
||||
* @advanced_tt: advanced thermal throttle required
|
||||
* @state: current Thermal Throttling state
|
||||
* @tt_power_mode: Thermal Throttling power mode index
|
||||
* being used to set power level when
|
||||
* when thermal throttling state != IWL_TI_0
|
||||
* the tt_power_mode should set to different
|
||||
* power mode based on the current tt state
|
||||
* @tt_previous_temperature: last measured temperature
|
||||
* @iwl_tt_restriction: ptr to restriction tbl, used by advance
|
||||
* thermal throttling to determine how many tx/rx streams
|
||||
* should be used in tt state; and can HT be enabled or not
|
||||
* @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling
|
||||
* state transaction
|
||||
* @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature
|
||||
* @ct_kill_exit_tm: timer to exit thermal kill
|
||||
*/
|
||||
struct iwl_tt_mgmt {
|
||||
enum iwl_tt_state state;
|
||||
bool advanced_tt;
|
||||
u8 tt_power_mode;
|
||||
bool ct_kill_toggle;
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
s32 tt_previous_temp;
|
||||
#endif
|
||||
struct iwl_tt_restriction *restriction;
|
||||
struct iwl_tt_trans *transaction;
|
||||
struct timer_list ct_kill_exit_tm;
|
||||
struct timer_list ct_kill_waiting_tm;
|
||||
};
|
||||
|
||||
u8 iwl_tt_current_power_mode(struct iwl_priv *priv);
|
||||
bool iwl_tt_is_low_power_state(struct iwl_priv *priv);
|
||||
bool iwl_ht_enabled(struct iwl_priv *priv);
|
||||
bool iwl_check_for_ct_kill(struct iwl_priv *priv);
|
||||
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv);
|
||||
enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv);
|
||||
void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
|
||||
void iwl_tt_exit_ct_kill(struct iwl_priv *priv);
|
||||
void iwl_tt_handler(struct iwl_priv *priv);
|
||||
void iwl_tt_initialize(struct iwl_priv *priv);
|
||||
void iwl_tt_exit(struct iwl_priv *priv);
|
||||
|
||||
#endif /* __iwl_tt_setting_h__ */
|
@ -470,8 +470,8 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
|
||||
{
|
||||
struct ieee80211_key_conf *keyconf = info->control.hw_key;
|
||||
|
||||
switch (keyconf->alg) {
|
||||
case ALG_CCMP:
|
||||
switch (keyconf->cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
|
||||
memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
|
||||
if (info->flags & IEEE80211_TX_CTL_AMPDU)
|
||||
@ -479,20 +479,20 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
|
||||
IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n");
|
||||
break;
|
||||
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
|
||||
ieee80211_get_tkip_key(keyconf, skb_frag,
|
||||
IEEE80211_TKIP_P2_KEY, tx_cmd->key);
|
||||
IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n");
|
||||
break;
|
||||
|
||||
case ALG_WEP:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
|
||||
/* fall through */
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP |
|
||||
(keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT);
|
||||
|
||||
if (keyconf->keylen == WEP_KEY_LEN_128)
|
||||
tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
|
||||
|
||||
memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
|
||||
|
||||
IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption "
|
||||
@ -500,7 +500,7 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
|
||||
break;
|
||||
|
||||
default:
|
||||
IWL_ERR(priv, "Unknown encode alg %d\n", keyconf->alg);
|
||||
IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-aspm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
@ -763,10 +764,10 @@ static void iwl_bg_ucode_trace(unsigned long data)
|
||||
static void iwl_rx_beacon_notif(struct iwl_priv *priv,
|
||||
struct iwl_rx_mem_buffer *rxb)
|
||||
{
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
||||
struct iwl4965_beacon_notif *beacon =
|
||||
(struct iwl4965_beacon_notif *)pkt->u.raw;
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
|
||||
|
||||
IWL_DEBUG_RX(priv, "beacon status %x retries %d iss %d "
|
||||
@ -778,6 +779,8 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv,
|
||||
le32_to_cpu(beacon->low_tsf), rate);
|
||||
#endif
|
||||
|
||||
priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
|
||||
|
||||
if ((priv->iw_mode == NL80211_IFTYPE_AP) &&
|
||||
(!test_bit(STATUS_EXIT_PENDING, &priv->status)))
|
||||
queue_work(priv->workqueue, &priv->beacon_update);
|
||||
@ -1656,24 +1659,37 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context);
|
||||
static int iwl_mac_setup_register(struct iwl_priv *priv,
|
||||
struct iwlagn_ucode_capabilities *capa);
|
||||
|
||||
#define UCODE_EXPERIMENTAL_INDEX 100
|
||||
#define UCODE_EXPERIMENTAL_TAG "exp"
|
||||
|
||||
static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first)
|
||||
{
|
||||
const char *name_pre = priv->cfg->fw_name_pre;
|
||||
char tag[8];
|
||||
|
||||
if (first)
|
||||
if (first) {
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
|
||||
priv->fw_index = UCODE_EXPERIMENTAL_INDEX;
|
||||
strcpy(tag, UCODE_EXPERIMENTAL_TAG);
|
||||
} else if (priv->fw_index == UCODE_EXPERIMENTAL_INDEX) {
|
||||
#endif
|
||||
priv->fw_index = priv->cfg->ucode_api_max;
|
||||
else
|
||||
sprintf(tag, "%d", priv->fw_index);
|
||||
} else {
|
||||
priv->fw_index--;
|
||||
sprintf(tag, "%d", priv->fw_index);
|
||||
}
|
||||
|
||||
if (priv->fw_index < priv->cfg->ucode_api_min) {
|
||||
IWL_ERR(priv, "no suitable firmware found!\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sprintf(priv->firmware_name, "%s%d%s",
|
||||
name_pre, priv->fw_index, ".ucode");
|
||||
sprintf(priv->firmware_name, "%s%s%s", name_pre, tag, ".ucode");
|
||||
|
||||
IWL_DEBUG_INFO(priv, "attempting to load firmware '%s'\n",
|
||||
IWL_DEBUG_INFO(priv, "attempting to load firmware %s'%s'\n",
|
||||
(priv->fw_index == UCODE_EXPERIMENTAL_INDEX)
|
||||
? "EXPERIMENTAL " : "",
|
||||
priv->firmware_name);
|
||||
|
||||
return request_firmware_nowait(THIS_MODULE, 1, priv->firmware_name,
|
||||
@ -1968,7 +1984,9 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
|
||||
memset(&pieces, 0, sizeof(pieces));
|
||||
|
||||
if (!ucode_raw) {
|
||||
IWL_ERR(priv, "request for firmware file '%s' failed.\n",
|
||||
if (priv->fw_index <= priv->cfg->ucode_api_max)
|
||||
IWL_ERR(priv,
|
||||
"request for firmware file '%s' failed.\n",
|
||||
priv->firmware_name);
|
||||
goto try_again;
|
||||
}
|
||||
@ -2016,7 +2034,9 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
|
||||
api_max, api_ver);
|
||||
|
||||
if (build)
|
||||
sprintf(buildstr, " build %u", build);
|
||||
sprintf(buildstr, " build %u%s", build,
|
||||
(priv->fw_index == UCODE_EXPERIMENTAL_INDEX)
|
||||
? " (EXP)" : "");
|
||||
else
|
||||
buildstr[0] = '\0';
|
||||
|
||||
@ -2589,6 +2609,52 @@ int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_ct_kill_config cmd;
|
||||
struct iwl_ct_kill_throttling_config adv_cmd;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
priv->thermal_throttle.ct_kill_toggle = false;
|
||||
|
||||
if (priv->cfg->support_ct_kill_exit) {
|
||||
adv_cmd.critical_temperature_enter =
|
||||
cpu_to_le32(priv->hw_params.ct_kill_threshold);
|
||||
adv_cmd.critical_temperature_exit =
|
||||
cpu_to_le32(priv->hw_params.ct_kill_exit_threshold);
|
||||
|
||||
ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD,
|
||||
sizeof(adv_cmd), &adv_cmd);
|
||||
if (ret)
|
||||
IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
|
||||
else
|
||||
IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
|
||||
"succeeded, "
|
||||
"critical temperature enter is %d,"
|
||||
"exit is %d\n",
|
||||
priv->hw_params.ct_kill_threshold,
|
||||
priv->hw_params.ct_kill_exit_threshold);
|
||||
} else {
|
||||
cmd.critical_temperature_R =
|
||||
cpu_to_le32(priv->hw_params.ct_kill_threshold);
|
||||
|
||||
ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD,
|
||||
sizeof(cmd), &cmd);
|
||||
if (ret)
|
||||
IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
|
||||
else
|
||||
IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
|
||||
"succeeded, "
|
||||
"critical temperature is %d\n",
|
||||
priv->hw_params.ct_kill_threshold);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* iwl_alive_start - called after REPLY_ALIVE notification received
|
||||
* from protocol/runtime uCode (initialization uCode's
|
||||
@ -3060,9 +3126,7 @@ void iwl_post_associate(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
||||
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
||||
iwlcore_commit_rxon(priv);
|
||||
|
||||
iwl_setup_rxon_timing(priv, vif);
|
||||
ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
|
||||
sizeof(priv->rxon_timing), &priv->rxon_timing);
|
||||
ret = iwl_send_rxon_timing(priv, vif);
|
||||
if (ret)
|
||||
IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
|
||||
"Attempting to continue.\n");
|
||||
@ -3298,9 +3362,7 @@ void iwl_config_ap(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
||||
iwlcore_commit_rxon(priv);
|
||||
|
||||
/* RXON Timing */
|
||||
iwl_setup_rxon_timing(priv, vif);
|
||||
ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
|
||||
sizeof(priv->rxon_timing), &priv->rxon_timing);
|
||||
ret = iwl_send_rxon_timing(priv, vif);
|
||||
if (ret)
|
||||
IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
|
||||
"Attempting to continue.\n");
|
||||
@ -3386,7 +3448,9 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
* in 1X mode.
|
||||
* In legacy wep mode, we use another host command to the uCode.
|
||||
*/
|
||||
if (key->alg == ALG_WEP && !sta && vif->type != NL80211_IFTYPE_AP) {
|
||||
if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
||||
key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
|
||||
!sta) {
|
||||
if (cmd == SET_KEY)
|
||||
is_default_wep_key = !priv->key_mapping_key;
|
||||
else
|
||||
@ -3581,6 +3645,7 @@ static void iwl_mac_channel_switch(struct ieee80211_hw *hw,
|
||||
struct iwl_priv *priv = hw->priv;
|
||||
const struct iwl_channel_info *ch_info;
|
||||
struct ieee80211_conf *conf = &hw->conf;
|
||||
struct ieee80211_channel *channel = ch_switch->channel;
|
||||
struct iwl_ht_config *ht_conf = &priv->current_ht_config;
|
||||
u16 ch;
|
||||
unsigned long flags = 0;
|
||||
@ -3604,11 +3669,10 @@ static void iwl_mac_channel_switch(struct ieee80211_hw *hw,
|
||||
mutex_lock(&priv->mutex);
|
||||
if (priv->cfg->ops->lib->set_channel_switch) {
|
||||
|
||||
ch = ieee80211_frequency_to_channel(
|
||||
ch_switch->channel->center_freq);
|
||||
ch = channel->hw_value;
|
||||
if (le16_to_cpu(priv->active_rxon.channel) != ch) {
|
||||
ch_info = iwl_get_channel_info(priv,
|
||||
conf->channel->band,
|
||||
channel->band,
|
||||
ch);
|
||||
if (!is_channel_valid(ch_info)) {
|
||||
IWL_DEBUG_MAC80211(priv, "invalid channel\n");
|
||||
@ -3637,15 +3701,12 @@ static void iwl_mac_channel_switch(struct ieee80211_hw *hw,
|
||||
} else
|
||||
ht_conf->is_40mhz = false;
|
||||
|
||||
/* if we are switching from ht to 2.4 clear flags
|
||||
* from any ht related info since 2.4 does not
|
||||
* support ht */
|
||||
if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
|
||||
if (le16_to_cpu(priv->staging_rxon.channel) != ch)
|
||||
priv->staging_rxon.flags = 0;
|
||||
|
||||
iwl_set_rxon_channel(priv, conf->channel);
|
||||
iwl_set_rxon_channel(priv, channel);
|
||||
iwl_set_rxon_ht(priv, ht_conf);
|
||||
iwl_set_flags_for_band(priv, conf->channel->band,
|
||||
iwl_set_flags_for_band(priv, channel->band,
|
||||
priv->vif);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
@ -3923,8 +3984,35 @@ static struct ieee80211_ops iwl_hw_ops = {
|
||||
.sta_remove = iwl_mac_sta_remove,
|
||||
.channel_switch = iwl_mac_channel_switch,
|
||||
.flush = iwl_mac_flush,
|
||||
.tx_last_beacon = iwl_mac_tx_last_beacon,
|
||||
};
|
||||
|
||||
static void iwl_hw_detect(struct iwl_priv *priv)
|
||||
{
|
||||
priv->hw_rev = _iwl_read32(priv, CSR_HW_REV);
|
||||
priv->hw_wa_rev = _iwl_read32(priv, CSR_HW_REV_WA_REG);
|
||||
pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &priv->rev_id);
|
||||
IWL_DEBUG_INFO(priv, "HW Revision ID = 0x%X\n", priv->rev_id);
|
||||
}
|
||||
|
||||
static int iwl_set_hw_params(struct iwl_priv *priv)
|
||||
{
|
||||
priv->hw_params.max_rxq_size = RX_QUEUE_SIZE;
|
||||
priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
|
||||
if (priv->cfg->mod_params->amsdu_size_8K)
|
||||
priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_8K);
|
||||
else
|
||||
priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_4K);
|
||||
|
||||
priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL;
|
||||
|
||||
if (priv->cfg->mod_params->disable_11n)
|
||||
priv->cfg->sku &= ~IWL_SKU_N;
|
||||
|
||||
/* Device-specific setup */
|
||||
return priv->cfg->ops->lib->set_hw_params(priv);
|
||||
}
|
||||
|
||||
static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
int err = 0;
|
||||
@ -3968,6 +4056,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
/**************************
|
||||
* 2. Initializing PCI bus
|
||||
**************************/
|
||||
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
|
||||
PCIE_LINK_STATE_CLKPM);
|
||||
|
||||
if (pci_enable_device(pdev)) {
|
||||
err = -ENODEV;
|
||||
goto out_ieee80211_free_hw;
|
||||
|
@ -1367,21 +1367,24 @@ struct iwl4965_rx_non_cfg_phy {
|
||||
} __packed;
|
||||
|
||||
|
||||
#define IWL50_RX_RES_PHY_CNT 8
|
||||
#define IWL50_RX_RES_AGC_IDX 1
|
||||
#define IWL50_RX_RES_RSSI_AB_IDX 2
|
||||
#define IWL50_RX_RES_RSSI_C_IDX 3
|
||||
#define IWL50_OFDM_AGC_MSK 0xfe00
|
||||
#define IWL50_OFDM_AGC_BIT_POS 9
|
||||
#define IWL50_OFDM_RSSI_A_MSK 0x00ff
|
||||
#define IWL50_OFDM_RSSI_A_BIT_POS 0
|
||||
#define IWL50_OFDM_RSSI_B_MSK 0xff0000
|
||||
#define IWL50_OFDM_RSSI_B_BIT_POS 16
|
||||
#define IWL50_OFDM_RSSI_C_MSK 0x00ff
|
||||
#define IWL50_OFDM_RSSI_C_BIT_POS 0
|
||||
#define IWLAGN_RX_RES_PHY_CNT 8
|
||||
#define IWLAGN_RX_RES_AGC_IDX 1
|
||||
#define IWLAGN_RX_RES_RSSI_AB_IDX 2
|
||||
#define IWLAGN_RX_RES_RSSI_C_IDX 3
|
||||
#define IWLAGN_OFDM_AGC_MSK 0xfe00
|
||||
#define IWLAGN_OFDM_AGC_BIT_POS 9
|
||||
#define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff
|
||||
#define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00
|
||||
#define IWLAGN_OFDM_RSSI_A_BIT_POS 0
|
||||
#define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000
|
||||
#define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000
|
||||
#define IWLAGN_OFDM_RSSI_B_BIT_POS 16
|
||||
#define IWLAGN_OFDM_RSSI_INBAND_C_BITMSK 0x00ff
|
||||
#define IWLAGN_OFDM_RSSI_ALLBAND_C_BITMSK 0xff00
|
||||
#define IWLAGN_OFDM_RSSI_C_BIT_POS 0
|
||||
|
||||
struct iwl5000_non_cfg_phy {
|
||||
__le32 non_cfg_phy[IWL50_RX_RES_PHY_CNT]; /* up to 8 phy entries */
|
||||
struct iwlagn_non_cfg_phy {
|
||||
__le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */
|
||||
} __packed;
|
||||
|
||||
|
||||
@ -1401,7 +1404,7 @@ struct iwl_rx_phy_res {
|
||||
u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */
|
||||
__le32 rate_n_flags; /* RATE_MCS_* */
|
||||
__le16 byte_count; /* frame's byte-count */
|
||||
__le16 reserved3;
|
||||
__le16 frame_time; /* frame's time on the air */
|
||||
} __packed;
|
||||
|
||||
struct iwl_rx_mpdu_res_start {
|
||||
@ -2092,8 +2095,8 @@ struct iwl_link_qual_general_params {
|
||||
} __packed;
|
||||
|
||||
#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */
|
||||
#define LINK_QUAL_AGG_TIME_LIMIT_MAX (65535)
|
||||
#define LINK_QUAL_AGG_TIME_LIMIT_MIN (0)
|
||||
#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000)
|
||||
#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100)
|
||||
|
||||
#define LINK_QUAL_AGG_DISABLE_START_DEF (3)
|
||||
#define LINK_QUAL_AGG_DISABLE_START_MAX (255)
|
||||
@ -2110,8 +2113,10 @@ struct iwl_link_qual_general_params {
|
||||
*/
|
||||
struct iwl_link_qual_agg_params {
|
||||
|
||||
/* Maximum number of uSec in aggregation.
|
||||
* Driver should set this to 4000 (4 milliseconds). */
|
||||
/*
|
||||
*Maximum number of uSec in aggregation.
|
||||
* default set to 4000 (4 milliseconds) if not configured in .cfg
|
||||
*/
|
||||
__le16 agg_time_limit;
|
||||
|
||||
/*
|
||||
@ -2919,6 +2924,11 @@ struct iwl_scancomplete_notification {
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
enum iwl_ibss_manager {
|
||||
IWL_NOT_IBSS_MANAGER = 0,
|
||||
IWL_IBSS_MANAGER = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* BEACON_NOTIFICATION = 0x90 (notification only, not a command)
|
||||
*/
|
||||
|
@ -183,14 +183,6 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_alloc_all);
|
||||
|
||||
void iwl_hw_detect(struct iwl_priv *priv)
|
||||
{
|
||||
priv->hw_rev = _iwl_read32(priv, CSR_HW_REV);
|
||||
priv->hw_wa_rev = _iwl_read32(priv, CSR_HW_REV_WA_REG);
|
||||
pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &priv->rev_id);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_hw_detect);
|
||||
|
||||
/*
|
||||
* QoS support
|
||||
*/
|
||||
@ -247,7 +239,11 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
|
||||
ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
|
||||
|
||||
ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF;
|
||||
if (priv->cfg->ampdu_factor)
|
||||
ht_info->ampdu_factor = priv->cfg->ampdu_factor;
|
||||
ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF;
|
||||
if (priv->cfg->ampdu_density)
|
||||
ht_info->ampdu_density = priv->cfg->ampdu_density;
|
||||
|
||||
ht_info->mcs.rx_mask[0] = 0xFF;
|
||||
if (rx_chains_num >= 2)
|
||||
@ -499,17 +495,19 @@ static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
|
||||
return new_val;
|
||||
}
|
||||
|
||||
void iwl_setup_rxon_timing(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
||||
int iwl_send_rxon_timing(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
||||
{
|
||||
u64 tsf;
|
||||
s32 interval_tm, rem;
|
||||
unsigned long flags;
|
||||
struct ieee80211_conf *conf = NULL;
|
||||
u16 beacon_int;
|
||||
|
||||
conf = ieee80211_get_hw_conf(priv->hw);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
lockdep_assert_held(&priv->mutex);
|
||||
|
||||
memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd));
|
||||
|
||||
priv->rxon_timing.timestamp = cpu_to_le64(priv->timestamp);
|
||||
priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval);
|
||||
|
||||
@ -532,14 +530,16 @@ void iwl_setup_rxon_timing(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
||||
rem = do_div(tsf, interval_tm);
|
||||
priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
IWL_DEBUG_ASSOC(priv,
|
||||
"beacon interval %d beacon timer %d beacon tim %d\n",
|
||||
le16_to_cpu(priv->rxon_timing.beacon_interval),
|
||||
le32_to_cpu(priv->rxon_timing.beacon_init_val),
|
||||
le16_to_cpu(priv->rxon_timing.atim_window));
|
||||
|
||||
return iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
|
||||
sizeof(priv->rxon_timing), &priv->rxon_timing);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_setup_rxon_timing);
|
||||
EXPORT_SYMBOL(iwl_send_rxon_timing);
|
||||
|
||||
void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt)
|
||||
{
|
||||
@ -912,25 +912,18 @@ u8 iwl_get_single_channel_number(struct iwl_priv *priv,
|
||||
EXPORT_SYMBOL(iwl_get_single_channel_number);
|
||||
|
||||
/**
|
||||
* iwl_set_rxon_channel - Set the phymode and channel values in staging RXON
|
||||
* @phymode: MODE_IEEE80211A sets to 5.2GHz; all else set to 2.4GHz
|
||||
* @channel: Any channel valid for the requested phymode
|
||||
* iwl_set_rxon_channel - Set the band and channel values in staging RXON
|
||||
* @ch: requested channel as a pointer to struct ieee80211_channel
|
||||
|
||||
* In addition to setting the staging RXON, priv->phymode is also set.
|
||||
* In addition to setting the staging RXON, priv->band is also set.
|
||||
*
|
||||
* NOTE: Does not commit to the hardware; it sets appropriate bit fields
|
||||
* in the staging RXON flag structure based on the phymode
|
||||
* in the staging RXON flag structure based on the ch->band
|
||||
*/
|
||||
int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch)
|
||||
{
|
||||
enum ieee80211_band band = ch->band;
|
||||
u16 channel = ieee80211_frequency_to_channel(ch->center_freq);
|
||||
|
||||
if (!iwl_get_channel_info(priv, band, channel)) {
|
||||
IWL_DEBUG_INFO(priv, "Could not set channel to %d [%d]\n",
|
||||
channel, band);
|
||||
return -EINVAL;
|
||||
}
|
||||
u16 channel = ch->hw_value;
|
||||
|
||||
if ((le16_to_cpu(priv->staging_rxon.channel) == channel) &&
|
||||
(priv->band == band))
|
||||
@ -1328,25 +1321,6 @@ out:
|
||||
EXPORT_SYMBOL(iwl_apm_init);
|
||||
|
||||
|
||||
int iwl_set_hw_params(struct iwl_priv *priv)
|
||||
{
|
||||
priv->hw_params.max_rxq_size = RX_QUEUE_SIZE;
|
||||
priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
|
||||
if (priv->cfg->mod_params->amsdu_size_8K)
|
||||
priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_8K);
|
||||
else
|
||||
priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_4K);
|
||||
|
||||
priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL;
|
||||
|
||||
if (priv->cfg->mod_params->disable_11n)
|
||||
priv->cfg->sku &= ~IWL_SKU_N;
|
||||
|
||||
/* Device-specific setup */
|
||||
return priv->cfg->ops->lib->set_hw_params(priv);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_set_hw_params);
|
||||
|
||||
int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -1496,76 +1470,6 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_send_statistics_request);
|
||||
|
||||
void iwl_rf_kill_ct_config(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_ct_kill_config cmd;
|
||||
struct iwl_ct_kill_throttling_config adv_cmd;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
priv->thermal_throttle.ct_kill_toggle = false;
|
||||
|
||||
if (priv->cfg->support_ct_kill_exit) {
|
||||
adv_cmd.critical_temperature_enter =
|
||||
cpu_to_le32(priv->hw_params.ct_kill_threshold);
|
||||
adv_cmd.critical_temperature_exit =
|
||||
cpu_to_le32(priv->hw_params.ct_kill_exit_threshold);
|
||||
|
||||
ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD,
|
||||
sizeof(adv_cmd), &adv_cmd);
|
||||
if (ret)
|
||||
IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
|
||||
else
|
||||
IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
|
||||
"succeeded, "
|
||||
"critical temperature enter is %d,"
|
||||
"exit is %d\n",
|
||||
priv->hw_params.ct_kill_threshold,
|
||||
priv->hw_params.ct_kill_exit_threshold);
|
||||
} else {
|
||||
cmd.critical_temperature_R =
|
||||
cpu_to_le32(priv->hw_params.ct_kill_threshold);
|
||||
|
||||
ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD,
|
||||
sizeof(cmd), &cmd);
|
||||
if (ret)
|
||||
IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
|
||||
else
|
||||
IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
|
||||
"succeeded, "
|
||||
"critical temperature is %d\n",
|
||||
priv->hw_params.ct_kill_threshold);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_rf_kill_ct_config);
|
||||
|
||||
|
||||
/*
|
||||
* CARD_STATE_CMD
|
||||
*
|
||||
* Use: Sets the device's internal card state to enable, disable, or halt
|
||||
*
|
||||
* When in the 'enable' state the card operates as normal.
|
||||
* When in the 'disable' state, the card enters into a low power mode.
|
||||
* When in the 'halt' state, the card is shut down and must be fully
|
||||
* restarted to come back on.
|
||||
*/
|
||||
int iwl_send_card_state(struct iwl_priv *priv, u32 flags, u8 meta_flag)
|
||||
{
|
||||
struct iwl_host_cmd cmd = {
|
||||
.id = REPLY_CARD_STATE_CMD,
|
||||
.len = sizeof(u32),
|
||||
.data = &flags,
|
||||
.flags = meta_flag,
|
||||
};
|
||||
|
||||
return iwl_send_cmd(priv, &cmd);
|
||||
}
|
||||
|
||||
void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
|
||||
struct iwl_rx_mem_buffer *rxb)
|
||||
{
|
||||
@ -1648,6 +1552,14 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_mac_conf_tx);
|
||||
|
||||
int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct iwl_priv *priv = hw->priv;
|
||||
|
||||
return priv->ibss_manager == IWL_IBSS_MANAGER;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iwl_mac_tx_last_beacon);
|
||||
|
||||
static void iwl_ht_conf(struct iwl_priv *priv,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
@ -2014,6 +1926,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
|
||||
struct iwl_priv *priv = hw->priv;
|
||||
const struct iwl_channel_info *ch_info;
|
||||
struct ieee80211_conf *conf = &hw->conf;
|
||||
struct ieee80211_channel *channel = conf->channel;
|
||||
struct iwl_ht_config *ht_conf = &priv->current_ht_config;
|
||||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
@ -2023,7 +1936,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
|
||||
mutex_lock(&priv->mutex);
|
||||
|
||||
IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n",
|
||||
conf->channel->hw_value, changed);
|
||||
channel->hw_value, changed);
|
||||
|
||||
if (unlikely(!priv->cfg->mod_params->disable_hw_scan &&
|
||||
test_bit(STATUS_SCANNING, &priv->status))) {
|
||||
@ -2054,8 +1967,8 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
|
||||
if (scan_active)
|
||||
goto set_ch_out;
|
||||
|
||||
ch = ieee80211_frequency_to_channel(conf->channel->center_freq);
|
||||
ch_info = iwl_get_channel_info(priv, conf->channel->band, ch);
|
||||
ch = channel->hw_value;
|
||||
ch_info = iwl_get_channel_info(priv, channel->band, ch);
|
||||
if (!is_channel_valid(ch_info)) {
|
||||
IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
|
||||
ret = -EINVAL;
|
||||
@ -2086,16 +1999,13 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
|
||||
* from BSS config in iwl_ht_conf */
|
||||
ht_conf->ht_protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
|
||||
|
||||
/* if we are switching from ht to 2.4 clear flags
|
||||
* from any ht related info since 2.4 does not
|
||||
* support ht */
|
||||
if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
|
||||
priv->staging_rxon.flags = 0;
|
||||
|
||||
iwl_set_rxon_channel(priv, conf->channel);
|
||||
iwl_set_rxon_channel(priv, channel);
|
||||
iwl_set_rxon_ht(priv, ht_conf);
|
||||
|
||||
iwl_set_flags_for_band(priv, conf->channel->band, priv->vif);
|
||||
iwl_set_flags_for_band(priv, channel->band, priv->vif);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (priv->cfg->ops->lib->update_bcast_station)
|
||||
|
@ -136,6 +136,12 @@ struct iwl_temp_ops {
|
||||
void (*set_calib_version)(struct iwl_priv *priv);
|
||||
};
|
||||
|
||||
struct iwl_tt_ops {
|
||||
bool (*lower_power_detection)(struct iwl_priv *priv);
|
||||
u8 (*tt_power_mode)(struct iwl_priv *priv);
|
||||
bool (*ct_kill_check)(struct iwl_priv *priv);
|
||||
};
|
||||
|
||||
struct iwl_lib_ops {
|
||||
/* set hw dependent parameters */
|
||||
int (*set_hw_params)(struct iwl_priv *priv);
|
||||
@ -212,6 +218,9 @@ struct iwl_lib_ops {
|
||||
void (*dev_txfifo_flush)(struct iwl_priv *priv, u16 flush_control);
|
||||
|
||||
struct iwl_debugfs_ops debugfs_ops;
|
||||
|
||||
/* thermal throttling */
|
||||
struct iwl_tt_ops tt_ops;
|
||||
};
|
||||
|
||||
struct iwl_led_ops {
|
||||
@ -269,6 +278,11 @@ struct iwl_mod_params {
|
||||
* @chain_noise_calib_by_driver: driver has the capability to perform
|
||||
* chain noise calibration operation
|
||||
* @scan_antennas: available antenna for scan operation
|
||||
* @need_dc_calib: need to perform init dc calibration
|
||||
* @bt_statistics: use BT version of statistics notification
|
||||
* @agg_time_limit: maximum number of uSec in aggregation
|
||||
* @ampdu_factor: Maximum A-MPDU length factor
|
||||
* @ampdu_density: Minimum A-MPDU spacing
|
||||
*
|
||||
* We enable the driver to be backward compatible wrt API version. The
|
||||
* driver specifies which APIs it supports (with @ucode_api_max being the
|
||||
@ -339,6 +353,9 @@ struct iwl_cfg {
|
||||
u8 scan_tx_antennas[IEEE80211_NUM_BANDS];
|
||||
const bool need_dc_calib;
|
||||
const bool bt_statistics;
|
||||
u16 agg_time_limit;
|
||||
u8 ampdu_factor;
|
||||
u8 ampdu_density;
|
||||
};
|
||||
|
||||
/***************************
|
||||
@ -347,10 +364,10 @@ struct iwl_cfg {
|
||||
|
||||
struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg,
|
||||
struct ieee80211_ops *hw_ops);
|
||||
void iwl_hw_detect(struct iwl_priv *priv);
|
||||
void iwl_activate_qos(struct iwl_priv *priv);
|
||||
int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
|
||||
const struct ieee80211_tx_queue_params *params);
|
||||
int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw);
|
||||
void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt);
|
||||
int iwl_check_rxon_cmd(struct iwl_priv *priv);
|
||||
int iwl_full_rxon_required(struct iwl_priv *priv);
|
||||
@ -372,7 +389,6 @@ int iwl_set_decrypted_flag(struct iwl_priv *priv,
|
||||
u32 decrypt_res,
|
||||
struct ieee80211_rx_status *stats);
|
||||
void iwl_irq_handle_error(struct iwl_priv *priv);
|
||||
int iwl_set_hw_params(struct iwl_priv *priv);
|
||||
void iwl_post_associate(struct iwl_priv *priv, struct ieee80211_vif *vif);
|
||||
void iwl_bss_info_changed(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
@ -527,7 +543,6 @@ int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
|
||||
int iwl_mac_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req);
|
||||
void iwl_bg_start_internal_scan(struct work_struct *work);
|
||||
void iwl_internal_short_hw_scan(struct iwl_priv *priv);
|
||||
int iwl_force_reset(struct iwl_priv *priv, int mode, bool external);
|
||||
u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
|
||||
@ -539,9 +554,6 @@ u16 iwl_get_active_dwell_time(struct iwl_priv *priv,
|
||||
u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
|
||||
enum ieee80211_band band,
|
||||
struct ieee80211_vif *vif);
|
||||
void iwl_bg_scan_check(struct work_struct *data);
|
||||
void iwl_bg_abort_scan(struct work_struct *work);
|
||||
void iwl_bg_scan_completed(struct work_struct *work);
|
||||
void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
|
||||
|
||||
/* For faster active scanning, scan will move to the next channel if fewer than
|
||||
@ -580,8 +592,6 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len,
|
||||
|
||||
int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
|
||||
|
||||
int iwl_send_card_state(struct iwl_priv *priv, u32 flags,
|
||||
u8 meta_flag);
|
||||
|
||||
/*****************************************************
|
||||
* PCI *
|
||||
@ -695,7 +705,6 @@ static inline int iwl_is_ready_rf(struct iwl_priv *priv)
|
||||
return iwl_is_ready(priv);
|
||||
}
|
||||
|
||||
extern void iwl_rf_kill_ct_config(struct iwl_priv *priv);
|
||||
extern void iwl_send_bt_config(struct iwl_priv *priv);
|
||||
extern int iwl_send_statistics_request(struct iwl_priv *priv,
|
||||
u8 flags, bool clear);
|
||||
@ -704,7 +713,7 @@ extern int iwl_send_lq_cmd(struct iwl_priv *priv,
|
||||
void iwl_apm_stop(struct iwl_priv *priv);
|
||||
int iwl_apm_init(struct iwl_priv *priv);
|
||||
|
||||
void iwl_setup_rxon_timing(struct iwl_priv *priv, struct ieee80211_vif *vif);
|
||||
int iwl_send_rxon_timing(struct iwl_priv *priv, struct ieee80211_vif *vif);
|
||||
static inline int iwl_send_rxon_assoc(struct iwl_priv *priv)
|
||||
{
|
||||
return priv->cfg->ops->hcmd->rxon_assoc(priv);
|
||||
|
@ -467,8 +467,7 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
|
||||
for (i = 0; i < supp_band->n_channels; i++)
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%d: %ddBm: BSS%s%s, %s.\n",
|
||||
ieee80211_frequency_to_channel(
|
||||
channels[i].center_freq),
|
||||
channels[i].hw_value,
|
||||
channels[i].max_power,
|
||||
channels[i].flags & IEEE80211_CHAN_RADAR ?
|
||||
" (IEEE 802.11h required)" : "",
|
||||
@ -491,8 +490,7 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
|
||||
for (i = 0; i < supp_band->n_channels; i++)
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%d: %ddBm: BSS%s%s, %s.\n",
|
||||
ieee80211_frequency_to_channel(
|
||||
channels[i].center_freq),
|
||||
channels[i].hw_value,
|
||||
channels[i].max_power,
|
||||
channels[i].flags & IEEE80211_CHAN_RADAR ?
|
||||
" (IEEE 802.11h required)" : "",
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "iwl-led.h"
|
||||
#include "iwl-power.h"
|
||||
#include "iwl-agn-rs.h"
|
||||
#include "iwl-agn-tt.h"
|
||||
|
||||
struct iwl_tx_queue;
|
||||
|
||||
@ -420,7 +421,7 @@ struct iwl_tid_data {
|
||||
};
|
||||
|
||||
struct iwl_hw_key {
|
||||
enum ieee80211_key_alg alg;
|
||||
u32 cipher;
|
||||
int keylen;
|
||||
u8 keyidx;
|
||||
u8 key[32];
|
||||
@ -434,7 +435,13 @@ union iwl_ht_rate_supp {
|
||||
};
|
||||
};
|
||||
|
||||
#define CFG_HT_RX_AMPDU_FACTOR_DEF (0x3)
|
||||
#define CFG_HT_RX_AMPDU_FACTOR_8K (0x0)
|
||||
#define CFG_HT_RX_AMPDU_FACTOR_16K (0x1)
|
||||
#define CFG_HT_RX_AMPDU_FACTOR_32K (0x2)
|
||||
#define CFG_HT_RX_AMPDU_FACTOR_64K (0x3)
|
||||
#define CFG_HT_RX_AMPDU_FACTOR_DEF CFG_HT_RX_AMPDU_FACTOR_64K
|
||||
#define CFG_HT_RX_AMPDU_FACTOR_MAX CFG_HT_RX_AMPDU_FACTOR_64K
|
||||
#define CFG_HT_RX_AMPDU_FACTOR_MIN CFG_HT_RX_AMPDU_FACTOR_8K
|
||||
|
||||
/*
|
||||
* Maximal MPDU density for TX aggregation
|
||||
@ -443,8 +450,13 @@ union iwl_ht_rate_supp {
|
||||
* 6 - 8us density
|
||||
* 7 - 16us density
|
||||
*/
|
||||
#define CFG_HT_MPDU_DENSITY_2USEC (0x4)
|
||||
#define CFG_HT_MPDU_DENSITY_4USEC (0x5)
|
||||
#define CFG_HT_MPDU_DENSITY_8USEC (0x6)
|
||||
#define CFG_HT_MPDU_DENSITY_16USEC (0x7)
|
||||
#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC
|
||||
#define CFG_HT_MPDU_DENSITY_MAX CFG_HT_MPDU_DENSITY_16USEC
|
||||
#define CFG_HT_MPDU_DENSITY_MIN (0x1)
|
||||
|
||||
struct iwl_ht_config {
|
||||
/* self configuration data */
|
||||
@ -1052,7 +1064,6 @@ struct iwl_event_log {
|
||||
#define IWL_DEF_MONITORING_PERIOD (1000)
|
||||
#define IWL_LONG_MONITORING_PERIOD (5000)
|
||||
#define IWL_ONE_HUNDRED_MSECS (100)
|
||||
#define IWL_SIXTY_SECS (60000)
|
||||
|
||||
enum iwl_reset {
|
||||
IWL_RF_RESET = 0,
|
||||
@ -1110,6 +1121,9 @@ struct iwl_priv {
|
||||
u32 ucode_beacon_time;
|
||||
int missed_beacon_threshold;
|
||||
|
||||
/* track IBSS manager (last beacon) status */
|
||||
u32 ibss_manager;
|
||||
|
||||
/* storing the jiffies when the plcp error rate is received */
|
||||
unsigned long plcp_jiffies;
|
||||
|
||||
|
@ -192,47 +192,6 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
|
||||
IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1);
|
||||
}
|
||||
|
||||
/* default Thermal Throttling transaction table
|
||||
* Current state | Throttling Down | Throttling Up
|
||||
*=============================================================================
|
||||
* Condition Nxt State Condition Nxt State Condition Nxt State
|
||||
*-----------------------------------------------------------------------------
|
||||
* IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
|
||||
* IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
|
||||
* IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
|
||||
* IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
|
||||
*=============================================================================
|
||||
*/
|
||||
static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = {
|
||||
{IWL_TI_0, IWL_ABSOLUTE_ZERO, 104},
|
||||
{IWL_TI_1, 105, CT_KILL_THRESHOLD - 1},
|
||||
{IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
|
||||
};
|
||||
static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = {
|
||||
{IWL_TI_0, IWL_ABSOLUTE_ZERO, 95},
|
||||
{IWL_TI_2, 110, CT_KILL_THRESHOLD - 1},
|
||||
{IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
|
||||
};
|
||||
static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = {
|
||||
{IWL_TI_1, IWL_ABSOLUTE_ZERO, 100},
|
||||
{IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX},
|
||||
{IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
|
||||
};
|
||||
static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = {
|
||||
{IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD},
|
||||
{IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX},
|
||||
{IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}
|
||||
};
|
||||
|
||||
/* Advance Thermal Throttling default restriction table */
|
||||
static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = {
|
||||
{IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true },
|
||||
{IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true },
|
||||
{IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false },
|
||||
{IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false }
|
||||
};
|
||||
|
||||
|
||||
static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv,
|
||||
struct iwl_powertable_cmd *cmd)
|
||||
{
|
||||
@ -308,7 +267,6 @@ static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)
|
||||
int iwl_power_update_mode(struct iwl_priv *priv, bool force)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
|
||||
bool update_chains;
|
||||
struct iwl_powertable_cmd cmd;
|
||||
@ -325,9 +283,13 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
|
||||
else if (priv->cfg->supports_idle &&
|
||||
priv->hw->conf.flags & IEEE80211_CONF_IDLE)
|
||||
iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_5, 20);
|
||||
else if (tt->state >= IWL_TI_1)
|
||||
iwl_static_sleep_cmd(priv, &cmd, tt->tt_power_mode, dtimper);
|
||||
else if (!enabled)
|
||||
else if (priv->cfg->ops->lib->tt_ops.lower_power_detection &&
|
||||
priv->cfg->ops->lib->tt_ops.tt_power_mode &&
|
||||
priv->cfg->ops->lib->tt_ops.lower_power_detection(priv)) {
|
||||
/* in thermal throttling low power state */
|
||||
iwl_static_sleep_cmd(priv, &cmd,
|
||||
priv->cfg->ops->lib->tt_ops.tt_power_mode(priv), dtimper);
|
||||
} else if (!enabled)
|
||||
iwl_power_sleep_cam_cmd(priv, &cmd);
|
||||
else if (priv->power_data.debug_sleep_level_override >= 0)
|
||||
iwl_static_sleep_cmd(priv, &cmd,
|
||||
@ -367,592 +329,6 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_power_update_mode);
|
||||
|
||||
bool iwl_ht_enabled(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
struct iwl_tt_restriction *restriction;
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
return true;
|
||||
restriction = tt->restriction + tt->state;
|
||||
return restriction->is_ht;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_ht_enabled);
|
||||
|
||||
bool iwl_within_ct_kill_margin(struct iwl_priv *priv)
|
||||
{
|
||||
s32 temp = priv->temperature; /* degrees CELSIUS except specified */
|
||||
bool within_margin = false;
|
||||
|
||||
if (priv->cfg->temperature_kelvin)
|
||||
temp = KELVIN_TO_CELSIUS(priv->temperature);
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
|
||||
CT_KILL_THRESHOLD_LEGACY) ? true : false;
|
||||
else
|
||||
within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
|
||||
CT_KILL_THRESHOLD) ? true : false;
|
||||
return within_margin;
|
||||
}
|
||||
|
||||
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
struct iwl_tt_restriction *restriction;
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
return IWL_ANT_OK_MULTI;
|
||||
restriction = tt->restriction + tt->state;
|
||||
return restriction->tx_stream;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tx_ant_restriction);
|
||||
|
||||
enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
struct iwl_tt_restriction *restriction;
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
return IWL_ANT_OK_MULTI;
|
||||
restriction = tt->restriction + tt->state;
|
||||
return restriction->rx_stream;
|
||||
}
|
||||
|
||||
#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */
|
||||
#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */
|
||||
|
||||
/*
|
||||
* toggle the bit to wake up uCode and check the temperature
|
||||
* if the temperature is below CT, uCode will stay awake and send card
|
||||
* state notification with CT_KILL bit clear to inform Thermal Throttling
|
||||
* Management to change state. Otherwise, uCode will go back to sleep
|
||||
* without doing anything, driver should continue the 5 seconds timer
|
||||
* to wake up uCode for temperature check until temperature drop below CT
|
||||
*/
|
||||
static void iwl_tt_check_exit_ct_kill(unsigned long data)
|
||||
{
|
||||
struct iwl_priv *priv = (struct iwl_priv *)data;
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
unsigned long flags;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
if (tt->state == IWL_TI_CT_KILL) {
|
||||
if (priv->thermal_throttle.ct_kill_toggle) {
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
priv->thermal_throttle.ct_kill_toggle = false;
|
||||
} else {
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
priv->thermal_throttle.ct_kill_toggle = true;
|
||||
}
|
||||
iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
||||
spin_lock_irqsave(&priv->reg_lock, flags);
|
||||
if (!iwl_grab_nic_access(priv))
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, flags);
|
||||
|
||||
/* Reschedule the ct_kill timer to occur in
|
||||
* CT_KILL_EXIT_DURATION seconds to ensure we get a
|
||||
* thermal update */
|
||||
IWL_DEBUG_POWER(priv, "schedule ct_kill exit timer\n");
|
||||
mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies +
|
||||
CT_KILL_EXIT_DURATION * HZ);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
|
||||
bool stop)
|
||||
{
|
||||
if (stop) {
|
||||
IWL_DEBUG_POWER(priv, "Stop all queues\n");
|
||||
if (priv->mac80211_registered)
|
||||
ieee80211_stop_queues(priv->hw);
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Schedule 5 seconds CT_KILL Timer\n");
|
||||
mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies +
|
||||
CT_KILL_EXIT_DURATION * HZ);
|
||||
} else {
|
||||
IWL_DEBUG_POWER(priv, "Wake all queues\n");
|
||||
if (priv->mac80211_registered)
|
||||
ieee80211_wake_queues(priv->hw);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_tt_ready_for_ct_kill(unsigned long data)
|
||||
{
|
||||
struct iwl_priv *priv = (struct iwl_priv *)data;
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
/* temperature timer expired, ready to go into CT_KILL state */
|
||||
if (tt->state != IWL_TI_CT_KILL) {
|
||||
IWL_DEBUG_POWER(priv, "entering CT_KILL state when temperature timer expired\n");
|
||||
tt->state = IWL_TI_CT_KILL;
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
iwl_perform_ct_kill_task(priv, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_prepare_ct_kill_task(struct iwl_priv *priv)
|
||||
{
|
||||
IWL_DEBUG_POWER(priv, "Prepare to enter IWL_TI_CT_KILL\n");
|
||||
/* make request to retrieve statistics information */
|
||||
iwl_send_statistics_request(priv, CMD_SYNC, false);
|
||||
/* Reschedule the ct_kill wait timer */
|
||||
mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
|
||||
jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION));
|
||||
}
|
||||
|
||||
#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY)
|
||||
#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100)
|
||||
#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90)
|
||||
|
||||
/*
|
||||
* Legacy thermal throttling
|
||||
* 1) Avoid NIC destruction due to high temperatures
|
||||
* Chip will identify dangerously high temperatures that can
|
||||
* harm the device and will power down
|
||||
* 2) Avoid the NIC power down due to high temperature
|
||||
* Throttle early enough to lower the power consumption before
|
||||
* drastic steps are needed
|
||||
*/
|
||||
static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
enum iwl_tt_state old_state;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if ((tt->tt_previous_temp) &&
|
||||
(temp > tt->tt_previous_temp) &&
|
||||
((temp - tt->tt_previous_temp) >
|
||||
IWL_TT_INCREASE_MARGIN)) {
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Temperature increase %d degree Celsius\n",
|
||||
(temp - tt->tt_previous_temp));
|
||||
}
|
||||
#endif
|
||||
old_state = tt->state;
|
||||
/* in Celsius */
|
||||
if (temp >= IWL_MINIMAL_POWER_THRESHOLD)
|
||||
tt->state = IWL_TI_CT_KILL;
|
||||
else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2)
|
||||
tt->state = IWL_TI_2;
|
||||
else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1)
|
||||
tt->state = IWL_TI_1;
|
||||
else
|
||||
tt->state = IWL_TI_0;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
tt->tt_previous_temp = temp;
|
||||
#endif
|
||||
/* stop ct_kill_waiting_tm timer */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
|
||||
if (tt->state != old_state) {
|
||||
switch (tt->state) {
|
||||
case IWL_TI_0:
|
||||
/*
|
||||
* When the system is ready to go back to IWL_TI_0
|
||||
* we only have to call iwl_power_update_mode() to
|
||||
* do so.
|
||||
*/
|
||||
break;
|
||||
case IWL_TI_1:
|
||||
tt->tt_power_mode = IWL_POWER_INDEX_3;
|
||||
break;
|
||||
case IWL_TI_2:
|
||||
tt->tt_power_mode = IWL_POWER_INDEX_4;
|
||||
break;
|
||||
default:
|
||||
tt->tt_power_mode = IWL_POWER_INDEX_5;
|
||||
break;
|
||||
}
|
||||
mutex_lock(&priv->mutex);
|
||||
if (old_state == IWL_TI_CT_KILL)
|
||||
clear_bit(STATUS_CT_KILL, &priv->status);
|
||||
if (tt->state != IWL_TI_CT_KILL &&
|
||||
iwl_power_update_mode(priv, true)) {
|
||||
/* TT state not updated
|
||||
* try again during next temperature read
|
||||
*/
|
||||
if (old_state == IWL_TI_CT_KILL)
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
tt->state = old_state;
|
||||
IWL_ERR(priv, "Cannot update power mode, "
|
||||
"TT state not updated\n");
|
||||
} else {
|
||||
if (tt->state == IWL_TI_CT_KILL) {
|
||||
if (force) {
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
iwl_perform_ct_kill_task(priv, true);
|
||||
} else {
|
||||
iwl_prepare_ct_kill_task(priv);
|
||||
tt->state = old_state;
|
||||
}
|
||||
} else if (old_state == IWL_TI_CT_KILL &&
|
||||
tt->state != IWL_TI_CT_KILL)
|
||||
iwl_perform_ct_kill_task(priv, false);
|
||||
IWL_DEBUG_POWER(priv, "Temperature state changed %u\n",
|
||||
tt->state);
|
||||
IWL_DEBUG_POWER(priv, "Power Index change to %u\n",
|
||||
tt->tt_power_mode);
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Advance thermal throttling
|
||||
* 1) Avoid NIC destruction due to high temperatures
|
||||
* Chip will identify dangerously high temperatures that can
|
||||
* harm the device and will power down
|
||||
* 2) Avoid the NIC power down due to high temperature
|
||||
* Throttle early enough to lower the power consumption before
|
||||
* drastic steps are needed
|
||||
* Actions include relaxing the power down sleep thresholds and
|
||||
* decreasing the number of TX streams
|
||||
* 3) Avoid throughput performance impact as much as possible
|
||||
*
|
||||
*=============================================================================
|
||||
* Condition Nxt State Condition Nxt State Condition Nxt State
|
||||
*-----------------------------------------------------------------------------
|
||||
* IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
|
||||
* IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
|
||||
* IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
|
||||
* IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
|
||||
*=============================================================================
|
||||
*/
|
||||
static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
int i;
|
||||
bool changed = false;
|
||||
enum iwl_tt_state old_state;
|
||||
struct iwl_tt_trans *transaction;
|
||||
|
||||
old_state = tt->state;
|
||||
for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) {
|
||||
/* based on the current TT state,
|
||||
* find the curresponding transaction table
|
||||
* each table has (IWL_TI_STATE_MAX - 1) entries
|
||||
* tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1))
|
||||
* will advance to the correct table.
|
||||
* then based on the current temperature
|
||||
* find the next state need to transaction to
|
||||
* go through all the possible (IWL_TI_STATE_MAX - 1) entries
|
||||
* in the current table to see if transaction is needed
|
||||
*/
|
||||
transaction = tt->transaction +
|
||||
((old_state * (IWL_TI_STATE_MAX - 1)) + i);
|
||||
if (temp >= transaction->tt_low &&
|
||||
temp <= transaction->tt_high) {
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if ((tt->tt_previous_temp) &&
|
||||
(temp > tt->tt_previous_temp) &&
|
||||
((temp - tt->tt_previous_temp) >
|
||||
IWL_TT_INCREASE_MARGIN)) {
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Temperature increase %d "
|
||||
"degree Celsius\n",
|
||||
(temp - tt->tt_previous_temp));
|
||||
}
|
||||
tt->tt_previous_temp = temp;
|
||||
#endif
|
||||
if (old_state !=
|
||||
transaction->next_state) {
|
||||
changed = true;
|
||||
tt->state =
|
||||
transaction->next_state;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* stop ct_kill_waiting_tm timer */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
|
||||
if (changed) {
|
||||
struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
|
||||
|
||||
if (tt->state >= IWL_TI_1) {
|
||||
/* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */
|
||||
tt->tt_power_mode = IWL_POWER_INDEX_5;
|
||||
if (!iwl_ht_enabled(priv))
|
||||
/* disable HT */
|
||||
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
|
||||
RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
|
||||
RXON_FLG_HT40_PROT_MSK |
|
||||
RXON_FLG_HT_PROT_MSK);
|
||||
else {
|
||||
/* check HT capability and set
|
||||
* according to the system HT capability
|
||||
* in case get disabled before */
|
||||
iwl_set_rxon_ht(priv, &priv->current_ht_config);
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* restore system power setting -- it will be
|
||||
* recalculated automatically.
|
||||
*/
|
||||
|
||||
/* check HT capability and set
|
||||
* according to the system HT capability
|
||||
* in case get disabled before */
|
||||
iwl_set_rxon_ht(priv, &priv->current_ht_config);
|
||||
}
|
||||
mutex_lock(&priv->mutex);
|
||||
if (old_state == IWL_TI_CT_KILL)
|
||||
clear_bit(STATUS_CT_KILL, &priv->status);
|
||||
if (tt->state != IWL_TI_CT_KILL &&
|
||||
iwl_power_update_mode(priv, true)) {
|
||||
/* TT state not updated
|
||||
* try again during next temperature read
|
||||
*/
|
||||
IWL_ERR(priv, "Cannot update power mode, "
|
||||
"TT state not updated\n");
|
||||
if (old_state == IWL_TI_CT_KILL)
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
tt->state = old_state;
|
||||
} else {
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Thermal Throttling to new state: %u\n",
|
||||
tt->state);
|
||||
if (old_state != IWL_TI_CT_KILL &&
|
||||
tt->state == IWL_TI_CT_KILL) {
|
||||
if (force) {
|
||||
IWL_DEBUG_POWER(priv,
|
||||
"Enter IWL_TI_CT_KILL\n");
|
||||
set_bit(STATUS_CT_KILL, &priv->status);
|
||||
iwl_perform_ct_kill_task(priv, true);
|
||||
} else {
|
||||
iwl_prepare_ct_kill_task(priv);
|
||||
tt->state = old_state;
|
||||
}
|
||||
} else if (old_state == IWL_TI_CT_KILL &&
|
||||
tt->state != IWL_TI_CT_KILL) {
|
||||
IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n");
|
||||
iwl_perform_ct_kill_task(priv, false);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* Card State Notification indicated reach critical temperature
|
||||
* if PSP not enable, no Thermal Throttling function will be performed
|
||||
* just set the GP1 bit to acknowledge the event
|
||||
* otherwise, go into IWL_TI_CT_KILL state
|
||||
* since Card State Notification will not provide any temperature reading
|
||||
* for Legacy mode
|
||||
* so just pass the CT_KILL temperature to iwl_legacy_tt_handler()
|
||||
* for advance mode
|
||||
* pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state
|
||||
*/
|
||||
static void iwl_bg_ct_enter(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter);
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
if (!iwl_is_ready(priv))
|
||||
return;
|
||||
|
||||
if (tt->state != IWL_TI_CT_KILL) {
|
||||
IWL_ERR(priv, "Device reached critical temperature "
|
||||
"- ucode going to sleep!\n");
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
iwl_legacy_tt_handler(priv,
|
||||
IWL_MINIMAL_POWER_THRESHOLD,
|
||||
true);
|
||||
else
|
||||
iwl_advance_tt_handler(priv,
|
||||
CT_KILL_THRESHOLD + 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Card State Notification indicated out of critical temperature
|
||||
* since Card State Notification will not provide any temperature reading
|
||||
* so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature
|
||||
* to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state
|
||||
*/
|
||||
static void iwl_bg_ct_exit(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit);
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
if (!iwl_is_ready(priv))
|
||||
return;
|
||||
|
||||
/* stop ct_kill_exit_tm timer */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
|
||||
|
||||
if (tt->state == IWL_TI_CT_KILL) {
|
||||
IWL_ERR(priv,
|
||||
"Device temperature below critical"
|
||||
"- ucode awake!\n");
|
||||
/*
|
||||
* exit from CT_KILL state
|
||||
* reset the current temperature reading
|
||||
*/
|
||||
priv->temperature = 0;
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
iwl_legacy_tt_handler(priv,
|
||||
IWL_REDUCED_PERFORMANCE_THRESHOLD_2,
|
||||
true);
|
||||
else
|
||||
iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void iwl_tt_enter_ct_kill(struct iwl_priv *priv)
|
||||
{
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_POWER(priv, "Queueing critical temperature enter.\n");
|
||||
queue_work(priv->workqueue, &priv->ct_enter);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_enter_ct_kill);
|
||||
|
||||
void iwl_tt_exit_ct_kill(struct iwl_priv *priv)
|
||||
{
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_POWER(priv, "Queueing critical temperature exit.\n");
|
||||
queue_work(priv->workqueue, &priv->ct_exit);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_exit_ct_kill);
|
||||
|
||||
static void iwl_bg_tt_work(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work);
|
||||
s32 temp = priv->temperature; /* degrees CELSIUS except specified */
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
if (priv->cfg->temperature_kelvin)
|
||||
temp = KELVIN_TO_CELSIUS(priv->temperature);
|
||||
|
||||
if (!priv->thermal_throttle.advanced_tt)
|
||||
iwl_legacy_tt_handler(priv, temp, false);
|
||||
else
|
||||
iwl_advance_tt_handler(priv, temp, false);
|
||||
}
|
||||
|
||||
void iwl_tt_handler(struct iwl_priv *priv)
|
||||
{
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_POWER(priv, "Queueing thermal throttling work.\n");
|
||||
queue_work(priv->workqueue, &priv->tt_work);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_handler);
|
||||
|
||||
/* Thermal throttling initialization
|
||||
* For advance thermal throttling:
|
||||
* Initialize Thermal Index and temperature threshold table
|
||||
* Initialize thermal throttling restriction table
|
||||
*/
|
||||
void iwl_tt_initialize(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1);
|
||||
struct iwl_tt_trans *transaction;
|
||||
|
||||
IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling\n");
|
||||
|
||||
memset(tt, 0, sizeof(struct iwl_tt_mgmt));
|
||||
|
||||
tt->state = IWL_TI_0;
|
||||
init_timer(&priv->thermal_throttle.ct_kill_exit_tm);
|
||||
priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv;
|
||||
priv->thermal_throttle.ct_kill_exit_tm.function =
|
||||
iwl_tt_check_exit_ct_kill;
|
||||
init_timer(&priv->thermal_throttle.ct_kill_waiting_tm);
|
||||
priv->thermal_throttle.ct_kill_waiting_tm.data = (unsigned long)priv;
|
||||
priv->thermal_throttle.ct_kill_waiting_tm.function =
|
||||
iwl_tt_ready_for_ct_kill;
|
||||
/* setup deferred ct kill work */
|
||||
INIT_WORK(&priv->tt_work, iwl_bg_tt_work);
|
||||
INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
|
||||
INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
|
||||
|
||||
if (priv->cfg->adv_thermal_throttle) {
|
||||
IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n");
|
||||
tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) *
|
||||
IWL_TI_STATE_MAX, GFP_KERNEL);
|
||||
tt->transaction = kzalloc(sizeof(struct iwl_tt_trans) *
|
||||
IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1),
|
||||
GFP_KERNEL);
|
||||
if (!tt->restriction || !tt->transaction) {
|
||||
IWL_ERR(priv, "Fallback to Legacy Throttling\n");
|
||||
priv->thermal_throttle.advanced_tt = false;
|
||||
kfree(tt->restriction);
|
||||
tt->restriction = NULL;
|
||||
kfree(tt->transaction);
|
||||
tt->transaction = NULL;
|
||||
} else {
|
||||
transaction = tt->transaction +
|
||||
(IWL_TI_0 * (IWL_TI_STATE_MAX - 1));
|
||||
memcpy(transaction, &tt_range_0[0], size);
|
||||
transaction = tt->transaction +
|
||||
(IWL_TI_1 * (IWL_TI_STATE_MAX - 1));
|
||||
memcpy(transaction, &tt_range_1[0], size);
|
||||
transaction = tt->transaction +
|
||||
(IWL_TI_2 * (IWL_TI_STATE_MAX - 1));
|
||||
memcpy(transaction, &tt_range_2[0], size);
|
||||
transaction = tt->transaction +
|
||||
(IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1));
|
||||
memcpy(transaction, &tt_range_3[0], size);
|
||||
size = sizeof(struct iwl_tt_restriction) *
|
||||
IWL_TI_STATE_MAX;
|
||||
memcpy(tt->restriction,
|
||||
&restriction_range[0], size);
|
||||
priv->thermal_throttle.advanced_tt = true;
|
||||
}
|
||||
} else {
|
||||
IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n");
|
||||
priv->thermal_throttle.advanced_tt = false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_initialize);
|
||||
|
||||
/* cleanup thermal throttling management related memory and timer */
|
||||
void iwl_tt_exit(struct iwl_priv *priv)
|
||||
{
|
||||
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
|
||||
|
||||
/* stop ct_kill_exit_tm timer if activated */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
|
||||
/* stop ct_kill_waiting_tm timer if activated */
|
||||
del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
|
||||
cancel_work_sync(&priv->tt_work);
|
||||
cancel_work_sync(&priv->ct_enter);
|
||||
cancel_work_sync(&priv->ct_exit);
|
||||
|
||||
if (priv->thermal_throttle.advanced_tt) {
|
||||
/* free advance thermal throttling memory */
|
||||
kfree(tt->restriction);
|
||||
tt->restriction = NULL;
|
||||
kfree(tt->transaction);
|
||||
tt->transaction = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_tt_exit);
|
||||
|
||||
/* initialize to default */
|
||||
void iwl_power_initialize(struct iwl_priv *priv)
|
||||
{
|
||||
|
@ -30,90 +30,6 @@
|
||||
|
||||
#include "iwl-commands.h"
|
||||
|
||||
#define IWL_ABSOLUTE_ZERO 0
|
||||
#define IWL_ABSOLUTE_MAX 0xFFFFFFFF
|
||||
#define IWL_TT_INCREASE_MARGIN 5
|
||||
#define IWL_TT_CT_KILL_MARGIN 3
|
||||
|
||||
enum iwl_antenna_ok {
|
||||
IWL_ANT_OK_NONE,
|
||||
IWL_ANT_OK_SINGLE,
|
||||
IWL_ANT_OK_MULTI,
|
||||
};
|
||||
|
||||
/* Thermal Throttling State Machine states */
|
||||
enum iwl_tt_state {
|
||||
IWL_TI_0, /* normal temperature, system power state */
|
||||
IWL_TI_1, /* high temperature detect, low power state */
|
||||
IWL_TI_2, /* higher temperature detected, lower power state */
|
||||
IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */
|
||||
IWL_TI_STATE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tt_restriction - Thermal Throttling restriction table
|
||||
* @tx_stream: number of tx stream allowed
|
||||
* @is_ht: ht enable/disable
|
||||
* @rx_stream: number of rx stream allowed
|
||||
*
|
||||
* This table is used by advance thermal throttling management
|
||||
* based on the current thermal throttling state, and determines
|
||||
* the number of tx/rx streams and the status of HT operation.
|
||||
*/
|
||||
struct iwl_tt_restriction {
|
||||
enum iwl_antenna_ok tx_stream;
|
||||
enum iwl_antenna_ok rx_stream;
|
||||
bool is_ht;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tt_trans - Thermal Throttling transaction table
|
||||
* @next_state: next thermal throttling mode
|
||||
* @tt_low: low temperature threshold to change state
|
||||
* @tt_high: high temperature threshold to change state
|
||||
*
|
||||
* This is used by the advanced thermal throttling algorithm
|
||||
* to determine the next thermal state to go based on the
|
||||
* current temperature.
|
||||
*/
|
||||
struct iwl_tt_trans {
|
||||
enum iwl_tt_state next_state;
|
||||
u32 tt_low;
|
||||
u32 tt_high;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tt_mgnt - Thermal Throttling Management structure
|
||||
* @advanced_tt: advanced thermal throttle required
|
||||
* @state: current Thermal Throttling state
|
||||
* @tt_power_mode: Thermal Throttling power mode index
|
||||
* being used to set power level when
|
||||
* when thermal throttling state != IWL_TI_0
|
||||
* the tt_power_mode should set to different
|
||||
* power mode based on the current tt state
|
||||
* @tt_previous_temperature: last measured temperature
|
||||
* @iwl_tt_restriction: ptr to restriction tbl, used by advance
|
||||
* thermal throttling to determine how many tx/rx streams
|
||||
* should be used in tt state; and can HT be enabled or not
|
||||
* @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling
|
||||
* state transaction
|
||||
* @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature
|
||||
* @ct_kill_exit_tm: timer to exit thermal kill
|
||||
*/
|
||||
struct iwl_tt_mgmt {
|
||||
enum iwl_tt_state state;
|
||||
bool advanced_tt;
|
||||
u8 tt_power_mode;
|
||||
bool ct_kill_toggle;
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
s32 tt_previous_temp;
|
||||
#endif
|
||||
struct iwl_tt_restriction *restriction;
|
||||
struct iwl_tt_trans *transaction;
|
||||
struct timer_list ct_kill_exit_tm;
|
||||
struct timer_list ct_kill_waiting_tm;
|
||||
};
|
||||
|
||||
enum iwl_power_level {
|
||||
IWL_POWER_INDEX_1,
|
||||
IWL_POWER_INDEX_2,
|
||||
@ -130,15 +46,6 @@ struct iwl_power_mgr {
|
||||
};
|
||||
|
||||
int iwl_power_update_mode(struct iwl_priv *priv, bool force);
|
||||
bool iwl_ht_enabled(struct iwl_priv *priv);
|
||||
bool iwl_within_ct_kill_margin(struct iwl_priv *priv);
|
||||
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv);
|
||||
enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv);
|
||||
void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
|
||||
void iwl_tt_exit_ct_kill(struct iwl_priv *priv);
|
||||
void iwl_tt_handler(struct iwl_priv *priv);
|
||||
void iwl_tt_initialize(struct iwl_priv *priv);
|
||||
void iwl_tt_exit(struct iwl_priv *priv);
|
||||
void iwl_power_initialize(struct iwl_priv *priv);
|
||||
|
||||
extern bool no_sleep_autoadjust;
|
||||
|
@ -378,7 +378,7 @@ void iwl_internal_short_hw_scan(struct iwl_priv *priv)
|
||||
queue_work(priv->workqueue, &priv->start_internal_scan);
|
||||
}
|
||||
|
||||
void iwl_bg_start_internal_scan(struct work_struct *work)
|
||||
static void iwl_bg_start_internal_scan(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv =
|
||||
container_of(work, struct iwl_priv, start_internal_scan);
|
||||
@ -418,9 +418,8 @@ void iwl_bg_start_internal_scan(struct work_struct *work)
|
||||
unlock:
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_bg_start_internal_scan);
|
||||
|
||||
void iwl_bg_scan_check(struct work_struct *data)
|
||||
static void iwl_bg_scan_check(struct work_struct *data)
|
||||
{
|
||||
struct iwl_priv *priv =
|
||||
container_of(data, struct iwl_priv, scan_check.work);
|
||||
@ -439,7 +438,6 @@ void iwl_bg_scan_check(struct work_struct *data)
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_bg_scan_check);
|
||||
|
||||
/**
|
||||
* iwl_fill_probe_req - fill in all required fields and IE for probe request
|
||||
@ -489,7 +487,7 @@ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_fill_probe_req);
|
||||
|
||||
void iwl_bg_abort_scan(struct work_struct *work)
|
||||
static void iwl_bg_abort_scan(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan);
|
||||
|
||||
@ -504,13 +502,13 @@ void iwl_bg_abort_scan(struct work_struct *work)
|
||||
iwl_send_scan_abort(priv);
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_bg_abort_scan);
|
||||
|
||||
void iwl_bg_scan_completed(struct work_struct *work)
|
||||
static void iwl_bg_scan_completed(struct work_struct *work)
|
||||
{
|
||||
struct iwl_priv *priv =
|
||||
container_of(work, struct iwl_priv, scan_completed);
|
||||
bool internal = false;
|
||||
bool scan_completed = false;
|
||||
|
||||
IWL_DEBUG_SCAN(priv, "SCAN complete scan\n");
|
||||
|
||||
@ -521,7 +519,8 @@ void iwl_bg_scan_completed(struct work_struct *work)
|
||||
priv->is_internal_short_scan = false;
|
||||
IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
|
||||
internal = true;
|
||||
} else {
|
||||
} else if (priv->scan_request) {
|
||||
scan_completed = true;
|
||||
priv->scan_request = NULL;
|
||||
priv->scan_vif = NULL;
|
||||
}
|
||||
@ -552,10 +551,9 @@ void iwl_bg_scan_completed(struct work_struct *work)
|
||||
* into driver again into functions that will attempt to take
|
||||
* mutex.
|
||||
*/
|
||||
if (!internal)
|
||||
if (scan_completed)
|
||||
ieee80211_scan_completed(priv->hw, false);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_bg_scan_completed);
|
||||
|
||||
void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
|
||||
{
|
||||
|
@ -818,7 +818,7 @@ int iwl_set_default_wep_key(struct iwl_priv *priv,
|
||||
|
||||
keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
|
||||
keyconf->hw_key_idx = HW_KEY_DEFAULT;
|
||||
priv->stations[IWL_AP_ID].keyinfo.alg = ALG_WEP;
|
||||
priv->stations[IWL_AP_ID].keyinfo.cipher = keyconf->cipher;
|
||||
|
||||
priv->wep_keys[keyconf->keyidx].key_size = keyconf->keylen;
|
||||
memcpy(&priv->wep_keys[keyconf->keyidx].key, &keyconf->key,
|
||||
@ -856,7 +856,7 @@ static int iwl_set_wep_dynamic_key_info(struct iwl_priv *priv,
|
||||
|
||||
spin_lock_irqsave(&priv->sta_lock, flags);
|
||||
|
||||
priv->stations[sta_id].keyinfo.alg = keyconf->alg;
|
||||
priv->stations[sta_id].keyinfo.cipher = keyconf->cipher;
|
||||
priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
|
||||
priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx;
|
||||
|
||||
@ -906,7 +906,7 @@ static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv,
|
||||
keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
|
||||
|
||||
spin_lock_irqsave(&priv->sta_lock, flags);
|
||||
priv->stations[sta_id].keyinfo.alg = keyconf->alg;
|
||||
priv->stations[sta_id].keyinfo.cipher = keyconf->cipher;
|
||||
priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
|
||||
|
||||
memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key,
|
||||
@ -955,7 +955,7 @@ static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv,
|
||||
|
||||
spin_lock_irqsave(&priv->sta_lock, flags);
|
||||
|
||||
priv->stations[sta_id].keyinfo.alg = keyconf->alg;
|
||||
priv->stations[sta_id].keyinfo.cipher = keyconf->cipher;
|
||||
priv->stations[sta_id].keyinfo.keylen = 16;
|
||||
|
||||
if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK)
|
||||
@ -1090,24 +1090,26 @@ int iwl_set_dynamic_key(struct iwl_priv *priv,
|
||||
priv->key_mapping_key++;
|
||||
keyconf->hw_key_idx = HW_KEY_DYNAMIC;
|
||||
|
||||
switch (keyconf->alg) {
|
||||
case ALG_CCMP:
|
||||
switch (keyconf->cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
ret = iwl_set_ccmp_dynamic_key_info(priv, keyconf, sta_id);
|
||||
break;
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
ret = iwl_set_tkip_dynamic_key_info(priv, keyconf, sta_id);
|
||||
break;
|
||||
case ALG_WEP:
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
ret = iwl_set_wep_dynamic_key_info(priv, keyconf, sta_id);
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(priv,
|
||||
"Unknown alg: %s alg = %d\n", __func__, keyconf->alg);
|
||||
"Unknown alg: %s cipher = %x\n", __func__,
|
||||
keyconf->cipher);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
IWL_DEBUG_WEP(priv, "Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d\n",
|
||||
keyconf->alg, keyconf->keylen, keyconf->keyidx,
|
||||
IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n",
|
||||
keyconf->cipher, keyconf->keylen, keyconf->keyidx,
|
||||
sta_id, ret);
|
||||
|
||||
return ret;
|
||||
|
@ -422,6 +422,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
|
||||
int len;
|
||||
u32 idx;
|
||||
u16 fix_size;
|
||||
bool is_ct_kill = false;
|
||||
|
||||
cmd->len = priv->cfg->ops->utils->get_hcmd_size(cmd->id, cmd->len);
|
||||
fix_size = (u16)(cmd->len + sizeof(out_cmd->hdr));
|
||||
@ -443,9 +444,11 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
|
||||
|
||||
if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
|
||||
IWL_ERR(priv, "No space in command queue\n");
|
||||
if (iwl_within_ct_kill_margin(priv))
|
||||
iwl_tt_enter_ct_kill(priv);
|
||||
else {
|
||||
if (priv->cfg->ops->lib->tt_ops.ct_kill_check) {
|
||||
is_ct_kill =
|
||||
priv->cfg->ops->lib->tt_ops.ct_kill_check(priv);
|
||||
}
|
||||
if (!is_ct_kill) {
|
||||
IWL_ERR(priv, "Restarting adapter due to queue full\n");
|
||||
queue_work(priv->workqueue, &priv->restart);
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-aspm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
@ -151,7 +152,7 @@ static int iwl3945_set_ccmp_dynamic_key_info(struct iwl_priv *priv,
|
||||
key_flags &= ~STA_KEY_FLG_INVALID;
|
||||
|
||||
spin_lock_irqsave(&priv->sta_lock, flags);
|
||||
priv->stations[sta_id].keyinfo.alg = keyconf->alg;
|
||||
priv->stations[sta_id].keyinfo.cipher = keyconf->cipher;
|
||||
priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
|
||||
memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key,
|
||||
keyconf->keylen);
|
||||
@ -222,23 +223,25 @@ static int iwl3945_set_dynamic_key(struct iwl_priv *priv,
|
||||
|
||||
keyconf->hw_key_idx = HW_KEY_DYNAMIC;
|
||||
|
||||
switch (keyconf->alg) {
|
||||
case ALG_CCMP:
|
||||
switch (keyconf->cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
ret = iwl3945_set_ccmp_dynamic_key_info(priv, keyconf, sta_id);
|
||||
break;
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
ret = iwl3945_set_tkip_dynamic_key_info(priv, keyconf, sta_id);
|
||||
break;
|
||||
case ALG_WEP:
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
ret = iwl3945_set_wep_dynamic_key_info(priv, keyconf, sta_id);
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(priv, "Unknown alg: %s alg = %d\n", __func__, keyconf->alg);
|
||||
IWL_ERR(priv, "Unknown alg: %s alg=%x\n", __func__,
|
||||
keyconf->cipher);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
IWL_DEBUG_WEP(priv, "Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d\n",
|
||||
keyconf->alg, keyconf->keylen, keyconf->keyidx,
|
||||
IWL_DEBUG_WEP(priv, "Set dynamic key: alg=%x len=%d idx=%d sta=%d ret=%d\n",
|
||||
keyconf->cipher, keyconf->keylen, keyconf->keyidx,
|
||||
sta_id, ret);
|
||||
|
||||
return ret;
|
||||
@ -254,10 +257,11 @@ static int iwl3945_remove_static_key(struct iwl_priv *priv)
|
||||
static int iwl3945_set_static_key(struct iwl_priv *priv,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
if (key->alg == ALG_WEP)
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
||||
key->cipher == WLAN_CIPHER_SUITE_WEP104)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
IWL_ERR(priv, "Static key invalid: alg %d\n", key->alg);
|
||||
IWL_ERR(priv, "Static key invalid: cipher %x\n", key->cipher);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -369,22 +373,24 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv,
|
||||
struct iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
|
||||
struct iwl_hw_key *keyinfo = &priv->stations[sta_id].keyinfo;
|
||||
|
||||
switch (keyinfo->alg) {
|
||||
case ALG_CCMP:
|
||||
tx_cmd->sec_ctl = 0;
|
||||
|
||||
switch (keyinfo->cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
|
||||
memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen);
|
||||
IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n");
|
||||
break;
|
||||
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
break;
|
||||
|
||||
case ALG_WEP:
|
||||
tx_cmd->sec_ctl = TX_CMD_SEC_WEP |
|
||||
(info->control.hw_key->hw_key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT;
|
||||
|
||||
if (keyinfo->keylen == 13)
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
|
||||
/* fall through */
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
tx_cmd->sec_ctl |= TX_CMD_SEC_WEP |
|
||||
(info->control.hw_key->hw_key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT;
|
||||
|
||||
memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen);
|
||||
|
||||
@ -393,7 +399,7 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv,
|
||||
break;
|
||||
|
||||
default:
|
||||
IWL_ERR(priv, "Unknown encode alg %d\n", keyinfo->alg);
|
||||
IWL_ERR(priv, "Unknown encode cipher %x\n", keyinfo->cipher);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -813,9 +819,9 @@ static void iwl3945_bg_beacon_update(struct work_struct *work)
|
||||
static void iwl3945_rx_beacon_notif(struct iwl_priv *priv,
|
||||
struct iwl_rx_mem_buffer *rxb)
|
||||
{
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
||||
struct iwl3945_beacon_notif *beacon = &(pkt->u.beacon_status);
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
u8 rate = beacon->beacon_notify_hdr.rate;
|
||||
|
||||
IWL_DEBUG_RX(priv, "beacon status %x retries %d iss %d "
|
||||
@ -827,6 +833,8 @@ static void iwl3945_rx_beacon_notif(struct iwl_priv *priv,
|
||||
le32_to_cpu(beacon->low_tsf), rate);
|
||||
#endif
|
||||
|
||||
priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
|
||||
|
||||
if ((priv->iw_mode == NL80211_IFTYPE_AP) &&
|
||||
(!test_bit(STATUS_EXIT_PENDING, &priv->status)))
|
||||
queue_work(priv->workqueue, &priv->beacon_update);
|
||||
@ -3086,10 +3094,7 @@ void iwl3945_post_associate(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
||||
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
||||
iwlcore_commit_rxon(priv);
|
||||
|
||||
memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd));
|
||||
iwl_setup_rxon_timing(priv, vif);
|
||||
rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
|
||||
sizeof(priv->rxon_timing), &priv->rxon_timing);
|
||||
rc = iwl_send_rxon_timing(priv, vif);
|
||||
if (rc)
|
||||
IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
|
||||
"Attempting to continue.\n");
|
||||
@ -3263,11 +3268,7 @@ void iwl3945_config_ap(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
||||
iwlcore_commit_rxon(priv);
|
||||
|
||||
/* RXON Timing */
|
||||
memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd));
|
||||
iwl_setup_rxon_timing(priv, vif);
|
||||
rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
|
||||
sizeof(priv->rxon_timing),
|
||||
&priv->rxon_timing);
|
||||
rc = iwl_send_rxon_timing(priv, vif);
|
||||
if (rc)
|
||||
IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
|
||||
"Attempting to continue.\n");
|
||||
@ -3785,10 +3786,8 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv)
|
||||
INIT_DELAYED_WORK(&priv->init_alive_start, iwl3945_bg_init_alive_start);
|
||||
INIT_DELAYED_WORK(&priv->alive_start, iwl3945_bg_alive_start);
|
||||
INIT_DELAYED_WORK(&priv->_3945.rfkill_poll, iwl3945_rfkill_poll);
|
||||
INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed);
|
||||
INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
|
||||
INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan);
|
||||
INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
|
||||
|
||||
iwl_setup_scan_deferred_work(priv);
|
||||
|
||||
iwl3945_hw_setup_deferred_work(priv);
|
||||
|
||||
@ -3853,6 +3852,7 @@ static struct ieee80211_ops iwl3945_hw_ops = {
|
||||
.hw_scan = iwl_mac_hw_scan,
|
||||
.sta_add = iwl3945_mac_sta_add,
|
||||
.sta_remove = iwl_mac_sta_remove,
|
||||
.tx_last_beacon = iwl_mac_tx_last_beacon,
|
||||
};
|
||||
|
||||
static int iwl3945_init_drv(struct iwl_priv *priv)
|
||||
@ -4009,6 +4009,9 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
|
||||
/***************************
|
||||
* 2. Initializing PCI bus
|
||||
* *************************/
|
||||
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
|
||||
PCIE_LINK_STATE_CLKPM);
|
||||
|
||||
if (pci_enable_device(pdev)) {
|
||||
err = -ENODEV;
|
||||
goto out_ieee80211_free_hw;
|
||||
|
@ -1195,11 +1195,8 @@ static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
|
||||
IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: "
|
||||
"oid is 0x%x\n", hdr->oid);
|
||||
|
||||
if (hdr->oid <= WIFI_IF_NTFY_MAX) {
|
||||
set_bit(hdr->oid, &iwm->wifi_ntfy[0]);
|
||||
wake_up_interruptible(&iwm->wifi_ntfy_queue);
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
switch (hdr->oid) {
|
||||
case UMAC_WIFI_IF_CMD_SET_PROFILE:
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <asm/unaligned.h>
|
||||
@ -526,20 +527,31 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
|
||||
|
||||
pos = scanresp->bssdesc_and_tlvbuffer;
|
||||
|
||||
lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer,
|
||||
scanresp->bssdescriptsize);
|
||||
|
||||
tsfdesc = pos + bsssize;
|
||||
tsfsize = 4 + 8 * scanresp->nr_sets;
|
||||
lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize);
|
||||
|
||||
/* Validity check: we expect a Marvell-Local TLV */
|
||||
i = get_unaligned_le16(tsfdesc);
|
||||
tsfdesc += 2;
|
||||
if (i != TLV_TYPE_TSFTIMESTAMP)
|
||||
if (i != TLV_TYPE_TSFTIMESTAMP) {
|
||||
lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Validity check: the TLV holds TSF values with 8 bytes each, so
|
||||
* the size in the TLV must match the nr_sets value */
|
||||
i = get_unaligned_le16(tsfdesc);
|
||||
tsfdesc += 2;
|
||||
if (i / 8 != scanresp->nr_sets)
|
||||
if (i / 8 != scanresp->nr_sets) {
|
||||
lbs_deb_scan("scan response: invalid number of TSF timestamp "
|
||||
"sets (expected %d got %d)\n", scanresp->nr_sets,
|
||||
i / 8);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < scanresp->nr_sets; i++) {
|
||||
const u8 *bssid;
|
||||
@ -581,8 +593,11 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
|
||||
id = *pos++;
|
||||
elen = *pos++;
|
||||
left -= 2;
|
||||
if (elen > left || elen == 0)
|
||||
if (elen > left || elen == 0) {
|
||||
lbs_deb_scan("scan response: invalid IE fmt\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (id == WLAN_EID_DS_PARAMS)
|
||||
chan_no = *pos;
|
||||
if (id == WLAN_EID_SSID) {
|
||||
@ -613,7 +628,9 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
|
||||
capa, intvl, ie, ielen,
|
||||
LBS_SCAN_RSSI_TO_MBM(rssi),
|
||||
GFP_KERNEL);
|
||||
}
|
||||
} else
|
||||
lbs_deb_scan("scan response: missing BSS channel IE\n");
|
||||
|
||||
tsfdesc += 8;
|
||||
}
|
||||
ret = 0;
|
||||
@ -1103,7 +1120,7 @@ static int lbs_associate(struct lbs_private *priv,
|
||||
lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp);
|
||||
|
||||
/* add auth type TLV */
|
||||
if (priv->fwrelease >= 0x09000000)
|
||||
if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9)
|
||||
pos += lbs_add_auth_type_tlv(pos, sme->auth_type);
|
||||
|
||||
/* add WPA/WPA2 TLV */
|
||||
@ -1114,6 +1131,9 @@ static int lbs_associate(struct lbs_private *priv,
|
||||
(u16)(pos - (u8 *) &cmd->iebuf);
|
||||
cmd->hdr.size = cpu_to_le16(len);
|
||||
|
||||
lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd,
|
||||
le16_to_cpu(cmd->hdr.size));
|
||||
|
||||
/* store for later use */
|
||||
memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN);
|
||||
|
||||
@ -1121,14 +1141,28 @@ static int lbs_associate(struct lbs_private *priv,
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
|
||||
/* generate connect message to cfg80211 */
|
||||
|
||||
resp = (void *) cmd; /* recast for easier field access */
|
||||
status = le16_to_cpu(resp->statuscode);
|
||||
|
||||
/* Convert statis code of old firmware */
|
||||
if (priv->fwrelease < 0x09000000)
|
||||
/* Older FW versions map the IEEE 802.11 Status Code in the association
|
||||
* response to the following values returned in resp->statuscode:
|
||||
*
|
||||
* IEEE Status Code Marvell Status Code
|
||||
* 0 -> 0x0000 ASSOC_RESULT_SUCCESS
|
||||
* 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
|
||||
* 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
|
||||
* 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
|
||||
* 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
|
||||
* others -> 0x0003 ASSOC_RESULT_REFUSED
|
||||
*
|
||||
* Other response codes:
|
||||
* 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
|
||||
* 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
|
||||
* association response from the AP)
|
||||
*/
|
||||
if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
break;
|
||||
@ -1150,11 +1184,16 @@ static int lbs_associate(struct lbs_private *priv,
|
||||
break;
|
||||
default:
|
||||
lbs_deb_assoc("association failure %d\n", status);
|
||||
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
/* v5 OLPC firmware does return the AP status code if
|
||||
* it's not one of the values above. Let that through.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lbs_deb_assoc("status %d, capability 0x%04x\n", status,
|
||||
le16_to_cpu(resp->capability));
|
||||
lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, "
|
||||
"aid 0x%04x\n", status, le16_to_cpu(resp->statuscode),
|
||||
le16_to_cpu(resp->capability), le16_to_cpu(resp->aid));
|
||||
|
||||
resp_ie_len = le16_to_cpu(resp->hdr.size)
|
||||
- sizeof(resp->hdr)
|
||||
@ -1174,7 +1213,6 @@ static int lbs_associate(struct lbs_private *priv,
|
||||
netif_tx_wake_all_queues(priv->dev);
|
||||
}
|
||||
|
||||
|
||||
done:
|
||||
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
|
||||
return ret;
|
||||
|
@ -8,7 +8,14 @@
|
||||
#define _LBS_DECL_H_
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
/* Should be terminated by a NULL entry */
|
||||
struct lbs_fw_table {
|
||||
int model;
|
||||
const char *helper;
|
||||
const char *fwname;
|
||||
};
|
||||
|
||||
struct lbs_private;
|
||||
struct sk_buff;
|
||||
@ -53,4 +60,10 @@ int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
|
||||
u32 lbs_fw_index_to_data_rate(u8 index);
|
||||
u8 lbs_data_rate_to_fw_index(u32 rate);
|
||||
|
||||
int lbs_get_firmware(struct device *dev, const char *user_helper,
|
||||
const char *user_mainfw, u32 card_model,
|
||||
const struct lbs_fw_table *fw_table,
|
||||
const struct firmware **helper,
|
||||
const struct firmware **mainfw);
|
||||
|
||||
#endif
|
||||
|
@ -48,7 +48,6 @@
|
||||
MODULE_AUTHOR("Holger Schurig <hs4233@mail.mn-solutions.de>");
|
||||
MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE("libertas_cs_helper.fw");
|
||||
|
||||
|
||||
|
||||
@ -61,9 +60,34 @@ struct if_cs_card {
|
||||
struct lbs_private *priv;
|
||||
void __iomem *iobase;
|
||||
bool align_regs;
|
||||
u32 model;
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
MODEL_UNKNOWN = 0x00,
|
||||
MODEL_8305 = 0x01,
|
||||
MODEL_8381 = 0x02,
|
||||
MODEL_8385 = 0x03
|
||||
};
|
||||
|
||||
static const struct lbs_fw_table fw_table[] = {
|
||||
{ MODEL_8305, "libertas/cf8305.bin", NULL },
|
||||
{ MODEL_8305, "libertas_cs_helper.fw", NULL },
|
||||
{ MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" },
|
||||
{ MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" },
|
||||
{ MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" },
|
||||
{ MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
MODULE_FIRMWARE("libertas/cf8305.bin");
|
||||
MODULE_FIRMWARE("libertas/cf8381_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/cf8381.bin");
|
||||
MODULE_FIRMWARE("libertas/cf8385_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/cf8385.bin");
|
||||
MODULE_FIRMWARE("libertas_cs_helper.fw");
|
||||
MODULE_FIRMWARE("libertas_cs.fw");
|
||||
|
||||
|
||||
/********************************************************************/
|
||||
/* Hardware access */
|
||||
@ -289,22 +313,19 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r
|
||||
#define CF8385_MANFID 0x02df
|
||||
#define CF8385_CARDID 0x8103
|
||||
|
||||
static inline int if_cs_hw_is_cf8305(struct pcmcia_device *p_dev)
|
||||
/* FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when
|
||||
* that gets fixed. Currently there's no way to access it from the probe hook.
|
||||
*/
|
||||
static inline u32 get_model(u16 manf_id, u16 card_id)
|
||||
{
|
||||
return (p_dev->manf_id == CF8305_MANFID &&
|
||||
p_dev->card_id == CF8305_CARDID);
|
||||
}
|
||||
|
||||
static inline int if_cs_hw_is_cf8381(struct pcmcia_device *p_dev)
|
||||
{
|
||||
return (p_dev->manf_id == CF8381_MANFID &&
|
||||
p_dev->card_id == CF8381_CARDID);
|
||||
}
|
||||
|
||||
static inline int if_cs_hw_is_cf8385(struct pcmcia_device *p_dev)
|
||||
{
|
||||
return (p_dev->manf_id == CF8385_MANFID &&
|
||||
p_dev->card_id == CF8385_CARDID);
|
||||
/* NOTE: keep in sync with if_cs_ids */
|
||||
if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID)
|
||||
return MODEL_8305;
|
||||
else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID)
|
||||
return MODEL_8381;
|
||||
else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID)
|
||||
return MODEL_8385;
|
||||
return MODEL_UNKNOWN;
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
@ -558,12 +579,11 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)
|
||||
*
|
||||
* Return 0 on success
|
||||
*/
|
||||
static int if_cs_prog_helper(struct if_cs_card *card)
|
||||
static int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw)
|
||||
{
|
||||
int ret = 0;
|
||||
int sent = 0;
|
||||
u8 scratch;
|
||||
const struct firmware *fw;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_CS);
|
||||
|
||||
@ -589,14 +609,6 @@ static int if_cs_prog_helper(struct if_cs_card *card)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* TODO: make firmware file configurable */
|
||||
ret = request_firmware(&fw, "libertas_cs_helper.fw",
|
||||
&card->p_dev->dev);
|
||||
if (ret) {
|
||||
lbs_pr_err("can't load helper firmware\n");
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
lbs_deb_cs("helper size %td\n", fw->size);
|
||||
|
||||
/* "Set the 5 bytes of the helper image to 0" */
|
||||
@ -635,7 +647,7 @@ static int if_cs_prog_helper(struct if_cs_card *card)
|
||||
if (ret < 0) {
|
||||
lbs_pr_err("can't download helper at 0x%x, ret %d\n",
|
||||
sent, ret);
|
||||
goto err_release;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
@ -644,17 +656,14 @@ static int if_cs_prog_helper(struct if_cs_card *card)
|
||||
sent += count;
|
||||
}
|
||||
|
||||
err_release:
|
||||
release_firmware(fw);
|
||||
done:
|
||||
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int if_cs_prog_real(struct if_cs_card *card)
|
||||
static int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
int ret = 0;
|
||||
int retry = 0;
|
||||
int len = 0;
|
||||
@ -662,21 +671,13 @@ static int if_cs_prog_real(struct if_cs_card *card)
|
||||
|
||||
lbs_deb_enter(LBS_DEB_CS);
|
||||
|
||||
/* TODO: make firmware file configurable */
|
||||
ret = request_firmware(&fw, "libertas_cs.fw",
|
||||
&card->p_dev->dev);
|
||||
if (ret) {
|
||||
lbs_pr_err("can't load firmware\n");
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
lbs_deb_cs("fw size %td\n", fw->size);
|
||||
|
||||
ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW,
|
||||
IF_CS_SQ_HELPER_OK);
|
||||
if (ret < 0) {
|
||||
lbs_pr_err("helper firmware doesn't answer\n");
|
||||
goto err_release;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (sent = 0; sent < fw->size; sent += len) {
|
||||
@ -691,7 +692,7 @@ static int if_cs_prog_real(struct if_cs_card *card)
|
||||
if (retry > 20) {
|
||||
lbs_pr_err("could not download firmware\n");
|
||||
ret = -ENODEV;
|
||||
goto err_release;
|
||||
goto done;
|
||||
}
|
||||
if (retry) {
|
||||
sent -= len;
|
||||
@ -710,7 +711,7 @@ static int if_cs_prog_real(struct if_cs_card *card)
|
||||
IF_CS_BIT_COMMAND);
|
||||
if (ret < 0) {
|
||||
lbs_pr_err("can't download firmware at 0x%x\n", sent);
|
||||
goto err_release;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
@ -718,9 +719,6 @@ static int if_cs_prog_real(struct if_cs_card *card)
|
||||
if (ret < 0)
|
||||
lbs_pr_err("firmware download failed\n");
|
||||
|
||||
err_release:
|
||||
release_firmware(fw);
|
||||
|
||||
done:
|
||||
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
|
||||
return ret;
|
||||
@ -824,6 +822,8 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
|
||||
unsigned int prod_id;
|
||||
struct lbs_private *priv;
|
||||
struct if_cs_card *card;
|
||||
const struct firmware *helper = NULL;
|
||||
const struct firmware *mainfw = NULL;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_CS);
|
||||
|
||||
@ -843,7 +843,6 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
|
||||
goto out1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate an interrupt line. Note that this does not assign
|
||||
* a handler to the interrupt, unless the 'Handler' member of
|
||||
@ -881,34 +880,47 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
|
||||
*/
|
||||
card->align_regs = 0;
|
||||
|
||||
card->model = get_model(p_dev->manf_id, p_dev->card_id);
|
||||
if (card->model == MODEL_UNKNOWN) {
|
||||
lbs_pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n",
|
||||
p_dev->manf_id, p_dev->card_id);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Check if we have a current silicon */
|
||||
prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID);
|
||||
if (if_cs_hw_is_cf8305(p_dev)) {
|
||||
if (card->model == MODEL_8305) {
|
||||
card->align_regs = 1;
|
||||
if (prod_id < IF_CS_CF8305_B1_REV) {
|
||||
lbs_pr_err("old chips like 8305 rev B3 "
|
||||
"aren't supported\n");
|
||||
lbs_pr_err("8305 rev B0 and older are not supported\n");
|
||||
ret = -ENODEV;
|
||||
goto out2;
|
||||
}
|
||||
}
|
||||
|
||||
if (if_cs_hw_is_cf8381(p_dev) && prod_id < IF_CS_CF8381_B3_REV) {
|
||||
lbs_pr_err("old chips like 8381 rev B3 aren't supported\n");
|
||||
if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) {
|
||||
lbs_pr_err("8381 rev B2 and older are not supported\n");
|
||||
ret = -ENODEV;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (if_cs_hw_is_cf8385(p_dev) && prod_id < IF_CS_CF8385_B1_REV) {
|
||||
lbs_pr_err("old chips like 8385 rev B1 aren't supported\n");
|
||||
if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) {
|
||||
lbs_pr_err("8385 rev B0 and older are not supported\n");
|
||||
ret = -ENODEV;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = lbs_get_firmware(&p_dev->dev, NULL, NULL, card->model,
|
||||
&fw_table[0], &helper, &mainfw);
|
||||
if (ret) {
|
||||
lbs_pr_err("failed to find firmware (%d)\n", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Load the firmware early, before calling into libertas.ko */
|
||||
ret = if_cs_prog_helper(card);
|
||||
if (ret == 0 && !if_cs_hw_is_cf8305(p_dev))
|
||||
ret = if_cs_prog_real(card);
|
||||
ret = if_cs_prog_helper(card, helper);
|
||||
if (ret == 0 && (card->model != MODEL_8305))
|
||||
ret = if_cs_prog_real(card, mainfw);
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
@ -957,6 +969,11 @@ out2:
|
||||
out1:
|
||||
pcmcia_disable_device(p_dev);
|
||||
out:
|
||||
if (helper)
|
||||
release_firmware(helper);
|
||||
if (mainfw)
|
||||
release_firmware(mainfw);
|
||||
|
||||
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
@ -993,6 +1010,7 @@ static struct pcmcia_device_id if_cs_ids[] = {
|
||||
PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID),
|
||||
PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID),
|
||||
PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID),
|
||||
/* NOTE: keep in sync with get_model() */
|
||||
PCMCIA_DEVICE_NULL,
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, if_cs_ids);
|
||||
|
@ -76,36 +76,32 @@ static const struct sdio_device_id if_sdio_ids[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(sdio, if_sdio_ids);
|
||||
|
||||
struct if_sdio_model {
|
||||
int model;
|
||||
const char *helper;
|
||||
const char *firmware;
|
||||
};
|
||||
#define MODEL_8385 0x04
|
||||
#define MODEL_8686 0x0b
|
||||
#define MODEL_8688 0x10
|
||||
|
||||
static struct if_sdio_model if_sdio_models[] = {
|
||||
{
|
||||
/* 8385 */
|
||||
.model = IF_SDIO_MODEL_8385,
|
||||
.helper = "sd8385_helper.bin",
|
||||
.firmware = "sd8385.bin",
|
||||
},
|
||||
{
|
||||
/* 8686 */
|
||||
.model = IF_SDIO_MODEL_8686,
|
||||
.helper = "sd8686_helper.bin",
|
||||
.firmware = "sd8686.bin",
|
||||
},
|
||||
{
|
||||
/* 8688 */
|
||||
.model = IF_SDIO_MODEL_8688,
|
||||
.helper = "sd8688_helper.bin",
|
||||
.firmware = "sd8688.bin",
|
||||
},
|
||||
static const struct lbs_fw_table fw_table[] = {
|
||||
{ MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" },
|
||||
{ MODEL_8385, "sd8385_helper.bin", "sd8385.bin" },
|
||||
{ MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" },
|
||||
{ MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" },
|
||||
{ MODEL_8686, "sd8686_helper.bin", "sd8686.bin" },
|
||||
{ MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" },
|
||||
{ MODEL_8688, "sd8688_helper.bin", "sd8688.bin" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
MODULE_FIRMWARE("libertas/sd8385_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/sd8385.bin");
|
||||
MODULE_FIRMWARE("sd8385_helper.bin");
|
||||
MODULE_FIRMWARE("sd8385.bin");
|
||||
MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/sd8686_v9.bin");
|
||||
MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/sd8686_v8.bin");
|
||||
MODULE_FIRMWARE("sd8686_helper.bin");
|
||||
MODULE_FIRMWARE("sd8686.bin");
|
||||
MODULE_FIRMWARE("libertas/sd8688_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/sd8688.bin");
|
||||
MODULE_FIRMWARE("sd8688_helper.bin");
|
||||
MODULE_FIRMWARE("sd8688.bin");
|
||||
|
||||
@ -187,11 +183,11 @@ static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err)
|
||||
u16 rx_len;
|
||||
|
||||
switch (card->model) {
|
||||
case IF_SDIO_MODEL_8385:
|
||||
case IF_SDIO_MODEL_8686:
|
||||
case MODEL_8385:
|
||||
case MODEL_8686:
|
||||
rx_len = if_sdio_read_scratch(card, &ret);
|
||||
break;
|
||||
case IF_SDIO_MODEL_8688:
|
||||
case MODEL_8688:
|
||||
default: /* for newer chipsets */
|
||||
rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret);
|
||||
if (!ret)
|
||||
@ -288,7 +284,7 @@ static int if_sdio_handle_event(struct if_sdio_card *card,
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SDIO);
|
||||
|
||||
if (card->model == IF_SDIO_MODEL_8385) {
|
||||
if (card->model == MODEL_8385) {
|
||||
event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
@ -466,10 +462,10 @@ static void if_sdio_host_to_card_worker(struct work_struct *work)
|
||||
|
||||
#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY)
|
||||
|
||||
static int if_sdio_prog_helper(struct if_sdio_card *card)
|
||||
static int if_sdio_prog_helper(struct if_sdio_card *card,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
int ret;
|
||||
const struct firmware *fw;
|
||||
unsigned long timeout;
|
||||
u8 *chunk_buffer;
|
||||
u32 chunk_size;
|
||||
@ -478,16 +474,10 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SDIO);
|
||||
|
||||
ret = request_firmware(&fw, card->helper, &card->func->dev);
|
||||
if (ret) {
|
||||
lbs_pr_err("can't load helper firmware\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
chunk_buffer = kzalloc(64, GFP_KERNEL);
|
||||
if (!chunk_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto release_fw;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdio_claim_host(card->func);
|
||||
@ -562,22 +552,19 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
|
||||
release:
|
||||
sdio_release_host(card->func);
|
||||
kfree(chunk_buffer);
|
||||
release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
lbs_pr_err("failed to load helper firmware\n");
|
||||
|
||||
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int if_sdio_prog_real(struct if_sdio_card *card)
|
||||
static int if_sdio_prog_real(struct if_sdio_card *card,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
int ret;
|
||||
const struct firmware *fw;
|
||||
unsigned long timeout;
|
||||
u8 *chunk_buffer;
|
||||
u32 chunk_size;
|
||||
@ -586,16 +573,10 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SDIO);
|
||||
|
||||
ret = request_firmware(&fw, card->firmware, &card->func->dev);
|
||||
if (ret) {
|
||||
lbs_pr_err("can't load firmware\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
chunk_buffer = kzalloc(512, GFP_KERNEL);
|
||||
if (!chunk_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto release_fw;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdio_claim_host(card->func);
|
||||
@ -685,15 +666,12 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
|
||||
release:
|
||||
sdio_release_host(card->func);
|
||||
kfree(chunk_buffer);
|
||||
release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
lbs_pr_err("failed to load firmware\n");
|
||||
|
||||
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -701,6 +679,8 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
|
||||
{
|
||||
int ret;
|
||||
u16 scratch;
|
||||
const struct firmware *helper = NULL;
|
||||
const struct firmware *mainfw = NULL;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SDIO);
|
||||
|
||||
@ -718,11 +698,18 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
|
||||
goto success;
|
||||
}
|
||||
|
||||
ret = if_sdio_prog_helper(card);
|
||||
ret = lbs_get_firmware(&card->func->dev, lbs_helper_name, lbs_fw_name,
|
||||
card->model, &fw_table[0], &helper, &mainfw);
|
||||
if (ret) {
|
||||
lbs_pr_err("failed to find firmware (%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = if_sdio_prog_helper(card, helper);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = if_sdio_prog_real(card);
|
||||
ret = if_sdio_prog_real(card, mainfw);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@ -733,8 +720,12 @@ success:
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
|
||||
if (helper)
|
||||
release_firmware(helper);
|
||||
if (mainfw)
|
||||
release_firmware(mainfw);
|
||||
|
||||
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -938,7 +929,7 @@ static int if_sdio_probe(struct sdio_func *func,
|
||||
"ID: %x", &model) == 1)
|
||||
break;
|
||||
if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
|
||||
model = IF_SDIO_MODEL_8385;
|
||||
model = MODEL_8385;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -956,13 +947,13 @@ static int if_sdio_probe(struct sdio_func *func,
|
||||
card->model = model;
|
||||
|
||||
switch (card->model) {
|
||||
case IF_SDIO_MODEL_8385:
|
||||
case MODEL_8385:
|
||||
card->scratch_reg = IF_SDIO_SCRATCH_OLD;
|
||||
break;
|
||||
case IF_SDIO_MODEL_8686:
|
||||
case MODEL_8686:
|
||||
card->scratch_reg = IF_SDIO_SCRATCH;
|
||||
break;
|
||||
case IF_SDIO_MODEL_8688:
|
||||
case MODEL_8688:
|
||||
default: /* for newer chipsets */
|
||||
card->scratch_reg = IF_SDIO_FW_STATUS;
|
||||
break;
|
||||
@ -972,49 +963,17 @@ static int if_sdio_probe(struct sdio_func *func,
|
||||
card->workqueue = create_workqueue("libertas_sdio");
|
||||
INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
|
||||
|
||||
for (i = 0;i < ARRAY_SIZE(if_sdio_models);i++) {
|
||||
if (card->model == if_sdio_models[i].model)
|
||||
/* Check if we support this card */
|
||||
for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
|
||||
if (card->model == fw_table[i].model)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(if_sdio_models)) {
|
||||
if (i == ARRAY_SIZE(fw_table)) {
|
||||
lbs_pr_err("unknown card model 0x%x\n", card->model);
|
||||
ret = -ENODEV;
|
||||
goto free;
|
||||
}
|
||||
|
||||
card->helper = if_sdio_models[i].helper;
|
||||
card->firmware = if_sdio_models[i].firmware;
|
||||
|
||||
kparam_block_sysfs_write(helper_name);
|
||||
if (lbs_helper_name) {
|
||||
char *helper = kstrdup(lbs_helper_name, GFP_KERNEL);
|
||||
if (!helper) {
|
||||
kparam_unblock_sysfs_write(helper_name);
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
lbs_deb_sdio("overriding helper firmware: %s\n",
|
||||
lbs_helper_name);
|
||||
card->helper = helper;
|
||||
card->helper_allocated = true;
|
||||
}
|
||||
kparam_unblock_sysfs_write(helper_name);
|
||||
|
||||
kparam_block_sysfs_write(fw_name);
|
||||
if (lbs_fw_name) {
|
||||
char *fw_name = kstrdup(lbs_fw_name, GFP_KERNEL);
|
||||
if (!fw_name) {
|
||||
kparam_unblock_sysfs_write(fw_name);
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
lbs_deb_sdio("overriding firmware: %s\n", lbs_fw_name);
|
||||
card->firmware = fw_name;
|
||||
card->firmware_allocated = true;
|
||||
}
|
||||
kparam_unblock_sysfs_write(fw_name);
|
||||
|
||||
sdio_claim_host(func);
|
||||
|
||||
ret = sdio_enable_func(func);
|
||||
@ -1028,7 +987,7 @@ static int if_sdio_probe(struct sdio_func *func,
|
||||
/* For 1-bit transfers to the 8686 model, we need to enable the
|
||||
* interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
|
||||
* bit to allow access to non-vendor registers. */
|
||||
if ((card->model == IF_SDIO_MODEL_8686) &&
|
||||
if ((card->model == MODEL_8686) &&
|
||||
(host->caps & MMC_CAP_SDIO_IRQ) &&
|
||||
(host->ios.bus_width == MMC_BUS_WIDTH_1)) {
|
||||
u8 reg;
|
||||
@ -1091,8 +1050,8 @@ static int if_sdio_probe(struct sdio_func *func,
|
||||
* Get rx_unit if the chip is SD8688 or newer.
|
||||
* SD8385 & SD8686 do not have rx_unit.
|
||||
*/
|
||||
if ((card->model != IF_SDIO_MODEL_8385)
|
||||
&& (card->model != IF_SDIO_MODEL_8686))
|
||||
if ((card->model != MODEL_8385)
|
||||
&& (card->model != MODEL_8686))
|
||||
card->rx_unit = if_sdio_read_rx_unit(card);
|
||||
else
|
||||
card->rx_unit = 0;
|
||||
@ -1108,7 +1067,7 @@ static int if_sdio_probe(struct sdio_func *func,
|
||||
/*
|
||||
* FUNC_INIT is required for SD8688 WLAN/BT multiple functions
|
||||
*/
|
||||
if (card->model == IF_SDIO_MODEL_8688) {
|
||||
if (card->model == MODEL_8688) {
|
||||
struct cmd_header cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
@ -1165,7 +1124,7 @@ static void if_sdio_remove(struct sdio_func *func)
|
||||
|
||||
card = sdio_get_drvdata(func);
|
||||
|
||||
if (user_rmmod && (card->model == IF_SDIO_MODEL_8688)) {
|
||||
if (user_rmmod && (card->model == MODEL_8688)) {
|
||||
/*
|
||||
* FUNC_SHUTDOWN is required for SD8688 WLAN/BT
|
||||
* multiple functions
|
||||
|
@ -12,10 +12,6 @@
|
||||
#ifndef _LBS_IF_SDIO_H
|
||||
#define _LBS_IF_SDIO_H
|
||||
|
||||
#define IF_SDIO_MODEL_8385 0x04
|
||||
#define IF_SDIO_MODEL_8686 0x0b
|
||||
#define IF_SDIO_MODEL_8688 0x10
|
||||
|
||||
#define IF_SDIO_IOPORT 0x00
|
||||
|
||||
#define IF_SDIO_H_INT_MASK 0x04
|
||||
|
@ -39,9 +39,6 @@ struct if_spi_card {
|
||||
struct lbs_private *priv;
|
||||
struct libertas_spi_platform_data *pdata;
|
||||
|
||||
char helper_fw_name[IF_SPI_FW_NAME_MAX];
|
||||
char main_fw_name[IF_SPI_FW_NAME_MAX];
|
||||
|
||||
/* The card ID and card revision, as reported by the hardware. */
|
||||
u16 card_id;
|
||||
u8 card_rev;
|
||||
@ -70,10 +67,28 @@ static void free_if_spi_card(struct if_spi_card *card)
|
||||
kfree(card);
|
||||
}
|
||||
|
||||
static struct chip_ident chip_id_to_device_name[] = {
|
||||
{ .chip_id = 0x04, .name = 8385 },
|
||||
{ .chip_id = 0x0b, .name = 8686 },
|
||||
#define MODEL_8385 0x04
|
||||
#define MODEL_8686 0x0b
|
||||
#define MODEL_8688 0x10
|
||||
|
||||
static const struct lbs_fw_table fw_table[] = {
|
||||
{ MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" },
|
||||
{ MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" },
|
||||
{ MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" },
|
||||
{ MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" },
|
||||
{ MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
MODULE_FIRMWARE("libertas/gspi8385_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8385_hlp.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8385.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8686_v9.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8686_hlp.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8686.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8688_helper.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8688.bin");
|
||||
|
||||
|
||||
/*
|
||||
* SPI Interface Unit Routines
|
||||
@ -399,26 +414,20 @@ static int spu_init(struct if_spi_card *card, int use_dummy_writes)
|
||||
* Firmware Loading
|
||||
*/
|
||||
|
||||
static int if_spi_prog_helper_firmware(struct if_spi_card *card)
|
||||
static int if_spi_prog_helper_firmware(struct if_spi_card *card,
|
||||
const struct firmware *firmware)
|
||||
{
|
||||
int err = 0;
|
||||
const struct firmware *firmware = NULL;
|
||||
int bytes_remaining;
|
||||
const u8 *fw;
|
||||
u8 temp[HELPER_FW_LOAD_CHUNK_SZ];
|
||||
struct spi_device *spi = card->spi;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SPI);
|
||||
|
||||
err = spu_set_interrupt_mode(card, 1, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
/* Get helper firmware image */
|
||||
err = request_firmware(&firmware, card->helper_fw_name, &spi->dev);
|
||||
if (err) {
|
||||
lbs_pr_err("request_firmware failed with err = %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
bytes_remaining = firmware->size;
|
||||
fw = firmware->data;
|
||||
|
||||
@ -429,13 +438,13 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card)
|
||||
err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
|
||||
HELPER_FW_LOAD_CHUNK_SZ);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
|
||||
err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
|
||||
IF_SPI_HIST_CMD_DOWNLOAD_RDY,
|
||||
IF_SPI_HIST_CMD_DOWNLOAD_RDY);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
|
||||
/* Feed the data into the command read/write port reg
|
||||
* in chunks of 64 bytes */
|
||||
@ -446,16 +455,16 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card)
|
||||
err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
|
||||
temp, HELPER_FW_LOAD_CHUNK_SZ);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
|
||||
/* Interrupt the boot code */
|
||||
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
|
||||
IF_SPI_CIC_CMD_DOWNLOAD_OVER);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ;
|
||||
fw += HELPER_FW_LOAD_CHUNK_SZ;
|
||||
}
|
||||
@ -465,18 +474,16 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card)
|
||||
* bootloader. This completes the helper download. */
|
||||
err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
|
||||
IF_SPI_CIC_CMD_DOWNLOAD_OVER);
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
|
||||
lbs_deb_spi("waiting for helper to boot...\n");
|
||||
|
||||
release_firmware:
|
||||
release_firmware(firmware);
|
||||
out:
|
||||
if (err)
|
||||
lbs_pr_err("failed to load helper firmware (err=%d)\n", err);
|
||||
@ -523,13 +530,12 @@ static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
|
||||
return len;
|
||||
}
|
||||
|
||||
static int if_spi_prog_main_firmware(struct if_spi_card *card)
|
||||
static int if_spi_prog_main_firmware(struct if_spi_card *card,
|
||||
const struct firmware *firmware)
|
||||
{
|
||||
int len, prev_len;
|
||||
int bytes, crc_err = 0, err = 0;
|
||||
const struct firmware *firmware = NULL;
|
||||
const u8 *fw;
|
||||
struct spi_device *spi = card->spi;
|
||||
u16 num_crc_errs;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SPI);
|
||||
@ -538,19 +544,11 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Get firmware image */
|
||||
err = request_firmware(&firmware, card->main_fw_name, &spi->dev);
|
||||
if (err) {
|
||||
lbs_pr_err("%s: can't get firmware '%s' from kernel. "
|
||||
"err = %d\n", __func__, card->main_fw_name, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
|
||||
if (err) {
|
||||
lbs_pr_err("%s: timed out waiting for initial "
|
||||
"scratch reg = 0\n", __func__);
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
}
|
||||
|
||||
num_crc_errs = 0;
|
||||
@ -560,7 +558,7 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
|
||||
while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) {
|
||||
if (len < 0) {
|
||||
err = len;
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
}
|
||||
if (bytes < 0) {
|
||||
/* If there are no more bytes left, we would normally
|
||||
@ -575,7 +573,7 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
|
||||
lbs_pr_err("Too many CRC errors encountered "
|
||||
"in firmware load.\n");
|
||||
err = -EIO;
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* Previous transfer succeeded. Advance counters. */
|
||||
@ -590,15 +588,15 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
|
||||
|
||||
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
|
||||
card->cmd_buffer, len);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
|
||||
IF_SPI_CIC_CMD_DOWNLOAD_OVER);
|
||||
if (err)
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
prev_len = len;
|
||||
}
|
||||
if (bytes > prev_len) {
|
||||
@ -611,12 +609,9 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
|
||||
SUCCESSFUL_FW_DOWNLOAD_MAGIC);
|
||||
if (err) {
|
||||
lbs_pr_err("failed to confirm the firmware download\n");
|
||||
goto release_firmware;
|
||||
goto out;
|
||||
}
|
||||
|
||||
release_firmware:
|
||||
release_firmware(firmware);
|
||||
|
||||
out:
|
||||
if (err)
|
||||
lbs_pr_err("failed to load firmware (err=%d)\n", err);
|
||||
@ -800,14 +795,16 @@ static int lbs_spi_thread(void *data)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY)
|
||||
if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) {
|
||||
err = if_spi_c2h_cmd(card);
|
||||
if (err)
|
||||
goto err;
|
||||
if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY)
|
||||
}
|
||||
if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) {
|
||||
err = if_spi_c2h_data(card);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* workaround: in PS mode, the card does not set the Command
|
||||
* Download Ready bit, but it sets TX Download Ready. */
|
||||
@ -886,37 +883,16 @@ static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
|
||||
* SPI callbacks
|
||||
*/
|
||||
|
||||
static int if_spi_calculate_fw_names(u16 card_id,
|
||||
char *helper_fw, char *main_fw)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(chip_id_to_device_name); ++i) {
|
||||
if (card_id == chip_id_to_device_name[i].chip_id)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(chip_id_to_device_name)) {
|
||||
lbs_pr_err("Unsupported chip_id: 0x%02x\n", card_id);
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
snprintf(helper_fw, IF_SPI_FW_NAME_MAX, "libertas/gspi%d_hlp.bin",
|
||||
chip_id_to_device_name[i].name);
|
||||
snprintf(main_fw, IF_SPI_FW_NAME_MAX, "libertas/gspi%d.bin",
|
||||
chip_id_to_device_name[i].name);
|
||||
return 0;
|
||||
}
|
||||
MODULE_FIRMWARE("libertas/gspi8385_hlp.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8385.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8686_hlp.bin");
|
||||
MODULE_FIRMWARE("libertas/gspi8686.bin");
|
||||
|
||||
static int __devinit if_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct if_spi_card *card;
|
||||
struct lbs_private *priv = NULL;
|
||||
struct libertas_spi_platform_data *pdata = spi->dev.platform_data;
|
||||
int err = 0;
|
||||
int err = 0, i;
|
||||
u32 scratch;
|
||||
struct sched_param param = { .sched_priority = 1 };
|
||||
const struct firmware *helper = NULL;
|
||||
const struct firmware *mainfw = NULL;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SPI);
|
||||
|
||||
@ -961,10 +937,25 @@ static int __devinit if_spi_probe(struct spi_device *spi)
|
||||
lbs_deb_spi("Firmware is already loaded for "
|
||||
"Marvell WLAN 802.11 adapter\n");
|
||||
else {
|
||||
err = if_spi_calculate_fw_names(card->card_id,
|
||||
card->helper_fw_name, card->main_fw_name);
|
||||
if (err)
|
||||
/* Check if we support this card */
|
||||
for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
|
||||
if (card->card_id == fw_table[i].model)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(fw_table)) {
|
||||
lbs_pr_err("Unsupported chip_id: 0x%02x\n",
|
||||
card->card_id);
|
||||
err = -ENODEV;
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
err = lbs_get_firmware(&card->spi->dev, NULL, NULL,
|
||||
card->card_id, &fw_table[0], &helper,
|
||||
&mainfw);
|
||||
if (err) {
|
||||
lbs_pr_err("failed to find firmware (%d)\n", err);
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter "
|
||||
"(chip_id = 0x%04x, chip_rev = 0x%02x) "
|
||||
@ -973,10 +964,10 @@ static int __devinit if_spi_probe(struct spi_device *spi)
|
||||
card->card_id, card->card_rev,
|
||||
spi->master->bus_num, spi->chip_select,
|
||||
spi->max_speed_hz);
|
||||
err = if_spi_prog_helper_firmware(card);
|
||||
err = if_spi_prog_helper_firmware(card, helper);
|
||||
if (err)
|
||||
goto free_card;
|
||||
err = if_spi_prog_main_firmware(card);
|
||||
err = if_spi_prog_main_firmware(card, mainfw);
|
||||
if (err)
|
||||
goto free_card;
|
||||
lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n");
|
||||
@ -1044,6 +1035,11 @@ remove_card:
|
||||
free_card:
|
||||
free_if_spi_card(card);
|
||||
out:
|
||||
if (helper)
|
||||
release_firmware(helper);
|
||||
if (mainfw)
|
||||
release_firmware(mainfw);
|
||||
|
||||
lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
@ -25,11 +25,6 @@
|
||||
|
||||
#define IF_SPI_FW_NAME_MAX 30
|
||||
|
||||
struct chip_ident {
|
||||
u16 chip_id;
|
||||
u16 name;
|
||||
};
|
||||
|
||||
#define MAX_MAIN_FW_LOAD_CRC_ERR 10
|
||||
|
||||
/* Chunk size when loading the helper firmware */
|
||||
|
@ -26,15 +26,25 @@
|
||||
|
||||
#define MESSAGE_HEADER_LEN 4
|
||||
|
||||
static char *lbs_fw_name = "usb8388.bin";
|
||||
static char *lbs_fw_name = NULL;
|
||||
module_param_named(fw_name, lbs_fw_name, charp, 0644);
|
||||
|
||||
MODULE_FIRMWARE("libertas/usb8388_v9.bin");
|
||||
MODULE_FIRMWARE("libertas/usb8388_v5.bin");
|
||||
MODULE_FIRMWARE("libertas/usb8388.bin");
|
||||
MODULE_FIRMWARE("libertas/usb8682.bin");
|
||||
MODULE_FIRMWARE("usb8388.bin");
|
||||
|
||||
enum {
|
||||
MODEL_UNKNOWN = 0x0,
|
||||
MODEL_8388 = 0x1,
|
||||
MODEL_8682 = 0x2
|
||||
};
|
||||
|
||||
static struct usb_device_id if_usb_table[] = {
|
||||
/* Enter the device signature inside */
|
||||
{ USB_DEVICE(0x1286, 0x2001) },
|
||||
{ USB_DEVICE(0x05a3, 0x8388) },
|
||||
{ USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 },
|
||||
{ USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 },
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
@ -66,6 +76,8 @@ static ssize_t if_usb_firmware_set(struct device *dev,
|
||||
struct if_usb_card *cardp = priv->card;
|
||||
int ret;
|
||||
|
||||
BUG_ON(buf == NULL);
|
||||
|
||||
ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_FW);
|
||||
if (ret == 0)
|
||||
return count;
|
||||
@ -91,6 +103,8 @@ static ssize_t if_usb_boot2_set(struct device *dev,
|
||||
struct if_usb_card *cardp = priv->card;
|
||||
int ret;
|
||||
|
||||
BUG_ON(buf == NULL);
|
||||
|
||||
ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_BOOT2);
|
||||
if (ret == 0)
|
||||
return count;
|
||||
@ -244,6 +258,7 @@ static int if_usb_probe(struct usb_interface *intf,
|
||||
init_waitqueue_head(&cardp->fw_wq);
|
||||
|
||||
cardp->udev = udev;
|
||||
cardp->model = (uint32_t) id->driver_info;
|
||||
iface_desc = intf->cur_altsetting;
|
||||
|
||||
lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
|
||||
@ -924,6 +939,38 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* table of firmware file names */
|
||||
static const struct {
|
||||
u32 model;
|
||||
const char *fwname;
|
||||
} fw_table[] = {
|
||||
{ MODEL_8388, "libertas/usb8388_v9.bin" },
|
||||
{ MODEL_8388, "libertas/usb8388_v5.bin" },
|
||||
{ MODEL_8388, "libertas/usb8388.bin" },
|
||||
{ MODEL_8388, "usb8388.bin" },
|
||||
{ MODEL_8682, "libertas/usb8682.bin" }
|
||||
};
|
||||
|
||||
static int get_fw(struct if_usb_card *cardp, const char *fwname)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Try user-specified firmware first */
|
||||
if (fwname)
|
||||
return request_firmware(&cardp->fw, fwname, &cardp->udev->dev);
|
||||
|
||||
/* Otherwise search for firmware to use */
|
||||
for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
|
||||
if (fw_table[i].model != cardp->model)
|
||||
continue;
|
||||
if (request_firmware(&cardp->fw, fw_table[i].fwname,
|
||||
&cardp->udev->dev) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int __if_usb_prog_firmware(struct if_usb_card *cardp,
|
||||
const char *fwname, int cmd)
|
||||
{
|
||||
@ -933,10 +980,9 @@ static int __if_usb_prog_firmware(struct if_usb_card *cardp,
|
||||
|
||||
lbs_deb_enter(LBS_DEB_USB);
|
||||
|
||||
ret = request_firmware(&cardp->fw, fwname, &cardp->udev->dev);
|
||||
if (ret < 0) {
|
||||
lbs_pr_err("request_firmware() failed with %#x\n", ret);
|
||||
lbs_pr_err("firmware %s not found\n", fwname);
|
||||
ret = get_fw(cardp, fwname);
|
||||
if (ret) {
|
||||
lbs_pr_err("failed to find firmware (%d)\n", ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ struct bootcmdresp
|
||||
/** USB card description structure*/
|
||||
struct if_usb_card {
|
||||
struct usb_device *udev;
|
||||
uint32_t model; /* MODEL_* */
|
||||
struct urb *rx_urb, *tx_urb;
|
||||
struct lbs_private *priv;
|
||||
|
||||
|
@ -1047,6 +1047,111 @@ void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lbs_notify_command_response);
|
||||
|
||||
/**
|
||||
* @brief Retrieves two-stage firmware
|
||||
*
|
||||
* @param dev A pointer to device structure
|
||||
* @param user_helper User-defined helper firmware file
|
||||
* @param user_mainfw User-defined main firmware file
|
||||
* @param card_model Bus-specific card model ID used to filter firmware table
|
||||
* elements
|
||||
* @param fw_table Table of firmware file names and device model numbers
|
||||
* terminated by an entry with a NULL helper name
|
||||
* @param helper On success, the helper firmware; caller must free
|
||||
* @param mainfw On success, the main firmware; caller must free
|
||||
*
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int lbs_get_firmware(struct device *dev, const char *user_helper,
|
||||
const char *user_mainfw, u32 card_model,
|
||||
const struct lbs_fw_table *fw_table,
|
||||
const struct firmware **helper,
|
||||
const struct firmware **mainfw)
|
||||
{
|
||||
const struct lbs_fw_table *iter;
|
||||
int ret;
|
||||
|
||||
BUG_ON(helper == NULL);
|
||||
BUG_ON(mainfw == NULL);
|
||||
|
||||
/* Try user-specified firmware first */
|
||||
if (user_helper) {
|
||||
ret = request_firmware(helper, user_helper, dev);
|
||||
if (ret) {
|
||||
lbs_pr_err("couldn't find helper firmware %s",
|
||||
user_helper);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (user_mainfw) {
|
||||
ret = request_firmware(mainfw, user_mainfw, dev);
|
||||
if (ret) {
|
||||
lbs_pr_err("couldn't find main firmware %s",
|
||||
user_mainfw);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (*helper && *mainfw)
|
||||
return 0;
|
||||
|
||||
/* Otherwise search for firmware to use. If neither the helper or
|
||||
* the main firmware were specified by the user, then we need to
|
||||
* make sure that found helper & main are from the same entry in
|
||||
* fw_table.
|
||||
*/
|
||||
iter = fw_table;
|
||||
while (iter && iter->helper) {
|
||||
if (iter->model != card_model)
|
||||
goto next;
|
||||
|
||||
if (*helper == NULL) {
|
||||
ret = request_firmware(helper, iter->helper, dev);
|
||||
if (ret)
|
||||
goto next;
|
||||
|
||||
/* If the device has one-stage firmware (ie cf8305) and
|
||||
* we've got it then we don't need to bother with the
|
||||
* main firmware.
|
||||
*/
|
||||
if (iter->fwname == NULL)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*mainfw == NULL) {
|
||||
ret = request_firmware(mainfw, iter->fwname, dev);
|
||||
if (ret && !user_helper) {
|
||||
/* Clear the helper if it wasn't user-specified
|
||||
* and the main firmware load failed, to ensure
|
||||
* we don't have mismatched firmware pairs.
|
||||
*/
|
||||
release_firmware(*helper);
|
||||
*helper = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (*helper && *mainfw)
|
||||
return 0;
|
||||
|
||||
next:
|
||||
iter++;
|
||||
}
|
||||
|
||||
fail:
|
||||
/* Failed */
|
||||
if (*helper) {
|
||||
release_firmware(*helper);
|
||||
*helper = NULL;
|
||||
}
|
||||
if (*mainfw) {
|
||||
release_firmware(*mainfw);
|
||||
*mainfw = NULL;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lbs_get_firmware);
|
||||
|
||||
static int __init lbs_init_module(void)
|
||||
{
|
||||
lbs_deb_enter(LBS_DEB_MAIN);
|
||||
|
@ -178,14 +178,17 @@ static int if_usb_probe(struct usb_interface *intf,
|
||||
le16_to_cpu(endpoint->wMaxPacketSize);
|
||||
cardp->ep_in = usb_endpoint_num(endpoint);
|
||||
|
||||
lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in);
|
||||
lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size);
|
||||
lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n",
|
||||
cardp->ep_in);
|
||||
lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n",
|
||||
cardp->ep_in_size);
|
||||
} else if (usb_endpoint_is_bulk_out(endpoint)) {
|
||||
cardp->ep_out_size =
|
||||
le16_to_cpu(endpoint->wMaxPacketSize);
|
||||
cardp->ep_out = usb_endpoint_num(endpoint);
|
||||
|
||||
lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out);
|
||||
lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n",
|
||||
cardp->ep_out);
|
||||
lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n",
|
||||
cardp->ep_out_size);
|
||||
}
|
||||
@ -318,10 +321,12 @@ static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
|
||||
|
||||
if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
|
||||
lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
|
||||
lbtf_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n",
|
||||
lbtf_deb_usb2(&cardp->udev->dev,
|
||||
"seqnum = %d totalbytes = %d\n",
|
||||
cardp->fwseqnum, cardp->totalbytes);
|
||||
} else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
|
||||
lbtf_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n");
|
||||
lbtf_deb_usb2(&cardp->udev->dev,
|
||||
"Host has finished FW downloading\n");
|
||||
lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n");
|
||||
|
||||
/* Host has finished FW downloading
|
||||
@ -400,7 +405,8 @@ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
|
||||
urb->transfer_flags |= URB_ZERO_PACKET;
|
||||
|
||||
if (usb_submit_urb(urb, GFP_ATOMIC)) {
|
||||
lbtf_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
|
||||
lbtf_deb_usbd(&cardp->udev->dev,
|
||||
"usb_submit_urb failed: %d\n", ret);
|
||||
goto tx_ret;
|
||||
}
|
||||
|
||||
@ -438,10 +444,12 @@ static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
|
||||
|
||||
cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
|
||||
|
||||
lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb);
|
||||
lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n",
|
||||
cardp->rx_urb);
|
||||
ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC);
|
||||
if (ret) {
|
||||
lbtf_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret);
|
||||
lbtf_deb_usbd(&cardp->udev->dev,
|
||||
"Submit Rx URB failed: %d\n", ret);
|
||||
kfree_skb(skb);
|
||||
cardp->rx_skb = NULL;
|
||||
lbtf_deb_leave(LBTF_DEB_USB);
|
||||
@ -541,19 +549,23 @@ static void if_usb_receive_fwload(struct urb *urb)
|
||||
syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader),
|
||||
GFP_ATOMIC);
|
||||
if (!syncfwheader) {
|
||||
lbtf_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n");
|
||||
lbtf_deb_usbd(&cardp->udev->dev,
|
||||
"Failure to allocate syncfwheader\n");
|
||||
kfree_skb(skb);
|
||||
lbtf_deb_leave(LBTF_DEB_USB);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!syncfwheader->cmd) {
|
||||
lbtf_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n");
|
||||
lbtf_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n",
|
||||
lbtf_deb_usb2(&cardp->udev->dev,
|
||||
"FW received Blk with correct CRC\n");
|
||||
lbtf_deb_usb2(&cardp->udev->dev,
|
||||
"FW received Blk seqnum = %d\n",
|
||||
le32_to_cpu(syncfwheader->seqnum));
|
||||
cardp->CRC_OK = 1;
|
||||
} else {
|
||||
lbtf_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n");
|
||||
lbtf_deb_usbd(&cardp->udev->dev,
|
||||
"FW received Blk with CRC error\n");
|
||||
cardp->CRC_OK = 0;
|
||||
}
|
||||
|
||||
@ -666,7 +678,8 @@ static void if_usb_receive(struct urb *urb)
|
||||
{
|
||||
/* Event cause handling */
|
||||
u32 event_cause = le32_to_cpu(pkt[1]);
|
||||
lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event_cause);
|
||||
lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n",
|
||||
event_cause);
|
||||
|
||||
/* Icky undocumented magic special case */
|
||||
if (event_cause & 0xffff0000) {
|
||||
|
@ -9,7 +9,8 @@
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - IBSS mode simulation (Beacon transmission with competition for "air time")
|
||||
* - Add TSF sync and fix IBSS beacon transmission by adding
|
||||
* competition for "air time" at TBTT
|
||||
* - RX filtering based on filter configuration (data->rx_filter)
|
||||
*/
|
||||
|
||||
@ -620,7 +621,8 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
|
||||
hwsim_check_magic(vif);
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_AP &&
|
||||
vif->type != NL80211_IFTYPE_MESH_POINT)
|
||||
vif->type != NL80211_IFTYPE_MESH_POINT &&
|
||||
vif->type != NL80211_IFTYPE_ADHOC)
|
||||
return;
|
||||
|
||||
skb = ieee80211_beacon_get(hw, vif);
|
||||
@ -1295,6 +1297,7 @@ static int __init init_mac80211_hwsim(void)
|
||||
hw->wiphy->interface_modes =
|
||||
BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_AP) |
|
||||
BIT(NL80211_IFTYPE_ADHOC) |
|
||||
BIT(NL80211_IFTYPE_MESH_POINT);
|
||||
|
||||
hw->flags = IEEE80211_HW_MFP_CAPABLE |
|
||||
|
@ -2,6 +2,7 @@ config P54_COMMON
|
||||
tristate "Softmac Prism54 support"
|
||||
depends on MAC80211 && EXPERIMENTAL
|
||||
select FW_LOADER
|
||||
select CRC_CCITT
|
||||
---help---
|
||||
This is common code for isl38xx/stlc45xx based modules.
|
||||
This module does nothing by itself - the USB/PCI/SPI front-ends
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <net/mac80211.h>
|
||||
#include <linux/crc-ccitt.h>
|
||||
|
||||
#include "p54.h"
|
||||
#include "eeprom.h"
|
||||
@ -540,6 +541,7 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
|
||||
int err;
|
||||
u8 *end = (u8 *)eeprom + len;
|
||||
u16 synth = 0;
|
||||
u16 crc16 = ~0;
|
||||
|
||||
wrap = (struct eeprom_pda_wrap *) eeprom;
|
||||
entry = (void *)wrap->data + le16_to_cpu(wrap->len);
|
||||
@ -655,16 +657,29 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
|
||||
}
|
||||
break;
|
||||
case PDR_END:
|
||||
/* make it overrun */
|
||||
entry_len = len;
|
||||
crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry));
|
||||
if (crc16 != le16_to_cpup((__le16 *)entry->data)) {
|
||||
wiphy_err(dev->wiphy, "eeprom failed checksum "
|
||||
"test!\n");
|
||||
err = -ENOMSG;
|
||||
goto err;
|
||||
} else {
|
||||
goto good_eeprom;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2);
|
||||
entry = (void *)entry + (entry_len + 1) * 2;
|
||||
}
|
||||
|
||||
wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n");
|
||||
err = -ENODATA;
|
||||
goto err;
|
||||
|
||||
good_eeprom:
|
||||
if (!synth || !priv->iq_autocal || !priv->output_limit ||
|
||||
!priv->curve_data) {
|
||||
wiphy_err(dev->wiphy,
|
||||
|
@ -123,10 +123,14 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
|
||||
bootrec = (struct bootrec *)&bootrec->data[len];
|
||||
}
|
||||
|
||||
if (fw_version)
|
||||
if (fw_version) {
|
||||
wiphy_info(priv->hw->wiphy,
|
||||
"FW rev %s - Softmac protocol %x.%x\n",
|
||||
fw_version, priv->fw_var >> 8, priv->fw_var & 0xff);
|
||||
snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version),
|
||||
"%s - %x.%x", fw_version,
|
||||
priv->fw_var >> 8, priv->fw_var & 0xff);
|
||||
}
|
||||
|
||||
if (priv->fw_var < 0x500)
|
||||
wiphy_info(priv->hw->wiphy,
|
||||
|
@ -429,8 +429,8 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
|
||||
|
||||
mutex_lock(&priv->conf_mutex);
|
||||
if (cmd == SET_KEY) {
|
||||
switch (key->alg) {
|
||||
case ALG_TKIP:
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL |
|
||||
BR_DESC_PRIV_CAP_TKIP))) {
|
||||
ret = -EOPNOTSUPP;
|
||||
@ -439,7 +439,8 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
|
||||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
|
||||
algo = P54_CRYPTO_TKIPMICHAEL;
|
||||
break;
|
||||
case ALG_WEP:
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out_unlock;
|
||||
@ -447,7 +448,7 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
|
||||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
|
||||
algo = P54_CRYPTO_WEP;
|
||||
break;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out_unlock;
|
||||
|
@ -671,7 +671,7 @@ static unsigned char p54spi_eeprom[] = {
|
||||
0xa8, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
|
||||
|
||||
0x02, 0x00, 0x00, 0x00, /* PDR_END */
|
||||
0xa8, 0xf5 /* bogus data */
|
||||
0x67, 0x99,
|
||||
};
|
||||
|
||||
#endif /* P54SPI_EEPROM_H */
|
||||
|
@ -683,14 +683,15 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb,
|
||||
}
|
||||
}
|
||||
|
||||
static u8 p54_convert_algo(enum ieee80211_key_alg alg)
|
||||
static u8 p54_convert_algo(u32 cipher)
|
||||
{
|
||||
switch (alg) {
|
||||
case ALG_WEP:
|
||||
switch (cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
return P54_CRYPTO_WEP;
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
return P54_CRYPTO_TKIPMICHAEL;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
return P54_CRYPTO_AESCCMP;
|
||||
default:
|
||||
return 0;
|
||||
@ -731,7 +732,7 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb)
|
||||
|
||||
if (info->control.hw_key) {
|
||||
crypt_offset = ieee80211_get_hdrlen_from_skb(skb);
|
||||
if (info->control.hw_key->alg == ALG_TKIP) {
|
||||
if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
|
||||
u8 *iv = (u8 *)(skb->data + crypt_offset);
|
||||
/*
|
||||
* The firmware excepts that the IV has to have
|
||||
@ -827,10 +828,10 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb)
|
||||
hdr->tries = ridx;
|
||||
txhdr->rts_rate_idx = 0;
|
||||
if (info->control.hw_key) {
|
||||
txhdr->key_type = p54_convert_algo(info->control.hw_key->alg);
|
||||
txhdr->key_type = p54_convert_algo(info->control.hw_key->cipher);
|
||||
txhdr->key_len = min((u8)16, info->control.hw_key->keylen);
|
||||
memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len);
|
||||
if (info->control.hw_key->alg == ALG_TKIP) {
|
||||
if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
|
||||
/* reserve space for the MIC key */
|
||||
len += 8;
|
||||
memcpy(skb_put(skb, 8), &(info->control.hw_key->key
|
||||
|
@ -355,7 +355,9 @@ static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev,
|
||||
* it is known that not work at least on some hardware.
|
||||
* SW crypto will be used in that case.
|
||||
*/
|
||||
if (key->alg == ALG_WEP && key->keyidx != 0)
|
||||
if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
||||
key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
|
||||
key->keyidx != 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
|
@ -1318,7 +1318,25 @@
|
||||
#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000)
|
||||
|
||||
/*
|
||||
* TX_STA_FIFO: TX Result for specific PID status fifo register
|
||||
* TX_STA_FIFO: TX Result for specific PID status fifo register.
|
||||
*
|
||||
* This register is implemented as FIFO with 16 entries in the HW. Each
|
||||
* register read fetches the next tx result. If the FIFO is full because
|
||||
* it wasn't read fast enough after the according interrupt (TX_FIFO_STATUS)
|
||||
* triggered, the hw seems to simply drop further tx results.
|
||||
*
|
||||
* VALID: 1: this tx result is valid
|
||||
* 0: no valid tx result -> driver should stop reading
|
||||
* PID_TYPE: The PID latched from the PID field in the TXWI, can be used
|
||||
* to match a frame with its tx result (even though the PID is
|
||||
* only 4 bits wide).
|
||||
* TX_SUCCESS: Indicates tx success (1) or failure (0)
|
||||
* TX_AGGRE: Indicates if the frame was part of an aggregate (1) or not (0)
|
||||
* TX_ACK_REQUIRED: Indicates if the frame needed to get ack'ed (1) or not (0)
|
||||
* WCID: The wireless client ID.
|
||||
* MCS: The tx rate used during the last transmission of this frame, be it
|
||||
* successful or not.
|
||||
* PHYMODE: The phymode used for the transmission.
|
||||
*/
|
||||
#define TX_STA_FIFO 0x1718
|
||||
#define TX_STA_FIFO_VALID FIELD32(0x00000001)
|
||||
@ -1945,6 +1963,13 @@ struct mac_iveiv_entry {
|
||||
|
||||
/*
|
||||
* Word1
|
||||
* ACK: 0: No Ack needed, 1: Ack needed
|
||||
* NSEQ: 0: Don't assign hw sequence number, 1: Assign hw sequence number
|
||||
* BW_WIN_SIZE: BA windows size of the recipient
|
||||
* WIRELESS_CLI_ID: Client ID for WCID table access
|
||||
* MPDU_TOTAL_BYTE_COUNT: Length of 802.11 frame
|
||||
* PACKETID: Will be latched into the TX_STA_FIFO register once the according
|
||||
* frame was processed. 0: Don't report tx status for this frame.
|
||||
*/
|
||||
#define TXWI_W1_ACK FIELD32(0x00000001)
|
||||
#define TXWI_W1_NSEQ FIELD32(0x00000002)
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
|
||||
Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
|
||||
Copyright (C) 2009 Gertjan van Wingerde <gwingerde@gmail.com>
|
||||
@ -427,8 +428,10 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2800_load_firmware);
|
||||
|
||||
void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
|
||||
void rt2800_write_tx_data(struct queue_entry *entry,
|
||||
struct txentry_desc *txdesc)
|
||||
{
|
||||
__le32 *txwi = rt2800_drv_get_txwi(entry);
|
||||
u32 word;
|
||||
|
||||
/*
|
||||
@ -437,7 +440,8 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
|
||||
rt2x00_desc_read(txwi, 0, &word);
|
||||
rt2x00_set_field32(&word, TXWI_W0_FRAG,
|
||||
test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
|
||||
rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, 0);
|
||||
rt2x00_set_field32(&word, TXWI_W0_MIMO_PS,
|
||||
test_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags));
|
||||
rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0);
|
||||
rt2x00_set_field32(&word, TXWI_W0_TS,
|
||||
test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
|
||||
@ -478,7 +482,7 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
|
||||
_rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */);
|
||||
_rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2800_write_txwi);
|
||||
EXPORT_SYMBOL_GPL(rt2800_write_tx_data);
|
||||
|
||||
static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxwi_w2)
|
||||
{
|
||||
@ -490,7 +494,7 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxwi_w2)
|
||||
u8 offset1;
|
||||
u8 offset2;
|
||||
|
||||
if (rt2x00dev->rx_status.band == IEEE80211_BAND_2GHZ) {
|
||||
if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
|
||||
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
|
||||
offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0);
|
||||
offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1);
|
||||
@ -569,6 +573,122 @@ void rt2800_process_rxwi(struct queue_entry *entry,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
|
||||
|
||||
void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
struct data_queue *queue;
|
||||
struct queue_entry *entry;
|
||||
__le32 *txwi;
|
||||
struct txdone_entry_desc txdesc;
|
||||
u32 word;
|
||||
u32 reg;
|
||||
int wcid, ack, pid, tx_wcid, tx_ack, tx_pid;
|
||||
u16 mcs, real_mcs;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO
|
||||
* at most X times and also stop processing once the TX_STA_FIFO_VALID
|
||||
* flag is not set anymore.
|
||||
*
|
||||
* The legacy drivers use X=TX_RING_SIZE but state in a comment
|
||||
* that the TX_STA_FIFO stack has a size of 16. We stick to our
|
||||
* tx ring size for now.
|
||||
*/
|
||||
for (i = 0; i < TX_ENTRIES; i++) {
|
||||
rt2800_register_read(rt2x00dev, TX_STA_FIFO, ®);
|
||||
if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
|
||||
break;
|
||||
|
||||
wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
|
||||
ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
|
||||
pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
|
||||
|
||||
/*
|
||||
* Skip this entry when it contains an invalid
|
||||
* queue identication number.
|
||||
*/
|
||||
if (pid <= 0 || pid > QID_RX)
|
||||
continue;
|
||||
|
||||
queue = rt2x00queue_get_queue(rt2x00dev, pid - 1);
|
||||
if (unlikely(!queue))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Inside each queue, we process each entry in a chronological
|
||||
* order. We first check that the queue is not empty.
|
||||
*/
|
||||
entry = NULL;
|
||||
while (!rt2x00queue_empty(queue)) {
|
||||
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||
if (!test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
|
||||
break;
|
||||
|
||||
rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
|
||||
}
|
||||
|
||||
if (!entry || rt2x00queue_empty(queue))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Check if we got a match by looking at WCID/ACK/PID
|
||||
* fields
|
||||
*/
|
||||
txwi = rt2800_drv_get_txwi(entry);
|
||||
|
||||
rt2x00_desc_read(txwi, 1, &word);
|
||||
tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
|
||||
tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK);
|
||||
tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID);
|
||||
|
||||
if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid))
|
||||
WARNING(rt2x00dev, "invalid TX_STA_FIFO content");
|
||||
|
||||
/*
|
||||
* Obtain the status about this packet.
|
||||
*/
|
||||
txdesc.flags = 0;
|
||||
rt2x00_desc_read(txwi, 0, &word);
|
||||
mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
|
||||
mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
|
||||
real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
|
||||
|
||||
/*
|
||||
* Ralink has a retry mechanism using a global fallback
|
||||
* table. We setup this fallback table to try the immediate
|
||||
* lower rate for all rates. In the TX_STA_FIFO, the MCS field
|
||||
* always contains the MCS used for the last transmission, be
|
||||
* it successful or not.
|
||||
*/
|
||||
if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) {
|
||||
/*
|
||||
* Transmission succeeded. The number of retries is
|
||||
* mcs - real_mcs
|
||||
*/
|
||||
__set_bit(TXDONE_SUCCESS, &txdesc.flags);
|
||||
txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
|
||||
} else {
|
||||
/*
|
||||
* Transmission failed. The number of retries is
|
||||
* always 7 in this case (for a total number of 8
|
||||
* frames sent).
|
||||
*/
|
||||
__set_bit(TXDONE_FAILURE, &txdesc.flags);
|
||||
txdesc.retry = rt2x00dev->long_retry;
|
||||
}
|
||||
|
||||
/*
|
||||
* the frame was retried at least once
|
||||
* -> hw used fallback rates
|
||||
*/
|
||||
if (txdesc.retry)
|
||||
__set_bit(TXDONE_FALLBACK, &txdesc.flags);
|
||||
|
||||
rt2x00lib_txdone(entry, &txdesc);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2800_txdone);
|
||||
|
||||
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
|
||||
{
|
||||
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
|
||||
@ -600,7 +720,7 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
|
||||
/*
|
||||
* Add the TXWI for the beacon to the skb.
|
||||
*/
|
||||
rt2800_write_txwi((__le32 *)entry->skb->data, txdesc);
|
||||
rt2800_write_tx_data(entry, txdesc);
|
||||
|
||||
/*
|
||||
* Dump beacon to userspace through debugfs.
|
||||
|
@ -1,4 +1,6 @@
|
||||
/*
|
||||
Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
|
||||
Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2009 Bartlomiej Zolnierkiewicz
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@ -44,6 +46,7 @@ struct rt2800_ops {
|
||||
int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev,
|
||||
const u8 *data, const size_t len);
|
||||
int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev);
|
||||
__le32 *(*drv_get_txwi)(struct queue_entry *entry);
|
||||
};
|
||||
|
||||
static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev,
|
||||
@ -126,6 +129,13 @@ static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev)
|
||||
return rt2800ops->drv_init_registers(rt2x00dev);
|
||||
}
|
||||
|
||||
static inline __le32 *rt2800_drv_get_txwi(struct queue_entry *entry)
|
||||
{
|
||||
const struct rt2800_ops *rt2800ops = entry->queue->rt2x00dev->ops->drv;
|
||||
|
||||
return rt2800ops->drv_get_txwi(entry);
|
||||
}
|
||||
|
||||
void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
|
||||
const u8 command, const u8 token,
|
||||
const u8 arg0, const u8 arg1);
|
||||
@ -135,9 +145,12 @@ int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev,
|
||||
int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
|
||||
const u8 *data, const size_t len);
|
||||
|
||||
void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc);
|
||||
void rt2800_write_tx_data(struct queue_entry *entry,
|
||||
struct txentry_desc *txdesc);
|
||||
void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc);
|
||||
|
||||
void rt2800_txdone(struct rt2x00_dev *rt2x00dev);
|
||||
|
||||
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);
|
||||
|
||||
extern const struct rt2x00debug rt2800_rt2x00debug;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
|
||||
Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
|
||||
Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
|
||||
@ -566,15 +566,11 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
|
||||
/*
|
||||
* TX descriptor initialization
|
||||
*/
|
||||
static void rt2800pci_write_tx_data(struct queue_entry* entry,
|
||||
struct txentry_desc *txdesc)
|
||||
static __le32 *rt2800pci_get_txwi(struct queue_entry *entry)
|
||||
{
|
||||
__le32 *txwi = (__le32 *) entry->skb->data;
|
||||
|
||||
rt2800_write_txwi(txwi, txdesc);
|
||||
return (__le32 *) entry->skb->data;
|
||||
}
|
||||
|
||||
|
||||
static void rt2800pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
|
||||
struct sk_buff *skb,
|
||||
struct txentry_desc *txdesc)
|
||||
@ -728,110 +724,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
|
||||
/*
|
||||
* Interrupt functions.
|
||||
*/
|
||||
static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
struct data_queue *queue;
|
||||
struct queue_entry *entry;
|
||||
__le32 *txwi;
|
||||
struct txdone_entry_desc txdesc;
|
||||
u32 word;
|
||||
u32 reg;
|
||||
int wcid, ack, pid, tx_wcid, tx_ack, tx_pid;
|
||||
u16 mcs, real_mcs;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO
|
||||
* at most X times and also stop processing once the TX_STA_FIFO_VALID
|
||||
* flag is not set anymore.
|
||||
*
|
||||
* The legacy drivers use X=TX_RING_SIZE but state in a comment
|
||||
* that the TX_STA_FIFO stack has a size of 16. We stick to our
|
||||
* tx ring size for now.
|
||||
*/
|
||||
for (i = 0; i < TX_ENTRIES; i++) {
|
||||
rt2800_register_read(rt2x00dev, TX_STA_FIFO, ®);
|
||||
if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
|
||||
break;
|
||||
|
||||
wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
|
||||
ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
|
||||
pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
|
||||
|
||||
/*
|
||||
* Skip this entry when it contains an invalid
|
||||
* queue identication number.
|
||||
*/
|
||||
if (pid <= 0 || pid > QID_RX)
|
||||
continue;
|
||||
|
||||
queue = rt2x00queue_get_queue(rt2x00dev, pid - 1);
|
||||
if (unlikely(!queue))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Inside each queue, we process each entry in a chronological
|
||||
* order. We first check that the queue is not empty.
|
||||
*/
|
||||
if (rt2x00queue_empty(queue))
|
||||
continue;
|
||||
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||
|
||||
/* Check if we got a match by looking at WCID/ACK/PID
|
||||
* fields */
|
||||
txwi = (__le32 *) entry->skb->data;
|
||||
|
||||
rt2x00_desc_read(txwi, 1, &word);
|
||||
tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
|
||||
tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK);
|
||||
tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID);
|
||||
|
||||
if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid))
|
||||
WARNING(rt2x00dev, "invalid TX_STA_FIFO content\n");
|
||||
|
||||
/*
|
||||
* Obtain the status about this packet.
|
||||
*/
|
||||
txdesc.flags = 0;
|
||||
rt2x00_desc_read(txwi, 0, &word);
|
||||
mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
|
||||
real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
|
||||
|
||||
/*
|
||||
* Ralink has a retry mechanism using a global fallback
|
||||
* table. We setup this fallback table to try the immediate
|
||||
* lower rate for all rates. In the TX_STA_FIFO, the MCS field
|
||||
* always contains the MCS used for the last transmission, be
|
||||
* it successful or not.
|
||||
*/
|
||||
if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) {
|
||||
/*
|
||||
* Transmission succeeded. The number of retries is
|
||||
* mcs - real_mcs
|
||||
*/
|
||||
__set_bit(TXDONE_SUCCESS, &txdesc.flags);
|
||||
txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
|
||||
} else {
|
||||
/*
|
||||
* Transmission failed. The number of retries is
|
||||
* always 7 in this case (for a total number of 8
|
||||
* frames sent).
|
||||
*/
|
||||
__set_bit(TXDONE_FAILURE, &txdesc.flags);
|
||||
txdesc.retry = 7;
|
||||
}
|
||||
|
||||
/*
|
||||
* the frame was retried at least once
|
||||
* -> hw used fallback rates
|
||||
*/
|
||||
if (txdesc.retry)
|
||||
__set_bit(TXDONE_FALLBACK, &txdesc.flags);
|
||||
|
||||
rt2x00lib_txdone(entry, &txdesc);
|
||||
}
|
||||
}
|
||||
|
||||
static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
struct ieee80211_conf conf = { .flags = 0 };
|
||||
@ -867,7 +759,7 @@ static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance)
|
||||
* 4 - Tx done interrupt.
|
||||
*/
|
||||
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
|
||||
rt2800pci_txdone(rt2x00dev);
|
||||
rt2800_txdone(rt2x00dev);
|
||||
|
||||
/*
|
||||
* 5 - Auto wakeup interrupt.
|
||||
@ -1011,6 +903,7 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
|
||||
.regbusy_read = rt2x00pci_regbusy_read,
|
||||
.drv_write_firmware = rt2800pci_write_firmware,
|
||||
.drv_init_registers = rt2800pci_init_registers,
|
||||
.drv_get_txwi = rt2800pci_get_txwi,
|
||||
};
|
||||
|
||||
static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
|
||||
@ -1030,7 +923,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
|
||||
.reset_tuner = rt2800_reset_tuner,
|
||||
.link_tuner = rt2800_link_tuner,
|
||||
.write_tx_desc = rt2800pci_write_tx_desc,
|
||||
.write_tx_data = rt2800pci_write_tx_data,
|
||||
.write_tx_data = rt2800_write_tx_data,
|
||||
.write_beacon = rt2800_write_beacon,
|
||||
.kick_tx_queue = rt2800pci_kick_tx_queue,
|
||||
.kill_tx_queue = rt2800pci_kill_tx_queue,
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
|
||||
Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
|
||||
Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
|
||||
@ -320,15 +321,14 @@ static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev,
|
||||
/*
|
||||
* TX descriptor initialization
|
||||
*/
|
||||
static void rt2800usb_write_tx_data(struct queue_entry* entry,
|
||||
struct txentry_desc *txdesc)
|
||||
static __le32 *rt2800usb_get_txwi(struct queue_entry *entry)
|
||||
{
|
||||
__le32 *txwi = (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE);
|
||||
|
||||
rt2800_write_txwi(txwi, txdesc);
|
||||
if (entry->queue->qid == QID_BEACON)
|
||||
return (__le32 *) (entry->skb->data);
|
||||
else
|
||||
return (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE);
|
||||
}
|
||||
|
||||
|
||||
static void rt2800usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
|
||||
struct sk_buff *skb,
|
||||
struct txentry_desc *txdesc)
|
||||
@ -378,6 +378,38 @@ static int rt2800usb_get_tx_data_len(struct queue_entry *entry)
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
* TX control handlers
|
||||
*/
|
||||
static void rt2800usb_work_txdone(struct work_struct *work)
|
||||
{
|
||||
struct rt2x00_dev *rt2x00dev =
|
||||
container_of(work, struct rt2x00_dev, txdone_work);
|
||||
struct data_queue *queue;
|
||||
struct queue_entry *entry;
|
||||
|
||||
rt2800_txdone(rt2x00dev);
|
||||
|
||||
/*
|
||||
* Process any trailing TX status reports for IO failures,
|
||||
* we loop until we find the first non-IO error entry. This
|
||||
* can either be a frame which is free, is being uploaded,
|
||||
* or has completed the upload but didn't have an entry
|
||||
* in the TX_STAT_FIFO register yet.
|
||||
*/
|
||||
tx_queue_for_each(rt2x00dev, queue) {
|
||||
while (!rt2x00queue_empty(queue)) {
|
||||
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||
|
||||
if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
|
||||
!test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
|
||||
break;
|
||||
|
||||
rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RX control handlers
|
||||
*/
|
||||
@ -514,6 +546,11 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
|
||||
*/
|
||||
rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
|
||||
|
||||
/*
|
||||
* Overwrite TX done handler
|
||||
*/
|
||||
PREPARE_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -549,6 +586,7 @@ static const struct rt2800_ops rt2800usb_rt2800_ops = {
|
||||
.regbusy_read = rt2x00usb_regbusy_read,
|
||||
.drv_write_firmware = rt2800usb_write_firmware,
|
||||
.drv_init_registers = rt2800usb_init_registers,
|
||||
.drv_get_txwi = rt2800usb_get_txwi,
|
||||
};
|
||||
|
||||
static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
|
||||
@ -566,7 +604,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
|
||||
.link_tuner = rt2800_link_tuner,
|
||||
.watchdog = rt2x00usb_watchdog,
|
||||
.write_tx_desc = rt2800usb_write_tx_desc,
|
||||
.write_tx_data = rt2800usb_write_tx_data,
|
||||
.write_tx_data = rt2800_write_tx_data,
|
||||
.write_beacon = rt2800_write_beacon,
|
||||
.get_tx_data_len = rt2800usb_get_tx_data_len,
|
||||
.kick_tx_queue = rt2x00usb_kick_tx_queue,
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
|
||||
Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
|
||||
<http://rt2x00.serialmonkey.com>
|
||||
|
||||
@ -698,6 +699,7 @@ struct rt2x00_dev {
|
||||
struct ieee80211_hw *hw;
|
||||
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
|
||||
enum ieee80211_band curr_band;
|
||||
int curr_freq;
|
||||
|
||||
/*
|
||||
* If enabled, the debugfs interface structures
|
||||
@ -849,11 +851,6 @@ struct rt2x00_dev {
|
||||
*/
|
||||
struct ieee80211_low_level_stats low_level_stats;
|
||||
|
||||
/*
|
||||
* RX configuration information.
|
||||
*/
|
||||
struct ieee80211_rx_status rx_status;
|
||||
|
||||
/*
|
||||
* Scheduled work.
|
||||
* NOTE: intf_work will use ieee80211_iterate_active_interfaces()
|
||||
@ -862,6 +859,12 @@ struct rt2x00_dev {
|
||||
*/
|
||||
struct work_struct intf_work;
|
||||
|
||||
/**
|
||||
* Scheduled work for TX/RX done handling (USB devices)
|
||||
*/
|
||||
struct work_struct rxdone_work;
|
||||
struct work_struct txdone_work;
|
||||
|
||||
/*
|
||||
* Data queue arrays for RX, TX and Beacon.
|
||||
* The Beacon array also contains the Atim queue
|
||||
@ -1071,6 +1074,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev);
|
||||
void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev);
|
||||
void rt2x00lib_txdone(struct queue_entry *entry,
|
||||
struct txdone_entry_desc *txdesc);
|
||||
void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status);
|
||||
void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
|
||||
struct queue_entry *entry);
|
||||
|
||||
|
@ -126,11 +126,6 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
|
||||
* ANTENNA_SW_DIVERSITY state to the driver.
|
||||
* If that happens, fallback to hardware defaults,
|
||||
* or our own default.
|
||||
* If diversity handling is active for a particular antenna,
|
||||
* we shouldn't overwrite that antenna.
|
||||
* The calls to rt2x00lib_config_antenna_check()
|
||||
* might have caused that we restore back to the already
|
||||
* active setting. If that has happened we can quit.
|
||||
*/
|
||||
if (!(ant->flags & ANTENNA_RX_DIVERSITY))
|
||||
config.rx = rt2x00lib_config_antenna_check(config.rx, def->rx);
|
||||
@ -142,9 +137,6 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
|
||||
else
|
||||
config.tx = active->tx;
|
||||
|
||||
if (config.rx == active->rx && config.tx == active->tx)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Antenna setup changes require the RX to be disabled,
|
||||
* else the changes will be ignored by the device.
|
||||
@ -209,10 +201,8 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
|
||||
rt2x00link_reset_tuner(rt2x00dev, false);
|
||||
|
||||
rt2x00dev->curr_band = conf->channel->band;
|
||||
rt2x00dev->curr_freq = conf->channel->center_freq;
|
||||
rt2x00dev->tx_power = conf->power_level;
|
||||
rt2x00dev->short_retry = conf->short_frame_max_tx_count;
|
||||
rt2x00dev->long_retry = conf->long_frame_max_tx_count;
|
||||
|
||||
rt2x00dev->rx_status.band = conf->channel->band;
|
||||
rt2x00dev->rx_status.freq = conf->channel->center_freq;
|
||||
}
|
||||
|
@ -31,15 +31,14 @@
|
||||
|
||||
enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key)
|
||||
{
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
if (key->keylen == WLAN_KEY_LEN_WEP40)
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
return CIPHER_WEP64;
|
||||
else
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
return CIPHER_WEP128;
|
||||
case ALG_TKIP:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
return CIPHER_TKIP;
|
||||
case ALG_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
return CIPHER_AES;
|
||||
default:
|
||||
return CIPHER_NONE;
|
||||
@ -95,7 +94,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev,
|
||||
overhead += key->iv_len;
|
||||
|
||||
if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) {
|
||||
if (key->alg == ALG_TKIP)
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
overhead += 8;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
|
||||
Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
<http://rt2x00.serialmonkey.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@ -383,14 +384,6 @@ void rt2x00lib_txdone(struct queue_entry *entry,
|
||||
* send the status report back.
|
||||
*/
|
||||
if (!(skbdesc_flags & SKBDESC_NOT_MAC80211))
|
||||
/*
|
||||
* Only PCI and SOC devices process the tx status in process
|
||||
* context. Hence use ieee80211_tx_status for PCI and SOC
|
||||
* devices and stick to ieee80211_tx_status_irqsafe for USB.
|
||||
*/
|
||||
if (rt2x00_is_usb(rt2x00dev))
|
||||
ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb);
|
||||
else
|
||||
ieee80211_tx_status(rt2x00dev->hw, entry->skb);
|
||||
else
|
||||
dev_kfree_skb_any(entry->skb);
|
||||
@ -403,7 +396,6 @@ void rt2x00lib_txdone(struct queue_entry *entry,
|
||||
|
||||
rt2x00dev->ops->lib->clear_entry(entry);
|
||||
|
||||
clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
|
||||
rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);
|
||||
|
||||
/*
|
||||
@ -416,6 +408,18 @@ void rt2x00lib_txdone(struct queue_entry *entry,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2x00lib_txdone);
|
||||
|
||||
void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status)
|
||||
{
|
||||
struct txdone_entry_desc txdesc;
|
||||
|
||||
txdesc.flags = 0;
|
||||
__set_bit(status, &txdesc.flags);
|
||||
txdesc.retry = 0;
|
||||
|
||||
rt2x00lib_txdone(entry, &txdesc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo);
|
||||
|
||||
static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
|
||||
struct rxdone_entry_desc *rxdesc)
|
||||
{
|
||||
@ -460,9 +464,13 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
|
||||
{
|
||||
struct rxdone_entry_desc rxdesc;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status;
|
||||
struct ieee80211_rx_status *rx_status;
|
||||
unsigned int header_length;
|
||||
int rate_idx;
|
||||
|
||||
if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
|
||||
goto submit_entry;
|
||||
|
||||
/*
|
||||
* Allocate a new sk_buffer. If no new buffer available, drop the
|
||||
* received frame and reuse the existing buffer.
|
||||
@ -527,39 +535,32 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
|
||||
*/
|
||||
rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc);
|
||||
rt2x00debug_update_crypto(rt2x00dev, &rxdesc);
|
||||
rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb);
|
||||
|
||||
/*
|
||||
* Initialize RX status information, and send frame
|
||||
* to mac80211.
|
||||
*/
|
||||
rx_status = IEEE80211_SKB_RXCB(entry->skb);
|
||||
rx_status->mactime = rxdesc.timestamp;
|
||||
rx_status->band = rt2x00dev->curr_band;
|
||||
rx_status->freq = rt2x00dev->curr_freq;
|
||||
rx_status->rate_idx = rate_idx;
|
||||
rx_status->signal = rxdesc.rssi;
|
||||
rx_status->flag = rxdesc.flags;
|
||||
rx_status->antenna = rt2x00dev->link.ant.active.rx;
|
||||
|
||||
/*
|
||||
* Send frame to mac80211 & debugfs.
|
||||
* mac80211 will clean up the skb structure.
|
||||
*/
|
||||
rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb);
|
||||
memcpy(IEEE80211_SKB_RXCB(entry->skb), rx_status, sizeof(*rx_status));
|
||||
|
||||
/*
|
||||
* Currently only PCI and SOC devices handle rx interrupts in process
|
||||
* context. Hence, use ieee80211_rx_irqsafe for USB and ieee80211_rx_ni
|
||||
* for PCI and SOC devices.
|
||||
*/
|
||||
if (rt2x00_is_usb(rt2x00dev))
|
||||
ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb);
|
||||
else
|
||||
ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
|
||||
|
||||
/*
|
||||
* Replace the skb with the freshly allocated one.
|
||||
*/
|
||||
entry->skb = skb;
|
||||
entry->flags = 0;
|
||||
|
||||
submit_entry:
|
||||
rt2x00dev->ops->lib->clear_entry(entry);
|
||||
|
||||
rt2x00queue_index_inc(entry->queue, Q_INDEX);
|
||||
rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2x00lib_rxdone);
|
||||
|
||||
@ -1017,6 +1018,8 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
|
||||
* Stop all work.
|
||||
*/
|
||||
cancel_work_sync(&rt2x00dev->intf_work);
|
||||
cancel_work_sync(&rt2x00dev->rxdone_work);
|
||||
cancel_work_sync(&rt2x00dev->txdone_work);
|
||||
|
||||
/*
|
||||
* Uninitialize device.
|
||||
|
@ -63,6 +63,9 @@ static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev)
|
||||
|
||||
INFO(rt2x00dev, "Firmware detected - version: %d.%d.\n",
|
||||
fw->data[fw->size - 4], fw->data[fw->size - 3]);
|
||||
snprintf(rt2x00dev->hw->wiphy->fw_version,
|
||||
sizeof(rt2x00dev->hw->wiphy->fw_version), "%d.%d",
|
||||
fw->data[fw->size - 4], fw->data[fw->size - 3]);
|
||||
|
||||
retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size);
|
||||
switch (retval) {
|
||||
|
@ -54,6 +54,16 @@ void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,
|
||||
*/
|
||||
if (txrate->flags & IEEE80211_TX_RC_MCS) {
|
||||
txdesc->mcs = txrate->idx;
|
||||
|
||||
/*
|
||||
* MIMO PS should be set to 1 for STA's using dynamic SM PS
|
||||
* when using more then one tx stream (>MCS7).
|
||||
*/
|
||||
if (tx_info->control.sta && txdesc->mcs > 7 &&
|
||||
(tx_info->control.sta->ht_cap.cap &
|
||||
(WLAN_HT_CAP_SM_PS_DYNAMIC <<
|
||||
IEEE80211_HT_CAP_SM_PS_SHIFT)))
|
||||
__set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags);
|
||||
} else {
|
||||
txdesc->mcs = rt2x00_get_rate_mcs(hwrate->mcs);
|
||||
if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
|
||||
Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
|
||||
<http://rt2x00.serialmonkey.com>
|
||||
|
||||
@ -730,9 +731,9 @@ void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev)
|
||||
rt2x00queue_reset(queue);
|
||||
|
||||
for (i = 0; i < queue->limit; i++) {
|
||||
queue->entries[i].flags = 0;
|
||||
|
||||
rt2x00dev->ops->lib->clear_entry(&queue->entries[i]);
|
||||
if (queue->qid == QID_RX)
|
||||
rt2x00queue_index_inc(queue, Q_INDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
|
||||
<http://rt2x00.serialmonkey.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@ -268,6 +268,7 @@ struct txdone_entry_desc {
|
||||
* @ENTRY_TXD_HT_AMPDU: This frame is part of an AMPDU.
|
||||
* @ENTRY_TXD_HT_BW_40: Use 40MHz Bandwidth.
|
||||
* @ENTRY_TXD_HT_SHORT_GI: Use short GI.
|
||||
* @ENTRY_TXD_HT_MIMO_PS: The receiving STA is in dynamic SM PS mode.
|
||||
*/
|
||||
enum txentry_desc_flags {
|
||||
ENTRY_TXD_RTS_FRAME,
|
||||
@ -286,6 +287,7 @@ enum txentry_desc_flags {
|
||||
ENTRY_TXD_HT_AMPDU,
|
||||
ENTRY_TXD_HT_BW_40,
|
||||
ENTRY_TXD_HT_SHORT_GI,
|
||||
ENTRY_TXD_HT_MIMO_PS,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -363,12 +365,16 @@ struct txentry_desc {
|
||||
* the device has signaled it is done with it.
|
||||
* @ENTRY_DATA_PENDING: This entry contains a valid frame and is waiting
|
||||
* for the signal to start sending.
|
||||
* @ENTRY_DATA_IO_FAILED: Hardware indicated that an IO error occured
|
||||
* while transfering the data to the hardware. No TX status report will
|
||||
* be expected from the hardware.
|
||||
*/
|
||||
enum queue_entry_flags {
|
||||
ENTRY_BCN_ASSIGNED,
|
||||
ENTRY_OWNER_DEVICE_DATA,
|
||||
ENTRY_OWNER_DEVICE_CRYPTO,
|
||||
ENTRY_DATA_PENDING,
|
||||
ENTRY_DATA_IO_FAILED
|
||||
};
|
||||
|
||||
/**
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user