xref: /haiku/src/add-ons/kernel/drivers/input/virtio_input/virtio_input.cpp (revision 3d4afef9cba2f328e238089d4609d00d4b1524f3)
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 	uint32 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 
75 static void
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 
163 
164 static void
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
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 Packet*
212 AllocPacket(VirtioInputDevice* dev)
213 {
214 	int32 idx = dev->freePackets;
215 	if (idx < 0)
216 		return NULL;
217 	dev->freePackets = dev->packets[idx].next;
218 
219 	return &dev->packets[idx];
220 }
221 
222 
223 static void
224 FreePacket(VirtioInputDevice* dev, Packet* pkt)
225 {
226 	pkt->next = dev->freePackets;
227 	dev->freePackets = pkt - dev->packets;
228 }
229 
230 
231 static void
232 ScheduleReadyPacket(VirtioInputDevice* dev, Packet* pkt)
233 {
234 	if (dev->readyPackets < 0)
235 		dev->readyPackets = pkt - dev->packets;
236 	else
237 		dev->packets[dev->lastReadyPacket].next = pkt - dev->packets;
238 
239 	dev->lastReadyPacket = pkt - dev->packets;
240 }
241 
242 
243 static Packet*
244 ConsumeReadyPacket(VirtioInputDevice* dev)
245 {
246 	if (dev->readyPackets < 0)
247 		return NULL;
248 	Packet* pkt = &dev->packets[dev->readyPackets];
249 	dev->readyPackets = pkt->next;
250 	if (dev->readyPackets < 0)
251 		dev->lastReadyPacket = -1;
252 	return pkt;
253 }
254 
255 
256 static void
257 virtio_input_callback(void* driverCookie, void* cookie)
258 {
259 	CALLED();
260 	VirtioInputDevice* dev = (VirtioInputDevice*)cookie;
261 
262 	Packet* pkt;
263 	while (dev->virtio->queue_dequeue(dev->virtio_queue, (void**)&pkt, NULL)) {
264 #ifdef TRACE_VIRTIO_INPUT
265 		TRACE("%" B_PRIdSSIZE ": ", pkt - dev->packets);
266 		WriteInputPacket(pkt->data);
267 		TRACE("\n");
268 #endif
269 		ScheduleReadyPacket(dev, pkt);
270 		release_sem_etc(dev->sem_cb.Get(), 1, B_DO_NOT_RESCHEDULE);
271 	}
272 }
273 
274 
275 //	#pragma mark - device module API
276 
277 
278 static status_t
279 virtio_input_init_device(void* _info, void** _cookie)
280 {
281 	CALLED();
282 	VirtioInputDevice* info = (VirtioInputDevice*)_info;
283 
284 	DeviceNodePutter<&gDeviceManager> parent(
285 		gDeviceManager->get_parent_node(info->node));
286 
287 	gDeviceManager->get_driver(parent.Get(),
288 		(driver_module_info **)&info->virtio,
289 		(void **)&info->virtio_device);
290 
291 	info->virtio->negotiate_features(info->virtio_device, 0,
292 		&info->features, NULL);
293 
294 	status_t status = B_OK;
295 /*
296 	status = info->virtio->read_device_config(
297 		info->virtio_device, 0, &info->config,
298 		sizeof(struct virtio_blk_config));
299 	if (status != B_OK)
300 		return status;
301 */
302 
303 	InitPackets(info, 8);
304 
305 	status = info->virtio->alloc_queues(info->virtio_device, 1,
306 		&info->virtio_queue);
307 	if (status != B_OK) {
308 		ERROR("queue allocation failed (%s)\n", strerror(status));
309 		return status;
310 	}
311 	TRACE("  queue: %p\n", info->virtio_queue);
312 
313 	status = info->virtio->queue_setup_interrupt(info->virtio_queue,
314 		virtio_input_callback, info);
315 	if (status < B_OK)
316 		return status;
317 
318 	for (uint32 i = 0; i < info->packetCnt; i++) {
319 		Packet* pkt = &info->packets[i];
320 		physical_entry pe = PacketPhysEntry(info, pkt);
321 		info->virtio->queue_request(info->virtio_queue, NULL, &pe, pkt);
322 	}
323 
324 	*_cookie = info;
325 	return B_OK;
326 }
327 
328 
329 static void
330 virtio_input_uninit_device(void* _cookie)
331 {
332 	CALLED();
333 	VirtioInputDevice* info = (VirtioInputDevice*)_cookie;
334 	(void)info;
335 }
336 
337 
338 static status_t
339 virtio_input_open(void* _info, const char* path, int openMode, void** _cookie)
340 {
341 	CALLED();
342 	VirtioInputDevice* info = (VirtioInputDevice*)_info;
343 
344 	ObjectDeleter<VirtioInputHandle>
345 		handle(new(std::nothrow) (VirtioInputHandle));
346 
347 	if (!handle.IsSet())
348 		return B_NO_MEMORY;
349 
350 	handle->info = info;
351 
352 	*_cookie = handle.Detach();
353 	return B_OK;
354 }
355 
356 
357 static status_t
358 virtio_input_close(void* cookie)
359 {
360 	CALLED();
361 	return B_OK;
362 }
363 
364 
365 static status_t
366 virtio_input_free(void* cookie)
367 {
368 	CALLED();
369 	ObjectDeleter<VirtioInputHandle> handle((VirtioInputHandle*)cookie);
370 	return B_OK;
371 }
372 
373 
374 static status_t
375 virtio_input_read(void* cookie, off_t pos, void* buffer, size_t* _length)
376 {
377 	return B_ERROR;
378 }
379 
380 
381 static status_t
382 virtio_input_write(void* cookie, off_t pos, const void* buffer,
383 	size_t* _length)
384 {
385 	*_length = 0;
386 	return B_ERROR;
387 }
388 
389 
390 static status_t
391 virtio_input_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
392 {
393 	CALLED();
394 
395 	VirtioInputHandle* handle = (VirtioInputHandle*)cookie;
396 	VirtioInputDevice* info = handle->info;
397 	(void)info;
398 
399 	TRACE("ioctl(op = %" B_PRIu32 ")\n", op);
400 
401 	switch (op) {
402 		case virtioInputRead: {
403 			TRACE("virtioInputRead\n");
404 			if (buffer == NULL || length < sizeof(VirtioInputPacket))
405 				return B_BAD_VALUE;
406 
407 			status_t res = acquire_sem(info->sem_cb.Get());
408 			if (res < B_OK)
409 				return res;
410 
411 			Packet* pkt = ConsumeReadyPacket(info);
412 			TRACE("  pkt: %" B_PRIdSSIZE "\n", pkt - info->packets);
413 
414 			physical_entry pe = PacketPhysEntry(info, pkt);
415 			info->virtio->queue_request(info->virtio_queue, NULL, &pe, pkt);
416 
417 			res = user_memcpy(buffer, pkt, sizeof(Packet));
418 			if (res < B_OK)
419 				return res;
420 
421 			return B_OK;
422 		}
423 	}
424 
425 	return B_DEV_INVALID_IOCTL;
426 }
427 
428 
429 //	#pragma mark - driver module API
430 
431 
432 static float
433 virtio_input_supports_device(device_node *parent)
434 {
435 	CALLED();
436 
437 	const char *bus;
438 	uint16 deviceType;
439 
440 	// make sure parent is really the Virtio bus manager
441 	if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
442 		return -1;
443 
444 	if (strcmp(bus, "virtio"))
445 		return 0.0;
446 
447 	// check whether it's really a Direct Access Device
448 	if (gDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
449 			&deviceType, true) != B_OK || deviceType != kVirtioDevInput)
450 		return 0.0;
451 
452 	TRACE("Virtio input device found!\n");
453 
454 	return 0.6;
455 }
456 
457 
458 static status_t
459 virtio_input_register_device(device_node *node)
460 {
461 	CALLED();
462 
463 	device_attr attrs[] = {
464 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "VirtIO input" }},
465 		{ NULL }
466 	};
467 
468 	return gDeviceManager->register_node(node, VIRTIO_INPUT_DRIVER_MODULE_NAME,
469 		attrs, NULL, NULL);
470 }
471 
472 
473 static status_t
474 virtio_input_init_driver(device_node *node, void **cookie)
475 {
476 	CALLED();
477 
478 	ObjectDeleter<VirtioInputDevice>
479 		info(new(std::nothrow) VirtioInputDevice());
480 
481 	if (!info.IsSet())
482 		return B_NO_MEMORY;
483 
484 	memset(info.Get(), 0, sizeof(*info.Get()));
485 
486 	info->sem_cb.SetTo(create_sem(0, "virtio_input_cb"));
487 	if (!info->sem_cb.IsSet())
488 		return info->sem_cb.Get();
489 
490 	info->node = node;
491 
492 	*cookie = info.Detach();
493 	return B_OK;
494 }
495 
496 
497 static void
498 virtio_input_uninit_driver(void *_cookie)
499 {
500 	CALLED();
501 	ObjectDeleter<VirtioInputDevice> info((VirtioInputDevice*)_cookie);
502 }
503 
504 
505 static status_t
506 virtio_input_register_child_devices(void* _cookie)
507 {
508 	CALLED();
509 	VirtioInputDevice* info = (VirtioInputDevice*)_cookie;
510 	status_t status;
511 
512 	int32 id = gDeviceManager->create_id(VIRTIO_INPUT_DEVICE_ID_GENERATOR);
513 	if (id < 0)
514 		return id;
515 
516 	char name[64];
517 	snprintf(name, sizeof(name), "input/virtio/%" B_PRId32 "/raw", id);
518 
519 	status = gDeviceManager->publish_device(info->node, name,
520 		VIRTIO_INPUT_DEVICE_MODULE_NAME);
521 
522 	if (status < B_OK) {
523 		ERROR("publish_device error: 0x%" B_PRIx32 "(%s) \n", status,
524 			strerror(status));
525 	}
526 
527 	return status;
528 }
529 
530 
531 //	#pragma mark -
532 
533 
534 module_dependency module_dependencies[] = {
535 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
536 	{ NULL }
537 };
538 
539 
540 struct device_module_info sVirtioInputDevice = {
541 	{
542 		VIRTIO_INPUT_DEVICE_MODULE_NAME,
543 		0,
544 		NULL
545 	},
546 
547 	virtio_input_init_device,
548 	virtio_input_uninit_device,
549 	NULL, // remove,
550 
551 	virtio_input_open,
552 	virtio_input_close,
553 	virtio_input_free,
554 	virtio_input_read,
555 	virtio_input_write,
556 	NULL,
557 	virtio_input_ioctl,
558 
559 	NULL,	// select
560 	NULL,	// deselect
561 };
562 
563 struct driver_module_info sVirtioInputDriver = {
564 	{
565 		VIRTIO_INPUT_DRIVER_MODULE_NAME,
566 		0,
567 		NULL
568 	},
569 
570 	virtio_input_supports_device,
571 	virtio_input_register_device,
572 	virtio_input_init_driver,
573 	virtio_input_uninit_driver,
574 	virtio_input_register_child_devices,
575 	NULL,	// rescan
576 	NULL,	// removed
577 };
578 
579 module_info* modules[] = {
580 	(module_info*)&sVirtioInputDriver,
581 	(module_info*)&sVirtioInputDevice,
582 	NULL
583 };
584