NFC: pn533: Del frame logic from Data Exchange cmd

Remove frame logic from transceive cb using new iface
for async send.

For pn533_wq_mi_recv() use pn533_send_cmd_direct_async which
sends the cmd directly to the hardware, skipping cmd queue.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Waldemar Rymarkiewicz 2012-12-10 14:42:46 +01:00 committed by Samuel Ortiz
parent 13003649b1
commit b1e666f503
1 changed files with 143 additions and 170 deletions

View File

@ -360,6 +360,7 @@ struct pn533 {
pn533_cmd_complete_t cmd_complete; pn533_cmd_complete_t cmd_complete;
void *cmd_complete_arg; void *cmd_complete_arg;
void *cmd_complete_mi_arg;
struct mutex cmd_lock; struct mutex cmd_lock;
u8 cmd; u8 cmd;
@ -848,6 +849,57 @@ static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code,
return rc; return rc;
} }
/*
* pn533_send_cmd_direct_async
*
* The function sends a piority cmd directly to the chip omiting the cmd
* queue. It's intended to be used by chaining mechanism of received responses
* where the host has to request every single chunk of data before scheduling
* next cmd from the queue.
*/
static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code,
struct sk_buff *req,
pn533_send_async_complete_t complete_cb,
void *complete_cb_context)
{
struct pn533_send_async_complete_arg *arg;
struct sk_buff *resp;
int rc;
int resp_len = PN533_FRAME_HEADER_LEN +
PN533_FRAME_MAX_PAYLOAD_LEN +
PN533_FRAME_TAIL_LEN;
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
resp = alloc_skb(resp_len, GFP_KERNEL);
if (!resp)
return -ENOMEM;
arg = kzalloc(sizeof(arg), GFP_KERNEL);
if (!arg) {
dev_kfree_skb(resp);
return -ENOMEM;
}
arg->complete_cb = complete_cb;
arg->complete_cb_context = complete_cb_context;
arg->resp = resp;
arg->req = req;
pn533_build_cmd_frame(cmd_code, req);
rc = __pn533_send_cmd_frame_async(dev, (struct pn533_frame *)req->data,
(struct pn533_frame *)resp->data,
resp_len, pn533_send_async_complete,
arg);
if (rc < 0) {
dev_kfree_skb(resp);
kfree(arg);
}
return rc;
}
static void pn533_wq_cmd(struct work_struct *work) static void pn533_wq_cmd(struct work_struct *work)
{ {
struct pn533 *dev = container_of(work, struct pn533, cmd_work); struct pn533 *dev = container_of(work, struct pn533, cmd_work);
@ -2024,69 +2076,7 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
return 0; return 0;
} }
static int pn533_build_tx_frame(struct pn533 *dev, struct sk_buff *skb,
bool target)
{
int payload_len = skb->len;
struct pn533_frame *out_frame;
u8 tg;
nfc_dev_dbg(&dev->interface->dev, "%s - Sending %d bytes", __func__,
payload_len);
if (payload_len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
/* TODO: Implement support to multi-part data exchange */
nfc_dev_err(&dev->interface->dev, "Data length greater than the"
" max allowed: %d",
PN533_CMD_DATAEXCH_DATA_MAXLEN);
return -ENOSYS;
}
skb_push(skb, PN533_FRAME_HEADER_LEN);
if (target == true) {
switch (dev->device_type) {
case PN533_DEVICE_PASORI:
if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
out_frame = (struct pn533_frame *) skb->data;
pn533_tx_frame_init(out_frame,
PN533_CMD_IN_COMM_THRU);
break;
}
default:
skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN);
out_frame = (struct pn533_frame *) skb->data;
pn533_tx_frame_init(out_frame,
PN533_CMD_IN_DATA_EXCHANGE);
tg = 1;
memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame),
&tg, sizeof(u8));
out_frame->datalen += sizeof(u8);
break;
}
} else {
skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1);
out_frame = (struct pn533_frame *) skb->data;
pn533_tx_frame_init(out_frame, PN533_CMD_TG_SET_DATA);
}
/* The data is already in the out_frame, just update the datalen */
out_frame->datalen += payload_len;
pn533_tx_frame_finish(out_frame);
skb_put(skb, PN533_FRAME_TAIL_LEN);
return 0;
}
struct pn533_data_exchange_arg { struct pn533_data_exchange_arg {
struct sk_buff *skb_resp;
struct sk_buff *skb_out;
data_exchange_cb_t cb; data_exchange_cb_t cb;
void *cb_context; void *cb_context;
}; };
@ -2130,47 +2120,44 @@ out:
} }
static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
u8 *params, int params_len) struct sk_buff *resp)
{ {
struct pn533_data_exchange_arg *arg = _arg; struct pn533_data_exchange_arg *arg = _arg;
struct sk_buff *skb = NULL, *skb_resp = arg->skb_resp; struct sk_buff *skb;
struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data; int rc = 0;
int err = 0; u8 status, ret, mi;
u8 status;
u8 cmd_ret;
nfc_dev_dbg(&dev->interface->dev, "%s", __func__); nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
dev_kfree_skb(arg->skb_out); if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
goto _error;
}
if (params_len < 0) { /* error */ status = resp->data[0];
err = params_len; ret = status & PN533_CMD_RET_MASK;
mi = status & PN533_CMD_MI_MASK;
skb_pull(resp, sizeof(status));
if (ret != PN533_CMD_RET_SUCCESS) {
nfc_dev_err(&dev->interface->dev,
"PN533 reported error %d when exchanging data",
ret);
rc = -EIO;
goto error; goto error;
} }
status = params[0]; skb_queue_tail(&dev->resp_q, resp);
cmd_ret = status & PN533_CMD_RET_MASK; if (mi) {
if (cmd_ret != PN533_CMD_RET_SUCCESS) { dev->cmd_complete_mi_arg = arg;
nfc_dev_err(&dev->interface->dev, "PN533 reported error %d when"
" exchanging data", cmd_ret);
err = -EIO;
goto error;
}
skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
skb_pull(skb_resp, PN533_FRAME_HEADER_LEN);
skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_LEN);
skb_queue_tail(&dev->resp_q, skb_resp);
if (status & PN533_CMD_MI_MASK) {
queue_work(dev->wq, &dev->mi_work); queue_work(dev->wq, &dev->mi_work);
return -EINPROGRESS; return -EINPROGRESS;
} }
skb = pn533_build_response(dev); skb = pn533_build_response(dev);
if (skb == NULL) if (!skb)
goto error; goto error;
arg->cb(arg->cb_context, skb, 0); arg->cb(arg->cb_context, skb, 0);
@ -2178,11 +2165,12 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
return 0; return 0;
error: error:
dev_kfree_skb(resp);
_error:
skb_queue_purge(&dev->resp_q); skb_queue_purge(&dev->resp_q);
dev_kfree_skb(skb_resp); arg->cb(arg->cb_context, NULL, rc);
arg->cb(arg->cb_context, NULL, err);
kfree(arg); kfree(arg);
return 0; return rc;
} }
static int pn533_transceive(struct nfc_dev *nfc_dev, static int pn533_transceive(struct nfc_dev *nfc_dev,
@ -2190,14 +2178,20 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
data_exchange_cb_t cb, void *cb_context) data_exchange_cb_t cb, void *cb_context)
{ {
struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct pn533 *dev = nfc_get_drvdata(nfc_dev);
struct pn533_frame *out_frame, *in_frame; struct pn533_data_exchange_arg *arg = NULL;
struct pn533_data_exchange_arg *arg;
struct sk_buff *skb_resp;
int skb_resp_len;
int rc; int rc;
nfc_dev_dbg(&dev->interface->dev, "%s", __func__); nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
/* TODO: Implement support to multi-part data exchange */
nfc_dev_err(&dev->interface->dev,
"Data length greater than the max allowed: %d",
PN533_CMD_DATAEXCH_DATA_MAXLEN);
rc = -ENOSYS;
goto error;
}
if (!dev->tgt_active_prot) { if (!dev->tgt_active_prot) {
nfc_dev_err(&dev->interface->dev, "Cannot exchange data if" nfc_dev_err(&dev->interface->dev, "Cannot exchange data if"
" there is no active target"); " there is no active target");
@ -2205,51 +2199,43 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
goto error; goto error;
} }
rc = pn533_build_tx_frame(dev, skb, true); arg = kmalloc(sizeof(*arg), GFP_KERNEL);
if (rc)
goto error;
skb_resp_len = PN533_FRAME_HEADER_LEN +
PN533_CMD_DATAEXCH_HEAD_LEN +
PN533_CMD_DATAEXCH_DATA_MAXLEN +
PN533_FRAME_TAIL_LEN;
skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL);
if (!skb_resp) {
rc = -ENOMEM;
goto error;
}
in_frame = (struct pn533_frame *) skb_resp->data;
out_frame = (struct pn533_frame *) skb->data;
arg = kmalloc(sizeof(struct pn533_data_exchange_arg), GFP_KERNEL);
if (!arg) { if (!arg) {
rc = -ENOMEM; rc = -ENOMEM;
goto free_skb_resp; goto error;
} }
arg->skb_resp = skb_resp;
arg->skb_out = skb;
arg->cb = cb; arg->cb = cb;
arg->cb_context = cb_context; arg->cb_context = cb_context;
rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, skb_resp_len, switch (dev->device_type) {
pn533_data_exchange_complete, arg); case PN533_DEVICE_PASORI:
if (rc) { if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
nfc_dev_err(&dev->interface->dev, "Error %d when trying to" rc = pn533_send_data_async(dev, PN533_CMD_IN_COMM_THRU,
" perform data_exchange", rc); skb,
goto free_arg; pn533_data_exchange_complete,
arg);
break;
} }
default:
*skb_push(skb, sizeof(u8)) = 1; /*TG*/
rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
skb, pn533_data_exchange_complete,
arg);
break;
}
if (rc < 0) /* rc from send_async */
goto error;
return 0; return 0;
free_arg:
kfree(arg);
free_skb_resp:
kfree_skb(skb_resp);
error: error:
kfree_skb(skb); kfree(arg);
dev_kfree_skb(skb);
return rc; return rc;
} }
@ -2305,63 +2291,50 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
static void pn533_wq_mi_recv(struct work_struct *work) static void pn533_wq_mi_recv(struct work_struct *work)
{ {
struct pn533 *dev = container_of(work, struct pn533, mi_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 sk_buff *skb;
struct pn533_frame *out_frame, *in_frame;
struct sk_buff *skb_resp;
int skb_resp_len;
int rc; int rc;
nfc_dev_dbg(&dev->interface->dev, "%s", __func__); nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
/* This is a zero payload size skb */ skb = pn533_alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN);
skb_cmd = pn533_alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN); if (!skb)
if (skb_cmd == NULL) goto error;
goto error_cmd;
skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN); switch (dev->device_type) {
case PN533_DEVICE_PASORI:
if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
rc = pn533_send_cmd_direct_async(dev,
PN533_CMD_IN_COMM_THRU,
skb,
pn533_data_exchange_complete,
dev->cmd_complete_mi_arg);
rc = pn533_build_tx_frame(dev, skb_cmd, true); break;
if (rc) }
goto error_frame; default:
*skb_put(skb, sizeof(u8)) = 1; /*TG*/
skb_resp_len = PN533_FRAME_HEADER_LEN + rc = pn533_send_cmd_direct_async(dev,
PN533_CMD_DATAEXCH_HEAD_LEN + PN533_CMD_IN_DATA_EXCHANGE,
PN533_CMD_DATAEXCH_DATA_MAXLEN + skb,
PN533_FRAME_TAIL_LEN; pn533_data_exchange_complete,
dev->cmd_complete_mi_arg);
skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL); break;
if (!skb_resp) {
rc = -ENOMEM;
goto error_frame;
} }
in_frame = (struct pn533_frame *) skb_resp->data; if (rc == 0) /* success */
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);
if (!rc)
return; return;
nfc_dev_err(&dev->interface->dev, "Error %d when trying to" nfc_dev_err(&dev->interface->dev,
" perform data_exchange", rc); "Error %d when trying to perform data_exchange", rc);
kfree_skb(skb_resp); dev_kfree_skb(skb);
kfree(dev->cmd_complete_arg);
error_frame: error:
kfree_skb(skb_cmd);
error_cmd:
pn533_send_ack(dev, GFP_KERNEL); pn533_send_ack(dev, GFP_KERNEL);
kfree(arg);
queue_work(dev->wq, &dev->cmd_work); queue_work(dev->wq, &dev->cmd_work);
} }