Skip to main content

starnix_modules_hvdcp_opti/
device.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::iio_file::{
6    build_battery_power_supply_directory, build_iio0_directory, build_iio1_directory,
7    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::DeviceMetadata;
15use starnix_core::device::service_watcher::watch_fidl_service;
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::cell::Cell;
26use std::sync::Arc;
27
28pub fn hvdcp_opti_init(kernel: &Kernel) {
29    let device_added = Cell::new(false);
30    watch_fidl_service(
31        kernel,
32        fhvdcpopti::ServiceMarker,
33        "hvdcp_opti",
34        async move |weak_kernel, service_proxy| {
35            let Some(kernel) = weak_kernel.upgrade() else {
36                return;
37            };
38            if device_added.get() {
39                log_warn!("Ignoring additional hvdcp_opti service instance");
40                return;
41            }
42            match register_hvdcp_opti_devices(&kernel, Arc::new(service_proxy)) {
43                Ok(()) => device_added.set(true),
44                Err(e) => log_warn!("Failed to initialize hvdcp_opti devices: {e:?}"),
45            }
46        },
47    );
48}
49
50fn register_hvdcp_opti_devices(
51    kernel: &Kernel,
52    service_proxy: Arc<fhvdcpopti::ServiceProxy>,
53) -> Result<(), Errno> {
54    let proxy = Arc::new(service_proxy.connect_to_iio_sync().map_err(|e| {
55        log_warn!("Could not connect to hvdcp_opti IIO protocol: {e}");
56        errno!(EINVAL)
57    })?);
58
59    let registry = &kernel.device_registry;
60
61    let qdb_class = registry.objects.class_with_dir("qbg".into(), |dir| {
62        dir.entry("qbg_context", ReadWriteBytesFile::new_node(), mode!(IFREG, 0o666));
63    });
64
65    // /dev/qbg
66    let qbg_service = service_proxy.clone();
67    registry.register_device(
68        kernel,
69        "qbg".into(),
70        DeviceMetadata::new("qbg".into(), DeviceId::new(484, 0), DeviceMode::Char),
71        qdb_class,
72        move |_current_task: &CurrentTask,
73              _id: DeviceId,
74              _node: &NamespaceNode,
75              _flags: OpenFlags| create_qbg_device(&qbg_service),
76    )?;
77
78    // /dev/qbg_battery
79    registry.register_device(
80        kernel,
81        "qbg_battery".into(),
82        DeviceMetadata::new("qbg_battery".into(), DeviceId::new(485, 0), DeviceMode::Char),
83        registry.objects.get_or_create_class("qbg_battery".into()),
84        move |_current_task: &CurrentTask,
85              _id: DeviceId,
86              _node: &NamespaceNode,
87              _flags: OpenFlags| create_battery_profile_device(&service_proxy),
88    )?;
89
90    let soc = registry.objects.soc_device();
91    let platform_bus = registry.objects.platform_bus();
92    let spmi = registry.add_bus_device(
93        "1c40000.qcom,spmi".into(),
94        Some(soc),
95        platform_bus.clone(),
96        build_device_directory,
97    );
98    let spmi_bus = registry.objects.get_or_create_bus("spmi".into());
99    let spmi_0 = registry.add_bus_device(
100        "spmi-0".into(),
101        Some(spmi),
102        spmi_bus.clone(),
103        build_device_directory,
104    );
105    let spmi_0_00 =
106        registry.add_bus_device("0-00".into(), Some(spmi_0), spmi_bus, build_device_directory);
107    let smblite = registry.add_bus_device(
108        "1c40000.qcom,spmi:qcom,pm5100@0:qcom,qpnp-smblite".into(),
109        Some(spmi_0_00.clone()),
110        platform_bus.clone(),
111        build_device_directory,
112    );
113    let qbg = registry.add_bus_device(
114        "1c40000.qcom,spmi:qcom,pm5100@0:qpnp,qbg@4f00".into(),
115        Some(spmi_0_00),
116        platform_bus,
117        build_device_directory,
118    );
119
120    // /sys/bus/iio/devices/iio:device{0,1}
121    let iio = registry.objects.get_or_create_bus("iio".into());
122    registry.add_bus_device("iio:device0".into(), Some(smblite), iio.clone(), |device, dir| {
123        build_iio0_directory(device, &proxy, dir)
124    });
125
126    registry.add_bus_device("iio:device1".into(), Some(qbg), iio, |device, dir| {
127        build_iio1_directory(device, &proxy, dir)
128    });
129
130    let power_supply = registry.objects.get_or_create_class("power_supply".into());
131    // /sys/class/power_supply/usb
132    registry.add_numberless_device(
133        "usb".into(),
134        /* parent = */ None,
135        power_supply.clone(),
136        |device, dir| build_usb_power_supply_directory(device, &proxy, dir),
137    );
138
139    // /sys/class/power_supply/battery
140    registry.add_numberless_device(
141        "battery".into(),
142        /* parent = */ None,
143        power_supply.clone(),
144        |device, dir| build_battery_power_supply_directory(device, &proxy, dir),
145    );
146
147    // /sys/class/power_supply/bms
148    registry.add_numberless_device(
149        "bms".into(),
150        /* parent = */ None,
151        power_supply,
152        build_device_directory,
153    );
154    Ok(())
155}