Commit cf3c7c7b authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'add-nci-suit-and-virtual-nci-device-driver'

Bongsu Jeon says:

====================
Add nci suit and virtual nci device driver

1/2 is the Virtual NCI device driver.
2/2 is the NCI selftest suite
====================

Link: https://lore.kernel.org/r/20210127130829.4026-1-bongsu.jeon@samsung.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 8c224751 f595cf12
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -12540,6 +12540,14 @@ F: include/net/nfc/
F:	include/uapi/linux/nfc.h
F:	net/nfc/
NFC VIRTUAL NCI DEVICE DRIVER
M:	Bongsu Jeon <bongsu.jeon@samsung.com>
L:	netdev@vger.kernel.org
L:	linux-nfc@lists.01.org (moderated for non-subscribers)
S:	Supported
F:	drivers/nfc/virtual_ncidev.c
F:	tools/testing/selftests/nci/
NFS, SUNRPC, AND LOCKD CLIENTS
M:	Trond Myklebust <trond.myklebust@hammerspace.com>
M:	Anna Schumaker <anna.schumaker@netapp.com>
+11 −0
Original line number Diff line number Diff line
@@ -49,6 +49,17 @@ config NFC_PORT100

	  If unsure, say N.

config NFC_VIRTUAL_NCI
	tristate "NCI device simulator driver"
	depends on NFC_NCI
	help
	  NCI virtual device simulates a NCI device to the user.
	  It can be used to validate the NCI module and applications.
	  This driver supports communication between the virtual NCI device and
	  module.

	  If unsure, say N.

source "drivers/nfc/fdp/Kconfig"
source "drivers/nfc/pn544/Kconfig"
source "drivers/nfc/pn533/Kconfig"
+1 −0
Original line number Diff line number Diff line
@@ -17,3 +17,4 @@ obj-$(CONFIG_NFC_ST_NCI) += st-nci/
obj-$(CONFIG_NFC_NXP_NCI)	+= nxp-nci/
obj-$(CONFIG_NFC_S3FWRN5)	+= s3fwrn5/
obj-$(CONFIG_NFC_ST95HF)	+= st95hf/
obj-$(CONFIG_NFC_VIRTUAL_NCI)	+= virtual_ncidev.o
+215 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Virtual NCI device simulation driver
 *
 * Copyright (C) 2020 Samsung Electrnoics
 * Bongsu Jeon <bongsu.jeon@samsung.com>
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <net/nfc/nci_core.h>

enum virtual_ncidev_mode {
	virtual_ncidev_enabled,
	virtual_ncidev_disabled,
	virtual_ncidev_disabling,
};

#define IOCTL_GET_NCIDEV_IDX    0
#define VIRTUAL_NFC_PROTOCOLS	(NFC_PROTO_JEWEL_MASK | \
				 NFC_PROTO_MIFARE_MASK | \
				 NFC_PROTO_FELICA_MASK | \
				 NFC_PROTO_ISO14443_MASK | \
				 NFC_PROTO_ISO14443_B_MASK | \
				 NFC_PROTO_ISO15693_MASK)

static enum virtual_ncidev_mode state;
static struct miscdevice miscdev;
static struct sk_buff *send_buff;
static struct nci_dev *ndev;
static DEFINE_MUTEX(nci_mutex);

static int virtual_nci_open(struct nci_dev *ndev)
{
	return 0;
}

static int virtual_nci_close(struct nci_dev *ndev)
{
	mutex_lock(&nci_mutex);
	kfree_skb(send_buff);
	send_buff = NULL;
	mutex_unlock(&nci_mutex);

	return 0;
}

static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
{
	mutex_lock(&nci_mutex);
	if (state != virtual_ncidev_enabled) {
		mutex_unlock(&nci_mutex);
		return 0;
	}

	if (send_buff) {
		mutex_unlock(&nci_mutex);
		return -1;
	}
	send_buff = skb_copy(skb, GFP_KERNEL);
	mutex_unlock(&nci_mutex);

	return 0;
}

