diff options
| author | Christophe Ricard <christophe.ricard@gmail.com> | 2015-10-25 17:54:36 -0400 |
|---|---|---|
| committer | Samuel Ortiz <sameo@linux.intel.com> | 2015-10-26 22:55:01 -0400 |
| commit | b1fa4dc4ffef843bda17f26bbf54fb5d8bc23d2b (patch) | |
| tree | 23444be92a93ba3c6d0f84bff6b522ac92d7023b /drivers/nfc/st-nci | |
| parent | ba723199d18eeb5021cd6ace4aaf90670d6b3133 (diff) | |
NFC: st-nci: Add support for proprietary commands
Add support for proprietary commands useful mainly for
factory testings. Here is a list:
- 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
like 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_DIRECT_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.
- HCI_DM_VDC_MEASUREMENT_VALUE: Allow to measure the field applied
on the CLF antenna. A value between 0 and 0x0f is returned. 0 is
maximum.
- HCI_DM_FWUPD_START: Allow to put CLF into firmware update mode.
It is a specific CLF command as there is no GPIO for this.
- HCI_DM_FWUPD_END: Allow to complete firmware update.
- HCI_DM_VDC_VALUE_COMPARISON: Allow to compare the field applied
on the CLF antenna to a reference value.
- MANUFACTURER_SPECIFIC: Allow to retrieve manufacturer specific data
received during a NCI_CORE_INIT_CMD.
Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc/st-nci')
| -rw-r--r-- | drivers/nfc/st-nci/Makefile | 2 | ||||
| -rw-r--r-- | drivers/nfc/st-nci/core.c | 13 | ||||
| -rw-r--r-- | drivers/nfc/st-nci/se.c | 29 | ||||
| -rw-r--r-- | drivers/nfc/st-nci/st-nci.h | 68 | ||||
| -rw-r--r-- | drivers/nfc/st-nci/vendor_cmds.c | 516 |
5 files changed, 618 insertions, 10 deletions
diff --git a/drivers/nfc/st-nci/Makefile b/drivers/nfc/st-nci/Makefile index 594c63d60070..ea40ace05fc2 100644 --- a/drivers/nfc/st-nci/Makefile +++ b/drivers/nfc/st-nci/Makefile | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | # Makefile for ST21NFCB NCI based NFC driver | 2 | # Makefile for ST21NFCB NCI based NFC driver |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | st-nci-objs = ndlc.o core.o se.o | 5 | st-nci-objs = ndlc.o core.o se.o vendor_cmds.o |
| 6 | obj-$(CONFIG_NFC_ST_NCI) += st-nci.o | 6 | obj-$(CONFIG_NFC_ST_NCI) += st-nci.o |
| 7 | 7 | ||
| 8 | st-nci_i2c-objs = i2c.o | 8 | st-nci_i2c-objs = i2c.o |
diff --git a/drivers/nfc/st-nci/core.c b/drivers/nfc/st-nci/core.c index 07bfc0f00050..73d36dd8345c 100644 --- a/drivers/nfc/st-nci/core.c +++ b/drivers/nfc/st-nci/core.c | |||
| @@ -152,14 +152,23 @@ int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, | |||
| 152 | 152 | ||
| 153 | nci_set_drvdata(ndlc->ndev, info); | 153 | nci_set_drvdata(ndlc->ndev, info); |
| 154 | 154 | ||
| 155 | r = st_nci_vendor_cmds_init(ndlc->ndev); | ||
| 156 | if (r) { | ||
| 157 | pr_err("Cannot register proprietary vendor cmds\n"); | ||
| 158 | goto err_reg_dev; | ||
| 159 | } | ||
| 160 | |||
| 155 | r = nci_register_device(ndlc->ndev); | 161 | r = nci_register_device(ndlc->ndev); |
| 156 | if (r) { | 162 | if (r) { |
| 157 | pr_err("Cannot register nfc device to nci core\n"); | 163 | pr_err("Cannot register nfc device to nci core\n"); |
| 158 | nci_free_device(ndlc->ndev); | 164 | goto err_reg_dev; |
| 159 | return r; | ||
| 160 | } | 165 | } |
| 161 | 166 | ||
| 162 | return st_nci_se_init(ndlc->ndev); | 167 | return st_nci_se_init(ndlc->ndev); |
| 168 | |||
| 169 | err_reg_dev: | ||
| 170 | nci_free_device(ndlc->ndev); | ||
| 171 | return r; | ||
| 163 | } | 172 | } |
| 164 | EXPORT_SYMBOL_GPL(st_nci_probe); | 173 | EXPORT_SYMBOL_GPL(st_nci_probe); |
| 165 | 174 | ||
diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c index 4c98346bab04..7ec4dba98a80 100644 --- a/drivers/nfc/st-nci/se.c +++ b/drivers/nfc/st-nci/se.c | |||
| @@ -39,7 +39,6 @@ struct st_nci_pipe_info { | |||
| 39 | #define ST_NCI_ESE_HOST_ID 0xc0 | 39 | #define ST_NCI_ESE_HOST_ID 0xc0 |
| 40 | 40 | ||
| 41 | /* Gates */ | 41 | /* Gates */ |
| 42 | #define ST_NCI_DEVICE_MGNT_GATE 0x01 | ||
| 43 | #define ST_NCI_APDU_READER_GATE 0xf0 | 42 | #define ST_NCI_APDU_READER_GATE 0xf0 |
| 44 | #define ST_NCI_CONNECTIVITY_GATE 0x41 | 43 | #define ST_NCI_CONNECTIVITY_GATE 0x41 |
| 45 | 44 | ||
| @@ -114,6 +113,8 @@ static struct nci_hci_gate st_nci_gates[] = { | |||
| 114 | 113 | ||
| 115 | {NCI_HCI_IDENTITY_MGMT_GATE, NCI_HCI_INVALID_PIPE, | 114 | {NCI_HCI_IDENTITY_MGMT_GATE, NCI_HCI_INVALID_PIPE, |
| 116 | ST_NCI_HOST_CONTROLLER_ID}, | 115 | ST_NCI_HOST_CONTROLLER_ID}, |
| 116 | {NCI_HCI_LOOPBACK_GATE, NCI_HCI_INVALID_PIPE, | ||
| 117 | ST_NCI_HOST_CONTROLLER_ID}, | ||
| 117 | 118 | ||
| 118 | /* Secure element pipes are created by secure element host */ | 119 | /* Secure element pipes are created by secure element host */ |
| 119 | {ST_NCI_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, | 120 | {ST_NCI_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, |
| @@ -376,8 +377,10 @@ void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe, | |||
| 376 | st_nci_hci_apdu_reader_event_received(ndev, event, skb); | 377 | st_nci_hci_apdu_reader_event_received(ndev, event, skb); |
| 377 | break; | 378 | break; |
| 378 | case ST_NCI_CONNECTIVITY_GATE: | 379 | case ST_NCI_CONNECTIVITY_GATE: |
| 379 | st_nci_hci_connectivity_event_received(ndev, host, event, | 380 | st_nci_hci_connectivity_event_received(ndev, host, event, skb); |
| 380 | skb); | 381 | break; |
| 382 | case NCI_HCI_LOOPBACK_GATE: | ||
| 383 | st_nci_hci_loopback_event_received(ndev, event, skb); | ||
| 381 | break; | 384 | break; |
| 382 | } | 385 | } |
| 383 | } | 386 | } |
| @@ -509,6 +512,7 @@ EXPORT_SYMBOL_GPL(st_nci_enable_se); | |||
| 509 | 512 | ||
| 510 | static int st_nci_hci_network_init(struct nci_dev *ndev) | 513 | static int st_nci_hci_network_init(struct nci_dev *ndev) |
| 511 | { | 514 | { |
| 515 | struct st_nci_info *info = nci_get_drvdata(ndev); | ||
| 512 | struct core_conn_create_dest_spec_params *dest_params; | 516 | struct core_conn_create_dest_spec_params *dest_params; |
| 513 | struct dest_spec_params spec_params; | 517 | struct dest_spec_params spec_params; |
| 514 | struct nci_conn_info *conn_info; | 518 | struct nci_conn_info *conn_info; |
| @@ -561,10 +565,17 @@ static int st_nci_hci_network_init(struct nci_dev *ndev) | |||
| 561 | if (r != NCI_HCI_ANY_OK) | 565 | if (r != NCI_HCI_ANY_OK) |
| 562 | goto free_dest_params; | 566 | goto free_dest_params; |
| 563 | 567 | ||
| 564 | r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id, | 568 | /* |
| 565 | NCI_NFCEE_ENABLE); | 569 | * In factory mode, we prevent secure elements activation |
| 566 | if (r != NCI_STATUS_OK) | 570 | * by disabling nfcee on the current HCI connection id. |
| 567 | goto free_dest_params; | 571 | * HCI will be used here only for proprietary commands. |
| 572 | */ | ||
| 573 | if (test_bit(ST_NCI_FACTORY_MODE, &info->flags)) | ||
| 574 | r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id, | ||
| 575 | NCI_NFCEE_DISABLE); | ||
| 576 | else | ||
| 577 | r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id, | ||
| 578 | NCI_NFCEE_ENABLE); | ||
| 568 | 579 | ||
| 569 | free_dest_params: | 580 | free_dest_params: |
| 570 | kfree(dest_params); | 581 | kfree(dest_params); |
| @@ -578,6 +589,7 @@ int st_nci_discover_se(struct nci_dev *ndev) | |||
| 578 | u8 param[2]; | 589 | u8 param[2]; |
| 579 | int r; | 590 | int r; |
| 580 | int se_count = 0; | 591 | int se_count = 0; |
| 592 | struct st_nci_info *info = nci_get_drvdata(ndev); | ||
| 581 | 593 | ||
| 582 | pr_debug("st_nci_discover_se\n"); | 594 | pr_debug("st_nci_discover_se\n"); |
| 583 | 595 | ||
| @@ -585,6 +597,9 @@ int st_nci_discover_se(struct nci_dev *ndev) | |||
| 585 | if (r != 0) | 597 | if (r != 0) |
| 586 | return r; | 598 | return r; |
| 587 | 599 | ||
| 600 | if (test_bit(ST_NCI_FACTORY_MODE, &info->flags)) | ||
| 601 | return 0; | ||
| 602 | |||
| 588 | param[0] = ST_NCI_UICC_HOST_ID; | 603 | param[0] = ST_NCI_UICC_HOST_ID; |
| 589 | param[1] = ST_NCI_HCI_HOST_ID_ESE; | 604 | param[1] = ST_NCI_HCI_HOST_ID_ESE; |
| 590 | r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE, | 605 | r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE, |
diff --git a/drivers/nfc/st-nci/st-nci.h b/drivers/nfc/st-nci/st-nci.h index 90c21eab3058..9c9bb19cc9ff 100644 --- a/drivers/nfc/st-nci/st-nci.h +++ b/drivers/nfc/st-nci/st-nci.h | |||
| @@ -34,6 +34,11 @@ | |||
| 34 | #define ST_NCI_ESE_MAX_LENGTH 33 | 34 | #define ST_NCI_ESE_MAX_LENGTH 33 |
| 35 | #define ST_NCI_HCI_HOST_ID_ESE 0xc0 | 35 | #define ST_NCI_HCI_HOST_ID_ESE 0xc0 |
| 36 | 36 | ||
| 37 | #define ST_NCI_DEVICE_MGNT_GATE 0x01 | ||
| 38 | |||
| 39 | #define ST_NCI_VENDOR_OUI 0x0080E1 /* STMicroelectronics */ | ||
| 40 | #define ST_NCI_FACTORY_MODE 2 | ||
| 41 | |||
| 37 | struct nci_mode_set_cmd { | 42 | struct nci_mode_set_cmd { |
| 38 | u8 cmd_type; | 43 | u8 cmd_type; |
| 39 | u8 mode; | 44 | u8 mode; |
| @@ -60,10 +65,69 @@ struct st_nci_se_info { | |||
| 60 | void *cb_context; | 65 | void *cb_context; |
| 61 | }; | 66 | }; |
| 62 | 67 | ||
| 68 | /** | ||
| 69 | * enum nfc_vendor_cmds - supported nfc vendor commands | ||
| 70 | * | ||
| 71 | * @FACTORY_MODE: Allow to set the driver into a mode where no secure element | ||
| 72 | * are activated. It does not consider any NFC_ATTR_VENDOR_DATA. | ||
| 73 | * @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command. | ||
| 74 | * It does not consider any NFC_ATTR_VENDOR_DATA. | ||
| 75 | * @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example | ||
| 76 | * RF trimmings or low level drivers configurations (I2C, SPI, SWP). | ||
| 77 | * @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing | ||
| 78 | * table following RF technology, CLF mode or protocol. | ||
| 79 | * @HCI_DM_GET_INFO: Allow to retrieve CLF information. | ||
| 80 | * @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low | ||
| 81 | * level drivers configurations or RF trimmings. | ||
| 82 | * @HCI_DM_DIRECT_LOAD: Allow to load a firmware into the CLF. A complete | ||
| 83 | * packet can be more than 8KB. | ||
| 84 | * @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF | ||
| 85 | * configuration changes without CLF power off. | ||
| 86 | * @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the | ||
| 87 | * white list). | ||
| 88 | * @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF | ||
| 89 | * technology. When using this command to anti-collision is done. | ||
| 90 | * @HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF | ||
| 91 | * connectivity. | ||
| 92 | * @HCI_DM_VDC_MEASUREMENT_VALUE: Allow to measure the field applied on the | ||
| 93 | * CLF antenna. A value between 0 and 0x0f is returned. 0 is maximum. | ||
| 94 | * @HCI_DM_FWUPD_START: Allow to put CLF into firmware update mode. It is a | ||
| 95 | * specific CLF command as there is no GPIO for this. | ||
| 96 | * @HCI_DM_FWUPD_END: Allow to complete firmware update. | ||
| 97 | * @HCI_DM_VDC_VALUE_COMPARISON: Allow to compare the field applied on the | ||
| 98 | * CLF antenna to a reference value. | ||
| 99 | * @MANUFACTURER_SPECIFIC: Allow to retrieve manufacturer specific data | ||
| 100 | * received during a NCI_CORE_INIT_CMD. | ||
| 101 | */ | ||
| 102 | enum nfc_vendor_cmds { | ||
| 103 | FACTORY_MODE, | ||
| 104 | HCI_CLEAR_ALL_PIPES, | ||
| 105 | HCI_DM_PUT_DATA, | ||
| 106 | HCI_DM_UPDATE_AID, | ||
| 107 | HCI_DM_GET_INFO, | ||
| 108 | HCI_DM_GET_DATA, | ||
| 109 | HCI_DM_DIRECT_LOAD, | ||
| 110 | HCI_DM_RESET, | ||
| 111 | HCI_GET_PARAM, | ||
| 112 | HCI_DM_FIELD_GENERATOR, | ||
| 113 | HCI_LOOPBACK, | ||
| 114 | HCI_DM_FWUPD_START, | ||
| 115 | HCI_DM_FWUPD_END, | ||
| 116 | HCI_DM_VDC_MEASUREMENT_VALUE, | ||
| 117 | HCI_DM_VDC_VALUE_COMPARISON, | ||
| 118 | MANUFACTURER_SPECIFIC, | ||
| 119 | }; | ||
| 120 | |||
| 121 | struct st_nci_vendor_info { | ||
| 122 | struct completion req_completion; | ||
| 123 | struct sk_buff *rx_skb; | ||
| 124 | }; | ||
| 125 | |||
| 63 | struct st_nci_info { | 126 | struct st_nci_info { |
| 64 | struct llt_ndlc *ndlc; | 127 | struct llt_ndlc *ndlc; |
| 65 | unsigned long flags; | 128 | unsigned long flags; |
| 66 | struct st_nci_se_info se_info; | 129 | struct st_nci_se_info se_info; |
| 130 | struct st_nci_vendor_info vendor_info; | ||
| 67 | }; | 131 | }; |
| 68 | 132 | ||
| 69 | void st_nci_remove(struct nci_dev *ndev); | 133 | void st_nci_remove(struct nci_dev *ndev); |
| @@ -85,4 +149,8 @@ void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe, | |||
| 85 | void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, | 149 | void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, |
| 86 | struct sk_buff *skb); | 150 | struct sk_buff *skb); |
| 87 | 151 | ||
| 152 | void st_nci_hci_loopback_event_received(struct nci_dev *ndev, u8 event, | ||
| 153 | struct sk_buff *skb); | ||
| 154 | int st_nci_vendor_cmds_init(struct nci_dev *ndev); | ||
| 155 | |||
| 88 | #endif /* __LOCAL_ST_NCI_H_ */ | 156 | #endif /* __LOCAL_ST_NCI_H_ */ |
diff --git a/drivers/nfc/st-nci/vendor_cmds.c b/drivers/nfc/st-nci/vendor_cmds.c new file mode 100644 index 000000000000..b5debce4ae0b --- /dev/null +++ b/drivers/nfc/st-nci/vendor_cmds.c | |||
| @@ -0,0 +1,516 @@ | |||
| 1 | /* | ||
| 2 | * Proprietary commands extension for STMicroelectronics NFC NCI Chip | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <net/genetlink.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/nfc.h> | ||
| 22 | #include <linux/delay.h> | ||
| 23 | #include <net/nfc/nci_core.h> | ||
| 24 | |||
| 25 | #include "st-nci.h" | ||
| 26 | |||
| 27 | #define ST_NCI_HCI_DM_GETDATA 0x10 | ||
| 28 | #define ST_NCI_HCI_DM_PUTDATA 0x11 | ||
| 29 | #define ST_NCI_HCI_DM_LOAD 0x12 | ||
| 30 | #define ST_NCI_HCI_DM_GETINFO 0x13 | ||
| 31 | #define ST_NCI_HCI_DM_FWUPD_START 0x14 | ||
| 32 | #define ST_NCI_HCI_DM_FWUPD_STOP 0x15 | ||
| 33 | #define ST_NCI_HCI_DM_UPDATE_AID 0x20 | ||
| 34 | #define ST_NCI_HCI_DM_RESET 0x3e | ||
| 35 | |||
| 36 | #define ST_NCI_HCI_DM_FIELD_GENERATOR 0x32 | ||
| 37 | #define ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE 0x33 | ||
| 38 | #define ST_NCI_HCI_DM_VDC_VALUE_COMPARISON 0x34 | ||
| 39 | |||
| 40 | #define ST_NCI_FACTORY_MODE_ON 1 | ||
| 41 | #define ST_NCI_FACTORY_MODE_OFF 0 | ||
| 42 | |||
| 43 | #define ST_NCI_EVT_POST_DATA 0x02 | ||
| 44 | |||
| 45 | struct get_param_data { | ||
| 46 | u8 gate; | ||
| 47 | u8 data; | ||
| 48 | } __packed; | ||
| 49 | |||
| 50 | static int st_nci_factory_mode(struct nfc_dev *dev, void *data, | ||
| 51 | size_t data_len) | ||
| 52 | { | ||
| 53 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 54 | struct st_nci_info *info = nci_get_drvdata(ndev); | ||
| 55 | |||
| 56 | if (data_len != 1) | ||
| 57 | return -EINVAL; | ||
| 58 | |||
| 59 | pr_debug("factory mode: %x\n", ((u8 *)data)[0]); | ||
| 60 | |||
| 61 | switch (((u8 *)data)[0]) { | ||
| 62 | case ST_NCI_FACTORY_MODE_ON: | ||
| 63 | test_and_set_bit(ST_NCI_FACTORY_MODE, &info->flags); | ||
| 64 | break; | ||
| 65 | case ST_NCI_FACTORY_MODE_OFF: | ||
| 66 | clear_bit(ST_NCI_FACTORY_MODE, &info->flags); | ||
| 67 | break; | ||
| 68 | default: | ||
| 69 | return -EINVAL; | ||
| 70 | } | ||
| 71 | |||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | static int st_nci_hci_clear_all_pipes(struct nfc_dev *dev, void *data, | ||
| 76 | size_t data_len) | ||
| 77 | { | ||
| 78 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 79 | |||
| 80 | return nci_hci_clear_all_pipes(ndev); | ||
| 81 | } | ||
| 82 | |||
| 83 | static int st_nci_hci_dm_put_data(struct nfc_dev *dev, void *data, | ||
| 84 | size_t data_len) | ||
| 85 | { | ||
| 86 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 87 | |||
| 88 | return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 89 | ST_NCI_HCI_DM_PUTDATA, data, | ||
| 90 | data_len, NULL); | ||
| 91 | } | ||
| 92 | |||
| 93 | static int st_nci_hci_dm_update_aid(struct nfc_dev *dev, void *data, | ||
| 94 | size_t data_len) | ||
| 95 | { | ||
| 96 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 97 | |||
| 98 | return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 99 | ST_NCI_HCI_DM_UPDATE_AID, data, data_len, NULL); | ||
| 100 | } | ||
| 101 | |||
| 102 | static int st_nci_hci_dm_get_info(struct nfc_dev *dev, void *data, | ||
| 103 | size_t data_len) | ||
| 104 | { | ||
| 105 | int r; | ||
| 106 | struct sk_buff *msg, *skb; | ||
| 107 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 108 | |||
| 109 | r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETINFO, | ||
| 110 | data, data_len, &skb); | ||
| 111 | if (r) | ||
| 112 | goto exit; | ||
| 113 | |||
| 114 | msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, | ||
| 115 | HCI_DM_GET_INFO, skb->len); | ||
| 116 | if (!msg) { | ||
| 117 | r = -ENOMEM; | ||
| 118 | goto free_skb; | ||
| 119 | } | ||
| 120 | |||
| 121 | if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { | ||
| 122 | kfree_skb(msg); | ||
| 123 | r = -ENOBUFS; | ||
| 124 | goto free_skb; | ||
| 125 | } | ||
| 126 | |||
| 127 | r = nfc_vendor_cmd_reply(msg); | ||
| 128 | |||
| 129 | free_skb: | ||
| 130 | kfree_skb(skb); | ||
| 131 | exit: | ||
| 132 | return r; | ||
| 133 | } | ||
| 134 | |||
| 135 | static int st_nci_hci_dm_get_data(struct nfc_dev *dev, void *data, | ||
| 136 | size_t data_len) | ||
| 137 | { | ||
| 138 | int r; | ||
| 139 | struct sk_buff *msg, *skb; | ||
| 140 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 141 | |||
| 142 | r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETDATA, | ||
| 143 | data, data_len, &skb); | ||
| 144 | if (r) | ||
| 145 | goto exit; | ||
| 146 | |||
| 147 | msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, | ||
| 148 | HCI_DM_GET_DATA, skb->len); | ||
| 149 | if (!msg) { | ||
| 150 | r = -ENOMEM; | ||
| 151 | goto free_skb; | ||
| 152 | } | ||
| 153 | |||
| 154 | if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { | ||
| 155 | kfree_skb(msg); | ||
| 156 | r = -ENOBUFS; | ||
| 157 | goto free_skb; | ||
| 158 | } | ||
| 159 | |||
| 160 | r = nfc_vendor_cmd_reply(msg); | ||
| 161 | |||
| 162 | free_skb: | ||
| 163 | kfree_skb(skb); | ||
| 164 | exit: | ||
| 165 | return r; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data, | ||
| 169 | size_t data_len) | ||
| 170 | { | ||
| 171 | int r; | ||
| 172 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 173 | |||
| 174 | dev->fw_download_in_progress = true; | ||
| 175 | r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 176 | ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL); | ||
| 177 | if (r) | ||
| 178 | dev->fw_download_in_progress = false; | ||
| 179 | |||
| 180 | return r; | ||
| 181 | } | ||
| 182 | |||
| 183 | static int st_nci_hci_dm_fwupd_end(struct nfc_dev *dev, void *data, | ||
| 184 | size_t data_len) | ||
| 185 | { | ||
| 186 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 187 | |||
| 188 | return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 189 | ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL); | ||
| 190 | } | ||
| 191 | |||
| 192 | static int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data, | ||
| 193 | size_t data_len) | ||
| 194 | { | ||
| 195 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 196 | |||
| 197 | if (dev->fw_download_in_progress) { | ||
| 198 | dev->fw_download_in_progress = false; | ||
| 199 | return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 200 | ST_NCI_HCI_DM_LOAD, data, data_len, NULL); | ||
| 201 | } | ||
| 202 | return -EPROTO; | ||
| 203 | } | ||
| 204 | |||
| 205 | static int st_nci_hci_dm_reset(struct nfc_dev *dev, void *data, | ||
| 206 | size_t data_len) | ||
| 207 | { | ||
| 208 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 209 | |||
| 210 | nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 211 | ST_NCI_HCI_DM_RESET, data, data_len, NULL); | ||
| 212 | msleep(200); | ||
| 213 | |||
| 214 | return 0; | ||
| 215 | } | ||
| 216 | |||
| 217 | static int st_nci_hci_get_param(struct nfc_dev *dev, void *data, | ||
| 218 | size_t data_len) | ||
| 219 | { | ||
| 220 | int r; | ||
| 221 | struct sk_buff *msg, *skb; | ||
| 222 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 223 | struct get_param_data *param = (struct get_param_data *)data; | ||
| 224 | |||
| 225 | if (data_len < sizeof(struct get_param_data)) | ||
| 226 | return -EPROTO; | ||
| 227 | |||
| 228 | r = nci_hci_get_param(ndev, param->gate, param->data, &skb); | ||
| 229 | if (r) | ||
| 230 | goto exit; | ||
| 231 | |||
| 232 | msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, | ||
| 233 | HCI_GET_PARAM, skb->len); | ||
| 234 | if (!msg) { | ||
| 235 | r = -ENOMEM; | ||
| 236 | goto free_skb; | ||
| 237 | } | ||
| 238 | |||
| 239 | if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { | ||
| 240 | kfree_skb(msg); | ||
| 241 | r = -ENOBUFS; | ||
| 242 | goto free_skb; | ||
| 243 | } | ||
| 244 | |||
| 245 | r = nfc_vendor_cmd_reply(msg); | ||
| 246 | |||
| 247 | free_skb: | ||
| 248 | kfree_skb(skb); | ||
| 249 | exit: | ||
| 250 | return r; | ||
| 251 | } | ||
| 252 | |||
| 253 | static int st_nci_hci_dm_field_generator(struct nfc_dev *dev, void *data, | ||
| 254 | size_t data_len) | ||
| 255 | { | ||
| 256 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 257 | |||
| 258 | return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 259 | ST_NCI_HCI_DM_FIELD_GENERATOR, data, data_len, NULL); | ||
| 260 | } | ||
| 261 | |||
| 262 | static int st_nci_hci_dm_vdc_measurement_value(struct nfc_dev *dev, void *data, | ||
| 263 | size_t data_len) | ||
| 264 | { | ||
| 265 | int r; | ||
| 266 | struct sk_buff *msg, *skb; | ||
| 267 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 268 | |||
| 269 | if (data_len != 4) | ||
| 270 | return -EPROTO; | ||
| 271 | |||
| 272 | r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 273 | ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE, | ||
| 274 | data, data_len, &skb); | ||
| 275 | if (r) | ||
| 276 | goto exit; | ||
| 277 | |||
| 278 | msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, | ||
| 279 | HCI_DM_VDC_MEASUREMENT_VALUE, skb->len); | ||
| 280 | if (!msg) { | ||
| 281 | r = -ENOMEM; | ||
| 282 | goto free_skb; | ||
| 283 | } | ||
| 284 | |||
| 285 | if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { | ||
| 286 | kfree_skb(msg); | ||
| 287 | r = -ENOBUFS; | ||
| 288 | goto free_skb; | ||
| 289 | } | ||
| 290 | |||
| 291 | r = nfc_vendor_cmd_reply(msg); | ||
| 292 | |||
| 293 | free_skb: | ||
| 294 | kfree_skb(skb); | ||
| 295 | exit: | ||
| 296 | return r; | ||
| 297 | } | ||
| 298 | |||
| 299 | static int st_nci_hci_dm_vdc_value_comparison(struct nfc_dev *dev, void *data, | ||
| 300 | size_t data_len) | ||
| 301 | { | ||
| 302 | int r; | ||
| 303 | struct sk_buff *msg, *skb; | ||
| 304 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 305 | |||
| 306 | if (data_len != 2) | ||
| 307 | return -EPROTO; | ||
| 308 | |||
| 309 | r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, | ||
| 310 | ST_NCI_HCI_DM_VDC_VALUE_COMPARISON, | ||
| 311 | data, data_len, &skb); | ||
| 312 | if (r) | ||
| 313 | goto exit; | ||
| 314 | |||
| 315 | msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, | ||
| 316 | HCI_DM_VDC_VALUE_COMPARISON, skb->len); | ||
| 317 | if (!msg) { | ||
| 318 | r = -ENOMEM; | ||
| 319 | goto free_skb; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { | ||
| 323 | kfree_skb(msg); | ||
| 324 | r = -ENOBUFS; | ||
| 325 | goto free_skb; | ||
| 326 | } | ||
| 327 | |||
| 328 | r = nfc_vendor_cmd_reply(msg); | ||
| 329 | |||
| 330 | free_skb: | ||
| 331 | kfree_skb(skb); | ||
| 332 | exit: | ||
| 333 | return r; | ||
| 334 | } | ||
| 335 | |||
| 336 | void st_nci_hci_loopback_event_received(struct nci_dev *ndev, u8 event, | ||
| 337 | struct sk_buff *skb) | ||
| 338 | { | ||
| 339 | struct st_nci_info *info = nci_get_drvdata(ndev); | ||
| 340 | |||
| 341 | switch (event) { | ||
| 342 | case ST_NCI_EVT_POST_DATA: | ||
| 343 | info->vendor_info.rx_skb = skb; | ||
| 344 | break; | ||
| 345 | default: | ||
| 346 | nfc_err(&ndev->nfc_dev->dev, "Unexpected event on loopback gate\n"); | ||
| 347 | } | ||
| 348 | complete(&info->vendor_info.req_completion); | ||
| 349 | } | ||
| 350 | EXPORT_SYMBOL(st_nci_hci_loopback_event_received); | ||
| 351 | |||
| 352 | static int st_nci_hci_loopback(struct nfc_dev *dev, void *data, | ||
| 353 | size_t data_len) | ||
| 354 | { | ||
| 355 | int r; | ||
| 356 | struct sk_buff *msg; | ||
| 357 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 358 | struct st_nci_info *info = nci_get_drvdata(ndev); | ||
| 359 | |||
| 360 | if (data_len <= 0) | ||
| 361 | return -EPROTO; | ||
| 362 | |||
| 363 | reinit_completion(&info->vendor_info.req_completion); | ||
| 364 | info->vendor_info.rx_skb = NULL; | ||
| 365 | |||
| 366 | r = nci_hci_send_event(ndev, NCI_HCI_LOOPBACK_GATE, | ||
| 367 | ST_NCI_EVT_POST_DATA, data, data_len); | ||
| 368 | if (r != data_len) { | ||
| 369 | r = -EPROTO; | ||
| 370 | goto exit; | ||
| 371 | } | ||
| 372 | |||
| 373 | wait_for_completion_interruptible(&info->vendor_info.req_completion); | ||
| 374 | |||
| 375 | if (!info->vendor_info.rx_skb || | ||
| 376 | info->vendor_info.rx_skb->len != data_len) { | ||
| 377 | r = -EPROTO; | ||
| 378 | goto exit; | ||
| 379 | } | ||
| 380 | |||
| 381 | msg = nfc_vendor_cmd_alloc_reply_skb(ndev->nfc_dev, | ||
| 382 | ST_NCI_VENDOR_OUI, | ||
| 383 | HCI_LOOPBACK, | ||
| 384 | info->vendor_info.rx_skb->len); | ||
| 385 | if (!msg) { | ||
| 386 | r = -ENOMEM; | ||
| 387 | goto free_skb; | ||
| 388 | } | ||
| 389 | |||
| 390 | if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len, | ||
| 391 | info->vendor_info.rx_skb->data)) { | ||
| 392 | kfree_skb(msg); | ||
| 393 | r = -ENOBUFS; | ||
| 394 | goto free_skb; | ||
| 395 | } | ||
| 396 | |||
| 397 | r = nfc_vendor_cmd_reply(msg); | ||
| 398 | free_skb: | ||
| 399 | kfree_skb(info->vendor_info.rx_skb); | ||
| 400 | exit: | ||
| 401 | return r; | ||
| 402 | } | ||
| 403 | |||
| 404 | static int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data, | ||
| 405 | size_t data_len) | ||
| 406 | { | ||
| 407 | struct sk_buff *msg; | ||
| 408 | struct nci_dev *ndev = nfc_get_drvdata(dev); | ||
| 409 | |||
| 410 | msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, | ||
| 411 | MANUFACTURER_SPECIFIC, | ||
| 412 | sizeof(ndev->manufact_specific_info)); | ||
| 413 | if (!msg) | ||
| 414 | return -ENOMEM; | ||
| 415 | |||
| 416 | if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info), | ||
| 417 | &ndev->manufact_specific_info)) { | ||
| 418 | kfree_skb(msg); | ||
| 419 | return -ENOBUFS; | ||
| 420 | } | ||
| 421 | |||
| 422 | return nfc_vendor_cmd_reply(msg); | ||
| 423 | } | ||
| 424 | |||
| 425 | static struct nfc_vendor_cmd st_nci_vendor_cmds[] = { | ||
| 426 | { | ||
| 427 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 428 | .subcmd = FACTORY_MODE, | ||
| 429 | .doit = st_nci_factory_mode, | ||
| 430 | }, | ||
| 431 | { | ||
| 432 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 433 | .subcmd = HCI_CLEAR_ALL_PIPES, | ||
| 434 | .doit = st_nci_hci_clear_all_pipes, | ||
| 435 | }, | ||
| 436 | { | ||
| 437 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 438 | .subcmd = HCI_DM_PUT_DATA, | ||
| 439 | .doit = st_nci_hci_dm_put_data, | ||
| 440 | }, | ||
| 441 | { | ||
| 442 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 443 | .subcmd = HCI_DM_UPDATE_AID, | ||
| 444 | .doit = st_nci_hci_dm_update_aid, | ||
| 445 | }, | ||
| 446 | { | ||
| 447 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 448 | .subcmd = HCI_DM_GET_INFO, | ||
| 449 | .doit = st_nci_hci_dm_get_info, | ||
| 450 | }, | ||
| 451 | { | ||
| 452 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 453 | .subcmd = HCI_DM_GET_DATA, | ||
| 454 | .doit = st_nci_hci_dm_get_data, | ||
| 455 | }, | ||
| 456 | { | ||
| 457 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 458 | .subcmd = HCI_DM_DIRECT_LOAD, | ||
| 459 | .doit = st_nci_hci_dm_direct_load, | ||
| 460 | }, | ||
| 461 | { | ||
| 462 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 463 | .subcmd = HCI_DM_RESET, | ||
| 464 | .doit = st_nci_hci_dm_reset, | ||
| 465 | }, | ||
| 466 | { | ||
| 467 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 468 | .subcmd = HCI_GET_PARAM, | ||
| 469 | .doit = st_nci_hci_get_param, | ||
| 470 | }, | ||
| 471 | { | ||
| 472 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 473 | .subcmd = HCI_DM_FIELD_GENERATOR, | ||
| 474 | .doit = st_nci_hci_dm_field_generator, | ||
| 475 | }, | ||
| 476 | { | ||
| 477 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 478 | .subcmd = HCI_DM_FWUPD_START, | ||
| 479 | .doit = st_nci_hci_dm_fwupd_start, | ||
| 480 | }, | ||
| 481 | { | ||
| 482 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 483 | .subcmd = HCI_DM_FWUPD_END, | ||
| 484 | .doit = st_nci_hci_dm_fwupd_end, | ||
| 485 | }, | ||
| 486 | { | ||
| 487 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 488 | .subcmd = HCI_LOOPBACK, | ||
| 489 | .doit = st_nci_hci_loopback, | ||
| 490 | }, | ||
| 491 | { | ||
| 492 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 493 | .subcmd = HCI_DM_VDC_MEASUREMENT_VALUE, | ||
| 494 | .doit = st_nci_hci_dm_vdc_measurement_value, | ||
| 495 | }, | ||
| 496 | { | ||
| 497 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 498 | .subcmd = HCI_DM_VDC_VALUE_COMPARISON, | ||
| 499 | .doit = st_nci_hci_dm_vdc_value_comparison, | ||
| 500 | }, | ||
| 501 | { | ||
| 502 | .vendor_id = ST_NCI_VENDOR_OUI, | ||
| 503 | .subcmd = MANUFACTURER_SPECIFIC, | ||
| 504 | .doit = st_nci_manufacturer_specific, | ||
| 505 | }, | ||
| 506 | }; | ||
| 507 | |||
| 508 | int st_nci_vendor_cmds_init(struct nci_dev *ndev) | ||
| 509 | { | ||
| 510 | struct st_nci_info *info = nci_get_drvdata(ndev); | ||
| 511 | |||
| 512 | init_completion(&info->vendor_info.req_completion); | ||
| 513 | return nfc_set_vendor_cmds(ndev->nfc_dev, st_nci_vendor_cmds, | ||
| 514 | sizeof(st_nci_vendor_cmds)); | ||
| 515 | } | ||
| 516 | EXPORT_SYMBOL(st_nci_vendor_cmds_init); | ||
