2 * Maru Virtio HW Key Device
4 * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
7 * GiWoong Kim <giwoong.kim@samsung.com>
8 * YeongKyoon Lee <yeongkyoon.lee@samsung.com>
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32 #include "emul_state.h"
33 #include "maru_virtio_hwkey.h"
34 #include "maru_device_ids.h"
37 MULTI_DEBUG_CHANNEL(qemu, hwkey);
39 #define DEVICE_NAME "virtio-hwkey"
44 typedef struct HwKeyEventEntry {
48 QTAILQ_ENTRY(HwKeyEventEntry) node;
51 /* the maximum number of HW key event that can be put into a queue */
52 #define MAX_HWKEY_EVENT_CNT 64
54 static HwKeyEventEntry _events_buf[MAX_HWKEY_EVENT_CNT];
55 static QTAILQ_HEAD(, HwKeyEventEntry) events_queue =
56 QTAILQ_HEAD_INITIALIZER(events_queue);
58 static unsigned int event_ringbuf_cnt; /* _events_buf */
59 static unsigned int event_queue_cnt; /* events_queue */
62 * VirtQueueElement queue
64 typedef struct ElementEntry {
65 unsigned int el_index;
66 unsigned int sg_index;
67 VirtQueueElement elem;
69 QTAILQ_ENTRY(ElementEntry) node;
72 static ElementEntry _elem_buf[10];
73 static QTAILQ_HEAD(, ElementEntry) elem_queue =
74 QTAILQ_HEAD_INITIALIZER(elem_queue);
76 static unsigned int elem_ringbuf_cnt; /* _elem_buf */
77 static unsigned int elem_queue_cnt; /* elem_queue */
81 /* lock for between communication thread and IO thread */
82 static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER;
83 static pthread_mutex_t elem_mutex = PTHREAD_MUTEX_INITIALIZER;
85 void maru_hwkey_event(int event_type, int keycode)
87 HwKeyEventEntry *entry = NULL;
89 if (unlikely(event_queue_cnt >= MAX_HWKEY_EVENT_CNT)) {
90 INFO("full hwkey event queue, lose event\n", event_queue_cnt);
92 qemu_bh_schedule(vhk->bh);
96 entry = &(_events_buf[event_ringbuf_cnt % MAX_HWKEY_EVENT_CNT]);
99 /* hwkey event is copied into the queue */
100 entry->hwkey.keycode = keycode;
101 entry->hwkey.event_type = event_type;
103 pthread_mutex_lock(&event_mutex);
105 entry->index = ++event_queue_cnt; // 1 ~
107 QTAILQ_INSERT_TAIL(&events_queue, entry, node);
109 pthread_mutex_unlock(&event_mutex);
111 /* call maru_virtio_hwkey_notify */
112 qemu_bh_schedule(vhk->bh);
114 TRACE("hwkey event (%d) : keycode=%d, event_type=%d\n",
115 entry->index, entry->hwkey.keycode, entry->hwkey.event_type);
118 static void maru_virtio_hwkey_handle(VirtIODevice *vdev, VirtQueue *vq)
120 int virt_sg_index = 0;
121 ElementEntry *elem_entry = NULL;
123 TRACE("maru_virtio_hwkey_handle\n");
125 if (unlikely(virtio_queue_empty(vhk->vq))) {
126 TRACE("virtqueue is empty\n");
131 elem_entry = &(_elem_buf[elem_ringbuf_cnt % 10]);
134 virt_sg_index = virtqueue_pop(vhk->vq, &elem_entry->elem);
135 if (virt_sg_index == 0) {
138 } else if (virt_sg_index < 0) {
139 ERR("virtqueue is broken\n");
144 pthread_mutex_lock(&elem_mutex);
146 elem_entry->el_index = ++elem_queue_cnt;
147 elem_entry->sg_index = (unsigned int)virt_sg_index;
149 /* save VirtQueueElement */
150 QTAILQ_INSERT_TAIL(&elem_queue, elem_entry, node);
151 /* call maru_virtio_hwkey_notify */
152 qemu_bh_schedule(vhk->bh);
154 pthread_mutex_unlock(&elem_mutex);
158 void maru_virtio_hwkey_notify(void)
160 HwKeyEventEntry *event_entry = NULL;
161 ElementEntry *elem_entry = NULL;
162 VirtQueueElement *element = NULL;
165 TRACE("maru_virtio_hwkey_notify\n");
167 if (unlikely(!virtio_queue_ready(vhk->vq))) {
168 ERR("virtio queue is not ready\n");
173 if (event_queue_cnt == 0) {
178 elem_entry = QTAILQ_FIRST(&elem_queue);
180 if ( elem_entry->sg_index > 0) {
181 /* get hwkey event from host queue */
182 event_entry = QTAILQ_FIRST(&events_queue);
184 TRACE("hwkey(%d) : keycode=%d, event_type=%d | \
185 event_queue_cnt=%d\n",
187 event_entry->hwkey.keycode, event_entry->hwkey.event_type,
190 element = &elem_entry->elem;
191 vbuf = element->in_sg[elem_entry->sg_index - 1].iov_base;
193 /* copy event into virtio buffer */
194 memcpy(vbuf, &(event_entry->hwkey), sizeof(EmulHwKeyEvent));
196 virtqueue_push(vhk->vq, element, sizeof(EmulHwKeyEvent));
197 virtio_notify(&vhk->vdev, vhk->vq);
200 pthread_mutex_lock(&event_mutex);
202 /* remove host event */
203 QTAILQ_REMOVE(&events_queue, event_entry, node);
206 pthread_mutex_unlock(&event_mutex);
210 static uint32_t virtio_hwkey_get_features(
211 VirtIODevice *vdev, uint32_t request_features)
214 return request_features;
217 static void maru_hwkey_bh(void *opaque)
219 maru_virtio_hwkey_notify();
222 VirtIODevice *maru_virtio_hwkey_init(DeviceState *dev)
224 INFO("initialize the hwkey device\n");
226 vhk = (VirtIOHwKey *)virtio_common_init(DEVICE_NAME,
227 VIRTIO_ID_HWKEY, 0 /*config_size*/, sizeof(VirtIOHwKey));
230 ERR("failed to initialize the hwkey device\n");
234 vhk->vdev.get_features = virtio_hwkey_get_features;
235 vhk->vq = virtio_add_queue(&vhk->vdev, 64, maru_virtio_hwkey_handle);
239 /* reset the counters */
240 event_queue_cnt = event_ringbuf_cnt = 0;
241 elem_queue_cnt = elem_ringbuf_cnt = 0;
244 vhk->bh = qemu_bh_new(maru_hwkey_bh, vhk);
249 void maru_virtio_hwkey_exit(VirtIODevice *vdev)
251 VirtIOHwKey *vhk = (VirtIOHwKey *)vdev;
253 INFO("exit the hwkey device\n");
256 qemu_bh_delete(vhk->bh);
259 virtio_cleanup(vdev);
261 pthread_mutex_destroy(&event_mutex);