Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH RFC] nfc: add a driver for pn532 connected on uart
From: Lars Poeschel @ 2013-08-22 14:28 UTC (permalink / raw)
  To: poeschel, lauro.venancio, aloisio.almeida, sameo, gregkh, jslaby,
	linux-kernel, linux-wireless, linux-nfc

From: Lars Poeschel <poeschel@lemonage.de>

This adds a driver for the nxp pn532 nfc chip.
It is not meant for merging. Instead it is meant to show that some
progress has been made and what the current state is and to help
testing.
Although I can do some basic things with this driver I expect it to
contain lots of bugs. Be aware!
This driver is heavily based on the pn533 driver and duplicates much
code. This has do be factored out some time.

Signed-off-by: Lars Poeschel <poeschel@lemonage.de>
---
 drivers/nfc/Kconfig      |    9 +
 drivers/nfc/Makefile     |    1 +
 drivers/nfc/pn532.c      | 2608 ++++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/tty.h |    1 +
 4 files changed, 2619 insertions(+)
 create mode 100644 drivers/nfc/pn532.c

diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index b0b64cc..e97303a 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -5,6 +5,15 @@
 menu "Near Field Communication (NFC) devices"
 	depends on NFC
 
+config NFC_PN532
+	tristate "NXP PN532 UART driver"
+	help
+	  NXP PN532 UART driver.
+	  This driver provides support for NFC NXP PN532 devices.
+
+	  Say Y here to compile support for PN532 devices into the
+	  kernel or say M to compile it as module (pn532).
+
 config NFC_PN533
 	tristate "NXP PN533 USB driver"
 	depends on USB
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index be7636a..a942734 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_NFC_PN544)		+= pn544/
 obj-$(CONFIG_NFC_MICROREAD)	+= microread/
+obj-$(CONFIG_NFC_PN532)		+= pn532.o
 obj-$(CONFIG_NFC_PN533)		+= pn533.o
 obj-$(CONFIG_NFC_WILINK)	+= nfcwilink.o
 obj-$(CONFIG_NFC_MEI_PHY)	+= mei_phy.o
