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
st21nfca_factory_mode(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_clear_all_pipes(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_dm_put_data(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_dm_update_aid(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_dm_get_info(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_dm_get_data(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_dm_load(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_dm_reset(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_get_param(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_dm_field_generator(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_hci_loopback_event_received(struct nfc_hci_dev * hdev,u8 event,struct sk_buff * skb)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
st21nfca_hci_loopback(struct nfc_dev * dev,void * data,size_t data_len)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
st21nfca_vendor_cmds_init(struct nfc_hci_dev * hdev)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);
365