From: munkyu.im Date: Thu, 26 Sep 2013 08:57:17 +0000 (+0900) Subject: nfc: add file operations for supporting nfc plugin X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8ca850447ad518eb819b0777f98eb373d07a6fee;p=sdk%2Femulator%2Femulator-kernel.git nfc: add file operations for supporting nfc plugin Change-Id: Ib1fc17df6071c67076ba061c7c4b8bfcd06d75af Signed-off-by: munkyu.im --- diff --git a/drivers/maru/maru_virtio_nfc.c b/drivers/maru/maru_virtio_nfc.c index 2192c576dec5..109b139fca3d 100755 --- a/drivers/maru/maru_virtio_nfc.c +++ b/drivers/maru/maru_virtio_nfc.c @@ -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 * YeongKyoon Lee * * This program is free software; you can redistribute it and/or @@ -37,180 +37,385 @@ #include #include #include +#include #include #include #include #include #include #include -#include -#include -#include -#include -#include +#include #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 "); -MODULE_DESCRIPTION("Emulator Virtio EmulatorVirtualDeviceInterface Driver"); +MODULE_AUTHOR("Munkyu Im "); +MODULE_DESCRIPTION("Emulator Virtio NFC Driver");