Merge tag 'pci-v5.15-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[linux-2.6-microblaze.git] / drivers / nfc / st-nci / vendor_cmds.c
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                 return r;
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         return r;
121 }
122
123 static 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                              data, data_len, &skb);
132         if (r)
133                 return r;
134
135         msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_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         return r;
153 }
154
155 static 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, data, data_len, NULL);
164         if (r)
165                 dev->fw_download_in_progress = false;
166
167         return r;
168 }
169
170 static 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, data, data_len, NULL);
177 }
178
179 static 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, data, data_len, NULL);
188         }
189         return -EPROTO;
190 }
191
192 static 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, data, data_len, NULL);
199         msleep(200);
200
201         return 0;
202 }
203
204 static 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, param->gate, param->data, &skb);
216         if (r)
217                 return r;
218
219         msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
220                                              HCI_GET_PARAM, skb->len);
221         if (!msg) {
222                 r = -ENOMEM;
223                 goto free_skb;
224         }
225
226         if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
227                 kfree_skb(msg);
228                 r = -ENOBUFS;
229                 goto free_skb;
230         }
231
232         r = nfc_vendor_cmd_reply(msg);
233
234 free_skb:
235         kfree_skb(skb);
236         return r;
237 }
238
239 static 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, data, data_len, NULL);
246 }
247
248 static 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                              data, data_len, &skb);
261         if (r)
262                 return r;
263
264         msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
265                                 HCI_DM_VDC_MEASUREMENT_VALUE, skb->len);
266         if (!msg) {
267                 r = -ENOMEM;
268                 goto free_skb;
269         }
270
271         if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
272                 kfree_skb(msg);
273                 r = -ENOBUFS;
274                 goto free_skb;
275         }
276
277         r = nfc_vendor_cmd_reply(msg);
278
279 free_skb:
280         kfree_skb(skb);
281         return r;
282 }
283
284 static 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                              data, data_len, &skb);
297         if (r)
298                 return r;
299
300         msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
301                                         HCI_DM_VDC_VALUE_COMPARISON, skb->len);
302         if (!msg) {
303                 r = -ENOMEM;
304                 goto free_skb;
305         }
306
307         if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
308                 kfree_skb(msg);
309                 r = -ENOBUFS;
310                 goto free_skb;
311         }
312
313         r = nfc_vendor_cmd_reply(msg);
314
315 free_skb:
316         kfree_skb(skb);
317         return r;
318 }
319
320 static 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, &skb);
331         if (r < 0)
332                 return r;
333
334         msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
335                                              LOOPBACK, skb->len);
336         if (!msg) {
337                 r = -ENOMEM;
338                 goto free_skb;
339         }
340
341         if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
342                 kfree_skb(msg);
343                 r = -ENOBUFS;
344                 goto free_skb;
345         }
346
347         r = nfc_vendor_cmd_reply(msg);
348 free_skb:
349         kfree_skb(skb);
350         return r;
351 }
352
353 static 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                                         MANUFACTURER_SPECIFIC,
361                                         sizeof(ndev->manufact_specific_info));
362         if (!msg)
363                 return -ENOMEM;
364
365         if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info),
366                     &ndev->manufact_specific_info)) {
367                 kfree_skb(msg);
368                 return -ENOBUFS;
369         }
370
371         return nfc_vendor_cmd_reply(msg);
372 }
373
374 static 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
457 int st_nci_vendor_cmds_init(struct nci_dev *ndev)
458 {
459         return nfc_set_vendor_cmds(ndev->nfc_dev, st_nci_vendor_cmds,
460                                    sizeof(st_nci_vendor_cmds));
461 }
462 EXPORT_SYMBOL(st_nci_vendor_cmds_init);