1 /*
2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2021, Haiku, Inc. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8 #include <virtio.h>
9 #include <virtio_defs.h>
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <new>
15
16 #include <kernel.h>
17 #include <fs/devfs.h>
18 #include <int.h>
19
20 #include <virtio_input_driver.h>
21
22 #include <AutoDeleter.h>
23 #include <AutoDeleterOS.h>
24 #include <AutoDeleterDrivers.h>
25 #include <debug.h>
26
27
28 //#define TRACE_VIRTIO_INPUT
29 #ifdef TRACE_VIRTIO_INPUT
30 # define TRACE(x...) dprintf("virtio_input: " x)
31 #else
32 # define TRACE(x...) ;
33 #endif
34 #define ERROR(x...) dprintf("virtio_input: " x)
35 #define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
36
37 #define VIRTIO_INPUT_DRIVER_MODULE_NAME "drivers/input/virtio_input/driver_v1"
38 #define VIRTIO_INPUT_DEVICE_MODULE_NAME "drivers/input/virtio_input/device_v1"
39 #define VIRTIO_INPUT_DEVICE_ID_GENERATOR "virtio_input/device_id"
40
41
42 struct Packet {
43 VirtioInputPacket data;
44 int32 next;
45 };
46
47
48 struct VirtioInputDevice {
49 device_node* node;
50 ::virtio_device virtio_device;
51 virtio_device_interface* virtio;
52 ::virtio_queue virtio_queue;
53
54 uint64 features;
55
56 uint32 packetCnt;
57 int32 freePackets;
58 int32 readyPackets, lastReadyPacket;
59 AreaDeleter packetArea;
60 phys_addr_t physAdr;
61 Packet* packets;
62
63 SemDeleter sem_cb;
64 };
65
66
67 struct VirtioInputHandle {
68 VirtioInputDevice* info;
69 };
70
71
72 device_manager_info* gDeviceManager;
73
74 #ifdef TRACE_VIRTIO_INPUT
75 static void
WriteInputPacket(const VirtioInputPacket & pkt)76 WriteInputPacket(const VirtioInputPacket &pkt)
77 {
78 switch (pkt.type) {
79 case kVirtioInputEvSyn:
80 TRACE("syn");
81 break;
82 case kVirtioInputEvKey:
83 TRACE("key, ");
84 switch (pkt.code) {
85 case kVirtioInputBtnLeft:
86 TRACE("left");
87 break;
88 case kVirtioInputBtnRight:
89 TRACE("middle");
90 break;
91 case kVirtioInputBtnMiddle:
92 TRACE("right");
93 break;
94 case kVirtioInputBtnGearDown:
95 TRACE("gearDown");
96 break;
97 case kVirtioInputBtnGearUp:
98 TRACE("gearUp");
99 break;
100 default:
101 TRACE("%d", pkt.code);
102 }
103 break;
104 case kVirtioInputEvRel:
105 TRACE("rel, ");
106 switch (pkt.code) {
107 case kVirtioInputRelX:
108 TRACE("relX");
109 break;
110 case kVirtioInputRelY:
111 TRACE("relY");
112 break;
113 case kVirtioInputRelZ:
114 TRACE("relZ");
115 break;
116 case kVirtioInputRelWheel:
117 TRACE("relWheel");
118 break;
119 default:
120 TRACE("%d", pkt.code);
121 }
122 break;
123 case kVirtioInputEvAbs:
124 TRACE("abs, ");
125 switch (pkt.code) {
126 case kVirtioInputAbsX:
127 TRACE("absX");
128 break;
129 case kVirtioInputAbsY:
130 TRACE("absY");
131 break;
132 case kVirtioInputAbsZ:
133 TRACE("absZ");
134 break;
135 default:
136 TRACE("%d", pkt.code);
137 }
138 break;
139 case kVirtioInputEvRep:
140 TRACE("rep");
141 break;
142 default:
143 TRACE("?(%d)", pkt.type);
144 }
145 switch (pkt.type) {
146 case kVirtioInputEvSyn:
147 break;
148 case kVirtioInputEvKey:
149 TRACE(", ");
150 if (pkt.value == 0) {
151 TRACE("up");
152 } else if (pkt.value == 1) {
153 TRACE("down");
154 } else {
155 TRACE("%" B_PRId32, pkt.value);
156 }
157 break;
158 default:
159 TRACE(", %" B_PRId32, pkt.value);
160 }
161 }
162 #endif
163
164 static void
InitPackets(VirtioInputDevice * dev,uint32 count)165 InitPackets(VirtioInputDevice* dev, uint32 count)
166 {
167 TRACE("InitPackets(%p, %" B_PRIu32 ")\n", dev, count);
168 size_t size = ROUNDUP(sizeof(Packet)*count, B_PAGE_SIZE);
169
170 dev->packetArea.SetTo(create_area("VirtIO input packets",
171 (void**)&dev->packets, B_ANY_KERNEL_ADDRESS, size,
172 B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA));
173 if (!dev->packetArea.IsSet()) {
174 ERROR("Unable to set packet area!");
175 return;
176 }
177
178 physical_entry pe;
179 if (get_memory_map(dev->packets, size, &pe, 1) < B_OK) {
180 ERROR("Unable to get memory map for input packets!");
181 return;
182 }
183 dev->physAdr = pe.address;
184 memset(dev->packets, 0, size);
185 dprintf(" size: 0x%" B_PRIxSIZE "\n", size);
186 dprintf(" virt: %p\n", dev->packets);
187 dprintf(" phys: %p\n", (void*)dev->physAdr);
188
189 dev->packetCnt = count;
190
191 dev->freePackets = 0;
192 for (uint32 i = 0; i < dev->packetCnt - 1; i++)
193 dev->packets[i].next = i + 1;
194 dev->packets[dev->packetCnt - 1].next = -1;
195
196 dev->readyPackets = -1;
197 dev->lastReadyPacket = -1;
198 }
199
200
201 static const physical_entry
PacketPhysEntry(VirtioInputDevice * dev,Packet * pkt)202 PacketPhysEntry(VirtioInputDevice* dev, Packet* pkt)
203 {
204 physical_entry pe;
205 pe.address = dev->physAdr + (uint8*)pkt - (uint8*)dev->packets;
206 pe.size = sizeof(VirtioInputPacket);
207 return pe;
208 }
209
210
211 static void
ScheduleReadyPacket(VirtioInputDevice * dev,Packet * pkt)212 ScheduleReadyPacket(VirtioInputDevice* dev, Packet* pkt)
213 {
214 if (dev->readyPackets < 0)
215 dev->readyPackets = pkt - dev->packets;
216 else
217 dev->packets[dev->lastReadyPacket].next = pkt - dev->packets;
218
219 dev->lastReadyPacket = pkt - dev->packets;
220 }
221
222
223 static Packet*
ConsumeReadyPacket(VirtioInputDevice * dev)224 ConsumeReadyPacket(VirtioInputDevice* dev)
225 {
226 if (dev->readyPackets < 0)
227 return NULL;
228 Packet* pkt = &dev->packets[dev->readyPackets];
229 dev->readyPackets = pkt->next;
230 if (dev->readyPackets < 0)
231 dev->lastReadyPacket = -1;
232 return pkt;
233 }
234
235
236 static void
virtio_input_callback(void * driverCookie,void * cookie)237 virtio_input_callback(void* driverCookie, void* cookie)
238 {
239 CALLED();
240 VirtioInputDevice* dev = (VirtioInputDevice*)cookie;
241
242 Packet* pkt;
243 while (dev->virtio->queue_dequeue(dev->virtio_queue, (void**)&pkt, NULL)) {
244 #ifdef TRACE_VIRTIO_INPUT
245 TRACE("%" B_PRIdSSIZE ": ", pkt - dev->packets);
246 WriteInputPacket(pkt->data);
247 TRACE("\n");
248 #endif
249 ScheduleReadyPacket(dev, pkt);
250 release_sem_etc(dev->sem_cb.Get(), 1, B_DO_NOT_RESCHEDULE);
251 }
252 }
253
254
255 // #pragma mark - device module API
256
257
258 static status_t
virtio_input_init_device(void * _info,void ** _cookie)259 virtio_input_init_device(void* _info, void** _cookie)
260 {
261 CALLED();
262 VirtioInputDevice* info = (VirtioInputDevice*)_info;
263
264 DeviceNodePutter<&gDeviceManager> parent(
265 gDeviceManager->get_parent_node(info->node));
266
267 gDeviceManager->get_driver(parent.Get(),
268 (driver_module_info **)&info->virtio,
269 (void **)&info->virtio_device);
270
271 info->virtio->negotiate_features(info->virtio_device, 0,
272 &info->features, NULL);
273
274 status_t status = B_OK;
275 /*
276 status = info->virtio->read_device_config(
277 info->virtio_device, 0, &info->config,
278 sizeof(struct virtio_blk_config));
279 if (status != B_OK)
280 return status;
281 */
282
283 InitPackets(info, 8);
284
285 status = info->virtio->alloc_queues(info->virtio_device, 1,
286 &info->virtio_queue, NULL);
287 if (status != B_OK) {
288 ERROR("queue allocation failed (%s)\n", strerror(status));
289 return status;
290 }
291 TRACE(" queue: %p\n", info->virtio_queue);
292
293 status = info->virtio->queue_setup_interrupt(info->virtio_queue,
294 virtio_input_callback, info);
295 if (status < B_OK)
296 return status;
297
298 for (uint32 i = 0; i < info->packetCnt; i++) {
299 Packet* pkt = &info->packets[i];
300 physical_entry pe = PacketPhysEntry(info, pkt);
301 info->virtio->queue_request(info->virtio_queue, NULL, &pe, pkt);
302 }
303
304 *_cookie = info;
305 return B_OK;
306 }
307
308
309 static void
virtio_input_uninit_device(void * _cookie)310 virtio_input_uninit_device(void* _cookie)
311 {
312 CALLED();
313 VirtioInputDevice* info = (VirtioInputDevice*)_cookie;
314 (void)info;
315 }
316
317
318 static status_t
virtio_input_open(void * _info,const char * path,int openMode,void ** _cookie)319 virtio_input_open(void* _info, const char* path, int openMode, void** _cookie)
320 {
321 CALLED();
322 VirtioInputDevice* info = (VirtioInputDevice*)_info;
323
324 ObjectDeleter<VirtioInputHandle>
325 handle(new(std::nothrow) (VirtioInputHandle));
326
327 if (!handle.IsSet())
328 return B_NO_MEMORY;
329
330 handle->info = info;
331
332 *_cookie = handle.Detach();
333 return B_OK;
334 }
335
336
337 static status_t
virtio_input_close(void * cookie)338 virtio_input_close(void* cookie)
339 {
340 CALLED();
341 return B_OK;
342 }
343
344
345 static status_t
virtio_input_free(void * cookie)346 virtio_input_free(void* cookie)
347 {
348 CALLED();
349 ObjectDeleter<VirtioInputHandle> handle((VirtioInputHandle*)cookie);
350 return B_OK;
351 }
352
353
354 static status_t
virtio_input_read(void * cookie,off_t pos,void * buffer,size_t * _length)355 virtio_input_read(void* cookie, off_t pos, void* buffer, size_t* _length)
356 {
357 return B_ERROR;
358 }
359
360
361 static status_t
virtio_input_write(void * cookie,off_t pos,const void * buffer,size_t * _length)362 virtio_input_write(void* cookie, off_t pos, const void* buffer,
363 size_t* _length)
364 {
365 *_length = 0;
366 return B_ERROR;
367 }
368
369
370 static status_t
virtio_input_ioctl(void * cookie,uint32 op,void * buffer,size_t length)371 virtio_input_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
372 {
373 CALLED();
374
375 VirtioInputHandle* handle = (VirtioInputHandle*)cookie;
376 VirtioInputDevice* info = handle->info;
377 (void)info;
378
379 TRACE("ioctl(op = %" B_PRIu32 ")\n", op);
380
381 switch (op) {
382 case virtioInputRead: {
383 TRACE("virtioInputRead\n");
384 if (buffer == NULL || length < sizeof(VirtioInputPacket))
385 return B_BAD_VALUE;
386
387 status_t res = acquire_sem(info->sem_cb.Get());
388 if (res < B_OK)
389 return res;
390
391 Packet* pkt = ConsumeReadyPacket(info);
392 TRACE(" pkt: %" B_PRIdSSIZE "\n", pkt - info->packets);
393
394 physical_entry pe = PacketPhysEntry(info, pkt);
395 info->virtio->queue_request(info->virtio_queue, NULL, &pe, pkt);
396
397 res = user_memcpy(buffer, pkt, sizeof(Packet));
398 if (res < B_OK)
399 return res;
400
401 return B_OK;
402 }
403 }
404
405 return B_DEV_INVALID_IOCTL;
406 }
407
408
409 // #pragma mark - driver module API
410
411
412 static float
virtio_input_supports_device(device_node * parent)413 virtio_input_supports_device(device_node *parent)
414 {
415 CALLED();
416
417 const char *bus;
418 uint16 deviceType;
419
420 // make sure parent is really the Virtio bus manager
421 if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
422 return -1;
423
424 if (strcmp(bus, "virtio"))
425 return 0.0;
426
427 // check whether it's really a Direct Access Device
428 if (gDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
429 &deviceType, true) != B_OK || deviceType != kVirtioDevInput)
430 return 0.0;
431
432 TRACE("Virtio input device found!\n");
433
434 return 0.6;
435 }
436
437
438 static status_t
virtio_input_register_device(device_node * node)439 virtio_input_register_device(device_node *node)
440 {
441 CALLED();
442
443 device_attr attrs[] = {
444 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "VirtIO input" }},
445 { NULL }
446 };
447
448 return gDeviceManager->register_node(node, VIRTIO_INPUT_DRIVER_MODULE_NAME,
449 attrs, NULL, NULL);
450 }
451
452
453 static status_t
virtio_input_init_driver(device_node * node,void ** cookie)454 virtio_input_init_driver(device_node *node, void **cookie)
455 {
456 CALLED();
457
458 ObjectDeleter<VirtioInputDevice>
459 info(new(std::nothrow) VirtioInputDevice());
460
461 if (!info.IsSet())
462 return B_NO_MEMORY;
463
464 memset(info.Get(), 0, sizeof(*info.Get()));
465
466 info->sem_cb.SetTo(create_sem(0, "virtio_input_cb"));
467 if (!info->sem_cb.IsSet())
468 return info->sem_cb.Get();
469
470 info->node = node;
471
472 *cookie = info.Detach();
473 return B_OK;
474 }
475
476
477 static void
virtio_input_uninit_driver(void * _cookie)478 virtio_input_uninit_driver(void *_cookie)
479 {
480 CALLED();
481 ObjectDeleter<VirtioInputDevice> info((VirtioInputDevice*)_cookie);
482 }
483
484
485 static status_t
virtio_input_register_child_devices(void * _cookie)486 virtio_input_register_child_devices(void* _cookie)
487 {
488 CALLED();
489 VirtioInputDevice* info = (VirtioInputDevice*)_cookie;
490 status_t status;
491
492 int32 id = gDeviceManager->create_id(VIRTIO_INPUT_DEVICE_ID_GENERATOR);
493 if (id < 0)
494 return id;
495
496 char name[64];
497 snprintf(name, sizeof(name), "input/virtio/%" B_PRId32 "/raw", id);
498
499 status = gDeviceManager->publish_device(info->node, name,
500 VIRTIO_INPUT_DEVICE_MODULE_NAME);
501
502 if (status < B_OK) {
503 ERROR("publish_device error: 0x%" B_PRIx32 "(%s) \n", status,
504 strerror(status));
505 }
506
507 return status;
508 }
509
510
511 // #pragma mark -
512
513
514 module_dependency module_dependencies[] = {
515 { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
516 { NULL }
517 };
518
519
520 struct device_module_info sVirtioInputDevice = {
521 {
522 VIRTIO_INPUT_DEVICE_MODULE_NAME,
523 0,
524 NULL
525 },
526
527 virtio_input_init_device,
528 virtio_input_uninit_device,
529 NULL, // remove,
530
531 virtio_input_open,
532 virtio_input_close,
533 virtio_input_free,
534 virtio_input_read,
535 virtio_input_write,
536 NULL,
537 virtio_input_ioctl,
538
539 NULL, // select
540 NULL, // deselect
541 };
542
543 struct driver_module_info sVirtioInputDriver = {
544 {
545 VIRTIO_INPUT_DRIVER_MODULE_NAME,
546 0,
547 NULL
548 },
549
550 virtio_input_supports_device,
551 virtio_input_register_device,
552 virtio_input_init_driver,
553 virtio_input_uninit_driver,
554 virtio_input_register_child_devices,
555 NULL, // rescan
556 NULL, // removed
557 };
558
559 module_info* modules[] = {
560 (module_info*)&sVirtioInputDriver,
561 (module_info*)&sVirtioInputDevice,
562 NULL
563 };
564