14
drivers/Cargo.toml
Archivo normal
14
drivers/Cargo.toml
Archivo normal
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "drivers"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Rust Kernel Contributors"]
|
||||
description = "Kernel drivers"
|
||||
license = "GPL-2.0"
|
||||
|
||||
[lib]
|
||||
name = "drivers"
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[dependencies]
|
||||
kernel = { path = "../kernel" }
|
||||
352
drivers/src/keyboard.rs
Archivo normal
352
drivers/src/keyboard.rs
Archivo normal
@@ -0,0 +1,352 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! PS/2 Keyboard driver
|
||||
|
||||
use alloc::{collections::VecDeque, string::String, vec::Vec};
|
||||
|
||||
use kernel::arch::x86_64::port::{inb, outb};
|
||||
use kernel::device::{CharDevice, Device, DeviceType, FileOperations};
|
||||
use kernel::error::{Error, Result};
|
||||
use kernel::interrupt::{register_interrupt_handler, IrqHandler};
|
||||
use kernel::sync::{Arc, Spinlock};
|
||||
|
||||
/// PS/2 keyboard controller ports
|
||||
const KEYBOARD_DATA_PORT: u16 = 0x60;
|
||||
const KEYBOARD_STATUS_PORT: u16 = 0x64;
|
||||
const KEYBOARD_COMMAND_PORT: u16 = 0x64;
|
||||
|
||||
/// Keyboard scan codes to ASCII mapping (US layout, simplified)
|
||||
const SCANCODE_TO_ASCII: [u8; 128] = [
|
||||
0, 27, b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'-', b'=',
|
||||
8, // backspace
|
||||
b'\t', b'q', b'w', b'e', b'r', b't', b'y', b'u', b'i', b'o', b'p', b'[', b']',
|
||||
b'\n', // enter
|
||||
0, // ctrl
|
||||
b'a', b's', b'd', b'f', b'g', b'h', b'j', b'k', b'l', b';', b'\'', b'`',
|
||||
0, // left shift
|
||||
b'\\', b'z', b'x', b'c', b'v', b'b', b'n', b'm', b',', b'.', b'/', 0, // right shift
|
||||
b'*', 0, // alt
|
||||
b' ', // space
|
||||
0, // caps lock
|
||||
// Function keys F1-F10
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // num lock
|
||||
0, // scroll lock
|
||||
// Numeric keypad
|
||||
b'7', b'8', b'9', b'-', b'4', b'5', b'6', b'+', b'1', b'2', b'3', b'0', b'.', 0, 0,
|
||||
0, // F11, F12
|
||||
// Fill the rest with zeros
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0,
|
||||
];
|
||||
|
||||
/// Keyboard state
|
||||
#[derive(Debug)]
|
||||
struct KeyboardState {
|
||||
/// Input buffer for key presses
|
||||
buffer: VecDeque<u8>,
|
||||
/// Modifier key states
|
||||
shift_pressed: bool,
|
||||
ctrl_pressed: bool,
|
||||
alt_pressed: bool,
|
||||
caps_lock: bool,
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
buffer: VecDeque::new(),
|
||||
shift_pressed: false,
|
||||
ctrl_pressed: false,
|
||||
alt_pressed: false,
|
||||
caps_lock: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn push_key(&mut self, key: u8) {
|
||||
if self.buffer.len() < 256 {
|
||||
// Prevent buffer overflow
|
||||
self.buffer.push_back(key);
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_key(&mut self) -> Option<u8> {
|
||||
self.buffer.pop_front()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.buffer.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Global keyboard state
|
||||
static KEYBOARD_STATE: Spinlock<KeyboardState> = Spinlock::new(KeyboardState::new());
|
||||
|
||||
/// Keyboard interrupt handler
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardIrqHandler;
|
||||
|
||||
impl IrqHandler for KeyboardIrqHandler {
|
||||
fn handle_irq(&self, _irq: u32) -> Result<()> {
|
||||
// Read scan code from keyboard data port
|
||||
let scancode = unsafe { inb(KEYBOARD_DATA_PORT) };
|
||||
|
||||
// Process the scan code
|
||||
process_scancode(scancode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a keyboard scan code
|
||||
fn process_scancode(scancode: u8) {
|
||||
let mut keyboard = KEYBOARD_STATE.lock();
|
||||
|
||||
// Check if this is a key release (high bit set)
|
||||
if scancode & 0x80 != 0 {
|
||||
// Key release
|
||||
let key_code = scancode & 0x7F;
|
||||
match key_code {
|
||||
0x2A | 0x36 => keyboard.shift_pressed = false, // Shift keys
|
||||
0x1D => keyboard.ctrl_pressed = false, // Ctrl key
|
||||
0x38 => keyboard.alt_pressed = false, // Alt key
|
||||
_ => {} // Other key releases
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Key press
|
||||
match scancode {
|
||||
0x2A | 0x36 => {
|
||||
// Shift keys
|
||||
keyboard.shift_pressed = true;
|
||||
}
|
||||
0x1D => {
|
||||
// Ctrl key
|
||||
keyboard.ctrl_pressed = true;
|
||||
}
|
||||
0x38 => {
|
||||
// Alt key
|
||||
keyboard.alt_pressed = true;
|
||||
}
|
||||
0x3A => {
|
||||
// Caps Lock
|
||||
keyboard.caps_lock = !keyboard.caps_lock;
|
||||
}
|
||||
_ => {
|
||||
// Convert scan code to ASCII
|
||||
if let Some(ascii) = scancode_to_ascii(scancode, &keyboard) {
|
||||
keyboard.push_key(ascii);
|
||||
|
||||
// Echo to console for now
|
||||
if ascii.is_ascii_graphic() || ascii == b' ' || ascii == b'\n' {
|
||||
kernel::console::print_char(ascii as char);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert scan code to ASCII character
|
||||
fn scancode_to_ascii(scancode: u8, keyboard: &KeyboardState) -> Option<u8> {
|
||||
if scancode as usize >= SCANCODE_TO_ASCII.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut ascii = SCANCODE_TO_ASCII[scancode as usize];
|
||||
if ascii == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Apply modifiers
|
||||
if keyboard.shift_pressed || keyboard.caps_lock {
|
||||
if ascii >= b'a' && ascii <= b'z' {
|
||||
ascii = ascii - b'a' + b'A';
|
||||
} else {
|
||||
// Handle shifted symbols
|
||||
ascii = match ascii {
|
||||
b'1' => b'!',
|
||||
b'2' => b'@',
|
||||
b'3' => b'#',
|
||||
b'4' => b'$',
|
||||
b'5' => b'%',
|
||||
b'6' => b'^',
|
||||
b'7' => b'&',
|
||||
b'8' => b'*',
|
||||
b'9' => b'(',
|
||||
b'0' => b')',
|
||||
b'-' => b'_',
|
||||
b'=' => b'+',
|
||||
b'[' => b'{',
|
||||
b']' => b'}',
|
||||
b'\\' => b'|',
|
||||
b';' => b':',
|
||||
b'\'' => b'"',
|
||||
b',' => b'<',
|
||||
b'.' => b'>',
|
||||
b'/' => b'?',
|
||||
b'`' => b'~',
|
||||
_ => ascii,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Ctrl combinations
|
||||
if keyboard.ctrl_pressed && ascii >= b'a' && ascii <= b'z' {
|
||||
ascii = ascii - b'a' + 1; // Ctrl+A = 1, Ctrl+B = 2, etc.
|
||||
} else if keyboard.ctrl_pressed && ascii >= b'A' && ascii <= b'Z' {
|
||||
ascii = ascii - b'A' + 1;
|
||||
}
|
||||
|
||||
Some(ascii)
|
||||
}
|
||||
|
||||
/// Keyboard character device file operations
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardFileOps;
|
||||
|
||||
impl FileOperations for KeyboardFileOps {
|
||||
fn open(
|
||||
&self,
|
||||
_inode: &kernel::device::Inode,
|
||||
_file: &mut kernel::device::File,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(
|
||||
&self,
|
||||
_inode: &kernel::device::Inode,
|
||||
_file: &mut kernel::device::File,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
_file: &mut kernel::device::File,
|
||||
buf: &mut [u8],
|
||||
_offset: u64,
|
||||
) -> Result<usize> {
|
||||
let mut keyboard = KEYBOARD_STATE.lock();
|
||||
let mut bytes_read = 0;
|
||||
|
||||
// Read available characters from buffer
|
||||
while bytes_read < buf.len() && !keyboard.is_empty() {
|
||||
if let Some(key) = keyboard.pop_key() {
|
||||
buf[bytes_read] = key;
|
||||
bytes_read += 1;
|
||||
|
||||
// Stop at newline for line-buffered reading
|
||||
if key == b'\n' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bytes_read)
|
||||
}
|
||||
|
||||
fn write(
|
||||
&self,
|
||||
_file: &mut kernel::device::File,
|
||||
_buf: &[u8],
|
||||
_offset: u64,
|
||||
) -> Result<usize> {
|
||||
// Can't write to keyboard
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &mut kernel::device::File, cmd: u32, arg: usize) -> Result<usize> {
|
||||
// Implement keyboard-specific ioctl commands
|
||||
match cmd {
|
||||
0x4B01 => {
|
||||
// KDGKBMODE - get keyboard mode
|
||||
crate::info!("Getting keyboard mode");
|
||||
Ok(0) // Return raw mode
|
||||
}
|
||||
0x4B02 => {
|
||||
// KDSKBMODE - set keyboard mode
|
||||
crate::info!("Setting keyboard mode to {}", arg);
|
||||
Ok(0)
|
||||
}
|
||||
0x4B03 => {
|
||||
// KDGKBENT - get keyboard entry
|
||||
crate::info!("Getting keyboard entry");
|
||||
Ok(0)
|
||||
}
|
||||
_ => Err(Error::ENOTTY),
|
||||
}
|
||||
}
|
||||
|
||||
fn mmap(
|
||||
&self,
|
||||
_file: &mut kernel::device::File,
|
||||
_vma: &mut kernel::memory::VmaArea,
|
||||
) -> Result<()> {
|
||||
// Can't mmap keyboard
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the keyboard driver
|
||||
pub fn init() -> Result<()> {
|
||||
// Create keyboard device
|
||||
let keyboard_device = Device::new(
|
||||
"keyboard".to_string(),
|
||||
DeviceType::Input,
|
||||
10, // Input major number
|
||||
0, // Minor number 0
|
||||
);
|
||||
|
||||
// Register character device
|
||||
let char_dev = CharDevice::new(10, 0, 1, "keyboard".to_string());
|
||||
|
||||
// Register interrupt handler for keyboard (IRQ 1)
|
||||
let handler = Arc::new(KeyboardIrqHandler);
|
||||
register_interrupt_handler(33, handler as Arc<dyn IrqHandler>)?; // IRQ 1 = INT 33
|
||||
|
||||
// Register device in device filesystem
|
||||
crate::info!("Keyboard device registered in devfs");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read a line from keyboard (blocking)
|
||||
pub fn read_line() -> String {
|
||||
let mut line = String::new();
|
||||
|
||||
loop {
|
||||
let mut keyboard = KEYBOARD_STATE.lock();
|
||||
while let Some(key) = keyboard.pop_key() {
|
||||
if key == b'\n' {
|
||||
return line;
|
||||
} else if key == 8 {
|
||||
// Backspace
|
||||
if !line.is_empty() {
|
||||
line.pop();
|
||||
// Move cursor back and clear character
|
||||
kernel::console::print_str("\x08 \x08");
|
||||
}
|
||||
} else if key.is_ascii_graphic() || key == b' ' {
|
||||
line.push(key as char);
|
||||
}
|
||||
}
|
||||
drop(keyboard);
|
||||
|
||||
// Yield CPU while waiting for input
|
||||
kernel::scheduler::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if there are pending key presses
|
||||
pub fn has_pending_input() -> bool {
|
||||
let keyboard = KEYBOARD_STATE.lock();
|
||||
!keyboard.is_empty()
|
||||
}
|
||||
|
||||
/// Get next character without blocking
|
||||
pub fn try_read_char() -> Option<char> {
|
||||
let mut keyboard = KEYBOARD_STATE.lock();
|
||||
keyboard.pop_key().map(|k| k as char)
|
||||
}
|
||||
16
drivers/src/lib.rs
Archivo normal
16
drivers/src/lib.rs
Archivo normal
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Kernel drivers library
|
||||
//!
|
||||
//! This crate contains various kernel drivers for the Rust kernel.
|
||||
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod keyboard; // Keyboard driver
|
||||
pub mod mem;
|
||||
pub mod ramdisk;
|
||||
pub mod rtl8139;
|
||||
pub mod serial; // Serial driver
|
||||
pub use ramdisk::*;
|
||||
186
drivers/src/mem.rs
Archivo normal
186
drivers/src/mem.rs
Archivo normal
@@ -0,0 +1,186 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Null, zero, and full device drivers
|
||||
//! Based on Linux drivers/char/mem.c
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use kernel::device::{CharDevice, File, FileOperations, Inode, VMA};
|
||||
use kernel::driver::CharDriverOps;
|
||||
use kernel::prelude::*;
|
||||
|
||||
/// Null device driver (/dev/null)
|
||||
#[derive(Debug)]
|
||||
struct NullDevice;
|
||||
|
||||
impl FileOperations for NullDevice {
|
||||
fn open(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&self, _file: &mut File, _buf: &mut [u8], _offset: u64) -> Result<usize> {
|
||||
// Reading from /dev/null always returns EOF
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write(&self, _file: &mut File, buf: &[u8], _offset: u64) -> Result<usize> {
|
||||
// Writing to /dev/null always succeeds and discards data
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &mut File, _cmd: u32, _arg: usize) -> Result<usize> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &mut File, _vma: &mut VMA) -> Result<()> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Zero device driver (/dev/zero)
|
||||
#[derive(Debug)]
|
||||
struct ZeroDevice;
|
||||
|
||||
impl FileOperations for ZeroDevice {
|
||||
fn open(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&self, _file: &mut File, buf: &mut [u8], _offset: u64) -> Result<usize> {
|
||||
// Reading from /dev/zero returns zeros
|
||||
for byte in buf.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write(&self, _file: &mut File, buf: &[u8], _offset: u64) -> Result<usize> {
|
||||
// Writing to /dev/zero always succeeds and discards data
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &mut File, _cmd: u32, _arg: usize) -> Result<usize> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &mut File, vma: &mut VMA) -> Result<()> {
|
||||
// /dev/zero can be mmap'd to get zero-filled pages
|
||||
// Implement proper mmap support for zero device
|
||||
crate::info!(
|
||||
"Mapping zero-filled pages at 0x{:x}",
|
||||
vma.vm_start.as_usize()
|
||||
);
|
||||
|
||||
// In a real implementation, this would:
|
||||
// 1. Set up anonymous pages filled with zeros
|
||||
// 2. Configure page fault handler to provide zero pages on demand
|
||||
// 3. Mark pages as copy-on-write if needed
|
||||
|
||||
// For now, just log the operation
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Full device driver (/dev/full)
|
||||
#[derive(Debug)]
|
||||
struct FullDevice;
|
||||
|
||||
impl FileOperations for FullDevice {
|
||||
fn open(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&self, _file: &mut File, buf: &mut [u8], _offset: u64) -> Result<usize> {
|
||||
// Reading from /dev/full returns zeros
|
||||
for byte in buf.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write(&self, _file: &mut File, _buf: &[u8], _offset: u64) -> Result<usize> {
|
||||
// Writing to /dev/full always fails with "no space left"
|
||||
Err(Error::OutOfMemory) // ENOSPC equivalent
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &mut File, _cmd: u32, _arg: usize) -> Result<usize> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &mut File, _vma: &mut VMA) -> Result<()> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory devices module
|
||||
struct MemoryDevicesModule {
|
||||
null_major: u32,
|
||||
zero_major: u32,
|
||||
full_major: u32,
|
||||
}
|
||||
|
||||
impl kernel::module::Module for MemoryDevicesModule {
|
||||
fn init(_module: &'static kernel::module::ThisModule) -> Result<Self> {
|
||||
info!("Memory devices module initializing...");
|
||||
|
||||
// Register /dev/null (major 1, minor 3)
|
||||
let null_major = kernel::device::register_chrdev(
|
||||
1,
|
||||
String::from("null"),
|
||||
Box::new(NullDevice),
|
||||
)?;
|
||||
|
||||
// Register /dev/zero (major 1, minor 5)
|
||||
let zero_major = kernel::device::register_chrdev(
|
||||
1,
|
||||
String::from("zero"),
|
||||
Box::new(ZeroDevice),
|
||||
)?;
|
||||
|
||||
// Register /dev/full (major 1, minor 7)
|
||||
let full_major = kernel::device::register_chrdev(
|
||||
1,
|
||||
String::from("full"),
|
||||
Box::new(FullDevice),
|
||||
)?;
|
||||
|
||||
info!(
|
||||
"Memory devices registered: null={}, zero={}, full={}",
|
||||
null_major, zero_major, full_major
|
||||
);
|
||||
|
||||
Ok(MemoryDevicesModule {
|
||||
null_major,
|
||||
zero_major,
|
||||
full_major,
|
||||
})
|
||||
}
|
||||
|
||||
fn exit(_module: &'static kernel::module::ThisModule) {
|
||||
info!("Memory devices module exiting");
|
||||
|
||||
// Unregister character devices
|
||||
kernel::device::unregister_chrdev(1).ok();
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: MemoryDevicesModule,
|
||||
name: "mem_devices",
|
||||
author: "Rust Kernel Contributors",
|
||||
description: "Memory devices (/dev/null, /dev/zero, /dev/full)",
|
||||
license: "GPL-2.0",
|
||||
}
|
||||
187
drivers/src/ramdisk.rs
Archivo normal
187
drivers/src/ramdisk.rs
Archivo normal
@@ -0,0 +1,187 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! RAM disk block device driver
|
||||
//! Based on Linux drivers/block/brd.c
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use kernel::device::{BlockDevice, Device, DeviceType};
|
||||
use kernel::driver::{BlockDriverOps, Driver};
|
||||
use kernel::memory::{AllocFlags, GFP_KERNEL};
|
||||
use kernel::prelude::*;
|
||||
|
||||
/// RAM disk device
|
||||
struct RamDisk {
|
||||
size: u64, // Size in bytes
|
||||
block_size: u32, // Block size in bytes
|
||||
data: Vec<u8>, // Actual storage
|
||||
}
|
||||
|
||||
impl RamDisk {
|
||||
fn new(size: u64, block_size: u32) -> Result<Self> {
|
||||
let mut data = Vec::new();
|
||||
data.try_reserve(size as usize)?;
|
||||
data.resize(size as usize, 0);
|
||||
|
||||
Ok(Self {
|
||||
size,
|
||||
block_size,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_block_count(&self) -> u64 {
|
||||
self.size / self.block_size as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDriverOps for RamDisk {
|
||||
fn read_block(&self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||
if block >= self.get_block_count() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let offset = (block * self.block_size as u64) as usize;
|
||||
let size = core::cmp::min(buffer.len(), self.block_size as usize);
|
||||
|
||||
if offset + size > self.data.len() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
buffer[..size].copy_from_slice(&self.data[offset..offset + size]);
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
fn write_block(&self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||
if block >= self.get_block_count() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let offset = (block * self.block_size as u64) as usize;
|
||||
let size = core::cmp::min(buffer.len(), self.block_size as usize);
|
||||
|
||||
if offset + size > self.data.len() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
// This is a bit unsafe due to the immutable reference, but for simplicity...
|
||||
// In a real implementation, we'd use proper interior mutability
|
||||
unsafe {
|
||||
let data_ptr = self.data.as_ptr() as *mut u8;
|
||||
let dest = core::slice::from_raw_parts_mut(data_ptr.add(offset), size);
|
||||
dest.copy_from_slice(&buffer[..size]);
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
fn get_block_size(&self) -> u32 {
|
||||
self.block_size
|
||||
}
|
||||
|
||||
fn get_total_blocks(&self) -> u64 {
|
||||
self.get_block_count()
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
// RAM disk doesn't need flushing
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// RAM disk driver
|
||||
struct RamDiskDriver {
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl RamDiskDriver {
|
||||
fn new() -> Self {
|
||||
Self { name: "ramdisk" }
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for RamDiskDriver {
|
||||
fn name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn probe(&self, device: &mut Device) -> Result<()> {
|
||||
info!("RAM disk driver probing device: {}", device.name());
|
||||
|
||||
// Create a 16MB RAM disk with 4KB blocks
|
||||
let ramdisk = RamDisk::new(16 * 1024 * 1024, 4096)?;
|
||||
|
||||
info!(
|
||||
"Created RAM disk: {} blocks of {} bytes each",
|
||||
ramdisk.get_total_blocks(),
|
||||
ramdisk.get_block_size()
|
||||
);
|
||||
|
||||
device.set_private_data(ramdisk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, device: &mut Device) -> Result<()> {
|
||||
info!("RAM disk driver removing device: {}", device.name());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// RAM disk module
|
||||
struct RamDiskModule {
|
||||
driver: RamDiskDriver,
|
||||
device_created: bool,
|
||||
}
|
||||
|
||||
impl kernel::module::Module for RamDiskModule {
|
||||
fn init(_module: &'static kernel::module::ThisModule) -> Result<Self> {
|
||||
info!("RAM disk module initializing...");
|
||||
|
||||
let driver = RamDiskDriver::new();
|
||||
|
||||
// Register the driver
|
||||
kernel::driver::register_driver(Box::new(driver))?;
|
||||
|
||||
// Create a RAM disk device
|
||||
let mut device = Device::new(
|
||||
String::from("ram0"),
|
||||
DeviceType::Block,
|
||||
1, // major number for RAM disk
|
||||
0, // minor number
|
||||
);
|
||||
|
||||
// Set up the driver for this device
|
||||
let ramdisk_driver = RamDiskDriver::new();
|
||||
device.set_driver(Box::new(ramdisk_driver))?;
|
||||
|
||||
// Register the device
|
||||
kernel::device::register_device(device)?;
|
||||
|
||||
info!("RAM disk device created and registered");
|
||||
|
||||
Ok(RamDiskModule {
|
||||
driver: RamDiskDriver::new(),
|
||||
device_created: true,
|
||||
})
|
||||
}
|
||||
|
||||
fn exit(_module: &'static kernel::module::ThisModule) {
|
||||
info!("RAM disk module exiting");
|
||||
|
||||
if self.device_created {
|
||||
kernel::device::unregister_device("ram0").ok();
|
||||
}
|
||||
|
||||
kernel::driver::unregister_driver("ramdisk").ok();
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: RamDiskModule,
|
||||
name: "ramdisk",
|
||||
author: "Rust Kernel Contributors",
|
||||
description: "RAM disk block device driver",
|
||||
license: "GPL-2.0",
|
||||
}
|
||||
302
drivers/src/rtl8139.rs
Archivo normal
302
drivers/src/rtl8139.rs
Archivo normal
@@ -0,0 +1,302 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! RTL8139 Network Driver
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::ToString;
|
||||
use core::ptr;
|
||||
use kernel::driver::{Driver, PciDevice, PciDeviceId, PciDriver};
|
||||
use kernel::error::{Error, Result};
|
||||
use kernel::memory::{allocator, vmalloc};
|
||||
use kernel::network::NetworkInterface;
|
||||
use kernel::pci_driver;
|
||||
use kernel::types::PhysAddr;
|
||||
|
||||
const REG_MAC0: u16 = 0x00;
|
||||
const REG_MAR0: u16 = 0x08;
|
||||
const REG_RBSTART: u16 = 0x30;
|
||||
const REG_CMD: u16 = 0x37;
|
||||
const REG_IMR: u16 = 0x3C;
|
||||
const REG_ISR: u16 = 0x3E;
|
||||
const REG_RCR: u16 = 0x44;
|
||||
const REG_CONFIG1: u16 = 0x52;
|
||||
|
||||
const REG_TX_STATUS_0: u16 = 0x10;
|
||||
const REG_TX_START_0: u16 = 0x20;
|
||||
|
||||
const CMD_RESET: u8 = 0x10;
|
||||
const CMD_RX_ENB: u8 = 0x08;
|
||||
const CMD_TX_ENB: u8 = 0x04;
|
||||
|
||||
const IMR_ROK: u16 = 1 << 0;
|
||||
const IMR_TOK: u16 = 1 << 2;
|
||||
|
||||
const RCR_AAP: u32 = 1 << 0; // AcceptAllPackets
|
||||
const RCR_APM: u32 = 1 << 1; // AcceptPhysicalMatch
|
||||
const RCR_AM: u32 = 1 << 2; // AcceptMulticast
|
||||
const RCR_AB: u32 = 1 << 3; // AcceptBroadcast
|
||||
const RCR_WRAP: u32 = 1 << 7;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Rtl8139Device {
|
||||
mmio_base: usize,
|
||||
mac: [u8; 6],
|
||||
rx_buffer: *mut u8,
|
||||
tx_buffer: *mut u8,
|
||||
rx_buffer_pos: usize,
|
||||
tx_cur: usize,
|
||||
up: bool,
|
||||
}
|
||||
|
||||
impl Rtl8139Device {
|
||||
fn new(mmio_base: usize) -> Self {
|
||||
Self {
|
||||
mmio_base,
|
||||
mac: [0; 6],
|
||||
rx_buffer: ptr::null_mut(),
|
||||
tx_buffer: ptr::null_mut(),
|
||||
rx_buffer_pos: 0,
|
||||
tx_cur: 0,
|
||||
up: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn read8(&self, offset: u16) -> u8 {
|
||||
unsafe { ptr::read_volatile((self.mmio_base + offset as usize) as *const u8) }
|
||||
}
|
||||
|
||||
fn write8(&self, offset: u16, val: u8) {
|
||||
unsafe { ptr::write_volatile((self.mmio_base + offset as usize) as *mut u8, val) }
|
||||
}
|
||||
|
||||
fn read16(&self, offset: u16) -> u16 {
|
||||
unsafe { ptr::read_volatile((self.mmio_base + offset as usize) as *const u16) }
|
||||
}
|
||||
|
||||
fn write16(&self, offset: u16, val: u16) {
|
||||
unsafe { ptr::write_volatile((self.mmio_base + offset as usize) as *mut u16, val) }
|
||||
}
|
||||
|
||||
fn read32(&self, offset: u16) -> u32 {
|
||||
unsafe { ptr::read_volatile((self.mmio_base + offset as usize) as *const u32) }
|
||||
}
|
||||
|
||||
fn write32(&self, offset: u16, val: u32) {
|
||||
unsafe { ptr::write_volatile((self.mmio_base + offset as usize) as *mut u32, val) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rtl8139Driver;
|
||||
|
||||
impl NetworkInterface for Rtl8139Device {
|
||||
fn name(&self) -> &str {
|
||||
"eth0" // Or some other name
|
||||
}
|
||||
|
||||
fn ip_address(&self) -> Option<kernel::network::Ipv4Address> {
|
||||
None // This will be set by the network stack
|
||||
}
|
||||
|
||||
fn mac_address(&self) -> kernel::network::MacAddress {
|
||||
kernel::network::MacAddress::new(self.mac)
|
||||
}
|
||||
|
||||
fn mtu(&self) -> u16 {
|
||||
1500
|
||||
}
|
||||
|
||||
fn is_up(&self) -> bool {
|
||||
self.up
|
||||
}
|
||||
|
||||
fn send_packet(&mut self, buffer: &kernel::network::NetworkBuffer) -> Result<()> {
|
||||
let tx_status_reg = REG_TX_STATUS_0 + (self.tx_cur * 4) as u16;
|
||||
|
||||
// The transmit buffers are laid out contiguously in memory after the receive buffer.
|
||||
let tx_buffer = unsafe { self.tx_buffer.add(self.tx_cur * 2048) };
|
||||
|
||||
// Copy the packet data to the transmit buffer.
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(buffer.data().as_ptr(), tx_buffer, buffer.len());
|
||||
}
|
||||
|
||||
// Write the buffer address to the transmit start register.
|
||||
let dma_addr = PhysAddr::new(tx_buffer as usize);
|
||||
self.write32(
|
||||
REG_TX_START_0 + (self.tx_cur * 4) as u16,
|
||||
dma_addr.as_usize() as u32,
|
||||
);
|
||||
|
||||
// Write the packet size and flags to the transmit status register.
|
||||
self.write32(tx_status_reg, buffer.len() as u32);
|
||||
|
||||
self.tx_cur = (self.tx_cur + 1) % 4;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn receive_packet(&mut self) -> Result<Option<kernel::network::NetworkBuffer>> {
|
||||
let isr = self.read16(REG_ISR);
|
||||
if (isr & IMR_ROK) == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Acknowledge the interrupt
|
||||
self.write16(REG_ISR, IMR_ROK);
|
||||
|
||||
let rx_ptr = self.rx_buffer as *const u8;
|
||||
let _header = unsafe {
|
||||
ptr::read_unaligned(rx_ptr.add(self.rx_buffer_pos) as *const u16)
|
||||
};
|
||||
let len = unsafe {
|
||||
ptr::read_unaligned(rx_ptr.add(self.rx_buffer_pos + 2) as *const u16)
|
||||
};
|
||||
|
||||
let data_ptr = unsafe { rx_ptr.add(self.rx_buffer_pos + 4) };
|
||||
let data = unsafe { core::slice::from_raw_parts(data_ptr, len as usize) };
|
||||
|
||||
// The data includes the Ethernet header.
|
||||
if data.len() < 14 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let dest_mac = kernel::network::MacAddress::new([
|
||||
data[0], data[1], data[2], data[3], data[4], data[5],
|
||||
]);
|
||||
let src_mac = kernel::network::MacAddress::new([
|
||||
data[6], data[7], data[8], data[9], data[10], data[11],
|
||||
]);
|
||||
let ethertype = u16::from_be_bytes([data[12], data[13]]);
|
||||
|
||||
let protocol = match ethertype {
|
||||
0x0800 => kernel::network::ProtocolType::IPv4,
|
||||
0x0806 => kernel::network::ProtocolType::ARP,
|
||||
_ => return Ok(None), // Unknown protocol
|
||||
};
|
||||
|
||||
let mut buffer = kernel::network::NetworkBuffer::from_data(data[14..].to_vec());
|
||||
buffer.set_protocol(protocol);
|
||||
buffer.set_mac_addresses(src_mac, dest_mac);
|
||||
|
||||
self.rx_buffer_pos = (self.rx_buffer_pos + len as usize + 4 + 3) & !3;
|
||||
if self.rx_buffer_pos > 8192 {
|
||||
self.rx_buffer_pos -= 8192;
|
||||
}
|
||||
self.write16(0x38, self.rx_buffer_pos as u16 - 16);
|
||||
|
||||
Ok(Some(buffer))
|
||||
}
|
||||
|
||||
fn set_up(&mut self, up: bool) -> Result<()> {
|
||||
if up {
|
||||
self.write8(REG_CMD, CMD_RX_ENB | CMD_TX_ENB);
|
||||
} else {
|
||||
self.write8(REG_CMD, 0x00);
|
||||
}
|
||||
self.up = up;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_mac_address(&mut self, _mac: kernel::network::MacAddress) -> Result<()> {
|
||||
// Not supported
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for Rtl8139Driver {
|
||||
fn name(&self) -> &str {
|
||||
"rtl8139"
|
||||
}
|
||||
|
||||
fn probe(&self, _device: &mut kernel::device::Device) -> Result<()> {
|
||||
// This will be called for a generic device.
|
||||
// We are a PCI driver, so we'll do our work in pci_probe.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, _device: &mut kernel::device::Device) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PciDriver for Rtl8139Driver {
|
||||
fn pci_ids(&self) -> &[PciDeviceId] {
|
||||
&[PciDeviceId::new(0x10EC, 0x8139)]
|
||||
}
|
||||
|
||||
fn pci_probe(&self, pci_dev: &mut PciDevice) -> Result<()> {
|
||||
kernel::info!("Probing rtl8139 device");
|
||||
|
||||
let bar0 = pci_dev.bars[0];
|
||||
if bar0.is_io() {
|
||||
return Err(Error::NotSupported);
|
||||
}
|
||||
|
||||
let mmio_base = bar0.address;
|
||||
kernel::info!("RTL8139 MMIO base: {:#x}", mmio_base);
|
||||
|
||||
let mmio_virt = vmalloc::vmap_phys(PhysAddr::new(mmio_base as usize), 0x100)?;
|
||||
kernel::info!("RTL8139 MMIO mapped to: {:#x}", mmio_virt.as_usize());
|
||||
|
||||
let mut device = Rtl8139Device::new(mmio_virt.as_usize());
|
||||
|
||||
// Power on
|
||||
device.write8(REG_CONFIG1, 0x00);
|
||||
|
||||
// Reset
|
||||
device.write8(REG_CMD, CMD_RESET);
|
||||
while (device.read8(REG_CMD) & CMD_RESET) != 0 {}
|
||||
|
||||
// Read MAC address
|
||||
for i in 0..6 {
|
||||
device.mac[i] = device.read8(REG_MAC0 + i as u16);
|
||||
}
|
||||
kernel::info!(
|
||||
"RTL8139 MAC address: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
|
||||
device.mac[0],
|
||||
device.mac[1],
|
||||
device.mac[2],
|
||||
device.mac[3],
|
||||
device.mac[4],
|
||||
device.mac[5]
|
||||
);
|
||||
|
||||
// Allocate DMA buffers
|
||||
let dma_pfn = allocator::alloc_pages(2, allocator::GfpFlags::DMA)?;
|
||||
let dma_addr = dma_pfn.to_phys_addr();
|
||||
device.rx_buffer = dma_addr.as_usize() as *mut u8;
|
||||
device.tx_buffer = (dma_addr.as_usize() + 8192) as *mut u8;
|
||||
|
||||
// Initialize receive buffer
|
||||
device.write32(REG_RBSTART, dma_addr.as_usize() as u32);
|
||||
|
||||
// Initialize transmit buffers
|
||||
for i in 0..4 {
|
||||
// Nothing to do here yet, we will set the buffer address when we send a packet.
|
||||
}
|
||||
|
||||
// Enable RX and TX
|
||||
device.write8(REG_CMD, CMD_RX_ENB | CMD_TX_ENB);
|
||||
|
||||
// Set RCR
|
||||
device.write32(REG_RCR, RCR_AAP | RCR_APM | RCR_AM | RCR_AB | RCR_WRAP);
|
||||
|
||||
// Enable interrupts
|
||||
device.write16(REG_IMR, IMR_TOK | IMR_ROK);
|
||||
|
||||
kernel::info!("RTL8139 device initialized");
|
||||
|
||||
let mut boxed_device = Box::new(device);
|
||||
boxed_device.set_up(true)?;
|
||||
kernel::network::add_network_interface("eth0".to_string(), boxed_device)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pci_remove(&self, _pci_dev: &mut PciDevice) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pci_driver!(Rtl8139Driver);
|
||||
403
drivers/src/serial.rs
Archivo normal
403
drivers/src/serial.rs
Archivo normal
@@ -0,0 +1,403 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Serial console driver (16550 UART)
|
||||
|
||||
use alloc::{collections::VecDeque, string::String, vec::Vec};
|
||||
|
||||
use kernel::arch::x86_64::port::{inb, outb};
|
||||
use kernel::device::{CharDevice, Device, DeviceType, FileOperations};
|
||||
use kernel::error::{Error, Result};
|
||||
use kernel::interrupt::{register_interrupt_handler, IrqHandler};
|
||||
use kernel::sync::{Arc, Spinlock};
|
||||
|
||||
/// Standard COM port addresses
|
||||
const COM1_BASE: u16 = 0x3F8;
|
||||
const COM2_BASE: u16 = 0x2F8;
|
||||
const COM3_BASE: u16 = 0x3E8;
|
||||
const COM4_BASE: u16 = 0x2E8;
|
||||
|
||||
/// UART register offsets
|
||||
const UART_DATA: u16 = 0; // Data register (R/W)
|
||||
const UART_IER: u16 = 1; // Interrupt Enable Register
|
||||
const UART_IIR: u16 = 2; // Interrupt Identification Register (R)
|
||||
const UART_FCR: u16 = 2; // FIFO Control Register (W)
|
||||
const UART_LCR: u16 = 3; // Line Control Register
|
||||
const UART_MCR: u16 = 4; // Modem Control Register
|
||||
const UART_LSR: u16 = 5; // Line Status Register
|
||||
const UART_MSR: u16 = 6; // Modem Status Register
|
||||
const UART_SCR: u16 = 7; // Scratch Register
|
||||
|
||||
/// Line Status Register bits
|
||||
const LSR_DATA_READY: u8 = 0x01; // Data available
|
||||
const LSR_OVERRUN_ERROR: u8 = 0x02; // Overrun error
|
||||
const LSR_PARITY_ERROR: u8 = 0x04; // Parity error
|
||||
const LSR_FRAMING_ERROR: u8 = 0x08; // Framing error
|
||||
const LSR_BREAK_INTERRUPT: u8 = 0x10; // Break interrupt
|
||||
const LSR_THR_EMPTY: u8 = 0x20; // Transmitter holding register empty
|
||||
const LSR_THR_EMPTY_IDLE: u8 = 0x40; // Transmitter empty and idle
|
||||
const LSR_FIFO_ERROR: u8 = 0x80; // FIFO error
|
||||
|
||||
/// Serial port structure
|
||||
#[derive(Debug)]
|
||||
pub struct SerialPort {
|
||||
/// Base I/O port address
|
||||
base: u16,
|
||||
/// Port name
|
||||
name: String,
|
||||
/// Receive buffer
|
||||
rx_buffer: VecDeque<u8>,
|
||||
/// Transmit buffer
|
||||
tx_buffer: VecDeque<u8>,
|
||||
/// Port configuration
|
||||
baudrate: u32,
|
||||
data_bits: u8,
|
||||
stop_bits: u8,
|
||||
parity: Parity,
|
||||
}
|
||||
|
||||
/// Parity settings
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Parity {
|
||||
None,
|
||||
Odd,
|
||||
Even,
|
||||
Mark,
|
||||
Space,
|
||||
}
|
||||
|
||||
impl SerialPort {
|
||||
/// Create a new serial port
|
||||
pub fn new(base: u16, name: String) -> Self {
|
||||
Self {
|
||||
base,
|
||||
name,
|
||||
rx_buffer: VecDeque::new(),
|
||||
tx_buffer: VecDeque::new(),
|
||||
baudrate: 115200,
|
||||
data_bits: 8,
|
||||
stop_bits: 1,
|
||||
parity: Parity::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the serial port
|
||||
pub fn init(&mut self) -> Result<()> {
|
||||
// Disable interrupts
|
||||
unsafe {
|
||||
outb(self.base + UART_IER, 0x00);
|
||||
}
|
||||
|
||||
// Set baud rate to 115200 (divisor = 1)
|
||||
unsafe {
|
||||
outb(self.base + UART_LCR, 0x80); // Enable DLAB
|
||||
outb(self.base + UART_DATA, 0x01); // Divisor low byte
|
||||
outb(self.base + UART_IER, 0x00); // Divisor high byte
|
||||
}
|
||||
|
||||
// Configure line: 8 data bits, 1 stop bit, no parity
|
||||
unsafe {
|
||||
outb(self.base + UART_LCR, 0x03);
|
||||
}
|
||||
|
||||
// Enable FIFO, clear them, with 14-byte threshold
|
||||
unsafe {
|
||||
outb(self.base + UART_FCR, 0xC7);
|
||||
}
|
||||
|
||||
// Enable IRQs, set RTS/DSR, set AUX2 (used for interrupts)
|
||||
unsafe {
|
||||
outb(self.base + UART_MCR, 0x0B);
|
||||
}
|
||||
|
||||
// Test serial chip (send 0xAE and check if serial returns same byte)
|
||||
unsafe {
|
||||
outb(self.base + UART_MCR, 0x1E); // Enable loopback mode
|
||||
outb(self.base + UART_DATA, 0xAE); // Send test byte
|
||||
|
||||
if inb(self.base + UART_DATA) != 0xAE {
|
||||
return Err(Error::EIO);
|
||||
}
|
||||
|
||||
// Disable loopback mode
|
||||
outb(self.base + UART_MCR, 0x0F);
|
||||
}
|
||||
|
||||
// Enable interrupts
|
||||
unsafe {
|
||||
outb(self.base + UART_IER, 0x01);
|
||||
} // Enable receive interrupt
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if data is available for reading
|
||||
pub fn is_receive_ready(&self) -> bool {
|
||||
unsafe { (inb(self.base + UART_LSR) & LSR_DATA_READY) != 0 }
|
||||
}
|
||||
|
||||
/// Check if transmitter is ready for data
|
||||
pub fn is_transmit_ready(&self) -> bool {
|
||||
unsafe { (inb(self.base + UART_LSR) & LSR_THR_EMPTY) != 0 }
|
||||
}
|
||||
|
||||
/// Read a byte from the serial port (non-blocking)
|
||||
pub fn read_byte(&mut self) -> Option<u8> {
|
||||
if !self.rx_buffer.is_empty() {
|
||||
return self.rx_buffer.pop_front();
|
||||
}
|
||||
|
||||
if self.is_receive_ready() {
|
||||
let byte = unsafe { inb(self.base + UART_DATA) };
|
||||
Some(byte)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a byte to the serial port
|
||||
pub fn write_byte(&mut self, byte: u8) -> Result<()> {
|
||||
// Wait until transmitter is ready
|
||||
while !self.is_transmit_ready() {
|
||||
// Could yield here in a real implementation
|
||||
}
|
||||
|
||||
unsafe {
|
||||
outb(self.base + UART_DATA, byte);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write a string to the serial port
|
||||
pub fn write_str(&mut self, s: &str) -> Result<()> {
|
||||
for byte in s.bytes() {
|
||||
self.write_byte(byte)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle receive interrupt
|
||||
pub fn handle_receive_interrupt(&mut self) {
|
||||
while self.is_receive_ready() {
|
||||
let byte = unsafe { inb(self.base + UART_DATA) };
|
||||
if self.rx_buffer.len() < 1024 {
|
||||
// Prevent buffer overflow
|
||||
self.rx_buffer.push_back(byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global serial ports
|
||||
static COM1: Spinlock<Option<SerialPort>> = Spinlock::new(None);
|
||||
|
||||
/// Serial interrupt handler
|
||||
#[derive(Debug)]
|
||||
pub struct SerialIrqHandler {
|
||||
port_base: u16,
|
||||
}
|
||||
|
||||
impl SerialIrqHandler {
|
||||
pub fn new(port_base: u16) -> Self {
|
||||
Self { port_base }
|
||||
}
|
||||
}
|
||||
|
||||
impl IrqHandler for SerialIrqHandler {
|
||||
fn handle_irq(&self, _irq: u32) -> Result<()> {
|
||||
// Handle COM1 interrupt
|
||||
if self.port_base == COM1_BASE {
|
||||
if let Some(ref mut port) = *COM1.lock() {
|
||||
port.handle_receive_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Serial console file operations
|
||||
#[derive(Debug)]
|
||||
pub struct SerialConsoleOps;
|
||||
|
||||
impl FileOperations for SerialConsoleOps {
|
||||
fn open(
|
||||
&self,
|
||||
_inode: &kernel::device::Inode,
|
||||
_file: &mut kernel::device::File,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(
|
||||
&self,
|
||||
_inode: &kernel::device::Inode,
|
||||
_file: &mut kernel::device::File,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
_file: &mut kernel::device::File,
|
||||
buf: &mut [u8],
|
||||
_offset: u64,
|
||||
) -> Result<usize> {
|
||||
let mut port = COM1.lock();
|
||||
if let Some(ref mut serial) = *port {
|
||||
let mut bytes_read = 0;
|
||||
|
||||
while bytes_read < buf.len() {
|
||||
if let Some(byte) = serial.read_byte() {
|
||||
buf[bytes_read] = byte;
|
||||
bytes_read += 1;
|
||||
|
||||
// Stop at newline
|
||||
if byte == b'\n' {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bytes_read)
|
||||
} else {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&self,
|
||||
_file: &mut kernel::device::File,
|
||||
buf: &[u8],
|
||||
_offset: u64,
|
||||
) -> Result<usize> {
|
||||
let mut port = COM1.lock();
|
||||
if let Some(ref mut serial) = *port {
|
||||
for &byte in buf {
|
||||
serial.write_byte(byte)?;
|
||||
}
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &mut kernel::device::File, cmd: u32, arg: usize) -> Result<usize> {
|
||||
// Implement serial-specific ioctl commands (baudrate, etc.)
|
||||
match cmd {
|
||||
0x5401 => {
|
||||
// TCGETS - get terminal attributes
|
||||
crate::info!("Getting terminal attributes");
|
||||
Ok(0)
|
||||
}
|
||||
0x5402 => {
|
||||
// TCSETS - set terminal attributes
|
||||
crate::info!("Setting terminal attributes to {}", arg);
|
||||
Ok(0)
|
||||
}
|
||||
0x540B => {
|
||||
// TCFLSH - flush terminal I/O
|
||||
crate::info!("Flushing terminal I/O");
|
||||
self.flush();
|
||||
Ok(0)
|
||||
}
|
||||
0x5415 => {
|
||||
// TIOCGSERIAL - get serial port info
|
||||
crate::info!("Getting serial port info");
|
||||
Ok(0x3f8) // Return COM1 port address
|
||||
}
|
||||
0x541F => {
|
||||
// TIOCGPTN - get pty number (not applicable for serial)
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
_ => {
|
||||
crate::info!("Unknown ioctl command: 0x{:x}", cmd);
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mmap(
|
||||
&self,
|
||||
_file: &mut kernel::device::File,
|
||||
_vma: &mut kernel::memory::VmaArea,
|
||||
) -> Result<()> {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize serial console
|
||||
pub fn init() -> Result<()> {
|
||||
// Initialize COM1
|
||||
let mut com1 = SerialPort::new(COM1_BASE, "ttyS0".to_string());
|
||||
com1.init()?;
|
||||
|
||||
*COM1.lock() = Some(com1);
|
||||
|
||||
// Register interrupt handler for COM1 (IRQ 4)
|
||||
let handler = Arc::new(SerialIrqHandler::new(COM1_BASE));
|
||||
register_interrupt_handler(36, handler as Arc<dyn IrqHandler>)?; // IRQ 4 = INT 36
|
||||
|
||||
// Create character device
|
||||
let char_dev = CharDevice::new(4, 64, 1, "ttyS0".to_string());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write to serial console (for kernel debugging)
|
||||
pub fn serial_print(s: &str) {
|
||||
let mut port = COM1.lock();
|
||||
if let Some(ref mut serial) = *port {
|
||||
let _ = serial.write_str(s);
|
||||
}
|
||||
}
|
||||
|
||||
/// Serial console macros
|
||||
#[macro_export]
|
||||
macro_rules! serial_print {
|
||||
($($arg:tt)*) => {
|
||||
$crate::drivers::serial::serial_print(&alloc::format!($($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! serial_println {
|
||||
() => ($crate::serial_print!("\n"));
|
||||
($($arg:tt)*) => ($crate::serial_print!("{}\n", alloc::format!($($arg)*)));
|
||||
}
|
||||
|
||||
/// Read a line from serial console
|
||||
pub fn read_line() -> Result<String> {
|
||||
let mut line = String::new();
|
||||
|
||||
loop {
|
||||
let mut port = COM1.lock();
|
||||
if let Some(ref mut serial) = *port {
|
||||
if let Some(byte) = serial.read_byte() {
|
||||
if byte == b'\r' || byte == b'\n' {
|
||||
// Echo newline
|
||||
let _ = serial.write_byte(b'\n');
|
||||
break;
|
||||
} else if byte == 8 || byte == 127 {
|
||||
// Backspace or DEL
|
||||
if !line.is_empty() {
|
||||
line.pop();
|
||||
// Echo backspace sequence
|
||||
let _ = serial.write_str("\x08 \x08");
|
||||
}
|
||||
} else if byte.is_ascii_graphic() || byte == b' ' {
|
||||
line.push(byte as char);
|
||||
// Echo character
|
||||
let _ = serial.write_byte(byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(port);
|
||||
|
||||
// Yield CPU while waiting
|
||||
kernel::scheduler::yield_now();
|
||||
}
|
||||
|
||||
Ok(line)
|
||||
}
|
||||
Referencia en una nueva incidencia
Block a user