Skip to main content

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}