Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1e666f5 authored by Waldemar Rymarkiewicz's avatar Waldemar Rymarkiewicz Committed by Samuel Ortiz
Browse files

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: default avatarWaldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent 13003649
Loading
Loading
Loading
Loading
+141 −168
Original line number Diff line number Diff line
@@ -360,6 +360,7 @@ struct pn533 {

	pn533_cmd_complete_t cmd_complete;
	void *cmd_complete_arg;
	void *cmd_complete_mi_arg;
	struct mutex cmd_lock;
	u8 cmd;

@@ -848,6 +849,57 @@ static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code,
	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)
{
	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;
}

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 sk_buff *skb_resp;
	struct sk_buff *skb_out;
	data_exchange_cb_t cb;
	void *cb_context;
};
@@ -2130,47 +2120,44 @@ out:
}

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 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;
	u8 cmd_ret;
	struct sk_buff *skb;
	int rc = 0;
	u8 status, ret, mi;

	nfc_dev_dbg(&dev->interface->dev, "%s", __func__);

	dev_kfree_skb(arg->skb_out);

	if (params_len < 0) { /* error */
		err = params_len;
		goto error;
	if (IS_ERR(resp)) {
		rc = PTR_ERR(resp);
		goto _error;
	}

	status = params[0];
	status = resp->data[0];
	ret = status & PN533_CMD_RET_MASK;
	mi = status & PN533_CMD_MI_MASK;

	skb_pull(resp, sizeof(status));

	cmd_ret = status & PN533_CMD_RET_MASK;
	if (cmd_ret != PN533_CMD_RET_SUCCESS) {
		nfc_dev_err(&dev->interface->dev, "PN533 reported error %d when"
						" exchanging data", cmd_ret);
		err = -EIO;
	if (ret != PN533_CMD_RET_SUCCESS) {
		nfc_dev_err(&dev->interface->dev,
			    "PN533 reported error %d when exchanging data",
			    ret);
		rc = -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);
	skb_queue_tail(&dev->resp_q, resp);

	if (status & PN533_CMD_MI_MASK) {
	if (mi) {
		dev->cmd_complete_mi_arg = arg;
		queue_work(dev->wq, &dev->mi_work);
		return -EINPROGRESS;
	}

	skb = pn533_build_response(dev);
	if (skb == NULL)
	if (!skb)
		goto error;

	arg->cb(arg->cb_context, skb, 0);
@@ -2178,11 +2165,12 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
	return 0;

error:
	dev_kfree_skb(resp);
_error:
	skb_queue_purge(&dev->resp_q);
	dev_kfree_skb(skb_resp);
	arg->cb(arg->cb_context, NULL, err);
	arg->cb(arg->cb_context, NULL, rc);
	kfree(arg);
	return 0;
	return rc;
}

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)
{
	struct pn533 *dev = nfc_get_drvdata(nfc_dev);
	struct pn533_frame *out_frame, *in_frame;
	struct pn533_data_exchange_arg *arg;
	struct sk_buff *skb_resp;
	int skb_resp_len;
	struct pn533_data_exchange_arg *arg = NULL;
	int rc;

	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) {
		nfc_dev_err(&dev->interface->dev, "Cannot exchange data if"
						" there is no active target");
@@ -2205,51 +2199,43 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
		goto error;
	}

	rc = pn533_build_tx_frame(dev, skb, true);
	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) {
	arg = kmalloc(sizeof(*arg), GFP_KERNEL);
	if (!arg) {
		rc = -ENOMEM;
		goto error;
	}

	in_frame = (struct pn533_frame *) skb_resp->data;
	out_frame = (struct pn533_frame *) skb->data;
	arg->cb = cb;
	arg->cb_context = cb_context;

	switch (dev->device_type) {
	case PN533_DEVICE_PASORI:
		if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
			rc = pn533_send_data_async(dev, PN533_CMD_IN_COMM_THRU,
						   skb,
						   pn533_data_exchange_complete,
						   arg);

	arg = kmalloc(sizeof(struct pn533_data_exchange_arg), GFP_KERNEL);
	if (!arg) {
		rc = -ENOMEM;
		goto free_skb_resp;
			break;
		}
	default:
		*skb_push(skb, sizeof(u8)) =  1; /*TG*/

	arg->skb_resp = skb_resp;
	arg->skb_out = skb;
	arg->cb = cb;
	arg->cb_context = cb_context;
		rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
					   skb, pn533_data_exchange_complete,
					   arg);

	rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, skb_resp_len,
					pn533_data_exchange_complete, arg);
	if (rc) {
		nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
						" perform data_exchange", rc);
		goto free_arg;
		break;
	}

	if (rc < 0) /* rc from send_async */
		goto error;

	return 0;

free_arg:
	kfree(arg);
free_skb_resp:
	kfree_skb(skb_resp);
error:
	kfree_skb(skb);
	kfree(arg);
	dev_kfree_skb(skb);
	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)
{
	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;

	struct sk_buff *skb;
	int rc;

	nfc_dev_dbg(&dev->interface->dev, "%s", __func__);

	/* This is a zero payload size skb */
	skb_cmd = pn533_alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN);
	if (skb_cmd == NULL)
		goto error_cmd;

	skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN);

	rc = pn533_build_tx_frame(dev, skb_cmd, true);
	if (rc)
		goto error_frame;
	skb = pn533_alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN);
	if (!skb)
		goto error;

	skb_resp_len = PN533_FRAME_HEADER_LEN +
		       PN533_CMD_DATAEXCH_HEAD_LEN +
		       PN533_CMD_DATAEXCH_DATA_MAXLEN +
		       PN533_FRAME_TAIL_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);

	skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL);
	if (!skb_resp) {
		rc = -ENOMEM;
		goto error_frame;
			break;
		}
	default:
		*skb_put(skb, sizeof(u8)) =  1; /*TG*/

	in_frame = (struct pn533_frame *) skb_resp->data;
	out_frame = (struct pn533_frame *) skb_cmd->data;
		rc = pn533_send_cmd_direct_async(dev,
						 PN533_CMD_IN_DATA_EXCHANGE,
						 skb,
						 pn533_data_exchange_complete,
						 dev->cmd_complete_mi_arg);

	arg->skb_resp = skb_resp;
	arg->skb_out = skb_cmd;
		break;
	}

	rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
					  skb_resp_len,
					  pn533_data_exchange_complete,
					  dev->cmd_complete_arg);
	if (!rc)
	if (rc == 0) /* success */
		return;

	nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
						" perform data_exchange", rc);

	kfree_skb(skb_resp);
	nfc_dev_err(&dev->interface->dev,
		    "Error %d when trying to perform data_exchange", rc);

error_frame:
	kfree_skb(skb_cmd);
	dev_kfree_skb(skb);
	kfree(dev->cmd_complete_arg);

error_cmd:
error:
	pn533_send_ack(dev, GFP_KERNEL);

	kfree(arg);

	queue_work(dev->wq, &dev->cmd_work);
}