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