diff --git a/drivers/nfc/pn532.c b/drivers/nfc/pn532.c
new file mode 100644
index 0000000..7e0db46
--- /dev/null
+++ b/drivers/nfc/pn532.c
@@ -0,0 +1,2608 @@
+/*
+ * Copyright (C) 2013 Lemonage Software GmbH
+ *
+ * Author:
+ *    Lars Poeschel <poeschel@lemonage.de>
+ *
+ * Heavily based on the pn533 driver by:
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#define DEBUG
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/nfc.h>
+#include <linux/netdevice.h>
+#include <net/nfc/nfc.h>
+
+#define DESCRIPTION "PN532 uart driver"
+#define PN532_MAX_DEVS		2
+
+#define PN532_ALL_PROTOCOLS (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK |\
+			     NFC_PROTO_FELICA_MASK | NFC_PROTO_ISO14443_MASK |\
+			     NFC_PROTO_NFC_DEP_MASK |\
+			     NFC_PROTO_ISO14443_B_MASK)
+
+#define PN532_NO_TYPE_B_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
+				   NFC_PROTO_MIFARE_MASK | \
+				   NFC_PROTO_FELICA_MASK | \
+				   NFC_PROTO_ISO14443_MASK | \
+				   NFC_PROTO_NFC_DEP_MASK)
+
+/* How much time we spend listening for initiators */
+#define PN532_LISTEN_TIME 2
+
+/* frame definitions */
+#define PN532_FRAME_HEADER_LEN (sizeof(struct pn532_frame) \
+					+ 2) /* data[0] TFI, data[1] CC */
+#define PN532_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
+
+/*
+ * Max extended frame payload len, excluding TFI and CC
+ * which are already in PN532_FRAME_HEADER_LEN.
+ */
+#define PN532_FRAME_MAX_PAYLOAD_LEN 263
+
+#define PN532_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2),
+				  Postamble (1) */
+#define PN532_FRAME_CHECKSUM(f) (f->data[f->datalen])
+#define PN532_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
+
+/* start of frame */
+#define PN532_SOF 0x00FF
+
+/* frame identifier: in/out/error */
+#define PN532_FRAME_IDENTIFIER(f) (f->data[0])
+#define PN532_DIR_OUT 0xD4
+#define PN532_DIR_IN 0xD5
+
+/* PN532 Commands */
+#define PN532_FRAME_CMD(f) (f->data[1])
+
+#define PN532_CMD_GET_FIRMWARE_VERSION 0x02
+#define PN532_CMD_SAM_CONFIGURATION 0x14
+#define PN532_CMD_RF_CONFIGURATION 0x32
+#define PN532_CMD_IN_DATA_EXCHANGE 0x40
+#define PN532_CMD_IN_COMM_THRU     0x42
+#define PN532_CMD_IN_LIST_PASSIVE_TARGET 0x4A
+#define PN532_CMD_IN_ATR 0x50
+#define PN532_CMD_IN_RELEASE 0x52
+#define PN532_CMD_IN_JUMP_FOR_DEP 0x56
+#define PN532_CMD_IN_AUTOPOLL 0x60
+
+#define PN532_CMD_TG_INIT_AS_TARGET 0x8c
+#define PN532_CMD_TG_GET_DATA 0x86
+#define PN532_CMD_TG_SET_DATA 0x8e
+#define PN532_CMD_UNDEF 0xff
+
+#define PN532_CMD_RESPONSE(cmd) (cmd + 1)
+
+/* PN532 Return codes */
+#define PN532_CMD_RET_MASK 0x3F
+#define PN532_CMD_MI_MASK 0x40
+#define PN532_CMD_RET_SUCCESS 0x00
+
+struct pn532;
+
+typedef int (*pn532_cmd_complete_t) (struct pn532 *dev, void *arg, int status);
+
+typedef int (*pn532_send_async_complete_t) (struct pn532 *dev, void *arg,
+					struct sk_buff *resp);
+
+/* structs for pn532 commands */
+
+/* PN532_CMD_GET_FIRMWARE_VERSION */
+struct pn532_fw_version {
+	u8 ic;
+	u8 ver;
+	u8 rev;
+	u8 support;
+};
+
+/* PN532_CMD_RF_CONFIGURATION */
+#define PN532_CFGITEM_TIMING 0x02
+#define PN532_CFGITEM_MAX_RETRIES 0x05
+#define PN532_CFGITEM_PASORI 0x82
+
+#define PN532_CONFIG_TIMING_102 0xb
+#define PN532_CONFIG_TIMING_204 0xc
+#define PN532_CONFIG_TIMING_409 0xd
+#define PN532_CONFIG_TIMING_819 0xe
+
+#define PN532_CONFIG_MAX_RETRIES_NO_RETRY 0x00
+#define PN532_CONFIG_MAX_RETRIES_ENDLESS 0xFF
+
+struct pn532_config_max_retries {
+	u8 mx_rty_atr;
+	u8 mx_rty_psl;
+	u8 mx_rty_passive_act;
+} __packed;
+
+struct pn532_config_timing {
+	u8 rfu;
+	u8 atr_res_timeout;
+	u8 dep_timeout;
+} __packed;
+
+/* PN532_CMD_IN_LIST_PASSIVE_TARGET */
+
+/* felica commands opcode */
+#define PN532_FELICA_OPC_SENSF_REQ 0
+#define PN532_FELICA_OPC_SENSF_RES 1
+/* felica SENSF_REQ parameters */
+#define PN532_FELICA_SENSF_SC_ALL 0xFFFF
+#define PN532_FELICA_SENSF_RC_NO_SYSTEM_CODE 0
+#define PN532_FELICA_SENSF_RC_SYSTEM_CODE 1
+#define PN532_FELICA_SENSF_RC_ADVANCED_PROTOCOL 2
+
+/* type B initiator_data values */
+#define PN532_TYPE_B_AFI_ALL_FAMILIES 0
+#define PN532_TYPE_B_POLL_METHOD_TIMESLOT 0
+#define PN532_TYPE_B_POLL_METHOD_PROBABILISTIC 1
+
+union pn532_cmd_poll_initdata {
+	struct {
+		u8 afi;
+		u8 polling_method;
+	} __packed type_b;
+	struct {
+		u8 opcode;
+		__be16 sc;
+		u8 rc;
+		u8 tsn;
+	} __packed felica;
+};
+
+/* Poll modulations */
+enum {
+	PN532_POLL_MOD_106KBPS_A,
+	PN532_POLL_MOD_212KBPS_FELICA,
+	PN532_POLL_MOD_424KBPS_FELICA,
+	PN532_POLL_MOD_106KBPS_JEWEL,
+	PN532_POLL_MOD_106KBPS_B,
+	PN532_LISTEN_MOD,
+
+	__PN532_POLL_MOD_AFTER_LAST,
+};
+#define PN532_POLL_MOD_MAX (__PN532_POLL_MOD_AFTER_LAST - 1)
+
+struct pn532_poll_modulations {
+	struct {
+		u8 maxtg;
+		u8 brty;
+		union pn532_cmd_poll_initdata initiator_data;
+	} __packed data;
+	u8 len;
+};
+
+static const struct pn532_poll_modulations poll_mod[] = {
+	[PN532_POLL_MOD_106KBPS_A] = {
+		.data = {
+			.maxtg = 1,
+			.brty = 0,
+		},
+		.len = 2,
+	},
+	[PN532_POLL_MOD_212KBPS_FELICA] = {
+		.data = {
+			.maxtg = 1,
+			.brty = 1,
+			.initiator_data.felica = {
+				.opcode = PN532_FELICA_OPC_SENSF_REQ,
+				.sc = PN532_FELICA_SENSF_SC_ALL,
+				.rc = PN532_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+				.tsn = 0,
+			},
+		},
+		.len = 7,
+	},
+	[PN532_POLL_MOD_424KBPS_FELICA] = {
+		.data = {
+			.maxtg = 1,
+			.brty = 2,
+			.initiator_data.felica = {
+				.opcode = PN532_FELICA_OPC_SENSF_REQ,
+				.sc = PN532_FELICA_SENSF_SC_ALL,
+				.rc = PN532_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+				.tsn = 0,
+			},
+		 },
+		.len = 7,
+	},
+	[PN532_POLL_MOD_106KBPS_JEWEL] = {
+		.data = {
+			.maxtg = 1,
+			.brty = 4,
+		},
+		.len = 2,
+	},
+	[PN532_POLL_MOD_106KBPS_B] = {
+		.data = {
+			.maxtg = 1,
+			.brty = 3,
+			.initiator_data.type_b = {
+				.afi = PN532_TYPE_B_AFI_ALL_FAMILIES,
+				.polling_method =
+					PN532_TYPE_B_POLL_METHOD_TIMESLOT,
+			},
+		},
+		.len = 3,
+	},
+	[PN532_LISTEN_MOD] = {
+		.len = 0,
+	},
+};
+
+/* PN532_CMD_IN_ATR */
+
+struct pn532_cmd_activate_response {
+	u8 status;
+	u8 nfcid3t[10];
+	u8 didt;
+	u8 bst;
+	u8 brt;
+	u8 to;
+	u8 ppt;
+	/* optional */
+	u8 gt[];
+} __packed;
+
+struct pn532_cmd_jump_dep_response {
+	u8 status;
+	u8 tg;
+	u8 nfcid3t[10];
+	u8 didt;
+	u8 bst;
+	u8 brt;
+	u8 to;
+	u8 ppt;
+	/* optional */
+	u8 gt[];
+} __packed;
+
+/* PN532_CMD_IN_AUTOPOLL */
+#define PN532_AUTOPOLL_POLLNR_INFINITE	0xff
+#define PN532_AUTOPOLL_PERIOD		0x03 /* in units of 150 ms */
+
+#define PN532_AUTOPOLL_TYPE_GENERIC_106		0x00
+#define PN532_AUTOPOLL_TYPE_GENERIC_212		0x01
+#define PN532_AUTOPOLL_TYPE_GENERIC_424		0x02
+#define PN532_AUTOPOLL_TYPE_JEWEL		0x04
+#define PN532_AUTOPOLL_TYPE_MIFARE		0x10
+#define PN532_AUTOPOLL_TYPE_FELICA212		0x11
+#define PN532_AUTOPOLL_TYPE_FELICA424		0x12
+#define PN532_AUTOPOLL_TYPE_ISOA		0x20
+#define PN532_AUTOPOLL_TYPE_ISOB		0x23
+#define PN532_AUTOPOLL_TYPE_DEP_ACTIVE_106	0x40
+#define PN532_AUTOPOLL_TYPE_DEP_ACTIVE_212	0x41
+#define PN532_AUTOPOLL_TYPE_DEP_ACTIVE_424	0x42
+#define PN532_AUTOPOLL_TYPE_DEP_PASSIVE_106	0x80
+#define PN532_AUTOPOLL_TYPE_DEP_PASSIVE_212	0x81
+#define PN532_AUTOPOLL_TYPE_DEP_PASSIVE_424	0x82
+
+/* PN532_TG_INIT_AS_TARGET */
+#define PN532_INIT_TARGET_PASSIVE 0x1
+#define PN532_INIT_TARGET_DEP 0x2
+
+#define PN532_INIT_TARGET_RESP_FRAME_MASK 0x3
+#define PN532_INIT_TARGET_RESP_ACTIVE     0x1
+#define PN532_INIT_TARGET_RESP_DEP        0x4
+
+#define PN532_MAGIC		0x162f
+
+struct pn532_frame {
+	u8 preamble;
+	__be16 start_frame;
+	u8 datalen;
+	u8 datalen_checksum;
+	u8 data[];
+} __packed;
+
+typedef int (*pn532_recv_complete_t) (struct pn532 *dev,
+					struct pn532_frame *frame);
+
+struct pn532 {
+	int magic;
+	struct tty_struct *tty;
+	struct nfc_dev *nfc_dev;
+
+	/* These are pointers to the malloc()ed frame buffers. */
+	/*
+	unsigned char		rbuff[PN532_FRAME_HEADER_LEN +
+				      PN532_FRAME_MAX_PAYLOAD_LEN +
+				      PN532_FRAME_TAIL_LEN
+				     ];	*//* receiver buffer	     */
+	unsigned char *rbuf;
+	int rlen; /* length of receive buffer */
+	int rcount; /* received chars counter    */
+	pn532_recv_complete_t recv_complete;
+
+	/*
+	unsigned char		xbuff[PN532_FRAME_HEADER_LEN +
+				      PN532_FRAME_MAX_PAYLOAD_LEN +
+				      PN532_FRAME_TAIL_LEN
+				     ];	*//* transmitter buffer	     */
+	unsigned char *xbuf;
+	unsigned char		*xhead;         /* pointer to next XMIT byte */
+	int			xleft;          /* bytes left in XMIT queue  */
+	unsigned	wakeup;
+
+	struct sk_buff_head resp_q;
+
+	struct workqueue_struct	*wq;
+	struct workqueue_struct	*wq_deactivate_tgt;
+	struct workqueue_struct	*wq_conn_nfc;
+	struct work_struct cmd_work;
+	struct work_struct cmd_complete_work;
+	struct work_struct poll_work;
+	struct work_struct mi_work;
+	struct work_struct tg_work;
+	struct work_struct conn_nfc_work;
+	struct work_struct deactivate_tgt_work;
+	struct timer_list listen_timer;
+	int wq_in_error;
+	int cancel_listen;
+
+	pn532_cmd_complete_t cmd_complete;
+	void *cmd_complete_arg;
+	void *cmd_complete_mi_arg;
+	struct mutex cmd_lock; /* Lock for modifying the cmd_pending, cmd,
+				  cmd_complete, cmd_complete_arg and setting
+				  the buffer of this struct */
+	u8 cmd;
+
+	struct pn532_poll_modulations *poll_mod_active[PN532_POLL_MOD_MAX + 1];
+	u8 poll_mod_count;
+	u8 poll_mod_curr;
+	u32 poll_protocols;
+	u32 listen_protocols;
+
+	u8 *gb;
+	size_t gb_len;
+
+	u8 tgt_available_prots;
+	u8 tgt_active_prot;
+	u8 tgt_mode;
+
+	u32 device_type;
+
+	struct list_head cmd_queue;
+	u8 cmd_pending;
+
+	struct pn532_frame_ops *ops;
+};
+
+struct pn532_cmd {
+	struct list_head queue;
+	u8 cmd_code;
+	struct sk_buff *req;
+	struct sk_buff *resp;
+	int resp_len;
+	void *arg;
+};
+
+struct pn532_frame_ops {
+	void (*tx_frame_init)(void *frame, u8 cmd_code);
+	void (*tx_frame_finish)(void *frame);
+	void (*tx_update_payload_len)(void *frame, int len);
+	int tx_header_len;
+	int tx_tail_len;
+
+	bool (*rx_is_frame_valid)(void *frame);
+	int (*rx_frame_size)(void *frame);
+	int rx_header_len;
+	int rx_tail_len;
+
+	int max_payload_len;
+	u8 (*get_cmd_code)(void *frame);
+};
+
+/* The rule: value + checksum = 0 */
+static inline u8 pn532_checksum(u8 value)
+{
+	return ~value + 1;
+}
+
+/* The rule: sum(data elements) + checksum = 0 */
+static u8 pn532_data_checksum(u8 *data, int datalen)
+{
+	u8 sum = 0;
+	int i;
+
+	for (i = 0; i < datalen; i++)
+		sum += data[i];
+
+	return pn532_checksum(sum);
+}
+
+static void pn532_tx_frame_init(void *_frame, u8 cmd_code)
+{
+	struct pn532_frame *frame = _frame;
+
+	frame->preamble = 0;
+	frame->start_frame = cpu_to_be16(PN532_SOF);
+	PN532_FRAME_IDENTIFIER(frame) = PN532_DIR_OUT;
+	PN532_FRAME_CMD(frame) = cmd_code;
+	frame->datalen = 2;
+}
+
+static void pn532_tx_frame_finish(void *_frame)
+{
+	struct pn532_frame *frame = _frame;
+
+	frame->datalen_checksum = pn532_checksum(frame->datalen);
+
+	PN532_FRAME_CHECKSUM(frame) =
+		pn532_data_checksum(frame->data, frame->datalen);
+
+	PN532_FRAME_POSTAMBLE(frame) = 0;
+}
+
+static void pn532_tx_update_payload_len(void *_frame, int len)
+{
+	struct pn532_frame *frame = _frame;
+
+	frame->datalen += len;
+}
+
+static bool pn532_rx_frame_is_valid(void *_frame)
+{
+	u8 checksum;
+	struct pn532_frame *frame = _frame;
+
+	if (frame->start_frame != cpu_to_be16(PN532_SOF))
+		return false;
+
+	checksum = pn532_checksum(frame->datalen);
+	if (checksum != frame->datalen_checksum)
+		return false;
+
+	checksum = pn532_data_checksum(frame->data, frame->datalen);
+	if (checksum != PN532_FRAME_CHECKSUM(frame))
+		return false;
+
+	return true;
+}
+
+static bool pn532_rx_frame_is_ack(struct pn532_frame *frame)
+{
+	if (frame->start_frame != cpu_to_be16(PN532_SOF))
+		return false;
+
+	if (frame->datalen != 0 || frame->datalen_checksum != 0xFF)
+		return false;
+
+	return true;
+}
+
+static inline int pn532_rx_frame_size(void *frame)
+{
+	struct pn532_frame *f = frame;
+
+	return sizeof(struct pn532_frame) + f->datalen + PN532_FRAME_TAIL_LEN;
+}
+
+static u8 pn532_get_cmd_code(void *frame)
+{
+	struct pn532_frame *f = frame;
+
+	return PN532_FRAME_CMD(f);
+}
+
+static struct pn532_frame_ops pn532_std_frame_ops = {
+	.tx_frame_init = pn532_tx_frame_init,
+	.tx_frame_finish = pn532_tx_frame_finish,
+	.tx_update_payload_len = pn532_tx_update_payload_len,
+	.tx_header_len = PN532_FRAME_HEADER_LEN,
+	.tx_tail_len = PN532_FRAME_TAIL_LEN,
+
+	.rx_is_frame_valid = pn532_rx_frame_is_valid,
+	.rx_frame_size = pn532_rx_frame_size,
+	.rx_header_len = PN532_FRAME_HEADER_LEN,
+	.rx_tail_len = PN532_FRAME_TAIL_LEN,
+
+	.max_payload_len =  PN532_FRAME_MAX_PAYLOAD_LEN,
+	.get_cmd_code = pn532_get_cmd_code,
+};
+
+static bool pn532_rx_frame_is_cmd_response(struct pn532 *dev, void *frame)
+{
+	return (dev->ops->get_cmd_code(frame) == PN532_CMD_RESPONSE(dev->cmd));
+}
+
+
+static void pn532_wq_cmd_complete(struct work_struct *work)
+{
+	struct pn532 *dev = container_of(work, struct pn532, cmd_complete_work);
+	int rc;
+
+	rc = dev->cmd_complete(dev, dev->cmd_complete_arg, dev->wq_in_error);
+
+	nfc_dev_dbg(dev->tty->dev, "%s rc:%i", __func__, rc);
+	if (rc != -EINPROGRESS)
+		queue_work(dev->wq, &dev->cmd_work);
+}
+
+static int pn532_recv_response(struct pn532 *dev, struct pn532_frame *frame)
+{
+	nfc_dev_dbg(dev->tty->dev, "%s Received a frame.", __func__);
+	print_hex_dump(KERN_DEBUG, "PN532 RX: ", DUMP_PREFIX_NONE, 16, 1,
+		       frame, dev->ops->rx_frame_size(frame), false);
+
+	if (!dev->ops->rx_is_frame_valid(frame)) {
+		nfc_dev_err(dev->tty->dev, "Received an invalid frame");
+		dev->wq_in_error = -EIO;
+		goto sched_wq;
+	}
+
+	if (!pn532_rx_frame_is_cmd_response(dev, frame)) {
+		nfc_dev_err(dev->tty->dev,
+			    "It it not the response to the last command");
+		dev->wq_in_error = -EIO;
+		goto sched_wq;
+	}
+
+	dev->wq_in_error = 0;
+	queue_work(dev->wq, &dev->cmd_complete_work);
+	return 0;
+
+sched_wq:
+	queue_work(dev->wq, &dev->cmd_complete_work);
+	return -1;
+}
+
+
+static int pn532_recv_ack(struct pn532 *dev, struct pn532_frame *frame)
+{
+	if (!pn532_rx_frame_is_ack(frame)) {
+		nfc_dev_err(dev->tty->dev, "Received an invalid ack");
+		dev->wq_in_error = -EIO;
+		goto sched_wq;
+	}
+
+	dev->recv_complete = pn532_recv_response;
+
+	return 0;
+
+sched_wq:
+	queue_work(dev->wq, &dev->cmd_complete_work);
+	return -1;
+}
+
+static int pn532_send_ack(struct pn532 *dev, gfp_t flags)
+{
+	u8 ack[PN532_FRAME_ACK_SIZE] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00};
+	/* spec 7.1.1.3:  Preamble, SoPC (2), ACK Code (2), Postamble */
+	int rc;
+
+	dev->xbuf = ack;
+
+	set_bit(TTY_DO_WRITE_WAKEUP, &dev->tty->flags);
+	rc = dev->tty->ops->write(dev->tty, dev->xbuf, sizeof(ack));
+	dev->xleft = sizeof(ack) - rc;
+	dev->xhead = dev->xbuf + rc;
+
+	return 0;
+}
+
+static int __pn532_send_frame_async(struct pn532 *dev,
+					struct sk_buff *out,
+					struct sk_buff *in,
+					int in_len,
+					pn532_cmd_complete_t cmd_complete,
+					void *arg)
+{
+	int count;
+	unsigned char wakeup[] = {0x55, 0x55, 0x00, 0x00, 0x00, 0x00};
+
+	nfc_dev_dbg(dev->tty->dev, "%s wakeup:%i", __func__, dev->wakeup);
+
+	dev->cmd = dev->ops->get_cmd_code(out->data);
+	dev->cmd_complete = cmd_complete;
+	dev->cmd_complete_arg = arg;
+
+	dev->xbuf = out->data;
+	dev->rbuf = in->data;
+	dev->rlen = in_len;
+
+	print_hex_dump(KERN_DEBUG, "PN532 TX: ", DUMP_PREFIX_NONE, 16, 1,
+		       out->data, out->len, false);
+
+	if (dev->wakeup) {
+		dev->tty->ops->write(dev->tty, wakeup, sizeof(wakeup));
+		dev->wakeup = 0;
+	}
+	set_bit(TTY_DO_WRITE_WAKEUP, &dev->tty->flags);
+	count = dev->tty->ops->write(dev->tty, dev->xbuf, out->len);
+	dev->xleft = out->len - count;
+	dev->xhead = dev->xbuf + count;
+
+	nfc_dev_dbg(dev->tty->dev, "count:%i xleft:%i", count, dev->xleft);
+
+	dev->recv_complete = pn532_recv_ack;
+
+	return 0;
+}
+
+static void pn532_build_cmd_frame(struct pn532 *dev, u8 cmd_code,
+				  struct sk_buff *skb)
+{
+	/* payload is already there, just update datalen */
+	int payload_len = skb->len;
+	struct pn532_frame_ops *ops = dev->ops;
+
+
+	skb_push(skb, ops->tx_header_len);
+	skb_put(skb, ops->tx_tail_len);
+
+	ops->tx_frame_init(skb->data, cmd_code);
+	ops->tx_update_payload_len(skb->data, payload_len);
+	ops->tx_frame_finish(skb->data);
+}
+
+struct pn532_send_async_complete_arg {
+	pn532_send_async_complete_t  complete_cb;
+	void *complete_cb_context;
+	struct sk_buff *resp;
+	struct sk_buff *req;
+};
+
+static int pn532_send_async_complete(struct pn532 *dev, void *_arg, int status)
+{
+	struct pn532_send_async_complete_arg *arg = _arg;
+
+	struct sk_buff *req = arg->req;
+	struct sk_buff *resp = arg->resp;
+
+	int rc;
+
+	dev_kfree_skb(req);
+
+	if (status < 0) {
+		nfc_dev_dbg(dev->tty->dev, "%s error way status < 0", __func__);
+		arg->complete_cb(dev, arg->complete_cb_context,
+				 ERR_PTR(status));
+		dev_kfree_skb(resp);
+		kfree(arg);
+		return status;
+	}
+
+	skb_put(resp, dev->ops->rx_frame_size(resp->data));
+	skb_pull(resp, dev->ops->rx_header_len);
+	skb_trim(resp, resp->len - dev->ops->rx_tail_len);
+
+	rc = arg->complete_cb(dev, arg->complete_cb_context, resp);
+
+	kfree(arg);
+	return rc;
+}
+
+static int __pn532_send_async(struct pn532 *dev, u8 cmd_code,
+			      struct sk_buff *req, struct sk_buff *resp,
+			      int resp_len,
+			      pn532_send_async_complete_t complete_cb,
+			      void *complete_cb_context)
+{
+	struct pn532_cmd *cmd;
+	struct pn532_send_async_complete_arg *arg;
+	int rc = 0;
+
+	nfc_dev_dbg(dev->tty->dev, "%s Sending command 0x%x",
+			__func__, cmd_code);
+
+	arg = kzalloc(sizeof(*arg), GFP_KERNEL);
+	if (!arg)
+		return -ENOMEM;
+
+	arg->complete_cb = complete_cb;
+	arg->complete_cb_context = complete_cb_context;
+	arg->resp = resp;
+	arg->req = req;
+
+	pn532_build_cmd_frame(dev, cmd_code, req);
+
+	mutex_lock(&dev->cmd_lock);
+
+	if (!dev->cmd_pending) {
+		rc = __pn532_send_frame_async(dev, req, resp, resp_len,
+					      pn532_send_async_complete, arg);
+		if (rc)
+			goto error;
+
+		dev->cmd_pending = 1;
+		goto unlock;
+	}
+
+	nfc_dev_dbg(dev->tty->dev, "%s Queuing command 0x%x", __func__,
+		    cmd_code);
+
+	cmd = kzalloc(sizeof(struct pn532_cmd), GFP_KERNEL);
+	if (!cmd) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	INIT_LIST_HEAD(&cmd->queue);
+	cmd->cmd_code = cmd_code;
+	cmd->req = req;
+	cmd->resp = resp;
+	cmd->resp_len = resp_len;
+	cmd->arg = arg;
+
+	list_add_tail(&cmd->queue, &dev->cmd_queue);
+
+	goto unlock;
+
+error:
+	kfree(arg);
+unlock:
+	mutex_unlock(&dev->cmd_lock);
+	return rc;
+}
+
+static int pn532_send_data_async(struct pn532 *dev, u8 cmd_code,
+				 struct sk_buff *req,
+				 pn532_send_async_complete_t complete_cb,
+				 void *complete_cb_context)
+{
+	struct sk_buff *resp;
+	int rc;
+	int  resp_len = dev->ops->rx_header_len +
+			dev->ops->max_payload_len +
+			dev->ops->rx_tail_len;
+
+	resp = nfc_alloc_recv_skb(resp_len, GFP_KERNEL);
+	if (!resp)
+		return -ENOMEM;
+
+	rc = __pn532_send_async(dev, cmd_code, req, resp, resp_len, complete_cb,
+				complete_cb_context);
+	if (rc)
+		dev_kfree_skb(resp);
+
+	return rc;
+}
+
+static int pn532_send_cmd_async(struct pn532 *dev, u8 cmd_code,
+				struct sk_buff *req,
+				pn532_send_async_complete_t complete_cb,
+				void *complete_cb_context)
+{
+	struct sk_buff *resp;
+	int rc;
+	int  resp_len = dev->ops->rx_header_len +
+			dev->ops->max_payload_len +
+			dev->ops->rx_tail_len;
+
+	resp = alloc_skb(resp_len, GFP_KERNEL);
+	if (!resp)
+		return -ENOMEM;
+
+	rc = __pn532_send_async(dev, cmd_code, req, resp, resp_len, complete_cb,
+				complete_cb_context);
+	if (rc)
+		dev_kfree_skb(resp);
+
+	return rc;
+}
+
+/*
+ * pn532_send_cmd_direct_async
+ *
+ * The function sends a piority cmd directly to the chip omiting the cmd
+ * queue. It's intended to be used by chaining mechanism of received responses
+ * where the host has to request every single chunk of data before scheduling
+ * next cmd from the queue.
+ */
+static int pn532_send_cmd_direct_async(struct pn532 *dev, u8 cmd_code,
+				       struct sk_buff *req,
+				       pn532_send_async_complete_t complete_cb,
+				       void *complete_cb_context)
+{
+	struct pn532_send_async_complete_arg *arg;
+	struct sk_buff *resp;
+	int rc;
+	int resp_len = dev->ops->rx_header_len +
+		       dev->ops->max_payload_len +
+		       dev->ops->rx_tail_len;
+
+	resp = alloc_skb(resp_len, GFP_KERNEL);
+	if (!resp)
+		return -ENOMEM;
+
+	arg = kzalloc(sizeof(*arg), GFP_KERNEL);
+	if (!arg) {
+		dev_kfree_skb(resp);
+		return -ENOMEM;
+	}
+
+	arg->complete_cb = complete_cb;
+	arg->complete_cb_context = complete_cb_context;
+	arg->resp = resp;
+	arg->req = req;
+
+	pn532_build_cmd_frame(dev, cmd_code, req);
+
+	rc = __pn532_send_frame_async(dev, req, resp, resp_len,
+				      pn532_send_async_complete, arg);
+	if (rc < 0) {
+		dev_kfree_skb(resp);
+		kfree(arg);
+	}
+
+	return rc;
+}
+
+static void pn532_wq_cmd(struct work_struct *work)
+{
+	struct pn532 *dev = container_of(work, struct pn532, cmd_work);
+	struct pn532_cmd *cmd;
+
+	mutex_lock(&dev->cmd_lock);
+
+	if (list_empty(&dev->cmd_queue)) {
+		dev->cmd_pending = 0;
+		mutex_unlock(&dev->cmd_lock);
+		return;
+	}
+
+	cmd = list_first_entry(&dev->cmd_queue, struct pn532_cmd, queue);
+
+	list_del(&cmd->queue);
+
+	mutex_unlock(&dev->cmd_lock);
+
+	__pn532_send_frame_async(dev, cmd->req, cmd->resp, cmd->resp_len,
+				 pn532_send_async_complete, cmd->arg);
+
+	kfree(cmd);
+}
+
+struct pn532_sync_cmd_response {
+	struct sk_buff *resp;
+	struct completion done;
+};
+
+static int pn532_send_sync_complete(struct pn532 *dev, void *_arg,
+				    struct sk_buff *resp)
+{
+	struct pn532_sync_cmd_response *arg = _arg;
+
+	nfc_dev_dbg(dev->tty->dev, "%s arg:%p resp:%p", __func__, arg, resp);
+
+	arg->resp = resp;
+	nfc_dev_dbg(dev->tty->dev, "%s done:%p", __func__, &arg->done);
+	complete(&arg->done);
+
+	return 0;
+}
+
+/*  pn532_send_cmd_sync
+ *
+ *  Please note the req parameter is freed inside the function to
+ *  limit a number of return value interpretations by the caller.
+ *
+ *  1. negative in case of error during TX path -> req should be freed
+ *
+ *  2. negative in case of error during RX path -> req should not be freed
+ *     as it's been already freed at the beginning of RX path by
+ *     async_complete_cb.
+ *
+ *  3. valid pointer in case of successful RX path
+ *
+ *  A caller has to check a return value with IS_ERR macro. If the test pass,
+ *  the returned pointer is valid.
+ *
+ * */
+static struct sk_buff *pn532_send_cmd_sync(struct pn532 *dev, u8 cmd_code,
+					       struct sk_buff *req)
+{
+	int rc;
+	struct pn532_sync_cmd_response arg;
+
+	init_completion(&arg.done);
+
+	rc = pn532_send_cmd_async(dev, cmd_code, req,
+				  pn532_send_sync_complete, &arg);
+	if (rc) {
+		dev_kfree_skb(req);
+		return ERR_PTR(rc);
+	}
+
+	/* TODO den Timeout richtig einstellen */
+	rc = wait_for_completion_timeout(&arg.done, HZ);
+	if (rc <= 0)
+		return ERR_PTR(-ETIMEDOUT);
+
+	return arg.resp;
+}
+
+/*
+ * Allocates a skb and reserves space for the tx_header
+ */
+static struct sk_buff *pn532_alloc_skb(struct pn532 *dev, unsigned int size)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(dev->ops->tx_header_len +
+			size +
+			dev->ops->tx_tail_len, GFP_KERNEL);
+
+	if (skb)
+		skb_reserve(skb, dev->ops->tx_header_len);
+
+	return skb;
+}
+
+struct pn532_target_type_a {
+	__be16 sens_res;
+	u8 sel_res;
+	u8 nfcid_len;
+	u8 nfcid_data[];
+} __packed;
+
+
+#define PN532_TYPE_A_SENS_RES_NFCID1(x) ((u8)((be16_to_cpu(x) & 0x00C0) >> 6))
+#define PN532_TYPE_A_SENS_RES_SSD(x) ((u8)((be16_to_cpu(x) & 0x001F) >> 0))
+#define PN532_TYPE_A_SENS_RES_PLATCONF(x) ((u8)((be16_to_cpu(x) & 0x0F00) >> 8))
+
+#define PN532_TYPE_A_SENS_RES_SSD_JEWEL 0x00
+#define PN532_TYPE_A_SENS_RES_PLATCONF_JEWEL 0x0C
+
+#define PN532_TYPE_A_SEL_PROT(x) (((x) & 0x60) >> 5)
+#define PN532_TYPE_A_SEL_CASCADE(x) (((x) & 0x04) >> 2)
+
+#define PN532_TYPE_A_SEL_PROT_MIFARE 0
+#define PN532_TYPE_A_SEL_PROT_ISO14443 1
+#define PN532_TYPE_A_SEL_PROT_DEP 2
+#define PN532_TYPE_A_SEL_PROT_ISO14443_DEP 3
+
+static bool pn532_target_type_a_is_valid(struct pn532_target_type_a *type_a,
+							int target_data_len)
+{
+	u8 ssd;
+	u8 platconf;
+
+	if (target_data_len < sizeof(struct pn532_target_type_a))
+		return false;
+
+	/* The lenght check of nfcid[] and ats[] are not being performed because
+	   the values are not being used */
+
+	/* Requirement 4.6.3.3 from NFC Forum Digital Spec */
+	ssd = PN532_TYPE_A_SENS_RES_SSD(type_a->sens_res);
+	platconf = PN532_TYPE_A_SENS_RES_PLATCONF(type_a->sens_res);
+
+	if ((ssd == PN532_TYPE_A_SENS_RES_SSD_JEWEL &&
+	     platconf != PN532_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+	    (ssd != PN532_TYPE_A_SENS_RES_SSD_JEWEL &&
+	     platconf == PN532_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+		return false;
+
+	/* Requirements 4.8.2.1, 4.8.2.3, 4.8.2.5 and 4.8.2.7 from NFC Forum */
+	if (PN532_TYPE_A_SEL_CASCADE(type_a->sel_res) != 0)
+		return false;
+
+	return true;
+}
+
+static int pn532_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
+							int tgt_data_len)
+{
+	struct pn532_target_type_a *tgt_type_a;
+
+	tgt_type_a = (struct pn532_target_type_a *)tgt_data;
+
+	if (!pn532_target_type_a_is_valid(tgt_type_a, tgt_data_len))
+		return -EPROTO;
+
+	switch (PN532_TYPE_A_SEL_PROT(tgt_type_a->sel_res)) {
+	case PN532_TYPE_A_SEL_PROT_MIFARE:
+		pr_debug("mifare");
+		nfc_tgt->supported_protocols = NFC_PROTO_MIFARE_MASK;
+		break;
+	case PN532_TYPE_A_SEL_PROT_ISO14443:
+		pr_debug("iso14443");
+		nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK;
+		break;
+	case PN532_TYPE_A_SEL_PROT_DEP:
+		pr_debug("dep");
+		nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+		break;
+	case PN532_TYPE_A_SEL_PROT_ISO14443_DEP:
+		pr_debug("iso dep");
+		nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK |
+							NFC_PROTO_NFC_DEP_MASK;
+		break;
+	}
+
+	nfc_tgt->sens_res = be16_to_cpu(tgt_type_a->sens_res);
+	nfc_tgt->sel_res = tgt_type_a->sel_res;
+	nfc_tgt->nfcid1_len = tgt_type_a->nfcid_len;
+	memcpy(nfc_tgt->nfcid1, tgt_type_a->nfcid_data, nfc_tgt->nfcid1_len);
+
+	return 0;
+}
+
+struct pn532_target_felica {
+	u8 pol_res;
+	u8 opcode;
+	u8 nfcid2[8];
+	u8 pad[8];
+	/* optional */
+	u8 syst_code[];
+} __packed;
+
+#define PN532_FELICA_SENSF_NFCID2_DEP_B1 0x01
+#define PN532_FELICA_SENSF_NFCID2_DEP_B2 0xFE
+
+static bool pn532_target_felica_is_valid(struct pn532_target_felica *felica,
+							int target_data_len)
+{
+	if (target_data_len < sizeof(struct pn532_target_felica))
+		return false;
+
+	if (felica->opcode != PN532_FELICA_OPC_SENSF_RES)
+		return false;
+
+	return true;
+}
+
+static int pn532_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
+							int tgt_data_len)
+{
+	struct pn532_target_felica *tgt_felica;
+
+	tgt_felica = (struct pn532_target_felica *)tgt_data;
+
+	if (!pn532_target_felica_is_valid(tgt_felica, tgt_data_len))
+		return -EPROTO;
+
+	if ((tgt_felica->nfcid2[0] == PN532_FELICA_SENSF_NFCID2_DEP_B1) &&
+	    (tgt_felica->nfcid2[1] == PN532_FELICA_SENSF_NFCID2_DEP_B2))
+		nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+	else
+		nfc_tgt->supported_protocols = NFC_PROTO_FELICA_MASK;
+
+	memcpy(nfc_tgt->sensf_res, &tgt_felica->opcode, 9);
+	nfc_tgt->sensf_res_len = 9;
+
+	return 0;
+}
+
+struct pn532_target_jewel {
+	__be16 sens_res;
+	u8 jewelid[4];
+} __packed;
+
+static bool pn532_target_jewel_is_valid(struct pn532_target_jewel *jewel,
+							int target_data_len)
+{
+	u8 ssd;
+	u8 platconf;
+
+	if (target_data_len < sizeof(struct pn532_target_jewel))
+		return false;
+
+	/* Requirement 4.6.3.3 from NFC Forum Digital Spec */
+	ssd = PN532_TYPE_A_SENS_RES_SSD(jewel->sens_res);
+	platconf = PN532_TYPE_A_SENS_RES_PLATCONF(jewel->sens_res);
+
+	if ((ssd == PN532_TYPE_A_SENS_RES_SSD_JEWEL &&
+	     platconf != PN532_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+	    (ssd != PN532_TYPE_A_SENS_RES_SSD_JEWEL &&
+	     platconf == PN532_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+		return false;
+
+	return true;
+}
+
+static int pn532_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data,
+							int tgt_data_len)
+{
+	struct pn532_target_jewel *tgt_jewel;
+
+	tgt_jewel = (struct pn532_target_jewel *)tgt_data;
+
+	if (!pn532_target_jewel_is_valid(tgt_jewel, tgt_data_len))
+		return -EPROTO;
+
+	nfc_tgt->supported_protocols = NFC_PROTO_JEWEL_MASK;
+	nfc_tgt->sens_res = be16_to_cpu(tgt_jewel->sens_res);
+	nfc_tgt->nfcid1_len = 4;
+	memcpy(nfc_tgt->nfcid1, tgt_jewel->jewelid, nfc_tgt->nfcid1_len);
+
+	return 0;
+}
+
+struct pn532_type_b_prot_info {
+	u8 bitrate;
+	u8 fsci_type;
+	u8 fwi_adc_fo;
+} __packed;
+
+#define PN532_TYPE_B_PROT_FCSI(x) (((x) & 0xF0) >> 4)
+#define PN532_TYPE_B_PROT_TYPE(x) (((x) & 0x0F) >> 0)
+#define PN532_TYPE_B_PROT_TYPE_RFU_MASK 0x8
+
+struct pn532_type_b_sens_res {
+	u8 opcode;
+	u8 nfcid[4];
+	u8 appdata[4];
+	struct pn532_type_b_prot_info prot_info;
+} __packed;
+
+#define PN532_TYPE_B_OPC_SENSB_RES 0x50
+
+struct pn532_target_type_b {
+	struct pn532_type_b_sens_res sensb_res;
+	u8 attrib_res_len;
+	u8 attrib_res[];
+} __packed;
+
+static bool pn532_target_type_b_is_valid(struct pn532_target_type_b *type_b,
+							int target_data_len)
+{
+	if (target_data_len < sizeof(struct pn532_target_type_b))
+		return false;
+
+	if (type_b->sensb_res.opcode != PN532_TYPE_B_OPC_SENSB_RES)
+		return false;
+
+	if (PN532_TYPE_B_PROT_TYPE(type_b->sensb_res.prot_info.fsci_type) &
+						PN532_TYPE_B_PROT_TYPE_RFU_MASK)
+		return false;
+
+	return true;
+}
+
+static int pn532_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data,
+							int tgt_data_len)
+{
+	struct pn532_target_type_b *tgt_type_b;
+
+	tgt_type_b = (struct pn532_target_type_b *)tgt_data;
+
+	if (!pn532_target_type_b_is_valid(tgt_type_b, tgt_data_len))
+		return -EPROTO;
+
+	nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_B_MASK;
+
+	return 0;
+}
+
+static inline void pn532_poll_next_mod(struct pn532 *dev)
+{
+	dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count;
+}
+
+static void pn532_poll_reset_mod_list(struct pn532 *dev)
+{
+	dev->poll_mod_count = 0;
+}
+
+struct pn532_autopoll_resp {
+	u8 type;
+	u8 ln;
+	u8 tg;
+	u8 tgdata[];
+};
+
+static int pn532_start_poll_complete(struct pn532 *dev, struct sk_buff *resp)
+{
+	u8 nbtg;
+	int rc;
+	struct pn532_autopoll_resp *pres;
+	struct nfc_target *nfc_tgt;
+
+	nbtg = resp->data[0];
+	if ((nbtg > 2) || (nbtg <= 0))
+		return -EAGAIN;
+
+	nfc_tgt = kzalloc(nbtg * sizeof(struct nfc_target), GFP_KERNEL);
+	if (!nfc_tgt)
+		return -ENOMEM;
+
+	pres = (struct pn532_autopoll_resp *)&resp->data[1];
+
+	while (nbtg--) {
+		nfc_dev_dbg(dev->tty->dev, "%s -type=0x%x tg=%d nbtg=%d ln=%d",
+			__func__, pres->type, pres->tg, nbtg, pres->ln);
+
+		if (pres->tg != 1)
+			return -EPROTO;
+
+		switch (pres->type) {
+		case PN532_AUTOPOLL_TYPE_ISOA:
+			nfc_dev_dbg(dev->tty->dev, "ISOA");
+			rc = pn532_target_found_type_a(nfc_tgt, pres->tgdata,
+						       pres->ln - 1);
+			break;
+		case PN532_AUTOPOLL_TYPE_FELICA212:
+		case PN532_AUTOPOLL_TYPE_FELICA424:
+			nfc_dev_dbg(dev->tty->dev, "FELICA");
+			rc = pn532_target_found_felica(nfc_tgt, pres->tgdata,
+						       pres->ln - 1);
+			break;
+		case PN532_AUTOPOLL_TYPE_JEWEL:
+			nfc_dev_dbg(dev->tty->dev, "JEWEL");
+			rc = pn532_target_found_jewel(nfc_tgt, pres->tgdata,
+						      pres->ln - 1);
+			break;
+		case PN532_AUTOPOLL_TYPE_ISOB:
+			nfc_dev_dbg(dev->tty->dev, "ISOB");
+			rc = pn532_target_found_type_b(nfc_tgt, pres->tgdata,
+						       pres->ln - 1);
+			break;
+		default:
+			nfc_dev_dbg(dev->tty->dev, "default");
+			nfc_dev_err(dev->tty->dev,
+				    "Unknown current poll modulation");
+			return -EPROTO;
+		}
+
+		nfc_dev_dbg(dev->tty->dev, "%s rc:%d", __func__, rc);
+
+		if (rc)
+			return rc;
+
+		if (!(nfc_tgt->supported_protocols & dev->poll_protocols)) {
+			nfc_dev_dbg(dev->tty->dev,
+				    "The Tg found doesn't have the desired protocol");
+			return -EAGAIN;
+		}
+
+		nfc_dev_dbg(dev->tty->dev,
+			    "Target found - supported protocols: 0x%x",
+			    nfc_tgt->supported_protocols);
+
+		dev->tgt_available_prots = nfc_tgt->supported_protocols;
+		pres = (struct pn532_autopoll_resp *)
+					(pres->tgdata + (pres->ln));
+	}
+	nfc_targets_found(dev->nfc_dev, nfc_tgt, resp->data[0]);
+
+	kfree(nfc_tgt);
+	return 0;
+}
+
+static struct sk_buff *pn532_alloc_poll_tg_frame(struct pn532 *dev)
+{
+	struct sk_buff *skb;
+	u8 *felica, *nfcid3, *gb;
+
+	u8 *gbytes = dev->gb;
+	size_t gbytes_len = dev->gb_len;
+
+	u8 felica_params[18] = {0x1, 0xfe, /* DEP */
+				0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */
+				0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+				0xff, 0xff}; /* System code */
+
+	u8 mifare_params[6] = {0x1, 0x1, /* SENS_RES */
+			       0x0, 0x0, 0x0,
+			       0x40}; /* SEL_RES for DEP */
+
+	unsigned int skb_len = 36 + /* mode (1), mifare (6),
+				       felica (18), nfcid3 (10), gb_len (1) */
+			       gbytes_len +
+			       1;  /* len Tk*/
+
+	skb = pn532_alloc_skb(dev, skb_len);
+	if (!skb)
+		return NULL;
+
+	/* DEP support only */
+	*skb_put(skb, 1) = PN532_INIT_TARGET_DEP;
+
+	/* MIFARE params */
+	memcpy(skb_put(skb, 6), mifare_params, 6);
+
+	/* Felica params */
+	felica = skb_put(skb, 18);
+	memcpy(felica, felica_params, 18);
+	get_random_bytes(felica + 2, 6);
+
+	/* NFCID3 */
+	nfcid3 = skb_put(skb, 10);
+	memset(nfcid3, 0, 10);
+	memcpy(nfcid3, felica, 8);
+
+	/* General bytes */
+	*skb_put(skb, 1) = gbytes_len;
+
+	gb = skb_put(skb, gbytes_len);
+	memcpy(gb, gbytes, gbytes_len);
+
+	/* Len Tk */
+	*skb_put(skb, 1) = 0;
+
+	return skb;
+}
+
+#define PN532_CMD_DATAEXCH_HEAD_LEN 1
+#define PN532_CMD_DATAEXCH_DATA_MAXLEN 262
+static int pn532_tm_get_data_complete(struct pn532 *dev, void *arg,
+				      struct sk_buff *resp)
+{
+	u8 status;
+
+	if (IS_ERR(resp))
+		return PTR_ERR(resp);
+
+	status = resp->data[0];
+	skb_pull(resp, sizeof(status));
+
+	if (status != 0) {
+		nfc_tm_deactivated(dev->nfc_dev);
+		dev->tgt_mode = 0;
+		dev_kfree_skb(resp);
+		return 0;
+	}
+
+	return nfc_tm_data_received(dev->nfc_dev, resp);
+}
+
+static void pn532_wq_tg_get_data(struct work_struct *work)
+{
+	struct pn532 *dev = container_of(work, struct pn532, tg_work);
+
+	struct sk_buff *skb;
+	int rc;
+
+	skb = pn532_alloc_skb(dev, 0);
+	if (!skb)
+		return;
+
+	rc = pn532_send_data_async(dev, PN532_CMD_TG_GET_DATA, skb,
+				   pn532_tm_get_data_complete, NULL);
+
+	if (rc < 0)
+		dev_kfree_skb(skb);
+
+	return;
+}
+
+static void pn532_listen_mode_timer(unsigned long data)
+{
+	struct pn532 *dev = (struct pn532 *)data;
+
+	nfc_dev_dbg(dev->tty->dev, "Listen mode timeout");
+
+	/* An ack will cancel the last issued command (poll) */
+	pn532_send_ack(dev, GFP_ATOMIC);
+
+	dev->cancel_listen = 1;
+
+	pn532_poll_next_mod(dev);
+
+	queue_work(dev->wq, &dev->poll_work);
+}
+
+static int pn532_poll_complete(struct pn532 *dev, void *arg,
+			       struct sk_buff *resp)
+{
+	int rc;
+
+	nfc_dev_dbg(dev->tty->dev, "%s", __func__);
+
+	if (IS_ERR(resp)) {
+		rc = PTR_ERR(resp);
+
+		nfc_dev_err(dev->tty->dev, "%s  Poll complete error %d",
+			    __func__, rc);
+
+		if (rc == -ENOENT) {
+			if (dev->poll_mod_count != 0)
+				return rc;
+			else
+				goto stop_poll;
+		} else if (rc < 0) {
+			nfc_dev_err(dev->tty->dev,
+				    "Error %d when running poll", rc);
+			goto stop_poll;
+		}
+	}
+
+	rc = pn532_start_poll_complete(dev, resp);
+	if (!rc)
+		goto done;
+
+done:
+	dev_kfree_skb(resp);
+	return rc;
+
+stop_poll:
+	nfc_dev_err(dev->tty->dev, "Polling operation has been stopped");
+
+	pn532_poll_reset_mod_list(dev);
+	dev->poll_protocols = 0;
+	return rc;
+}
+
+static struct sk_buff *pn532_alloc_poll_in_frame(struct pn532 *dev,
+					struct pn532_poll_modulations *mod)
+{
+	struct sk_buff *skb;
+
+	skb = pn532_alloc_skb(dev, mod->len);
+	if (!skb)
+		return NULL;
+
+	memcpy(skb_put(skb, mod->len), &mod->data, mod->len);
+
+	return skb;
+}
+
+static int pn532_send_poll_frame(struct pn532 *dev)
+{
+	struct pn532_poll_modulations *mod;
+	struct sk_buff *skb;
+	int rc;
+	u8 cmd_code;
+
+	mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+	nfc_dev_dbg(dev->tty->dev, "%s mod len %d\n",
+		    __func__, mod->len);
+
+	if (mod->len == 0) {  /* Listen mode */
+		cmd_code = PN532_CMD_TG_INIT_AS_TARGET;
+		skb = pn532_alloc_poll_tg_frame(dev);
+	} else {  /* Polling mode */
+		cmd_code =  PN532_CMD_IN_AUTOPOLL;
+		skb = pn532_alloc_poll_in_frame(dev, mod);
+	}
+
+	if (!skb) {
+		nfc_dev_err(dev->tty->dev, "Failed to allocate skb.");
+		return -ENOMEM;
+	}
+
+	rc = pn532_send_cmd_async(dev, cmd_code, skb, pn532_poll_complete,
+				  NULL);
+	if (rc < 0) {
+		dev_kfree_skb(skb);
+		nfc_dev_err(dev->tty->dev, "Polling loop error %d", rc);
+	}
+
+	return rc;
+}
+
+static void pn532_wq_poll(struct work_struct *work)
+{
+	struct pn532 *dev = container_of(work, struct pn532, poll_work);
+	struct pn532_poll_modulations *cur_mod;
+	int rc;
+
+	cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+	nfc_dev_dbg(dev->tty->dev,
+		    "%s cancel_listen %d modulation len %d",
+		    __func__, dev->cancel_listen, cur_mod->len);
+
+	if (dev->cancel_listen == 1) {
+		dev->cancel_listen = 0;
+		/*
+		usb_kill_urb(dev->in_urb);
+		*/
+	}
+
+	rc = pn532_send_poll_frame(dev);
+	if (rc)
+		return;
+
+	if (cur_mod->len == 0 && dev->poll_mod_count > 1)
+		mod_timer(&dev->listen_timer, jiffies + PN532_LISTEN_TIME * HZ);
+
+	return;
+}
+
+static int pn532_start_auto_poll(struct nfc_dev *nfc_dev,
+			    u32 im_protocols, u32 tm_protocols)
+{
+	struct pn532 *dev = nfc_get_drvdata(nfc_dev);
+	u8	rc;
+	struct sk_buff *skb;
+
+	nfc_dev_dbg(dev->tty->dev,
+		    "%s: im protocols 0x%x tm protocols 0x%x",
+		    __func__, im_protocols, tm_protocols);
+
+	if (dev->tgt_active_prot) {
+		nfc_dev_err(dev->tty->dev,
+			    "Cannot poll with a target already activated");
+		return -EBUSY;
+	}
+
+	if (dev->tgt_mode) {
+		nfc_dev_err(dev->tty->dev,
+			    "Cannot poll while already being activated");
+		return -EBUSY;
+	}
+
+	if (tm_protocols) {
+		dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
+		if (dev->gb == NULL)
+			tm_protocols = 0;
+	}
+
+	skb = pn532_alloc_skb(dev, 4 + 6);
+	if (!skb) {
+		nfc_dev_err(dev->tty->dev, "Failed to allocate skb.");
+		return -ENOMEM;
+	}
+
+	*skb_put(skb, 1) = PN532_AUTOPOLL_POLLNR_INFINITE;
+	*skb_put(skb, 1) = PN532_AUTOPOLL_PERIOD;
+
+	if ((im_protocols & NFC_PROTO_MIFARE_MASK) ||
+	    (im_protocols & NFC_PROTO_ISO14443_MASK) ||
+	    (im_protocols & NFC_PROTO_NFC_DEP_MASK))
+		*skb_put(skb, 1) = PN532_AUTOPOLL_TYPE_ISOA;
+
+	if (im_protocols & NFC_PROTO_FELICA_MASK ||
+	    im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+		*skb_put(skb, 1) = PN532_AUTOPOLL_TYPE_FELICA212;
+		*skb_put(skb, 1) = PN532_AUTOPOLL_TYPE_FELICA424;
+	}
+
+	if (im_protocols & NFC_PROTO_JEWEL_MASK)
+		*skb_put(skb, 1) = PN532_AUTOPOLL_TYPE_JEWEL;
+
+	if (im_protocols & NFC_PROTO_ISO14443_B_MASK)
+		*skb_put(skb, 1) = PN532_AUTOPOLL_TYPE_ISOB;
+
+	if (tm_protocols)
+		*skb_put(skb, 1) = PN532_AUTOPOLL_TYPE_DEP_PASSIVE_106;
+
+	rc = pn532_send_cmd_async(dev, PN532_CMD_IN_AUTOPOLL, skb,
+				  pn532_poll_complete, NULL);
+	if (rc < 0) {
+		dev_kfree_skb(skb);
+		nfc_dev_err(dev->tty->dev, "Error setting up autopoll %d", rc);
+	}
+	dev->poll_protocols = im_protocols;
+	dev->listen_protocols = tm_protocols;
+	return rc;
+}
+
+static void pn532_stop_poll(struct nfc_dev *nfc_dev)
+{
+	struct pn532 *dev = nfc_get_drvdata(nfc_dev);
+
+	del_timer(&dev->listen_timer);
+
+	if (!dev->poll_mod_count) {
+		nfc_dev_dbg(dev->tty->dev,
+			    "Polling operation was not running");
+		return;
+	}
+
+	/* An ack will cancel the last issued command (poll) */
+	pn532_send_ack(dev, GFP_KERNEL);
+
+	/* prevent pn532_start_poll_complete to issue a new poll meanwhile */
+	/*
+	dev->rbuf = NULL;
+	*/
+
+	pn532_poll_reset_mod_list(dev);
+}
+
+static int pn532_activate_target_nfcdep(struct pn532 *dev)
+{
+	struct pn532_cmd_activate_response *rsp;
+	u16 gt_len;
+	int rc;
+
+	struct sk_buff *skb;
+	struct sk_buff *resp;
+
+	skb = pn532_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/
+	if (!skb)
+		return -ENOMEM;
+
+	*skb_put(skb, sizeof(u8)) = 1; /* TG */
+	*skb_put(skb, sizeof(u8)) = 0; /* Next */
+
+	resp = pn532_send_cmd_sync(dev, PN532_CMD_IN_ATR, skb);
+	if (IS_ERR(resp))
+		return PTR_ERR(resp);
+
+	rsp = (struct pn532_cmd_activate_response *)resp->data;
+	rc = rsp->status & PN532_CMD_RET_MASK;
+	if (rc != PN532_CMD_RET_SUCCESS) {
+		dev_kfree_skb(resp);
+		return -EIO;
+	}
+
+	/* ATR_RES general bytes are located at offset 16 */
+	gt_len = resp->len - 16;
+	rc = nfc_set_remote_general_bytes(dev->nfc_dev, rsp->gt, gt_len);
+
+	dev_kfree_skb(resp);
+	return rc;
+}
+
+static int pn532_activate_target(struct nfc_dev *nfc_dev,
+				 struct nfc_target *target, u32 protocol)
+{
+	struct pn532 *dev = nfc_get_drvdata(nfc_dev);
+	int rc;
+
+	nfc_dev_dbg(dev->tty->dev, "%s - protocol=%u", __func__,
+		    protocol);
+
+	if (dev->poll_mod_count) {
+		nfc_dev_err(dev->tty->dev,
+			    "Cannot activate while polling");
+		return -EBUSY;
+	}
+
+	if (dev->tgt_active_prot) {
+		nfc_dev_err(dev->tty->dev,
+			    "There is already an active target");
+		return -EBUSY;
+	}
+
+	if (!dev->tgt_available_prots) {
+		nfc_dev_err(dev->tty->dev,
+			    "There is no available target to activate");
+		return -EINVAL;
+	}
+
+	if (!(dev->tgt_available_prots & (1 << protocol))) {
+		nfc_dev_err(dev->tty->dev,
+			    "Target doesn't support requested proto %u",
+			    protocol);
+		return -EINVAL;
+	}
+
+	if (protocol == NFC_PROTO_NFC_DEP) {
+		rc = pn532_activate_target_nfcdep(dev);
+		if (rc) {
+			nfc_dev_err(dev->tty->dev,
+				    "Activating target with DEP failed %d", rc);
+			return rc;
+		}
+	}
+
+	dev->tgt_active_prot = protocol;
+	dev->tgt_available_prots = 0;
+
+	return 0;
+}
+
+static void pn532_wq_deactivate_tgt(struct work_struct *work)
+{
+	struct pn532 *dev =
+		container_of(work, struct pn532, deactivate_tgt_work);
+	struct sk_buff *skb;
+	struct sk_buff *resp;
+
+	int rc;
+
+	if (!dev->tgt_active_prot) {
+		nfc_dev_err(dev->tty->dev, "There is no active target");
+		return;
+	}
+
+	dev->tgt_active_prot = 0;
+	skb_queue_purge(&dev->resp_q);
+
+	skb = pn532_alloc_skb(dev, sizeof(u8));
+	if (!skb)
+		return;
+
+	*skb_put(skb, 1) = 1; /* TG*/
+
+	resp = pn532_send_cmd_sync(dev, PN532_CMD_IN_RELEASE, skb);
+	if (IS_ERR(resp))
+		return;
+
+	rc = resp->data[0] & PN532_CMD_RET_MASK;
+	if (rc != PN532_CMD_RET_SUCCESS)
+		nfc_dev_err(dev->tty->dev,
+			    "Error 0x%x when releasing the target", rc);
+
+	dev_kfree_skb(resp);
+	return;
+}
+
+static void pn532_deactivate_target(struct nfc_dev *nfc_dev,
+				    struct nfc_target *target)
+{
+	struct pn532 *dev = nfc_get_drvdata(nfc_dev);
+
+	queue_work(dev->wq_deactivate_tgt, &dev->deactivate_tgt_work);
+}
+
+
+static int pn532_in_dep_link_up_complete(struct pn532 *dev, void *arg,
+					 struct sk_buff *resp)
+{
+	struct pn532_cmd_jump_dep_response *rsp;
+	u8 target_gt_len;
+	int rc;
+	u8 active = *(u8 *)arg;
+
+	kfree(arg);
+
+	if (IS_ERR(resp))
+		return PTR_ERR(resp);
+
+	if (dev->tgt_available_prots &&
+	    !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) {
+		nfc_dev_err(dev->tty->dev,
+			    "The target does not support DEP");
+		rc =  -EINVAL;
+		goto error;
+	}
+
+	rsp = (struct pn532_cmd_jump_dep_response *)resp->data;
+
+	rc = rsp->status & PN532_CMD_RET_MASK;
+	if (rc != PN532_CMD_RET_SUCCESS) {
+		nfc_dev_err(dev->tty->dev,
+			    "Bringing DEP link up failed %d", rc);
+		goto error;
+	}
+
+	if (!dev->tgt_available_prots) {
+		struct nfc_target nfc_target;
+
+		nfc_dev_dbg(dev->tty->dev, "Creating new target");
+
+		nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+		nfc_target.nfcid1_len = 10;
+		memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len);
+		rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1);
+		if (rc)
+			goto error;
+
+		dev->tgt_available_prots = 0;
+	}
+
+	dev->tgt_active_prot = NFC_PROTO_NFC_DEP;
+
+	/* ATR_RES general bytes are located at offset 17 */
+	target_gt_len = resp->len - 17;
+	rc = nfc_set_remote_general_bytes(dev->nfc_dev,
+					  rsp->gt, target_gt_len);
+	if (rc == 0)
+		rc = nfc_dep_link_is_up(dev->nfc_dev,
+					dev->nfc_dev->targets[0].idx,
+					!active, NFC_RF_INITIATOR);
+
+error:
+	dev_kfree_skb(resp);
+	return rc;
+}
+
+static int pn532_mod_to_baud(struct pn532 *dev)
+{
+	switch (dev->poll_mod_curr) {
+	case PN532_POLL_MOD_106KBPS_A:
+		return 0;
+	case PN532_POLL_MOD_212KBPS_FELICA:
+		return 1;
+	case PN532_POLL_MOD_424KBPS_FELICA:
+		return 2;
+	default:
+		return -EINVAL;
+	}
+}
+
+#define PASSIVE_DATA_LEN 5
+static int pn532_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
+			     u8 comm_mode, u8 *gb, size_t gb_len)
+{
+	struct pn532 *dev = nfc_get_drvdata(nfc_dev);
+	struct sk_buff *skb;
+	int rc, baud, skb_len;
+	u8 *next, *arg;
+
+	u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
+
+	if (dev->poll_mod_count) {
+		nfc_dev_err(dev->tty->dev,
+			    "Cannot bring the DEP link up while polling");
+		return -EBUSY;
+	}
+
+	if (dev->tgt_active_prot) {
+		nfc_dev_err(dev->tty->dev,
+			    "There is already an active target");
+		return -EBUSY;
+	}
+
+	baud = pn532_mod_to_baud(dev);
+	if (baud < 0) {
+		nfc_dev_err(dev->tty->dev,
+			    "Invalid curr modulation %d", dev->poll_mod_curr);
+		return baud;
+	}
+
+	skb_len = 3 + gb_len; /* ActPass + BR + Next */
+	if (comm_mode == NFC_COMM_PASSIVE)
+		skb_len += PASSIVE_DATA_LEN;
+
+	skb = pn532_alloc_skb(dev, skb_len);
+	if (!skb)
+		return -ENOMEM;
+
+	*skb_put(skb, 1) = !comm_mode;  /* ActPass */
+	*skb_put(skb, 1) = baud;  /* Baud rate */
+
+	next = skb_put(skb, 1);  /* Next */
+	*next = 0;
+
+	if (comm_mode == NFC_COMM_PASSIVE && baud > 0) {
+		memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data,
+		       PASSIVE_DATA_LEN);
+		*next |= 1;
+	}
+
+	if (gb != NULL && gb_len > 0) {
+		memcpy(skb_put(skb, gb_len), gb, gb_len);
+		*next |= 4; /* We have some Gi */
+	} else {
+		*next = 0;
+	}
+
+	arg = kmalloc(sizeof(*arg), GFP_KERNEL);
+	if (!arg) {
+		dev_kfree_skb(skb);
+		return -ENOMEM;
+	}
+
+	*arg = !comm_mode;
+
+	rc = pn532_send_cmd_async(dev, PN532_CMD_IN_JUMP_FOR_DEP, skb,
+				  pn532_in_dep_link_up_complete, arg);
+
+	if (rc < 0) {
+		dev_kfree_skb(skb);
+		kfree(arg);
+	}
+
+	return rc;
+}
+
+static int pn532_dep_link_down(struct nfc_dev *nfc_dev)
+{
+	struct pn532 *dev = nfc_get_drvdata(nfc_dev);
+
+	pn532_poll_reset_mod_list(dev);
+
+	if (dev->tgt_mode || dev->tgt_active_prot) {
+		pn532_send_ack(dev, GFP_KERNEL);
+		dev->rbuf = NULL;
+	}
+
+	dev->tgt_active_prot = 0;
+	dev->tgt_mode = 0;
+
+	skb_queue_purge(&dev->resp_q);
+
+	return 0;
+}
+
+struct pn532_data_exchange_arg {
+	data_exchange_cb_t cb;
+	void *cb_context;
+};
+
+static struct sk_buff *pn532_build_response(struct pn532 *dev)
+{
+	struct sk_buff *skb, *tmp, *t;
+	unsigned int skb_len = 0, tmp_len = 0;
+
+	if (skb_queue_empty(&dev->resp_q))
+		return NULL;
+
+	if (skb_queue_len(&dev->resp_q) == 1) {
+		skb = skb_dequeue(&dev->resp_q);
+		goto out;
+	}
+
+	skb_queue_walk_safe(&dev->resp_q, tmp, t)
+		skb_len += tmp->len;
+
+	skb = alloc_skb(skb_len, GFP_KERNEL);
+	if (skb == NULL)
+		goto out;
+
+	skb_put(skb, skb_len);
+
+	skb_queue_walk_safe(&dev->resp_q, tmp, t) {
+		memcpy(skb->data + tmp_len, tmp->data, tmp->len);
+		tmp_len += tmp->len;
+	}
+
+out:
+	skb_queue_purge(&dev->resp_q);
+
+	return skb;
+}
+
+static int pn532_data_exchange_complete(struct pn532 *dev, void *_arg,
+					struct sk_buff *resp)
+{
+	struct pn532_data_exchange_arg *arg = _arg;
+	struct sk_buff *skb;
+	int rc = 0;
+	u8 status, ret, mi;
+
+	if (IS_ERR(resp)) {
+		rc = PTR_ERR(resp);
+		goto _error;
+	}
+
+	status = resp->data[0];
+	ret = status & PN532_CMD_RET_MASK;
+	mi = status & PN532_CMD_MI_MASK;
+
+	skb_pull(resp, sizeof(status));
+
+	if (ret != PN532_CMD_RET_SUCCESS) {
+		nfc_dev_err(dev->tty->dev,
+			    "PN532 reported error %d when exchanging data",
+			    ret);
+		rc = -EIO;
+		goto error;
+	}
+
+	skb_queue_tail(&dev->resp_q, resp);
+
+	if (mi) {
+		dev->cmd_complete_mi_arg = arg;
+		queue_work(dev->wq, &dev->mi_work);
+		return -EINPROGRESS;
+	}
+
+	skb = pn532_build_response(dev);
+	if (!skb)
+		goto error;
+
+	arg->cb(arg->cb_context, skb, 0);
+	kfree(arg);
+	return 0;
+
+error:
+	dev_kfree_skb(resp);
+_error:
+	skb_queue_purge(&dev->resp_q);
+	arg->cb(arg->cb_context, NULL, rc);
+	kfree(arg);
+	return rc;
+}
+
+static int pn532_transceive(struct nfc_dev *nfc_dev,
+			    struct nfc_target *target, struct sk_buff *skb,
+			    data_exchange_cb_t cb, void *cb_context)
+{
+	struct pn532 *dev = nfc_get_drvdata(nfc_dev);
+	struct pn532_data_exchange_arg *arg = NULL;
+	int rc;
+
+	nfc_dev_dbg(dev->tty->dev, "%s", __func__);
+
+	if (skb->len > PN532_CMD_DATAEXCH_DATA_MAXLEN) {
+		/* TODO: Implement support to multi-part data exchange */
+		nfc_dev_err(dev->tty->dev,
+			    "Data length greater than the max allowed: %d",
+			    PN532_CMD_DATAEXCH_DATA_MAXLEN);
+		rc = -ENOSYS;
+		goto error;
+	}
+
+	if (!dev->tgt_active_prot) {
+		nfc_dev_err(dev->tty->dev,
+			    "Can't exchange data if there is no active target");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	arg = kmalloc(sizeof(*arg), GFP_KERNEL);
+	if (!arg) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	arg->cb = cb;
+	arg->cb_context = cb_context;
+
+	*skb_push(skb, sizeof(u8)) =  1; /*TG*/
+
+	rc = pn532_send_data_async(dev, PN532_CMD_IN_DATA_EXCHANGE,
+				   skb, pn532_data_exchange_complete,
+				   arg);
+
+	if (rc < 0) /* rc from send_async */
+		goto error;
+
+	return 0;
+
+error:
+	kfree(arg);
+	dev_kfree_skb(skb);
+	return rc;
+}
+
+static int pn532_tm_send_complete(struct pn532 *dev, void *arg,
+				  struct sk_buff *resp)
+{
+	u8 status;
+
+	if (IS_ERR(resp))
+		return PTR_ERR(resp);
+
+	status = resp->data[0];
+
+	dev_kfree_skb(resp);
+
+	if (status != 0) {
+		nfc_tm_deactivated(dev->nfc_dev);
+
+		dev->tgt_mode = 0;
+
+		return 0;
+	}
+
+	queue_work(dev->wq, &dev->tg_work);
+
+	return 0;
+}
+
+static int pn532_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+	struct pn532 *dev = nfc_get_drvdata(nfc_dev);
+	int rc;
+
+	if (skb->len > PN532_CMD_DATAEXCH_DATA_MAXLEN) {
+		nfc_dev_err(dev->tty->dev,
+			    "Data length greater than the max allowed: %d",
+			    PN532_CMD_DATAEXCH_DATA_MAXLEN);
+		return -ENOSYS;
+	}
+
+	rc = pn532_send_data_async(dev, PN532_CMD_TG_SET_DATA, skb,
+				   pn532_tm_send_complete, NULL);
+	if (rc < 0)
+		dev_kfree_skb(skb);
+
+	return rc;
+}
+
+static void pn532_wq_mi_recv(struct work_struct *work)
+{
+	struct pn532 *dev = container_of(work, struct pn532, mi_work);
+
+	struct sk_buff *skb;
+	int rc;
+
+	skb = pn532_alloc_skb(dev, PN532_CMD_DATAEXCH_HEAD_LEN);
+	if (!skb)
+		goto error;
+
+	*skb_put(skb, sizeof(u8)) =  1; /*TG*/
+
+	rc = pn532_send_cmd_direct_async(dev,
+					 PN532_CMD_IN_DATA_EXCHANGE,
+					 skb,
+					 pn532_data_exchange_complete,
+					 dev->cmd_complete_mi_arg);
+
+	if (rc == 0) /* success */
+		return;
+
+	nfc_dev_err(dev->tty->dev,
+		    "Error %d when trying to perform data_exchange", rc);
+
+	dev_kfree_skb(skb);
+	kfree(dev->cmd_complete_arg);
+
+error:
+	pn532_send_ack(dev, GFP_KERNEL);
+	queue_work(dev->wq, &dev->cmd_work);
+}
+
+static int pn532_set_configuration(struct pn532 *dev, u8 cfgitem, u8 *cfgdata,
+								u8 cfgdata_len)
+{
+	struct sk_buff *skb;
+	struct sk_buff *resp;
+
+	int skb_len;
+
+	skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */
+
+	skb = pn532_alloc_skb(dev, skb_len);
+	if (!skb)
+		return -ENOMEM;
+
+	*skb_put(skb, sizeof(cfgitem)) = cfgitem;
+	memcpy(skb_put(skb, cfgdata_len), cfgdata, cfgdata_len);
+
+	resp = pn532_send_cmd_sync(dev, PN532_CMD_RF_CONFIGURATION, skb);
+	if (IS_ERR(resp))
+		return PTR_ERR(resp);
+
+	dev_kfree_skb(resp);
+	return 0;
+}
+
+static int pn532_get_firmware_version(struct pn532 *dev,
+				      struct pn532_fw_version *fv)
+{
+	struct sk_buff *skb;
+	struct sk_buff *resp;
+
+	skb = pn532_alloc_skb(dev, 0);
+	if (!skb)
+		return -ENOMEM;
+
+	resp = pn532_send_cmd_sync(dev, PN532_CMD_GET_FIRMWARE_VERSION, skb);
+	if (IS_ERR(resp))
+		return PTR_ERR(resp);
+
+	fv->ic = resp->data[0];
+	fv->ver = resp->data[1];
+	fv->rev = resp->data[2];
+	fv->support = resp->data[3];
+
+	dev_kfree_skb(resp);
+	return 0;
+}
+
+static int pn532_sam_configuration(struct pn532 *dev)
+{
+	struct sk_buff *skb;
+	struct sk_buff *resp;
+
+	skb = pn532_alloc_skb(dev, sizeof(u8));
+	if (!skb)
+		return -ENOMEM;
+
+	*skb_put(skb, sizeof(u8)) = 0x01; /* Mode(timeout and IRQ left out) */
+
+	resp = pn532_send_cmd_sync(dev, PN532_CMD_SAM_CONFIGURATION, skb);
+	if (IS_ERR(resp))
+		return PTR_ERR(resp);
+
+	dev_kfree_skb(resp);
+
+	return 0;
+}
+
+static struct nfc_ops pn532_nfc_ops = {
+	/* TODO implement dev_up und dev_down */
+	.dev_up = NULL,
+	.dev_down = NULL,
+	.dep_link_up = pn532_dep_link_up,
+	.dep_link_down = pn532_dep_link_down,
+	.start_poll = pn532_start_auto_poll,
+	.stop_poll = pn532_stop_poll,
+	.activate_target = pn532_activate_target,
+	.deactivate_target = pn532_deactivate_target,
+	.im_transceive = pn532_transceive,
+	.tm_send = pn532_tm_send,
+};
+
+static int pn532_setup(struct pn532 *dev)
+{
+	struct pn532_config_max_retries max_retries;
+	struct pn532_config_timing timing;
+	int rc;
+
+	max_retries.mx_rty_atr = PN532_CONFIG_MAX_RETRIES_ENDLESS;
+	max_retries.mx_rty_psl = 2;
+	max_retries.mx_rty_passive_act = PN532_CONFIG_MAX_RETRIES_NO_RETRY;
+
+	timing.rfu = PN532_CONFIG_TIMING_102;
+	timing.atr_res_timeout = PN532_CONFIG_TIMING_204;
+	timing.dep_timeout = PN532_CONFIG_TIMING_409;
+
+	rc = pn532_set_configuration(dev, PN532_CFGITEM_MAX_RETRIES,
+				     (u8 *)&max_retries, sizeof(max_retries));
+	if (rc) {
+		nfc_dev_err(dev->tty->dev,
+			    "Error on setting MAX_RETRIES config");
+		return rc;
+	}
+
+
+	rc = pn532_set_configuration(dev, PN532_CFGITEM_TIMING,
+				     (u8 *)&timing, sizeof(timing));
+	if (rc) {
+		nfc_dev_err(dev->tty->dev,
+			    "Error on setting RF timings");
+		return rc;
+	}
+
+	return 0;
+}
+
+static void pn532_connect_nfc(struct work_struct *work)
+{
+	struct pn532 *dev = container_of(work, struct pn532, conn_nfc_work);
+	struct pn532_fw_version fw_ver;
+	int rc = -ENOMEM;
+
+	if (dev == NULL || dev->tty == NULL || dev->tty->dev == NULL)
+		return;
+
+	mutex_init(&dev->cmd_lock);
+
+	INIT_WORK(&dev->cmd_work, pn532_wq_cmd);
+	INIT_WORK(&dev->cmd_complete_work, pn532_wq_cmd_complete);
+	INIT_WORK(&dev->mi_work, pn532_wq_mi_recv);
+	INIT_WORK(&dev->tg_work, pn532_wq_tg_get_data);
+	INIT_WORK(&dev->poll_work, pn532_wq_poll);
+	INIT_WORK(&dev->deactivate_tgt_work, pn532_wq_deactivate_tgt);
+	dev->wq = alloc_ordered_workqueue("pn532", 0);
+	if (dev->wq == NULL)
+		return;
+
+	dev->wq_deactivate_tgt = alloc_workqueue("pn532_add", 0, 0);
+	if (dev->wq_deactivate_tgt == NULL) {
+		destroy_workqueue(dev->wq);
+		return;
+	}
+
+	init_timer(&dev->listen_timer);
+	dev->listen_timer.data = (unsigned long) dev;
+	dev->listen_timer.function = pn532_listen_mode_timer;
+
+	skb_queue_head_init(&dev->resp_q);
+
+	INIT_LIST_HEAD(&dev->cmd_queue);
+
+	dev->ops = &pn532_std_frame_ops;
+
+	dev->wakeup = 1;
+	rc = pn532_sam_configuration(dev);
+
+	if (rc < 0) {
+		destroy_workqueue(dev->wq_deactivate_tgt);
+		destroy_workqueue(dev->wq);
+		return;
+	}
+
+	memset(&fw_ver, 0, sizeof(fw_ver));
+	rc = pn532_get_firmware_version(dev, &fw_ver);
+	if (rc < 0) {
+		destroy_workqueue(dev->wq_deactivate_tgt);
+		destroy_workqueue(dev->wq);
+		return;
+	}
+
+	nfc_dev_info(dev->tty->dev,
+		     "NXP PN532 firmware ver %d.%d now attached",
+		     fw_ver.ver, fw_ver.rev);
+
+
+	dev->nfc_dev = nfc_allocate_device(&pn532_nfc_ops, PN532_ALL_PROTOCOLS,
+					   dev->ops->tx_header_len +
+					   PN532_CMD_DATAEXCH_HEAD_LEN,
+					   dev->ops->tx_tail_len);
+	if (!dev->nfc_dev) {
+		destroy_workqueue(dev->wq_deactivate_tgt);
+		destroy_workqueue(dev->wq);
+		return;
+	}
+
+	nfc_set_parent_dev(dev->nfc_dev, dev->tty->dev);
+	nfc_set_drvdata(dev->nfc_dev, dev);
+
+	rc = nfc_register_device(dev->nfc_dev);
+	if (rc)
+		goto free_nfc_dev;
+
+	rc = pn532_setup(dev);
+	if (rc)
+		goto unregister_nfc_dev;
+
+	destroy_workqueue(dev->wq_conn_nfc);
+	return;
+
+unregister_nfc_dev:
+	nfc_unregister_device(dev->nfc_dev);
+
+free_nfc_dev:
+	nfc_free_device(dev->nfc_dev);
+}
+
+/*
+ * line discipline
+ */
+
+/*
+ * scans the buffer if it contains a pn532 frame. It is not checked if it is
+ * valid. This could be done with pn532_rx_frame_is_valid. It therefore does
+ * not require, that the frame starts at the beginning of the buffer.
+ * This is useful for malformed or errornous transmitted frames. Returns the
+ * bufferposition where the frame starts.
+ */
+static struct pn532_frame *pn532_rx_is_frame(unsigned char *buf, u8 buf_len)
+{
+	int i;
+	u16 frame_len;
+	u16 *flp;
+
+	/* minimal frame length = 5 */
+	for (i = 0; i + 4 < buf_len; i++) {
+		/* search start code */
+		if (buf[i] == 0x00 &&
+			buf[i + 1] == 0x00 &&
+			buf[i + 2] == 0xff) {
+			/* type */
+			switch (buf[i + 3]) {
+			case 0x00: /* ACK frame ? */
+				if (buf[i + 4] == 0xff)
+					return (struct pn532_frame *) &buf[i];
+				break;
+			case 0x01: /* error frame */
+				if ((buf[i + 4] == 0xff) &&
+					(buf_len >= 6))
+					return (struct pn532_frame *) &buf[i];
+				break;
+			case 0xff: /* extended frame ?*/
+				flp = (u16 *)&buf[i + 5];
+				frame_len = be16_to_cpu(*flp);
+				if (buf_len >= frame_len + 9)
+					return (struct pn532_frame *) &buf[i];
+				break;
+			default: /* normal information frame */
+				frame_len = buf[i + 3];
+				if (buf_len >= frame_len + 6)
+					return (struct pn532_frame *) &buf[i];
+				break;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static void pn532_receive_buf(struct tty_struct *tty,
+			      const unsigned char *cp, char *fp, int count)
+{
+	struct pn532_frame *frame;
+	struct pn532 *dev = (struct pn532 *)tty->disc_data;
+	unsigned char c;
+
+	nfc_dev_dbg(dev->tty->dev, "count:%i rbuf:%p rlen:%i rcount:%i",
+		count, dev->rbuf, dev->rlen, dev->rcount);
+
+	print_hex_dump(KERN_DEBUG, "frischRX: ", DUMP_PREFIX_NONE, 16, 1,
+		       cp, count, false);
+
+	if (!dev || dev->magic != PN532_MAGIC || dev->rbuf == NULL)
+		return;
+
+	while (count-- && (dev->rlen - dev->rcount > 0)) {
+		if (fp && *fp++) {
+			/*
+			if (!test_and_set_bit(PN532_ERROR, &dev->flags))
+			{
+				// TODO handle error
+			}
+			*/
+			cp++;
+			continue;
+		}
+		c = *cp++;
+		/*
+		if (c != 0)
+			continue;
+		*/
+
+		dev->rbuf[dev->rcount] = c;
+		frame = pn532_rx_is_frame(dev->rbuf, dev->rcount++);
+
+		if (frame != NULL)
+			if (!dev->recv_complete(dev, frame))
+				dev->rcount = 0;
+	}
+}
+
+static void pn532_write_wakeup(struct tty_struct *tty)
+{
+	int actual;
+	struct pn532 *pn532 = (struct pn532 *)tty->disc_data;
+
+	if (!pn532 || pn532->magic != PN532_MAGIC)
+		return;
+
+	if (pn532->xleft <= 0) {
+		clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+		return;
+	}
+
+	actual = tty->ops->write(tty, pn532->xhead, pn532->xleft);
+	pn532->xleft -= actual;
+	pn532->xhead += actual;
+}
+
+static int pn532_open(struct tty_struct *tty)
+{
+	struct pn532 *pn532;
+	int err;
+
+	if (tty->ops->write == NULL)
+		return -EOPNOTSUPP;
+
+	/*
+	mutex_lock();
+	*/
+
+	pn532 = tty->disc_data;
+
+	err = -EEXIST;
+	/* First make sure we're not already connected. */
+	if (pn532 && pn532->magic == PN532_MAGIC)
+		goto err_exit;
+
+	err = -ENFILE;
+	pn532 = kzalloc(sizeof(*pn532), GFP_KERNEL);
+	if (pn532 == NULL)
+		goto err_exit;
+
+	pn532->tty = tty;
+
+	INIT_WORK(&pn532->conn_nfc_work, pn532_connect_nfc);
+	pn532->wq_conn_nfc = alloc_workqueue("pn532_connect_nfc", 0, 0);
+	if (pn532->wq_conn_nfc == NULL)
+		goto err_free;
+
+	tty->disc_data = pn532;
+
+	/* Done.  We have linked the TTY line to a channel. */
+	/*
+	mutex_unlock();
+	*/
+	tty->receive_room = 65536;	/* We don't flow control */
+
+	queue_work(pn532->wq_conn_nfc, &pn532->conn_nfc_work);
+	pn532->magic = PN532_MAGIC;
+	/* TTY layer expects 0 on success */
+	return 0;
+
+err_free:
+	kfree(pn532);
+err_exit:
+	/*
+	mutex_unlock();
+	*/
+
+	/* Count references from TTY module */
+	return err;
+}
+
+static void pn532_close(struct tty_struct *tty)
+{
+	struct pn532 *pn532 = (struct pn532 *) tty->disc_data;
+	struct pn532_cmd *cmd, *n;
+
+	/* First make sure we're connected. */
+	if (!pn532 || pn532->magic != PN532_MAGIC || pn532->tty != tty)
+		return;
+
+	if (pn532->nfc_dev) {
+		destroy_workqueue(pn532->wq_deactivate_tgt);
+		destroy_workqueue(pn532->wq);
+
+		nfc_unregister_device(pn532->nfc_dev);
+		nfc_free_device(pn532->nfc_dev);
+
+		skb_queue_purge(&pn532->resp_q);
+
+		del_timer(&pn532->listen_timer);
+
+		list_for_each_entry_safe(cmd, n, &pn532->cmd_queue, queue) {
+			list_del(&cmd->queue);
+			kfree(cmd);
+		}
+		nfc_dev_info(pn532->tty->dev, "NXP PN532 NFC device disconnected");
+	}
+	tty->disc_data = NULL;
+
+	kfree(pn532);
+}
+
+static int pn532_hangup(struct tty_struct *tty)
+{
+	pn532_close(tty);
+	return 0;
+}
+
+/* Perform I/O control on an active SLCAN channel. */
+static int pn532_ioctl(struct tty_struct *tty, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	struct pn532 *pn532 = (struct pn532 *) tty->disc_data;
+	unsigned int tmp;
+
+	/* First make sure we're connected. */
+	if (!pn532 || pn532->magic != PN532_MAGIC)
+		return -EINVAL;
+
+	switch (cmd) {
+	case SIOCGIFNAME:
+		if (!pn532->nfc_dev)
+			return -EINVAL;
+		tmp = strlen(nfc_device_name(pn532->nfc_dev)) + 1;
+		if (copy_to_user((void __user *)arg,
+					nfc_device_name(pn532->nfc_dev), tmp))
+			return -EFAULT;
+		return 0;
+
+	case SIOCSIFHWADDR:
+		return -EINVAL;
+
+	default:
+		return tty_mode_ioctl(tty, file, cmd, arg);
+	}
+}
+
+static struct tty_ldisc_ops pn532_ldisc = {
+	.owner		= THIS_MODULE,
+	.magic		= TTY_LDISC_MAGIC,
+	.name		= "pn532",
+	.open		= pn532_open,
+	.close		= pn532_close,
+	.hangup		= pn532_hangup,
+	.ioctl		= pn532_ioctl,
+	.receive_buf	= pn532_receive_buf,
+	.write_wakeup	= pn532_write_wakeup,
+};
+
+static int __init pn532_init(void)
+{
+	int ret;
+
+	ret = tty_register_ldisc(N_PN532, &pn532_ldisc);
+	if (ret)
+		pr_err("pn532: can not register line discipline\n");
+
+	return ret;
+}
+
+static void __exit pn532_exit(void)
+{
+	int ret;
+
+	ret = tty_unregister_ldisc(N_PN532);
+	if (ret)
+		pr_err("pn532: can not unregister ldisc (err %d)\n", ret);
+}
+
+module_init(pn532_init);
+module_exit(pn532_exit);
+
+MODULE_ALIAS_LDISC(N_PN532);
+MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
+MODULE_DESCRIPTION(DESCRIPTION);
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/tty.h b/include/uapi/linux/tty.h
index dac199a..e6b6d77 100644
--- a/include/uapi/linux/tty.h
+++ b/include/uapi/linux/tty.h
@@ -34,5 +34,6 @@
 #define N_TI_WL		22	/* for TI's WL BT, FM, GPS combo chips */
 #define N_TRACESINK	23	/* Trace data routing for MIPI P1149.7 */
 #define N_TRACEROUTER	24	/* Trace data routing for MIPI P1149.7 */
+#define N_PN532		25	/* NXP PN532 NFC chip */
 
 #endif /* _UAPI_LINUX_TTY_H */
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH] mac80211: ignore obviously bogus ECSAs in probe response frames
From: Seth Forshee @ 2013-08-22 15:09 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, John W. Linville
In-Reply-To: <20130822141011.GA24445@thinkpad-t410>

On Thu, Aug 22, 2013 at 09:10:11AM -0500, Seth Forshee wrote:
> On Thu, Aug 22, 2013 at 04:01:03PM +0200, Johannes Berg wrote:
> > On Thu, 2013-08-22 at 08:53 -0500, Seth Forshee wrote:
> > > The Netgear WNDAP360 sends invalid ECSA IEs in probe response
> > > frames
> > 
> > I think we shouldn't be checking probe response frames at all. That
> > seems like a mistake. Can you try this?
> 
> I had considered this, but the spec says that it's at least valid for
> the AP to be sending ECSAs in probe responses. IEEE 802.11-2012 section
> 10.3.3.2:
> 
>   ...an AP shall inform associated STAs that the AP is moving to a new
>   channel and/or operating class and maintain the association by
>   advertising the switch using Extended Channel Switch Announcement
>   elements in any transmitted Beacon frames, Probe Response frames, and
>   Extended Channel Switch Announcement frames until the intended channel
>   switch time.
> 
> Perhaps we can still ignore them though? I suppose we'd expect to
> receive some other frame with the ECSA before it actually happens.

Anyway, your fix also eliminates the disconnects, so whichever solution
you think best is fine. There is a build failure though.

+       ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
+                                        elems, true);

Needs to be &elems here.

Thanks,
Seth

^ permalink raw reply

* Re: pull-request: mac80211 2013-08-21
From: John W. Linville @ 2013-08-22 15:39 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <1377092661.15268.13.camel@jlt4.sipsolutions.net>

On Wed, Aug 21, 2013 at 03:44:21PM +0200, Johannes Berg wrote:
> John,
> 
> I have three more patches for the 3.11 stream: Felix's fix for the
> fairly visible brcmsmac crash, a fix from Simon for an IBSS join bug I
> found and a fix for a channel context bug in IBSS I'd introduced.
> 
> Let me know if there's any problem.
> 
> Thanks,
> johannes
> 
> The following changes since commit dee8a9732e713480075adbbca8eb220c5b8d1216:
> 
>   cfg80211: don't request disconnect if not connected (2013-08-14 14:00:19 +0200)
> 
> are available in the git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git for-john
> 
> for you to fetch changes up to 75a423f493ffdf741acae27bf179cd560f7813d7:
> 
>   mac80211: ibss: fix ignored channel parameter (2013-08-21 15:33:08 +0200)
> 
> ----------------------------------------------------------------
> Felix Fietkau (1):
>       mac80211: add a flag to indicate CCK support for HT clients
> 
> Johannes Berg (1):
>       mac80211: add missing channel context release
> 
> Simon Wunderlich (1):
>       mac80211: ibss: fix ignored channel parameter

Pulling now...

-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply

* [PATCH] mac80211: only respond to probe request with mesh ID
From: Chun-Yeow Yeoh @ 2013-08-22 16:44 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, linville, devel, distro11s, Chun-Yeow Yeoh

From: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com>

Previously, the mesh STA responds to probe request from legacy STA
but now it will respond to legacy STA only if the legacy STA has
included the specific mesh ID or wildcard mesh ID in the probe request.

Signed-off-by: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com>
Acked-by: Thomas Pedersen <thomas@cozybit.com>
Acked-by: Javier Cardona <javier@cozybit.com>
---
 net/mac80211/mesh.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 885a5f6..a660fb8 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -832,6 +832,9 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
 
 	ieee802_11_parse_elems(pos, len - baselen, false, &elems);
 
+	if(!elems.mesh_id)
+		return;
+
 	/* 802.11-2012 10.1.4.3.2 */
 	if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
 	     !is_broadcast_ether_addr(mgmt->da)) ||
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH v2] mac80211: only respond to probe request with mesh ID
From: Chun-Yeow Yeoh @ 2013-08-22 17:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, linville, devel, distro11s, Chun-Yeow Yeoh

