Compare commits

...

5 Commits

Author SHA1 Message Date
alnyan 0e979a9e09 spi: initial spi device support 2026-02-04 14:46:57 +02:00
alnyan ed9d7a7145 usb: add ft232 driver 2026-02-03 17:28:15 +02:00
alnyan 6b5dd9f673 irq: more flexible interrupt tables 2026-02-03 13:29:49 +02:00
alnyan 58dbaddf11 i2c: implement initial support for i2c devices 2026-02-03 12:08:15 +02:00
alnyan 218e391505 aarch64: better page fault info 2026-02-03 12:07:52 +02:00
54 changed files with 5221 additions and 186 deletions
Generated
+2
View File
@@ -469,6 +469,7 @@ dependencies = [
name = "device-api"
version = "0.1.0"
dependencies = [
"async-trait",
"device-api-macros",
"yggdrasil-abi",
]
@@ -2676,6 +2677,7 @@ dependencies = [
name = "ygg_driver_bsp_bcm283x"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"device-tree",
+180 -3
View File
@@ -134,23 +134,72 @@
pinctrl-names = "default";
bootph-all;
// UART
uart0_ctsrts_gpio30: uart0_ctsrts_gpio30 {
brcm,pins = <30>, <31>;
brcm,pull = <2>, <0>;
brcm,function = <7>;
};
uart0_gpio32: uart0_gpio32 {
brcm,pins = <32>, <33>;
brcm,pull = <0>, <2>;
brcm,function = <8>;
};
uart1_gpio14: uart1_gpio14 {
brcm,pins = <14>, <15>;
brcm,function = <2>;
bootph-all;
};
uart2_gpio: uart2_pins {
brcm,pins = <0>, <1>;
brcm,function = <3>;
brcm,pull = <0>, <2>;
};
// I²C
i2c0if_gpio0: i2c0if-gpio0 {
brcm,pins = <0>, <1>;
brcm,function = <4>;
};
i2c0if_gpio44: i2c0if-gpio44 {
brcm,pins = <44>, <45>;
brcm,function = <5>;
};
i2c1_gpio: i2c1 {
brcm,pins = <2>, <3>;
brcm,function = <4>;
brcm,pull = <2>;
};
i2c3_gpio: i2c3 {
brcm,pins = <4>, <5>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c4_gpio: i2c4 {
brcm,pins = <8>, <9>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c5_gpio: i2c5 {
brcm,pins = <12>, <13>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c6_gpio: i2c6 {
brcm,pins = <22>, <23>;
brcm,function = <2>;
brcm,pull = <2>;
};
// SPI
spi0_gpio: spi0_pins {
brcm,pins = <9>, <10>, <11>;
brcm,function = <4>;
};
spi0_cs_gpio: spi0_cs_pins {
brcm,pins = <8>, <7>;
brcm,function = <1>;
};
};
uart0: serial@7e201000 {
@@ -163,7 +212,7 @@
"apb_pclk";
arm,primecell-periphid = <0x241011>;
pinctrl-names = "default";
pinctrl-0 = <&uart0_ctsrts_gpio30 &uart0_gpio32>;
pinctrl-0 = <&uart0_ctsrts_gpio30>, <&uart0_gpio32>;
uart-has-rtscts;
status = "okay";
skip-init;
@@ -189,6 +238,132 @@
bootph-all;
};
i2c0if: i2c@7e205000 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205000 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
clock-frequency = <0x186a0>;
};
i2c0mux: i2c0mux {
compatible = "i2c-mux-pinctrl";
#address-cells = <0x01>;
#size-cells = <0x00>;
i2c-parent = <&i2c0if>;
status = "disabled";
pinctrl-names = "i2c0", "i2c_csi_dsi";
pinctrl-0 = <&i2c0if_gpio0>;
pinctrl-1 = <&i2c0if_gpio44>;
i2c0: i2c@0 {
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
};
i2c_csi_dsi: i2c@1 {
reg = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
};
};
i2c1: i2c@7e804000 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_gpio>;
clock-frequency = <0x186a0>;
};
// TODO: memory access crashes on qemu (not implemented?)
i2c3: i2c@7e205600 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205600 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c3_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c4: i2c@7e205800 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205800 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c4_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c5: i2c@7e205a00 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205a00 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c5_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c6: i2c@7e205c00 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205c00 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c6_gpio>;
pinctrl-names = "default";
};
spi0: spi@7e204000 {
compatible = "brcm,bcm2835-spi";
reg = <0x7e204000 0x200>;
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
dmas = <&dma 0x06>,
<&dma 0x07>;
dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&spi0_gpio>, <&spi0_cs_gpio>;
cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
spi0_0: spidev@0 {
compatible = "spidev";
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
spi-max-frequency = <125000000>;
};
spi0_1: spidev@1 {
compatible = "spidev";
reg = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
spi-max-frequency = <125000000>;
};
};
l1_intc: local_intc@40000000 {
compatible = "brcm,bcm2836-l1-intc";
reg = <0x40000000 0x100>;
@@ -263,6 +438,8 @@
clock-names = "uartclk",
"apb_pclk";
arm,primecell-periphid = <0x241011>;
pinctrl-0 = <&uart2_gpio>;
pinctrl-names = "default";
status = "disabled";
};
Binary file not shown.
File diff suppressed because it is too large Load Diff
+30 -7
View File
@@ -22,7 +22,7 @@ use crate::KernelTableManagerImpl;
use super::dc_cvac;
bitflags! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct PageAttributes: u64 {
const PRESENT = 1 << 0;
@@ -71,8 +71,8 @@ pub struct L3;
#[derive(Debug, Clone, Copy)]
pub enum EntryType {
Table(PhysicalAddress),
Page(PhysicalAddress),
Table(PageAttributes, PhysicalAddress),
Page(PageAttributes, PhysicalAddress),
Invalid,
}
@@ -311,9 +311,14 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
if !self.is_present() {
EntryType::Invalid
} else if let Some(table) = self.as_table() {
EntryType::Table(table)
let attributes = self.attributes();
EntryType::Table(attributes, table)
} else {
EntryType::Page(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
let attributes = self.attributes();
EntryType::Page(
attributes,
PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK),
)
}
}
}
@@ -430,8 +435,26 @@ impl From<PageAttributes> for MapAttributes {
impl fmt::Display for EntryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Table(address) => write!(f, "table @ {address:#x}"),
Self::Page(address) => write!(f, "page @ {address:#x}"),
&Self::Table(attrs, address) => {
let mask = match attrs & PageAttributes::AP_ACCESS_MASK {
PageAttributes::AP_BOTH_READONLY => "r- r-",
PageAttributes::AP_BOTH_READWRITE => "rw rw",
PageAttributes::AP_KERNEL_READONLY => "r- --",
PageAttributes::AP_KERNEL_READWRITE => "rw --",
_ => unreachable!(),
};
write!(f, "table @ {address:#010x} {mask}")
}
&Self::Page(attrs, address) => {
let mask = match attrs & PageAttributes::AP_ACCESS_MASK {
PageAttributes::AP_BOTH_READONLY => "r- r-",
PageAttributes::AP_BOTH_READWRITE => "rw rw",
PageAttributes::AP_KERNEL_READONLY => "r- --",
PageAttributes::AP_KERNEL_READWRITE => "rw --",
_ => unreachable!(),
};
write!(f, "page @ {address:#010x} {mask}")
}
Self::Invalid => f.write_str("<invalid>"),
}
}
+1
View File
@@ -16,3 +16,4 @@ tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
async-trait.workspace = true
+93
View File
@@ -0,0 +1,93 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz},
device::{Device, DeviceInitContext},
};
use device_tree::{
DeviceTreePropertyRead, TProp,
driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver},
};
use libk::error::Error;
use crate::mbox::{Bcm2835Mbox, Bcm2835VcClock};
const CLK_VPU: u32 = 0x14;
struct Cprman {
clk_osc: ClockHandle,
mbox: Arc<Bcm2835Mbox>,
}
impl Device for Cprman {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let _ = &self.clk_osc;
Ok(())
}
fn display_name(&self) -> &str {
"bcm2711-cprman"
}
}
impl ClockController for Cprman {
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
let vc_clock = match clock {
Some(CLK_VPU) => Bcm2835VcClock::Core,
Some(n) => todo!("clock_rate({n:#02x})"),
None => return Err(Error::InvalidArgument),
};
let rate = self.mbox.vc_clock_rate(vc_clock)?;
Ok(rate)
}
fn set_clock_rate(&self, clock: Option<u32>, rate: Hertz) -> Result<Hertz, Error> {
let _ = clock;
let _ = rate;
todo!()
}
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
let _ = clock;
todo!()
}
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
let _ = clock;
todo!()
}
}
impl DeviceTreeClockController for Cprman {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
parent: self.clone(),
clock: Some(clock),
},
1,
))
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-cprman"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let _base = node.map_base(context, 0)?;
let mbox = Node::by_compatible("brcm,bcm2835-mbox", true).unwrap().device().unwrap();
let mbox = Arc::downcast::<Bcm2835Mbox>(mbox.as_any()).unwrap();
let clk_osc = node.clock(0)?;
let cprman = Arc::new(Cprman {
clk_osc,
mbox,
});
node.make_clock_controller(cprman.clone());
Some(cprman)
}
}
}
+290
View File
@@ -0,0 +1,290 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
i2c::{I2CAddress, I2CController},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
use yggdrasil_abi::io::device::i2c::I2CMasterInfo;
register_bitfields! {
u32,
C [
I2CEN OFFSET(15) NUMBITS(1) [],
INTR OFFSET(10) NUMBITS(1) [],
INTT OFFSET(9) NUMBITS(1) [],
INTD OFFSET(8) NUMBITS(1) [],
ST OFFSET(7) NUMBITS(1) [],
CLEAR OFFSET(4) NUMBITS(2) [],
READ OFFSET(0) NUMBITS(1) [],
],
S [
CLKT OFFSET(9) NUMBITS(1) [],
ERR OFFSET(8) NUMBITS(1) [],
RXF OFFSET(7) NUMBITS(1) [],
TXE OFFSET(6) NUMBITS(1) [],
RXD OFFSET(5) NUMBITS(1) [],
TXD OFFSET(4) NUMBITS(1) [],
RXR OFFSET(3) NUMBITS(1) [],
TXW OFFSET(2) NUMBITS(1) [],
DONE OFFSET(1) NUMBITS(1) [],
TA OFFSET(0) NUMBITS(1) [],
],
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => C: ReadWrite<u32, C::Register>),
(0x04 => S: ReadWrite<u32, S::Register>),
(0x08 => DLEN: ReadWrite<u32>),
(0x0C => A: ReadWrite<u32>),
(0x10 => FIFO: ReadWrite<u32>),
(0x14 => DIV: ReadWrite<u32>),
(0x18 => DEL: ReadWrite<u32>),
(0x1C => CLKT: ReadWrite<u32>),
(0x20 => @END),
}
}
struct I2C {
name: &'static str,
clock_frequency: Option<Hertz>,
irq: IrqHandle,
clock: Option<ClockHandle>,
regs: IrqSafeRwLock<DeviceMemoryIo<'static, Regs>>,
index: OneTimeInit<u32>,
}
impl Regs {
fn start_transfer(&self, name: &str, buflen: u16, address: I2CAddress, read: bool) {
log::debug!(
"{}: start address={}, read={}, len={}",
name,
address,
read,
buflen
);
let address = address.as_8_bit().unwrap();
let read = if read { C::READ::SET } else { C::READ::CLEAR };
self.S.write(S::ERR::SET + S::DONE::SET);
self.DLEN.set(buflen as u32);
self.C.modify(C::ST::CLEAR + C::I2CEN::CLEAR);
self.A.set(address as u32);
self.C.modify(read + C::ST::SET + C::I2CEN::SET);
}
fn finish_transfer(&self, name: &str) -> Result<(), Error> {
log::debug!("{}: finish transfer", name);
let status = self.S.extract();
self.C.set(0);
if status.matches_all(S::ERR::SET) {
self.S.write(S::DONE::SET + S::ERR::SET);
return Err(Error::HostUnreachable);
}
if status.matches_all(S::DONE::SET) {
self.S.write(S::DONE::SET);
}
Ok(())
}
fn write_byte(&self, byte: u8) -> Result<bool, Error> {
loop {
let status = self.S.extract();
if status.matches_all(S::ERR::SET) {
self.C.write(C::CLEAR.val(1));
self.S.write(S::ERR::SET + S::DONE::SET);
// TODO better code
return Err(Error::HostUnreachable);
}
if status.matches_all(S::DONE::SET) {
self.C.set(0);
self.S.write(S::DONE::SET);
return Ok(false);
}
if status.matches_all(S::TXD::SET) {
self.FIFO.set(byte as u32);
return Ok(true);
}
core::hint::spin_loop();
}
}
fn read_byte(&self) -> Result<Option<u8>, Error> {
loop {
let status = self.S.extract();
if status.matches_all(S::ERR::SET) {
self.C.write(C::CLEAR.val(1));
self.S.write(S::ERR::SET + S::DONE::SET);
// TODO better code
return Err(Error::HostUnreachable);
}
if status.matches_all(S::DONE::SET) {
self.C.set(0);
self.S.write(S::DONE::SET);
return Ok(None);
}
if status.matches_all(S::RXD::SET) {
let val = self.FIFO.get() as u8;
return Ok(Some(val));
}
core::hint::spin_loop();
}
}
}
impl Device for I2C {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// TODO
let _ = &self.clock;
log::info!("{}: initialize", self.name);
let regs = self.regs.write();
regs.C.set(0);
let index = DEVICE_REGISTRY.i2c.register_bus(self.clone())?;
self.index.init(index);
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
// TODO
let _ = &self.irq;
// self.irq.register(self.clone())?;
// self.irq.enable()?;
// let regs = self.regs.write();
// regs.C.modify(C::INTD::SET);
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl InterruptHandler for I2C {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
todo!()
}
}
impl I2CController for I2C {
fn bus_number(&self) -> u32 {
*self.index.get()
}
fn child_number(&self) -> Option<u32> {
None
}
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
if let Some(frequency) = self.clock_frequency {
// Fixed frequency
if speed == frequency {
Ok(speed)
} else {
Err(Error::InvalidArgument)
}
} else {
todo!("Set i2c speed = {speed}")
}
}
fn capabilities(&self) -> I2CMasterInfo {
let max_speed_hz = if let Some(frequency) = self.clock_frequency {
frequency
} else {
todo!()
}
.0 as u32;
I2CMasterInfo {
max_speed_hz,
supports_10bit_addresses: true,
}
}
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
let buflen: u16 = buffer
.len()
.try_into()
.map_err(|_| Error::InvalidArgument)?;
let regs = self.regs.write();
// TODO DMA/interrupts
regs.start_transfer(self.name, buflen, address, false);
let mut bytes_written = 0;
// let mut done = false;
for &byte in buffer {
if regs.write_byte(byte)? {
bytes_written += 1;
} else {
break;
}
}
regs.finish_transfer(self.name)?;
Ok(bytes_written)
}
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
let buflen: u16 = buffer
.len()
.try_into()
.map_err(|_| Error::InvalidArgument)?;
let regs = self.regs.write();
// TODO DMA/interrupts
regs.start_transfer(self.name, buflen, address, true);
let mut bytes_read = 0;
// let mut done = false;
for byte in buffer {
if let Some(val) = regs.read_byte()? {
*byte = val;
bytes_read += 1;
} else {
break;
}
}
regs.finish_transfer(self.name)?;
Ok(bytes_read)
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-i2c", "brcm,bcm2835-i2c"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clock_frequency = node.prop_usize("clock-frequency").map(|p| Hertz(p as u64));
let base = node.map_base(context, 0)?;
let name = node.name().unwrap_or("bcm2835-i2c");
let irq = node.interrupt(0)?;
let clock = node.clock(0);
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let i2c = Arc::new(I2C {
name,
clock_frequency,
irq,
clock,
index: OneTimeInit::new(),
regs: IrqSafeRwLock::new(regs)
});
node.make_i2c_controller(i2c.clone());
Some(i2c)
}
}
}
+3
View File
@@ -4,5 +4,8 @@ extern crate alloc;
mod aux;
// mod aux_uart;
mod cprman;
mod gpio;
mod i2c;
mod mbox;
mod spi;
+46 -9
View File
@@ -1,7 +1,10 @@
use core::{cell::UnsafeCell, mem::MaybeUninit};
use core::{any::Any, cell::UnsafeCell, mem::MaybeUninit};
use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_api::{
clock::Hertz,
device::{Device, DeviceInitContext},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use kernel_arch_aarch64::mem::table::L3;
use libk::device::{
@@ -61,7 +64,13 @@ register_structs! {
}
}
struct Bcm2835Mbox {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub(crate) enum Bcm2835VcClock {
Core = 4,
}
pub(crate) struct Bcm2835Mbox {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
@@ -85,13 +94,20 @@ impl Device for Bcm2835Mbox {
fn display_name(&self) -> &str {
"bcm2835-mbox"
}
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
self
}
}
impl Bcm2835Mbox {
const RAM_TO_GPU_OFFSET: u32 = 0x40000000;
const CHANNEL_FRAMEBUFFER: u32 = 1;
const CHANNEL_ARM2VC: u32 = 8;
pub unsafe fn call_raw(&self, buffer: PhysicalAddress, channel: u32) -> Result<(), Error> {
log::info!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
log::debug!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
let address = buffer
.try_into_u32()
@@ -125,10 +141,31 @@ impl Bcm2835Mbox {
}
}
pub fn configure_framebuffer(
&self,
mode_info: &DisplayMode,
) -> Result<FramebufferResponse, Error> {
pub fn vc_clock_rate(&self, clock: Bcm2835VcClock) -> Result<Hertz, Error> {
let mut buffer = PageBox::new_slice(0u32, 4096 / 4)?;
buffer[0] = 9 * 4; // buffer size
buffer[1] = 0; // process request
// Tag 1 (Response)
buffer[2] = 0x00030002; // tag
buffer[3] = 0; // value buffer size
buffer[4] = 0; // request
buffer[5] = clock as u32; // value buffer[0]
buffer[6] = 0; // value buffer[1]
buffer[7] = 0;
// End tag
buffer[8] = 0;
unsafe { self.call_raw(buffer.as_physical_address(), Self::CHANNEL_ARM2VC) }?;
if buffer[4] == 0 || buffer[1] == 0 {
todo!();
}
let rate = Hertz::from(buffer[6]);
Ok(rate)
}
fn configure_framebuffer(&self, mode_info: &DisplayMode) -> Result<FramebufferResponse, Error> {
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct FramebufferRequest {
@@ -158,7 +195,7 @@ impl Bcm2835Mbox {
}))?;
// TODO flush cache for buffer
unsafe { self.call_raw(buffer.as_physical_address(), 0x01) }?;
unsafe { self.call_raw(buffer.as_physical_address(), Self::CHANNEL_FRAMEBUFFER) }?;
let buffer = buffer.get_mut();
+202
View File
@@ -0,0 +1,202 @@
use core::iter;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
interrupt::IrqHandle,
spi::SpiController,
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
use yggdrasil_abi::io::device::spi::{SpiClockPhase, SpiClockPolarity, SpiConfig};
register_bitfields! {
u32,
CS [
LEN_LONG OFFSET(25) NUMBITS(1) [],
DMA_LEN OFFSET(24) NUMBITS(1) [],
CSPOL2 OFFSET(23) NUMBITS(1) [],
CSPOL1 OFFSET(22) NUMBITS(1) [],
CSPOL0 OFFSET(21) NUMBITS(1) [],
RXF OFFSET(20) NUMBITS(1) [],
RXR OFFSET(19) NUMBITS(1) [],
TXD OFFSET(18) NUMBITS(1) [],
RXD OFFSET(17) NUMBITS(1) [],
DONE OFFSET(16) NUMBITS(1) [],
LEN OFFSET(13) NUMBITS(1) [],
REN OFFSET(12) NUMBITS(1) [],
ADCS OFFSET(11) NUMBITS(1) [],
INTR OFFSET(10) NUMBITS(1) [],
INTD OFFSET(9) NUMBITS(1) [],
DMAEN OFFSET(8) NUMBITS(1) [],
TA OFFSET(7) NUMBITS(1) [],
CSPOL OFFSET(6) NUMBITS(1) [],
CLEAR OFFSET(4) NUMBITS(2) [],
CPOL OFFSET(3) NUMBITS(1) [
IdleLow = 0,
IdleHigh = 1
],
CPHA OFFSET(2) NUMBITS(1) [
CaptureOnFirstTransition = 0,
CaptureOnSecondTransition = 1
],
CS OFFSET(0) NUMBITS(2) [],
]
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => CS: ReadWrite<u32, CS::Register>),
(0x04 => FIFO: ReadWrite<u32>),
(0x08 => CLK: ReadWrite<u32>),
(0x0C => DLEN: ReadWrite<u32>),
(0x10 => LTOH: ReadWrite<u32>),
(0x14 => DC: ReadWrite<u32>),
(0x18 => @END),
}
}
pub struct Spi {
name: &'static str,
clock: ClockHandle,
irq: IrqHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl Device for Spi {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let _ = &self.irq;
let regs = self.regs.lock();
regs.CS.set(0);
DEVICE_REGISTRY.spi.register_bus(self.clone()).ok();
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl Regs {
fn start_transfer(&self, core_clock: Hertz, config: &SpiConfig) -> Result<(), Error> {
let want_clock = Hertz::from(config.frequency.unwrap_or(100000));
let divider = Hertz::divider(core_clock, want_clock).ok_or(Error::InvalidArgument)?;
let divider: u16 = divider.try_into().map_err(|_| Error::InvalidArgument)?;
let divider = divider.wrapping_add(1) & !1;
let mut cs = CS::REN::SET + CS::TA::SET;
if let Some(cs_index) = config.chip_select_index
&& cs_index < 3
{
cs += CS::CS.val(cs_index);
}
if let Some(cpol) = config.clock_polarity {
match cpol {
SpiClockPolarity::IdleLow => cs += CS::CPOL::IdleLow,
SpiClockPolarity::IdleHigh => cs += CS::CPOL::IdleHigh,
}
}
if let Some(cpha) = config.clock_phase {
match cpha {
SpiClockPhase::CaptureOnFirstTransition => cs += CS::CPHA::CaptureOnFirstTransition,
SpiClockPhase::CaptureOnSecondTransition => {
cs += CS::CPHA::CaptureOnSecondTransition
}
}
}
self.CLK.set(divider as u32);
self.CS.write(cs);
Ok(())
}
fn tx_rx_byte(&self, tx: u8) -> Result<Option<u8>, Error> {
// Wait for tx
loop {
let status = self.CS.extract();
if status.matches_all(CS::TXD::SET) {
break;
}
if status.matches_all(CS::DONE::SET) {
return Ok(None);
}
}
self.FIFO.set(tx as u32);
// Wait for rx
loop {
let status = self.CS.extract();
if status.matches_all(CS::RXD::SET) {
break;
}
if status.matches_all(CS::DONE::SET) {
return Ok(None);
}
}
Ok(Some(self.FIFO.get() as u8))
}
}
#[async_trait]
impl SpiController for Spi {
async fn spi_transfer(
&self,
tx_buffer: &[u8],
rx_buffer: &mut [u8],
config: &SpiConfig,
) -> Result<usize, Error> {
// TODO interrupt/DMA
log::debug!("{}: spi_transfer {} bytes", self.name, rx_buffer.len());
let core_clock = self.clock.rate()?;
let regs = self.regs.lock();
regs.start_transfer(core_clock, config)?;
let mut len = 0;
for (&tx, rx) in iter::zip(tx_buffer, rx_buffer) {
match regs.tx_rx_byte(tx)? {
Some(b) => {
*rx = b;
len += 1;
}
None => {
break;
}
}
}
Ok(len)
}
}
device_tree_driver! {
compatible: ["brcm,bcm2835-spi"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("bcm2835-spi");
let base = node.map_base(context, 0)?;
let irq = node.interrupt(0)?;
let clock = node.clock(0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let spi = Arc::new(Spi {
regs: IrqSafeSpinlock::new(regs),
name,
clock,
irq
});
Some(spi)
}
}
}
+12 -13
View File
@@ -2,8 +2,7 @@ use alloc::{sync::Arc, vec::Vec};
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
IrqHandle, IrqOptions, IrqVector,
ExternalInterruptController, InterruptHandler, Irq, IrqHandle, IrqOptions, IrqVector,
},
};
use device_tree::{
@@ -13,7 +12,10 @@ use device_tree::{
},
};
use kernel_arch_riscv64::boot_hart_id;
use libk::{arch::Cpu, device::register_external_interrupt_controller};
use libk::{
arch::Cpu,
device::{interrupt::FixedInterruptTable, register_external_interrupt_controller},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::{
@@ -77,8 +79,7 @@ register_structs! {
struct Context {
enable: IrqSafeRwLock<DeviceMemoryIo<'static, ContextEnableRegs>>,
control: IrqSafeRwLock<DeviceMemoryIo<'static, ContextControlRegs>>,
// TODO scale the table depending on effective MAX_IRQS value
table: IrqSafeRwLock<FixedInterruptTable<128>>,
table: FixedInterruptTable,
}
struct Inner {
@@ -163,13 +164,13 @@ impl ExternalInterruptController for Plic {
.inspect_err(|_| log::error!("plic: no context for hart {bsp_hart_id}"))?
.context
.get();
let mut table = context.table.write();
// let mut table = context.table.write();
log::info!(
"Bind irq #{irq} -> hart {bsp_hart_id}, {:?}",
handler.display_name()
);
table.insert(irq as usize, handler)?;
context.table.insert(irq as usize, handler);
Ok(())
}
@@ -183,7 +184,7 @@ impl ExternalInterruptController for Plic {
let context = context.context.get();
let control = context.control.write();
let table = context.table.read();
// let table = context.table.read();
loop {
let irq = control.CLAIM.get();
@@ -192,9 +193,7 @@ impl ExternalInterruptController for Plic {
}
let vector = IrqVector::Irq(Irq::External(irq));
if let Some(handler) = table.handler(irq as usize) {
handler.clone().handle_irq(vector);
} else {
if !context.table.handle_irq_line(irq as usize, vector) {
log::warn!("plic: no handler for IRQ #{irq}");
}
@@ -242,7 +241,7 @@ impl Device for Plic {
context.context.init(Context {
enable: IrqSafeRwLock::new(enable),
control: IrqSafeRwLock::new(control),
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
table: FixedInterruptTable::new(MAX_IRQS), // table: IrqSafeRwLock::new(FixedInterruptTable::new()),
});
}
@@ -377,8 +377,8 @@ impl UsbDeviceDriver for UsbHubDriver {
"USB Hub"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
let _ = protocol;
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool {
let _ = (protocol, vid, pid);
class == 0x09 && subclass == 0x00
}
}
@@ -12,13 +12,14 @@ use crate::{
mod hid;
mod hub;
mod mass_storage;
mod serial;
#[async_trait]
pub trait UsbDeviceDriver: Send + Sync {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError>;
fn name(&self) -> &'static str;
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool;
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool;
}
#[async_trait]
@@ -33,9 +34,11 @@ async fn spawn_device_driver(device: Arc<UsbDeviceAccess>) -> Result<bool, UsbEr
let class = device.device_descriptor.device_class;
let subclass = device.device_descriptor.device_subclass;
let protocol = device.device_descriptor.device_protocol;
let vid = device.device_descriptor.id_vendor;
let pid = device.device_descriptor.id_product;
let Some(driver) = USB_DEVICE_DRIVERS.read().iter().find_map(|driver| {
driver
.probe(class, subclass, protocol)
.probe(class, subclass, protocol, vid, pid)
.then(|| driver.clone())
}) else {
return Ok(false);
@@ -127,6 +130,7 @@ pub fn register_interface_driver(driver: Arc<dyn UsbInterfaceDriver + 'static>)
pub fn register_default_class_drivers() {
register_device_driver(Arc::new(hub::UsbHubDriver));
register_device_driver(Arc::new(serial::FT232Driver));
register_interface_driver(Arc::new(hid::UsbHidKeyboardDriver));
register_interface_driver(Arc::new(hid::UsbHidMouseDriver));
register_interface_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
@@ -0,0 +1,311 @@
use core::sync::atomic::{AtomicBool, Ordering};
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc};
use async_trait::async_trait;
use libk::{
block,
error::Error,
fs::devfs,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_util::sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::{
io::{FileMode, TerminalOptions, TerminalOutputOptions},
process::ProcessId,
};
use crate::{
class_driver::UsbDeviceDriver,
communication::UsbDirection,
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
error::UsbError,
info::UsbEndpointType,
pipe::{control::ControlTransferSetup, normal::UsbBulkOutPipeAccess},
};
pub struct FT232Driver;
struct FT232Device {
device: Arc<UsbDeviceAccess>,
bulk_out: UsbBulkOutPipeAccess,
baud_rate: IrqSafeRwLock<u32>,
}
pub struct UsbSerialDeviceWrapper {
device: Arc<dyn UsbSerialDevice>,
index: u32,
disconnected: AtomicBool,
}
#[async_trait]
pub trait UsbSerialDevice: Send + Sync + 'static {
async fn setup(&self) -> Result<(), UsbError>;
async fn set_baud_rate(&self, baud: u32) -> Result<(), UsbError>;
fn baud_rate(&self) -> u32;
async fn write(&self, buffer: &[u8], options: TerminalOutputOptions)
-> Result<usize, UsbError>;
fn display_name(&self) -> &str;
fn device(&self) -> &Arc<UsbDeviceAccess>;
}
#[async_trait]
impl UsbDeviceDetachHandler for UsbSerialDeviceWrapper {
async fn handle_device_detach(&self) {
self.disconnected.store(true, Ordering::Release);
log::info!("USB serial #{} disconnected", self.index);
remove_usb_serial(self.index);
}
}
impl TerminalOutput for UsbSerialDeviceWrapper {
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
self.write_multiple(&[byte], options)?;
Ok(())
}
fn write_multiple(
&self,
bytes: &[u8],
options: &TerminalOutputOptions,
) -> Result<usize, Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let result = block!(self.device.write(bytes, *options).await)?;
match result {
Ok(len) => Ok(len),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!("{}: write error: {:?}", self.device.display_name(), error);
Err(Error::InvalidOperation)
}
}
}
fn baud_rate(&self) -> u32 {
self.device.baud_rate()
}
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let result = block!(self.device.set_baud_rate(baud).await)?;
match result {
Ok(()) => Ok(()),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!(
"{}: baud rate set error: {:?}",
self.device.display_name(),
error
);
Err(Error::InvalidOperation)
}
}
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let _ = pid;
let result = block! { self.device.setup().await }?;
match result {
Ok(()) => Ok(()),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!("{}: setup error: {:?}", self.device.display_name(), error);
Err(Error::InvalidOperation)
}
}
}
}
impl FT232Device {
const FTDI_RESET: u8 = 0;
const FTDI_SET_BAUD_RATE: u8 = 3;
async fn ftdi_reset(&self, port: u16) -> Result<(), UsbError> {
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: 0b01000000,
b_request: Self::FTDI_RESET,
w_value: 0,
w_index: port,
w_length: 0,
})
.await
}
async fn ftdi_set_baud_rate(&self, port: u16, baud: u32) -> Result<(), UsbError> {
let w_value = match baud {
300 => 0x2710,
600 => 0x1388,
1200 => 0x09C4,
2400 => 0x04E2,
4800 => 0x0271,
9600 => 0x4138,
19200 => 0x809C,
38400 => 0xC04E,
57600 => 0x0034,
115200 => 0x001A,
230400 => 0x000D,
460800 => 0x4006,
921600 => 0x8003,
_ => {
log::warn!("ft232: unsupported baud rate {baud}");
return Err(UsbError::InvalidConfiguration);
}
};
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: 0b01000000,
b_request: Self::FTDI_SET_BAUD_RATE,
w_value,
w_index: port,
w_length: 0,
})
.await
}
}
#[async_trait]
impl UsbSerialDevice for FT232Device {
async fn setup(&self) -> Result<(), UsbError> {
log::info!("ft232: setup");
Ok(())
}
async fn set_baud_rate(&self, baud: u32) -> Result<(), UsbError> {
self.ftdi_set_baud_rate(0, baud).await?;
// *self.baud_rate.write() = baud;
log::info!("ft232: set baud rate {baud}");
Ok(())
}
fn baud_rate(&self) -> u32 {
*self.baud_rate.read()
}
async fn write(
&self,
buffer: &[u8],
options: TerminalOutputOptions,
) -> Result<usize, UsbError> {
if options.contains(TerminalOutputOptions::NL_TO_CRNL) {
for &byte in buffer {
if byte == b'\n' {
self.bulk_out.write(b"\r").await?;
}
self.bulk_out.write(&[byte]).await?;
}
Ok(buffer.len())
} else {
self.bulk_out.write(buffer).await
}
}
fn display_name(&self) -> &str {
"FT232 Serial Converter"
}
fn device(&self) -> &Arc<UsbDeviceAccess> {
&self.device
}
}
#[async_trait]
impl UsbDeviceDriver for FT232Driver {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
let interface = device.interface(0);
let endpoints = interface.endpoints();
let bulk_in = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::In)
.ok_or(UsbError::InvalidConfiguration)?;
let bulk_out = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::Out)
.ok_or(UsbError::InvalidConfiguration)?;
let bulk_in = device
.open_bulk_in_pipe(bulk_in.number, bulk_in.max_packet_size as _)
.await?;
let bulk_out = device
.open_bulk_out_pipe(bulk_out.number, bulk_out.max_packet_size as _)
.await?;
let ft232 = Arc::new(FT232Device {
device,
bulk_out,
baud_rate: IrqSafeRwLock::new(115200),
});
ft232.ftdi_reset(0).await?;
let terminal = register_usb_serial(ft232)?;
let mut buffer = [0; 3];
loop {
let len = bulk_in.read(&mut buffer).await?;
if len < 2 {
continue;
}
for &byte in &buffer[2..len] {
terminal.write_to_input(byte);
}
}
}
fn name(&self) -> &'static str {
"FT232 Serial Converter"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool {
let _ = (class, subclass, protocol);
vid == 0x0403 && pid == 0x6001
}
}
static USB_SERIALS: IrqSafeSpinlock<BTreeMap<u32, Arc<Terminal<Arc<UsbSerialDeviceWrapper>>>>> =
IrqSafeSpinlock::new(BTreeMap::new());
fn register_usb_serial(
serial: Arc<dyn UsbSerialDevice>,
) -> Result<Arc<Terminal<Arc<UsbSerialDeviceWrapper>>>, UsbError> {
let mut serials = USB_SERIALS.lock();
for i in 0..64 {
if serials.contains_key(&i) {
continue;
}
let wrapper = Arc::new(UsbSerialDeviceWrapper {
device: serial,
index: i,
disconnected: AtomicBool::new(false),
});
wrapper.device.device().set_detach_handler(wrapper.clone());
let input = TerminalInput::with_capacity(64).expect("Couldn't allocate input buffer");
let terminal = Arc::new(Terminal::from_parts(
TerminalOptions::const_default(),
input,
wrapper,
));
serials.insert(i, terminal.clone());
let name = alloc::format!("ttyUSB{i}");
devfs::add_named_char_device(terminal.clone(), name.clone(), FileMode::new(0o600)).ok();
return Ok(terminal);
}
Err(UsbError::DriverError)
}
fn remove_usb_serial(index: u32) {
let serial = USB_SERIALS.lock().remove(&index);
if serial.is_none() {
log::warn!("usb-serial #{index} doesn't exist in the table");
}
let name = alloc::format!("ttyUSB{index}");
devfs::remove_node(name).ok();
}
+7
View File
@@ -37,6 +37,9 @@ pub trait PortConfig: Sync + Send {
pub trait Access: Sized + Send + 'static {
type Config: Config;
/// # Safety
///
/// Unsafe: allows physical memory access.
unsafe fn map(config: &<Self::Config as Config>::Port) -> Result<Self, Error>;
fn read(&self, reg: u8) -> u8;
@@ -76,6 +79,10 @@ impl<A: Access> Uart8250<A> {
}
}
/// # Safety
///
/// Unsafe: the function is meant only to be called once, directly
/// interacts with the device state.
pub unsafe fn init_inner(self: &Arc<Self>) -> Result<(), Error> {
let ports = self.ports.iter();
let port_configs = self.config.ports().iter();
+1 -1
View File
@@ -109,7 +109,7 @@ impl<A: Access> Port8250<A> {
let regs = inner.output().regs.lock();
let iir = regs.read(REG_IIR) & IIR_MASK;
if iir == IIR_RXDA {
Some(regs.read(REG_RBR) as u8)
Some(regs.read(REG_RBR))
} else {
None
}
+1
View File
@@ -9,6 +9,7 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
yggdrasil-abi.workspace = true
device-api-macros = { workspace = true, optional = true }
async-trait.workspace = true
[features]
default = []
+22
View File
@@ -26,6 +26,28 @@ pub trait IntoHertz {
}
}
impl Hertz {
pub fn divider(base: Self, desired: Self) -> Option<u32> {
if desired > base || desired.0 == 0 {
return None;
}
let value = base.0 / desired.0;
value.try_into().ok()
}
}
impl From<u32> for Hertz {
fn from(value: u32) -> Self {
Self(value as u64)
}
}
impl From<u64> for Hertz {
fn from(value: u64) -> Self {
Self(value)
}
}
impl Mul<u32> for Hertz {
type Output = Hertz;
+6
View File
@@ -1,3 +1,5 @@
use core::any::Any;
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
@@ -51,4 +53,8 @@ pub trait Device: Sync + Send {
fn as_clock_controller(self: Arc<Self>) -> Option<Arc<dyn ClockController>> {
None
}
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
unimplemented!()
}
}
+147
View File
@@ -0,0 +1,147 @@
use core::{
fmt,
ops::Deref,
sync::atomic::{AtomicU16, AtomicU32, Ordering},
};
use alloc::sync::Arc;
use yggdrasil_abi::{error::Error, io::device::i2c::I2CMasterInfo, process::ProcessId};
use crate::{
clock::Hertz,
device::{Device, DeviceInitContext},
};
pub struct I2CAddress(u16);
pub struct I2CDevice {
device: Arc<dyn I2CController>,
address: AtomicU16,
user: AtomicU32,
}
pub trait I2CController: Device {
fn bus_number(&self) -> u32;
fn child_number(&self) -> Option<u32>;
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error>;
fn capabilities(&self) -> I2CMasterInfo;
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error>;
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error>;
}
impl I2CAddress {
pub fn as_8_bit(&self) -> Option<u8> {
self.0.try_into().ok()
}
pub fn as_10_bit(&self) -> u16 {
self.0
}
pub fn is_8_bit(&self) -> bool {
self.0 <= 0xFF
}
}
impl fmt::Display for I2CAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl From<u8> for I2CAddress {
fn from(value: u8) -> Self {
Self(value as u16)
}
}
impl TryFrom<u16> for I2CAddress {
type Error = Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
if value >= (1 << 10) {
return Err(Error::InvalidArgument);
}
Ok(Self(value))
}
}
impl Deref for I2CDevice {
type Target = dyn I2CController;
fn deref(&self) -> &Self::Target {
&*self.device
}
}
impl From<Arc<dyn I2CController>> for I2CDevice {
fn from(value: Arc<dyn I2CController>) -> Self {
Self {
device: value,
address: AtomicU16::new(0),
user: AtomicU32::new(0),
}
}
}
impl I2CDevice {
pub fn set_slave_address(&self, address: I2CAddress) {
self.address.store(address.0, Ordering::Release);
}
pub fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
self.device.set_speed(speed)
}
pub fn slave_address(&self) -> I2CAddress {
I2CAddress(self.address.load(Ordering::Acquire))
}
pub fn check_lock(&self, id: ProcessId) -> Result<(), Error> {
if self.user.load(Ordering::Acquire) == id.bits() {
Ok(())
} else {
Err(Error::PermissionDenied)
}
}
pub fn lock(&self, process: ProcessId) -> Result<(), Error> {
if self
.user
.compare_exchange(0, process.bits(), Ordering::Acquire, Ordering::Relaxed)
.is_ok()
{
Ok(())
} else {
Err(Error::WouldBlock)
}
}
pub fn release(&self, process: ProcessId) -> Result<(), Error> {
if self
.user
.compare_exchange(process.bits(), 0, Ordering::Release, Ordering::Relaxed)
.is_ok()
{
Ok(())
} else {
Err(Error::InvalidOperation)
}
}
}
impl Device for I2CDevice {
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
self.device.clone().init(cx)
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
self.device.clone().init_irq()
}
fn display_name(&self) -> &str {
self.device.display_name()
}
}
-45
View File
@@ -83,10 +83,6 @@ pub trait InterruptHandler: Device {
fn handle_irq(self: Arc<Self>, vector: IrqVector) -> bool;
}
pub trait InterruptTable: Sync {
fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>>;
}
pub trait ExternalInterruptController: Device {
/// Performs IRQ delivery method configuration and registers a handler to execute when it is
/// fired
@@ -151,10 +147,6 @@ pub trait LocalInterruptController: Device {
fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error>;
}
pub struct FixedInterruptTable<const N: usize> {
rows: [Option<Arc<dyn InterruptHandler>>; N],
}
impl IrqHandle {
pub fn register(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
self.intc.register_irq(self.irq, self.options, handler)
@@ -165,43 +157,6 @@ impl IrqHandle {
}
}
impl<const N: usize> FixedInterruptTable<N> {
pub const fn new() -> Self {
Self {
rows: [const { None }; N],
}
}
pub fn insert(&mut self, index: usize, entry: Arc<dyn InterruptHandler>) -> Result<(), Error> {
let row = self.rows.get_mut(index).ok_or(Error::InvalidArgument)?;
if row.is_some() {
return Err(Error::AlreadyExists);
}
*row = Some(entry);
Ok(())
}
pub fn insert_least_loaded(
&mut self,
handler: Arc<dyn InterruptHandler>,
) -> Result<usize, Error> {
let index = self
.rows
.iter()
.position(|p| p.is_none())
.ok_or(Error::InvalidArgument)?;
self.rows[index].replace(handler);
Ok(index)
}
}
impl<const N: usize> InterruptTable for FixedInterruptTable<N> {
#[inline]
fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>> {
self.rows.get(index).and_then(|p| p.as_ref())
}
}
impl IrqLevel {
pub fn override_default(self, value: IrqLevel) -> Self {
match self {
+2
View File
@@ -8,8 +8,10 @@ pub mod bus;
pub mod clock;
pub mod device;
pub mod gpio;
pub mod i2c;
pub mod interrupt;
pub mod serial;
pub mod spi;
pub mod timer;
pub mod dma;
+15
View File
@@ -0,0 +1,15 @@
use alloc::boxed::Box;
use async_trait::async_trait;
use yggdrasil_abi::{error::Error, io::device::spi::SpiConfig};
use crate::device::Device;
#[async_trait]
pub trait SpiController: Device {
async fn spi_transfer(
&self,
tx_buffer: &[u8],
rx_buffer: &mut [u8],
config: &SpiConfig,
) -> Result<usize, Error>;
}
@@ -9,6 +9,10 @@
/// `.init_array`-based mechanism.
pub macro device_tree_driver(
compatible: [$($compatible:literal),+ $(,)?],
$(config: {
$($config_field:ident : $config_value:expr),+
$(,)?
},)?
driver: $driver:tt
) {
struct __DtDriver;
@@ -21,8 +25,16 @@ pub macro device_tree_driver(
static __REGISTER_FN: extern "C" fn() = __register_fn;
extern "C" fn __register_fn() {
#[allow(clippy::needless_update)]
let config = $crate::driver::DriverConfig {
$(
$($config_field: $config_value,)+
)?
..Default::default()
};
$crate::driver::register_driver(
&[$($compatible),+],
config,
&__DT_DRIVER
)
}
+1 -1
View File
@@ -15,7 +15,7 @@ pub mod util;
pub use controller::{map_interrupt, map_interrupt_at};
pub use macros::device_tree_driver;
pub use registry::{lookup_phandle, register_driver};
pub use registry::{DriverConfig, lookup_phandle, register_driver};
pub use syscon::DeviceTreeSyscon;
pub use traits::{
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreePinController,
+20 -1
View File
@@ -7,8 +7,22 @@ use crate::DeviceTree;
use super::{Driver, Node};
/// Device tree driver configuration
#[derive(Debug)]
pub struct DriverConfig {
/// If `false`, pinctrl setup for the node needs to be performed manually
pub auto_pinctrl: bool,
}
impl Default for DriverConfig {
fn default() -> Self {
Self { auto_pinctrl: true }
}
}
pub(crate) struct DriverRegistration {
pub(crate) compatible: &'static [&'static str],
pub(crate) config: DriverConfig,
pub(crate) imp: &'static dyn Driver,
}
@@ -25,9 +39,14 @@ impl DriverRegistration {
}
#[doc(hidden)]
pub fn register_driver(compatible: &'static [&'static str], driver: &'static dyn Driver) {
pub fn register_driver(
compatible: &'static [&'static str],
config: DriverConfig,
driver: &'static dyn Driver,
) {
DRIVERS.write().push(DriverRegistration {
compatible,
config,
imp: driver,
});
}
+42 -17
View File
@@ -10,6 +10,7 @@ use device_api::{
clock::{ClockController, ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
gpio::PinHandle,
i2c::I2CController,
interrupt::{ExternalInterruptController, IrqHandle, MessageInterruptController},
};
use fdt_rs::spec::Phandle;
@@ -61,6 +62,7 @@ pub struct Node {
pub(crate) reset_controller: OneTimeInit<Arc<dyn DeviceTreeResetController>>,
pub(crate) system_controller: OneTimeInit<Arc<dyn DeviceTreeSyscon>>,
pub(crate) pin_controller: OneTimeInit<Arc<dyn DeviceTreePinController>>,
pub(crate) i2c_controller: OneTimeInit<Arc<dyn I2CController>>,
}
pub(crate) struct ProbedDevice {
@@ -105,24 +107,29 @@ impl Node {
.as_str_list()
.find_map(|c| drivers.iter().find(|d| d.matches(c)));
// if libk::config::get().device_tree.log_missing && driver.is_none() {
// // FIXME don't spam virtio missing stuff
// if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
// for (i, compatible) in compatible.as_str_list().enumerate() {
// if i == 0 {
// log::warn!("No driver for {name:?} ({compatible:?})");
// } else {
// log::warn!(" also {compatible:?}");
// }
// }
// }
// }
#[cfg(any(not(target_arch = "x86_64"), rust_analyzer))]
{
if libk::config::get().device_tree.log_missing && driver.is_none() {
// FIXME don't spam virtio missing stuff
if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
for (i, compatible) in compatible.as_str_list().enumerate() {
if i == 0 {
log::warn!("No driver for {name:?} ({compatible:?})");
} else {
log::warn!(" also {compatible:?}");
}
}
}
}
}
let driver = driver?;
// Initialize default pinctrl before probing
node.setup_pins()
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
.ok()?;
if driver.config.auto_pinctrl {
node.setup_pins(0)
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
.ok()?;
}
let device = driver.imp.probe(node, cx);
@@ -198,6 +205,11 @@ impl Node {
self.interrupt_controller.init(intc);
}
/// Makes node an I²C controller node
pub fn make_i2c_controller(&self, i2c: Arc<dyn I2CController>) {
self.i2c_controller.init(i2c);
}
/// When called from an msi-controller driver, informs the node of its capability as an MSI
/// controller.
pub fn make_msi_controller(&self, intc: Arc<dyn MessageInterruptController>) {
@@ -314,6 +326,11 @@ impl Node {
}
}
/// Returns the device associated with this node
pub fn device(self: Arc<Self>) -> Option<Arc<dyn Device>> {
Some(self.probe()?.device.clone())
}
/// Returns an iterator over the node's children
pub fn children(&self) -> impl Iterator<Item = &Arc<Node>> {
self.children.get().iter()
@@ -451,6 +468,11 @@ impl Node {
self.pin_controller.try_get().cloned()
}
/// Attempts to get an I2C controller represented by this node, if any
pub fn as_i2c_controller(&self) -> Option<Arc<dyn I2CController>> {
self.i2c_controller.try_get().cloned()
}
/// Returns the `#address-cells` value of the node's parent bus
pub fn bus_address_cells(&self) -> usize {
self.bus_address_cells
@@ -527,9 +549,11 @@ impl Node {
tree::dump(self.dt_node.clone(), 0);
}
fn setup_pins(&self) -> Result<(), Error> {
/// Configure the `index`th pinctrl mode for the peripheral node
pub fn setup_pins(&self, index: usize) -> Result<(), Error> {
// TODO lookup pin state by name
let Some(pinctrl0) = self.prop_usize("pinctrl-0") else {
let field_name = alloc::format!("pinctrl-{index}");
let Some(pinctrl0) = self.prop_usize(&field_name) else {
return Ok(());
};
let pinctrl0 = lookup_phandle(pinctrl0 as Phandle, false).ok_or(Error::DoesNotExist)?;
@@ -596,6 +620,7 @@ fn unflatten_node(
reset_controller: OneTimeInit::new(),
system_controller: OneTimeInit::new(),
pin_controller: OneTimeInit::new(),
i2c_controller: OneTimeInit::new(),
});
if let Some(phandle) = phandle {
+76
View File
@@ -0,0 +1,76 @@
use core::task::{Context, Poll};
use alloc::boxed::Box;
use async_trait::async_trait;
use device_api::{
clock::Hertz,
i2c::{I2CAddress, I2CDevice},
};
use yggdrasil_abi::{
error::Error,
io::device::i2c::{self, I2CRequestVariant},
option::RequestValue,
process::ProcessId,
};
use crate::{device::char::CharDevice, task::thread::Thread, vfs::FileReadiness};
// TODO timeouts and better access
#[async_trait]
impl CharDevice for I2CDevice {
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
self.read_nonblocking(buffer)
}
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
self.write_nonblocking(buffer)
}
fn read_nonblocking(&self, buffer: &mut [u8]) -> Result<usize, Error> {
let pid = Thread::current().process_id();
self.check_lock(pid)?;
self.i2c_read(self.slave_address(), buffer)
}
fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
let pid = Thread::current().process_id();
self.check_lock(pid)?;
self.i2c_write(self.slave_address(), buffer)
}
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
if let Ok(req) = I2CRequestVariant::try_from(option) {
match req {
I2CRequestVariant::SetAddress => {
let address = i2c::SetAddress::load_request(&buffer[..len])?;
let address = I2CAddress::try_from(address)?;
self.set_slave_address(address);
Ok(0)
}
I2CRequestVariant::SetSpeed => {
let speed = i2c::SetSpeed::load_request(&buffer[..len])?;
let speed = Hertz::from(speed);
let actual = self.set_speed(speed)?.0 as u32;
let len = i2c::SetSpeed::store_response(&actual, buffer)?;
Ok(len)
}
I2CRequestVariant::GetMasterInfo => {
let info = self.capabilities();
let len = i2c::GetMasterInfo::store_response(&info, buffer)?;
Ok(len)
}
}
} else {
Err(Error::InvalidArgument)
}
}
fn open(&self, process: ProcessId) -> Result<(), Error> {
I2CDevice::lock(self, process)
}
fn close(&self, process: ProcessId) -> Result<(), Error> {
I2CDevice::release(self, process)
}
}
impl FileReadiness for I2CDevice {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
let _ = cx;
todo!()
}
}
@@ -3,10 +3,13 @@ use core::{any::Any, ops::Deref};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use yggdrasil_abi::error::Error;
use yggdrasil_abi::{error::Error, process::ProcessId};
use crate::vfs::{CommonImpl, FileReadiness, NodeRef};
pub mod i2c;
pub mod spi;
#[async_trait]
pub trait CharDevice: Device + FileReadiness {
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
@@ -25,6 +28,15 @@ pub trait CharDevice: Device + FileReadiness {
Err(Error::NotImplemented)
}
fn open(&self, process: ProcessId) -> Result<(), Error> {
let _ = process;
Ok(())
}
fn close(&self, process: ProcessId) -> Result<(), Error> {
let _ = process;
Ok(())
}
fn is_readable(&self) -> bool {
true
}
+94
View File
@@ -0,0 +1,94 @@
use core::task::{Context, Poll};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{device::Device, spi::SpiController};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use yggdrasil_abi::{
error::Error,
io::device::spi::{self, SpiConfig, SpiRequestVariant},
option::RequestValue,
};
use crate::{
device::char::CharDevice,
task::{mem::ForeignPointer, thread::Thread},
vfs::FileReadiness,
};
pub struct SpiControllerDevice {
device: Arc<dyn SpiController>,
config: IrqSafeRwLock<SpiConfig>,
}
impl From<Arc<dyn SpiController>> for SpiControllerDevice {
fn from(value: Arc<dyn SpiController>) -> Self {
Self {
device: value,
config: IrqSafeRwLock::new(Default::default()),
}
}
}
impl Device for SpiControllerDevice {
fn display_name(&self) -> &str {
self.device.display_name()
}
}
// TODO timeouts and better access
#[async_trait]
impl CharDevice for SpiControllerDevice {
async fn read(&self, _buffer: &mut [u8]) -> Result<usize, Error> {
Err(Error::InvalidOperation)
}
async fn write(&self, _buffer: &[u8]) -> Result<usize, Error> {
Err(Error::InvalidOperation)
}
fn read_nonblocking(&self, _buffer: &mut [u8]) -> Result<usize, Error> {
Err(Error::InvalidOperation)
}
fn write_nonblocking(&self, _buffer: &[u8]) -> Result<usize, Error> {
Err(Error::InvalidOperation)
}
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
if let Ok(request) = SpiRequestVariant::try_from(option) {
match request {
SpiRequestVariant::Transfer => {
let xfer = spi::Transfer::load_request(&buffer[..len])?;
let thread = Thread::current();
let space = thread.address_space();
let len = xfer.len;
let tx_buffer = unsafe { xfer.tx_buffer.validate_user_slice(len, space) }?;
let rx_buffer = unsafe { xfer.rx_buffer.validate_user_slice_mut(len, space) }?;
let config = self.config.read();
let len = block!(
self.device
.spi_transfer(tx_buffer, rx_buffer, &config)
.await
)??;
let len = spi::Transfer::store_response(&len, buffer)?;
Ok(len)
}
SpiRequestVariant::SetConfig => {
// TODO validate config against min/max controller capabilities
let config = spi::SetConfig::load_request(&buffer[..len])?;
*self.config.write() = config;
Ok(0)
}
}
} else {
todo!()
}
}
}
impl FileReadiness for SpiControllerDevice {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
let _ = cx;
todo!()
}
}
+44
View File
@@ -0,0 +1,44 @@
use alloc::{sync::Arc, vec::Vec};
use device_api::interrupt::{InterruptHandler, IrqVector};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
pub struct FixedInterruptTable {
rows: Vec<IrqSafeRwLock<Vec<Arc<dyn InterruptHandler>>>>,
}
impl FixedInterruptTable {
pub fn new(irq_lines: usize) -> Self {
Self {
rows: (0..irq_lines)
.map(|_| IrqSafeRwLock::new(Vec::new()))
.collect(),
}
}
pub fn insert(&self, line: usize, handler: Arc<dyn InterruptHandler>) {
let mut row = self.rows[line].write();
row.push(handler);
}
pub fn insert_least_loaded(&self, handler: Arc<dyn InterruptHandler>) -> usize {
let min_line = self
.rows
.iter()
.enumerate()
.min_by_key(|(_, row)| row.read().len())
.unwrap()
.0;
self.insert(min_line, handler);
min_line
}
pub fn handle_irq_line(&self, line: usize, vector: IrqVector) -> bool {
let row = self.rows[line].read();
for handler in row.iter() {
if handler.clone().handle_irq(vector) {
return true;
}
}
false
}
}
+51
View File
@@ -4,12 +4,17 @@ use core::{
};
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
use device_api::{
i2c::{I2CController, I2CDevice},
spi::SpiController,
};
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::{error::Error, io::FileMode};
use crate::{
config,
debug::{self, DebugSink},
device::char::spi::SpiControllerDevice,
fs::devfs,
vfs::NodeRef,
};
@@ -46,16 +51,30 @@ pub struct SerialTerminalRegistry {
registry: GenericRegistry<Arc<dyn CharDevice>>,
}
// Manages devices: i2c<N>, i2c<N>-<M>
pub struct I2CDeviceRegistry {
registry: GenericRegistry<Arc<I2CDevice>>,
}
// Manages devices: spi<X>.<Y>
pub struct SpiDeviceRegistry {
registry: GenericRegistry<Arc<SpiControllerDevice>>,
}
pub struct DeviceRegistry {
pub display: DisplayDeviceRegistry,
pub terminal: TerminalRegistry,
pub serial_terminal: SerialTerminalRegistry,
pub i2c: I2CDeviceRegistry,
pub spi: SpiDeviceRegistry,
}
pub static DEVICE_REGISTRY: DeviceRegistry = DeviceRegistry {
display: DisplayDeviceRegistry::new(),
terminal: TerminalRegistry::new(),
serial_terminal: SerialTerminalRegistry::new(),
i2c: I2CDeviceRegistry::new(),
spi: SpiDeviceRegistry::new(),
};
impl TerminalRegistry {
@@ -117,6 +136,38 @@ impl DisplayDeviceRegistry {
}
}
impl I2CDeviceRegistry {
pub const fn new() -> Self {
Self {
registry: GenericRegistry::new(),
}
}
pub fn register_bus(&self, device: Arc<dyn I2CController>) -> Result<u32, Error> {
let wrapper = Arc::new(I2CDevice::from(device));
let id = self.registry.register(wrapper.clone())?;
let name = format!("i2c{id}");
devfs::add_named_char_device(wrapper.clone(), &name, FileMode::new(0o600))?;
Ok(id)
}
}
impl SpiDeviceRegistry {
pub const fn new() -> Self {
Self {
registry: GenericRegistry::new(),
}
}
pub fn register_bus(&self, device: Arc<dyn SpiController>) -> Result<u32, Error> {
let wrapper = Arc::new(SpiControllerDevice::from(device));
let id = self.registry.register(wrapper.clone())?;
let name = format!("spi{id}");
devfs::add_named_char_device(wrapper.clone(), &name, FileMode::new(0o600))?;
Ok(id)
}
}
impl<T> GenericRegistry<T> {
pub const fn new() -> Self {
Self {
+1
View File
@@ -7,6 +7,7 @@ use yggdrasil_abi::error::Error;
pub mod block;
pub mod char;
pub mod display;
pub mod interrupt;
pub mod manager;
// TODO get rid of this, this does not work when there are multiple interrupt controllers
+9 -1
View File
@@ -1,5 +1,5 @@
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::{error::Error, io::SeekFrom};
use yggdrasil_abi::{error::Error, io::SeekFrom, process::ProcessId};
use crate::{
device::{block::BlockDeviceFile, char::CharDeviceFile},
@@ -20,6 +20,7 @@ pub struct CharFile {
pub(super) node: NodeRef,
pub(super) read: bool,
pub(super) write: bool,
pub(super) pid: ProcessId,
}
impl BlockFile {
@@ -103,3 +104,10 @@ impl CharFile {
self.device.0.is_terminal()
}
}
impl Drop for CharFile {
fn drop(&mut self) {
// TODO doesn't work with fork
self.device.close(self.pid).ok();
}
}
+6 -1
View File
@@ -33,7 +33,7 @@ use yggdrasil_abi::{
use crate::{
device::{block::BlockDeviceFile, char::CharDeviceFile},
task::process::Process,
task::{process::Process, thread::Thread},
vfs::{
FdPoll, FileReadiness, Node, SharedMemory, TimerFile,
node::NodeRef,
@@ -254,6 +254,10 @@ impl File {
node: NodeRef,
opts: OpenOptions,
) -> Result<Arc<Self>, Error> {
let pid = Thread::current().process_id();
device.open(pid)?;
let read = opts.contains(OpenOptions::READ);
let write = opts.contains(OpenOptions::WRITE);
@@ -269,6 +273,7 @@ impl File {
node,
read,
write,
pid,
})))
}
+56 -1
View File
@@ -1,5 +1,6 @@
use core::{
mem::MaybeUninit,
ops::Deref,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll},
};
@@ -19,7 +20,7 @@ use yggdrasil_abi::{
device::{self, TerminalRequestVariant},
},
option::RequestValue,
process::{ProcessGroupId, Signal},
process::{ProcessGroupId, ProcessId, Signal},
};
use crate::{device::char::CharDevice, task::process::Process};
@@ -82,6 +83,15 @@ pub trait TerminalOutput: Sync + Send {
Err(Error::NotImplemented)
}
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
let _ = pid;
Ok(())
}
fn close(&self, pid: ProcessId) -> Result<(), Error> {
let _ = pid;
Ok(())
}
}
struct InputBuffer {
@@ -424,6 +434,13 @@ impl<O: TerminalOutput> CharDevice for Terminal<O> {
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
self.handle_device_request(option, buffer, len)
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
self.output.open(pid)
}
fn close(&self, pid: ProcessId) -> Result<(), Error> {
self.output.close(pid)
}
}
impl<O: TerminalOutput> FileReadiness for Terminal<O> {
@@ -438,6 +455,44 @@ impl<O: TerminalOutput> Device for Terminal<O> {
}
}
impl<T: TerminalOutput> TerminalOutput for Arc<T> {
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
self.deref().write(byte, options)
}
fn write_multiple(
&self,
bytes: &[u8],
options: &TerminalOutputOptions,
) -> Result<usize, Error> {
self.deref().write_multiple(bytes, options)
}
fn notify_readers(&self) {
self.deref().notify_readers();
}
fn size(&self) -> TerminalSize {
self.deref().size()
}
fn set_size(&self, size: TerminalSize) -> Result<(), Error> {
self.deref().set_size(size)
}
fn baud_rate(&self) -> u32 {
self.deref().baud_rate()
}
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
self.deref().set_baud_rate(baud)
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
self.deref().open(pid)
}
fn close(&self, pid: ProcessId) -> Result<(), Error> {
self.deref().close(pid)
}
}
pub fn read_all(source: &mut RingBuffer<u8>, target: &mut [u8], eof: Option<u8>) -> usize {
let mut pos = 0;
while pos < target.len()
+1 -1
View File
@@ -39,7 +39,7 @@ use crate::{
static BSP_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
pub(crate) static SPIN_TABLE_STACK: AtomicUsize = AtomicUsize::new(0);
static mut KERNEL_LOAD_BASE: u64 = 0;
pub(crate) static mut KERNEL_LOAD_BASE: u64 = 0;
static mut DTB_PHYSICAL_ADDRESS: PhysicalAddress = PhysicalAddress::ZERO;
unsafe extern "C" fn relocate_kernel(image_base: i64, rela_start: usize, rela_end: usize) {
+5 -5
View File
@@ -16,7 +16,7 @@ use abi::{SyscallFunction, process::Signal};
use kernel_arch::{Architecture, ArchitectureImpl, sync::hack_locks};
use kernel_arch_aarch64::{
context::ExceptionFrame,
mem::table::{EntryType, L1, L2, L3, PageTable},
mem::table::{EntryType, L1, L2, L3, PageAttributes, PageTable},
};
use libk::{device::external_interrupt_controller, task::thread::Thread};
use libk_mm::{PageFaultKind, address::PhysicalAddress, table::EntryLevelExt};
@@ -55,24 +55,24 @@ unsafe fn perform_ptw<F: Fn(u32, EntryType)>(virt: usize, handler: F) {
handler(0, EntryType::Invalid);
return;
};
handler(0, EntryType::Table(ttbr_phys));
handler(0, EntryType::Table(PageAttributes::empty(), ttbr_phys));
let l2 = l1.walk(l1i);
handler(1, l2);
let l2 = match l2 {
EntryType::Table(l2) => PageTable::<L2>::from_physical(l2).unwrap(),
EntryType::Table(_, l2) => PageTable::<L2>::from_physical(l2).unwrap(),
_ => return,
};
let l3 = l2.walk(l2i);
handler(2, l3);
let l3 = match l3 {
EntryType::Table(l3) => PageTable::<L3>::from_physical(l3).unwrap(),
EntryType::Table(_, l3) => PageTable::<L3>::from_physical(l3).unwrap(),
_ => return,
};
let l3e = match l3[l3i].as_page() {
Some(page) => EntryType::Page(page),
Some(page) => EntryType::Page(l3[l3i].attributes(), page),
None => EntryType::Invalid,
};
handler(3, l3e);
+15 -20
View File
@@ -8,9 +8,8 @@ use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
IpiDeliveryTarget, IpiMessage, Irq, IrqHandle, IrqLevel, IrqOptions, IrqTrigger, IrqVector,
LocalInterruptController,
ExternalInterruptController, InterruptHandler, IpiDeliveryTarget, IpiMessage, Irq,
IrqHandle, IrqLevel, IrqOptions, IrqTrigger, IrqVector, LocalInterruptController,
},
};
use device_tree::{
@@ -18,12 +17,15 @@ use device_tree::{
driver::{DeviceTreeInterruptController, Node, ProbeContext, device_tree_driver},
};
use kernel_arch_aarch64::{CPU_COUNT, GicInterface};
use libk::{arch::Cpu, device::register_external_interrupt_controller, task::cpu_index};
use libk::{
arch::Cpu,
device::{interrupt::FixedInterruptTable, register_external_interrupt_controller},
task::cpu_index,
};
use libk_mm::{
address::PhysicalAddress,
device::{DeviceMemoryIo, RawDeviceMemoryMapping},
};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use self::{gicc::Gicc, gicd::Gicd};
@@ -43,7 +45,7 @@ pub mod gicv2m;
pub struct Gic {
gicc: Gicc,
gicd: Gicd,
table: IrqSafeRwLock<FixedInterruptTable<MAX_IRQ>>,
table: FixedInterruptTable,
}
unsafe impl Sync for Gic {}
@@ -72,7 +74,7 @@ impl ExternalInterruptController for Gic {
options: IrqOptions,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let mut table = self.table.write();
// let mut table = self.table.write();
let index = match irq {
Irq::External(i) => i + GIC_SPI_START,
@@ -89,7 +91,7 @@ impl ExternalInterruptController for Gic {
if index >= GIC_SPI_START as usize {
self.gicd.configure_irq(index, options);
}
table.insert(index, handler)?;
self.table.insert(index, handler);
Ok(())
}
@@ -128,17 +130,9 @@ impl ExternalInterruptController for Gic {
Irq::External(irq_number as u32 - GIC_SPI_START)
};
{
let table = self.table.read();
let entry = match table.handler(irq_number) {
Some(handler) => handler.clone(),
None => {
log::warn!("No handler for irq{}", irq_number);
return;
}
};
entry.handle_irq(IrqVector::Irq(irq));
let vector = IrqVector::Irq(irq);
if !self.table.handle_irq_line(irq_number, vector) {
log::warn!("No handler for irq{irq_number}");
}
}
}
@@ -242,11 +236,12 @@ impl Gic {
// self.gicd.init(gicd);
// self.gicc.init(gicc);
let table = FixedInterruptTable::new(MAX_IRQ);
Ok(Self {
gicd,
gicc,
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
table, // table: IrqSafeRwLock::new(FixedInterruptTable::new()),
})
}
}
+1
View File
@@ -287,6 +287,7 @@ impl AArch64 {
MACHINE_NAME.init(machine.into());
}
log::info!("Boot arguments: {bootargs:?}");
log::info!("Boot address: {:#x}", unsafe { boot::KERNEL_LOAD_BASE });
log::info!("Initializing aarch64 platform");
+9 -11
View File
@@ -5,13 +5,14 @@ use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
IrqLevel, IrqOptions, IrqTrigger, IrqVector,
ExternalInterruptController, InterruptHandler, Irq, IrqLevel, IrqOptions, IrqTrigger,
IrqVector,
},
};
use kernel_arch_x86::ISA_IRQ_OFFSET;
use libk::device::interrupt::FixedInterruptTable;
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock};
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
@@ -64,7 +65,7 @@ struct Inner {
pub struct IoApic {
inner: IrqSafeSpinlock<Inner>,
isa_redirections: [Option<IsaRedirection>; 16],
table: IrqSafeRwLock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>,
table: FixedInterruptTable,
}
impl Regs {
@@ -170,7 +171,8 @@ impl ExternalInterruptController for IoApic {
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let mut inner = self.inner.lock();
let table_vector = self.table.write().insert_least_loaded(handler.clone())?;
let table_vector = self.table.insert_least_loaded(handler.clone());
// let table_vector = self.table.write().insert_least_loaded(handler.clone())?;
let gsi_target_vector = (table_vector as u32) + IO_APIC_VECTOR_OFFSET;
let bsp_apic = *BSP_APIC_ID.get();
@@ -216,12 +218,8 @@ impl ExternalInterruptController for IoApic {
}
fn handle_specific_irq(&self, gsi: usize) {
let table = self.table.read();
let vector = IrqVector::Irq(Irq::External(gsi as u32));
if let Some(handler) = table.handler(gsi) {
handler.clone().handle_irq(vector);
} else {
if !self.table.handle_irq_line(gsi, vector) {
log::warn!("No handler set for GSI #{}", gsi);
}
}
@@ -295,7 +293,7 @@ impl IoApic {
Ok(Arc::new(Self {
isa_redirections,
inner: IrqSafeSpinlock::new(inner),
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
table: FixedInterruptTable::new(POPULATED_EXTERNAL_VECTORS as usize),
}))
}
+112
View File
@@ -0,0 +1,112 @@
#![allow(missing_docs)]
use abi::{
error::Error,
io::{FileMode, device::i2c::I2CMasterInfo},
};
use alloc::{string::String, sync::Arc};
use device_api::{
clock::Hertz,
device::{Device, DeviceInitContext},
i2c::{I2CAddress, I2CController, I2CDevice},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver, lookup_phandle};
use libk::fs::devfs;
pub struct I2CMuxChild {
name: String,
channel: u32,
parent: Arc<dyn I2CController>,
}
pub struct I2CMux {
name: &'static str,
}
impl I2CMux {
pub fn from_fdt(node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let i2c_parent = node.prop_usize("i2c-parent")? as u32;
let i2c_parent = lookup_phandle(i2c_parent, true)?;
let i2c_parent = i2c_parent.as_i2c_controller()?;
let name = node.name().unwrap_or("i2c-mux-pinctrl");
let bus_index = i2c_parent.bus_number();
for (index, child) in node.children().enumerate() {
node.setup_pins(index).unwrap();
let channel_index = child.prop_usize("reg")? as u32;
let name = alloc::format!("{name}@{channel_index}");
let channel: Arc<dyn I2CController> = Arc::new(I2CMuxChild {
name,
channel: channel_index,
parent: i2c_parent.clone(),
});
let device = Arc::new(I2CDevice::from(channel));
let fs_name = alloc::format!("i2c{bus_index}-{channel_index}");
devfs::add_named_char_device(device, fs_name, FileMode::new(0o600)).ok();
}
Some(Arc::new(I2CMux { name }))
}
}
impl Device for I2CMux {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl Device for I2CMuxChild {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
unreachable!()
}
fn display_name(&self) -> &str {
&self.name
}
}
impl I2CController for I2CMuxChild {
fn bus_number(&self) -> u32 {
self.parent.bus_number()
}
fn child_number(&self) -> Option<u32> {
Some(self.channel)
}
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
self.parent.set_speed(speed)
}
fn capabilities(&self) -> I2CMasterInfo {
self.parent.capabilities()
}
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
// TODO channel select?
self.parent.i2c_write(address, buffer)
}
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
// TODO channel select?
self.parent.i2c_read(address, buffer)
}
}
device_tree_driver! {
compatible: ["i2c-mux-pinctrl"],
config: {
auto_pinctrl: false,
},
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
I2CMux::from_fdt(node, context)
}
}
}
+2
View File
@@ -6,6 +6,8 @@ use libk_util::OneTimeInit;
pub mod bus;
pub mod clock;
pub mod display;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
pub mod i2c;
pub mod power;
// pub mod timer;
+28
View File
@@ -39,6 +39,34 @@ impl_primitive_serde!(
usize: [read_usize, write_usize]
);
impl<T> Serialize for *const T {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_usize(self.addr())
}
}
impl<T> Serialize for *mut T {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_usize(self.addr())
}
}
impl<'de, T: 'de> Deserialize<'de> for *const T {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer
.read_usize()
.map(core::ptr::with_exposed_provenance)
}
}
impl<'de, T: 'de> Deserialize<'de> for *mut T {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer
.read_usize()
.map(core::ptr::with_exposed_provenance_mut)
}
}
impl Serialize for () {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
let _ = serializer;
+111
View File
@@ -55,6 +55,117 @@ request_group!(
}
);
/// I²C device requests
pub mod i2c {
/// I²C master device information
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct I2CMasterInfo {
/// Maximum clock rate supported by the controller
pub max_speed_hz: u32,
/// `true` if the controller supports 10-bit addressing mode
pub supports_10bit_addresses: bool,
}
request_group!(
#[doc = "I²C device requests"]
pub enum I2CRequestVariant<'de> {
#[doc = "Selects the address on the I²C bus"]
0x2000: SetAddress(u16, ()),
#[doc = "Configures device speed (in Hertz)"]
0x2001: SetSpeed(u32, u32),
#[doc = "Queries I²C master capabilities"]
0x2010: GetMasterInfo((), I2CMasterInfo),
}
);
abi_serde::impl_struct_serde!(I2CMasterInfo: [
max_speed_hz, supports_10bit_addresses
]);
}
/// SPI device requests
pub mod spi {
#![allow(missing_docs)]
use crate::{bitflags, primitive_enum};
/// Represents a single SPI transfer
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct SpiTransfer {
/// Tx buffer base pointer
pub tx_buffer: *const u8,
/// Rx buffer base pointer
pub rx_buffer: *mut u8,
/// Transfer size
pub len: usize,
}
primitive_enum! {
pub enum SpiClockPolarity: u32 {
IdleLow = 0,
IdleHigh = 1,
}
[with_serde]
}
primitive_enum! {
pub enum SpiClockPhase: u32 {
CaptureOnFirstTransition = 0,
CaptureOnSecondTransition = 1
}
[with_serde]
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SpiConfig {
pub clock_polarity: Option<SpiClockPolarity>,
pub clock_phase: Option<SpiClockPhase>,
pub frequency: Option<u32>,
pub chip_select_index: Option<u32>,
}
bitflags! {
#[doc = "SPI supported bits-per-word settings"]
pub struct SpiSupportedBitsPerWord: u32 {
#[doc = "8-bit words support"]
const BITS_8: bit 0;
}
}
/// SPI controller capabilities
pub struct SpiCapabilities {
/// Maximum clock rate supported
pub max_speed_hz: u32,
/// Supports SPI master mode
pub supports_master_mode: bool,
/// Supports SPI slave mode
pub supports_slave_mode: bool,
/// Mask
pub supported_bits_per_word: u32,
}
request_group!(
#[doc = "SPI device requests"]
pub enum SpiRequestVariant<'de> {
#[doc = "Perform a SPI transfer"]
0x2000: Transfer(SpiTransfer, usize),
#[doc = "Set SPI configuration"]
0x2001: SetConfig(SpiConfig, ()),
}
);
abi_serde::impl_struct_serde!(SpiTransfer: [
tx_buffer, rx_buffer, len
]);
abi_serde::impl_struct_serde!(SpiConfig: [
clock_polarity, clock_phase, frequency, chip_select_index
]);
}
abi_serde::impl_struct_serde!(Framebuffer: [
width, height, stride, size
]);
+48
View File
@@ -0,0 +1,48 @@
use std::{
io::{self, Read, Write},
path::Path,
};
use crate::sys::{I2CMaster as SysI2CMaster, i2c::I2CMasterImpl};
pub use crate::sys::I2CMasterConfig;
pub struct I2CMaster(I2CMasterImpl);
impl Read for I2CMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl Write for I2CMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl I2CMaster {
pub fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self> {
I2CMasterImpl::open(device, config).map(Self)
}
pub fn set_slave_address(&mut self, address: u16) -> io::Result<()> {
self.0.set_slave_address(address)
}
pub fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()> {
self.0.set_speed_hz(speed_hz)
}
pub fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize> {
self.0.smbus_read(reg, data)
}
pub fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize> {
self.0.smbus_write(reg, data)
}
}
+2
View File
@@ -5,12 +5,14 @@
pub(crate) mod sys;
pub mod fs;
pub mod i2c;
pub mod io;
pub mod mem;
pub mod net;
pub mod path;
pub mod process;
pub mod signal;
pub mod spi;
pub mod term;
pub mod time {
+21
View File
@@ -0,0 +1,21 @@
use std::{io, path::Path};
use crate::sys::{SpiDevice as SysSpiDevice, spi::SpiDeviceImpl};
pub use crate::sys::{SpiClockPhase, SpiClockPolarity, SpiConfig};
pub struct SpiDevice(SpiDeviceImpl);
impl SpiDevice {
pub fn open<P: AsRef<Path>>(device: P, config: &SpiConfig) -> io::Result<Self> {
SpiDeviceImpl::open(device, config).map(Self)
}
pub fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> io::Result<usize> {
self.0.transfer(tx, rx)
}
pub fn set_config(&mut self, config: &SpiConfig) -> io::Result<()> {
self.0.set_config(config)
}
}
+40
View File
@@ -64,6 +64,46 @@ pub(crate) trait SerialPort: Read + Write + AsRawFd + IntoRawFd + Sized {
fn open<P: AsRef<Path>>(device: P, options: &SerialPortSettings) -> io::Result<Self>;
}
#[derive(Debug, Default)]
pub struct I2CMasterConfig {
pub slave_address: Option<u16>,
pub speed_hz: Option<u32>,
}
pub(crate) trait I2CMaster: Read + Write + AsRawFd + Sized {
fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self>;
fn set_slave_address(&mut self, address: u16) -> io::Result<()>;
fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()>;
fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize>;
fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpiClockPolarity {
IdleLow,
IdleHigh,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpiClockPhase {
CaptureOnFirstTransition,
CaptureOnSecondTransition,
}
#[derive(Debug, Default)]
pub struct SpiConfig {
pub clock_polarity: Option<SpiClockPolarity>,
pub clock_phase: Option<SpiClockPhase>,
pub frequency: Option<u32>,
pub chip_select_index: Option<u32>,
}
pub(crate) trait SpiDevice: AsRawFd + Sized {
fn open<P: AsRef<Path>>(device: P, config: &SpiConfig) -> io::Result<Self>;
fn set_config(&mut self, config: &SpiConfig) -> io::Result<()>;
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> io::Result<usize>;
}
pub trait TerminalOptions: Copy {
fn normal() -> Self;
fn raw() -> Self;
@@ -0,0 +1,72 @@
use std::{
fs::{File, OpenOptions},
io::{self, Read, Write},
os::fd::{AsRawFd, RawFd},
path::Path,
};
use runtime::{abi::io::device::i2c, rt::io::device::device_request};
use crate::sys::{I2CMaster, I2CMasterConfig};
pub struct I2CMasterImpl {
file: File,
}
impl Read for I2CMasterImpl {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.file.read(buf)
}
}
impl Write for I2CMasterImpl {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.file.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.file.flush()
}
}
impl I2CMaster for I2CMasterImpl {
fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self> {
let file = OpenOptions::new().read(true).write(true).open(device)?;
let mut this = Self { file };
if let Some(address) = config.slave_address {
this.set_slave_address(address)?;
}
if let Some(speed_hz) = config.speed_hz {
this.set_speed_hz(speed_hz)?;
}
Ok(this)
}
fn set_slave_address(&mut self, address: u16) -> io::Result<()> {
let mut buffer = [0; 4];
device_request::<i2c::SetAddress>(self.file.as_raw_fd(), &mut buffer, &address)?;
Ok(())
}
fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()> {
let mut buffer = [0; 4];
device_request::<i2c::SetSpeed>(self.file.as_raw_fd(), &mut buffer, &speed_hz)?;
Ok(())
}
fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize> {
self.write_all(&[reg])?;
self.read(data)
}
fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize> {
self.write_all(&[reg])?;
self.write(data)
}
}
impl AsRawFd for I2CMasterImpl {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
@@ -1,4 +1,5 @@
pub mod fs;
pub mod i2c;
pub mod mem;
pub mod path;
pub mod pid;
@@ -7,6 +8,7 @@ pub mod poll;
pub mod pty;
pub mod serial;
pub mod socket;
pub mod spi;
pub mod term;
pub mod time;
pub mod timer;
@@ -0,0 +1,76 @@
use std::{
cmp,
fs::{File, OpenOptions},
io,
os::fd::{AsRawFd, RawFd},
path::Path,
};
use runtime::{abi::io::device::spi as sys_spi, rt::io::device::device_request};
use crate::sys::{SpiClockPhase, SpiClockPolarity, SpiConfig, SpiDevice};
pub struct SpiDeviceImpl {
file: File,
}
impl SpiDevice for SpiDeviceImpl {
fn open<P: AsRef<Path>>(device: P, config: &SpiConfig) -> io::Result<Self> {
let file = OpenOptions::new().read(true).write(true).open(device)?;
let mut this = Self { file };
this.set_config(config)?;
Ok(this)
}
fn set_config(&mut self, config: &SpiConfig) -> io::Result<()> {
let clock_polarity = config.clock_polarity.map(Into::into);
let clock_phase = config.clock_phase.map(Into::into);
let sys_config = sys_spi::SpiConfig {
clock_polarity,
clock_phase,
frequency: config.frequency,
chip_select_index: config.chip_select_index,
};
let mut buffer = [0; 256];
device_request::<sys_spi::SetConfig>(self.file.as_raw_fd(), &mut buffer, &sys_config)?;
Ok(())
}
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> io::Result<usize> {
let xfer = sys_spi::SpiTransfer {
tx_buffer: tx.as_ptr(),
rx_buffer: rx.as_mut_ptr(),
len: cmp::min(tx.len(), rx.len()),
};
let mut buffer = [0; 32];
let len = device_request::<sys_spi::Transfer>(self.file.as_raw_fd(), &mut buffer, &xfer)?;
Ok(len)
}
}
impl AsRawFd for SpiDeviceImpl {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
impl From<SpiClockPolarity> for sys_spi::SpiClockPolarity {
fn from(value: SpiClockPolarity) -> Self {
match value {
SpiClockPolarity::IdleLow => Self::IdleLow,
SpiClockPolarity::IdleHigh => Self::IdleHigh,
}
}
}
impl From<SpiClockPhase> for sys_spi::SpiClockPhase {
fn from(value: SpiClockPhase) -> Self {
match value {
SpiClockPhase::CaptureOnFirstTransition => Self::CaptureOnFirstTransition,
SpiClockPhase::CaptureOnSecondTransition => Self::CaptureOnSecondTransition,
}
}
}
+142 -44
View File
@@ -1,62 +1,160 @@
#![feature(let_chains)]
use std::collections::{HashSet, VecDeque};
use stuff::{
autocomplete,
readline::{Buffer, NonEmptyVec, Readline, ReadlineProvider},
use std::{
io::{self, Write, stdout},
path::{Path, PathBuf},
process::ExitCode,
thread,
time::Duration,
};
#[derive(Default)]
struct P {
history: VecDeque<String>,
use clap::Parser;
use cross::{
i2c::{I2CMaster, I2CMasterConfig},
spi::{SpiConfig, SpiDevice},
};
#[derive(Debug, Parser)]
struct Args {
#[clap(subcommand)]
command: Subcommand,
}
impl ReadlineProvider for P {
fn completions(&mut self, buffer: &Buffer) -> Option<NonEmptyVec<String>> {
let word_before_cursor = buffer.word_before_cursor();
let is_first = buffer.is_first_word();
#[derive(Debug, clap::Subcommand)]
enum Subcommand {
M41T80 {
#[clap(short, long, default_value_t = 0x40, help = "I²C device address")]
address: u8,
#[clap(help = "I²C controller path")]
device: PathBuf,
},
JedecFlash {
#[clap(short, long, default_value_t = 4 * 1024 * 1024, help = "SPI flash capacity")]
capacity: usize,
#[clap(help = "SPI device path")]
device: PathBuf,
},
}
let mut entries = HashSet::new();
if let Some(word) = word_before_cursor
&& !is_first
{
autocomplete::complete_from_cwd(&mut entries, word, &Default::default());
} else {
let prefix = word_before_cursor.unwrap_or("");
autocomplete::complete_binary_from_env(&mut entries, prefix);
}
struct M41T80 {
master: I2CMaster,
}
let mut entries = NonEmptyVec::try_from_iter(entries.drain()).ok()?;
entries.sort();
#[derive(Debug, PartialEq)]
struct Time {
year: u16,
mon: u8,
day: u8,
Some(entries)
hour: u8,
min: u8,
sec: u8,
millis: u16,
}
impl M41T80 {
fn open<P: AsRef<Path>>(path: P, config: &I2CMasterConfig) -> io::Result<Self> {
let master = I2CMaster::open(path, config)?;
Ok(Self { master })
}
fn push_history(&mut self, entry: &str) {
self.history.push_back(entry.into());
}
fn read_time(&mut self) -> io::Result<Time> {
let mut buffer = [0; 9];
self.master.smbus_read(0x00, &mut buffer)?;
fn lookup_history(&mut self, depth: usize) -> Option<String> {
if depth >= self.history.len() {
return None;
}
let index = self.history.len() - depth - 1;
Some(self.history[index].clone())
let millis = ((buffer[0] >> 4) as u16 * 100) | ((buffer[0] & 0xF) as u16 * 10);
let sec = ((buffer[1] >> 4) & 0x7) * 10 + (buffer[1] & 0xF);
let min = ((buffer[2] >> 4) & 0x7) * 10 + (buffer[2] & 0xF);
let hour = ((buffer[3] >> 4) & 0x3) * 10 + (buffer[3] & 0xF);
let day = ((buffer[5] >> 4) & 0x3) * 10 + (buffer[5] & 0xF);
let mon = ((buffer[6] >> 4) & 0x1) * 10 + (buffer[6] & 0xF);
let year = ((buffer[7] >> 4) as u16 * 10) + ((buffer[7] & 0xF) as u16) + 2000;
Ok(Time {
millis,
sec,
min,
hour,
year,
day,
mon,
})
}
}
fn main() {
logsink::setup_logging(true);
let mut readline = Readline::new(P::default()).unwrap();
fn run_m41t80(device: PathBuf, address: u8) -> io::Result<()> {
// let device = "/dev/i2c0";
let i2c_config = I2CMasterConfig {
slave_address: Some(address as _),
speed_hz: None,
};
let mut m41t80 = M41T80::open(device, &i2c_config)?;
loop {
let Some(line) = readline.readline().unwrap() else {
break;
};
println!("{line:?}");
if line == "exit" {
let time = m41t80.read_time()?;
print!(
"\r{:04}/{:02}/{:02} {:02}:{:02}:{:02}",
time.year, time.mon, time.day, time.hour, time.min, time.sec,
);
stdout().flush().ok();
thread::sleep(Duration::from_millis(100));
}
}
fn run_jedec(device: PathBuf, capacity: usize) -> io::Result<()> {
let config = SpiConfig::default();
let mut spi = SpiDevice::open(device, &config)?;
let tx = [0x13, 0x00, 0x00, 0x00, 0x00];
let mut rx = [0; 5];
spi.transfer(&tx, &mut rx).unwrap();
let mut memory = vec![0xFF; capacity];
const READ_BUFFER: usize = 1024;
let tx_word = [0xFF; READ_BUFFER];
let mut rx_word = [0; READ_BUFFER];
let mut bytes_read = 0;
for i in 0..capacity / READ_BUFFER {
if spi.transfer(&tx_word, &mut rx_word)? != READ_BUFFER {
break;
}
memory[i * READ_BUFFER..i * READ_BUFFER + READ_BUFFER].copy_from_slice(&rx_word);
bytes_read += READ_BUFFER;
}
for (i, &byte) in memory.iter().enumerate() {
if i >= bytes_read {
print!("??");
} else {
print!("{:02x}", byte);
}
if (i + 1) % 2 == 0 {
print!(" ");
}
if (i + 1) % 32 == 0 {
println!();
}
}
Ok(())
}
fn run(args: Args) -> io::Result<()> {
match args.command {
Subcommand::M41T80 { address, device } => run_m41t80(device, address),
Subcommand::JedecFlash { capacity, device } => run_jedec(device, capacity),
}
}
fn main() -> ExitCode {
logsink::setup_logging(true);
let args = Args::parse();
match run(args) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{error}");
ExitCode::FAILURE
}
}
}