xref: /haiku/src/add-ons/kernel/drivers/input/virtio_input/virtio_input.cpp (revision 3634f142352af2428aed187781fc9d75075e9140)
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
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
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 void
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*
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
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
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);
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
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
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
338 virtio_input_close(void* cookie)
339 {
340 	CALLED();
341 	return B_OK;
342 }
343 
344 
345 static status_t
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
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
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
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
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
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
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
478 virtio_input_uninit_driver(void *_cookie)
479 {
480 	CALLED();
481 	ObjectDeleter<VirtioInputDevice> info((VirtioInputDevice*)_cookie);
482 }
483 
484 
485 static status_t
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