blob: ab765e5478c018884090b2b5bcfdc511c2732436 [file] [log] [blame]
Christophe Ricard15d17172015-10-26 07:50:11 +01001/*
2 * Proprietary commands extension for STMicroelectronics NFC Chip
3 *
4 * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <net/genetlink.h>
20#include <linux/module.h>
21#include <linux/nfc.h>
22#include <net/nfc/hci.h>
23#include <net/nfc/llc.h>
24
25#include "st21nfca.h"
26
27#define ST21NFCA_HCI_DM_GETDATA 0x10
28#define ST21NFCA_HCI_DM_PUTDATA 0x11
29#define ST21NFCA_HCI_DM_LOAD 0x12
30#define ST21NFCA_HCI_DM_GETINFO 0x13
31#define ST21NFCA_HCI_DM_UPDATE_AID 0x20
32#define ST21NFCA_HCI_DM_RESET 0x3e
33
34#define ST21NFCA_HCI_DM_FIELD_GENERATOR 0x32
35
36#define ST21NFCA_FACTORY_MODE_ON 1
37#define ST21NFCA_FACTORY_MODE_OFF 0
38
39#define ST21NFCA_EVT_POST_DATA 0x02
40
41struct get_param_data {
42 u8 gate;
43 u8 data;
44} __packed;
45
46static int st21nfca_factory_mode(struct nfc_dev *dev, void *data,
47 size_t data_len)
48{
49 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
50
51 if (data_len != 1)
52 return -EINVAL;
53
54 pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
55
56 switch (((u8 *)data)[0]) {
57 case ST21NFCA_FACTORY_MODE_ON:
58 test_and_set_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
59 break;
60 case ST21NFCA_FACTORY_MODE_OFF:
61 clear_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
62 break;
63 default:
64 return -EINVAL;
65 }
66
67 return 0;
68}
69
70static int st21nfca_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
71 size_t data_len)
72{
73 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
74
75 return nfc_hci_disconnect_all_gates(hdev);
76}
77
78static int st21nfca_hci_dm_put_data(struct nfc_dev *dev, void *data,
79 size_t data_len)
80{
81 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
82
83 return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
84 ST21NFCA_HCI_DM_PUTDATA, data,
85 data_len, NULL);
86}
87
88static int st21nfca_hci_dm_update_aid(struct nfc_dev *dev, void *data,
89 size_t data_len)
90{
91 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
92
93 return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
94 ST21NFCA_HCI_DM_UPDATE_AID, data, data_len, NULL);
95}
96
97static int st21nfca_hci_dm_get_info(struct nfc_dev *dev, void *data,
98 size_t data_len)
99{
100 int r;
101 struct sk_buff *msg, *skb;
102 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
103
104 r = nfc_hci_send_cmd(hdev,
105 ST21NFCA_DEVICE_MGNT_GATE,
106 ST21NFCA_HCI_DM_GETINFO,
107 data, data_len, &skb);
108 if (r)
109 goto exit;
110
111 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
112 HCI_DM_GET_INFO, skb->len);
113 if (!msg) {
114 r = -ENOMEM;
115 goto free_skb;
116 }
117
118 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
119 kfree_skb(msg);
120 r = -ENOBUFS;
121 goto free_skb;
122 }
123
124 r = nfc_vendor_cmd_reply(msg);
125
126free_skb:
127 kfree_skb(skb);
128exit:
129 return r;
130}
131
132static int st21nfca_hci_dm_get_data(struct nfc_dev *dev, void *data,
133 size_t data_len)
134{
135 int r;
136 struct sk_buff *msg, *skb;
137 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
138
139 r = nfc_hci_send_cmd(hdev,
140 ST21NFCA_DEVICE_MGNT_GATE,
141 ST21NFCA_HCI_DM_GETDATA,
142 data, data_len, &skb);
143 if (r)
144 goto exit;
145
146 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
147 HCI_DM_GET_DATA, skb->len);
148 if (!msg) {
149 r = -ENOMEM;
150 goto free_skb;
151 }
152
153 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
154 kfree_skb(msg);
155 r = -ENOBUFS;
156 goto free_skb;
157 }
158
159 r = nfc_vendor_cmd_reply(msg);
160
161free_skb:
162 kfree_skb(skb);
163exit:
164 return r;
165}
166
167static int st21nfca_hci_dm_load(struct nfc_dev *dev, void *data,
168 size_t data_len)
169{
170 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
171
172 return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
173 ST21NFCA_HCI_DM_LOAD, data, data_len, NULL);
174}
175
176static int st21nfca_hci_dm_reset(struct nfc_dev *dev, void *data,
177 size_t data_len)
178{
179 int r;
180 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
181
182 r = nfc_hci_send_cmd_async(hdev, ST21NFCA_DEVICE_MGNT_GATE,
183 ST21NFCA_HCI_DM_RESET, data, data_len, NULL, NULL);
184 if (r < 0)
185 return r;
186
187 r = nfc_llc_stop(hdev->llc);
188 if (r < 0)
189 return r;
190
191 return nfc_llc_start(hdev->llc);
192}
193
194static int st21nfca_hci_get_param(struct nfc_dev *dev, void *data,
195 size_t data_len)
196{
197 int r;
198 struct sk_buff *msg, *skb;
199 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
200 struct get_param_data *param = (struct get_param_data *)data;
201
202 if (data_len < sizeof(struct get_param_data))
203 return -EPROTO;
204
205 r = nfc_hci_get_param(hdev, param->gate, param->data, &skb);
206 if (r)
207 goto exit;
208
209 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
210 HCI_GET_PARAM, skb->len);
211 if (!msg) {
212 r = -ENOMEM;
213 goto free_skb;
214 }
215
216 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
217 kfree_skb(msg);
218 r = -ENOBUFS;
219 goto free_skb;
220 }
221
222 r = nfc_vendor_cmd_reply(msg);
223
224free_skb:
225 kfree_skb(skb);
226exit:
227 return r;
228}
229
230static int st21nfca_hci_dm_field_generator(struct nfc_dev *dev, void *data,
231 size_t data_len)
232{
233 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
234
235 return nfc_hci_send_cmd(hdev,
236 ST21NFCA_DEVICE_MGNT_GATE,
237 ST21NFCA_HCI_DM_FIELD_GENERATOR,
238 data, data_len, NULL);
239}
240
241int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *hdev, u8 event,
242 struct sk_buff *skb)
243{
244 struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
245
246 switch (event) {
247 case ST21NFCA_EVT_POST_DATA:
248 info->vendor_info.rx_skb = skb;
249 break;
250 default:
251 nfc_err(&hdev->ndev->dev, "Unexpected event on loopback gate\n");
252 }
253 complete(&info->vendor_info.req_completion);
254 return 0;
255}
256EXPORT_SYMBOL(st21nfca_hci_loopback_event_received);
257
258static int st21nfca_hci_loopback(struct nfc_dev *dev, void *data,
259 size_t data_len)
260{
261 int r;
262 struct sk_buff *msg;
263 struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
264 struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
265
266 if (data_len <= 0)
267 return -EPROTO;
268
269 reinit_completion(&info->vendor_info.req_completion);
270 info->vendor_info.rx_skb = NULL;
271
272 r = nfc_hci_send_event(hdev, NFC_HCI_LOOPBACK_GATE,
273 ST21NFCA_EVT_POST_DATA, data, data_len);
274 if (r < 0) {
275 r = -EPROTO;
276 goto exit;
277 }
278
279 wait_for_completion_interruptible(&info->vendor_info.req_completion);
280 if (!info->vendor_info.rx_skb ||
281 info->vendor_info.rx_skb->len != data_len) {
282 r = -EPROTO;
283 goto exit;
284 }
285
286 msg = nfc_vendor_cmd_alloc_reply_skb(hdev->ndev,
287 ST21NFCA_VENDOR_OUI,
288 HCI_LOOPBACK,
289 info->vendor_info.rx_skb->len);
290 if (!msg) {
291 r = -ENOMEM;
292 goto free_skb;
293 }
294
295 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len,
296 info->vendor_info.rx_skb->data)) {
297 kfree_skb(msg);
298 r = -ENOBUFS;
299 goto free_skb;
300 }
301
302 r = nfc_vendor_cmd_reply(msg);
303free_skb:
304 kfree_skb(info->vendor_info.rx_skb);
305exit:
306 return r;
307}
308
309static struct nfc_vendor_cmd st21nfca_vendor_cmds[] = {
310 {
311 .vendor_id = ST21NFCA_VENDOR_OUI,
312 .subcmd = FACTORY_MODE,
313 .doit = st21nfca_factory_mode,
314 },
315 {
316 .vendor_id = ST21NFCA_VENDOR_OUI,
317 .subcmd = HCI_CLEAR_ALL_PIPES,
318 .doit = st21nfca_hci_clear_all_pipes,
319 },
320 {
321 .vendor_id = ST21NFCA_VENDOR_OUI,
322 .subcmd = HCI_DM_PUT_DATA,
323 .doit = st21nfca_hci_dm_put_data,
324 },
325 {
326 .vendor_id = ST21NFCA_VENDOR_OUI,
327 .subcmd = HCI_DM_UPDATE_AID,
328 .doit = st21nfca_hci_dm_update_aid,
329 },
330 {
331 .vendor_id = ST21NFCA_VENDOR_OUI,
332 .subcmd = HCI_DM_GET_INFO,
333 .doit = st21nfca_hci_dm_get_info,
334 },
335 {
336 .vendor_id = ST21NFCA_VENDOR_OUI,
337 .subcmd = HCI_DM_GET_DATA,
338 .doit = st21nfca_hci_dm_get_data,
339 },
340 {
341 .vendor_id = ST21NFCA_VENDOR_OUI,
342 .subcmd = HCI_DM_LOAD,
343 .doit = st21nfca_hci_dm_load,
344 },
345 {
346 .vendor_id = ST21NFCA_VENDOR_OUI,
347 .subcmd = HCI_DM_RESET,
348 .doit = st21nfca_hci_dm_reset,
349 },
350 {
351 .vendor_id = ST21NFCA_VENDOR_OUI,
352 .subcmd = HCI_GET_PARAM,
353 .doit = st21nfca_hci_get_param,
354 },
355 {
356 .vendor_id = ST21NFCA_VENDOR_OUI,
357 .subcmd = HCI_DM_FIELD_GENERATOR,
358 .doit = st21nfca_hci_dm_field_generator,
359 },
360 {
361 .vendor_id = ST21NFCA_VENDOR_OUI,
362 .subcmd = HCI_LOOPBACK,
363 .doit = st21nfca_hci_loopback,
364 },
365};
366
367int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev)
368{
369 struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
370
371 init_completion(&info->vendor_info.req_completion);
372 return nfc_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds,
373 sizeof(st21nfca_vendor_cmds));
374}
375EXPORT_SYMBOL(st21nfca_vendor_cmds_init);