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 
30 struct get_param_data {
31 	u8 gate;
32 	u8 data;
33 } __packed;
34 
st21nfca_factory_mode(struct nfc_dev * dev,void * data,size_t data_len)35 static 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 
st21nfca_hci_clear_all_pipes(struct nfc_dev * dev,void * data,size_t data_len)59 static 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 
st21nfca_hci_dm_put_data(struct nfc_dev * dev,void * data,size_t data_len)67 static 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 
st21nfca_hci_dm_update_aid(struct nfc_dev * dev,void * data,size_t data_len)77 static 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 
st21nfca_hci_dm_get_info(struct nfc_dev * dev,void * data,size_t data_len)86 static 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 
115 free_skb:
116 	kfree_skb(skb);
117 exit:
118 	return r;
119 }
120 
st21nfca_hci_dm_get_data(struct nfc_dev * dev,void * data,size_t data_len)121 static 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 
150 free_skb:
151 	kfree_skb(skb);
152 exit:
153 	return r;
154 }
155 
st21nfca_hci_dm_load(struct nfc_dev * dev,void * data,size_t data_len)156 static 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 
st21nfca_hci_dm_reset(struct nfc_dev * dev,void * data,size_t data_len)165 static 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 
st21nfca_hci_get_param(struct nfc_dev * dev,void * data,size_t data_len)183 static 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 
213 free_skb:
214 	kfree_skb(skb);
215 exit:
216 	return r;
217 }
218 
st21nfca_hci_dm_field_generator(struct nfc_dev * dev,void * data,size_t data_len)219 static 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 
st21nfca_hci_loopback_event_received(struct nfc_hci_dev * hdev,u8 event,struct sk_buff * skb)230 int 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 }
245 EXPORT_SYMBOL(st21nfca_hci_loopback_event_received);
246 
st21nfca_hci_loopback(struct nfc_dev * dev,void * data,size_t data_len)247 static 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);
292 free_skb:
293 	kfree_skb(info->vendor_info.rx_skb);
294 exit:
295 	return r;
296 }
297 
298 static 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 
st21nfca_vendor_cmds_init(struct nfc_hci_dev * hdev)356 int 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 }
364 EXPORT_SYMBOL(st21nfca_vendor_cmds_init);
365