cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

vendor_cmds.c (10277B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Proprietary commands extension for STMicroelectronics NFC NCI Chip
      4 *
      5 * Copyright (C) 2014-2015  STMicroelectronics SAS. All rights reserved.
      6 */
      7
      8#include <net/genetlink.h>
      9#include <linux/module.h>
     10#include <linux/nfc.h>
     11#include <linux/delay.h>
     12#include <net/nfc/nci_core.h>
     13
     14#include "st-nci.h"
     15
     16#define ST_NCI_HCI_DM_GETDATA			0x10
     17#define ST_NCI_HCI_DM_PUTDATA			0x11
     18#define ST_NCI_HCI_DM_LOAD			0x12
     19#define ST_NCI_HCI_DM_GETINFO			0x13
     20#define ST_NCI_HCI_DM_FWUPD_START		0x14
     21#define ST_NCI_HCI_DM_FWUPD_STOP		0x15
     22#define ST_NCI_HCI_DM_UPDATE_AID		0x20
     23#define ST_NCI_HCI_DM_RESET			0x3e
     24
     25#define ST_NCI_HCI_DM_FIELD_GENERATOR		0x32
     26#define ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE	0x33
     27#define ST_NCI_HCI_DM_VDC_VALUE_COMPARISON	0x34
     28
     29#define ST_NCI_FACTORY_MODE_ON			1
     30#define ST_NCI_FACTORY_MODE_OFF			0
     31
     32#define ST_NCI_EVT_POST_DATA			0x02
     33
     34struct get_param_data {
     35	u8 gate;
     36	u8 data;
     37} __packed;
     38
     39static int st_nci_factory_mode(struct nfc_dev *dev, void *data,
     40			       size_t data_len)
     41{
     42	struct nci_dev *ndev = nfc_get_drvdata(dev);
     43	struct st_nci_info *info = nci_get_drvdata(ndev);
     44
     45	if (data_len != 1)
     46		return -EINVAL;
     47
     48	pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
     49
     50	switch (((u8 *)data)[0]) {
     51	case ST_NCI_FACTORY_MODE_ON:
     52		test_and_set_bit(ST_NCI_FACTORY_MODE, &info->flags);
     53	break;
     54	case ST_NCI_FACTORY_MODE_OFF:
     55		clear_bit(ST_NCI_FACTORY_MODE, &info->flags);
     56	break;
     57	default:
     58		return -EINVAL;
     59	}
     60
     61	return 0;
     62}
     63
     64static int st_nci_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
     65				      size_t data_len)
     66{
     67	struct nci_dev *ndev = nfc_get_drvdata(dev);
     68
     69	return nci_hci_clear_all_pipes(ndev);
     70}
     71
     72static int st_nci_hci_dm_put_data(struct nfc_dev *dev, void *data,
     73				  size_t data_len)
     74{
     75	struct nci_dev *ndev = nfc_get_drvdata(dev);
     76
     77	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
     78				ST_NCI_HCI_DM_PUTDATA, data,
     79				data_len, NULL);
     80}
     81
     82static int st_nci_hci_dm_update_aid(struct nfc_dev *dev, void *data,
     83				    size_t data_len)
     84{
     85	struct nci_dev *ndev = nfc_get_drvdata(dev);
     86
     87	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
     88			ST_NCI_HCI_DM_UPDATE_AID, data, data_len, NULL);
     89}
     90
     91static int st_nci_hci_dm_get_info(struct nfc_dev *dev, void *data,
     92				  size_t data_len)
     93{
     94	int r;
     95	struct sk_buff *msg, *skb;
     96	struct nci_dev *ndev = nfc_get_drvdata(dev);
     97
     98	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETINFO,
     99			     data, data_len, &skb);
    100	if (r)
    101		return r;
    102
    103	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
    104					     HCI_DM_GET_INFO, skb->len);
    105	if (!msg) {
    106		r = -ENOMEM;
    107		goto free_skb;
    108	}
    109
    110	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
    111		kfree_skb(msg);
    112		r = -ENOBUFS;
    113		goto free_skb;
    114	}
    115
    116	r = nfc_vendor_cmd_reply(msg);
    117
    118free_skb:
    119	kfree_skb(skb);
    120	return r;
    121}
    122
    123static int st_nci_hci_dm_get_data(struct nfc_dev *dev, void *data,
    124				  size_t data_len)
    125{
    126	int r;
    127	struct sk_buff *msg, *skb;
    128	struct nci_dev *ndev = nfc_get_drvdata(dev);
    129
    130	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETDATA,
    131			     data, data_len, &skb);
    132	if (r)
    133		return r;
    134
    135	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
    136					     HCI_DM_GET_DATA, skb->len);
    137	if (!msg) {
    138		r = -ENOMEM;
    139		goto free_skb;
    140	}
    141
    142	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
    143		kfree_skb(msg);
    144		r = -ENOBUFS;
    145		goto free_skb;
    146	}
    147
    148	r = nfc_vendor_cmd_reply(msg);
    149
    150free_skb:
    151	kfree_skb(skb);
    152	return r;
    153}
    154
    155static int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data,
    156				     size_t data_len)
    157{
    158	int r;
    159	struct nci_dev *ndev = nfc_get_drvdata(dev);
    160
    161	dev->fw_download_in_progress = true;
    162	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
    163			ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL);
    164	if (r)
    165		dev->fw_download_in_progress = false;
    166
    167	return r;
    168}
    169
    170static int st_nci_hci_dm_fwupd_end(struct nfc_dev *dev, void *data,
    171				   size_t data_len)
    172{
    173	struct nci_dev *ndev = nfc_get_drvdata(dev);
    174
    175	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
    176			ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL);
    177}
    178
    179static int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data,
    180				     size_t data_len)
    181{
    182	struct nci_dev *ndev = nfc_get_drvdata(dev);
    183
    184	if (dev->fw_download_in_progress) {
    185		dev->fw_download_in_progress = false;
    186		return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
    187				ST_NCI_HCI_DM_LOAD, data, data_len, NULL);
    188	}
    189	return -EPROTO;
    190}
    191
    192static int st_nci_hci_dm_reset(struct nfc_dev *dev, void *data,
    193			       size_t data_len)
    194{
    195	struct nci_dev *ndev = nfc_get_drvdata(dev);
    196
    197	nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
    198			ST_NCI_HCI_DM_RESET, data, data_len, NULL);
    199	msleep(200);
    200
    201	return 0;
    202}
    203
    204static int st_nci_hci_get_param(struct nfc_dev *dev, void *data,
    205				size_t data_len)
    206{
    207	int r;
    208	struct sk_buff *msg, *skb;
    209	struct nci_dev *ndev = nfc_get_drvdata(dev);
    210	struct get_param_data *param = (struct get_param_data *)data;
    211
    212	if (data_len < sizeof(struct get_param_data))
    213		return -EPROTO;
    214
    215	r = nci_hci_get_param(ndev, param->gate, param->data, &skb);
    216	if (r)
    217		return r;
    218
    219	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
    220					     HCI_GET_PARAM, skb->len);
    221	if (!msg) {
    222		r = -ENOMEM;
    223		goto free_skb;
    224	}
    225
    226	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
    227		kfree_skb(msg);
    228		r = -ENOBUFS;
    229		goto free_skb;
    230	}
    231
    232	r = nfc_vendor_cmd_reply(msg);
    233
    234free_skb:
    235	kfree_skb(skb);
    236	return r;
    237}
    238
    239static int st_nci_hci_dm_field_generator(struct nfc_dev *dev, void *data,
    240					 size_t data_len)
    241{
    242	struct nci_dev *ndev = nfc_get_drvdata(dev);
    243
    244	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
    245				ST_NCI_HCI_DM_FIELD_GENERATOR, data, data_len, NULL);
    246}
    247
    248static int st_nci_hci_dm_vdc_measurement_value(struct nfc_dev *dev, void *data,
    249					       size_t data_len)
    250{
    251	int r;
    252	struct sk_buff *msg, *skb;
    253	struct nci_dev *ndev = nfc_get_drvdata(dev);
    254
    255	if (data_len != 4)
    256		return -EPROTO;
    257
    258	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
    259			     ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE,
    260			     data, data_len, &skb);
    261	if (r)
    262		return r;
    263
    264	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
    265				HCI_DM_VDC_MEASUREMENT_VALUE, skb->len);
    266	if (!msg) {
    267		r = -ENOMEM;
    268		goto free_skb;
    269	}
    270
    271	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
    272		kfree_skb(msg);
    273		r = -ENOBUFS;
    274		goto free_skb;
    275	}
    276
    277	r = nfc_vendor_cmd_reply(msg);
    278
    279free_skb:
    280	kfree_skb(skb);
    281	return r;
    282}
    283
    284static int st_nci_hci_dm_vdc_value_comparison(struct nfc_dev *dev, void *data,
    285					      size_t data_len)
    286{
    287	int r;
    288	struct sk_buff *msg, *skb;
    289	struct nci_dev *ndev = nfc_get_drvdata(dev);
    290
    291	if (data_len != 2)
    292		return -EPROTO;
    293
    294	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
    295			     ST_NCI_HCI_DM_VDC_VALUE_COMPARISON,
    296			     data, data_len, &skb);
    297	if (r)
    298		return r;
    299
    300	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
    301					HCI_DM_VDC_VALUE_COMPARISON, skb->len);
    302	if (!msg) {
    303		r = -ENOMEM;
    304		goto free_skb;
    305	}
    306
    307	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
    308		kfree_skb(msg);
    309		r = -ENOBUFS;
    310		goto free_skb;
    311	}
    312
    313	r = nfc_vendor_cmd_reply(msg);
    314
    315free_skb:
    316	kfree_skb(skb);
    317	return r;
    318}
    319
    320static int st_nci_loopback(struct nfc_dev *dev, void *data,
    321			   size_t data_len)
    322{
    323	int r;
    324	struct sk_buff *msg, *skb;
    325	struct nci_dev *ndev = nfc_get_drvdata(dev);
    326
    327	if (data_len <= 0)
    328		return -EPROTO;
    329
    330	r = nci_nfcc_loopback(ndev, data, data_len, &skb);
    331	if (r < 0)
    332		return r;
    333
    334	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
    335					     LOOPBACK, skb->len);
    336	if (!msg) {
    337		r = -ENOMEM;
    338		goto free_skb;
    339	}
    340
    341	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
    342		kfree_skb(msg);
    343		r = -ENOBUFS;
    344		goto free_skb;
    345	}
    346
    347	r = nfc_vendor_cmd_reply(msg);
    348free_skb:
    349	kfree_skb(skb);
    350	return r;
    351}
    352
    353static int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data,
    354					size_t data_len)
    355{
    356	struct sk_buff *msg;
    357	struct nci_dev *ndev = nfc_get_drvdata(dev);
    358
    359	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
    360					MANUFACTURER_SPECIFIC,
    361					sizeof(ndev->manufact_specific_info));
    362	if (!msg)
    363		return -ENOMEM;
    364
    365	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info),
    366		    &ndev->manufact_specific_info)) {
    367		kfree_skb(msg);
    368		return -ENOBUFS;
    369	}
    370
    371	return nfc_vendor_cmd_reply(msg);
    372}
    373
    374static const struct nfc_vendor_cmd st_nci_vendor_cmds[] = {
    375	{
    376		.vendor_id = ST_NCI_VENDOR_OUI,
    377		.subcmd = FACTORY_MODE,
    378		.doit = st_nci_factory_mode,
    379	},
    380	{
    381		.vendor_id = ST_NCI_VENDOR_OUI,
    382		.subcmd = HCI_CLEAR_ALL_PIPES,
    383		.doit = st_nci_hci_clear_all_pipes,
    384	},
    385	{
    386		.vendor_id = ST_NCI_VENDOR_OUI,
    387		.subcmd = HCI_DM_PUT_DATA,
    388		.doit = st_nci_hci_dm_put_data,
    389	},
    390	{
    391		.vendor_id = ST_NCI_VENDOR_OUI,
    392		.subcmd = HCI_DM_UPDATE_AID,
    393		.doit = st_nci_hci_dm_update_aid,
    394	},
    395	{
    396		.vendor_id = ST_NCI_VENDOR_OUI,
    397		.subcmd = HCI_DM_GET_INFO,
    398		.doit = st_nci_hci_dm_get_info,
    399	},
    400	{
    401		.vendor_id = ST_NCI_VENDOR_OUI,
    402		.subcmd = HCI_DM_GET_DATA,
    403		.doit = st_nci_hci_dm_get_data,
    404	},
    405	{
    406		.vendor_id = ST_NCI_VENDOR_OUI,
    407		.subcmd = HCI_DM_DIRECT_LOAD,
    408		.doit = st_nci_hci_dm_direct_load,
    409	},
    410	{
    411		.vendor_id = ST_NCI_VENDOR_OUI,
    412		.subcmd = HCI_DM_RESET,
    413		.doit = st_nci_hci_dm_reset,
    414	},
    415	{
    416		.vendor_id = ST_NCI_VENDOR_OUI,
    417		.subcmd = HCI_GET_PARAM,
    418		.doit = st_nci_hci_get_param,
    419	},
    420	{
    421		.vendor_id = ST_NCI_VENDOR_OUI,
    422		.subcmd = HCI_DM_FIELD_GENERATOR,
    423		.doit = st_nci_hci_dm_field_generator,
    424	},
    425	{
    426		.vendor_id = ST_NCI_VENDOR_OUI,
    427		.subcmd = HCI_DM_FWUPD_START,
    428		.doit = st_nci_hci_dm_fwupd_start,
    429	},
    430	{
    431		.vendor_id = ST_NCI_VENDOR_OUI,
    432		.subcmd = HCI_DM_FWUPD_END,
    433		.doit = st_nci_hci_dm_fwupd_end,
    434	},
    435	{
    436		.vendor_id = ST_NCI_VENDOR_OUI,
    437		.subcmd = LOOPBACK,
    438		.doit = st_nci_loopback,
    439	},
    440	{
    441		.vendor_id = ST_NCI_VENDOR_OUI,
    442		.subcmd = HCI_DM_VDC_MEASUREMENT_VALUE,
    443		.doit = st_nci_hci_dm_vdc_measurement_value,
    444	},
    445	{
    446		.vendor_id = ST_NCI_VENDOR_OUI,
    447		.subcmd = HCI_DM_VDC_VALUE_COMPARISON,
    448		.doit = st_nci_hci_dm_vdc_value_comparison,
    449	},
    450	{
    451		.vendor_id = ST_NCI_VENDOR_OUI,
    452		.subcmd = MANUFACTURER_SPECIFIC,
    453		.doit = st_nci_manufacturer_specific,
    454	},
    455};
    456
    457int st_nci_vendor_cmds_init(struct nci_dev *ndev)
    458{
    459	return nci_set_vendor_cmds(ndev, st_nci_vendor_cmds,
    460				   sizeof(st_nci_vendor_cmds));
    461}
    462EXPORT_SYMBOL(st_nci_vendor_cmds_init);