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; |
2bd83245 | 61 | t.speed_hz = nspi->xfer_speed_hz; |
ee9596d4 FD |
62 | |
63 | spi_message_init(&m); | |
64 | spi_message_add_tail(&t, &m); | |
65 | ||
fa544fff | 66 | return spi_sync(nspi->spi, &m); |
ee9596d4 FD |
67 | } |
68 | ||
2bed2785 EL |
69 | int nci_spi_send(struct nci_spi *nspi, |
70 | struct completion *write_handshake_completion, | |
71 | struct sk_buff *skb) | |
8a00a61b | 72 | { |
ee9596d4 FD |
73 | unsigned int payload_len = skb->len; |
74 | unsigned char *hdr; | |
75 | int ret; | |
76 | long completion_rc; | |
77 | ||
ee9596d4 FD |
78 | /* add the NCI SPI header to the start of the buffer */ |
79 | hdr = skb_push(skb, NCI_SPI_HDR_LEN); | |
80 | hdr[0] = NCI_SPI_DIRECT_WRITE; | |
fa544fff | 81 | hdr[1] = nspi->acknowledge_mode; |
ee9596d4 FD |
82 | hdr[2] = payload_len >> 8; |
83 | hdr[3] = payload_len & 0xFF; | |
84 | ||
fa544fff | 85 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { |
ee9596d4 FD |
86 | u16 crc; |
87 | ||
88 | crc = crc_ccitt(CRC_INIT, skb->data, skb->len); | |
89 | *skb_put(skb, 1) = crc >> 8; | |
90 | *skb_put(skb, 1) = crc & 0xFF; | |
91 | } | |
92 | ||
2bed2785 EL |
93 | if (write_handshake_completion) { |
94 | /* Trick SPI driver to raise chip select */ | |
95 | ret = __nci_spi_send(nspi, NULL, 1); | |
96 | if (ret) | |
97 | goto done; | |
ee9596d4 | 98 | |
2bed2785 EL |
99 | /* wait for NFC chip hardware handshake to complete */ |
100 | if (wait_for_completion_timeout(write_handshake_completion, | |
101 | msecs_to_jiffies(1000)) == 0) { | |
102 | ret = -ETIME; | |
103 | goto done; | |
104 | } | |
105 | } | |
ee9596d4 | 106 | |
2bed2785 | 107 | ret = __nci_spi_send(nspi, skb, 0); |
fa544fff | 108 | if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED) |
ee9596d4 FD |
109 | goto done; |
110 | ||
9bec44bf | 111 | reinit_completion(&nspi->req_completion); |
d5937511 | 112 | completion_rc = wait_for_completion_interruptible_timeout( |
fa544fff | 113 | &nspi->req_completion, |
d5937511 | 114 | NCI_SPI_SEND_TIMEOUT); |
ee9596d4 | 115 | |
fa544fff | 116 | if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK) |
ee9596d4 FD |
117 | ret = -EIO; |
118 | ||
119 | done: | |
2bed2785 EL |
120 | kfree_skb(skb); |
121 | ||
ee9596d4 | 122 | return ret; |
8a00a61b | 123 | } |
fa544fff | 124 | EXPORT_SYMBOL_GPL(nci_spi_send); |
8a00a61b FD |
125 | |
126 | /* ---- Interface to NCI SPI drivers ---- */ | |
127 | ||
128 | /** | |
fa544fff | 129 | * nci_spi_allocate_spi - allocate a new nci spi |
8a00a61b FD |
130 | * |
131 | * @spi: SPI device | |
fa544fff | 132 | * @acknowledge_mode: Acknowledge mode used by the NFC device |
8a00a61b | 133 | * @delay: delay between transactions in us |
fa544fff | 134 | * @ndev: nci dev to send incoming nci frames to |
8a00a61b | 135 | */ |
fa544fff | 136 | struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi, |
fa544fff EL |
137 | u8 acknowledge_mode, unsigned int delay, |
138 | struct nci_dev *ndev) | |
8a00a61b | 139 | { |
fa544fff | 140 | struct nci_spi *nspi; |
8a00a61b | 141 | |
fa544fff EL |
142 | nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL); |
143 | if (!nspi) | |
8a00a61b FD |
144 | return NULL; |
145 | ||
fa544fff EL |
146 | nspi->acknowledge_mode = acknowledge_mode; |
147 | nspi->xfer_udelay = delay; | |
2bd83245 VC |
148 | /* Use controller max SPI speed by default */ |
149 | nspi->xfer_speed_hz = 0; | |
645d5087 | 150 | nspi->spi = spi; |
fa544fff | 151 | nspi->ndev = ndev; |
9bec44bf | 152 | init_completion(&nspi->req_completion); |
8a00a61b | 153 | |
fa544fff | 154 | return nspi; |
8a00a61b | 155 | } |
fa544fff | 156 | EXPORT_SYMBOL_GPL(nci_spi_allocate_spi); |
391d8a2d | 157 | |
fa544fff | 158 | static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge) |
391d8a2d FD |
159 | { |
160 | struct sk_buff *skb; | |
161 | unsigned char *hdr; | |
162 | u16 crc; | |
163 | int ret; | |
164 | ||
fa544fff | 165 | skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL); |
391d8a2d FD |
166 | |
167 | /* add the NCI SPI header to the start of the buffer */ | |
168 | hdr = skb_push(skb, NCI_SPI_HDR_LEN); | |
169 | hdr[0] = NCI_SPI_DIRECT_WRITE; | |
170 | hdr[1] = NCI_SPI_CRC_ENABLED; | |
171 | hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT; | |
172 | hdr[3] = 0; | |
173 | ||
174 | crc = crc_ccitt(CRC_INIT, skb->data, skb->len); | |
175 | *skb_put(skb, 1) = crc >> 8; | |
176 | *skb_put(skb, 1) = crc & 0xFF; | |
177 | ||
2bed2785 | 178 | ret = __nci_spi_send(nspi, skb, 0); |
391d8a2d FD |
179 | |
180 | kfree_skb(skb); | |
181 | ||
182 | return ret; | |
183 | } | |
184 | ||
22d4aae5 | 185 | static struct sk_buff *__nci_spi_read(struct nci_spi *nspi) |
391d8a2d FD |
186 | { |
187 | struct sk_buff *skb; | |
188 | struct spi_message m; | |
189 | unsigned char req[2], resp_hdr[2]; | |
190 | struct spi_transfer tx, rx; | |
191 | unsigned short rx_len = 0; | |
192 | int ret; | |
193 | ||
194 | spi_message_init(&m); | |
a4ada6ca EL |
195 | |
196 | memset(&tx, 0, sizeof(struct spi_transfer)); | |
391d8a2d | 197 | req[0] = NCI_SPI_DIRECT_READ; |
fa544fff | 198 | req[1] = nspi->acknowledge_mode; |
391d8a2d FD |
199 | tx.tx_buf = req; |
200 | tx.len = 2; | |
201 | tx.cs_change = 0; | |
2bd83245 | 202 | tx.speed_hz = nspi->xfer_speed_hz; |
391d8a2d | 203 | spi_message_add_tail(&tx, &m); |
a4ada6ca EL |
204 | |
205 | memset(&rx, 0, sizeof(struct spi_transfer)); | |
391d8a2d FD |
206 | rx.rx_buf = resp_hdr; |
207 | rx.len = 2; | |
208 | rx.cs_change = 1; | |
2bd83245 | 209 | rx.speed_hz = nspi->xfer_speed_hz; |
391d8a2d | 210 | spi_message_add_tail(&rx, &m); |
a4ada6ca | 211 | |
fa544fff | 212 | ret = spi_sync(nspi->spi, &m); |
391d8a2d FD |
213 | if (ret) |
214 | return NULL; | |
215 | ||
fa544fff | 216 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) |
391d8a2d FD |
217 | rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) + |
218 | resp_hdr[1] + NCI_SPI_CRC_LEN; | |
219 | else | |
220 | rx_len = (resp_hdr[0] << 8) | resp_hdr[1]; | |
221 | ||
fa544fff | 222 | skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL); |
391d8a2d FD |
223 | if (!skb) |
224 | return NULL; | |
225 | ||
226 | spi_message_init(&m); | |
a4ada6ca EL |
227 | |
228 | memset(&rx, 0, sizeof(struct spi_transfer)); | |
391d8a2d FD |
229 | rx.rx_buf = skb_put(skb, rx_len); |
230 | rx.len = rx_len; | |
231 | rx.cs_change = 0; | |
fa544fff | 232 | rx.delay_usecs = nspi->xfer_udelay; |
2bd83245 | 233 | rx.speed_hz = nspi->xfer_speed_hz; |
391d8a2d | 234 | spi_message_add_tail(&rx, &m); |
a4ada6ca | 235 | |
fa544fff | 236 | ret = spi_sync(nspi->spi, &m); |
391d8a2d FD |
237 | if (ret) |
238 | goto receive_error; | |
239 | ||
fa544fff | 240 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { |
391d8a2d FD |
241 | *skb_push(skb, 1) = resp_hdr[1]; |
242 | *skb_push(skb, 1) = resp_hdr[0]; | |
243 | } | |
244 | ||
245 | return skb; | |
246 | ||
247 | receive_error: | |
248 | kfree_skb(skb); | |
249 | ||
250 | return NULL; | |
251 | } | |
252 | ||
253 | static int nci_spi_check_crc(struct sk_buff *skb) | |
254 | { | |
255 | u16 crc_data = (skb->data[skb->len - 2] << 8) | | |
256 | skb->data[skb->len - 1]; | |
257 | int ret; | |
258 | ||
259 | ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN) | |
260 | == crc_data); | |
261 | ||
262 | skb_trim(skb, skb->len - NCI_SPI_CRC_LEN); | |
263 | ||
264 | return ret; | |
265 | } | |
266 | ||
267 | static u8 nci_spi_get_ack(struct sk_buff *skb) | |
268 | { | |
269 | u8 ret; | |
270 | ||
271 | ret = skb->data[0] >> NCI_SPI_ACK_SHIFT; | |
272 | ||
273 | /* Remove NFCC part of the header: ACK, NACK and MSB payload len */ | |
274 | skb_pull(skb, 2); | |
275 | ||
276 | return ret; | |
277 | } | |
278 | ||
279 | /** | |
22d4aae5 | 280 | * nci_spi_read - read frame from NCI SPI drivers |
391d8a2d | 281 | * |
fa544fff | 282 | * @nspi: The nci spi |
391d8a2d FD |
283 | * Context: can sleep |
284 | * | |
285 | * This call may only be used from a context that may sleep. The sleep | |
286 | * is non-interruptible, and has no timeout. | |
287 | * | |
22d4aae5 | 288 | * It returns an allocated skb containing the frame on success, or NULL. |
391d8a2d | 289 | */ |
22d4aae5 | 290 | struct sk_buff *nci_spi_read(struct nci_spi *nspi) |
391d8a2d FD |
291 | { |
292 | struct sk_buff *skb; | |
391d8a2d | 293 | |
391d8a2d | 294 | /* Retrieve frame from SPI */ |
22d4aae5 EL |
295 | skb = __nci_spi_read(nspi); |
296 | if (!skb) | |
391d8a2d | 297 | goto done; |
391d8a2d | 298 | |
fa544fff | 299 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { |
391d8a2d | 300 | if (!nci_spi_check_crc(skb)) { |
fa544fff | 301 | send_acknowledge(nspi, ACKNOWLEDGE_NACK); |
391d8a2d FD |
302 | goto done; |
303 | } | |
304 | ||
305 | /* In case of acknowledged mode: if ACK or NACK received, | |
306 | * unblock completion of latest frame sent. | |
307 | */ | |
fa544fff EL |
308 | nspi->req_result = nci_spi_get_ack(skb); |
309 | if (nspi->req_result) | |
310 | complete(&nspi->req_completion); | |
391d8a2d FD |
311 | } |
312 | ||
313 | /* If there is no payload (ACK/NACK only frame), | |
314 | * free the socket buffer | |
315 | */ | |
22d4aae5 | 316 | if (!skb->len) { |
391d8a2d | 317 | kfree_skb(skb); |
22d4aae5 | 318 | skb = NULL; |
391d8a2d FD |
319 | goto done; |
320 | } | |
321 | ||
fa544fff EL |
322 | if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) |
323 | send_acknowledge(nspi, ACKNOWLEDGE_ACK); | |
391d8a2d | 324 | |
391d8a2d | 325 | done: |
391d8a2d | 326 | |
22d4aae5 | 327 | return skb; |
391d8a2d | 328 | } |
22d4aae5 | 329 | EXPORT_SYMBOL_GPL(nci_spi_read); |
fcd9d046 VC |
330 | |
331 | MODULE_LICENSE("GPL"); |