aboutsummaryrefslogtreecommitdiffstats
path: root/net/nfc/nci/spi.c
diff options
context:
space:
mode:
authorFrederic Danis <frederic.danis@linux.intel.com>2013-05-29 09:35:04 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2013-06-14 07:44:16 -0400
commit391d8a2da787257aeaf952c974405b53926e3fb3 (patch)
tree038e24a044d3f3780c76d61a13f65c038e8c05bc /net/nfc/nci/spi.c
parentee9596d467e4d05c77a8c883aeeb5b74d1a3cd31 (diff)
NFC: Add NCI over SPI receive
Before any operation, driver interruption is de-asserted to prevent race condition between TX and RX. Transaction starts by emitting "Direct read" and acknowledged mode bytes. Then packet length is read allowing to allocate correct NCI socket buffer. After that payload is retrieved. A delay after the transaction can be added. This delay is determined by the driver during nci_spi_allocate_device() call and can be 0. If acknowledged mode is set: - CRC of header and payload is checked - if frame reception fails (CRC error): NACK is sent - if received frame has ACK or NACK flag: unblock nci_spi_send() Payload is passed to NCI module. At the end, driver interruption is re asserted. Signed-off-by: Frederic Danis <frederic.danis@linux.intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'net/nfc/nci/spi.c')
-rw-r--r--net/nfc/nci/spi.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c
index 6258461e6998..70afc387a965 100644
--- a/net/nfc/nci/spi.c
+++ b/net/nfc/nci/spi.c
@@ -26,6 +26,8 @@
26 26
27#define NCI_SPI_HDR_LEN 4 27#define NCI_SPI_HDR_LEN 4
28#define NCI_SPI_CRC_LEN 2 28#define NCI_SPI_CRC_LEN 2
29#define NCI_SPI_ACK_SHIFT 6
30#define NCI_SPI_MSB_PAYLOAD_MASK 0x3F
29 31
30#define NCI_SPI_SEND_TIMEOUT (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \ 32#define NCI_SPI_SEND_TIMEOUT (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \
31 NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT) 33 NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT)
@@ -203,3 +205,175 @@ void nci_spi_unregister_device(struct nci_spi_dev *ndev)
203 nci_unregister_device(ndev->nci_dev); 205 nci_unregister_device(ndev->nci_dev);
204} 206}
205EXPORT_SYMBOL_GPL(nci_spi_unregister_device); 207EXPORT_SYMBOL_GPL(nci_spi_unregister_device);
208
209static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
210{
211 struct sk_buff *skb;
212 unsigned char *hdr;
213 u16 crc;
214 int ret;
215
216 skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL);
217
218 /* add the NCI SPI header to the start of the buffer */
219 hdr = skb_push(skb, NCI_SPI_HDR_LEN);
220 hdr[0] = NCI_SPI_DIRECT_WRITE;
221 hdr[1] = NCI_SPI_CRC_ENABLED;
222 hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
223 hdr[3] = 0;
224
225 crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
226 *skb_put(skb, 1) = crc >> 8;
227 *skb_put(skb, 1) = crc & 0xFF;
228
229 ret = __nci_spi_send(ndev, skb);
230
231 kfree_skb(skb);
232
233 return ret;
234}
235
236static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
237{
238 struct sk_buff *skb;
239 struct spi_message m;
240 unsigned char req[2], resp_hdr[2];
241 struct spi_transfer tx, rx;
242 unsigned short rx_len = 0;
243 int ret;
244
245 spi_message_init(&m);
246 req[0] = NCI_SPI_DIRECT_READ;
247 req[1] = ndev->acknowledge_mode;
248 tx.tx_buf = req;
249 tx.len = 2;
250 tx.cs_change = 0;
251 spi_message_add_tail(&tx, &m);
252 rx.rx_buf = resp_hdr;
253 rx.len = 2;
254 rx.cs_change = 1;
255 spi_message_add_tail(&rx, &m);
256 ret = spi_sync(ndev->spi, &m);
257
258 if (ret)
259 return NULL;
260
261 if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
262 rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
263 resp_hdr[1] + NCI_SPI_CRC_LEN;
264 else
265 rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
266
267 skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL);
268 if (!skb)
269 return NULL;
270
271 spi_message_init(&m);
272 rx.rx_buf = skb_put(skb, rx_len);
273 rx.len = rx_len;
274 rx.cs_change = 0;
275 rx.delay_usecs = ndev->xfer_udelay;
276 spi_message_add_tail(&rx, &m);
277 ret = spi_sync(ndev->spi, &m);
278
279 if (ret)
280 goto receive_error;
281
282 if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
283 *skb_push(skb, 1) = resp_hdr[1];
284 *skb_push(skb, 1) = resp_hdr[0];
285 }
286
287 return skb;
288
289receive_error:
290 kfree_skb(skb);
291
292 return NULL;
293}
294
295static int nci_spi_check_crc(struct sk_buff *skb)
296{
297 u16 crc_data = (skb->data[skb->len - 2] << 8) |
298 skb->data[skb->len - 1];
299 int ret;
300
301 ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
302 == crc_data);
303
304 skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
305
306 return ret;
307}
308
309static u8 nci_spi_get_ack(struct sk_buff *skb)
310{
311 u8 ret;
312
313 ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
314
315 /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
316 skb_pull(skb, 2);
317
318 return ret;
319}
320
321/**
322 * nci_spi_recv_frame - receive frame from NCI SPI drivers
323 *
324 * @ndev: The nci spi device
325 * Context: can sleep
326 *
327 * This call may only be used from a context that may sleep. The sleep
328 * is non-interruptible, and has no timeout.
329 *
330 * It returns zero on success, else a negative error code.
331 */
332int nci_spi_recv_frame(struct nci_spi_dev *ndev)
333{
334 struct sk_buff *skb;
335 int ret = 0;
336
337 ndev->ops->deassert_int(ndev);
338
339 /* Retrieve frame from SPI */
340 skb = __nci_spi_recv_frame(ndev);
341 if (!skb) {
342 ret = -EIO;
343 goto done;
344 }
345
346 if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
347 if (!nci_spi_check_crc(skb)) {
348 send_acknowledge(ndev, ACKNOWLEDGE_NACK);
349 goto done;
350 }
351
352 /* In case of acknowledged mode: if ACK or NACK received,
353 * unblock completion of latest frame sent.
354 */
355 ndev->req_result = nci_spi_get_ack(skb);
356 if (ndev->req_result)
357 complete(&ndev->req_completion);
358 }
359
360 /* If there is no payload (ACK/NACK only frame),
361 * free the socket buffer
362 */
363 if (skb->len == 0) {
364 kfree_skb(skb);
365 goto done;
366 }
367
368 if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
369 send_acknowledge(ndev, ACKNOWLEDGE_ACK);
370
371 /* Forward skb to NCI core layer */
372 ret = nci_recv_frame(ndev->nci_dev, skb);
373
374done:
375 ndev->ops->assert_int(ndev);
376
377 return ret;
378}
379EXPORT_SYMBOL_GPL(nci_spi_recv_frame);