nfc: add file operations for supporting nfc plugin 24/10624/1
authormunkyu.im <munkyu.im@samsung.com>
Thu, 26 Sep 2013 08:57:17 +0000 (17:57 +0900)
committerKitae Kim <kt920.kim@samsung.com>
Tue, 8 Oct 2013 05:50:22 +0000 (14:50 +0900)
Change-Id: Ib1fc17df6071c67076ba061c7c4b8bfcd06d75af
Signed-off-by: munkyu.im <munkyu.im@samsung.com>
drivers/maru/maru_virtio_nfc.c

index 2192c576dec505881bd6c023e8a3e252add57b35..109b139fca3d624f4ec17af65810fa106abde2b5 100755 (executable)
@@ -1,10 +1,10 @@
 /*
  * Maru Virtio NFC Device Driver
  *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
  *
  * Contact:
- *  DaiYoung Kim <munkyu.im@samsung.com>
+ *  Munkyu Im <munkyu.im@samsung.com>
  *  YeongKyoon Lee <yeongkyoon.lee@samsung.com>
  *
  * This program is free software; you can redistribute it and/or
 #include <linux/slab.h>
 #include <linux/poll.h>
 #include <linux/interrupt.h>
+#include <linux/spinlock.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
 #include <linux/workqueue.h>
 #include <linux/virtio.h>
 #include <linux/virtio_ids.h>
 #include <linux/virtio_config.h>
-#include <linux/sysfs.h>
-#include <linux/syscalls.h>
-#include <linux/fcntl.h>
-#include <asm/uaccess.h>
-#include <linux/file.h>
+#include <linux/cdev.h>
 
 #define DRIVER_NAME "NFC"
 
 #define LOG(fmt, ...) \
-       printk(KERN_ERR "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__)
+    printk(KERN_ERR "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__)
 
-#define CLASS_NAME             "network"
-#define DEVICE_NAME            "nfc"
-#define NFC_DATA_FILE "/sys/devices/virtual/network/nfc/data"
+#define NUM_OF_NFC  2
+#define DEVICE_NAME     "nfc"
 
 /* device protocol */
-#define __MAX_BUF_SIZE 4096
+#define __MAX_BUF_SIZE  1024
 
 enum
 {
-       route_qemu = 0,
-       route_control_server = 1,
-       route_monitor = 2
-};
-
-enum request_cmd {
-       request_get = 0,
-       request_set,
-       request_answer
+    route_qemu = 0,
+    route_control_server = 1,
+    route_monitor = 2
 };
 
 typedef unsigned int CSCliSN;
 
 struct msg_info {
-       char buf[__MAX_BUF_SIZE];
+    char buf[__MAX_BUF_SIZE];
+    uint32_t route;
+};
 
-       uint32_t route;
-       uint32_t use;
-       uint16_t count;
-       uint16_t index;
+static int g_wake_up_interruptible_count = 0;
+static int g_read_count = 0;
+
+/* device protocol */
 
-       CSCliSN cclisn;
+#define SIZEOF_MSG_INFO sizeof(struct msg_info)
+
+struct msg_buf {
+    struct msg_info msg;
+    struct list_head list;
 };
 
-static struct device* device;
+#define SIZEOF_MSG_BUF  sizeof(struct msg_buf)
 
-static char data[PAGE_SIZE] = {0,};
+enum {
+    NFC_READ = 0, NFC_WRITE = 1
+};
 
-static ssize_t show_data(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       printk("NFC-[%s] \n", __FUNCTION__);
-       return snprintf(buf, PAGE_SIZE, "%s", data);
-}
+struct virtnfc_info {
 
-static ssize_t store_data(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
-       printk("NFC-[%s] \n", __FUNCTION__);
-    strncpy(data, buf, count);
-       return strnlen(buf, PAGE_SIZE);
-}
+    wait_queue_head_t waitqueue;
+    spinlock_t inbuf_lock;
+    spinlock_t outvq_lock;
 
-static struct device_attribute da = __ATTR(data, 0644, show_data, store_data);
+    struct cdev cdev;
+    char name[10];
 
-/* device protocol */
+    int index;
+    bool guest_connected;
 
-#define SIZEOF_MSG_INFO        sizeof(struct msg_info)
+} *pnfc_info[NUM_OF_NFC];
 
 struct virtio_nfc {
-       struct virtio_device* vdev;
-       struct virtqueue* rvq;
-       struct virtqueue* svq;
-
-       struct msg_info read_msginfo;
-       struct msg_info send_msginfo;
+    struct virtio_device* vdev;
+    struct virtqueue* rvq;
+    struct virtqueue* svq;
 
-       struct list_head read_list;
-       struct list_head write_list;
+    struct msg_info read_msginfo;
+    struct msg_info send_msginfo;
 
-       struct scatterlist sg_read[2];
-       struct scatterlist sg_send[2];
+    struct list_head read_list;
+    struct list_head write_list;
 
-    void *private;
+    struct scatterlist sg_read[2];
+    struct scatterlist sg_send[2];
 };
 
 struct virtio_nfc *vnfc;
 
 static struct virtio_device_id id_table[] = { { VIRTIO_ID_NFC,
-               VIRTIO_DEV_ANY_ID }, { 0 }, };
+        VIRTIO_DEV_ANY_ID }, { 0 }, };
 
