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/nfc.h>
  32#include <net/nfc/nci.h>
  33#include <net/nfc/nci_core.h>
  34#include <linux/ti_wilink_st.h>
  35
  36#define NFCWILINK_CHNL                  12
  37#define NFCWILINK_OPCODE                7
  38#define NFCWILINK_MAX_FRAME_SIZE        300
  39#define NFCWILINK_HDR_LEN               4
  40#define NFCWILINK_OFFSET_LEN_IN_HDR     1
  41#define NFCWILINK_LEN_SIZE              2
  42#define NFCWILINK_REGISTER_TIMEOUT      8000    /* 8 sec */
  43
  44struct nfcwilink_hdr {
  45        u8 chnl;
  46        u8 opcode;
  47        u16 len;
  48} __packed;
  49
  50struct nfcwilink {
  51        struct platform_device          *pdev;
  52        struct nci_dev                  *ndev;
  53        unsigned long                   flags;
  54
  55        char                            st_register_cb_status;
  56        long                            (*st_write) (struct sk_buff *);
  57        struct completion               st_register_completed;
  58};
  59
  60/* NFCWILINK driver flags */
  61enum {
  62        NFCWILINK_RUNNING,
  63};
  64
  65/* Called by ST when registration is complete */
  66static void nfcwilink_register_complete(void *priv_data, char data)
  67{
  68        struct nfcwilink *drv = priv_data;
  69
  70        nfc_dev_dbg(&drv->pdev->dev, "register_complete entry");
  71
  72        /* store ST registration status */
  73        drv->st_register_cb_status = data;
  74
  75        /* complete the wait in nfc_st_open() */
  76        complete(&drv->st_register_completed);
  77}
  78
  79/* Called by ST when receive data is available */
  80static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
  81{
  82        struct nfcwilink *drv = priv_data;
  83        int rc;
  84
  85        nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
  86
  87        if (!skb)
  88                return -EFAULT;
  89
  90        if (!drv) {
  91                kfree_skb(skb);
  92                return -EFAULT;
  93        }
  94
  95        /* strip the ST header
  96        (apart for the chnl byte, which is not received in the hdr) */
  97        skb_pull(skb, (NFCWILINK_HDR_LEN-1));
  98
  99        skb->dev = (void *) drv->ndev;
 100
 101        /* Forward skb to NCI core layer */
 102        rc = nci_recv_frame(skb);
 103        if (rc < 0) {
 104                nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc);
 105                return rc;
 106        }
 107
 108        return 0;
 109}
 110
 111/* protocol structure registered with ST */
 112static struct st_proto_s nfcwilink_proto = {
 113        .chnl_id = NFCWILINK_CHNL,
 114        .max_frame_size = NFCWILINK_MAX_FRAME_SIZE,
 115        .hdr_len = (NFCWILINK_HDR_LEN-1),       /* not including chnl byte */
 116        .offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR,
 117        .len_size = NFCWILINK_LEN_SIZE,
 118        .reserve = 0,
 119        .recv = nfcwilink_receive,
 120        .reg_complete_cb = nfcwilink_register_complete,
 121        .write = NULL,
 122};
 123
 124static int nfcwilink_open(struct nci_dev *ndev)
 125{
 126        struct nfcwilink *drv = nci_get_drvdata(ndev);
 127        unsigned long comp_ret;
 128        int rc;
 129
 130        nfc_dev_dbg(&drv->pdev->dev, "open entry");
 131
 132        if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) {
 133                rc = -EBUSY;
 134                goto exit;
 135        }
 136
 137        nfcwilink_proto.priv_data = drv;
 138
 139        init_completion(&drv->st_register_completed);
 140        drv->st_register_cb_status = -EINPROGRESS;
 141
 142        rc = st_register(&nfcwilink_proto);
 143        if (rc < 0) {
 144                if (rc == -EINPROGRESS) {
 145                        comp_ret = wait_for_completion_timeout(
 146                        &drv->st_register_completed,
 147                        msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT));
 148
 149                        nfc_dev_dbg(&drv->pdev->dev,
 150                        "wait_for_completion_timeout returned %ld",
 151                        comp_ret);
 152
 153                        if (comp_ret == 0) {
 154                                /* timeout */
 155                                rc = -ETIMEDOUT;
 156                                goto clear_exit;
 157                        } else if (drv->st_register_cb_status != 0) {
 158                                rc = drv->st_register_cb_status;
 159                                nfc_dev_err(&drv->pdev->dev,
 160                                "st_register_cb failed %d", rc);
 161                                goto clear_exit;
 162                        }
 163                } else {
 164                        nfc_dev_err(&drv->pdev->dev,
 165                                "st_register failed %d", rc);
 166                        goto clear_exit;
 167                }
 168        }
 169
 170        /* st_register MUST fill the write callback */
 171        BUG_ON(nfcwilink_proto.write == NULL);
 172        drv->st_write = nfcwilink_proto.write;
 173
 174        goto exit;
 175
 176clear_exit:
 177        clear_bit(NFCWILINK_RUNNING, &drv->flags);
 178
 179exit:
 180        return rc;
 181}
 182
 183static int nfcwilink_close(struct nci_dev *ndev)
 184{
 185        struct nfcwilink *drv = nci_get_drvdata(ndev);
 186        int rc;
 187
 188        nfc_dev_dbg(&drv->pdev->dev, "close entry");
 189
 190        if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags))
 191                return 0;
 192
 193        rc = st_unregister(&nfcwilink_proto);
 194        if (rc)
 195                nfc_dev_err(&drv->pdev->dev, "st_unregister failed %d", rc);
 196
 197        drv->st_write = NULL;
 198
 199        return rc;
 200}
 201
 202static int nfcwilink_send(struct sk_buff *skb)
 203{
 204        struct nci_dev *ndev = (struct nci_dev *)skb->dev;
 205        struct nfcwilink *drv = nci_get_drvdata(ndev);
 206        struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
 207        long len;
 208
 209        nfc_dev_dbg(&drv->pdev->dev, "send entry, len %d", skb->len);
 210
 211        if (!test_bit(NFCWILINK_RUNNING, &drv->flags))
 212                return -EBUSY;
 213
 214        /* add the ST hdr to the start of the buffer */
 215        hdr.len = skb->len;
 216        memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN);
 217
 218        /* Insert skb to shared transport layer's transmit queue.
 219         * Freeing skb memory is taken care in shared transport layer,
 220         * so don't free skb memory here.
 221         */
 222        len = drv->st_write(skb);
 223        if (len < 0) {
 224                kfree_skb(skb);
 225                nfc_dev_err(&drv->pdev->dev, "st_write failed %ld", len);
 226                return -EFAULT;
 227        }
 228
 229        return 0;
 230}
 231
 232static struct nci_ops nfcwilink_ops = {
 233        .open = nfcwilink_open,
 234        .close = nfcwilink_close,
 235        .send = nfcwilink_send,
 236};
 237
 238static int nfcwilink_probe(struct platform_device *pdev)
 239{
 240        static struct nfcwilink *drv;
 241        int rc;
 242        u32 protocols;
 243
 244        nfc_dev_dbg(&pdev->dev, "probe entry");
 245
 246        drv = kzalloc(sizeof(struct nfcwilink), GFP_KERNEL);
 247        if (!drv) {
 248                rc = -ENOMEM;
 249                goto exit;
 250        }
 251
 252        drv->pdev = pdev;
 253
 254        protocols = NFC_PROTO_JEWEL_MASK
 255                        | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
 256                        | NFC_PROTO_ISO14443_MASK
 257                        | NFC_PROTO_NFC_DEP_MASK;
 258
 259        drv->ndev = nci_allocate_device(&nfcwilink_ops,
 260                                        protocols,
 261                                        NFCWILINK_HDR_LEN,
 262                                        0);
 263        if (!drv->ndev) {
 264                nfc_dev_err(&pdev->dev, "nci_allocate_device failed");
 265                rc = -ENOMEM;
 266                goto free_exit;
 267        }
 268
 269        nci_set_parent_dev(drv->ndev, &pdev->dev);
 270        nci_set_drvdata(drv->ndev, drv);
 271
 272        rc = nci_register_device(drv->ndev);
 273        if (rc < 0) {
 274                nfc_dev_err(&pdev->dev, "nci_register_device failed %d", rc);
 275                goto free_dev_exit;
 276        }
 277
 278        dev_set_drvdata(&pdev->dev, drv);
 279
 280        goto exit;
 281
 282free_dev_exit:
 283        nci_free_device(drv->ndev);
 284
 285free_exit:
 286        kfree(drv);
 287
 288exit:
 289        return rc;
 290}
 291
 292static int nfcwilink_remove(struct platform_device *pdev)
 293{
 294        struct nfcwilink *drv = dev_get_drvdata(&pdev->dev);
 295        struct nci_dev *ndev;
 296
 297        nfc_dev_dbg(&pdev->dev, "remove entry");
 298
 299        if (!drv)
 300                return -EFAULT;
 301
 302        ndev = drv->ndev;
 303
 304        nci_unregister_device(ndev);
 305        nci_free_device(ndev);
 306
 307        kfree(drv);
 308
 309        dev_set_drvdata(&pdev->dev, NULL);
 310
 311        return 0;
 312}
 313
 314static struct platform_driver nfcwilink_driver = {
 315        .probe = nfcwilink_probe,
 316        .remove = nfcwilink_remove,
 317        .driver = {
 318                .name = "nfcwilink",
 319                .owner = THIS_MODULE,
 320        },
 321};
 322
 323/* ------- Module Init/Exit interfaces ------ */
 324static int __init nfcwilink_init(void)
 325{
 326        printk(KERN_INFO "NFC Driver for TI WiLink");
 327
 328        return platform_driver_register(&nfcwilink_driver);
 329}
 330
 331static void __exit nfcwilink_exit(void)
 332{
 333        platform_driver_unregister(&nfcwilink_driver);
 334}
 335
 336module_init(nfcwilink_init);
 337module_exit(nfcwilink_exit);
 338
 339/* ------ Module Info ------ */
 340
 341MODULE_AUTHOR("Ilan Elias <ilane@ti.com>");
 342MODULE_DESCRIPTION("NFC Driver for TI Shared Transport");
 343MODULE_LICENSE("GPL");
 344
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.