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 (8210B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Proprietary commands extension for STMicroelectronics NFC 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 <net/nfc/hci.h>
     12#include <net/nfc/llc.h>
     13
     14#include "st21nfca.h"
     15
     16#define ST21NFCA_HCI_DM_GETDATA			0x10
     17#define ST21NFCA_HCI_DM_PUTDATA			0x11
     18#define ST21NFCA_HCI_DM_LOAD			0x12
     19#define ST21NFCA_HCI_DM_GETINFO			0x13
     20#define ST21NFCA_HCI_DM_UPDATE_AID		0x20
     21#define ST21NFCA_HCI_DM_RESET			0x3e
     22
     23#define ST21NFCA_HCI_DM_FIELD_GENERATOR		0x32
     24
     25#define ST21NFCA_FACTORY_MODE_ON		1
     26#define ST21NFCA_FACTORY_MODE_OFF		0
     27
     28#define ST21NFCA_EVT_POST_DATA			0x02
     29
     30struct get_param_data {
     31	u8 gate;
     32	u8 data;
     33} __packed;
     34
     35static int st21nfca_factory_mode(struct nfc_dev *dev, void *data,
     36			       size_t data_len)
     37{
     38	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
     39
     40	if (data_len != 1)
     41		return -EINVAL;
     42
     43	pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
     44
     45	switch (((u8 *)data)[0]) {
     46	case ST21NFCA_FACTORY_MODE_ON:
     47		test_and_set_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
     48	break;
     49	case ST21NFCA_FACTORY_MODE_OFF:
     50		clear_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
     51	break;
     52	default:
     53		return -EINVAL;
     54	}
     55
     56	return 0;
     57}
     58
     59static int st21nfca_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
     60				      size_t data_len)
     61{
     62	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
     63
     64	return nfc_hci_disconnect_all_gates(hdev);
     65}
     66
     67static int st21nfca_hci_dm_put_data(struct nfc_dev *dev, void *data,
     68				  size_t data_len)
     69{
     70	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
     71
     72	return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
     73				ST21NFCA_HCI_DM_PUTDATA, data,
     74				data_len, NULL);
     75}
     76
     77static int st21nfca_hci_dm_update_aid(struct nfc_dev *dev, void *data,
     78				    size_t data_len)
     79{
     80	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
     81
     82	return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
     83			ST21NFCA_HCI_DM_UPDATE_AID, data, data_len, NULL);
     84}
     85
     86static int st21nfca_hci_dm_get_info(struct nfc_dev *dev, void *data,
     87				    size_t data_len)
     88{
     89	int r;
     90	struct sk_buff *msg, *skb;
     91	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
     92
     93	r = nfc_hci_send_cmd(hdev,
     94			     ST21NFCA_DEVICE_MGNT_GATE,
     95			     ST21NFCA_HCI_DM_GETINFO,
     96			     data, data_len, &skb);
     97	if (r)
     98		goto exit;
     99
    100	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
    101					     HCI_DM_GET_INFO, skb->len);
    102	if (!msg) {
    103		r = -ENOMEM;
    104		goto free_skb;
    105	}
    106
    107	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
    108		kfree_skb(msg);
    109		r = -ENOBUFS;
    110		goto free_skb;
    111	}
    112
    113	r = nfc_vendor_cmd_reply(msg);
    114
    115free_skb:
    116	kfree_skb(skb);
    117exit:
    118	return r;
    119}
    120
    121static int st21nfca_hci_dm_get_data(struct nfc_dev *dev, void *data,
    122				    size_t data_len)
    123{
    124	int r;
    125	struct sk_buff *msg, *skb;
    126	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
    127
    128	r = nfc_hci_send_cmd(hdev,
    129			     ST21NFCA_DEVICE_MGNT_GATE,
    130			     ST21NFCA_HCI_DM_GETDATA,
    131			     data, data_len, &skb);
    132	if (r)
    133		goto exit;
    134
    135	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_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);
    152exit:
    153	return r;
    154}
    155
    156static int st21nfca_hci_dm_load(struct nfc_dev *dev, void *data,
    157				size_t data_len)
    158{
    159	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
    160
    161	return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
    162				ST21NFCA_HCI_DM_LOAD, data, data_len, NULL);
    163}
    164
    165static int st21nfca_hci_dm_reset(struct nfc_dev *dev, void *data,
    166				 size_t data_len)
    167{
    168	int r;
    169	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
    170
    171	r = nfc_hci_send_cmd_async(hdev, ST21NFCA_DEVICE_MGNT_GATE,
    172			ST21NFCA_HCI_DM_RESET, data, data_len, NULL, NULL);
    173	if (r < 0)
    174		return r;
    175
    176	r = nfc_llc_stop(hdev->llc);
    177	if (r < 0)
    178		return r;
    179
    180	return nfc_llc_start(hdev->llc);
    181}
    182
    183static int st21nfca_hci_get_param(struct nfc_dev *dev, void *data,
    184				  size_t data_len)
    185{
    186	int r;
    187	struct sk_buff *msg, *skb;
    188	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
    189	struct get_param_data *param = (struct get_param_data *)data;
    190
    191	if (data_len < sizeof(struct get_param_data))
    192		return -EPROTO;
    193
    194	r = nfc_hci_get_param(hdev, param->gate, param->data, &skb);
    195	if (r)
    196		goto exit;
    197
    198	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
    199					     HCI_GET_PARAM, skb->len);
    200	if (!msg) {
    201		r = -ENOMEM;
    202		goto free_skb;
    203	}
    204
    205	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
    206		kfree_skb(msg);
    207		r = -ENOBUFS;
    208		goto free_skb;
    209	}
    210
    211	r = nfc_vendor_cmd_reply(msg);
    212
    213free_skb:
    214	kfree_skb(skb);
    215exit:
    216	return r;
    217}
    218
    219static int st21nfca_hci_dm_field_generator(struct nfc_dev *dev, void *data,
    220					   size_t data_len)
    221{
    222	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
    223
    224	return nfc_hci_send_cmd(hdev,
    225				ST21NFCA_DEVICE_MGNT_GATE,
    226				ST21NFCA_HCI_DM_FIELD_GENERATOR,
    227				data, data_len, NULL);
    228}
    229
    230int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *hdev, u8 event,
    231					 struct sk_buff *skb)
    232{
    233	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
    234
    235	switch (event) {
    236	case ST21NFCA_EVT_POST_DATA:
    237		info->vendor_info.rx_skb = skb;
    238	break;
    239	default:
    240		nfc_err(&hdev->ndev->dev, "Unexpected event on loopback gate\n");
    241	}
    242	complete(&info->vendor_info.req_completion);
    243	return 0;
    244}
    245EXPORT_SYMBOL(st21nfca_hci_loopback_event_received);
    246
    247static int st21nfca_hci_loopback(struct nfc_dev *dev, void *data,
    248				 size_t data_len)
    249{
    250	int r;
    251	struct sk_buff *msg;
    252	struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
    253	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
    254
    255	if (data_len <= 0)
    256		return -EPROTO;
    257
    258	reinit_completion(&info->vendor_info.req_completion);
    259	info->vendor_info.rx_skb = NULL;
    260
    261	r = nfc_hci_send_event(hdev, NFC_HCI_LOOPBACK_GATE,
    262			       ST21NFCA_EVT_POST_DATA, data, data_len);
    263	if (r < 0) {
    264		r = -EPROTO;
    265		goto exit;
    266	}
    267
    268	wait_for_completion_interruptible(&info->vendor_info.req_completion);
    269	if (!info->vendor_info.rx_skb ||
    270	    info->vendor_info.rx_skb->len != data_len) {
    271		r = -EPROTO;
    272		goto exit;
    273	}
    274
    275	msg = nfc_vendor_cmd_alloc_reply_skb(hdev->ndev,
    276					ST21NFCA_VENDOR_OUI,
    277					HCI_LOOPBACK,
    278					info->vendor_info.rx_skb->len);
    279	if (!msg) {
    280		r = -ENOMEM;
    281		goto free_skb;
    282	}
    283
    284	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len,
    285		    info->vendor_info.rx_skb->data)) {
    286		kfree_skb(msg);
    287		r = -ENOBUFS;
    288		goto free_skb;
    289	}
    290
    291	r = nfc_vendor_cmd_reply(msg);
    292free_skb:
    293	kfree_skb(info->vendor_info.rx_skb);
    294exit:
    295	return r;
    296}
    297
    298static const struct nfc_vendor_cmd st21nfca_vendor_cmds[] = {
    299	{
    300		.vendor_id = ST21NFCA_VENDOR_OUI,
    301		.subcmd = FACTORY_MODE,
    302		.doit = st21nfca_factory_mode,
    303	},
    304	{
    305		.vendor_id = ST21NFCA_VENDOR_OUI,
    306		.subcmd = HCI_CLEAR_ALL_PIPES,
    307		.doit = st21nfca_hci_clear_all_pipes,
    308	},
    309	{
    310		.vendor_id = ST21NFCA_VENDOR_OUI,
    311		.subcmd = HCI_DM_PUT_DATA,
    312		.doit = st21nfca_hci_dm_put_data,
    313	},
    314	{
    315		.vendor_id = ST21NFCA_VENDOR_OUI,
    316		.subcmd = HCI_DM_UPDATE_AID,
    317		.doit = st21nfca_hci_dm_update_aid,
    318	},
    319	{
    320		.vendor_id = ST21NFCA_VENDOR_OUI,
    321		.subcmd = HCI_DM_GET_INFO,
    322		.doit = st21nfca_hci_dm_get_info,
    323	},
    324	{
    325		.vendor_id = ST21NFCA_VENDOR_OUI,
    326		.subcmd = HCI_DM_GET_DATA,
    327		.doit = st21nfca_hci_dm_get_data,
    328	},
    329	{
    330		.vendor_id = ST21NFCA_VENDOR_OUI,
    331		.subcmd = HCI_DM_LOAD,
    332		.doit = st21nfca_hci_dm_load,
    333	},
    334	{
    335		.vendor_id = ST21NFCA_VENDOR_OUI,
    336		.subcmd = HCI_DM_RESET,
    337		.doit = st21nfca_hci_dm_reset,
    338	},
    339	{
    340		.vendor_id = ST21NFCA_VENDOR_OUI,
    341		.subcmd = HCI_GET_PARAM,
    342		.doit = st21nfca_hci_get_param,
    343	},
    344	{
    345		.vendor_id = ST21NFCA_VENDOR_OUI,
    346		.subcmd = HCI_DM_FIELD_GENERATOR,
    347		.doit = st21nfca_hci_dm_field_generator,
    348	},
    349	{
    350		.vendor_id = ST21NFCA_VENDOR_OUI,
    351		.subcmd = HCI_LOOPBACK,
    352		.doit = st21nfca_hci_loopback,
    353	},
    354};
    355
    356int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev)
    357{
    358	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
    359
    360	init_completion(&info->vendor_info.req_completion);
    361	return nfc_hci_set_vendor_cmds(hdev, st21nfca_vendor_cmds,
    362				       sizeof(st21nfca_vendor_cmds));
    363}
    364EXPORT_SYMBOL(st21nfca_vendor_cmds_init);