+static dev_t nfc_dev_number;
 static struct class* nfc_class;
 
 
+static void* __xmalloc(size_t size)
+{
+    void* p = kmalloc(size, GFP_KERNEL);
+    if (!p)
+        return NULL;
+    return p;
+}
+
 int make_buf_and_kick(void)
 {
-       int ret;
-       memset(&vnfc->read_msginfo, 0x00, sizeof(vnfc->read_msginfo));
-       ret = virtqueue_add_buf(vnfc->rvq, vnfc->sg_read, 0, 1, &vnfc->read_msginfo,
-                       GFP_ATOMIC );
-       if (ret < 0) {
-               LOG("failed to add buffer to virtqueue.(%d)\n", ret);
-               return ret;
-       }
-
-       virtqueue_kick(vnfc->rvq);
-
-       return 0;
+    int ret;
+    memset(&vnfc->read_msginfo, 0x00, sizeof(vnfc->read_msginfo));
+    ret = virtqueue_add_buf(vnfc->rvq, vnfc->sg_read, 0, 1, &vnfc->read_msginfo,
+            GFP_ATOMIC );
+    if (ret < 0) {
+        LOG("failed to add buffer to virtqueue.(%d)\n", ret);
+        return ret;
+    }
+
+    virtqueue_kick(vnfc->rvq);
+
+    return 0;
+}
+
+static int add_inbuf(struct virtqueue *vq, struct msg_info *msg)
+{
+    struct scatterlist sg[1];
+    int ret;
+
+    sg_init_one(sg, msg, sizeof(struct msg_info));
+
+    ret = virtqueue_add_buf(vq, sg, 0, 1, msg, GFP_ATOMIC);
+    virtqueue_kick(vq);
+    return ret;
 }
 
