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