* Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
*
* Contact:
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
* Jinhyung Jo <jinhyung.jo@samsung.com>
- * Sangho Park <sangho1206.park@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>
+#include "maru_virtio_device.h"
+
MODULE_LICENSE("GPL2");
MODULE_AUTHOR("Jinhyung Jo <jinhyung.jo@samsung.com>");
MODULE_DESCRIPTION("Emulator Virtio Rotary Driver");
#define VR_LOG(log_level, fmt, ...) \
printk(log_level "%s: " fmt, DRIVER_NAME, ##__VA_ARGS__)
-#define ROTARY_EVENT_MAX 32
struct rotary_event {
int32_t delta;
int32_t type;
};
-#define ROTARY_EVENT_BUF_SIZE \
- (ROTARY_EVENT_MAX * sizeof(struct rotary_event))
-
struct virtio_rotary {
- struct virtio_device *vdev;
- struct virtqueue *vq;
- struct input_dev *idev;
- struct mutex mutex;
+ struct maru_virtio_device *mdev;
- struct rotary_event event[ROTARY_EVENT_MAX];
+ struct input_dev *idev;
+ struct rotary_event evtbuf[MAX_BUF_COUNT];
};
-struct virtio_rotary *vrtr;
-
static int last_pos; /* 0 ~ 360 */
static int last_detent;
_n; \
})
+/*
static int add_inbuf(struct virtqueue *vq, struct rotary_event *event)
{
struct scatterlist sg[1];
return ret;
}
+*/
static int get_rotary_pos(int value)
{
return REMAINDER(value, 360);
}
-static void vq_rotary_handler(struct virtqueue *vq)
+static void vq_rotary_callback(struct virtqueue *vq)
{
int err = 0;
- struct rotary_event *data;
+ struct rotary_event *event;
unsigned int len = 0;
- size_t j, num_event;
-
- data = (struct rotary_event *)virtqueue_get_buf(vq, &len);
- if (!data) {
- VR_LOG(KERN_ERR, "there is no available buffer\n");
- return;
- }
+ struct virtio_rotary *vrtr = vq->vdev->priv;
+ struct maru_virtio_device *mdev = vrtr->mdev;
+ unsigned long flags;
- num_event = (size_t)len / sizeof(struct rotary_event);
- VR_LOG(KERN_DEBUG, "len(%u), num_event(%zu)\n", len, num_event);
-
- for (j = 0; j < num_event; j++) {
+ spin_lock_irqsave(&mdev->lock, flags);
+ while ((event = virtqueue_get_buf(mdev->vq, &len)) != NULL) {
int i = 0;
int pos = 0;
int value = 0;
- struct rotary_event event;
- memcpy(&event, &data[j],
- sizeof(struct rotary_event));
+ spin_unlock_irqrestore(&mdev->lock, flags);
- event.delta %= 360;
- if (event.delta == 0)
+ event->delta %= 360;
+ if (event->delta == 0)
continue;
- pos = get_rotary_pos(last_pos + event.delta);
+ pos = get_rotary_pos(last_pos + event->delta);
VR_LOG(KERN_DEBUG,
- "rotary event: idx(%zu), event.delta(%d), pos(%d)\n",
- j, event.delta, pos);
+ "rotary event: event.delta(%d), pos(%d)\n", event->delta, pos);
- for (i = 1; i <= abs(event.delta); i++) {
- value = (event.delta > 0) ? last_pos + i : last_pos - i;
+ for (i = 1; i <= abs(event->delta); i++) {
+ value = (event->delta > 0) ? last_pos + i : last_pos - i;
if ((value % DETENT_UNIT) == 0) {
input_report_rel(vrtr->idev, REL_WHEEL, 1);
input_sync(vrtr->idev);
if (get_rotary_pos(value) != last_detent) {
last_detent = get_rotary_pos(value);
- if (event.delta > 0) { /* CW */
+ if (event->delta > 0) { /* CW */
input_report_rel(vrtr->idev,
REL_WHEEL, 2);
} else { /* CCW */
VR_LOG(KERN_INFO,
"rotary event: delta(%d), detent(%d)\n",
- event.delta, last_detent);
+ event->delta, last_detent);
}
}
last_pos = pos;
- }
- err = add_inbuf(vrtr->vq, vrtr->event);
- if (err < 0) {
- VR_LOG(KERN_ERR, "failed to add buffer to virtqueue\n");
- return;
+ // add new buffer
+ spin_lock_irqsave(&mdev->lock, flags);
+ err = add_new_buf(mdev, event, sizeof(*event));
+ if (err < 0) {
+ printk(KERN_ERR "failed to add buffer\n");
+ }
}
- virtqueue_kick(vrtr->vq);
+
+ virtqueue_kick(mdev->vq);
+ spin_unlock_irqrestore(&mdev->lock, flags);
}
static int input_rotary_open(struct input_dev *dev)
static int virtio_rotary_probe(struct virtio_device *vdev)
{
int ret = 0;
+ struct virtio_rotary *vrtr = vdev->priv;
if (vrtr) {
VR_LOG(KERN_ERR, "driver is already exist\n");
}
vdev->priv = vrtr = kzalloc(sizeof(struct virtio_rotary), GFP_KERNEL);
- if (!vrtr)
+ if (!vrtr) {
return -ENOMEM;
-
- vrtr->vdev = vdev;
- mutex_init(&vrtr->mutex);
-
- vrtr->vq = virtio_find_single_vq(vrtr->vdev,
- vq_rotary_handler,
- "maru-rotary-vq");
- if (IS_ERR(vrtr->vq)) {
- ret = PTR_ERR(vrtr->vq);
- kfree(vrtr);
- vdev->priv = NULL;
- return ret;
+ }
+ vrtr->mdev = kzalloc(sizeof(*vrtr->mdev), GFP_KERNEL);
+ if (!vrtr->mdev) {
+ ret = -ENOMEM;
+ goto err3;
}
/* register for input device */
vrtr->idev = input_allocate_device();
if (!vrtr->idev) {
VR_LOG(KERN_ERR, "failed to allocate a input device\n");
- kfree(vrtr);
- vdev->priv = NULL;
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err2;
}
vrtr->idev->name = DRIVER_NAME;
ret = input_register_device(vrtr->idev);
if (ret) {
VR_LOG(KERN_ERR, "failed to register a input device\n");
- input_free_device(vrtr->idev);
- kfree(vrtr);
- vdev->priv = NULL;
- return ret;
+ ret = -1;
+ goto err;
}
- ret = add_inbuf(vrtr->vq, vrtr->event);
- if (ret < 0) {
- VR_LOG(KERN_ERR, "failed to add buffer to virtqueue\n");
- input_unregister_device(vrtr->idev);
- input_free_device(vrtr->idev);
- kfree(vrtr);
- vdev->priv = NULL;
- return ret;
+ ret = init_virtio_device(vdev, vrtr->mdev, vq_rotary_callback,
+ vrtr->evtbuf, sizeof(struct rotary_event));
+ if (ret) {
+ goto err;
}
- VR_LOG(KERN_INFO, "driver probe done\n");
+ printk(KERN_INFO "virtio rotary driver is probed\n");
return 0;
+
+err:
+ input_free_device(vrtr->idev);
+err2:
+ kfree(vrtr->mdev);
+err3:
+ kfree(vrtr);
+ vdev->priv = NULL;
+ return ret;
}
static void virtio_rotary_remove(struct virtio_device *vdev)
{
+ struct virtio_rotary *vrtr = vdev->priv;
+
if (!vrtr) {
VR_LOG(KERN_ERR, "rotary instance is NULL\n");
return;
}
- vdev->config->reset(vdev);
- vdev->config->del_vqs(vdev);
-
input_unregister_device(vrtr->idev);
input_free_device(vrtr->idev);
+ deinit_virtio_device(vdev);
kfree(vrtr);
- vrtr = NULL;
- vdev->priv = NULL;
+
VR_LOG(KERN_INFO, "driver is removed\n");
}