xref: /qemu/hw/input/virtio-input.c (revision f2ad72b3)
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 "qemu/iov.h"
9 
10 #include "hw/qdev.h"
11 #include "hw/virtio/virtio.h"
12 #include "hw/virtio/virtio-input.h"
13 
14 #include "standard-headers/linux/input.h"
15 
16 /* ----------------------------------------------------------------- */
17 
18 void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
19 {
20     VirtQueueElement elem;
21     unsigned have, need;
22     int i, len;
23 
24     if (!vinput->active) {
25         return;
26     }
27 
28     /* queue up events ... */
29     if (vinput->qindex == vinput->qsize) {
30         vinput->qsize++;
31         vinput->queue = realloc(vinput->queue, vinput->qsize *
32                                 sizeof(virtio_input_event));
33     }
34     vinput->queue[vinput->qindex++] = *event;
35 
36     /* ... until we see a report sync ... */
37     if (event->type != cpu_to_le16(EV_SYN) ||
38         event->code != cpu_to_le16(SYN_REPORT)) {
39         return;
40     }
41 
42     /* ... then check available space ... */
43     need = sizeof(virtio_input_event) * vinput->qindex;
44     virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0);
45     if (have < need) {
46         vinput->qindex = 0;
47         fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__);
48         return;
49     }
50 
51     /* ... and finally pass them to the guest */
52     for (i = 0; i < vinput->qindex; i++) {
53         if (!virtqueue_pop(vinput->evt, &elem)) {
54             /* should not happen, we've checked for space beforehand */
55             fprintf(stderr, "%s: Huh?  No vq elem available ...\n", __func__);
56             return;
57         }
58         len = iov_from_buf(elem.in_sg, elem.in_num,
59                            0, vinput->queue+i, sizeof(virtio_input_event));
60         virtqueue_push(vinput->evt, &elem, len);
61     }
62     virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
63     vinput->qindex = 0;
64 }
65 
66 static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
67 {
68     /* nothing */
69 }
70 
71 static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq)
72 {
73     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
74     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
75     virtio_input_event event;
76     VirtQueueElement elem;
77     int len;
78 
79     while (virtqueue_pop(vinput->sts, &elem)) {
80         memset(&event, 0, sizeof(event));
81         len = iov_to_buf(elem.out_sg, elem.out_num,
82                          0, &event, sizeof(event));
83         if (vic->handle_status) {
84             vic->handle_status(vinput, &event);
85         }
86         virtqueue_push(vinput->sts, &elem, len);
87     }
88     virtio_notify(vdev, vinput->sts);
89 }
90 
91 static virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
92                                                      uint8_t select,
93                                                      uint8_t subsel)
94 {
95     VirtIOInputConfig *cfg;
96 
97     QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
98         if (select == cfg->config.select &&
99             subsel == cfg->config.subsel) {
100             return &cfg->config;
101         }
102     }
103     return NULL;
104 }
105 
106 void virtio_input_add_config(VirtIOInput *vinput,
107                              virtio_input_config *config)
108 {
109     VirtIOInputConfig *cfg;
110 
111     if (virtio_input_find_config(vinput, config->select, config->subsel)) {
112         /* should not happen */
113         fprintf(stderr, "%s: duplicate config: %d/%d\n",
114                 __func__, config->select, config->subsel);
115         abort();
116     }
117 
118     cfg = g_new0(VirtIOInputConfig, 1);
119     cfg->config = *config;
120     QTAILQ_INSERT_TAIL(&vinput->cfg_list, cfg, node);
121 }
122 
123 void virtio_input_init_config(VirtIOInput *vinput,
124                               virtio_input_config *config)
125 {
126     int i = 0;
127 
128     QTAILQ_INIT(&vinput->cfg_list);
129     while (config[i].select) {
130         virtio_input_add_config(vinput, config + i);
131         i++;
132     }
133 }
134 
135 void virtio_input_idstr_config(VirtIOInput *vinput,
136                                uint8_t select, const char *string)
137 {
138     virtio_input_config id;
139 
140     if (!string) {
141         return;
142     }
143     memset(&id, 0, sizeof(id));
144     id.select = select;
145     id.size = snprintf(id.u.string, sizeof(id.u.string), "%s", string);
146     virtio_input_add_config(vinput, &id);
147 }
148 
149 static void virtio_input_get_config(VirtIODevice *vdev, uint8_t *config_data)
150 {
151     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
152     virtio_input_config *config;
153 
154     config = virtio_input_find_config(vinput, vinput->cfg_select,
155                                       vinput->cfg_subsel);
156     if (config) {
157         memcpy(config_data, config, vinput->cfg_size);
158     } else {
159         memset(config_data, 0, vinput->cfg_size);
160     }
161 }
162 
163 static void virtio_input_set_config(VirtIODevice *vdev,
164                                     const uint8_t *config_data)
165 {
166     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
167     virtio_input_config *config = (virtio_input_config *)config_data;
168 
169     vinput->cfg_select = config->select;
170     vinput->cfg_subsel = config->subsel;
171     virtio_notify_config(vdev);
172 }
173 
174 static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f,
175                                           Error **errp)
176 {
177     return f;
178 }
179 
180 static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val)
181 {
182     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
183     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
184 
185     if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
186         if (!vinput->active) {
187             vinput->active = true;
188             if (vic->change_active) {
189                 vic->change_active(vinput);
190             }
191         }
192     }
193 }
194 
195 static void virtio_input_reset(VirtIODevice *vdev)
196 {
197     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
198     VirtIOInput *vinput = VIRTIO_INPUT(vdev);
199 
200     if (vinput->active) {
201         vinput->active = false;
202         if (vic->change_active) {
203             vic->change_active(vinput);
204         }
205     }
206 }
207 
208 static void virtio_input_device_realize(DeviceState *dev, Error **errp)
209 {
210     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
211     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
212     VirtIOInput *vinput = VIRTIO_INPUT(dev);
213     VirtIOInputConfig *cfg;
214     Error *local_err = NULL;
215 
216     if (vic->realize) {
217         vic->realize(dev, &local_err);
218         if (local_err) {
219             error_propagate(errp, local_err);
220             return;
221         }
222     }
223 
224     virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
225                               vinput->serial);
226 
227     QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
228         if (vinput->cfg_size < cfg->config.size) {
229             vinput->cfg_size = cfg->config.size;
230         }
231     }
232     vinput->cfg_size += 8;
233     assert(vinput->cfg_size <= sizeof(virtio_input_config));
234 
235     virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
236                 vinput->cfg_size);
237     vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
238     vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
239 }
240 
241 static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
242 {
243     VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
244     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
245     Error *local_err = NULL;
246 
247     if (vic->unrealize) {
248         vic->unrealize(dev, &local_err);
249         if (local_err) {
250             error_propagate(errp, local_err);
251             return;
252         }
253     }
254     virtio_cleanup(vdev);
255 }
256 
257 static Property virtio_input_properties[] = {
258     DEFINE_PROP_STRING("serial", VirtIOInput, serial),
259     DEFINE_PROP_END_OF_LIST(),
260 };
261 
262 static void virtio_input_class_init(ObjectClass *klass, void *data)
263 {
264     DeviceClass *dc = DEVICE_CLASS(klass);
265     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
266 
267     dc->props          = virtio_input_properties;
268     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
269     vdc->realize      = virtio_input_device_realize;
270     vdc->unrealize    = virtio_input_device_unrealize;
271     vdc->get_config   = virtio_input_get_config;
272     vdc->set_config   = virtio_input_set_config;
273     vdc->get_features = virtio_input_get_features;
274     vdc->set_status   = virtio_input_set_status;
275     vdc->reset        = virtio_input_reset;
276 }
277 
278 static const TypeInfo virtio_input_info = {
279     .name          = TYPE_VIRTIO_INPUT,
280     .parent        = TYPE_VIRTIO_DEVICE,
281     .instance_size = sizeof(VirtIOInput),
282     .class_size    = sizeof(VirtIOInputClass),
283     .class_init    = virtio_input_class_init,
284     .abstract      = true,
285 };
286 
287 /* ----------------------------------------------------------------- */
288 
289 static void virtio_register_types(void)
290 {
291     type_register_static(&virtio_input_info);
292 }
293 
294 type_init(virtio_register_types)
295