From: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com>

Previously, the mesh STA responds to probe request from legacy STA
but now it will only respond to legacy STA if the legacy STA does include 
the specific mesh ID or wildcard mesh ID in the probe request.

The iw patch "iw: scan using meshid" can be used either by legacy STA
or by mesh STA to do active scanning by inserting the mesh ID in the 
probe request frame.

Signed-off-by: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com>
Acked-by: Thomas Pedersen <thomas@cozybit.com>
Acked-by: Javier Cardona <javier@cozybit.com>
---
v2: modify comments (Thomas)

 net/mac80211/mesh.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 885a5f6..a660fb8 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -832,6 +832,9 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
 
 	ieee802_11_parse_elems(pos, len - baselen, false, &elems);
 
+	if(!elems.mesh_id)
+		return;
+
 	/* 802.11-2012 10.1.4.3.2 */
 	if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
 	     !is_broadcast_ether_addr(mgmt->da)) ||
-- 
1.7.9.5


^ permalink raw reply related

* Re: [PATCH v2] mac80211: only respond to probe request with mesh ID
From: Bob Copeland @ 2013-08-22 17:13 UTC (permalink / raw)
  To: Chun-Yeow Yeoh; +Cc: linux-wireless, johannes, linville, devel, distro11s
In-Reply-To: <1377190892-5328-1-git-send-email-yeohchunyeow@gmail.com>

