Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion codes.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ┌────────────────────────────────────────────┐
// │ (c) 2025 Linuxperoxo • FILE: codes.zig │
// │ (c) 2026 Linuxperoxo • FILE: codes.zig │
// │ Author: Linuxperoxo │
// └────────────────────────────────────────────┘

Expand Down
1 change: 1 addition & 0 deletions config/modules/menuconfig.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ pub const Load_T: type = @import("types.zig").Load_T;
pub const ModulesSelection: Menuconfig_T = .{
.ke_m_rootfs = .yes,
.ke_m_devfs = .yes,
.ke_m_fb = .yes,
};
1 change: 1 addition & 0 deletions config/modules/types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ pub const Load_T: type = enum {
pub const Menuconfig_T: type = struct {
ke_m_rootfs: Load_T,
ke_m_devfs: Load_T,
ke_m_fb: Load_T,
};
8 changes: 8 additions & 0 deletions drivers/video/fb/vga/config.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ┌─────────────────────────────────────────────┐
// │ (c) 2026 Linuxperoxo • FILE: config.zig │
// │ Author: Linuxperoxo │
// └─────────────────────────────────────────────┘

const types: type = @import("types.zig");

pub const VideoVendor: types.ConfigVendors_T = .qemu;
25 changes: 25 additions & 0 deletions drivers/video/fb/vga/device.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ┌──────────────────────────────────────────────┐
// │ (c) 2026 Linuxperoxo • FILE: device.zig │
// │ Author: Linuxperoxo │
// └──────────────────────────────────────────────┘

const devices: type = @import("root").interfaces.devices;
const ops: type = @import("ops.zig");

pub var fb_device: devices.Dev_T = .{
.name = "fb",
.type = .char,
.ops = &fb_device_ops,
.flags = .{
.control = .{
.minor = 0,
.max = 0,
},
},
};

pub const fb_device_ops: devices.DevOps_T = .{
.write = &ops.write,
.read = &ops.read,
.ioctl = &ops.ioctl,
};
81 changes: 81 additions & 0 deletions drivers/video/fb/vga/fb.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// ┌─────────────────────────────────────────────┐
// │ (c) 2026 Linuxperoxo • FILE: fb.zig │
// │ Author: Linuxperoxo │
// └─────────────────────────────────────────────┘

const devices: type = @import("root").interfaces.devices;
const module: type = @import("root").interfaces.module;
const device: type = @import("device.zig");
const ops: type = @import("ops.zig");
const vfs: type = @import("root").interfaces.vfs;
const devfs: type = __SaturnModuleDescription__.request_lib("devfs-operations").?;

pub const __SaturnModuleDescription__: module.ModuleDescription_T = .{
.mod = &vga_fb,
.load = .linkable,
.arch = &[_]module.ModuleDescriptionTarget_T {
.i386,
},
.libs = .{
.outside = &[_]module.ModuleDescriptionLibOut_T {
module.ModuleDescriptionLibOut_T {
.mod = "ke_m_devfs",
.lib = "devfs-operations",
.version = .{
.current = {},
},
.flags = .{
.required = 1,
},
},
},
},
};

const vga_fb: module.Mod_T = .{
.name = "ke_m_fb",
.desc = "Core Kernel VGA Framebuffer",
.author = "Linuxperoxo",
.version = "0.1.0",
.license = .GPL2_only,
.type = .driver,
.deps = &[_][]const u8 {
"ke_m_devfs"
},
.init = &init,
.exit = &exit,
.control = &vga_control,
};

var vga_control: module.ModControlFlags_T = .{
.init = 1,
.exit = 1,
.remove = 1,
.anon = 1,
};

var major: devices.Major_T = undefined;

fn init() anyerror!void {
major =
try devices.next_major();
try devices.dev_add(major, &device.fb_device);

errdefer devices.dev_rm(major, &device.fb_device) catch unreachable;
try ops.set_video_physio();

try devfs.create_device_node(major, 0, 0, 0, vfs.mode_T {
.owner = vfs.R | vfs.W,
.group = vfs.R | vfs.W,
.other = 0,
});
}

fn exit() anyerror!void {
errdefer {
// klog()
}
try module.rmmod(&vga_fb);
try devices.dev_rm(major, &device.fb_device);
ops.unset_video_physio();
}
82 changes: 82 additions & 0 deletions drivers/video/fb/vga/ops.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// ┌─────────────────────────────────────────────┐
// │ (c) 2026 Linuxperoxo • FILE: ops.zig │
// │ Author: Linuxperoxo │
// └─────────────────────────────────────────────┘

const phys: type = @import("root").ar.target_code.physio;
const config: type = @import("config.zig");
const types: type = @import("types.zig");
const devices: type = @import("root").interfaces.devices;

pub var pci_physio_video: ?*phys.PhysIo_T = null;

// ===================== AUX

pub inline fn set_video_physio() types.FbErr_T!void {
pci_physio_video = phys.physio_search(.{
.identified = .{
.class = .display,
.vendor = switch(comptime config.VideoVendor) {
.qemu => .qemu,
.amd => .amd,
.intel => .intel,
.nvidia => .nvidia,
},
},
}) catch return types.FbErr_T.ExpectNoNFound;
// map bars to virtual
}

pub inline fn unset_video_physio() void {
pci_physio_video = null;
}

pub inline fn check_video_physio() types.FbErr_T!void {
if(pci_physio_video == null
or pci_physio_video.?.status == .missing) return types.FbErr_T.MissingDevice;
}

// ===================== OPS

pub noinline fn write(_: devices.Minor_T, data: []const u8, offset: usize) types.FbErr_T!void {
try check_video_physio();
_ = data;
_ = offset;
}

pub noinline fn read(_: devices.Minor_T, offset: usize) types.FbErr_T![]u8 {
try check_video_physio();
_ = offset;
return @constCast("Hello, World!"); // NOTE: TEST
}

pub noinline fn ioctl(_: devices.Minor_T, command: usize, data: ?*anyopaque) types.FbErr_T!usize {
try check_video_physio();
return sw: switch(@as(types.FbCommands_T, @enumFromInt(command))) {
.color => {
if(data == null) break :sw types.FbErr_T.UnexpectedData;
unreachable;
},

.move => {
if(data == null) break :sw types.FbErr_T.UnexpectedData;
unreachable;
},

.put => {
if(data == null) break :sw types.FbErr_T.UnexpectedData;
unreachable;
},

.load => {
if(data == null) break :sw types.FbErr_T.UnexpectedData;
unreachable;
},

.clear => {
break :sw 0;
},

_ => types.FbErr_T.InvalidCommand,
};
}
27 changes: 27 additions & 0 deletions drivers/video/fb/vga/types.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// ┌─────────────────────────────────────────────┐
// │ (c) 2026 Linuxperoxo • FILE: types.zig │
// │ Author: Linuxperoxo │
// └─────────────────────────────────────────────┘

pub const ConfigVendors_T: type = enum {
nvidia,
qemu,
intel,
amd,
};

pub const FbErr_T: type = error {
ExpectNoNFound,
InvalidCommand,
UnexpectedData,
MissingDevice,
};

pub const FbCommands_T: type = enum(usize) {
color = 0x80,
clear = 0x70,
move = 0x60,
put = 0x50,
load = 0x40,
_,
};
10 changes: 10 additions & 0 deletions fs/devfs/allocator.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// ┌──────────────────────────────────────────────────┐
// │ (c) 2026 Linuxperoxo • FILE: allocator.zig │
// │ Author: Linuxperoxo │
// └──────────────────────────────────────────────────┘

const buildByteAllocator = @import("root").lib.memory.sba.buildByteAllocator;

pub const sba: type = struct {
pub var allocator = buildByteAllocator(null, .{}) {};
};
65 changes: 65 additions & 0 deletions fs/devfs/aux.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// ┌──────────────────────────────────────────────┐
// │ (c) 2026 Linuxperoxo • FILE: aux.zig │
// │ Author: Linuxperoxo │
// └──────────────────────────────────────────────┘

const vfs: type = @import("root").interfaces.vfs;
const types: type = @import("types.zig");
const allocator: type = @import("allocator.zig");
const dfs: type = @import("fs.zig");
const devices: type = @import("root").interfaces.devices;
const fmt: type = @import("root").lib.utils.fmt;

pub inline fn check_init(dev_list: *types.DevfsList_T) types.DevfsListErr_T!void {
if(!dev_list.is_initialized())
try dev_list.init(&allocator.sba.allocator);
}

pub inline fn dentry_device_info(dentry: *vfs.Dentry_T) types.DevfsErr_T!*const types.DevfsPrivate_T {
if(dentry.d_private == null)
return types.DevfsErr_T.CorruptDentry;
return @ptrCast(@alignCast(dentry.d_private.?));
}

var inode_count: usize = 0;
pub inline fn new_dentry_device(major: devices.Major_T, minor: devices.Minor_T, uid: vfs.uid_T, gid: vfs.gid_T, mode: vfs.mode_T) anyerror!*vfs.Dentry_T {
const new_dentry: *vfs.Dentry_T = &(try allocator.sba.allocator.alloc(vfs.Dentry_T, 1))[0];
errdefer allocator.sba.allocator.free(new_dentry) catch unreachable;

const new_device_node: *types.DevfsPrivate_T = &(try allocator.sba.allocator.alloc(types.DevfsPrivate_T, 1))[0];
errdefer allocator.sba.allocator.free(new_device_node) catch unreachable;

const new_device_inode: *vfs.Inode_T = &(try allocator.sba.allocator.alloc(vfs.Inode_T, 1))[0];
errdefer allocator.sba.allocator.free(new_device_inode) catch unreachable;

const new_device_name: []u8 = try fmt.format(&allocator.sba.allocator, "{s}{d}", .{
devices.dev_info(major, .name) catch unreachable,
minor,
});

new_dentry.* = .{
.d_name = new_device_name,
.d_inode = new_device_inode,
.d_op = &dfs.devfs_ops,
.d_sblock = null,
.d_private = new_device_node,
.child = null,
.parent = null,
.older_brother = null,
.younger_brother = null,
};

new_device_inode.* = .{
.uid = uid,
.gid = gid,
.mode = mode,
.nlinks = 0,
.inode = inode_count,
.type = .char,
.data_block = @intFromPtr(new_dentry),
.data_inode = @intFromPtr(new_dentry),
};

inode_count += 1;
return new_dentry;
}
Loading