e7fe3183b0
Effectively the only way to recover from errors in the communication with pcid is by restarting the driver from scratch possibly after restarting pcid. As such moving the aborts from individual drivers to pcid_interface simplifies drivers while at the same time allowing nicer error messages.
174 lines
6.0 KiB
Rust
174 lines
6.0 KiB
Rust
#![cfg_attr(target_arch = "aarch64", feature(stdsimd))] // Required for yield instruction
|
|
|
|
extern crate byteorder;
|
|
extern crate syscall;
|
|
|
|
use std::io::{Read, Write};
|
|
use std::os::fd::AsRawFd;
|
|
use std::usize;
|
|
|
|
use event::{EventFlags, RawEventQueue};
|
|
use pcid_interface::PciFunctionHandle;
|
|
use redox_scheme::{RequestKind, Response, SignalBehavior, Socket};
|
|
use syscall::error::{Error, ENODEV};
|
|
|
|
use log::{error, info};
|
|
use syscall::{EAGAIN, EWOULDBLOCK};
|
|
|
|
use crate::scheme::DiskScheme;
|
|
|
|
pub mod ahci;
|
|
pub mod scheme;
|
|
|
|
fn main() {
|
|
redox_daemon::Daemon::new(daemon).expect("ahcid: failed to daemonize");
|
|
}
|
|
|
|
fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
|
let mut pcid_handle = PciFunctionHandle::connect_default();
|
|
let pci_config = pcid_handle.config();
|
|
|
|
let mut name = pci_config.func.name();
|
|
name.push_str("_ahci");
|
|
|
|
let irq = pci_config
|
|
.func
|
|
.legacy_interrupt_line
|
|
.expect("ahcid: no legacy interrupts supported");
|
|
|
|
common::setup_logging(
|
|
"disk",
|
|
"pcie",
|
|
&name,
|
|
log::LevelFilter::Info,
|
|
log::LevelFilter::Info,
|
|
);
|
|
|
|
info!(" + AHCI {}", pci_config.func.display());
|
|
|
|
let address = unsafe { pcid_handle.map_bar(5) }.ptr.as_ptr() as usize;
|
|
{
|
|
let scheme_name = format!("disk.{}", name);
|
|
let socket = Socket::nonblock(&scheme_name).expect("ahcid: failed to create disk scheme");
|
|
|
|
let mut irq_file = irq.irq_handle("ahcid");
|
|
let irq_fd = irq_file.as_raw_fd() as usize;
|
|
|
|
let mut event_queue = RawEventQueue::new().expect("ahcid: failed to create event queue");
|
|
|
|
libredox::call::setrens(0, 0).expect("ahcid: failed to enter null namespace");
|
|
|
|
event_queue
|
|
.subscribe(socket.inner().raw(), 1, EventFlags::READ)
|
|
.expect("ahcid: failed to event scheme socket");
|
|
event_queue
|
|
.subscribe(irq_fd, 1, EventFlags::READ)
|
|
.expect("ahcid: failed to event irq scheme");
|
|
|
|
daemon.ready().expect("ahcid: failed to notify parent");
|
|
|
|
let (hba_mem, disks) = ahci::disks(address as usize, &name);
|
|
let mut scheme = DiskScheme::new(scheme_name, hba_mem, disks);
|
|
|
|
let mut mounted = true;
|
|
let mut todo = Vec::new();
|
|
while mounted {
|
|
let Some(event) = event_queue
|
|
.next()
|
|
.transpose()
|
|
.expect("ahcid: failed to read event file")
|
|
else {
|
|
break;
|
|
};
|
|
if event.fd == socket.inner().raw() {
|
|
loop {
|
|
let sqe = match socket.next_request(SignalBehavior::Interrupt) {
|
|
Ok(None) => {
|
|
mounted = false;
|
|
break;
|
|
}
|
|
Ok(Some(s)) => {
|
|
if let RequestKind::Call(call) = s.kind() {
|
|
call
|
|
} else {
|
|
// TODO: Support e.g. cancellation
|
|
continue;
|
|
}
|
|
}
|
|
Err(err) => {
|
|
if err.errno == EWOULDBLOCK || err.errno == EAGAIN {
|
|
break;
|
|
} else {
|
|
panic!("ahcid: failed to read disk scheme: {}", err);
|
|
}
|
|
}
|
|
};
|
|
|
|
if let Some(response) = sqe.handle_scheme_block(&mut scheme) {
|
|
// TODO: handle full CQE?
|
|
socket
|
|
.write_response(response, SignalBehavior::Restart)
|
|
.expect("ahcid: failed to write disk scheme");
|
|
} else {
|
|
todo.push(sqe);
|
|
}
|
|
}
|
|
} else if event.fd == irq_fd {
|
|
let mut irq = [0; 8];
|
|
if irq_file
|
|
.read(&mut irq)
|
|
.expect("ahcid: failed to read irq file")
|
|
>= irq.len()
|
|
{
|
|
if scheme.irq() {
|
|
irq_file
|
|
.write(&irq)
|
|
.expect("ahcid: failed to write irq file");
|
|
|
|
// Handle todos in order to finish previous packets if possible
|
|
let mut i = 0;
|
|
while i < todo.len() {
|
|
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
|
|
let _sqe = todo.remove(i);
|
|
socket
|
|
.write_response(resp, SignalBehavior::Restart)
|
|
.expect("ahcid: failed to write disk scheme");
|
|
} else {
|
|
i += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
error!("Unknown event {}", event.fd);
|
|
}
|
|
|
|
// Handle todos to start new packets if possible
|
|
let mut i = 0;
|
|
while i < todo.len() {
|
|
if let Some(response) = todo[i].handle_scheme_block(&mut scheme) {
|
|
let _sqe = todo.remove(i);
|
|
socket
|
|
.write_response(response, SignalBehavior::Restart)
|
|
.expect("ahcid: failed to write disk scheme");
|
|
} else {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
if !mounted {
|
|
for sqe in todo.drain(..) {
|
|
socket
|
|
.write_response(
|
|
Response::new(&sqe, Err(Error::new(ENODEV))),
|
|
SignalBehavior::Restart,
|
|
)
|
|
.expect("ahcid: failed to write disk scheme");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::process::exit(0);
|
|
}
|