146fe7771SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b1fa4dc4SChristophe Ricard /*
3b1fa4dc4SChristophe Ricard * Proprietary commands extension for STMicroelectronics NFC NCI Chip
4b1fa4dc4SChristophe Ricard *
5b1fa4dc4SChristophe Ricard * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
6b1fa4dc4SChristophe Ricard */
7b1fa4dc4SChristophe Ricard
8b1fa4dc4SChristophe Ricard #include <net/genetlink.h>
9b1fa4dc4SChristophe Ricard #include <linux/module.h>
10b1fa4dc4SChristophe Ricard #include <linux/nfc.h>
11b1fa4dc4SChristophe Ricard #include <linux/delay.h>
12b1fa4dc4SChristophe Ricard #include <net/nfc/nci_core.h>
13b1fa4dc4SChristophe Ricard
14b1fa4dc4SChristophe Ricard #include "st-nci.h"
15b1fa4dc4SChristophe Ricard
16b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_GETDATA 0x10
17b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_PUTDATA 0x11
18b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_LOAD 0x12
19b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_GETINFO 0x13
20b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_FWUPD_START 0x14
21b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_FWUPD_STOP 0x15
22b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_UPDATE_AID 0x20
23b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_RESET 0x3e
24b1fa4dc4SChristophe Ricard
25b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_FIELD_GENERATOR 0x32
26b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE 0x33
27b1fa4dc4SChristophe Ricard #define ST_NCI_HCI_DM_VDC_VALUE_COMPARISON 0x34
28b1fa4dc4SChristophe Ricard
29b1fa4dc4SChristophe Ricard #define ST_NCI_FACTORY_MODE_ON 1
30b1fa4dc4SChristophe Ricard #define ST_NCI_FACTORY_MODE_OFF 0
31b1fa4dc4SChristophe Ricard
32b1fa4dc4SChristophe Ricard #define ST_NCI_EVT_POST_DATA 0x02
33b1fa4dc4SChristophe Ricard
34b1fa4dc4SChristophe Ricard struct get_param_data {
35b1fa4dc4SChristophe Ricard u8 gate;
36b1fa4dc4SChristophe Ricard u8 data;
37b1fa4dc4SChristophe Ricard } __packed;
38b1fa4dc4SChristophe Ricard
st_nci_factory_mode(struct nfc_dev * dev,void * data,size_t data_len)39b1fa4dc4SChristophe Ricard static int st_nci_factory_mode(struct nfc_dev *dev, void *data,
40b1fa4dc4SChristophe Ricard size_t data_len)
41b1fa4dc4SChristophe Ricard {
42b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
43b1fa4dc4SChristophe Ricard struct st_nci_info *info = nci_get_drvdata(ndev);
44b1fa4dc4SChristophe Ricard
45b1fa4dc4SChristophe Ricard if (data_len != 1)
46b1fa4dc4SChristophe Ricard return -EINVAL;
47b1fa4dc4SChristophe Ricard
48b1fa4dc4SChristophe Ricard pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
49b1fa4dc4SChristophe Ricard
50b1fa4dc4SChristophe Ricard switch (((u8 *)data)[0]) {
51b1fa4dc4SChristophe Ricard case ST_NCI_FACTORY_MODE_ON:
52b1fa4dc4SChristophe Ricard test_and_set_bit(ST_NCI_FACTORY_MODE, &info->flags);
53b1fa4dc4SChristophe Ricard break;
54b1fa4dc4SChristophe Ricard case ST_NCI_FACTORY_MODE_OFF:
55b1fa4dc4SChristophe Ricard clear_bit(ST_NCI_FACTORY_MODE, &info->flags);
56b1fa4dc4SChristophe Ricard break;
57b1fa4dc4SChristophe Ricard default:
58b1fa4dc4SChristophe Ricard return -EINVAL;
59b1fa4dc4SChristophe Ricard }
60b1fa4dc4SChristophe Ricard
61b1fa4dc4SChristophe Ricard return 0;
62b1fa4dc4SChristophe Ricard }
63b1fa4dc4SChristophe Ricard
st_nci_hci_clear_all_pipes(struct nfc_dev * dev,void * data,size_t data_len)64b1fa4dc4SChristophe Ricard static int st_nci_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
65b1fa4dc4SChristophe Ricard size_t data_len)
66b1fa4dc4SChristophe Ricard {
67b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
68b1fa4dc4SChristophe Ricard
69b1fa4dc4SChristophe Ricard return nci_hci_clear_all_pipes(ndev);
70b1fa4dc4SChristophe Ricard }
71b1fa4dc4SChristophe Ricard
st_nci_hci_dm_put_data(struct nfc_dev * dev,void * data,size_t data_len)72b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_put_data(struct nfc_dev *dev, void *data,
73b1fa4dc4SChristophe Ricard size_t data_len)
74b1fa4dc4SChristophe Ricard {
75b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
76b1fa4dc4SChristophe Ricard
77b1fa4dc4SChristophe Ricard return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
78b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_PUTDATA, data,
79b1fa4dc4SChristophe Ricard data_len, NULL);
80b1fa4dc4SChristophe Ricard }
81b1fa4dc4SChristophe Ricard
st_nci_hci_dm_update_aid(struct nfc_dev * dev,void * data,size_t data_len)82b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_update_aid(struct nfc_dev *dev, void *data,
83b1fa4dc4SChristophe Ricard size_t data_len)
84b1fa4dc4SChristophe Ricard {
85b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
86b1fa4dc4SChristophe Ricard
87b1fa4dc4SChristophe Ricard return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
88b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_UPDATE_AID, data, data_len, NULL);
89b1fa4dc4SChristophe Ricard }
90b1fa4dc4SChristophe Ricard
st_nci_hci_dm_get_info(struct nfc_dev * dev,void * data,size_t data_len)91b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_get_info(struct nfc_dev *dev, void *data,
92b1fa4dc4SChristophe Ricard size_t data_len)
93b1fa4dc4SChristophe Ricard {
94b1fa4dc4SChristophe Ricard int r;
95b1fa4dc4SChristophe Ricard struct sk_buff *msg, *skb;
96b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
97b1fa4dc4SChristophe Ricard
98b1fa4dc4SChristophe Ricard r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETINFO,
99b1fa4dc4SChristophe Ricard data, data_len, &skb);
100b1fa4dc4SChristophe Ricard if (r)
101c7a551b2Swengjianfeng return r;
102b1fa4dc4SChristophe Ricard
103b1fa4dc4SChristophe Ricard msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
104b1fa4dc4SChristophe Ricard HCI_DM_GET_INFO, skb->len);
105b1fa4dc4SChristophe Ricard if (!msg) {
106b1fa4dc4SChristophe Ricard r = -ENOMEM;
107b1fa4dc4SChristophe Ricard goto free_skb;
108b1fa4dc4SChristophe Ricard }
109b1fa4dc4SChristophe Ricard
110b1fa4dc4SChristophe Ricard if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
111b1fa4dc4SChristophe Ricard kfree_skb(msg);
112b1fa4dc4SChristophe Ricard r = -ENOBUFS;
113b1fa4dc4SChristophe Ricard goto free_skb;
114b1fa4dc4SChristophe Ricard }
115b1fa4dc4SChristophe Ricard
116b1fa4dc4SChristophe Ricard r = nfc_vendor_cmd_reply(msg);
117b1fa4dc4SChristophe Ricard
118b1fa4dc4SChristophe Ricard free_skb:
119b1fa4dc4SChristophe Ricard kfree_skb(skb);
120b1fa4dc4SChristophe Ricard return r;
121b1fa4dc4SChristophe Ricard }
122b1fa4dc4SChristophe Ricard
st_nci_hci_dm_get_data(struct nfc_dev * dev,void * data,size_t data_len)123b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_get_data(struct nfc_dev *dev, void *data,
124b1fa4dc4SChristophe Ricard size_t data_len)
125b1fa4dc4SChristophe Ricard {
126b1fa4dc4SChristophe Ricard int r;
127b1fa4dc4SChristophe Ricard struct sk_buff *msg, *skb;
128b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
129b1fa4dc4SChristophe Ricard
130b1fa4dc4SChristophe Ricard r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETDATA,
131b1fa4dc4SChristophe Ricard data, data_len, &skb);
132b1fa4dc4SChristophe Ricard if (r)
133c7a551b2Swengjianfeng return r;
134b1fa4dc4SChristophe Ricard
135b1fa4dc4SChristophe Ricard msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
136b1fa4dc4SChristophe Ricard HCI_DM_GET_DATA, skb->len);
137b1fa4dc4SChristophe Ricard if (!msg) {
138b1fa4dc4SChristophe Ricard r = -ENOMEM;
139b1fa4dc4SChristophe Ricard goto free_skb;
140b1fa4dc4SChristophe Ricard }
141b1fa4dc4SChristophe Ricard
142b1fa4dc4SChristophe Ricard if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
143b1fa4dc4SChristophe Ricard kfree_skb(msg);
144b1fa4dc4SChristophe Ricard r = -ENOBUFS;
145b1fa4dc4SChristophe Ricard goto free_skb;
146b1fa4dc4SChristophe Ricard }
147b1fa4dc4SChristophe Ricard
148b1fa4dc4SChristophe Ricard r = nfc_vendor_cmd_reply(msg);
149b1fa4dc4SChristophe Ricard
150b1fa4dc4SChristophe Ricard free_skb:
151b1fa4dc4SChristophe Ricard kfree_skb(skb);
152b1fa4dc4SChristophe Ricard return r;
153b1fa4dc4SChristophe Ricard }
154b1fa4dc4SChristophe Ricard
st_nci_hci_dm_fwupd_start(struct nfc_dev * dev,void * data,size_t data_len)155b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data,
156b1fa4dc4SChristophe Ricard size_t data_len)
157b1fa4dc4SChristophe Ricard {
158b1fa4dc4SChristophe Ricard int r;
159b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
160b1fa4dc4SChristophe Ricard
161b1fa4dc4SChristophe Ricard dev->fw_download_in_progress = true;
162b1fa4dc4SChristophe Ricard r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
163b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL);
164b1fa4dc4SChristophe Ricard if (r)
165b1fa4dc4SChristophe Ricard dev->fw_download_in_progress = false;
166b1fa4dc4SChristophe Ricard
167b1fa4dc4SChristophe Ricard return r;
168b1fa4dc4SChristophe Ricard }
169b1fa4dc4SChristophe Ricard
st_nci_hci_dm_fwupd_end(struct nfc_dev * dev,void * data,size_t data_len)170b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_fwupd_end(struct nfc_dev *dev, void *data,
171b1fa4dc4SChristophe Ricard size_t data_len)
172b1fa4dc4SChristophe Ricard {
173b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
174b1fa4dc4SChristophe Ricard
175b1fa4dc4SChristophe Ricard return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
176b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL);
177b1fa4dc4SChristophe Ricard }
178b1fa4dc4SChristophe Ricard
st_nci_hci_dm_direct_load(struct nfc_dev * dev,void * data,size_t data_len)179b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data,
180b1fa4dc4SChristophe Ricard size_t data_len)
181b1fa4dc4SChristophe Ricard {
182b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
183b1fa4dc4SChristophe Ricard
184b1fa4dc4SChristophe Ricard if (dev->fw_download_in_progress) {
185b1fa4dc4SChristophe Ricard dev->fw_download_in_progress = false;
186b1fa4dc4SChristophe Ricard return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
187b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_LOAD, data, data_len, NULL);
188b1fa4dc4SChristophe Ricard }
189b1fa4dc4SChristophe Ricard return -EPROTO;
190b1fa4dc4SChristophe Ricard }
191b1fa4dc4SChristophe Ricard
st_nci_hci_dm_reset(struct nfc_dev * dev,void * data,size_t data_len)192b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_reset(struct nfc_dev *dev, void *data,
193b1fa4dc4SChristophe Ricard size_t data_len)
194b1fa4dc4SChristophe Ricard {
195b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
196b1fa4dc4SChristophe Ricard
197b1fa4dc4SChristophe Ricard nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
198b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_RESET, data, data_len, NULL);
199b1fa4dc4SChristophe Ricard msleep(200);
200b1fa4dc4SChristophe Ricard
201b1fa4dc4SChristophe Ricard return 0;
202b1fa4dc4SChristophe Ricard }
203b1fa4dc4SChristophe Ricard
st_nci_hci_get_param(struct nfc_dev * dev,void * data,size_t data_len)204b1fa4dc4SChristophe Ricard static int st_nci_hci_get_param(struct nfc_dev *dev, void *data,
205b1fa4dc4SChristophe Ricard size_t data_len)
206b1fa4dc4SChristophe Ricard {
207b1fa4dc4SChristophe Ricard int r;
208b1fa4dc4SChristophe Ricard struct sk_buff *msg, *skb;
209b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
210b1fa4dc4SChristophe Ricard struct get_param_data *param = (struct get_param_data *)data;
211b1fa4dc4SChristophe Ricard
212b1fa4dc4SChristophe Ricard if (data_len < sizeof(struct get_param_data))
213b1fa4dc4SChristophe Ricard return -EPROTO;
214b1fa4dc4SChristophe Ricard
215b1fa4dc4SChristophe Ricard r = nci_hci_get_param(ndev, param->gate, param->data, &skb);
216b1fa4dc4SChristophe Ricard if (r)
217c7a551b2Swengjianfeng return r;
218b1fa4dc4SChristophe Ricard
219b1fa4dc4SChristophe Ricard msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
220b1fa4dc4SChristophe Ricard HCI_GET_PARAM, skb->len);
221b1fa4dc4SChristophe Ricard if (!msg) {
222b1fa4dc4SChristophe Ricard r = -ENOMEM;
223b1fa4dc4SChristophe Ricard goto free_skb;
224b1fa4dc4SChristophe Ricard }
225b1fa4dc4SChristophe Ricard
226b1fa4dc4SChristophe Ricard if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
227b1fa4dc4SChristophe Ricard kfree_skb(msg);
228b1fa4dc4SChristophe Ricard r = -ENOBUFS;
229b1fa4dc4SChristophe Ricard goto free_skb;
230b1fa4dc4SChristophe Ricard }
231b1fa4dc4SChristophe Ricard
232b1fa4dc4SChristophe Ricard r = nfc_vendor_cmd_reply(msg);
233b1fa4dc4SChristophe Ricard
234b1fa4dc4SChristophe Ricard free_skb:
235b1fa4dc4SChristophe Ricard kfree_skb(skb);
236b1fa4dc4SChristophe Ricard return r;
237b1fa4dc4SChristophe Ricard }
238b1fa4dc4SChristophe Ricard
st_nci_hci_dm_field_generator(struct nfc_dev * dev,void * data,size_t data_len)239b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_field_generator(struct nfc_dev *dev, void *data,
240b1fa4dc4SChristophe Ricard size_t data_len)
241b1fa4dc4SChristophe Ricard {
242b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
243b1fa4dc4SChristophe Ricard
244b1fa4dc4SChristophe Ricard return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
245b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_FIELD_GENERATOR, data, data_len, NULL);
246b1fa4dc4SChristophe Ricard }
247b1fa4dc4SChristophe Ricard
st_nci_hci_dm_vdc_measurement_value(struct nfc_dev * dev,void * data,size_t data_len)248b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_vdc_measurement_value(struct nfc_dev *dev, void *data,
249b1fa4dc4SChristophe Ricard size_t data_len)
250b1fa4dc4SChristophe Ricard {
251b1fa4dc4SChristophe Ricard int r;
252b1fa4dc4SChristophe Ricard struct sk_buff *msg, *skb;
253b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
254b1fa4dc4SChristophe Ricard
255b1fa4dc4SChristophe Ricard if (data_len != 4)
256b1fa4dc4SChristophe Ricard return -EPROTO;
257b1fa4dc4SChristophe Ricard
258b1fa4dc4SChristophe Ricard r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
259b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE,
260b1fa4dc4SChristophe Ricard data, data_len, &skb);
261b1fa4dc4SChristophe Ricard if (r)
262c7a551b2Swengjianfeng return r;
263b1fa4dc4SChristophe Ricard
264b1fa4dc4SChristophe Ricard msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
265b1fa4dc4SChristophe Ricard HCI_DM_VDC_MEASUREMENT_VALUE, skb->len);
266b1fa4dc4SChristophe Ricard if (!msg) {
267b1fa4dc4SChristophe Ricard r = -ENOMEM;
268b1fa4dc4SChristophe Ricard goto free_skb;
269b1fa4dc4SChristophe Ricard }
270b1fa4dc4SChristophe Ricard
271b1fa4dc4SChristophe Ricard if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
272b1fa4dc4SChristophe Ricard kfree_skb(msg);
273b1fa4dc4SChristophe Ricard r = -ENOBUFS;
274b1fa4dc4SChristophe Ricard goto free_skb;
275b1fa4dc4SChristophe Ricard }
276b1fa4dc4SChristophe Ricard
277b1fa4dc4SChristophe Ricard r = nfc_vendor_cmd_reply(msg);
278b1fa4dc4SChristophe Ricard
279b1fa4dc4SChristophe Ricard free_skb:
280b1fa4dc4SChristophe Ricard kfree_skb(skb);
281b1fa4dc4SChristophe Ricard return r;
282b1fa4dc4SChristophe Ricard }
283b1fa4dc4SChristophe Ricard
st_nci_hci_dm_vdc_value_comparison(struct nfc_dev * dev,void * data,size_t data_len)284b1fa4dc4SChristophe Ricard static int st_nci_hci_dm_vdc_value_comparison(struct nfc_dev *dev, void *data,
285b1fa4dc4SChristophe Ricard size_t data_len)
286b1fa4dc4SChristophe Ricard {
287b1fa4dc4SChristophe Ricard int r;
288b1fa4dc4SChristophe Ricard struct sk_buff *msg, *skb;
289b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
290b1fa4dc4SChristophe Ricard
291b1fa4dc4SChristophe Ricard if (data_len != 2)
292b1fa4dc4SChristophe Ricard return -EPROTO;
293b1fa4dc4SChristophe Ricard
294b1fa4dc4SChristophe Ricard r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
295b1fa4dc4SChristophe Ricard ST_NCI_HCI_DM_VDC_VALUE_COMPARISON,
296b1fa4dc4SChristophe Ricard data, data_len, &skb);
297b1fa4dc4SChristophe Ricard if (r)
298c7a551b2Swengjianfeng return r;
299b1fa4dc4SChristophe Ricard
300b1fa4dc4SChristophe Ricard msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
301b1fa4dc4SChristophe Ricard HCI_DM_VDC_VALUE_COMPARISON, skb->len);
302b1fa4dc4SChristophe Ricard if (!msg) {
303b1fa4dc4SChristophe Ricard r = -ENOMEM;
304b1fa4dc4SChristophe Ricard goto free_skb;
305b1fa4dc4SChristophe Ricard }
306b1fa4dc4SChristophe Ricard
307b1fa4dc4SChristophe Ricard if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
308b1fa4dc4SChristophe Ricard kfree_skb(msg);
309b1fa4dc4SChristophe Ricard r = -ENOBUFS;
310b1fa4dc4SChristophe Ricard goto free_skb;
311b1fa4dc4SChristophe Ricard }
312b1fa4dc4SChristophe Ricard
313b1fa4dc4SChristophe Ricard r = nfc_vendor_cmd_reply(msg);
314b1fa4dc4SChristophe Ricard
315b1fa4dc4SChristophe Ricard free_skb:
316b1fa4dc4SChristophe Ricard kfree_skb(skb);
317b1fa4dc4SChristophe Ricard return r;
318b1fa4dc4SChristophe Ricard }
319b1fa4dc4SChristophe Ricard
st_nci_loopback(struct nfc_dev * dev,void * data,size_t data_len)3203aacd7feSChristophe Ricard static int st_nci_loopback(struct nfc_dev *dev, void *data,
321b1fa4dc4SChristophe Ricard size_t data_len)
322b1fa4dc4SChristophe Ricard {
323b1fa4dc4SChristophe Ricard int r;
3243aacd7feSChristophe Ricard struct sk_buff *msg, *skb;
325b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
326b1fa4dc4SChristophe Ricard
327b1fa4dc4SChristophe Ricard if (data_len <= 0)
328b1fa4dc4SChristophe Ricard return -EPROTO;
329b1fa4dc4SChristophe Ricard
3303aacd7feSChristophe Ricard r = nci_nfcc_loopback(ndev, data, data_len, &skb);
3313aacd7feSChristophe Ricard if (r < 0)
3323aacd7feSChristophe Ricard return r;
333b1fa4dc4SChristophe Ricard
3343aacd7feSChristophe Ricard msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
3353aacd7feSChristophe Ricard LOOPBACK, skb->len);
336b1fa4dc4SChristophe Ricard if (!msg) {
337b1fa4dc4SChristophe Ricard r = -ENOMEM;
338b1fa4dc4SChristophe Ricard goto free_skb;
339b1fa4dc4SChristophe Ricard }
340b1fa4dc4SChristophe Ricard
3413aacd7feSChristophe Ricard if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
342b1fa4dc4SChristophe Ricard kfree_skb(msg);
343b1fa4dc4SChristophe Ricard r = -ENOBUFS;
344b1fa4dc4SChristophe Ricard goto free_skb;
345b1fa4dc4SChristophe Ricard }
346b1fa4dc4SChristophe Ricard
347b1fa4dc4SChristophe Ricard r = nfc_vendor_cmd_reply(msg);
348b1fa4dc4SChristophe Ricard free_skb:
3493aacd7feSChristophe Ricard kfree_skb(skb);
350b1fa4dc4SChristophe Ricard return r;
351b1fa4dc4SChristophe Ricard }
352b1fa4dc4SChristophe Ricard
st_nci_manufacturer_specific(struct nfc_dev * dev,void * data,size_t data_len)353b1fa4dc4SChristophe Ricard static int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data,
354b1fa4dc4SChristophe Ricard size_t data_len)
355b1fa4dc4SChristophe Ricard {
356b1fa4dc4SChristophe Ricard struct sk_buff *msg;
357b1fa4dc4SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(dev);
358b1fa4dc4SChristophe Ricard
359b1fa4dc4SChristophe Ricard msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI,
360b1fa4dc4SChristophe Ricard MANUFACTURER_SPECIFIC,
361b1fa4dc4SChristophe Ricard sizeof(ndev->manufact_specific_info));
362b1fa4dc4SChristophe Ricard if (!msg)
363b1fa4dc4SChristophe Ricard return -ENOMEM;
364b1fa4dc4SChristophe Ricard
365b1fa4dc4SChristophe Ricard if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info),
366b1fa4dc4SChristophe Ricard &ndev->manufact_specific_info)) {
367b1fa4dc4SChristophe Ricard kfree_skb(msg);
368b1fa4dc4SChristophe Ricard return -ENOBUFS;
369b1fa4dc4SChristophe Ricard }
370b1fa4dc4SChristophe Ricard
371b1fa4dc4SChristophe Ricard return nfc_vendor_cmd_reply(msg);
372b1fa4dc4SChristophe Ricard }
373b1fa4dc4SChristophe Ricard
37415944ad2SKrzysztof Kozlowski static const struct nfc_vendor_cmd st_nci_vendor_cmds[] = {
375b1fa4dc4SChristophe Ricard {
376b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
377b1fa4dc4SChristophe Ricard .subcmd = FACTORY_MODE,
378b1fa4dc4SChristophe Ricard .doit = st_nci_factory_mode,
379b1fa4dc4SChristophe Ricard },
380b1fa4dc4SChristophe Ricard {
381b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
382b1fa4dc4SChristophe Ricard .subcmd = HCI_CLEAR_ALL_PIPES,
383b1fa4dc4SChristophe Ricard .doit = st_nci_hci_clear_all_pipes,
384b1fa4dc4SChristophe Ricard },
385b1fa4dc4SChristophe Ricard {
386b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
387b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_PUT_DATA,
388b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_put_data,
389b1fa4dc4SChristophe Ricard },
390b1fa4dc4SChristophe Ricard {
391b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
392b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_UPDATE_AID,
393b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_update_aid,
394b1fa4dc4SChristophe Ricard },
395b1fa4dc4SChristophe Ricard {
396b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
397b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_GET_INFO,
398b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_get_info,
399b1fa4dc4SChristophe Ricard },
400b1fa4dc4SChristophe Ricard {
401b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
402b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_GET_DATA,
403b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_get_data,
404b1fa4dc4SChristophe Ricard },
405b1fa4dc4SChristophe Ricard {
406b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
407b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_DIRECT_LOAD,
408b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_direct_load,
409b1fa4dc4SChristophe Ricard },
410b1fa4dc4SChristophe Ricard {
411b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
412b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_RESET,
413b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_reset,
414b1fa4dc4SChristophe Ricard },
415b1fa4dc4SChristophe Ricard {
416b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
417b1fa4dc4SChristophe Ricard .subcmd = HCI_GET_PARAM,
418b1fa4dc4SChristophe Ricard .doit = st_nci_hci_get_param,
419b1fa4dc4SChristophe Ricard },
420b1fa4dc4SChristophe Ricard {
421b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
422b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_FIELD_GENERATOR,
423b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_field_generator,
424b1fa4dc4SChristophe Ricard },
425b1fa4dc4SChristophe Ricard {
426b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
427b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_FWUPD_START,
428b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_fwupd_start,
429b1fa4dc4SChristophe Ricard },
430b1fa4dc4SChristophe Ricard {
431b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
432b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_FWUPD_END,
433b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_fwupd_end,
434b1fa4dc4SChristophe Ricard },
435b1fa4dc4SChristophe Ricard {
436b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
4373aacd7feSChristophe Ricard .subcmd = LOOPBACK,
4383aacd7feSChristophe Ricard .doit = st_nci_loopback,
439b1fa4dc4SChristophe Ricard },
440b1fa4dc4SChristophe Ricard {
441b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
442b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_VDC_MEASUREMENT_VALUE,
443b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_vdc_measurement_value,
444b1fa4dc4SChristophe Ricard },
445b1fa4dc4SChristophe Ricard {
446b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
447b1fa4dc4SChristophe Ricard .subcmd = HCI_DM_VDC_VALUE_COMPARISON,
448b1fa4dc4SChristophe Ricard .doit = st_nci_hci_dm_vdc_value_comparison,
449b1fa4dc4SChristophe Ricard },
450b1fa4dc4SChristophe Ricard {
451b1fa4dc4SChristophe Ricard .vendor_id = ST_NCI_VENDOR_OUI,
452b1fa4dc4SChristophe Ricard .subcmd = MANUFACTURER_SPECIFIC,
453b1fa4dc4SChristophe Ricard .doit = st_nci_manufacturer_specific,
454b1fa4dc4SChristophe Ricard },
455b1fa4dc4SChristophe Ricard };
456b1fa4dc4SChristophe Ricard
st_nci_vendor_cmds_init(struct nci_dev * ndev)457b1fa4dc4SChristophe Ricard int st_nci_vendor_cmds_init(struct nci_dev *ndev)
458b1fa4dc4SChristophe Ricard {
459*f7bfd110SJakub Kicinski return nci_set_vendor_cmds(ndev, st_nci_vendor_cmds,
460b1fa4dc4SChristophe Ricard sizeof(st_nci_vendor_cmds));
461b1fa4dc4SChristophe Ricard }
462b1fa4dc4SChristophe Ricard EXPORT_SYMBOL(st_nci_vendor_cmds_init);
463