]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/nfc/st21nfca/vendor_cmds.c
Merge tag 'gfs2-4.12.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2...
[mirror_ubuntu-artful-kernel.git] / drivers / nfc / st21nfca / vendor_cmds.c
1 /*
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
41 struct get_param_data {
42 u8 gate;
43 u8 data;
44 } __packed;
45
46 static 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
70 static 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
78 static 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
88 static 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
97 static 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
126 free_skb:
127 kfree_skb(skb);
128 exit:
129 return r;
130 }
131
132 static 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
161 free_skb:
162 kfree_skb(skb);
163 exit:
164 return r;
165 }
166
167 static 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
176 static 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
194 static 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
224 free_skb:
225 kfree_skb(skb);
226 exit:
227 return r;
228 }
229
230 static 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
241 int 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 }
256 EXPORT_SYMBOL(st21nfca_hci_loopback_event_received);
257
258 static 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);
303 free_skb:
304 kfree_skb(info->vendor_info.rx_skb);
305 exit:
306 return r;
307 }
308
309 static 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
367 int 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 }
375 EXPORT_SYMBOL(st21nfca_vendor_cmds_init);