Linux-libre 5.4.48-gnu
[librecmc/linux-libre.git] / drivers / nfc / st21nfca / vendor_cmds.c
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
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
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
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
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
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
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
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
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
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
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
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
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 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
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_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds,
362                                    sizeof(st21nfca_vendor_cmds));
363 }
364 EXPORT_SYMBOL(st21nfca_vendor_cmds_init);