Skip to main content

starnix_modules_mmcblk_stub/
lib.rs

1// Copyright 2026 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
5#![cfg_attr(test, recursion_limit = "256")]
6
7use starnix_core::device::DeviceMode;
8use starnix_core::device::kobject::DeviceMetadata;
9use starnix_core::fs::sysfs::{
10    BlockDeviceInfo, build_block_device_directory, build_device_directory,
11};
12use starnix_core::task::{CurrentTask, Kernel};
13use starnix_core::vfs::pseudo::stub_empty_file::StubEmptyFile;
14use starnix_core::vfs::{FileOps, FsString, NamespaceNode};
15use starnix_logging::{bug_ref, track_stub};
16use starnix_uapi::device_id::DeviceId;
17use starnix_uapi::errno;
18use starnix_uapi::errors::Errno;
19use starnix_uapi::file_mode::mode;
20use starnix_uapi::open_flags::OpenFlags;
21use std::sync::Arc;
22
23struct MmcBlockDevice;
24
25impl BlockDeviceInfo for MmcBlockDevice {
26    fn size(&self) -> Result<usize, Errno> {
27        track_stub!(TODO("https://fxbug.dev/488067251"), "mmcblk query size");
28        Err(errno!(ENOTSUP))
29    }
30}
31
32/// Initializes stub eMMC host (`mmc0`), card (`mmc0:0001`), and block (`mmcblk0`) devices.
33pub fn mmcblk_stub_device_init(kernel: &Kernel) -> Result<(), Errno> {
34    let registry = &kernel.device_registry;
35    let objects = &registry.objects;
36
37    // TODO(https://fxbug.dev/425942145): Parent `mmc0` under the SoC's SDHCI controller
38    // platform device, once the product-specific controller address is known.
39    let mmc0 = registry.add_numberless_device(
40        "mmc0".into(),
41        /* parent = */ None,
42        objects.get_or_create_class("mmc_host".into()),
43        build_device_directory,
44    );
45    let mmc_card = registry.add_bus_device(
46        "mmc0:0001".into(),
47        Some(mmc0),
48        objects.get_or_create_bus("mmc".into()),
49        |device, dir| {
50            build_device_directory(device, dir);
51            for name in ["fwrev", "hwrev", "life_time", "manfid", "pre_eol_info", "serial"] {
52                dir.entry(
53                    name,
54                    StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
55                    mode!(IFREG, 0o444),
56                );
57            }
58        },
59    );
60
61    let name = FsString::from("mmcblk0");
62    let device = Arc::new(MmcBlockDevice);
63    let device_weak = Arc::downgrade(&device);
64    registry.add_device_with_dir(
65        kernel,
66        name.as_ref(),
67        DeviceMetadata::new(name.clone(), DeviceId::MMCBLK0, DeviceMode::Block),
68        Some(mmc_card),
69        objects.block_class(),
70        |device, dir| build_block_device_directory(device, device_weak, dir),
71        move |_current_task: &CurrentTask,
72              _id: DeviceId,
73              _node: &NamespaceNode,
74              _flags: OpenFlags|
75              -> Result<Box<dyn FileOps>, Errno> {
76            // Retain `Arc<MmcBlockDevice>` for the lifetime of the registered device so the
77            // `Weak<dyn BlockDeviceInfo>` passed to `build_block_device_directory` remains valid.
78            let _keep_alive = &device;
79            track_stub!(TODO("https://fxbug.dev/488067251"), "mmcblk open device");
80            Err(errno!(ENOTSUP))
81        },
82    );
83    Ok(())
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use starnix_core::testing::{anon_test_file, spawn_kernel_and_run};
90    use starnix_core::vfs::VecOutputBuffer;
91
92    #[::fuchsia::test]
93    async fn mmcblk_stub_device_init_registers_host_card_and_block_devices() {
94        spawn_kernel_and_run(async |current_task| {
95            let kernel = current_task.kernel();
96            mmcblk_stub_device_init(kernel).expect("mmcblk_stub_device_init should succeed");
97
98            let root = &kernel.device_registry.objects.root;
99            assert!(root.lookup("class/mmc_host/mmc0".into()).is_some());
100            assert!(root.lookup("bus/mmc/devices/mmc0:0001".into()).is_some());
101            assert!(root.lookup("block/mmcblk0".into()).is_some());
102            assert!(root.lookup("class/block/mmcblk0".into()).is_some());
103            assert!(root.lookup("devices/virtual/mmc_host/mmc0/uevent".into()).is_some());
104            assert!(
105                root.lookup("devices/virtual/mmc_host/mmc0/mmc0:0001/life_time".into()).is_some()
106            );
107            assert!(
108                root.lookup("devices/virtual/mmc_host/mmc0/mmc0:0001/block/mmcblk0/holders".into())
109                    .is_some()
110            );
111
112            let size_node = root
113                .lookup("devices/virtual/mmc_host/mmc0/mmc0:0001/block/mmcblk0/size".into())
114                .expect("size node should exist");
115            let file_ops = size_node.create_file_ops(current_task, OpenFlags::RDONLY).unwrap();
116            let file = anon_test_file(current_task, file_ops, OpenFlags::RDONLY);
117            let mut buf = VecOutputBuffer::new(10);
118            assert_eq!(file.read(current_task, &mut buf).unwrap_err(), errno!(ENOTSUP));
119        })
120        .await;
121    }
122}