diff options
author | Frederic Danis <frederic.danis@linux.intel.com> | 2013-05-29 09:35:03 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-06-14 07:44:15 -0400 |
commit | ee9596d467e4d05c77a8c883aeeb5b74d1a3cd31 (patch) | |
tree | 256dad6d872ced15c55723fcdccfcdcf3d87734a /net/nfc | |
parent | 8a00a61b0ef2bfd1b468dd20c0d0b1a94a8f7475 (diff) |
NFC: Add NCI over SPI send
Before any operation, driver interruption is de-asserted to prevent
race condition between TX and RX.
The NCI over SPI header is added in front of NCI packet.
If acknowledged mode is set, CRC-16-CCITT is added to the packet.
Then the packet is forwarded to SPI module to be sent.
A delay after the transaction is added.
This delay is determined by the driver during nci_spi_allocate_device()
call and can be 0.
After data has been sent, driver interruption is re-asserted.
If acknowledged mode is set, nci_spi_send will block until
acknowledgment is received.
Signed-off-by: Frederic Danis <frederic.danis@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'net/nfc')
-rw-r--r-- | net/nfc/nci/spi.c | 71 |
1 files changed, 70 insertions, 1 deletions
diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c index ebcdba51418c..6258461e6998 100644 --- a/net/nfc/nci/spi.c +++ b/net/nfc/nci/spi.c | |||
@@ -20,12 +20,25 @@ | |||
20 | 20 | ||
21 | #include <linux/export.h> | 21 | #include <linux/export.h> |
22 | #include <linux/spi/spi.h> | 22 | #include <linux/spi/spi.h> |
23 | #include <linux/crc-ccitt.h> | ||
23 | #include <linux/nfc.h> | 24 | #include <linux/nfc.h> |
24 | #include <net/nfc/nci_core.h> | 25 | #include <net/nfc/nci_core.h> |
25 | 26 | ||
26 | #define NCI_SPI_HDR_LEN 4 | 27 | #define NCI_SPI_HDR_LEN 4 |
27 | #define NCI_SPI_CRC_LEN 2 | 28 | #define NCI_SPI_CRC_LEN 2 |
28 | 29 | ||
30 | #define NCI_SPI_SEND_TIMEOUT (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \ | ||
31 | NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT) | ||
32 | |||
33 | #define NCI_SPI_DIRECT_WRITE 0x01 | ||
34 | #define NCI_SPI_DIRECT_READ 0x02 | ||
35 | |||
36 | #define ACKNOWLEDGE_NONE 0 | ||
37 | #define ACKNOWLEDGE_ACK 1 | ||
38 | #define ACKNOWLEDGE_NACK 2 | ||
39 | |||
40 | #define CRC_INIT 0xFFFF | ||
41 | |||
29 | static int nci_spi_open(struct nci_dev *nci_dev) | 42 | static int nci_spi_open(struct nci_dev *nci_dev) |
30 | { | 43 | { |
31 | struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev); | 44 | struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev); |
@@ -40,9 +53,65 @@ static int nci_spi_close(struct nci_dev *nci_dev) | |||
40 | return ndev->ops->close(ndev); | 53 | return ndev->ops->close(ndev); |
41 | } | 54 | } |
42 | 55 | ||
56 | static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb) | ||
57 | { | ||
58 | struct spi_message m; | ||
59 | struct spi_transfer t; | ||
60 | |||
61 | t.tx_buf = skb->data; | ||
62 | t.len = skb->len; | ||
63 | t.cs_change = 0; | ||
64 | t.delay_usecs = ndev->xfer_udelay; | ||
65 | |||
66 | spi_message_init(&m); | ||
67 | spi_message_add_tail(&t, &m); | ||
68 | |||
69 | return spi_sync(ndev->spi, &m); | ||
70 | } | ||
71 | |||
43 | static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb) | 72 | static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb) |
44 | { | 73 | { |
45 | return 0; | 74 | struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev); |
75 | unsigned int payload_len = skb->len; | ||
76 | unsigned char *hdr; | ||
77 | int ret; | ||
78 | long completion_rc; | ||
79 | |||
80 | ndev->ops->deassert_int(ndev); | ||
81 | |||
82 | /* add the NCI SPI header to the start of the buffer */ | ||
83 | hdr = skb_push(skb, NCI_SPI_HDR_LEN); | ||
84 | hdr[0] = NCI_SPI_DIRECT_WRITE; | ||
85 | hdr[1] = ndev->acknowledge_mode; | ||
86 | hdr[2] = payload_len >> 8; | ||
87 | hdr[3] = payload_len & 0xFF; | ||
88 | |||
89 | if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) { | ||
90 | u16 crc; | ||
91 | |||
92 | crc = crc_ccitt(CRC_INIT, skb->data, skb->len); | ||
93 | *skb_put(skb, 1) = crc >> 8; | ||
94 | *skb_put(skb, 1) = crc & 0xFF; | ||
95 | } | ||
96 | |||
97 | ret = __nci_spi_send(ndev, skb); | ||
98 | |||
99 | kfree_skb(skb); | ||
100 | ndev->ops->assert_int(ndev); | ||
101 | |||
102 | if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED) | ||
103 | goto done; | ||
104 | |||
105 | init_completion(&ndev->req_completion); | ||
106 | completion_rc = | ||
107 | wait_for_completion_interruptible_timeout(&ndev->req_completion, | ||
108 | NCI_SPI_SEND_TIMEOUT); | ||
109 | |||
110 | if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK) | ||
111 | ret = -EIO; | ||
112 | |||
113 | done: | ||
114 | return ret; | ||
46 | } | 115 | } |
47 | 116 | ||
48 | static struct nci_ops nci_spi_ops = { | 117 | static struct nci_ops nci_spi_ops = { |