diff --git a/hal/src/lib.rs b/hal/src/lib.rs
index 5e79d738a..1a1123f0d 100644
--- a/hal/src/lib.rs
+++ b/hal/src/lib.rs
@@ -53,23 +53,9 @@ pub trait Configuration
{
/// Platform peripheral base trait
///
-/// Defines the fundamental operations that all platform peripherals must implement.
-/// This trait provides a unified interface for enabling and disabling peripheral devices
-/// across different hardware platforms.
-///
-/// All peripheral drivers should implement this trait to ensure consistent power
-/// management and resource control capabilities.
-///
-/// # Trait Bounds
-///
-/// This trait requires implementations to be:
-/// - `Sync` - Safe to share references between threads, Peripherals are often accessed from multiple contexts.
-/// - `Send` - Safe to transfer ownership between threads, Peripherals always exists in system memory.
-/// - `'static` - Lives for the entire duration of the program
-///
-/// These bounds ensure that peripheral instances can be safely used in multi-threaded
-/// environments and stored in static variables, which is common in embedded systems.
-pub trait PlatPeri: Sync + Send + 'static {
+/// Peripherals would be a global singleton,
+/// and the HAL layer would provide a static reference to the peripheral.
+pub trait PlatPeri: 'static {
fn enable(&self) {}
fn disable(&self) {}
}
diff --git a/kernel/src/boards/seeed_xiao_esp32c3/Kconfig b/kernel/src/boards/seeed_xiao_esp32c3/Kconfig
index 0bc6e0a0f..480c4de35 100644
--- a/kernel/src/boards/seeed_xiao_esp32c3/Kconfig
+++ b/kernel/src/boards/seeed_xiao_esp32c3/Kconfig
@@ -19,7 +19,27 @@ choice
Set irq priority bits to 8.
endchoice
-choice
+config LCD
+ bool "Enable LCD device"
+ default n
+ help
+ Enable LCD module
+
+config FT6336U
+ bool "Enable touch screen device"
+ default n
+ help
+ Enable touch screen
+
+config GPIO
+ bool "Enable gpio device"
+ default n
+ help
+ Enable gpio device
+
+if LCD
+
+ choice
prompt "LCD controller"
default ST7789
@@ -32,7 +52,9 @@ choice
bool "Enable LCD ST7796"
help
Enable the ST7796 LCD controller.
-endchoice
+ endchoice
+
+endif
config BME280
bool "BME280 Sensor"
diff --git a/kernel/src/boards/seeed_xiao_esp32c3/mod.rs b/kernel/src/boards/seeed_xiao_esp32c3/mod.rs
index 1e897fbfa..27eebb7d5 100644
--- a/kernel/src/boards/seeed_xiao_esp32c3/mod.rs
+++ b/kernel/src/boards/seeed_xiao_esp32c3/mod.rs
@@ -119,6 +119,9 @@ const RTC_CNTL_WDTCONFIG0_REG: usize = RTC_CNTL_BASE + 0x90;
const USB_SERIAL_JTAG_IRQ: Interrupt = Interrupt::new(26, USB_SERIAL_JTAG_INT_NUM);
const SYSTIMER_TARGET0_IRQ: Interrupt = Interrupt::new(37, TARGET0_INT_NUM);
+const LED_DEVICE_MAJOR: usize = 242;
+const LED_B_DEVICE_MINOR: usize = 0;
+const LED_R_DEVICE_MINOR: usize = 1;
pub(crate) fn init() {
assert!(!local_irq_enabled());
@@ -189,6 +192,10 @@ crate::define_peripheral! {
blueos_driver::gpio::esp32_gpio::Esp32GpioOutputPin::new(21)),
(lcd_cs, blueos_driver::gpio::esp32_gpio::Esp32GpioOutputPin,
blueos_driver::gpio::esp32_gpio::Esp32GpioOutputPin::new(20)),
+ (led_b, blueos_driver::gpio::esp32_gpio::Esp32GpioOutputPin,
+ blueos_driver::gpio::esp32_gpio::Esp32GpioOutputPin::new(2)),
+ (led_r, blueos_driver::gpio::esp32_gpio::Esp32GpioOutputPin,
+ blueos_driver::gpio::esp32_gpio::Esp32GpioOutputPin::new(3)),
}
#[inline(always)]
@@ -215,6 +222,8 @@ crate::define_pin_states!(
(5, 1, false, true, false, 2, None, None, true, false), // lcd dc
(4, 1, false, true, false, 2, None, None, true, false), // lcd rst
(21, 1, false, true, false, 2, None, None, true, false), // touch rst
+ (2, 1, false, true, false, 2, None, None, true, false), // led blue
+ (3, 1, false, true, false, 2, None, None, true, false), // led red
);
crate::define_bus! {
@@ -226,6 +235,7 @@ crate::define_bus! {
crate::drivers::lcd::st7789::St7789Config:: {
rst: get_device!(rst_pin),
dc: get_device!(dc_pin),
+ cs: Some(get_device!(lcd_cs)),
}
),
#[cfg(st7796)]
@@ -233,12 +243,27 @@ crate::define_bus! {
crate::drivers::lcd::st7796::St7796Config:: {
rst: get_device!(rst_pin),
dc: get_device!(dc_pin),
+ cs: Some(get_device!(lcd_cs)),
orientation: mipidsi::options::Orientation::new()
.rotate(mipidsi::options::Rotation::Deg0)
.flip_horizontal(),
}
),
),
+ (
+ i2c_bus,
+ crate::devices::i2c_core::block_i2c::BlockI2c,
+ #[cfg(ft6336u)]
+ (ft6336u, crate::drivers::input::ft6336u::Ft6336uConfig,
+ crate::drivers::input::ft6336u::Ft6336uConfig:: {
+ rst: get_device!(touch_rst_pin),
+ }
+ ),
+ #[cfg(bme280)]
+ (bme280, crate::drivers::sensor::bme280::Bme280Config,
+ crate::drivers::sensor::bme280::Bme280Config::new(0x76)
+ ),
+ ),
}
pub(crate) fn init_spi_bus() {
@@ -283,4 +308,64 @@ pub(crate) fn init_spi_bus() {
}
}
-pub(crate) fn init_i2c_bus() {}
+pub(crate) fn init_i2c_bus() {
+ use crate::{
+ devices::{bus::Bus, i2c_core::block_i2c::BlockI2c},
+ drivers::InitDriver,
+ };
+ use alloc::sync::Arc;
+
+ if let Ok(block_i2c) = BlockI2c::new(get_device!(i2c0)) {
+ let i2c_bus = Arc::new(Bus::new(block_i2c));
+ for device in crate::boards::get_bus_devices!(i2c_bus) {
+ i2c_bus.register_device(device).unwrap();
+ }
+
+ #[cfg(ft6336u)]
+ if let Ok(driver) =
+ i2c_bus.probe_driver(&crate::drivers::input::ft6336u::Ft6336uDriverModule::<
+ blueos_driver::gpio::esp32_gpio::Esp32GpioOutputPin,
+ >::new())
+ {
+ if let Err(error) = driver.init(&i2c_bus) {
+ log::warn!("Failed to initialize FT6336U driver: {}", error);
+ }
+ } else {
+ log::warn!("Failed to probe FT6336U driver");
+ }
+
+ #[cfg(bme280)]
+ if let Ok(driver) =
+ i2c_bus.probe_driver(&crate::drivers::sensor::bme280::Bme280DriverModule)
+ {
+ if let Err(error) = driver.init(&i2c_bus) {
+ log::warn!("Failed to initialize BME280 driver: {}", error);
+ }
+ } else {
+ log::warn!("Failed to probe BME280 driver");
+ }
+ } else {
+ log::warn!("Failed to initialize ESP32-C3 I2C0 bus");
+ }
+}
+
+pub(crate) fn init_gpio() {
+ crate::devices::gpio::GeneralGpio::new(
+ get_device!(led_b),
+ Some(crate::devices::gpio::Level::High),
+ )
+ .register(
+ alloc::string::String::from("led_b"),
+ crate::devices::DeviceId::new(LED_DEVICE_MAJOR, LED_B_DEVICE_MINOR),
+ )
+ .expect("Failed to register led_b");
+ crate::devices::gpio::GeneralGpio::new(
+ get_device!(led_r),
+ Some(crate::devices::gpio::Level::High),
+ )
+ .register(
+ alloc::string::String::from("led_r"),
+ crate::devices::DeviceId::new(LED_DEVICE_MAJOR, LED_R_DEVICE_MINOR),
+ )
+ .expect("Failed to register led_r");
+}
diff --git a/kernel/src/boot.rs b/kernel/src/boot.rs
index a963289e3..6c1a9f139 100644
--- a/kernel/src/boot.rs
+++ b/kernel/src/boot.rs
@@ -115,6 +115,12 @@ extern "C" fn init() {
// initialize virtio
virtio::init_virtio(&fdt);
}
+ #[cfg(spi_core)]
+ crate::boards::init_spi_bus();
+ #[cfg(i2c_core)]
+ crate::boards::init_i2c_bus();
+ #[cfg(gpio)]
+ crate::boards::init_gpio();
#[cfg(enable_vfs)]
init_vfs();
@@ -129,11 +135,6 @@ extern "C" fn init() {
net::net_manager::init();
}
- #[cfg(spi_core)]
- crate::boards::init_spi_bus();
- #[cfg(i2c_core)]
- crate::boards::init_i2c_bus();
-
// it's an bug in fact, but at now we use a workaround let newlib do the c++ runtime initialization
#[cfg(not(target_board = "newlib_mps3_an547"))]
run_init_array();
@@ -197,7 +198,12 @@ fn init_apps() {
unsafe {
let mut app = addr_of!(__bk_app_array_start);
while app < addr_of!(__bk_app_array_end) {
- thread::Builder::new(thread::Entry::C(*app)).start();
+ let stack =
+ thread::Stack::from_size(blueos_kconfig::CONFIG_MAIN_THREAD_STACK_SIZE as usize)
+ .expect("Invalid main thread stack size");
+ thread::Builder::new(thread::Entry::C(*app))
+ .set_stack(stack)
+ .start();
app = app.offset(1);
}
}
diff --git a/kernel/src/devices/bus/mod.rs b/kernel/src/devices/bus/mod.rs
index 6c74d1901..2f3d64d09 100644
--- a/kernel/src/devices/bus/mod.rs
+++ b/kernel/src/devices/bus/mod.rs
@@ -33,12 +33,7 @@ pub struct Bus {
unsafe impl Send for Bus {}
unsafe impl Sync for Bus {}
-pub trait BusInterface: Sync + Send + Sized {
- type Region;
- fn read_region(&self, region: Self::Region, buffer: &mut [u8]) -> crate::drivers::Result<()>;
-
- fn write_region(&self, region: Self::Region, data: &[u8]) -> crate::drivers::Result<()>;
-}
+pub trait BusInterface: Sized {}
impl Bus {
pub fn new(intf: B) -> Self {
diff --git a/kernel/src/devices/framebuffer.rs b/kernel/src/devices/framebuffer.rs
index 12044399d..768f71f68 100644
--- a/kernel/src/devices/framebuffer.rs
+++ b/kernel/src/devices/framebuffer.rs
@@ -14,9 +14,9 @@
use crate::devices::{Device, DeviceClass, DeviceId, DeviceManager};
use alloc::{format, string::String, sync::Arc, vec, vec::Vec};
+use blueos_infra::tinyrwlock::RwLock;
use embedded_io::ErrorKind;
use libc::{FBIOGET_FSCREENINFO, FBIOGET_VSCREENINFO, FBIOPUT_VSCREENINFO};
-use spin::Mutex;
/// Linux framebuffer character-device major number.
pub const FRAMEBUFFER_MAJOR: usize = 29;
@@ -171,7 +171,7 @@ unsafe fn store_user_variable_info(
}
/// Driver-facing framebuffer operations.
-pub trait FramebufferOps: Send + Sync {
+pub trait FramebufferOps {
/// Return fixed framebuffer metadata.
fn fixed_info(&self) -> Result;
@@ -180,31 +180,34 @@ pub trait FramebufferOps: Send + Sync {
/// Validate and apply a variable-info update, returning the effective state.
fn set_variable_info(
- &self,
+ &mut self,
variable_info: &FramebufferVariableInfo,
) -> Result;
/// Read framebuffer bytes starting at `offset`.
- fn read_bytes(&self, offset: u64, buf: &mut [u8]) -> Result;
+ fn read_bytes(&mut self, offset: u64, buf: &mut [u8]) -> Result;
/// Write framebuffer bytes starting at `offset`.
- fn write_bytes(&self, offset: u64, buf: &[u8]) -> Result;
+ fn write_bytes(&mut self, offset: u64, buf: &[u8]) -> Result;
/// Return the framebuffer byte length.
fn byte_len(&self) -> Result;
}
/// Character-device wrapper for a framebuffer implementation.
-pub struct FramebufferDevice {
+pub struct FramebufferDevice {
name: String,
id: DeviceId,
- ops: Arc,
+ ops: Arc>,
}
-impl FramebufferDevice {
+unsafe impl Send for FramebufferDevice {}
+unsafe impl Sync for FramebufferDevice {}
+
+impl FramebufferDevice {
/// Create a framebuffer device named `fb{index}` with Linux framebuffer major `29`.
#[must_use]
- pub fn new(index: usize, ops: Arc) -> Self {
+ pub fn new(index: usize, ops: Arc>) -> Self {
Self::with_id(
format!("fb{index}"),
DeviceId::new(FRAMEBUFFER_MAJOR, index),
@@ -214,12 +217,12 @@ impl FramebufferDevice {
/// Create a framebuffer device with an explicit name and device id.
#[must_use]
- pub fn with_id(name: String, id: DeviceId, ops: Arc) -> Self {
+ pub fn with_id(name: String, id: DeviceId, ops: Arc>) -> Self {
Self { name, id, ops }
}
/// Register a framebuffer device named `fb{index}`.
- pub fn register(index: usize, ops: Arc) -> Result<(), ErrorKind> {
+ pub fn register(index: usize, ops: Arc>) -> Result<(), ErrorKind> {
Self::register_device(Arc::new(Self::new(index, ops)))
}
@@ -244,12 +247,12 @@ impl FramebufferDevice {
/// Return fixed framebuffer metadata.
pub fn fixed_info(&self) -> Result {
- self.ops.fixed_info()
+ self.ops.read().fixed_info()
}
/// Return variable framebuffer metadata.
pub fn variable_info(&self) -> Result {
- self.ops.variable_info()
+ self.ops.read().variable_info()
}
/// Validate and apply a variable-info update, returning the effective state.
@@ -257,11 +260,11 @@ impl FramebufferDevice {
&self,
variable_info: &FramebufferVariableInfo,
) -> Result {
- self.ops.set_variable_info(variable_info)
+ self.ops.write().set_variable_info(variable_info)
}
}
-impl Device for FramebufferDevice {
+impl Device for FramebufferDevice {
fn name(&self) -> String {
self.name.clone()
}
@@ -279,7 +282,7 @@ impl Device for FramebufferDevice {
return Ok(0);
}
- let byte_len = self.ops.byte_len()?;
+ let byte_len = self.ops.read().byte_len()?;
if pos >= byte_len {
return Ok(0);
}
@@ -287,7 +290,7 @@ impl Device for FramebufferDevice {
let remaining = byte_len - pos;
let read_len =
usize::try_from(remaining).map_or(buf.len(), |remaining| remaining.min(buf.len()));
- self.ops.read_bytes(pos, &mut buf[..read_len])
+ self.ops.write().read_bytes(pos, &mut buf[..read_len])
}
fn write(&self, pos: u64, buf: &[u8], _is_nonblocking: bool) -> Result {
@@ -295,7 +298,7 @@ impl Device for FramebufferDevice {
return Ok(0);
}
- let byte_len = self.ops.byte_len()?;
+ let byte_len = self.ops.read().byte_len()?;
if pos >= byte_len {
return Ok(0);
}
@@ -303,17 +306,17 @@ impl Device for FramebufferDevice {
let remaining = byte_len - pos;
let write_len =
usize::try_from(remaining).map_or(buf.len(), |remaining| remaining.min(buf.len()));
- self.ops.write_bytes(pos, &buf[..write_len])
+ self.ops.write().write_bytes(pos, &buf[..write_len])
}
fn ioctl(&self, request: u32, arg: usize) -> Result<(), ErrorKind> {
match request {
req if req == FBIOGET_FSCREENINFO => {
- let fixed_info = self.ops.fixed_info()?;
+ let fixed_info = self.ops.read().fixed_info()?;
unsafe { store_user_fixed_info(arg as *mut FramebufferFixedInfo, &fixed_info) }
}
req if req == FBIOGET_VSCREENINFO => {
- let variable_info = self.ops.variable_info()?;
+ let variable_info = self.ops.read().variable_info()?;
unsafe {
store_user_variable_info(arg as *mut FramebufferVariableInfo, &variable_info)
}
@@ -321,7 +324,7 @@ impl Device for FramebufferDevice {
req if req == FBIOPUT_VSCREENINFO => {
let requested_info =
unsafe { load_user_variable_info(arg as *const FramebufferVariableInfo)? };
- let effective_info = self.ops.set_variable_info(&requested_info)?;
+ let effective_info = self.ops.write().set_variable_info(&requested_info)?;
unsafe {
store_user_variable_info(arg as *mut FramebufferVariableInfo, &effective_info)
}
@@ -331,6 +334,6 @@ impl Device for FramebufferDevice {
}
fn capacity(&self) -> Result {
- self.ops.byte_len()
+ self.ops.read().byte_len()
}
}
diff --git a/kernel/src/devices/gpio/mod.rs b/kernel/src/devices/gpio/mod.rs
index 42cdf1f14..0b76b52ce 100644
--- a/kernel/src/devices/gpio/mod.rs
+++ b/kernel/src/devices/gpio/mod.rs
@@ -12,12 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use core::cell::Cell;
+
+use crate::{
+ devices::{Device, DeviceClass, DeviceId, DeviceManager},
+ sync::SpinLock,
+};
+use alloc::{string::String, sync::Arc};
+use embedded_io::ErrorKind;
+
pub struct GeneralGpio {
inner: &'static T,
+ level: Option,
}
impl !Sync for GeneralGpio {}
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Level {
Low,
High,
@@ -25,18 +36,122 @@ pub(crate) enum Level {
impl GeneralGpio {
pub fn new(inner: &'static T, level: Option) -> Self {
- let mut gpio = GeneralGpio { inner };
+ let mut gpio = GeneralGpio { inner, level: None };
if let Some(level) = level {
gpio.set_level(level).ok();
}
gpio
}
+ /// Register this GPIO as a character device.
+ ///
+ /// Reads return `0\n` or `1\n`; writes accept ASCII `0` or `1`, with
+ /// optional surrounding whitespace.
+ pub fn register(self, name: String, id: DeviceId) -> Result<(), ErrorKind> {
+ let device = Arc::new(GeneralGpioDevice::new(name.clone(), id, self));
+ DeviceManager::get().register_device(name, device)
+ }
+
fn set_level(&mut self, level: Level) -> crate::drivers::Result<()> {
match level {
- Level::Low => self.inner.set_low().map_err(|_| crate::error::code::EIO),
- Level::High => self.inner.set_high().map_err(|_| crate::error::code::EIO),
+ Level::Low => self.inner.set_low().map_err(|_| crate::error::code::EIO)?,
+ Level::High => self.inner.set_high().map_err(|_| crate::error::code::EIO)?,
+ };
+ self.level = Some(level);
+ Ok(())
+ }
+}
+
+struct GeneralGpioDevice {
+ name: String,
+ id: DeviceId,
+ inner: SpinLock<&'static T>,
+ level: Cell