diff options
author | Samuel Ortiz <sameo@linux.intel.com> | 2012-04-10 13:43:18 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-04-12 15:10:44 -0400 |
commit | 6ff73fd239ff5d6f1ebfe5b5f7f560d9fad7d749 (patch) | |
tree | 8c66777743df9ff8521c22bbe5efa73f26c65d6b /drivers/nfc | |
parent | 4849f85ee36db94033a7c8b32689458d6f435e80 (diff) |
NFC: pn533 Rx chaining support
When buffers on the receiption path exceed 262 bytes, the pn533 uses
a chaining mechanism where the initiator has to send NULL data frames
to fetch the remaining frames.
We do that from a workqueue context while holding the cmd lock. Once the
MI bit is gone, we aggregate the queued received skbs.
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/nfc')
-rw-r--r-- | drivers/nfc/pn533.c | 144 |
1 files changed, 128 insertions, 16 deletions
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index f2ee06f059f9..e6ec16d92e65 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c | |||
@@ -266,8 +266,11 @@ struct pn533 { | |||
266 | int in_maxlen; | 266 | int in_maxlen; |
267 | struct pn533_frame *in_frame; | 267 | struct pn533_frame *in_frame; |
268 | 268 | ||
269 | struct sk_buff_head resp_q; | ||
270 | |||
269 | struct workqueue_struct *wq; | 271 | struct workqueue_struct *wq; |
270 | struct work_struct cmd_work; | 272 | struct work_struct cmd_work; |
273 | struct work_struct mi_work; | ||
271 | struct pn533_frame *wq_in_frame; | 274 | struct pn533_frame *wq_in_frame; |
272 | int wq_in_error; | 275 | int wq_in_error; |
273 | 276 | ||
@@ -1256,6 +1259,8 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, u32 target_idx) | |||
1256 | 1259 | ||
1257 | dev->tgt_active_prot = 0; | 1260 | dev->tgt_active_prot = 0; |
1258 | 1261 | ||
1262 | skb_queue_purge(&dev->resp_q); | ||
1263 | |||
1259 | pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_RELEASE); | 1264 | pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_RELEASE); |
1260 | 1265 | ||
1261 | tg = 1; | 1266 | tg = 1; |
@@ -1454,11 +1459,49 @@ struct pn533_data_exchange_arg { | |||
1454 | void *cb_context; | 1459 | void *cb_context; |
1455 | }; | 1460 | }; |
1456 | 1461 | ||
1462 | static struct sk_buff *pn533_build_response(struct pn533 *dev) | ||
1463 | { | ||
1464 | struct sk_buff *skb, *tmp, *t; | ||
1465 | unsigned int skb_len = 0, tmp_len = 0; | ||
1466 | |||
1467 | nfc_dev_dbg(&dev->interface->dev, "%s\n", __func__); | ||
1468 | |||
1469 | if (skb_queue_empty(&dev->resp_q)) | ||
1470 | return NULL; | ||
1471 | |||
1472 | if (skb_queue_len(&dev->resp_q) == 1) { | ||
1473 | skb = skb_dequeue(&dev->resp_q); | ||
1474 | goto out; | ||
1475 | } | ||
1476 | |||
1477 | skb_queue_walk_safe(&dev->resp_q, tmp, t) | ||
1478 | skb_len += tmp->len; | ||
1479 | |||
1480 | nfc_dev_dbg(&dev->interface->dev, "%s total length %d\n", | ||
1481 | __func__, skb_len); | ||
1482 | |||
1483 | skb = alloc_skb(skb_len, GFP_KERNEL); | ||
1484 | if (skb == NULL) | ||
1485 | goto out; | ||
1486 | |||
1487 | skb_put(skb, skb_len); | ||
1488 | |||
1489 | skb_queue_walk_safe(&dev->resp_q, tmp, t) { | ||
1490 | memcpy(skb->data + tmp_len, tmp->data, tmp->len); | ||
1491 | tmp_len += tmp->len; | ||
1492 | } | ||
1493 | |||
1494 | out: | ||
1495 | skb_queue_purge(&dev->resp_q); | ||
1496 | |||
1497 | return skb; | ||
1498 | } | ||
1499 | |||
1457 | static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, | 1500 | static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, |
1458 | u8 *params, int params_len) | 1501 | u8 *params, int params_len) |
1459 | { | 1502 | { |
1460 | struct pn533_data_exchange_arg *arg = _arg; | 1503 | struct pn533_data_exchange_arg *arg = _arg; |
1461 | struct sk_buff *skb_resp = arg->skb_resp; | 1504 | struct sk_buff *skb = NULL, *skb_resp = arg->skb_resp; |
1462 | struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data; | 1505 | struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data; |
1463 | int err = 0; | 1506 | int err = 0; |
1464 | u8 status; | 1507 | u8 status; |
@@ -1466,15 +1509,13 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, | |||
1466 | 1509 | ||
1467 | nfc_dev_dbg(&dev->interface->dev, "%s", __func__); | 1510 | nfc_dev_dbg(&dev->interface->dev, "%s", __func__); |
1468 | 1511 | ||
1469 | dev_kfree_skb_irq(arg->skb_out); | 1512 | dev_kfree_skb(arg->skb_out); |
1470 | 1513 | ||
1471 | if (params_len < 0) { /* error */ | 1514 | if (params_len < 0) { /* error */ |
1472 | err = params_len; | 1515 | err = params_len; |
1473 | goto error; | 1516 | goto error; |
1474 | } | 1517 | } |
1475 | 1518 | ||
1476 | skb_put(skb_resp, PN533_FRAME_SIZE(in_frame)); | ||
1477 | |||
1478 | status = params[0]; | 1519 | status = params[0]; |
1479 | 1520 | ||
1480 | cmd_ret = status & PN533_CMD_RET_MASK; | 1521 | cmd_ret = status & PN533_CMD_RET_MASK; |
@@ -1485,25 +1526,27 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, | |||
1485 | goto error; | 1526 | goto error; |
1486 | } | 1527 | } |
1487 | 1528 | ||
1529 | skb_put(skb_resp, PN533_FRAME_SIZE(in_frame)); | ||
1530 | skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN); | ||
1531 | skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE); | ||
1532 | skb_queue_tail(&dev->resp_q, skb_resp); | ||
1533 | |||
1488 | if (status & PN533_CMD_MI_MASK) { | 1534 | if (status & PN533_CMD_MI_MASK) { |
1489 | /* TODO: Implement support to multi-part data exchange */ | 1535 | queue_work(dev->wq, &dev->mi_work); |
1490 | nfc_dev_err(&dev->interface->dev, "Multi-part message not yet" | 1536 | return -EINPROGRESS; |
1491 | " supported"); | ||
1492 | /* Prevent the other messages from controller */ | ||
1493 | pn533_send_ack(dev, GFP_ATOMIC); | ||
1494 | err = -ENOSYS; | ||
1495 | goto error; | ||
1496 | } | 1537 | } |
1497 | 1538 | ||
1498 | skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN); | 1539 | skb = pn533_build_response(dev); |
1499 | skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE); | 1540 | if (skb == NULL) |
1541 | goto error; | ||
1500 | 1542 | ||
1501 | arg->cb(arg->cb_context, skb_resp, 0); | 1543 | arg->cb(arg->cb_context, skb, 0); |
1502 | kfree(arg); | 1544 | kfree(arg); |
1503 | return 0; | 1545 | return 0; |
1504 | 1546 | ||
1505 | error: | 1547 | error: |
1506 | dev_kfree_skb_irq(skb_resp); | 1548 | skb_queue_purge(&dev->resp_q); |
1549 | dev_kfree_skb(skb_resp); | ||
1507 | arg->cb(arg->cb_context, NULL, err); | 1550 | arg->cb(arg->cb_context, NULL, err); |
1508 | kfree(arg); | 1551 | kfree(arg); |
1509 | return 0; | 1552 | return 0; |
@@ -1578,6 +1621,68 @@ error: | |||
1578 | return rc; | 1621 | return rc; |
1579 | } | 1622 | } |
1580 | 1623 | ||
1624 | static void pn533_wq_mi_recv(struct work_struct *work) | ||
1625 | { | ||
1626 | struct pn533 *dev = container_of(work, struct pn533, mi_work); | ||
1627 | struct sk_buff *skb_cmd; | ||
1628 | struct pn533_data_exchange_arg *arg = dev->cmd_complete_arg; | ||
1629 | struct pn533_frame *out_frame, *in_frame; | ||
1630 | struct sk_buff *skb_resp; | ||
1631 | int skb_resp_len; | ||
1632 | int rc; | ||
1633 | |||
1634 | nfc_dev_dbg(&dev->interface->dev, "%s", __func__); | ||
1635 | |||
1636 | /* This is a zero payload size skb */ | ||
1637 | skb_cmd = alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN + PN533_FRAME_TAIL_SIZE, | ||
1638 | GFP_KERNEL); | ||
1639 | if (skb_cmd == NULL) | ||
1640 | goto error_cmd; | ||
1641 | |||
1642 | skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN); | ||
1643 | |||
1644 | rc = pn533_data_exchange_tx_frame(dev, skb_cmd); | ||
1645 | if (rc) | ||
1646 | goto error_frame; | ||
1647 | |||
1648 | skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN + | ||
1649 | PN533_CMD_DATAEXCH_DATA_MAXLEN + | ||
1650 | PN533_FRAME_TAIL_SIZE; | ||
1651 | skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL); | ||
1652 | if (!skb_resp) { | ||
1653 | rc = -ENOMEM; | ||
1654 | goto error_frame; | ||
1655 | } | ||
1656 | |||
1657 | in_frame = (struct pn533_frame *) skb_resp->data; | ||
1658 | out_frame = (struct pn533_frame *) skb_cmd->data; | ||
1659 | |||
1660 | arg->skb_resp = skb_resp; | ||
1661 | arg->skb_out = skb_cmd; | ||
1662 | |||
1663 | rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, | ||
1664 | skb_resp_len, | ||
1665 | pn533_data_exchange_complete, | ||
1666 | dev->cmd_complete_arg, GFP_KERNEL); | ||
1667 | if (!rc) | ||
1668 | return; | ||
1669 | |||
1670 | nfc_dev_err(&dev->interface->dev, "Error %d when trying to" | ||
1671 | " perform data_exchange", rc); | ||
1672 | |||
1673 | kfree_skb(skb_resp); | ||
1674 | |||
1675 | error_frame: | ||
1676 | kfree_skb(skb_cmd); | ||
1677 | |||
1678 | error_cmd: | ||
1679 | pn533_send_ack(dev, GFP_KERNEL); | ||
1680 | |||
1681 | kfree(arg); | ||
1682 | |||
1683 | up(&dev->cmd_lock); | ||
1684 | } | ||
1685 | |||
1581 | static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, | 1686 | static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, |
1582 | u8 cfgdata_len) | 1687 | u8 cfgdata_len) |
1583 | { | 1688 | { |
@@ -1676,10 +1781,15 @@ static int pn533_probe(struct usb_interface *interface, | |||
1676 | pn533_send_complete, dev); | 1781 | pn533_send_complete, dev); |
1677 | 1782 | ||
1678 | INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete); | 1783 | INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete); |
1679 | dev->wq = create_singlethread_workqueue("pn533"); | 1784 | INIT_WORK(&dev->mi_work, pn533_wq_mi_recv); |
1785 | dev->wq = alloc_workqueue("pn533", | ||
1786 | WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM, | ||
1787 | 1); | ||
1680 | if (dev->wq == NULL) | 1788 | if (dev->wq == NULL) |
1681 | goto error; | 1789 | goto error; |
1682 | 1790 | ||
1791 | skb_queue_head_init(&dev->resp_q); | ||
1792 | |||
1683 | usb_set_intfdata(interface, dev); | 1793 | usb_set_intfdata(interface, dev); |
1684 | 1794 | ||
1685 | pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION); | 1795 | pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION); |
@@ -1756,6 +1866,8 @@ static void pn533_disconnect(struct usb_interface *interface) | |||
1756 | 1866 | ||
1757 | destroy_workqueue(dev->wq); | 1867 | destroy_workqueue(dev->wq); |
1758 | 1868 | ||
1869 | skb_queue_purge(&dev->resp_q); | ||
1870 | |||
1759 | kfree(dev->in_frame); | 1871 | kfree(dev->in_frame); |
1760 | usb_free_urb(dev->in_urb); | 1872 | usb_free_urb(dev->in_urb); |
1761 | kfree(dev->out_frame); | 1873 | kfree(dev->out_frame); |