On Thu, Aug 22, 2013 at 10:01:32AM -0700, Chun-Yeow Yeoh wrote:
> +	if(!elems.mesh_id)
> +		return;
> +

if (!elems.mesh_id)

(checkpatch will warn about that)

-- 
Bob Copeland %% www.bobcopeland.com

^ permalink raw reply

* [PATCH v3] mac80211: only respond to probe request with mesh ID
From: Chun-Yeow Yeoh @ 2013-08-22 17:28 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, linville, devel, distro11s, Chun-Yeow Yeoh

From: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com>

Previously, the mesh STA responds to probe request from legacy STA
but now it will only respond to legacy STA if the legacy STA does include 
the specific mesh ID or wildcard mesh ID in the probe request.

The iw patch "iw: scan using meshid" can be used either by legacy STA
or by mesh STA to do active scanning by inserting the mesh ID in the 
probe request frame.

Signed-off-by: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com>
Acked-by: Thomas Pedersen <thomas@cozybit.com>
Acked-by: Javier Cardona <javier@cozybit.com>
---
v2: modify comments (Thomas)
v3: style problem (Bob)

 net/mac80211/mesh.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 885a5f6..a660fb8 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -832,6 +832,9 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
 
 	ieee802_11_parse_elems(pos, len - baselen, false, &elems);
 
