xref: /openbmc/qemu/hw/input/virtio-input.c (revision 06a47ef5)
1 /*
2  * This work is licensed under the terms of the GNU GPL, version 2 or
3  * (at your option) any later version.  See the COPYING file in the
4  * top-level directory.
5  */
6 
7 #include "qemu/osdep.h"
8 #include "qapi/error.h"
9 #include "qemu/iov.h"
10 #include "qemu/module.h"
11 #include "trace.h"
12 
13 #include "hw/virtio/virtio.h"
14 #include "hw/qdev-properties.h"
15 #include "hw/virtio/virtio-input.h"
16 
17 #include "standard-headers/linux/input.h"
18 
19 #define VIRTIO_INPUT_VM_VERSION 1
20 
21 /* ----------------------------------------------------------------- */
22 
23 void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
24 {
25     VirtQueueElement *elem;
26     int i, len;
27 
28     if (!vinput->active) {
29         return;
30     }
31 
32     /* queue up events ... */
33     if (vinput->qindex == vinput->qsize) {
34         vinput->qsize++;
35         vinput->queue = g_realloc(vinput->queue, vinput->qsize *
36                                   sizeof(vinput->queue[0]));
37     }
38     vinput->queue[vinput->qindex++].event = *event;
39 
40     /* ... until we see a report sync ... */
41     if (event->type != cpu_to_le16(EV_SYN) ||
42         event->code != cpu_to_le16(SYN_REPORT)) {
43         return;
44     }
45 
46     /* ... then check available space ... */
47     for (i = 0; i < vinput->qindex; i++) {
48         elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement));
49         if (!elem) {
50             while (--i >= 0) {
51                 virtqueue_unpop(vinput->evt, vinput->queue[i].elem, 0);
52             }
53             vinput->qindex = 0;
54             trace_virtio_input_queue_full();
55             return;
56         }
57         vinput->queue[i].elem = elem;
58     }
59 
60     /* ... and finally pass them to the guest */
61     for (i = 0; i < vinput->qindex; i++) {
62         elem = vinput->queue[i].elem;
63         len = iov_from_buf(elem->in_sg, elem->in_num,
64                            0, &vinput->queue[i].event, sizeof(virtio_input_event));
65         virtqueue_push(vinput->evt, elem, len);
66         g_free(elem);
67     }
68     virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
69     vinput->qindex = 0;
70 }
71 
72 static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
73 {
74     /* nothing */
75 }
76 
77 static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq)
78 {
79     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
80     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
81     virtio_input_event event;
82     VirtQueueElement *elem;
83     int len;
84 
85     for (;;) {
86         elem = virtqueue_pop(vinput->sts, sizeof(VirtQueueElement));
87         if (!elem) {
88             break;
89         }
90 
91         memset(&event, 0, sizeof(event));
92         len = iov_to_buf(elem->out_sg, elem->out_num,
93                          0, &event, sizeof(event));
94         if (vic->handle_status) {
95             vic->handle_status(vinput, &event);
96         }
97         virtqueue_push(vinput->sts, elem, len);
98         g_free(elem);
99     }
100     virtio_notify(vdev, vinput->sts);
101 }
102 
103 virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
104                                               uint8_t select,
105                                               uint8_t subsel)
106 {
107     VirtIOInputConfig *cfg;
108 
109     QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
110         if (select == cfg->config.select &&
111             subsel == cfg->config.subsel) {
112             return &cfg->config;
113         }
114     }
115     return NULL;
116 }
117 
118 void virtio_input_add_config(VirtIOInput *vinput,
119                              virtio_input_config *config)
120 {
121     VirtIOInputConfig *cfg;
122 
123     if (virtio_input_find_config(vinput, config->select, config->subsel)) {
124         /* should not happen */
125         fprintf(stderr, "%s: duplicate config: %d/%d\n",
126                 __func__, config->select, config->subsel);
127         abort();
128     }
129 
130     cfg = g_new0(VirtIOInputConfig, 1);
131     cfg->config = *config;
132     QTAILQ_INSERT_TAIL(&vinput->cfg_list, cfg, node);
133 }
134 
135 void virtio_input_init_config(VirtIOInput *vinput,
136                               virtio_input_config *config)
137 {
138     int i = 0;
139 
140     QTAILQ_INIT(&vinput->cfg_list);
141     while (config[i].select) {
142         virtio_input_add_config(vinput, config + i);
143         i++;
144     }
145 }
146 
147 void virtio_input_idstr_config(VirtIOInput *vinput,
148                                uint8_t select, const char *string)
149 {
150     virtio_input_config id;
151 
152     if (!string) {
153         return;
154     }
155     memset(&id, 0, sizeof(id));
156     id.select = select;
157     id.size = snprintf(id.u.string, sizeof(id.u.string), "%s", string);
158     virtio_input_add_config(vinput, &id);
159 }
160 
161 static void virtio_input_get_config(VirtIODevice *vdev, uint8_t *config_data)
162 {
163     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
164     virtio_input_config *config;
165 
166     config = virtio_input_find_config(vinput, vinput->cfg_select,
167                                       vinput->cfg_subsel);
168     if (config) {
169         memcpy(config_data, config, vinput->cfg_size);
170     } else {
171         memset(config_data, 0, vinput->cfg_size);
172     }
173 }
174 
175 static void virtio_input_set_config(VirtIODevice *vdev,
176                                     const uint8_t *config_data)
177 {
178     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
179     virtio_input_config *config = (virtio_input_config *)config_data;
180 
181     vinput->cfg_select = config->select;
182     vinput->cfg_subsel = config->subsel;
183     virtio_notify_config(vdev);
184 }
185 
186 static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f,
187                                           Error **errp)
188 {
189     return f;
190 }
191 
192 static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val)
193 {
194     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
195     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
196 
197     if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
198         if (!vinput->active) {
199             vinput->active = true;
200             if (vic->change_active) {
201                 vic->change_active(vinput);
202             }
203         }
204     }
205 }
206 
207 static void virtio_input_reset(VirtIODevice *vdev)
208 {
209     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
210     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
211 
212     if (vinput->active) {
213         vinput->active = false;
214         if (vic->change_active) {
215             vic->change_active(vinput);
216         }
217     }
218 }
219 
220 static int virtio_input_post_load(void *opaque, int version_id)
221 {
222     VirtIOInput *vinput = opaque;
223     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput);
224     VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
225 
226     vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK;
227     if (vic->change_active) {
228         vic->change_active(vinput);
229     }
230     return 0;
231 }
232 
233 static void virtio_input_device_realize(DeviceState *dev, Error **errp)
234 {
235     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
236     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
237     VirtIOInput *vinput = VIRTIO_INPUT(dev);
238     VirtIOInputConfig *cfg;
239     Error *local_err = NULL;
240 
241     if (vic->realize) {
242         vic->realize(dev, &local_err);
243         if (local_err) {
244             error_propagate(errp, local_err);
245             return;
246         }
247     }
248 
249     virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
250                               vinput->serial);
251 
252     QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
253         if (vinput->cfg_size < cfg->config.size) {
254             vinput->cfg_size = cfg->config.size;
255         }
256     }
257     vinput->cfg_size += 8;
258     assert(vinput->cfg_size <= sizeof(virtio_input_config));
259 
260     virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
261                 vinput->cfg_size);
262     vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
263     vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
264 }
265 
266 static void virtio_input_finalize(Object *obj)
267 {
268     VirtIOInput *vinput = VIRTIO_INPUT(obj);
269     VirtIOInputConfig *cfg, *next;
270 
271     QTAILQ_FOREACH_SAFE(cfg, &vinput->cfg_list, node, next) {
272         QTAILQ_REMOVE(&vinput->cfg_list, cfg, node);
273         g_free(cfg);
274     }
275 
276     g_free(vinput->queue);
277 }
278 
279 static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
280 {
281     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
282     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
283     VirtIOInput *vinput = VIRTIO_INPUT(dev);
284     Error *local_err = NULL;
285 
286     if (vic->unrealize) {
287         vic->unrealize(dev, &local_err);
288         if (local_err) {
289             error_propagate(errp, local_err);
290             return;
291         }
292     }
293     virtio_delete_queue(vinput->evt);
294     virtio_delete_queue(vinput->sts);
295     virtio_cleanup(vdev);
296 }
297 
298 static const VMStateDescription vmstate_virtio_input = {
299     .name = "virtio-input",
300     .minimum_version_id = VIRTIO_INPUT_VM_VERSION,
301     .version_id = VIRTIO_INPUT_VM_VERSION,
302     .fields = (VMStateField[]) {
303         VMSTATE_VIRTIO_DEVICE,
304         VMSTATE_END_OF_LIST()
305     },
306     .post_load = virtio_input_post_load,
307 };
308 
309 static Property virtio_input_properties[] = {
310     DEFINE_PROP_STRING("serial", VirtIOInput, serial),
311     DEFINE_PROP_END_OF_LIST(),
312 };
313 
314 static void virtio_input_class_init(ObjectClass *klass, void *data)
315 {
316     DeviceClass *dc = DEVICE_CLASS(klass);
317     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
318 
319     device_class_set_props(dc, virtio_input_properties);
320     dc->vmsd           = &vmstate_virtio_input;
321     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
322     vdc->realize      = virtio_input_device_realize;
323     vdc->unrealize    = virtio_input_device_unrealize;
324     vdc->get_config   = virtio_input_get_config;
325     vdc->set_config   = virtio_input_set_config;
326     vdc->get_features = virtio_input_get_features;
327     vdc->set_status   = virtio_input_set_status;
328     vdc->reset        = virtio_input_reset;
329 }
330 
331 static const TypeInfo virtio_input_info = {
332     .name          = TYPE_VIRTIO_INPUT,
333     .parent        = TYPE_VIRTIO_DEVICE,
334     .instance_size = sizeof(VirtIOInput),
335     .class_size    = sizeof(VirtIOInputClass),
336     .class_init    = virtio_input_class_init,
337     .abstract      = true,
338     .instance_finalize = virtio_input_finalize,
339 };
340 
341 /* ----------------------------------------------------------------- */
342 
343 static void virtio_register_types(void)
344 {
345     type_register_static(&virtio_input_info);
346 }
347 
348 type_init(virtio_register_types)
349