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 static int st_nci_loopback(struct nfc_dev *dev, void *data, 337 size_t data_len) 338 { 339 int r; 340 struct sk_buff *msg, *skb; 341 struct nci_dev *ndev = nfc_get_drvdata(dev); 342 343 if (data_len <= 0) 344 return -EPROTO; 345 346 r = nci_nfcc_loopback(ndev, data, data_len, &skb); 347 if (r < 0) 348 return r; 349 350 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, 351 LOOPBACK, skb->len); 352 if (!msg) { 353 r = -ENOMEM; 354 goto free_skb; 355 } 356 357 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { 358 kfree_skb(msg); 359 r = -ENOBUFS; 360 goto free_skb; 361 } 362 363 r = nfc_vendor_cmd_reply(msg); 364 free_skb: 365 kfree_skb(skb); 366 return r; 367 } 368 369 static int st_nci_manufacturer_specific(struct nfc_dev *dev, void *data, 370 size_t data_len) 371 { 372 struct sk_buff *msg; 373 struct nci_dev *ndev = nfc_get_drvdata(dev); 374 375 msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST_NCI_VENDOR_OUI, 376 MANUFACTURER_SPECIFIC, 377 sizeof(ndev->manufact_specific_info)); 378 if (!msg) 379 return -ENOMEM; 380 381 if (nla_put(msg, NFC_ATTR_VENDOR_DATA, sizeof(ndev->manufact_specific_info), 382 &ndev->manufact_specific_info)) { 383 kfree_skb(msg); 384 return -ENOBUFS; 385 } 386 387 return nfc_vendor_cmd_reply(msg); 388 } 389 390 static struct nfc_vendor_cmd st_nci_vendor_cmds[] = { 391 { 392 .vendor_id = ST_NCI_VENDOR_OUI, 393 .subcmd = FACTORY_MODE, 394 .doit = st_nci_factory_mode, 395 }, 396 { 397 .vendor_id = ST_NCI_VENDOR_OUI, 398 .subcmd = HCI_CLEAR_ALL_PIPES, 399 .doit = st_nci_hci_clear_all_pipes, 400 }, 401 { 402 .vendor_id = ST_NCI_VENDOR_OUI, 403 .subcmd = HCI_DM_PUT_DATA, 404 .doit = st_nci_hci_dm_put_data, 405 }, 406 { 407 .vendor_id = ST_NCI_VENDOR_OUI, 408 .subcmd = HCI_DM_UPDATE_AID, 409 .doit = st_nci_hci_dm_update_aid, 410 }, 411 { 412 .vendor_id = ST_NCI_VENDOR_OUI, 413 .subcmd = HCI_DM_GET_INFO, 414 .doit = st_nci_hci_dm_get_info, 415 }, 416 { 417 .vendor_id = ST_NCI_VENDOR_OUI, 418 .subcmd = HCI_DM_GET_DATA, 419 .doit = st_nci_hci_dm_get_data, 420 }, 421 { 422 .vendor_id = ST_NCI_VENDOR_OUI, 423 .subcmd = HCI_DM_DIRECT_LOAD, 424 .doit = st_nci_hci_dm_direct_load, 425 }, 426 { 427 .vendor_id = ST_NCI_VENDOR_OUI, 428 .subcmd = HCI_DM_RESET, 429 .doit = st_nci_hci_dm_reset, 430 }, 431 { 432 .vendor_id = ST_NCI_VENDOR_OUI, 433 .subcmd = HCI_GET_PARAM, 434 .doit = st_nci_hci_get_param, 435 }, 436 { 437 .vendor_id = ST_NCI_VENDOR_OUI, 438 .subcmd = HCI_DM_FIELD_GENERATOR, 439 .doit = st_nci_hci_dm_field_generator, 440 }, 441 { 442 .vendor_id = ST_NCI_VENDOR_OUI, 443 .subcmd = HCI_DM_FWUPD_START, 444 .doit = st_nci_hci_dm_fwupd_start, 445 }, 446 { 447 .vendor_id = ST_NCI_VENDOR_OUI, 448 .subcmd = HCI_DM_FWUPD_END, 449 .doit = st_nci_hci_dm_fwupd_end, 450 }, 451 { 452 .vendor_id = ST_NCI_VENDOR_OUI, 453 .subcmd = LOOPBACK, 454 .doit = st_nci_loopback, 455 }, 456 { 457 .vendor_id = ST_NCI_VENDOR_OUI, 458 .subcmd = HCI_DM_VDC_MEASUREMENT_VALUE, 459 .doit = st_nci_hci_dm_vdc_measurement_value, 460 }, 461 { 462 .vendor_id = ST_NCI_VENDOR_OUI, 463 .subcmd = HCI_DM_VDC_VALUE_COMPARISON, 464 .doit = st_nci_hci_dm_vdc_value_comparison, 465 }, 466 { 467 .vendor_id = ST_NCI_VENDOR_OUI, 468 .subcmd = MANUFACTURER_SPECIFIC, 469 .doit = st_nci_manufacturer_specific, 470 }, 471 }; 472 473 int st_nci_vendor_cmds_init(struct nci_dev *ndev) 474 { 475 return nfc_set_vendor_cmds(ndev->nfc_dev, st_nci_vendor_cmds, 476 sizeof(st_nci_vendor_cmds)); 477 } 478 EXPORT_SYMBOL(st_nci_vendor_cmds_init); 479