421c1e5aba804e04f6fb66830731f9ac651d0307
[sdk/emulator/qemu.git] / tizen / src / hw / maru_virtio_hwkey.c
1 /*
2  * Maru Virtio HW Key Device
3  *
4  * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
5  *
6  * Contact:
7  *  GiWoong Kim <giwoong.kim@samsung.com>
8  *  YeongKyoon Lee <yeongkyoon.lee@samsung.com>
9  *
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.
14  *
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.
19  *
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.
23  *
24  * Contributors:
25  * - S-Core Co., Ltd
26  *
27  */
28
29
30 #include <pthread.h>
31 #include "emul_state.h"
32 #include "maru_virtio_hwkey.h"
33 #include "maru_device_ids.h"
34 #include "debug_ch.h"
35
36 MULTI_DEBUG_CHANNEL(qemu, hwkey);
37
38 #define DEVICE_NAME "virtio-hwkey"
39 #define MAX_BUF_COUNT 64
40 static int vqidx = 0;
41
42 /*
43  * HW key event queue
44  */
45 typedef struct HwKeyEventEntry {
46     unsigned int index;
47     EmulHWKeyEvent hwkey;
48
49     QTAILQ_ENTRY(HwKeyEventEntry) node;
50 } HwKeyEventEntry;
51
52 /* the maximum number of HW key event that can be put into a queue */
53 #define MAX_HWKEY_EVENT_CNT 64
54
55 static HwKeyEventEntry _events_buf[MAX_HWKEY_EVENT_CNT];
56 static QTAILQ_HEAD(, HwKeyEventEntry) events_queue =
57     QTAILQ_HEAD_INITIALIZER(events_queue);
58
59 static unsigned int event_ringbuf_cnt; /* _events_buf */
60 static unsigned int event_queue_cnt; /* events_queue */
61
62 /*
63  * VirtQueueElement queue
64  */
65 typedef struct ElementEntry {
66     unsigned int el_index;
67     unsigned int sg_index;
68     VirtQueueElement elem;
69
70     QTAILQ_ENTRY(ElementEntry) node;
71 } ElementEntry;
72
73 static QTAILQ_HEAD(, ElementEntry) elem_queue =
74     QTAILQ_HEAD_INITIALIZER(elem_queue);
75
76 static unsigned int elem_ringbuf_cnt; /* _elem_buf */
77 static unsigned int elem_queue_cnt; /* elem_queue */
78
79 VirtIOHWKey *vhk;
80 VirtQueueElement elem_vhk;
81
82 /* lock for between communication thread and IO thread */
83 static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER;
84
85 void maru_hwkey_event(int event_type, int keycode)
86 {
87     HwKeyEventEntry *entry = NULL;
88
89     if (unlikely(event_queue_cnt >= MAX_HWKEY_EVENT_CNT)) {
90         INFO("full hwkey event queue, lose event\n", event_queue_cnt);
91
92         qemu_bh_schedule(vhk->bh);
93         return;
94     }
95
96     entry = &(_events_buf[event_ringbuf_cnt % MAX_HWKEY_EVENT_CNT]);
97     event_ringbuf_cnt++;
98
99     /* hwkey event is copied into the queue */
100     entry->hwkey.keycode = keycode;
101     entry->hwkey.event_type = event_type;
102
103     pthread_mutex_lock(&event_mutex);
104
105     entry->index = ++event_queue_cnt; // 1 ~
106
107     QTAILQ_INSERT_TAIL(&events_queue, entry, node);
108
109     pthread_mutex_unlock(&event_mutex);
110
111     /* call maru_virtio_hwkey_notify */
112     qemu_bh_schedule(vhk->bh);
113
114     TRACE("hwkey event (%d) : keycode=%d, event_type=%d\n",
115         entry->index, entry->hwkey.keycode, entry->hwkey.event_type);
116 }
117
118 static void maru_virtio_hwkey_handle(VirtIODevice *vdev, VirtQueue *vq)
119 {
120     int virt_sg_index = 0;
121
122     TRACE("maru_virtio_hwkey_handle\n");
123
124     if (unlikely(virtio_queue_empty(vhk->vq))) {
125         TRACE("virtqueue is empty\n");
126         return;
127     }
128     /* Get a queue buffer which is written by guest side. */
129     do {
130         virt_sg_index = virtqueue_pop(vq, &elem_vhk);
131         TRACE("virtqueue pop.\n");
132     } while (virt_sg_index < MAX_BUF_COUNT);
133 }
134
135 void maru_virtio_hwkey_notify(void)
136 {
137     HwKeyEventEntry *event_entry = NULL;
138
139     TRACE("maru_virtio_hwkey_notify\n");
140
141     if (unlikely(!virtio_queue_ready(vhk->vq))) {
142         ERR("virtio queue is not ready\n");
143         return;
144     }
145
146     while (true) {
147         if (event_queue_cnt == 0) {
148             TRACE("no event\n");
149             break;
150         }
151
152         /* get hwkey event from host queue */
153         event_entry = QTAILQ_FIRST(&events_queue);
154
155         printf("keycode=%d, event_type=%d, event_queue_cnt=%d, vqidx=%d\n",
156               event_entry->hwkey.keycode, event_entry->hwkey.event_type,
157               event_queue_cnt, vqidx);
158
159         /* copy event into virtio buffer */
160         memcpy(elem_vhk.in_sg[vqidx++].iov_base, &(event_entry->hwkey), sizeof(EmulHWKeyEvent));
161         if (vqidx == MAX_BUF_COUNT) {
162             vqidx = 0;
163         }
164
165         virtqueue_push(vhk->vq, &elem_vhk, sizeof(EmulHWKeyEvent));
166         virtio_notify(&vhk->vdev, vhk->vq);
167
168         pthread_mutex_lock(&event_mutex);
169
170         /* remove host event */
171         QTAILQ_REMOVE(&events_queue, event_entry, node);
172         event_queue_cnt--;
173
174         pthread_mutex_unlock(&event_mutex);
175     }
176 }
177
178 static uint32_t virtio_hwkey_get_features(
179     VirtIODevice *vdev, uint32_t request_features)
180 {
181     // TODO:
182     return request_features;
183 }
184
185 static void maru_hwkey_bh(void *opaque)
186 {
187     maru_virtio_hwkey_notify();
188 }
189
190 static int virtio_hwkey_device_init(VirtIODevice *vdev)
191 {
192     INFO("initialize the hwkey device\n");
193     DeviceState *qdev = DEVICE(vdev);
194     vhk = VIRTIO_HWKEY(vdev);
195
196     virtio_init(vdev, TYPE_VIRTIO_HWKEY, VIRTIO_ID_HWKEY, 0);
197
198     if (vdev == NULL) {
199         ERR("failed to initialize the hwkey device\n");
200         return -1;
201     }
202
203     vhk->vq = virtio_add_queue(vdev, MAX_BUF_COUNT, maru_virtio_hwkey_handle);
204
205     vhk->qdev = qdev;
206
207     /* reset the counters */
208     event_queue_cnt = event_ringbuf_cnt = 0;
209     elem_queue_cnt = elem_ringbuf_cnt = 0;
210
211     /* bottom-half */
212     vhk->bh = qemu_bh_new(maru_hwkey_bh, vhk);
213
214     return 0;
215 }
216
217 static int virtio_hwkey_device_exit(DeviceState *qdev)
218 {
219     VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
220
221     INFO("exit the hwkey device\n");
222
223     if (vhk->bh) {
224         qemu_bh_delete(vhk->bh);
225     }
226
227     virtio_cleanup(vdev);
228
229     pthread_mutex_destroy(&event_mutex);
230
231     return 0;
232 }
233
234 static void virtio_hwkey_device_reset(VirtIODevice *vdev)
235 {
236         INFO("reset hwkey device\n");
237         vqidx = 0;
238 }
239
240 static void virtio_hwkey_class_init(ObjectClass *klass, void *data)
241 {
242     DeviceClass *dc = DEVICE_CLASS(klass);
243     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
244     dc->exit = virtio_hwkey_device_exit;
245     vdc->init = virtio_hwkey_device_init;
246         vdc->reset = virtio_hwkey_device_reset;
247     vdc->get_features = virtio_hwkey_get_features;
248 }
249
250 static const TypeInfo virtio_hwkey_info = {
251     .name = TYPE_VIRTIO_HWKEY,
252     .parent = TYPE_VIRTIO_DEVICE,
253     .instance_size = sizeof(VirtIOHWKey),
254     .class_init = virtio_hwkey_class_init,
255 };
256
257 static void virtio_register_types(void)
258 {
259     type_register_static(&virtio_hwkey_info);
260 }
261
262 type_init(virtio_register_types)
263