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