* Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
*
* Contact:
- * Kitae Kim <kitae.kim@samsung.com>
+ * GiWoong Kim <giwoong.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
#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");
+MODULE_AUTHOR("SeokYeon Hwangm <syeon.hwang@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;
+#define MAX_BUF_COUNT 100
+
+//#define DEBUG
struct EmulKbdEvent
{
struct virtqueue *vq;
struct input_dev *idev;
- struct EmulKbdEvent kbdevt[KBD_BUF_SIZE];
- struct scatterlist sg[KBD_BUF_SIZE];
+ struct scatterlist sg[1];
+ struct EmulKbdEvent evtbuf[MAX_BUF_COUNT];
- struct mutex event_mutex;
+ spinlock_t lock;
};
-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;
- }
+ unsigned int len; /* not used */
+ struct virtio_keyboard *vkbd = vq->vdev->priv;
+ struct EmulKbdEvent *event;
+
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&vkbd->lock, flags);
+ while ((event = virtqueue_get_buf(vkbd->vq, &len)) != NULL) {
+ spin_unlock_irqrestore(&vkbd->lock, flags);
+#ifdef DEBUG
+ printk(KERN_ERR "keyboard code = %d, value = %d\n", event->code,
+ event->value);
#endif
- /* how to get keycode and value. */
- input_event(vkbd->idev, EV_KEY, kbdevent.code, kbdevent.value);
+ input_event(vkbd->idev, EV_KEY, event->code, event->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;
+
+ // add new buffer
+ spin_lock_irqsave(&vkbd->lock, flags);
+ sg_init_one(vkbd->sg, event, sizeof(*event));
+ err = virtqueue_add_inbuf(vkbd->vq, vkbd->sg,
+ 1, event, GFP_ATOMIC);
+
+ if (err < 0) {
+ printk(KERN_ERR "failed to add buffer!\n");
}
}
- err = virtqueue_add_inbuf(vq, vkbd->sg, KBD_BUF_SIZE, (void *)vkbd->kbdevt, GFP_ATOMIC);
- if (err < 0) {
- VKBD_LOG(KERN_ERR, "failed to add buffer to virtqueue.\n");
- return;
- }
virtqueue_kick(vkbd->vq);
+ spin_unlock_irqrestore(&vkbd->lock, flags);
}
static int input_keyboard_open(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;
+ int err = 0;
+ unsigned long flags;
+ struct virtio_keyboard *vkbd;
VKBD_LOG(KERN_INFO, "driver is probed\n");
- vqidx = 0;
- vdev->priv = vkbd = kmalloc(sizeof(struct virtio_keyboard), GFP_KERNEL);
+ vdev->priv = vkbd = kzalloc(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)) {
return ret;
}
- for (; index < KBD_BUF_SIZE; index++) {
- sg_set_buf(&vkbd->sg[index],
- &vkbd->kbdevt[index],
- sizeof(struct EmulKbdEvent));
- }
-
- ret = virtqueue_add_inbuf(vkbd->vq, vkbd->sg, KBD_BUF_SIZE, (void *)vkbd->kbdevt, 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) {
return ret;
}
- for (; index < KBD_BUF_SIZE; index++) {
- sg_set_buf(&vkbd->sg[index],
- &vkbd->kbdevt[index],
- sizeof(struct EmulKbdEvent));
+ spin_lock_irqsave(&vkbd->lock, flags);
+ for (index = 0; index < MAX_BUF_COUNT; index++) {
+ sg_init_one(vkbd->sg, &vkbd->evtbuf[index], sizeof(&vkbd->evtbuf[index]));
+
+ err = virtqueue_add_inbuf(vkbd->vq, vkbd->sg,
+ 1, &vkbd->evtbuf[index], GFP_ATOMIC);
+
+ if (err < 0) {
+ printk(KERN_ERR "failed to add buffer\n");
+
+ kfree(vkbd);
+ vdev->priv = NULL;
+ return err;
+ }
}
+ spin_unlock_irqrestore(&vkbd->lock, flags);
virtqueue_kick(vkbd->vq);
static void virtio_keyboard_remove(struct virtio_device *vdev)
{
+ struct virtio_keyboard *vkbd = vdev->priv;
+
VKBD_LOG(KERN_INFO, "driver is removed.\n");
if (!vkbd) {
VKBD_LOG(KERN_ERR, "vkbd is NULL.\n");
* Contact:
* GiWoong Kim <giwoong.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
#include <linux/virtio_config.h>
#include <linux/kthread.h>
+
MODULE_LICENSE("GPL2");
-MODULE_AUTHOR("GiWoong Kim <giwoong.kim@samsung.com>");
-MODULE_DESCRIPTION("Emulator Virtio Touchscreen driver");
+MODULE_AUTHOR("SeokYeon Hwangm <syeon.hwang@samsung.com>");
+MODULE_DESCRIPTION("Emulator virtio touchscreen driver");
#define DEVICE_NAME "virtio-touchscreen"
+#define MAX_TRKID 10
+#define ABS_PRESSURE_MAX 255
+
+#define MAX_BUF_COUNT MAX_TRKID
+
+//#define DEBUG
+
/* This structure must match the qemu definitions */
-typedef struct EmulTouchEvent {
+struct touch_event
+{
uint16_t x, y, z;
uint8_t state;
-} EmulTouchEvent;
-EmulTouchEvent *event;
+};
-typedef struct virtio_touchscreen
+struct virtio_touchscreen
{
struct virtio_device *vdev;
- struct virtqueue *vq;
struct input_dev *idev;
- /* The thread servicing the touchscreen */
- struct task_struct *thread;
-} virtio_touchscreen;
-virtio_touchscreen *vt;
-
-
-#define MAX_TRKID 10
-#define TOUCHSCREEN_RESOLUTION_X 5040
-#define TOUCHSCREEN_RESOLUTION_Y 3780
-#define ABS_PRESSURE_MAX 255
+ struct virtqueue *vq;
+ struct scatterlist sg[1];
+ struct touch_event evtbuf[MAX_BUF_COUNT];
-#define MAX_BUF_COUNT MAX_TRKID
-struct scatterlist sg[MAX_BUF_COUNT];
-EmulTouchEvent vbuf[MAX_BUF_COUNT];
+ spinlock_t lock;
+};
static struct virtio_device_id id_table[] = {
{ VIRTIO_ID_TOUCHSCREEN, VIRTIO_DEV_ANY_ID },
};
-#if 0
-/**
- * @brief : event polling
- */
-static int run_touchscreen(void *_vtouchscreen)
-{
- virtio_touchscreen *vt = NULL;
- int err = 0;
- unsigned int len = 0; /* not used */
- unsigned int index = 0;
- unsigned int recv_index = 0;
- unsigned int id = 0; /* finger id */
-
- struct input_dev *input_dev = NULL;
- EmulTouchEvent *event = NULL;
-
- vt = (virtio_touchscreen *)_vtouchscreen;
- input_dev = vt->idev;
-
- sg_init_table(sg, MAX_BUF_COUNT);
-
- for (index = 0; index < MAX_BUF_COUNT; index++) {
- sg_set_buf(&sg[index], &vbuf[index], sizeof(EmulTouchEvent));
-
- err = virtqueue_add_inbuf(vt->vq, sg, index + 1, (void *)index + 1, GFP_ATOMIC);
- if (err < 0) {
- printk(KERN_ERR "failed to add buf\n");
- }
- }
- virtqueue_kick(vt->vq);
-
- index = 0;
-
- while (!kthread_should_stop())
- {
- while ((recv_index = (unsigned int)virtqueue_get_buf(vt->vq, &len)) == 0) {
- cpu_relax();
- }
-
- do {
- event = &vbuf[recv_index - 1];
-#if 0
- printk(KERN_INFO "touch x=%d, y=%d, z=%d, state=%d, recv_index=%d\n",
- event->x, event->y, event->z, event->state, recv_index);
-#endif
-
- id = event->z;
-
- /* Multi-touch Protocol is B */
- if (event->state != 0)
- { /* pressed */
- input_mt_slot(input_dev, id);
- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 10);
- input_report_abs(input_dev, ABS_MT_POSITION_X, event->x);
- input_report_abs(input_dev, ABS_MT_POSITION_Y, event->y);
- }
- else
- { /* released */
- input_mt_slot(input_dev, id);
- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
- }
-
- input_sync(input_dev);
-
- /* expose buffer to other end */
- err = virtqueue_add_inbuf(vt->vq, sg, recv_index, (void *)recv_index, GFP_ATOMIC);
- if (err < 0) {
- printk(KERN_ERR "failed to add buf\n");
- }
-
- recv_index = (unsigned int)virtqueue_get_buf(vt->vq, &len);
- if (recv_index == 0) {
- break;
- }
- } while(true);
-
- virtqueue_kick(vt->vq);
- }
-
- printk(KERN_INFO "virtio touchscreen thread is stopped\n");
-
- return 0;
-}
-#endif
-
-
-int err;
-unsigned int len; /* not used */
-size_t buf_index;
-size_t recv_index;
-unsigned int finger_id; /* finger id */
-
/**
* @brief : callback for virtqueue
*/
static void vq_touchscreen_callback(struct virtqueue *vq)
{
-#if 0
- printk(KERN_INFO "vq touchscreen callback\n");
+ unsigned int len; /* not used */
+ unsigned int finger_id; /* finger id */
+ struct virtio_touchscreen *vt = vq->vdev->priv;
+ struct touch_event *event;
+
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&vt->lock, flags);
+ while ((event = virtqueue_get_buf(vt->vq, &len)) != NULL) {
+ spin_unlock_irqrestore(&vt->lock, flags);
+#ifdef DEBUG
+ printk(KERN_INFO "touchscreen x=%d, y=%d, z=%d, state=%d, len=%d\n",
+ event->x, event->y, event->z, event->state, len);
#endif
-
- recv_index = (size_t)virtqueue_get_buf(vt->vq, &len);
- if (recv_index == 0) {
- printk(KERN_ERR "failed to get buffer\n");
- return;
- }
-
- do {
- event = &vbuf[recv_index - 1];
-
-#if 0
- printk(KERN_INFO "touch x=%d, y=%d, z=%d, state=%d, recv_index=%d\n",
- event->x, event->y, event->z, event->state, recv_index);
-#endif
-
finger_id = event->z;
if (finger_id < MAX_TRKID) {
printk(KERN_ERR "%d is an invalid finger id!\n", finger_id);
}
- /* expose buffer to other end */
- err = virtqueue_add_inbuf(vt->vq, sg,
- (unsigned int)recv_index, (void *)recv_index, GFP_ATOMIC);
+ // add new buffer
+ spin_lock_irqsave(&vt->lock, flags);
+ sg_init_one(vt->sg, event, sizeof(*event));
+ err = virtqueue_add_inbuf(vt->vq, vt->sg,
+ 1, event, GFP_ATOMIC);
if (err < 0) {
printk(KERN_ERR "failed to add buffer!\n");
}
-
- recv_index = (size_t)virtqueue_get_buf(vt->vq, &len);
- if (recv_index == 0) {
- break;
- }
- } while(true);
+ }
virtqueue_kick(vt->vq);
-}
-
-static int virtio_touchscreen_open(struct inode *inode, struct file *file)
-{
- printk(KERN_INFO "virtio touchscreen device is opened\n");
- return 0;
-}
-
-static int virtio_touchscreen_release(struct inode *inode, struct file *file)
-{
- printk(KERN_INFO "virtio touchscreen device is closed\n");
- return 0;
+ spin_unlock_irqrestore(&vt->lock, flags);
}
static int input_touchscreen_open(struct input_dev *dev)
printk(KERN_INFO "input touchscreen device is closed\n");
}
-struct file_operations virtio_touchscreen_fops = {
- .owner = THIS_MODULE,
- .open = virtio_touchscreen_open,
- .release = virtio_touchscreen_release,
-};
-
extern char *saved_command_line;
+
#define VM_RESOLUTION_KEY "vm_resolution="
static int virtio_touchscreen_probe(struct virtio_device *vdev)
int err = 0;
int ret = 0;
+ unsigned long flags;
+ int index;
+ struct virtio_touchscreen *vt;
+
printk(KERN_INFO "virtio touchscreen driver is probed\n");
/* init virtio */
- vdev->priv = vt = kmalloc(sizeof(*vt), GFP_KERNEL);
+ vdev->priv = vt = kzalloc(sizeof(*vt), GFP_KERNEL);
if (!vt) {
return -ENOMEM;
}
+ spin_lock_init(&vt->lock);
+
vt->vdev = vdev;
vt->vq = virtio_find_single_vq(vt->vdev,
return ret;
}
- /* enable callback */
- virtqueue_enable_cb(vt->vq);
-
- sg_init_table(sg, MAX_BUF_COUNT);
-
- /* prepare the buffers */
- for (buf_index = 0; buf_index < MAX_BUF_COUNT; buf_index++) {
- sg_set_buf(&sg[buf_index], &vbuf[buf_index], sizeof(EmulTouchEvent));
-
- err = virtqueue_add_inbuf(vt->vq, sg,
- buf_index + 1, (void *)buf_index + 1, GFP_ATOMIC);
-
- if (err < 0) {
- printk(KERN_ERR "failed to add buffer\n");
-
- kfree(vt);
- vdev->priv = NULL;
- return ret;
- }
- }
-
cmdline = kzalloc(strlen(saved_command_line) + 1, GFP_KERNEL);
if (cmdline) {
/* get VM resolution */
return ret;
}
-#if 0 /* using a thread */
+ spin_lock_irqsave(&vt->lock, flags);
+ /* prepare the buffers */
+ for (index = 0; index < MAX_BUF_COUNT; index++) {
+ sg_init_one(vt->sg, &vt->evtbuf[index], sizeof(&vt->evtbuf[index]));
- /* Responses from the hypervisor occur through the get_buf function */
- vt->thread = kthread_run(run_touchscreen, vt, "vtouchscreen");
- if (IS_ERR(vt->thread)) {
- printk(KERN_ERR "unable to start the virtio touchscreen thread\n");
- ret = PTR_ERR(vt->thread);
+ err = virtqueue_add_inbuf(vt->vq, vt->sg,
+ 1, &vt->evtbuf[index], GFP_ATOMIC);
- input_mt_destroy_slots(vt->idev);
- input_free_device(vt->idev);
- kfree(vt);
- vdev->priv = NULL;
- return ret;
+ if (err < 0) {
+ printk(KERN_ERR "failed to add buffer\n");
+
+ kfree(vt);
+ vdev->priv = NULL;
+ return err;
+ }
}
-#else /* using a callback */
+ spin_unlock_irqrestore(&vt->lock, flags);
virtqueue_kick(vt->vq);
- buf_index = 0;
-
-#endif
-
return 0;
}
static void virtio_touchscreen_remove(struct virtio_device *vdev)
{
- virtio_touchscreen *vts = NULL;
+ struct virtio_touchscreen *vt = NULL;
printk(KERN_INFO "virtio touchscreen driver is removed\n");
- vts = vdev->priv;
-
- kthread_stop(vts->thread);
+ vt = vdev->priv;
vdev->config->reset(vdev); /* reset device */
vdev->config->del_vqs(vdev); /* clean up the queues */
- input_unregister_device(vts->idev);
- input_mt_destroy_slots(vts->idev);
+ input_unregister_device(vt->idev);
+ input_mt_destroy_slots(vt->idev);
- kfree(vts);
+ kfree(vt);
}
MODULE_DEVICE_TABLE(virtio, id_table);
module_init(virtio_touchscreen_init);
module_exit(virtio_touchscreen_exit);
-