1use super::iio_file::{
6 build_battery_power_supply_directory, build_bms_power_supply_directory, build_iio0_directory,
7 build_iio3_directory, build_usb_power_supply_directory,
8};
9use super::qbg_battery_file::create_battery_profile_device;
10use super::qbg_file::create_qbg_device;
11use super::utils::ReadWriteBytesFile;
12use fidl_fuchsia_hardware_qcom_hvdcpopti as fhvdcpopti;
13use starnix_core::device::DeviceMode;
14use starnix_core::device::kobject::{Class, DeviceMetadata};
15use starnix_core::device::service_watcher::{ServiceBindingScope, ServiceWatcher};
16use starnix_core::fs::sysfs::build_device_directory;
17use starnix_core::task::{CurrentTask, Kernel};
18use starnix_core::vfs::NamespaceNode;
19use starnix_logging::log_warn;
20use starnix_uapi::device_id::DeviceId;
21use starnix_uapi::errno;
22use starnix_uapi::errors::Errno;
23use starnix_uapi::file_mode::mode;
24use starnix_uapi::open_flags::OpenFlags;
25use std::sync::Arc;
26
27pub fn hvdcp_opti_init(kernel: &Kernel) {
28 let qbg_class = kernel.device_registry.objects.class_with_dir("qbg".into(), |dir| {
29 dir.entry("qbg_context", ReadWriteBytesFile::new_node(), mode!(IFREG, 0o666));
30 });
31 ServiceWatcher::register_singleton(
32 kernel,
33 fhvdcpopti::ServiceMarker,
34 move |current_task, service_proxy, scope| {
35 if let Err(e) =
36 register_hvdcp_opti_devices(current_task, &qbg_class, service_proxy, scope)
37 {
38 log_warn!("Failed to initialize hvdcp_opti devices: {e:?}");
39 }
40 },
41 );
42}
43
44fn register_hvdcp_opti_devices(
45 current_task: &CurrentTask,
46 qbg_class: &Class,
47 service_proxy: Arc<fhvdcpopti::ServiceProxy>,
48 scope: &mut ServiceBindingScope,
49) -> Result<(), Errno> {
50 let proxy = Arc::new(service_proxy.connect_to_iio_sync().map_err(|e| {
51 log_warn!("Could not connect to hvdcp_opti IIO protocol: {e}");
52 errno!(EINVAL)
53 })?);
54
55 let kernel = current_task.kernel();
56 let registry = &kernel.device_registry;
57 let objects = ®istry.objects;
58 let qbg_battery_class = objects.get_or_create_class("qbg_battery".into());
59 let soc = objects.soc_device();
60 let platform_bus = objects.platform_bus();
61 let spmi_bus = objects.get_or_create_bus("spmi".into());
62 let iio = objects.get_or_create_bus("iio".into());
63 let power_supply = objects.power_supply_class();
64
65 let spmi = registry.add_bus_device(
66 "1c40000.qcom,spmi".into(),
67 Some(soc),
68 platform_bus.clone(),
69 build_device_directory,
70 );
71 let spmi_0 = registry.add_bus_device(
72 "spmi-0".into(),
73 Some(spmi.clone()),
74 spmi_bus.clone(),
75 build_device_directory,
76 );
77 let spmi_0_00 = registry.add_bus_device(
78 "0-00".into(),
79 Some(spmi_0.clone()),
80 spmi_bus,
81 build_device_directory,
82 );
83 let smblite = registry.add_bus_device(
84 "1c40000.qcom,spmi:qcom,pm5100@0:qcom,qpnp-smblite".into(),
85 Some(spmi_0_00.clone()),
86 platform_bus.clone(),
87 build_device_directory,
88 );
89 let qbg = registry.add_bus_device(
90 "1c40000.qcom,spmi:qcom,pm5100@0:qpnp,qbg@4f00".into(),
91 Some(spmi_0_00.clone()),
92 platform_bus,
93 build_device_directory,
94 );
95
96 let iio0 = registry.add_bus_device(
98 "iio:device0".into(),
99 Some(smblite.clone()),
100 iio.clone(),
101 |device, dir| build_iio0_directory(device, &proxy, dir),
102 );
103
104 let iio3 =
105 registry.add_bus_device("iio:device3".into(), Some(qbg.clone()), iio, |device, dir| {
106 build_iio3_directory(device, &proxy, dir)
107 });
108
109 let usb = registry.add_numberless_device(
111 "usb".into(),
112 Some(smblite.clone()),
113 power_supply.clone(),
114 |device, dir| build_usb_power_supply_directory(device, &proxy, dir),
115 );
116
117 let battery = registry.add_numberless_device(
119 "battery".into(),
120 Some(smblite.clone()),
121 power_supply.clone(),
122 |device, dir| build_battery_power_supply_directory(device, &proxy, dir),
123 );
124
125 let bms = registry.add_numberless_device(
127 "bms".into(),
128 Some(qbg.clone()),
129 power_supply,
130 build_bms_power_supply_directory,
131 );
132
133 let sysfs_devices = [spmi, spmi_0, spmi_0_00, smblite, qbg, iio0, iio3, usb, battery, bms];
134
135 let qbg_service = service_proxy.clone();
140 let qbg_dev = registry.add_device(
141 kernel,
142 "qbg".into(),
143 DeviceMetadata::new("qbg".into(), DeviceId::new(484, 0), DeviceMode::Char),
144 qbg_class.clone(),
145 move |_current_task: &CurrentTask,
146 _id: DeviceId,
147 _node: &NamespaceNode,
148 _flags: OpenFlags| create_qbg_device(&qbg_service),
149 );
150
151 let battery_service = service_proxy;
153 let qbg_battery_dev = registry.add_device(
154 kernel,
155 "qbg_battery".into(),
156 DeviceMetadata::new("qbg_battery".into(), DeviceId::new(485, 0), DeviceMode::Char),
157 qbg_battery_class,
158 move |_current_task: &CurrentTask,
159 _id: DeviceId,
160 _node: &NamespaceNode,
161 _flags: OpenFlags| create_battery_profile_device(&battery_service),
162 );
163
164 for dev in sysfs_devices {
165 scope.add_scoped_device(dev);
166 }
167 scope.add_scoped_device(qbg_dev);
168 scope.add_scoped_device(qbg_battery_dev);
169 scope.monitor_channel(proxy);
170 Ok(())
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use fidl::endpoints::{MemberOpener, ServiceProxy as _};
177 use starnix_core::device::kobject::{Device, UEventAction, UEventContext};
178 use starnix_core::testing::spawn_kernel_and_run;
179 use starnix_sync::Mutex;
180
181 struct NoopMemberOpener;
182
183 impl MemberOpener for NoopMemberOpener {
184 fn open_member(&self, _member: &str, _server_end: zx::Channel) -> Result<(), fidl::Error> {
185 Ok(())
186 }
187
188 fn instance_name(&self) -> &str {
189 "default"
190 }
191 }
192
193 #[::fuchsia::test]
194 async fn register_hvdcp_opti_devices_registers_sysfs_before_dev_nodes() {
195 spawn_kernel_and_run(async |current_task| {
196 let kernel = current_task.kernel();
197 let qbg_class = kernel.device_registry.objects.class_with_dir("qbg".into(), |dir| {
198 dir.entry("qbg_context", ReadWriteBytesFile::new_node(), mode!(IFREG, 0o666));
199 });
200 let events = Arc::new(Mutex::new(Vec::new()));
201 let listener_events = events.clone();
202 let key = kernel.device_registry.register_listener(
203 move |action: UEventAction, device: Device, _context: UEventContext| {
204 listener_events.lock().push((action, device.path_from_depth(0)));
205 },
206 );
207
208 let service_proxy =
209 Arc::new(fhvdcpopti::ServiceProxy::from_member_opener(Box::new(NoopMemberOpener)));
210 let mut scope = ServiceBindingScope::default();
211 register_hvdcp_opti_devices(current_task, &qbg_class, service_proxy, &mut scope)
212 .expect("register_hvdcp_opti_devices");
213
214 kernel.device_registry.unregister_listener(&key);
215
216 assert_eq!(
217 std::mem::take(&mut *events.lock()),
218 vec![
219 (UEventAction::Add, "devices/platform/soc/1c40000.qcom,spmi".into()),
220 (UEventAction::Add, "devices/platform/soc/1c40000.qcom,spmi/spmi-0".into()),
221 (
222 UEventAction::Add,
223 "devices/platform/soc/1c40000.qcom,spmi/spmi-0/0-00".into(),
224 ),
225 (
226 UEventAction::Add,
227 "devices/platform/soc/1c40000.qcom,spmi/spmi-0/0-00/1c40000.qcom,spmi:qcom,pm5100@0:qcom,qpnp-smblite".into(),
228 ),
229 (
230 UEventAction::Add,
231 "devices/platform/soc/1c40000.qcom,spmi/spmi-0/0-00/1c40000.qcom,spmi:qcom,pm5100@0:qpnp,qbg@4f00".into(),
232 ),
233 (
234 UEventAction::Add,
235 "devices/platform/soc/1c40000.qcom,spmi/spmi-0/0-00/1c40000.qcom,spmi:qcom,pm5100@0:qcom,qpnp-smblite/iio:device0".into(),
236 ),
237 (
238 UEventAction::Add,
239 "devices/platform/soc/1c40000.qcom,spmi/spmi-0/0-00/1c40000.qcom,spmi:qcom,pm5100@0:qpnp,qbg@4f00/iio:device3".into(),
240 ),
241 (
242 UEventAction::Add,
243 "devices/platform/soc/1c40000.qcom,spmi/spmi-0/0-00/1c40000.qcom,spmi:qcom,pm5100@0:qcom,qpnp-smblite/power_supply/usb".into(),
244 ),
245 (
246 UEventAction::Add,
247 "devices/platform/soc/1c40000.qcom,spmi/spmi-0/0-00/1c40000.qcom,spmi:qcom,pm5100@0:qcom,qpnp-smblite/power_supply/battery".into(),
248 ),
249 (
250 UEventAction::Add,
251 "devices/platform/soc/1c40000.qcom,spmi/spmi-0/0-00/1c40000.qcom,spmi:qcom,pm5100@0:qpnp,qbg@4f00/power_supply/bms".into(),
252 ),
253 (UEventAction::Add, "devices/virtual/qbg/qbg".into()),
254 (UEventAction::Add, "devices/virtual/qbg_battery/qbg_battery".into()),
255 ]
256 );
257 })
258 .await;
259 }
260}