162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Proprietary commands extension for STMicroelectronics NFC NCI Chip
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014-2015  STMicroelectronics SAS. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <net/genetlink.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/nfc.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <net/nfc/nci_core.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "st-nci.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define ST_NCI_HCI_DM_GETDATA			0x10
1762306a36Sopenharmony_ci#define ST_NCI_HCI_DM_PUTDATA			0x11
1862306a36Sopenharmony_ci#define ST_NCI_HCI_DM_LOAD			0x12
1962306a36Sopenharmony_ci#define ST_NCI_HCI_DM_GETINFO			0x13
2062306a36Sopenharmony_ci#define ST_NCI_HCI_DM_FWUPD_START		0x14
2162306a36Sopenharmony_ci#define ST_NCI_HCI_DM_FWUPD_STOP		0x15
2262306a36Sopenharmony_ci#define ST_NCI_HCI_DM_UPDATE_AID		0x20
2362306a36Sopenharmony_ci#define ST_NCI_HCI_DM_RESET			0x3e
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define ST_NCI_HCI_DM_FIELD_GENERATOR		0x32
2662306a36Sopenharmony_ci#define ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE	0x33
2762306a36Sopenharmony_ci#define ST_NCI_HCI_DM_VDC_VALUE_COMPARISON	0x34
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define ST_NCI_FACTORY_MODE_ON			1
3062306a36Sopenharmony_ci#define ST_NCI_FACTORY_MODE_OFF			0
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define ST_NCI_EVT_POST_DATA			0x02
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct get_param_data {
3562306a36Sopenharmony_ci	u8 gate;
3662306a36Sopenharmony_ci	u8 data;
3762306a36Sopenharmony_ci} __packed;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int st_nci_factory_mode(struct nfc_dev *dev, void *data,
4062306a36Sopenharmony_ci			       size_t data_len)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
4362306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (data_len != 1)
4662306a36Sopenharmony_ci		return -EINVAL;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	switch (((u8 *)data)[0]) {
5162306a36Sopenharmony_ci	case ST_NCI_FACTORY_MODE_ON:
5262306a36Sopenharmony_ci		test_and_set_bit(ST_NCI_FACTORY_MODE, &info->flags);
5362306a36Sopenharmony_ci	break;
5462306a36Sopenharmony_ci	case ST_NCI_FACTORY_MODE_OFF:
5562306a36Sopenharmony_ci		clear_bit(ST_NCI_FACTORY_MODE, &info->flags);
5662306a36Sopenharmony_ci	break;
5762306a36Sopenharmony_ci	default:
5862306a36Sopenharmony_ci		return -EINVAL;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return 0;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int st_nci_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
6562306a36Sopenharmony_ci				      size_t data_len)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return nci_hci_clear_all_pipes(ndev);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int st_nci_hci_dm_put_data(struct nfc_dev *dev, void *data,
7362306a36Sopenharmony_ci				  size_t data_len)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
7862306a36Sopenharmony_ci				ST_NCI_HCI_DM_PUTDATA, data,
7962306a36Sopenharmony_ci				data_len, NULL);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int st_nci_hci_dm_update_aid(struct nfc_dev *dev, void *data,
8362306a36Sopenharmony_ci				    size_t data_len)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
8862306a36Sopenharmony_ci			ST_NCI_HCI_DM_UPDATE_AID, data, data_len, NULL);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int st_nci_hci_dm_get_info(struct nfc_dev *dev, void *data,
9262306a36Sopenharmony_ci				  size_t data_len)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	int r;
9562306a36Sopenharmony_ci	struct sk_buff *msg, *skb;
9662306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETINFO,
9962306a36Sopenharmony_ci			     data, data_len, &skb);
10062306a36Sopenharmony_ci	if (r)
10162306a36Sopenharmony_ci		return r;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
10462306a36Sopenharmony_ci					     HCI_DM_GET_INFO, skb->len);
10562306a36Sopenharmony_ci	if (!msg) {
10662306a36Sopenharmony_ci		r = -ENOMEM;
10762306a36Sopenharmony_ci		goto free_skb;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
11162306a36Sopenharmony_ci		kfree_skb(msg);
11262306a36Sopenharmony_ci		r = -ENOBUFS;
11362306a36Sopenharmony_ci		goto free_skb;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	r = nfc_vendor_cmd_reply(msg);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cifree_skb:
11962306a36Sopenharmony_ci	kfree_skb(skb);
12062306a36Sopenharmony_ci	return r;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int st_nci_hci_dm_get_data(struct nfc_dev *dev, void *data,
12462306a36Sopenharmony_ci				  size_t data_len)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	int r;
12762306a36Sopenharmony_ci	struct sk_buff *msg, *skb;
12862306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETDATA,
13162306a36Sopenharmony_ci			     data, data_len, &skb);
13262306a36Sopenharmony_ci	if (r)
13362306a36Sopenharmony_ci		return r;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
13662306a36Sopenharmony_ci					     HCI_DM_GET_DATA, skb->len);
13762306a36Sopenharmony_ci	if (!msg) {
13862306a36Sopenharmony_ci		r = -ENOMEM;
13962306a36Sopenharmony_ci		goto free_skb;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
14362306a36Sopenharmony_ci		kfree_skb(msg);
14462306a36Sopenharmony_ci		r = -ENOBUFS;
14562306a36Sopenharmony_ci		goto free_skb;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	r = nfc_vendor_cmd_reply(msg);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cifree_skb:
15162306a36Sopenharmony_ci	kfree_skb(skb);
15262306a36Sopenharmony_ci	return r;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data,
15662306a36Sopenharmony_ci				     size_t data_len)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	int r;
15962306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	dev->fw_download_in_progress = true;
16262306a36Sopenharmony_ci	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
16362306a36Sopenharmony_ci			ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL);
16462306a36Sopenharmony_ci	if (r)
16562306a36Sopenharmony_ci		dev->fw_download_in_progress = false;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return r;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int st_nci_hci_dm_fwupd_end(struct nfc_dev *dev, void *data,
17162306a36Sopenharmony_ci				   size_t data_len)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
17662306a36Sopenharmony_ci			ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data,
18062306a36Sopenharmony_ci				     size_t data_len)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (dev->fw_download_in_progress) {
18562306a36Sopenharmony_ci		dev->fw_download_in_progress = false;
18662306a36Sopenharmony_ci		return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
18762306a36Sopenharmony_ci				ST_NCI_HCI_DM_LOAD, data, data_len, NULL);
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci	return -EPROTO;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int st_nci_hci_dm_reset(struct nfc_dev *dev, void *data,
19362306a36Sopenharmony_ci			       size_t data_len)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
19862306a36Sopenharmony_ci			ST_NCI_HCI_DM_RESET, data, data_len, NULL);
19962306a36Sopenharmony_ci	msleep(200);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int st_nci_hci_get_param(struct nfc_dev *dev, void *data,
20562306a36Sopenharmony_ci				size_t data_len)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	int r;
20862306a36Sopenharmony_ci	struct sk_buff *msg, *skb;
20962306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
21062306a36Sopenharmony_ci	struct get_param_data *param = (struct get_param_data *)data;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (data_len < sizeof(struct get_param_data))
21362306a36Sopenharmony_ci		return -EPROTO;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	r = nci_hci_get_param(ndev, param->gate, param->data, &skb);
21662306a36Sopenharmony_ci	if (r)
21762306a36Sopenharmony_ci		return r;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
22062306a36Sopenharmony_ci					     HCI_GET_PARAM, skb->len);
22162306a36Sopenharmony_ci	if (!msg) {
22262306a36Sopenharmony_ci		r = -ENOMEM;
22362306a36Sopenharmony_ci		goto free_skb;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
22762306a36Sopenharmony_ci		kfree_skb(msg);
22862306a36Sopenharmony_ci		r = -ENOBUFS;
22962306a36Sopenharmony_ci		goto free_skb;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	r = nfc_vendor_cmd_reply(msg);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cifree_skb:
23562306a36Sopenharmony_ci	kfree_skb(skb);
23662306a36Sopenharmony_ci	return r;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int st_nci_hci_dm_field_generator(struct nfc_dev *dev, void *data,
24062306a36Sopenharmony_ci					 size_t data_len)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
24562306a36Sopenharmony_ci				ST_NCI_HCI_DM_FIELD_GENERATOR, data, data_len, NULL);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int st_nci_hci_dm_vdc_measurement_value(struct nfc_dev *dev, void *data,
24962306a36Sopenharmony_ci					       size_t data_len)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	int r;
25262306a36Sopenharmony_ci	struct sk_buff *msg, *skb;
25362306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (data_len != 4)
25662306a36Sopenharmony_ci		return -EPROTO;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
25962306a36Sopenharmony_ci			     ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE,
26062306a36Sopenharmony_ci			     data, data_len, &skb);
26162306a36Sopenharmony_ci	if (r)
26262306a36Sopenharmony_ci		return r;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
26562306a36Sopenharmony_ci				HCI_DM_VDC_MEASUREMENT_VALUE, skb->len);
26662306a36Sopenharmony_ci	if (!msg) {
26762306a36Sopenharmony_ci		r = -ENOMEM;
26862306a36Sopenharmony_ci		goto free_skb;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
27262306a36Sopenharmony_ci		kfree_skb(msg);
27362306a36Sopenharmony_ci		r = -ENOBUFS;
27462306a36Sopenharmony_ci		goto free_skb;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	r = nfc_vendor_cmd_reply(msg);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cifree_skb:
28062306a36Sopenharmony_ci	kfree_skb(skb);
28162306a36Sopenharmony_ci	return r;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int st_nci_hci_dm_vdc_value_comparison(struct nfc_dev *dev, void *data,
28562306a36Sopenharmony_ci					      size_t data_len)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	int r;
28862306a36Sopenharmony_ci	struct sk_buff *msg, *skb;
28962306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (data_len != 2)
29262306a36Sopenharmony_ci		return -EPROTO;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
29562306a36Sopenharmony_ci			     ST_NCI_HCI_DM_VDC_VALUE_COMPARISON,
29662306a36Sopenharmony_ci			     data, data_len, &skb);
29762306a36Sopenharmony_ci	if (r)
29862306a36Sopenharmony_ci		return r;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
30162306a36Sopenharmony_ci					HCI_DM_VDC_VALUE_COMPARISON, skb->len);
30262306a36Sopenharmony_ci	if (!msg) {
30362306a36Sopenharmony_ci		r = -ENOMEM;
30462306a36Sopenharmony_ci		goto free_skb;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
30862306a36Sopenharmony_ci		kfree_skb(msg);
30962306a36Sopenharmony_ci		r = -ENOBUFS;
31062306a36Sopenharmony_ci		goto free_skb;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	r = nfc_vendor_cmd_reply(msg);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cifree_skb:
31662306a36Sopenharmony_ci	kfree_skb(skb);
31762306a36Sopenharmony_ci	return r;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int st_nci_loopback(struct nfc_dev *dev, void *data,
32162306a36Sopenharmony_ci			   size_t data_len)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int r;
32462306a36Sopenharmony_ci	struct sk_buff *msg, *skb;
32562306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (data_len <= 0)
32862306a36Sopenharmony_ci		return -EPROTO;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	r = nci_nfcc_loopback(ndev, data, data_len, &skb);
33162306a36Sopenharmony_ci	if (r < 0)
33262306a36Sopenharmony_ci		return r;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
33562306a36Sopenharmony_ci					     LOOPBACK, skb->len);
33662306a36Sopenharmony_ci	if (!msg) {
33762306a36Sopenharmony_ci		r = -ENOMEM;
33862306a36Sopenharmony_ci		goto free_skb;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
34262306a36Sopenharmony_ci		kfree_skb(msg);
34362306a36Sopenharmony_ci		r = -ENOBUFS;
34462306a36Sopenharmony_ci		goto free_skb;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	r = nfc_vendor_cmd_reply(msg);
34862306a36Sopenharmony_cifree_skb:
34962306a36Sopenharmony_ci	kfree_skb(skb);
35062306a36Sopenharmony_ci	return r;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data,
35462306a36Sopenharmony_ci					size_t data_len)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct sk_buff *msg;
35762306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(dev);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
36062306a36Sopenharmony_ci					MANUFACTURER_SPECIFIC,
36162306a36Sopenharmony_ci					sizeof(ndev->manufact_specific_info));
36262306a36Sopenharmony_ci	if (!msg)
36362306a36Sopenharmony_ci		return -ENOMEM;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info),
36662306a36Sopenharmony_ci		    &ndev->manufact_specific_info)) {
36762306a36Sopenharmony_ci		kfree_skb(msg);
36862306a36Sopenharmony_ci		return -ENOBUFS;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return nfc_vendor_cmd_reply(msg);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic const struct nfc_vendor_cmd st_nci_vendor_cmds[] = {
37562306a36Sopenharmony_ci	{
37662306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
37762306a36Sopenharmony_ci		.subcmd = FACTORY_MODE,
37862306a36Sopenharmony_ci		.doit = st_nci_factory_mode,
37962306a36Sopenharmony_ci	},
38062306a36Sopenharmony_ci	{
38162306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
38262306a36Sopenharmony_ci		.subcmd = HCI_CLEAR_ALL_PIPES,
38362306a36Sopenharmony_ci		.doit = st_nci_hci_clear_all_pipes,
38462306a36Sopenharmony_ci	},
38562306a36Sopenharmony_ci	{
38662306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
38762306a36Sopenharmony_ci		.subcmd = HCI_DM_PUT_DATA,
38862306a36Sopenharmony_ci		.doit = st_nci_hci_dm_put_data,
38962306a36Sopenharmony_ci	},
39062306a36Sopenharmony_ci	{
39162306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
39262306a36Sopenharmony_ci		.subcmd = HCI_DM_UPDATE_AID,
39362306a36Sopenharmony_ci		.doit = st_nci_hci_dm_update_aid,
39462306a36Sopenharmony_ci	},
39562306a36Sopenharmony_ci	{
39662306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
39762306a36Sopenharmony_ci		.subcmd = HCI_DM_GET_INFO,
39862306a36Sopenharmony_ci		.doit = st_nci_hci_dm_get_info,
39962306a36Sopenharmony_ci	},
40062306a36Sopenharmony_ci	{
40162306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
40262306a36Sopenharmony_ci		.subcmd = HCI_DM_GET_DATA,
40362306a36Sopenharmony_ci		.doit = st_nci_hci_dm_get_data,
40462306a36Sopenharmony_ci	},
40562306a36Sopenharmony_ci	{
40662306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
40762306a36Sopenharmony_ci		.subcmd = HCI_DM_DIRECT_LOAD,
40862306a36Sopenharmony_ci		.doit = st_nci_hci_dm_direct_load,
40962306a36Sopenharmony_ci	},
41062306a36Sopenharmony_ci	{
41162306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
41262306a36Sopenharmony_ci		.subcmd = HCI_DM_RESET,
41362306a36Sopenharmony_ci		.doit = st_nci_hci_dm_reset,
41462306a36Sopenharmony_ci	},
41562306a36Sopenharmony_ci	{
41662306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
41762306a36Sopenharmony_ci		.subcmd = HCI_GET_PARAM,
41862306a36Sopenharmony_ci		.doit = st_nci_hci_get_param,
41962306a36Sopenharmony_ci	},
42062306a36Sopenharmony_ci	{
42162306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
42262306a36Sopenharmony_ci		.subcmd = HCI_DM_FIELD_GENERATOR,
42362306a36Sopenharmony_ci		.doit = st_nci_hci_dm_field_generator,
42462306a36Sopenharmony_ci	},
42562306a36Sopenharmony_ci	{
42662306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
42762306a36Sopenharmony_ci		.subcmd = HCI_DM_FWUPD_START,
42862306a36Sopenharmony_ci		.doit = st_nci_hci_dm_fwupd_start,
42962306a36Sopenharmony_ci	},
43062306a36Sopenharmony_ci	{
43162306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
43262306a36Sopenharmony_ci		.subcmd = HCI_DM_FWUPD_END,
43362306a36Sopenharmony_ci		.doit = st_nci_hci_dm_fwupd_end,
43462306a36Sopenharmony_ci	},
43562306a36Sopenharmony_ci	{
43662306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
43762306a36Sopenharmony_ci		.subcmd = LOOPBACK,
43862306a36Sopenharmony_ci		.doit = st_nci_loopback,
43962306a36Sopenharmony_ci	},
44062306a36Sopenharmony_ci	{
44162306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
44262306a36Sopenharmony_ci		.subcmd = HCI_DM_VDC_MEASUREMENT_VALUE,
44362306a36Sopenharmony_ci		.doit = st_nci_hci_dm_vdc_measurement_value,
44462306a36Sopenharmony_ci	},
44562306a36Sopenharmony_ci	{
44662306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
44762306a36Sopenharmony_ci		.subcmd = HCI_DM_VDC_VALUE_COMPARISON,
44862306a36Sopenharmony_ci		.doit = st_nci_hci_dm_vdc_value_comparison,
44962306a36Sopenharmony_ci	},
45062306a36Sopenharmony_ci	{
45162306a36Sopenharmony_ci		.vendor_id = ST_NCI_VENDOR_OUI,
45262306a36Sopenharmony_ci		.subcmd = MANUFACTURER_SPECIFIC,
45362306a36Sopenharmony_ci		.doit = st_nci_manufacturer_specific,
45462306a36Sopenharmony_ci	},
45562306a36Sopenharmony_ci};
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ciint st_nci_vendor_cmds_init(struct nci_dev *ndev)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	return nci_set_vendor_cmds(ndev, st_nci_vendor_cmds,
46062306a36Sopenharmony_ci				   sizeof(st_nci_vendor_cmds));
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ciEXPORT_SYMBOL(st_nci_vendor_cmds_init);
463