use std::sync::{Arc, Mutex}; use redox_scheme::scheme::SchemeSync; use redox_scheme::{CallerCtx, OpenResult}; use syscall::error::{Error, Result, EBADF, EINVAL}; use syscall::flag::{O_RDWR, O_STAT}; use syscall::schemev2::NewFdFlags; use xhcid_interface::XhciEndpHandle; use crate::SerialState; /// Scheme fd number for the read-only "state" file (serial line state). const STATE_FD: usize = 2; pub struct AcmScheme { bulk_in: Mutex, bulk_out: Mutex, serial_state: Arc>, open_count: Mutex, } impl AcmScheme { pub fn new(bulk_in: XhciEndpHandle, bulk_out: XhciEndpHandle, serial_state: Arc>) -> Self { Self { bulk_in: Mutex::new(bulk_in), bulk_out: Mutex::new(bulk_out), serial_state, open_count: Mutex::new(0), } } } impl SchemeSync for AcmScheme { fn openat(&mut self, fd: usize, path: &str, _flags: usize, _fcntl_flags: u32, _ctx: &CallerCtx) -> Result { if fd != 1 { return Err(Error::new(EBADF)); } if path == "state" { return Ok(OpenResult::ThisScheme { number: STATE_FD, flags: NewFdFlags::from_bits_truncate(O_STAT as u8) }); } if path.is_empty() || path == "." || path == "/" { *self.open_count.lock().unwrap_or_else(|e| e.into_inner()) += 1; return Ok(OpenResult::ThisScheme { number: 1, flags: NewFdFlags::from_bits_truncate(O_STAT as u8) }); } let mut count = self.open_count.lock().unwrap_or_else(|e| e.into_inner()); let next_id = 3 + *count; *count += 1; Ok(OpenResult::OtherScheme { fd: next_id }) } fn read(&mut self, id: usize, buf: &mut [u8], _offset: u64, _fcntl_flags: u32, _ctx: &CallerCtx) -> Result { if buf.is_empty() { return Err(Error::new(EBADF)); } if id == STATE_FD { let state = self.serial_state.lock().unwrap_or_else(|e| e.into_inner()); let text = state.to_display(); let bytes = text.as_bytes(); let n = bytes.len().min(buf.len()); buf[..n].copy_from_slice(&bytes[..n]); return Ok(n); } if id < 3 { return Err(Error::new(EBADF)); } let mut bulk_in = self.bulk_in.lock().unwrap_or_else(|e| e.into_inner()); match bulk_in.transfer_read(buf) { Ok(status) if status.bytes_transferred > 0 => Ok(status.bytes_transferred as usize), Ok(_) => Ok(0), Err(e) => { log::warn!("ACM scheme: read: {}", e); Err(Error::new(EINVAL)) } } } fn write(&mut self, id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32, _ctx: &CallerCtx) -> Result { if id < 3 || buf.is_empty() { return Err(Error::new(EBADF)); } let mut bulk_out = self.bulk_out.lock().unwrap_or_else(|e| e.into_inner()); match bulk_out.transfer_write(buf) { Ok(status) if status.bytes_transferred > 0 => Ok(status.bytes_transferred as usize), Ok(_) => Ok(0), Err(e) => { log::warn!("ACM scheme: write: {}", e); Err(Error::new(EINVAL)) } } } fn fsync(&mut self, _id: usize, _ctx: &CallerCtx) -> Result<()> { Ok(()) } }