xref: /aosp_15_r20/external/crosvm/rutabaga_gfx/ffi/src/lib.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2021 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 ///! C-bindings for the rutabaga_gfx crate
6 extern crate rutabaga_gfx;
7 
8 use std::cell::RefCell;
9 use std::convert::TryInto;
10 use std::ffi::CStr;
11 use std::ffi::CString;
12 use std::fs::File;
13 use std::io::IoSliceMut;
14 use std::os::raw::c_char;
15 use std::os::raw::c_void;
16 use std::panic::catch_unwind;
17 use std::panic::AssertUnwindSafe;
18 use std::path::Path;
19 use std::path::PathBuf;
20 use std::ptr::copy_nonoverlapping;
21 use std::ptr::null;
22 use std::ptr::null_mut;
23 use std::slice::from_raw_parts;
24 use std::slice::from_raw_parts_mut;
25 
26 #[cfg(unix)]
27 use libc::iovec;
28 use libc::EINVAL;
29 use libc::ESRCH;
30 use rutabaga_gfx::ResourceCreate3D;
31 use rutabaga_gfx::ResourceCreateBlob;
32 use rutabaga_gfx::Rutabaga;
33 use rutabaga_gfx::RutabagaBuilder;
34 use rutabaga_gfx::RutabagaChannel;
35 use rutabaga_gfx::RutabagaComponentType;
36 use rutabaga_gfx::RutabagaDebug;
37 use rutabaga_gfx::RutabagaDebugHandler;
38 use rutabaga_gfx::RutabagaDescriptor;
39 use rutabaga_gfx::RutabagaFence;
40 use rutabaga_gfx::RutabagaFenceHandler;
41 use rutabaga_gfx::RutabagaFromRawDescriptor;
42 use rutabaga_gfx::RutabagaHandle;
43 use rutabaga_gfx::RutabagaIntoRawDescriptor;
44 use rutabaga_gfx::RutabagaIovec;
45 use rutabaga_gfx::RutabagaResult;
46 use rutabaga_gfx::RutabagaWsi;
47 use rutabaga_gfx::Transfer3D;
48 use rutabaga_gfx::RUTABAGA_DEBUG_ERROR;
49 
50 #[cfg(not(unix))]
51 #[repr(C)]
52 pub struct iovec {
53     pub iov_base: *mut c_void,
54     pub iov_len: usize,
55 }
56 
57 const NO_ERROR: i32 = 0;
58 const RUTABAGA_WSI_SURFACELESS: u64 = 1;
59 
60 thread_local! {
61     static S_DEBUG_HANDLER: RefCell<Option<RutabagaDebugHandler>> = const { RefCell::new(None) };
62 }
63 
log_error(debug_string: String)64 fn log_error(debug_string: String) {
65     S_DEBUG_HANDLER.with(|handler_cell| {
66         if let Some(handler) = &*handler_cell.borrow() {
67             let cstring = CString::new(debug_string.as_str()).expect("CString creation failed");
68 
69             let debug = RutabagaDebug {
70                 debug_type: RUTABAGA_DEBUG_ERROR,
71                 message: cstring.as_ptr(),
72             };
73 
74             handler.call(debug);
75         }
76     });
77 }
78 
return_result<T>(result: RutabagaResult<T>) -> i3279 fn return_result<T>(result: RutabagaResult<T>) -> i32 {
80     if let Err(e) = result {
81         log_error(e.to_string());
82         -EINVAL
83     } else {
84         NO_ERROR
85     }
86 }
87 
88 macro_rules! return_on_error {
89     ($result:expr) => {
90         match $result {
91             Ok(t) => t,
92             Err(e) => {
93                 log_error(e.to_string());
94                 return -EINVAL;
95             }
96         }
97     };
98 }
99 
100 macro_rules! return_on_io_error {
101     ($result:expr) => {
102         match $result {
103             Ok(t) => t,
104             Err(e) => {
105                 log_error(e.to_string());
106                 return -e.raw_os_error().unwrap_or(EINVAL);
107             }
108         }
109     };
110 }
111 
112 #[allow(non_camel_case_types)]
113 type rutabaga = Rutabaga;
114 
115 #[allow(non_camel_case_types)]
116 type rutabaga_create_blob = ResourceCreateBlob;
117 
118 #[allow(non_camel_case_types)]
119 type rutabaga_create_3d = ResourceCreate3D;
120 
121 #[allow(non_camel_case_types)]
122 type rutabaga_transfer = Transfer3D;
123 
124 #[allow(non_camel_case_types)]
125 type rutabaga_fence = RutabagaFence;
126 
127 #[allow(non_camel_case_types)]
128 type rutabaga_debug = RutabagaDebug;
129 
130 #[repr(C)]
131 #[derive(Copy, Clone)]
132 pub struct rutabaga_iovecs {
133     pub iovecs: *mut iovec,
134     pub num_iovecs: usize,
135 }
136 
137 #[repr(C)]
138 #[derive(Copy, Clone)]
139 pub struct rutabaga_handle {
140     pub os_handle: i64,
141     pub handle_type: u32,
142 }
143 
144 #[repr(C)]
145 pub struct rutabaga_mapping {
146     pub ptr: *mut c_void,
147     pub size: u64,
148 }
149 
150 #[repr(C)]
151 pub struct rutabaga_channel {
152     pub channel_name: *const c_char,
153     pub channel_type: u32,
154 }
155 
156 #[repr(C)]
157 pub struct rutabaga_channels {
158     pub channels: *const rutabaga_channel,
159     pub num_channels: usize,
160 }
161 
162 #[repr(C)]
163 pub struct rutabaga_command {
164     pub ctx_id: u32,
165     pub cmd_size: u32,
166     pub cmd: *mut u8,
167     pub num_in_fences: u32,
168     pub fence_ids: *mut u64,
169 }
170 
171 #[allow(non_camel_case_types)]
172 pub type rutabaga_fence_callback = extern "C" fn(user_data: u64, fence: &rutabaga_fence);
173 
174 #[allow(non_camel_case_types)]
175 pub type rutabaga_debug_callback = extern "C" fn(user_data: u64, debug: &rutabaga_debug);
176 
177 #[repr(C)]
178 pub struct rutabaga_builder<'a> {
179     pub user_data: u64,
180     pub capset_mask: u64,
181     pub wsi: u64,
182     pub fence_cb: rutabaga_fence_callback,
183     pub debug_cb: Option<rutabaga_debug_callback>,
184     pub channels: Option<&'a rutabaga_channels>,
185     pub renderer_features: *const c_char,
186 }
187 
create_ffi_fence_handler( user_data: u64, fence_cb: rutabaga_fence_callback, ) -> RutabagaFenceHandler188 fn create_ffi_fence_handler(
189     user_data: u64,
190     fence_cb: rutabaga_fence_callback,
191 ) -> RutabagaFenceHandler {
192     RutabagaFenceHandler::new(move |completed_fence| fence_cb(user_data, &completed_fence))
193 }
194 
create_ffi_debug_handler( user_data: u64, debug_cb: rutabaga_debug_callback, ) -> RutabagaDebugHandler195 fn create_ffi_debug_handler(
196     user_data: u64,
197     debug_cb: rutabaga_debug_callback,
198 ) -> RutabagaDebugHandler {
199     RutabagaDebugHandler::new(move |rutabaga_debug| debug_cb(user_data, &rutabaga_debug))
200 }
201 
202 #[no_mangle]
203 /// # Safety
204 /// - `capset_names` must be a null-terminated C-string.
rutabaga_calculate_capset_mask( capset_names: *const c_char, capset_mask: &mut u64, ) -> i32205 pub unsafe extern "C" fn rutabaga_calculate_capset_mask(
206     capset_names: *const c_char,
207     capset_mask: &mut u64,
208 ) -> i32 {
209     catch_unwind(AssertUnwindSafe(|| {
210         if capset_names == null() {
211             return -EINVAL;
212         }
213 
214         let c_str_slice = CStr::from_ptr(capset_names);
215         let result = c_str_slice.to_str();
216         let str_slice = return_on_error!(result);
217         *capset_mask = rutabaga_gfx::calculate_capset_mask(str_slice.split(':'));
218         NO_ERROR
219     }))
220     .unwrap_or(-ESRCH)
221 }
222 
223 /// # Safety
224 /// - If `(*builder).channels` is not null, the caller must ensure `(*channels).channels` points to
225 ///   a valid array of `struct rutabaga_channel` of size `(*channels).num_channels`.
226 /// - The `channel_name` field of `struct rutabaga_channel` must be a null-terminated C-string.
227 #[no_mangle]
rutabaga_init(builder: &rutabaga_builder, ptr: &mut *mut rutabaga) -> i32228 pub unsafe extern "C" fn rutabaga_init(builder: &rutabaga_builder, ptr: &mut *mut rutabaga) -> i32 {
229     catch_unwind(AssertUnwindSafe(|| {
230         let fence_handler = create_ffi_fence_handler((*builder).user_data, (*builder).fence_cb);
231         let mut debug_handler_opt: Option<RutabagaDebugHandler> = None;
232 
233         if let Some(func) = (*builder).debug_cb {
234             let debug_handler = create_ffi_debug_handler((*builder).user_data, func);
235             S_DEBUG_HANDLER.with(|handler_cell| {
236                 *handler_cell.borrow_mut() = Some(debug_handler.clone());
237             });
238             debug_handler_opt = Some(debug_handler);
239         }
240 
241         let mut rutabaga_channels_opt = None;
242         if let Some(channels) = (*builder).channels {
243             let mut rutabaga_channels: Vec<RutabagaChannel> = Vec::new();
244             let channels_slice = from_raw_parts(channels.channels, channels.num_channels);
245 
246             for channel in channels_slice {
247                 let c_str_slice = CStr::from_ptr(channel.channel_name);
248                 let result = c_str_slice.to_str();
249                 let str_slice = return_on_error!(result);
250                 let string = str_slice.to_owned();
251                 let path = PathBuf::from(&string);
252 
253                 rutabaga_channels.push(RutabagaChannel {
254                     base_channel: path,
255                     channel_type: channel.channel_type,
256                 });
257             }
258 
259             rutabaga_channels_opt = Some(rutabaga_channels);
260         }
261 
262         let mut renderer_features_opt = None;
263         let renderer_features_ptr = (*builder).renderer_features;
264         if !renderer_features_ptr.is_null() {
265             let c_str_slice = CStr::from_ptr(renderer_features_ptr);
266             let result = c_str_slice.to_str();
267             let str_slice = return_on_error!(result);
268             let string = str_slice.to_owned();
269             renderer_features_opt = Some(string);
270         }
271 
272         let mut component_type = RutabagaComponentType::CrossDomain;
273         if (*builder).capset_mask == 0 {
274             component_type = RutabagaComponentType::Rutabaga2D;
275         }
276 
277         let rutabaga_wsi = match (*builder).wsi {
278             RUTABAGA_WSI_SURFACELESS => RutabagaWsi::Surfaceless,
279             _ => return -EINVAL,
280         };
281 
282         let result = RutabagaBuilder::new(component_type, (*builder).capset_mask)
283             .set_use_external_blob(false)
284             .set_use_egl(true)
285             .set_wsi(rutabaga_wsi)
286             .set_debug_handler(debug_handler_opt)
287             .set_rutabaga_channels(rutabaga_channels_opt)
288             .set_renderer_features(renderer_features_opt)
289             .build(fence_handler, None);
290 
291         let rtbg = return_on_error!(result);
292         *ptr = Box::into_raw(Box::new(rtbg)) as _;
293         NO_ERROR
294     }))
295     .unwrap_or(-ESRCH)
296 }
297 
298 /// # Safety
299 /// - `ptr` must have been created by `rutabaga_init`.
300 #[no_mangle]
rutabaga_finish(ptr: &mut *mut rutabaga) -> i32301 pub extern "C" fn rutabaga_finish(ptr: &mut *mut rutabaga) -> i32 {
302     catch_unwind(AssertUnwindSafe(|| {
303         let _ = unsafe { Box::from_raw(*ptr) };
304         *ptr = null_mut();
305         NO_ERROR
306     }))
307     .unwrap_or(-ESRCH)
308 }
309 
310 #[no_mangle]
rutabaga_get_num_capsets(ptr: &mut rutabaga, num_capsets: &mut u32) -> i32311 pub extern "C" fn rutabaga_get_num_capsets(ptr: &mut rutabaga, num_capsets: &mut u32) -> i32 {
312     catch_unwind(AssertUnwindSafe(|| {
313         *num_capsets = ptr.get_num_capsets();
314         NO_ERROR
315     }))
316     .unwrap_or(-ESRCH)
317 }
318 
319 #[no_mangle]
rutabaga_get_capset_info( ptr: &mut rutabaga, capset_index: u32, capset_id: &mut u32, capset_version: &mut u32, capset_size: &mut u32, ) -> i32320 pub extern "C" fn rutabaga_get_capset_info(
321     ptr: &mut rutabaga,
322     capset_index: u32,
323     capset_id: &mut u32,
324     capset_version: &mut u32,
325     capset_size: &mut u32,
326 ) -> i32 {
327     catch_unwind(AssertUnwindSafe(|| {
328         let result = ptr.get_capset_info(capset_index);
329         let info = return_on_error!(result);
330         *capset_id = info.0;
331         *capset_version = info.1;
332         *capset_size = info.2;
333         NO_ERROR
334     }))
335     .unwrap_or(-ESRCH)
336 }
337 
338 /// # Safety
339 /// - `capset` must point an array of bytes of size `capset_size`.
340 #[no_mangle]
rutabaga_get_capset( ptr: &mut rutabaga, capset_id: u32, version: u32, capset: *mut u8, capset_size: u32, ) -> i32341 pub unsafe extern "C" fn rutabaga_get_capset(
342     ptr: &mut rutabaga,
343     capset_id: u32,
344     version: u32,
345     capset: *mut u8,
346     capset_size: u32,
347 ) -> i32 {
348     catch_unwind(AssertUnwindSafe(|| {
349         let size: usize = capset_size.try_into().map_err(|_e| -EINVAL).unwrap();
350         let result = ptr.get_capset(capset_id, version);
351         let vec = return_on_error!(result);
352         copy_nonoverlapping(vec.as_ptr(), capset, size);
353         NO_ERROR
354     }))
355     .unwrap_or(-ESRCH)
356 }
357 
358 #[no_mangle]
rutabaga_context_create( ptr: &mut rutabaga, ctx_id: u32, context_init: u32, context_name: *const c_char, context_name_len: u32, ) -> i32359 pub extern "C" fn rutabaga_context_create(
360     ptr: &mut rutabaga,
361     ctx_id: u32,
362     context_init: u32,
363     context_name: *const c_char,
364     context_name_len: u32,
365 ) -> i32 {
366     let mut name: Option<&str> = None;
367     if !context_name.is_null() && context_name_len > 0 {
368         // Safe because context_name is not NULL and len is a positive integer, so the caller
369         // is expected to provide a valid pointer to an array of bytes at least as long as the
370         // passed length. If the provided byte array doesn't contain valid utf-8, name will be
371         // None.
372         let view = unsafe {
373             std::slice::from_raw_parts(context_name as *const u8, context_name_len as usize)
374         };
375         name = std::str::from_utf8(view).ok();
376     }
377 
378     catch_unwind(AssertUnwindSafe(|| {
379         let result = ptr.create_context(ctx_id, context_init, name);
380         return_result(result)
381     }))
382     .unwrap_or(-ESRCH)
383 }
384 
385 #[no_mangle]
rutabaga_context_destroy(ptr: &mut rutabaga, ctx_id: u32) -> i32386 pub extern "C" fn rutabaga_context_destroy(ptr: &mut rutabaga, ctx_id: u32) -> i32 {
387     catch_unwind(AssertUnwindSafe(|| {
388         let result = ptr.destroy_context(ctx_id);
389         return_result(result)
390     }))
391     .unwrap_or(-ESRCH)
392 }
393 
394 #[no_mangle]
rutabaga_context_attach_resource( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, ) -> i32395 pub extern "C" fn rutabaga_context_attach_resource(
396     ptr: &mut rutabaga,
397     ctx_id: u32,
398     resource_id: u32,
399 ) -> i32 {
400     catch_unwind(AssertUnwindSafe(|| {
401         let result = ptr.context_attach_resource(ctx_id, resource_id);
402         return_result(result)
403     }))
404     .unwrap_or(-ESRCH)
405 }
406 
407 #[no_mangle]
rutabaga_context_detach_resource( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, ) -> i32408 pub extern "C" fn rutabaga_context_detach_resource(
409     ptr: &mut rutabaga,
410     ctx_id: u32,
411     resource_id: u32,
412 ) -> i32 {
413     catch_unwind(AssertUnwindSafe(|| {
414         let result = ptr.context_detach_resource(ctx_id, resource_id);
415         return_result(result)
416     }))
417     .unwrap_or(-ESRCH)
418 }
419 
420 #[no_mangle]
rutabaga_resource_create_3d( ptr: &mut rutabaga, resource_id: u32, create_3d: &rutabaga_create_3d, ) -> i32421 pub extern "C" fn rutabaga_resource_create_3d(
422     ptr: &mut rutabaga,
423     resource_id: u32,
424     create_3d: &rutabaga_create_3d,
425 ) -> i32 {
426     catch_unwind(AssertUnwindSafe(|| {
427         let result = ptr.resource_create_3d(resource_id, *create_3d);
428         return_result(result)
429     }))
430     .unwrap_or(-ESRCH)
431 }
432 
433 /// # Safety
434 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
435 ///   iovecs of size `(*iovecs).num_iovecs`.
436 /// - Each iovec must point to valid memory starting at `iov_base` with length `iov_len`.
437 /// - Each iovec must valid until the resource's backing is explictly detached or the resource is is
438 ///   unreferenced.
439 #[no_mangle]
rutabaga_resource_attach_backing( ptr: &mut rutabaga, resource_id: u32, iovecs: &rutabaga_iovecs, ) -> i32440 pub unsafe extern "C" fn rutabaga_resource_attach_backing(
441     ptr: &mut rutabaga,
442     resource_id: u32,
443     iovecs: &rutabaga_iovecs,
444 ) -> i32 {
445     catch_unwind(AssertUnwindSafe(|| {
446         let slice = from_raw_parts((*iovecs).iovecs, (*iovecs).num_iovecs);
447         let vecs = slice
448             .iter()
449             .map(|iov| RutabagaIovec {
450                 base: iov.iov_base,
451                 len: iov.iov_len,
452             })
453             .collect();
454 
455         let result = ptr.attach_backing(resource_id, vecs);
456         return_result(result)
457     }))
458     .unwrap_or(-ESRCH)
459 }
460 
461 #[no_mangle]
rutabaga_resource_detach_backing(ptr: &mut rutabaga, resource_id: u32) -> i32462 pub extern "C" fn rutabaga_resource_detach_backing(ptr: &mut rutabaga, resource_id: u32) -> i32 {
463     catch_unwind(AssertUnwindSafe(|| {
464         let result = ptr.detach_backing(resource_id);
465         return_result(result)
466     }))
467     .unwrap_or(-ESRCH)
468 }
469 
470 /// # Safety
471 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
472 ///   iovecs of size `(*iovecs).num_iovecs`.
473 #[no_mangle]
rutabaga_resource_transfer_read( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, transfer: &rutabaga_transfer, buf: Option<&iovec>, ) -> i32474 pub unsafe extern "C" fn rutabaga_resource_transfer_read(
475     ptr: &mut rutabaga,
476     ctx_id: u32,
477     resource_id: u32,
478     transfer: &rutabaga_transfer,
479     buf: Option<&iovec>,
480 ) -> i32 {
481     catch_unwind(AssertUnwindSafe(|| {
482         let mut slice_opt = None;
483         if let Some(iovec) = buf {
484             slice_opt = Some(IoSliceMut::new(std::slice::from_raw_parts_mut(
485                 iovec.iov_base as *mut u8,
486                 iovec.iov_len,
487             )));
488         }
489 
490         let result = ptr.transfer_read(ctx_id, resource_id, *transfer, slice_opt);
491         return_result(result)
492     }))
493     .unwrap_or(-ESRCH)
494 }
495 
496 #[no_mangle]
rutabaga_resource_transfer_write( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, transfer: &rutabaga_transfer, ) -> i32497 pub extern "C" fn rutabaga_resource_transfer_write(
498     ptr: &mut rutabaga,
499     ctx_id: u32,
500     resource_id: u32,
501     transfer: &rutabaga_transfer,
502 ) -> i32 {
503     catch_unwind(AssertUnwindSafe(|| {
504         let result = ptr.transfer_write(ctx_id, resource_id, *transfer);
505         return_result(result)
506     }))
507     .unwrap_or(-ESRCH)
508 }
509 
510 /// # Safety
511 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
512 ///   iovecs of size `(*iovecs).num_iovecs`.
513 /// - If `handle` is not null, the caller must ensure it is a valid OS-descriptor.  Ownership is
514 ///   transfered to rutabaga.
515 /// - Each iovec must valid until the resource's backing is explictly detached or the resource is is
516 ///   unreferenced.
517 #[no_mangle]
rutabaga_resource_create_blob( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, create_blob: &rutabaga_create_blob, iovecs: Option<&rutabaga_iovecs>, handle: Option<&rutabaga_handle>, ) -> i32518 pub unsafe extern "C" fn rutabaga_resource_create_blob(
519     ptr: &mut rutabaga,
520     ctx_id: u32,
521     resource_id: u32,
522     create_blob: &rutabaga_create_blob,
523     iovecs: Option<&rutabaga_iovecs>,
524     handle: Option<&rutabaga_handle>,
525 ) -> i32 {
526     catch_unwind(AssertUnwindSafe(|| {
527         let mut iovecs_opt: Option<Vec<RutabagaIovec>> = None;
528         if let Some(iovs) = iovecs {
529             let slice = if iovs.num_iovecs != 0 {
530                 from_raw_parts(iovs.iovecs, iovs.num_iovecs)
531             } else {
532                 &[]
533             };
534             let vecs = slice
535                 .iter()
536                 .map(|iov| RutabagaIovec {
537                     base: iov.iov_base,
538                     len: iov.iov_len,
539                 })
540                 .collect();
541             iovecs_opt = Some(vecs);
542         }
543 
544         let mut handle_opt: Option<RutabagaHandle> = None;
545 
546         // Only needed on Unix, since there is no way to create a handle from guest memory on
547         // Windows.
548         #[cfg(unix)]
549         if let Some(hnd) = handle {
550             handle_opt = Some(RutabagaHandle {
551                 os_handle: RutabagaDescriptor::from_raw_descriptor(
552                     (*hnd).os_handle.try_into().unwrap(),
553                 ),
554                 handle_type: (*hnd).handle_type,
555             });
556         }
557 
558         let result =
559             ptr.resource_create_blob(ctx_id, resource_id, *create_blob, iovecs_opt, handle_opt);
560 
561         return_result(result)
562     }))
563     .unwrap_or(-ESRCH)
564 }
565 
566 #[no_mangle]
rutabaga_resource_unref(ptr: &mut rutabaga, resource_id: u32) -> i32567 pub extern "C" fn rutabaga_resource_unref(ptr: &mut rutabaga, resource_id: u32) -> i32 {
568     catch_unwind(AssertUnwindSafe(|| {
569         let result = ptr.unref_resource(resource_id);
570         return_result(result)
571     }))
572     .unwrap_or(-ESRCH)
573 }
574 
575 /// # Safety
576 /// Caller owns raw descriptor on success and is responsible for closing it.
577 #[no_mangle]
rutabaga_resource_export_blob( ptr: &mut rutabaga, resource_id: u32, handle: &mut rutabaga_handle, ) -> i32578 pub extern "C" fn rutabaga_resource_export_blob(
579     ptr: &mut rutabaga,
580     resource_id: u32,
581     handle: &mut rutabaga_handle,
582 ) -> i32 {
583     catch_unwind(AssertUnwindSafe(|| {
584         let result = ptr.export_blob(resource_id);
585         let hnd = return_on_error!(result);
586 
587         (*handle).handle_type = hnd.handle_type;
588         (*handle).os_handle = hnd.os_handle.into_raw_descriptor() as i64;
589         NO_ERROR
590     }))
591     .unwrap_or(-ESRCH)
592 }
593 
594 #[no_mangle]
rutabaga_resource_map( ptr: &mut rutabaga, resource_id: u32, mapping: &mut rutabaga_mapping, ) -> i32595 pub extern "C" fn rutabaga_resource_map(
596     ptr: &mut rutabaga,
597     resource_id: u32,
598     mapping: &mut rutabaga_mapping,
599 ) -> i32 {
600     catch_unwind(AssertUnwindSafe(|| {
601         let result = ptr.map(resource_id);
602         let internal_map = return_on_error!(result);
603         (*mapping).ptr = internal_map.ptr as *mut c_void;
604         (*mapping).size = internal_map.size;
605         NO_ERROR
606     }))
607     .unwrap_or(-ESRCH)
608 }
609 
610 #[no_mangle]
rutabaga_resource_unmap(ptr: &mut rutabaga, resource_id: u32) -> i32611 pub extern "C" fn rutabaga_resource_unmap(ptr: &mut rutabaga, resource_id: u32) -> i32 {
612     catch_unwind(AssertUnwindSafe(|| {
613         let result = ptr.unmap(resource_id);
614         return_result(result)
615     }))
616     .unwrap_or(-ESRCH)
617 }
618 
619 #[no_mangle]
rutabaga_resource_map_info( ptr: &mut rutabaga, resource_id: u32, map_info: &mut u32, ) -> i32620 pub extern "C" fn rutabaga_resource_map_info(
621     ptr: &mut rutabaga,
622     resource_id: u32,
623     map_info: &mut u32,
624 ) -> i32 {
625     catch_unwind(AssertUnwindSafe(|| {
626         let result = ptr.map_info(resource_id);
627         *map_info = return_on_error!(result);
628         NO_ERROR
629     }))
630     .unwrap_or(-ESRCH)
631 }
632 
633 /// # Safety
634 /// - `commands` must point to a contiguous memory region of `size` bytes.
635 #[no_mangle]
rutabaga_submit_command( ptr: &mut rutabaga, cmd: &rutabaga_command, ) -> i32636 pub unsafe extern "C" fn rutabaga_submit_command(
637     ptr: &mut rutabaga,
638     cmd: &rutabaga_command,
639 ) -> i32 {
640     catch_unwind(AssertUnwindSafe(|| {
641         let cmd_slice = if cmd.cmd_size != 0 {
642             from_raw_parts_mut(cmd.cmd, cmd.cmd_size as usize)
643         } else {
644             &mut []
645         };
646         let fence_ids = if cmd.num_in_fences != 0 {
647             from_raw_parts(cmd.fence_ids, cmd.num_in_fences as usize)
648         } else {
649             &mut []
650         };
651 
652         let result = ptr.submit_command(cmd.ctx_id, cmd_slice, fence_ids);
653         return_result(result)
654     }))
655     .unwrap_or(-ESRCH)
656 }
657 
658 #[no_mangle]
rutabaga_create_fence(ptr: &mut rutabaga, fence: &rutabaga_fence) -> i32659 pub extern "C" fn rutabaga_create_fence(ptr: &mut rutabaga, fence: &rutabaga_fence) -> i32 {
660     catch_unwind(AssertUnwindSafe(|| {
661         let result = ptr.create_fence(*fence);
662         return_result(result)
663     }))
664     .unwrap_or(-ESRCH)
665 }
666 
667 /// # Safety
668 /// - `dir` must be a null-terminated C-string.
669 #[no_mangle]
rutabaga_snapshot(ptr: &mut rutabaga, dir: *const c_char) -> i32670 pub unsafe extern "C" fn rutabaga_snapshot(ptr: &mut rutabaga, dir: *const c_char) -> i32 {
671     catch_unwind(AssertUnwindSafe(|| {
672         let c_str_slice = CStr::from_ptr(dir);
673 
674         let result = c_str_slice.to_str();
675         let directory = return_on_error!(result);
676 
677         let file = return_on_io_error!(File::create(Path::new(directory).join("snapshot")));
678         let result = ptr.snapshot(&mut std::io::BufWriter::new(file), directory);
679         return_result(result)
680     }))
681     .unwrap_or(-ESRCH)
682 }
683 
684 /// # Safety
685 /// - `dir` must be a null-terminated C-string.
686 #[no_mangle]
rutabaga_restore(ptr: &mut rutabaga, dir: *const c_char) -> i32687 pub unsafe extern "C" fn rutabaga_restore(ptr: &mut rutabaga, dir: *const c_char) -> i32 {
688     catch_unwind(AssertUnwindSafe(|| {
689         let c_str_slice = CStr::from_ptr(dir);
690 
691         let result = c_str_slice.to_str();
692         let directory = return_on_error!(result);
693 
694         let file = return_on_io_error!(File::open(Path::new(directory).join("snapshot")));
695         let result = ptr.restore(&mut std::io::BufReader::new(file), directory);
696         return_result(result)
697     }))
698     .unwrap_or(-ESRCH)
699 }
700 
701 #[no_mangle]
rutabaga_resource_wait_sync(ptr: &mut rutabaga, resource_id: u32) -> i32702 pub extern "C" fn rutabaga_resource_wait_sync(ptr: &mut rutabaga, resource_id: u32) -> i32 {
703     catch_unwind(AssertUnwindSafe(|| {
704         let result = ptr.wait_sync(resource_id);
705         return_result(result)
706     }))
707     .unwrap_or(-ESRCH)
708 }
709