Skip to content

Commit

Permalink
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/linville/wireless-next into for-davem

Conflicts:
	drivers/net/wireless/iwlwifi/iwl-testmode.c
	include/net/nfc/nfc.h
	net/nfc/netlink.c
	net/wireless/nl80211.c
  • Loading branch information
John W. Linville committed Apr 18, 2012
2 parents 91fbe33 + b5abcf0 commit 59ef43e
Show file tree
Hide file tree
Showing 290 changed files with 14,493 additions and 7,242 deletions.
155 changes: 155 additions & 0 deletions Documentation/nfc/nfc-hci.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
HCI backend for NFC Core

Author: Eric Lapuyade, Samuel Ortiz
Contact: eric.lapuyade@intel.com, samuel.ortiz@intel.com

General
-------

The HCI layer implements much of the ETSI TS 102 622 V10.2.0 specification. It
enables easy writing of HCI-based NFC drivers. The HCI layer runs as an NFC Core
backend, implementing an abstract nfc device and translating NFC Core API
to HCI commands and events.

HCI
---

HCI registers as an nfc device with NFC Core. Requests coming from userspace are
routed through netlink sockets to NFC Core and then to HCI. From this point,
they are translated in a sequence of HCI commands sent to the HCI layer in the
host controller (the chip). The sending context blocks while waiting for the
response to arrive.
HCI events can also be received from the host controller. They will be handled
and a translation will be forwarded to NFC Core as needed.
HCI uses 2 execution contexts:
- one if for executing commands : nfc_hci_msg_tx_work(). Only one command
can be executing at any given moment.
- one if for dispatching received events and responses : nfc_hci_msg_rx_work()

HCI Session initialization:
---------------------------

The Session initialization is an HCI standard which must unfortunately
support proprietary gates. This is the reason why the driver will pass a list
of proprietary gates that must be part of the session. HCI will ensure all
those gates have pipes connected when the hci device is set up.

HCI Gates and Pipes
-------------------

A gate defines the 'port' where some service can be found. In order to access
a service, one must create a pipe to that gate and open it. In this
implementation, pipes are totally hidden. The public API only knows gates.
This is consistent with the driver need to send commands to proprietary gates
without knowing the pipe connected to it.

Driver interface
----------------

A driver would normally register itself with HCI and provide the following
entry points:

struct nfc_hci_ops {
int (*open)(struct nfc_hci_dev *hdev);
void (*close)(struct nfc_hci_dev *hdev);
int (*xmit)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
int (*start_poll)(struct nfc_hci_dev *hdev, u32 protocols);
int (*target_from_gate)(struct nfc_hci_dev *hdev, u8 gate,
struct nfc_target *target);
};

open() and close() shall turn the hardware on and off. xmit() shall simply
write a frame to the chip. start_poll() is an optional entrypoint that shall
set the hardware in polling mode. This must be implemented only if the hardware
uses proprietary gates or a mechanism slightly different from the HCI standard.
target_from_gate() is another optional entrypoint to return the protocols
corresponding to a proprietary gate.

On the rx path, the driver is responsible to push incoming HCP frames to HCI
using nfc_hci_recv_frame(). HCI will take care of re-aggregation and handling
This must be done from a context that can sleep.

SHDLC
-----

Most chips use shdlc to ensure integrity and delivery ordering of the HCP
frames between the host controller (the chip) and hosts (entities connected
to the chip, like the cpu). In order to simplify writing the driver, an shdlc
layer is available for use by the driver.
When used, the driver actually registers with shdlc, and shdlc will register
with HCI. HCI sees shdlc as the driver and thus send its HCP frames
through shdlc->xmit.
SHDLC adds a new execution context (nfc_shdlc_sm_work()) to run its state
machine and handle both its rx and tx path.

Included Drivers
----------------

An HCI based driver for an NXP PN544, connected through I2C bus, and using
shdlc is included.

Execution Contexts
------------------

The execution contexts are the following:
- IRQ handler (IRQH):
fast, cannot sleep. stores incoming frames into an shdlc rx queue

- SHDLC State Machine worker (SMW)
handles shdlc rx & tx queues. Dispatches HCI cmd responses.

- HCI Tx Cmd worker (MSGTXWQ)
Serialize execution of HCI commands. Complete execution in case of resp timeout.

- HCI Rx worker (MSGRXWQ)
Dispatches incoming HCI commands or events.

- Syscall context from a userspace call (SYSCALL)
Any entrypoint in HCI called from NFC Core

Workflow executing an HCI command (using shdlc)
-----------------------------------------------

Executing an HCI command can easily be performed synchronously using the
following API:

int nfc_hci_send_cmd (struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
const u8 *param, size_t param_len, struct sk_buff **skb)

The API must be invoked from a context that can sleep. Most of the time, this
will be the syscall context. skb will return the result that was received in
the response.

