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