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:
John W. Linville 2010-08-25 14:51:42 -04:00
commit e569aa78ba
158 changed files with 4645 additions and 2858 deletions

View 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 &ndash; 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>

View file

@ -12,7 +12,7 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \ kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \ gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
mac80211.xml debugobjects.xml sh.xml regulator.xml \ 80211.xml debugobjects.xml sh.xml regulator.xml \
alsa-driver-api.xml writing-an-alsa-driver.xml \ alsa-driver-api.xml writing-an-alsa-driver.xml \
tracepoint.xml media.xml drm.xml tracepoint.xml media.xml drm.xml

View file

@ -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>

View file

@ -2061,11 +2061,12 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
int i; 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", "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; return -EOPNOTSUPP;
key->hw_key_idx = key->keyidx; key->hw_key_idx = key->keyidx;

View file

@ -1190,14 +1190,13 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
if (info->control.hw_key) { if (info->control.hw_key) {
icv = info->control.hw_key->icv_len; icv = info->control.hw_key->icv_len;
switch (info->control.hw_key->alg) { switch (info->control.hw_key->cipher) {
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
case WLAN_CIPHER_SUITE_TKIP:
keytype = AR9170_TX_MAC_ENCR_RC4; keytype = AR9170_TX_MAC_ENCR_RC4;
break; break;
case ALG_TKIP: case WLAN_CIPHER_SUITE_CCMP:
keytype = AR9170_TX_MAC_ENCR_RC4;
break;
case ALG_CCMP:
keytype = AR9170_TX_MAC_ENCR_AES; keytype = AR9170_TX_MAC_ENCR_AES;
break; break;
default: 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)) if ((!ar->vif) || (ar->disable_offload))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (key->alg) { switch (key->cipher) {
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
if (key->keylen == WLAN_KEY_LEN_WEP40) ktype = AR9170_ENC_ALG_WEP64;
ktype = AR9170_ENC_ALG_WEP64;
else
ktype = AR9170_ENC_ALG_WEP128;
break; break;
case ALG_TKIP: case WLAN_CIPHER_SUITE_WEP104:
ktype = AR9170_ENC_ALG_WEP128;
break;
case WLAN_CIPHER_SUITE_TKIP:
ktype = AR9170_ENC_ALG_TKIP; ktype = AR9170_ENC_ALG_TKIP;
break; break;
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
ktype = AR9170_ENC_ALG_AESCCMP; ktype = AR9170_ENC_ALG_AESCCMP;
break; break;
default: default:
@ -1827,7 +1826,7 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
if (err) if (err)
goto out; goto out;
if (key->alg == ALG_TKIP) { if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL,
ktype, 1, key->key + 16, 16); ktype, 1, key->key + 16, 16);
if (err) if (err)
@ -1864,7 +1863,7 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
if (err) if (err)
goto out; goto out;
if (key->alg == ALG_TKIP) { if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
err = ar9170_upload_key(ar, key->hw_key_idx, err = ar9170_upload_key(ar, key->hw_key_idx,
NULL, NULL,
AR9170_ENC_ALG_NONE, 1, AR9170_ENC_ALG_NONE, 1,

View file

@ -552,9 +552,9 @@ ath5k_ani_mib_intr(struct ath5k_hw *ah)
if (ah->ah_sc->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO) if (ah->ah_sc->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
return; return;
/* if one of the errors triggered, we can get a superfluous second /* If one of the errors triggered, we can get a superfluous second
* interrupt, even though we have already reset the register. the * interrupt, even though we have already reset the register. The
* function detects that so we can return early */ * function detects that so we can return early. */
if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0) if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0)
return; return;

View file

@ -175,7 +175,7 @@
#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0 #define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0
#define AR5K_TUNE_RADAR_ALERT false #define AR5K_TUNE_RADAR_ALERT false
#define AR5K_TUNE_MIN_TX_FIFO_THRES 1 #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 #define AR5K_TUNE_REGISTER_TIMEOUT 20000
/* Register for RSSI threshold has a mask of 0xff, so 255 seems to /* Register for RSSI threshold has a mask of 0xff, so 255 seems to
* be the max value. */ * be the max value. */
@ -343,9 +343,6 @@ struct ath5k_srev_name {
#define AR5K_SREV_PHY_5413 0x61 #define AR5K_SREV_PHY_5413 0x61
#define AR5K_SREV_PHY_2425 0x70 #define AR5K_SREV_PHY_2425 0x70
/* IEEE defs */
#define IEEE80211_MAX_LEN 2500
/* TODO add support to mac80211 for vendor-specific rates and modes */ /* 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); void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class);
/* BSSID Functions */ /* BSSID Functions */
int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac); 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); void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask);
/* Receive start/stop functions */ /* Receive start/stop functions */
void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah); void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah);

View file

@ -139,12 +139,12 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
else else
ah->ah_version = AR5K_AR5212; ah->ah_version = AR5K_AR5212;
/*Fill the ath5k_hw struct with the needed functions*/ /* Fill the ath5k_hw struct with the needed functions */
ret = ath5k_hw_init_desc_functions(ah); ret = ath5k_hw_init_desc_functions(ah);
if (ret) if (ret)
goto err_free; 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); ret = ath5k_hw_nic_wakeup(ah, 0, true);
if (ret) if (ret)
goto err_free; goto err_free;
@ -158,7 +158,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
CHANNEL_5GHZ); CHANNEL_5GHZ);
ah->ah_phy = AR5K_PHY(0); 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) { switch (ah->ah_radio_5ghz_revision & 0xf0) {
case AR5K_SREV_RAD_5111: case AR5K_SREV_RAD_5111:
ah->ah_radio = AR5K_RF5111; 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 */ /* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN); 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_set_opmode(ah, sc->opmode);
ath5k_hw_rfgain_opt_init(ah); ath5k_hw_rfgain_opt_init(ah);

View file

@ -612,7 +612,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
goto err_free; 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); sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
if (!sc->ah) { if (!sc->ah) {
ret = -ENOMEM; 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. * Check if the MAC has multi-rate retry support.
* We do this by trying to setup a fake extended * We do this by trying to setup a fake extended
* descriptor. MAC's that don't have support will * descriptor. MACs that don't have support will
* return false w/o doing anything. MAC's that do * return false w/o doing anything. MACs that do
* support it will return true w/o doing anything. * support it will return true w/o doing anything.
*/ */
ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0); 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 * Allocate hardware transmit queues: one queue for
* beacon frames and one data queue for each QoS * 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. * these queues at the needed time.
*/ */
ret = ath5k_beaconq_setup(ah); 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: * NB: the order of these is important:
* o call the 802.11 layer before detaching ath5k_hw to * 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 * key cache entries can be handled
* o reclaim the tx queue data structures after calling * o reclaim the tx queue data structures after calling
* the 802.11 layer as we'll get called back to reclaim * 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. * Enable interrupts only for EOL and DESC conditions.
* We mark tx descriptors to receive a DESC interrupt * 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 * EOL to reap descriptors. Note that this is done to
* reduce interrupt load and this only defers reaping * reduce interrupt load and this only defers reaping
* descriptors, never transmitting frames. Aside from * descriptors, never transmitting frames. Aside from
@ -1709,7 +1709,7 @@ ath5k_rx_start(struct ath5k_softc *sc)
struct ath5k_buf *bf; struct ath5k_buf *bf;
int ret; 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", ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
common->cachelsz, common->rx_bufsize); 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) 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 * This function expects an 802.11 frame and returns the number of
* bytes added, or -1 if we don't have enought header room. * bytes added, or -1 if we don't have enough header room.
*/ */
static int ath5k_add_padding(struct sk_buff *skb) static int ath5k_add_padding(struct sk_buff *skb)
{ {
int padpos = ath5k_common_padpos(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 * The MAC header is padded to have 32-bit boundary if the
* bytes removed * 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) static int ath5k_remove_padding(struct sk_buff *skb)
{ {
int padpos = ath5k_common_padpos(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; 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); ath5k_remove_padding(skb);
rxs = IEEE80211_SKB_RXCB(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; return true;
} }
/* let crypto-error packets fall through in MNTR */ /* reject any frames with non-crypto errors */
if ((rs->rs_status & ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) || if (rs->rs_status & ~(AR5K_RXERR_DECRYPT))
sc->opmode != NL80211_IFTYPE_MONITOR)
return false; 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. * default antenna which is supposed to be an omni.
* *
* Note2: On sectored scenarios it's possible to have * Note2: On sectored scenarios it's possible to have
* multiple antennas (1omni -the default- and 14 sectors) * multiple antennas (1 omni -- the default -- and 14
* so if we choose to actually support this mode we need * sectors), so if we choose to actually support this
* to allow user to set how many antennas we have and tweak * mode, we need to allow the user to set how many antennas
* the code below to send beacons on all of them. * we have and tweak the code below to send beacons
* on all of them.
*/ */
if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP) if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP)
antenna = sc->bsent & 4 ? 2 : 1; 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"); ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION || if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION)) {
sc->opmode == NL80211_IFTYPE_MONITOR)) {
ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL); ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL);
return; return;
} }
/* /*
* Check if the previous beacon has gone out. If * 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 * period and wait for the next. Missed beacons
* indicate a problem and should not occur. If we * indicate a problem and should not occur. If we
* miss too many consecutive beacons reset the device. * 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); 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 * The hardware expects the header padded to 4 byte boundaries.
* if this is not the case we add the padding after the header * If this is not the case, we add the padding after the header.
*/ */
padsize = ath5k_add_padding(skb); padsize = ath5k_add_padding(skb);
if (padsize < 0) { if (padsize < 0) {
@ -3049,7 +3044,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_MONITOR:
sc->opmode = vif->type; sc->opmode = vif->type;
break; break;
default: default:
@ -3233,9 +3227,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
rfilt |= AR5K_RX_FILTER_PHYERR; rfilt |= AR5K_RX_FILTER_PHYERR;
/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons /* 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) 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 /* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not
* set we should only pass on control frames for this * 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) { switch (sc->opmode) {
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_MONITOR:
rfilt |= AR5K_RX_FILTER_CONTROL | rfilt |= AR5K_RX_FILTER_CONTROL |
AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_BEACON |
AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROBEREQ |
@ -3274,7 +3267,7 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
/* Set multicast bits */ /* Set multicast bits */
ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]); 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 */ * be set in HW */
sc->filter_flags = rfilt; 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) if (sc->opmode == NL80211_IFTYPE_AP)
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (key->alg) { switch (key->cipher) {
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
case ALG_TKIP: case WLAN_CIPHER_SUITE_WEP104:
case WLAN_CIPHER_SUITE_TKIP:
break; break;
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
if (sc->ah->ah_aes_support) if (sc->ah->ah_aes_support)
break; break;
@ -3475,7 +3469,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
/* Cache for later use during resets */ /* Cache for later use during resets */
memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
common->curaid = 0; common->curaid = 0;
ath5k_hw_set_associd(ah); ath5k_hw_set_bssid(ah);
mmiowb(); mmiowb();
} }
@ -3493,7 +3487,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
"Bss Info ASSOC %d, bssid: %pM\n", "Bss Info ASSOC %d, bssid: %pM\n",
bss_conf->aid, common->curbssid); bss_conf->aid, common->curbssid);
common->curaid = bss_conf->aid; common->curaid = bss_conf->aid;
ath5k_hw_set_associd(ah); ath5k_hw_set_bssid(ah);
/* Once ANI is available you would start it here */ /* Once ANI is available you would start it here */
} }
} }

View file

@ -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 * This function increases/decreases the tx trigger level for the tx fifo
* buffer (aka FIFO threshold) that is used to indicate when PCU flushes * 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 * 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 * 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 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: Link this with tx DMA size ?
* XXX: Use it to save interrupts ? * XXX: Use it to save interrupts ?

View file

@ -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 * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC
* steps that match with the power values we read from eeprom. On * steps that match with the power values we read from eeprom. On
* older eeprom versions (< 3.2) these steps are equaly spaced at * 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) * (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 * 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 * 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 /* 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 * So we need to calculate the starting offset for each section by using
* these two functions */ * these two functions */

View file

@ -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 * @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); struct ath_common *common = ath5k_hw_common(ah);
u16 tim_offset = 0; u16 tim_offset = 0;
/* /*
* Set simple BSSID mask on 5212 * Set BSSID mask on 5212
*/ */
if (ah->ah_version == AR5K_AR5212) if (ah->ah_version == AR5K_AR5212)
ath_hw_setbssidmask(common); ath_hw_setbssidmask(common);
/* /*
* Set BSSID which triggers the "SME Join" operation * Set BSSID
*/ */
ath5k_hw_reg_write(ah, ath5k_hw_reg_write(ah,
get_unaligned_le32(common->curbssid), get_unaligned_le32(common->curbssid),
@ -695,21 +694,18 @@ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry)
static static
int ath5k_keycache_type(const struct ieee80211_key_conf *key) int ath5k_keycache_type(const struct ieee80211_key_conf *key)
{ {
switch (key->alg) { switch (key->cipher) {
case ALG_TKIP: case WLAN_CIPHER_SUITE_TKIP:
return AR5K_KEYTABLE_TYPE_TKIP; return AR5K_KEYTABLE_TYPE_TKIP;
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
return AR5K_KEYTABLE_TYPE_CCM; return AR5K_KEYTABLE_TYPE_CCM;
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
if (key->keylen == WLAN_KEY_LEN_WEP40) return AR5K_KEYTABLE_TYPE_40;
return AR5K_KEYTABLE_TYPE_40; case WLAN_CIPHER_SUITE_WEP104:
else if (key->keylen == WLAN_KEY_LEN_WEP104) return AR5K_KEYTABLE_TYPE_104;
return AR5K_KEYTABLE_TYPE_104;
return -EINVAL;
default: default:
return -EINVAL; return -EINVAL;
} }
return -EINVAL;
} }
/* /*
@ -728,7 +724,7 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
bool is_tkip; bool is_tkip;
const u8 *key_ptr; 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. * key->keylen comes in from mac80211 in bytes.

View file

@ -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. * (temperature mostly) based on feedback from a power detector.
* *
* It's only used on RF5111 and RF5112, later RF chips seem to have * 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 /* 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) static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah)
{ {
const struct ath5k_gain_opt *go; const struct ath5k_gain_opt *go;
@ -367,7 +367,7 @@ done:
return ret; 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 * Check for a new gain reading and schedule an adjustment
* if needed. * if needed.
* *
@ -433,7 +433,7 @@ done:
return ah->ah_gain.g_state; 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 * this one works on all RF chips and has nothing to do
* with gain_F calibration */ * with gain_F calibration */
int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq) 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, int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
unsigned int mode) unsigned int mode)
@ -571,7 +571,7 @@ int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
return -EINVAL; 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 * ah->ah_rf_banks based on ah->ah_rf_banks_size
* we set above */ * we set above */
if (ah->ah_rf_banks == NULL) { 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 */ /* Limit max power if we have a CTL available */
ath5k_get_max_ctl_power(ah, channel); 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: Antenna reduction stuff */
/* FIXME: Limit power on turbo modes */ /* FIXME: Limit power on turbo modes */

View file

@ -1911,7 +1911,7 @@
#define AR5K_PHY_TURBO 0x9804 /* Register Address */ #define AR5K_PHY_TURBO 0x9804 /* Register Address */
#define AR5K_PHY_TURBO_MODE 0x00000001 /* Enable turbo mode */ #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_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 * PHY agility command register

View file

@ -959,7 +959,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
AR5K_QUEUE_DCU_SEQNUM(0)); 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 * As a workaround save it here and restore
* it later so that it's back in time after * it later so that it's back in time after
* reset. This way it'll get re-synced on the * 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; return ret;
/* Spur info is available only from EEPROM versions /* 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 */ * static values for older versions */
if (ah->ah_mac_srev >= AR5K_SREV_AR5424) if (ah->ah_mac_srev >= AR5K_SREV_AR5424)
ath5k_hw_set_spur_mitigation_filter(ah, 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 */ /* Restore bssid and bssid mask */
ath5k_hw_set_associd(ah); ath5k_hw_set_bssid(ah);
/* Set PCU config */ /* Set PCU config */
ath5k_hw_set_opmode(ah, op_mode); 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 /* Set RSSI/BRSSI thresholds
* *
* Note: If we decide to set this value * Note: If we decide to set this value
* dynamicaly, have in mind that when AR5K_RSSI_THR * dynamically, keep in mind that when AR5K_RSSI_THR
* register is read it might return 0x40 if we haven't * register is read, it might return 0x40 if we haven't
* wrote anything to it plus BMISS RSSI threshold is zeroed. * written anything to it. Also, BMISS RSSI threshold is zeroed.
* So doing a save/restore procedure here isn't the right * 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 | ath5k_hw_reg_write(ah, (AR5K_TUNE_RSSI_THRES |
AR5K_TUNE_BMISS_THRES << AR5K_TUNE_BMISS_THRES <<
AR5K_RSSI_THR_BMISS_S), 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 * 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); phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1);
ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, 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 * This method is used to calibrate some static offsets
* used together with on-the fly I/Q calibration (the * 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. * interrupt rx path.
* *
* While rx path is re-routed to the power detector we also * 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 * 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. * out and/or noise floor calibration might timeout.
*/ */
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,

View file

@ -25,10 +25,10 @@
* *
* We don't write on those registers directly but * We don't write on those registers directly but
* we send a data packet on the chip, using a special register, * we send a data packet on the chip, using a special register,
* that holds all the settings we need. After we 've sent the * that holds all the settings we need. After we've sent the
* data packet, we write on another special register to notify hw * data packet, we write on another special register to notify hw
* to apply the settings. This is done so that control registers * 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. * are applied faster on the hw.
* *
* We call each data packet an "RF Bank" and all the data we write * We call each data packet an "RF Bank" and all the data we write

View file

@ -46,6 +46,7 @@ ath9k_htc-y += htc_hst.o \
htc_drv_txrx.o \ htc_drv_txrx.o \
htc_drv_main.o \ htc_drv_main.o \
htc_drv_beacon.o \ htc_drv_beacon.o \
htc_drv_init.o htc_drv_init.o \
htc_drv_gpio.o
obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o

View file

@ -185,7 +185,7 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
ath_print(common, ATH_DBG_INTERRUPT, ath_print(common, ATH_DBG_INTERRUPT,
"AR_INTR_SYNC_LOCAL_TIMEOUT\n"); "AR_INTR_SYNC_LOCAL_TIMEOUT\n");
REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause);
(void) REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR); (void) REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR);
} }