Internally, execution is asynchronous. So all this API does is to enqueue the
HCI command, setup a local wait queue on stack, and wait_event() for completion.
The wait is not interruptible because it is guaranteed that the command will
complete after some short timeout anyway.

MSGTXWQ context will then be scheduled and invoke nfc_hci_msg_tx_work().
This function will dequeue the next pending command and send its HCP fragments
to the lower layer which happens to be shdlc. It will then start a timer to be
able to complete the command with a timeout error if no response arrive.

SMW context gets scheduled and invokes nfc_shdlc_sm_work(). This function
handles shdlc framing in and out. It uses the driver xmit to send frames and
receives incoming frames in an skb queue filled from the driver IRQ handler.
SHDLC I(nformation) frames payload are HCP fragments. They are agregated to
form complete HCI frames, which can be a response, command, or event.

HCI Responses are dispatched immediately from this context to unblock
waiting command execution. Reponse processing involves invoking the completion
callback that was provided by nfc_hci_msg_tx_work() when it sent the command.
The completion callback will then wake the syscall context.

Workflow receiving an HCI event or command
------------------------------------------

HCI commands or events are not dispatched from SMW context. Instead, they are
queued to HCI rx_queue and will be dispatched from HCI rx worker
context (MSGRXWQ). This is done this way to allow a cmd or event handler
to also execute other commands (for example, handling the
NFC_HCI_EVT_TARGET_DISCOVERED event from PN544 requires to issue an
ANY_GET_PARAMETER to the reader A gate to get information on the target
that was discovered).

Typically, such an event will be propagated to NFC Core from MSGRXWQ context.
27 changes: 10 additions & 17 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -6669,6 +6669,16 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Maintained
F: sound/soc/codecs/twl4030*

TI WILINK WIRELESS DRIVERS
M: Luciano Coelho <coelho@ti.com>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/wl12xx
W: http://wireless.kernel.org/en/users/Drivers/wl1251
T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git
S: Maintained
F: drivers/net/wireless/ti/
F: include/linux/wl12xx.h

TIPC NETWORK LAYER
M: Jon Maloy <jon.maloy@ericsson.com>
M: Allan Stephens <allan.stephens@windriver.com>
Expand Down Expand Up @@ -7425,23 +7435,6 @@ M: Miloslav Trmac <mitr@volny.cz>
S: Maintained
F: drivers/input/misc/wistron_btns.c

WL1251 WIRELESS DRIVER
M: Luciano Coelho <coelho@ti.com>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/wl1251
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
S: Maintained
F: drivers/net/wireless/wl1251/*

WL1271 WIRELESS DRIVER
M: Luciano Coelho <coelho@ti.com>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/wl12xx
T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git
S: Maintained
F: drivers/net/wireless/wl12xx/
F: include/linux/wl12xx.h

WL3501 WIRELESS PCMCIA CARD DRIVER
M: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
L: linux-wireless@vger.kernel.org
Expand Down
3 changes: 1 addition & 2 deletions drivers/net/wireless/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,7 @@ source "drivers/net/wireless/orinoco/Kconfig"
source "drivers/net/wireless/p54/Kconfig"
source "drivers/net/wireless/rt2x00/Kconfig"
source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/wl1251/Kconfig"
source "drivers/net/wireless/wl12xx/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/mwifiex/Kconfig"

Expand Down
4 changes: 1 addition & 3 deletions drivers/net/wireless/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ obj-$(CONFIG_ATH_COMMON) += ath/

obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o

obj-$(CONFIG_WL1251) += wl1251/
obj-$(CONFIG_WL12XX) += wl12xx/
obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/
obj-$(CONFIG_WL_TI) += ti/

obj-$(CONFIG_IWM) += iwmc3200wifi/

Expand Down
17 changes: 1 addition & 16 deletions drivers/net/wireless/adm8211.c
Original file line number Diff line number Diff line change
Expand Up @@ -1991,19 +1991,4 @@ static struct pci_driver adm8211_driver = {
#endif /* CONFIG_PM */
};



static int __init adm8211_init(void)
{
return pci_register_driver(&adm8211_driver);
}


static void __exit adm8211_exit(void)
{
pci_unregister_driver(&adm8211_driver);
}


module_init(adm8211_init);
module_exit(adm8211_exit);
module_pci_driver(adm8211_driver);
6 changes: 2 additions & 4 deletions drivers/net/wireless/at76c50x-usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -2512,10 +2512,8 @@ static void __exit at76_mod_exit(void)

printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n");
usb_deregister(&at76_driver);
for (i = 0; i < ARRAY_SIZE(firmwares); i++) {
if (firmwares[i].fw)
release_firmware(firmwares[i].fw);
}
for (i = 0; i < ARRAY_SIZE(firmwares); i++)
release_firmware(firmwares[i].fw);
led_trigger_unregister_simple(ledtrig_tx);
}

Expand Down
2 changes: 1 addition & 1 deletion drivers/net/wireless/ath/ath5k/ath5k.h
Original file line number Diff line number Diff line change
Expand Up @@ -1527,7 +1527,7 @@ void ath5k_eeprom_detach(struct ath5k_hw *ah);

/* Protocol Control Unit Functions */
/* Helpers */
int ath5k_hw_get_frame_duration(struct ath5k_hw *ah,
int ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band,
int len, struct ieee80211_rate *rate, bool shortpre);
unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah);
unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah);
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/wireless/ath/ath5k/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -1170,7 +1170,7 @@ ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb,

if (ieee80211_is_beacon(mgmt->frame_control) &&
le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
compare_ether_addr(mgmt->bssid, common->curbssid) == 0) {
/*
* Received an IBSS beacon with the same BSSID. Hardware *must*
* have updated the local TSF. We have to work around various
Expand Down Expand Up @@ -1234,7 +1234,7 @@ ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi)

/* only beacons from our BSSID */
if (!ieee80211_is_beacon(mgmt->frame_control) ||
memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
compare_ether_addr(mgmt->bssid, common->curbssid) != 0)
return;

ewma_add(&ah->ah_beacon_rssi_avg, rssi);
Expand Down
27 changes: 2 additions & 25 deletions drivers/net/wireless/ath/ath5k/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = {
{ PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
{ PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
{ PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */
{ PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */
{ 0 }
};
MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table);
Expand Down Expand Up @@ -339,28 +340,4 @@ static struct pci_driver ath5k_pci_driver = {
.driver.pm = ATH5K_PM_OPS,
};

/*
* Module init/exit functions
*/
static int __init
init_ath5k_pci(void)
{
int ret;

ret = pci_register_driver(&ath5k_pci_driver);
if (ret) {
pr_err("pci: can't register pci driver\n");
return ret;
}

return 0;
}

static void __exit
exit_ath5k_pci(void)
{
pci_unregister_driver(&ath5k_pci_driver);
}

module_init(init_ath5k_pci);
module_exit(exit_ath5k_pci);
module_pci_driver(ath5k_pci_driver);
9 changes: 5 additions & 4 deletions drivers/net/wireless/ath/ath5k/pcu.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ static const unsigned int ack_rates_high[] =
* bwmodes.
*/
int
ath5k_hw_get_frame_duration(struct ath5k_hw *ah,
ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band,
int len, struct ieee80211_rate *rate, bool shortpre)
{
int sifs, preamble, plcp_bits, sym_time;
Expand All @@ -120,7 +120,7 @@ ath5k_hw_get_frame_duration(struct ath5k_hw *ah,
/* Fallback */
if (!ah->ah_bwmode) {
__le16 raw_dur = ieee80211_generic_frame_duration(ah->hw,
NULL, len, rate);
NULL, band, len, rate);

/* subtract difference between long and short preamble */
dur = le16_to_cpu(raw_dur);
Expand Down Expand Up @@ -302,14 +302,15 @@ ath5k_hw_write_rate_duration(struct ath5k_hw *ah)
* actual rate for this rate. See mac80211 tx.c
* ieee80211_duration() for a brief description of
* what rate we should choose to TX ACKs. */
tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, false);
tx_time = ath5k_hw_get_frame_duration(ah, band, 10,
rate, false);

ath5k_hw_reg_write(ah, tx_time, reg);

if (!(rate->flags & IEEE80211_RATE_SHORT_PREAMBLE))
continue;

tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, true);
tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, true);
ath5k_hw_reg_write(ah, tx_time,
reg + (AR5K_SET_SHORT_PREAMBLE << 2));
}
Expand Down
8 changes: 5 additions & 3 deletions drivers/net/wireless/ath/ath5k/qcu.c
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
{
struct ieee80211_channel *channel = ah->ah_current_channel;
enum ieee80211_band band;
struct ieee80211_rate *rate;
u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock;
u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
Expand Down Expand Up @@ -600,11 +601,12 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
* Also we have different lowest rate for 802.11a
*/
if (channel->band == IEEE80211_BAND_5GHZ)
rate = &ah->sbands[IEEE80211_BAND_5GHZ].bitrates[0];
band = IEEE80211_BAND_5GHZ;
else
rate = &ah->sbands[IEEE80211_BAND_2GHZ].bitrates[0];
band = IEEE80211_BAND_2GHZ;

ack_tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, false);
rate = &ah->sbands[band].bitrates[0];
ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false);

/* ack_tx_time includes an SIFS already */
eifs = ack_tx_time + sifs + 2 * slot_time;
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/wireless/ath/ath6kl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
obj-$(CONFIG_ATH6KL) += ath6kl_core.o
ath6kl_core-y += debug.o
ath6kl_core-y += hif.o
ath6kl_core-y += htc.o
ath6kl_core-y += htc_mbox.o
ath6kl_core-y += htc_pipe.o
ath6kl_core-y += bmi.o
ath6kl_core-y += cfg80211.o
ath6kl_core-y += init.o
Expand Down
Loading

0 comments on commit 59ef43e

Please sign in to comment.