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
st_nci_factory_mode(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_clear_all_pipes(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_put_data(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_update_aid(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_get_info(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_get_data(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_fwupd_start(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_fwupd_end(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_direct_load(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_reset(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_get_param(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_field_generator(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_vdc_measurement_value(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_hci_dm_vdc_value_comparison(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_loopback(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_manufacturer_specific(struct nfc_dev * dev,void * data,size_t data_len)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
st_nci_vendor_cmds_init(struct nci_dev * ndev)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