|
@@ -266,8 +266,11 @@ struct pn533 {
|
|
|
int in_maxlen;
|
|
|
struct pn533_frame *in_frame;
|
|
|
|
|
|
+ struct sk_buff_head resp_q;
|
|
|
+
|
|
|
struct workqueue_struct *wq;
|
|
|
struct work_struct cmd_work;
|
|
|
+ struct work_struct mi_work;
|
|
|
struct pn533_frame *wq_in_frame;
|
|
|
int wq_in_error;
|
|
|
|
|
@@ -1256,6 +1259,8 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, u32 target_idx)
|
|
|
|
|
|
dev->tgt_active_prot = 0;
|
|
|
|
|
|
+ skb_queue_purge(&dev->resp_q);
|
|
|
+
|
|
|
pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_RELEASE);
|
|
|
|
|
|
tg = 1;
|
|
@@ -1454,11 +1459,49 @@ struct pn533_data_exchange_arg {
|
|
|
void *cb_context;
|
|
|
};
|
|
|
|
|
|
+static struct sk_buff *pn533_build_response(struct pn533 *dev)
|
|
|
+{
|
|
|
+ struct sk_buff *skb, *tmp, *t;
|
|
|
+ unsigned int skb_len = 0, tmp_len = 0;
|
|
|
+
|
|
|
+ nfc_dev_dbg(&dev->interface->dev, "%s\n", __func__);
|
|
|
+
|
|
|
+ if (skb_queue_empty(&dev->resp_q))
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ if (skb_queue_len(&dev->resp_q) == 1) {
|
|
|
+ skb = skb_dequeue(&dev->resp_q);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ skb_queue_walk_safe(&dev->resp_q, tmp, t)
|
|
|
+ skb_len += tmp->len;
|
|
|
+
|
|
|
+ nfc_dev_dbg(&dev->interface->dev, "%s total length %d\n",
|
|
|
+ __func__, skb_len);
|
|
|
+
|
|
|
+ skb = alloc_skb(skb_len, GFP_KERNEL);
|
|
|
+ if (skb == NULL)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ skb_put(skb, skb_len);
|
|
|
+
|
|
|
+ skb_queue_walk_safe(&dev->resp_q, tmp, t) {
|
|
|
+ memcpy(skb->data + tmp_len, tmp->data, tmp->len);
|
|
|
+ tmp_len += tmp->len;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ skb_queue_purge(&dev->resp_q);
|
|
|
+
|
|
|
+ return skb;
|
|
|
+}
|
|
|
+
|
|
|
static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
|
|
|
u8 *params, int params_len)
|
|
|
{
|
|
|
struct pn533_data_exchange_arg *arg = _arg;
|
|
|
- struct sk_buff *skb_resp = arg->skb_resp;
|
|
|
+ struct sk_buff *skb = NULL, *skb_resp = arg->skb_resp;
|
|
|
struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
|
|
|
int err = 0;
|
|
|
u8 status;
|
|
@@ -1466,15 +1509,13 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
|
|
|
|
|
|
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
|
|
|
|
|
|
- dev_kfree_skb_irq(arg->skb_out);
|
|
|
+ dev_kfree_skb(arg->skb_out);
|
|
|
|
|
|
if (params_len < 0) { /* error */
|
|
|
err = params_len;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
- skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
|
|
|
-
|
|
|
status = params[0];
|
|
|
|
|
|
cmd_ret = status & PN533_CMD_RET_MASK;
|
|
@@ -1485,25 +1526,27 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
+ skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
|
|
|
+ skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
|
|
|
+ skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
|
|
|
+ skb_queue_tail(&dev->resp_q, skb_resp);
|
|
|
+
|
|
|
if (status & PN533_CMD_MI_MASK) {
|
|
|
- /* TODO: Implement support to multi-part data exchange */
|
|
|
- nfc_dev_err(&dev->interface->dev, "Multi-part message not yet"
|
|
|
- " supported");
|
|
|
- /* Prevent the other messages from controller */
|
|
|
- pn533_send_ack(dev, GFP_ATOMIC);
|
|
|
- err = -ENOSYS;
|
|
|
- goto error;
|
|
|
+ queue_work(dev->wq, &dev->mi_work);
|
|
|
+ return -EINPROGRESS;
|
|
|
}
|
|
|
|
|
|
- skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
|
|
|
- skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
|
|
|
+ skb = pn533_build_response(dev);
|
|
|
+ if (skb == NULL)
|
|
|
+ goto error;
|
|
|
|
|
|
- arg->cb(arg->cb_context, skb_resp, 0);
|
|
|
+ arg->cb(arg->cb_context, skb, 0);
|
|
|
kfree(arg);
|
|
|
return 0;
|
|
|
|
|
|
error:
|
|
|
- dev_kfree_skb_irq(skb_resp);
|
|
|
+ skb_queue_purge(&dev->resp_q);
|
|
|
+ dev_kfree_skb(skb_resp);
|
|
|
arg->cb(arg->cb_context, NULL, err);
|
|
|
kfree(arg);
|
|
|
return 0;
|
|
@@ -1578,6 +1621,68 @@ error:
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+static void pn533_wq_mi_recv(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct pn533 *dev = container_of(work, struct pn533, mi_work);
|
|
|
+ struct sk_buff *skb_cmd;
|
|
|
+ struct pn533_data_exchange_arg *arg = dev->cmd_complete_arg;
|
|
|
+ struct pn533_frame *out_frame, *in_frame;
|
|
|
+ struct sk_buff *skb_resp;
|
|
|
+ int skb_resp_len;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
|
|
|
+
|
|
|
+ /* This is a zero payload size skb */
|
|
|
+ skb_cmd = alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN + PN533_FRAME_TAIL_SIZE,
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (skb_cmd == NULL)
|
|
|
+ goto error_cmd;
|
|
|
+
|
|
|
+ skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN);
|
|
|
+
|
|
|
+ rc = pn533_data_exchange_tx_frame(dev, skb_cmd);
|
|
|
+ if (rc)
|
|
|
+ goto error_frame;
|
|
|
+
|
|
|
+ skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
|
|
|
+ PN533_CMD_DATAEXCH_DATA_MAXLEN +
|
|
|
+ PN533_FRAME_TAIL_SIZE;
|
|
|
+ skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL);
|
|
|
+ if (!skb_resp) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto error_frame;
|
|
|
+ }
|
|
|
+
|
|
|
+ in_frame = (struct pn533_frame *) skb_resp->data;
|
|
|
+ out_frame = (struct pn533_frame *) skb_cmd->data;
|
|
|
+
|
|
|
+ arg->skb_resp = skb_resp;
|
|
|
+ arg->skb_out = skb_cmd;
|
|
|
+
|
|
|
+ rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
|
|
|
+ skb_resp_len,
|
|
|
+ pn533_data_exchange_complete,
|
|
|
+ dev->cmd_complete_arg, GFP_KERNEL);
|
|
|
+ if (!rc)
|
|
|
+ return;
|
|
|
+
|
|
|
+ nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
|
|
|
+ " perform data_exchange", rc);
|
|
|
+
|
|
|
+ kfree_skb(skb_resp);
|
|
|
+
|
|
|
+error_frame:
|
|
|
+ kfree_skb(skb_cmd);
|
|
|
+
|
|
|
+error_cmd:
|
|
|
+ pn533_send_ack(dev, GFP_KERNEL);
|
|
|
+
|
|
|
+ kfree(arg);
|
|
|
+
|
|
|
+ up(&dev->cmd_lock);
|
|
|
+}
|
|
|
+
|
|
|
static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
|
|
|
u8 cfgdata_len)
|
|
|
{
|
|
@@ -1676,10 +1781,15 @@ static int pn533_probe(struct usb_interface *interface,
|
|
|
pn533_send_complete, dev);
|
|
|
|
|
|
INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete);
|
|
|
- dev->wq = create_singlethread_workqueue("pn533");
|
|
|
+ INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
|
|
|
+ dev->wq = alloc_workqueue("pn533",
|
|
|
+ WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
|
|
|
+ 1);
|
|
|
if (dev->wq == NULL)
|
|
|
goto error;
|
|
|
|
|
|
+ skb_queue_head_init(&dev->resp_q);
|
|
|
+
|
|
|
usb_set_intfdata(interface, dev);
|
|
|
|
|
|
pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION);
|
|
@@ -1756,6 +1866,8 @@ static void pn533_disconnect(struct usb_interface *interface)
|
|
|
|
|
|
destroy_workqueue(dev->wq);
|
|
|
|
|
|
+ skb_queue_purge(&dev->resp_q);
|
|
|
+
|
|
|
kfree(dev->in_frame);
|
|
|
usb_free_urb(dev->in_urb);
|
|
|
kfree(dev->out_frame);
|