static struct nci_ops virtual_nci_ops = {
	.open = virtual_nci_open,
	.close = virtual_nci_close,
	.send = virtual_nci_send
};

static ssize_t virtual_ncidev_read(struct file *file, char __user *buf,
				   size_t count, loff_t *ppos)
{
	size_t actual_len;

	mutex_lock(&nci_mutex);
	if (!send_buff) {
		mutex_unlock(&nci_mutex);
		return 0;
	}

	actual_len = min_t(size_t, count, send_buff->len);

	if (copy_to_user(buf, send_buff->data, actual_len)) {
		mutex_unlock(&nci_mutex);
		return -EFAULT;
	}

	skb_pull(send_buff, actual_len);
	if (send_buff->len == 0) {
		consume_skb(send_buff);
		send_buff = NULL;
	}
	mutex_unlock(&nci_mutex);

	return actual_len;
}

static ssize_t virtual_ncidev_write(struct file *file,
				    const char __user *buf,
				    size_t count, loff_t *ppos)
{
	struct sk_buff *skb;

	skb = alloc_skb(count, GFP_KERNEL);
	if (!skb)
		return -ENOMEM;

	if (copy_from_user(skb_put(skb, count), buf, count)) {
		kfree_skb(skb);
		return -EFAULT;
	}

	nci_recv_frame(ndev, skb);
	return count;
}

static int virtual_ncidev_open(struct inode *inode, struct file *file)
{
	int ret = 0;

	mutex_lock(&nci_mutex);
	if (state != virtual_ncidev_disabled) {
		mutex_unlock(&nci_mutex);
		return -EBUSY;
	}

	ndev = nci_allocate_device(&virtual_nci_ops, VIRTUAL_NFC_PROTOCOLS,
				   0, 0);
	if (!ndev) {
		mutex_unlock(&nci_mutex);
		return -ENOMEM;
	}

	ret = nci_register_device(ndev);
	if (ret < 0) {
		nci_free_device(ndev);
		mutex_unlock(&nci_mutex);
		return ret;
	}
	state = virtual_ncidev_enabled;
	mutex_unlock(&nci_mutex);

	return 0;
}

static int virtual_ncidev_close(struct inode *inode, struct file *file)
{
	mutex_lock(&nci_mutex);

	if (state == virtual_ncidev_enabled) {
		state = virtual_ncidev_disabling;
		mutex_unlock(&nci_mutex);

		nci_unregister_device(ndev);
		nci_free_device(ndev);

		mutex_lock(&nci_mutex);
	}

	state = virtual_ncidev_disabled;
	mutex_unlock(&nci_mutex);

	return 0;
}

static long virtual_ncidev_ioctl(struct file *flip, unsigned int cmd,
				 unsigned long arg)
{
	struct nfc_dev *nfc_dev = ndev->nfc_dev;
	void __user *p = (void __user *)arg;

	if (cmd != IOCTL_GET_NCIDEV_IDX)
		return -ENOTTY;

	if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx)))
		return -EFAULT;

	return 0;
}

static const struct file_operations virtual_ncidev_fops = {
	.owner = THIS_MODULE,
	.read = virtual_ncidev_read,
	.write = virtual_ncidev_write,
	.open = virtual_ncidev_open,
	.release = virtual_ncidev_close,
	.unlocked_ioctl = virtual_ncidev_ioctl
};

static int __init virtual_ncidev_init(void)
{
	state = virtual_ncidev_disabled;
	miscdev.minor = MISC_DYNAMIC_MINOR;
	miscdev.name = "virtual_nci";
	miscdev.fops = &virtual_ncidev_fops;
	miscdev.mode = S_IALLUGO;

	return misc_register(&miscdev);
}

static void __exit virtual_ncidev_exit(void)
{
	misc_deregister(&miscdev);
}

module_init(virtual_ncidev_init);
module_exit(virtual_ncidev_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Virtual NCI device simulation driver");
MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ TARGETS += memory-hotplug
TARGETS += mincore
TARGETS += mount
TARGETS += mqueue
TARGETS += nci
TARGETS += net
TARGETS += net/forwarding
TARGETS += net/mptcp
Loading