linux/net/nfc/nci/spi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2013  Intel Corporation. All rights reserved.
   4 */
   5
   6#define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
   7
   8#include <linux/module.h>
   9
  10#include <linux/export.h>
  11#include <linux/spi/spi.h>
  12#include <linux/crc-ccitt.h>
  13#include <net/nfc/nci_core.h>
  14
  15#define NCI_SPI_ACK_SHIFT               6
  16#define NCI_SPI_MSB_PAYLOAD_MASK        0x3F
  17
  18#define NCI_SPI_SEND_TIMEOUT    (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \
  19                                        NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT)
  20
  21#define NCI_SPI_DIRECT_WRITE    0x01
  22#define NCI_SPI_DIRECT_READ     0x02
  23
  24#define ACKNOWLEDGE_NONE        0
  25#define ACKNOWLEDGE_ACK         1
  26#define ACKNOWLEDGE_NACK        2
  27
  28#define CRC_INIT                0xFFFF
  29
  30static int __nci_spi_send(struct nci_spi *nspi, const struct sk_buff *skb,
  31                          int cs_change)
  32{
  33        struct spi_message m;
  34        struct spi_transfer t;
  35
  36        memset(&t, 0, sizeof(struct spi_transfer));
  37        /* a NULL skb means we just want the SPI chip select line to raise */
  38        if (skb) {
  39                t.tx_buf = skb->data;
  40                t.len = skb->len;
  41        } else {
  42                /* still set tx_buf non NULL to make the driver happy */
  43                t.tx_buf = &t;
  44                t.len = 0;
  45        }
  46        t.cs_change = cs_change;
  47        t.delay.value = nspi->xfer_udelay;
  48        t.delay.unit = SPI_DELAY_UNIT_USECS;
  49        t.speed_hz = nspi->xfer_speed_hz;
  50
  51        spi_message_init(&m);
  52        spi_message_add_tail(&t, &m);
  53
  54        return spi_sync(nspi->spi, &m);
  55}
  56
  57int nci_spi_send(struct nci_spi *nspi,
  58                 struct completion *write_handshake_completion,
  59                 struct sk_buff *skb)
  60{
  61        unsigned int payload_len = skb->len;
  62        unsigned char *hdr;
  63        int ret;
  64        long completion_rc;
  65
  66        /* add the NCI SPI header to the start of the buffer */
  67        hdr = skb_push(skb, NCI_SPI_HDR_LEN);
  68        hdr[0] = NCI_SPI_DIRECT_WRITE;
  69        hdr[1] = nspi->acknowledge_mode;
  70        hdr[2] = payload_len >> 8;
  71        hdr[3] = payload_len & 0xFF;
  72
  73        if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
  74                u16 crc;
  75
  76                crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
  77                skb_put_u8(skb, crc >> 8);
  78                skb_put_u8(skb, crc & 0xFF);
  79        }
  80
  81        if (write_handshake_completion) {
  82                /* Trick SPI driver to raise chip select */
  83                ret = __nci_spi_send(nspi, NULL, 1);
  84                if (ret)
  85                        goto done;
  86
  87                /* wait for NFC chip hardware handshake to complete */
  88                if (wait_for_completion_timeout(write_handshake_completion,
  89                                                msecs_to_jiffies(1000)) == 0) {
  90                        ret = -ETIME;
  91                        goto done;
  92                }
  93        }
  94
  95        ret = __nci_spi_send(nspi, skb, 0);
  96        if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
  97                goto done;
  98
  99        reinit_completion(&nspi->req_completion);
 100        completion_rc = wait_for_completion_interruptible_timeout(
 101                                                        &nspi->req_completion,
 102                                                        NCI_SPI_SEND_TIMEOUT);
 103
 104        if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK)
 105                ret = -EIO;
 106
 107done:
 108        kfree_skb(skb);
 109
 110        return ret;
 111}
 112EXPORT_SYMBOL_GPL(nci_spi_send);
 113
 114/* ---- Interface to NCI SPI drivers ---- */
 115
 116/**
 117 * nci_spi_allocate_spi - allocate a new nci spi
 118 *
 119 * @spi: SPI device
 120 * @acknowledge_mode: Acknowledge mode used by the NFC device
 121 * @delay: delay between transactions in us
 122 * @ndev: nci dev to send incoming nci frames to
 123 */
 124struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
 125                                     u8 acknowledge_mode, unsigned int delay,
 126                                     struct nci_dev *ndev)
 127{
 128        struct nci_spi *nspi;
 129
 130        nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
 131        if (!nspi)
 132                return NULL;
 133
 134        nspi->acknowledge_mode = acknowledge_mode;
 135        nspi->xfer_udelay = delay;
 136        /* Use controller max SPI speed by default */
 137        nspi->xfer_speed_hz = 0;
 138        nspi->spi = spi;
 139        nspi->ndev = ndev;
 140        init_completion(&nspi->req_completion);
 141
 142        return nspi;
 143}
 144EXPORT_SYMBOL_GPL(nci_spi_allocate_spi);
 145
 146static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
 147{
 148        struct sk_buff *skb;
 149        unsigned char *hdr;
 150        u16 crc;
 151        int ret;
 152
 153        skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL);
 154
 155        /* add the NCI SPI header to the start of the buffer */
 156        hdr = skb_push(skb, NCI_SPI_HDR_LEN);
 157        hdr[0] = NCI_SPI_DIRECT_WRITE;
 158        hdr[1] = NCI_SPI_CRC_ENABLED;
 159        hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
 160        hdr[3] = 0;
 161
 162        crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
 163        skb_put_u8(skb, crc >> 8);
 164        skb_put_u8(skb, crc & 0xFF);
 165
 166        ret = __nci_spi_send(nspi, skb, 0);
 167
 168        kfree_skb(skb);
 169
 170        return ret;
 171}
 172
 173static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
 174{
 175        struct sk_buff *skb;
 176        struct spi_message m;
 177        unsigned char req[2], resp_hdr[2];
 178        struct spi_transfer tx, rx;
 179        unsigned short rx_len = 0;
 180        int ret;
 181
 182        spi_message_init(&m);
 183
 184        memset(&tx, 0, sizeof(struct spi_transfer));
 185        req[0] = NCI_SPI_DIRECT_READ;
 186        req[1] = nspi->acknowledge_mode;
 187        tx.tx_buf = req;
 188        tx.len = 2;
 189        tx.cs_change = 0;
 190        tx.speed_hz = nspi->xfer_speed_hz;
 191        spi_message_add_tail(&tx, &m);
 192
 193        memset(&rx, 0, sizeof(struct spi_transfer));
 194        rx.rx_buf = resp_hdr;
 195        rx.len = 2;
 196        rx.cs_change = 1;
 197        rx.speed_hz = nspi->xfer_speed_hz;
 198        spi_message_add_tail(&rx, &m);
 199
 200        ret = spi_sync(nspi->spi, &m);
 201        if (ret)
 202                return NULL;
 203
 204        if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
 205                rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
 206                                resp_hdr[1] + NCI_SPI_CRC_LEN;
 207        else
 208                rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
 209
 210        skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL);
 211        if (!skb)
 212                return NULL;
 213
 214        spi_message_init(&m);
 215
 216        memset(&rx, 0, sizeof(struct spi_transfer));
 217        rx.rx_buf = skb_put(skb, rx_len);
 218        rx.len = rx_len;
 219        rx.cs_change = 0;
 220        rx.delay.value = nspi->xfer_udelay;
 221        rx.delay.unit = SPI_DELAY_UNIT_USECS;
 222        rx.speed_hz = nspi->xfer_speed_hz;
 223        spi_message_add_tail(&rx, &m);
 224
 225        ret = spi_sync(nspi->spi, &m);
 226        if (ret)
 227                goto receive_error;
 228
 229        if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
 230                *(u8 *)skb_push(skb, 1) = resp_hdr[1];
 231                *(u8 *)skb_push(skb, 1) = resp_hdr[0];
 232        }
 233
 234        return skb;
 235
 236receive_error:
 237        kfree_skb(skb);
 238
 239        return NULL;
 240}
 241
 242static int nci_spi_check_crc(struct sk_buff *skb)
 243{
 244        u16 crc_data = (skb->data[skb->len - 2] << 8) |
 245                        skb->data[skb->len - 1];
 246        int ret;
 247
 248        ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
 249                        == crc_data);
 250
 251        skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
 252
 253        return ret;
 254}
 255
 256static u8 nci_spi_get_ack(struct sk_buff *skb)
 257{
 258        u8 ret;
 259
 260        ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
 261
 262        /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
 263        skb_pull(skb, 2);
 264
 265        return ret;
 266}
 267
 268/**
 269 * nci_spi_read - read frame from NCI SPI drivers
 270 *
 271 * @nspi: The nci spi
 272 * Context: can sleep
 273 *
 274 * This call may only be used from a context that may sleep.  The sleep
 275 * is non-interruptible, and has no timeout.
 276 *
 277 * It returns an allocated skb containing the frame on success, or NULL.
 278 */
 279struct sk_buff *nci_spi_read(struct nci_spi *nspi)
 280{
 281        struct sk_buff *skb;
 282
 283        /* Retrieve frame from SPI */
 284        skb = __nci_spi_read(nspi);
 285        if (!skb)
 286                goto done;
 287
 288        if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
 289                if (!nci_spi_check_crc(skb)) {
 290                        send_acknowledge(nspi, ACKNOWLEDGE_NACK);
 291                        goto done;
 292                }
 293
 294                /* In case of acknowledged mode: if ACK or NACK received,
 295                 * unblock completion of latest frame sent.
 296                 */
 297                nspi->req_result = nci_spi_get_ack(skb);
 298                if (nspi->req_result)
 299                        complete(&nspi->req_completion);
 300        }
 301
 302        /* If there is no payload (ACK/NACK only frame),
 303         * free the socket buffer
 304         */
 305        if (!skb->len) {
 306                kfree_skb(skb);
 307                skb = NULL;
 308                goto done;
 309        }
 310
 311        if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
 312                send_acknowledge(nspi, ACKNOWLEDGE_ACK);
 313
 314done:
 315
 316        return skb;
 317}
 318EXPORT_SYMBOL_GPL(nci_spi_read);
 319
 320MODULE_LICENSE("GPL");
 321