blob: 3bcdae41b2f035ecd4f851c4ed2f16f6aefdd7e3 [file] [log] [blame]
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +01001/*
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
Peter Maydell9b8bfe22016-01-26 18:17:07 +00007#include "qemu/osdep.h"
Markus Armbrusterda34e652016-03-14 09:01:28 +01008#include "qapi/error.h"
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +01009#include "qemu/iov.h"
Markus Armbruster0b8fa322019-05-23 16:35:07 +020010#include "qemu/module.h"
Gerd Hoffmannc80276b2016-06-23 11:51:35 +020011#include "trace.h"
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010012
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010013#include "hw/virtio/virtio.h"
Markus Armbrustera27bd6c2019-08-12 07:23:51 +020014#include "hw/qdev-properties.h"
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010015#include "hw/virtio/virtio-input.h"
16
17#include "standard-headers/linux/input.h"
18
Gerd Hoffmann2d738372016-04-05 14:31:41 +020019#define VIRTIO_INPUT_VM_VERSION 1
20
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010021/* ----------------------------------------------------------------- */
22
23void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
24{
Paolo Bonzini51b19eb2016-02-04 16:26:51 +020025 VirtQueueElement *elem;
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010026 int i, len;
27
Gerd Hoffmannd9460a72015-10-16 13:33:07 +020028 if (!vinput->active) {
29 return;
30 }
31
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010032 /* queue up events ... */
33 if (vinput->qindex == vinput->qsize) {
34 vinput->qsize++;
Ladi Prosek0f5a15e2017-03-24 15:24:49 +010035 vinput->queue = g_realloc(vinput->queue, vinput->qsize *
Ladi Prosek57094542017-03-24 15:24:50 +010036 sizeof(vinput->queue[0]));
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010037 }
Ladi Prosek57094542017-03-24 15:24:50 +010038 vinput->queue[vinput->qindex++].event = *event;
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010039
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 ... */
Ladi Prosek57094542017-03-24 15:24:50 +010047 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;
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010058 }
59
60 /* ... and finally pass them to the guest */
61 for (i = 0; i < vinput->qindex; i++) {
Ladi Prosek57094542017-03-24 15:24:50 +010062 elem = vinput->queue[i].elem;
Paolo Bonzini51b19eb2016-02-04 16:26:51 +020063 len = iov_from_buf(elem->in_sg, elem->in_num,
Ladi Prosek57094542017-03-24 15:24:50 +010064 0, &vinput->queue[i].event, sizeof(virtio_input_event));
Paolo Bonzini51b19eb2016-02-04 16:26:51 +020065 virtqueue_push(vinput->evt, elem, len);
66 g_free(elem);
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010067 }
68 virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
69 vinput->qindex = 0;
70}
71
72static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
73{
74 /* nothing */
75}
76
77static 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;
Paolo Bonzini51b19eb2016-02-04 16:26:51 +020082 VirtQueueElement *elem;
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010083 int len;
84
Paolo Bonzini51b19eb2016-02-04 16:26:51 +020085 for (;;) {
86 elem = virtqueue_pop(vinput->sts, sizeof(VirtQueueElement));
87 if (!elem) {
88 break;
89 }
90
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010091 memset(&event, 0, sizeof(event));
Paolo Bonzini51b19eb2016-02-04 16:26:51 +020092 len = iov_to_buf(elem->out_sg, elem->out_num,
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010093 0, &event, sizeof(event));
94 if (vic->handle_status) {
95 vic->handle_status(vinput, &event);
96 }
Paolo Bonzini51b19eb2016-02-04 16:26:51 +020097 virtqueue_push(vinput->sts, elem, len);
98 g_free(elem);
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +010099 }
100 virtio_notify(vdev, vinput->sts);
101}
102
Ladi Prosekb065e272016-04-13 16:43:23 +0200103virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
104 uint8_t select,
105 uint8_t subsel)
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100106{
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
118void 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
135void 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
147void 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
161static 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
175static 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
Jason Wang9d5b7312015-07-27 17:49:19 +0800186static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f,
187 Error **errp)
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100188{
189 return f;
190}
191
192static 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
207static 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
Halil Pasic73a17342016-10-06 14:55:45 +0200220static int virtio_input_post_load(void *opaque, int version_id)
Gerd Hoffmann2d738372016-04-05 14:31:41 +0200221{
222 VirtIOInput *vinput = opaque;
223 VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput);
224 VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
Gerd Hoffmann2d738372016-04-05 14:31:41 +0200225
Gerd Hoffmann2d738372016-04-05 14:31:41 +0200226 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
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100233static 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,
Gerd Hoffmann6f2b9a52015-06-18 17:45:47 +0200250 vinput->serial);
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100251
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
Jonah Palmer3857cd52022-04-01 09:23:18 -0400260 virtio_init(vdev, VIRTIO_ID_INPUT, vinput->cfg_size);
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100261 vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
262 vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
263}
264
Marc-André Lureau0137a552016-07-15 10:55:12 +0200265static void virtio_input_finalize(Object *obj)
266{
267 VirtIOInput *vinput = VIRTIO_INPUT(obj);
268 VirtIOInputConfig *cfg, *next;
269
270 QTAILQ_FOREACH_SAFE(cfg, &vinput->cfg_list, node, next) {
271 QTAILQ_REMOVE(&vinput->cfg_list, cfg, node);
272 g_free(cfg);
273 }
Ladi Prosek0f5a15e2017-03-24 15:24:49 +0100274
275 g_free(vinput->queue);
Marc-André Lureau0137a552016-07-15 10:55:12 +0200276}
Marc-André Lureau509ec362019-11-21 13:56:49 +0400277
Markus Armbrusterb69c3c22020-05-05 17:29:24 +0200278static void virtio_input_device_unrealize(DeviceState *dev)
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100279{
280 VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
281 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
Michael S. Tsirkin087ed8a2019-12-09 11:55:10 -0500282 VirtIOInput *vinput = VIRTIO_INPUT(dev);
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100283
284 if (vic->unrealize) {
Markus Armbrusterb69c3c22020-05-05 17:29:24 +0200285 vic->unrealize(dev);
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100286 }
Michael S. Tsirkin087ed8a2019-12-09 11:55:10 -0500287 virtio_delete_queue(vinput->evt);
288 virtio_delete_queue(vinput->sts);
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100289 virtio_cleanup(vdev);
290}
291
Halil Pasic73a17342016-10-06 14:55:45 +0200292static const VMStateDescription vmstate_virtio_input = {
293 .name = "virtio-input",
294 .minimum_version_id = VIRTIO_INPUT_VM_VERSION,
295 .version_id = VIRTIO_INPUT_VM_VERSION,
Richard Hendersonaf0f07d2023-12-21 14:16:14 +1100296 .fields = (const VMStateField[]) {
Halil Pasic73a17342016-10-06 14:55:45 +0200297 VMSTATE_VIRTIO_DEVICE,
298 VMSTATE_END_OF_LIST()
299 },
300 .post_load = virtio_input_post_load,
301};
Dr. David Alan Gilbert428d2ed2016-07-14 18:22:53 +0100302
Gerd Hoffmann6f2b9a52015-06-18 17:45:47 +0200303static Property virtio_input_properties[] = {
304 DEFINE_PROP_STRING("serial", VirtIOInput, serial),
305 DEFINE_PROP_END_OF_LIST(),
306};
307
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100308static void virtio_input_class_init(ObjectClass *klass, void *data)
309{
310 DeviceClass *dc = DEVICE_CLASS(klass);
311 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
312
Marc-André Lureau4f67d302020-01-10 19:30:32 +0400313 device_class_set_props(dc, virtio_input_properties);
Dr. David Alan Gilbert428d2ed2016-07-14 18:22:53 +0100314 dc->vmsd = &vmstate_virtio_input;
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100315 set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
316 vdc->realize = virtio_input_device_realize;
317 vdc->unrealize = virtio_input_device_unrealize;
318 vdc->get_config = virtio_input_get_config;
319 vdc->set_config = virtio_input_set_config;
320 vdc->get_features = virtio_input_get_features;
321 vdc->set_status = virtio_input_set_status;
322 vdc->reset = virtio_input_reset;
323}
324
325static const TypeInfo virtio_input_info = {
326 .name = TYPE_VIRTIO_INPUT,
327 .parent = TYPE_VIRTIO_DEVICE,
328 .instance_size = sizeof(VirtIOInput),
329 .class_size = sizeof(VirtIOInputClass),
330 .class_init = virtio_input_class_init,
331 .abstract = true,
Marc-André Lureau0137a552016-07-15 10:55:12 +0200332 .instance_finalize = virtio_input_finalize,
Gerd Hoffmannf73ddba2014-03-14 14:39:20 +0100333};
334
335/* ----------------------------------------------------------------- */
336
337static void virtio_register_types(void)
338{
339 type_register_static(&virtio_input_info);
340}
341
342type_init(virtio_register_types)