Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Virtual NCI device simulation driver
0004  *
0005  * Copyright (C) 2020 Samsung Electrnoics
0006  * Bongsu Jeon <bongsu.jeon@samsung.com>
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/module.h>
0011 #include <linux/miscdevice.h>
0012 #include <linux/mutex.h>
0013 #include <linux/wait.h>
0014 #include <net/nfc/nci_core.h>
0015 
0016 enum virtual_ncidev_mode {
0017     virtual_ncidev_enabled,
0018     virtual_ncidev_disabled,
0019     virtual_ncidev_disabling,
0020 };
0021 
0022 #define IOCTL_GET_NCIDEV_IDX    0
0023 #define VIRTUAL_NFC_PROTOCOLS   (NFC_PROTO_JEWEL_MASK | \
0024                  NFC_PROTO_MIFARE_MASK | \
0025                  NFC_PROTO_FELICA_MASK | \
0026                  NFC_PROTO_ISO14443_MASK | \
0027                  NFC_PROTO_ISO14443_B_MASK | \
0028                  NFC_PROTO_ISO15693_MASK)
0029 
0030 static enum virtual_ncidev_mode state;
0031 static DECLARE_WAIT_QUEUE_HEAD(wq);
0032 static struct miscdevice miscdev;
0033 static struct sk_buff *send_buff;
0034 static struct nci_dev *ndev;
0035 static DEFINE_MUTEX(nci_mutex);
0036 
0037 static int virtual_nci_open(struct nci_dev *ndev)
0038 {
0039     return 0;
0040 }
0041 
0042 static int virtual_nci_close(struct nci_dev *ndev)
0043 {
0044     mutex_lock(&nci_mutex);
0045     kfree_skb(send_buff);
0046     send_buff = NULL;
0047     mutex_unlock(&nci_mutex);
0048 
0049     return 0;
0050 }
0051 
0052 static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
0053 {
0054     mutex_lock(&nci_mutex);
0055     if (state != virtual_ncidev_enabled) {
0056         mutex_unlock(&nci_mutex);
0057         return 0;
0058     }
0059 
0060     if (send_buff) {
0061         mutex_unlock(&nci_mutex);
0062         return -1;
0063     }
0064     send_buff = skb_copy(skb, GFP_KERNEL);
0065     mutex_unlock(&nci_mutex);
0066     wake_up_interruptible(&wq);
0067 
0068     return 0;
0069 }
0070 
0071 static const struct nci_ops virtual_nci_ops = {
0072     .open = virtual_nci_open,
0073     .close = virtual_nci_close,
0074     .send = virtual_nci_send
0075 };
0076 
0077 static ssize_t virtual_ncidev_read(struct file *file, char __user *buf,
0078                    size_t count, loff_t *ppos)
0079 {
0080     size_t actual_len;
0081 
0082     mutex_lock(&nci_mutex);
0083     while (!send_buff) {
0084         mutex_unlock(&nci_mutex);
0085         if (wait_event_interruptible(wq, send_buff))
0086             return -EFAULT;
0087         mutex_lock(&nci_mutex);
0088     }
0089 
0090     actual_len = min_t(size_t, count, send_buff->len);
0091 
0092     if (copy_to_user(buf, send_buff->data, actual_len)) {
0093         mutex_unlock(&nci_mutex);
0094         return -EFAULT;
0095     }
0096 
0097     skb_pull(send_buff, actual_len);
0098     if (send_buff->len == 0) {
0099         consume_skb(send_buff);
0100         send_buff = NULL;
0101     }
0102     mutex_unlock(&nci_mutex);
0103 
0104     return actual_len;
0105 }
0106 
0107 static ssize_t virtual_ncidev_write(struct file *file,
0108                     const char __user *buf,
0109                     size_t count, loff_t *ppos)
0110 {
0111     struct sk_buff *skb;
0112 
0113     skb = alloc_skb(count, GFP_KERNEL);
0114     if (!skb)
0115         return -ENOMEM;
0116 
0117     if (copy_from_user(skb_put(skb, count), buf, count)) {
0118         kfree_skb(skb);
0119         return -EFAULT;
0120     }
0121 
0122     nci_recv_frame(ndev, skb);
0123     return count;
0124 }
0125 
0126 static int virtual_ncidev_open(struct inode *inode, struct file *file)
0127 {
0128     int ret = 0;
0129 
0130     mutex_lock(&nci_mutex);
0131     if (state != virtual_ncidev_disabled) {
0132         mutex_unlock(&nci_mutex);
0133         return -EBUSY;
0134     }
0135 
0136     ndev = nci_allocate_device(&virtual_nci_ops, VIRTUAL_NFC_PROTOCOLS,
0137                    0, 0);
0138     if (!ndev) {
0139         mutex_unlock(&nci_mutex);
0140         return -ENOMEM;
0141     }
0142 
0143     ret = nci_register_device(ndev);
0144     if (ret < 0) {
0145         nci_free_device(ndev);
0146         mutex_unlock(&nci_mutex);
0147         return ret;
0148     }
0149     state = virtual_ncidev_enabled;
0150     mutex_unlock(&nci_mutex);
0151 
0152     return 0;
0153 }
0154 
0155 static int virtual_ncidev_close(struct inode *inode, struct file *file)
0156 {
0157     mutex_lock(&nci_mutex);
0158 
0159     if (state == virtual_ncidev_enabled) {
0160         state = virtual_ncidev_disabling;
0161         mutex_unlock(&nci_mutex);
0162 
0163         nci_unregister_device(ndev);
0164         nci_free_device(ndev);
0165 
0166         mutex_lock(&nci_mutex);
0167     }
0168 
0169     state = virtual_ncidev_disabled;
0170     mutex_unlock(&nci_mutex);
0171 
0172     return 0;
0173 }
0174 
0175 static long virtual_ncidev_ioctl(struct file *flip, unsigned int cmd,
0176                  unsigned long arg)
0177 {
0178     const struct nfc_dev *nfc_dev = ndev->nfc_dev;
0179     void __user *p = (void __user *)arg;
0180 
0181     if (cmd != IOCTL_GET_NCIDEV_IDX)
0182         return -ENOTTY;
0183 
0184     if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx)))
0185         return -EFAULT;
0186 
0187     return 0;
0188 }
0189 
0190 static const struct file_operations virtual_ncidev_fops = {
0191     .owner = THIS_MODULE,
0192     .read = virtual_ncidev_read,
0193     .write = virtual_ncidev_write,
0194     .open = virtual_ncidev_open,
0195     .release = virtual_ncidev_close,
0196     .unlocked_ioctl = virtual_ncidev_ioctl
0197 };
0198 
0199 static int __init virtual_ncidev_init(void)
0200 {
0201     state = virtual_ncidev_disabled;
0202     miscdev.minor = MISC_DYNAMIC_MINOR;
0203     miscdev.name = "virtual_nci";
0204     miscdev.fops = &virtual_ncidev_fops;
0205     miscdev.mode = 0600;
0206 
0207     return misc_register(&miscdev);
0208 }
0209 
0210 static void __exit virtual_ncidev_exit(void)
0211 {
0212     misc_deregister(&miscdev);
0213 }
0214 
0215 module_init(virtual_ncidev_init);
0216 module_exit(virtual_ncidev_exit);
0217 
0218 MODULE_LICENSE("GPL");
0219 MODULE_DESCRIPTION("Virtual NCI device simulation driver");
0220 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");