+	if (!elems.mesh_id)
+		return;
+
 	/* 802.11-2012 10.1.4.3.2 */
 	if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
 	     !is_broadcast_ether_addr(mgmt->da)) ||
-- 
1.7.9.5


^ permalink raw reply related

* Re: Intel PRO/Wireless 2100 Driver Firmware
From: Stanislav Yakovlev @ 2013-08-22 17:30 UTC (permalink / raw)
  To: Marcus Laitala; +Cc: wireless
In-Reply-To: <CA+7kRpWhLEKZYzGN-Eu4dPDM2+JQRkmJYWP1CJf0jzWSN=KVJA@mail.gmail.com>

Hi Marcus,

On 20 August 2013 17:44, Marcus Laitala <marcus.vitsar@gmail.com> wrote:
> I tried to download the Intel PRO/Wireless 2100 Driver Firmware
> from: http://ipw2100.sourceforge.net/firmware.php
>
> I only get the following error:
> This webpage is not available"
>
> As if the files/webpage is no longer there/online. I just wanted to
> tell who ever is in charge of those that those files are no longer
> downloadable.

Thank you for the information. The firmware can be downloaded from the
Fedora repository:
http://pkgs.fedoraproject.org/repo/pkgs/ipw2100-firmware/

Stanislav.

^ permalink raw reply

* Re: pull request: bluetooth-next 2013-08-21
From: John W. Linville @ 2013-08-22 18:28 UTC (permalink / raw)
  To: Gustavo Padovan, linux-wireless, linux-bluetooth, linux-kernel
In-Reply-To: <20130821161218.GH14645@joana>

On Wed, Aug 21, 2013 at 06:12:18PM +0200, Gustavo Padovan wrote:
> Hi John,
> 
> Patches to 3.12, here we have:
> 
> * implementation of a proper tty_port for RFCOMM devices, this fixes some
> issues people were seeing lately in the kernel.
> * Add voice_setting option for SCO, it is used for SCO Codec selection
> * bugfixes, small improvements and clean ups
> 
> Please pull, Thanks.
> 
> 	Gustavo
> 
> --
> The following changes since commit 72bb2f2678878dd4a758e628957f29ce28000d88:
> 
>   bgmac: make bgmac depend on bcm47xx (2013-07-24 11:05:19 -0400)
> 
> are available in the git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next master
> 
> for you to fetch changes up to 2dea632f9acad076370fe871d4ccc93868621403:
> 
>   Bluetooth: Add SCO connection fallback (2013-08-21 16:47:13 +0200)

Pulling now...

-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply

* [PATCH v3 2/2] rt2x00: rt2800lib: don't hardcode beacon offsets
From: Gabor Juhos @ 2013-08-22 18:53 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users, Gabor Juhos
In-Reply-To: <1377197602-5567-1-git-send-email-juhosg@openwrt.org>

The values written into the BCN_OFFSET[01] registers are
hardcoded in the rt2800_init_register function.

Add a macro and a helper function to derive these values
directly from the base address of a given beacon, and use
the new function instead of the hardcoded numbers.

The patch contains no functional changes. The programmed
register values are the same before and after the patch.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
---
Changes since v2:
  - separated from the 'rt2x00: fix beaconing on RT3593' patch-set

Changes since v1: ---
---
 drivers/net/wireless/rt2x00/rt2800.h    |    2 ++
 drivers/net/wireless/rt2x00/rt2800lib.c |   30 ++++++++++++++++++++++--------
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index e25e5bf..fa33b5e 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -2024,6 +2024,8 @@ struct mac_iveiv_entry {
 	  (((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \
 	  (HW_BEACON_BASE6 - ((__index - 6) * 0x0200))))
 
+#define BEACON_BASE_TO_OFFSET(_base)	(((_base) - 0x4000) / 64)
+
 /*
  * BBP registers.
  * The wordsize of the BBP is 8 bits.
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 38606e2..20243dc 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -946,6 +946,12 @@ static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev,
 	return HW_BEACON_BASE(index);
 }
 
+static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
+					  unsigned int index)
+{
+	return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
+}
+
 void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -4474,17 +4480,25 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
 		return ret;
 
 	rt2800_register_read(rt2x00dev, BCN_OFFSET0, &reg);
-	rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */
-	rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */
-	rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */
-	rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */
+	rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0,
+			   rt2800_get_beacon_offset(rt2x00dev, 0));
+	rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1,
+			   rt2800_get_beacon_offset(rt2x00dev, 1));
+	rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2,
+			   rt2800_get_beacon_offset(rt2x00dev, 2));
+	rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3,
+			   rt2800_get_beacon_offset(rt2x00dev, 3));
 	rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
 
 	rt2800_register_read(rt2x00dev, BCN_OFFSET1, &reg);
-	rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */
-	rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */
-	rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */
-	rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */
+	rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4,
+			   rt2800_get_beacon_offset(rt2x00dev, 4));
+	rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5,
+			   rt2800_get_beacon_offset(rt2x00dev, 5));
+	rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6,
+			   rt2800_get_beacon_offset(rt2x00dev, 6));
+	rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7,
+			   rt2800_get_beacon_offset(rt2x00dev, 7));
 	rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
 
 	rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
-- 
1.7.10

^ permalink raw reply related

* [PATCH v3 1/2] rt2x00: rt2800lib: add rt2800_hw_beacon_base helper
From: Gabor Juhos @ 2013-08-22 18:53 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, users, Gabor Juhos

The HW_BEACON_BASE() macro returns the base address
of a given beacon, however the returned values are
not usable on all chipsets. On devices which have
selectable shared memory parts, some beacon may be
located in the high part of the shared memory.

Instead of extending the already complicated macro,
add a new helper function and use that to get the
base address of a given beacon.

The actual patch contains no functional changes, the
helper function will be extended in a further patch
to handle different chipsets' requirements.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
---
Changes since v2:
  - separated from the 'rt2x00: fix beaconing on RT3593' patch-set
  - update commit log

Changes since v1: ---
---
 drivers/net/wireless/rt2x00/rt2800lib.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index aa6b6b0..38606e2 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -940,6 +940,12 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi)
 }
 EXPORT_SYMBOL_GPL(rt2800_txdone_entry);
 
+static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev,
+					  unsigned int index)
+{
+	return HW_BEACON_BASE(index);
+}
+
 void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -992,7 +998,8 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 		return;
 	}
 
-	beacon_base = HW_BEACON_BASE(entry->entry_idx);
+	beacon_base = rt2800_hw_beacon_base(rt2x00dev, entry->entry_idx);
+
 	rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
 				   entry->skb->len + padding_len);
 
@@ -1017,7 +1024,7 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
 	const int txwi_desc_size = rt2x00dev->bcn->winfo_size;
 	unsigned int beacon_base;
 
-	beacon_base = HW_BEACON_BASE(index);
+	beacon_base = rt2800_hw_beacon_base(rt2x00dev, index);
 
 	/*
 	 * For the Beacon base registers we only need to clear
-- 
1.7.10

^ permalink raw reply related

* [PATCH] staging: vt6656: s_uGetRTSCTSRsvTime return u16 endian corrected value.
From: Malcolm Priestley @ 2013-08-22 20:03 UTC (permalink / raw)
  To: gregkh; +Cc: linux-wireless

s_uGetRTSCTSRsvTime always needs to be corrected to u16 endian value.

Change function to return u16 endian corrected value.

Signed-off-by: Malcolm Priestley <tvboxspy@gmail.com>
---
 drivers/staging/vt6656/rxtx.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c
index d4b9b60..4f36ec8 100644
--- a/drivers/staging/vt6656/rxtx.c
+++ b/drivers/staging/vt6656/rxtx.c
@@ -121,7 +121,7 @@ static void s_vSWencryption(struct vnt_private *pDevice,
 static unsigned int s_uGetTxRsvTime(struct vnt_private *pDevice, u8 byPktType,
 	u32 cbFrameLength, u16 wRate, int bNeedAck);
 
-static u32 s_uGetRTSCTSRsvTime(struct vnt_private *pDevice, u8 byRTSRsvType,
+static u16 s_uGetRTSCTSRsvTime(struct vnt_private *pDevice, u8 byRTSRsvType,
 	u8 byPktType, u32 cbFrameLength, u16 wCurrentRate);
 
 static void s_vFillCTSHead(struct vnt_private *pDevice, u32 uDMAIdx,
@@ -349,7 +349,7 @@ static u32 s_uGetTxRsvTime(struct vnt_private *pDevice, u8 byPktType,
 }
 
 //byFreqType: 0=>5GHZ 1=>2.4GHZ
-static u32 s_uGetRTSCTSRsvTime(struct vnt_private *pDevice,
+static u16 s_uGetRTSCTSRsvTime(struct vnt_private *pDevice,
 	u8 byRTSRsvType, u8 byPktType, u32 cbFrameLength, u16 wCurrentRate)
 {
 	u32 uRrvTime, uRTSTime, uCTSTime, uAckTime, uDataTime;
@@ -379,7 +379,7 @@ static u32 s_uGetRTSCTSRsvTime(struct vnt_private *pDevice,
 
     //RTSRrvTime
     uRrvTime = uRTSTime + uCTSTime + uAckTime + uDataTime + 3*pDevice->uSIFS;
-    return uRrvTime;
+	return cpu_to_le16((u16)uRrvTime);
 }
 
 //byFreqType 0: 5GHz, 1:2.4Ghz
@@ -876,9 +876,12 @@ static void s_vGenerateTxParameter(struct vnt_private *pDevice,
             if (pvRrvTime) {
 		struct vnt_rrv_time_rts *pBuf =
 			(struct vnt_rrv_time_rts *)pvRrvTime;
-                pBuf->wRTSTxRrvTime_aa = cpu_to_le16((u16)s_uGetRTSCTSRsvTime(pDevice, 2, byPktType, cbFrameSize, wCurrentRate));//2:RTSTxRrvTime_aa, 1:2.4GHz
-                pBuf->wRTSTxRrvTime_ba = cpu_to_le16((u16)s_uGetRTSCTSRsvTime(pDevice, 1, byPktType, cbFrameSize, wCurrentRate));//1:RTSTxRrvTime_ba, 1:2.4GHz
-                pBuf->wRTSTxRrvTime_bb = cpu_to_le16((u16)s_uGetRTSCTSRsvTime(pDevice, 0, byPktType, cbFrameSize, wCurrentRate));//0:RTSTxRrvTime_bb, 1:2.4GHz
+		pBuf->wRTSTxRrvTime_aa = s_uGetRTSCTSRsvTime(pDevice, 2,
+				byPktType, cbFrameSize, wCurrentRate);
+		pBuf->wRTSTxRrvTime_ba = s_uGetRTSCTSRsvTime(pDevice, 1,
+				byPktType, cbFrameSize, wCurrentRate);
+		pBuf->wRTSTxRrvTime_bb = s_uGetRTSCTSRsvTime(pDevice, 0,
+				byPktType, cbFrameSize, wCurrentRate);
                 pBuf->wTxRrvTime_a = cpu_to_le16((u16) s_uGetTxRsvTime(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK));//2.4G OFDM
                 pBuf->wTxRrvTime_b = cpu_to_le16((u16) s_uGetTxRsvTime(pDevice, PK_TYPE_11B, cbFrameSize, pDevice->byTopCCKBasicRate, bNeedACK));//1:CCK
             }
@@ -893,7 +896,8 @@ static void s_vGenerateTxParameter(struct vnt_private *pDevice,
 				(struct vnt_rrv_time_cts *)pvRrvTime;
                 pBuf->wTxRrvTime_a = cpu_to_le16((u16)s_uGetTxRsvTime(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK));//2.4G OFDM
                 pBuf->wTxRrvTime_b = cpu_to_le16((u16)s_uGetTxRsvTime(pDevice, PK_TYPE_11B, cbFrameSize, pDevice->byTopCCKBasicRate, bNeedACK));//1:CCK
-                pBuf->wCTSTxRrvTime_ba = cpu_to_le16((u16)s_uGetRTSCTSRsvTime(pDevice, 3, byPktType, cbFrameSize, wCurrentRate));//3:CTSTxRrvTime_Ba, 1:2.4GHz
+		pBuf->wCTSTxRrvTime_ba = s_uGetRTSCTSRsvTime(pDevice, 3,
+				byPktType, cbFrameSize, wCurrentRate);
             }
             //Fill CTS
             s_vFillCTSHead(pDevice, uDMAIdx, byPktType, pvCTS, cbFrameSize, bNeedACK, bDisCRC, wCurrentRate, byFBOption);
@@ -906,7 +910,8 @@ static void s_vGenerateTxParameter(struct vnt_private *pDevice,
             if (pvRrvTime) {
 		struct vnt_rrv_time_ab *pBuf =
 				(struct vnt_rrv_time_ab *)pvRrvTime;
-                pBuf->wRTSTxRrvTime = cpu_to_le16((u16)s_uGetRTSCTSRsvTime(pDevice, 2, byPktType, cbFrameSize, wCurrentRate));//2:RTSTxRrvTime_aa, 0:5GHz
+		pBuf->wRTSTxRrvTime = s_uGetRTSCTSRsvTime(pDevice, 2,
+				byPktType, cbFrameSize, wCurrentRate);
                 pBuf->wTxRrvTime = cpu_to_le16((u16)s_uGetTxRsvTime(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK));//0:OFDM
             }
             //Fill RTS
@@ -928,7 +933,8 @@ static void s_vGenerateTxParameter(struct vnt_private *pDevice,
             if (pvRrvTime) {
 		struct vnt_rrv_time_ab *pBuf =
 				(struct vnt_rrv_time_ab *)pvRrvTime;
-                pBuf->wRTSTxRrvTime = cpu_to_le16((u16)s_uGetRTSCTSRsvTime(pDevice, 0, byPktType, cbFrameSize, wCurrentRate));//0:RTSTxRrvTime_bb, 1:2.4GHz
+		pBuf->wRTSTxRrvTime = s_uGetRTSCTSRsvTime(pDevice, 0,
+				byPktType, cbFrameSize, wCurrentRate);
                 pBuf->wTxRrvTime = cpu_to_le16((u16)s_uGetTxRsvTime(pDevice, PK_TYPE_11B, cbFrameSize, wCurrentRate, bNeedACK));//1:CCK
             }
             //Fill RTS
-- 
1.8.1.2



^ permalink raw reply related

* Re: [RFC] mac80211: fix locking with ieee80211_resume_disconnect()
From: Eliad Peller @ 2013-08-22 21:37 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless@vger.kernel.org, Johannes Berg
In-Reply-To: <1377117341-9327-1-git-send-email-johannes@sipsolutions.net>

On Wed, Aug 21, 2013 at 11:35 PM, Johannes Berg
<johannes@sipsolutions.net> wrote:
> From: Johannes Berg <johannes.berg@intel.com>
>
> If it is needed to disconnect multiple virtual interfaces after
> (WoWLAN-) suspend, the most obvious approach would be to iterate
> all interfaces by calling ieee80211_iterate_active_interfaces()
> and then call ieee80211_resume_disconnect() for each one. This
> is what the iwlmvm driver does.
>
> Unfortunately, this causes a locking dependency from mac80211's
> iflist_mtx to the key_mtx. This is problematic as the former is
> intentionally never held while calling any driver operation to
> allow drivers to iterate with their own locks held. The key_mtx
> is held while installing a key into the driver though, so this
> new lock dependency means drivers implementing the logic above
> can no longer hold their own lock while iterating.
>
> To fix this, modify the ieee80211_resume_disconnect() API to do
> the iteration in there (using RCU) while holding the key_mtx so
> there's no new lock dependency, and let the driver device which
> interface should be disconnected by passing a decision function
> that returns true/false.
>
can't you simply add a new rtnl-locked interface iterator?

Eliad.

^ permalink raw reply

* Re: ipw2200: Fix race condition in the command completion acknowledge
From: Stanislav Yakovlev @ 2013-08-22 22:19 UTC (permalink / raw)
  To: Stan G; +Cc: wireless, linux-kernel
In-Reply-To: <CAOMQbqHRkF4_gK=GA587LDghkYtd1hRQhLObV+Tbe9zEDbS5-Q@mail.gmail.com>

Hi Stan,

> I am having intermittent problems with dropping wifi network while the  AP
> is still visible and dmesg produces this:
>
>       [26617.864791] ipw2200: Failed to send ASSOCIATE: Already sending a
> command.
>

Thank you for the information, I will take a look. Could you please
reproduce the problem with debug information enabled?

modprobe -rv ipw2200
modprobe -v ipw2200 debug=0x1000

Stanislav.

^ permalink raw reply

* RE: [linux-nfc] [PATCH RFC] nfc: add a driver for pn532 connected on uart
From: Rymarkiewicz, WaldemarX @ 2013-08-23  5:23 UTC (permalink / raw)
  To: Lars Poeschel, poeschel@lemonage.de, lauro.venancio@openbossa.org,
	aloisio.almeida@openbossa.org, sameo@linux.intel.com,
	gregkh@linuxfoundation.org, jslaby@suse.cz,
	linux-kernel@vger.kernel.org, linux-wireless@vger.kernel.org,
	linux-nfc@lists.01.org
In-Reply-To: <1377181699-1668-1-git-send-email-larsi@wh2.tu-dresden.de>

Hi  Lars,

>This adds a driver for the nxp pn532 nfc chip.
>It is not meant for merging. Instead it is meant to show that some
>progress has been made and what the current state is and to help
>testing.
>Although I can do some basic things with this driver I expect it to
>contain lots of bugs. Be aware!
>This driver is heavily based on the pn533 driver and duplicates much
>code. This has do be factored out some time.

I'm not sure if this is expected approach adding new drivers. You duplicates most of pn533 code which is not good.

Also, note that pn533 and pn532  are pretty the same chips (with small differences) and it would be quite natural to support both with one driver. Pn533 already reads chip version on init, so at this point you already know with which chip you are dealing with.

I suggest to separate transport layer from the core in pn533 and add support for uart and usb separately. This is exactly what I've planned while changing pn533 to support acr122 device.

Thanks,
/Waldek

^ permalink raw reply

* [PATCH] mac80211: remove IEEE80211_STA_BEACON_POLL
From: Stanislaw Gruszka @ 2013-08-23  6:52 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

When we detect beacon lost we do active AP probing (using nullfunc
frame or probe request), there is no need to have beacon polling. Flag
IEEE80211_STA_BEACON_POLL seems to be used just for historical reasons.

Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
---
 net/mac80211/ieee80211_i.h |    1 -
 net/mac80211/mlme.c        |   38 +++++++++-----------------------------
 net/mac80211/scan.c        |    3 +--
 3 files changed, 10 insertions(+), 32 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b618651..d353c64 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -322,7 +322,6 @@ struct ieee80211_roc_work {
 
 /* flags used in struct ieee80211_if_managed.flags */
 enum ieee80211_sta_flags {
-	IEEE80211_STA_BEACON_POLL	= BIT(0),
 	IEEE80211_STA_CONNECTION_POLL	= BIT(1),
 	IEEE80211_STA_CONTROL_PORT	= BIT(2),
 	IEEE80211_STA_DISABLE_HT	= BIT(4),
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 2aab130..23694b7 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -893,8 +893,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
 	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
 		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
 
-	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-			    IEEE80211_STA_CONNECTION_POLL))
+	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
 		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
 
 	ieee80211_tx_skb(sdata, skb);