View file

@ -423,6 +423,7 @@ int ath_beaconq_config(struct ath_softc *sc);
#define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */ #define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */
#define ATH_ANI_POLLINTERVAL_OLD 100 /* 100 ms */ #define ATH_ANI_POLLINTERVAL_OLD 100 /* 100 ms */
#define ATH_ANI_POLLINTERVAL_NEW 1000 /* 1000 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_LONG_CALINTERVAL 30000 /* 30 seconds */
#define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */
@ -436,14 +437,6 @@ void ath_ani_calibrate(unsigned long data);
/* BTCOEX */ /* 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 { struct ath_btcoex {
bool hw_timer_enabled; bool hw_timer_enabled;
spinlock_t btcoex_lock; spinlock_t btcoex_lock;

View file

@ -359,11 +359,12 @@ void ath_beacon_tasklet(unsigned long data)
sc->beacon.bmisscnt++; sc->beacon.bmisscnt++;
if (sc->beacon.bmisscnt < BSTUCK_THRESH) { if (sc->beacon.bmisscnt < BSTUCK_THRESH) {
ath_print(common, ATH_DBG_BEACON, ath_print(common, ATH_DBG_BSTUCK,
"missed %u consecutive beacons\n", "missed %u consecutive beacons\n",
sc->beacon.bmisscnt); sc->beacon.bmisscnt);
ath9k_hw_bstuck_nfcal(ah);
} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
ath_print(common, ATH_DBG_BEACON, ath_print(common, ATH_DBG_BSTUCK,
"beacon is officially stuck\n"); "beacon is officially stuck\n");
sc->sc_flags |= SC_OP_TSF_RESET; sc->sc_flags |= SC_OP_TSF_RESET;
ath_reset(sc, false); ath_reset(sc, false);
@ -373,7 +374,7 @@ void ath_beacon_tasklet(unsigned long data)
} }
if (sc->beacon.bmisscnt != 0) { if (sc->beacon.bmisscnt != 0) {
ath_print(common, ATH_DBG_BEACON, ath_print(common, ATH_DBG_BSTUCK,
"resume beacon xmit after %u misses\n", "resume beacon xmit after %u misses\n",
sc->beacon.bmisscnt); sc->beacon.bmisscnt);
sc->beacon.bmisscnt = 0; sc->beacon.bmisscnt = 0;

View file

@ -168,6 +168,7 @@ EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight);
static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah) static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah)
{ {
struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
u32 val;
/* /*
* Program coex mode and weight registers to * 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_WEIGHT, btcoex_hw->bt_coex_weights);
REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_hw->bt_coex_mode2); 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_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0);

View file

@ -19,8 +19,7 @@
/* Common calibration code */ /* Common calibration code */
/* We can tune this as we go by monitoring really low values */ #define ATH9K_NF_TOO_HIGH -60
#define ATH9K_NF_TOO_LOW -60
static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) 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; 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) 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; int i;
h = cal->nfCalHist;
limit = ath9k_hw_get_nf_limits(ah, ah->curchan);
for (i = 0; i < NUM_NF_READINGS; i++) { for (i = 0; i < NUM_NF_READINGS; i++) {
h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
@ -63,7 +90,39 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
h[i].privNF = h[i].privNF =
ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); 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, static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah,
@ -104,19 +163,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
ah->cal_samples = 0; 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 */ /* This is done for the currently configured channel */
bool ath9k_hw_reset_calvalid(struct ath_hw *ah) 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", "NF calibrated [%s] [chain %d] is %d\n",
(i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); (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, ath_print(common, ATH_DBG_CALIBRATE,
"NF[%d] (%d) > MAX (%d), correcting to MAX", "NF[%d] (%d) > MAX (%d), correcting to MAX",
i, nf[i], limit->max); i, nf[i], ATH9K_NF_TOO_HIGH);
nf[i] = limit->max; nf[i] = limit->max;
} else if (nf[i] < limit->min) { } else if (nf[i] < limit->min) {
ath_print(common, ATH_DBG_CALIBRATE, 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; h = caldata->nfCalHist;
caldata->nfcal_pending = false; 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; caldata->rawNoiseFloor = h[0].privNF;
return true; return true;
} }
@ -361,3 +407,28 @@ s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
return ah->caldata->rawNoiseFloor; return ah->caldata->rawNoiseFloor;
} }
EXPORT_SYMBOL(ath9k_hw_getchan_noise); 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);

View file

@ -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); bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
struct ath9k_channel *chan); 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); s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_hw_reset_calibration(struct ath_hw *ah, void ath9k_hw_reset_calibration(struct ath_hw *ah,
struct ath9k_cal_list *currCal); struct ath9k_cal_list *currCal);

View file

@ -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); struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
if (tx_info->control.hw_key) { 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; 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; 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; return ATH9K_KEY_TYPE_AES;
default:
break;
}
} }
return ATH9K_KEY_TYPE_CLEAR; 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, static int ath_reserve_key_cache_slot(struct ath_common *common,
enum ieee80211_key_alg alg) u32 cipher)
{ {
int i; int i;
if (alg == ALG_TKIP) if (cipher == WLAN_CIPHER_SUITE_TKIP)
return ath_reserve_key_cache_slot_tkip(common); return ath_reserve_key_cache_slot_tkip(common);
/* First, try to find slots that would not be available for TKIP. */ /* 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)); memset(&hk, 0, sizeof(hk));
switch (key->alg) { switch (key->cipher) {
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
hk.kv_type = ATH9K_CIPHER_WEP; hk.kv_type = ATH9K_CIPHER_WEP;
break; break;
case ALG_TKIP: case WLAN_CIPHER_SUITE_TKIP:
hk.kv_type = ATH9K_CIPHER_TKIP; hk.kv_type = ATH9K_CIPHER_TKIP;
break; break;
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
hk.kv_type = ATH9K_CIPHER_AES_CCM; hk.kv_type = ATH9K_CIPHER_AES_CCM;
break; break;
default: default:
@ -316,7 +322,7 @@ int ath9k_cmn_key_config(struct ath_common *common,
memcpy(gmac, vif->addr, ETH_ALEN); memcpy(gmac, vif->addr, ETH_ALEN);
gmac[0] |= 0x01; gmac[0] |= 0x01;
mac = gmac; mac = gmac;
idx = ath_reserve_key_cache_slot(common, key->alg); idx = ath_reserve_key_cache_slot(common, key->cipher);
break; break;
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
if (!sta) { if (!sta) {
@ -326,7 +332,7 @@ int ath9k_cmn_key_config(struct ath_common *common,
memcpy(gmac, sta->addr, ETH_ALEN); memcpy(gmac, sta->addr, ETH_ALEN);
gmac[0] |= 0x01; gmac[0] |= 0x01;
mac = gmac; mac = gmac;
idx = ath_reserve_key_cache_slot(common, key->alg); idx = ath_reserve_key_cache_slot(common, key->cipher);
break; break;
default: default:
idx = key->keyidx; idx = key->keyidx;
@ -348,13 +354,13 @@ int ath9k_cmn_key_config(struct ath_common *common,
return -EOPNOTSUPP; return -EOPNOTSUPP;
mac = sta->addr; mac = sta->addr;
idx = ath_reserve_key_cache_slot(common, key->alg); idx = ath_reserve_key_cache_slot(common, key->cipher);
} }
if (idx < 0) if (idx < 0)
return -ENOSPC; /* no free key cache entries */ 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, ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
vif->type == NL80211_IFTYPE_AP); vif->type == NL80211_IFTYPE_AP);
else else
@ -364,7 +370,7 @@ int ath9k_cmn_key_config(struct ath_common *common,
return -EIO; return -EIO;
set_bit(idx, common->keymap); set_bit(idx, common->keymap);
if (key->alg == ALG_TKIP) { if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
set_bit(idx + 64, common->keymap); set_bit(idx + 64, common->keymap);
if (common->splitmic) { if (common->splitmic) {
set_bit(idx + 32, common->keymap); set_bit(idx + 32, common->keymap);
@ -389,7 +395,7 @@ void ath9k_cmn_key_delete(struct ath_common *common,
return; return;
clear_bit(key->hw_key_idx, common->keymap); clear_bit(key->hw_key_idx, common->keymap);
if (key->alg != ALG_TKIP) if (key->cipher != WLAN_CIPHER_SUITE_TKIP)
return; return;
clear_bit(key->hw_key_idx + 64, common->keymap); 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); 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) static int __init ath9k_cmn_init(void)
{ {
return 0; return 0;

View file

@ -52,6 +52,14 @@
#define ATH_EP_RND(x, mul) \ #define ATH_EP_RND(x, mul) \
((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (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_padpos(__le16 frame_control);
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw, 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, void ath9k_cmn_key_delete(struct ath_common *common,
struct ieee80211_key_conf *key); struct ieee80211_key_conf *key);
int ath9k_cmn_count_streams(unsigned int chainmask, int max); 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);

View file

@ -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, static void ath9k_gen_timer_start(struct ath_hw *ah,
struct ath_gen_timer *timer, struct ath_gen_timer *timer,
u32 timer_next, 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_softc *sc = (struct ath_softc *) data;
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_btcoex *btcoex = &sc->btcoex; struct ath_btcoex *btcoex = &sc->btcoex;
struct ath_common *common = ath9k_hw_common(ah);
u32 timer_period; u32 timer_period;
bool is_btscan; bool is_btscan;
@ -328,7 +299,7 @@ static void ath_btcoex_period_timer(unsigned long data)
spin_lock_bh(&btcoex->btcoex_lock); 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); btcoex->bt_stomp_type);
spin_unlock_bh(&btcoex->btcoex_lock); 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_softc *sc = (struct ath_softc *)arg;
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_btcoex *btcoex = &sc->btcoex; struct ath_btcoex *btcoex = &sc->btcoex;
struct ath_common *common = ath9k_hw_common(ah);
bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN; 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"); "no stomp timer running\n");
spin_lock_bh(&btcoex->btcoex_lock); spin_lock_bh(&btcoex->btcoex_lock);
if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) 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) 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); spin_unlock_bh(&btcoex->btcoex_lock);
} }

View file

@ -920,7 +920,8 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,
} }
ret = ath9k_htc_hw_init(hif_dev->htc_handle, 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) { if (ret) {
ret = -EINVAL; ret = -EINVAL;
goto err_htc_hw_init; goto err_htc_hw_init;

View file

@ -316,17 +316,32 @@ struct htc_beacon_config {
u8 dtim_count; u8 dtim_count;
}; };
#define OP_INVALID BIT(0) struct ath_btcoex {
#define OP_SCANNING BIT(1) u32 bt_priority_cnt;
#define OP_FULL_RESET BIT(2) unsigned long bt_priority_time;
#define OP_LED_ASSOCIATED BIT(3) int bt_stomp_type; /* Types of BT stomping */
#define OP_LED_ON BIT(4) u32 btcoex_no_stomp;
#define OP_PREAMBLE_SHORT BIT(5) u32 btcoex_period;
#define OP_PROTECT_ENABLE BIT(6) u32 btscan_no_stomp;
#define OP_ASSOCIATED BIT(7) };
#define OP_ENABLE_BEACON BIT(8)
#define OP_LED_DEINIT BIT(9) void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv);
#define OP_UNPLUGGED BIT(10) 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)
#define OP_LED_ASSOCIATED BIT(3)
#define OP_LED_ON BIT(4)
#define OP_PREAMBLE_SHORT BIT(5)
#define OP_PROTECT_ENABLE BIT(6)
#define OP_ASSOCIATED BIT(7)
#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 ath9k_htc_priv {
struct device *dev; struct device *dev;
@ -391,6 +406,9 @@ struct ath9k_htc_priv {
int cabq; int cabq;
int hwq_map[WME_NUM_AC]; 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 #ifdef CONFIG_ATH9K_HTC_DEBUGFS
struct ath9k_debug debug; struct ath9k_debug debug;
#endif #endif
@ -443,7 +461,7 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv);
void ath9k_deinit_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, 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); void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug);
#ifdef CONFIG_PM #ifdef CONFIG_PM
int ath9k_htc_resume(struct htc_target *htc_handle); int ath9k_htc_resume(struct htc_target *htc_handle);

View 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);
}

View file

@ -41,6 +41,8 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
.max_power = 20, \ .max_power = 20, \
} }
#define ATH_HTC_BTCOEX_PRODUCT_ID "wb193"
static struct ieee80211_channel ath9k_2ghz_channels[] = { static struct ieee80211_channel ath9k_2ghz_channels[] = {
CHAN2G(2412, 0), /* Channel 1 */ CHAN2G(2412, 0), /* Channel 1 */
CHAN2G(2417, 1), /* Channel 2 */ 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; 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_hw *ah = NULL;
struct ath_common *common; 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_channels_rates(priv);
ath9k_init_misc(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; return 0;
err_queues: err_queues:
@ -734,7 +765,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
SET_IEEE80211_PERM_ADDR(hw, common->macaddr); 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 ieee80211_hw *hw = priv->hw;
struct ath_common *common; struct ath_common *common;
@ -743,7 +775,7 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid)
struct ath_regulatory *reg; struct ath_regulatory *reg;
/* Bring up device */ /* Bring up device */
error = ath9k_init_priv(priv, devid); error = ath9k_init_priv(priv, devid, product);
if (error != 0) if (error != 0)
goto err_init; goto err_init;
@ -801,7 +833,7 @@ err_init:
} }
int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
u16 devid) u16 devid, char *product)
{ {
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct ath9k_htc_priv *priv; 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. */ /* The device may have been unplugged earlier. */
priv->op_flags &= ~OP_UNPLUGGED; priv->op_flags &= ~OP_UNPLUGGED;
ret = ath9k_init_device(priv, devid); ret = ath9k_init_device(priv, devid, product);
if (ret) if (ret)
goto err_init; goto err_init;

View file

@ -1210,6 +1210,12 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
ieee80211_wake_queues(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); mutex_unlock(&priv->mutex);
return ret; return ret;
@ -1254,6 +1260,12 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
"Monitor interface removed\n"); "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_phy_disable(ah);
ath9k_hw_disable(ah); ath9k_hw_disable(ah);
ath9k_hw_configpcipowersave(ah, 1, 1); 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; key->hw_key_idx = ret;
/* push IV and Michael MIC generation to stack */ /* push IV and Michael MIC generation to stack */
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; 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; 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; key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
ret = 0; ret = 0;
} }

View file

@ -462,9 +462,9 @@ void ath9k_htc_hw_free(struct htc_target *htc)
} }
int ath9k_htc_hw_init(struct htc_target *target, 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"); printk(KERN_ERR "Failed to initialize the device\n");
return -ENODEV; return -ENODEV;
} }

View file

@ -239,7 +239,7 @@ struct htc_target *ath9k_htc_hw_alloc(void *hif_handle,
struct device *dev); struct device *dev);
void ath9k_htc_hw_free(struct htc_target *htc); void ath9k_htc_hw_free(struct htc_target *htc);
int ath9k_htc_hw_init(struct htc_target *target, 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); void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug);
#endif /* HTC_HST_H */ #endif /* HTC_HST_H */

View file

@ -355,6 +355,7 @@ struct ath9k_hw_cal_data {
int16_t rawNoiseFloor; int16_t rawNoiseFloor;
bool paprd_done; bool paprd_done;
bool nfcal_pending; bool nfcal_pending;
bool nfcal_interference;
u16 small_signal_gain[AR9300_MAX_CHAINS]; u16 small_signal_gain[AR9300_MAX_CHAINS];
u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ];
struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];

View file

@ -226,9 +226,10 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
caldata = &aphy->caldata; caldata = &aphy->caldata;
ath_print(common, ATH_DBG_CONFIG, 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, 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); spin_lock_bh(&sc->sc_resetlock);
@ -395,7 +396,12 @@ void ath_ani_calibrate(unsigned long data)
bool shortcal = false; bool shortcal = false;
bool aniflag = false; bool aniflag = false;
unsigned int timestamp = jiffies_to_msecs(jiffies); 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) ? short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
@ -407,7 +413,7 @@ void ath_ani_calibrate(unsigned long data)
ath9k_ps_wakeup(sc); ath9k_ps_wakeup(sc);
/* Long calibration runs independently of short calibration. */ /* 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; longcal = true;
ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
common->ani.longcal_timer = timestamp; common->ani.longcal_timer = timestamp;
@ -1776,9 +1782,10 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
key->hw_key_idx = ret; key->hw_key_idx = ret;
/* push IV and Michael MIC generation to stack */ /* push IV and Michael MIC generation to stack */
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; 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; 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; key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
ret = 0; ret = 0;
} }

View file

@ -85,6 +85,8 @@ static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
return "WMI_TGT_DETACH_CMDID"; return "WMI_TGT_DETACH_CMDID";
case WMI_TGT_TXQ_ENABLE_CMDID: case WMI_TGT_TXQ_ENABLE_CMDID:
return "WMI_TGT_TXQ_ENABLE_CMDID"; return "WMI_TGT_TXQ_ENABLE_CMDID";
case WMI_AGGR_LIMIT_CMD:
return "WMI_AGGR_LIMIT_CMD";
} }
return "Bogus"; return "Bogus";

