v4l/v4l2/api.rs
1use std::ffi::CString;
2use std::os::unix::ffi::OsStrExt;
3use std::{io, path::Path};
4
5use crate::v4l2::vidioc;
6
7#[cfg(feature = "v4l-sys")]
8mod detail {
9 use crate::v4l2::vidioc;
10 use crate::v4l_sys::*;
11 use std::convert::TryInto;
12
13 pub unsafe fn open(path: *const std::os::raw::c_char, flags: i32) -> std::os::raw::c_int {
14 v4l2_open(path, flags)
15 }
16 pub unsafe fn close(fd: std::os::raw::c_int) -> std::os::raw::c_int {
17 v4l2_close(fd)
18 }
19 pub unsafe fn ioctl(
20 fd: std::os::raw::c_int,
21 request: vidioc::_IOC_TYPE,
22 argp: *mut std::os::raw::c_void,
23 ) -> std::os::raw::c_int {
24 // libv4l expects `request` to be a u64, but this is not guaranteed on all platforms.
25 // For the default CI platform (x86_64) clippy will complain about a useless conversion.
26 #![allow(clippy::useless_conversion)]
27 v4l2_ioctl(
28 fd,
29 request.try_into().expect("vidioc::_IOC_TYPE -> u64 failed"),
30 argp,
31 )
32 }
33 pub unsafe fn mmap(
34 start: *mut std::os::raw::c_void,
35 length: usize,
36 prot: std::os::raw::c_int,
37 flags: std::os::raw::c_int,
38 fd: std::os::raw::c_int,
39 offset: libc::off_t,
40 ) -> *mut std::os::raw::c_void {
41 // libv4l expects `request` to be a u64, but this is not guaranteed on all platforms.
42 // For the default CI platform (x86_64) clippy will complain about a useless conversion.
43 #![allow(clippy::useless_conversion)]
44 v4l2_mmap(
45 start,
46 length.try_into().expect("usize -> c size_t failed"),
47 prot,
48 flags,
49 fd,
50 offset as i64,
51 )
52 }
53 pub unsafe fn munmap(start: *mut std::os::raw::c_void, length: usize) -> std::os::raw::c_int {
54 v4l2_munmap(start, length.try_into().expect("usize -> c size_t failed"))
55 }
56}
57
58#[cfg(feature = "v4l2-sys")]
59mod detail {
60 use crate::v4l2::vidioc;
61
62 pub unsafe fn open(path: *const std::os::raw::c_char, flags: i32) -> std::os::raw::c_int {
63 libc::open(path, flags)
64 }
65 pub unsafe fn close(fd: std::os::raw::c_int) -> std::os::raw::c_int {
66 libc::close(fd)
67 }
68 pub unsafe fn ioctl(
69 fd: std::os::raw::c_int,
70 request: vidioc::_IOC_TYPE,
71 argp: *mut std::os::raw::c_void,
72 ) -> std::os::raw::c_int {
73 /*
74 * It turns out the libc crate (and libc itself!) defines ioctl() with
75 * different, incompatible argument types on different platforms. To
76 * hack around this without conditional compilation, use syscall()
77 * instead as a drop-in replacement. Details:
78 * https://github.com/rust-lang/libc/issues/1036
79 */
80 libc::syscall(libc::SYS_ioctl, fd, request, argp) as std::os::raw::c_int
81 }
82 pub unsafe fn mmap(
83 start: *mut std::os::raw::c_void,
84 length: usize,
85 prot: std::os::raw::c_int,
86 flags: std::os::raw::c_int,
87 fd: std::os::raw::c_int,
88 offset: libc::off_t,
89 ) -> *mut std::os::raw::c_void {
90 libc::mmap(start, length, prot, flags, fd, offset)
91 }
92 pub unsafe fn munmap(start: *mut std::os::raw::c_void, length: usize) -> std::os::raw::c_int {
93 libc::munmap(start, length)
94 }
95}
96
97/// A convenience wrapper around v4l2_open.
98///
99/// Returns the file descriptor on success.
100/// In case of errors, the last OS error will be reported, aka errno on Linux.
101///
102/// # Arguments
103///
104/// * `path` - Path to the device node
105/// * `flags` - Open flags
106///
107/// # Example
108///
109/// ```
110/// extern crate v4l;
111///
112/// use v4l::v4l2;
113///
114/// let fd = v4l2::open("/dev/video0", libc::O_RDWR);
115/// ```
116pub fn open<P: AsRef<Path>>(path: P, flags: i32) -> io::Result<std::os::raw::c_int> {
117 let fd: std::os::raw::c_int;
118 let c_path = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
119
120 unsafe {
121 fd = detail::open(c_path.as_ptr(), flags);
122 }
123
124 if fd == -1 {
125 Err(io::Error::last_os_error())
126 } else {
127 Ok(fd)
128 }
129}
130
131/// A convenience wrapper around v4l2_close.
132///
133/// In case of errors, the last OS error will be reported, aka errno on Linux.
134///
135/// # Arguments
136///
137/// * `fd` - File descriptor of a previously opened device
138///
139/// # Example
140///
141/// ```
142/// extern crate v4l;
143///
144/// use v4l::v4l2;
145///
146/// let fd = v4l2::open("/dev/video0", libc::O_RDWR);
147/// if let Ok(fd) = fd {
148/// v4l2::close(fd).unwrap();
149/// }
150/// ```
151pub fn close(fd: std::os::raw::c_int) -> io::Result<()> {
152 let ret: std::os::raw::c_int;
153 unsafe {
154 ret = detail::close(fd);
155 }
156
157 if ret == -1 {
158 Err(io::Error::last_os_error())
159 } else {
160 Ok(())
161 }
162}
163
164/// A convenience wrapper around v4l2_ioctl.
165///
166/// In case of errors, the last OS error will be reported, aka errno on Linux.
167///
168/// # Arguments
169///
170/// * `fd` - File descriptor
171/// * `request` - IO control code (see [`vidioc`])
172/// * `argp` - Pointer to memory region holding the argument type
173///
174/// # Safety
175///
176/// For maximum flexibility, argp must be a raw pointer. Thus, the entire function is unsafe.
177///
178/// # Example
179///
180/// ```
181/// extern crate v4l;
182///
183/// use std::mem;
184///
185/// use v4l::v4l_sys::*;
186/// use v4l::v4l2;
187///
188/// let fd = v4l2::open("/dev/video0", libc::O_RDWR);
189/// let mut v4l2_caps: v4l2_capability;
190/// unsafe {
191/// v4l2_caps = mem::zeroed();
192/// }
193///
194/// if let Ok(fd) = fd {
195/// unsafe {
196/// v4l2::ioctl(fd, v4l2::vidioc::VIDIOC_QUERYCAP,
197/// &mut v4l2_caps as *mut _ as *mut std::os::raw::c_void);
198/// }
199/// }
200/// ```
201pub unsafe fn ioctl(
202 fd: std::os::raw::c_int,
203 request: vidioc::_IOC_TYPE,
204 argp: *mut std::os::raw::c_void,
205) -> io::Result<()> {
206 let ret = detail::ioctl(fd, request, argp);
207
208 if ret == -1 {
209 Err(io::Error::last_os_error())
210 } else {
211 Ok(())
212 }
213}
214
215/// A convenience wrapper around v4l2_mmap.
216///
217/// In case of errors, the last OS error will be reported, aka errno on Linux.
218///
219/// # Arguments
220///
221/// * `start` - Starting address of the new mapping, usually NULL
222/// * `length` - Length of the mapped region
223/// * `prot` - Desired memory protection of the mapped region
224/// * `flags` - Mapping flags
225/// * `fd` - File descriptor representing an opened device
226/// * `offset` - Offset in the source region, usually 0
227///
228/// # Safety
229///
230/// Start must be a raw pointer. Thus, the entire function is unsafe.
231///
232/// # Example
233///
234/// ```
235/// extern crate v4l;
236///
237/// use std::ptr;
238/// use v4l::v4l2;
239///
240/// let fd = v4l2::open("/dev/video0", libc::O_RDWR);
241/// if let Ok(fd) = fd {
242/// /* VIDIOC_REQBUFS */
243/// /* VIDIOC_QUERYBUF */
244/// let mapping_length: usize = 1000;
245///
246/// unsafe {
247/// let mapping = v4l2::mmap(ptr::null_mut(), mapping_length,
248/// libc::PROT_READ | libc::PROT_WRITE,
249/// libc::MAP_SHARED, fd, 0);
250/// }
251/// v4l2::close(fd).unwrap();
252/// }
253/// ```
254pub unsafe fn mmap(
255 start: *mut std::os::raw::c_void,
256 length: usize,
257 prot: std::os::raw::c_int,
258 flags: std::os::raw::c_int,
259 fd: std::os::raw::c_int,
260 offset: libc::off_t,
261) -> io::Result<*mut std::os::raw::c_void> {
262 let ret = detail::mmap(start, length, prot, flags, fd, offset);
263 if ret as usize == std::usize::MAX {
264 Err(io::Error::last_os_error())
265 } else {
266 Ok(ret)
267 }
268}
269
270/// A convenience wrapper around v4l2_munmap.
271///
272/// In case of errors, the last OS error will be reported, aka errno on Linux.
273///
274/// # Arguments
275///
276/// * `start` - Starting address of the mapping
277/// * `length` - Length of the mapped region
278///
279/// # Safety
280///
281/// Start must be a raw pointer. Thus, the entire function is unsafe.
282///
283/// # Example
284///
285/// ```
286/// extern crate v4l;
287///
288/// use std::ptr;
289/// use v4l::v4l2;
290///
291/// let fd = v4l2::open("/dev/video0", libc::O_RDWR);
292/// if let Ok(fd) = fd {
293/// /* VIDIOC_REQBUFS */
294/// /* VIDIOC_QUERYBUF */
295/// let mapping_length: usize = 1000;
296///
297/// unsafe {
298/// let mapping = v4l2::mmap(ptr::null_mut(), mapping_length,
299/// libc::PROT_READ | libc::PROT_WRITE,
300/// libc::MAP_SHARED, fd, 0);
301/// if let Ok(mapping) = mapping {
302/// v4l2::munmap(mapping, mapping_length).unwrap();
303/// }
304/// }
305/// v4l2::close(fd).unwrap();
306/// }
307/// ```
308pub unsafe fn munmap(start: *mut std::os::raw::c_void, length: usize) -> io::Result<()> {
309 let ret = detail::munmap(start, length);
310 if ret == -1 {
311 Err(io::Error::last_os_error())
312 } else {
313 Ok(())
314 }
315}