forked from Smithay/udev-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce hardware DB querying with
udev::hwdb
(Smithay#21)
This adds support for `udev`'s "hwdb" (hardware database) APIs. It introduces the `hwdb` module and `Hwdb` structure for querying the hardware database using modaliases. Additionally, the `list` module is introduced. `list` contains a generic wrapper for `udev`'s `udev_list_entry` and is used by `hwdb::Hwdb`to return query results. Other APIs that currently use their own specialized wrappers for `udev_list_entry` can be rewritten to use `list::List`, although this changeset doesn't do so to minimize churn.
- Loading branch information
Showing
3 changed files
with
175 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
use std::ffi::{CString, OsStr}; | ||
use std::io::Result; | ||
use std::marker::PhantomData; | ||
use std::os::unix::ffi::OsStrExt; | ||
|
||
use libc::c_char; | ||
|
||
use ffi; | ||
use list::List; | ||
use FromRaw; | ||
|
||
/// Rust wrapper for the `udev_hwdb` struct, which provides access to `udev`'s | ||
/// hardware database API. | ||
/// | ||
/// Like the `udev` struct, `udev_hwdb` is refcounted and automatically managed | ||
/// by the Rust wrapper. | ||
pub struct Hwdb { | ||
hwdb: *mut ffi::udev_hwdb, | ||
} | ||
|
||
impl Clone for Hwdb { | ||
fn clone(&self) -> Self { | ||
unsafe { Self::from_raw(ffi::udev_hwdb_ref(self.hwdb)) } | ||
} | ||
} | ||
|
||
impl Drop for Hwdb { | ||
fn drop(&mut self) { | ||
unsafe { ffi::udev_hwdb_unref(self.hwdb) }; | ||
} | ||
} | ||
|
||
as_ffi!(Hwdb, hwdb, ffi::udev_hwdb, ffi::udev_hwdb_ref); | ||
|
||
impl Hwdb { | ||
/// Creates a new Hwdb context. | ||
pub fn new() -> Result<Self> { | ||
// NOTE: udev_hwdb_new states that its first parameter is unused. | ||
// However, older versions of udev check it against NULL, so we can't just pass an | ||
// empty pointer in. Instead, we pass in a garbage pointer. | ||
let junk: *mut ffi::udev = 0x41414141_41414141 as *mut ffi::udev; | ||
let ptr = try_alloc!(unsafe { ffi::udev_hwdb_new(junk) }); | ||
Ok(unsafe { Self::from_raw(ptr) }) | ||
} | ||
|
||
/// Queries the hardware database with the given `modalias` query, | ||
/// returning an iterator over each matching entry. | ||
pub fn query<S: AsRef<OsStr>>(&self, modalias: S) -> List<Hwdb> { | ||
// NOTE: This expect can fail if someone passes a string that contains an internal NUL. | ||
let modalias = CString::new(modalias.as_ref().as_bytes()) | ||
.expect("query() called with malformed modalias string"); | ||
List { | ||
entry: unsafe { | ||
ffi::udev_hwdb_get_properties_list_entry( | ||
self.hwdb, | ||
modalias.as_ptr() as *const c_char, | ||
0, | ||
) | ||
}, | ||
phantom: PhantomData, | ||
} | ||
} | ||
|
||
/// Returns the first entry value with the given name, or `None` if no result exists. | ||
pub fn query_one<'a, S: AsRef<OsStr>>(&'a self, modalias: S, name: S) -> Option<&'a OsStr> { | ||
self.query(modalias) | ||
.find(|e| e.name == name.as_ref()) | ||
.map(|e| e.value) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_query() { | ||
let hwdb = Hwdb::new().unwrap(); | ||
// Query the hwdb for a device that should always be known: | ||
// the Linux Foundation's USB 1.1. root hub | ||
let results: Vec<_> = hwdb.query("usb:v1D6Bp0001").collect(); | ||
|
||
assert_eq!(results.len(), 2); | ||
|
||
// We expect an ID_VENDOR_FROM_DATABASE and an ID_MODEL_FROM_DATABASE with corresponding | ||
// values; no order is specified by udev. | ||
|
||
assert!(results | ||
.iter() | ||
.find(|e| e.name == "ID_VENDOR_FROM_DATABASE") | ||
.is_some()); | ||
assert!(results | ||
.iter() | ||
.find(|e| e.name == "ID_MODEL_FROM_DATABASE") | ||
.is_some()); | ||
|
||
assert!(results | ||
.iter() | ||
.find(|e| e.value == "Linux Foundation") | ||
.is_some()); | ||
assert!(results.iter().find(|e| e.value == "1.1 root hub").is_some()); | ||
} | ||
|
||
#[test] | ||
fn test_query_one() { | ||
let hwdb = Hwdb::new().unwrap(); | ||
let value = hwdb | ||
.query_one("usb:v1D6Bp0001", "ID_MODEL_FROM_DATABASE") | ||
.unwrap(); | ||
|
||
assert_eq!(value, "1.1 root hub"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use std::ffi::OsStr; | ||
use std::marker::PhantomData; | ||
|
||
use ffi; | ||
use util; | ||
|
||
/// Rust wrapper for the `udev_list_entry` struct, which provides sequential | ||
/// access to an associative list of string names and values. | ||
/// | ||
/// Each `List<T>` is parametrized on the Rust wrapper type that owns its | ||
/// underlying data. For example, `List<Hwdb>` indicates a list owned by | ||
/// some open handle to the `udev` hardware database. | ||
pub struct List<'a, T: 'a> { | ||
pub(crate) entry: *mut ffi::udev_list_entry, | ||
pub(crate) phantom: PhantomData<&'a T>, | ||
} | ||
|
||
impl<'a, T> Iterator for List<'a, T> { | ||
type Item = Entry<'a>; | ||
|
||
fn next(&mut self) -> Option<Entry<'a>> { | ||
if self.entry.is_null() { | ||
None | ||
} else { | ||
let name = | ||
unsafe { util::ptr_to_os_str_unchecked(ffi::udev_list_entry_get_name(self.entry)) }; | ||
let value = unsafe { | ||
util::ptr_to_os_str_unchecked(ffi::udev_list_entry_get_value(self.entry)) | ||
}; | ||
|
||
self.entry = unsafe { ffi::udev_list_entry_get_next(self.entry) }; | ||
|
||
Some(Entry { name, value }) | ||
} | ||
} | ||
|
||
fn size_hint(&self) -> (usize, Option<usize>) { | ||
(0, None) | ||
} | ||
} | ||
|
||
/// Rust wrapper for each entry in `List`, each of which contains a name and a value. | ||
pub struct Entry<'a> { | ||
pub(crate) name: &'a OsStr, | ||
pub(crate) value: &'a OsStr, | ||
} | ||
|
||
impl<'a> Entry<'a> { | ||
/// Returns the entry name. | ||
pub fn name(&self) -> &OsStr { | ||
self.name | ||
} | ||
|
||
/// Returns the entry value. | ||
pub fn value(&self) -> &OsStr { | ||
self.value | ||
} | ||
} |