linux/drivers/nfc/nfcwilink.c
<<
>>
Prefs
   1/*
   2 *  Texas Instrument's NFC Driver For Shared Transport.
   3 *
   4 *  NFC Driver acts as interface between NCI core and
   5 *  TI Shared Transport Layer.
   6 *
   7 *  Copyright (C) 2011 Texas Instruments, Inc.
   8 *
   9 *  Written by Ilan Elias <ilane@ti.com>
  10 *
  11 *  Acknowledgements:
  12 *  This file is based on btwilink.c, which was written
  13 *  by Raja Mani and Pavan Savoy.
  14 *
  15 *  This program is free software; you can redistribute it and/or modify
  16 *  it under the terms of the GNU General Public License version 2 as
  17 *  published by the Free Software Foundation.
  18 *
  19 *  This program is distributed in the hope that it will be useful,
  20 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  21 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22 *  GNU General Public License for more details.
  23 *
  24 *  You should have received a copy of the GNU General Public License
  25 *  along with this program; if not, write to the Free Software
  26 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  27 *
  28 */
  29#include <linux/platform_device.h>
  30#include <linux/module.h>
  31#include <linux/types.h>
  32#include <linux/firmware.h>
  33#include <linux/nfc.h>
  34#include <net/nfc/nci.h>
  35#include <net/nfc/nci_core.h>
  36#include <linux/ti_wilink_st.h>
  37
  38#define NFCWILINK_CHNL                  12
  39#define NFCWILINK_OPCODE                7
  40#define NFCWILINK_MAX_FRAME_SIZE        300
  41#define NFCWILINK_HDR_LEN               4
  42#define NFCWILINK_OFFSET_LEN_IN_HDR     1
  43#define NFCWILINK_LEN_SIZE              2
  44#define NFCWILINK_REGISTER_TIMEOUT      8000    /* 8 sec */
  45#define NFCWILINK_CMD_TIMEOUT           5000    /* 5 sec */
  46
  47#define BTS_FILE_NAME_MAX_SIZE          40
  48#define BTS_FILE_HDR_MAGIC              0x42535442
  49#define BTS_FILE_CMD_MAX_LEN            0xff
  50#define BTS_FILE_ACTION_TYPE_SEND_CMD   1
  51
  52#define NCI_VS_NFCC_INFO_CMD_GID        0x2f
  53#define NCI_VS_NFCC_INFO_CMD_OID        0x12
  54#define NCI_VS_NFCC_INFO_RSP_GID        0x4f
  55#define NCI_VS_NFCC_INFO_RSP_OID        0x12
  56
  57struct nfcwilink_hdr {
  58        __u8 chnl;
  59        __u8 opcode;
  60        __le16 len;
  61} __packed;
  62
  63struct nci_vs_nfcc_info_cmd {
  64        __u8 gid;
  65        __u8 oid;
  66        __u8 plen;
  67} __packed;
  68
  69struct nci_vs_nfcc_info_rsp {
  70        __u8 gid;
  71        __u8 oid;
  72        __u8 plen;
  73        __u8 status;
  74        __u8 hw_id;
  75        __u8 sw_ver_x;
  76        __u8 sw_ver_z;
  77        __u8 patch_id;
  78} __packed;
  79
  80struct bts_file_hdr {
  81        __le32 magic;
  82        __le32 ver;
  83        __u8 rfu[24];
  84        __u8 actions[0];
  85} __packed;
  86
  87struct bts_file_action {
  88        __le16 type;
  89        __le16 len;
  90        __u8 data[0];
  91} __packed;
  92
  93struct nfcwilink {
  94        struct platform_device          *pdev;
  95        struct nci_dev                  *ndev;
  96        unsigned long                   flags;
  97
  98        char                            st_register_cb_status;
  99        long                            (*st_write) (struct sk_buff *);
 100
 101        struct completion               completed;
 102
 103        struct nci_vs_nfcc_info_rsp     nfcc_info;
 104};
 105
 106/* NFCWILINK driver flags */
 107enum {
 108        NFCWILINK_RUNNING,
 109        NFCWILINK_FW_DOWNLOAD,
 110};
 111
 112static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb);
 113
 114static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
 115{
 116        struct sk_buff *skb;
 117
 118        skb = alloc_skb(len + NFCWILINK_HDR_LEN, how);
 119        if (skb)
 120                skb_reserve(skb, NFCWILINK_HDR_LEN);
 121
 122        return skb;
 123}
 124
 125static void nfcwilink_fw_download_receive(struct nfcwilink *drv,
 126                                                struct sk_buff *skb)
 127{
 128        struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data;
 129
 130        /* Detect NCI_VS_NFCC_INFO_RSP and store the result */
 131        if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) &&
 132                (rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) {
 133                memcpy(&drv->nfcc_info, rsp,
 134                        sizeof(struct nci_vs_nfcc_info_rsp));
 135        }
 136
 137        kfree_skb(skb);
 138
 139        complete(&drv->completed);
 140}
 141
 142static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
 143{
 144        struct nci_vs_nfcc_info_cmd *cmd;
 145        struct sk_buff *skb;
 146        unsigned long comp_ret;
 147        int rc;
 148
 149        nfc_dev_dbg(&drv->pdev->dev, "get_bts_file_name entry");
 150
 151        skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd),
 152                                        GFP_KERNEL);
 153        if (!skb) {
 154                nfc_dev_err(&drv->pdev->dev,
 155                                "no memory for nci_vs_nfcc_info_cmd");
 156                return -ENOMEM;
 157        }
 158
 159        cmd = (struct nci_vs_nfcc_info_cmd *)
 160                        skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd));
 161        cmd->gid = NCI_VS_NFCC_INFO_CMD_GID;
 162        cmd->oid = NCI_VS_NFCC_INFO_CMD_OID;
 163        cmd->plen = 0;
 164
 165        drv->nfcc_info.plen = 0;
 166
 167        rc = nfcwilink_send(drv->ndev, skb);
 168        if (rc)
 169                return rc;
 170
 171        comp_ret = wait_for_completion_timeout(&drv->completed,
 172                                msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
 173        nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
 174                        comp_ret);
 175        if (comp_ret == 0) {
 176                nfc_dev_err(&drv->pdev->dev,
 177                                "timeout on wait_for_completion_timeout");
 178                return -ETIMEDOUT;
 179        }
 180
 181        nfc_dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d",
 182                        drv->nfcc_info.plen,
 183                        drv->nfcc_info.status);
 184
 185        if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) {
 186                nfc_dev_err(&drv->pdev->dev,
 187                                "invalid nci_vs_nfcc_info_rsp");
 188                return -EINVAL;
 189        }
 190
 191        snprintf(file_name, BTS_FILE_NAME_MAX_SIZE,
 192                        "TINfcInit_%d.%d.%d.%d.bts",
 193                        drv->nfcc_info.hw_id,
 194                        drv->nfcc_info.sw_ver_x,
 195                        drv->nfcc_info.sw_ver_z,
 196                        drv->nfcc_info.patch_id);
 197
 198        nfc_dev_info(&drv->pdev->dev, "nfcwilink FW file name: %s", file_name);
 199
 200        return 0;
 201}
 202
 203static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
 204{
 205        struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data;
 206        struct sk_buff *skb;
 207        unsigned long comp_ret;
 208        int rc;
 209
 210        nfc_dev_dbg(&drv->pdev->dev, "send_bts_cmd entry");
 211
 212        /* verify valid cmd for the NFC channel */
 213        if ((len <= sizeof(struct nfcwilink_hdr)) ||
 214                (len > BTS_FILE_CMD_MAX_LEN) ||
 215                (hdr->chnl != NFCWILINK_CHNL) ||
 216                (hdr->opcode != NFCWILINK_OPCODE)) {
 217                nfc_dev_err(&drv->pdev->dev,
 218                        "ignoring invalid bts cmd, len %d, chnl %d, opcode %d",
 219                        len, hdr->chnl, hdr->opcode);
 220                return 0;
 221        }
 222
 223        /* remove the ST header */
 224        len -= sizeof(struct nfcwilink_hdr);
 225        data += sizeof(struct nfcwilink_hdr);
 226
 227        skb = nfcwilink_skb_alloc(len, GFP_KERNEL);
 228        if (!skb) {
 229                nfc_dev_err(&drv->pdev->dev, "no memory for bts cmd");
 230                return -ENOMEM;
 231        }
 232
 233        memcpy(skb_put(skb, len), data, len);
 234
 235        rc = nfcwilink_send(drv->ndev, skb);
 236        if (rc)
 237                return rc;
 238
 239        comp_ret = wait_for_completion_timeout(&drv->completed,
 240                                msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
 241        nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
 242                        comp_ret);
 243        if (comp_ret == 0) {
 244                nfc_dev_err(&drv->pdev->dev,
 245                                "timeout on wait_for_completion_timeout");
 246                return -ETIMEDOUT;
 247        }
 248
 249        return 0;
 250}
 251
 252static int nfcwilink_download_fw(struct nfcwilink *drv)
 253{
 254        unsigned char file_name[BTS_FILE_NAME_MAX_SIZE];
 255        const struct firmware *fw;
 256        __u16 action_type, action_len;
 257        __u8 *ptr;
 258        int len, rc;
 259
 260        nfc_dev_dbg(&drv->pdev->dev, "download_fw entry");
 261
 262        set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
 263
 264        rc = nfcwilink_get_bts_file_name(drv, file_name);
 265        if (rc)
 266                goto exit;
 267
 268        rc = request_firmware(&fw, file_name, &drv->pdev->dev);
 269        if (rc) {
 270                nfc_dev_err(&drv->pdev->dev, "request_firmware failed %d", rc);
 271
 272                /* if the file is not found, don't exit with failure */
 273                if (rc == -ENOENT)
 274                        rc = 0;
 275
 276                goto exit;
 277        }
 278
 279        len = fw->size;
 280        ptr = (__u8 *)fw->data;
 281
 282        if ((len == 0) || (ptr == NULL)) {
 283                nfc_dev_dbg(&drv->pdev->dev,
 284                                "request_firmware returned size %d", len);
 285                goto release_fw;
 286        }
 287
 288        if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) !=
 289                        BTS_FILE_HDR_MAGIC) {
 290                nfc_dev_err(&drv->pdev->dev, "wrong bts magic number");
 291                rc = -EINVAL;
 292                goto release_fw;
 293        }
 294
 295        /* remove the BTS header */
 296        len -= sizeof(struct bts_file_hdr);
 297        ptr += sizeof(struct bts_file_hdr);
 298
 299        while (len > 0) {
 300                action_type =
 301                        __le16_to_cpu(((struct bts_file_action *)ptr)->type);
 302                action_len =
 303                        __le16_to_cpu(((struct bts_file_action *)ptr)->len);
 304
 305                nfc_dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d",
 306                                action_type, action_len);
 307
 308                switch (action_type) {
 309                case BTS_FILE_ACTION_TYPE_SEND_CMD:
 310                        rc = nfcwilink_send_bts_cmd(drv,
 311                                        ((struct bts_file_action *)ptr)->data,
 312                                        action_len);
 313                        if (rc)
 314                                goto release_fw;
 315                        break;
 316                }
 317
 318                /* advance to the next action */
 319                len -= (sizeof(struct bts_file_action) + action_len);
 320                ptr += (sizeof(struct bts_file_action) + action_len);
 321        }
 322
 323release_fw:
 324        release_firmware(fw);
 325
 326exit:
 327        clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
 328        return rc;
 329}
 330
 331/* Called by ST when registration is complete */
 332static void nfcwilink_register_complete(void *priv_data, char data)
 333{
 334        struct nfcwilink *drv = priv_data;
 335
 336        nfc_dev_dbg(&drv->pdev->dev, "register_complete entry");
 337
 338        /* store ST registration status */
 339        drv->st_register_cb_status = data;
 340
 341        /* complete the wait in nfc_st_open() */
 342        complete(&drv->completed);
 343}
 344
 345/* Called by ST when receive data is available */
 346static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
 347{
 348        struct nfcwilink *drv = priv_data;
 349        int rc;
 350
 351        if (!skb)
 352                return -EFAULT;
 353
 354        if (!drv) {
 355                kfree_skb(skb);
 356                return -EFAULT;
 357        }
 358
 359        nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
 360
 361        /* strip the ST header
 362        (apart for the chnl byte, which is not received in the hdr) */
 363        skb_pull(skb, (NFCWILINK_HDR_LEN-1));
 364
 365        if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) {
 366                nfcwilink_fw_download_receive(drv, skb);
 367                return 0;
 368        }
 369
 370        /* Forward skb to NCI core layer */
 371        rc = nci_recv_frame(drv->ndev, skb);
 372        if (rc < 0) {
 373                nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc);
 374                return rc;
 375        }
 376
 377        return 0;
 378}
 379
 380/* protocol structure registered with ST */
 381static struct st_proto_s nfcwilink_proto = {
 382        .chnl_id = NFCWILINK_CHNL,
 383        .max_frame_size = NFCWILINK_MAX_FRAME_SIZE,
 384        .hdr_len = (NFCWILINK_HDR_LEN-1),       /* not including chnl byte */
 385        .offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR,
 386        .len_size = NFCWILINK_LEN_SIZE,
 387        .reserve = 0,
 388        .recv = nfcwilink_receive,
 389        .reg_complete_cb = nfcwilink_register_complete,
 390        .write = NULL,
 391};
 392
 393static int nfcwilink_open(struct nci_dev *ndev)
 394{
 395        struct nfcwilink *drv = nci_get_drvdata(ndev);
 396        unsigned long comp_ret;
 397        int rc;
 398
 399        nfc_dev_dbg(&drv->pdev->dev, "open entry");
 400
 401        if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) {
 402                rc = -EBUSY;
 403                goto exit;
 404        }
 405
 406        nfcwilink_proto.priv_data = drv;
 407
 408        init_completion(&drv->completed);
 409        drv->st_register_cb_status = -EINPROGRESS;
 410
 411        rc = st_register(&nfcwilink_proto);
 412        if (rc < 0) {
 413                if (rc == -EINPROGRESS) {
 414                        comp_ret = wait_for_completion_timeout(
 415                        &drv->completed,
 416                        msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT));
 417
 418                        nfc_dev_dbg(&drv->pdev->dev,
 419                        "wait_for_completion_timeout returned %ld",
 420                        comp_ret);
 421
 422                        if (comp_ret == 0) {
 423                                /* timeout */
 424                                rc = -ETIMEDOUT;
 425                                goto clear_exit;
 426                        } else if (drv->st_register_cb_status != 0) {
 427                                rc = drv->st_register_cb_status;
 428                                nfc_dev_err(&drv->pdev->dev,
 429                                "st_register_cb failed %d", rc);
 430                                goto clear_exit;
 431                        }
 432                } else {
 433                        nfc_dev_err(&drv->pdev->dev,
 434                                "st_register failed %d", rc);
 435                        goto clear_exit;
 436                }
 437        }
 438
 439        /* st_register MUST fill the write callback */
 440        BUG_ON(nfcwilink_proto.write == NULL);
 441        drv->st_write = nfcwilink_proto.write;
 442
 443        if (nfcwilink_download_fw(drv)) {
 444                nfc_dev_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d",
 445                                rc);
 446                /* open should succeed, even if the FW download failed */
 447        }
 448
 449        goto exit;
 450
 451clear_exit:
 452        clear_bit(NFCWILINK_RUNNING, &drv->flags);
 453
 454exit:
 455        return rc;
 456}
 457
 458static int nfcwilink_close(struct nci_dev *ndev)
 459{
 460        struct nfcwilink *drv = nci_get_drvdata(ndev);
 461        int rc;
 462
 463        nfc_dev_dbg(&drv->pdev->dev, "close entry");
 464
 465        if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags))
 466                return 0;
 467
 468        rc = st_unregister(&nfcwilink_proto);
 469        if (rc)
 470                nfc_dev_err(&drv->pdev->dev, "st_unregister failed %d", rc);
 471
 472        drv->st_write = NULL;
 473
 474        return rc;
 475}
 476
 477static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
 478{
 479        struct nfcwilink *drv = nci_get_drvdata(ndev);
 480        struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
 481        long len;
 482
 483        nfc_dev_dbg(&drv->pdev->dev, "send entry, len %d", skb->len);
 484
 485        if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) {
 486                kfree_skb(skb);
 487                return -EINVAL;
 488        }
 489
 490        /* add the ST hdr to the start of the buffer */
 491        hdr.len = cpu_to_le16(skb->len);
 492        memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN);
 493
 494        /* Insert skb to shared transport layer's transmit queue.
 495         * Freeing skb memory is taken care in shared transport layer,
 496         * so don't free skb memory here.
 497         */
 498        len = drv->st_write(skb);
 499        if (len < 0) {
 500                kfree_skb(skb);
 501                nfc_dev_err(&drv->pdev->dev, "st_write failed %ld", len);
 502                return -EFAULT;
 503        }
 504
 505        return 0;
 506}
 507
 508static struct nci_ops nfcwilink_ops = {
 509        .open = nfcwilink_open,
 510        .close = nfcwilink_close,
 511        .send = nfcwilink_send,
 512};
 513
 514static int nfcwilink_probe(struct platform_device *pdev)
 515{
 516        static struct nfcwilink *drv;
 517        int rc;
 518        __u32 protocols;
 519
 520        nfc_dev_dbg(&pdev->dev, "probe entry");
 521
 522        drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL);
 523        if (!drv) {
 524                rc = -ENOMEM;
 525                goto exit;
 526        }
 527
 528        drv->pdev = pdev;
 529
 530        protocols = NFC_PROTO_JEWEL_MASK
 531                | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
 532                | NFC_PROTO_ISO14443_MASK
 533                | NFC_PROTO_ISO14443_B_MASK
 534                | NFC_PROTO_NFC_DEP_MASK;
 535
 536        drv->ndev = nci_allocate_device(&nfcwilink_ops,
 537                                        protocols,
 538                                        NFCWILINK_HDR_LEN,
 539                                        0);
 540        if (!drv->ndev) {
 541                nfc_dev_err(&pdev->dev, "nci_allocate_device failed");
 542                rc = -ENOMEM;
 543                goto exit;
 544        }
 545
 546        nci_set_parent_dev(drv->ndev, &pdev->dev);
 547        nci_set_drvdata(drv->ndev, drv);
 548
 549        rc = nci_register_device(drv->ndev);
 550        if (rc < 0) {
 551                nfc_dev_err(&pdev->dev, "nci_register_device failed %d", rc);
 552                goto free_dev_exit;
 553        }
 554
 555        dev_set_drvdata(&pdev->dev, drv);
 556
 557        goto exit;
 558
 559free_dev_exit:
 560        nci_free_device(drv->ndev);
 561
 562exit:
 563        return rc;
 564}
 565
 566static int nfcwilink_remove(struct platform_device *pdev)
 567{
 568        struct nfcwilink *drv = dev_get_drvdata(&pdev->dev);
 569        struct nci_dev *ndev;
 570
 571        nfc_dev_dbg(&pdev->dev, "remove entry");
 572
 573        if (!drv)
 574                return -EFAULT;
 575
 576        ndev = drv->ndev;
 577
 578        nci_unregister_device(ndev);
 579        nci_free_device(ndev);
 580
 581        dev_set_drvdata(&pdev->dev, NULL);
 582
 583        return 0;
 584}
 585
 586static struct platform_driver nfcwilink_driver = {
 587        .probe = nfcwilink_probe,
 588        .remove = nfcwilink_remove,
 589        .driver = {
 590                .name = "nfcwilink",
 591                .owner = THIS_MODULE,
 592        },
 593};
 594
 595module_platform_driver(nfcwilink_driver);
 596
 597/* ------ Module Info ------ */
 598
 599MODULE_AUTHOR("Ilan Elias <ilane@ti.com>");
 600MODULE_DESCRIPTION("NFC Driver for TI Shared Transport");
 601MODULE_LICENSE("GPL");
 602
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.