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

Commit fa544fff authored by Eric Lapuyade's avatar Eric Lapuyade Committed by Samuel Ortiz
Browse files

NFC: NCI: Simplify NCI SPI to become a simple framing/checking layer



NCI SPI layer should not manage the nci dev, this is the job of the nci
chipset driver. This layer should be limited to frame/deframe nci
packets, and optionnaly check integrity (crc) and manage the ack/nak
protocol.

The NCI SPI must not be mixed up with an NCI dev. spi_[dev|device] are
therefore renamed to a simple spi for more clarity.
The header and crc sizes are moved to nci.h so that drivers can use
them to reserve space in outgoing skbs.
nci_spi_send() is exported to be accessible by drivers.

Signed-off-by: default avatarEric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent 08f13acf
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -166,6 +166,10 @@
#define NCI_GID_NFCEE_MGMT					0x2
#define NCI_GID_PROPRIETARY					0xf

/* ----- NCI over SPI head/crc(tail) room needed for outgoing frames ----- */
#define NCI_SPI_HDR_LEN						4
#define NCI_SPI_CRC_LEN						2

/* ---- NCI Packet structures ---- */
#define NCI_CTRL_HDR_SIZE					3
#define NCI_DATA_HDR_SIZE					3
+11 −30
Original line number Diff line number Diff line
@@ -207,16 +207,14 @@ int nci_to_errno(__u8 code);
#define NCI_SPI_CRC_ENABLED	0x01

/* ----- NCI SPI structures ----- */
struct nci_spi_dev;
struct nci_spi;

struct nci_spi_ops {
	int (*open)(struct nci_spi_dev *nsdev);
	int (*close)(struct nci_spi_dev *nsdev);
	void (*assert_int)(struct nci_spi_dev *nsdev);
	void (*deassert_int)(struct nci_spi_dev *nsdev);
	void (*assert_int)(struct nci_spi *nspi);
	void (*deassert_int)(struct nci_spi *nspi);
};

struct nci_spi_dev {
struct nci_spi {
	struct nci_dev		*ndev;
	struct spi_device	*spi;
	struct nci_spi_ops	*ops;
@@ -227,31 +225,14 @@ struct nci_spi_dev {

	struct completion	req_completion;
	u8			req_result;

	void			*driver_data;
};

/* ----- NCI SPI Devices ----- */
struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
/* ----- NCI SPI ----- */
struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
				     struct nci_spi_ops *ops,
						u32 supported_protocols,
						u32 supported_se,
						u8 acknowledge_mode,
						unsigned int delay);
void nci_spi_free_device(struct nci_spi_dev *nsdev);
int nci_spi_register_device(struct nci_spi_dev *nsdev);
void nci_spi_unregister_device(struct nci_spi_dev *nsdev);
int nci_spi_recv_frame(struct nci_spi_dev *nsdev);

static inline void nci_spi_set_drvdata(struct nci_spi_dev *nsdev,
					    void *data)
{
	nsdev->driver_data = data;
}

static inline void *nci_spi_get_drvdata(struct nci_spi_dev *nsdev)
{
	return nsdev->driver_data;
}
				     u8 acknowledge_mode, unsigned int delay,
				     struct nci_dev *ndev);
int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb);
int nci_spi_recv_frame(struct nci_spi *nspi);

#endif /* __NCI_CORE_H */
+55 −125
Original line number Diff line number Diff line
@@ -24,8 +24,6 @@
#include <linux/nfc.h>
#include <net/nfc/nci_core.h>

#define NCI_SPI_HDR_LEN			4
#define NCI_SPI_CRC_LEN			2
#define NCI_SPI_ACK_SHIFT		6
#define NCI_SPI_MSB_PAYLOAD_MASK	0x3F

@@ -41,21 +39,7 @@

#define CRC_INIT		0xFFFF

static int nci_spi_open(struct nci_dev *ndev)
{
	struct nci_spi_dev *nsdev = nci_get_drvdata(ndev);

	return nsdev->ops->open(nsdev);
}

static int nci_spi_close(struct nci_dev *ndev)
{
	struct nci_spi_dev *nsdev = nci_get_drvdata(ndev);

	return nsdev->ops->close(nsdev);
}

static int __nci_spi_send(struct nci_spi_dev *nsdev, struct sk_buff *skb)
static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
{
	struct spi_message m;
	struct spi_transfer t;
@@ -63,32 +47,31 @@ static int __nci_spi_send(struct nci_spi_dev *nsdev, struct sk_buff *skb)
	t.tx_buf = skb->data;
	t.len = skb->len;
	t.cs_change = 0;
	t.delay_usecs = nsdev->xfer_udelay;
	t.delay_usecs = nspi->xfer_udelay;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);

	return spi_sync(nsdev->spi, &m);
	return spi_sync(nspi->spi, &m);
}