View file

@ -71,6 +71,7 @@ enum wmi_cmd_id {
WMI_TX_AGGR_ENABLE_CMDID, WMI_TX_AGGR_ENABLE_CMDID,
WMI_TGT_DETACH_CMDID, WMI_TGT_DETACH_CMDID,
WMI_TGT_TXQ_ENABLE_CMDID, WMI_TGT_TXQ_ENABLE_CMDID,
WMI_AGGR_LIMIT_CMD = 0x0026,
}; };
enum wmi_event_id { enum wmi_event_id {

View file

@ -1407,22 +1407,6 @@ static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
return htype; 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, static void assign_aggr_tid_seqno(struct sk_buff *skb,
struct ath_buf *bf) 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_state.bfs_paprd_timestamp = jiffies;
bf->bf_flags = setup_tx_flags(skb, use_ldpc); 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) { if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) {
bf->bf_frmlen += tx_info->control.hw_key->icv_len; bf->bf_frmlen += tx_info->control.hw_key->icv_len;
bf->bf_keyix = tx_info->control.hw_key->hw_key_idx; bf->bf_keyix = tx_info->control.hw_key->hw_key_idx;

View file

@ -36,6 +36,7 @@
* @ATH_DBG_PS: power save processing * @ATH_DBG_PS: power save processing
* @ATH_DBG_HWTIMER: hardware timer handling * @ATH_DBG_HWTIMER: hardware timer handling
* @ATH_DBG_BTCOEX: bluetooth coexistance * @ATH_DBG_BTCOEX: bluetooth coexistance
* @ATH_DBG_BSTUCK: stuck beacons
* @ATH_DBG_ANY: enable all debugging * @ATH_DBG_ANY: enable all debugging
* *
* The debug level is used to control the amount and type of debugging output * 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_HWTIMER = 0x00001000,
ATH_DBG_BTCOEX = 0x00002000, ATH_DBG_BTCOEX = 0x00002000,
ATH_DBG_WMI = 0x00004000, ATH_DBG_WMI = 0x00004000,
ATH_DBG_BSTUCK = 0x00008000,
ATH_DBG_ANY = 0xffffffff ATH_DBG_ANY = 0xffffffff
}; };

View file

@ -2280,6 +2280,7 @@ out:
static int b43_upload_microcode(struct b43_wldev *dev) 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 size_t hdr_len = sizeof(struct b43_fw_header);
const __be32 *data; const __be32 *data;
unsigned int i, len; 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)) { if (b43_is_old_txhdr_format(dev)) {
/* We're over the deadline, but we keep support for old fw /* We're over the deadline, but we keep support for old fw
* until it turns out to be in major conflict with something new. */ * 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; err = -EINVAL;
switch (key->alg) { switch (key->cipher) {
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
if (key->keylen == WLAN_KEY_LEN_WEP40) algorithm = B43_SEC_ALGO_WEP40;
algorithm = B43_SEC_ALGO_WEP40;
else
algorithm = B43_SEC_ALGO_WEP104;
break; break;
case ALG_TKIP: case WLAN_CIPHER_SUITE_WEP104:
algorithm = B43_SEC_ALGO_WEP104;
break;
case WLAN_CIPHER_SUITE_TKIP:
algorithm = B43_SEC_ALGO_TKIP; algorithm = B43_SEC_ALGO_TKIP;
break; break;
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
algorithm = B43_SEC_ALGO_AES; algorithm = B43_SEC_ALGO_AES;
break; break;
default: 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); B43_WARN_ON(dev && b43_status(dev) > B43_STAT_INITIALIZED);
if (!dev || b43_status(dev) != B43_STAT_INITIALIZED) if (!dev || b43_status(dev) != B43_STAT_INITIALIZED)
return; return;
/* Unregister HW RNG driver */
b43_rng_exit(dev->wl);
b43_set_status(dev, B43_STAT_UNINIT); b43_set_status(dev, B43_STAT_UNINIT);
/* Stop the microcode PSM. */ /* 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); b43_set_status(dev, B43_STAT_INITIALIZED);
/* Register HW RNG driver */
b43_rng_init(dev->wl);
out: out:
return err; return err;
@ -4984,7 +4996,6 @@ static int b43_probe(struct ssb_device *dev, const struct ssb_device_id *id)
if (err) if (err)
goto err_one_core_detach; goto err_one_core_detach;
b43_leds_register(wl->current_dev); b43_leds_register(wl->current_dev);
b43_rng_init(wl);
} }
out: out:
@ -5020,7 +5031,6 @@ static void b43_remove(struct ssb_device *dev)
b43_one_core_detach(dev); b43_one_core_detach(dev);
if (list_empty(&wl->devlist)) { if (list_empty(&wl->devlist)) {
b43_rng_exit(wl);
b43_leds_unregister(wl); b43_leds_unregister(wl);
/* Last core on the chip unregistered. /* Last core on the chip unregistered.
* We can destroy common struct b43_wl. * We can destroy common struct b43_wl.

View file

@ -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 */ /* 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; struct b43_phy_n *nphy = dev->phy.n;
u8 i, j; 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, 0, events1, delays1, 7);
b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 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 (dev->phy.rev < 2) {
if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2) 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) { } else if (dev->phy.rev == 2) {
b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0); b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0);
b43_phy_write(dev, B43_NPHY_CRSCHECK3, 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); 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 * Init N-PHY
* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N * 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_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA);
b43_nphy_bmac_clock_fgc(dev, 0); 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_pa_override(dev, false);
b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); 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) 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) if (nphy->mphase_cal_phase_id > 0)
;/* TODO PHY Periodic Calibration Multi-Phase Restart */ ;/* TODO PHY Periodic Calibration Multi-Phase Restart */

View file

@ -1623,6 +1623,7 @@ error:
static int b43legacy_upload_microcode(struct b43legacy_wldev *dev) 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 size_t hdr_len = sizeof(struct b43legacy_fw_header);
const __be32 *data; const __be32 *data;
unsigned int i; unsigned int i;
@ -1732,6 +1733,10 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
dev->fw.rev = fwrev; dev->fw.rev = fwrev;
dev->fw.patch = fwpatch; 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; return 0;
error: error:

View file

@ -1696,7 +1696,7 @@ static int prism2_request_scan(struct net_device *dev)
hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE,
HFA384X_ROAMING_FIRMWARE); HFA384X_ROAMING_FIRMWARE);
return 0; return ret;
} }
#else /* !PRISM2_NO_STATION_MODES */ #else /* !PRISM2_NO_STATION_MODES */

View file

@ -3056,9 +3056,9 @@ static void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
packet = list_entry(element, struct ipw2100_tx_packet, list); 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], &txq->drv[txq->next],
(void *)(txq->nic + txq->next * (u32) (txq->nic + txq->next *
sizeof(struct ipw2100_bd))); sizeof(struct ipw2100_bd)));
packet->index = txq->next; packet->index = txq->next;

View file

@ -3,6 +3,9 @@ config IWLWIFI
depends on PCI && MAC80211 depends on PCI && MAC80211
select FW_LOADER select FW_LOADER
menu "Debugging Options"
depends on IWLWIFI
config IWLWIFI_DEBUG config IWLWIFI_DEBUG
bool "Enable full debugging output in iwlagn and iwl3945 drivers" bool "Enable full debugging output in iwlagn and iwl3945 drivers"
depends on IWLWIFI depends on IWLWIFI
@ -36,6 +39,12 @@ config IWLWIFI_DEBUGFS
is a low-impact option that allows getting insight into the is a low-impact option that allows getting insight into the
driver's state at runtime. 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 config IWLWIFI_DEVICE_TRACING
bool "iwlwifi device access tracing" bool "iwlwifi device access tracing"
depends on IWLWIFI depends on IWLWIFI
@ -53,6 +62,7 @@ config IWLWIFI_DEVICE_TRACING
If unsure, say Y so we can help you better when problems If unsure, say Y so we can help you better when problems
occur. occur.
endmenu
config IWLAGN config IWLAGN
tristate "Intel Wireless WiFi Next Gen AGN (iwlagn)" tristate "Intel Wireless WiFi Next Gen AGN (iwlagn)"

View file

@ -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.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-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-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_IWLWIFI_DEBUGFS) += iwl-agn-debugfs.o
iwlagn-$(CONFIG_IWL4965) += iwl-4965.o iwlagn-$(CONFIG_IWL4965) += iwl-4965.o

View file

@ -229,6 +229,11 @@ static struct iwl_lib_ops iwl1000_lib = {
.check_ack_health = iwl_good_ack_health, .check_ack_health = iwl_good_ack_health,
.txfifo_flush = iwlagn_txfifo_flush, .txfifo_flush = iwlagn_txfifo_flush,
.dev_txfifo_flush = iwlagn_dev_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 = { static const struct iwl_ops iwl1000_ops = {

View file

@ -1470,7 +1470,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv,
cmd.band = band; cmd.band = band;
cmd.expect_beacon = 0; 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.channel = cpu_to_le16(ch);
cmd.rxon_flags = priv->staging_rxon.flags; cmd.rxon_flags = priv->staging_rxon.flags;
cmd.rxon_filter_flags = priv->staging_rxon.filter_flags; cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;

View file

@ -291,7 +291,7 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
}; };
cmd.band = priv->band == IEEE80211_BAND_2GHZ; 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", IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
priv->active_rxon.channel, ch); priv->active_rxon.channel, ch);
cmd.channel = cpu_to_le16(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, .check_ack_health = iwl_good_ack_health,
.txfifo_flush = iwlagn_txfifo_flush, .txfifo_flush = iwlagn_txfifo_flush,
.dev_txfifo_flush = iwlagn_dev_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 = { 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, .check_ack_health = iwl_good_ack_health,
.txfifo_flush = iwlagn_txfifo_flush, .txfifo_flush = iwlagn_txfifo_flush,
.dev_txfifo_flush = iwlagn_dev_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 = { static const struct iwl_ops iwl5000_ops = {

View file

@ -214,7 +214,7 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
}; };
cmd.band = priv->band == IEEE80211_BAND_2GHZ; 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", IWL_DEBUG_11H(priv, "channel switch from %u to %u\n",
priv->active_rxon.channel, ch); priv->active_rxon.channel, ch);
cmd.channel = cpu_to_le16(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, .check_ack_health = iwl_good_ack_health,
.txfifo_flush = iwlagn_txfifo_flush, .txfifo_flush = iwlagn_txfifo_flush,
.dev_txfifo_flush = iwlagn_dev_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 = { static const struct iwl_ops iwl6000_ops = {

View file

@ -235,13 +235,13 @@ static int iwlagn_calc_rssi(struct iwl_priv *priv,
/* data from PHY/DSP regarding signal strength, etc., /* data from PHY/DSP regarding signal strength, etc.,
* contents are always there, not configurable by host * contents are always there, not configurable by host
*/ */
struct iwl5000_non_cfg_phy *ncphy = struct iwlagn_non_cfg_phy *ncphy =
(struct iwl5000_non_cfg_phy *)rx_resp->non_cfg_phy_buf; (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
u32 val, rssi_a, rssi_b, rssi_c, max_rssi; u32 val, rssi_a, rssi_b, rssi_c, max_rssi;
u8 agc; u8 agc;
val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_AGC_IDX]); val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]);
agc = (val & IWL50_OFDM_AGC_MSK) >> IWL50_OFDM_AGC_BIT_POS; agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS;
/* Find max rssi among 3 possible receivers. /* Find max rssi among 3 possible receivers.
* These values are measured by the digital signal processor (DSP). * 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. * if the radio's automatic gain control (AGC) is working right.
* AGC value (see below) will provide the "interesting" info. * AGC value (see below) will provide the "interesting" info.
*/ */
val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_RSSI_AB_IDX]); val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]);
rssi_a = (val & IWL50_OFDM_RSSI_A_MSK) >> IWL50_OFDM_RSSI_A_BIT_POS; rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >>
rssi_b = (val & IWL50_OFDM_RSSI_B_MSK) >> IWL50_OFDM_RSSI_B_BIT_POS; IWLAGN_OFDM_RSSI_A_BIT_POS;
val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_RSSI_C_IDX]); rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >>
rssi_c = (val & IWL50_OFDM_RSSI_C_MSK) >> IWL50_OFDM_RSSI_C_BIT_POS; 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, rssi_a, rssi_b);
max_rssi = max_t(u32, max_rssi, rssi_c); max_rssi = max_t(u32, max_rssi, rssi_c);

View file

@ -1098,7 +1098,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
if (chan->band != band) if (chan->band != band)
continue; continue;
channel = ieee80211_frequency_to_channel(chan->center_freq); channel = chan->hw_value;
scan_ch->channel = cpu_to_le16(channel); scan_ch->channel = cpu_to_le16(channel);
ch_info = iwl_get_channel_info(priv, band, channel); ch_info = iwl_get_channel_info(priv, band, channel);

View file

