forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
723 lines
19 KiB
723 lines
19 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* HCI based Driver for Inside Secure microread NFC Chip |
|
* |
|
* Copyright (C) 2013 Intel Corporation. All rights reserved. |
|
*/ |
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|
|
|
#include <linux/module.h> |
|
#include <linux/delay.h> |
|
#include <linux/slab.h> |
|
#include <linux/crc-ccitt.h> |
|
|
|
#include <linux/nfc.h> |
|
#include <net/nfc/nfc.h> |
|
#include <net/nfc/hci.h> |
|
#include <net/nfc/llc.h> |
|
|
|
#include "microread.h" |
|
|
|
/* Proprietary gates, events, commands and registers */ |
|
/* Admin */ |
|
#define MICROREAD_GATE_ID_ADM NFC_HCI_ADMIN_GATE |
|
#define MICROREAD_GATE_ID_MGT 0x01 |
|
#define MICROREAD_GATE_ID_OS 0x02 |
|
#define MICROREAD_GATE_ID_TESTRF 0x03 |
|
#define MICROREAD_GATE_ID_LOOPBACK NFC_HCI_LOOPBACK_GATE |
|
#define MICROREAD_GATE_ID_IDT NFC_HCI_ID_MGMT_GATE |
|
#define MICROREAD_GATE_ID_LMS NFC_HCI_LINK_MGMT_GATE |
|
|
|
/* Reader */ |
|
#define MICROREAD_GATE_ID_MREAD_GEN 0x10 |
|
#define MICROREAD_GATE_ID_MREAD_ISO_B NFC_HCI_RF_READER_B_GATE |
|
#define MICROREAD_GATE_ID_MREAD_NFC_T1 0x12 |
|
#define MICROREAD_GATE_ID_MREAD_ISO_A NFC_HCI_RF_READER_A_GATE |
|
#define MICROREAD_GATE_ID_MREAD_NFC_T3 0x14 |
|
#define MICROREAD_GATE_ID_MREAD_ISO_15_3 0x15 |
|
#define MICROREAD_GATE_ID_MREAD_ISO_15_2 0x16 |
|
#define MICROREAD_GATE_ID_MREAD_ISO_B_3 0x17 |
|
#define MICROREAD_GATE_ID_MREAD_BPRIME 0x18 |
|
#define MICROREAD_GATE_ID_MREAD_ISO_A_3 0x19 |
|
|
|
/* Card */ |
|
#define MICROREAD_GATE_ID_MCARD_GEN 0x20 |
|
#define MICROREAD_GATE_ID_MCARD_ISO_B 0x21 |
|
#define MICROREAD_GATE_ID_MCARD_BPRIME 0x22 |
|
#define MICROREAD_GATE_ID_MCARD_ISO_A 0x23 |
|
#define MICROREAD_GATE_ID_MCARD_NFC_T3 0x24 |
|
#define MICROREAD_GATE_ID_MCARD_ISO_15_3 0x25 |
|
#define MICROREAD_GATE_ID_MCARD_ISO_15_2 0x26 |
|
#define MICROREAD_GATE_ID_MCARD_ISO_B_2 0x27 |
|
#define MICROREAD_GATE_ID_MCARD_ISO_CUSTOM 0x28 |
|
#define MICROREAD_GATE_ID_SECURE_ELEMENT 0x2F |
|
|
|
/* P2P */ |
|
#define MICROREAD_GATE_ID_P2P_GEN 0x30 |
|
#define MICROREAD_GATE_ID_P2P_TARGET 0x31 |
|
#define MICROREAD_PAR_P2P_TARGET_MODE 0x01 |
|
#define MICROREAD_PAR_P2P_TARGET_GT 0x04 |
|
#define MICROREAD_GATE_ID_P2P_INITIATOR 0x32 |
|
#define MICROREAD_PAR_P2P_INITIATOR_GI 0x01 |
|
#define MICROREAD_PAR_P2P_INITIATOR_GT 0x03 |
|
|
|
/* Those pipes are created/opened by default in the chip */ |
|
#define MICROREAD_PIPE_ID_LMS 0x00 |
|
#define MICROREAD_PIPE_ID_ADMIN 0x01 |
|
#define MICROREAD_PIPE_ID_MGT 0x02 |
|
#define MICROREAD_PIPE_ID_OS 0x03 |
|
#define MICROREAD_PIPE_ID_HDS_LOOPBACK 0x04 |
|
#define MICROREAD_PIPE_ID_HDS_IDT 0x05 |
|
#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_B 0x08 |
|
#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_BPRIME 0x09 |
|
#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_A 0x0A |
|
#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_15_3 0x0B |
|
#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_15_2 0x0C |
|
#define MICROREAD_PIPE_ID_HDS_MCARD_NFC_T3 0x0D |
|
#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_B_2 0x0E |
|
#define MICROREAD_PIPE_ID_HDS_MCARD_CUSTOM 0x0F |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_B 0x10 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_NFC_T1 0x11 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_A 0x12 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_15_3 0x13 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_15_2 0x14 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_NFC_T3 0x15 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_B_3 0x16 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_BPRIME 0x17 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_A_3 0x18 |
|
#define MICROREAD_PIPE_ID_HDS_MREAD_GEN 0x1B |
|
#define MICROREAD_PIPE_ID_HDS_STACKED_ELEMENT 0x1C |
|
#define MICROREAD_PIPE_ID_HDS_INSTANCES 0x1D |
|
#define MICROREAD_PIPE_ID_HDS_TESTRF 0x1E |
|
#define MICROREAD_PIPE_ID_HDS_P2P_TARGET 0x1F |
|
#define MICROREAD_PIPE_ID_HDS_P2P_INITIATOR 0x20 |
|
|
|
/* Events */ |
|
#define MICROREAD_EVT_MREAD_DISCOVERY_OCCURED NFC_HCI_EVT_TARGET_DISCOVERED |
|
#define MICROREAD_EVT_MREAD_CARD_FOUND 0x3D |
|
#define MICROREAD_EMCF_A_ATQA 0 |
|
#define MICROREAD_EMCF_A_SAK 2 |
|
#define MICROREAD_EMCF_A_LEN 3 |
|
#define MICROREAD_EMCF_A_UID 4 |
|
#define MICROREAD_EMCF_A3_ATQA 0 |
|
#define MICROREAD_EMCF_A3_SAK 2 |
|
#define MICROREAD_EMCF_A3_LEN 3 |
|
#define MICROREAD_EMCF_A3_UID 4 |
|
#define MICROREAD_EMCF_B_UID 0 |
|
#define MICROREAD_EMCF_T1_ATQA 0 |
|
#define MICROREAD_EMCF_T1_UID 4 |
|
#define MICROREAD_EMCF_T3_UID 0 |
|
#define MICROREAD_EVT_MREAD_DISCOVERY_START NFC_HCI_EVT_READER_REQUESTED |
|
#define MICROREAD_EVT_MREAD_DISCOVERY_START_SOME 0x3E |
|
#define MICROREAD_EVT_MREAD_DISCOVERY_STOP NFC_HCI_EVT_END_OPERATION |
|
#define MICROREAD_EVT_MREAD_SIM_REQUESTS 0x3F |
|
#define MICROREAD_EVT_MCARD_EXCHANGE NFC_HCI_EVT_TARGET_DISCOVERED |
|
#define MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_TO_RF 0x20 |
|
#define MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_FROM_RF 0x21 |
|
#define MICROREAD_EVT_MCARD_FIELD_ON 0x11 |
|
#define MICROREAD_EVT_P2P_TARGET_ACTIVATED 0x13 |
|
#define MICROREAD_EVT_P2P_TARGET_DEACTIVATED 0x12 |
|
#define MICROREAD_EVT_MCARD_FIELD_OFF 0x14 |
|
|
|
/* Commands */ |
|
#define MICROREAD_CMD_MREAD_EXCHANGE 0x10 |
|
#define MICROREAD_CMD_MREAD_SUBSCRIBE 0x3F |
|
|
|
/* Hosts IDs */ |
|
#define MICROREAD_ELT_ID_HDS NFC_HCI_TERMINAL_HOST_ID |
|
#define MICROREAD_ELT_ID_SIM NFC_HCI_UICC_HOST_ID |
|
#define MICROREAD_ELT_ID_SE1 0x03 |
|
#define MICROREAD_ELT_ID_SE2 0x04 |
|
#define MICROREAD_ELT_ID_SE3 0x05 |
|
|
|
static struct nfc_hci_gate microread_gates[] = { |
|
{MICROREAD_GATE_ID_ADM, MICROREAD_PIPE_ID_ADMIN}, |
|
{MICROREAD_GATE_ID_LOOPBACK, MICROREAD_PIPE_ID_HDS_LOOPBACK}, |
|
{MICROREAD_GATE_ID_IDT, MICROREAD_PIPE_ID_HDS_IDT}, |
|
{MICROREAD_GATE_ID_LMS, MICROREAD_PIPE_ID_LMS}, |
|
{MICROREAD_GATE_ID_MREAD_ISO_B, MICROREAD_PIPE_ID_HDS_MREAD_ISO_B}, |
|
{MICROREAD_GATE_ID_MREAD_ISO_A, MICROREAD_PIPE_ID_HDS_MREAD_ISO_A}, |
|
{MICROREAD_GATE_ID_MREAD_ISO_A_3, MICROREAD_PIPE_ID_HDS_MREAD_ISO_A_3}, |
|
{MICROREAD_GATE_ID_MGT, MICROREAD_PIPE_ID_MGT}, |
|
{MICROREAD_GATE_ID_OS, MICROREAD_PIPE_ID_OS}, |
|
{MICROREAD_GATE_ID_MREAD_NFC_T1, MICROREAD_PIPE_ID_HDS_MREAD_NFC_T1}, |
|
{MICROREAD_GATE_ID_MREAD_NFC_T3, MICROREAD_PIPE_ID_HDS_MREAD_NFC_T3}, |
|
{MICROREAD_GATE_ID_P2P_TARGET, MICROREAD_PIPE_ID_HDS_P2P_TARGET}, |
|
{MICROREAD_GATE_ID_P2P_INITIATOR, MICROREAD_PIPE_ID_HDS_P2P_INITIATOR} |
|
}; |
|
|
|
/* Largest headroom needed for outgoing custom commands */ |
|
#define MICROREAD_CMDS_HEADROOM 2 |
|
#define MICROREAD_CMD_TAILROOM 2 |
|
|
|
struct microread_info { |
|
struct nfc_phy_ops *phy_ops; |
|
void *phy_id; |
|
|
|
struct nfc_hci_dev *hdev; |
|
|
|
int async_cb_type; |
|
data_exchange_cb_t async_cb; |
|
void *async_cb_context; |
|
}; |
|
|
|
static int microread_open(struct nfc_hci_dev *hdev) |
|
{ |
|
struct microread_info *info = nfc_hci_get_clientdata(hdev); |
|
|
|
return info->phy_ops->enable(info->phy_id); |
|
} |
|
|
|
static void microread_close(struct nfc_hci_dev *hdev) |
|
{ |
|
struct microread_info *info = nfc_hci_get_clientdata(hdev); |
|
|
|
info->phy_ops->disable(info->phy_id); |
|
} |
|
|
|
static int microread_hci_ready(struct nfc_hci_dev *hdev) |
|
{ |
|
int r; |
|
u8 param[4]; |
|
|
|
param[0] = 0x03; |
|
r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_A, |
|
MICROREAD_CMD_MREAD_SUBSCRIBE, param, 1, NULL); |
|
if (r) |
|
return r; |
|
|
|
r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_A_3, |
|
MICROREAD_CMD_MREAD_SUBSCRIBE, NULL, 0, NULL); |
|
if (r) |
|
return r; |
|
|
|
param[0] = 0x00; |
|
param[1] = 0x03; |
|
param[2] = 0x00; |
|
r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_B, |
|
MICROREAD_CMD_MREAD_SUBSCRIBE, param, 3, NULL); |
|
if (r) |
|
return r; |
|
|
|
r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_NFC_T1, |
|
MICROREAD_CMD_MREAD_SUBSCRIBE, NULL, 0, NULL); |
|
if (r) |
|
return r; |
|
|
|
param[0] = 0xFF; |
|
param[1] = 0xFF; |
|
param[2] = 0x00; |
|
param[3] = 0x00; |
|
r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_NFC_T3, |
|
MICROREAD_CMD_MREAD_SUBSCRIBE, param, 4, NULL); |
|
|
|
return r; |
|
} |
|
|
|
static int microread_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) |
|
{ |
|
struct microread_info *info = nfc_hci_get_clientdata(hdev); |
|
|
|
return info->phy_ops->write(info->phy_id, skb); |
|
} |
|
|
|
static int microread_start_poll(struct nfc_hci_dev *hdev, |
|
u32 im_protocols, u32 tm_protocols) |
|
{ |
|
int r; |
|
|
|
u8 param[2]; |
|
u8 mode; |
|
|
|
param[0] = 0x00; |
|
param[1] = 0x00; |
|
|
|
if (im_protocols & NFC_PROTO_ISO14443_MASK) |
|
param[0] |= (1 << 2); |
|
|
|
if (im_protocols & NFC_PROTO_ISO14443_B_MASK) |
|
param[0] |= 1; |
|
|
|
if (im_protocols & NFC_PROTO_MIFARE_MASK) |
|
param[1] |= 1; |
|
|
|
if (im_protocols & NFC_PROTO_JEWEL_MASK) |
|
param[0] |= (1 << 1); |
|
|
|
if (im_protocols & NFC_PROTO_FELICA_MASK) |
|
param[0] |= (1 << 5); |
|
|
|
if (im_protocols & NFC_PROTO_NFC_DEP_MASK) |
|
param[1] |= (1 << 1); |
|
|
|
if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { |
|
hdev->gb = nfc_get_local_general_bytes(hdev->ndev, |
|
&hdev->gb_len); |
|
if (hdev->gb == NULL || hdev->gb_len == 0) { |
|
im_protocols &= ~NFC_PROTO_NFC_DEP_MASK; |
|
tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK; |
|
} |
|
} |
|
|
|
r = nfc_hci_send_event(hdev, MICROREAD_GATE_ID_MREAD_ISO_A, |
|
MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, 0); |
|
if (r) |
|
return r; |
|
|
|
mode = 0xff; |
|
r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET, |
|
MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1); |
|
if (r) |
|
return r; |
|
|
|
if (im_protocols & NFC_PROTO_NFC_DEP_MASK) { |
|
r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_INITIATOR, |
|
MICROREAD_PAR_P2P_INITIATOR_GI, |
|
hdev->gb, hdev->gb_len); |
|
if (r) |
|
return r; |
|
} |
|
|
|
if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { |
|
r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET, |
|
MICROREAD_PAR_P2P_TARGET_GT, |
|
hdev->gb, hdev->gb_len); |
|
if (r) |
|
return r; |
|
|
|
mode = 0x02; |
|
r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET, |
|
MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1); |
|
if (r) |
|
return r; |
|
} |
|
|
|
return nfc_hci_send_event(hdev, MICROREAD_GATE_ID_MREAD_ISO_A, |
|
MICROREAD_EVT_MREAD_DISCOVERY_START_SOME, |
|
param, 2); |
|
} |
|
|
|
static int microread_dep_link_up(struct nfc_hci_dev *hdev, |
|
struct nfc_target *target, u8 comm_mode, |
|
u8 *gb, size_t gb_len) |
|
{ |
|
struct sk_buff *rgb_skb = NULL; |
|
int r; |
|
|
|
r = nfc_hci_get_param(hdev, target->hci_reader_gate, |
|
MICROREAD_PAR_P2P_INITIATOR_GT, &rgb_skb); |
|
if (r < 0) |
|
return r; |
|
|
|
if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) { |
|
r = -EPROTO; |
|
goto exit; |
|
} |
|
|
|
r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data, |
|
rgb_skb->len); |
|
if (r == 0) |
|
r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode, |
|
NFC_RF_INITIATOR); |
|
exit: |
|
kfree_skb(rgb_skb); |
|
|
|
return r; |
|
} |
|
|
|
static int microread_dep_link_down(struct nfc_hci_dev *hdev) |
|
{ |
|
return nfc_hci_send_event(hdev, MICROREAD_GATE_ID_P2P_INITIATOR, |
|
MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, 0); |
|
} |
|
|
|
static int microread_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, |
|
struct nfc_target *target) |
|
{ |
|
switch (gate) { |
|
case MICROREAD_GATE_ID_P2P_INITIATOR: |
|
target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; |
|
break; |
|
default: |
|
return -EPROTO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int microread_complete_target_discovered(struct nfc_hci_dev *hdev, |
|
u8 gate, |
|
struct nfc_target *target) |
|
{ |
|
return 0; |
|
} |
|
|
|
#define MICROREAD_CB_TYPE_READER_ALL 1 |
|
|
|
static void microread_im_transceive_cb(void *context, struct sk_buff *skb, |
|
int err) |
|
{ |
|
struct microread_info *info = context; |
|
|
|
switch (info->async_cb_type) { |
|
case MICROREAD_CB_TYPE_READER_ALL: |
|
if (err == 0) { |
|
if (skb->len == 0) { |
|
err = -EPROTO; |
|
kfree_skb(skb); |
|
info->async_cb(info->async_cb_context, NULL, |
|
-EPROTO); |
|
return; |
|
} |
|
|
|
if (skb->data[skb->len - 1] != 0) { |
|
err = nfc_hci_result_to_errno( |
|
skb->data[skb->len - 1]); |
|
kfree_skb(skb); |
|
info->async_cb(info->async_cb_context, NULL, |
|
err); |
|
return; |
|
} |
|
|
|
skb_trim(skb, skb->len - 1); /* RF Error ind. */ |
|
} |
|
info->async_cb(info->async_cb_context, skb, err); |
|
break; |
|
default: |
|
if (err == 0) |
|
kfree_skb(skb); |
|
break; |
|
} |
|
} |
|
|
|
/* |
|
* Returns: |
|
* <= 0: driver handled the data exchange |
|
* 1: driver doesn't especially handle, please do standard processing |
|
*/ |
|
static int microread_im_transceive(struct nfc_hci_dev *hdev, |
|
struct nfc_target *target, |
|
struct sk_buff *skb, data_exchange_cb_t cb, |
|
void *cb_context) |
|
{ |
|
struct microread_info *info = nfc_hci_get_clientdata(hdev); |
|
u8 control_bits; |
|
u16 crc; |
|
|
|
pr_info("data exchange to gate 0x%x\n", target->hci_reader_gate); |
|
|
|
if (target->hci_reader_gate == MICROREAD_GATE_ID_P2P_INITIATOR) { |
|
*(u8 *)skb_push(skb, 1) = 0; |
|
|
|
return nfc_hci_send_event(hdev, target->hci_reader_gate, |
|
MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_TO_RF, |
|
skb->data, skb->len); |
|
} |
|
|
|
switch (target->hci_reader_gate) { |
|
case MICROREAD_GATE_ID_MREAD_ISO_A: |
|
control_bits = 0xCB; |
|
break; |
|
case MICROREAD_GATE_ID_MREAD_ISO_A_3: |
|
control_bits = 0xCB; |
|
break; |
|
case MICROREAD_GATE_ID_MREAD_ISO_B: |
|
control_bits = 0xCB; |
|
break; |
|
case MICROREAD_GATE_ID_MREAD_NFC_T1: |
|
control_bits = 0x1B; |
|
|
|
crc = crc_ccitt(0xffff, skb->data, skb->len); |
|
crc = ~crc; |
|
skb_put_u8(skb, crc & 0xff); |
|
skb_put_u8(skb, crc >> 8); |
|
break; |
|
case MICROREAD_GATE_ID_MREAD_NFC_T3: |
|
control_bits = 0xDB; |
|
break; |
|
default: |
|
pr_info("Abort im_transceive to invalid gate 0x%x\n", |
|
target->hci_reader_gate); |
|
return 1; |
|
} |
|
|
|
*(u8 *)skb_push(skb, 1) = control_bits; |
|
|
|
info->async_cb_type = MICROREAD_CB_TYPE_READER_ALL; |
|
info->async_cb = cb; |
|
info->async_cb_context = cb_context; |
|
|
|
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, |
|
MICROREAD_CMD_MREAD_EXCHANGE, |
|
skb->data, skb->len, |
|
microread_im_transceive_cb, info); |
|
} |
|
|
|
static int microread_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) |
|
{ |
|
int r; |
|
|
|
r = nfc_hci_send_event(hdev, MICROREAD_GATE_ID_P2P_TARGET, |
|
MICROREAD_EVT_MCARD_EXCHANGE, |
|
skb->data, skb->len); |
|
|
|
kfree_skb(skb); |
|
|
|
return r; |
|
} |
|
|
|
static void microread_target_discovered(struct nfc_hci_dev *hdev, u8 gate, |
|
struct sk_buff *skb) |
|
{ |
|
struct nfc_target *targets; |
|
int r = 0; |
|
|
|
pr_info("target discovered to gate 0x%x\n", gate); |
|
|
|
targets = kzalloc(sizeof(struct nfc_target), GFP_KERNEL); |
|
if (targets == NULL) { |
|
r = -ENOMEM; |
|
goto exit; |
|
} |
|
|
|
targets->hci_reader_gate = gate; |
|
|
|
switch (gate) { |
|
case MICROREAD_GATE_ID_MREAD_ISO_A: |
|
targets->supported_protocols = |
|
nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A_SAK]); |
|
targets->sens_res = |
|
be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A_ATQA]); |
|
targets->sel_res = skb->data[MICROREAD_EMCF_A_SAK]; |
|
targets->nfcid1_len = skb->data[MICROREAD_EMCF_A_LEN]; |
|
if (targets->nfcid1_len > sizeof(targets->nfcid1)) { |
|
r = -EINVAL; |
|
goto exit_free; |
|
} |
|
memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A_UID], |
|
targets->nfcid1_len); |
|
break; |
|
case MICROREAD_GATE_ID_MREAD_ISO_A_3: |
|
targets->supported_protocols = |
|
nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A3_SAK]); |
|
targets->sens_res = |
|
be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A3_ATQA]); |
|
targets->sel_res = skb->data[MICROREAD_EMCF_A3_SAK]; |
|
targets->nfcid1_len = skb->data[MICROREAD_EMCF_A3_LEN]; |
|
if (targets->nfcid1_len > sizeof(targets->nfcid1)) { |
|
r = -EINVAL; |
|
goto exit_free; |
|
} |
|
memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A3_UID], |
|
targets->nfcid1_len); |
|
break; |
|
case MICROREAD_GATE_ID_MREAD_ISO_B: |
|
targets->supported_protocols = NFC_PROTO_ISO14443_B_MASK; |
|
memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_B_UID], 4); |
|
targets->nfcid1_len = 4; |
|
break; |
|
case MICROREAD_GATE_ID_MREAD_NFC_T1: |
|
targets->supported_protocols = NFC_PROTO_JEWEL_MASK; |
|
targets->sens_res = |
|
le16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_T1_ATQA]); |
|
memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T1_UID], 4); |
|
targets->nfcid1_len = 4; |
|
break; |
|
case MICROREAD_GATE_ID_MREAD_NFC_T3: |
|
targets->supported_protocols = NFC_PROTO_FELICA_MASK; |
|
memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T3_UID], 8); |
|
targets->nfcid1_len = 8; |
|
break; |
|
default: |
|
pr_info("discard target discovered to gate 0x%x\n", gate); |
|
goto exit_free; |
|
} |
|
|
|
r = nfc_targets_found(hdev->ndev, targets, 1); |
|
|
|
exit_free: |
|
kfree(targets); |
|
|
|
exit: |
|
kfree_skb(skb); |
|
|
|
if (r) |
|
pr_err("Failed to handle discovered target err=%d\n", r); |
|
} |
|
|
|
static int microread_event_received(struct nfc_hci_dev *hdev, u8 pipe, |
|
u8 event, struct sk_buff *skb) |
|
{ |
|
int r; |
|
u8 gate = hdev->pipes[pipe].gate; |
|
u8 mode; |
|
|
|
pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate); |
|
|
|
switch (event) { |
|
case MICROREAD_EVT_MREAD_CARD_FOUND: |
|
microread_target_discovered(hdev, gate, skb); |
|
return 0; |
|
|
|
case MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_FROM_RF: |
|
if (skb->len < 1) { |
|
kfree_skb(skb); |
|
return -EPROTO; |
|
} |
|
|
|
if (skb->data[skb->len - 1]) { |
|
kfree_skb(skb); |
|
return -EIO; |
|
} |
|
|
|
skb_trim(skb, skb->len - 1); |
|
|
|
r = nfc_tm_data_received(hdev->ndev, skb); |
|
break; |
|
|
|
case MICROREAD_EVT_MCARD_FIELD_ON: |
|
case MICROREAD_EVT_MCARD_FIELD_OFF: |
|
kfree_skb(skb); |
|
return 0; |
|
|
|
case MICROREAD_EVT_P2P_TARGET_ACTIVATED: |
|
r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, |
|
NFC_COMM_PASSIVE, skb->data, |
|
skb->len); |
|
|
|
kfree_skb(skb); |
|
break; |
|
|
|
case MICROREAD_EVT_MCARD_EXCHANGE: |
|
if (skb->len < 1) { |
|
kfree_skb(skb); |
|
return -EPROTO; |
|
} |
|
|
|
if (skb->data[skb->len-1]) { |
|
kfree_skb(skb); |
|
return -EIO; |
|
} |
|
|
|
skb_trim(skb, skb->len - 1); |
|
|
|
r = nfc_tm_data_received(hdev->ndev, skb); |
|
break; |
|
|
|
case MICROREAD_EVT_P2P_TARGET_DEACTIVATED: |
|
kfree_skb(skb); |
|
|
|
mode = 0xff; |
|
r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET, |
|
MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1); |
|
if (r) |
|
break; |
|
|
|
r = nfc_hci_send_event(hdev, gate, |
|
MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, |
|
0); |
|
break; |
|
|
|
default: |
|
return 1; |
|
} |
|
|
|
return r; |
|
} |
|
|
|
static struct nfc_hci_ops microread_hci_ops = { |
|
.open = microread_open, |
|
.close = microread_close, |
|
.hci_ready = microread_hci_ready, |
|
.xmit = microread_xmit, |
|
.start_poll = microread_start_poll, |
|
.dep_link_up = microread_dep_link_up, |
|
.dep_link_down = microread_dep_link_down, |
|
.target_from_gate = microread_target_from_gate, |
|
.complete_target_discovered = microread_complete_target_discovered, |
|
.im_transceive = microread_im_transceive, |
|
.tm_send = microread_tm_send, |
|
.check_presence = NULL, |
|
.event_received = microread_event_received, |
|
}; |
|
|
|
int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, |
|
int phy_headroom, int phy_tailroom, int phy_payload, |
|
struct nfc_hci_dev **hdev) |
|
{ |
|
struct microread_info *info; |
|
unsigned long quirks = 0; |
|
u32 protocols; |
|
struct nfc_hci_init_data init_data; |
|
int r; |
|
|
|
info = kzalloc(sizeof(struct microread_info), GFP_KERNEL); |
|
if (!info) { |
|
r = -ENOMEM; |
|
goto err_info_alloc; |
|
} |
|
|
|
info->phy_ops = phy_ops; |
|
info->phy_id = phy_id; |
|
|
|
init_data.gate_count = ARRAY_SIZE(microread_gates); |
|
memcpy(init_data.gates, microread_gates, sizeof(microread_gates)); |
|
|
|
strcpy(init_data.session_id, "MICROREA"); |
|
|
|
set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks); |
|
|
|
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(µread_hci_ops, &init_data, |
|
quirks, protocols, llc_name, |
|
phy_headroom + |
|
MICROREAD_CMDS_HEADROOM, |
|
phy_tailroom + |
|
MICROREAD_CMD_TAILROOM, |
|
phy_payload); |
|
if (!info->hdev) { |
|
pr_err("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; |
|
|
|
*hdev = info->hdev; |
|
|
|
return 0; |
|
|
|
err_regdev: |
|
nfc_hci_free_device(info->hdev); |
|
|
|
err_alloc_hdev: |
|
kfree(info); |
|
|
|
err_info_alloc: |
|
return r; |
|
} |
|
EXPORT_SYMBOL(microread_probe); |
|
|
|
void microread_remove(struct nfc_hci_dev *hdev) |
|
{ |
|
struct microread_info *info = nfc_hci_get_clientdata(hdev); |
|
|
|
nfc_hci_unregister_device(hdev); |
|
nfc_hci_free_device(hdev); |
|
kfree(info); |
|
} |
|
EXPORT_SYMBOL(microread_remove); |
|
|
|
MODULE_LICENSE("GPL"); |
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
|