Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
6.4 kB · 247 lines
Rust
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248use crate::udev_list;use crate::udev_list::udev_list_entry;use std::io::BufRead;use std::{ffi, fs, io, ptr, sync};
const USB_IDS_PATH: &str = env!("LIBUDEV_ZERO_USB_IDS_PATH");
#[allow(non_camel_case_types)]#[repr(C)]pub struct udev { _private: [u8; 0],}
#[derive(Default)]struct Hwdb { head: udev_list::ListEntry,}
#[allow(non_camel_case_types)]#[repr(C)]pub struct udev_hwdb { _private: [u8; 0],}
type UdevLogFn = Option< unsafe extern "C" fn( *mut udev, ffi::c_int, *const ffi::c_char, ffi::c_int, *const ffi::c_char, *const ffi::c_char, *mut ffi::c_void, ),>;
#[unsafe(no_mangle)]pub extern "C" fn udev_new() -> *mut udev { sync::Arc::into_raw(sync::Arc::new(udev { _private: [] })) .cast_mut() .cast()}
#[unsafe(no_mangle)]pub extern "C" fn udev_ref(udev: *mut udev) -> *mut udev { if !udev.is_null() { unsafe { sync::Arc::increment_strong_count(udev.cast_const()) }; }
udev}
#[unsafe(no_mangle)]pub extern "C" fn udev_unref(udev: *mut udev) -> *mut udev { if udev.is_null() { return ptr::null_mut(); }
let raw = udev; let udev = unsafe { sync::Arc::from_raw(udev.cast_const()) }; if sync::Arc::into_inner(udev).is_some() { ptr::null_mut() } else { raw }}
#[unsafe(no_mangle)]pub extern "C" fn udev_set_log_fn(_udev: *mut udev, _log_fn: UdevLogFn) {}
#[unsafe(no_mangle)]pub extern "C" fn udev_set_log_priority(_udev: *mut udev, _priority: ffi::c_int) {}
#[unsafe(no_mangle)]pub extern "C" fn udev_get_log_priority(_udev: *mut udev) -> ffi::c_int { 0}#[unsafe(no_mangle)]pub extern "C" fn udev_hwdb_new(_udev: *mut udev) -> *mut udev_hwdb { sync::Arc::into_raw(sync::Arc::new(Hwdb::default())) .cast::<udev_hwdb>() .cast_mut()}
#[unsafe(no_mangle)]pub extern "C" fn udev_hwdb_ref(hwdb: *mut udev_hwdb) -> *mut udev_hwdb { if !hwdb.is_null() { unsafe { sync::Arc::increment_strong_count(hwdb.cast::<Hwdb>()) }; }
hwdb}
#[unsafe(no_mangle)]pub extern "C" fn udev_hwdb_unref(hwdb: *mut udev_hwdb) -> *mut udev_hwdb { if !hwdb.is_null() { unsafe { drop(sync::Arc::from_raw(hwdb.cast::<Hwdb>())); } }
ptr::null_mut()}
#[unsafe(no_mangle)]pub extern "C" fn udev_hwdb_get_properties_list_entry( hwdb: *mut udev_hwdb, modalias: *const ffi::c_char, _flags: ffi::c_uint,) -> *mut udev_list_entry { if hwdb.is_null() || modalias.is_null() { return ptr::null_mut(); }
let hwdb = unsafe { &mut *hwdb.cast::<Hwdb>() }; if let Some(next) = hwdb.head.next.take() { drop(next); }
let modalias = unsafe { ffi::CStr::from_ptr(modalias) }.to_string_lossy(); match parse_usb_modalias(&modalias) { Some((vendor_id, Some(product_id))) => { if let Some(name) = usb_ids_lookup_product(vendor_id, product_id) { hwdb.head.add_entry( ffi::CString::new("ID_MODEL_FROM_DATABASE").unwrap(), Some(name), ); } } Some((vendor_id, None)) => { if let Some(name) = usb_ids_lookup_vendor(vendor_id) { hwdb.head.add_entry( ffi::CString::new("ID_VENDOR_FROM_DATABASE").unwrap(), Some(name), ); } } None => {} }
hwdb.head .next .as_deref_mut() .map_or(ptr::null_mut(), |next| ptr::from_mut(next).cast())}
fn usb_ids_lookup_product(vendor_id: u16, product_id: u16) -> Option<ffi::CString> { let file = fs::File::open(USB_IDS_PATH).ok()?; let reader = io::BufReader::new(file);
let mut in_vendor = false; for line in reader.lines().map_while(Result::ok) { if line.is_empty() || line.starts_with('#') { continue; }
if in_vendor { let Some(product_line) = line.strip_prefix('\t') else { break; };
if product_line.starts_with('\t') { continue; }
let Some(id_text) = product_line.split_whitespace().next() else { continue; }; let Ok(id) = u16::from_str_radix(id_text, 16) else { continue; }; if id != product_id { continue; }
let Some((_, name)) = product_line.split_once(' ') else { break; }; let name = name.trim_start_matches(' ').trim_end_matches('\r');
return ffi::CString::new(name).ok(); }
if line.starts_with('\t') { continue; }
let Some(id_text) = line.split_whitespace().next() else { continue; }; let Ok(id) = u16::from_str_radix(id_text, 16) else { continue; }; if id == vendor_id { in_vendor = true; } }
None}
fn usb_ids_lookup_vendor(vendor_id: u16) -> Option<ffi::CString> { let file = fs::File::open(USB_IDS_PATH).ok()?; let reader = io::BufReader::new(file);
for line in reader.lines().map_while(Result::ok) { if line.is_empty() || line.starts_with('#') || line.starts_with('\t') { continue; }
let Some(id_text) = line.split_whitespace().next() else { continue; }; let Ok(id) = u16::from_str_radix(id_text, 16) else { continue; }; if id != vendor_id { continue; }
let Some((_, name)) = line.split_once(' ') else { break; }; let name = name.trim_start_matches(' ').trim_end_matches('\r'); if name.is_empty() { break; }
return ffi::CString::new(name).ok(); }
None}
fn parse_usb_modalias(modalias: &str) -> Option<(u16, Option<u16>)> { let rest = modalias.strip_prefix("usb:v")?;
let vendor_id = u16::from_str_radix(rest.get(..4)?, 16).ok()?;
if let Some(product) = rest.get(4..)?.strip_prefix('p') { let product_id = u16::from_str_radix(product.get(..4)?, 16).ok()?;
Some((vendor_id, Some(product_id))) } else { Some((vendor_id, None)) }}