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