1 // Copyright 2024 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::collections::btree_map::Entry; 6 use std::collections::BTreeMap as Map; 7 use std::path::PathBuf; 8 9 use rutabaga_gfx::kumquat_support::RutabagaListener; 10 use rutabaga_gfx::kumquat_support::RutabagaWaitContext; 11 use rutabaga_gfx::kumquat_support::RutabagaWaitTimeout; 12 use rutabaga_gfx::RutabagaAsBorrowedDescriptor as AsBorrowedDescriptor; 13 use rutabaga_gfx::RutabagaError; 14 use rutabaga_gfx::RutabagaResult; 15 16 use crate::kumquat_gpu::KumquatGpu; 17 use crate::kumquat_gpu::KumquatGpuConnection; 18 19 enum KumquatConnection { 20 GpuListener, 21 GpuConnection(KumquatGpuConnection), 22 } 23 24 pub struct Kumquat { 25 connection_id: u64, 26 wait_ctx: RutabagaWaitContext, 27 kumquat_gpu_opt: Option<KumquatGpu>, 28 gpu_listener_opt: Option<RutabagaListener>, 29 connections: Map<u64, KumquatConnection>, 30 } 31 32 impl Kumquat { run(&mut self) -> RutabagaResult<()>33 pub fn run(&mut self) -> RutabagaResult<()> { 34 let events = self.wait_ctx.wait(RutabagaWaitTimeout::NoTimeout)?; 35 for event in events { 36 let mut hung_up = false; 37 match self.connections.entry(event.connection_id) { 38 Entry::Occupied(mut o) => { 39 let connection = o.get_mut(); 40 match connection { 41 KumquatConnection::GpuListener => { 42 if let Some(ref listener) = self.gpu_listener_opt { 43 let stream = listener.accept()?; 44 self.connection_id += 1; 45 let new_gpu_conn = KumquatGpuConnection::new(stream); 46 self.wait_ctx.add( 47 self.connection_id, 48 new_gpu_conn.as_borrowed_descriptor(), 49 )?; 50 self.connections.insert( 51 self.connection_id, 52 KumquatConnection::GpuConnection(new_gpu_conn), 53 ); 54 } 55 } 56 KumquatConnection::GpuConnection(ref mut gpu_conn) => { 57 if event.readable { 58 if let Some(ref mut kumquat_gpu) = self.kumquat_gpu_opt { 59 hung_up = 60 !gpu_conn.process_command(kumquat_gpu)? && event.hung_up; 61 } 62 } 63 64 if hung_up { 65 self.wait_ctx.delete(gpu_conn.as_borrowed_descriptor())?; 66 o.remove_entry(); 67 } 68 } 69 } 70 } 71 Entry::Vacant(_) => { 72 return Err(RutabagaError::SpecViolation("no connection found")) 73 } 74 } 75 } 76 77 Ok(()) 78 } 79 } 80 81 pub struct KumquatBuilder { 82 capset_names_opt: Option<String>, 83 gpu_socket_opt: Option<String>, 84 renderer_features_opt: Option<String>, 85 } 86 87 impl KumquatBuilder { new() -> KumquatBuilder88 pub fn new() -> KumquatBuilder { 89 KumquatBuilder { 90 capset_names_opt: None, 91 gpu_socket_opt: None, 92 renderer_features_opt: None, 93 } 94 } 95 set_capset_names(mut self, capset_names: String) -> KumquatBuilder96 pub fn set_capset_names(mut self, capset_names: String) -> KumquatBuilder { 97 self.capset_names_opt = Some(capset_names); 98 self 99 } 100 set_gpu_socket(mut self, gpu_socket_opt: Option<String>) -> KumquatBuilder101 pub fn set_gpu_socket(mut self, gpu_socket_opt: Option<String>) -> KumquatBuilder { 102 self.gpu_socket_opt = gpu_socket_opt; 103 self 104 } 105 set_renderer_features(mut self, renderer_features: String) -> KumquatBuilder106 pub fn set_renderer_features(mut self, renderer_features: String) -> KumquatBuilder { 107 self.renderer_features_opt = Some(renderer_features); 108 self 109 } 110 build(self) -> RutabagaResult<Kumquat>111 pub fn build(self) -> RutabagaResult<Kumquat> { 112 let connection_id: u64 = 0; 113 let mut wait_ctx = RutabagaWaitContext::new()?; 114 let mut kumquat_gpu_opt: Option<KumquatGpu> = None; 115 let mut gpu_listener_opt: Option<RutabagaListener> = None; 116 let mut connections: Map<u64, KumquatConnection> = Default::default(); 117 118 if let Some(gpu_socket) = self.gpu_socket_opt { 119 // Remove path if it exists 120 let path = PathBuf::from(&gpu_socket); 121 let _ = std::fs::remove_file(&path); 122 123 // Should not panic, since main.rs always calls set_capset_names and 124 // set_renderer_features, even with the empty string. 125 kumquat_gpu_opt = Some(KumquatGpu::new( 126 self.capset_names_opt.unwrap(), 127 self.renderer_features_opt.unwrap(), 128 )?); 129 130 let gpu_listener = RutabagaListener::bind(path)?; 131 wait_ctx.add(connection_id, gpu_listener.as_borrowed_descriptor())?; 132 connections.insert(connection_id, KumquatConnection::GpuListener); 133 gpu_listener_opt = Some(gpu_listener); 134 } 135 136 Ok(Kumquat { 137 connection_id, 138 wait_ctx, 139 kumquat_gpu_opt, 140 gpu_listener_opt, 141 connections, 142 }) 143 } 144 } 145