From 2cb72bab00b3f3e81debe6b5209983be4b2a82dc Mon Sep 17 00:00:00 2001 From: GiWoong Kim Date: Thu, 9 Jan 2014 16:10:50 +0900 Subject: [PATCH] keyboard: added Maru virtio keyboard driver Change-Id: I9372dee6c75e6580cbf944e0a3c3f41610c822c3 Signed-off-by: GiWoong Kim --- arch/x86/configs/i386_tizen_emul_defconfig | 1 + drivers/maru/Kconfig | 4 + drivers/maru/Makefile | 1 + drivers/maru/maru_virtio_keyboard.c | 291 +++++++++++++++++++++++++++++ 4 files changed, 297 insertions(+) create mode 100644 drivers/maru/maru_virtio_keyboard.c diff --git a/arch/x86/configs/i386_tizen_emul_defconfig b/arch/x86/configs/i386_tizen_emul_defconfig index da89e93..18ddee2 100644 --- a/arch/x86/configs/i386_tizen_emul_defconfig +++ b/arch/x86/configs/i386_tizen_emul_defconfig @@ -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 diff --git a/drivers/maru/Kconfig b/drivers/maru/Kconfig index f8adb6a..45de29a 100644 --- a/drivers/maru/Kconfig +++ b/drivers/maru/Kconfig @@ -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 + diff --git a/drivers/maru/Makefile b/drivers/maru/Makefile index ef9bc6c..ee2f362 100644 --- a/drivers/maru/Makefile +++ b/drivers/maru/Makefile @@ -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 index 0000000..2c123a0 --- /dev/null +++ b/drivers/maru/maru_virtio_keyboard.c @@ -0,0 +1,291 @@ +/* + * Maru Virtio Keyboard Device Driver + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Kitae Kim + * SeokYeon Hwang + * YeongKyoon Lee + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Kitae Kim "); +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); -- 2.7.4