linux/drivers/nfc/s3fwrn5/core.c
<<
>>
Prefs
   1/*
   2 * NCI based driver for Samsung S3FWRN5 NFC chip
   3 *
   4 * Copyright (C) 2015 Samsung Electrnoics
   5 * Robert Baldyga <r.baldyga@samsung.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms and conditions of the GNU General Public License,
   9 * version 2 or later, as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <linux/module.h>
  21#include <net/nfc/nci_core.h>
  22
  23#include "s3fwrn5.h"
  24#include "firmware.h"
  25#include "nci.h"
  26
  27#define S3FWRN5_NFC_PROTOCOLS  (NFC_PROTO_JEWEL_MASK | \
  28                                NFC_PROTO_MIFARE_MASK | \
  29                                NFC_PROTO_FELICA_MASK | \
  30                                NFC_PROTO_ISO14443_MASK | \
  31                                NFC_PROTO_ISO14443_B_MASK | \
  32                                NFC_PROTO_ISO15693_MASK)
  33
  34static int s3fwrn5_firmware_update(struct s3fwrn5_info *info)
  35{
  36        bool need_update;
  37        int ret;
  38
  39        s3fwrn5_fw_init(&info->fw_info, "sec_s3fwrn5_firmware.bin");
  40
  41        /* Update firmware */
  42
  43        s3fwrn5_set_wake(info, false);
  44        s3fwrn5_set_mode(info, S3FWRN5_MODE_FW);
  45
  46        ret = s3fwrn5_fw_setup(&info->fw_info);
  47        if (ret < 0)
  48                return ret;
  49
  50        need_update = s3fwrn5_fw_check_version(&info->fw_info,
  51                info->ndev->manufact_specific_info);
  52        if (!need_update)
  53                goto out;
  54
  55        dev_info(&info->ndev->nfc_dev->dev, "Detected new firmware version\n");
  56
  57        ret = s3fwrn5_fw_download(&info->fw_info);
  58        if (ret < 0)
  59                goto out;
  60
  61        /* Update RF configuration */
  62
  63        s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
  64
  65        s3fwrn5_set_wake(info, true);
  66        ret = s3fwrn5_nci_rf_configure(info, "sec_s3fwrn5_rfreg.bin");
  67        s3fwrn5_set_wake(info, false);
  68
  69out:
  70        s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
  71        s3fwrn5_fw_cleanup(&info->fw_info);
  72        return ret;
  73}
  74
  75static int s3fwrn5_nci_open(struct nci_dev *ndev)
  76{
  77        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
  78
  79        if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_COLD)
  80                return  -EBUSY;
  81
  82        s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
  83        s3fwrn5_set_wake(info, true);
  84
  85        return 0;
  86}
  87
  88static int s3fwrn5_nci_close(struct nci_dev *ndev)
  89{
  90        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
  91
  92        s3fwrn5_set_wake(info, false);
  93        s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
  94
  95        return 0;
  96}
  97
  98static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
  99{
 100        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
 101        int ret;
 102
 103        mutex_lock(&info->mutex);
 104
 105        if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_NCI) {
 106                mutex_unlock(&info->mutex);
 107                return -EINVAL;
 108        }
 109
 110        ret = s3fwrn5_write(info, skb);
 111        if (ret < 0)
 112                kfree_skb(skb);
 113
 114        mutex_unlock(&info->mutex);
 115        return ret;
 116}
 117
 118static int s3fwrn5_nci_post_setup(struct nci_dev *ndev)
 119{
 120        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
 121        int ret;
 122
 123        ret = s3fwrn5_firmware_update(info);
 124        if (ret < 0)
 125                goto out;
 126
 127        /* NCI core reset */
 128
 129        s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
 130        s3fwrn5_set_wake(info, true);
 131
 132        ret = nci_core_reset(info->ndev);
 133        if (ret < 0)
 134                goto out;
 135
 136        ret = nci_core_init(info->ndev);
 137
 138out:
 139        return ret;
 140}
 141
 142static struct nci_ops s3fwrn5_nci_ops = {
 143        .open = s3fwrn5_nci_open,
 144        .close = s3fwrn5_nci_close,
 145        .send = s3fwrn5_nci_send,
 146        .post_setup = s3fwrn5_nci_post_setup,
 147};
 148
 149int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
 150        const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
 151{
 152        struct s3fwrn5_info *info;
 153        int ret;
 154
 155        info = devm_kzalloc(pdev, sizeof(*info), GFP_KERNEL);
 156        if (!info)
 157                return -ENOMEM;
 158
 159        info->phy_id = phy_id;
 160        info->pdev = pdev;
 161        info->phy_ops = phy_ops;
 162        info->max_payload = max_payload;
 163        mutex_init(&info->mutex);
 164
 165        s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
 166
 167        s3fwrn5_nci_get_prop_ops(&s3fwrn5_nci_ops.prop_ops,
 168                &s3fwrn5_nci_ops.n_prop_ops);
 169
 170        info->ndev = nci_allocate_device(&s3fwrn5_nci_ops,
 171                S3FWRN5_NFC_PROTOCOLS, 0, 0);
 172        if (!info->ndev)
 173                return -ENOMEM;
 174
 175        nci_set_parent_dev(info->ndev, pdev);
 176        nci_set_drvdata(info->ndev, info);
 177
 178        ret = nci_register_device(info->ndev);
 179        if (ret < 0) {
 180                nci_free_device(info->ndev);
 181                return ret;
 182        }
 183
 184        info->fw_info.ndev = info->ndev;
 185
 186        *ndev = info->ndev;
 187
 188        return ret;
 189}
 190EXPORT_SYMBOL(s3fwrn5_probe);
 191
 192void s3fwrn5_remove(struct nci_dev *ndev)
 193{
 194        struct s3fwrn5_info *info = nci_get_drvdata(ndev);
 195
 196        s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
 197
 198        nci_unregister_device(ndev);
 199        nci_free_device(ndev);
 200}
 201EXPORT_SYMBOL(s3fwrn5_remove);
 202
 203int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
 204        enum s3fwrn5_mode mode)
 205{
 206        switch (mode) {
 207        case S3FWRN5_MODE_NCI:
 208                return nci_recv_frame(ndev, skb);
 209        case S3FWRN5_MODE_FW:
 210                return s3fwrn5_fw_recv_frame(ndev, skb);
 211        default:
 212                return -ENODEV;
 213        }
 214}
 215EXPORT_SYMBOL(s3fwrn5_recv_frame);
 216
 217MODULE_LICENSE("GPL");
 218MODULE_DESCRIPTION("Samsung S3FWRN5 NFC driver");
 219MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");
 220