@ -82,6 +82,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
struct iwl_lq_sta *lq_sta); struct iwl_lq_sta *lq_sta);
static void rs_fill_link_cmd(struct iwl_priv *priv, static void rs_fill_link_cmd(struct iwl_priv *priv,
struct iwl_lq_sta *lq_sta, u32 rate_n_flags); 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 #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 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
u8 mcs; u8 mcs;
memset(tbl, 0, sizeof(struct iwl_scale_tbl_info));
*rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
if (*rate_idx == IWL_RATE_INVALID) { 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]); other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
} else { } else {
IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n"); 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 */ /* The last TX rate is cached in lq_sta; it's set in if/else above */
lq_sta->last_rate_n_flags = tx_rate; lq_sta->last_rate_n_flags = tx_rate;
done:
/* See if there's a better rate or modulation mode to try. */ /* See if there's a better rate or modulation mode to try. */
if (sta && sta->supp_rates[sband->band]) if (sta && sta->supp_rates[sband->band])
rs_rate_scale_perform(priv, skb, sta, lq_sta); 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]); struct iwl_rate_scale_data *window = &(tbl->win[index]);
u32 sz = (sizeof(struct iwl_scale_tbl_info) - u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); (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 valid_tx_ant = priv->hw_params.valid_tx_ant;
u8 tx_chains_num = priv->hw_params.tx_chains_num; u8 tx_chains_num = priv->hw_params.tx_chains_num;
int ret = 0; 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 && else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE &&
tbl->action > IWL_LEGACY_SWITCH_SISO) tbl->action > IWL_LEGACY_SWITCH_SISO)
tbl->action = IWL_LEGACY_SWITCH_SISO; tbl->action = IWL_LEGACY_SWITCH_SISO;
start_action = tbl->action;
for (; ;) { for (; ;) {
lq_sta->action_counter++; lq_sta->action_counter++;
switch (tbl->action) { 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; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
u32 sz = (sizeof(struct iwl_scale_tbl_info) - u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); (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 valid_tx_ant = priv->hw_params.valid_tx_ant;
u8 tx_chains_num = priv->hw_params.tx_chains_num; u8 tx_chains_num = priv->hw_params.tx_chains_num;
u8 update_search_tbl_counter = 0; u8 update_search_tbl_counter = 0;
@ -1414,6 +1430,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
/* stay in SISO */ /* stay in SISO */
tbl->action = IWL_SISO_SWITCH_ANTENNA1; tbl->action = IWL_SISO_SWITCH_ANTENNA1;
} }
start_action = tbl->action;
for (;;) { for (;;) {
lq_sta->action_counter++; lq_sta->action_counter++;
switch (tbl->action) { 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; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
u32 sz = (sizeof(struct iwl_scale_tbl_info) - u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); (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 valid_tx_ant = priv->hw_params.valid_tx_ant;
u8 tx_chains_num = priv->hw_params.tx_chains_num; u8 tx_chains_num = priv->hw_params.tx_chains_num;
u8 update_search_tbl_counter = 0; u8 update_search_tbl_counter = 0;
@ -1553,6 +1570,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv,
/* switch in SISO */ /* switch in SISO */
tbl->action = IWL_MIMO2_SWITCH_SISO_A; tbl->action = IWL_MIMO2_SWITCH_SISO_A;
} }
start_action = tbl->action;
for (;;) { for (;;) {
lq_sta->action_counter++; lq_sta->action_counter++;
switch (tbl->action) { 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; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
u32 sz = (sizeof(struct iwl_scale_tbl_info) - u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); (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 valid_tx_ant = priv->hw_params.valid_tx_ant;
u8 tx_chains_num = priv->hw_params.tx_chains_num; u8 tx_chains_num = priv->hw_params.tx_chains_num;
int ret; int ret;
@ -1694,6 +1712,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv,
/* switch in SISO */ /* switch in SISO */
tbl->action = IWL_MIMO3_SWITCH_SISO_A; tbl->action = IWL_MIMO3_SWITCH_SISO_A;
} }
start_action = tbl->action;
for (;;) { for (;;) {
lq_sta->action_counter++; lq_sta->action_counter++;
switch (tbl->action) { 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); rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
/* Interpret new_rate (rate_n_flags) */ /* Interpret new_rate (rate_n_flags) */
memset(&tbl_type, 0, sizeof(tbl_type));
rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, rs_get_tbl_info_from_mcs(new_rate, lq_sta->band,
&tbl_type, &rate_idx); &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_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_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
lq_cmd->agg_params.agg_time_limit = lq_cmd->agg_params.agg_time_limit =
cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); 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) static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)

View 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);

View 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__ */

View file

@ -470,8 +470,8 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
{ {
struct ieee80211_key_conf *keyconf = info->control.hw_key; struct ieee80211_key_conf *keyconf = info->control.hw_key;
switch (keyconf->alg) { switch (keyconf->cipher) {
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
tx_cmd->sec_ctl = TX_CMD_SEC_CCM; tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
if (info->flags & IEEE80211_TX_CTL_AMPDU) 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"); IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n");
break; break;
case ALG_TKIP: case WLAN_CIPHER_SUITE_TKIP:
tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
ieee80211_get_tkip_key(keyconf, skb_frag, ieee80211_get_tkip_key(keyconf, skb_frag,
IEEE80211_TKIP_P2_KEY, tx_cmd->key); IEEE80211_TKIP_P2_KEY, tx_cmd->key);
IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n"); IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n");
break; 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 | tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP |
(keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); (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); memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " 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; break;
default: default:
IWL_ERR(priv, "Unknown encode alg %d\n", keyconf->alg); IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher);
break; break;
} }
} }

View file

@ -33,6 +33,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pci-aspm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/delay.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, static void iwl_rx_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb) struct iwl_rx_mem_buffer *rxb)
{ {
#ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl4965_beacon_notif *beacon = struct iwl4965_beacon_notif *beacon =
(struct iwl4965_beacon_notif *)pkt->u.raw; (struct iwl4965_beacon_notif *)pkt->u.raw;
#ifdef CONFIG_IWLWIFI_DEBUG
u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
IWL_DEBUG_RX(priv, "beacon status %x retries %d iss %d " 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); le32_to_cpu(beacon->low_tsf), rate);
#endif #endif
priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
if ((priv->iw_mode == NL80211_IFTYPE_AP) && if ((priv->iw_mode == NL80211_IFTYPE_AP) &&
(!test_bit(STATUS_EXIT_PENDING, &priv->status))) (!test_bit(STATUS_EXIT_PENDING, &priv->status)))
queue_work(priv->workqueue, &priv->beacon_update); 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, static int iwl_mac_setup_register(struct iwl_priv *priv,
struct iwlagn_ucode_capabilities *capa); 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) static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first)
{ {
const char *name_pre = priv->cfg->fw_name_pre; 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; priv->fw_index = priv->cfg->ucode_api_max;
else sprintf(tag, "%d", priv->fw_index);
} else {
priv->fw_index--; priv->fw_index--;
sprintf(tag, "%d", priv->fw_index);
}
if (priv->fw_index < priv->cfg->ucode_api_min) { if (priv->fw_index < priv->cfg->ucode_api_min) {
IWL_ERR(priv, "no suitable firmware found!\n"); IWL_ERR(priv, "no suitable firmware found!\n");
return -ENOENT; return -ENOENT;
} }
sprintf(priv->firmware_name, "%s%d%s", sprintf(priv->firmware_name, "%s%s%s", name_pre, tag, ".ucode");
name_pre, priv->fw_index, ".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); priv->firmware_name);
return request_firmware_nowait(THIS_MODULE, 1, priv->firmware_name, return request_firmware_nowait(THIS_MODULE, 1, priv->firmware_name,
@ -1968,8 +1984,10 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
memset(&pieces, 0, sizeof(pieces)); memset(&pieces, 0, sizeof(pieces));
if (!ucode_raw) { if (!ucode_raw) {
IWL_ERR(priv, "request for firmware file '%s' failed.\n", if (priv->fw_index <= priv->cfg->ucode_api_max)
priv->firmware_name); IWL_ERR(priv,
"request for firmware file '%s' failed.\n",
priv->firmware_name);
goto try_again; goto try_again;
} }
@ -2016,7 +2034,9 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
api_max, api_ver); api_max, api_ver);
if (build) if (build)
sprintf(buildstr, " build %u", build); sprintf(buildstr, " build %u%s", build,
(priv->fw_index == UCODE_EXPERIMENTAL_INDEX)
? " (EXP)" : "");
else else
buildstr[0] = '\0'; buildstr[0] = '\0';
@ -2589,6 +2609,52 @@ int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
return pos; 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 * iwl_alive_start - called after REPLY_ALIVE notification received
* from protocol/runtime uCode (initialization uCode's * 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; priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
iwlcore_commit_rxon(priv); iwlcore_commit_rxon(priv);
iwl_setup_rxon_timing(priv, vif); ret = iwl_send_rxon_timing(priv, vif);
ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
sizeof(priv->rxon_timing), &priv->rxon_timing);
if (ret) if (ret)
IWL_WARN(priv, "REPLY_RXON_TIMING failed - " IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
"Attempting to continue.\n"); "Attempting to continue.\n");
@ -3298,9 +3362,7 @@ void iwl_config_ap(struct iwl_priv *priv, struct ieee80211_vif *vif)
iwlcore_commit_rxon(priv); iwlcore_commit_rxon(priv);
/* RXON Timing */ /* RXON Timing */
iwl_setup_rxon_timing(priv, vif); ret = iwl_send_rxon_timing(priv, vif);
ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
sizeof(priv->rxon_timing), &priv->rxon_timing);
if (ret) if (ret)
IWL_WARN(priv, "REPLY_RXON_TIMING failed - " IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
"Attempting to continue.\n"); "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 1X mode.
* In legacy wep mode, we use another host command to the uCode. * 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) if (cmd == SET_KEY)
is_default_wep_key = !priv->key_mapping_key; is_default_wep_key = !priv->key_mapping_key;
else else
@ -3581,6 +3645,7 @@ static void iwl_mac_channel_switch(struct ieee80211_hw *hw,
struct iwl_priv *priv = hw->priv; struct iwl_priv *priv = hw->priv;
const struct iwl_channel_info *ch_info; const struct iwl_channel_info *ch_info;
struct ieee80211_conf *conf = &hw->conf; struct ieee80211_conf *conf = &hw->conf;
struct ieee80211_channel *channel = ch_switch->channel;
struct iwl_ht_config *ht_conf = &priv->current_ht_config; struct iwl_ht_config *ht_conf = &priv->current_ht_config;
u16 ch; u16 ch;
unsigned long flags = 0; unsigned long flags = 0;
@ -3604,11 +3669,10 @@ static void iwl_mac_channel_switch(struct ieee80211_hw *hw,
mutex_lock(&priv->mutex); mutex_lock(&priv->mutex);
if (priv->cfg->ops->lib->set_channel_switch) { if (priv->cfg->ops->lib->set_channel_switch) {
ch = ieee80211_frequency_to_channel( ch = channel->hw_value;
ch_switch->channel->center_freq);
if (le16_to_cpu(priv->active_rxon.channel) != ch) { if (le16_to_cpu(priv->active_rxon.channel) != ch) {
ch_info = iwl_get_channel_info(priv, ch_info = iwl_get_channel_info(priv,
conf->channel->band, channel->band,
ch); ch);
if (!is_channel_valid(ch_info)) { if (!is_channel_valid(ch_info)) {
IWL_DEBUG_MAC80211(priv, "invalid channel\n"); IWL_DEBUG_MAC80211(priv, "invalid channel\n");
@ -3637,15 +3701,12 @@ static void iwl_mac_channel_switch(struct ieee80211_hw *hw,
} else } else
ht_conf->is_40mhz = false; ht_conf->is_40mhz = false;
/* if we are switching from ht to 2.4 clear flags if (le16_to_cpu(priv->staging_rxon.channel) != ch)
* 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; 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_rxon_ht(priv, ht_conf);
iwl_set_flags_for_band(priv, conf->channel->band, iwl_set_flags_for_band(priv, channel->band,
priv->vif); priv->vif);
spin_unlock_irqrestore(&priv->lock, flags); spin_unlock_irqrestore(&priv->lock, flags);
@ -3923,8 +3984,35 @@ static struct ieee80211_ops iwl_hw_ops = {
.sta_remove = iwl_mac_sta_remove, .sta_remove = iwl_mac_sta_remove,
.channel_switch = iwl_mac_channel_switch, .channel_switch = iwl_mac_channel_switch,
.flush = iwl_mac_flush, .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) static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{ {
int err = 0; 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 * 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)) { if (pci_enable_device(pdev)) {
err = -ENODEV; err = -ENODEV;
goto out_ieee80211_free_hw; goto out_ieee80211_free_hw;

View file

@ -1367,21 +1367,24 @@ struct iwl4965_rx_non_cfg_phy {
} __packed; } __packed;
#define IWL50_RX_RES_PHY_CNT 8 #define IWLAGN_RX_RES_PHY_CNT 8
#define IWL50_RX_RES_AGC_IDX 1 #define IWLAGN_RX_RES_AGC_IDX 1
#define IWL50_RX_RES_RSSI_AB_IDX 2 #define IWLAGN_RX_RES_RSSI_AB_IDX 2
#define IWL50_RX_RES_RSSI_C_IDX 3 #define IWLAGN_RX_RES_RSSI_C_IDX 3
#define IWL50_OFDM_AGC_MSK 0xfe00 #define IWLAGN_OFDM_AGC_MSK 0xfe00
#define IWL50_OFDM_AGC_BIT_POS 9 #define IWLAGN_OFDM_AGC_BIT_POS 9
#define IWL50_OFDM_RSSI_A_MSK 0x00ff #define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff
#define IWL50_OFDM_RSSI_A_BIT_POS 0 #define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00
#define IWL50_OFDM_RSSI_B_MSK 0xff0000 #define IWLAGN_OFDM_RSSI_A_BIT_POS 0
#define IWL50_OFDM_RSSI_B_BIT_POS 16 #define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000
#define IWL50_OFDM_RSSI_C_MSK 0x00ff #define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000
#define IWL50_OFDM_RSSI_C_BIT_POS 0 #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 { struct iwlagn_non_cfg_phy {
__le32 non_cfg_phy[IWL50_RX_RES_PHY_CNT]; /* up to 8 phy entries */ __le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */
} __packed; } __packed;
@ -1401,7 +1404,7 @@ struct iwl_rx_phy_res {
u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */
__le32 rate_n_flags; /* RATE_MCS_* */ __le32 rate_n_flags; /* RATE_MCS_* */
__le16 byte_count; /* frame's byte-count */ __le16 byte_count; /* frame's byte-count */
__le16 reserved3; __le16 frame_time; /* frame's time on the air */
} __packed; } __packed;
struct iwl_rx_mpdu_res_start { struct iwl_rx_mpdu_res_start {
@ -2092,8 +2095,8 @@ struct iwl_link_qual_general_params {
} __packed; } __packed;
#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */
#define LINK_QUAL_AGG_TIME_LIMIT_MAX (65535) #define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000)
#define LINK_QUAL_AGG_TIME_LIMIT_MIN (0) #define LINK_QUAL_AGG_TIME_LIMIT_MIN (100)
#define LINK_QUAL_AGG_DISABLE_START_DEF (3) #define LINK_QUAL_AGG_DISABLE_START_DEF (3)
#define LINK_QUAL_AGG_DISABLE_START_MAX (255) #define LINK_QUAL_AGG_DISABLE_START_MAX (255)
@ -2110,8 +2113,10 @@ struct iwl_link_qual_general_params {
*/ */
struct iwl_link_qual_agg_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; __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) * BEACON_NOTIFICATION = 0x90 (notification only, not a command)
*/ */

View file

@ -183,14 +183,6 @@ out:
} }
EXPORT_SYMBOL(iwl_alloc_all); 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 * 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->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; 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; 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; ht_info->mcs.rx_mask[0] = 0xFF;
if (rx_chains_num >= 2) 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; 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; u64 tsf;
s32 interval_tm, rem; s32 interval_tm, rem;
unsigned long flags;
struct ieee80211_conf *conf = NULL; struct ieee80211_conf *conf = NULL;
u16 beacon_int; u16 beacon_int;
conf = ieee80211_get_hw_conf(priv->hw); 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.timestamp = cpu_to_le64(priv->timestamp);
priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); 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); rem = do_div(tsf, interval_tm);
priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem); priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
spin_unlock_irqrestore(&priv->lock, flags);
IWL_DEBUG_ASSOC(priv, IWL_DEBUG_ASSOC(priv,
"beacon interval %d beacon timer %d beacon tim %d\n", "beacon interval %d beacon timer %d beacon tim %d\n",
le16_to_cpu(priv->rxon_timing.beacon_interval), le16_to_cpu(priv->rxon_timing.beacon_interval),
le32_to_cpu(priv->rxon_timing.beacon_init_val), le32_to_cpu(priv->rxon_timing.beacon_init_val),
le16_to_cpu(priv->rxon_timing.atim_window)); 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) 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); EXPORT_SYMBOL(iwl_get_single_channel_number);
/** /**
* iwl_set_rxon_channel - Set the phymode and channel values in staging RXON * iwl_set_rxon_channel - Set the band and channel values in staging RXON
* @phymode: MODE_IEEE80211A sets to 5.2GHz; all else set to 2.4GHz * @ch: requested channel as a pointer to struct ieee80211_channel
* @channel: Any channel valid for the requested phymode
* 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 * 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) int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch)
{ {
enum ieee80211_band band = ch->band; enum ieee80211_band band = ch->band;
u16 channel = ieee80211_frequency_to_channel(ch->center_freq); u16 channel = ch->hw_value;
if (!iwl_get_channel_info(priv, band, channel)) {
IWL_DEBUG_INFO(priv, "Could not set channel to %d [%d]\n",
channel, band);
return -EINVAL;
}
if ((le16_to_cpu(priv->staging_rxon.channel) == channel) && if ((le16_to_cpu(priv->staging_rxon.channel) == channel) &&
(priv->band == band)) (priv->band == band))
@ -1328,25 +1321,6 @@ out:
EXPORT_SYMBOL(iwl_apm_init); 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 iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
{ {
int ret = 0; 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); 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, void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb) 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); 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, static void iwl_ht_conf(struct iwl_priv *priv,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
@ -2014,6 +1926,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
struct iwl_priv *priv = hw->priv; struct iwl_priv *priv = hw->priv;
const struct iwl_channel_info *ch_info; const struct iwl_channel_info *ch_info;
struct ieee80211_conf *conf = &hw->conf; struct ieee80211_conf *conf = &hw->conf;
struct ieee80211_channel *channel = conf->channel;
struct iwl_ht_config *ht_conf = &priv->current_ht_config; struct iwl_ht_config *ht_conf = &priv->current_ht_config;
unsigned long flags = 0; unsigned long flags = 0;
int ret = 0; int ret = 0;
@ -2023,7 +1936,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
mutex_lock(&priv->mutex); mutex_lock(&priv->mutex);
IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n", 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 && if (unlikely(!priv->cfg->mod_params->disable_hw_scan &&
test_bit(STATUS_SCANNING, &priv->status))) { test_bit(STATUS_SCANNING, &priv->status))) {
@ -2054,8 +1967,8 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
if (scan_active) if (scan_active)
goto set_ch_out; goto set_ch_out;
ch = ieee80211_frequency_to_channel(conf->channel->center_freq); ch = channel->hw_value;
ch_info = iwl_get_channel_info(priv, conf->channel->band, ch); ch_info = iwl_get_channel_info(priv, channel->band, ch);
if (!is_channel_valid(ch_info)) { if (!is_channel_valid(ch_info)) {
IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n"); IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
ret = -EINVAL; ret = -EINVAL;
@ -2086,16 +1999,13 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
* from BSS config in iwl_ht_conf */ * from BSS config in iwl_ht_conf */
ht_conf->ht_protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; 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)) if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
priv->staging_rxon.flags = 0; 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_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); spin_unlock_irqrestore(&priv->lock, flags);
if (priv->cfg->ops->lib->update_bcast_station) if (priv->cfg->ops->lib->update_bcast_station)

View file

