1/*
2 * Copyright 2023, J��r��me Duval, jerome.duval@gmail.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <new>
8
9#include <graphic_driver.h>
10
11#include <lock.h>
12#include <virtio.h>
13#include <virtio_info.h>
14
15#include <util/AutoLock.h>
16
17#include "viogpu.h"
18
19
20#define VIRTIO_GPU_DRIVER_MODULE_NAME "drivers/graphics/virtio_gpu/driver_v1"
21#define VIRTIO_GPU_DEVICE_MODULE_NAME "drivers/graphics/virtio_gpu/device_v1"
22#define VIRTIO_GPU_DEVICE_ID_GENERATOR	"virtio_gpu/device_id"
23
24
25typedef struct {
26	device_node*			node;
27	::virtio_device			virtio_device;
28	virtio_device_interface*	virtio;
29
30	uint64 					features;
31
32	::virtio_queue			controlQueue;
33	mutex					commandLock;
34	area_id					commandArea;
35	addr_t					commandBuffer;
36	phys_addr_t				commandPhysAddr;
37	sem_id					commandDone;
38	uint64					fenceId;
39
40	::virtio_queue			cursorQueue;
41
42	int						displayResourceId;
43	uint32					framebufferWidth;
44	uint32					framebufferHeight;
45	area_id					framebufferArea;
46	addr_t					framebuffer;
47	size_t					framebufferSize;
48	uint32					displayWidth;
49	uint32					displayHeight;
50
51	thread_id				updateThread;
52	bool					updateThreadRunning;
53
54	area_id					sharedArea;
55	virtio_gpu_shared_info* sharedInfo;
56} virtio_gpu_driver_info;
57
58
59typedef struct {
60	virtio_gpu_driver_info*		info;
61} virtio_gpu_handle;
62
63
64#include <stdio.h>
65#include <string.h>
66#include <stdlib.h>
67
68#include <fs/devfs.h>
69
70#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
71
72
73#define DEVICE_NAME				"virtio_gpu"
74#define ACCELERANT_NAME	"virtio_gpu.accelerant"
75//#define TRACE_VIRTIO_GPU
76#ifdef TRACE_VIRTIO_GPU
77#	define TRACE(x...) dprintf(DEVICE_NAME ": " x)
78#else
79#	define TRACE(x...) ;
80#endif
81#define ERROR(x...)			dprintf("\33[33m" DEVICE_NAME ":\33[0m " x)
82#define CALLED() 			TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
83
84
85static device_manager_info* sDeviceManager;
86
87
88static void virtio_gpu_vqwait(void* driverCookie, void* cookie);
89
90
91const char*
92get_feature_name(uint64 feature)
93{
94	switch (feature) {
95		case VIRTIO_GPU_F_VIRGL:
96			return "virgl";
97		case VIRTIO_GPU_F_EDID:
98			return "edid";
99		case VIRTIO_GPU_F_RESOURCE_UUID:
100			return "res_uuid";
101		case VIRTIO_GPU_F_RESOURCE_BLOB:
102			return "res_blob";
103	}
104	return NULL;
105}
106
107
108static status_t
109virtio_gpu_drain_queues(virtio_gpu_driver_info* info)
110{
111	while (info->virtio->queue_dequeue(info->controlQueue, NULL, NULL))
112		;
113
114	while (info->virtio->queue_dequeue(info->cursorQueue, NULL, NULL))
115		;
116
117	return B_OK;
118}
119
120
121status_t
122virtio_gpu_send_cmd(virtio_gpu_driver_info* info, void *cmd, size_t cmdSize, void *response,
123    size_t responseSize)
124{
125	struct virtio_gpu_ctrl_hdr *hdr = (struct virtio_gpu_ctrl_hdr *)info->commandBuffer;
126	struct virtio_gpu_ctrl_hdr *responseHdr = (struct virtio_gpu_ctrl_hdr *)response;
127
128	memcpy((void*)info->commandBuffer, cmd, cmdSize);
129	memset((void*)(info->commandBuffer + cmdSize), 0, responseSize);
130	hdr->flags |= VIRTIO_GPU_FLAG_FENCE;
131	hdr->fence_id = ++info->fenceId;
132
133	physical_entry entries[] {
134		{ info->commandPhysAddr, cmdSize },
135		{ info->commandPhysAddr + cmdSize, responseSize },
136	};
137	if (!info->virtio->queue_is_empty(info->controlQueue))
138		return B_ERROR;
139
140	status_t status = info->virtio->queue_request_v(info->controlQueue, entries, 1, 1, NULL);
141	if (status != B_OK)
142		return status;
143
144	acquire_sem(info->commandDone);
145
146	while (!info->virtio->queue_dequeue(info->controlQueue, NULL, NULL))
147		spin(10);
148
149	memcpy(response, (void*)(info->commandBuffer + cmdSize), responseSize);
150
151	if (responseHdr->fence_id != info->fenceId) {
152		ERROR("response fence id not right\n");
153	}
154	return B_OK;
155}
156
157
158status_t
159virtio_gpu_get_display_info(virtio_gpu_driver_info* info)
160{
161	CALLED();
162	struct virtio_gpu_ctrl_hdr hdr = {};
163	struct virtio_gpu_resp_display_info displayInfo = {};
164
165	hdr.type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO;
166
167	virtio_gpu_send_cmd(info, &hdr, sizeof(hdr), &displayInfo, sizeof(displayInfo));
168
169	if (displayInfo.hdr.type != VIRTIO_GPU_RESP_OK_DISPLAY_INFO) {
170		ERROR("failed getting display info\n");
171		return B_ERROR;
172	}
173
174	if (!displayInfo.pmodes[0].enabled) {
175		ERROR("pmodes[0] is not enabled\n");
176		return B_BAD_VALUE;
177	}
178
179	info->displayWidth = displayInfo.pmodes[0].r.width;
180	info->displayHeight = displayInfo.pmodes[0].r.height;
181	TRACE("virtio_gpu_get_display_info width %" B_PRIu32 " height %" B_PRIu32 "\n",
182		info->displayWidth, info->displayHeight);
183
184	return B_OK;
185}
186
187
188status_t
189virtio_gpu_get_edids(virtio_gpu_driver_info* info, int scanout)
190{
191	CALLED();
192	struct virtio_gpu_cmd_get_edid getEdid = {};
193	struct virtio_gpu_resp_edid response = {};
194	getEdid.hdr.type = VIRTIO_GPU_CMD_GET_EDID;
195	getEdid.scanout = scanout;
196
197	virtio_gpu_send_cmd(info, &getEdid, sizeof(getEdid), &response, sizeof(response));
198
199	if (response.hdr.type != VIRTIO_GPU_RESP_OK_EDID) {
200		ERROR("failed getting edids %d\n", response.hdr.type);
201		return B_ERROR;
202	}
203
204	info->sharedInfo->has_edid = true;
205	memcpy(&info->sharedInfo->edid_raw, response.edid, sizeof(edid1_raw));
206	TRACE("virtio_gpu_get_edids success\n");
207
208	return B_OK;
209}
210
211
212status_t
213virtio_gpu_create_2d(virtio_gpu_driver_info* info, int resourceId, int width, int height)
214{
215	CALLED();
216	struct virtio_gpu_resource_create_2d resource = {};
217	struct virtio_gpu_ctrl_hdr response = {};
218
219	resource.hdr.type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D;
220	resource.resource_id = resourceId;
221	resource.format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
222	resource.width = width;
223	resource.height = height;
224
225	virtio_gpu_send_cmd(info, &resource, sizeof(resource), &response, sizeof(response));
226
227	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
228		ERROR("viogpu_create_2d: failed %d\n", response.type);
229		return B_ERROR;
230	}
231
232	return B_OK;
233}
234
235
236status_t
237virtio_gpu_unref(virtio_gpu_driver_info* info, int resourceId)
238{
239	CALLED();
240	struct virtio_gpu_resource_unref resource = {};
241	struct virtio_gpu_ctrl_hdr response = {};
242
243	resource.hdr.type = VIRTIO_GPU_CMD_RESOURCE_UNREF;
244	resource.resource_id = resourceId;
245
246	virtio_gpu_send_cmd(info, &resource, sizeof(resource), &response, sizeof(response));
247
248	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
249		ERROR("virtio_gpu_unref: failed %d\n", response.type);
250		return B_ERROR;
251	}
252
253	return B_OK;
254}
255
256
257status_t
258virtio_gpu_attach_backing(virtio_gpu_driver_info* info, int resourceId)
259{
260	CALLED();
261	struct virtio_gpu_resource_attach_backing_entries {
262		struct virtio_gpu_resource_attach_backing backing;
263		struct virtio_gpu_mem_entry entries[16];
264	} _PACKED backing = {};
265	struct virtio_gpu_ctrl_hdr response = {};
266
267	physical_entry entries[16] = {};
268	status_t status = get_memory_map((void*)info->framebuffer, info->framebufferSize, entries, 16);
269	if (status != B_OK) {
270		ERROR("virtio_gpu_attach_backing get_memory_map failed: %s\n", strerror(status));
271		return status;
272	}
273
274	backing.backing.hdr.type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING;
275	backing.backing.resource_id = resourceId;
276	for (int i = 0; i < 16; i++) {
277		if (entries[i].size == 0)
278			break;
279		TRACE("virtio_gpu_attach_backing %d %" B_PRIxPHYSADDR " %" B_PRIxPHYSADDR "\n", i,
280			entries[i].address, entries[i].size);
281		backing.entries[i].addr = entries[i].address;
282		backing.entries[i].length = entries[i].size;
283		backing.backing.nr_entries++;
284	}
285
286	virtio_gpu_send_cmd(info, &backing, sizeof(backing), &response, sizeof(response));
287
288	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
289		ERROR("virtio_gpu_attach_backing failed: %d\n", response.type);
290		return B_ERROR;
291	}
292
293	return B_OK;
294}
295
296
297status_t
298virtio_gpu_detach_backing(virtio_gpu_driver_info* info, int resourceId)
299{
300	CALLED();
301	struct virtio_gpu_resource_detach_backing backing;
302	struct virtio_gpu_ctrl_hdr response = {};
303
304	backing.hdr.type = VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING;
305	backing.resource_id = resourceId;
306
307	virtio_gpu_send_cmd(info, &backing, sizeof(backing), &response, sizeof(response));
308
309	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
310		ERROR("virtio_gpu_detach_backing failed: %d\n", response.type);
311		return B_ERROR;
312	}
313
314	return B_OK;
315}
316
317
318status_t
319virtio_gpu_set_scanout(virtio_gpu_driver_info* info, int scanoutId, int resourceId,
320    uint32 width, uint32 height)
321{
322	CALLED();
323	struct virtio_gpu_set_scanout set_scanout = {};
324	struct virtio_gpu_ctrl_hdr response = {};
325
326	set_scanout.hdr.type = VIRTIO_GPU_CMD_SET_SCANOUT;
327	set_scanout.scanout_id = scanoutId;
328	set_scanout.resource_id = resourceId;
329	set_scanout.r.width = width;
330	set_scanout.r.height = height;
331
332	virtio_gpu_send_cmd(info, &set_scanout, sizeof(set_scanout), &response, sizeof(response));
333
334	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
335		ERROR("virtio_gpu_set_scanout failed %d\n", response.type);
336		return B_ERROR;
337	}
338
339	return B_OK;
340}
341
342
343status_t
344virtio_gpu_transfer_to_host_2d(virtio_gpu_driver_info* info, int resourceId,
345    uint32 width, uint32 height)
346{
347	struct virtio_gpu_transfer_to_host_2d transferToHost = {};
348	struct virtio_gpu_ctrl_hdr response = {};
349
350	transferToHost.hdr.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D;
351	transferToHost.resource_id = resourceId;
352	transferToHost.r.width = width;
353	transferToHost.r.height = height;
354
355	virtio_gpu_send_cmd(info, &transferToHost, sizeof(transferToHost), &response,
356		sizeof(response));
357
358	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
359		ERROR("virtio_gpu_transfer_to_host_2d failed %d\n", response.type);
360		return B_ERROR;
361	}
362
363	return B_OK;
364}
365
366
367status_t
368virtio_gpu_flush_resource(virtio_gpu_driver_info* info, int resourceId, uint32 width,
369    uint32 height)
370{
371	struct virtio_gpu_resource_flush resourceFlush = {};
372	struct virtio_gpu_ctrl_hdr response = {};
373
374	resourceFlush.hdr.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH;
375	resourceFlush.resource_id = resourceId;
376	resourceFlush.r.width = width;
377	resourceFlush.r.height = height;
378
379	virtio_gpu_send_cmd(info, &resourceFlush, sizeof(resourceFlush), &response, sizeof(response));
380
381	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
382		ERROR("virtio_gpu_flush_resource failed %d\n", response.type);
383		return B_ERROR;
384	}
385
386	return B_OK;
387}
388
389
390status_t
391virtio_update_thread(void *arg)
392{
393	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)arg;
394
395	while (info->updateThreadRunning) {
396		bigtime_t start = system_time();
397		MutexLocker commandLocker(&info->commandLock);
398		virtio_gpu_transfer_to_host_2d(info, info->displayResourceId, info->displayWidth,
399			info->displayHeight);
400		virtio_gpu_flush_resource(info, info->displayResourceId, info->displayWidth, info->displayHeight);
401		bigtime_t delay = system_time() - start;
402		if (delay < 20000)
403			snooze(20000 - delay);
404	}
405	return B_OK;
406}
407
408
409status_t
410virtio_gpu_set_display_mode(virtio_gpu_driver_info* info, display_mode *mode)
411{
412	CALLED();
413
414	int newResourceId = info->displayResourceId + 1;
415
416	// create framebuffer area
417	TRACE("virtio_gpu_set_display_mode %" B_PRIu16 " %" B_PRIu16 "\n", mode->virtual_width,
418		mode->virtual_height);
419
420	status_t status = virtio_gpu_create_2d(info, newResourceId, mode->virtual_width, mode->virtual_height);
421	if (status != B_OK)
422		return status;
423
424	status = virtio_gpu_attach_backing(info, newResourceId);
425	if (status != B_OK)
426		return status;
427
428	status = virtio_gpu_unref(info, info->displayResourceId);
429	if (status != B_OK)
430		return status;
431
432	info->displayResourceId = newResourceId;
433	info->displayWidth = mode->virtual_width;
434	info->displayHeight = mode->virtual_height;
435
436	status = virtio_gpu_set_scanout(info, 0, 0, 0, 0);
437	if (status != B_OK)
438		return status;
439
440	status = virtio_gpu_set_scanout(info, 0, info->displayResourceId, info->displayWidth, info->displayHeight);
441	if (status != B_OK)
442		return status;
443
444	status = virtio_gpu_transfer_to_host_2d(info, info->displayResourceId, info->displayWidth, info->displayHeight);
445	if (status != B_OK)
446		return status;
447
448	status = virtio_gpu_flush_resource(info, info->displayResourceId, info->displayWidth, info->displayHeight);
449	if (status != B_OK)
450		return status;
451
452	{
453		virtio_gpu_shared_info& sharedInfo = *info->sharedInfo;
454		sharedInfo.frame_buffer_area = info->framebufferArea;
455		sharedInfo.frame_buffer = (uint8*)info->framebuffer;
456		sharedInfo.bytes_per_row = info->displayWidth * 4;
457		sharedInfo.current_mode.virtual_width = info->displayWidth;
458		sharedInfo.current_mode.virtual_height = info->displayHeight;
459		sharedInfo.current_mode.space = B_RGB32;
460	}
461
462	return B_OK;
463}
464
465
466//	#pragma mark - device module API
467
468
469static status_t
470virtio_gpu_init_device(void* _info, void** _cookie)
471{
472	CALLED();
473	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_info;
474
475	device_node* parent = sDeviceManager->get_parent_node(info->node);
476	sDeviceManager->get_driver(parent, (driver_module_info**)&info->virtio,
477		(void**)&info->virtio_device);
478	sDeviceManager->put_node(parent);
479
480	info->virtio->negotiate_features(info->virtio_device, VIRTIO_GPU_F_EDID,
481		 &info->features, &get_feature_name);
482
483	// TODO read config
484
485	// Setup queues
486	::virtio_queue virtioQueues[2];
487	status_t status = info->virtio->alloc_queues(info->virtio_device, 2,
488		virtioQueues);
489	if (status != B_OK) {
490		ERROR("queue allocation failed (%s)\n", strerror(status));
491		return status;
492	}
493
494	info->controlQueue = virtioQueues[0];
495	info->cursorQueue = virtioQueues[1];
496
497	// create command buffer area
498	info->commandArea = create_area("virtiogpu command buffer", (void**)&info->commandBuffer,
499		B_ANY_KERNEL_BLOCK_ADDRESS, B_PAGE_SIZE,
500		B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
501	if (info->commandArea < B_OK) {
502		status = info->commandArea;
503		goto err1;
504	}
505
506	physical_entry entry;
507	status = get_memory_map((void*)info->commandBuffer, B_PAGE_SIZE, &entry, 1);
508	if (status != B_OK)
509		goto err2;
510
511	info->commandPhysAddr = entry.address;
512	mutex_init(&info->commandLock, "virtiogpu command lock");
513
514	// Setup interrupt
515	status = info->virtio->setup_interrupt(info->virtio_device, NULL, info);
516	if (status != B_OK) {
517		ERROR("interrupt setup failed (%s)\n", strerror(status));
518		goto err3;
519	}
520
521	status = info->virtio->queue_setup_interrupt(info->controlQueue,
522		virtio_gpu_vqwait, info);
523	if (status != B_OK) {
524		ERROR("queue interrupt setup failed (%s)\n", strerror(status));
525		goto err3;
526	}
527
528	*_cookie = info;
529	return B_OK;
530
531err3:
532err2:
533	delete_area(info->commandArea);
534err1:
535	return status;
536}
537
538
539static void
540virtio_gpu_uninit_device(void* _cookie)
541{
542	CALLED();
543	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_cookie;
544
545	info->virtio->free_interrupts(info->virtio_device);
546
547	mutex_destroy(&info->commandLock);
548
549	delete_area(info->commandArea);
550	info->commandArea = -1;
551	info->virtio->free_queues(info->virtio_device);
552}
553
554
555static status_t
556virtio_gpu_open(void* _info, const char* path, int openMode, void** _cookie)
557{
558	CALLED();
559	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_info;
560	status_t status;
561	size_t sharedSize = (sizeof(virtio_gpu_shared_info) + 7) & ~7;
562	MutexLocker commandLocker;
563
564	virtio_gpu_handle* handle = (virtio_gpu_handle*)malloc(
565		sizeof(virtio_gpu_handle));
566	if (handle == NULL)
567		return B_NO_MEMORY;
568
569	info->commandDone = create_sem(1, "virtio_gpu_command");
570	if (info->commandDone < B_OK)
571		goto error;
572
573	info->sharedArea = create_area("virtio_gpu shared info",
574		(void**)&info->sharedInfo, B_ANY_KERNEL_ADDRESS,
575		ROUND_TO_PAGE_SIZE(sharedSize), B_FULL_LOCK,
576		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
577	if (info->sharedArea < 0)
578		goto error;
579	memset(info->sharedInfo, 0, sizeof(virtio_gpu_shared_info));
580
581	commandLocker.SetTo(&info->commandLock, false, true);
582
583	status = virtio_gpu_get_display_info(info);
584	if (status != B_OK)
585		goto error;
586
587	if ((info->features & VIRTIO_GPU_F_EDID) != 0)
588		virtio_gpu_get_edids(info, 0);
589
590	// so we can fit every mode
591	info->framebufferWidth = 3840;
592	info->framebufferHeight = 2160;
593
594	// create framebuffer area
595	info->framebufferSize = 4 * info->framebufferWidth * info->framebufferHeight;
596	info->framebufferArea = create_area("virtio_gpu framebuffer", (void**)&info->framebuffer,
597		B_ANY_KERNEL_ADDRESS, info->framebufferSize,
598		B_FULL_LOCK | B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
599	if (info->framebufferArea < B_OK) {
600		status = info->framebufferArea;
601		goto error;
602	}
603
604	info->displayResourceId = 1;
605	status = virtio_gpu_create_2d(info, info->displayResourceId, info->displayWidth,
606		info->displayHeight);
607	if (status != B_OK)
608		goto error;
609
610	status = virtio_gpu_attach_backing(info, info->displayResourceId);
611	if (status != B_OK)
612		goto error;
613
614	status = virtio_gpu_set_scanout(info, 0, info->displayResourceId, info->displayWidth,
615		info->displayHeight);
616	if (status != B_OK)
617		goto error;
618
619	{
620		virtio_gpu_shared_info& sharedInfo = *info->sharedInfo;
621		sharedInfo.frame_buffer_area = info->framebufferArea;
622		sharedInfo.frame_buffer = (uint8*)info->framebuffer;
623		sharedInfo.bytes_per_row = info->displayWidth * 4;
624		sharedInfo.current_mode.virtual_width = info->displayWidth;
625		sharedInfo.current_mode.virtual_height = info->displayHeight;
626		sharedInfo.current_mode.space = B_RGB32;
627	}
628	info->updateThreadRunning = true;
629	info->updateThread = spawn_kernel_thread(virtio_update_thread, "virtio_gpu update",
630		B_DISPLAY_PRIORITY, info);
631	if (info->updateThread < B_OK)
632		goto error;
633	resume_thread(info->updateThread);
634
635	handle->info = info;
636
637	*_cookie = handle;
638	return B_OK;
639
640error:
641	delete_area(info->framebufferArea);
642	info->framebufferArea = -1;
643	delete_sem(info->commandDone);
644	info->commandDone = -1;
645	free(handle);
646	return B_ERROR;
647}
648
649
650static status_t
651virtio_gpu_close(void* cookie)
652{
653	virtio_gpu_handle* handle = (virtio_gpu_handle*)cookie;
654	CALLED();
655
656	virtio_gpu_driver_info* info = handle->info;
657	info->updateThreadRunning = false;
658	delete_sem(info->commandDone);
659	info->commandDone  = -1;
660
661	return B_OK;
662}
663
664
665static status_t
666virtio_gpu_free(void* cookie)
667{
668	CALLED();
669	virtio_gpu_handle* handle = (virtio_gpu_handle*)cookie;
670
671	virtio_gpu_driver_info* info = handle->info;
672	int32 result;
673	wait_for_thread(info->updateThread, &result);
674	info->updateThread = -1;
675	virtio_gpu_drain_queues(info);
676	free(handle);
677	return B_OK;
678}
679
680
681static void
682virtio_gpu_vqwait(void* driverCookie, void* cookie)
683{
684	CALLED();
685	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)cookie;
686
687	release_sem_etc(info->commandDone, 1, B_DO_NOT_RESCHEDULE);
688}
689
690
691static status_t
692virtio_gpu_read(void* cookie, off_t pos, void* buffer, size_t* _length)
693{
694	*_length = 0;
695	return B_NOT_ALLOWED;
696}
697
698
699static status_t
700virtio_gpu_write(void* cookie, off_t pos, const void* buffer,
701	size_t* _length)
702{
703	*_length = 0;
704	return B_NOT_ALLOWED;
705}
706
707
708static status_t
709virtio_gpu_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
710{
711	CALLED();
712	virtio_gpu_handle* handle = (virtio_gpu_handle*)cookie;
713	virtio_gpu_driver_info* info = handle->info;
714
715	// TRACE("ioctl(op = %lx)\n", op);
716
717	switch (op) {
718		case B_GET_ACCELERANT_SIGNATURE:
719			dprintf(DEVICE_NAME ": acc: %s\n", ACCELERANT_NAME);
720			if (user_strlcpy((char*)buffer, ACCELERANT_NAME,
721					B_FILE_NAME_LENGTH) < B_OK)
722				return B_BAD_ADDRESS;
723
724			return B_OK;
725
726		// needed to share data between kernel and accelerant
727		case VIRTIO_GPU_GET_PRIVATE_DATA:
728			return user_memcpy(buffer, &info->sharedArea, sizeof(area_id));
729
730		case VIRTIO_GPU_SET_DISPLAY_MODE:
731		{
732			if (length != sizeof(display_mode))
733				return B_BAD_VALUE;
734
735			display_mode mode;
736			if (user_memcpy(&mode, buffer, sizeof(display_mode)) != B_OK)
737				return B_BAD_ADDRESS;
738
739			MutexLocker commandLocker(&info->commandLock);
740
741			return virtio_gpu_set_display_mode(info, &mode);
742		}
743		default:
744			ERROR("ioctl: unknown message %" B_PRIx32 "\n", op);
745			break;
746	}
747
748
749	return B_DEV_INVALID_IOCTL;
750}
751
752
753//	#pragma mark - driver module API
754
755
756static float
757virtio_gpu_supports_device(device_node* parent)
758{
759	CALLED();
760	const char* bus;
761	uint16 deviceType;
762
763	// make sure parent is really the Virtio bus manager
764	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
765		return -1;
766
767	if (strcmp(bus, "virtio"))
768		return 0.0;
769
770	// check whether it's really a Virtio GPU device
771	if (sDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
772			&deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_GPU)
773		return 0.0;
774
775	TRACE("Virtio gpu device found!\n");
776
777	return 0.6;
778}
779
780
781static status_t
782virtio_gpu_register_device(device_node* node)
783{
784	CALLED();
785
786	device_attr attrs[] = {
787		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Virtio GPU"} },
788		{ NULL }
789	};
790
791	return sDeviceManager->register_node(node, VIRTIO_GPU_DRIVER_MODULE_NAME,
792		attrs, NULL, NULL);
793}
794
795
796static status_t
797virtio_gpu_init_driver(device_node* node, void** cookie)
798{
799	CALLED();
800
801	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)malloc(
802		sizeof(virtio_gpu_driver_info));
803	if (info == NULL)
804		return B_NO_MEMORY;
805
806	memset(info, 0, sizeof(*info));
807
808	info->node = node;
809
810	*cookie = info;
811	return B_OK;
812}
813
814
815static void
816virtio_gpu_uninit_driver(void* _cookie)
817{
818	CALLED();
819	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_cookie;
820	free(info);
821}
822
823
824static status_t
825virtio_gpu_register_child_devices(void* _cookie)
826{
827	CALLED();
828	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_cookie;
829	status_t status;
830
831	int32 id = sDeviceManager->create_id(VIRTIO_GPU_DEVICE_ID_GENERATOR);
832	if (id < 0)
833		return id;
834
835	char name[64];
836	snprintf(name, sizeof(name), "graphics/virtio/%" B_PRId32,
837		id);
838
839	status = sDeviceManager->publish_device(info->node, name,
840		VIRTIO_GPU_DEVICE_MODULE_NAME);
841
842	return status;
843}
844
845
846//	#pragma mark -
847
848
849module_dependency module_dependencies[] = {
850	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
851	{}
852};
853
854struct device_module_info sVirtioGpuDevice = {
855	{
856		VIRTIO_GPU_DEVICE_MODULE_NAME,
857		0,
858		NULL
859	},
860
861	virtio_gpu_init_device,
862	virtio_gpu_uninit_device,
863	NULL, // remove,
864
865	virtio_gpu_open,
866	virtio_gpu_close,
867	virtio_gpu_free,
868	virtio_gpu_read,
869	virtio_gpu_write,
870	NULL,	// io
871	virtio_gpu_ioctl,
872
873	NULL,	// select
874	NULL,	// deselect
875};
876
877struct driver_module_info sVirtioGpuDriver = {
878	{
879		VIRTIO_GPU_DRIVER_MODULE_NAME,
880		0,
881		NULL
882	},
883
884	virtio_gpu_supports_device,
885	virtio_gpu_register_device,
886	virtio_gpu_init_driver,
887	virtio_gpu_uninit_driver,
888	virtio_gpu_register_child_devices,
889	NULL,	// rescan
890	NULL,	// removed
891};
892
893module_info* modules[] = {
894	(module_info*)&sVirtioGpuDriver,
895	(module_info*)&sVirtioGpuDevice,
896	NULL
897};
898