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