@ -136,6 +136,12 @@ struct iwl_temp_ops {
void (*set_calib_version)(struct iwl_priv *priv); 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 { struct iwl_lib_ops {
/* set hw dependent parameters */ /* set hw dependent parameters */
int (*set_hw_params)(struct iwl_priv *priv); 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); void (*dev_txfifo_flush)(struct iwl_priv *priv, u16 flush_control);
struct iwl_debugfs_ops debugfs_ops; struct iwl_debugfs_ops debugfs_ops;
/* thermal throttling */
struct iwl_tt_ops tt_ops;
}; };
struct iwl_led_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_calib_by_driver: driver has the capability to perform
* chain noise calibration operation * chain noise calibration operation
* @scan_antennas: available antenna for scan 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 * We enable the driver to be backward compatible wrt API version. The
* driver specifies which APIs it supports (with @ucode_api_max being 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]; u8 scan_tx_antennas[IEEE80211_NUM_BANDS];
const bool need_dc_calib; const bool need_dc_calib;
const bool bt_statistics; 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_hw *iwl_alloc_all(struct iwl_cfg *cfg,
struct ieee80211_ops *hw_ops); struct ieee80211_ops *hw_ops);
void iwl_hw_detect(struct iwl_priv *priv);
void iwl_activate_qos(struct iwl_priv *priv); void iwl_activate_qos(struct iwl_priv *priv);
int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
const struct ieee80211_tx_queue_params *params); 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); void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt);
int iwl_check_rxon_cmd(struct iwl_priv *priv); int iwl_check_rxon_cmd(struct iwl_priv *priv);
int iwl_full_rxon_required(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, u32 decrypt_res,
struct ieee80211_rx_status *stats); struct ieee80211_rx_status *stats);
void iwl_irq_handle_error(struct iwl_priv *priv); 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_post_associate(struct iwl_priv *priv, struct ieee80211_vif *vif);
void iwl_bss_info_changed(struct ieee80211_hw *hw, void iwl_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, 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, int iwl_mac_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req); 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); void iwl_internal_short_hw_scan(struct iwl_priv *priv);
int iwl_force_reset(struct iwl_priv *priv, int mode, bool external); 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, 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, u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
enum ieee80211_band band, enum ieee80211_band band,
struct ieee80211_vif *vif); 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); void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
/* For faster active scanning, scan will move to the next channel if fewer than /* 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_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 * * PCI *
@ -695,7 +705,6 @@ static inline int iwl_is_ready_rf(struct iwl_priv *priv)
return iwl_is_ready(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 void iwl_send_bt_config(struct iwl_priv *priv);
extern int iwl_send_statistics_request(struct iwl_priv *priv, extern int iwl_send_statistics_request(struct iwl_priv *priv,
u8 flags, bool clear); 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); void iwl_apm_stop(struct iwl_priv *priv);
int iwl_apm_init(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) static inline int iwl_send_rxon_assoc(struct iwl_priv *priv)
{ {
return priv->cfg->ops->hcmd->rxon_assoc(priv); return priv->cfg->ops->hcmd->rxon_assoc(priv);

View file

@ -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++) for (i = 0; i < supp_band->n_channels; i++)
pos += scnprintf(buf + pos, bufsz - pos, pos += scnprintf(buf + pos, bufsz - pos,
"%d: %ddBm: BSS%s%s, %s.\n", "%d: %ddBm: BSS%s%s, %s.\n",
ieee80211_frequency_to_channel( channels[i].hw_value,
channels[i].center_freq),
channels[i].max_power, channels[i].max_power,
channels[i].flags & IEEE80211_CHAN_RADAR ? channels[i].flags & IEEE80211_CHAN_RADAR ?
" (IEEE 802.11h required)" : "", " (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++) for (i = 0; i < supp_band->n_channels; i++)
pos += scnprintf(buf + pos, bufsz - pos, pos += scnprintf(buf + pos, bufsz - pos,
"%d: %ddBm: BSS%s%s, %s.\n", "%d: %ddBm: BSS%s%s, %s.\n",
ieee80211_frequency_to_channel( channels[i].hw_value,
channels[i].center_freq),
channels[i].max_power, channels[i].max_power,
channels[i].flags & IEEE80211_CHAN_RADAR ? channels[i].flags & IEEE80211_CHAN_RADAR ?
" (IEEE 802.11h required)" : "", " (IEEE 802.11h required)" : "",

View file

@ -47,6 +47,7 @@
#include "iwl-led.h" #include "iwl-led.h"
#include "iwl-power.h" #include "iwl-power.h"
#include "iwl-agn-rs.h" #include "iwl-agn-rs.h"
#include "iwl-agn-tt.h"
struct iwl_tx_queue; struct iwl_tx_queue;
@ -420,7 +421,7 @@ struct iwl_tid_data {
}; };
struct iwl_hw_key { struct iwl_hw_key {
enum ieee80211_key_alg alg; u32 cipher;
int keylen; int keylen;
u8 keyidx; u8 keyidx;
u8 key[32]; 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 * Maximal MPDU density for TX aggregation
@ -443,8 +450,13 @@ union iwl_ht_rate_supp {
* 6 - 8us density * 6 - 8us density
* 7 - 16us density * 7 - 16us density
*/ */
#define CFG_HT_MPDU_DENSITY_2USEC (0x4)
#define CFG_HT_MPDU_DENSITY_4USEC (0x5) #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_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 { struct iwl_ht_config {
/* self configuration data */ /* self configuration data */
@ -1052,7 +1064,6 @@ struct iwl_event_log {
#define IWL_DEF_MONITORING_PERIOD (1000) #define IWL_DEF_MONITORING_PERIOD (1000)
#define IWL_LONG_MONITORING_PERIOD (5000) #define IWL_LONG_MONITORING_PERIOD (5000)
#define IWL_ONE_HUNDRED_MSECS (100) #define IWL_ONE_HUNDRED_MSECS (100)
#define IWL_SIXTY_SECS (60000)
enum iwl_reset { enum iwl_reset {
IWL_RF_RESET = 0, IWL_RF_RESET = 0,
@ -1110,6 +1121,9 @@ struct iwl_priv {
u32 ucode_beacon_time; u32 ucode_beacon_time;
int missed_beacon_threshold; int missed_beacon_threshold;
/* track IBSS manager (last beacon) status */
u32 ibss_manager;
/* storing the jiffies when the plcp error rate is received */ /* storing the jiffies when the plcp error rate is received */
unsigned long plcp_jiffies; unsigned long plcp_jiffies;

View file

@ -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); 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, static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv,
struct iwl_powertable_cmd *cmd) 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 iwl_power_update_mode(struct iwl_priv *priv, bool force)
{ {
int ret = 0; int ret = 0;
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
bool update_chains; bool update_chains;
struct iwl_powertable_cmd cmd; 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 && else if (priv->cfg->supports_idle &&
priv->hw->conf.flags & IEEE80211_CONF_IDLE) priv->hw->conf.flags & IEEE80211_CONF_IDLE)
iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_5, 20); iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_5, 20);
else if (tt->state >= IWL_TI_1) else if (priv->cfg->ops->lib->tt_ops.lower_power_detection &&
iwl_static_sleep_cmd(priv, &cmd, tt->tt_power_mode, dtimper); priv->cfg->ops->lib->tt_ops.tt_power_mode &&
else if (!enabled) 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); iwl_power_sleep_cam_cmd(priv, &cmd);
else if (priv->power_data.debug_sleep_level_override >= 0) else if (priv->power_data.debug_sleep_level_override >= 0)
iwl_static_sleep_cmd(priv, &cmd, 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); 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 */ /* initialize to default */
void iwl_power_initialize(struct iwl_priv *priv) void iwl_power_initialize(struct iwl_priv *priv)
{ {

View file

@ -30,90 +30,6 @@
#include "iwl-commands.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;
};
enum iwl_power_level { enum iwl_power_level {
IWL_POWER_INDEX_1, IWL_POWER_INDEX_1,
IWL_POWER_INDEX_2, IWL_POWER_INDEX_2,
@ -130,15 +46,6 @@ struct iwl_power_mgr {
}; };
int iwl_power_update_mode(struct iwl_priv *priv, bool force); 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); void iwl_power_initialize(struct iwl_priv *priv);
extern bool no_sleep_autoadjust; extern bool no_sleep_autoadjust;

View file

@ -378,7 +378,7 @@ void iwl_internal_short_hw_scan(struct iwl_priv *priv)
queue_work(priv->workqueue, &priv->start_internal_scan); 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 = struct iwl_priv *priv =
container_of(work, struct iwl_priv, start_internal_scan); container_of(work, struct iwl_priv, start_internal_scan);
@ -418,9 +418,8 @@ void iwl_bg_start_internal_scan(struct work_struct *work)
unlock: unlock:
mutex_unlock(&priv->mutex); 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 = struct iwl_priv *priv =
container_of(data, struct iwl_priv, scan_check.work); 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); mutex_unlock(&priv->mutex);
} }
EXPORT_SYMBOL(iwl_bg_scan_check);
/** /**
* iwl_fill_probe_req - fill in all required fields and IE for probe request * 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); 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); 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); iwl_send_scan_abort(priv);
mutex_unlock(&priv->mutex); 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 = struct iwl_priv *priv =
container_of(work, struct iwl_priv, scan_completed); container_of(work, struct iwl_priv, scan_completed);
bool internal = false; bool internal = false;
bool scan_completed = false;
IWL_DEBUG_SCAN(priv, "SCAN complete scan\n"); 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; priv->is_internal_short_scan = false;
IWL_DEBUG_SCAN(priv, "internal short scan completed\n"); IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
internal = true; internal = true;
} else { } else if (priv->scan_request) {
scan_completed = true;
priv->scan_request = NULL; priv->scan_request = NULL;
priv->scan_vif = 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 * into driver again into functions that will attempt to take
* mutex. * mutex.
*/ */
if (!internal) if (scan_completed)
ieee80211_scan_completed(priv->hw, false); ieee80211_scan_completed(priv->hw, false);
} }
EXPORT_SYMBOL(iwl_bg_scan_completed);
void iwl_setup_scan_deferred_work(struct iwl_priv *priv) void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
{ {

View file

@ -818,7 +818,7 @@ int iwl_set_default_wep_key(struct iwl_priv *priv,
keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
keyconf->hw_key_idx = HW_KEY_DEFAULT; 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; priv->wep_keys[keyconf->keyidx].key_size = keyconf->keylen;
memcpy(&priv->wep_keys[keyconf->keyidx].key, &keyconf->key, 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); 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.keylen = keyconf->keylen;
priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; 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; keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
spin_lock_irqsave(&priv->sta_lock, flags); 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.keylen = keyconf->keylen;
memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 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); 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; priv->stations[sta_id].keyinfo.keylen = 16;
if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) 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++; priv->key_mapping_key++;
keyconf->hw_key_idx = HW_KEY_DYNAMIC; keyconf->hw_key_idx = HW_KEY_DYNAMIC;
switch (keyconf->alg) { switch (keyconf->cipher) {
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
ret = iwl_set_ccmp_dynamic_key_info(priv, keyconf, sta_id); ret = iwl_set_ccmp_dynamic_key_info(priv, keyconf, sta_id);
break; break;
case ALG_TKIP: case WLAN_CIPHER_SUITE_TKIP:
ret = iwl_set_tkip_dynamic_key_info(priv, keyconf, sta_id); ret = iwl_set_tkip_dynamic_key_info(priv, keyconf, sta_id);
break; 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); ret = iwl_set_wep_dynamic_key_info(priv, keyconf, sta_id);
break; break;
default: default:
IWL_ERR(priv, IWL_ERR(priv,
"Unknown alg: %s alg = %d\n", __func__, keyconf->alg); "Unknown alg: %s cipher = %x\n", __func__,
keyconf->cipher);
ret = -EINVAL; ret = -EINVAL;
} }
IWL_DEBUG_WEP(priv, "Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d\n", IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n",
keyconf->alg, keyconf->keylen, keyconf->keyidx, keyconf->cipher, keyconf->keylen, keyconf->keyidx,
sta_id, ret); sta_id, ret);
return ret; return ret;

View file

