diff options
Diffstat (limited to 'kernel/drivers/nfc/st21nfca')
-rw-r--r-- | kernel/drivers/nfc/st21nfca/Makefile | 2 | ||||
-rw-r--r-- | kernel/drivers/nfc/st21nfca/core.c (renamed from kernel/drivers/nfc/st21nfca/st21nfca.c) | 48 | ||||
-rw-r--r-- | kernel/drivers/nfc/st21nfca/dep.c (renamed from kernel/drivers/nfc/st21nfca/st21nfca_dep.c) | 2 | ||||
-rw-r--r-- | kernel/drivers/nfc/st21nfca/i2c.c | 1 | ||||
-rw-r--r-- | kernel/drivers/nfc/st21nfca/se.c (renamed from kernel/drivers/nfc/st21nfca/st21nfca_se.c) | 18 | ||||
-rw-r--r-- | kernel/drivers/nfc/st21nfca/st21nfca.h | 148 | ||||
-rw-r--r-- | kernel/drivers/nfc/st21nfca/st21nfca_dep.h | 43 | ||||
-rw-r--r-- | kernel/drivers/nfc/st21nfca/st21nfca_se.h | 63 | ||||
-rw-r--r-- | kernel/drivers/nfc/st21nfca/vendor_cmds.c | 375 |
9 files changed, 541 insertions, 159 deletions
diff --git a/kernel/drivers/nfc/st21nfca/Makefile b/kernel/drivers/nfc/st21nfca/Makefile index 97edab4bb..ded6489c3 100644 --- a/kernel/drivers/nfc/st21nfca/Makefile +++ b/kernel/drivers/nfc/st21nfca/Makefile @@ -2,7 +2,7 @@ # Makefile for ST21NFCA HCI based NFC driver # -st21nfca_hci-objs = st21nfca.o st21nfca_dep.o st21nfca_se.o +st21nfca_hci-objs = core.o dep.o se.o vendor_cmds.o obj-$(CONFIG_NFC_ST21NFCA) += st21nfca_hci.o st21nfca_i2c-objs = i2c.o diff --git a/kernel/drivers/nfc/st21nfca/st21nfca.c b/kernel/drivers/nfc/st21nfca/core.c index 051286562..dd8b150fb 100644 --- a/kernel/drivers/nfc/st21nfca/st21nfca.c +++ b/kernel/drivers/nfc/st21nfca/core.c @@ -22,8 +22,6 @@ #include <net/nfc/llc.h> #include "st21nfca.h" -#include "st21nfca_dep.h" -#include "st21nfca_se.h" #define DRIVER_DESC "HCI NFC driver for ST21NFCA" @@ -87,12 +85,13 @@ static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES); static struct nfc_hci_gate st21nfca_gates[] = { {NFC_HCI_ADMIN_GATE, NFC_HCI_ADMIN_PIPE}, + {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE}, + {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_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_LINK_MGMT_PIPE}, {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE}, {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE}, - {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE}, {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE}, {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE}, {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE}, @@ -163,7 +162,6 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DM_GETINFO, pipe_info, sizeof(pipe_info), &skb_pipe_info); - if (r) continue; @@ -185,43 +183,33 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) continue; } - for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) && + for (j = 3; (j < ARRAY_SIZE(st21nfca_gates)) && (st21nfca_gates[j].gate != info->dst_gate_id) ; j++) ; if (j < ARRAY_SIZE(st21nfca_gates) && st21nfca_gates[j].gate == info->dst_gate_id && ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) { - st21nfca_gates[j].pipe = pipe_info[2]; + hdev->init_data.gates[j].pipe = pipe_info[2]; hdev->gate2pipe[st21nfca_gates[j].gate] = - st21nfca_gates[j].pipe; - hdev->pipes[st21nfca_gates[j].pipe].gate = - st21nfca_gates[j].gate; - hdev->pipes[st21nfca_gates[j].pipe].dest_host = - info->src_host_id; + pipe_info[2]; + hdev->pipes[pipe_info[2]].gate = + st21nfca_gates[j].gate; + hdev->pipes[pipe_info[2]].dest_host = + info->src_host_id; } kfree_skb(skb_pipe_info); } /* - * 3 gates have a well known pipe ID. - * They will never appear in the pipe list + * 3 gates have a well known pipe ID. Only NFC_HCI_LINK_MGMT_GATE + * is not yet open at this stage. */ - if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) { - for (i = skb_pipe_list->len + 3; - i < ARRAY_SIZE(st21nfca_gates) - 2; i++) { - r = nfc_hci_connect_gate(hdev, - NFC_HCI_HOST_CONTROLLER_ID, - st21nfca_gates[i].gate, - st21nfca_gates[i].pipe); - if (r < 0) - goto free_list; - } - } + r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, + NFC_HCI_LINK_MGMT_GATE, + NFC_HCI_LINK_MGMT_PIPE); - memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates)); -free_list: kfree_skb(skb_pipe_list); return r; } @@ -905,6 +893,8 @@ static int st21nfca_admin_event_received(struct nfc_hci_dev *hdev, u8 event, } } break; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on admin gate\n"); } kfree_skb(skb); return 0; @@ -933,6 +923,8 @@ static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, event, skb); case ST21NFCA_APDU_READER_GATE: return st21nfca_apdu_reader_event_received(hdev, event, skb); + case NFC_HCI_LOOPBACK_GATE: + return st21nfca_hci_loopback_event_received(hdev, event, skb); default: return 1; } @@ -993,7 +985,6 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, * persistent info to discriminate 2 identical chips */ dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES); - if (dev_num >= ST21NFCA_NUM_DEVICES) return -ENODEV; @@ -1035,6 +1026,7 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, *hdev = info->hdev; st21nfca_dep_init(info->hdev); st21nfca_se_init(info->hdev); + st21nfca_vendor_cmds_init(info->hdev); return 0; diff --git a/kernel/drivers/nfc/st21nfca/st21nfca_dep.c b/kernel/drivers/nfc/st21nfca/dep.c index 8882181d6..798a32bba 100644 --- a/kernel/drivers/nfc/st21nfca/st21nfca_dep.c +++ b/kernel/drivers/nfc/st21nfca/dep.c @@ -17,7 +17,6 @@ #include <net/nfc/hci.h> #include "st21nfca.h" -#include "st21nfca_dep.h" #define ST21NFCA_NFCIP1_INITIATOR 0x00 #define ST21NFCA_NFCIP1_REQ 0xd4 @@ -436,6 +435,7 @@ int st21nfca_dep_event_received(struct nfc_hci_dev *hdev, return r; return 0; default: + nfc_err(&hdev->ndev->dev, "Unexpected event on card f gate\n"); return 1; } kfree_skb(skb); diff --git a/kernel/drivers/nfc/st21nfca/i2c.c b/kernel/drivers/nfc/st21nfca/i2c.c index a32143951..a98da33e6 100644 --- a/kernel/drivers/nfc/st21nfca/i2c.c +++ b/kernel/drivers/nfc/st21nfca/i2c.c @@ -94,6 +94,7 @@ struct st21nfca_i2c_phy { int hard_fault; struct mutex phy_lock; }; + static u8 len_seq[] = { 16, 24, 12, 29 }; static u16 wait_tab[] = { 2, 3, 5, 15, 20, 40}; diff --git a/kernel/drivers/nfc/st21nfca/st21nfca_se.c b/kernel/drivers/nfc/st21nfca/se.c index 3197e9bb6..c79d99b24 100644 --- a/kernel/drivers/nfc/st21nfca/st21nfca_se.c +++ b/kernel/drivers/nfc/st21nfca/se.c @@ -17,10 +17,9 @@ #include <net/nfc/hci.h> #include "st21nfca.h" -#include "st21nfca_se.h" #define ST21NFCA_EVT_UICC_ACTIVATE 0x10 -#define ST21NFCA_EVT_UICC_DEACTIVATE 0x13 +#define ST21NFCA_EVT_UICC_DEACTIVATE 0x13 #define ST21NFCA_EVT_SE_HARD_RESET 0x20 #define ST21NFCA_EVT_SE_SOFT_RESET 0x11 #define ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER 0x21 @@ -101,7 +100,7 @@ static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx, u8 state) { struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); - int r; + int r, i; struct sk_buff *sk_host_list; u8 se_event, host_id; @@ -149,7 +148,10 @@ static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx, if (r < 0) return r; - host_id = sk_host_list->data[sk_host_list->len - 1]; + for (i = 0; i < sk_host_list->len && + sk_host_list->data[i] != se_idx; i++) + ; + host_id = sk_host_list->data[i]; kfree_skb(sk_host_list); if (state == ST21NFCA_SE_MODE_ON && host_id == se_idx) @@ -165,6 +167,9 @@ int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev) struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); int se_count = 0; + if (test_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks)) + return 0; + if (info->se_status->is_uicc_present) { nfc_add_se(hdev->ndev, NFC_HCI_UICC_HOST_ID, NFC_SE_UICC); se_count++; @@ -189,7 +194,6 @@ int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx) * Same for eSE. */ r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_ON); - if (r == ST21NFCA_ESE_HOST_ID) { st21nfca_se_get_atr(hdev); r = nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE, @@ -340,6 +344,7 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, r = nfc_se_transaction(hdev->ndev, host, transaction); break; default: + nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n"); return 1; } kfree_skb(skb); @@ -371,6 +376,9 @@ int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev, mod_timer(&info->se_info.bwi_timer, jiffies + msecs_to_jiffies(info->se_info.wt_timeout)); break; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on apdu reader gate\n"); + return 1; } exit: diff --git a/kernel/drivers/nfc/st21nfca/st21nfca.h b/kernel/drivers/nfc/st21nfca/st21nfca.h index 15a78d330..94ffb0501 100644 --- a/kernel/drivers/nfc/st21nfca/st21nfca.h +++ b/kernel/drivers/nfc/st21nfca/st21nfca.h @@ -18,9 +18,8 @@ #define __LOCAL_ST21NFCA_H_ #include <net/nfc/hci.h> - -#include "st21nfca_dep.h" -#include "st21nfca_se.h" +#include <linux/skbuff.h> +#include <linux/workqueue.h> #define HCI_MODE 0 @@ -46,28 +45,115 @@ #define ST21NFCA_HCI_LLC_MAX_SIZE (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \ ST21NFCA_HCI_LLC_MAX_PAYLOAD) +/* Reader RF commands */ +#define ST21NFCA_WR_XCHG_DATA 0x10 + +#define ST21NFCA_DEVICE_MGNT_GATE 0x01 +#define ST21NFCA_RF_READER_F_GATE 0x14 +#define ST21NFCA_RF_CARD_F_GATE 0x24 +#define ST21NFCA_APDU_READER_GATE 0xf0 +#define ST21NFCA_CONNECTIVITY_GATE 0x41 + +/* + * ref ISO7816-3 chap 8.1. the initial character TS is followed by a + * sequence of at most 32 characters. + */ +#define ST21NFCA_ESE_MAX_LENGTH 33 +#define ST21NFCA_ESE_HOST_ID 0xc0 + #define DRIVER_DESC "HCI NFC driver for ST21NFCA" -#define ST21NFCA_HCI_MODE 0 +#define ST21NFCA_HCI_MODE 0 +#define ST21NFCA_NUM_DEVICES 256 -#define ST21NFCA_NUM_DEVICES 256 +#define ST21NFCA_VENDOR_OUI 0x0080E1 /* STMicroelectronics */ +#define ST21NFCA_FACTORY_MODE 2 struct st21nfca_se_status { bool is_ese_present; bool is_uicc_present; }; -int st21nfca_hci_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 st21nfca_se_status *se_status); -void st21nfca_hci_remove(struct nfc_hci_dev *hdev); - enum st21nfca_state { ST21NFCA_ST_COLD, ST21NFCA_ST_READY, }; +/** + * enum nfc_vendor_cmds - supported nfc vendor commands + * + * @FACTORY_MODE: Allow to set the driver into a mode where no secure element + * are activated. It does not consider any NFC_ATTR_VENDOR_DATA. + * @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command. + * It does not consider any NFC_ATTR_VENDOR_DATA. + * @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example + * RF trimmings or low level drivers configurations (I2C, SPI, SWP). + * @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing + * table following RF technology, CLF mode or protocol. + * @HCI_DM_GET_INFO: Allow to retrieve CLF information. + * @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low + * level drivers configurations or RF trimmings. + * @HCI_DM_LOAD: Allow to load a firmware into the CLF. A complete + * packet can be more than 8KB. + * @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF + * configuration changes without CLF power off. + * @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the + * white list). + * @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF + * technology. When using this command to anti-collision is done. + * @HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF + * connectivity. + */ +enum nfc_vendor_cmds { + FACTORY_MODE, + HCI_CLEAR_ALL_PIPES, + HCI_DM_PUT_DATA, + HCI_DM_UPDATE_AID, + HCI_DM_GET_INFO, + HCI_DM_GET_DATA, + HCI_DM_LOAD, + HCI_DM_RESET, + HCI_GET_PARAM, + HCI_DM_FIELD_GENERATOR, + HCI_LOOPBACK, +}; + +struct st21nfca_vendor_info { + struct completion req_completion; + struct sk_buff *rx_skb; +}; + +struct st21nfca_dep_info { + struct sk_buff *tx_pending; + struct work_struct tx_work; + u8 curr_nfc_dep_pni; + u32 idx; + u8 to; + u8 did; + u8 bsi; + u8 bri; + u8 lri; +} __packed; + +struct st21nfca_se_info { + u8 atr[ST21NFCA_ESE_MAX_LENGTH]; + struct completion req_completion; + + struct timer_list bwi_timer; + int wt_timeout; /* in msecs */ + bool bwi_active; + + struct timer_list se_active_timer; + bool se_active; + int expected_pipes; + int count_pipes; + + bool xch_error; + + se_io_cb_t cb; + void *cb_context; +}; + struct st21nfca_hci_info { struct nfc_phy_ops *phy_ops; void *phy_id; @@ -85,15 +171,41 @@ struct st21nfca_hci_info { struct st21nfca_dep_info dep_info; struct st21nfca_se_info se_info; + struct st21nfca_vendor_info vendor_info; }; -/* Reader RF commands */ -#define ST21NFCA_WR_XCHG_DATA 0x10 +int st21nfca_hci_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 st21nfca_se_status *se_status); +void st21nfca_hci_remove(struct nfc_hci_dev *hdev); -#define ST21NFCA_DEVICE_MGNT_GATE 0x01 -#define ST21NFCA_RF_READER_F_GATE 0x14 -#define ST21NFCA_RF_CARD_F_GATE 0x24 -#define ST21NFCA_APDU_READER_GATE 0xf0 -#define ST21NFCA_CONNECTIVITY_GATE 0x41 +int st21nfca_dep_event_received(struct nfc_hci_dev *hdev, + u8 event, struct sk_buff *skb); +int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb); + +int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len); +int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb); +void st21nfca_dep_init(struct nfc_hci_dev *hdev); +void st21nfca_dep_deinit(struct nfc_hci_dev *hdev); + +int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, + u8 event, struct sk_buff *skb); +int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev, + u8 event, struct sk_buff *skb); + +int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev); +int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx); +int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx); +int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context); + +void st21nfca_se_init(struct nfc_hci_dev *hdev); +void st21nfca_se_deinit(struct nfc_hci_dev *hdev); + +int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *ndev, u8 event, + struct sk_buff *skb); +int st21nfca_vendor_cmds_init(struct nfc_hci_dev *ndev); #endif /* __LOCAL_ST21NFCA_H_ */ diff --git a/kernel/drivers/nfc/st21nfca/st21nfca_dep.h b/kernel/drivers/nfc/st21nfca/st21nfca_dep.h deleted file mode 100644 index baf4664b4..000000000 --- a/kernel/drivers/nfc/st21nfca/st21nfca_dep.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2014 STMicroelectronics SAS. 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/>. - */ - -#ifndef __ST21NFCA_DEP_H -#define __ST21NFCA_DEP_H - -#include <linux/skbuff.h> -#include <linux/workqueue.h> - -struct st21nfca_dep_info { - struct sk_buff *tx_pending; - struct work_struct tx_work; - u8 curr_nfc_dep_pni; - u32 idx; - u8 to; - u8 did; - u8 bsi; - u8 bri; - u8 lri; -} __packed; - -int st21nfca_dep_event_received(struct nfc_hci_dev *hdev, - u8 event, struct sk_buff *skb); -int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb); - -int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len); -int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb); -void st21nfca_dep_init(struct nfc_hci_dev *hdev); -void st21nfca_dep_deinit(struct nfc_hci_dev *hdev); -#endif /* __ST21NFCA_DEP_H */ diff --git a/kernel/drivers/nfc/st21nfca/st21nfca_se.h b/kernel/drivers/nfc/st21nfca/st21nfca_se.h deleted file mode 100644 index b172cfcae..000000000 --- a/kernel/drivers/nfc/st21nfca/st21nfca_se.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2014 STMicroelectronics SAS. 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/>. - */ - -#ifndef __ST21NFCA_SE_H -#define __ST21NFCA_SE_H - -#include <linux/skbuff.h> -#include <linux/workqueue.h> - -/* - * ref ISO7816-3 chap 8.1. the initial character TS is followed by a - * sequence of at most 32 characters. - */ -#define ST21NFCA_ESE_MAX_LENGTH 33 -#define ST21NFCA_ESE_HOST_ID 0xc0 - -struct st21nfca_se_info { - u8 atr[ST21NFCA_ESE_MAX_LENGTH]; - struct completion req_completion; - - struct timer_list bwi_timer; - int wt_timeout; /* in msecs */ - bool bwi_active; - - struct timer_list se_active_timer; - bool se_active; - int expected_pipes; - int count_pipes; - - bool xch_error; - - se_io_cb_t cb; - void *cb_context; -}; - -int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, - u8 event, struct sk_buff *skb); -int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev, - u8 event, struct sk_buff *skb); - -int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev); -int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx); -int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx); -int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx, - u8 *apdu, size_t apdu_length, - se_io_cb_t cb, void *cb_context); - -void st21nfca_se_init(struct nfc_hci_dev *hdev); -void st21nfca_se_deinit(struct nfc_hci_dev *hdev); -#endif /* __ST21NFCA_SE_H */ diff --git a/kernel/drivers/nfc/st21nfca/vendor_cmds.c b/kernel/drivers/nfc/st21nfca/vendor_cmds.c new file mode 100644 index 000000000..ab765e547 --- /dev/null +++ b/kernel/drivers/nfc/st21nfca/vendor_cmds.c @@ -0,0 +1,375 @@ +/* + * Proprietary commands extension for STMicroelectronics NFC Chip + * + * Copyright (C) 2014-2015 STMicroelectronics SAS. 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/>. + */ + +#include <net/genetlink.h> +#include <linux/module.h> +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "st21nfca.h" + +#define ST21NFCA_HCI_DM_GETDATA 0x10 +#define ST21NFCA_HCI_DM_PUTDATA 0x11 +#define ST21NFCA_HCI_DM_LOAD 0x12 +#define ST21NFCA_HCI_DM_GETINFO 0x13 +#define ST21NFCA_HCI_DM_UPDATE_AID 0x20 +#define ST21NFCA_HCI_DM_RESET 0x3e + +#define ST21NFCA_HCI_DM_FIELD_GENERATOR 0x32 + +#define ST21NFCA_FACTORY_MODE_ON 1 +#define ST21NFCA_FACTORY_MODE_OFF 0 + +#define ST21NFCA_EVT_POST_DATA 0x02 + +struct get_param_data { + u8 gate; + u8 data; +} __packed; + +static int st21nfca_factory_mode(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + if (data_len != 1) + return -EINVAL; + + pr_debug("factory mode: %x\n", ((u8 *)data)[0]); + + switch (((u8 *)data)[0]) { + case ST21NFCA_FACTORY_MODE_ON: + test_and_set_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks); + break; + case ST21NFCA_FACTORY_MODE_OFF: + clear_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int st21nfca_hci_clear_all_pipes(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_disconnect_all_gates(hdev); +} + +static int st21nfca_hci_dm_put_data(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_PUTDATA, data, + data_len, NULL); +} + +static int st21nfca_hci_dm_update_aid(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_UPDATE_AID, data, data_len, NULL); +} + +static int st21nfca_hci_dm_get_info(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_GETINFO, + data, data_len, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_DM_GET_INFO, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_get_data(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_GETDATA, + data, data_len, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_DM_GET_DATA, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_load(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_LOAD, data, data_len, NULL); +} + +static int st21nfca_hci_dm_reset(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd_async(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_RESET, data, data_len, NULL, NULL); + if (r < 0) + return r; + + r = nfc_llc_stop(hdev->llc); + if (r < 0) + return r; + + return nfc_llc_start(hdev->llc); +} + +static int st21nfca_hci_get_param(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + struct get_param_data *param = (struct get_param_data *)data; + + if (data_len < sizeof(struct get_param_data)) + return -EPROTO; + + r = nfc_hci_get_param(hdev, param->gate, param->data, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_GET_PARAM, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_field_generator(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_FIELD_GENERATOR, + data, data_len, NULL); +} + +int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *hdev, u8 event, + struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + switch (event) { + case ST21NFCA_EVT_POST_DATA: + info->vendor_info.rx_skb = skb; + break; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on loopback gate\n"); + } + complete(&info->vendor_info.req_completion); + return 0; +} +EXPORT_SYMBOL(st21nfca_hci_loopback_event_received); + +static int st21nfca_hci_loopback(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + if (data_len <= 0) + return -EPROTO; + + reinit_completion(&info->vendor_info.req_completion); + info->vendor_info.rx_skb = NULL; + + r = nfc_hci_send_event(hdev, NFC_HCI_LOOPBACK_GATE, + ST21NFCA_EVT_POST_DATA, data, data_len); + if (r < 0) { + r = -EPROTO; + goto exit; + } + + wait_for_completion_interruptible(&info->vendor_info.req_completion); + if (!info->vendor_info.rx_skb || + info->vendor_info.rx_skb->len != data_len) { + r = -EPROTO; + goto exit; + } + + msg = nfc_vendor_cmd_alloc_reply_skb(hdev->ndev, + ST21NFCA_VENDOR_OUI, + HCI_LOOPBACK, + info->vendor_info.rx_skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len, + info->vendor_info.rx_skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); +free_skb: + kfree_skb(info->vendor_info.rx_skb); +exit: + return r; +} + +static struct nfc_vendor_cmd st21nfca_vendor_cmds[] = { + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = FACTORY_MODE, + .doit = st21nfca_factory_mode, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_CLEAR_ALL_PIPES, + .doit = st21nfca_hci_clear_all_pipes, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_PUT_DATA, + .doit = st21nfca_hci_dm_put_data, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_UPDATE_AID, + .doit = st21nfca_hci_dm_update_aid, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_GET_INFO, + .doit = st21nfca_hci_dm_get_info, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_GET_DATA, + .doit = st21nfca_hci_dm_get_data, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_LOAD, + .doit = st21nfca_hci_dm_load, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_RESET, + .doit = st21nfca_hci_dm_reset, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_GET_PARAM, + .doit = st21nfca_hci_get_param, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_FIELD_GENERATOR, + .doit = st21nfca_hci_dm_field_generator, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_LOOPBACK, + .doit = st21nfca_hci_loopback, + }, +}; + +int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + init_completion(&info->vendor_info.req_completion); + return nfc_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds, + sizeof(st21nfca_vendor_cmds)); +} +EXPORT_SYMBOL(st21nfca_vendor_cmds_init); |