Add drive device (#555)

* Add drive device

* Add TODO comments

* Add specific information to device files

* Handle invalid drive

* Create device file for each possible drive

* Change error messages on read failures

* Replace From with TryFrom for Device

* Refactor Device::try_from

* Add TODO comment

* Handle drive end of file

* Fix merge issue
This commit is contained in:
Vincent Ollivier 2024-03-11 12:29:25 +01:00 committed by GitHub
parent 10d9ba5833
commit 6f1de4edf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 170 additions and 49 deletions

View File

@ -98,10 +98,10 @@ pub fn open_device(path: &str) -> Option<usize> {
syscall::open(path, flags)
}
pub fn create_device(path: &str, kind: DeviceType) -> Option<usize> {
pub fn create_device(path: &str, buf: &[u8]) -> Option<usize> {
let flags = OpenFlag::Create as usize | OpenFlag::Device as usize;
if let Some(handle) = syscall::open(path, flags) {
syscall::write(handle, &kind.buf());
syscall::write(handle, buf);
return Some(handle);
}
None

View File

@ -1,4 +1,6 @@
use crate::sys;
use crate::api::fs::{FileIO, IO};
use alloc::string::String;
use alloc::vec::Vec;
use bit_field::BitField;
@ -316,33 +318,40 @@ pub fn init() {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Drive {
pub bus: u8,
pub dsk: u8,
blocks: u32,
model: String,
serial: String,
block_count: u32,
block_index: u32,
}
impl Drive {
pub fn size() -> usize {
BLOCK_SIZE
}
pub fn open(bus: u8, dsk: u8) -> Option<Self> {
let mut buses = BUSES.lock();
let res = buses[bus as usize].identify_drive(dsk);
if let Ok(IdentifyResponse::Ata(res)) = res {
let buf = res.map(u16::to_be_bytes).concat();
let serial = String::from_utf8_lossy(&buf[20..40]).trim().into();
let model = String::from_utf8_lossy(&buf[54..94]).trim().into();
let blocks = u32::from_be_bytes(buf[120..124].try_into().unwrap()).
rotate_left(16);
let serial = String::from_utf8_lossy(&buf[20..40]).trim().into();
let block_count = u32::from_be_bytes(
buf[120..124].try_into().unwrap()
).rotate_left(16);
let block_index = 0;
Some(Self {
bus,
dsk,
model,
serial,
blocks,
block_count,
block_index,
})
} else {
None
@ -354,7 +363,7 @@ impl Drive {
}
pub fn block_count(&self) -> u32 {
self.blocks
self.block_count
}
fn humanized_size(&self) -> (usize, String) {
@ -369,6 +378,34 @@ impl Drive {
}
}
impl FileIO for Drive {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
if self.block_index == self.block_count {
return Ok(0);
}
let mut buses = BUSES.lock();
let _ = buses[self.bus as usize].read(self.dsk, self.block_index, buf);
let n = buf.len();
self.block_index += 1;
Ok(n)
}
fn write(&mut self, _buf: &[u8]) -> Result<usize, ()> {
unimplemented!();
}
fn close(&mut self) {
}
fn poll(&mut self, event: IO) -> bool {
match event {
IO::Read => true,
IO::Write => false,
}
}
}
impl fmt::Display for Drive {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (size, unit) = self.humanized_size();

View File

@ -3,6 +3,7 @@ use super::dir::Dir;
use super::file::File;
use super::{dirname, filename, realpath, FileIO, IO};
use crate::sys::ata::Drive;
use crate::sys::clock::{Realtime, Uptime};
use crate::sys::cmos::RTC;
use crate::sys::console::Console;
@ -12,6 +13,8 @@ use crate::sys::random::Random;
use alloc::vec;
use alloc::vec::Vec;
use core::convert::TryFrom;
use core::convert::TryInto;
#[derive(PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
@ -25,10 +28,33 @@ pub enum DeviceType {
RTC = 6,
TcpSocket = 7,
UdpSocket = 8,
Drive = 9,
}
impl TryFrom<&[u8]> for DeviceType {
type Error = ();
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
match buf.get(0) {
Some(i) if *i == DeviceType::Null as u8 => Ok(DeviceType::Null),
Some(i) if *i == DeviceType::File as u8 => Ok(DeviceType::File),
Some(i) if *i == DeviceType::Console as u8 => Ok(DeviceType::Console),
Some(i) if *i == DeviceType::Random as u8 => Ok(DeviceType::Random),
Some(i) if *i == DeviceType::Uptime as u8 => Ok(DeviceType::Uptime),
Some(i) if *i == DeviceType::Realtime as u8 => Ok(DeviceType::Realtime),
Some(i) if *i == DeviceType::RTC as u8 => Ok(DeviceType::RTC),
Some(i) if *i == DeviceType::TcpSocket as u8 => Ok(DeviceType::TcpSocket),
Some(i) if *i == DeviceType::UdpSocket as u8 => Ok(DeviceType::UdpSocket),
Some(i) if *i == DeviceType::Drive as u8 => Ok(DeviceType::Drive),
_ => Err(()),
}
}
}
// Used when creating a device
impl DeviceType {
// Return a buffer for the file representing the device in the filesystem.
// The first byte is the device type. The remaining bytes can be used to
// store specific device informations.
pub fn buf(self) -> Vec<u8> {
let len = match self {
DeviceType::RTC => RTC::size(),
@ -37,10 +63,11 @@ impl DeviceType {
DeviceType::Console => Console::size(),
DeviceType::TcpSocket => TcpSocket::size(),
DeviceType::UdpSocket => UdpSocket::size(),
_ => 1,
DeviceType::Drive => Drive::size(),
_ => 1,
};
let mut res = vec![0; len];
res[0] = self as u8;
res[0] = self as u8; // Device type
res
}
}
@ -56,39 +83,51 @@ pub enum Device {
RTC(RTC),
TcpSocket(TcpSocket),
UdpSocket(UdpSocket),
Drive(Drive),
}
impl From<u8> for Device {
fn from(i: u8) -> Self {
match i {
i if i == DeviceType::Null as u8 => {
Device::Null
impl TryFrom<&[u8]> for Device {
type Error = ();
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
match buf.try_into() {
Ok(DeviceType::Null) => {
Ok(Device::Null)
}
i if i == DeviceType::File as u8 => {
Device::File(File::new())
Ok(DeviceType::File) => {
Ok(Device::File(File::new()))
}
i if i == DeviceType::Console as u8 => {
Device::Console(Console::new())
Ok(DeviceType::Console) => {
Ok(Device::Console(Console::new()))
}
i if i == DeviceType::Random as u8 => {
Device::Random(Random::new())
Ok(DeviceType::Random) => {
Ok(Device::Random(Random::new()))
}
i if i == DeviceType::Uptime as u8 => {
Device::Uptime(Uptime::new())
Ok(DeviceType::Uptime) => {
Ok(Device::Uptime(Uptime::new()))
}
i if i == DeviceType::Realtime as u8 => {
Device::Realtime(Realtime::new())
Ok(DeviceType::Realtime) => {
Ok(Device::Realtime(Realtime::new()))
}
i if i == DeviceType::RTC as u8 => {
Device::RTC(RTC::new())
Ok(DeviceType::RTC) => {
Ok(Device::RTC(RTC::new()))
}
i if i == DeviceType::TcpSocket as u8 => {
Device::TcpSocket(TcpSocket::new())
Ok(DeviceType::TcpSocket) => {
Ok(Device::TcpSocket(TcpSocket::new()))
}
i if i == DeviceType::UdpSocket as u8 => {
Device::UdpSocket(UdpSocket::new())
Ok(DeviceType::UdpSocket) => {
Ok(Device::UdpSocket(UdpSocket::new()))
}
_ => unimplemented!(),
Ok(DeviceType::Drive) if buf.len() > 2 => {
let bus = buf[1];
let dsk = buf[2];
if let Some(drive) = Drive::open(bus, dsk) {
Ok(Device::Drive(drive))
} else {
Err(())
}
}
_ => Err(()),
}
}
}
@ -115,7 +154,7 @@ impl Device {
if dir_entry.is_device() {
let block = LinkedBlock::read(dir_entry.addr());
let data = block.data();
return Some(data[0].into());
return data.try_into().ok();
}
}
}
@ -137,6 +176,7 @@ impl FileIO for Device {
Device::RTC(io) => io.read(buf),
Device::TcpSocket(io) => io.read(buf),
Device::UdpSocket(io) => io.read(buf),
Device::Drive(io) => io.read(buf),
}
}
@ -151,6 +191,7 @@ impl FileIO for Device {
Device::RTC(io) => io.write(buf),
Device::TcpSocket(io) => io.write(buf),
Device::UdpSocket(io) => io.write(buf),
Device::Drive(io) => io.write(buf),
}
}
@ -165,6 +206,7 @@ impl FileIO for Device {
Device::RTC(io) => io.close(),
Device::TcpSocket(io) => io.close(),
Device::UdpSocket(io) => io.close(),
Device::Drive(io) => io.close(),
}
}
@ -179,6 +221,7 @@ impl FileIO for Device {
Device::RTC(io) => io.poll(event),
Device::TcpSocket(io) => io.poll(event),
Device::UdpSocket(io) => io.poll(event),
Device::Drive(io) => io.poll(event),
}
}
}

View File

@ -29,7 +29,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
Err(ExitCode::Failure)
}
} else {
error!("Could not find file '{}'", source);
error!("Could not read file '{}'", source);
Err(ExitCode::Failure)
}
}

View File

@ -46,7 +46,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
Err(ExitCode::Failure)
}
} else {
error!("Could not find file '{}'", pathname);
error!("Could not read file '{}'", pathname);
Err(ExitCode::Failure)
}
}

View File

@ -22,7 +22,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
print_hex(&buf);
Ok(())
} else {
error!("Could not find file '{}'", pathname);
error!("Could not read file '{}'", pathname);
Err(ExitCode::Failure)
}
}

View File

@ -33,6 +33,13 @@ pub fn copy_files(verbose: bool) {
);
copy_file("/bin/sleep", include_bytes!("../../dsk/bin/sleep"), verbose);
create_dir("/dev/ata", verbose); // Drives
create_dir("/dev/ata/0", verbose);
create_dev("/dev/ata/0/0", DeviceType::Drive, verbose);
create_dev("/dev/ata/0/1", DeviceType::Drive, verbose);
create_dir("/dev/ata/1", verbose);
create_dev("/dev/ata/1/0", DeviceType::Drive, verbose);
create_dev("/dev/ata/1/1", DeviceType::Drive, verbose);
create_dir("/dev/clk", verbose); // Clock
create_dev("/dev/clk/uptime", DeviceType::Uptime, verbose);
create_dev("/dev/clk/realtime", DeviceType::Realtime, verbose);
@ -336,7 +343,16 @@ fn create_dir(pathname: &str, verbose: bool) {
fn create_dev(pathname: &str, dev: DeviceType, verbose: bool) {
if syscall::info(pathname).is_none() {
if let Some(handle) = fs::create_device(pathname, dev) {
let mut buf = dev.buf();
// NOTE: The first byte of `buf` contains the device type
match pathname {
"/dev/ata/0/0" => { buf[1] = 0; buf[2] = 0 },
"/dev/ata/0/1" => { buf[1] = 0; buf[2] = 1 },
"/dev/ata/1/0" => { buf[1] = 1; buf[2] = 0 },
"/dev/ata/1/1" => { buf[1] = 1; buf[2] = 1 },
_ => {},
}
if let Some(handle) = fs::create_device(pathname, &buf) {
syscall::close(handle);
if verbose {
println!("Created '{}'", pathname);

View File

@ -176,7 +176,7 @@ fn eval_load_args(
ensure_length_eq!(args, 1);
let path = string(&eval(&args[0], env)?)?;
let mut input = fs::read_to_string(&path).
or(could_not!("find file '{}'", path))?;
or(could_not!("read file '{}'", path))?;
loop {
let (rest, _) = parse_eval(&input, env)?;
if rest.is_empty() {

View File

@ -356,7 +356,27 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
if args[1] == "-h" || args[1] == "--help" {
return help();
}
exec(env, args[1])
let path = args[1];
if let Ok(mut input) = fs::read_to_string(path) {
loop {
match parse_eval(&input, env) {
Ok((rest, _)) => {
if rest.is_empty() {
break;
}
input = rest;
}
Err(Err::Reason(msg)) => {
error!("{}", msg);
return Err(ExitCode::Failure);
}
}
}
Ok(())
} else {
error!("Could not read file '{}'", path);
Err(ExitCode::Failure)
}
}
}

View File

@ -85,10 +85,15 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
} else if info.is_dir() {
usr::list::main(args)
} else if info.is_device() {
// TODO: Improve device file usage
let is_char_device = info.size() == 4;
let is_float_device = info.size() == 8;
let is_eof_device = info.size() > 8;
// TODO: Add a way to read the device file to get its type directly
// instead of relying on the various device file sizes. We could
// maybe allow `sys::fs::file::File::open()` to open a Device file
// as a regular file and read the type in the first byte of the
// file.
let n = info.size();
let is_char_device = n == 4;
let is_float_device = n == 8;
let is_block_device = n > 8;
loop {
if console::end_of_text() || console::end_of_transmission() {
println!();
@ -117,7 +122,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
for b in bytes {
print!("{}", b as char);
}
if is_eof_device {
if is_block_device {
println!();
return Ok(());
}

View File

@ -681,7 +681,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
Ok(())
} else {
error!("Could not find file '{}'", path);
error!("Could not read file '{}'", path);
Err(ExitCode::Failure)
}
}
@ -723,7 +723,7 @@ fn test_shell() {
// Redirect standard error explicitely
exec("hex /nope 2=> /tmp/test3").ok();
assert!(api::fs::read_to_string("/tmp/test3").unwrap().
contains("Could not find file '/nope'"));
contains("Could not read file '/nope'"));
let mut config = Config::new();
exec_with_config("set b 42", &mut config).ok();