@ -422,6 +422,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
int len; int len;
u32 idx; u32 idx;
u16 fix_size; u16 fix_size;
bool is_ct_kill = false;
cmd->len = priv->cfg->ops->utils->get_hcmd_size(cmd->id, cmd->len); cmd->len = priv->cfg->ops->utils->get_hcmd_size(cmd->id, cmd->len);
fix_size = (u16)(cmd->len + sizeof(out_cmd->hdr)); 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)) { if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
IWL_ERR(priv, "No space in command queue\n"); IWL_ERR(priv, "No space in command queue\n");
if (iwl_within_ct_kill_margin(priv)) if (priv->cfg->ops->lib->tt_ops.ct_kill_check) {
iwl_tt_enter_ct_kill(priv); is_ct_kill =
else { priv->cfg->ops->lib->tt_ops.ct_kill_check(priv);
}
if (!is_ct_kill) {
IWL_ERR(priv, "Restarting adapter due to queue full\n"); IWL_ERR(priv, "Restarting adapter due to queue full\n");
queue_work(priv->workqueue, &priv->restart); queue_work(priv->workqueue, &priv->restart);
} }

View file

@ -33,6 +33,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pci-aspm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/delay.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; key_flags &= ~STA_KEY_FLG_INVALID;
spin_lock_irqsave(&priv->sta_lock, flags); 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.keylen = keyconf->keylen;
memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key,
keyconf->keylen); keyconf->keylen);
@ -222,23 +223,25 @@ static int iwl3945_set_dynamic_key(struct iwl_priv *priv,
keyconf->hw_key_idx = HW_KEY_DYNAMIC; keyconf->hw_key_idx = HW_KEY_DYNAMIC;
switch (keyconf->alg) { switch (keyconf->cipher) {
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
ret = iwl3945_set_ccmp_dynamic_key_info(priv, keyconf, sta_id); ret = iwl3945_set_ccmp_dynamic_key_info(priv, keyconf, sta_id);
break; break;
case ALG_TKIP: case WLAN_CIPHER_SUITE_TKIP:
ret = iwl3945_set_tkip_dynamic_key_info(priv, keyconf, sta_id); ret = iwl3945_set_tkip_dynamic_key_info(priv, keyconf, sta_id);
break; 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); ret = iwl3945_set_wep_dynamic_key_info(priv, keyconf, sta_id);
break; break;
default: 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; ret = -EINVAL;
} }
IWL_DEBUG_WEP(priv, "Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d\n", IWL_DEBUG_WEP(priv, "Set dynamic key: alg=%x len=%d idx=%d sta=%d ret=%d\n",
keyconf->alg, keyconf->keylen, keyconf->keyidx, keyconf->cipher, keyconf->keylen, keyconf->keyidx,
sta_id, ret); sta_id, ret);
return 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, static int iwl3945_set_static_key(struct iwl_priv *priv,
struct ieee80211_key_conf *key) 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; 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; return -EINVAL;
} }
@ -369,23 +373,25 @@ 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 iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
struct iwl_hw_key *keyinfo = &priv->stations[sta_id].keyinfo; struct iwl_hw_key *keyinfo = &priv->stations[sta_id].keyinfo;
switch (keyinfo->alg) { tx_cmd->sec_ctl = 0;
case ALG_CCMP:
switch (keyinfo->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
tx_cmd->sec_ctl = TX_CMD_SEC_CCM; tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen); memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen);
IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n");
break; break;
case ALG_TKIP: case WLAN_CIPHER_SUITE_TKIP:
break; break;
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP104:
tx_cmd->sec_ctl = TX_CMD_SEC_WEP | 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; (info->control.hw_key->hw_key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT;
if (keyinfo->keylen == 13)
tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen); memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen);
IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption "
@ -393,7 +399,7 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv,
break; break;
default: default:
IWL_ERR(priv, "Unknown encode alg %d\n", keyinfo->alg); IWL_ERR(priv, "Unknown encode cipher %x\n", keyinfo->cipher);
break; 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, static void iwl3945_rx_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb) struct iwl_rx_mem_buffer *rxb)
{ {
#ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl3945_beacon_notif *beacon = &(pkt->u.beacon_status); struct iwl3945_beacon_notif *beacon = &(pkt->u.beacon_status);
#ifdef CONFIG_IWLWIFI_DEBUG
u8 rate = beacon->beacon_notify_hdr.rate; u8 rate = beacon->beacon_notify_hdr.rate;
IWL_DEBUG_RX(priv, "beacon status %x retries %d iss %d " 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); le32_to_cpu(beacon->low_tsf), rate);
#endif #endif
priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
if ((priv->iw_mode == NL80211_IFTYPE_AP) && if ((priv->iw_mode == NL80211_IFTYPE_AP) &&
(!test_bit(STATUS_EXIT_PENDING, &priv->status))) (!test_bit(STATUS_EXIT_PENDING, &priv->status)))
queue_work(priv->workqueue, &priv->beacon_update); 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; priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
iwlcore_commit_rxon(priv); iwlcore_commit_rxon(priv);
memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd)); rc = iwl_send_rxon_timing(priv, vif);
iwl_setup_rxon_timing(priv, vif);
rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
sizeof(priv->rxon_timing), &priv->rxon_timing);
if (rc) if (rc)
IWL_WARN(priv, "REPLY_RXON_TIMING failed - " IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
"Attempting to continue.\n"); "Attempting to continue.\n");
@ -3263,11 +3268,7 @@ void iwl3945_config_ap(struct iwl_priv *priv, struct ieee80211_vif *vif)
iwlcore_commit_rxon(priv); iwlcore_commit_rxon(priv);
/* RXON Timing */ /* RXON Timing */
memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd)); rc = iwl_send_rxon_timing(priv, vif);
iwl_setup_rxon_timing(priv, vif);
rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
sizeof(priv->rxon_timing),
&priv->rxon_timing);
if (rc) if (rc)
IWL_WARN(priv, "REPLY_RXON_TIMING failed - " IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
"Attempting to continue.\n"); "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->init_alive_start, iwl3945_bg_init_alive_start);
INIT_DELAYED_WORK(&priv->alive_start, iwl3945_bg_alive_start); INIT_DELAYED_WORK(&priv->alive_start, iwl3945_bg_alive_start);
INIT_DELAYED_WORK(&priv->_3945.rfkill_poll, iwl3945_rfkill_poll); 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); iwl_setup_scan_deferred_work(priv);
INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan);
INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
iwl3945_hw_setup_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, .hw_scan = iwl_mac_hw_scan,
.sta_add = iwl3945_mac_sta_add, .sta_add = iwl3945_mac_sta_add,
.sta_remove = iwl_mac_sta_remove, .sta_remove = iwl_mac_sta_remove,
.tx_last_beacon = iwl_mac_tx_last_beacon,
}; };
static int iwl3945_init_drv(struct iwl_priv *priv) 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 * 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)) { if (pci_enable_device(pdev)) {
err = -ENODEV; err = -ENODEV;
goto out_ieee80211_free_hw; goto out_ieee80211_free_hw;

View file

@ -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: " IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: "
"oid is 0x%x\n", hdr->oid); "oid is 0x%x\n", hdr->oid);
if (hdr->oid <= WIFI_IF_NTFY_MAX) { set_bit(hdr->oid, &iwm->wifi_ntfy[0]);
set_bit(hdr->oid, &iwm->wifi_ntfy[0]); wake_up_interruptible(&iwm->wifi_ntfy_queue);
wake_up_interruptible(&iwm->wifi_ntfy_queue);
} else
return -EINVAL;
switch (hdr->oid) { switch (hdr->oid) {
case UMAC_WIFI_IF_CMD_SET_PROFILE: case UMAC_WIFI_IF_CMD_SET_PROFILE:

View file

@ -10,6 +10,7 @@
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/wait.h>
#include <linux/ieee80211.h> #include <linux/ieee80211.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include <asm/unaligned.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; pos = scanresp->bssdesc_and_tlvbuffer;
lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer,
scanresp->bssdescriptsize);
tsfdesc = pos + bsssize; tsfdesc = pos + bsssize;
tsfsize = 4 + 8 * scanresp->nr_sets; 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 */ /* Validity check: we expect a Marvell-Local TLV */
i = get_unaligned_le16(tsfdesc); i = get_unaligned_le16(tsfdesc);
tsfdesc += 2; 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; goto done;
}
/* Validity check: the TLV holds TSF values with 8 bytes each, so /* Validity check: the TLV holds TSF values with 8 bytes each, so
* the size in the TLV must match the nr_sets value */ * the size in the TLV must match the nr_sets value */
i = get_unaligned_le16(tsfdesc); i = get_unaligned_le16(tsfdesc);
tsfdesc += 2; 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; goto done;
}
for (i = 0; i < scanresp->nr_sets; i++) { for (i = 0; i < scanresp->nr_sets; i++) {
const u8 *bssid; const u8 *bssid;
@ -581,8 +593,11 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
id = *pos++; id = *pos++;
elen = *pos++; elen = *pos++;
left -= 2; left -= 2;
if (elen > left || elen == 0) if (elen > left || elen == 0) {
lbs_deb_scan("scan response: invalid IE fmt\n");
goto done; goto done;
}
if (id == WLAN_EID_DS_PARAMS) if (id == WLAN_EID_DS_PARAMS)
chan_no = *pos; chan_no = *pos;
if (id == WLAN_EID_SSID) { 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, capa, intvl, ie, ielen,
LBS_SCAN_RSSI_TO_MBM(rssi), LBS_SCAN_RSSI_TO_MBM(rssi),
GFP_KERNEL); GFP_KERNEL);
} } else
lbs_deb_scan("scan response: missing BSS channel IE\n");
tsfdesc += 8; tsfdesc += 8;
} }
ret = 0; 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); lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp);
/* add auth type TLV */ /* 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); pos += lbs_add_auth_type_tlv(pos, sme->auth_type);
/* add WPA/WPA2 TLV */ /* add WPA/WPA2 TLV */
@ -1114,6 +1131,9 @@ static int lbs_associate(struct lbs_private *priv,
(u16)(pos - (u8 *) &cmd->iebuf); (u16)(pos - (u8 *) &cmd->iebuf);
cmd->hdr.size = cpu_to_le16(len); 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 */ /* store for later use */
memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN); memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN);
@ -1121,14 +1141,28 @@ static int lbs_associate(struct lbs_private *priv,
if (ret) if (ret)
goto done; goto done;
/* generate connect message to cfg80211 */ /* generate connect message to cfg80211 */
resp = (void *) cmd; /* recast for easier field access */ resp = (void *) cmd; /* recast for easier field access */
status = le16_to_cpu(resp->statuscode); status = le16_to_cpu(resp->statuscode);
/* Convert statis code of old firmware */ /* Older FW versions map the IEEE 802.11 Status Code in the association
if (priv->fwrelease < 0x09000000) * 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) { switch (status) {
case 0: case 0:
break; break;
@ -1150,11 +1184,16 @@ static int lbs_associate(struct lbs_private *priv,
break; break;
default: default:
lbs_deb_assoc("association failure %d\n", status); 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, lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, "
le16_to_cpu(resp->capability)); "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) resp_ie_len = le16_to_cpu(resp->hdr.size)
- sizeof(resp->hdr) - sizeof(resp->hdr)
@ -1174,7 +1213,6 @@ static int lbs_associate(struct lbs_private *priv,
netif_tx_wake_all_queues(priv->dev); netif_tx_wake_all_queues(priv->dev);
} }
done: done:
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
return ret; return ret;

View file

@ -8,7 +8,14 @@
#define _LBS_DECL_H_ #define _LBS_DECL_H_
#include <linux/netdevice.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 lbs_private;
struct sk_buff; 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); u32 lbs_fw_index_to_data_rate(u8 index);
u8 lbs_data_rate_to_fw_index(u32 rate); 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 #endif

View file

@ -48,7 +48,6 @@
MODULE_AUTHOR("Holger Schurig <hs4233@mail.mn-solutions.de>"); MODULE_AUTHOR("Holger Schurig <hs4233@mail.mn-solutions.de>");
MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_FIRMWARE("libertas_cs_helper.fw");
@ -61,9 +60,34 @@ struct if_cs_card {
struct lbs_private *priv; struct lbs_private *priv;
void __iomem *iobase; void __iomem *iobase;
bool align_regs; 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 */ /* 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_MANFID 0x02df
#define CF8385_CARDID 0x8103 #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 && /* NOTE: keep in sync with if_cs_ids */
p_dev->card_id == CF8305_CARDID); if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID)
} return MODEL_8305;
else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID)
static inline int if_cs_hw_is_cf8381(struct pcmcia_device *p_dev) return MODEL_8381;
{ else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID)
return (p_dev->manf_id == CF8381_MANFID && return MODEL_8385;
p_dev->card_id == CF8381_CARDID); return MODEL_UNKNOWN;
}
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);
} }
/********************************************************************/ /********************************************************************/
@ -558,12 +579,11 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)
* *
* Return 0 on success * 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 ret = 0;
int sent = 0; int sent = 0;
u8 scratch; u8 scratch;
const struct firmware *fw;
lbs_deb_enter(LBS_DEB_CS); lbs_deb_enter(LBS_DEB_CS);
@ -589,14 +609,6 @@ static int if_cs_prog_helper(struct if_cs_card *card)
goto done; 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); lbs_deb_cs("helper size %td\n", fw->size);
/* "Set the 5 bytes of the helper image to 0" */ /* "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) { if (ret < 0) {
lbs_pr_err("can't download helper at 0x%x, ret %d\n", lbs_pr_err("can't download helper at 0x%x, ret %d\n",
sent, ret); sent, ret);
goto err_release; goto done;
} }
if (count == 0) if (count == 0)
@ -644,17 +656,14 @@ static int if_cs_prog_helper(struct if_cs_card *card)
sent += count; sent += count;
} }
err_release:
release_firmware(fw);
done: done:
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
return 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 ret = 0;
int retry = 0; int retry = 0;
int len = 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); 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); lbs_deb_cs("fw size %td\n", fw->size);
ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW, ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW,
IF_CS_SQ_HELPER_OK); IF_CS_SQ_HELPER_OK);
if (ret < 0) { if (ret < 0) {
lbs_pr_err("helper firmware doesn't answer\n"); lbs_pr_err("helper firmware doesn't answer\n");
goto err_release; goto done;
} }
for (sent = 0; sent < fw->size; sent += len) { 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) { if (retry > 20) {
lbs_pr_err("could not download firmware\n"); lbs_pr_err("could not download firmware\n");
ret = -ENODEV; ret = -ENODEV;
goto err_release; goto done;
} }
if (retry) { if (retry) {
sent -= len; sent -= len;
@ -710,7 +711,7 @@ static int if_cs_prog_real(struct if_cs_card *card)
IF_CS_BIT_COMMAND); IF_CS_BIT_COMMAND);
if (ret < 0) { if (ret < 0) {
lbs_pr_err("can't download firmware at 0x%x\n", sent); 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) if (ret < 0)
lbs_pr_err("firmware download failed\n"); lbs_pr_err("firmware download failed\n");
err_release:
release_firmware(fw);
done: done:
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
return ret; return ret;
@ -824,6 +822,8 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
unsigned int prod_id; unsigned int prod_id;
struct lbs_private *priv; struct lbs_private *priv;
struct if_cs_card *card; struct if_cs_card *card;
const struct firmware *helper = NULL;
const struct firmware *mainfw = NULL;
lbs_deb_enter(LBS_DEB_CS); lbs_deb_enter(LBS_DEB_CS);
@ -843,7 +843,6 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
goto out1; goto out1;
} }
/* /*
* Allocate an interrupt line. Note that this does not assign * Allocate an interrupt line. Note that this does not assign
* a handler to the interrupt, unless the 'Handler' member of * 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->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 */ /* Check if we have a current silicon */
prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); 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; card->align_regs = 1;
if (prod_id < IF_CS_CF8305_B1_REV) { if (prod_id < IF_CS_CF8305_B1_REV) {
lbs_pr_err("old chips like 8305 rev B3 " lbs_pr_err("8305 rev B0 and older are not supported\n");
"aren't supported\n");
ret = -ENODEV; ret = -ENODEV;
goto out2; goto out2;
} }
} }
if (if_cs_hw_is_cf8381(p_dev) && prod_id < IF_CS_CF8381_B3_REV) { if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) {
lbs_pr_err("old chips like 8381 rev B3 aren't supported\n"); lbs_pr_err("8381 rev B2 and older are not supported\n");
ret = -ENODEV; ret = -ENODEV;
goto out2; goto out2;
} }
if (if_cs_hw_is_cf8385(p_dev) && prod_id < IF_CS_CF8385_B1_REV) { if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) {
lbs_pr_err("old chips like 8385 rev B1 aren't supported\n"); lbs_pr_err("8385 rev B0 and older are not supported\n");
ret = -ENODEV; ret = -ENODEV;
goto out2; 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 */ /* Load the firmware early, before calling into libertas.ko */
ret = if_cs_prog_helper(card); ret = if_cs_prog_helper(card, helper);
if (ret == 0 && !if_cs_hw_is_cf8305(p_dev)) if (ret == 0 && (card->model != MODEL_8305))
ret = if_cs_prog_real(card); ret = if_cs_prog_real(card, mainfw);
if (ret) if (ret)
goto out2; goto out2;
@ -957,6 +969,11 @@ out2:
out1: out1:
pcmcia_disable_device(p_dev); pcmcia_disable_device(p_dev);
out: out:
if (helper)
release_firmware(helper);
if (mainfw)
release_firmware(mainfw);
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
return 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(CF8305_MANFID, CF8305_CARDID),
PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID), PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID),
PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID), PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID),
/* NOTE: keep in sync with get_model() */
PCMCIA_DEVICE_NULL, PCMCIA_DEVICE_NULL,
}; };
MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); MODULE_DEVICE_TABLE(pcmcia, if_cs_ids);

View file

@ -76,36 +76,32 @@ static const struct sdio_device_id if_sdio_ids[] = {
MODULE_DEVICE_TABLE(sdio, if_sdio_ids); MODULE_DEVICE_TABLE(sdio, if_sdio_ids);
struct if_sdio_model { #define MODEL_8385 0x04
int model; #define MODEL_8686 0x0b
const char *helper; #define MODEL_8688 0x10
const char *firmware;
};
static struct if_sdio_model if_sdio_models[] = { static const struct lbs_fw_table fw_table[] = {
{ { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" },
/* 8385 */ { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" },
.model = IF_SDIO_MODEL_8385, { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" },
.helper = "sd8385_helper.bin", { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" },
.firmware = "sd8385.bin", { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" },
}, { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" },
{ { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" },
/* 8686 */ { 0, NULL, NULL }
.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",
},
}; };
MODULE_FIRMWARE("libertas/sd8385_helper.bin");
MODULE_FIRMWARE("libertas/sd8385.bin");
MODULE_FIRMWARE("sd8385_helper.bin"); MODULE_FIRMWARE("sd8385_helper.bin");
MODULE_FIRMWARE("sd8385.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_helper.bin");
MODULE_FIRMWARE("sd8686.bin"); MODULE_FIRMWARE("sd8686.bin");
MODULE_FIRMWARE("libertas/sd8688_helper.bin");
MODULE_FIRMWARE("libertas/sd8688.bin");
MODULE_FIRMWARE("sd8688_helper.bin"); MODULE_FIRMWARE("sd8688_helper.bin");
MODULE_FIRMWARE("sd8688.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; u16 rx_len;
switch (card->model) { switch (card->model) {
case IF_SDIO_MODEL_8385: case MODEL_8385:
case IF_SDIO_MODEL_8686: case MODEL_8686:
rx_len = if_sdio_read_scratch(card, &ret); rx_len = if_sdio_read_scratch(card, &ret);
break; break;
case IF_SDIO_MODEL_8688: case MODEL_8688:
default: /* for newer chipsets */ default: /* for newer chipsets */
rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret); rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret);
if (!ret) if (!ret)
@ -288,7 +284,7 @@ static int if_sdio_handle_event(struct if_sdio_card *card,
lbs_deb_enter(LBS_DEB_SDIO); 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); event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);
if (ret) if (ret)
goto out; 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) #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; int ret;
const struct firmware *fw;
unsigned long timeout; unsigned long timeout;
u8 *chunk_buffer; u8 *chunk_buffer;
u32 chunk_size; u32 chunk_size;
@ -478,16 +474,10 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
lbs_deb_enter(LBS_DEB_SDIO); 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); chunk_buffer = kzalloc(64, GFP_KERNEL);
if (!chunk_buffer) { if (!chunk_buffer) {
ret = -ENOMEM; ret = -ENOMEM;
goto release_fw; goto out;
} }
sdio_claim_host(card->func); sdio_claim_host(card->func);
@ -562,22 +552,19 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
release: release:
sdio_release_host(card->func); sdio_release_host(card->func);
kfree(chunk_buffer); kfree(chunk_buffer);
release_fw:
release_firmware(fw);
out: out:
if (ret) if (ret)
lbs_pr_err("failed to load helper firmware\n"); lbs_pr_err("failed to load helper firmware\n");
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
return 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; int ret;
const struct firmware *fw;
unsigned long timeout; unsigned long timeout;
u8 *chunk_buffer; u8 *chunk_buffer;
u32 chunk_size; u32 chunk_size;
@ -586,16 +573,10 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
lbs_deb_enter(LBS_DEB_SDIO); 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); chunk_buffer = kzalloc(512, GFP_KERNEL);
if (!chunk_buffer) { if (!chunk_buffer) {
ret = -ENOMEM; ret = -ENOMEM;
goto release_fw; goto out;
} }
sdio_claim_host(card->func); sdio_claim_host(card->func);
@ -685,15 +666,12 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
release: release:
sdio_release_host(card->func); sdio_release_host(card->func);
kfree(chunk_buffer); kfree(chunk_buffer);
release_fw:
release_firmware(fw);
out: out:
if (ret) if (ret)
lbs_pr_err("failed to load firmware\n"); lbs_pr_err("failed to load firmware\n");
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
return ret; return ret;
} }
@ -701,6 +679,8 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
{ {
int ret; int ret;
u16 scratch; u16 scratch;
const struct firmware *helper = NULL;
const struct firmware *mainfw = NULL;
lbs_deb_enter(LBS_DEB_SDIO); lbs_deb_enter(LBS_DEB_SDIO);
@ -718,11 +698,18 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
goto success; 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) if (ret)
goto out; goto out;
ret = if_sdio_prog_real(card); ret = if_sdio_prog_real(card, mainfw);
if (ret) if (ret)
goto out; goto out;
@ -733,8 +720,12 @@ success:
ret = 0; ret = 0;
out: 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; return ret;
} }
@ -938,7 +929,7 @@ static int if_sdio_probe(struct sdio_func *func,
"ID: %x", &model) == 1) "ID: %x", &model) == 1)
break; break;
if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
model = IF_SDIO_MODEL_8385; model = MODEL_8385;
break; break;
} }
} }
@ -956,13 +947,13 @@ static int if_sdio_probe(struct sdio_func *func,
card->model = model; card->model = model;
switch (card->model) { switch (card->model) {
case IF_SDIO_MODEL_8385: case MODEL_8385:
card->scratch_reg = IF_SDIO_SCRATCH_OLD; card->scratch_reg = IF_SDIO_SCRATCH_OLD;
break; break;
case IF_SDIO_MODEL_8686: case MODEL_8686:
card->scratch_reg = IF_SDIO_SCRATCH; card->scratch_reg = IF_SDIO_SCRATCH;
break; break;
case IF_SDIO_MODEL_8688: case MODEL_8688:
default: /* for newer chipsets */ default: /* for newer chipsets */
card->scratch_reg = IF_SDIO_FW_STATUS; card->scratch_reg = IF_SDIO_FW_STATUS;
break; break;
@ -972,49 +963,17 @@ static int if_sdio_probe(struct sdio_func *func,
card->workqueue = create_workqueue("libertas_sdio"); card->workqueue = create_workqueue("libertas_sdio");
INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
for (i = 0;i < ARRAY_SIZE(if_sdio_models);i++) { /* Check if we support this card */
if (card->model == if_sdio_models[i].model) for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
if (card->model == fw_table[i].model)
break; break;
} }
if (i == ARRAY_SIZE(fw_table)) {
if (i == ARRAY_SIZE(if_sdio_models)) {
lbs_pr_err("unknown card model 0x%x\n", card->model); lbs_pr_err("unknown card model 0x%x\n", card->model);
ret = -ENODEV; ret = -ENODEV;
goto free; 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); sdio_claim_host(func);
ret = sdio_enable_func(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 /* 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 * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
* bit to allow access to non-vendor registers. */ * 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->caps & MMC_CAP_SDIO_IRQ) &&
(host->ios.bus_width == MMC_BUS_WIDTH_1)) { (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
u8 reg; 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. * Get rx_unit if the chip is SD8688 or newer.
* SD8385 & SD8686 do not have rx_unit. * SD8385 & SD8686 do not have rx_unit.
*/ */
if ((card->model != IF_SDIO_MODEL_8385) if ((card->model != MODEL_8385)
&& (card->model != IF_SDIO_MODEL_8686)) && (card->model != MODEL_8686))
card->rx_unit = if_sdio_read_rx_unit(card); card->rx_unit = if_sdio_read_rx_unit(card);
else else
card->rx_unit = 0; 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 * 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; struct cmd_header cmd;
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
@ -1165,7 +1124,7 @@ static void if_sdio_remove(struct sdio_func *func)
card = sdio_get_drvdata(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 * FUNC_SHUTDOWN is required for SD8688 WLAN/BT
* multiple functions * multiple functions

View file

@ -12,10 +12,6 @@
#ifndef _LBS_IF_SDIO_H #ifndef _LBS_IF_SDIO_H
#define _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_IOPORT 0x00
#define IF_SDIO_H_INT_MASK 0x04 #define IF_SDIO_H_INT_MASK 0x04

View file

@ -39,9 +39,6 @@ struct if_spi_card {
struct lbs_private *priv; struct lbs_private *priv;
struct libertas_spi_platform_data *pdata; 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. */ /* The card ID and card revision, as reported by the hardware. */
u16 card_id; u16 card_id;
u8 card_rev; u8 card_rev;
@ -70,10 +67,28 @@ static void free_if_spi_card(struct if_spi_card *card)
kfree(card); kfree(card);
} }
static struct chip_ident chip_id_to_device_name[] = { #define MODEL_8385 0x04
{ .chip_id = 0x04, .name = 8385 }, #define MODEL_8686 0x0b
{ .chip_id = 0x0b, .name = 8686 }, #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 * SPI Interface Unit Routines
@ -399,26 +414,20 @@ static int spu_init(struct if_spi_card *card, int use_dummy_writes)
* Firmware Loading * 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; int err = 0;
const struct firmware *firmware = NULL;
int bytes_remaining; int bytes_remaining;
const u8 *fw; const u8 *fw;
u8 temp[HELPER_FW_LOAD_CHUNK_SZ]; u8 temp[HELPER_FW_LOAD_CHUNK_SZ];
struct spi_device *spi = card->spi;
lbs_deb_enter(LBS_DEB_SPI); lbs_deb_enter(LBS_DEB_SPI);
err = spu_set_interrupt_mode(card, 1, 0); err = spu_set_interrupt_mode(card, 1, 0);
if (err) if (err)
goto out; 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; bytes_remaining = firmware->size;
fw = firmware->data; 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, err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
HELPER_FW_LOAD_CHUNK_SZ); HELPER_FW_LOAD_CHUNK_SZ);
if (err) if (err)
goto release_firmware; goto out;
err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, 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_SPI_HIST_CMD_DOWNLOAD_RDY); IF_SPI_HIST_CMD_DOWNLOAD_RDY);
if (err) if (err)
goto release_firmware; goto out;
/* Feed the data into the command read/write port reg /* Feed the data into the command read/write port reg
* in chunks of 64 bytes */ * 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, err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
temp, HELPER_FW_LOAD_CHUNK_SZ); temp, HELPER_FW_LOAD_CHUNK_SZ);
if (err) if (err)
goto release_firmware; goto out;
/* Interrupt the boot code */ /* Interrupt the boot code */
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
if (err) if (err)
goto release_firmware; goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
IF_SPI_CIC_CMD_DOWNLOAD_OVER); IF_SPI_CIC_CMD_DOWNLOAD_OVER);
if (err) if (err)
goto release_firmware; goto out;
bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ; bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ;
fw += 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. */ * bootloader. This completes the helper download. */
err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
if (err) if (err)
goto release_firmware; goto out;
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
if (err) if (err)
goto release_firmware; goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
IF_SPI_CIC_CMD_DOWNLOAD_OVER); IF_SPI_CIC_CMD_DOWNLOAD_OVER);
goto release_firmware; goto out;
lbs_deb_spi("waiting for helper to boot...\n"); lbs_deb_spi("waiting for helper to boot...\n");
release_firmware:
release_firmware(firmware);
out: out:
if (err) if (err)
lbs_pr_err("failed to load helper firmware (err=%d)\n", 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; 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 len, prev_len;
int bytes, crc_err = 0, err = 0; int bytes, crc_err = 0, err = 0;
const struct firmware *firmware = NULL;
const u8 *fw; const u8 *fw;
struct spi_device *spi = card->spi;
u16 num_crc_errs; u16 num_crc_errs;
lbs_deb_enter(LBS_DEB_SPI); lbs_deb_enter(LBS_DEB_SPI);
@ -538,19 +544,11 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
if (err) if (err)
goto out; 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); err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
if (err) { if (err) {
lbs_pr_err("%s: timed out waiting for initial " lbs_pr_err("%s: timed out waiting for initial "
"scratch reg = 0\n", __func__); "scratch reg = 0\n", __func__);
goto release_firmware; goto out;
} }
num_crc_errs = 0; 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))) { while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) {
if (len < 0) { if (len < 0) {
err = len; err = len;
goto release_firmware; goto out;
} }
if (bytes < 0) { if (bytes < 0) {
/* If there are no more bytes left, we would normally /* 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 " lbs_pr_err("Too many CRC errors encountered "
"in firmware load.\n"); "in firmware load.\n");
err = -EIO; err = -EIO;
goto release_firmware; goto out;
} }
} else { } else {
/* Previous transfer succeeded. Advance counters. */ /* 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); err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
if (err) if (err)
goto release_firmware; goto out;
err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
card->cmd_buffer, len); card->cmd_buffer, len);
if (err) if (err)
goto release_firmware; goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG , err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
IF_SPI_CIC_CMD_DOWNLOAD_OVER); IF_SPI_CIC_CMD_DOWNLOAD_OVER);
if (err) if (err)
goto release_firmware; goto out;
prev_len = len; prev_len = len;
} }
if (bytes > prev_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); SUCCESSFUL_FW_DOWNLOAD_MAGIC);
if (err) { if (err) {
lbs_pr_err("failed to confirm the firmware download\n"); lbs_pr_err("failed to confirm the firmware download\n");
goto release_firmware; goto out;
} }
release_firmware:
release_firmware(firmware);
out: out:
if (err) if (err)
lbs_pr_err("failed to load firmware (err=%d)\n", 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; goto err;
} }
if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) {
err = if_spi_c2h_cmd(card); err = if_spi_c2h_cmd(card);
if (err) if (err)
goto 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); err = if_spi_c2h_data(card);
if (err) if (err)
goto err; goto err;
}
/* workaround: in PS mode, the card does not set the Command /* workaround: in PS mode, the card does not set the Command
* Download Ready bit, but it sets TX Download Ready. */ * 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 * 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) static int __devinit if_spi_probe(struct spi_device *spi)
{ {
struct if_spi_card *card; struct if_spi_card *card;
struct lbs_private *priv = NULL; struct lbs_private *priv = NULL;
struct libertas_spi_platform_data *pdata = spi->dev.platform_data; struct libertas_spi_platform_data *pdata = spi->dev.platform_data;
int err = 0; int err = 0, i;
u32 scratch; u32 scratch;
struct sched_param param = { .sched_priority = 1 }; struct sched_param param = { .sched_priority = 1 };
const struct firmware *helper = NULL;
const struct firmware *mainfw = NULL;
lbs_deb_enter(LBS_DEB_SPI); 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 " lbs_deb_spi("Firmware is already loaded for "
"Marvell WLAN 802.11 adapter\n"); "Marvell WLAN 802.11 adapter\n");
else { else {
err = if_spi_calculate_fw_names(card->card_id, /* Check if we support this card */
card->helper_fw_name, card->main_fw_name); for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
if (err) 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; 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 " lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter "
"(chip_id = 0x%04x, chip_rev = 0x%02x) " "(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, card->card_id, card->card_rev,
spi->master->bus_num, spi->chip_select, spi->master->bus_num, spi->chip_select,
spi->max_speed_hz); spi->max_speed_hz);
err = if_spi_prog_helper_firmware(card); err = if_spi_prog_helper_firmware(card, helper);
if (err) if (err)
goto free_card; goto free_card;
err = if_spi_prog_main_firmware(card); err = if_spi_prog_main_firmware(card, mainfw);
if (err) if (err)
goto free_card; goto free_card;
lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n"); lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n");
@ -1044,6 +1035,11 @@ remove_card:
free_card: free_card:
free_if_spi_card(card); free_if_spi_card(card);
out: out:
if (helper)
release_firmware(helper);
if (mainfw)
release_firmware(mainfw);
lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
return err; return err;
} }

