193aead46428d38729f430f395a8403795a019e54Ilan Elias/*
293aead46428d38729f430f395a8403795a019e54Ilan Elias *  Texas Instrument's NFC Driver For Shared Transport.
393aead46428d38729f430f395a8403795a019e54Ilan Elias *
493aead46428d38729f430f395a8403795a019e54Ilan Elias *  NFC Driver acts as interface between NCI core and
593aead46428d38729f430f395a8403795a019e54Ilan Elias *  TI Shared Transport Layer.
693aead46428d38729f430f395a8403795a019e54Ilan Elias *
793aead46428d38729f430f395a8403795a019e54Ilan Elias *  Copyright (C) 2011 Texas Instruments, Inc.
893aead46428d38729f430f395a8403795a019e54Ilan Elias *
993aead46428d38729f430f395a8403795a019e54Ilan Elias *  Written by Ilan Elias <ilane@ti.com>
1093aead46428d38729f430f395a8403795a019e54Ilan Elias *
1193aead46428d38729f430f395a8403795a019e54Ilan Elias *  Acknowledgements:
1293aead46428d38729f430f395a8403795a019e54Ilan Elias *  This file is based on btwilink.c, which was written
1393aead46428d38729f430f395a8403795a019e54Ilan Elias *  by Raja Mani and Pavan Savoy.
1493aead46428d38729f430f395a8403795a019e54Ilan Elias *
1593aead46428d38729f430f395a8403795a019e54Ilan Elias *  This program is free software; you can redistribute it and/or modify
1693aead46428d38729f430f395a8403795a019e54Ilan Elias *  it under the terms of the GNU General Public License version 2 as
1793aead46428d38729f430f395a8403795a019e54Ilan Elias *  published by the Free Software Foundation.
1893aead46428d38729f430f395a8403795a019e54Ilan Elias *
1993aead46428d38729f430f395a8403795a019e54Ilan Elias *  This program is distributed in the hope that it will be useful,
2093aead46428d38729f430f395a8403795a019e54Ilan Elias *  but WITHOUT ANY WARRANTY; without even the implied warranty of
2193aead46428d38729f430f395a8403795a019e54Ilan Elias *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2293aead46428d38729f430f395a8403795a019e54Ilan Elias *  GNU General Public License for more details.
2393aead46428d38729f430f395a8403795a019e54Ilan Elias *
2493aead46428d38729f430f395a8403795a019e54Ilan Elias *  You should have received a copy of the GNU General Public License
2598b32decc83ed3137e3ddbc918b102f8fc406b6dJeff Kirsher *  along with this program; if not, see <http://www.gnu.org/licenses/>.
2693aead46428d38729f430f395a8403795a019e54Ilan Elias *
2793aead46428d38729f430f395a8403795a019e54Ilan Elias */
2893aead46428d38729f430f395a8403795a019e54Ilan Elias#include <linux/platform_device.h>
29baf79c33e2c67b50b4415670c7baedb9702805f5Paul Gortmaker#include <linux/module.h>
303ed1326d2e693d555e62241c2e2209f01506214eIlan Elias#include <linux/types.h>
311195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#include <linux/firmware.h>
3293aead46428d38729f430f395a8403795a019e54Ilan Elias#include <linux/nfc.h>
3393aead46428d38729f430f395a8403795a019e54Ilan Elias#include <net/nfc/nci.h>
3493aead46428d38729f430f395a8403795a019e54Ilan Elias#include <net/nfc/nci_core.h>
3593aead46428d38729f430f395a8403795a019e54Ilan Elias#include <linux/ti_wilink_st.h>
3693aead46428d38729f430f395a8403795a019e54Ilan Elias
3793aead46428d38729f430f395a8403795a019e54Ilan Elias#define NFCWILINK_CHNL			12
3893aead46428d38729f430f395a8403795a019e54Ilan Elias#define NFCWILINK_OPCODE		7
3993aead46428d38729f430f395a8403795a019e54Ilan Elias#define NFCWILINK_MAX_FRAME_SIZE	300
4093aead46428d38729f430f395a8403795a019e54Ilan Elias#define NFCWILINK_HDR_LEN		4
4193aead46428d38729f430f395a8403795a019e54Ilan Elias#define NFCWILINK_OFFSET_LEN_IN_HDR	1
4293aead46428d38729f430f395a8403795a019e54Ilan Elias#define NFCWILINK_LEN_SIZE		2
4393aead46428d38729f430f395a8403795a019e54Ilan Elias#define NFCWILINK_REGISTER_TIMEOUT	8000	/* 8 sec */
441195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define NFCWILINK_CMD_TIMEOUT		5000	/* 5 sec */
451195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
461195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define BTS_FILE_NAME_MAX_SIZE		40
471195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define BTS_FILE_HDR_MAGIC		0x42535442
481195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define BTS_FILE_CMD_MAX_LEN		0xff
491195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define BTS_FILE_ACTION_TYPE_SEND_CMD	1
501195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
511195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define NCI_VS_NFCC_INFO_CMD_GID	0x2f
521195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define NCI_VS_NFCC_INFO_CMD_OID	0x12
531195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define NCI_VS_NFCC_INFO_RSP_GID	0x4f
541195d89b2defd92829ed54938e60312e62dc8747Ilan Elias#define NCI_VS_NFCC_INFO_RSP_OID	0x12
5593aead46428d38729f430f395a8403795a019e54Ilan Elias
5693aead46428d38729f430f395a8403795a019e54Ilan Eliasstruct nfcwilink_hdr {
573ed1326d2e693d555e62241c2e2209f01506214eIlan Elias	__u8 chnl;
583ed1326d2e693d555e62241c2e2209f01506214eIlan Elias	__u8 opcode;
593ed1326d2e693d555e62241c2e2209f01506214eIlan Elias	__le16 len;
6093aead46428d38729f430f395a8403795a019e54Ilan Elias} __packed;
6193aead46428d38729f430f395a8403795a019e54Ilan Elias
621195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstruct nci_vs_nfcc_info_cmd {
631195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 gid;
641195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 oid;
651195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 plen;
661195d89b2defd92829ed54938e60312e62dc8747Ilan Elias} __packed;
671195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
681195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstruct nci_vs_nfcc_info_rsp {
691195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 gid;
701195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 oid;
711195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 plen;
721195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 status;
731195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 hw_id;
741195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 sw_ver_x;
751195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 sw_ver_z;
761195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 patch_id;
771195d89b2defd92829ed54938e60312e62dc8747Ilan Elias} __packed;
781195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
791195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstruct bts_file_hdr {
801195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__le32 magic;
811195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__le32 ver;
821195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 rfu[24];
831195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 actions[0];
841195d89b2defd92829ed54938e60312e62dc8747Ilan Elias} __packed;
851195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
861195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstruct bts_file_action {
871195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__le16 type;
881195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__le16 len;
891195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 data[0];
901195d89b2defd92829ed54938e60312e62dc8747Ilan Elias} __packed;
911195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
9293aead46428d38729f430f395a8403795a019e54Ilan Eliasstruct nfcwilink {
9393aead46428d38729f430f395a8403795a019e54Ilan Elias	struct platform_device		*pdev;
9493aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nci_dev			*ndev;
9593aead46428d38729f430f395a8403795a019e54Ilan Elias	unsigned long			flags;
9693aead46428d38729f430f395a8403795a019e54Ilan Elias
9793aead46428d38729f430f395a8403795a019e54Ilan Elias	char				st_register_cb_status;
9893aead46428d38729f430f395a8403795a019e54Ilan Elias	long				(*st_write) (struct sk_buff *);
991195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1001195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	struct completion		completed;
1011195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1021195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	struct nci_vs_nfcc_info_rsp	nfcc_info;
10393aead46428d38729f430f395a8403795a019e54Ilan Elias};
10493aead46428d38729f430f395a8403795a019e54Ilan Elias
10593aead46428d38729f430f395a8403795a019e54Ilan Elias/* NFCWILINK driver flags */
10693aead46428d38729f430f395a8403795a019e54Ilan Eliasenum {
10793aead46428d38729f430f395a8403795a019e54Ilan Elias	NFCWILINK_RUNNING,
1081195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	NFCWILINK_FW_DOWNLOAD,
10993aead46428d38729f430f395a8403795a019e54Ilan Elias};
11093aead46428d38729f430f395a8403795a019e54Ilan Elias
1111095e69f47926db6f1350a9d6a38626521580e87Frederic Danisstatic int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb);
1121195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1131195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstatic inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
1141195d89b2defd92829ed54938e60312e62dc8747Ilan Elias{
1151195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	struct sk_buff *skb;
1161195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1171195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	skb = alloc_skb(len + NFCWILINK_HDR_LEN, how);
1181195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (skb)
1191195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		skb_reserve(skb, NFCWILINK_HDR_LEN);
1201195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1211195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	return skb;
1221195d89b2defd92829ed54938e60312e62dc8747Ilan Elias}
1231195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1241195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstatic void nfcwilink_fw_download_receive(struct nfcwilink *drv,
1251195d89b2defd92829ed54938e60312e62dc8747Ilan Elias						struct sk_buff *skb)
1261195d89b2defd92829ed54938e60312e62dc8747Ilan Elias{
1271195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data;
1281195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1291195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	/* Detect NCI_VS_NFCC_INFO_RSP and store the result */
1301195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) &&
1311195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		(rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) {
1321195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		memcpy(&drv->nfcc_info, rsp,
1331195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			sizeof(struct nci_vs_nfcc_info_rsp));
1341195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
1351195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1361195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	kfree_skb(skb);
1371195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1381195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	complete(&drv->completed);
1391195d89b2defd92829ed54938e60312e62dc8747Ilan Elias}
1401195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1411195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstatic int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
1421195d89b2defd92829ed54938e60312e62dc8747Ilan Elias{
1431195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	struct nci_vs_nfcc_info_cmd *cmd;
1441195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	struct sk_buff *skb;
1451195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	unsigned long comp_ret;
1461195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	int rc;
1471195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1481195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd),
1491195d89b2defd92829ed54938e60312e62dc8747Ilan Elias					GFP_KERNEL);
1501195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (!skb) {
151073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev,
152073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches			"no memory for nci_vs_nfcc_info_cmd\n");
1531195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return -ENOMEM;
1541195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
1551195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1561195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	cmd = (struct nci_vs_nfcc_info_cmd *)
1571195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd));
1581195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	cmd->gid = NCI_VS_NFCC_INFO_CMD_GID;
1591195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	cmd->oid = NCI_VS_NFCC_INFO_CMD_OID;
1601195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	cmd->plen = 0;
1611195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1621195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	drv->nfcc_info.plen = 0;
1631195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1641095e69f47926db6f1350a9d6a38626521580e87Frederic Danis	rc = nfcwilink_send(drv->ndev, skb);
1651195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (rc)
1661195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return rc;
1671195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1681195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	comp_ret = wait_for_completion_timeout(&drv->completed,
1691195d89b2defd92829ed54938e60312e62dc8747Ilan Elias				msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
170b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches	dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
171b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches		comp_ret);
1721195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (comp_ret == 0) {
17317936b43f0fdede23582d83a45622751409c99b9Joe Perches		nfc_err(&drv->pdev->dev,
174b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches			"timeout on wait_for_completion_timeout\n");
1751195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return -ETIMEDOUT;
1761195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
1771195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
178b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches	dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n",
179b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches		drv->nfcc_info.plen, drv->nfcc_info.status);
1801195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1811195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) {
182073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n");
1831195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return -EINVAL;
1841195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
1851195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1861195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	snprintf(file_name, BTS_FILE_NAME_MAX_SIZE,
1871195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			"TINfcInit_%d.%d.%d.%d.bts",
1881195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			drv->nfcc_info.hw_id,
1891195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			drv->nfcc_info.sw_ver_x,
1901195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			drv->nfcc_info.sw_ver_z,
1911195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			drv->nfcc_info.patch_id);
1921195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
193073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches	nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name);
1941195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1951195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	return 0;
1961195d89b2defd92829ed54938e60312e62dc8747Ilan Elias}
1971195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
1981195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstatic int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
1991195d89b2defd92829ed54938e60312e62dc8747Ilan Elias{
2001195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data;
2011195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	struct sk_buff *skb;
2021195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	unsigned long comp_ret;
2031195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	int rc;
2041195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2051195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	/* verify valid cmd for the NFC channel */
2061195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if ((len <= sizeof(struct nfcwilink_hdr)) ||
2071195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		(len > BTS_FILE_CMD_MAX_LEN) ||
2081195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		(hdr->chnl != NFCWILINK_CHNL) ||
2091195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		(hdr->opcode != NFCWILINK_OPCODE)) {
210073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev,
211073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches			"ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n",
2121195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			len, hdr->chnl, hdr->opcode);
2131195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return 0;
2141195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
2151195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2161195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	/* remove the ST header */
2171195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	len -= sizeof(struct nfcwilink_hdr);
2181195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	data += sizeof(struct nfcwilink_hdr);
2191195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2201195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	skb = nfcwilink_skb_alloc(len, GFP_KERNEL);
2211195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (!skb) {
222073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev, "no memory for bts cmd\n");
2231195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return -ENOMEM;
2241195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
2251195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2261195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	memcpy(skb_put(skb, len), data, len);
2271195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2281095e69f47926db6f1350a9d6a38626521580e87Frederic Danis	rc = nfcwilink_send(drv->ndev, skb);
2291195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (rc)
2301195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return rc;
2311195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2321195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	comp_ret = wait_for_completion_timeout(&drv->completed,
2331195d89b2defd92829ed54938e60312e62dc8747Ilan Elias				msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
234b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches	dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
235b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches		comp_ret);
2361195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (comp_ret == 0) {
237073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev,
238073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches			"timeout on wait_for_completion_timeout\n");
2391195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return -ETIMEDOUT;
2401195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
2411195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2421195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	return 0;
2431195d89b2defd92829ed54938e60312e62dc8747Ilan Elias}
2441195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2451195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasstatic int nfcwilink_download_fw(struct nfcwilink *drv)
2461195d89b2defd92829ed54938e60312e62dc8747Ilan Elias{
2471195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	unsigned char file_name[BTS_FILE_NAME_MAX_SIZE];
2481195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	const struct firmware *fw;
2491195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u16 action_type, action_len;
2501195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	__u8 *ptr;
2511195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	int len, rc;
2521195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2531195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
2541195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2551195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	rc = nfcwilink_get_bts_file_name(drv, file_name);
2561195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (rc)
2571195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		goto exit;
2581195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2591195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	rc = request_firmware(&fw, file_name, &drv->pdev->dev);
2601195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (rc) {
261073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc);
2621195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2631195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		/* if the file is not found, don't exit with failure */
2641195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		if (rc == -ENOENT)
2651195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			rc = 0;
2661195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2671195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		goto exit;
2681195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
2691195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2701195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	len = fw->size;
2711195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	ptr = (__u8 *)fw->data;
2721195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2731195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if ((len == 0) || (ptr == NULL)) {
274b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches		dev_dbg(&drv->pdev->dev,
275b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches			"request_firmware returned size %d\n", len);
2761195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		goto release_fw;
2771195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
2781195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2791195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) !=
2801195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			BTS_FILE_HDR_MAGIC) {
281073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev, "wrong bts magic number\n");
2821195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		rc = -EINVAL;
2831195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		goto release_fw;
2841195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
2851195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2861195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	/* remove the BTS header */
2871195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	len -= sizeof(struct bts_file_hdr);
2881195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	ptr += sizeof(struct bts_file_hdr);
2891195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2901195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	while (len > 0) {
2911195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		action_type =
2921195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			__le16_to_cpu(((struct bts_file_action *)ptr)->type);
2931195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		action_len =
2941195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			__le16_to_cpu(((struct bts_file_action *)ptr)->len);
2951195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
296b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches		dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n",
297b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches			action_type, action_len);
2981195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
2991195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		switch (action_type) {
3001195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		case BTS_FILE_ACTION_TYPE_SEND_CMD:
3011195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			rc = nfcwilink_send_bts_cmd(drv,
3021195d89b2defd92829ed54938e60312e62dc8747Ilan Elias					((struct bts_file_action *)ptr)->data,
3031195d89b2defd92829ed54938e60312e62dc8747Ilan Elias					action_len);
3041195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			if (rc)
3051195d89b2defd92829ed54938e60312e62dc8747Ilan Elias				goto release_fw;
3061195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			break;
3071195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		}
3081195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
3091195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		/* advance to the next action */
3101195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		len -= (sizeof(struct bts_file_action) + action_len);
3111195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		ptr += (sizeof(struct bts_file_action) + action_len);
3121195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
3131195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
3141195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasrelease_fw:
3151195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	release_firmware(fw);
3161195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
3171195d89b2defd92829ed54938e60312e62dc8747Ilan Eliasexit:
3181195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
3191195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	return rc;
3201195d89b2defd92829ed54938e60312e62dc8747Ilan Elias}
3211195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
32293aead46428d38729f430f395a8403795a019e54Ilan Elias/* Called by ST when registration is complete */
32393aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic void nfcwilink_register_complete(void *priv_data, char data)
32493aead46428d38729f430f395a8403795a019e54Ilan Elias{
32593aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nfcwilink *drv = priv_data;
32693aead46428d38729f430f395a8403795a019e54Ilan Elias
32793aead46428d38729f430f395a8403795a019e54Ilan Elias	/* store ST registration status */
32893aead46428d38729f430f395a8403795a019e54Ilan Elias	drv->st_register_cb_status = data;
32993aead46428d38729f430f395a8403795a019e54Ilan Elias
33093aead46428d38729f430f395a8403795a019e54Ilan Elias	/* complete the wait in nfc_st_open() */
3311195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	complete(&drv->completed);
33293aead46428d38729f430f395a8403795a019e54Ilan Elias}
33393aead46428d38729f430f395a8403795a019e54Ilan Elias
33493aead46428d38729f430f395a8403795a019e54Ilan Elias/* Called by ST when receive data is available */
33593aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
33693aead46428d38729f430f395a8403795a019e54Ilan Elias{
33793aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nfcwilink *drv = priv_data;
33893aead46428d38729f430f395a8403795a019e54Ilan Elias	int rc;
33993aead46428d38729f430f395a8403795a019e54Ilan Elias
34093aead46428d38729f430f395a8403795a019e54Ilan Elias	if (!skb)
34193aead46428d38729f430f395a8403795a019e54Ilan Elias		return -EFAULT;
34293aead46428d38729f430f395a8403795a019e54Ilan Elias
34393aead46428d38729f430f395a8403795a019e54Ilan Elias	if (!drv) {
34493aead46428d38729f430f395a8403795a019e54Ilan Elias		kfree_skb(skb);
34593aead46428d38729f430f395a8403795a019e54Ilan Elias		return -EFAULT;
34693aead46428d38729f430f395a8403795a019e54Ilan Elias	}
34793aead46428d38729f430f395a8403795a019e54Ilan Elias
348b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches	dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len);
349d6650a2ccfdcf536841c2f880a1a6f19fce2e3a1Wei Yongjun
35093aead46428d38729f430f395a8403795a019e54Ilan Elias	/* strip the ST header
35193aead46428d38729f430f395a8403795a019e54Ilan Elias	(apart for the chnl byte, which is not received in the hdr) */
35293aead46428d38729f430f395a8403795a019e54Ilan Elias	skb_pull(skb, (NFCWILINK_HDR_LEN-1));
35393aead46428d38729f430f395a8403795a019e54Ilan Elias
3541195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) {
3551195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		nfcwilink_fw_download_receive(drv, skb);
3561195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		return 0;
3571195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
3581195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
35993aead46428d38729f430f395a8403795a019e54Ilan Elias	/* Forward skb to NCI core layer */
3601095e69f47926db6f1350a9d6a38626521580e87Frederic Danis	rc = nci_recv_frame(drv->ndev, skb);
36193aead46428d38729f430f395a8403795a019e54Ilan Elias	if (rc < 0) {
362073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc);
36393aead46428d38729f430f395a8403795a019e54Ilan Elias		return rc;
36493aead46428d38729f430f395a8403795a019e54Ilan Elias	}
36593aead46428d38729f430f395a8403795a019e54Ilan Elias
36693aead46428d38729f430f395a8403795a019e54Ilan Elias	return 0;
36793aead46428d38729f430f395a8403795a019e54Ilan Elias}
36893aead46428d38729f430f395a8403795a019e54Ilan Elias
36993aead46428d38729f430f395a8403795a019e54Ilan Elias/* protocol structure registered with ST */
37093aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic struct st_proto_s nfcwilink_proto = {
37193aead46428d38729f430f395a8403795a019e54Ilan Elias	.chnl_id = NFCWILINK_CHNL,
37293aead46428d38729f430f395a8403795a019e54Ilan Elias	.max_frame_size = NFCWILINK_MAX_FRAME_SIZE,
37393aead46428d38729f430f395a8403795a019e54Ilan Elias	.hdr_len = (NFCWILINK_HDR_LEN-1),	/* not including chnl byte */
37493aead46428d38729f430f395a8403795a019e54Ilan Elias	.offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR,
37593aead46428d38729f430f395a8403795a019e54Ilan Elias	.len_size = NFCWILINK_LEN_SIZE,
37693aead46428d38729f430f395a8403795a019e54Ilan Elias	.reserve = 0,
37793aead46428d38729f430f395a8403795a019e54Ilan Elias	.recv = nfcwilink_receive,
37893aead46428d38729f430f395a8403795a019e54Ilan Elias	.reg_complete_cb = nfcwilink_register_complete,
37993aead46428d38729f430f395a8403795a019e54Ilan Elias	.write = NULL,
38093aead46428d38729f430f395a8403795a019e54Ilan Elias};
38193aead46428d38729f430f395a8403795a019e54Ilan Elias
38293aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic int nfcwilink_open(struct nci_dev *ndev)
38393aead46428d38729f430f395a8403795a019e54Ilan Elias{
38493aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nfcwilink *drv = nci_get_drvdata(ndev);
38593aead46428d38729f430f395a8403795a019e54Ilan Elias	unsigned long comp_ret;
38693aead46428d38729f430f395a8403795a019e54Ilan Elias	int rc;
38793aead46428d38729f430f395a8403795a019e54Ilan Elias
38893aead46428d38729f430f395a8403795a019e54Ilan Elias	if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) {
38993aead46428d38729f430f395a8403795a019e54Ilan Elias		rc = -EBUSY;
39093aead46428d38729f430f395a8403795a019e54Ilan Elias		goto exit;
39193aead46428d38729f430f395a8403795a019e54Ilan Elias	}
39293aead46428d38729f430f395a8403795a019e54Ilan Elias
39393aead46428d38729f430f395a8403795a019e54Ilan Elias	nfcwilink_proto.priv_data = drv;
39493aead46428d38729f430f395a8403795a019e54Ilan Elias
3951195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	init_completion(&drv->completed);
39693aead46428d38729f430f395a8403795a019e54Ilan Elias	drv->st_register_cb_status = -EINPROGRESS;
39793aead46428d38729f430f395a8403795a019e54Ilan Elias
39893aead46428d38729f430f395a8403795a019e54Ilan Elias	rc = st_register(&nfcwilink_proto);
39993aead46428d38729f430f395a8403795a019e54Ilan Elias	if (rc < 0) {
40093aead46428d38729f430f395a8403795a019e54Ilan Elias		if (rc == -EINPROGRESS) {
40193aead46428d38729f430f395a8403795a019e54Ilan Elias			comp_ret = wait_for_completion_timeout(
4021195d89b2defd92829ed54938e60312e62dc8747Ilan Elias			&drv->completed,
40393aead46428d38729f430f395a8403795a019e54Ilan Elias			msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT));
40493aead46428d38729f430f395a8403795a019e54Ilan Elias
405b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches			dev_dbg(&drv->pdev->dev,
406b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches				"wait_for_completion_timeout returned %ld\n",
407b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches				comp_ret);
40893aead46428d38729f430f395a8403795a019e54Ilan Elias
40993aead46428d38729f430f395a8403795a019e54Ilan Elias			if (comp_ret == 0) {
41093aead46428d38729f430f395a8403795a019e54Ilan Elias				/* timeout */
41193aead46428d38729f430f395a8403795a019e54Ilan Elias				rc = -ETIMEDOUT;
41293aead46428d38729f430f395a8403795a019e54Ilan Elias				goto clear_exit;
41393aead46428d38729f430f395a8403795a019e54Ilan Elias			} else if (drv->st_register_cb_status != 0) {
41493aead46428d38729f430f395a8403795a019e54Ilan Elias				rc = drv->st_register_cb_status;
415073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches				nfc_err(&drv->pdev->dev,
416073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches					"st_register_cb failed %d\n", rc);
41793aead46428d38729f430f395a8403795a019e54Ilan Elias				goto clear_exit;
41893aead46428d38729f430f395a8403795a019e54Ilan Elias			}
41993aead46428d38729f430f395a8403795a019e54Ilan Elias		} else {
420073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches			nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc);
42193aead46428d38729f430f395a8403795a019e54Ilan Elias			goto clear_exit;
42293aead46428d38729f430f395a8403795a019e54Ilan Elias		}
42393aead46428d38729f430f395a8403795a019e54Ilan Elias	}
42493aead46428d38729f430f395a8403795a019e54Ilan Elias
42593aead46428d38729f430f395a8403795a019e54Ilan Elias	/* st_register MUST fill the write callback */
42693aead46428d38729f430f395a8403795a019e54Ilan Elias	BUG_ON(nfcwilink_proto.write == NULL);
42793aead46428d38729f430f395a8403795a019e54Ilan Elias	drv->st_write = nfcwilink_proto.write;
42893aead46428d38729f430f395a8403795a019e54Ilan Elias
4291195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	if (nfcwilink_download_fw(drv)) {
430073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n",
431073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches			rc);
4321195d89b2defd92829ed54938e60312e62dc8747Ilan Elias		/* open should succeed, even if the FW download failed */
4331195d89b2defd92829ed54938e60312e62dc8747Ilan Elias	}
4341195d89b2defd92829ed54938e60312e62dc8747Ilan Elias
43593aead46428d38729f430f395a8403795a019e54Ilan Elias	goto exit;
43693aead46428d38729f430f395a8403795a019e54Ilan Elias
43793aead46428d38729f430f395a8403795a019e54Ilan Eliasclear_exit:
43893aead46428d38729f430f395a8403795a019e54Ilan Elias	clear_bit(NFCWILINK_RUNNING, &drv->flags);
43993aead46428d38729f430f395a8403795a019e54Ilan Elias
44093aead46428d38729f430f395a8403795a019e54Ilan Eliasexit:
44193aead46428d38729f430f395a8403795a019e54Ilan Elias	return rc;
44293aead46428d38729f430f395a8403795a019e54Ilan Elias}
44393aead46428d38729f430f395a8403795a019e54Ilan Elias
44493aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic int nfcwilink_close(struct nci_dev *ndev)
44593aead46428d38729f430f395a8403795a019e54Ilan Elias{
44693aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nfcwilink *drv = nci_get_drvdata(ndev);
44793aead46428d38729f430f395a8403795a019e54Ilan Elias	int rc;
44893aead46428d38729f430f395a8403795a019e54Ilan Elias
44993aead46428d38729f430f395a8403795a019e54Ilan Elias	if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags))
45093aead46428d38729f430f395a8403795a019e54Ilan Elias		return 0;
45193aead46428d38729f430f395a8403795a019e54Ilan Elias
45293aead46428d38729f430f395a8403795a019e54Ilan Elias	rc = st_unregister(&nfcwilink_proto);
45393aead46428d38729f430f395a8403795a019e54Ilan Elias	if (rc)
454073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc);
45593aead46428d38729f430f395a8403795a019e54Ilan Elias
45693aead46428d38729f430f395a8403795a019e54Ilan Elias	drv->st_write = NULL;
45793aead46428d38729f430f395a8403795a019e54Ilan Elias
45893aead46428d38729f430f395a8403795a019e54Ilan Elias	return rc;
45993aead46428d38729f430f395a8403795a019e54Ilan Elias}
46093aead46428d38729f430f395a8403795a019e54Ilan Elias
4611095e69f47926db6f1350a9d6a38626521580e87Frederic Danisstatic int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
46293aead46428d38729f430f395a8403795a019e54Ilan Elias{
46393aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nfcwilink *drv = nci_get_drvdata(ndev);
46493aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
46593aead46428d38729f430f395a8403795a019e54Ilan Elias	long len;
46693aead46428d38729f430f395a8403795a019e54Ilan Elias
467b48348395ff665f49c7c684c93c5ce09fd0a0307Joe Perches	dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len);
46893aead46428d38729f430f395a8403795a019e54Ilan Elias
469ea9917d6f9e355646258b8d08ac69108908618a2Ilan Elias	if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) {
470ea9917d6f9e355646258b8d08ac69108908618a2Ilan Elias		kfree_skb(skb);
471ea9917d6f9e355646258b8d08ac69108908618a2Ilan Elias		return -EINVAL;
472ea9917d6f9e355646258b8d08ac69108908618a2Ilan Elias	}
47393aead46428d38729f430f395a8403795a019e54Ilan Elias
47493aead46428d38729f430f395a8403795a019e54Ilan Elias	/* add the ST hdr to the start of the buffer */
4753ed1326d2e693d555e62241c2e2209f01506214eIlan Elias	hdr.len = cpu_to_le16(skb->len);
47693aead46428d38729f430f395a8403795a019e54Ilan Elias	memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN);
47793aead46428d38729f430f395a8403795a019e54Ilan Elias
47893aead46428d38729f430f395a8403795a019e54Ilan Elias	/* Insert skb to shared transport layer's transmit queue.
47993aead46428d38729f430f395a8403795a019e54Ilan Elias	 * Freeing skb memory is taken care in shared transport layer,
48093aead46428d38729f430f395a8403795a019e54Ilan Elias	 * so don't free skb memory here.
48193aead46428d38729f430f395a8403795a019e54Ilan Elias	 */
48293aead46428d38729f430f395a8403795a019e54Ilan Elias	len = drv->st_write(skb);
48393aead46428d38729f430f395a8403795a019e54Ilan Elias	if (len < 0) {
48493aead46428d38729f430f395a8403795a019e54Ilan Elias		kfree_skb(skb);
485073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len);
48693aead46428d38729f430f395a8403795a019e54Ilan Elias		return -EFAULT;
48793aead46428d38729f430f395a8403795a019e54Ilan Elias	}
48893aead46428d38729f430f395a8403795a019e54Ilan Elias
48993aead46428d38729f430f395a8403795a019e54Ilan Elias	return 0;
49093aead46428d38729f430f395a8403795a019e54Ilan Elias}
49193aead46428d38729f430f395a8403795a019e54Ilan Elias
49293aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic struct nci_ops nfcwilink_ops = {
49393aead46428d38729f430f395a8403795a019e54Ilan Elias	.open = nfcwilink_open,
49493aead46428d38729f430f395a8403795a019e54Ilan Elias	.close = nfcwilink_close,
49593aead46428d38729f430f395a8403795a019e54Ilan Elias	.send = nfcwilink_send,
49693aead46428d38729f430f395a8403795a019e54Ilan Elias};
49793aead46428d38729f430f395a8403795a019e54Ilan Elias
49893aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic int nfcwilink_probe(struct platform_device *pdev)
49993aead46428d38729f430f395a8403795a019e54Ilan Elias{
50093aead46428d38729f430f395a8403795a019e54Ilan Elias	static struct nfcwilink *drv;
50193aead46428d38729f430f395a8403795a019e54Ilan Elias	int rc;
5023ed1326d2e693d555e62241c2e2209f01506214eIlan Elias	__u32 protocols;
50393aead46428d38729f430f395a8403795a019e54Ilan Elias
5045f4d6214ef5e9b1ff6a72ddfa387c1d72adfac98Julia Lawall	drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL);
50593aead46428d38729f430f395a8403795a019e54Ilan Elias	if (!drv) {
50693aead46428d38729f430f395a8403795a019e54Ilan Elias		rc = -ENOMEM;
50793aead46428d38729f430f395a8403795a019e54Ilan Elias		goto exit;
50893aead46428d38729f430f395a8403795a019e54Ilan Elias	}
50993aead46428d38729f430f395a8403795a019e54Ilan Elias
51093aead46428d38729f430f395a8403795a019e54Ilan Elias	drv->pdev = pdev;
51193aead46428d38729f430f395a8403795a019e54Ilan Elias
51293aead46428d38729f430f395a8403795a019e54Ilan Elias	protocols = NFC_PROTO_JEWEL_MASK
51301d719a2287ec34f631800d10f1fad3c134c3e89Samuel Ortiz		| NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
51401d719a2287ec34f631800d10f1fad3c134c3e89Samuel Ortiz		| NFC_PROTO_ISO14443_MASK
51501d719a2287ec34f631800d10f1fad3c134c3e89Samuel Ortiz		| NFC_PROTO_ISO14443_B_MASK
51601d719a2287ec34f631800d10f1fad3c134c3e89Samuel Ortiz		| NFC_PROTO_NFC_DEP_MASK;
51793aead46428d38729f430f395a8403795a019e54Ilan Elias
51893aead46428d38729f430f395a8403795a019e54Ilan Elias	drv->ndev = nci_allocate_device(&nfcwilink_ops,
51993aead46428d38729f430f395a8403795a019e54Ilan Elias					protocols,
52093aead46428d38729f430f395a8403795a019e54Ilan Elias					NFCWILINK_HDR_LEN,
52193aead46428d38729f430f395a8403795a019e54Ilan Elias					0);
52293aead46428d38729f430f395a8403795a019e54Ilan Elias	if (!drv->ndev) {
523073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&pdev->dev, "nci_allocate_device failed\n");
52493aead46428d38729f430f395a8403795a019e54Ilan Elias		rc = -ENOMEM;
5255f4d6214ef5e9b1ff6a72ddfa387c1d72adfac98Julia Lawall		goto exit;
52693aead46428d38729f430f395a8403795a019e54Ilan Elias	}
52793aead46428d38729f430f395a8403795a019e54Ilan Elias
52893aead46428d38729f430f395a8403795a019e54Ilan Elias	nci_set_parent_dev(drv->ndev, &pdev->dev);
52993aead46428d38729f430f395a8403795a019e54Ilan Elias	nci_set_drvdata(drv->ndev, drv);
53093aead46428d38729f430f395a8403795a019e54Ilan Elias
53193aead46428d38729f430f395a8403795a019e54Ilan Elias	rc = nci_register_device(drv->ndev);
53293aead46428d38729f430f395a8403795a019e54Ilan Elias	if (rc < 0) {
533073a625f0b80fb7613220a56375b0f3d2831af1bJoe Perches		nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc);
53493aead46428d38729f430f395a8403795a019e54Ilan Elias		goto free_dev_exit;
53593aead46428d38729f430f395a8403795a019e54Ilan Elias	}
53693aead46428d38729f430f395a8403795a019e54Ilan Elias
53793aead46428d38729f430f395a8403795a019e54Ilan Elias	dev_set_drvdata(&pdev->dev, drv);
53893aead46428d38729f430f395a8403795a019e54Ilan Elias
53993aead46428d38729f430f395a8403795a019e54Ilan Elias	goto exit;
54093aead46428d38729f430f395a8403795a019e54Ilan Elias
54193aead46428d38729f430f395a8403795a019e54Ilan Eliasfree_dev_exit:
54293aead46428d38729f430f395a8403795a019e54Ilan Elias	nci_free_device(drv->ndev);
54393aead46428d38729f430f395a8403795a019e54Ilan Elias
54493aead46428d38729f430f395a8403795a019e54Ilan Eliasexit:
54593aead46428d38729f430f395a8403795a019e54Ilan Elias	return rc;
54693aead46428d38729f430f395a8403795a019e54Ilan Elias}
54793aead46428d38729f430f395a8403795a019e54Ilan Elias
54893aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic int nfcwilink_remove(struct platform_device *pdev)
54993aead46428d38729f430f395a8403795a019e54Ilan Elias{
55093aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nfcwilink *drv = dev_get_drvdata(&pdev->dev);
55193aead46428d38729f430f395a8403795a019e54Ilan Elias	struct nci_dev *ndev;
55293aead46428d38729f430f395a8403795a019e54Ilan Elias
55393aead46428d38729f430f395a8403795a019e54Ilan Elias	if (!drv)
55493aead46428d38729f430f395a8403795a019e54Ilan Elias		return -EFAULT;
55593aead46428d38729f430f395a8403795a019e54Ilan Elias
55693aead46428d38729f430f395a8403795a019e54Ilan Elias	ndev = drv->ndev;
55793aead46428d38729f430f395a8403795a019e54Ilan Elias
55893aead46428d38729f430f395a8403795a019e54Ilan Elias	nci_unregister_device(ndev);
55993aead46428d38729f430f395a8403795a019e54Ilan Elias	nci_free_device(ndev);
56093aead46428d38729f430f395a8403795a019e54Ilan Elias
56193aead46428d38729f430f395a8403795a019e54Ilan Elias	return 0;
56293aead46428d38729f430f395a8403795a019e54Ilan Elias}
56393aead46428d38729f430f395a8403795a019e54Ilan Elias
56493aead46428d38729f430f395a8403795a019e54Ilan Eliasstatic struct platform_driver nfcwilink_driver = {
56593aead46428d38729f430f395a8403795a019e54Ilan Elias	.probe = nfcwilink_probe,
56693aead46428d38729f430f395a8403795a019e54Ilan Elias	.remove = nfcwilink_remove,
56793aead46428d38729f430f395a8403795a019e54Ilan Elias	.driver = {
56893aead46428d38729f430f395a8403795a019e54Ilan Elias		.name = "nfcwilink",
56993aead46428d38729f430f395a8403795a019e54Ilan Elias		.owner = THIS_MODULE,
57093aead46428d38729f430f395a8403795a019e54Ilan Elias	},
57193aead46428d38729f430f395a8403795a019e54Ilan Elias};
57293aead46428d38729f430f395a8403795a019e54Ilan Elias
573058576ddfb31762bfce70086a3c12678a97c4d75Syam Sidhardhanmodule_platform_driver(nfcwilink_driver);
57493aead46428d38729f430f395a8403795a019e54Ilan Elias
57593aead46428d38729f430f395a8403795a019e54Ilan Elias/* ------ Module Info ------ */
57693aead46428d38729f430f395a8403795a019e54Ilan Elias
57793aead46428d38729f430f395a8403795a019e54Ilan EliasMODULE_AUTHOR("Ilan Elias <ilane@ti.com>");
57893aead46428d38729f430f395a8403795a019e54Ilan EliasMODULE_DESCRIPTION("NFC Driver for TI Shared Transport");
57993aead46428d38729f430f395a8403795a019e54Ilan EliasMODULE_LICENSE("GPL");
580