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		goto exit;
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);
120exit:
121	return r;
122}
123
124static int st_nci_hci_dm_get_data(struct nfc_dev *dev, void *data,
125				  size_t data_len)
126{
127	int r;
128	struct sk_buff *msg, *skb;
129	struct nci_dev *ndev = nfc_get_drvdata(dev);
130
131	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETDATA,
132			     data, data_len, &skb);
133	if (r)
134		goto exit;
135
136	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
137					     HCI_DM_GET_DATA, skb->len);
138	if (!msg) {
139		r = -ENOMEM;
140		goto free_skb;
141	}
142
143	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
144		kfree_skb(msg);
145		r = -ENOBUFS;
146		goto free_skb;
147	}
148
149	r = nfc_vendor_cmd_reply(msg);
150
151free_skb:
152	kfree_skb(skb);
153exit:
154	return r;
155}
156
157static int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data,
158				     size_t data_len)
159{
160	int r;
161	struct nci_dev *ndev = nfc_get_drvdata(dev);
162
163	dev->fw_download_in_progress = true;
164	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
165			ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL);
166	if (r)
167		dev->fw_download_in_progress = false;
168
169	return r;
170}
171
172static int st_nci_hci_dm_fwupd_end(struct nfc_dev *dev, void *data,
173				   size_t data_len)
174{
175	struct nci_dev *ndev = nfc_get_drvdata(dev);
176
177	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
178			ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL);
179}
180
181static int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data,
182				     size_t data_len)
183{
184	struct nci_dev *ndev = nfc_get_drvdata(dev);
185
186	if (dev->fw_download_in_progress) {
187		dev->fw_download_in_progress = false;
188		return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
189				ST_NCI_HCI_DM_LOAD, data, data_len, NULL);
190	}
191	return -EPROTO;
192}
193
194static int st_nci_hci_dm_reset(struct nfc_dev *dev, void *data,
195			       size_t data_len)
196{
197	struct nci_dev *ndev = nfc_get_drvdata(dev);
198
199	nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
200			ST_NCI_HCI_DM_RESET, data, data_len, NULL);
201	msleep(200);
202
203	return 0;
204}
205
206static int st_nci_hci_get_param(struct nfc_dev *dev, void *data,
207				size_t data_len)
208{
209	int r;
210	struct sk_buff *msg, *skb;
211	struct nci_dev *ndev = nfc_get_drvdata(dev);
212	struct get_param_data *param = (struct get_param_data *)data;
213
214	if (data_len < sizeof(struct get_param_data))
215		return -EPROTO;
216
217	r = nci_hci_get_param(ndev, param->gate, param->data, &skb);
218	if (r)
219		goto exit;
220
221	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
222					     HCI_GET_PARAM, skb->len);
223	if (!msg) {
224		r = -ENOMEM;
225		goto free_skb;
226	}
227
228	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
229		kfree_skb(msg);
230		r = -ENOBUFS;
231		goto free_skb;
232	}
233
234	r = nfc_vendor_cmd_reply(msg);
235
236free_skb:
237	kfree_skb(skb);
238exit:
239	return r;
240}
241
242static int st_nci_hci_dm_field_generator(struct nfc_dev *dev, void *data,
243					 size_t data_len)
244{
245	struct nci_dev *ndev = nfc_get_drvdata(dev);
246
247	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
248				ST_NCI_HCI_DM_FIELD_GENERATOR, data, data_len, NULL);
249}
250
251static int st_nci_hci_dm_vdc_measurement_value(struct nfc_dev *dev, void *data,
252					       size_t data_len)
253{
254	int r;
255	struct sk_buff *msg, *skb;
256	struct nci_dev *ndev = nfc_get_drvdata(dev);
257
258	if (data_len != 4)
259		return -EPROTO;
260
261	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
262			     ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE,
263			     data, data_len, &skb);
264	if (r)
265		goto exit;
266
267	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
268				HCI_DM_VDC_MEASUREMENT_VALUE, skb->len);
269	if (!msg) {
270		r = -ENOMEM;
271		goto free_skb;
272	}
273
274	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
275		kfree_skb(msg);
276		r = -ENOBUFS;
277		goto free_skb;
278	}
279
280	r = nfc_vendor_cmd_reply(msg);
281
282free_skb:
283	kfree_skb(skb);
284exit:
285	return r;
286}
287
288static int st_nci_hci_dm_vdc_value_comparison(struct nfc_dev *dev, void *data,
289					      size_t data_len)
290{
291	int r;
292	struct sk_buff *msg, *skb;
293	struct nci_dev *ndev = nfc_get_drvdata(dev);
294
295	if (data_len != 2)
296		return -EPROTO;
297
298	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
299			     ST_NCI_HCI_DM_VDC_VALUE_COMPARISON,
300			     data, data_len, &skb);
301	if (r)
302		goto exit;
303
304	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
305					HCI_DM_VDC_VALUE_COMPARISON, skb->len);
306	if (!msg) {
307		r = -ENOMEM;
308		goto free_skb;
309	}
310
311	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
312		kfree_skb(msg);
313		r = -ENOBUFS;
314		goto free_skb;
315	}
316
317	r = nfc_vendor_cmd_reply(msg);
318
319free_skb:
320	kfree_skb(skb);
321exit:
322	return r;
323}
324
325static int st_nci_loopback(struct nfc_dev *dev, void *data,
326			   size_t data_len)
327{
328	int r;
329	struct sk_buff *msg, *skb;
330	struct nci_dev *ndev = nfc_get_drvdata(dev);
331
332	if (data_len <= 0)
333		return -EPROTO;
334
335	r = nci_nfcc_loopback(ndev, data, data_len, &skb);
336	if (r < 0)
337		return r;
338
339	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
340					     LOOPBACK, skb->len);
341	if (!msg) {
342		r = -ENOMEM;
343		goto free_skb;
344	}
345
346	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
347		kfree_skb(msg);
348		r = -ENOBUFS;
349		goto free_skb;
350	}
351
352	r = nfc_vendor_cmd_reply(msg);
353free_skb:
354	kfree_skb(skb);
355	return r;
356}
357
358static int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data,
359					size_t data_len)
360{
361	struct sk_buff *msg;
362	struct nci_dev *ndev = nfc_get_drvdata(dev);
363
364	msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
365					MANUFACTURER_SPECIFIC,
366					sizeof(ndev->manufact_specific_info));
367	if (!msg)
368		return -ENOMEM;
369
370	if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info),
371		    &ndev->manufact_specific_info)) {
372		kfree_skb(msg);
373		return -ENOBUFS;
374	}
375
376	return nfc_vendor_cmd_reply(msg);
377}
378
379static struct nfc_vendor_cmd st_nci_vendor_cmds[] = {
380	{
381		.vendor_id = ST_NCI_VENDOR_OUI,
382		.subcmd = FACTORY_MODE,
383		.doit = st_nci_factory_mode,
384	},
385	{
386		.vendor_id = ST_NCI_VENDOR_OUI,
387		.subcmd = HCI_CLEAR_ALL_PIPES,
388		.doit = st_nci_hci_clear_all_pipes,
389	},
390	{
391		.vendor_id = ST_NCI_VENDOR_OUI,
392		.subcmd = HCI_DM_PUT_DATA,
393		.doit = st_nci_hci_dm_put_data,
394	},
395	{
396		.vendor_id = ST_NCI_VENDOR_OUI,
397		.subcmd = HCI_DM_UPDATE_AID,
398		.doit = st_nci_hci_dm_update_aid,
399	},
400	{
401		.vendor_id = ST_NCI_VENDOR_OUI,
402		.subcmd = HCI_DM_GET_INFO,
403		.doit = st_nci_hci_dm_get_info,
404	},
405	{
406		.vendor_id = ST_NCI_VENDOR_OUI,
407		.subcmd = HCI_DM_GET_DATA,
408		.doit = st_nci_hci_dm_get_data,
409	},
410	{
411		.vendor_id = ST_NCI_VENDOR_OUI,
412		.subcmd = HCI_DM_DIRECT_LOAD,
413		.doit = st_nci_hci_dm_direct_load,
414	},
415	{
416		.vendor_id = ST_NCI_VENDOR_OUI,
417		.subcmd = HCI_DM_RESET,
418		.doit = st_nci_hci_dm_reset,
419	},
420	{
421		.vendor_id = ST_NCI_VENDOR_OUI,
422		.subcmd = HCI_GET_PARAM,
423		.doit = st_nci_hci_get_param,
424	},
425	{
426		.vendor_id = ST_NCI_VENDOR_OUI,
427		.subcmd = HCI_DM_FIELD_GENERATOR,
428		.doit = st_nci_hci_dm_field_generator,
429	},
430	{
431		.vendor_id = ST_NCI_VENDOR_OUI,
432		.subcmd = HCI_DM_FWUPD_START,
433		.doit = st_nci_hci_dm_fwupd_start,
434	},
435	{
436		.vendor_id = ST_NCI_VENDOR_OUI,
437		.subcmd = HCI_DM_FWUPD_END,
438		.doit = st_nci_hci_dm_fwupd_end,
439	},
440	{
441		.vendor_id = ST_NCI_VENDOR_OUI,
442		.subcmd = LOOPBACK,
443		.doit = st_nci_loopback,
444	},
445	{
446		.vendor_id = ST_NCI_VENDOR_OUI,
447		.subcmd = HCI_DM_VDC_MEASUREMENT_VALUE,
448		.doit = st_nci_hci_dm_vdc_measurement_value,
449	},
450	{
451		.vendor_id = ST_NCI_VENDOR_OUI,
452		.subcmd = HCI_DM_VDC_VALUE_COMPARISON,
453		.doit = st_nci_hci_dm_vdc_value_comparison,
454	},
455	{
456		.vendor_id = ST_NCI_VENDOR_OUI,
457		.subcmd = MANUFACTURER_SPECIFIC,
458		.doit = st_nci_manufacturer_specific,
459	},
460};
461
462int st_nci_vendor_cmds_init(struct nci_dev *ndev)
463{
464	return nfc_set_vendor_cmds(ndev->nfc_dev, st_nci_vendor_cmds,
465				   sizeof(st_nci_vendor_cmds));
466}
467EXPORT_SYMBOL(st_nci_vendor_cmds_init);
468