View file

@ -25,11 +25,6 @@
#define IF_SPI_FW_NAME_MAX 30 #define IF_SPI_FW_NAME_MAX 30
struct chip_ident {
u16 chip_id;
u16 name;
};
#define MAX_MAIN_FW_LOAD_CRC_ERR 10 #define MAX_MAIN_FW_LOAD_CRC_ERR 10
/* Chunk size when loading the helper firmware */ /* Chunk size when loading the helper firmware */

View file

@ -26,15 +26,25 @@
#define MESSAGE_HEADER_LEN 4 #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_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"); MODULE_FIRMWARE("usb8388.bin");
enum {
MODEL_UNKNOWN = 0x0,
MODEL_8388 = 0x1,
MODEL_8682 = 0x2
};
static struct usb_device_id if_usb_table[] = { static struct usb_device_id if_usb_table[] = {
/* Enter the device signature inside */ /* Enter the device signature inside */
{ USB_DEVICE(0x1286, 0x2001) }, { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 },
{ USB_DEVICE(0x05a3, 0x8388) }, { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 },
{} /* Terminating entry */ {} /* Terminating entry */
}; };
@ -66,6 +76,8 @@ static ssize_t if_usb_firmware_set(struct device *dev,
struct if_usb_card *cardp = priv->card; struct if_usb_card *cardp = priv->card;
int ret; int ret;
BUG_ON(buf == NULL);
ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_FW); ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_FW);
if (ret == 0) if (ret == 0)
return count; return count;
@ -91,6 +103,8 @@ static ssize_t if_usb_boot2_set(struct device *dev,
struct if_usb_card *cardp = priv->card; struct if_usb_card *cardp = priv->card;
int ret; int ret;
BUG_ON(buf == NULL);
ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_BOOT2); ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_BOOT2);
if (ret == 0) if (ret == 0)
return count; return count;
@ -244,6 +258,7 @@ static int if_usb_probe(struct usb_interface *intf,
init_waitqueue_head(&cardp->fw_wq); init_waitqueue_head(&cardp->fw_wq);
cardp->udev = udev; cardp->udev = udev;
cardp->model = (uint32_t) id->driver_info;
iface_desc = intf->cur_altsetting; iface_desc = intf->cur_altsetting;
lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" 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; 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, static int __if_usb_prog_firmware(struct if_usb_card *cardp,
const char *fwname, int cmd) 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); lbs_deb_enter(LBS_DEB_USB);
ret = request_firmware(&cardp->fw, fwname, &cardp->udev->dev); ret = get_fw(cardp, fwname);
if (ret < 0) { if (ret) {
lbs_pr_err("request_firmware() failed with %#x\n", ret); lbs_pr_err("failed to find firmware (%d)\n", ret);
lbs_pr_err("firmware %s not found\n", fwname);
goto done; goto done;
} }

View file

@ -43,6 +43,7 @@ struct bootcmdresp
/** USB card description structure*/ /** USB card description structure*/
struct if_usb_card { struct if_usb_card {
struct usb_device *udev; struct usb_device *udev;
uint32_t model; /* MODEL_* */
struct urb *rx_urb, *tx_urb; struct urb *rx_urb, *tx_urb;
struct lbs_private *priv; struct lbs_private *priv;

View file

@ -1047,6 +1047,111 @@ void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx)
} }
EXPORT_SYMBOL_GPL(lbs_notify_command_response); 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) static int __init lbs_init_module(void)
{ {
lbs_deb_enter(LBS_DEB_MAIN); lbs_deb_enter(LBS_DEB_MAIN);

View file

@ -54,7 +54,7 @@ static int if_usb_reset_device(struct if_usb_card *cardp);
/** /**
* if_usb_wrike_bulk_callback - call back to handle URB status * if_usb_wrike_bulk_callback - call back to handle URB status
* *
* @param urb pointer to urb structure * @param urb pointer to urb structure
*/ */
static void if_usb_write_bulk_callback(struct urb *urb) static void if_usb_write_bulk_callback(struct urb *urb)
{ {
@ -178,16 +178,19 @@ static int if_usb_probe(struct usb_interface *intf,
le16_to_cpu(endpoint->wMaxPacketSize); le16_to_cpu(endpoint->wMaxPacketSize);
cardp->ep_in = usb_endpoint_num(endpoint); cardp->ep_in = usb_endpoint_num(endpoint);
lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n",
lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); 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)) { } else if (usb_endpoint_is_bulk_out(endpoint)) {
cardp->ep_out_size = cardp->ep_out_size =
le16_to_cpu(endpoint->wMaxPacketSize); le16_to_cpu(endpoint->wMaxPacketSize);
cardp->ep_out = usb_endpoint_num(endpoint); 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", lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n",
cardp->ep_out_size); cardp->ep_out_size);
} }
} }
if (!cardp->ep_out_size || !cardp->ep_in_size) { if (!cardp->ep_out_size || !cardp->ep_in_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)) { 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, "There are data to follow\n");
lbtf_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", lbtf_deb_usb2(&cardp->udev->dev,
cardp->fwseqnum, cardp->totalbytes); "seqnum = %d totalbytes = %d\n",
cardp->fwseqnum, cardp->totalbytes);
} else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { } 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"); lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n");
/* Host has finished FW downloading /* Host has finished FW downloading
@ -367,7 +372,7 @@ EXPORT_SYMBOL_GPL(if_usb_reset_device);
/** /**
* usb_tx_block - transfer data to the device * usb_tx_block - transfer data to the device
* *
* @priv pointer to struct lbtf_private * @priv pointer to struct lbtf_private
* @payload pointer to payload data * @payload pointer to payload data
* @nb data length * @nb data length
* @data non-zero for data, zero for commands * @data non-zero for data, zero for commands
@ -400,7 +405,8 @@ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
urb->transfer_flags |= URB_ZERO_PACKET; urb->transfer_flags |= URB_ZERO_PACKET;
if (usb_submit_urb(urb, GFP_ATOMIC)) { 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; 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; 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); ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC);
if (ret) { 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); kfree_skb(skb);
cardp->rx_skb = NULL; cardp->rx_skb = NULL;
lbtf_deb_leave(LBTF_DEB_USB); lbtf_deb_leave(LBTF_DEB_USB);
@ -522,14 +530,14 @@ static void if_usb_receive_fwload(struct urb *urb)
} }
} else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) { } else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) {
pr_info("boot cmd response cmd_tag error (%d)\n", pr_info("boot cmd response cmd_tag error (%d)\n",
bcmdresp.cmd); bcmdresp.cmd);
} else if (bcmdresp.result != BOOT_CMD_RESP_OK) { } else if (bcmdresp.result != BOOT_CMD_RESP_OK) {
pr_info("boot cmd response result error (%d)\n", pr_info("boot cmd response result error (%d)\n",
bcmdresp.result); bcmdresp.result);
} else { } else {
cardp->bootcmdresp = 1; cardp->bootcmdresp = 1;
lbtf_deb_usbd(&cardp->udev->dev, lbtf_deb_usbd(&cardp->udev->dev,
"Received valid boot command response\n"); "Received valid boot command response\n");
} }
kfree_skb(skb); kfree_skb(skb);
@ -541,19 +549,23 @@ static void if_usb_receive_fwload(struct urb *urb)
syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader), syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader),
GFP_ATOMIC); GFP_ATOMIC);
if (!syncfwheader) { 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); kfree_skb(skb);
lbtf_deb_leave(LBTF_DEB_USB); lbtf_deb_leave(LBTF_DEB_USB);
return; return;
} }
if (!syncfwheader->cmd) { if (!syncfwheader->cmd) {
lbtf_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); lbtf_deb_usb2(&cardp->udev->dev,
lbtf_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", "FW received Blk with correct CRC\n");
le32_to_cpu(syncfwheader->seqnum)); lbtf_deb_usb2(&cardp->udev->dev,
"FW received Blk seqnum = %d\n",
le32_to_cpu(syncfwheader->seqnum));
cardp->CRC_OK = 1; cardp->CRC_OK = 1;
} else { } 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; cardp->CRC_OK = 0;
} }
@ -666,7 +678,8 @@ static void if_usb_receive(struct urb *urb)
{ {
/* Event cause handling */ /* Event cause handling */
u32 event_cause = le32_to_cpu(pkt[1]); 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 */ /* Icky undocumented magic special case */
if (event_cause & 0xffff0000) { if (event_cause & 0xffff0000) {
@ -689,7 +702,7 @@ static void if_usb_receive(struct urb *urb)
} }
default: default:
lbtf_deb_usbd(&cardp->udev->dev, lbtf_deb_usbd(&cardp->udev->dev,
"libertastf: unknown command type 0x%X\n", recvtype); "libertastf: unknown command type 0x%X\n", recvtype);
kfree_skb(skb); kfree_skb(skb);
break; break;
} }

View file

