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