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
25 typedef 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
59 typedef 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
85 static device_manager_info* sDeviceManager;
86
87
88 static void virtio_gpu_vqwait(void* driverCookie, void* cookie);
89
90
91 const char*
get_feature_name(uint64 feature)92 get_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
108 static status_t
virtio_gpu_drain_queues(virtio_gpu_driver_info * info)109 virtio_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
121 status_t
virtio_gpu_send_cmd(virtio_gpu_driver_info * info,void * cmd,size_t cmdSize,void * response,size_t responseSize)122 virtio_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
158 status_t
virtio_gpu_get_display_info(virtio_gpu_driver_info * info)159 virtio_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
188 status_t
virtio_gpu_get_edids(virtio_gpu_driver_info * info,int scanout)189 virtio_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
212 status_t
virtio_gpu_create_2d(virtio_gpu_driver_info * info,int resourceId,int width,int height)213 virtio_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
236 status_t
virtio_gpu_unref(virtio_gpu_driver_info * info,int resourceId)237 virtio_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
257 status_t
virtio_gpu_attach_backing(virtio_gpu_driver_info * info,int resourceId)258 virtio_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
297 status_t
virtio_gpu_detach_backing(virtio_gpu_driver_info * info,int resourceId)298 virtio_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
318 status_t
virtio_gpu_set_scanout(virtio_gpu_driver_info * info,int scanoutId,int resourceId,uint32 width,uint32 height)319 virtio_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
343 status_t
virtio_gpu_transfer_to_host_2d(virtio_gpu_driver_info * info,int resourceId,uint32 width,uint32 height)344 virtio_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
367 status_t
virtio_gpu_flush_resource(virtio_gpu_driver_info * info,int resourceId,uint32 width,uint32 height)368 virtio_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
390 status_t
virtio_update_thread(void * arg)391 virtio_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
409 status_t
virtio_gpu_set_display_mode(virtio_gpu_driver_info * info,display_mode * mode)410 virtio_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
469 static status_t
virtio_gpu_init_device(void * _info,void ** _cookie)470 virtio_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, NULL);
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
531 err3:
532 err2:
533 delete_area(info->commandArea);
534 err1:
535 return status;
536 }
537
538
539 static void
virtio_gpu_uninit_device(void * _cookie)540 virtio_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
555 static status_t
virtio_gpu_open(void * _info,const char * path,int openMode,void ** _cookie)556 virtio_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
640 error:
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
650 static status_t
virtio_gpu_close(void * cookie)651 virtio_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
665 static status_t
virtio_gpu_free(void * cookie)666 virtio_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
681 static void
virtio_gpu_vqwait(void * driverCookie,void * cookie)682 virtio_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
691 static status_t
virtio_gpu_read(void * cookie,off_t pos,void * buffer,size_t * _length)692 virtio_gpu_read(void* cookie, off_t pos, void* buffer, size_t* _length)
693 {
694 *_length = 0;
695 return B_NOT_ALLOWED;
696 }
697
698
699 static status_t
virtio_gpu_write(void * cookie,off_t pos,const void * buffer,size_t * _length)700 virtio_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
708 static status_t
virtio_gpu_ioctl(void * cookie,uint32 op,void * buffer,size_t length)709 virtio_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
756 static float
virtio_gpu_supports_device(device_node * parent)757 virtio_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
781 static status_t
virtio_gpu_register_device(device_node * node)782 virtio_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
796 static status_t
virtio_gpu_init_driver(device_node * node,void ** cookie)797 virtio_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
815 static void
virtio_gpu_uninit_driver(void * _cookie)816 virtio_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
824 static status_t
virtio_gpu_register_child_devices(void * _cookie)825 virtio_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
849 module_dependency module_dependencies[] = {
850 {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
851 {}
852 };
853
854 struct 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
877 struct 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
893 module_info* modules[] = {
894 (module_info*)&sVirtioGpuDriver,
895 (module_info*)&sVirtioGpuDevice,
896 NULL
897 };
898