@@ -1374,8 +1373,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
 	if (!mgd->associated)
 		return false;
 
-	if (mgd->flags & (IEEE80211_STA_BEACON_POLL |
-			  IEEE80211_STA_CONNECTION_POLL))
+	if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
 		return false;
 
 	if (!mgd->have_beacon)
@@ -1691,8 +1689,7 @@ static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
 {
 	lockdep_assert_held(&sdata->local->mtx);
 
-	sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
-				IEEE80211_STA_BEACON_POLL);
+	sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
 	ieee80211_run_deferred_scan(sdata->local);
 }
 
@@ -1954,11 +1951,8 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
 	struct ieee80211_local *local = sdata->local;
 
 	mutex_lock(&local->mtx);
-	if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-			      IEEE80211_STA_CONNECTION_POLL))) {
-		mutex_unlock(&local->mtx);
-		return;
-	}
+	if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
+		goto out;
 
 	__ieee80211_stop_poll(sdata);
 
@@ -2094,15 +2088,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
 	 * because otherwise we would reset the timer every time and
 	 * never check whether we received a probe response!
 	 */
-	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-			    IEEE80211_STA_CONNECTION_POLL))
+	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
 		already = true;
 
-	if (beacon)
-		ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
-	else
-		ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
-
 	mutex_unlock(&sdata->local->mtx);
 
 	if (already)
@@ -3069,17 +3057,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 		}
 	}
 
-	if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
+	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
 		mlme_dbg_ratelimited(sdata,
 				     "cancelling AP probe due to a received beacon\n");
-		mutex_lock(&local->mtx);
-		ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
-		ieee80211_run_deferred_scan(local);
-		mutex_unlock(&local->mtx);
-
-		mutex_lock(&local->iflist_mtx);
-		ieee80211_recalc_ps(local, -1);
-		mutex_unlock(&local->iflist_mtx);
+		ieee80211_reset_ap_probe(sdata);
 	}
 
 	/*
@@ -3548,8 +3529,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 	} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
 		run_again(sdata, ifmgd->assoc_data->timeout);
 
-	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-			    IEEE80211_STA_CONNECTION_POLL) &&
+	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
 	    ifmgd->associated) {
 		u8 bssid[ETH_ALEN];
 		int max_tries;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 08afe74..ecb57b0 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -391,8 +391,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local,
 		return false;
 
 	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-	    sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
-				  IEEE80211_STA_CONNECTION_POLL))
+	    sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)
 		return false;
 
 	return true;
-- 
1.7.1


^ permalink raw reply related

* Re: [RFC] mac80211: fix locking with ieee80211_resume_disconnect()
From: Johannes Berg @ 2013-08-23  7:50 UTC (permalink / raw)
  To: Eliad Peller; +Cc: linux-wireless@vger.kernel.org
In-Reply-To: <CAB3XZEexJ28291uKi=0tPvsORHQinbc272KRe2m=Zr6_CV3vFA@mail.gmail.com>

On Fri, 2013-08-23 at 00:37 +0300, Eliad Peller wrote:

> can't you simply add a new rtnl-locked interface iterator?

Hmm, yeah, I guess that would be simpler :)

johannes


^ permalink raw reply

* Re: [PATCH v3 1/2] rt2x00: rt2800lib: add rt2800_hw_beacon_base helper
From: Helmut Schaa @ 2013-08-23  8:23 UTC (permalink / raw)
  To: Gabor Juhos; +Cc: John W. Linville, linux-wireless, rt2x00 Users List
In-Reply-To: <1377197602-5567-1-git-send-email-juhosg@openwrt.org>

On Thu, Aug 22, 2013 at 8:53 PM, Gabor Juhos <juhosg@openwrt.org> wrote:
> The HW_BEACON_BASE() macro returns the base address
> of a given beacon, however the returned values are
> not usable on all chipsets. On devices which have
> selectable shared memory parts, some beacon may be
> located in the high part of the shared memory.
>
> Instead of extending the already complicated macro,
> add a new helper function and use that to get the
> base address of a given beacon.
>
> The actual patch contains no functional changes, the
> helper function will be extended in a further patch
> to handle different chipsets' requirements.
>
> Signed-off-by: Gabor Juhos <juhosg@openwrt.org>

Looks good.

Acked-by: Helmut Schaa <helmut.schaa@googlemail.com>

> ---
> Changes since v2:
>   - separated from the 'rt2x00: fix beaconing on RT3593' patch-set
>   - update commit log
>
> Changes since v1: ---
> ---
>  drivers/net/wireless/rt2x00/rt2800lib.c |   11 +++++++++--
>  1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
> index aa6b6b0..38606e2 100644
> --- a/drivers/net/wireless/rt2x00/rt2800lib.c
> +++ b/drivers/net/wireless/rt2x00/rt2800lib.c
> @@ -940,6 +940,12 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi)
>  }
>  EXPORT_SYMBOL_GPL(rt2800_txdone_entry);
>
> +static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev,
> +                                         unsigned int index)
> +{
> +       return HW_BEACON_BASE(index);
> +}
> +
>  void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
>  {
>         struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
> @@ -992,7 +998,8 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
>                 return;
>         }
>
> -       beacon_base = HW_BEACON_BASE(entry->entry_idx);
> +       beacon_base = rt2800_hw_beacon_base(rt2x00dev, entry->entry_idx);
> +
>         rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
>                                    entry->skb->len + padding_len);
>
> @@ -1017,7 +1024,7 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
>         const int txwi_desc_size = rt2x00dev->bcn->winfo_size;
>         unsigned int beacon_base;
>
> -       beacon_base = HW_BEACON_BASE(index);
> +       beacon_base = rt2800_hw_beacon_base(rt2x00dev, index);
>
>         /*
>          * For the Beacon base registers we only need to clear
> --
> 1.7.10
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 2/2] rt2x00: rt2800lib: don't hardcode beacon offsets
From: Helmut Schaa @ 2013-08-23  8:31 UTC (permalink / raw)
  To: Gabor Juhos; +Cc: John W. Linville, linux-wireless, rt2x00 Users List
In-Reply-To: <1377197602-5567-2-git-send-email-juhosg@openwrt.org>

On Thu, Aug 22, 2013 at 8:53 PM, Gabor Juhos <juhosg@openwrt.org> wrote:
> The values written into the BCN_OFFSET[01] registers are
> hardcoded in the rt2800_init_register function.
>
> Add a macro and a helper function to derive these values
> directly from the base address of a given beacon, and use
> the new function instead of the hardcoded numbers.
>
> The patch contains no functional changes. The programmed
> register values are the same before and after the patch.
>
> Signed-off-by: Gabor Juhos <juhosg@openwrt.org>

Fine with me as well.
Acked-by: Helmut Schaa <helmut.schaa@googlemail.com>

> ---
> Changes since v2:
>   - separated from the 'rt2x00: fix beaconing on RT3593' patch-set
>
> Changes since v1: ---
> ---
>  drivers/net/wireless/rt2x00/rt2800.h    |    2 ++
>  drivers/net/wireless/rt2x00/rt2800lib.c |   30 ++++++++++++++++++++++--------
>  2 files changed, 24 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
> index e25e5bf..fa33b5e 100644
> --- a/drivers/net/wireless/rt2x00/rt2800.h
> +++ b/drivers/net/wireless/rt2x00/rt2800.h
> @@ -2024,6 +2024,8 @@ struct mac_iveiv_entry {
>           (((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \
>           (HW_BEACON_BASE6 - ((__index - 6) * 0x0200))))
>
> +#define BEACON_BASE_TO_OFFSET(_base)   (((_base) - 0x4000) / 64)
> +
>  /*
>   * BBP registers.
>   * The wordsize of the BBP is 8 bits.
> diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
> index 38606e2..20243dc 100644
> --- a/drivers/net/wireless/rt2x00/rt2800lib.c
> +++ b/drivers/net/wireless/rt2x00/rt2800lib.c
> @@ -946,6 +946,12 @@ static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev,
>         return HW_BEACON_BASE(index);
>  }
>
> +static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
> +                                         unsigned int index)
> +{
> +       return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
> +}
> +
>  void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
>  {
>         struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
> @@ -4474,17 +4480,25 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
>                 return ret;
>
>         rt2800_register_read(rt2x00dev, BCN_OFFSET0, &reg);
> -       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */
> -       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */
> -       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */
> -       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */
> +       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0,
> +                          rt2800_get_beacon_offset(rt2x00dev, 0));
> +       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1,
> +                          rt2800_get_beacon_offset(rt2x00dev, 1));
> +       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2,
> +                          rt2800_get_beacon_offset(rt2x00dev, 2));
> +       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3,
> +                          rt2800_get_beacon_offset(rt2x00dev, 3));
>         rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
>
>         rt2800_register_read(rt2x00dev, BCN_OFFSET1, &reg);
> -       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */
> -       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */
> -       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */
> -       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */
> +       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4,
> +                          rt2800_get_beacon_offset(rt2x00dev, 4));
> +       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5,
> +                          rt2800_get_beacon_offset(rt2x00dev, 5));
> +       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6,
> +                          rt2800_get_beacon_offset(rt2x00dev, 6));
> +       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7,
> +                          rt2800_get_beacon_offset(rt2x00dev, 7));
>         rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
>
>         rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
> --
> 1.7.10
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [RFC] mac80211: add ieee80211_iterate_active_interfaces_rtnl()
From: Johannes Berg @ 2013-08-23  8:43 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

If it is needed to disconnect multiple virtual interfaces after
(WoWLAN-) suspend, the most obvious approach would be to iterate
all interfaces by calling ieee80211_iterate_active_interfaces()
and then call ieee80211_resume_disconnect() for each one. This
is what the iwlmvm driver does.

Unfortunately, this causes a locking dependency from mac80211's
iflist_mtx to the key_mtx. This is problematic as the former is
intentionally never held while calling any driver operation to
allow drivers to iterate with their own locks held. The key_mtx
is held while installing a key into the driver though, so this
new lock dependency means drivers implementing the logic above
can no longer hold their own lock while iterating.

To fix this, add a new ieee80211_iterate_active_interfaces_rtnl()
function that iterates while the RTNL is already held. This is
true during suspend/resume, so that then the locking dependency
isn't introduced. Use this in the iwlmvm driver to get rid of the
potential deadlock.

While at it, also refactor the various interface iterators and
keep only a single implementation called by the various cases.

Change-Id: I9b578c7dacefbb818b4113da12770aec9d0b06c4
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 drivers/net/wireless/iwlwifi/mvm/d3.c |  7 ++--
 include/net/mac80211.h                | 19 ++++++++++
 net/mac80211/util.c                   | 71 +++++++++++++++++------------------
 3 files changed, 56 insertions(+), 41 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index 5585538..4b0d033 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -1411,10 +1411,9 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
 
  out:
 	if (!test)
