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);
463