1 /* 2 * Proprietary commands extension for STMicroelectronics NFC NCI 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 <linux/delay.h> 23 #include <net/nfc/nci_core.h> 24 25 #include "st-nci.h" 26 27 #define ST_NCI_HCI_DM_GETDATA 0x10 28 #define ST_NCI_HCI_DM_PUTDATA 0x11 29 #define ST_NCI_HCI_DM_LOAD 0x12 30 #define ST_NCI_HCI_DM_GETINFO 0x13 31 #define ST_NCI_HCI_DM_FWUPD_START 0x14 32 #define ST_NCI_HCI_DM_FWUPD_STOP 0x15 33 #define ST_NCI_HCI_DM_UPDATE_AID 0x20 34 #define ST_NCI_HCI_DM_RESET 0x3e 35 36 #define ST_NCI_HCI_DM_FIELD_GENERATOR 0x32 37 #define ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE 0x33 38 #define ST_NCI_HCI_DM_VDC_VALUE_COMPARISON 0x34 39 40 #define ST_NCI_FACTORY_MODE_ON 1 41 #define ST_NCI_FACTORY_MODE_OFF 0 42 43 #define ST_NCI_EVT_POST_DATA 0x02 44 45 struct get_param_data { 46 u8 gate; 47 u8 data; 48 } __packed; 49 50 static int st_nci_factory_mode(struct nfc_dev *dev, void *data, 51 size_t data_len) 52 { 53 struct nci_dev *ndev = nfc_get_drvdata(dev); 54 struct st_nci_info *info = nci_get_drvdata(ndev); 55 56 if (data_len != 1) 57 return -EINVAL; 58 59 pr_debug("factory mode: %x\n", ((u8 *)data)[0]); 60 61 switch (((u8 *)data)[0]) { 62 case ST_NCI_FACTORY_MODE_ON: 63 test_and_set_bit(ST_NCI_FACTORY_MODE, &info->flags); 64 break; 65 case ST_NCI_FACTORY_MODE_OFF: 66 clear_bit(ST_NCI_FACTORY_MODE, &info->flags); 67 break; 68 default: 69 return -EINVAL; 70 } 71 72 return 0; 73 } 74 75 static int st_nci_hci_clear_all_pipes(struct nfc_dev *dev, void *data, 76 size_t data_len) 77 { 78 struct nci_dev *ndev = nfc_get_drvdata(dev); 79 80 return nci_hci_clear_all_pipes(ndev); 81 } 82 83 static int st_nci_hci_dm_put_data(struct nfc_dev *dev, void *data, 84 size_t data_len) 85 { 86 struct nci_dev *ndev = nfc_get_drvdata(dev); 87 88 return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 89 ST_NCI_HCI_DM_PUTDATA, data, 90 data_len, NULL); 91 } 92 93 static int st_nci_hci_dm_update_aid(struct nfc_dev *dev, void *data, 94 size_t data_len) 95 { 96 struct nci_dev *ndev = nfc_get_drvdata(dev); 97 98 return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 99 ST_NCI_HCI_DM_UPDATE_AID, data, data_len, NULL); 100 } 101 102 static int st_nci_hci_dm_get_info(struct nfc_dev *dev, void *data, 103 size_t data_len) 104 { 105 int r; 106 struct sk_buff *msg, *skb; 107 struct nci_dev *ndev = nfc_get_drvdata(dev); 108 109 r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETINFO, 110 data, data_len, &skb); 111 if (r) 112 goto exit; 113 114 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, 115 HCI_DM_GET_INFO, skb->len); 116 if (!msg) { 117 r = -ENOMEM; 118 goto free_skb; 119 } 120 121 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { 122 kfree_skb(msg); 123 r = -ENOBUFS; 124 goto free_skb; 125 } 126 127 r = nfc_vendor_cmd_reply(msg); 128 129 free_skb: 130 kfree_skb(skb); 131 exit: 132 return r; 133 } 134 135 static int st_nci_hci_dm_get_data(struct nfc_dev *dev, void *data, 136 size_t data_len) 137 { 138 int r; 139 struct sk_buff *msg, *skb; 140 struct nci_dev *ndev = nfc_get_drvdata(dev); 141 142 r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, ST_NCI_HCI_DM_GETDATA, 143 data, data_len, &skb); 144 if (r) 145 goto exit; 146 147 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, 148 HCI_DM_GET_DATA, skb->len); 149 if (!msg) { 150 r = -ENOMEM; 151 goto free_skb; 152 } 153 154 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { 155 kfree_skb(msg); 156 r = -ENOBUFS; 157 goto free_skb; 158 } 159 160 r = nfc_vendor_cmd_reply(msg); 161 162 free_skb: 163 kfree_skb(skb); 164 exit: 165 return r; 166 } 167 168 static int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data, 169 size_t data_len) 170 { 171 int r; 172 struct nci_dev *ndev = nfc_get_drvdata(dev); 173 174 dev->fw_download_in_progress = true; 175 r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 176 ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL); 177 if (r) 178 dev->fw_download_in_progress = false; 179 180 return r; 181 } 182 183 static int st_nci_hci_dm_fwupd_end(struct nfc_dev *dev, void *data, 184 size_t data_len) 185 { 186 struct nci_dev *ndev = nfc_get_drvdata(dev); 187 188 return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 189 ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL); 190 } 191 192 static int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data, 193 size_t data_len) 194 { 195 struct nci_dev *ndev = nfc_get_drvdata(dev); 196 197 if (dev->fw_download_in_progress) { 198 dev->fw_download_in_progress = false; 199 return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 200 ST_NCI_HCI_DM_LOAD, data, data_len, NULL); 201 } 202 return -EPROTO; 203 } 204 205 static int st_nci_hci_dm_reset(struct nfc_dev *dev, void *data, 206 size_t data_len) 207 { 208 struct nci_dev *ndev = nfc_get_drvdata(dev); 209 210 nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 211 ST_NCI_HCI_DM_RESET, data, data_len, NULL); 212 msleep(200); 213 214 return 0; 215 } 216 217 static int st_nci_hci_get_param(struct nfc_dev *dev, void *data, 218 size_t data_len) 219 { 220 int r; 221 struct sk_buff *msg, *skb; 222 struct nci_dev *ndev = nfc_get_drvdata(dev); 223 struct get_param_data *param = (struct get_param_data *)data; 224 225 if (data_len < sizeof(struct get_param_data)) 226 return -EPROTO; 227 228 r = nci_hci_get_param(ndev, param->gate, param->data, &skb); 229 if (r) 230 goto exit; 231 232 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, 233 HCI_GET_PARAM, skb->len); 234 if (!msg) { 235 r = -ENOMEM; 236 goto free_skb; 237 } 238 239 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { 240 kfree_skb(msg); 241 r = -ENOBUFS; 242 goto free_skb; 243 } 244 245 r = nfc_vendor_cmd_reply(msg); 246 247 free_skb: 248 kfree_skb(skb); 249 exit: 250 return r; 251 } 252 253 static int st_nci_hci_dm_field_generator(struct nfc_dev *dev, void *data, 254 size_t data_len) 255 { 256 struct nci_dev *ndev = nfc_get_drvdata(dev); 257 258 return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 259 ST_NCI_HCI_DM_FIELD_GENERATOR, data, data_len, NULL); 260 } 261 262 static int st_nci_hci_dm_vdc_measurement_value(struct nfc_dev *dev, void *data, 263 size_t data_len) 264 { 265 int r; 266 struct sk_buff *msg, *skb; 267 struct nci_dev *ndev = nfc_get_drvdata(dev); 268 269 if (data_len != 4) 270 return -EPROTO; 271 272 r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 273 ST_NCI_HCI_DM_VDC_MEASUREMENT_VALUE, 274 data, data_len, &skb); 275 if (r) 276 goto exit; 277 278 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, 279 HCI_DM_VDC_MEASUREMENT_VALUE, skb->len); 280 if (!msg) { 281 r = -ENOMEM; 282 goto free_skb; 283 } 284 285 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { 286 kfree_skb(msg); 287 r = -ENOBUFS; 288 goto free_skb; 289 } 290 291 r = nfc_vendor_cmd_reply(msg); 292 293 free_skb: 294 kfree_skb(skb); 295 exit: 296 return r; 297 } 298 299 static int st_nci_hci_dm_vdc_value_comparison(struct nfc_dev *dev, void *data, 300 size_t data_len) 301 { 302 int r; 303 struct sk_buff *msg, *skb; 304 struct nci_dev *ndev = nfc_get_drvdata(dev); 305 306 if (data_len != 2) 307 return -EPROTO; 308 309 r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, 310 ST_NCI_HCI_DM_VDC_VALUE_COMPARISON, 311 data, data_len, &skb); 312 if (r) 313 goto exit; 314 315 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, 316 HCI_DM_VDC_VALUE_COMPARISON, skb->len); 317 if (!msg) { 318 r = -ENOMEM; 319 goto free_skb; 320 } 321 322 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { 323 kfree_skb(msg); 324 r = -ENOBUFS; 325 goto free_skb; 326 } 327 328 r = nfc_vendor_cmd_reply(msg); 329 330 free_skb: 331 kfree_skb(skb); 332 exit: 333 return r; 334 } 335 336 void st_nci_hci_loopback_event_received(struct nci_dev *ndev, u8 event, 337 struct sk_buff *skb) 338 { 339 struct st_nci_info *info = nci_get_drvdata(ndev); 340 341 switch (event) { 342 case ST_NCI_EVT_POST_DATA: 343 info->vendor_info.rx_skb = skb; 344 break; 345 default: 346 nfc_err(&ndev->nfc_dev->dev, "Unexpected event on loopback gate\n"); 347 } 348 complete(&info->vendor_info.req_completion); 349 } 350 EXPORT_SYMBOL(st_nci_hci_loopback_event_received); 351 352 static int st_nci_hci_loopback(struct nfc_dev *dev, void *data, 353 size_t data_len) 354 { 355 int r; 356 struct sk_buff *msg; 357 struct nci_dev *ndev = nfc_get_drvdata(dev); 358 struct st_nci_info *info = nci_get_drvdata(ndev); 359 360 if (data_len <= 0) 361 return -EPROTO; 362 363 reinit_completion(&info->vendor_info.req_completion); 364 info->vendor_info.rx_skb = NULL; 365 366 r = nci_hci_send_event(ndev, NCI_HCI_LOOPBACK_GATE, 367 ST_NCI_EVT_POST_DATA, data, data_len); 368 if (r != data_len) { 369 r = -EPROTO; 370 goto exit; 371 } 372 373 wait_for_completion_interruptible(&info->vendor_info.req_completion); 374 375 if (!info->vendor_info.rx_skb || 376 info->vendor_info.rx_skb->len != data_len) { 377 r = -EPROTO; 378 goto exit; 379 } 380 381 msg = nfc_vendor_cmd_alloc_reply_skb(ndev->nfc_dev, 382 ST_NCI_VENDOR_OUI, 383 HCI_LOOPBACK, 384 info->vendor_info.rx_skb->len); 385 if (!msg) { 386 r = -ENOMEM; 387 goto free_skb; 388 } 389 390 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len, 391 info->vendor_info.rx_skb->data)) { 392 kfree_skb(msg); 393 r = -ENOBUFS; 394 goto free_skb; 395 } 396 397 r = nfc_vendor_cmd_reply(msg); 398 free_skb: 399 kfree_skb(info->vendor_info.rx_skb); 400 exit: 401 return r; 402 } 403 404 static int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data, 405 size_t data_len) 406 { 407 struct sk_buff *msg; 408 struct nci_dev *ndev = nfc_get_drvdata(dev); 409 410 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, 411 MANUFACTURER_SPECIFIC, 412 sizeof(ndev->manufact_specific_info)); 413 if (!msg) 414 return -ENOMEM; 415 416 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info), 417 &ndev->manufact_specific_info)) { 418 kfree_skb(msg); 419 return -ENOBUFS; 420 } 421 422 return nfc_vendor_cmd_reply(msg); 423 } 424 425 static struct nfc_vendor_cmd st_nci_vendor_cmds[] = { 426 { 427 .vendor_id = ST_NCI_VENDOR_OUI, 428 .subcmd = FACTORY_MODE, 429 .doit = st_nci_factory_mode, 430 }, 431 { 432 .vendor_id = ST_NCI_VENDOR_OUI, 433 .subcmd = HCI_CLEAR_ALL_PIPES, 434 .doit = st_nci_hci_clear_all_pipes, 435 }, 436 { 437 .vendor_id = ST_NCI_VENDOR_OUI, 438 .subcmd = HCI_DM_PUT_DATA, 439 .doit = st_nci_hci_dm_put_data, 440 }, 441 { 442 .vendor_id = ST_NCI_VENDOR_OUI, 443 .subcmd = HCI_DM_UPDATE_AID, 444 .doit = st_nci_hci_dm_update_aid, 445 }, 446 { 447 .vendor_id = ST_NCI_VENDOR_OUI, 448 .subcmd = HCI_DM_GET_INFO, 449 .doit = st_nci_hci_dm_get_info, 450 }, 451 { 452 .vendor_id = ST_NCI_VENDOR_OUI, 453 .subcmd = HCI_DM_GET_DATA, 454 .doit = st_nci_hci_dm_get_data, 455 }, 456 { 457 .vendor_id = ST_NCI_VENDOR_OUI, 458 .subcmd = HCI_DM_DIRECT_LOAD, 459 .doit = st_nci_hci_dm_direct_load, 460 }, 461 { 462 .vendor_id = ST_NCI_VENDOR_OUI, 463 .subcmd = HCI_DM_RESET, 464 .doit = st_nci_hci_dm_reset, 465 }, 466 { 467 .vendor_id = ST_NCI_VENDOR_OUI, 468 .subcmd = HCI_GET_PARAM, 469 .doit = st_nci_hci_get_param, 470 }, 471 { 472 .vendor_id = ST_NCI_VENDOR_OUI, 473 .subcmd = HCI_DM_FIELD_GENERATOR, 474 .doit = st_nci_hci_dm_field_generator, 475 }, 476 { 477 .vendor_id = ST_NCI_VENDOR_OUI, 478 .subcmd = HCI_DM_FWUPD_START, 479 .doit = st_nci_hci_dm_fwupd_start, 480 }, 481 { 482 .vendor_id = ST_NCI_VENDOR_OUI, 483 .subcmd = HCI_DM_FWUPD_END, 484 .doit = st_nci_hci_dm_fwupd_end, 485 }, 486 { 487 .vendor_id = ST_NCI_VENDOR_OUI, 488 .subcmd = HCI_LOOPBACK, 489 .doit = st_nci_hci_loopback, 490 }, 491 { 492 .vendor_id = ST_NCI_VENDOR_OUI, 493 .subcmd = HCI_DM_VDC_MEASUREMENT_VALUE, 494 .doit = st_nci_hci_dm_vdc_measurement_value, 495 }, 496 { 497 .vendor_id = ST_NCI_VENDOR_OUI, 498 .subcmd = HCI_DM_VDC_VALUE_COMPARISON, 499 .doit = st_nci_hci_dm_vdc_value_comparison, 500 }, 501 { 502 .vendor_id = ST_NCI_VENDOR_OUI, 503 .subcmd = MANUFACTURER_SPECIFIC, 504 .doit = st_nci_manufacturer_specific, 505 }, 506 }; 507 508 int st_nci_vendor_cmds_init(struct nci_dev *ndev) 509 { 510 struct st_nci_info *info = nci_get_drvdata(ndev); 511 512 init_completion(&info->vendor_info.req_completion); 513 return nfc_set_vendor_cmds(ndev->nfc_dev, st_nci_vendor_cmds, 514 sizeof(st_nci_vendor_cmds)); 515 } 516 EXPORT_SYMBOL(st_nci_vendor_cmds_init); 517