-		ieee80211_iterate_active_interfaces(mvm->hw,
-						    IEEE80211_IFACE_ITER_NORMAL,
-						    iwl_mvm_d3_disconnect_iter,
-						    NULL);
+		ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
+			IEEE80211_IFACE_ITER_NORMAL,
+			iwl_mvm_d3_disconnect_iter, NULL);
 
 	/* return 1 to reconfigure the device */
 	set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index cb5eba8..96dba53 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3833,6 +3833,25 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
 						void *data);
 
 /**
+ * ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
+ *
+ * This function iterates over the interfaces associated with a given
+ * hardware that are currently active and calls the callback for them.
+ * This version can only be used while holding the RTNL.
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
+ * @iterator: the iterator function to call, cannot sleep
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
+					      u32 iter_flags,
+					      void (*iterator)(void *data,
+						u8 *mac,
+						struct ieee80211_vif *vif),
+					      void *data);
+
+/**
  * ieee80211_queue_work - add work onto the mac80211 workqueue
  *
  * Drivers and mac80211 use this to add work onto the mac80211 workqueue.
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index d23c5a7..f2c0e6b 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -567,18 +567,15 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
 					IEEE80211_QUEUE_STOP_REASON_FLUSH);
 }
 
-void ieee80211_iterate_active_interfaces(
-	struct ieee80211_hw *hw, u32 iter_flags,
-	void (*iterator)(void *data, u8 *mac,
-			 struct ieee80211_vif *vif),
-	void *data)
+void __iterate_active_interfaces(struct ieee80211_local *local,
+				 u32 iter_flags,
+				 void (*iterator)(void *data, u8 *mac,
+						  struct ieee80211_vif *vif),
+				 void *data)
 {
-	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata;
 
-	mutex_lock(&local->iflist_mtx);
-
-	list_for_each_entry(sdata, &local->interfaces, list) {
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 		switch (sdata->vif.type) {
 		case NL80211_IFTYPE_MONITOR:
 			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
@@ -597,13 +594,25 @@ void ieee80211_iterate_active_interfaces(
 				 &sdata->vif);
 	}
 
-	sdata = rcu_dereference_protected(local->monitor_sdata,
-					  lockdep_is_held(&local->iflist_mtx));
+	sdata = rcu_dereference_check(local->monitor_sdata,
+				      lockdep_is_held(&local->iflist_mtx) ||
+				      lockdep_rtnl_is_held());
 	if (sdata &&
 	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
 	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))
 		iterator(data, sdata->vif.addr, &sdata->vif);
+}
 
+void ieee80211_iterate_active_interfaces(
+	struct ieee80211_hw *hw, u32 iter_flags,
+	void (*iterator)(void *data, u8 *mac,
+			 struct ieee80211_vif *vif),
+	void *data)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+
+	mutex_lock(&local->iflist_mtx);
+	__iterate_active_interfaces(local, iter_flags, iterator, data);
 	mutex_unlock(&local->iflist_mtx);
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
@@ -615,38 +624,26 @@ void ieee80211_iterate_active_interfaces_atomic(
 	void *data)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_sub_if_data *sdata;
 
 	rcu_read_lock();
+	__iterate_active_interfaces(local, iter_flags, iterator, data);
+	rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
 
-	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-		switch (sdata->vif.type) {
-		case NL80211_IFTYPE_MONITOR:
-			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
-				continue;
-			break;
-		case NL80211_IFTYPE_AP_VLAN:
-			continue;
-		default:
-			break;
-		}
-		if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
-		    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
-			continue;
-		if (ieee80211_sdata_running(sdata))
-			iterator(data, sdata->vif.addr,
-				 &sdata->vif);
-	}
+void ieee80211_iterate_active_interfaces_rtnl(
+	struct ieee80211_hw *hw, u32 iter_flags,
+	void (*iterator)(void *data, u8 *mac,
+			 struct ieee80211_vif *vif),
+	void *data)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
 
-	sdata = rcu_dereference(local->monitor_sdata);
-	if (sdata &&
-	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
-	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))
-		iterator(data, sdata->vif.addr, &sdata->vif);
+	ASSERT_RTNL();
 
-	rcu_read_unlock();
+	__iterate_active_interfaces(local, iter_flags, iterator, data);
 }
-EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
+EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
 
 /*
  * Nothing should have been stuffed into the workqueue during
-- 
1.8.4.rc2


^ permalink raw reply related

* Re: [RFC] mac80211: add ieee80211_iterate_active_interfaces_rtnl()
From: Johannes Berg @ 2013-08-23  8:46 UTC (permalink / raw)
  To: linux-wireless
In-Reply-To: <1377247432-22145-1-git-send-email-johannes@sipsolutions.net>

On Fri, 2013-08-23 at 10:43 +0200, Johannes Berg wrote:

> Change-Id: I9b578c7dacefbb818b4113da12770aec9d0b06c4

will remove ...

> +void __iterate_active_interfaces(struct ieee80211_local *local,
> +				 u32 iter_flags,
> +				 void (*iterator)(void *data, u8 *mac,
> +						  struct ieee80211_vif *vif),
> +				 void *data)

should be static.

johannes


^ permalink raw reply

* [PATCH v2 00/16] wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware
From: Eugene Krasnikov @ 2013-08-23  8:58 UTC (permalink / raw)
  To: linux-wireless; +Cc: wcn36xx, Eugene Krasnikov

This is a mac80211 driver for Qualcomm WCN3660/WCN3680 devices. So
far WCN3660/WCN3680 is available only on MSM platform.

We got a lot of useful comments from Joe and Johannes. Hopefully the second
round will be the same productive.

To make review easier the driver sent in several patches, one patch per file, but after
review it will be sent as one big file.

Firmware can be found here:
https://github.com/AOKP/vendor_sony/find/jb-mr1

Wiki page is available here:
http://wireless.kernel.org/en/users/Drivers/wcn36xx

A lot people made a contribution to this driver. Her is the list in
alphabetical order:

Eugene Krasnikov <k.eugene.e@gmail.com>
Kalle Valo <kvalo@qca.qualcomm.com>
Olof Johansson <dev@skyshaper.net>
Pontus Fuchs <pontus.fuchs@gmail.com>
Yanbo Li <yanbol@qti.qualcomm.com>

Signed-off-by: Eugene Krasnikov <k.eugene.e@gmail.com>

---

Eugene Krasnikov (16):
  wcn36xx: Add main.c
  wcn36xx: add debug.c
  wcn36xx: add debug.h
  wcn36xx: add dxe.c
  wcn36xx: add dxe.h
  wcn36xx: add hal.h
  wcn36xx: add pmc.c
  wcn36xx: add pmc.h
  wcn36xx: add smd.c
  wcn36xx: add smd.h
  wcn36xx: add txrx.c
  wcn36xx: add txrx.h
  wcn36xx: add wcn36xx.h
  wcn36xx: add Makefile
  wcn36xx: add Kconfig
  wcn36xx: Add wcn36xx to ath Makefile and Kconfig

 drivers/net/wireless/ath/Kconfig           |    1 +
 drivers/net/wireless/ath/Makefile          |    1 +
 drivers/net/wireless/ath/wcn36xx/Kconfig   |   16 +
 drivers/net/wireless/ath/wcn36xx/Makefile  |    9 +
 drivers/net/wireless/ath/wcn36xx/debug.c   |  166 +
 drivers/net/wireless/ath/wcn36xx/debug.h   |   49 +
 drivers/net/wireless/ath/wcn36xx/dxe.c     |  804 +++++
 drivers/net/wireless/ath/wcn36xx/dxe.h     |  281 ++
 drivers/net/wireless/ath/wcn36xx/hal.h     | 4657 ++++++++++++++++++++++++++++
 drivers/net/wireless/ath/wcn36xx/main.c    | 1047 +++++++
 drivers/net/wireless/ath/wcn36xx/pmc.c     |   46 +
 drivers/net/wireless/ath/wcn36xx/pmc.h     |   32 +
 drivers/net/wireless/ath/wcn36xx/smd.c     | 1530 +++++++++
 drivers/net/wireless/ath/wcn36xx/smd.h     |  121 +
 drivers/net/wireless/ath/wcn36xx/txrx.c    |  256 ++
 drivers/net/wireless/ath/wcn36xx/txrx.h    |  160 +
 drivers/net/wireless/ath/wcn36xx/wcn36xx.h |  236 ++
 17 files changed, 9412 insertions(+)
 create mode 100644 drivers/net/wireless/ath/wcn36xx/Kconfig
 create mode 100644 drivers/net/wireless/ath/wcn36xx/Makefile
 create mode 100644 drivers/net/wireless/ath/wcn36xx/debug.c
 create mode 100644 drivers/net/wireless/ath/wcn36xx/debug.h
 create mode 100644 drivers/net/wireless/ath/wcn36xx/dxe.c
 create mode 100644 drivers/net/wireless/ath/wcn36xx/dxe.h
 create mode 100644 drivers/net/wireless/ath/wcn36xx/hal.h
 create mode 100644 drivers/net/wireless/ath/wcn36xx/main.c
 create mode 100644 drivers/net/wireless/ath/wcn36xx/pmc.c
 create mode 100644 drivers/net/wireless/ath/wcn36xx/pmc.h
 create mode 100644 drivers/net/wireless/ath/wcn36xx/smd.c
 create mode 100644 drivers/net/wireless/ath/wcn36xx/smd.h
 create mode 100644 drivers/net/wireless/ath/wcn36xx/txrx.c
 create mode 100644 drivers/net/wireless/ath/wcn36xx/txrx.h
 create mode 100644 drivers/net/wireless/ath/wcn36xx/wcn36xx.h

-- 
1.8.2.2


^ permalink raw reply

* [PATCH v2 01/16] wcn36xx: Add main.c
From: Eugene Krasnikov @ 2013-08-23  8:58 UTC (permalink / raw)
  To: linux-wireless; +Cc: wcn36xx, Eugene Krasnikov
In-Reply-To: <1377248299-21007-1-git-send-email-k.eugene.e@gmail.com>

Main file.

Signed-off-by: Eugene Krasnikov <k.eugene.e@gmail.com>
---
 drivers/net/wireless/ath/wcn36xx/main.c | 1047 +++++++++++++++++++++++++++++++
 1 file changed, 1047 insertions(+)
 create mode 100644 drivers/net/wireless/ath/wcn36xx/main.c

diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
new file mode 100644
index 0000000..2b262ee
--- /dev/null
+++ b/drivers/net/wireless/ath/wcn36xx/main.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "wcn36xx.h"
+
+unsigned int debug_mask;
+module_param(debug_mask, uint, 0644);
+MODULE_PARM_DESC(debug_mask, "Debugging mask");
+
+#define CHAN2G(_freq, _idx) { \
+	.band = IEEE80211_BAND_2GHZ, \
+	.center_freq = (_freq), \
+	.hw_value = (_idx), \
+	.max_power = 25, \
+}
+
+#define CHAN5G(_freq, _idx) { \
+	.band = IEEE80211_BAND_5GHZ, \
+	.center_freq = (_freq), \
+	.hw_value = (_idx), \
+	.max_power = 25, \
+}
+
+/* The wcn firmware expects channel values to matching
+ * their mnemonic values. So use these for .hw_value. */
+static struct ieee80211_channel wcn_2ghz_channels[] = {
+	CHAN2G(2412, 1), /* Channel 1 */
+	CHAN2G(2417, 2), /* Channel 2 */
+	CHAN2G(2422, 3), /* Channel 3 */
+	CHAN2G(2427, 4), /* Channel 4 */
+	CHAN2G(2432, 5), /* Channel 5 */
+	CHAN2G(2437, 6), /* Channel 6 */
+	CHAN2G(2442, 7), /* Channel 7 */
+	CHAN2G(2447, 8), /* Channel 8 */
+	CHAN2G(2452, 9), /* Channel 9 */
+	CHAN2G(2457, 10), /* Channel 10 */
+	CHAN2G(2462, 11), /* Channel 11 */
+	CHAN2G(2467, 12), /* Channel 12 */
+	CHAN2G(2472, 13), /* Channel 13 */
+	CHAN2G(2484, 14)  /* Channel 14 */
+
+};
+
+static struct ieee80211_channel wcn_5ghz_channels[] = {
+	CHAN5G(5180, 36),
+	CHAN5G(5200, 40),
+	CHAN5G(5220, 44),
+	CHAN5G(5240, 48),
+	CHAN5G(5260, 52),
+	CHAN5G(5280, 56),
+	CHAN5G(5300, 60),
+	CHAN5G(5320, 64),
+	CHAN5G(5500, 100),
+	CHAN5G(5520, 104),
+	CHAN5G(5540, 108),
+	CHAN5G(5560, 112),
+	CHAN5G(5580, 116),
+	CHAN5G(5600, 120),
+	CHAN5G(5620, 124),
+	CHAN5G(5640, 128),
+	CHAN5G(5660, 132),
+	CHAN5G(5700, 140),
+	CHAN5G(5745, 149),
+	CHAN5G(5765, 153),
+	CHAN5G(5785, 157),
+	CHAN5G(5805, 161),
+	CHAN5G(5825, 165)
+};
+
+#define RATE(_bitrate, _hw_rate, _flags) { \
+	.bitrate        = (_bitrate),                   \
+	.flags          = (_flags),                     \
+	.hw_value       = (_hw_rate),                   \
+	.hw_value_short = (_hw_rate)  \
+}
+
+static struct ieee80211_rate wcn_2ghz_rates[] = {
+	RATE(10, HW_RATE_INDEX_1MBPS, 0),
+	RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(60, HW_RATE_INDEX_6MBPS, 0),
+	RATE(90, HW_RATE_INDEX_9MBPS, 0),
+	RATE(120, HW_RATE_INDEX_12MBPS, 0),
+	RATE(180, HW_RATE_INDEX_18MBPS, 0),
+	RATE(240, HW_RATE_INDEX_24MBPS, 0),
+	RATE(360, HW_RATE_INDEX_36MBPS, 0),
+	RATE(480, HW_RATE_INDEX_48MBPS, 0),
+	RATE(540, HW_RATE_INDEX_54MBPS, 0)
+};
+
+static struct ieee80211_rate wcn_5ghz_rates[] = {
+	RATE(60, HW_RATE_INDEX_6MBPS, 0),
+	RATE(90, HW_RATE_INDEX_9MBPS, 0),
+	RATE(120, HW_RATE_INDEX_12MBPS, 0),
+	RATE(180, HW_RATE_INDEX_18MBPS, 0),
+	RATE(240, HW_RATE_INDEX_24MBPS, 0),
+	RATE(360, HW_RATE_INDEX_36MBPS, 0),
+	RATE(480, HW_RATE_INDEX_48MBPS, 0),
+	RATE(540, HW_RATE_INDEX_54MBPS, 0)
+};
+
+static struct ieee80211_supported_band wcn_band_2ghz = {
+	.channels	= wcn_2ghz_channels,
+	.n_channels	= ARRAY_SIZE(wcn_2ghz_channels),
+	.bitrates	= wcn_2ghz_rates,
+	.n_bitrates	= ARRAY_SIZE(wcn_2ghz_rates),
+	.ht_cap		= {
+		.cap =	IEEE80211_HT_CAP_GRN_FLD |
+			IEEE80211_HT_CAP_SGI_20 |
+			IEEE80211_HT_CAP_DSSSCCK40 |
+			IEEE80211_HT_CAP_LSIG_TXOP_PROT,
+		.ht_supported = true,
+		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
+		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+		.mcs = {
+			.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+			.rx_highest = cpu_to_le16(72),
+			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		}
+	}
+};
+
+static struct ieee80211_supported_band wcn_band_5ghz = {
+	.channels	= wcn_5ghz_channels,
+	.n_channels	= ARRAY_SIZE(wcn_5ghz_channels),
+	.bitrates	= wcn_5ghz_rates,
+	.n_bitrates	= ARRAY_SIZE(wcn_5ghz_rates),
+	.ht_cap		= {
+		.cap =	IEEE80211_HT_CAP_GRN_FLD |
+			IEEE80211_HT_CAP_SGI_20 |
+			IEEE80211_HT_CAP_DSSSCCK40 |
+			IEEE80211_HT_CAP_LSIG_TXOP_PROT |
+			IEEE80211_HT_CAP_SGI_40 |
+			IEEE80211_HT_CAP_SUP_WIDTH_20_40,
+		.ht_supported = true,
+		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
+		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+		.mcs = {
+			.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+			.rx_highest = cpu_to_le16(72),
+			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		}
+	}
+};
+
+static const struct ieee80211_iface_limit if_limits[] = {
+	{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) },
+	{ .max = 1, .types = BIT(NL80211_IFTYPE_AP) },
+};
+
+static const struct ieee80211_iface_combination if_comb = {
+	.limits = if_limits,
+	.n_limits = ARRAY_SIZE(if_limits),
+	.max_interfaces = 2,
+	.num_different_channels = 1,
+};
+
+#ifdef CONFIG_PM
+
+static const struct wiphy_wowlan_support wowlan_support = {
+	.flags = WIPHY_WOWLAN_ANY
+};
+
+#endif
+
+static inline u8 get_sta_index(struct ieee80211_vif *vif,
+			       struct wcn36xx_sta *sta_priv)
+{
+	return NL80211_IFTYPE_STATION == vif->type ?
+	       sta_priv->bss_sta_index :
+	       sta_priv->sta_index;
+}
+
+static int wcn36xx_start(struct ieee80211_hw *hw)
+{
+	struct wcn36xx *wcn = hw->priv;
+	int ret;
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n");
+
+	/* SMD initialization */
+	ret = wcn36xx_smd_open(wcn);
+	if (ret) {
+		wcn36xx_err("Failed to open smd channel: %d\n", ret);
+		goto out_err;
+	}
+
+	/* Allocate memory pools for Mgmt BD headers and Data BD headers */
+	ret = wcn36xx_dxe_allocate_mem_pools(wcn);
+	if (ret) {
+		wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret);
+		goto out_smd_close;
+	}
+
+	ret = wcn36xx_dxe_alloc_ctl_blks(wcn);
+	if (ret) {
+		wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret);
+		goto out_free_dxe_pool;
+	}
+
+	/* Maximum SMD message size is 4k */
+	wcn->smd_buf = kmalloc(WCN36XX_SMD_BUF_SIZE, GFP_KERNEL);
+	if (!wcn->smd_buf) {
+		wcn36xx_err("Failed to allocate smd buf\n");
+		ret = -ENOMEM;
+		goto out_free_dxe_ctl;
+	}
+
+	ret = wcn36xx_smd_load_nv(wcn);
+	if (ret) {
+		wcn36xx_err("Failed to push NV to chip\n");
+		goto out_free_smd_buf;
+	}
+
+	ret = wcn36xx_smd_start(wcn);
+	if (ret) {
+		wcn36xx_err("Failed to start chip\n");
+		goto out_free_smd_buf;
+	}
+
+	/* DMA channel initialization */
+	ret = wcn36xx_dxe_init(wcn);
+	if (ret) {
+		wcn36xx_err("DXE init failed\n");
+		goto out_smd_stop;
+	}
+
+	wcn36xx_pmc_init(wcn);
+	wcn36xx_debugfs_init(wcn);
+
+	if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
+		ret = wcn36xx_smd_feature_caps_exchange(wcn);
+		if (ret)
+			wcn36xx_warn("Exchange feature caps failed\n");
+	}
+
+	return 0;
+
+out_smd_stop:
+	wcn36xx_smd_stop(wcn);
+out_free_smd_buf:
+	kfree(wcn->smd_buf);
+out_free_dxe_pool:
+	wcn36xx_dxe_free_mem_pools(wcn);
+out_free_dxe_ctl:
+	wcn36xx_dxe_free_ctl_blks(wcn);
+out_smd_close:
+	wcn36xx_smd_close(wcn);
+out_err:
+	return ret;
+}
+
+static void wcn36xx_stop(struct ieee80211_hw *hw)
+{
+	struct wcn36xx *wcn = hw->priv;
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n");
+
+	wcn36xx_debugfs_exit(wcn);
+	wcn36xx_smd_stop(wcn);
+	wcn36xx_dxe_deinit(wcn);
+	wcn36xx_smd_close(wcn);
+
+	wcn36xx_dxe_free_mem_pools(wcn);
+	wcn36xx_dxe_free_ctl_blks(wcn);
+
+	kfree(wcn->smd_buf);
+}
+
+static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct wcn36xx *wcn = hw->priv;
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed);
+
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		int ch = WCN36XX_HW_CHANNEL(wcn);
+		wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
+			    ch);
+		wcn36xx_smd_switch_channel(wcn, ch);
+	}
+
+	return 0;
+}
+
+#define WCN36XX_SUPPORTED_FILTERS (0)
+
+static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
+				     unsigned int changed,
+				     unsigned int *total, u64 multicast)
+{
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
+
+	*total &= WCN36XX_SUPPORTED_FILTERS;
+}
+
+static void wcn36xx_tx(struct ieee80211_hw *hw,
+		       struct ieee80211_tx_control *control,
+		       struct sk_buff *skb)
+{
+	struct wcn36xx *wcn = hw->priv;
+	struct wcn36xx_sta *sta_priv = NULL;
+
+	if (control->sta)
+		sta_priv = (struct wcn36xx_sta *)control->sta->drv_priv;
+
+	if (wcn36xx_start_tx(wcn, sta_priv, skb))
+		ieee80211_free_txskb(wcn->hw, skb);
+}
+
+static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+			   struct ieee80211_vif *vif,
+			   struct ieee80211_sta *sta,
+			   struct ieee80211_key_conf *key_conf)
+{
+	struct wcn36xx *wcn = hw->priv;
+	struct wcn36xx_sta *sta_priv = NULL;
+	int ret = 0;
+	u8 key[WLAN_MAX_KEY_LEN];
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n");
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n",
+		    cmd, key_conf->cipher, key_conf->keyidx,
+		    key_conf->keylen, key_conf->flags);
+	wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ",
+			 key_conf->key,
+			 key_conf->keylen);
+	sta_priv = sta ? (struct wcn36xx_sta *)sta->drv_priv : wcn->sta;
+
+	switch (key_conf->cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+		wcn->encrypt_type = WCN36XX_HAL_ED_WEP40;
+		break;
+	case WLAN_CIPHER_SUITE_WEP104:
+		wcn->encrypt_type = WCN36XX_HAL_ED_WEP40;
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		wcn->encrypt_type = WCN36XX_HAL_ED_CCMP;
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		wcn->encrypt_type = WCN36XX_HAL_ED_TKIP;
+		break;
+	default:
+		wcn36xx_err("Unsupported key type 0x%x\n",
+			      key_conf->cipher);
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	switch (cmd) {
+	case SET_KEY:
+		if (WCN36XX_HAL_ED_TKIP == wcn->encrypt_type) {
+			/*
+			 * Supplicant is sending key in the wrong order:
+			 * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b)
+			 * but HW expects it to be in the order as described in
+			 * IEEE 802.11 spec (see chapter 11.7) like this:
+			 * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b)
+			 */
+			memcpy(key, key_conf->key, 16);
+			memcpy(key + 16, key_conf->key + 24, 8);
+			memcpy(key + 24, key_conf->key + 16, 8);
+		} else {
+			memcpy(key, key_conf->key, key_conf->keylen);
+		}
+
+		if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) {
+			sta_priv->is_data_encrypted = true;
+			/* Reconfigure bss with encrypt_type */
+			if (NL80211_IFTYPE_STATION == vif->type)
+				wcn36xx_smd_config_bss(wcn,
+						       vif,
+						       sta,
+						       sta->addr,
+						       true);
+
+			wcn36xx_smd_set_stakey(wcn,
+				wcn->encrypt_type,
+				key_conf->keyidx,
+				key_conf->keylen,
+				key,
+				get_sta_index(vif, sta_priv));
+		} else {
+			wcn36xx_smd_set_bsskey(wcn,
+				wcn->encrypt_type,
+				key_conf->keyidx,
+				key_conf->keylen,
+				key);
+			if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) ||
+			    (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) {
+				sta_priv->is_data_encrypted = true;
+				wcn36xx_smd_set_stakey(wcn,
+					wcn->encrypt_type,
+					key_conf->keyidx,
+					key_conf->keylen,
+					key,
+					get_sta_index(vif, sta_priv));
+			}
+		}
+		break;
+	case DISABLE_KEY:
+		if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
+			wcn36xx_smd_remove_bsskey(wcn,
+				wcn->encrypt_type,
+				key_conf->keyidx);
+		} else {
+			sta_priv->is_data_encrypted = false;
+			/* do not remove key if disassociated */
+			if (wcn->aid)
+				wcn36xx_smd_remove_stakey(wcn,
+					wcn->encrypt_type,
+					key_conf->keyidx,
+					get_sta_index(vif, sta_priv));
+		}
+		break;
+	default:
+		wcn36xx_err("Unsupported key cmd 0x%x\n", cmd);
+		ret = -EOPNOTSUPP;
+		goto out;
+		break;
+	}
+
+out:
+	return ret;
+}
+
+static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
+{
+	struct wcn36xx *wcn = hw->priv;
+
+	wcn36xx_smd_init_scan(wcn);
+	wcn36xx_smd_start_scan(wcn);
+}
+
+static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw)
+{
+	struct wcn36xx *wcn = hw->priv;
+
+	wcn36xx_smd_end_scan(wcn);
+	wcn36xx_smd_finish_scan(wcn);
+}
+
+static void wcn36xx_update_allowed_rates(struct wcn36xx *wcn,
+					 struct ieee80211_sta *sta)
+{
+	int i, size;
+	u16 *rates_table;
+	u32 rates = sta->supp_rates[wcn->hw->conf.chandef.chan->band];
+
+	memset(&wcn->supported_rates, 0, sizeof(wcn->supported_rates));
+	wcn->supported_rates.op_rate_mode = STA_11n;
+
+	size = ARRAY_SIZE(wcn->supported_rates.dsss_rates);
+	rates_table = wcn->supported_rates.dsss_rates;
+	if (wcn->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) {
+		for (i = 0; i < size; i++) {
+			if (rates & 0x01) {
+				rates_table[i] = wcn_2ghz_rates[i].hw_value;
+				rates = rates >> 1;
+			}
+		}
+	}
+
+	size = ARRAY_SIZE(wcn->supported_rates.ofdm_rates);
+	rates_table = wcn->supported_rates.ofdm_rates;
+	for (i = 0; i < size; i++) {
+		if (rates & 0x01) {
+			rates_table[i] = wcn_5ghz_rates[i].hw_value;
+			rates = rates >> 1;
+		}
+	}
+
+	if (sta->ht_cap.ht_supported) {
+		memcpy(wcn->supported_rates.supported_mcs_set,
+		       sta->ht_cap.mcs.rx_mask,
+		       sizeof(sta->ht_cap.mcs.rx_mask));
+		BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) >
+			     sizeof(wcn->supported_rates.supported_mcs_set));
+	}
+}
+
+static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif,
+				     struct ieee80211_bss_conf *bss_conf,
+				     u32 changed)
+{
+	struct wcn36xx *wcn = hw->priv;
+	struct sk_buff *skb = NULL;
+	u16 tim_off, tim_len;
+	enum wcn36xx_hal_link_state link_state;
+
+	wcn->current_vif = (struct wcn36xx_vif *)vif->drv_priv;
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
+		    vif, changed);
+
+	if (changed & BSS_CHANGED_BEACON_INFO) {
+		wcn36xx_dbg(WCN36XX_DBG_MAC,
+			    "mac bss changed dtim period %d\n",
+			    bss_conf->dtim_period);
+
+		wcn->dtim_period = bss_conf->dtim_period;
+	}
+
+	if (changed & BSS_CHANGED_BSSID) {
+		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n",
+			    bss_conf->bssid);
+
+		if (!is_zero_ether_addr(bss_conf->bssid)) {
+			wcn->is_joining = true;
+			wcn->current_vif->bss_index = 0xff;
+			wcn36xx_smd_join(wcn, bss_conf->bssid,
+					 vif->addr, WCN36XX_HW_CHANNEL(wcn));
+			wcn36xx_smd_config_bss(wcn, vif, NULL,
+					       bss_conf->bssid, false);
+		} else {
+			wcn->is_joining = false;
+			wcn36xx_smd_delete_bss(wcn);
+		}
+	}
+
+	if (changed & BSS_CHANGED_SSID) {
+		wcn36xx_dbg(WCN36XX_DBG_MAC,
+			    "mac bss changed ssid\n");
+		wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ",
+				 bss_conf->ssid, bss_conf->ssid_len);
+
+		wcn->ssid.length = bss_conf->ssid_len;
+		memcpy(&wcn->ssid.ssid, bss_conf->ssid, bss_conf->ssid_len);
+	}
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		wcn->is_joining = false;
+		if (bss_conf->assoc) {
+			struct ieee80211_sta *sta;
+			struct wcn36xx_sta *sta_priv;
+
+			wcn36xx_dbg(WCN36XX_DBG_MAC,
+				    "mac assoc bss %pM vif %pM AID=%d\n",
+				     bss_conf->bssid,
+				     vif->addr,
+				     bss_conf->aid);
+
+			wcn->aid = bss_conf->aid;
+
+			rcu_read_lock();
+			sta = ieee80211_find_sta(vif, bss_conf->bssid);
+			if (!sta) {
+				wcn36xx_err("sta %pM is not found\n",
+					      bss_conf->bssid);
+				rcu_read_unlock();
+				goto out;
+			}
+			sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+
+			wcn36xx_update_allowed_rates(wcn, sta);
+
+			wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
+				vif->addr,
+				WCN36XX_HAL_LINK_POSTASSOC_STATE);
+			wcn->sta = sta_priv;
+			wcn36xx_smd_config_bss(wcn, vif, sta,
+					       bss_conf->bssid,
+					       true);
+			rcu_read_unlock();
+		} else {
+			wcn36xx_dbg(WCN36XX_DBG_MAC,
+				    "disassociated bss %pM vif %pM AID=%d\n",
+				    bss_conf->bssid,
+				    vif->addr,
+				    bss_conf->aid);
+			wcn->aid = 0;
+			wcn36xx_smd_set_link_st(wcn,
+						bss_conf->bssid,
+						vif->addr,
+						WCN36XX_HAL_LINK_IDLE_STATE);
+		}
+	}
+
+	if (changed & BSS_CHANGED_AP_PROBE_RESP) {
+		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
+		skb = ieee80211_proberesp_get(hw, vif);
+		if (!skb) {
+			wcn36xx_err("failed to alloc probereq skb\n");
+			goto out;
+		}
+
+		wcn36xx_smd_update_proberesp_tmpl(wcn, skb);
+		dev_kfree_skb(skb);
+	}
+
+	if (changed & BSS_CHANGED_BEACON_ENABLED) {
+		wcn36xx_dbg(WCN36XX_DBG_MAC,
+			    "mac bss changed beacon enabled %d\n",
+			    bss_conf->enable_beacon);
+
+		if (bss_conf->enable_beacon) {
+			wcn->current_vif->bss_index = 0xff;
+			wcn36xx_smd_config_bss(wcn, vif, NULL,
+					       wcn->addresses.addr, false);
+			skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
+						       &tim_len);
+			if (!skb) {
+				wcn36xx_err("failed to alloc beacon skb\n");
+				goto out;
+			}
+			wcn36xx_smd_send_beacon(wcn, skb, tim_off, 0);
+			dev_kfree_skb(skb);
+
+			if (vif->type == NL80211_IFTYPE_ADHOC ||
+			    vif->type == NL80211_IFTYPE_MESH_POINT)
+				link_state = WCN36XX_HAL_LINK_IBSS_STATE;
+			else
+				link_state = WCN36XX_HAL_LINK_AP_STATE;
+
+			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
+						link_state);
+		} else {
+			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
+						WCN36XX_HAL_LINK_IDLE_STATE);
+			wcn36xx_smd_delete_bss(wcn);
+		}
+	}
+out:
+	return;
+}
+
+/* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */
+static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	struct wcn36xx *wcn = hw->priv;
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
+
+	wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
+	return 0;
+}
+
+static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct wcn36xx *wcn = hw->priv;
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
+	wcn36xx_smd_delete_sta_self(wcn, vif->addr);
+}
+
+static int wcn36xx_add_interface(struct ieee80211_hw *hw,
+				 struct ieee80211_vif *vif)
+{
+	struct wcn36xx *wcn = hw->priv;
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
+		    vif, vif->type);
+
+	wcn->current_vif = (struct wcn36xx_vif *)vif->drv_priv;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_STATION:
+		wcn36xx_smd_add_sta_self(wcn, vif->addr, 0);
+		break;
+	case NL80211_IFTYPE_AP:
+		wcn36xx_smd_add_sta_self(wcn, vif->addr, 0);
+		break;
+	case NL80211_IFTYPE_ADHOC:
+	case NL80211_IFTYPE_MESH_POINT:
+		wcn36xx_smd_add_sta_self(wcn, vif->addr, 0);
+		break;
+	default:
+		wcn36xx_warn("Unsupported interface type requested: %d\n",
+			     vif->type);
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+			   struct ieee80211_sta *sta)
+{
+	struct wcn36xx *wcn = hw->priv;
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
+		    vif, sta->addr);
+
+	wcn->sta = (struct wcn36xx_sta *)sta->drv_priv;
+	wcn->aid = sta->aid;
+	wcn36xx_smd_config_sta(wcn, vif, sta);
+
+	return 0;
+}
+
+static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif,
+			      struct ieee80211_sta *sta)
+{
+	struct wcn36xx *wcn = hw->priv;
+	struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
+		    vif, sta->addr, sta_priv->sta_index);
+
+	wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow)
+{
+	struct wcn36xx *wcn = hw->priv;
+	struct ieee80211_vif *vif = container_of((void *)wcn->current_vif,
+						 struct ieee80211_vif,
+						 drv_priv);
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n");
+
+	mutex_lock(&wcn->pm_mutex);
+
+	/* Enter BMPS only in connected state */
+	if ((wcn->aid > 0) &&
+	    (wcn->pw_state != WCN36XX_BMPS) &&
+	    (NL80211_IFTYPE_STATION == vif->type))
+		wcn36xx_pmc_enter_bmps_state(wcn, vif->bss_conf.sync_tsf);
+
+	wcn->is_suspended = true;
+	wcn->is_con_lost_pending = false;
+
+	mutex_unlock(&wcn->pm_mutex);
+
+	return 0;
+}
+
+static int wcn36xx_resume(struct ieee80211_hw *hw)
+{
+	struct wcn36xx *wcn = hw->priv;
+	struct ieee80211_vif *vif = container_of((void *)wcn->current_vif,
+						 struct ieee80211_vif,
+						 drv_priv);
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n");
+
+	wcn->is_suspended = false;
+
+	if (wcn->pw_state == WCN36XX_BMPS)
+		wcn36xx_pmc_exit_bmps_state(wcn);
+
+	if (wcn->is_con_lost_pending) {
+		wcn36xx_dbg(WCN36XX_DBG_MAC, "report connection lost\n");
+		ieee80211_connection_loss(vif);
+	}
+
+	return 0;
+}
+
+#endif
+
+static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
+		    struct ieee80211_vif *vif,
+		    enum ieee80211_ampdu_mlme_action action,
+		    struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+		    u8 buf_size)
+{
+	struct wcn36xx *wcn = hw->priv;
+	struct wcn36xx_sta *sta_priv = NULL;
+
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
+		    action, tid);
+
+	sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+
+	switch (action) {
+	case IEEE80211_AMPDU_RX_START:
+		sta_priv->tid = tid;
+		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
+			get_sta_index(vif, sta_priv));
+		wcn36xx_smd_add_ba(wcn);
+		wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
+		ieee80211_start_tx_ba_session(sta, tid, 0);
+		break;
+	case IEEE80211_AMPDU_RX_STOP:
+		wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
+		break;
+	case IEEE80211_AMPDU_TX_START:
+		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+		break;
+	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
+			get_sta_index(vif, sta_priv));
+		break;
+	case IEEE80211_AMPDU_TX_STOP_FLUSH:
+	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+	case IEEE80211_AMPDU_TX_STOP_CONT:
+		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+		break;
+	default:
+		wcn36xx_err("Unknown AMPDU action\n");
+	}
+
+	return 0;
+}
+
+static const struct ieee80211_ops wcn36xx_ops = {
+	.start			= wcn36xx_start,
+	.stop			= wcn36xx_stop,
+	.add_interface		= wcn36xx_add_interface,
+	.remove_interface	= wcn36xx_remove_interface,
+#ifdef CONFIG_PM
+	.suspend		= wcn36xx_suspend,
+	.resume			= wcn36xx_resume,
+#endif
+	.config			= wcn36xx_config,
+	.configure_filter       = wcn36xx_configure_filter,
+	.tx			= wcn36xx_tx,
+	.set_key		= wcn36xx_set_key,
+	.sw_scan_start		= wcn36xx_sw_scan_start,
+	.sw_scan_complete	= wcn36xx_sw_scan_complete,
+	.bss_info_changed	= wcn36xx_bss_info_changed,
+	.set_rts_threshold	= wcn36xx_set_rts_threshold,
+	.sta_add		= wcn36xx_sta_add,
+	.sta_remove		= wcn36xx_sta_remove,
+	.ampdu_action		= wcn36xx_ampdu_action,
+};
+
+static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
+{
+	int ret = 0;
+
+	static const u32 cipher_suites[] = {
+		WLAN_CIPHER_SUITE_WEP40,
+		WLAN_CIPHER_SUITE_WEP104,
+		WLAN_CIPHER_SUITE_TKIP,
+		WLAN_CIPHER_SUITE_CCMP,
+	};
+
+	wcn->hw->flags = IEEE80211_HW_SIGNAL_DBM |
+		IEEE80211_HW_HAS_RATE_CONTROL |
+		IEEE80211_HW_SUPPORTS_PS |
+		IEEE80211_HW_CONNECTION_MONITOR |
+		IEEE80211_HW_AMPDU_AGGREGATION |
+		IEEE80211_HW_TIMING_BEACON_ONLY;
+
+	wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_AP) |
+		BIT(NL80211_IFTYPE_ADHOC) |
+		BIT(NL80211_IFTYPE_MESH_POINT);
+
+	wcn->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wcn_band_2ghz;
+	wcn->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wcn_band_5ghz;
+
+	wcn->hw->wiphy->cipher_suites = cipher_suites;
+	wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+	wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+
+#ifdef CONFIG_PM
+	wcn->hw->wiphy->wowlan = &wowlan_support;
+#endif
+
+	wcn->hw->wiphy->n_addresses = 1;
+	wcn->hw->wiphy->addresses = &wcn->addresses;
+
+	wcn->hw->max_listen_interval = 200;
+
+	wcn->hw->queues = 4;
+
+	SET_IEEE80211_DEV(wcn->hw, wcn->dev);
+
+	wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta);
+	wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif);
+
+	return ret;
+}
+
+static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
+					  struct platform_device *pdev)
+{
+	struct resource *res;
+	/* Set TX IRQ */
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					   "wcnss_wlantx_irq");
+	if (!res) {
+		wcn36xx_err("failed to get tx_irq\n");
+		return -ENOENT;
+	}
+	wcn->tx_irq = res->start;
+
+	/* Set RX IRQ */
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					   "wcnss_wlanrx_irq");
+	if (!res) {
+		wcn36xx_err("failed to get rx_irq\n");
+		return -ENOENT;
+	}
+	wcn->rx_irq = res->start;
+
+	/* Map the memory */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						 "wcnss_mmio");
+	if (!res) {
+		wcn36xx_err("failed to get mmio\n");
+		return -ENOENT;
+	}
+	wcn->mmio = ioremap(res->start, resource_size(res));
+	if (!wcn->mmio) {
+		wcn36xx_err("failed to map io memory\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int wcn36xx_probe(struct platform_device *pdev)
+{
+	struct ieee80211_hw *hw;
+	struct wcn36xx *wcn;
+	int ret;
+	u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = {
+		HW_RATE_INDEX_6MBPS,
+		HW_RATE_INDEX_9MBPS,
+		HW_RATE_INDEX_12MBPS,
+		HW_RATE_INDEX_18MBPS,
+		HW_RATE_INDEX_24MBPS,
+		HW_RATE_INDEX_36MBPS,
+		HW_RATE_INDEX_48MBPS,
+		HW_RATE_INDEX_54MBPS
+	};
+	u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = {
+		HW_RATE_INDEX_1MBPS,
+		HW_RATE_INDEX_2MBPS,
+		HW_RATE_INDEX_5_5MBPS,
+		HW_RATE_INDEX_11MBPS
+	};
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
+
+	hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
+	if (!hw) {
+		wcn36xx_err("failed to alloc hw\n");
+		ret = -ENOMEM;
+		goto out_err;
+	}
+	platform_set_drvdata(pdev, hw);
+	wcn = hw->priv;
+	wcn->hw = hw;
+	wcn->dev = &pdev->dev;
+	wcn->ctrl_ops = pdev->dev.platform_data;
+
+	mutex_init(&wcn->pm_mutex);
+	mutex_init(&wcn->smd_mutex);
+
+	/* Configuring supported rates */
+	wcn->supported_rates.op_rate_mode = STA_11n;
+	memcpy(wcn->supported_rates.dsss_rates, dsss_rates,
+		sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES);
+	memcpy(wcn->supported_rates.ofdm_rates, ofdm_rates,
+		sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES);
+	wcn->supported_rates.supported_mcs_set[0] = 0xFF;
+
+	if (!wcn->ctrl_ops->get_hw_mac(wcn->addresses.addr)) {
+		wcn36xx_info("mac address: %pM\n", wcn->addresses.addr);
+		SET_IEEE80211_PERM_ADDR(wcn->hw, wcn->addresses.addr);
+	}
+
+	ret = wcn36xx_platform_get_resources(wcn, pdev);
+	if (ret)
+		goto out_wq;
+
+	wcn36xx_init_ieee80211(wcn);
+	ret = ieee80211_register_hw(wcn->hw);
+	if (ret)
+		goto out_unmap;
+
+	return 0;
+
+out_unmap:
+	iounmap(wcn->mmio);
+out_wq:
+	ieee80211_free_hw(hw);
+out_err:
+	return ret;
+}
+static int wcn36xx_remove(struct platform_device *pdev)
+{
+	struct ieee80211_hw *hw = platform_get_drvdata(pdev);
+	struct wcn36xx *wcn = hw->priv;
+	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
+
+	mutex_destroy(&wcn->pm_mutex);
+	mutex_destroy(&wcn->smd_mutex);
+
+	ieee80211_unregister_hw(hw);
+	iounmap(wcn->mmio);
+	ieee80211_free_hw(hw);
+
+	return 0;
+}
+static const struct platform_device_id wcn36xx_platform_id_table[] = {
+	{
+		.name = "wcn36xx",
+		.driver_data = 0
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table);
+
+static struct platform_driver wcn36xx_driver = {
+	.probe      = wcn36xx_probe,
+	.remove     = wcn36xx_remove,
+	.driver         = {
+		.name   = "wcn36xx",
+		.owner  = THIS_MODULE,
+	},
+	.id_table    = wcn36xx_platform_id_table,
+};
+
+static int __init wcn36xx_init(void)
+{
+	platform_driver_register(&wcn36xx_driver);
+	return 0;
+}
+module_init(wcn36xx_init);
+
+static void __exit wcn36xx_exit(void)
+{
+	platform_driver_unregister(&wcn36xx_driver);
+}
+module_exit(wcn36xx_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
+MODULE_FIRMWARE(WLAN_NV_FILE);
-- 
1.8.2.2


^ permalink raw reply related

* [PATCH v2 02/16] wcn36xx: add debug.c
From: Eugene Krasnikov @ 2013-08-23  8:58 UTC (permalink / raw)
  To: linux-wireless; +Cc: wcn36xx, Eugene Krasnikov
In-Reply-To: <1377248299-21007-1-git-send-email-k.eugene.e@gmail.com>

adding debug.c file

Signed-off-by: Eugene Krasnikov <k.eugene.e@gmail.com>
---
 drivers/net/wireless/ath/wcn36xx/debug.c | 166 +++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)
 create mode 100644 drivers/net/wireless/ath/wcn36xx/debug.c

diff --git a/drivers/net/wireless/ath/wcn36xx/debug.c b/drivers/net/wireless/ath/wcn36xx/debug.c
new file mode 100644
index 0000000..91508f3
--- /dev/null
+++ b/drivers/net/wireless/ath/wcn36xx/debug.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include "wcn36xx.h"
+#include "debug.h"
+#include "pmc.h"
+
+#ifdef CONFIG_WCN36XX_DEBUGFS
+
+static int wcn36xx_debugfs_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf,
+				   size_t count, loff_t *ppos)
+{
+	struct wcn36xx *wcn = file->private_data;
+	char buf[3];
+
+	if (wcn->pw_state == WCN36XX_BMPS)
+		buf[0] = '1';
+	else
+		buf[0] = '0';
+
+	buf[1] = '\n';
+	buf[2] = 0x00;
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t write_file_bool_bmps(struct file *file,
+				    const char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct wcn36xx *wcn = file->private_data;
+	struct ieee80211_vif *vif = container_of((void *)wcn->current_vif,
+						 struct ieee80211_vif,
+						 drv_priv);
+	char buf[32];
+	int buf_size;
+
+	buf_size = min(count, (sizeof(buf)-1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+
+	switch (buf[0]) {
+	case 'y':
+	case 'Y':
+	case '1':
+		wcn36xx_enable_keep_alive_null_packet(wcn);
+		wcn36xx_pmc_enter_bmps_state(wcn, vif->bss_conf.sync_tsf);
+		break;
+	case 'n':
+	case 'N':
+	case '0':
+		wcn36xx_pmc_exit_bmps_state(wcn);
+		break;
+	}
+
+	return count;
+}
+
+static const struct file_operations fops_wcn36xx_bmps = {
+	.open  =       wcn36xx_debugfs_open,
+	.read  =       read_file_bool_bmps,
+	.write =       write_file_bool_bmps,
+};
+
+static ssize_t write_file_dump(struct file *file,
+				    const char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct wcn36xx *wcn = file->private_data;
+	char buf[255], *tmp;
+	int buf_size;
+	u32 arg[WCN36xx_MAX_DUMP_ARGS];
+	int i;
+
+	memset(buf, 0, sizeof(buf));
+	memset(arg, 0, sizeof(arg));
+
+	buf_size = min(count, (sizeof(buf) - 1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+
+	tmp = buf;
+
+	for (i = 0; i < WCN36xx_MAX_DUMP_ARGS; i++) {
+		char *begin;
+		begin = strsep(&tmp, " ");
+		if (begin == NULL)
+			break;
+
+		if (kstrtoul(begin, 0, (unsigned long *)(arg + i)) != 0)
+			break;
+	}
+
+	wcn36xx_info("DUMP args is %d %d %d %d %d\n", arg[0], arg[1], arg[2],
+		     arg[3], arg[4]);
+	wcn36xx_smd_dump_cmd_req(wcn, arg[0], arg[1], arg[2], arg[3], arg[4]);
+
+	return count;
+}
+
+static const struct file_operations fops_wcn36xx_dump = {
+	.open  =       wcn36xx_debugfs_open,
+	.write =       write_file_dump,
+};
+
+#define ADD_FILE(name, mode, fop, priv_data)		\
+	do {							\
+		struct dentry *d;				\
+		d = debugfs_create_file(__stringify(name),	\
+					mode, dfs->rootdir,	\
+					priv_data, fop);	\
+		dfs->file_##name.dentry = d;			\
+		if (IS_ERR(d)) {				\
+			wcn36xx_warn("Create the debugfs entry failed");\
+			dfs->file_##name.dentry = NULL;		\
+		}						\
+	} while (0)
+
+
+void wcn36xx_debugfs_init(struct wcn36xx *wcn)
+{
+	struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
+
+	dfs->rootdir = debugfs_create_dir(KBUILD_MODNAME,
+					  wcn->hw->wiphy->debugfsdir);
+	if (IS_ERR(dfs->rootdir)) {
+		wcn36xx_warn("Create the debugfs failed\n");
+		dfs->rootdir = NULL;
+	}
+
+	ADD_FILE(bmps_switcher, S_IRUSR | S_IWUSR,
+		 &fops_wcn36xx_bmps, wcn);
+	ADD_FILE(dump, S_IWUSR, &fops_wcn36xx_dump, wcn);
+}
+
+void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
+{
+	struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
+	debugfs_remove_recursive(dfs->rootdir);
+}
+
+#endif /* CONFIG_WCN36XX_DEBUGFS */
-- 
1.8.2.2


^ permalink raw reply related

* [PATCH v2 03/16] wcn36xx: add debug.h
From: Eugene Krasnikov @ 2013-08-23  8:58 UTC (permalink / raw)
  To: linux-wireless; +Cc: wcn36xx, Eugene Krasnikov
In-Reply-To: <1377248299-21007-1-git-send-email-k.eugene.e@gmail.com>

Adding debug.h

Signed-off-by: Eugene Krasnikov <k.eugene.e@gmail.com>
---
 drivers/net/wireless/ath/wcn36xx/debug.h | 49 ++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)
 create mode 100644 drivers/net/wireless/ath/wcn36xx/debug.h

diff --git a/drivers/net/wireless/ath/wcn36xx/debug.h b/drivers/net/wireless/ath/wcn36xx/debug.h
new file mode 100644
index 0000000..46307aa
--- /dev/null
+++ b/drivers/net/wireless/ath/wcn36xx/debug.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WCN36XX_DEBUG_H_
+#define _WCN36XX_DEBUG_H_
+
+#include <linux/kernel.h>
+
+#define WCN36xx_MAX_DUMP_ARGS	5
+
+#ifdef CONFIG_WCN36XX_DEBUGFS
+struct wcn36xx_dfs_file {
+	struct dentry *dentry;
+	u32 value;
+};
+
+struct wcn36xx_dfs_entry {
+	struct dentry *rootdir;
+	struct wcn36xx_dfs_file file_bmps_switcher;
+	struct wcn36xx_dfs_file file_dump;
+};
+
+void wcn36xx_debugfs_init(struct wcn36xx *wcn);
+void wcn36xx_debugfs_exit(struct wcn36xx *wcn);
+
+#else
+static inline void wcn36xx_debugfs_init(struct wcn36xx *wcn)
+{
+}
+static inline void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
+{
+}
+
+#endif /* CONFIG_WCN36XX_DEBUGFS */
+
+#endif	/* _WCN36XX_DEBUG_H_ */
-- 
1.8.2.2


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox