xref: /openbmc/linux/drivers/nfc/st-nci/vendor_cmds.c (revision 884caada)
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 
34 struct get_param_data {
35 	u8 gate;
36 	u8 data;
37 } __packed;
38 
39 static 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 
64 static 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 
72 static 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 
82 static 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 
91 static 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 
118 free_skb:
119 	kfree_skb(skb);
120 exit:
121 	return r;
122 }
123 
124 static 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 
151 free_skb:
152 	kfree_skb(skb);
153 exit:
154 	return r;
155 }
156 
157 static 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 
172 static 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 
181 static 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 
194 static 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 
206 static 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 
236 free_skb:
237 	kfree_skb(skb);
238 exit:
239 	return r;
240 }
241 
242 static 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 
251 static 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 
282 free_skb:
283 	kfree_skb(skb);
284 exit:
285 	return r;
286 }
287 
288 static 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 
319 free_skb:
320 	kfree_skb(skb);
321 exit:
322 	return r;
323 }
324 
325 static 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);
353 free_skb:
354 	kfree_skb(skb);
355 	return r;
356 }
357 
358 static 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 
379 static 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 
462 int 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 }
467 EXPORT_SYMBOL(st_nci_vendor_cmds_init);
468