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