mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-02 03:56:39 +07:00
96e324024b
xmit callback provided by a driver encapsulates upper layers data and sends it to the hardware. So, HCI does not know the exact amount of data being sent and thus can't handle partially sent frames properly. Therefore, the driver must return 0 for completely sent frame or negative for failure. Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> Acked-by: Eric Lapuyade <eric.lapuyade@intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
1024 lines
24 KiB
C
1024 lines
24 KiB
C
/*
|
|
* HCI based Driver for NXP PN544 NFC Chip
|
|
*
|
|
* Copyright (C) 2012 Intel Corporation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the
|
|
* Free Software Foundation, Inc.,
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <linux/crc-ccitt.h>
|
|
#include <linux/module.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/nfc.h>
|
|
#include <net/nfc/hci.h>
|
|
#include <net/nfc/llc.h>
|
|
|
|
#include <linux/nfc/pn544.h>
|
|
|
|
#define DRIVER_DESC "HCI NFC driver for PN544"
|
|
|
|
#define PN544_HCI_DRIVER_NAME "pn544_hci"
|
|
|
|
/* Timing restrictions (ms) */
|
|
#define PN544_HCI_RESETVEN_TIME 30
|
|
|
|
static struct i2c_device_id pn544_hci_id_table[] = {
|
|
{"pn544", 0},
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, pn544_hci_id_table);
|
|
|
|
#define HCI_MODE 0
|
|
#define FW_MODE 1
|
|
|
|
/* framing in HCI mode */
|
|
#define PN544_HCI_LLC_LEN 1
|
|
#define PN544_HCI_LLC_CRC 2
|
|
#define PN544_HCI_LLC_LEN_CRC (PN544_HCI_LLC_LEN + PN544_HCI_LLC_CRC)
|
|
#define PN544_HCI_LLC_MIN_SIZE (1 + PN544_HCI_LLC_LEN_CRC)
|
|
#define PN544_HCI_LLC_MAX_PAYLOAD 29
|
|
#define PN544_HCI_LLC_MAX_SIZE (PN544_HCI_LLC_LEN_CRC + 1 + \
|
|
PN544_HCI_LLC_MAX_PAYLOAD)
|
|
|
|
enum pn544_state {
|
|
PN544_ST_COLD,
|
|
PN544_ST_FW_READY,
|
|
PN544_ST_READY,
|
|
};
|
|
|
|
#define FULL_VERSION_LEN 11
|
|
|
|
/* Proprietary commands */
|
|
#define PN544_WRITE 0x3f
|
|
|
|
/* Proprietary gates, events, commands and registers */
|
|
|
|
/* NFC_HCI_RF_READER_A_GATE additional registers and commands */
|
|
#define PN544_RF_READER_A_AUTO_ACTIVATION 0x10
|
|
#define PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION 0x12
|
|
#define PN544_MIFARE_CMD 0x21
|
|
|
|
/* Commands that apply to all RF readers */
|
|
#define PN544_RF_READER_CMD_PRESENCE_CHECK 0x30
|
|
#define PN544_RF_READER_CMD_ACTIVATE_NEXT 0x32
|
|
|
|
/* NFC_HCI_ID_MGMT_GATE additional registers */
|
|
#define PN544_ID_MGMT_FULL_VERSION_SW 0x10
|
|
|
|
#define PN544_RF_READER_ISO15693_GATE 0x12
|
|
|
|
#define PN544_RF_READER_F_GATE 0x14
|
|
#define PN544_FELICA_ID 0x04
|
|
#define PN544_FELICA_RAW 0x20
|
|
|
|
#define PN544_RF_READER_JEWEL_GATE 0x15
|
|
#define PN544_JEWEL_RAW_CMD 0x23
|
|
|
|
#define PN544_RF_READER_NFCIP1_INITIATOR_GATE 0x30
|
|
#define PN544_RF_READER_NFCIP1_TARGET_GATE 0x31
|
|
|
|
#define PN544_SYS_MGMT_GATE 0x90
|
|
#define PN544_SYS_MGMT_INFO_NOTIFICATION 0x02
|
|
|
|
#define PN544_POLLING_LOOP_MGMT_GATE 0x94
|
|
#define PN544_PL_RDPHASES 0x06
|
|
#define PN544_PL_EMULATION 0x07
|
|
#define PN544_PL_NFCT_DEACTIVATED 0x09
|
|
|
|
#define PN544_SWP_MGMT_GATE 0xA0
|
|
|
|
#define PN544_NFC_WI_MGMT_GATE 0xA1
|
|
|
|
static struct nfc_hci_gate pn544_gates[] = {
|
|
{NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE},
|
|
{NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE},
|
|
{NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE},
|
|
{NFC_HCI_LINK_MGMT_GATE, NFC_HCI_INVALID_PIPE},
|
|
{NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE},
|
|
{NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_SYS_MGMT_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_SWP_MGMT_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_POLLING_LOOP_MGMT_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_NFC_WI_MGMT_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_RF_READER_JEWEL_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_RF_READER_NFCIP1_INITIATOR_GATE, NFC_HCI_INVALID_PIPE},
|
|
{PN544_RF_READER_NFCIP1_TARGET_GATE, NFC_HCI_INVALID_PIPE}
|
|
};
|
|
|
|
/* Largest headroom needed for outgoing custom commands */
|
|
#define PN544_CMDS_HEADROOM 2
|
|
#define PN544_FRAME_HEADROOM 1
|
|
#define PN544_FRAME_TAILROOM 2
|
|
|
|
struct pn544_hci_info {
|
|
struct i2c_client *i2c_dev;
|
|
struct nfc_hci_dev *hdev;
|
|
|
|
enum pn544_state state;
|
|
|
|
struct mutex info_lock;
|
|
|
|
unsigned int gpio_en;
|
|
unsigned int gpio_irq;
|
|
unsigned int gpio_fw;
|
|
unsigned int en_polarity;
|
|
|
|
int hard_fault; /*
|
|
* < 0 if hardware error occured (e.g. i2c err)
|
|
* and prevents normal operation.
|
|
*/
|
|
int async_cb_type;
|
|
data_exchange_cb_t async_cb;
|
|
void *async_cb_context;
|
|
};
|
|
|
|
static void pn544_hci_platform_init(struct pn544_hci_info *info)
|
|
{
|
|
int polarity, retry, ret;
|
|
char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 };
|
|
int count = sizeof(rset_cmd);
|
|
|
|
pr_info(DRIVER_DESC ": %s\n", __func__);
|
|
dev_info(&info->i2c_dev->dev, "Detecting nfc_en polarity\n");
|
|
|
|
/* Disable fw download */
|
|
gpio_set_value(info->gpio_fw, 0);
|
|
|
|
for (polarity = 0; polarity < 2; polarity++) {
|
|
info->en_polarity = polarity;
|
|
retry = 3;
|
|
while (retry--) {
|
|
/* power off */
|
|
gpio_set_value(info->gpio_en, !info->en_polarity);
|
|
usleep_range(10000, 15000);
|
|
|
|
/* power on */
|
|
gpio_set_value(info->gpio_en, info->en_polarity);
|
|
usleep_range(10000, 15000);
|
|
|
|
/* send reset */
|
|
dev_dbg(&info->i2c_dev->dev, "Sending reset cmd\n");
|
|
ret = i2c_master_send(info->i2c_dev, rset_cmd, count);
|
|
if (ret == count) {
|
|
dev_info(&info->i2c_dev->dev,
|
|
"nfc_en polarity : active %s\n",
|
|
(polarity == 0 ? "low" : "high"));
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
dev_err(&info->i2c_dev->dev,
|
|
"Could not detect nfc_en polarity, fallback to active high\n");
|
|
|
|
out:
|
|
gpio_set_value(info->gpio_en, !info->en_polarity);
|
|
}
|
|
|
|
static int pn544_hci_enable(struct pn544_hci_info *info, int mode)
|
|
{
|
|
pr_info(DRIVER_DESC ": %s\n", __func__);
|
|
|
|
gpio_set_value(info->gpio_fw, 0);
|
|
gpio_set_value(info->gpio_en, info->en_polarity);
|
|
usleep_range(10000, 15000);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pn544_hci_disable(struct pn544_hci_info *info)
|
|
{
|
|
pr_info(DRIVER_DESC ": %s\n", __func__);
|
|
|
|
gpio_set_value(info->gpio_fw, 0);
|
|
gpio_set_value(info->gpio_en, !info->en_polarity);
|
|
usleep_range(10000, 15000);
|
|
|
|
gpio_set_value(info->gpio_en, info->en_polarity);
|
|
usleep_range(10000, 15000);
|
|
|
|
gpio_set_value(info->gpio_en, !info->en_polarity);
|
|
usleep_range(10000, 15000);
|
|
}
|
|
|
|
static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len)
|
|
{
|
|
int r;
|
|
|
|
usleep_range(3000, 6000);
|
|
|
|
r = i2c_master_send(client, buf, len);
|
|
|
|
if (r == -EREMOTEIO) { /* Retry, chip was in standby */
|
|
usleep_range(6000, 10000);
|
|
r = i2c_master_send(client, buf, len);
|
|
}
|
|
|
|
if (r >= 0) {
|
|
if (r != len)
|
|
return -EREMOTEIO;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static int check_crc(u8 *buf, int buflen)
|
|
{
|
|
int len;
|
|
u16 crc;
|
|
|
|
len = buf[0] + 1;
|
|
crc = crc_ccitt(0xffff, buf, len - 2);
|
|
crc = ~crc;
|
|
|
|
if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) {
|
|
pr_err(PN544_HCI_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n",
|
|
crc, buf[len - 1], buf[len - 2]);
|
|
|
|
pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
|
|
print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
|
|
16, 2, buf, buflen, false);
|
|
return -EPERM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees
|
|
* that i2c bus will be flushed and that next read will start on a new frame.
|
|
* returned skb contains only LLC header and payload.
|
|
* returns:
|
|
* -EREMOTEIO : i2c read error (fatal)
|
|
* -EBADMSG : frame was incorrect and discarded
|
|
* -ENOMEM : cannot allocate skb, frame dropped
|
|
*/
|
|
static int pn544_hci_i2c_read(struct i2c_client *client, struct sk_buff **skb)
|
|
{
|
|
int r;
|
|
u8 len;
|
|
u8 tmp[PN544_HCI_LLC_MAX_SIZE - 1];
|
|
|
|
r = i2c_master_recv(client, &len, 1);
|
|
if (r != 1) {
|
|
dev_err(&client->dev, "cannot read len byte\n");
|
|
return -EREMOTEIO;
|
|
}
|
|
|
|
if ((len < (PN544_HCI_LLC_MIN_SIZE - 1)) ||
|
|
(len > (PN544_HCI_LLC_MAX_SIZE - 1))) {
|
|
dev_err(&client->dev, "invalid len byte\n");
|
|
r = -EBADMSG;
|
|
goto flush;
|
|
}
|
|
|
|
*skb = alloc_skb(1 + len, GFP_KERNEL);
|
|
if (*skb == NULL) {
|
|
r = -ENOMEM;
|
|
goto flush;
|
|
}
|
|
|
|
*skb_put(*skb, 1) = len;
|
|
|
|
r = i2c_master_recv(client, skb_put(*skb, len), len);
|
|
if (r != len) {
|
|
kfree_skb(*skb);
|
|
return -EREMOTEIO;
|
|
}
|
|
|
|
r = check_crc((*skb)->data, (*skb)->len);
|
|
if (r != 0) {
|
|
kfree_skb(*skb);
|
|
r = -EBADMSG;
|
|
goto flush;
|
|
}
|
|
|
|
skb_pull(*skb, 1);
|
|
skb_trim(*skb, (*skb)->len - 2);
|
|
|
|
usleep_range(3000, 6000);
|
|
|
|
return 0;
|
|
|
|
flush:
|
|
if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
|
|
r = -EREMOTEIO;
|
|
|
|
usleep_range(3000, 6000);
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Reads an shdlc frame from the chip. This is not as straightforward as it
|
|
* seems. There are cases where we could loose the frame start synchronization.
|
|
* The frame format is len-data-crc, and corruption can occur anywhere while
|
|
* transiting on i2c bus, such that we could read an invalid len.
|
|
* In order to recover synchronization with the next frame, we must be sure
|
|
* to read the real amount of data without using the len byte. We do this by
|
|
* assuming the following:
|
|
* - the chip will always present only one single complete frame on the bus
|
|
* before triggering the interrupt
|
|
* - the chip will not present a new frame until we have completely read
|
|
* the previous one (or until we have handled the interrupt).
|
|
* The tricky case is when we read a corrupted len that is less than the real
|
|
* len. We must detect this here in order to determine that we need to flush
|
|
* the bus. This is the reason why we check the crc here.
|
|
*/
|
|
static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)
|
|
{
|
|
struct pn544_hci_info *info = dev_id;
|
|
struct i2c_client *client;
|
|
struct sk_buff *skb = NULL;
|
|
int r;
|
|
|
|
if (!info || irq != info->i2c_dev->irq) {
|
|
WARN_ON_ONCE(1);
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
client = info->i2c_dev;
|
|
dev_dbg(&client->dev, "IRQ\n");
|
|
|
|
if (info->hard_fault != 0)
|
|
return IRQ_HANDLED;
|
|
|
|
r = pn544_hci_i2c_read(client, &skb);
|
|
if (r == -EREMOTEIO) {
|
|
info->hard_fault = r;
|
|
|
|
nfc_hci_recv_frame(info->hdev, NULL);
|
|
|
|
return IRQ_HANDLED;
|
|
} else if ((r == -ENOMEM) || (r == -EBADMSG)) {
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
nfc_hci_recv_frame(info->hdev, skb);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int pn544_hci_open(struct nfc_hci_dev *hdev)
|
|
{
|
|
struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
int r = 0;
|
|
|
|
mutex_lock(&info->info_lock);
|
|
|
|
if (info->state != PN544_ST_COLD) {
|
|
r = -EBUSY;
|
|
goto out;
|
|
}
|
|
|
|
r = pn544_hci_enable(info, HCI_MODE);
|
|
|
|
if (r == 0)
|
|
info->state = PN544_ST_READY;
|
|
|
|
out:
|
|
mutex_unlock(&info->info_lock);
|
|
return r;
|
|
}
|
|
|
|
static void pn544_hci_close(struct nfc_hci_dev *hdev)
|
|
{
|
|
struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
mutex_lock(&info->info_lock);
|
|
|
|
if (info->state == PN544_ST_COLD)
|
|
goto out;
|
|
|
|
pn544_hci_disable(info);
|
|
|
|
info->state = PN544_ST_COLD;
|
|
|
|
out:
|
|
mutex_unlock(&info->info_lock);
|
|
}
|
|
|
|
static int pn544_hci_ready(struct nfc_hci_dev *hdev)
|
|
{
|
|
struct sk_buff *skb;
|
|
static struct hw_config {
|
|
u8 adr[2];
|
|
u8 value;
|
|
} hw_config[] = {
|
|
{{0x9f, 0x9a}, 0x00},
|
|
|
|
{{0x98, 0x10}, 0xbc},
|
|
|
|
{{0x9e, 0x71}, 0x00},
|
|
|
|
{{0x98, 0x09}, 0x00},
|
|
|
|
{{0x9e, 0xb4}, 0x00},
|
|
|
|
{{0x9e, 0xd9}, 0xff},
|
|
{{0x9e, 0xda}, 0xff},
|
|
{{0x9e, 0xdb}, 0x23},
|
|
{{0x9e, 0xdc}, 0x21},
|
|
{{0x9e, 0xdd}, 0x22},
|
|
{{0x9e, 0xde}, 0x24},
|
|
|
|
{{0x9c, 0x01}, 0x08},
|
|
|
|
{{0x9e, 0xaa}, 0x01},
|
|
|
|
{{0x9b, 0xd1}, 0x0d},
|
|
{{0x9b, 0xd2}, 0x24},
|
|
{{0x9b, 0xd3}, 0x0a},
|
|
{{0x9b, 0xd4}, 0x22},
|
|
{{0x9b, 0xd5}, 0x08},
|
|
{{0x9b, 0xd6}, 0x1e},
|
|
{{0x9b, 0xdd}, 0x1c},
|
|
|
|
{{0x9b, 0x84}, 0x13},
|
|
{{0x99, 0x81}, 0x7f},
|
|
{{0x99, 0x31}, 0x70},
|
|
|
|
{{0x98, 0x00}, 0x3f},
|
|
|
|
{{0x9f, 0x09}, 0x00},
|
|
|
|
{{0x9f, 0x0a}, 0x05},
|
|
|
|
{{0x9e, 0xd1}, 0xa1},
|
|
{{0x99, 0x23}, 0x00},
|
|
|
|
{{0x9e, 0x74}, 0x80},
|
|
|
|
{{0x9f, 0x28}, 0x10},
|
|
|
|
{{0x9f, 0x35}, 0x14},
|
|
|
|
{{0x9f, 0x36}, 0x60},
|
|
|
|
{{0x9c, 0x31}, 0x00},
|
|
|
|
{{0x9c, 0x32}, 0xc8},
|
|
|
|
{{0x9c, 0x19}, 0x40},
|
|
|
|
{{0x9c, 0x1a}, 0x40},
|
|
|
|
{{0x9c, 0x0c}, 0x00},
|
|
|
|
{{0x9c, 0x0d}, 0x00},
|
|
|
|
{{0x9c, 0x12}, 0x00},
|
|
|
|
{{0x9c, 0x13}, 0x00},
|
|
|
|
{{0x98, 0xa2}, 0x0e},
|
|
|
|
{{0x98, 0x93}, 0x40},
|
|
|
|
{{0x98, 0x7d}, 0x02},
|
|
{{0x98, 0x7e}, 0x00},
|
|
{{0x9f, 0xc8}, 0x01},
|
|
};
|
|
struct hw_config *p = hw_config;
|
|
int count = ARRAY_SIZE(hw_config);
|
|
struct sk_buff *res_skb;
|
|
u8 param[4];
|
|
int r;
|
|
|
|
param[0] = 0;
|
|
while (count--) {
|
|
param[1] = p->adr[0];
|
|
param[2] = p->adr[1];
|
|
param[3] = p->value;
|
|
|
|
r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_WRITE,
|
|
param, 4, &res_skb);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (res_skb->len != 1) {
|
|
kfree_skb(res_skb);
|
|
return -EPROTO;
|
|
}
|
|
|
|
if (res_skb->data[0] != p->value) {
|
|
kfree_skb(res_skb);
|
|
return -EIO;
|
|
}
|
|
|
|
kfree_skb(res_skb);
|
|
|
|
p++;
|
|
}
|
|
|
|
param[0] = NFC_HCI_UICC_HOST_ID;
|
|
r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
|
|
NFC_HCI_ADMIN_WHITELIST, param, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
param[0] = 0x3d;
|
|
r = nfc_hci_set_param(hdev, PN544_SYS_MGMT_GATE,
|
|
PN544_SYS_MGMT_INFO_NOTIFICATION, param, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
param[0] = 0x0;
|
|
r = nfc_hci_set_param(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
PN544_RF_READER_A_AUTO_ACTIVATION, param, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
NFC_HCI_EVT_END_OPERATION, NULL, 0);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
param[0] = 0x1;
|
|
r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
|
|
PN544_PL_NFCT_DEACTIVATED, param, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
param[0] = 0x0;
|
|
r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
|
|
PN544_PL_RDPHASES, param, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
|
|
PN544_ID_MGMT_FULL_VERSION_SW, &skb);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (skb->len != FULL_VERSION_LEN) {
|
|
kfree_skb(skb);
|
|
return -EINVAL;
|
|
}
|
|
|
|
print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ",
|
|
DUMP_PREFIX_NONE, 16, 1,
|
|
skb->data, FULL_VERSION_LEN, false);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pn544_hci_add_len_crc(struct sk_buff *skb)
|
|
{
|
|
u16 crc;
|
|
int len;
|
|
|
|
len = skb->len + 2;
|
|
*skb_push(skb, 1) = len;
|
|
|
|
crc = crc_ccitt(0xffff, skb->data, skb->len);
|
|
crc = ~crc;
|
|
*skb_put(skb, 1) = crc & 0xff;
|
|
*skb_put(skb, 1) = crc >> 8;
|
|
}
|
|
|
|
static void pn544_hci_remove_len_crc(struct sk_buff *skb)
|
|
{
|
|
skb_pull(skb, PN544_FRAME_HEADROOM);
|
|
skb_trim(skb, PN544_FRAME_TAILROOM);
|
|
}
|
|
|
|
static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
struct i2c_client *client = info->i2c_dev;
|
|
int r;
|
|
|
|
if (info->hard_fault != 0)
|
|
return info->hard_fault;
|
|
|
|
pn544_hci_add_len_crc(skb);
|
|
r = pn544_hci_i2c_write(client, skb->data, skb->len);
|
|
pn544_hci_remove_len_crc(skb);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
|
|
u32 im_protocols, u32 tm_protocols)
|
|
{
|
|
u8 phases = 0;
|
|
int r;
|
|
u8 duration[2];
|
|
u8 activated;
|
|
|
|
pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
|
|
__func__, im_protocols, tm_protocols);
|
|
|
|
r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
NFC_HCI_EVT_END_OPERATION, NULL, 0);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
duration[0] = 0x18;
|
|
duration[1] = 0x6a;
|
|
r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
|
|
PN544_PL_EMULATION, duration, 2);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
activated = 0;
|
|
r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
|
|
PN544_PL_NFCT_DEACTIVATED, &activated, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (im_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK |
|
|
NFC_PROTO_JEWEL_MASK))
|
|
phases |= 1; /* Type A */
|
|
if (im_protocols & NFC_PROTO_FELICA_MASK) {
|
|
phases |= (1 << 2); /* Type F 212 */
|
|
phases |= (1 << 3); /* Type F 424 */
|
|
}
|
|
|
|
phases |= (1 << 5); /* NFC active */
|
|
|
|
r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE,
|
|
PN544_PL_RDPHASES, &phases, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
|
|
if (r < 0)
|
|
nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
NFC_HCI_EVT_END_OPERATION, NULL, 0);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
|
|
struct nfc_target *target)
|
|
{
|
|
switch (gate) {
|
|
case PN544_RF_READER_F_GATE:
|
|
target->supported_protocols = NFC_PROTO_FELICA_MASK;
|
|
break;
|
|
case PN544_RF_READER_JEWEL_GATE:
|
|
target->supported_protocols = NFC_PROTO_JEWEL_MASK;
|
|
target->sens_res = 0x0c00;
|
|
break;
|
|
default:
|
|
return -EPROTO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
|
u8 gate,
|
|
struct nfc_target *target)
|
|
{
|
|
struct sk_buff *uid_skb;
|
|
int r = 0;
|
|
|
|
if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
|
|
if (target->nfcid1_len != 4 && target->nfcid1_len != 7 &&
|
|
target->nfcid1_len != 10)
|
|
return -EPROTO;
|
|
|
|
r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
PN544_RF_READER_CMD_ACTIVATE_NEXT,
|
|
target->nfcid1, target->nfcid1_len, NULL);
|
|
} else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) {
|
|
r = nfc_hci_get_param(hdev, PN544_RF_READER_F_GATE,
|
|
PN544_FELICA_ID, &uid_skb);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (uid_skb->len != 8) {
|
|
kfree_skb(uid_skb);
|
|
return -EPROTO;
|
|
}
|
|
|
|
r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
|
|
PN544_RF_READER_CMD_ACTIVATE_NEXT,
|
|
uid_skb->data, uid_skb->len, NULL);
|
|
kfree_skb(uid_skb);
|
|
} else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) {
|
|
/*
|
|
* TODO: maybe other ISO 14443 require some kind of continue
|
|
* activation, but for now we've seen only this one below.
|
|
*/
|
|
if (target->sens_res == 0x4403) /* Type 4 Mifare DESFire */
|
|
r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION,
|
|
NULL, 0, NULL);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
#define PN544_CB_TYPE_READER_F 1
|
|
|
|
static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb,
|
|
int err)
|
|
{
|
|
struct pn544_hci_info *info = context;
|
|
|
|
switch (info->async_cb_type) {
|
|
case PN544_CB_TYPE_READER_F:
|
|
if (err == 0)
|
|
skb_pull(skb, 1);
|
|
info->async_cb(info->async_cb_context, skb, err);
|
|
break;
|
|
default:
|
|
if (err == 0)
|
|
kfree_skb(skb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define MIFARE_CMD_AUTH_KEY_A 0x60
|
|
#define MIFARE_CMD_AUTH_KEY_B 0x61
|
|
#define MIFARE_CMD_HEADER 2
|
|
#define MIFARE_UID_LEN 4
|
|
#define MIFARE_KEY_LEN 6
|
|
#define MIFARE_CMD_LEN 12
|
|
/*
|
|
* Returns:
|
|
* <= 0: driver handled the data exchange
|
|
* 1: driver doesn't especially handle, please do standard processing
|
|
*/
|
|
static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev,
|
|
struct nfc_target *target,
|
|
struct sk_buff *skb, data_exchange_cb_t cb,
|
|
void *cb_context)
|
|
{
|
|
struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,
|
|
target->hci_reader_gate);
|
|
|
|
switch (target->hci_reader_gate) {
|
|
case NFC_HCI_RF_READER_A_GATE:
|
|
if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
|
|
/*
|
|
* It seems that pn544 is inverting key and UID for
|
|
* MIFARE authentication commands.
|
|
*/
|
|
if (skb->len == MIFARE_CMD_LEN &&
|
|
(skb->data[0] == MIFARE_CMD_AUTH_KEY_A ||
|
|
skb->data[0] == MIFARE_CMD_AUTH_KEY_B)) {
|
|
u8 uid[MIFARE_UID_LEN];
|
|
u8 *data = skb->data + MIFARE_CMD_HEADER;
|
|
|
|
memcpy(uid, data + MIFARE_KEY_LEN,
|
|
MIFARE_UID_LEN);
|
|
memmove(data + MIFARE_UID_LEN, data,
|
|
MIFARE_KEY_LEN);
|
|
memcpy(data, uid, MIFARE_UID_LEN);
|
|
}
|
|
|
|
return nfc_hci_send_cmd_async(hdev,
|
|
target->hci_reader_gate,
|
|
PN544_MIFARE_CMD,
|
|
skb->data, skb->len,
|
|
cb, cb_context);
|
|
} else
|
|
return 1;
|
|
case PN544_RF_READER_F_GATE:
|
|
*skb_push(skb, 1) = 0;
|
|
*skb_push(skb, 1) = 0;
|
|
|
|
info->async_cb_type = PN544_CB_TYPE_READER_F;
|
|
info->async_cb = cb;
|
|
info->async_cb_context = cb_context;
|
|
|
|
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
|
PN544_FELICA_RAW, skb->data,
|
|
skb->len,
|
|
pn544_hci_data_exchange_cb, info);
|
|
case PN544_RF_READER_JEWEL_GATE:
|
|
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
|
PN544_JEWEL_RAW_CMD, skb->data,
|
|
skb->len, cb, cb_context);
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
|
|
struct nfc_target *target)
|
|
{
|
|
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
|
PN544_RF_READER_CMD_PRESENCE_CHECK,
|
|
NULL, 0, NULL);
|
|
}
|
|
|
|
static struct nfc_hci_ops pn544_hci_ops = {
|
|
.open = pn544_hci_open,
|
|
.close = pn544_hci_close,
|
|
.hci_ready = pn544_hci_ready,
|
|
.xmit = pn544_hci_xmit,
|
|
.start_poll = pn544_hci_start_poll,
|
|
.target_from_gate = pn544_hci_target_from_gate,
|
|
.complete_target_discovered = pn544_hci_complete_target_discovered,
|
|
.data_exchange = pn544_hci_data_exchange,
|
|
.check_presence = pn544_hci_check_presence,
|
|
};
|
|
|
|
static int __devinit pn544_hci_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct pn544_hci_info *info;
|
|
struct pn544_nfc_platform_data *pdata;
|
|
int r = 0;
|
|
u32 protocols;
|
|
struct nfc_hci_init_data init_data;
|
|
|
|
dev_dbg(&client->dev, "%s\n", __func__);
|
|
dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
|
|
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
dev_err(&client->dev, "Need I2C_FUNC_I2C\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL);
|
|
if (!info) {
|
|
dev_err(&client->dev,
|
|
"Cannot allocate memory for pn544_hci_info.\n");
|
|
r = -ENOMEM;
|
|
goto err_info_alloc;
|
|
}
|
|
|
|
info->i2c_dev = client;
|
|
info->state = PN544_ST_COLD;
|
|
mutex_init(&info->info_lock);
|
|
i2c_set_clientdata(client, info);
|
|
|
|
pdata = client->dev.platform_data;
|
|
if (pdata == NULL) {
|
|
dev_err(&client->dev, "No platform data\n");
|
|
r = -EINVAL;
|
|
goto err_pdata;
|
|
}
|
|
|
|
if (pdata->request_resources == NULL) {
|
|
dev_err(&client->dev, "request_resources() missing\n");
|
|
r = -EINVAL;
|
|
goto err_pdata;
|
|
}
|
|
|
|
r = pdata->request_resources(client);
|
|
if (r) {
|
|
dev_err(&client->dev, "Cannot get platform resources\n");
|
|
goto err_pdata;
|
|
}
|
|
|
|
info->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
|
|
info->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
|
|
info->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
|
|
|
|
pn544_hci_platform_init(info);
|
|
|
|
r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn,
|
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
|
PN544_HCI_DRIVER_NAME, info);
|
|
if (r < 0) {
|
|
dev_err(&client->dev, "Unable to register IRQ handler\n");
|
|
goto err_rti;
|
|
}
|
|
|
|
init_data.gate_count = ARRAY_SIZE(pn544_gates);
|
|
|
|
memcpy(init_data.gates, pn544_gates, sizeof(pn544_gates));
|
|
|
|
/*
|
|
* TODO: Session id must include the driver name + some bus addr
|
|
* persistent info to discriminate 2 identical chips
|
|
*/
|
|
strcpy(init_data.session_id, "ID544HCI");
|
|
|
|
protocols = NFC_PROTO_JEWEL_MASK |
|
|
NFC_PROTO_MIFARE_MASK |
|
|
NFC_PROTO_FELICA_MASK |
|
|
NFC_PROTO_ISO14443_MASK |
|
|
NFC_PROTO_ISO14443_B_MASK |
|
|
NFC_PROTO_NFC_DEP_MASK;
|
|
|
|
info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
|
|
protocols, LLC_SHDLC_NAME,
|
|
PN544_FRAME_HEADROOM +
|
|
PN544_CMDS_HEADROOM,
|
|
PN544_FRAME_TAILROOM,
|
|
PN544_HCI_LLC_MAX_PAYLOAD);
|
|
if (!info->hdev) {
|
|
dev_err(&client->dev, "Cannot allocate nfc hdev.\n");
|
|
r = -ENOMEM;
|
|
goto err_alloc_hdev;
|
|
}
|
|
|
|
nfc_hci_set_clientdata(info->hdev, info);
|
|
|
|
r = nfc_hci_register_device(info->hdev);
|
|
if (r)
|
|
goto err_regdev;
|
|
|
|
return 0;
|
|
|
|
err_regdev:
|
|
nfc_hci_free_device(info->hdev);
|
|
|
|
err_alloc_hdev:
|
|
free_irq(client->irq, info);
|
|
|
|
err_rti:
|
|
if (pdata->free_resources != NULL)
|
|
pdata->free_resources();
|
|
|
|
err_pdata:
|
|
kfree(info);
|
|
|
|
err_info_alloc:
|
|
return r;
|
|
}
|
|
|
|
static __devexit int pn544_hci_remove(struct i2c_client *client)
|
|
{
|
|
struct pn544_hci_info *info = i2c_get_clientdata(client);
|
|
struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
|
|
|
|
dev_dbg(&client->dev, "%s\n", __func__);
|
|
|
|
nfc_hci_free_device(info->hdev);
|
|
|
|
if (info->state != PN544_ST_COLD) {
|
|
if (pdata->disable)
|
|
pdata->disable();
|
|
}
|
|
|
|
free_irq(client->irq, info);
|
|
if (pdata->free_resources)
|
|
pdata->free_resources();
|
|
|
|
kfree(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct i2c_driver pn544_hci_driver = {
|
|
.driver = {
|
|
.name = PN544_HCI_DRIVER_NAME,
|
|
},
|
|
.probe = pn544_hci_probe,
|
|
.id_table = pn544_hci_id_table,
|
|
.remove = __devexit_p(pn544_hci_remove),
|
|
};
|
|
|
|
static int __init pn544_hci_init(void)
|
|
{
|
|
int r;
|
|
|
|
pr_debug(DRIVER_DESC ": %s\n", __func__);
|
|
|
|
r = i2c_add_driver(&pn544_hci_driver);
|
|
if (r) {
|
|
pr_err(PN544_HCI_DRIVER_NAME ": driver registration failed\n");
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit pn544_hci_exit(void)
|
|
{
|
|
i2c_del_driver(&pn544_hci_driver);
|
|
}
|
|
|
|
module_init(pn544_hci_init);
|
|
module_exit(pn544_hci_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|