@ -9,7 +9,8 @@
/* /*
* TODO: * 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) * - 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); hwsim_check_magic(vif);
if (vif->type != NL80211_IFTYPE_AP && if (vif->type != NL80211_IFTYPE_AP &&
vif->type != NL80211_IFTYPE_MESH_POINT) vif->type != NL80211_IFTYPE_MESH_POINT &&
vif->type != NL80211_IFTYPE_ADHOC)
return; return;
skb = ieee80211_beacon_get(hw, vif); skb = ieee80211_beacon_get(hw, vif);
@ -1295,6 +1297,7 @@ static int __init init_mac80211_hwsim(void)
hw->wiphy->interface_modes = hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT); BIT(NL80211_IFTYPE_MESH_POINT);
hw->flags = IEEE80211_HW_MFP_CAPABLE | hw->flags = IEEE80211_HW_MFP_CAPABLE |

View file

@ -2,6 +2,7 @@ config P54_COMMON
tristate "Softmac Prism54 support" tristate "Softmac Prism54 support"
depends on MAC80211 && EXPERIMENTAL depends on MAC80211 && EXPERIMENTAL
select FW_LOADER select FW_LOADER
select CRC_CCITT
---help--- ---help---
This is common code for isl38xx/stlc45xx based modules. This is common code for isl38xx/stlc45xx based modules.
This module does nothing by itself - the USB/PCI/SPI front-ends This module does nothing by itself - the USB/PCI/SPI front-ends

View file

@ -23,6 +23,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#include <linux/crc-ccitt.h>
#include "p54.h" #include "p54.h"
#include "eeprom.h" #include "eeprom.h"
@ -540,6 +541,7 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
int err; int err;
u8 *end = (u8 *)eeprom + len; u8 *end = (u8 *)eeprom + len;
u16 synth = 0; u16 synth = 0;
u16 crc16 = ~0;
wrap = (struct eeprom_pda_wrap *) eeprom; wrap = (struct eeprom_pda_wrap *) eeprom;
entry = (void *)wrap->data + le16_to_cpu(wrap->len); 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; break;
case PDR_END: case PDR_END:
/* make it overrun */ crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry));
entry_len = len; 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; break;
default: default:
break; break;
} }
entry = (void *)entry + (entry_len + 1)*2; 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 || if (!synth || !priv->iq_autocal || !priv->output_limit ||
!priv->curve_data) { !priv->curve_data) {
wiphy_err(dev->wiphy, wiphy_err(dev->wiphy,

View file

@ -123,10 +123,14 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
bootrec = (struct bootrec *)&bootrec->data[len]; bootrec = (struct bootrec *)&bootrec->data[len];
} }
if (fw_version) if (fw_version) {
wiphy_info(priv->hw->wiphy, wiphy_info(priv->hw->wiphy,
"FW rev %s - Softmac protocol %x.%x\n", "FW rev %s - Softmac protocol %x.%x\n",
fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); 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) if (priv->fw_var < 0x500)
wiphy_info(priv->hw->wiphy, wiphy_info(priv->hw->wiphy,

View file

@ -429,8 +429,8 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
mutex_lock(&priv->conf_mutex); mutex_lock(&priv->conf_mutex);
if (cmd == SET_KEY) { if (cmd == SET_KEY) {
switch (key->alg) { switch (key->cipher) {
case ALG_TKIP: case WLAN_CIPHER_SUITE_TKIP:
if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL | if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL |
BR_DESC_PRIV_CAP_TKIP))) { BR_DESC_PRIV_CAP_TKIP))) {
ret = -EOPNOTSUPP; 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; key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
algo = P54_CRYPTO_TKIPMICHAEL; algo = P54_CRYPTO_TKIPMICHAEL;
break; break;
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) { if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) {
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
goto out_unlock; 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; key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
algo = P54_CRYPTO_WEP; algo = P54_CRYPTO_WEP;
break; break;
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) { if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) {
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
goto out_unlock; goto out_unlock;

View file

@ -671,7 +671,7 @@ static unsigned char p54spi_eeprom[] = {
0xa8, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, 0xa8, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
0x02, 0x00, 0x00, 0x00, /* PDR_END */ 0x02, 0x00, 0x00, 0x00, /* PDR_END */
0xa8, 0xf5 /* bogus data */ 0x67, 0x99,
}; };
#endif /* P54SPI_EEPROM_H */ #endif /* P54SPI_EEPROM_H */

View file

@ -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) { switch (cipher) {
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
return P54_CRYPTO_WEP; return P54_CRYPTO_WEP;
case ALG_TKIP: case WLAN_CIPHER_SUITE_TKIP:
return P54_CRYPTO_TKIPMICHAEL; return P54_CRYPTO_TKIPMICHAEL;
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
return P54_CRYPTO_AESCCMP; return P54_CRYPTO_AESCCMP;
default: default:
return 0; return 0;
@ -731,7 +732,7 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb)
if (info->control.hw_key) { if (info->control.hw_key) {
crypt_offset = ieee80211_get_hdrlen_from_skb(skb); 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); u8 *iv = (u8 *)(skb->data + crypt_offset);
/* /*
* The firmware excepts that the IV has to have * 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; hdr->tries = ridx;
txhdr->rts_rate_idx = 0; txhdr->rts_rate_idx = 0;
if (info->control.hw_key) { 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); txhdr->key_len = min((u8)16, info->control.hw_key->keylen);
memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len); 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 */ /* reserve space for the MIC key */
len += 8; len += 8;
memcpy(skb_put(skb, 8), &(info->control.hw_key->key memcpy(skb_put(skb, 8), &(info->control.hw_key->key

View file

@ -3234,7 +3234,7 @@ prism54_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
switch (cmd) { switch (cmd) {
case PRISM54_HOSTAPD: case PRISM54_HOSTAPD:
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
ret = prism54_hostapd(ndev, &wrq->u.data); ret = prism54_hostapd(ndev, &wrq->u.data);
return ret; return ret;
} }

View file

@ -355,7 +355,9 @@ static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev,
* it is known that not work at least on some hardware. * it is known that not work at least on some hardware.
* SW crypto will be used in that case. * 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; return -EOPNOTSUPP;
/* /*

View file

@ -1318,7 +1318,25 @@
#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000) #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 0x1718
#define TX_STA_FIFO_VALID FIELD32(0x00000001) #define TX_STA_FIFO_VALID FIELD32(0x00000001)
@ -1945,6 +1963,13 @@ struct mac_iveiv_entry {
/* /*
* Word1 * 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_ACK FIELD32(0x00000001)
#define TXWI_W1_NSEQ FIELD32(0x00000002) #define TXWI_W1_NSEQ FIELD32(0x00000002)

View file

@ -1,4 +1,5 @@
/* /*
Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com> Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com>
Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Copyright (C) 2009 Gertjan van Wingerde <gwingerde@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); 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; u32 word;
/* /*
@ -437,7 +440,8 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
rt2x00_desc_read(txwi, 0, &word); rt2x00_desc_read(txwi, 0, &word);
rt2x00_set_field32(&word, TXWI_W0_FRAG, rt2x00_set_field32(&word, TXWI_W0_FRAG,
test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); 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_CF_ACK, 0);
rt2x00_set_field32(&word, TXWI_W0_TS, rt2x00_set_field32(&word, TXWI_W0_TS,
test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); 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, 2, 0 /* skbdesc->iv[0] */);
_rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */); _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) 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 offset1;
u8 offset2; 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); rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0); offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0);
offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1); 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); 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, &reg);
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) void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
{ {
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; 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. * 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. * Dump beacon to userspace through debugfs.

View file

@ -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 Copyright (C) 2009 Bartlomiej Zolnierkiewicz
This program is free software; you can redistribute it and/or modify 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, int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len); const u8 *data, const size_t len);
int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev); 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, 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); 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, void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
const u8 command, const u8 token, const u8 command, const u8 token,
const u8 arg0, const u8 arg1); 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, int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len); 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_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); void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);
extern const struct rt2x00debug rt2800_rt2x00debug; extern const struct rt2x00debug rt2800_rt2x00debug;

View file

@ -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 Alban Browaeys <prahal@yahoo.com>
Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> 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 * TX descriptor initialization
*/ */
static void rt2800pci_write_tx_data(struct queue_entry* entry, static __le32 *rt2800pci_get_txwi(struct queue_entry *entry)
struct txentry_desc *txdesc)
{ {
__le32 *txwi = (__le32 *) entry->skb->data; return (__le32 *) entry->skb->data;
rt2800_write_txwi(txwi, txdesc);
} }
static void rt2800pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, static void rt2800pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
struct sk_buff *skb, struct sk_buff *skb,
struct txentry_desc *txdesc) struct txentry_desc *txdesc)
@ -728,110 +724,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
/* /*
* Interrupt functions. * 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, &reg);
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) static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
{ {
struct ieee80211_conf conf = { .flags = 0 }; 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. * 4 - Tx done interrupt.
*/ */
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
rt2800pci_txdone(rt2x00dev); rt2800_txdone(rt2x00dev);
/* /*
* 5 - Auto wakeup interrupt. * 5 - Auto wakeup interrupt.
@ -1011,6 +903,7 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
.regbusy_read = rt2x00pci_regbusy_read, .regbusy_read = rt2x00pci_regbusy_read,
.drv_write_firmware = rt2800pci_write_firmware, .drv_write_firmware = rt2800pci_write_firmware,
.drv_init_registers = rt2800pci_init_registers, .drv_init_registers = rt2800pci_init_registers,
.drv_get_txwi = rt2800pci_get_txwi,
}; };
static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { 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, .reset_tuner = rt2800_reset_tuner,
.link_tuner = rt2800_link_tuner, .link_tuner = rt2800_link_tuner,
.write_tx_desc = rt2800pci_write_tx_desc, .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, .write_beacon = rt2800_write_beacon,
.kick_tx_queue = rt2800pci_kick_tx_queue, .kick_tx_queue = rt2800pci_kick_tx_queue,
.kill_tx_queue = rt2800pci_kill_tx_queue, .kill_tx_queue = rt2800pci_kill_tx_queue,

View file

@ -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 Mattias Nissler <mattias.nissler@gmx.de>
Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> 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 * TX descriptor initialization
*/ */
static void rt2800usb_write_tx_data(struct queue_entry* entry, static __le32 *rt2800usb_get_txwi(struct queue_entry *entry)
struct txentry_desc *txdesc)
{ {
__le32 *txwi = (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE); if (entry->queue->qid == QID_BEACON)
return (__le32 *) (entry->skb->data);
rt2800_write_txwi(txwi, txdesc); else
return (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE);
} }
static void rt2800usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, static void rt2800usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
struct sk_buff *skb, struct sk_buff *skb,
struct txentry_desc *txdesc) struct txentry_desc *txdesc)
@ -378,6 +378,38 @@ static int rt2800usb_get_tx_data_len(struct queue_entry *entry)
return length; 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 * RX control handlers
*/ */
@ -514,6 +546,11 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
*/ */
rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
/*
* Overwrite TX done handler
*/
PREPARE_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone);
return 0; return 0;
} }
@ -549,6 +586,7 @@ static const struct rt2800_ops rt2800usb_rt2800_ops = {
.regbusy_read = rt2x00usb_regbusy_read, .regbusy_read = rt2x00usb_regbusy_read,
.drv_write_firmware = rt2800usb_write_firmware, .drv_write_firmware = rt2800usb_write_firmware,
.drv_init_registers = rt2800usb_init_registers, .drv_init_registers = rt2800usb_init_registers,
.drv_get_txwi = rt2800usb_get_txwi,
}; };
static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { 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, .link_tuner = rt2800_link_tuner,
.watchdog = rt2x00usb_watchdog, .watchdog = rt2x00usb_watchdog,
.write_tx_desc = rt2800usb_write_tx_desc, .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, .write_beacon = rt2800_write_beacon,
.get_tx_data_len = rt2800usb_get_tx_data_len, .get_tx_data_len = rt2800usb_get_tx_data_len,
.kick_tx_queue = rt2x00usb_kick_tx_queue, .kick_tx_queue = rt2x00usb_kick_tx_queue,

View file

@ -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> Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
<http://rt2x00.serialmonkey.com> <http://rt2x00.serialmonkey.com>
@ -698,6 +699,7 @@ struct rt2x00_dev {
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
enum ieee80211_band curr_band; enum ieee80211_band curr_band;
int curr_freq;
/* /*
* If enabled, the debugfs interface structures * If enabled, the debugfs interface structures
@ -849,11 +851,6 @@ struct rt2x00_dev {
*/ */
struct ieee80211_low_level_stats low_level_stats; struct ieee80211_low_level_stats low_level_stats;
/*
* RX configuration information.
*/
struct ieee80211_rx_status rx_status;
/* /*
* Scheduled work. * Scheduled work.
* NOTE: intf_work will use ieee80211_iterate_active_interfaces() * NOTE: intf_work will use ieee80211_iterate_active_interfaces()
@ -862,6 +859,12 @@ struct rt2x00_dev {
*/ */
struct work_struct intf_work; 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. * Data queue arrays for RX, TX and Beacon.
* The Beacon array also contains the Atim queue * 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_pretbtt(struct rt2x00_dev *rt2x00dev);
void rt2x00lib_txdone(struct queue_entry *entry, void rt2x00lib_txdone(struct queue_entry *entry,
struct txdone_entry_desc *txdesc); struct txdone_entry_desc *txdesc);
void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status);
void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
struct queue_entry *entry); struct queue_entry *entry);

View file

@ -126,11 +126,6 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
* ANTENNA_SW_DIVERSITY state to the driver. * ANTENNA_SW_DIVERSITY state to the driver.
* If that happens, fallback to hardware defaults, * If that happens, fallback to hardware defaults,
* or our own default. * 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)) if (!(ant->flags & ANTENNA_RX_DIVERSITY))
config.rx = rt2x00lib_config_antenna_check(config.rx, def->rx); config.rx = rt2x00lib_config_antenna_check(config.rx, def->rx);
@ -142,9 +137,6 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
else else
config.tx = active->tx; config.tx = active->tx;
if (config.rx == active->rx && config.tx == active->tx)
return;
/* /*
* Antenna setup changes require the RX to be disabled, * Antenna setup changes require the RX to be disabled,
* else the changes will be ignored by the device. * 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); rt2x00link_reset_tuner(rt2x00dev, false);
rt2x00dev->curr_band = conf->channel->band; rt2x00dev->curr_band = conf->channel->band;
rt2x00dev->curr_freq = conf->channel->center_freq;
rt2x00dev->tx_power = conf->power_level; rt2x00dev->tx_power = conf->power_level;
rt2x00dev->short_retry = conf->short_frame_max_tx_count; rt2x00dev->short_retry = conf->short_frame_max_tx_count;
rt2x00dev->long_retry = conf->long_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;
} }

View file

@ -31,15 +31,14 @@
enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key)
{ {
switch (key->alg) { switch (key->cipher) {
case ALG_WEP: case WLAN_CIPHER_SUITE_WEP40:
if (key->keylen == WLAN_KEY_LEN_WEP40) return CIPHER_WEP64;
return CIPHER_WEP64; case WLAN_CIPHER_SUITE_WEP104:
else return CIPHER_WEP128;
return CIPHER_WEP128; case WLAN_CIPHER_SUITE_TKIP:
case ALG_TKIP:
return CIPHER_TKIP; return CIPHER_TKIP;
case ALG_CCMP: case WLAN_CIPHER_SUITE_CCMP:
return CIPHER_AES; return CIPHER_AES;
default: default:
return CIPHER_NONE; return CIPHER_NONE;
@ -95,7 +94,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev,
overhead += key->iv_len; overhead += key->iv_len;
if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) {
if (key->alg == ALG_TKIP) if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
overhead += 8; overhead += 8;
} }

View file

@ -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> <http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -383,15 +384,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
* send the status report back. * send the status report back.
*/ */
if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) if (!(skbdesc_flags & SKBDESC_NOT_MAC80211))
/* ieee80211_tx_status(rt2x00dev->hw, entry->skb);
* 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 else
dev_kfree_skb_any(entry->skb); dev_kfree_skb_any(entry->skb);
@ -403,7 +396,6 @@ void rt2x00lib_txdone(struct queue_entry *entry,
rt2x00dev->ops->lib->clear_entry(entry); rt2x00dev->ops->lib->clear_entry(entry);
clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE); rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);
/* /*
@ -416,6 +408,18 @@ void rt2x00lib_txdone(struct queue_entry *entry,
} }
EXPORT_SYMBOL_GPL(rt2x00lib_txdone); 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, static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
struct rxdone_entry_desc *rxdesc) struct rxdone_entry_desc *rxdesc)
{ {
@ -460,9 +464,13 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
{ {
struct rxdone_entry_desc rxdesc; struct rxdone_entry_desc rxdesc;
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status; struct ieee80211_rx_status *rx_status;
unsigned int header_length; unsigned int header_length;
int rate_idx; 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 * Allocate a new sk_buffer. If no new buffer available, drop the
* received frame and reuse the existing buffer. * 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); rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc);
rt2x00debug_update_crypto(rt2x00dev, &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->mactime = rxdesc.timestamp;
rx_status->band = rt2x00dev->curr_band;
rx_status->freq = rt2x00dev->curr_freq;
rx_status->rate_idx = rate_idx; rx_status->rate_idx = rate_idx;
rx_status->signal = rxdesc.rssi; rx_status->signal = rxdesc.rssi;
rx_status->flag = rxdesc.flags; rx_status->flag = rxdesc.flags;
rx_status->antenna = rt2x00dev->link.ant.active.rx; rx_status->antenna = rt2x00dev->link.ant.active.rx;
/* ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
* 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. * Replace the skb with the freshly allocated one.
*/ */
entry->skb = skb; entry->skb = skb;
entry->flags = 0;
submit_entry:
rt2x00dev->ops->lib->clear_entry(entry); rt2x00dev->ops->lib->clear_entry(entry);
rt2x00queue_index_inc(entry->queue, Q_INDEX); rt2x00queue_index_inc(entry->queue, Q_INDEX);
rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);
} }
EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); EXPORT_SYMBOL_GPL(rt2x00lib_rxdone);
@ -1017,6 +1018,8 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
* Stop all work. * Stop all work.
*/ */
cancel_work_sync(&rt2x00dev->intf_work); cancel_work_sync(&rt2x00dev->intf_work);
cancel_work_sync(&rt2x00dev->rxdone_work);
cancel_work_sync(&rt2x00dev->txdone_work);
/* /*
* Uninitialize device. * Uninitialize device.

View file

@ -63,6 +63,9 @@ static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev)
INFO(rt2x00dev, "Firmware detected - version: %d.%d.\n", INFO(rt2x00dev, "Firmware detected - version: %d.%d.\n",
fw->data[fw->size - 4], fw->data[fw->size - 3]); 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); retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size);
switch (retval) { switch (retval) {

View file

@ -54,6 +54,16 @@ void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,
*/ */
if (txrate->flags & IEEE80211_TX_RC_MCS) { if (txrate->flags & IEEE80211_TX_RC_MCS) {
txdesc->mcs = txrate->idx; 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 { } else {
txdesc->mcs = rt2x00_get_rate_mcs(hwrate->mcs); txdesc->mcs = rt2x00_get_rate_mcs(hwrate->mcs);
if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)

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