| /* |
| * HCI based Driver for Inside Secure microread NFC Chip |
| * |
| * Copyright (C) 2013 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, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #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); |