46fe777164
Based on 1 normalized pattern(s): 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 see http www gnu org licenses extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 33 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190531081038.745679586@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
724 lines
19 KiB
C
724 lines
19 KiB
C
// 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);
|