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
30struct get_param_data {
31 u8 gate;
32 u8 data;
33} __packed;
34
35static 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, addr: &hdev->quirks);
48 break;
49 case ST21NFCA_FACTORY_MODE_OFF:
50 clear_bit(ST21NFCA_FACTORY_MODE, addr: &hdev->quirks);
51 break;
52 default:
53 return -EINVAL;
54 }
55
56 return 0;
57}
58
59static 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
67static 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, param: data,
74 param_len: data_len, NULL);
75}
76
77static 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, param: data, param_len: data_len, NULL);
84}
85
86static 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 param: data, param_len: data_len, skb: &skb);
97 if (r)
98 goto exit;
99
100 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
101 subcmd: HCI_DM_GET_INFO, approxlen: skb->len);
102 if (!msg) {
103 r = -ENOMEM;
104 goto free_skb;
105 }
106
107 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: skb->len, data: skb->data)) {
108 kfree_skb(skb: msg);
109 r = -ENOBUFS;
110 goto free_skb;
111 }
112
113 r = nfc_vendor_cmd_reply(skb: msg);
114
115free_skb:
116 kfree_skb(skb);
117exit:
118 return r;
119}
120
121static 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 param: data, param_len: data_len, skb: &skb);
132 if (r)
133 goto exit;
134
135 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_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);
152exit:
153 return r;
154}
155
156static 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, param: data, param_len: data_len, NULL);
163}
164
165static 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, param: data, param_len: data_len, NULL, NULL);
173 if (r < 0)
174 return r;
175
176 r = nfc_llc_stop(llc: hdev->llc);
177 if (r < 0)
178 return r;
179
180 return nfc_llc_start(llc: hdev->llc);
181}
182
183static 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, gate: param->gate, idx: param->data, skb: &skb);
195 if (r)
196 goto exit;
197
198 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
199 subcmd: HCI_GET_PARAM, approxlen: skb->len);
200 if (!msg) {
201 r = -ENOMEM;
202 goto free_skb;
203 }
204
205 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: skb->len, data: skb->data)) {
206 kfree_skb(skb: msg);
207 r = -ENOBUFS;
208 goto free_skb;
209 }
210
211 r = nfc_vendor_cmd_reply(skb: msg);
212
213free_skb:
214 kfree_skb(skb);
215exit:
216 return r;
217}
218
219static 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 param: data, param_len: data_len, NULL);
228}
229
230int 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}
245EXPORT_SYMBOL(st21nfca_hci_loopback_event_received);
246
247static 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(x: &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, param: data, param_len: data_len);
263 if (r < 0) {
264 r = -EPROTO;
265 goto exit;
266 }
267
268 wait_for_completion_interruptible(x: &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(dev: hdev->ndev,
276 ST21NFCA_VENDOR_OUI,
277 subcmd: HCI_LOOPBACK,
278 approxlen: info->vendor_info.rx_skb->len);
279 if (!msg) {
280 r = -ENOMEM;
281 goto free_skb;
282 }
283
284 if (nla_put(skb: msg, attrtype: NFC_ATTR_VENDOR_DATA, attrlen: info->vendor_info.rx_skb->len,
285 data: info->vendor_info.rx_skb->data)) {
286 kfree_skb(skb: msg);
287 r = -ENOBUFS;
288 goto free_skb;
289 }
290
291 r = nfc_vendor_cmd_reply(skb: msg);
292free_skb:
293 kfree_skb(skb: info->vendor_info.rx_skb);
294exit:
295 return r;
296}
297
298static const 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
356int 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(x: &info->vendor_info.req_completion);
361 return nfc_hci_set_vendor_cmds(hdev, cmds: st21nfca_vendor_cmds,
362 n_cmds: sizeof(st21nfca_vendor_cmds));
363}
364EXPORT_SYMBOL(st21nfca_vendor_cmds_init);
365

source code of linux/drivers/nfc/st21nfca/vendor_cmds.c