diff --git a/rust/modules/device_manager/sys/BUILD.gn b/rust/modules/device_manager/sys/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..3b58bb141aef35528dcbac320b1719c0791ff213 --- /dev/null +++ b/rust/modules/device_manager/sys/BUILD.gn @@ -0,0 +1,35 @@ +# Copyright (C) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//base/msdp/device_status/device_status.gni") + +ohos_rust_shared_library("fusion_device_manager_rust") { + sources = [ "src/lib.rs" ] + + if (fusion_interaction_coordination) { + features = [ "coordination" ] + } + + deps = [ + "${device_status_root_path}/rust/data/sys:fusion_data_rust", + "${device_status_root_path}/rust/utils:fusion_utils_rust", + ] + + external_deps = [ "hilog:hilog_rust" ] + + crate_name = "fusion_device_manager_rust" + crate_type = "dylib" + + subsystem_name = "${device_status_subsystem_name}" + part_name = "${device_status_part_name}" +} diff --git a/rust/modules/device_manager/sys/src/device.rs b/rust/modules/device_manager/sys/src/device.rs new file mode 100644 index 0000000000000000000000000000000000000000..9b8c32820bd394d8357161398568e5d46172f3cd --- /dev/null +++ b/rust/modules/device_manager/sys/src/device.rs @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! TODO: add documentation. + +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::os::fd::RawFd; +use std::ffi::{ c_char, c_int, c_long, CStr, CString }; +use std::fmt::{ Display, Formatter, Error }; +use std::mem::MaybeUninit; +use std::time::Duration; +use hilog_rust::{ debug, error, hilog, info, warn, HiLogLabel, LogType }; +use fusion_data_rust::{ FusionResult, FusionErrorCode, DEV_INPUT_PATH }; +use fusion_utils_rust::{ call_debug_enter, errno, last_error }; +use crate::interfaces::{ IDevice, KeyboardType }; +use crate::linux_input::{ eviocgname, eviocgphys, eviocguniq, eviocgprop, eviocgbit, eviocgid }; + +const DEFAULT_WAIT_TIME: u64 = 500; +const LOG_LABEL: HiLogLabel = HiLogLabel { + log_type: LogType::LogCore, + domain: 0xD002220, + tag: "FusionDevice" +}; + +const BITS_PER_UINT8: usize = 8usize; +const EV_KEY: usize = 0x01; +const EV_REL: usize = 0x02; +const EV_ABS: usize = 0x03; +const SPLIT_SYMBOL: &str = "|"; +const EMPTYSTR: &str = "" ; +const RET_ERR: i32 = -1 ; +const RET_OK: i32 = 0 ; + +const fn bit_offset(bit: usize) -> usize +{ + bit % BITS_PER_UINT8 +} + +const fn byte_offset(bit: usize) -> usize +{ + bit / BITS_PER_UINT8 +} + +const fn test_bit(bit: usize, array: &[u8]) -> bool +{ + if byte_offset(bit) < array.len() { + (array[byte_offset(bit)] & (1u8 << bit_offset(bit))) == (1u8 << bit_offset(bit)) + } else { + false + } +} + +fn set_bit(bit: usize, array: &mut [u8]) +{ + if byte_offset(bit) < array.len() { + array[byte_offset(bit)] |= 1u8 << bit_offset(bit); + } +} + +const fn nbytes(nbits: usize) -> usize +{ + (nbits + BITS_PER_UINT8 - 1) / BITS_PER_UINT8 +} + +/// DeviceCapability +pub enum DeviceCapability { + /// Keyboard + Keyboard, + /// Pointer + Pointer, + /// Touch + Touch, + /// TabletTool + TabletTool, + /// TabletPad + TabletPad, + /// Gesture + Gesture, + /// Switch + Switch, + /// Joystick + Joystick, +} + +pub const N_DEVICE_CAPABILITIES: usize = 8; + +impl From for usize { + fn from(value: DeviceCapability) -> Self { + match value { + DeviceCapability::Keyboard => { 0usize }, + DeviceCapability::Pointer => { 1usize }, + DeviceCapability::Touch => { 2usize }, + DeviceCapability::TabletTool => { 3usize }, + DeviceCapability::TabletPad => { 4usize }, + DeviceCapability::Gesture => { 5usize }, + DeviceCapability::Switch => { 6usize }, + DeviceCapability::Joystick => { 7usize }, + } + } +} + +impl TryFrom for DeviceCapability { + type Error = FusionErrorCode; + + fn try_from(value: usize) -> Result { + match value { + _ if usize::from(DeviceCapability::Keyboard) == value => { Ok(DeviceCapability::Keyboard) }, + _ if usize::from(DeviceCapability::Pointer) == value => { Ok(DeviceCapability::Pointer) }, + _ if usize::from(DeviceCapability::Touch) == value => { Ok(DeviceCapability::Touch) }, + _ if usize::from(DeviceCapability::TabletTool) == value => { Ok(DeviceCapability::TabletTool) }, + _ if usize::from(DeviceCapability::TabletPad) == value => { Ok(DeviceCapability::TabletPad) }, + _ if usize::from(DeviceCapability::Gesture) == value => { Ok(DeviceCapability::Gesture) }, + _ if usize::from(DeviceCapability::Switch) == value => { Ok(DeviceCapability::Switch) }, + _ if usize::from(DeviceCapability::Joystick) == value => { Ok(DeviceCapability::Joystick) }, + _ => { Err(FusionErrorCode::Fail) }, + } + } +} + +/// TODO: add documentation. +pub struct Device { + fd: RawFd, + dev_id: i32, + bus: i32, + version: i32, + product: i32, + vendor: i32, + dev_path: String, + sys_path: String, + name: String, + phys: String, + uniq: String, + caps: [u8; nbytes(N_DEVICE_CAPABILITIES)], + ev_bitmask: [u8; nbytes(libc::EV_MAX as usize)], + key_bitmask: [u8; nbytes(libc::KEY_MAX as usize)], + abs_bitmask: [u8; nbytes(libc::ABS_MAX as usize)], + rel_bitmask: [u8; nbytes(libc::REL_MAX as usize)], + prop_bitmask: [u8; nbytes(libc::INPUT_PROP_MAX as usize)], +} + +impl Device { + fn is_character_device(dev_path: &str) -> bool { + false + } + + fn parse_device_id(node_name: &str) -> FusionResult { + call_debug_enter!("Device::parse_device_id"); + Err(-1) + } + + fn check_node_path(node_name: &str) -> FusionResult { + let dev_path = String::from(DEV_INPUT_PATH) + node_name; + if Self::is_character_device(&dev_path) { + Ok(dev_path) + } else { + warn!(LOG_LABEL, "Not character device: {}", dev_path); + Err(-1) + } + } + + fn check_system_path(node_name: &str) -> FusionResult { + Err(-1) + } + + /// TODO: add documentation. + pub fn new(node_name: &str) -> FusionResult { + let dev_id = Self::parse_device_id(node_name)?; + let dev_path = Self::check_node_path(node_name)?; + let sys_path = Self::check_system_path(node_name)?; + + Ok(Self { + fd: -1, + dev_id, + bus: 0, + version: 0, + product: 0, + vendor: 0, + dev_path, + sys_path, + name: String::default(), + phys: String::default(), + uniq: String::default(), + caps: [0; nbytes(N_DEVICE_CAPABILITIES)], + ev_bitmask: [0; nbytes(libc::EV_MAX as usize)], + key_bitmask: [0; nbytes(libc::KEY_MAX as usize)], + abs_bitmask: [0; nbytes(libc::ABS_MAX as usize)], + rel_bitmask: [0; nbytes(libc::REL_MAX as usize)], + prop_bitmask: [0; nbytes(libc::INPUT_PROP_MAX as usize)], + }) + } + + fn query_device_info(&mut self) { + call_debug_enter!("Device::query_device_info"); + type Buffer = [c_char; libc::PATH_MAX as usize]; + let mut buffer = MaybeUninit::::zeroed(); + let mut zeroed_buffer = unsafe { buffer.assume_init_mut() }; + let mut rc = unsafe { + libc::ioctl(self.fd, eviocgname(std::mem::size_of_val(zeroed_buffer) - 1usize) as c_long, + zeroed_buffer.as_mut_ptr()) + }; + if rc < 0 { + error!(LOG_LABEL, "DeviceError in query_device_info(): Getting device name failed"); + } else { + match unsafe { CStr::from_ptr(zeroed_buffer.as_ptr()) }.to_str() { + Ok(s) => { + self.name = s.to_string(); + } + Err(err) => { + error!(LOG_LABEL, + "DeviceError in query_device_info(): Converting device name to string type failed"); + } + } + } + + let mut input_id=libc::input_id{ bustype: 0, vendor: 0, product: 0, version: 0 }; + rc = unsafe { + libc::ioctl(self.fd, eviocgid() as c_long, &mut input_id) + }; + if rc < 0 { + error!(LOG_LABEL, "Getting input id fail"); + } else { + self.bus = input_id.bustype as i32; + self.product = input_id.product as i32; + self.vendor = input_id.vendor as i32; + self.version = input_id.version as i32; + } + + buffer = MaybeUninit::::zeroed(); + zeroed_buffer = unsafe { buffer.assume_init_mut() }; + rc = unsafe { + libc::ioctl(self.fd, eviocgphys(std::mem::size_of_val(zeroed_buffer) - 1usize) as c_long, + zeroed_buffer.as_mut_ptr()) + }; + if rc < 0 { + error!(LOG_LABEL, "DeviceError in query_device_info(): Getting device location failed"); + } else { + match unsafe { CStr::from_ptr(zeroed_buffer.as_ptr()) }.to_str() { + Ok(s) => { + self.phys = s.to_string(); + } + Err(err) => { + error!(LOG_LABEL, + "DeviceError in query_device_info(): Converting device location to string type failed"); + } + } + } + + buffer = MaybeUninit::::zeroed(); + zeroed_buffer = unsafe { buffer.assume_init_mut() }; + rc = unsafe { + libc::ioctl(self.fd, eviocguniq(std::mem::size_of_val(zeroed_buffer) - 1usize) as c_int, + zeroed_buffer.as_mut_ptr()) + }; + if rc < 0 { + error!(LOG_LABEL, "DeviceError in query_device_info(): Getting device uniq failed"); + } else { + match unsafe { CStr::from_ptr(zeroed_buffer.as_ptr()) }.to_str() { + Ok(s) => { + self.uniq = s.to_string(); + } + Err(err) => { + error!(LOG_LABEL, + "DeviceError in query_device_info(): Converting device uniq to string type failed"); + } + } + } + } + + /// TODO: add documentation. + fn query_supported_events(&mut self) { + call_debug_enter!("Device::query_supported_events"); + let mut rc = unsafe { + libc::ioctl(self.fd, eviocgbit(0, std::mem::size_of_val(&self.ev_bitmask)) as c_long, &self.ev_bitmask) + }; + if rc < 0 { + error!(LOG_LABEL, "Getting events mask fail"); + } + rc = unsafe { + libc::ioctl(self.fd, eviocgbit(EV_KEY, std::mem::size_of_val(&self.key_bitmask)) as c_long, &self.key_bitmask) + }; + if rc < 0 { + error!(LOG_LABEL, "Getting key mask fail"); + } + rc = unsafe { + libc::ioctl(self.fd, eviocgbit(EV_ABS, std::mem::size_of_val(&self.abs_bitmask)) as c_long, &self.abs_bitmask) + }; + if rc < 0 { + error!(LOG_LABEL, "Getting abs mask fail"); + } + rc = unsafe { + libc::ioctl(self.fd, eviocgbit(EV_REL, std::mem::size_of_val(&self.rel_bitmask)) as c_long, &self.rel_bitmask) + }; + if rc < 0 { + error!(LOG_LABEL, "Getting rel mask fail"); + } + rc = unsafe { + libc::ioctl(self.fd, eviocgprop(std::mem::size_of_val(&self.prop_bitmask)) as c_long, &self.prop_bitmask) + }; + if rc < 0 { + error!(LOG_LABEL, "Getting properties mask fail"); + } + } + + fn has_event_type(&self, event_type: usize) -> bool { + test_bit(event_type, &self.ev_bitmask) + } + + fn has_key(&self, key: usize) -> bool { + test_bit(key, &self.key_bitmask) + } + + fn has_abs(&self, axis: usize) -> bool { + test_bit(axis, &self.abs_bitmask) + } + + fn has_rel(&self, axis: usize) -> bool { + test_bit(axis, &self.rel_bitmask) + } + + fn has_property(&self, prop: usize) -> bool { + test_bit(prop, &self.prop_bitmask) + } +} + +impl IDevice for Device { + fn open(&mut self) -> FusionResult { + call_debug_enter!("Device::open"); + let mut n_retries: i32 = 6; + + loop { + self.fd = unsafe { + libc::open(self.dev_path.as_ptr() as *const c_char, libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC) + }; + if self.fd < 0 { + error!(LOG_LABEL, "Unable to open device \'{}\':{}", self.dev_path, last_error()); + if (errno() != libc::ENOENT) && (n_retries > 0) { + n_retries -= 1; + const DEFAULT_WAIT_TIME: u64 = 500; + std::thread::sleep(Duration::from_millis(DEFAULT_WAIT_TIME)); + info!(LOG_LABEL, "Retry opening device \'{}\'", self.dev_path); + } else { + return Err(-1); + } + } else { + debug!(LOG_LABEL, "Opening \'{}\' successfully", self.dev_path); + break; + } + } + Ok(0) + } + + fn close(&mut self) { + + } + + fn id(&self) -> i32 { + self.dev_id + } + + fn dev_path(&self) -> &str { + &self.dev_path + } + + fn sys_path(&self) -> &str { + &self.sys_path + } + + fn name(&self) -> &str { + &self.name + } + + fn bus(&self) -> i32 { + self.bus + } + + fn version(&self) -> i32 { + self.version + } + + fn product(&self) -> i32 { + self.product + } + + fn vendor(&self) -> i32 { + self.vendor + } + + fn phys(&self) -> &str { + &self.phys + } + + fn uniq(&self) -> &str { + &self.uniq + } + + fn keyboard_type(&self) -> KeyboardType { + KeyboardType::Unknown + } + + fn is_pointer_device(&self) -> bool { + false + } + + fn is_keyboard(&self) -> bool { + false + } +} + +impl Display for Device { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> + { + writeln!(f, "\nDevice {{")?; + writeln!(f, " fd: {},", self.fd)?; + writeln!(f, " dev_id: {},", self.dev_id)?; + writeln!(f, " bus: {},", self.bus)?; + writeln!(f, " version: {},", self.version)?; + writeln!(f, " product: {},", self.product)?; + writeln!(f, " vendor: {},", self.vendor)?; + writeln!(f, " dev_path: {},", self.dev_path)?; + writeln!(f, " sys_path: {},", self.sys_path)?; + writeln!(f, " name: {},", self.name)?; + writeln!(f, " phys: {},", self.phys)?; + writeln!(f, " uniq: {},", self.uniq)?; + writeln!(f, " caps: {:?},", self.caps)?; + writeln!(f, " ev_bitmask: {:?},", self.ev_bitmask)?; + writeln!(f, " key_bitmask: {:?},", self.key_bitmask)?; + writeln!(f, " abs_bitmask: {:?},", self.abs_bitmask)?; + writeln!(f, " rel_bitmask: {:?},", self.rel_bitmask)?; + writeln!(f, " prop_bitmask: {:?},", self.prop_bitmask)?; + writeln!(f, "}}")?; + Ok(()) + } +} diff --git a/rust/modules/device_manager/sys/src/device_collection.rs b/rust/modules/device_manager/sys/src/device_collection.rs new file mode 100644 index 0000000000000000000000000000000000000000..3545ebd386ff8a1bd3e852669947944a59fced49 --- /dev/null +++ b/rust/modules/device_manager/sys/src/device_collection.rs @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! Implementation of container containing records of devices. + +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::cmp::{ Ord, Ordering }; +use std::cell::RefCell; +use std::collections::{ BTreeSet, HashMap }; +use std::ffi::{ CString, c_char }; +use std::rc::{ Rc, Weak }; +use hilog_rust::{ hilog, warn, HiLogLabel, LogType }; +use fusion_utils_rust::{ call_debug_enter }; +use crate::interfaces::{ IDevice, IHotplugHandler, IDeviceManager, IDeviceObserver }; +use crate::device::Device; + +const LOG_LABEL: HiLogLabel = HiLogLabel { + log_type: LogType::LogCore, + domain: 0xD002220, + tag: "DeviceCollection" +}; + +struct WeakHolder { + inner: Weak>, +} + +impl WeakHolder { + pub fn new(value: &Rc>) -> Self { + Self { + inner: Rc::downgrade(value), + } + } +} + +impl Ord for WeakHolder { + fn cmp(&self, other: &Self) -> Ordering { + self.inner.as_ptr().cmp(&other.inner.as_ptr()) + } +} + +impl PartialOrd for WeakHolder { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for WeakHolder { + fn eq(&self, other: &Self) -> bool { + self.inner.ptr_eq(&other.inner) + } +} + +impl Eq for WeakHolder {} + +impl From>> for WeakHolder { + // Required method + fn from(value: Weak>) -> Self { + Self { + inner: value, + } + } +} + +#[derive(Default)] +pub struct DeviceCollection { + devices: HashMap>, + observers: BTreeSet>, +} + +impl DeviceCollection { + fn on_device_added(&self, dev: &Rc) { + + } +} + +impl IHotplugHandler for DeviceCollection { + fn add_device(&mut self, node_name: &str) { + let dev = Rc::new( + match Device::new(node_name) { + Ok(dev) => { dev }, + Err(_) => { + return; + } + } + ); + + match self.devices.insert(dev.id(), dev.clone()) { + Some(v) => { + warn!(LOG_LABEL, "Device {} is updated", dev.id()); + } + None => { + self.on_device_added(&dev); + } + } + } + + fn remove_device(&mut self, node_name: &str) { + + } +} + +impl IDeviceManager for DeviceCollection { + fn device(&self, id: i32) -> Option> { + None + } + + fn add_observer(&mut self, observer: &Rc>) { + if !self.observers.insert(WeakHolder::new(observer)) { + warn!(LOG_LABEL, "Observer is duplicate"); + } + } + + fn remove_observer(&mut self, observer: &Rc>) { + let holder = WeakHolder::new(observer); + if !self.observers.remove(&holder) { + warn!(LOG_LABEL, "Not present"); + } + } + + fn retrigger_hotplug(&self, observer: &Rc>) { + call_debug_enter!("DeviceCollection::retrigger_hotplug"); + for (id, dev) in &self.devices { + observer.borrow_mut().on_device_added(dev); + } + } +} diff --git a/rust/modules/device_manager/sys/src/device_manager.rs b/rust/modules/device_manager/sys/src/device_manager.rs new file mode 100644 index 0000000000000000000000000000000000000000..8a195f95f4e24e2455b4dd847cdc5bd32f965d4c --- /dev/null +++ b/rust/modules/device_manager/sys/src/device_manager.rs @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! Implementation of device manager. + +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::cell::RefCell; +use std::ffi::{ CString, c_char }; +use std::rc::Rc; +use hilog_rust::{ hilog, HiLogLabel, LogType }; +use fusion_utils_rust::call_debug_enter; +use crate::interfaces::{ IDeviceManager, IDevice, IDeviceObserver }; +use crate::enumerator::Enumerator; +use crate::monitor::Monitor; +use crate::device_collection::DeviceCollection; + +const LOG_LABEL: HiLogLabel = HiLogLabel { + log_type: LogType::LogCore, + domain: 0xD002220, + tag: "DeviceManager" +}; + +/// TODO: add documentation. +#[derive(Default)] +pub struct DeviceManager { + enumerator: Enumerator, + monitor: Monitor, + devices: Rc>, +} + +impl DeviceManager { + /// TODO: add documentation. + pub fn init(&mut self) { + self.enumerator.set_hotplug_handler(self.devices.clone()); + self.monitor.set_hotplug_handler(self.devices.clone()); + } +} + +impl IDeviceManager for DeviceManager { + fn device(&self, id: i32) -> Option> { + self.devices.borrow().device(id) + } + + fn add_observer(&mut self, observer: &Rc>) { + call_debug_enter!("DeviceManager::add_observer"); + } + + fn remove_observer(&mut self, observer: &Rc>) { + call_debug_enter!("DeviceManager::remove_observer"); + } + + fn retrigger_hotplug(&self, observer: &Rc>) { + call_debug_enter!("DeviceManager::retrigger_hotplug"); + } +} diff --git a/rust/modules/device_manager/sys/src/device_observer.rs b/rust/modules/device_manager/sys/src/device_observer.rs new file mode 100644 index 0000000000000000000000000000000000000000..a4775b5a658446b796ea8736d16736a7e190f101 --- /dev/null +++ b/rust/modules/device_manager/sys/src/device_observer.rs @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! Implementation of device manager. + +#![allow(dead_code)] +#![allow(unused_variables)] + +use fusion_utils_rust::call_debug_enter; +use crate::interfaces::IDeviceObserver; + +/// TODO: add documentation. +#[derive(Default)] +pub struct DeviceObserver { +} + +impl IDeviceObserver for DeviceObserver { + fn on_device_added(&mut self, dev: &Rc) { + call_debug_enter!("DeviceObserver::on_device_added"); + } + + fn on_device_removed(&mut self, dev: &Rc) { + call_debug_enter!("DeviceObserver::on_device_removed"); + } +} diff --git a/rust/modules/device_manager/sys/src/enumerator.rs b/rust/modules/device_manager/sys/src/enumerator.rs new file mode 100644 index 0000000000000000000000000000000000000000..52eaa38cd20437cf602ac3ea6dff8b4fb122f334 --- /dev/null +++ b/rust/modules/device_manager/sys/src/enumerator.rs @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! TODO: add documentation. + +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::cell::RefCell; +use std::ffi::{ CString, c_char }; +use std::fs::DirEntry; +use std::fmt::{ Display, Formatter, Error }; +use std::mem::MaybeUninit; +use std::rc::Rc; +use hilog_rust::{ debug, error, hilog, HiLogLabel, LogType }; +use fusion_data_rust::{ DEV_INPUT_PATH }; +use fusion_utils_rust::{ call_debug_enter, last_error }; +use crate::interfaces::IHotplugHandler; + +const LOG_LABEL: HiLogLabel = HiLogLabel { + log_type: LogType::LogCore, + domain: 0xD002220, + tag: "Enumerator" +}; + +/// TODO: add documentation. +#[derive(Default)] +pub struct Enumerator { + hotplug: Option>>, +} + +impl Enumerator { + pub fn set_hotplug_handler(&mut self, hotplug: Rc>) { + call_debug_enter!("Enumerator::set_hotplug_handler"); + self.hotplug.replace(hotplug); + } + + pub fn scan_devices(&self) { + call_debug_enter!("Enumerator::scan_devices"); + match std::fs::read_dir(DEV_INPUT_PATH) { + Ok(read_dir) => { + for dent in read_dir { + match &dent { + Ok(dir_entry) => { + unsafe { self.check_dir_entry(dir_entry) }; + } + Err(err) => { + error!(LOG_LABEL, "error: {}", err); + } + } + } + } + Err(err) => { + error!(LOG_LABEL, "read_dir fail:{}", last_error()); + } + } + } + + unsafe fn check_dir_entry(&self, dir_entry: &DirEntry) { + call_debug_enter!("Enumerator::check_dir_entry"); + let file_name = dir_entry.file_name(); + let node_name = match file_name.to_str() { + Some(node_name) => { node_name }, + None => { + error!(LOG_LABEL, "Not valid Unicode: {:?}", dir_entry.file_name()); + return; + } + }; + let dev_path = String::from(DEV_INPUT_PATH) + node_name; + let mut state_buf = MaybeUninit::::zeroed(); + + if libc::stat(dev_path.as_ptr() as *const c_char, state_buf.as_mut_ptr()) != 0 { + error!(LOG_LABEL, "stat({}) fail: {}", dev_path, last_error()); + return; + } + if (state_buf.assume_init_ref().st_mode & libc::S_IFMT) != libc::S_IFCHR { + debug!(LOG_LABEL, "Not character device: {}", dev_path); + return; + } + self.add_device(node_name); + } + + fn add_device(&self, node_name: &str) { + } +} + +impl Display for Enumerator { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> + { + writeln!(f, "\nEnumerator {{")?; + writeln!(f, "}}")?; + Ok(()) + } +} diff --git a/rust/modules/device_manager/sys/src/interfaces.rs b/rust/modules/device_manager/sys/src/interfaces.rs new file mode 100644 index 0000000000000000000000000000000000000000..d88dd2974883834a941fce2df1d0bdb3c1feb6ac --- /dev/null +++ b/rust/modules/device_manager/sys/src/interfaces.rs @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! TODO: add documentation. + +#![allow(dead_code)] +#![allow(unused_variables)] + + +use std::rc::Rc; +use std::cell::RefCell; +use fusion_data_rust::FusionResult; + +/// Interface for handling hotplug events. +pub trait IHotplugHandler { + fn add_device(&mut self, node_name: &str); + fn remove_device(&mut self, node_name: &str); +} + +/// TODO: add documentation. +pub enum KeyboardType { + Unknown, + AlphabeticKeyboard, + DigitalKeyboard, + HandWrittenPen, + RemoteControl, +} + +pub trait IDevice { + fn open(&mut self) -> FusionResult; + fn close(&mut self); + + fn id(&self) -> i32; + fn dev_path(&self) -> &str; + fn sys_path(&self) -> &str; + fn name(&self) -> &str; + fn bus(&self) -> i32; + fn version(&self) -> i32; + fn product(&self) -> i32; + fn vendor(&self) -> i32; + fn phys(&self) -> &str; + fn uniq(&self) -> &str; + fn keyboard_type(&self) -> KeyboardType; + fn is_pointer_device(&mut self) -> bool; + fn is_keyboard(&mut self) -> bool; +} + +/// TODO: add documentation. +pub trait IDeviceObserver { + fn on_device_added(&mut self, dev: &Rc>); + fn on_device_removed(&mut self, dev: &Rc>); +} + +/// Interface of device manager. +pub trait IDeviceManager { + fn device(&self, id: i32) -> Option>>; + fn add_observer(&mut self, observer: &Rc>); + fn remove_observer(&mut self, observer: &Rc>); + fn retrigger_hotplug(&self, observer: &Rc>); +} diff --git a/rust/modules/device_manager/sys/src/lib.rs b/rust/modules/device_manager/sys/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..69ba49e3accfb1ccb1c7add33f2f1141fa211d77 --- /dev/null +++ b/rust/modules/device_manager/sys/src/lib.rs @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! TODO: add documentation. + +#![feature(rustc_private)] + +extern crate libc; +extern crate regex; + +mod device; +mod device_collection; +mod device_manager; +mod enumerator; +mod interfaces; +mod linux_input; +mod monitor; + +pub use device_manager::DeviceManager; +pub use monitor::Monitor; diff --git a/rust/modules/device_manager/sys/src/linux_input.rs b/rust/modules/device_manager/sys/src/linux_input.rs new file mode 100644 index 0000000000000000000000000000000000000000..6d16c9506e9374b351946b3ff556fa783f0c2ea3 --- /dev/null +++ b/rust/modules/device_manager/sys/src/linux_input.rs @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! TODO: add documentation. + +#![allow(dead_code)] + +const _IOC_READ: usize = 2usize; +const _IOC_NRBITS: usize = 8usize; +const _IOC_TYPEBITS: usize = 8usize; +const _IOC_SIZEBITS: usize = 14usize; +const _IOC_DIRBITS: usize = 2usize; +const _IOC_NRSHIFT: usize = 0usize; +const _IOC_TYPESHIFT: usize = _IOC_NRSHIFT + _IOC_NRBITS; +const _IOC_SIZESHIFT: usize = _IOC_TYPESHIFT + _IOC_TYPEBITS; +const _IOC_DIRSHIFT: usize = _IOC_SIZESHIFT + _IOC_SIZEBITS; + +const fn _ioc(dir: usize, _type: usize, nr: usize, size: usize) -> usize +{ + (dir << _IOC_DIRSHIFT) | + (_type << _IOC_TYPESHIFT) | + (nr << _IOC_NRSHIFT) | + (size << _IOC_SIZESHIFT) +} + +const fn _ior(a: usize, b: usize, c: usize) -> usize +{ + _ioc(_IOC_READ,a,b,c) +} + +/// get device name +pub const fn eviocgname(len: usize) -> usize +{ + _ioc(_IOC_READ, 'E' as usize, 0x06, len) +} + +/// get physical location +pub const fn eviocgphys(len: usize) -> usize +{ + _ioc(_IOC_READ, 'E' as usize, 0x07, len) +} + +/// get unique identifier +pub const fn eviocguniq(len: usize) -> usize +{ + _ioc(_IOC_READ, 'E' as usize, 0x08, len) +} + +/// get device properties +pub const fn eviocgprop(len: usize) -> usize +{ + _ioc(_IOC_READ, 'E' as usize, 0x09, len) +} + +/// get event bits +pub const fn eviocgbit(ev:usize,len:usize) -> usize +{ + _ioc(_IOC_READ, 'E' as usize, 0x20 + (ev), len) +} + +/// get device ID +pub fn eviocgid() -> usize +{ + _ior('E' as usize, 0x02, std::mem::size_of::()) +} diff --git a/rust/modules/device_manager/sys/src/monitor.rs b/rust/modules/device_manager/sys/src/monitor.rs new file mode 100644 index 0000000000000000000000000000000000000000..e7fbac865b111674b2950b49d6a32e7db2f87493 --- /dev/null +++ b/rust/modules/device_manager/sys/src/monitor.rs @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! TODO: add documentation. + +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::cell::RefCell; +use std::default::Default; +use std::ffi::{ c_int, c_char, c_void, CStr, CString }; +use std::fmt::{ Display, Formatter, Error }; +use std::rc::Rc; +use hilog_rust::{ debug, error, hilog, warn, HiLogLabel, LogType }; +use fusion_data_rust::{ FusionResult, DEV_INPUT_PATH }; +use fusion_utils_rust::{ call_debug_enter, last_error }; +use crate::interfaces::IHotplugHandler; + +const BUFSIZE: usize = std::mem::size_of::() + libc::FILENAME_MAX as usize + 1; +const MAX_EVENTS_NUM: usize = 20; +const LOG_LABEL: HiLogLabel = HiLogLabel { + log_type: LogType::LogCore, + domain: 0xD002220, + tag: "Monitor" +}; + +/// TODO: add documentation. +#[derive(Default)] +pub struct Monitor { + /// TODO: add documentation. + pub inotify_fd: c_int, + dev_input_wd: c_int, + hotplug: Option>>, +} + +impl Monitor { + /// get refenrance from pointer + /// # Safety + /// object pointer is valid + pub unsafe fn as_ref<'a>(object: *const Self) -> Option<&'a Self>{ + object.as_ref() + } + + /// get mut refenrance from pointer + /// # Safety + /// object pointer is valid + pub unsafe fn as_mut<'a>(object: *mut Self) -> Option<&'a mut Self>{ + object.as_mut() + } + + /// TODO: add documentation. + pub fn new() -> Self { + Self { + inotify_fd: -1, + dev_input_wd: -1, + hotplug: None, + } + } + + /// TODO: add documentation. + pub fn set_hotplug_handler(&mut self, hotplug: Rc>) { + call_debug_enter!("Monitor::set_hotplug_handler"); + self.hotplug.replace(hotplug); + } + + /// TODO: add documentation. + pub fn enable(&mut self) -> FusionResult<()> { + call_debug_enter!("Monitor::enable"); + self.open_connection()?; + match self.enable_receiving() { + Ok(_) => { + Ok(()) + } + Err(err) => { + self.disable(); + Err(err) + } + } + } + + /// TODO: add documentation. + pub fn disable(&mut self) { + call_debug_enter!("Monitor::disable"); + if self.dev_input_wd != -1 { + let ret = unsafe { + libc::inotify_rm_watch(self.inotify_fd, self.dev_input_wd) + }; + if ret != 0 { + error!(LOG_LABEL, "inotify_rm_watch({}, {}) fail", self.inotify_fd, self.dev_input_wd); + } + self.dev_input_wd = -1; + } + if self.inotify_fd != -1 { + unsafe { + libc::close(self.inotify_fd); + } + self.inotify_fd = -1; + } + } + + fn open_connection(&mut self) -> FusionResult<()> { + call_debug_enter!("Monitor::open_connection"); + if self.inotify_fd != -1 { + debug!(LOG_LABEL, "Inotify has been initialized"); + return Ok(()); + } + self.inotify_fd = unsafe { + libc::inotify_init1(libc::IN_NONBLOCK | libc::IN_CLOEXEC) + }; + if self.inotify_fd != -1 { + Ok(()) + } else { + error!(LOG_LABEL, "inotify_init1 fail"); + Err(-1) + } + } + + fn enable_receiving(&mut self) -> FusionResult<()> { + call_debug_enter!("Monitor::enable_receiving"); + let input_path = CString::new(DEV_INPUT_PATH).unwrap(); + self.dev_input_wd = unsafe { + libc::inotify_add_watch(self.inotify_fd, input_path.as_ptr(), libc::IN_CREATE | libc::IN_DELETE) + }; + if self.dev_input_wd != -1 { + Ok(()) + } else { + error!(LOG_LABEL, "Fail to watch \'{}\', inotify_fd:{}", DEV_INPUT_PATH, self.inotify_fd); + Err(-1) + } + } + + unsafe fn receive_device(&self) -> FusionResult { + call_debug_enter!("Monitor::receive_device"); + let mut buf: [c_char; BUFSIZE] = [0; BUFSIZE]; + let num_read = libc::read(self.inotify_fd, buf.as_mut_ptr() as *mut c_void, std::mem::size_of_val(&buf)); + if num_read < 0 { + error!(LOG_LABEL, "read() fail, inotify_fd:{}, error:{}", self.inotify_fd, last_error()); + return Err(-1); + } + if num_read == 0 { + warn!(LOG_LABEL, "End of file, inotify_fd:{}", self.inotify_fd); + return Err(-1); + } + debug!(LOG_LABEL, "Read {} bytes from inotify events", num_read); + let mut p = buf.as_ptr(); + + while p < buf.as_ptr().add(num_read as usize) { + let pe = p.cast::(); + self.handle_inotify_event(pe); + let event = pe.as_ref().unwrap(); + p = p.add(std::mem::size_of::() + event.len as usize); + } + Ok(0) + } + + unsafe fn handle_inotify_event(&self, pe: *const libc::inotify_event) { + let event = pe.as_ref().unwrap(); + if event.len == 0 { + warn!(LOG_LABEL, "name is none"); + return; + } + let pname = pe.add(1) as *const c_char; + let node_name = match CStr::from_ptr(pname).to_str() { + Ok(node_name) => { node_name }, + Err(err) => { + error!(LOG_LABEL, "error: {:?}", @public(err)); + return; + } + }; + + if (event.mask & libc::IN_CREATE) == libc::IN_CREATE { + self.add_device(node_name); + } else if (event.mask & libc::IN_DELETE) == libc::IN_DELETE { + self.remove_device(node_name); + } + } + + fn add_device(&self, node_name: &str) { + call_debug_enter!("Monitor::add_device"); + match &self.hotplug { + Some(hotplug) => { + hotplug.borrow_mut().add_device(node_name); + } + None => { + debug!(LOG_LABEL, "Device manager is none"); + } + } + } + + fn remove_device(&self, node_name: &str) { + call_debug_enter!("Monitor::remove_device"); + match &self.hotplug { + Some(hotplug) => { + hotplug.borrow_mut().remove_device(node_name); + } + None => { + debug!(LOG_LABEL, "Device manager is none"); + } + } + } +} + +impl Display for Monitor { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> + { + writeln!(f, "\nMonitor {{")?; + writeln!(f, " inotify_fd: {},", self.inotify_fd)?; + writeln!(f, " dev_input_wd: {},", self.dev_input_wd)?; + writeln!(f, "}}")?; + Ok(()) + } +}