linux/drivers/nfc/st21nfca/vendor_cmds.c
<<
>>
Prefs
   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 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_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds,
 362                                   sizeof(st21nfca_vendor_cmds));
 363}
 364EXPORT_SYMBOL(st21nfca_vendor_cmds_init);
 365