static int nci_spi_send(struct nci_dev *ndev, struct sk_buff *skb)
int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb)
{
	struct nci_spi_dev *nsdev = nci_get_drvdata(ndev);
	unsigned int payload_len = skb->len;
	unsigned char *hdr;
	int ret;
	long completion_rc;

	nsdev->ops->deassert_int(nsdev);
	nspi->ops->deassert_int(nspi);

	/* add the NCI SPI header to the start of the buffer */
	hdr = skb_push(skb, NCI_SPI_HDR_LEN);
	hdr[0] = NCI_SPI_DIRECT_WRITE;
	hdr[1] = nsdev->acknowledge_mode;
	hdr[1] = nspi->acknowledge_mode;
	hdr[2] = payload_len >> 8;
	hdr[3] = payload_len & 0xFF;

	if (nsdev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
		u16 crc;

		crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
@@ -96,123 +79,70 @@ static int nci_spi_send(struct nci_dev *ndev, struct sk_buff *skb)
		*skb_put(skb, 1) = crc & 0xFF;
	}

	ret = __nci_spi_send(nsdev, skb);
	ret = __nci_spi_send(nspi, skb);

	kfree_skb(skb);
	nsdev->ops->assert_int(nsdev);
	nspi->ops->assert_int(nspi);

	if (ret != 0 || nsdev->acknowledge_mode == NCI_SPI_CRC_DISABLED)
	if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
		goto done;

	init_completion(&nsdev->req_completion);
	init_completion(&nspi->req_completion);
	completion_rc =	wait_for_completion_interruptible_timeout(
							&nsdev->req_completion,
							&nspi->req_completion,
							NCI_SPI_SEND_TIMEOUT);

	if (completion_rc <= 0 || nsdev->req_result == ACKNOWLEDGE_NACK)
	if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK)
		ret = -EIO;

done:
	return ret;
}

static struct nci_ops nci_spi_ops = {
	.open = nci_spi_open,
	.close = nci_spi_close,
	.send = nci_spi_send,
};
EXPORT_SYMBOL_GPL(nci_spi_send);

/* ---- Interface to NCI SPI drivers ---- */

/**
 * nci_spi_allocate_device - allocate a new nci spi device
 * nci_spi_allocate_spi - allocate a new nci spi
 *
 * @spi: SPI device
 * @ops: device operations
 * @supported_protocols: NFC protocols supported by the device
 * @supported_se: NFC Secure Elements supported by the device
 * @acknowledge_mode: Acknowledge mode used by the device
 * @acknowledge_mode: Acknowledge mode used by the NFC device
 * @delay: delay between transactions in us
 * @ndev: nci dev to send incoming nci frames to
 */
struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
				     struct nci_spi_ops *ops,
						u32 supported_protocols,
						u32 supported_se,
						u8 acknowledge_mode,
						unsigned int delay)
				     u8 acknowledge_mode, unsigned int delay,
				     struct nci_dev *ndev)
{
	struct nci_spi_dev *nsdev;
	int tailroom = 0;

	if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int)
		return NULL;

	if (!supported_protocols)
		return NULL;
	struct nci_spi *nspi;

	nsdev = devm_kzalloc(&spi->dev, sizeof(struct nci_spi_dev), GFP_KERNEL);
	if (!nsdev)
	if (!ops->assert_int || !ops->deassert_int)
		return NULL;

	nsdev->ops = ops;
	nsdev->acknowledge_mode = acknowledge_mode;
	nsdev->xfer_udelay = delay;

	if (acknowledge_mode == NCI_SPI_CRC_ENABLED)
		tailroom += NCI_SPI_CRC_LEN;

	nsdev->ndev = nci_allocate_device(&nci_spi_ops, supported_protocols,
					  NCI_SPI_HDR_LEN, tailroom);
	if (!nsdev->ndev)
	nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
	if (!nspi)
		return NULL;

	nci_set_drvdata(nsdev->ndev, nsdev);

	return nsdev;
}
EXPORT_SYMBOL_GPL(nci_spi_allocate_device);

/**
 * nci_spi_free_device - deallocate nci spi device
 *
 * @nsdev: The nci spi device to deallocate
 */
void nci_spi_free_device(struct nci_spi_dev *nsdev)
{
	nci_free_device(nsdev->ndev);
}
EXPORT_SYMBOL_GPL(nci_spi_free_device);
	nspi->ops = ops;
	nspi->acknowledge_mode = acknowledge_mode;
	nspi->xfer_udelay = delay;

/**
 * nci_spi_register_device - register a nci spi device in the nfc subsystem
 *
 * @pdev: The nci spi device to register
 */
int nci_spi_register_device(struct nci_spi_dev *nsdev)
{
	return nci_register_device(nsdev->ndev);
}
EXPORT_SYMBOL_GPL(nci_spi_register_device);
	nspi->ndev = ndev;

/**
 * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem
 *
 * @dev: The nci spi device to unregister
 */
void nci_spi_unregister_device(struct nci_spi_dev *nsdev)
{
	nci_unregister_device(nsdev->ndev);
	return nspi;
}
EXPORT_SYMBOL_GPL(nci_spi_unregister_device);
EXPORT_SYMBOL_GPL(nci_spi_allocate_spi);

