vendor_cmds.c (8210B)
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, &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 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, data, 74 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, data, 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 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 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 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 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, data, 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, 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 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, 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 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 data, 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(&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); 292free_skb: 293 kfree_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(&info->vendor_info.req_completion); 361 return nfc_hci_set_vendor_cmds(hdev, st21nfca_vendor_cmds, 362 sizeof(st21nfca_vendor_cmds)); 363} 364EXPORT_SYMBOL(st21nfca_vendor_cmds_init);