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, addr: &info->flags);
53 break;
54 case ST_NCI_FACTORY_MODE_OFF:
55 clear_bit(ST_NCI_FACTORY_MODE, addr: &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, param: data,
79 param_len: 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, param: data, param_len: 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 param: data, param_len: data_len, skb: &skb);
100 if (r)
101 return r;
102
103 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
104 subcmd: HCI_DM_GET_INFO, approxlen: skb->len);
105 if (!msg) {
106 r = -ENOMEM;
107 goto free_skb;
108 }
109
110 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: skb->len, data: skb->data)) {
111 kfree_skb(skb: msg);
112 r = -ENOBUFS;
113 goto free_skb;
114 }
115
116 r = nfc_vendor_cmd_reply(skb: 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 param: data, param_len: data_len, skb: &skb);
132 if (r)
133 return r;
134
135 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
136 subcmd: HCI_DM_GET_DATA, approxlen: skb->len);
137 if (!msg) {
138 r = -ENOMEM;
139 goto free_skb;
140 }
141
142 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: skb->len, data: skb->data)) {
143 kfree_skb(skb: msg);
144 r = -ENOBUFS;
145 goto free_skb;
146 }
147
148 r = nfc_vendor_cmd_reply(skb: 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, param: data, param_len: 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, param: data, param_len: 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, param: data, param_len: 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, param: data, param_len: data_len, NULL);
199 msleep(msecs: 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, gate: param->gate, idx: param->data, skb: &skb);
216 if (r)
217 return r;
218
219 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
220 subcmd: HCI_GET_PARAM, approxlen: skb->len);
221 if (!msg) {
222 r = -ENOMEM;
223 goto free_skb;
224 }
225
226 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: skb->len, data: skb->data)) {
227 kfree_skb(skb: msg);
228 r = -ENOBUFS;
229 goto free_skb;
230 }
231
232 r = nfc_vendor_cmd_reply(skb: 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, param: data, param_len: 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 param: data, param_len: data_len, skb: &skb);
261 if (r)
262 return r;
263
264 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
265 subcmd: HCI_DM_VDC_MEASUREMENT_VALUE, approxlen: skb->len);
266 if (!msg) {
267 r = -ENOMEM;
268 goto free_skb;
269 }
270
271 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: skb->len, data: skb->data)) {
272 kfree_skb(skb: msg);
273 r = -ENOBUFS;
274 goto free_skb;
275 }
276
277 r = nfc_vendor_cmd_reply(skb: 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 param: data, param_len: data_len, skb: &skb);
297 if (r)
298 return r;
299
300 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
301 subcmd: HCI_DM_VDC_VALUE_COMPARISON, approxlen: skb->len);
302 if (!msg) {
303 r = -ENOMEM;
304 goto free_skb;
305 }
306
307 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: skb->len, data: skb->data)) {
308 kfree_skb(skb: msg);
309 r = -ENOBUFS;
310 goto free_skb;
311 }
312
313 r = nfc_vendor_cmd_reply(skb: 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, resp: &skb);
331 if (r < 0)
332 return r;
333
334 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
335 subcmd: LOOPBACK, approxlen: skb->len);
336 if (!msg) {
337 r = -ENOMEM;
338 goto free_skb;
339 }
340
341 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: skb->len, data: skb->data)) {
342 kfree_skb(skb: msg);
343 r = -ENOBUFS;
344 goto free_skb;
345 }
346
347 r = nfc_vendor_cmd_reply(skb: 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 subcmd: MANUFACTURER_SPECIFIC,
361 approxlen: sizeof(ndev->manufact_specific_info));
362 if (!msg)
363 return -ENOMEM;
364
365 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: sizeof(ndev->manufact_specific_info),
366 data: &ndev->manufact_specific_info)) {
367 kfree_skb(skb: msg);
368 return -ENOBUFS;
369 }
370
371 return nfc_vendor_cmd_reply(skb: 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, cmds: st_nci_vendor_cmds,
460 n_cmds: sizeof(st_nci_vendor_cmds));
461}
462EXPORT_SYMBOL(st_nci_vendor_cmds_init);
463

source code of linux/drivers/nfc/st-nci/vendor_cmds.c