static int send_acknowledge(struct nci_spi_dev *nsdev, u8 acknowledge)
static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
{
	struct sk_buff *skb;
	unsigned char *hdr;
	u16 crc;
	int ret;

	skb = nci_skb_alloc(nsdev->ndev, 0, GFP_KERNEL);
	skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL);

	/* add the NCI SPI header to the start of the buffer */
	hdr = skb_push(skb, NCI_SPI_HDR_LEN);
@@ -225,14 +155,14 @@ static int send_acknowledge(struct nci_spi_dev *nsdev, u8 acknowledge)
	*skb_put(skb, 1) = crc >> 8;
	*skb_put(skb, 1) = crc & 0xFF;

	ret = __nci_spi_send(nsdev, skb);
	ret = __nci_spi_send(nspi, skb);

	kfree_skb(skb);

	return ret;
}

static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *nsdev)
static struct sk_buff *__nci_spi_recv_frame(struct nci_spi *nspi)
{
	struct sk_buff *skb;
	struct spi_message m;
@@ -243,7 +173,7 @@ static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *nsdev)

	spi_message_init(&m);
	req[0] = NCI_SPI_DIRECT_READ;
	req[1] = nsdev->acknowledge_mode;
	req[1] = nspi->acknowledge_mode;
	tx.tx_buf = req;
	tx.len = 2;
	tx.cs_change = 0;
@@ -252,18 +182,18 @@ static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *nsdev)
	rx.len = 2;
	rx.cs_change = 1;
	spi_message_add_tail(&rx, &m);
	ret = spi_sync(nsdev->spi, &m);
	ret = spi_sync(nspi->spi, &m);

	if (ret)
		return NULL;

	if (nsdev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
		rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
				resp_hdr[1] + NCI_SPI_CRC_LEN;
	else
		rx_len = (resp_hdr[0] << 8) | resp_hdr[1];

	skb = nci_skb_alloc(nsdev->ndev, rx_len, GFP_KERNEL);
	skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL);
	if (!skb)
		return NULL;

@@ -271,14 +201,14 @@ static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *nsdev)
	rx.rx_buf = skb_put(skb, rx_len);
	rx.len = rx_len;
	rx.cs_change = 0;
	rx.delay_usecs = nsdev->xfer_udelay;
	rx.delay_usecs = nspi->xfer_udelay;
	spi_message_add_tail(&rx, &m);
	ret = spi_sync(nsdev->spi, &m);
	ret = spi_sync(nspi->spi, &m);

	if (ret)
		goto receive_error;

	if (nsdev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
		*skb_push(skb, 1) = resp_hdr[1];
		*skb_push(skb, 1) = resp_hdr[0];
	}
@@ -320,7 +250,7 @@ static u8 nci_spi_get_ack(struct sk_buff *skb)
/**
 * nci_spi_recv_frame - receive frame from NCI SPI drivers
 *
 * @nsdev: The nci spi device
 * @nspi: The nci spi
 * Context: can sleep
 *
 * This call may only be used from a context that may sleep.  The sleep
@@ -328,32 +258,32 @@ static u8 nci_spi_get_ack(struct sk_buff *skb)
 *
 * It returns zero on success, else a negative error code.
 */
int nci_spi_recv_frame(struct nci_spi_dev *nsdev)
int nci_spi_recv_frame(struct nci_spi *nspi)
{
	struct sk_buff *skb;
	int ret = 0;

	nsdev->ops->deassert_int(nsdev);
	nspi->ops->deassert_int(nspi);

	/* Retrieve frame from SPI */
	skb = __nci_spi_recv_frame(nsdev);
	skb = __nci_spi_recv_frame(nspi);
	if (!skb) {
		ret = -EIO;
		goto done;
	}

	if (nsdev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
		if (!nci_spi_check_crc(skb)) {
			send_acknowledge(nsdev, ACKNOWLEDGE_NACK);
			send_acknowledge(nspi, ACKNOWLEDGE_NACK);
			goto done;
		}

		/* In case of acknowledged mode: if ACK or NACK received,
		 * unblock completion of latest frame sent.
		 */
		nsdev->req_result = nci_spi_get_ack(skb);
		if (nsdev->req_result)
			complete(&nsdev->req_completion);
		nspi->req_result = nci_spi_get_ack(skb);
		if (nspi->req_result)
			complete(&nspi->req_completion);
	}

	/* If there is no payload (ACK/NACK only frame),
@@ -364,14 +294,14 @@ int nci_spi_recv_frame(struct nci_spi_dev *nsdev)
		goto done;
	}

	if (nsdev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
		send_acknowledge(nsdev, ACKNOWLEDGE_ACK);
	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
		send_acknowledge(nspi, ACKNOWLEDGE_ACK);

	/* Forward skb to NCI core layer */
	ret = nci_recv_frame(nsdev->ndev, skb);
	ret = nci_recv_frame(nspi->ndev, skb);

done:
	nsdev->ops->assert_int(nsdev);
	nspi->ops->assert_int(nspi);

	return ret;
}