keyboard: added Maru virtio keyboard driver
authorGiWoong Kim <giwoong.kim@samsung.com>
Thu, 9 Jan 2014 07:10:50 +0000 (16:10 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Thu, 27 Feb 2014 06:26:50 +0000 (15:26 +0900)
Change-Id: I9372dee6c75e6580cbf944e0a3c3f41610c822c3
Signed-off-by: GiWoong Kim <giwoong.kim@samsung.com>
arch/x86/configs/i386_tizen_emul_defconfig
drivers/maru/Kconfig
drivers/maru/Makefile
drivers/maru/maru_virtio_keyboard.c [new file with mode: 0644]

index da89e93..18ddee2 100644 (file)
@@ -2814,6 +2814,7 @@ CONFIG_IOMMU_SUPPORT=y
 CONFIG_MARU=y
 CONFIG_MARU_VIRTIO_TOUCHSCREEN=y
 CONFIG_MARU_VIRTIO_HWKEY=y
+CONFIG_MARU_VIRTIO_KEYBOARD=y
 
 #
 # Firmware Drivers
index f8adb6a..45de29a 100644 (file)
@@ -10,3 +10,7 @@ config MARU_VIRTIO_HWKEY
        tristate "MARU Virtio HW Key Driver"
        depends on MARU != n
 
+config MARU_VIRTIO_KEYBOARD
+       tristate "MARU Virtio Keyboard Driver"
+       depends on MARU != n
+
index ef9bc6c..ee2f362 100644 (file)
@@ -1,2 +1,3 @@
 obj-$(CONFIG_MARU_VIRTIO_TOUCHSCREEN) += maru_virtio_touchscreen.o
 obj-$(CONFIG_MARU_VIRTIO_HWKEY) += maru_virtio_hwkey.o
+obj-$(CONFIG_MARU_VIRTIO_KEYBOARD) += maru_virtio_keyboard.o
diff --git a/drivers/maru/maru_virtio_keyboard.c b/drivers/maru/maru_virtio_keyboard.c
new file mode 100644 (file)
index 0000000..2c123a0
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Maru Virtio Keyboard Device Driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *  Kitae Kim <kitae.kim@samsung.com>
+ *  SeokYeon Hwang <syeon.hwang@samsung.com>
+ *  YeongKyoon Lee <yeongkyoon.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+
+MODULE_LICENSE("GPL2");
+MODULE_AUTHOR("Kitae Kim <kt920.kim@samsung.com>");
+MODULE_DESCRIPTION("Emulator Virtio Keyboard Driver");
+
+#define DRIVER_NAME "virtio-keyboard"
+#define VKBD_LOG(log_level, fmt, ...) \
+       printk(log_level "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__)
+
+#define KBD_BUF_SIZE 100
+static int vqidx = 0;
+
+struct EmulKbdEvent
+{
+       uint16_t code;
+       uint16_t value;
+};
+
+struct virtio_keyboard
+{
+       struct virtio_device *vdev;
+       struct virtqueue *vq;
+       struct input_dev *idev;
+
+       struct EmulKbdEvent kbdevt[KBD_BUF_SIZE];
+       struct scatterlist sg[KBD_BUF_SIZE];
+
+       struct mutex event_mutex;
+};
+
+struct virtio_keyboard *vkbd;
+
+static struct virtio_device_id id_table[] = {
+       { VIRTIO_ID_KEYBOARD, VIRTIO_DEV_ANY_ID },
+       { 0 },
+};
+
+static void vq_keyboard_handle(struct virtqueue *vq)
+{
+       int err = 0, len = 0;
+       void *data;
+       struct EmulKbdEvent kbdevent;
+
+       VKBD_LOG(KERN_DEBUG, "virtqueue callback.\n");
+       data = virtqueue_get_buf(vq, &len);
+       if (!data) {
+               VKBD_LOG(KERN_ERR, "there is no available buffer.\n");
+               return;
+       }
+
+        VKBD_LOG(KERN_DEBUG, "vqidx: %d\n", vqidx);
+        while (1) {
+               memcpy(&kbdevent, &vkbd->kbdevt[vqidx], sizeof(kbdevent));
+#if 1
+               if (kbdevent.code == 0) {
+                       break;
+               }
+#endif
+               /* how to get keycode and value. */
+               input_event(vkbd->idev, EV_KEY, kbdevent.code, kbdevent.value);
+               input_sync(vkbd->idev);
+               printk(KERN_ERR "input_event code = %d, value = %d\n", kbdevent.code, kbdevent.value);
+               memset(&vkbd->kbdevt[vqidx], 0x00, sizeof(kbdevent));
+               vqidx++;
+               if (vqidx == KBD_BUF_SIZE) {
+                       vqidx = 0;
+               }
+       }
+       err = virtqueue_add_buf (vq, vkbd->sg, 0, KBD_BUF_SIZE, (void *)KBD_BUF_SIZE, GFP_ATOMIC);
+       if (err < 0) {
+               VKBD_LOG(KERN_ERR, "failed to add buffer to virtqueue.\n");
+               return;
+       }
+
+       virtqueue_kick(vkbd->vq);
+}
+
+static int input_keyboard_open(struct input_dev *dev)
+{
+       VKBD_LOG(KERN_DEBUG, "input_keyboard_open\n");
+       return 0;
+}
+
+static void input_keyboard_close(struct input_dev *dev)
+{
+       VKBD_LOG(KERN_DEBUG, "input_keyboard_close\n");
+}
+
+#if 0
+static int virtio_keyboard_open(struct inode *inode, struct file *file)
+{
+       VKBD_LOG(KERN_DEBUG, "opened.\n");
+       return 0;
+}
+
+static int virtio_keyboard_release(struct inode *inode, struct file *file)
+{
+       VKBD_LOG(KERN_DEBUG, "closed\n");
+       return 0;
+}
+
+struct file_operations virtio_keyboard_fops = {
+       .owner   = THIS_MODULE,
+       .open   = virtio_keyboard_open,
+       .release = virtio_keyboard_release,
+};
+#endif
+
+static int virtio_keyboard_probe(struct virtio_device *vdev)
+{
+       int ret = 0;
+       int index = 0;
+
+       VKBD_LOG(KERN_INFO, "driver is probed\n");
+       vqidx = 0;
+
+       vdev->priv = vkbd = kmalloc(sizeof(struct virtio_keyboard), GFP_KERNEL);
+       if (!vkbd) {
+               return -ENOMEM;
+       }
+       memset(&vkbd->kbdevt, 0x00, sizeof(vkbd->kbdevt));
+
+       vkbd->vdev = vdev;
+       mutex_init(&vkbd->event_mutex);
+
+       vkbd->vq = virtio_find_single_vq(vkbd->vdev, vq_keyboard_handle, "virtio-keyboard-vq");
+       if (IS_ERR(vkbd->vq)) {
+               ret = PTR_ERR(vkbd->vq);
+               kfree(vkbd);
+               vdev->priv = NULL;
+               return ret;
+       }
+
+       for (; index < KBD_BUF_SIZE; index++) {
+               sg_set_buf(&vkbd->sg[index],
+                               &vkbd->kbdevt[index],
+                               sizeof(struct EmulKbdEvent));
+       }
+
+       ret = virtqueue_add_buf(vkbd->vq, vkbd->sg, 0, KBD_BUF_SIZE, (void *)KBD_BUF_SIZE, GFP_ATOMIC);
+       if (ret < 0) {
+               VKBD_LOG(KERN_ERR, "failed to add buffer to virtqueue.\n");
+               kfree(vkbd);
+               vdev->priv = NULL;
+               return ret;
+       }
+
+       /* register for input device */
+       vkbd->idev = input_allocate_device();
+       if (!vkbd->idev) {
+               VKBD_LOG(KERN_ERR, "failed to allocate a input device.\n");
+               kfree(vkbd);
+               vdev->priv = NULL;
+               return -ENOMEM;
+       }
+
+       vkbd->idev->name = "Maru VirtIO Keyboard";
+       vkbd->idev->dev.parent = &(vdev->dev);
+
+       input_set_drvdata(vkbd->idev, vkbd);
+       vkbd->idev->open = input_keyboard_open;
+       vkbd->idev->close = input_keyboard_close;
+
+       /* initialize a device as a keyboard device.
+        * refer to struct input_dev from input.h.  */
+       vkbd->idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP)
+                               | BIT_MASK(EV_MSC) | BIT_MASK(EV_LED);
+       vkbd->idev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL)
+                               | BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE)
+                               | BIT_MASK(LED_KANA);
+       set_bit(MSC_SCAN, vkbd->idev->mscbit);
+
+       /* set keybit field as xinput keyboard. */
+       vkbd->idev->keybit[0] = 0xfffffffe;
+       vkbd->idev->keybit[1] = 0xffffffff;
+       vkbd->idev->keybit[2] = 0xffefffff;
+       vkbd->idev->keybit[3] = 0xfebeffdf;
+       vkbd->idev->keybit[4] = 0xc14057ff;
+       vkbd->idev->keybit[5] = 0xff9f207a;
+       vkbd->idev->keybit[6] = 0x7;
+       vkbd->idev->keybit[7] = 0x10000;
+
+       ret = input_register_device(vkbd->idev);
+       if (ret) {
+               VKBD_LOG(KERN_ERR, "failed to register a input device.\n");
+               input_free_device(vkbd->idev);
+               kfree(vkbd);
+               vdev->priv = NULL;
+               return ret;
+       }
+
+       for (; index < KBD_BUF_SIZE; index++) {
+               sg_set_buf(&vkbd->sg[index],
+                               &vkbd->kbdevt[index],
+                               sizeof(struct EmulKbdEvent));
+       }
+
+       virtqueue_kick(vkbd->vq);
+
+       return 0;
+}
+
+static void virtio_keyboard_remove(struct virtio_device *vdev)
+{
+       VKBD_LOG(KERN_INFO, "driver is removed.\n");
+       if (!vkbd) {
+               VKBD_LOG(KERN_ERR, "vkbd is NULL.\n");
+               return;
+       }
+
+       vdev->config->reset(vdev);
+       vdev->config->del_vqs(vdev);
+
+       input_unregister_device(vkbd->idev);
+
+       kfree(vkbd);
+       vkbd = NULL;
+}
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+
+static struct virtio_driver virtio_keyboard_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .owner = THIS_MODULE,
+       },
+       .id_table = id_table,
+       .probe = virtio_keyboard_probe,
+       .remove = virtio_keyboard_remove,
+#if 0
+#ifdef CONFIG_PM
+       .freeze = virtio_codec_freeze,
+       .restore = virtio_codec_restore,
+#endif
+#endif
+};
+
+static int __init virtio_keyboard_init(void)
+{
+       VKBD_LOG(KERN_INFO, "driver is initialized.\n");
+       return register_virtio_driver(&virtio_keyboard_driver);
+}
+
+static void __exit virtio_keyboard_exit(void)
+{
+       VKBD_LOG(KERN_INFO, "driver is destroyed.\n");
+       unregister_virtio_driver(&virtio_keyboard_driver);
+}
+
+module_init(virtio_keyboard_init);
+module_exit(virtio_keyboard_exit);