update source for tizen_2.1
[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 "console.h"
32 #include "emul_state.h"
33 #include "maru_virtio_hwkey.h"
34 #include "maru_device_ids.h"
35 #include "debug_ch.h"
36
37 MULTI_DEBUG_CHANNEL(qemu, hwkey);
38
39 #define DEVICE_NAME "virtio-hwkey"
40
41 /*
42  * HW key event queue
43  */
44 typedef struct HwKeyEventEntry {
45     unsigned int index;
46     EmulHwKeyEvent hwkey;
47
48     QTAILQ_ENTRY(HwKeyEventEntry) node;
49 } HwKeyEventEntry;
50
51 /* the maximum number of HW key event that can be put into a queue */
52 #define MAX_HWKEY_EVENT_CNT 64
53
54 static HwKeyEventEntry _events_buf[MAX_HWKEY_EVENT_CNT];
55 static QTAILQ_HEAD(, HwKeyEventEntry) events_queue =
56     QTAILQ_HEAD_INITIALIZER(events_queue);
57
58 static unsigned int event_ringbuf_cnt; /* _events_buf */
59 static unsigned int event_queue_cnt; /* events_queue */
60
61 /*
62  * VirtQueueElement queue
63  */
64 typedef struct ElementEntry {
65     unsigned int el_index;
66     unsigned int sg_index;
67     VirtQueueElement elem;
68
69     QTAILQ_ENTRY(ElementEntry) node;
70 } ElementEntry;
71
72 static ElementEntry _elem_buf[10];
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
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;
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     ElementEntry *elem_entry = NULL;
122
123     TRACE("maru_virtio_hwkey_handle\n");
124
125     if (unlikely(virtio_queue_empty(vhk->vq))) {
126         TRACE("virtqueue is empty\n");
127         return;
128     }
129
130     while (true) {
131         elem_entry = &(_elem_buf[elem_ringbuf_cnt % 10]);
132         elem_ringbuf_cnt++;
133
134         virt_sg_index = virtqueue_pop(vhk->vq, &elem_entry->elem);
135         if (virt_sg_index == 0) {
136             elem_ringbuf_cnt--;
137             break;
138         } else if (virt_sg_index < 0) {
139             ERR("virtqueue is broken\n");
140             elem_ringbuf_cnt--;
141             return;
142         }
143
144         pthread_mutex_lock(&elem_mutex);
145
146         elem_entry->el_index = ++elem_queue_cnt;
147         elem_entry->sg_index = (unsigned int)virt_sg_index;
148
149         /* save VirtQueueElement */
150         QTAILQ_INSERT_TAIL(&elem_queue, elem_entry, node);
151         /* call maru_virtio_hwkey_notify */
152         qemu_bh_schedule(vhk->bh);
153
154         pthread_mutex_unlock(&elem_mutex);
155     }
156 }
157
158 void maru_virtio_hwkey_notify(void)
159 {
160     HwKeyEventEntry *event_entry = NULL;
161     ElementEntry *elem_entry = NULL;
162     VirtQueueElement *element = NULL;
163     void *vbuf = NULL;
164
165     TRACE("maru_virtio_hwkey_notify\n");
166
167     if (unlikely(!virtio_queue_ready(vhk->vq))) {
168         ERR("virtio queue is not ready\n");
169         return;
170     }
171
172     while (true) {
173         if (event_queue_cnt == 0) {
174             TRACE("no event\n");
175             break;
176         }
177
178         elem_entry = QTAILQ_FIRST(&elem_queue);
179
180         if ( elem_entry->sg_index > 0) {
181             /* get hwkey event from host queue */
182             event_entry = QTAILQ_FIRST(&events_queue);
183
184             TRACE("hwkey(%d) : keycode=%d, event_type=%d | \
185               event_queue_cnt=%d\n",
186               event_entry->index,
187               event_entry->hwkey.keycode, event_entry->hwkey.event_type,
188               event_queue_cnt);
189           
190             element = &elem_entry->elem;
191             vbuf = element->in_sg[elem_entry->sg_index - 1].iov_base;
192
193             /* copy event into virtio buffer */
194             memcpy(vbuf, &(event_entry->hwkey), sizeof(EmulHwKeyEvent));
195
196             virtqueue_push(vhk->vq, element, sizeof(EmulHwKeyEvent));
197             virtio_notify(&vhk->vdev, vhk->vq);
198         }
199
200         pthread_mutex_lock(&event_mutex);
201
202         /* remove host event */
203         QTAILQ_REMOVE(&events_queue, event_entry, node);
204         event_queue_cnt--;
205
206         pthread_mutex_unlock(&event_mutex);
207     }
208 }
209
210 static uint32_t virtio_hwkey_get_features(
211     VirtIODevice *vdev, uint32_t request_features)
212 {
213     // TODO:
214     return request_features;
215 }
216
217 static void maru_hwkey_bh(void *opaque)
218 {
219     maru_virtio_hwkey_notify();
220 }
221
222 VirtIODevice *maru_virtio_hwkey_init(DeviceState *dev)
223 {
224     INFO("initialize the hwkey device\n");
225
226     vhk = (VirtIOHwKey *)virtio_common_init(DEVICE_NAME,
227         VIRTIO_ID_HWKEY, 0 /*config_size*/, sizeof(VirtIOHwKey));
228
229     if (vhk == NULL) {
230         ERR("failed to initialize the hwkey device\n");
231         return NULL;
232     }
233
234     vhk->vdev.get_features = virtio_hwkey_get_features;
235     vhk->vq = virtio_add_queue(&vhk->vdev, 64, maru_virtio_hwkey_handle);
236
237     vhk->qdev = dev;
238
239     /* reset the counters */
240     event_queue_cnt = event_ringbuf_cnt = 0;
241     elem_queue_cnt = elem_ringbuf_cnt = 0;
242
243     /* bottom-half */
244     vhk->bh = qemu_bh_new(maru_hwkey_bh, vhk);
245
246     return &(vhk->vdev);
247 }
248
249 void maru_virtio_hwkey_exit(VirtIODevice *vdev)
250 {
251     VirtIOHwKey *vhk = (VirtIOHwKey *)vdev;
252
253     INFO("exit the hwkey device\n");
254
255     if (vhk->bh) {
256         qemu_bh_delete(vhk->bh);
257     }
258
259     virtio_cleanup(vdev);
260
261     pthread_mutex_destroy(&event_mutex);
262 }
263