starry-kernel 0.5.10

A Linux-compatible OS kernel built on ArceOS unikernel
Documentation
use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};
use core::{any::Any, task::Context};

use ax_errno::AxError;
use axfs_ng_vfs::{NodeFlags, NodeType, VfsResult};
use axpoll::{IoEvents, Pollable};
use starry_vm::VmMutPtr;

use super::{
    descriptor::{
        USBDEVFS_CAP_BULK_CONTINUATION, USBDEVFS_CONNECTINFO, USBDEVFS_CONTROL,
        USBDEVFS_GET_CAPABILITIES, UsbdevfsConnectInfo, bus_name, device_name,
        parse_numeric_component, usb_device_id,
    },
    manager::UsbFsManager,
};
use crate::pseudofs::{Device, DeviceOps, NodeOpsMux, SimpleDir, SimpleDirOps, SimpleFs};

pub(super) struct UsbRootDir {
    pub(super) fs: Arc<SimpleFs>,
    pub(super) manager: Arc<UsbFsManager>,
}

impl SimpleDirOps for UsbRootDir {
    fn is_cacheable(&self) -> bool {
        false
    }

    fn child_names<'a>(&'a self) -> Box<dyn Iterator<Item = Cow<'a, str>> + 'a> {
        let mut names = self
            .manager
            .bus_numbers()
            .into_iter()
            .map(bus_name)
            .collect::<Vec<_>>();
        names.sort();
        Box::new(names.into_iter().map(Cow::Owned))
    }

    fn lookup_child(&self, name: &str) -> VfsResult<NodeOpsMux> {
        let Some(bus_num) = parse_numeric_component(name) else {
            return Err(AxError::NotFound);
        };
        if !self.manager.bus_numbers().contains(&bus_num) {
            return Err(AxError::NotFound);
        }

        let fs = self.fs.clone();
        let manager = self.manager.clone();
        Ok(NodeOpsMux::Dir(SimpleDir::new_maker(
            fs.clone(),
            Arc::new(UsbBusDir {
                fs,
                manager,
                bus_num,
            }),
        )))
    }
}

struct UsbBusDir {
    fs: Arc<SimpleFs>,
    manager: Arc<UsbFsManager>,
    bus_num: u8,
}

impl SimpleDirOps for UsbBusDir {
    fn is_cacheable(&self) -> bool {
        false
    }

    fn child_names<'a>(&'a self) -> Box<dyn Iterator<Item = Cow<'a, str>> + 'a> {
        let mut names = self
            .manager
            .device_numbers(self.bus_num)
            .into_iter()
            .map(device_name)
            .collect::<Vec<_>>();
        names.sort();
        Box::new(names.into_iter().map(Cow::Owned))
    }

    fn lookup_child(&self, name: &str) -> VfsResult<NodeOpsMux> {
        let Some(device_num) = parse_numeric_component(name) else {
            return Err(AxError::NotFound);
        };
        if self
            .manager
            .device_snapshot(self.bus_num, device_num)
            .is_none()
        {
            return Err(AxError::NotFound);
        }

        Ok(NodeOpsMux::File(Device::new(
            self.fs.clone(),
            NodeType::CharacterDevice,
            usb_device_id(self.bus_num, device_num),
            Arc::new(UsbDeviceOps {
                manager: self.manager.clone(),
                bus_num: self.bus_num,
                device_num,
            }),
        )))
    }
}

pub(super) struct UsbDeviceOps {
    pub(super) manager: Arc<UsbFsManager>,
    pub(super) bus_num: u8,
    pub(super) device_num: u8,
}

impl DeviceOps for UsbDeviceOps {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> VfsResult<usize> {
        let snapshot = self
            .manager
            .device_snapshot(self.bus_num, self.device_num)
            .ok_or(AxError::NotFound)?;
        let offset = offset as usize;
        if offset >= snapshot.descriptor_blob.len() {
            return Ok(0);
        }
        let data = &snapshot.descriptor_blob[offset..];
        let len = data.len().min(buf.len());
        buf[..len].copy_from_slice(&data[..len]);
        Ok(len)
    }

    fn write_at(&self, _buf: &[u8], _offset: u64) -> VfsResult<usize> {
        Err(AxError::InvalidInput)
    }

    fn ioctl(&self, cmd: u32, arg: usize) -> VfsResult<usize> {
        let snapshot = self
            .manager
            .device_snapshot(self.bus_num, self.device_num)
            .ok_or(AxError::NotFound)?;
        match cmd {
            USBDEVFS_CONNECTINFO => {
                (arg as *mut UsbdevfsConnectInfo).vm_write(UsbdevfsConnectInfo {
                    devnum: snapshot.device_num as u32,
                    slow: 0,
                    _padding: [0; 3],
                })?;
                Ok(0)
            }
            USBDEVFS_GET_CAPABILITIES => {
                (arg as *mut u32).vm_write(USBDEVFS_CAP_BULK_CONTINUATION)?;
                Ok(0)
            }
            USBDEVFS_CONTROL => {
                self.manager
                    .snapshot_device_ioctl(self.bus_num, self.device_num, cmd, arg)
            }
            _ => Err(AxError::Unsupported),
        }
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn flags(&self) -> NodeFlags {
        NodeFlags::NON_CACHEABLE | NodeFlags::STREAM
    }

    fn as_pollable(&self) -> Option<&dyn Pollable> {
        Some(self)
    }
}

impl Pollable for UsbDeviceOps {
    fn poll(&self) -> IoEvents {
        IoEvents::IN | IoEvents::OUT
    }

    fn register(&self, _context: &mut Context<'_>, _events: IoEvents) {}
}