-static void write_file(char *filename, char *data, int len)
+static bool has_readdata(struct virtnfc_info *nfc)
 {
-    struct file *file;
-    loff_t pos = 0;
-    int fd;
-
-    mm_segment_t old_fs = get_fs();
-    set_fs(KERNEL_DS);
-
-    fd = sys_open(filename, O_WRONLY|O_CREAT, 0644);
-    if (fd >= 0) {
-        sys_write(fd, data, strlen(data));
-        file = fget(fd);
-        if (file) {
-            vfs_write(file, data, len, &pos);
-            fput(file);
-            file_update_time(file);
+    bool ret;
+    unsigned long flags;
+
+    spin_lock_irqsave(&nfc->inbuf_lock, flags);
+
+    ret = true;
+
+    if (list_empty(&vnfc->read_list))
+        ret = false;
+
+    spin_unlock_irqrestore(&nfc->inbuf_lock, flags);
+
+    return ret;
+}
+
+
+static int nfc_open(struct inode* inode, struct file* filp)
+{
+    int i, ret;
+    struct virtnfc_info* nfc_info;
+    struct cdev *cdev = inode->i_cdev;
+
+    nfc_info = NULL;
+    LOG("nfc_open\n");
+
+    for (i = 0; i < NUM_OF_NFC; i++)
+    {
+        LOG("nfc info index = %d, cdev dev = %d, inode dev = %d\n",
+                i, pnfc_info[i]->cdev.dev, cdev->dev);
+
+        if (pnfc_info[i]->cdev.dev == cdev->dev)
+        {
+            nfc_info = pnfc_info[i];
+            break;
         }
-        sys_close(fd);
     }
-    set_fs(old_fs);
+
+    filp->private_data = nfc_info;
+
+    nfc_info->guest_connected = true;
+
+
+    ret = make_buf_and_kick();
+    if (ret < 0)
+        return ret;
+
+
+    LOG("nfc_opened\n");
+    return 0;
 }
 
-static void nfc_recv_done(struct virtqueue *rvq) {
+static int nfc_close(struct inode* i, struct file* filp) {
+    struct virtnfc_info *nfc_info;
 
-       unsigned int len;
-       struct msg_info* msg;
-    int err;
-       msg = (struct msg_info*) virtqueue_get_buf(vnfc->rvq, &len);
-       if (msg == NULL ) {
-               LOG("failed to virtqueue_get_buf\n");
-               return;
-       }
-
-    if(msg->route == request_set) {
-        memset(data, 0x00, sizeof(data));
-        write_file(NFC_DATA_FILE, msg->buf, len);
-    }else if(msg->route == request_get) {
-       memset(&vnfc->send_msginfo, 0, sizeof(vnfc->send_msginfo));
-       vnfc->send_msginfo.route = request_answer;
-               strcpy(vnfc->send_msginfo.buf, data);
-        err = virtqueue_add_buf(vnfc->svq, vnfc->sg_send, 1, 0,        &vnfc->send_msginfo, GFP_ATOMIC);
-       if (err < 0) {
-               LOG("failed to add buffer to virtqueue (err = %d)", err);
-                   return;
-       }
-
-           virtqueue_kick(vnfc->svq);
+    nfc_info = filp->private_data;
+    nfc_info->guest_connected = false;
 
+    LOG("nfc_closed\n");
+    return 0;
+}
+
+
+
+static ssize_t nfc_read(struct file *filp, char __user *ubuf, size_t len,
+        loff_t *f_pos)
+{
+    struct virtnfc_info *nfc;
+
+    ssize_t ret;
+    struct msg_buf* next;
+    unsigned long flags;
+
+    LOG("nfc_read\n");
+    nfc = filp->private_data;
+    if (!has_readdata(nfc))
+    {
+        if (filp->f_flags & O_NONBLOCK)
+        {
+            LOG("list is empty, return EAGAIN\n");
+            return -EAGAIN;
+        }
+        return -EFAULT;
+    }
+
+    next = list_first_entry(&vnfc->read_list, struct msg_buf, list);
+    if (next == NULL) {
+        LOG("invliad list entry\n");
+        return -EFAULT;
     }
-    
-       LOG("msg buf: %sroute: %d, len: %d", msg->buf, msg->route, msg->use);
 
-    make_buf_and_kick();
+    ret = copy_to_user(ubuf, &next->msg, len);
+
+    list_del(&next->list);
+    kfree(next);
+
+    spin_lock_irqsave(&pnfc_info[NFC_READ]->inbuf_lock, flags);
+
+
+    if (add_inbuf(vnfc->rvq, &vnfc->read_msginfo) < 0)
+    {
+        LOG("failed add_buf\n");
+    }
+
+    spin_unlock_irqrestore(&pnfc_info[NFC_READ]->inbuf_lock, flags);
+
+
+    LOG("nfc_read count = %d!\n", ++g_read_count);
+
+    if (ret < 0)
+        return -EFAULT;
+
+
+
+    *f_pos += len;
+
+    return len;
+}
+
+static ssize_t nfc_write(struct file *f, const char __user *ubuf, size_t len,
+        loff_t* f_pos)
+{
+    int err = 0;
+    ssize_t ret = 0;
+
+    LOG("start of nfc_write len= %d, msglen = %d\n", len, sizeof(vnfc->send_msginfo));
+
+    if (vnfc == NULL) {
+        LOG("invalid nfc handle\n");
+        return 0;
+    }
+
+    memset(&vnfc->send_msginfo, 0, sizeof(vnfc->send_msginfo));
+    ret = copy_from_user(&vnfc->send_msginfo, ubuf, sizeof(vnfc->send_msginfo));
+
+    LOG("copy_from_user ret = %d, msg = %s\n", ret, vnfc->send_msginfo.buf);
+    LOG("msg buf: %s, route: %d, \n",
+            vnfc->send_msginfo.buf, vnfc->send_msginfo.route);
+
+    if (ret) {
+        ret = -EFAULT;
+        return ret;
+    }
+
+    sg_init_one(vnfc->sg_send, &vnfc->send_msginfo, sizeof(vnfc->send_msginfo));
+
+    err = virtqueue_add_buf(vnfc->svq, vnfc->sg_send, 1, 0,
+            &vnfc->send_msginfo, GFP_ATOMIC);
+
+    /*
+    err = virtqueue_add_buf(vnfc->svq, vnfc->sg_send, 1, 0,
+                &_msg, GFP_ATOMIC);*/
+
+    if (err < 0) {
+        LOG("failed to add buffer to virtqueue (err = %d)\n", err);
+        return 0;
+    }
+
+    virtqueue_kick(vnfc->svq);
+
+    LOG("send to host\n");
+
+    return len;
+}
+
+static unsigned int nfc_poll(struct file *filp, poll_table *wait)
+{
+    struct virtnfc_info *nfc;
+    unsigned int ret;
+
+    nfc = filp->private_data;
+    poll_wait(filp, &nfc->waitqueue, wait);
+
+    if (!nfc->guest_connected) {
+        /* nfc got unplugged */
+        return POLLHUP;
+    }
+
+    ret = 0;
+
+    if (has_readdata(nfc))
+    {
+        LOG("POLLIN | POLLRDNORM\n");
+        ret |= POLLIN | POLLRDNORM;
+    }
+
+    return ret;
+}
+
+static struct file_operations nfc_fops = {
+        .owner = THIS_MODULE,
+        .open = nfc_open,
+        .release = nfc_close,
+        .read = nfc_read,
+        .write = nfc_write,
+        .poll  = nfc_poll,
+};
+
+
+
+static void nfc_recv_done(struct virtqueue *rvq) {
+
+    unsigned int len;
+    unsigned long flags;
+    struct msg_info* msg;
+    struct msg_buf* msgbuf;
+    LOG("nfc_recv_done\n");
+    /* TODO : check if guest has been connected. */
+
+    msg = (struct msg_info*) virtqueue_get_buf(vnfc->rvq, &len);
+    if (msg == NULL ) {
+        LOG("failed to virtqueue_get_buf\n");
+        return;
+    }
+
+    INIT_LIST_HEAD(&vnfc->read_list);
+    do {
+
+        /* insert into queue */
+        msgbuf = (struct msg_buf*) __xmalloc(SIZEOF_MSG_BUF);
+        memset(msgbuf, 0x00, sizeof(*msgbuf));
+        memcpy(&(msgbuf->msg), msg, sizeof(*msg));
+
+        //LOG("copied msg data = %s, %s\n", msgbuf->msg.buf, msg->buf);
+
+        spin_lock_irqsave(&pnfc_info[NFC_READ]->inbuf_lock, flags);
+
+        list_add_tail(&msgbuf->list, &vnfc->read_list);
+        //LOG("== wake_up_interruptible = %d!\n", ++g_wake_up_interruptible_count);
+
+        spin_unlock_irqrestore(&pnfc_info[NFC_READ]->inbuf_lock, flags);
+
+        wake_up_interruptible(&pnfc_info[NFC_READ]->waitqueue);
+
+        msg = (struct msg_info*) virtqueue_get_buf(vnfc->rvq, &len);
+        if (msg == NULL) {
+            break;
+        }
+
+    } while (true);
+    /*
+    if (add_inbuf(vnfc->rvq, &vnfc->readmsginfo) < 0)
+    {
+        LOG("failed add_buf\n");
+    }
+    */
 }
 
 static void nfc_send_done(struct virtqueue *svq) {
-       unsigned int len = 0;
+    unsigned int len = 0;
 
-       virtqueue_get_buf(svq, &len);
+    virtqueue_get_buf(svq, &len);
 }
 
 /*
@@ -218,136 +423,166 @@ static void nfc_send_done(struct virtqueue *svq) {
  */
 
 static int init_vqs(struct virtio_nfc *nfc) {
-       struct virtqueue *vqs[2];
-       vq_callback_t *callbacks[] = { nfc_recv_done, nfc_send_done };
-       const char *names[] = { "nfc_input", "nfc_output" };
-       int err;
+    struct virtqueue *vqs[2];
+    vq_callback_t *callbacks[] = { nfc_recv_done, nfc_send_done };
+    const char *names[] = { "nfc_input", "nfc_output" };
+    int err;
 
-       err = nfc->vdev->config->find_vqs(nfc->vdev, 2, vqs, callbacks, names);
-       if (err < 0)
-               return err;
+    err = nfc->vdev->config->find_vqs(nfc->vdev, 2, vqs, callbacks, names);
+    if (err < 0)
+        return err;
 
-       nfc->rvq = vqs[0];
-       nfc->svq = vqs[1];
+    nfc->rvq = vqs[0];
+    nfc->svq = vqs[1];
 
-       return 0;
+    return 0;
 }
 
-static int init_device(void)
+int init_device(void)
 {
-       int ret;
+    int i, ret;
 
-       nfc_class = class_create(THIS_MODULE, CLASS_NAME);
+    if (alloc_chrdev_region(&nfc_dev_number, 0, NUM_OF_NFC, DEVICE_NAME) < 0) {
+        LOG("fail to alloc_chrdev_region\n");
+        return -1;
+    }
 
-       device = device_create(nfc_class, NULL, (dev_t)NULL, NULL, DEVICE_NAME);
-    if (device < 0) {
-                       LOG("NFC device creation is failed.");
-                       return -1;
-       }
+    nfc_class = class_create(THIS_MODULE, DEVICE_NAME);
 
-       ret = device_create_file(device, &da);
-       if (ret) {
-               device_destroy(nfc_class, (dev_t)NULL);
-           class_destroy(nfc_class);
+    if (nfc_class == NULL ) {
+        unregister_chrdev_region(nfc_dev_number, NUM_OF_NFC);
         return -1;
     }
 
-       return 0;
+    for (i = 0; i < NUM_OF_NFC; i++) {
+        pnfc_info[i] = kmalloc(sizeof(struct virtnfc_info), GFP_KERNEL);
+
+        if (!pnfc_info[i]) {
+            LOG("Bad malloc\n");
+            return -ENOMEM;
+        }
+
+        sprintf(pnfc_info[i]->name, "%s%d", DEVICE_NAME, i);
+
+        pnfc_info[i]->index = i;
+        pnfc_info[i]->guest_connected = false;
+
+        cdev_init(&pnfc_info[i]->cdev, &nfc_fops);
+        pnfc_info[i]->cdev.owner = THIS_MODULE;
+        ret = cdev_add(&pnfc_info[i]->cdev, (nfc_dev_number + i), 1);
+
+        /* init wait queue */
+        init_waitqueue_head(&pnfc_info[i]->waitqueue);
+        spin_lock_init(&pnfc_info[i]->inbuf_lock);
+        spin_lock_init(&pnfc_info[i]->outvq_lock);
+
+        if (ret == -1) {
+            LOG("Bad cdev\n");
+            return ret;
+        }
+
+        device_create(nfc_class, NULL, (nfc_dev_number + i), NULL, "%s%d",
+                DEVICE_NAME, i);
+    }
+
+    return 0;
 }
 
 
 static int nfc_probe(struct virtio_device* dev) {
-       int ret;
+    int ret;
+    LOG("nfc_probe\n");
+    vnfc = kmalloc(sizeof(struct virtio_nfc), GFP_KERNEL);
 
-       vnfc = kmalloc(sizeof(struct virtio_nfc), GFP_KERNEL);
+    INIT_LIST_HEAD(&vnfc->read_list);
 
-       INIT_LIST_HEAD(&vnfc->read_list);
+    vnfc->vdev = dev;
+    dev->priv = vnfc;
 
-       vnfc->vdev = dev;
-       dev->priv = vnfc;
+    ret = init_device();
+    if (ret)
+    {
+        LOG("failed to init_device\n");
+        return ret;
+    }
+    ret = init_vqs(vnfc);
+    if (ret) {
+        dev->config->del_vqs(dev);
+        kfree(vnfc);
+        dev->priv = NULL;
+
+        LOG("failed to init_vqs\n");
+        return ret;
+    }
 
-       ret = init_device();
-       if (ret)
-       {
-               LOG("failed to _init_device\n");
-               return ret;
-       }
-       ret = init_vqs(vnfc);
-       if (ret) {
-               dev->config->del_vqs(dev);
-               kfree(vnfc);
-               dev->priv = NULL;
+     /* enable callback */
+    virtqueue_enable_cb(vnfc->rvq);
+    virtqueue_enable_cb(vnfc->svq);
 
-               LOG("failed to init_vqs\n");
-               return ret;
-       }
 
-        /* enable callback */
-       virtqueue_enable_cb(vnfc->rvq);
-       virtqueue_enable_cb(vnfc->svq);
+    memset(&vnfc->read_msginfo, 0x00, sizeof(vnfc->read_msginfo));
+    sg_set_buf(vnfc->sg_read, &vnfc->read_msginfo, sizeof(struct msg_info));
 
-       memset(&vnfc->read_msginfo, 0x00, sizeof(vnfc->read_msginfo));
-       sg_set_buf(vnfc->sg_read, &vnfc->read_msginfo, sizeof(struct msg_info));
+    memset(&vnfc->send_msginfo, 0x00, sizeof(vnfc->send_msginfo));
+    sg_set_buf(vnfc->sg_send, &vnfc->send_msginfo, sizeof(struct msg_info));
 
-       memset(&vnfc->send_msginfo, 0x00, sizeof(vnfc->send_msginfo));
-       sg_set_buf(vnfc->sg_send, &vnfc->send_msginfo, sizeof(struct msg_info));
 
+    sg_init_one(vnfc->sg_read, &vnfc->read_msginfo, sizeof(vnfc->read_msginfo));
+    sg_init_one(vnfc->sg_send, &vnfc->send_msginfo, sizeof(vnfc->send_msginfo));
 
-       sg_init_one(vnfc->sg_read, &vnfc->read_msginfo, sizeof(vnfc->read_msginfo));
-       sg_init_one(vnfc->sg_send, &vnfc->send_msginfo, sizeof(vnfc->send_msginfo));
 
-    ret = make_buf_and_kick();
-       if (ret) {
-               dev->config->del_vqs(dev);
-               kfree(vnfc);
-               dev->priv = NULL;
-               LOG("failed to send buf");
-               return ret;
-       }
-
-       LOG("NFC Probe completed");
-       return 0;
+
+    LOG("NFC Probe completed");
+    return 0;
 }
 
 static void __devexit nfc_remove(struct virtio_device* dev)
 {
-       struct virtio_nfc* _nfc = dev->priv;
-       if (!_nfc)
-       {
-               LOG("nfc is NULL\n");
-               return;
-       }
+    struct virtio_nfc* _nfc = dev->priv;
+    if (!_nfc)
+    {
+        LOG("nfc is NULL\n");
+        return;
+    }
 
-       dev->config->reset(dev);
-       dev->config->del_vqs(dev);
+    dev->config->reset(dev);
+    dev->config->del_vqs(dev);
 
-       kfree(_nfc);
+    kfree(_nfc);
 
-       LOG("driver is removed.\n");
+    LOG("driver is removed.\n");
 }
 
 MODULE_DEVICE_TABLE(virtio, id_table);
 
 static struct virtio_driver virtio_nfc_driver = {
-               .driver = {
-                               .name = KBUILD_MODNAME,
-                               .owner = THIS_MODULE ,
-               },
-               .id_table = id_table,
-               .probe = nfc_probe,
-               .remove = nfc_remove,
+        .driver = {
+                .name = KBUILD_MODNAME,
+                .owner = THIS_MODULE ,
+        },
+        .id_table = id_table,
+        .probe = nfc_probe,
+        .remove = nfc_remove,
 };
 
 static int __init nfc_init(void)
 {
-       LOG("NFC driver initialized.\n");
+    LOG("NFC driver initialized.\n");
 
-       return register_virtio_driver(&virtio_nfc_driver);
+    return register_virtio_driver(&virtio_nfc_driver);
 }
 
 static void __exit nfc_exit(void)
 {
-    device_destroy(nfc_class, (dev_t)NULL);
+    int i;
+
+    unregister_chrdev_region(nfc_dev_number, NUM_OF_NFC);
+
+    for (i = 0; i < NUM_OF_NFC; i++) {
+        device_destroy(nfc_class, MKDEV(MAJOR(nfc_dev_number), i));
+        cdev_del(&pnfc_info[i]->cdev);
+        kfree(pnfc_info[i]);
+    }
 
     /*device_destroy(nfc_class, nfc_dev_number);*/
 
@@ -362,6 +597,6 @@ module_init(nfc_init);
 module_exit(nfc_exit);
 
 MODULE_LICENSE("GPL2");
-MODULE_AUTHOR("DaiYoung Kim <daiyoung777.kim@samsung.com>");
-MODULE_DESCRIPTION("Emulator Virtio EmulatorVirtualDeviceInterface Driver");
+MODULE_AUTHOR("Munkyu Im <munkyu.im@samsung.com>");
+MODULE_DESCRIPTION("Emulator Virtio NFC Driver");