1#![allow(dead_code)]
12
13use super::gpu_accel::{
14 self, ConnectorStatus, ConnectorType, DisplayMode, EncoderType, PageFlipRequest,
15};
16use crate::error::KernelError;
17
18pub(crate) const DRM_IOCTL_VERSION: u32 = 0x00;
24pub(crate) const DRM_IOCTL_GEM_CLOSE: u32 = 0x09;
26pub(crate) const DRM_IOCTL_GET_CAP: u32 = 0x0C;
28pub(crate) const DRM_IOCTL_SET_MASTER: u32 = 0x1E;
30pub(crate) const DRM_IOCTL_DROP_MASTER: u32 = 0x1F;
32pub(crate) const DRM_IOCTL_PRIME_HANDLE_TO_FD: u32 = 0x2D;
34pub(crate) const DRM_IOCTL_PRIME_FD_TO_HANDLE: u32 = 0x2E;
36pub(crate) const DRM_IOCTL_MODE_GETRESOURCES: u32 = 0xA0;
38pub(crate) const DRM_IOCTL_MODE_GETCRTC: u32 = 0xA1;
40pub(crate) const DRM_IOCTL_MODE_SETCRTC: u32 = 0xA2;
42pub(crate) const DRM_IOCTL_MODE_GETENCODER: u32 = 0xA6;
44pub(crate) const DRM_IOCTL_MODE_GETCONNECTOR: u32 = 0xA7;
46pub(crate) const DRM_IOCTL_MODE_PAGE_FLIP: u32 = 0xB0;
48pub(crate) const DRM_IOCTL_MODE_CREATE_DUMB: u32 = 0xB2;
50pub(crate) const DRM_IOCTL_MODE_MAP_DUMB: u32 = 0xB3;
52pub(crate) const DRM_IOCTL_MODE_DESTROY_DUMB: u32 = 0xB4;
54
55pub(crate) const DRM_CAP_DUMB_BUFFER: u64 = 0x01;
61pub(crate) const DRM_CAP_PRIME: u64 = 0x05;
63pub(crate) const DRM_CAP_TIMESTAMP_MONOTONIC: u64 = 0x06;
65
66#[repr(C)]
72#[derive(Debug, Clone)]
73pub(crate) struct DrmVersion {
74 pub version_major: i32,
75 pub version_minor: i32,
76 pub version_patchlevel: i32,
77 pub name_len: u32,
78 pub name_ptr: u64,
79 pub date_len: u32,
80 pub date_ptr: u64,
81 pub desc_len: u32,
82 pub desc_ptr: u64,
83}
84
85#[repr(C)]
87#[derive(Debug, Clone, Copy)]
88pub(crate) struct DrmGetCap {
89 pub capability: u64,
90 pub value: u64,
91}
92
93#[repr(C)]
95#[derive(Debug, Clone, Copy)]
96pub(crate) struct DrmGemClose {
97 pub handle: u32,
98 pub pad: u32,
99}
100
101#[repr(C)]
103#[derive(Debug, Clone, Copy)]
104pub(crate) struct DrmPrimeHandleToFd {
105 pub handle: u32,
106 pub flags: u32,
107 pub fd: i32,
108 pub pad: u32,
109}
110
111#[repr(C)]
113#[derive(Debug, Clone, Copy)]
114pub(crate) struct DrmPrimeFdToHandle {
115 pub fd: i32,
116 pub pad: u32,
117 pub handle: u32,
118 pub pad2: u32,
119}
120
121#[repr(C)]
123#[derive(Debug, Clone, Copy)]
124pub(crate) struct DrmModeCardRes {
125 pub fb_id_ptr: u64,
126 pub crtc_id_ptr: u64,
127 pub connector_id_ptr: u64,
128 pub encoder_id_ptr: u64,
129 pub count_fbs: u32,
130 pub count_crtcs: u32,
131 pub count_connectors: u32,
132 pub count_encoders: u32,
133 pub min_width: u32,
134 pub max_width: u32,
135 pub min_height: u32,
136 pub max_height: u32,
137}
138
139#[repr(C)]
141#[derive(Debug, Clone, Copy)]
142pub(crate) struct DrmModeInfo {
143 pub clock: u32,
144 pub hdisplay: u16,
145 pub hsync_start: u16,
146 pub hsync_end: u16,
147 pub htotal: u16,
148 pub hskew: u16,
149 pub vdisplay: u16,
150 pub vsync_start: u16,
151 pub vsync_end: u16,
152 pub vtotal: u16,
153 pub vscan: u16,
154 pub vrefresh: u32,
155 pub flags: u32,
156 pub mode_type: u32,
157 pub name: [u8; 32],
158}
159
160impl DrmModeInfo {
161 pub(crate) fn from_display_mode(mode: &DisplayMode) -> Self {
163 let mut name = [0u8; 32];
164 let name_str = alloc::format!("{}x{}", mode.hdisplay, mode.vdisplay);
166 let copy_len = name_str.len().min(31);
167 name[..copy_len].copy_from_slice(&name_str.as_bytes()[..copy_len]);
168
169 Self {
170 clock: mode.clock_khz,
171 hdisplay: mode.hdisplay as u16,
172 hsync_start: mode.hsync_start as u16,
173 hsync_end: mode.hsync_end as u16,
174 htotal: mode.htotal as u16,
175 hskew: 0,
176 vdisplay: mode.vdisplay as u16,
177 vsync_start: mode.vsync_start as u16,
178 vsync_end: mode.vsync_end as u16,
179 vtotal: mode.vtotal as u16,
180 vscan: 0,
181 vrefresh: mode.vrefresh_mhz / 1000,
183 flags: 0,
184 mode_type: 0x40, name,
186 }
187 }
188
189 pub(crate) fn to_display_mode(self) -> DisplayMode {
191 DisplayMode {
192 hdisplay: self.hdisplay as u32,
193 vdisplay: self.vdisplay as u32,
194 clock_khz: self.clock,
195 hsync_start: self.hsync_start as u32,
196 hsync_end: self.hsync_end as u32,
197 htotal: self.htotal as u32,
198 vsync_start: self.vsync_start as u32,
199 vsync_end: self.vsync_end as u32,
200 vtotal: self.vtotal as u32,
201 vrefresh_mhz: self.vrefresh.checked_mul(1000).unwrap_or(60000),
202 }
203 }
204}
205
206#[repr(C)]
208#[derive(Debug, Clone, Copy)]
209pub(crate) struct DrmModeCrtc {
210 pub set_connectors_ptr: u64,
211 pub count_connectors: u32,
212 pub crtc_id: u32,
213 pub fb_id: u32,
214 pub x: u32,
215 pub y: u32,
216 pub gamma_size: u32,
217 pub mode_valid: u32,
218 pub mode: DrmModeInfo,
219}
220
221#[repr(C)]
223#[derive(Debug, Clone, Copy)]
224pub(crate) struct DrmModeEncoder {
225 pub encoder_id: u32,
226 pub encoder_type: u32,
227 pub crtc_id: u32,
228 pub possible_crtcs: u32,
229 pub possible_clones: u32,
230}
231
232#[repr(C)]
234#[derive(Debug, Clone, Copy)]
235pub(crate) struct DrmModeGetConnector {
236 pub encoders_ptr: u64,
237 pub modes_ptr: u64,
238 pub props_ptr: u64,
239 pub prop_values_ptr: u64,
240 pub count_modes: u32,
241 pub count_props: u32,
242 pub count_encoders: u32,
243 pub encoder_id: u32,
244 pub connector_id: u32,
245 pub connector_type: u32,
246 pub connector_type_id: u32,
247 pub connection: u32,
248 pub mm_width: u32,
249 pub mm_height: u32,
250 pub subpixel: u32,
251 pub pad: u32,
252}
253
254#[repr(C)]
256#[derive(Debug, Clone, Copy)]
257pub(crate) struct DrmModeCreateDumb {
258 pub height: u32,
259 pub width: u32,
260 pub bpp: u32,
261 pub flags: u32,
262 pub handle: u32,
264 pub pitch: u32,
266 pub size: u64,
268}
269
270#[repr(C)]
272#[derive(Debug, Clone, Copy)]
273pub(crate) struct DrmModeMapDumb {
274 pub handle: u32,
275 pub pad: u32,
276 pub offset: u64,
278}
279
280#[repr(C)]
282#[derive(Debug, Clone, Copy)]
283pub(crate) struct DrmModeDestroyDumb {
284 pub handle: u32,
285}
286
287#[repr(C)]
289#[derive(Debug, Clone, Copy)]
290pub(crate) struct DrmModePageFlip {
291 pub crtc_id: u32,
292 pub fb_id: u32,
293 pub flags: u32,
294 pub reserved: u32,
295 pub user_data: u64,
296}
297
298pub(crate) fn drm_ioctl_dispatch(_fd: i32, request: u64, arg: *mut u8) -> Result<i32, KernelError> {
311 let cmd = (request & 0xFF) as u32;
316
317 match cmd {
318 DRM_IOCTL_VERSION => handle_version(arg),
319 DRM_IOCTL_GET_CAP => handle_get_cap(arg),
320 DRM_IOCTL_GEM_CLOSE => handle_gem_close(arg),
321 DRM_IOCTL_SET_MASTER => Ok(0), DRM_IOCTL_DROP_MASTER => Ok(0), DRM_IOCTL_PRIME_HANDLE_TO_FD => handle_prime_handle_to_fd(arg),
324 DRM_IOCTL_PRIME_FD_TO_HANDLE => handle_prime_fd_to_handle(arg),
325 DRM_IOCTL_MODE_GETRESOURCES => handle_mode_get_resources(arg),
326 DRM_IOCTL_MODE_GETCRTC => handle_mode_get_crtc(arg),
327 DRM_IOCTL_MODE_SETCRTC => handle_mode_set_crtc(arg),
328 DRM_IOCTL_MODE_GETENCODER => handle_mode_get_encoder(arg),
329 DRM_IOCTL_MODE_GETCONNECTOR => handle_mode_get_connector(arg),
330 DRM_IOCTL_MODE_PAGE_FLIP => handle_mode_page_flip(arg),
331 DRM_IOCTL_MODE_CREATE_DUMB => handle_mode_create_dumb(arg),
332 DRM_IOCTL_MODE_MAP_DUMB => handle_mode_map_dumb(arg),
333 DRM_IOCTL_MODE_DESTROY_DUMB => handle_mode_destroy_dumb(arg),
334 _ => Err(KernelError::OperationNotSupported {
335 operation: "unsupported DRM ioctl",
336 }),
337 }
338}
339
340fn handle_version(arg: *mut u8) -> Result<i32, KernelError> {
346 if arg.is_null() {
347 return Err(KernelError::OperationNotSupported {
348 operation: "null arg for DRM_IOCTL_VERSION",
349 });
350 }
351 let ver = unsafe { &mut *(arg as *mut DrmVersion) };
353
354 ver.version_major = 1;
355 ver.version_minor = 0;
356 ver.version_patchlevel = 0;
357
358 let driver_name = b"veridian-drm";
360 if ver.name_ptr != 0 && ver.name_len > 0 {
361 let copy_len = (ver.name_len as usize).min(driver_name.len());
362 unsafe {
364 core::ptr::copy_nonoverlapping(driver_name.as_ptr(), ver.name_ptr as *mut u8, copy_len);
365 }
366 }
367 ver.name_len = driver_name.len() as u32;
368
369 let date = b"20260307";
371 if ver.date_ptr != 0 && ver.date_len > 0 {
372 let copy_len = (ver.date_len as usize).min(date.len());
373 unsafe {
375 core::ptr::copy_nonoverlapping(date.as_ptr(), ver.date_ptr as *mut u8, copy_len);
376 }
377 }
378 ver.date_len = date.len() as u32;
379
380 let desc = b"VeridianOS VirtIO GPU DRM driver";
382 if ver.desc_ptr != 0 && ver.desc_len > 0 {
383 let copy_len = (ver.desc_len as usize).min(desc.len());
384 unsafe {
386 core::ptr::copy_nonoverlapping(desc.as_ptr(), ver.desc_ptr as *mut u8, copy_len);
387 }
388 }
389 ver.desc_len = desc.len() as u32;
390
391 Ok(0)
392}
393
394fn handle_get_cap(arg: *mut u8) -> Result<i32, KernelError> {
396 if arg.is_null() {
397 return Err(KernelError::OperationNotSupported {
398 operation: "null arg for DRM_IOCTL_GET_CAP",
399 });
400 }
401 let cap = unsafe { &mut *(arg as *mut DrmGetCap) };
403
404 cap.value = match cap.capability {
405 DRM_CAP_DUMB_BUFFER => 1,
406 DRM_CAP_PRIME => 1,
407 DRM_CAP_TIMESTAMP_MONOTONIC => 1,
408 _ => 0,
409 };
410
411 Ok(0)
412}
413
414fn handle_gem_close(arg: *mut u8) -> Result<i32, KernelError> {
416 if arg.is_null() {
417 return Err(KernelError::OperationNotSupported {
418 operation: "null arg for DRM_IOCTL_GEM_CLOSE",
419 });
420 }
421 let close = unsafe { &*(arg as *const DrmGemClose) };
423
424 gpu_accel::with_gem(|gem| {
425 gem.destroy_buffer(close.handle);
426 });
427
428 Ok(0)
429}
430
431fn handle_prime_handle_to_fd(arg: *mut u8) -> Result<i32, KernelError> {
433 if arg.is_null() {
434 return Err(KernelError::OperationNotSupported {
435 operation: "null arg for PRIME_HANDLE_TO_FD",
436 });
437 }
438 let prime = unsafe { &mut *(arg as *mut DrmPrimeHandleToFd) };
440
441 let exists =
443 gpu_accel::with_gem(|gem| gem.find_buffer(prime.handle).is_some()).unwrap_or(false);
444
445 if !exists {
446 return Err(KernelError::OperationNotSupported {
447 operation: "invalid GEM handle for PRIME export",
448 });
449 }
450
451 prime.fd = (prime.handle as i32).saturating_add(1000);
453
454 Ok(0)
455}
456
457fn handle_prime_fd_to_handle(arg: *mut u8) -> Result<i32, KernelError> {
459 if arg.is_null() {
460 return Err(KernelError::OperationNotSupported {
461 operation: "null arg for PRIME_FD_TO_HANDLE",
462 });
463 }
464 let prime = unsafe { &mut *(arg as *mut DrmPrimeFdToHandle) };
466
467 let handle = (prime.fd).saturating_sub(1000) as u32;
469
470 let exists = gpu_accel::with_gem(|gem| {
471 if gem.find_buffer(handle).is_some() {
472 gem.add_ref(handle);
473 true
474 } else {
475 false
476 }
477 })
478 .unwrap_or(false);
479
480 if !exists {
481 return Err(KernelError::OperationNotSupported {
482 operation: "invalid PRIME fd for import",
483 });
484 }
485
486 prime.handle = handle;
487
488 Ok(0)
489}
490
491fn handle_mode_get_resources(arg: *mut u8) -> Result<i32, KernelError> {
493 if arg.is_null() {
494 return Err(KernelError::OperationNotSupported {
495 operation: "null arg for MODE_GETRESOURCES",
496 });
497 }
498 let res = unsafe { &mut *(arg as *mut DrmModeCardRes) };
500
501 gpu_accel::with_kms(|kms| {
502 res.count_fbs = kms.framebuffers.len() as u32;
504 res.count_crtcs = kms.crtcs.len() as u32;
505 res.count_connectors = kms.connectors.len() as u32;
506 res.count_encoders = kms.encoders.len() as u32;
507
508 if res.fb_id_ptr != 0 && !kms.framebuffers.is_empty() {
510 let ptr = res.fb_id_ptr as *mut u32;
511 for (i, fb) in kms.framebuffers.iter().enumerate() {
512 unsafe {
514 ptr.add(i).write(fb.fb_id);
515 }
516 }
517 }
518
519 if res.crtc_id_ptr != 0 && !kms.crtcs.is_empty() {
520 let ptr = res.crtc_id_ptr as *mut u32;
521 for (i, crtc) in kms.crtcs.iter().enumerate() {
522 unsafe {
524 ptr.add(i).write(crtc.crtc_id);
525 }
526 }
527 }
528
529 if res.connector_id_ptr != 0 && !kms.connectors.is_empty() {
530 let ptr = res.connector_id_ptr as *mut u32;
531 for (i, conn) in kms.connectors.iter().enumerate() {
532 unsafe {
534 ptr.add(i).write(conn.connector_id);
535 }
536 }
537 }
538
539 if res.encoder_id_ptr != 0 && !kms.encoders.is_empty() {
540 let ptr = res.encoder_id_ptr as *mut u32;
541 for (i, enc) in kms.encoders.iter().enumerate() {
542 unsafe {
544 ptr.add(i).write(enc.encoder_id);
545 }
546 }
547 }
548
549 res.min_width = 1;
551 res.max_width = 7680;
552 res.min_height = 1;
553 res.max_height = 4320;
554 });
555
556 Ok(0)
557}
558
559fn handle_mode_get_crtc(arg: *mut u8) -> Result<i32, KernelError> {
561 if arg.is_null() {
562 return Err(KernelError::OperationNotSupported {
563 operation: "null arg for MODE_GETCRTC",
564 });
565 }
566 let crtc_arg = unsafe { &mut *(arg as *mut DrmModeCrtc) };
568
569 let found = gpu_accel::with_kms(|kms| {
570 if let Some(crtc) = kms.find_crtc(crtc_arg.crtc_id) {
571 crtc_arg.fb_id = crtc.fb_id.unwrap_or(0);
572 crtc_arg.x = 0;
573 crtc_arg.y = 0;
574 crtc_arg.gamma_size = crtc.gamma_size;
575
576 if let Some(ref mode) = crtc.mode {
577 crtc_arg.mode_valid = 1;
578 crtc_arg.mode = DrmModeInfo::from_display_mode(mode);
579 } else {
580 crtc_arg.mode_valid = 0;
581 }
582 true
583 } else {
584 false
585 }
586 })
587 .unwrap_or(false);
588
589 if !found {
590 return Err(KernelError::OperationNotSupported {
591 operation: "CRTC not found",
592 });
593 }
594
595 Ok(0)
596}
597
598fn handle_mode_set_crtc(arg: *mut u8) -> Result<i32, KernelError> {
600 if arg.is_null() {
601 return Err(KernelError::OperationNotSupported {
602 operation: "null arg for MODE_SETCRTC",
603 });
604 }
605 let crtc_arg = unsafe { &*(arg as *const DrmModeCrtc) };
607
608 let success = gpu_accel::with_kms(|kms| {
609 if let Some(crtc) = kms.crtcs.iter_mut().find(|c| c.crtc_id == crtc_arg.crtc_id) {
610 crtc.fb_id = if crtc_arg.fb_id != 0 {
611 Some(crtc_arg.fb_id)
612 } else {
613 None
614 };
615
616 if crtc_arg.mode_valid != 0 {
617 crtc.mode = Some(crtc_arg.mode.to_display_mode());
618 crtc.active = true;
619 } else {
620 crtc.mode = None;
621 crtc.active = false;
622 }
623 true
624 } else {
625 false
626 }
627 })
628 .unwrap_or(false);
629
630 if !success {
631 return Err(KernelError::OperationNotSupported {
632 operation: "CRTC set failed",
633 });
634 }
635
636 Ok(0)
637}
638
639fn handle_mode_get_encoder(arg: *mut u8) -> Result<i32, KernelError> {
641 if arg.is_null() {
642 return Err(KernelError::OperationNotSupported {
643 operation: "null arg for MODE_GETENCODER",
644 });
645 }
646 let enc_arg = unsafe { &mut *(arg as *mut DrmModeEncoder) };
648
649 let found = gpu_accel::with_kms(|kms| {
650 if let Some(enc) = kms
651 .encoders
652 .iter()
653 .find(|e| e.encoder_id == enc_arg.encoder_id)
654 {
655 enc_arg.encoder_type = match enc.encoder_type {
656 EncoderType::None => 0,
657 EncoderType::Dac => 1,
658 EncoderType::Tmds => 2,
659 EncoderType::Lvds => 3,
660 EncoderType::DpMst => 4,
661 EncoderType::Virtual => 5,
662 };
663 enc_arg.crtc_id = enc.crtc_id.unwrap_or(0);
664 enc_arg.possible_crtcs = enc.possible_crtcs;
665 enc_arg.possible_clones = 0;
666 true
667 } else {
668 false
669 }
670 })
671 .unwrap_or(false);
672
673 if !found {
674 return Err(KernelError::OperationNotSupported {
675 operation: "encoder not found",
676 });
677 }
678
679 Ok(0)
680}
681
682fn handle_mode_get_connector(arg: *mut u8) -> Result<i32, KernelError> {
684 if arg.is_null() {
685 return Err(KernelError::OperationNotSupported {
686 operation: "null arg for MODE_GETCONNECTOR",
687 });
688 }
689 let conn_arg = unsafe { &mut *(arg as *mut DrmModeGetConnector) };
691
692 let found = gpu_accel::with_kms(|kms| {
693 if let Some(conn) = kms
694 .connectors
695 .iter()
696 .find(|c| c.connector_id == conn_arg.connector_id)
697 {
698 conn_arg.encoder_id = conn.encoder_id.unwrap_or(0);
699 conn_arg.connector_type = match conn.connector_type {
700 ConnectorType::Hdmi => 11,
701 ConnectorType::DisplayPort => 14,
702 ConnectorType::Vga => 1,
703 ConnectorType::Edp => 14,
704 ConnectorType::Dvi => 3,
705 ConnectorType::Lvds => 7,
706 ConnectorType::Virtual => 15,
707 };
708 conn_arg.connector_type_id = 1;
709 conn_arg.connection = match conn.status {
710 ConnectorStatus::Connected => 1,
711 ConnectorStatus::Disconnected => 2,
712 _ => 3, };
714 conn_arg.mm_width = 530; conn_arg.mm_height = 300;
716 conn_arg.subpixel = 1; conn_arg.count_modes = conn.modes.len() as u32;
718 conn_arg.count_props = 0;
719 conn_arg.count_encoders = if conn.encoder_id.is_some() { 1 } else { 0 };
720
721 if conn_arg.modes_ptr != 0 && !conn.modes.is_empty() {
723 let ptr = conn_arg.modes_ptr as *mut DrmModeInfo;
724 for (i, mode) in conn.modes.iter().enumerate() {
725 unsafe {
727 ptr.add(i).write(DrmModeInfo::from_display_mode(mode));
728 }
729 }
730 }
731
732 if conn_arg.encoders_ptr != 0 {
734 if let Some(enc_id) = conn.encoder_id {
735 unsafe {
737 (conn_arg.encoders_ptr as *mut u32).write(enc_id);
738 }
739 }
740 }
741
742 true
743 } else {
744 false
745 }
746 })
747 .unwrap_or(false);
748
749 if !found {
750 return Err(KernelError::OperationNotSupported {
751 operation: "connector not found",
752 });
753 }
754
755 Ok(0)
756}
757
758fn handle_mode_create_dumb(arg: *mut u8) -> Result<i32, KernelError> {
760 if arg.is_null() {
761 return Err(KernelError::OperationNotSupported {
762 operation: "null arg for MODE_CREATE_DUMB",
763 });
764 }
765 let dumb = unsafe { &mut *(arg as *mut DrmModeCreateDumb) };
767
768 let bpp = if dumb.bpp == 0 { 32 } else { dumb.bpp };
770 let pitch = dumb
771 .width
772 .checked_mul(bpp / 8)
773 .ok_or(KernelError::OperationNotSupported {
774 operation: "dumb buffer pitch overflow",
775 })?;
776 let size = (pitch as u64).checked_mul(dumb.height as u64).ok_or(
777 KernelError::OperationNotSupported {
778 operation: "dumb buffer size overflow",
779 },
780 )?;
781
782 let handle = gpu_accel::with_gem(|gem| gem.create_buffer(size as usize))
784 .flatten()
785 .ok_or(KernelError::OperationNotSupported {
786 operation: "GEM allocation failed for dumb buffer",
787 })?;
788
789 dumb.handle = handle;
790 dumb.pitch = pitch;
791 dumb.size = size;
792
793 Ok(0)
794}
795
796fn handle_mode_map_dumb(arg: *mut u8) -> Result<i32, KernelError> {
798 if arg.is_null() {
799 return Err(KernelError::OperationNotSupported {
800 operation: "null arg for MODE_MAP_DUMB",
801 });
802 }
803 let map = unsafe { &mut *(arg as *mut DrmModeMapDumb) };
805
806 let exists = gpu_accel::with_gem(|gem| gem.find_buffer(map.handle).is_some()).unwrap_or(false);
808
809 if !exists {
810 return Err(KernelError::OperationNotSupported {
811 operation: "invalid handle for MAP_DUMB",
812 });
813 }
814
815 map.offset = (map.handle as u64) << 12;
818
819 Ok(0)
820}
821
822fn handle_mode_destroy_dumb(arg: *mut u8) -> Result<i32, KernelError> {
824 if arg.is_null() {
825 return Err(KernelError::OperationNotSupported {
826 operation: "null arg for MODE_DESTROY_DUMB",
827 });
828 }
829 let destroy = unsafe { &*(arg as *const DrmModeDestroyDumb) };
831
832 gpu_accel::with_gem(|gem| {
833 gem.destroy_buffer(destroy.handle);
834 });
835
836 Ok(0)
837}
838
839fn handle_mode_page_flip(arg: *mut u8) -> Result<i32, KernelError> {
841 if arg.is_null() {
842 return Err(KernelError::OperationNotSupported {
843 operation: "null arg for MODE_PAGE_FLIP",
844 });
845 }
846 let flip = unsafe { &*(arg as *const DrmModePageFlip) };
848
849 let success = gpu_accel::with_page_flip(|pf| {
850 pf.request_flip(PageFlipRequest {
851 crtc_id: flip.crtc_id,
852 fb_id: flip.fb_id,
853 user_data: flip.user_data,
854 })
855 })
856 .unwrap_or(false);
857
858 if !success {
859 return Err(KernelError::OperationNotSupported {
860 operation: "page flip request failed",
861 });
862 }
863
864 Ok(0)
865}
866
867#[cfg(test)]
872mod tests {
873 use super::*;
874
875 #[test]
876 fn test_drm_mode_info_conversion() {
877 let mode = DisplayMode::mode_1080p60();
878 let info = DrmModeInfo::from_display_mode(&mode);
879 assert_eq!(info.hdisplay, 1920);
880 assert_eq!(info.vdisplay, 1080);
881 assert_eq!(info.vrefresh, 60);
882 assert_eq!(info.clock, 148500);
883
884 let back = info.to_display_mode();
885 assert_eq!(back.hdisplay, 1920);
886 assert_eq!(back.vdisplay, 1080);
887 }
888
889 #[test]
890 fn test_drm_mode_info_wxga() {
891 let mode = DisplayMode::mode_wxga60();
892 let info = DrmModeInfo::from_display_mode(&mode);
893 assert_eq!(info.hdisplay, 1280);
894 assert_eq!(info.vdisplay, 800);
895 }
896
897 #[test]
898 fn test_create_dumb_pitch_calculation() {
899 let mut dumb = DrmModeCreateDumb {
901 height: 1080,
902 width: 1920,
903 bpp: 32,
904 flags: 0,
905 handle: 0,
906 pitch: 0,
907 size: 0,
908 };
909
910 let bpp = dumb.bpp;
913 let pitch = dumb.width * (bpp / 8);
914 dumb.pitch = pitch;
915 dumb.size = (pitch as u64) * (dumb.height as u64);
916
917 assert_eq!(dumb.pitch, 7680);
918 assert_eq!(dumb.size, 7680 * 1080);
919 }
920}