update source for tizen_2.1
[sdk/emulator/qemu.git] / tizen / src / hw / maru_virtio_touchscreen.c
1 /*
2  * Maru Virtio Touchscreen Device
3  *
4  * Copyright (c) 2011 - 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 "maru_virtio_touchscreen.h"
33 #include "maru_device_ids.h"
34 #include "debug_ch.h"
35
36 MULTI_DEBUG_CHANNEL(qemu, touchscreen);
37
38
39 #define DEVICE_NAME "virtio-touchscreen"
40
41 /*
42  * touch event queue
43  */
44 typedef struct TouchEventEntry {
45     unsigned int index;
46     EmulTouchEvent touch;
47
48     QTAILQ_ENTRY(TouchEventEntry) node;
49 } TouchEventEntry;
50
51 /* the maximum number of touch event that can be put into a queue */
52 #define MAX_TOUCH_EVENT_CNT 256
53
54 static TouchEventEntry _events_buf[MAX_TOUCH_EVENT_CNT];
55 static QTAILQ_HEAD(, TouchEventEntry) 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
80 TouchscreenState *ts;
81
82 /* lock for between communication thread and IO thread */
83 static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER;
84 static pthread_mutex_t elem_mutex = PTHREAD_MUTEX_INITIALIZER;
85
86
87 void virtio_touchscreen_event(void *opaque, int x, int y, int z, int buttons_state)
88 {
89     TouchEventEntry *entry = NULL;
90
91     if (unlikely(event_queue_cnt >= MAX_TOUCH_EVENT_CNT)) {
92         INFO("full touch event queue, lose event\n", event_queue_cnt);
93
94         qemu_bh_schedule(ts->bh);
95         return;
96     }
97
98     entry = &(_events_buf[event_ringbuf_cnt % MAX_TOUCH_EVENT_CNT]);
99     event_ringbuf_cnt++;
100
101     /* mouse event is copied into the queue */
102     entry->touch.x = x;
103     entry->touch.y = y;
104     entry->touch.z = z;
105     entry->touch.state = buttons_state;
106
107     pthread_mutex_lock(&event_mutex);
108
109     entry->index = ++event_queue_cnt; // 1 ~
110
111     QTAILQ_INSERT_TAIL(&events_queue, entry, node);
112
113     pthread_mutex_unlock(&event_mutex);
114
115     /* call maru_virtio_touchscreen_notify */
116     qemu_bh_schedule(ts->bh);
117
118     TRACE("touch event (%d) : x=%d, y=%d, z=%d, state=%d\n",
119         entry->index, entry->touch.x, entry->touch.y, entry->touch.z, entry->touch.state);
120 }
121
122 static void maru_virtio_touchscreen_handle(VirtIODevice *vdev, VirtQueue *vq)
123 {
124 #if 0 /* not used yet */
125     if (ts->eh_entry == NULL) {
126         void *vbuf = NULL;
127         VirtQueueElement elem;
128         int max_trkid = 0;
129
130         virtqueue_pop(ts->vq, &elem);
131         vbuf = elem.in_sg[0].iov_base;
132         memcpy(&max_trkid, vbuf, sizeof(max_trkid));
133
134         if (max_trkid > 0) {
135             INFO("virtio touchscreen's maximum of tracking id = %d\n", max_trkid);
136
137             /* register a event handler */
138             ts->eh_entry = qemu_add_mouse_event_handler(
139                 virtio_touchscreen_event, ts, 1, "QEMU Virtio Touchscreen");
140             qemu_activate_mouse_event_handler(ts->eh_entry);
141
142             //TODO:
143             virtqueue_push(ts->vq, &elem, 0);
144             virtio_notify(&(ts->vdev), ts->vq);
145         } else {
146             INFO("virtio touchscreen is not added to qemu mouse event handler\n");
147         }
148     }
149 #endif
150
151     int virt_sg_index = 0;
152     ElementEntry *elem_entry = NULL;
153
154     TRACE("maru_virtio_touchscreen_handle\n");
155
156     if (unlikely(virtio_queue_empty(ts->vq))) {
157         TRACE("virtqueue is empty\n");
158         return;
159     }
160
161     while (true) {
162         elem_entry = &(_elem_buf[elem_ringbuf_cnt % 10]);
163         elem_ringbuf_cnt++;
164
165         virt_sg_index = virtqueue_pop(ts->vq, &elem_entry->elem);
166         if (virt_sg_index == 0) {
167             elem_ringbuf_cnt--;
168             break;
169         } else if (virt_sg_index < 0) {
170             ERR("virtqueue is broken\n");
171             elem_ringbuf_cnt--;
172             return;
173         }
174
175         pthread_mutex_lock(&elem_mutex);
176
177         elem_entry->el_index = ++elem_queue_cnt;
178         elem_entry->sg_index = (unsigned int)virt_sg_index;
179
180         /* save VirtQueueElement */
181         QTAILQ_INSERT_TAIL(&elem_queue, elem_entry, node);
182
183         if (ts->waitBuf == true) {
184             ts->waitBuf = false;
185
186             /* call maru_virtio_touchscreen_notify */
187             qemu_bh_schedule(ts->bh);
188         }
189
190         pthread_mutex_unlock(&elem_mutex);
191     }
192 }
193
194 void maru_virtio_touchscreen_notify(void)
195 {
196     ElementEntry *elem_entry = NULL;
197     unsigned int ii = 0;
198
199     TRACE("maru_virtio_touchscreen_notify\n");
200
201     if (unlikely(!virtio_queue_ready(ts->vq))) {
202         ERR("virtio queue is not ready\n");
203         return;
204     }
205
206     while (true) {
207         if (event_queue_cnt == 0) {
208             TRACE("no event\n");
209             break;
210         } else if (elem_queue_cnt == 0) {
211             TRACE("no buffer\n");
212
213             pthread_mutex_lock(&elem_mutex);
214             /* maybe next time */
215             ts->waitBuf = true;
216             pthread_mutex_unlock(&elem_mutex);
217             break;
218         }
219
220         elem_entry = QTAILQ_FIRST(&elem_queue);
221
222         if (elem_entry->sg_index > 0) {
223             TouchEventEntry *event_entry = NULL;
224             VirtQueueElement *element = NULL;
225             void *vbuf = NULL;
226
227             element = &elem_entry->elem;
228             vbuf = element->in_sg[elem_entry->sg_index - 1].iov_base;
229
230             /* get touch event from host queue */
231             event_entry = QTAILQ_FIRST(&events_queue);
232
233             TRACE("touch(%d) : x=%d, y=%d, z=%d, state=%d | \
234                 event_queue_cnt=%d, elem.index=%d, elem.in_num=%d, sg_index=%d\n",
235                 event_entry->index, event_entry->touch.x, event_entry->touch.y,
236                 event_entry->touch.z, event_entry->touch.state,
237                 event_queue_cnt, element->index, element->in_num,
238                 elem_entry->sg_index);
239
240             /* copy event into virtio buffer */
241             memcpy(vbuf, &(event_entry->touch), sizeof(event_entry->touch));
242
243             pthread_mutex_lock(&event_mutex);
244
245             /* remove host event */
246             QTAILQ_REMOVE(&events_queue, event_entry, node);
247             event_queue_cnt--;
248
249             pthread_mutex_unlock(&event_mutex);
250
251             /* put buffer into virtio queue */
252             virtqueue_fill(ts->vq, element, sizeof(EmulTouchEvent), ii++);
253         }
254
255         pthread_mutex_lock(&elem_mutex);
256
257         QTAILQ_REMOVE(&elem_queue, elem_entry, node);
258         elem_queue_cnt--;
259
260         pthread_mutex_unlock(&elem_mutex);
261     }
262
263     if (ii != 0) {
264         /* signal other side */
265         virtqueue_flush(ts->vq, ii);
266         /* notify to guest */
267         virtio_notify(&(ts->vdev), ts->vq);
268     }
269 }
270
271 static uint32_t virtio_touchscreen_get_features(
272     VirtIODevice *vdev, uint32_t request_features)
273 {
274     // TODO:
275     return request_features;
276 }
277
278 static void maru_touchscreen_bh(void *opaque)
279 {
280     //TouchscreenState *ts = (TouchscreenState *)opaque;
281
282     maru_virtio_touchscreen_notify();
283 }
284
285 VirtIODevice *maru_virtio_touchscreen_init(DeviceState *dev)
286 {
287     INFO("initialize the touchscreen device\n");
288
289     ts = (TouchscreenState *)virtio_common_init(DEVICE_NAME,
290         VIRTIO_ID_TOUCHSCREEN, 0 /*config_size*/, sizeof(TouchscreenState));
291
292     if (ts == NULL) {
293         ERR("failed to initialize the touchscreen device\n");
294         return NULL;
295     }
296
297     ts->vdev.get_features = virtio_touchscreen_get_features;
298     ts->vq = virtio_add_queue(&ts->vdev, 64, maru_virtio_touchscreen_handle);
299
300     ts->qdev = dev;
301
302     /* reset the counters */
303     event_queue_cnt = event_ringbuf_cnt = 0;
304     elem_queue_cnt = elem_ringbuf_cnt = 0;
305
306     ts->waitBuf = false;
307
308     /* bottom halves */
309     ts->bh = qemu_bh_new(maru_touchscreen_bh, ts);
310
311 #if 1
312     /* register a event handler */
313     ts->eh_entry = qemu_add_mouse_event_handler(
314         virtio_touchscreen_event, ts, 1, "QEMU Virtio Touchscreen");
315     qemu_activate_mouse_event_handler(ts->eh_entry);
316     INFO("virtio touchscreen is added to qemu mouse event handler\n");
317 #endif
318
319     return &(ts->vdev);
320 }
321
322 void maru_virtio_touchscreen_exit(VirtIODevice *vdev)
323 {
324     INFO("exit the touchscreen device\n");
325
326     if (ts->eh_entry) {
327         qemu_remove_mouse_event_handler(ts->eh_entry);
328     }
329
330     if (ts->bh) {
331         qemu_bh_delete(ts->bh);
332     }
333
334     virtio_cleanup(vdev);
335
336     pthread_mutex_destroy(&event_mutex);
337     pthread_mutex_destroy(&elem_mutex);
338 }
339