From 629ec4f55ba6a17bbebcadc66e9d89824e797b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Sun, 16 Jun 2024 18:44:55 +0200 Subject: [PATCH] Improves demo to be standalone --- .github/workflows/validate.yml | 4 +- build.zig | 10 +++- demo/README.md | 8 --- demo/blink-fast.uf2 | Bin 45568 -> 0 bytes demo/blink-slow.uf2 | Bin 45568 -> 0 bytes demo/main.zig | 100 +++++++++++---------------------- src/fatfs.zig | 4 +- 7 files changed, 44 insertions(+), 82 deletions(-) delete mode 100644 demo/README.md delete mode 100644 demo/blink-fast.uf2 delete mode 100644 demo/blink-slow.uf2 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index b059495..3409e1b 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -18,9 +18,9 @@ jobs: with: version: 0.13.0 - - name: Basic Build + - name: Build Demo run: | - zig build + zig build -Dmkfs install demo - name: Exercise Build options run: | diff --git a/build.zig b/build.zig index c919175..8ea15f2 100644 --- a/build.zig +++ b/build.zig @@ -6,6 +6,10 @@ fn bad_config(comptime fmt: []const u8, args: anytype) noreturn { } pub fn build(b: *std.Build) void { + // targets: + + const demo_step = b.step("demo", "Builds the demo:"); + // options: const target = b.standardTargetOptions(.{}); @@ -166,7 +170,7 @@ pub fn build(b: *std.Build) void { // usage demo: const exe = b.addExecutable(.{ - .name = "zfat", + .name = "zfat-demo", .root_source_file = b.path("demo/main.zig"), .target = target, .optimize = optimize, @@ -174,8 +178,8 @@ pub fn build(b: *std.Build) void { exe.root_module.addImport("zfat", zfat_mod); - // exe.linkLibC(); - b.installArtifact(exe); + const demo_exe = b.addInstallArtifact(exe, .{}); + demo_step.dependOn(&demo_exe.step); const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); diff --git a/demo/README.md b/demo/README.md deleted file mode 100644 index 71611c0..0000000 --- a/demo/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Demo Project - -This folder contains a tiny project that writes a source file into a selected target FAT file system as `firmware.uf2`. - -This can be used as a UF2 bootloader. - -- `blink-slow.uf2` is a tiny demo for the Raspberry Pi Pico which blinks the LED with a slow period. -- `blink-fast.uf2` is a tiny demo for the Raspberry Pi Pico which blinks the LED with a fast period. diff --git a/demo/blink-fast.uf2 b/demo/blink-fast.uf2 deleted file mode 100644 index 9e0a22870aa12d180524d55187bde2e5de0a1895..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45568 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 C86GnL diff --git a/demo/main.zig b/demo/main.zig index 7afe9f2..1088d5b 100644 --- a/demo/main.zig +++ b/demo/main.zig @@ -6,41 +6,31 @@ const fatfs = @import("zfat"); var global_fs: fatfs.FileSystem = undefined; // requires pointer stability -var image_disk: Disk = .{}; +var image_disk: Disk = undefined; pub fn main() !u8 { - const args = try std.process.argsAlloc(std.heap.page_allocator); - defer std.process.argsFree(std.heap.page_allocator, args); - - if (args.len != 2) { - std.log.err("requires source file!", .{}); - return 1; - } - - var source_file = std.fs.cwd().openFile(args[1], .{}) catch |e| { - std.log.err("failed to open firmware {s}: {s}", .{ args[1], @errorName(e) }); - return 1; - }; - defer source_file.close(); - - image_disk.open("/dev/sdb1") catch |e| { - std.log.err("failed to open disk /dev/sdb1: {s}", .{@errorName(e)}); - return 1; + image_disk = Disk{ + .sectors = try std.heap.page_allocator.alloc([Disk.sector_size]u8, 100_000), // ~40 MB }; - defer image_disk.close(); + defer std.heap.page_allocator.free(image_disk.sectors); fatfs.disks[0] = &image_disk.interface; + var workspace: [4096]u8 = undefined; + try fatfs.mkfs("0:", .{ + .filesystem = .fat32, + .sector_align = 1, + .use_partitions = true, + }, &workspace); + try global_fs.mount("0:", true); defer fatfs.FileSystem.unmount("0:") catch |e| std.log.err("failed to unmount filesystem: {s}", .{@errorName(e)}); { - var fifo = std.fifo.LinearFifo(u8, .{ .Static = 8192 }).init(); - var file = try fatfs.File.create("0:/firmware.uf2"); defer file.close(); - try fifo.pump(source_file.reader(), file.writer()); + try file.writer().writeAll("Hello, World!\r\n"); } return 0; @@ -56,44 +46,21 @@ pub const Disk = struct { .writeFn = write, .ioctlFn = ioctl, }, - backing_file: ?std.fs.File = null, - - fn open(self: *Disk, path: []const u8) !void { - self.close(); - self.backing_file = try std.fs.cwd().openFile(path, .{ .mode = .read_write }); - } - - fn close(self: *Disk) void { - if (self.backing_file) |*file| { - file.close(); - self.backing_file = null; - } - } + sectors: [][sector_size]u8, pub fn getStatus(interface: *fatfs.Disk) fatfs.Disk.Status { const self: *Disk = @fieldParentPtr("interface", interface); + _ = self; return fatfs.Disk.Status{ - .initialized = (self.backing_file != null), - .disk_present = (self.backing_file != null), + .initialized = true, + .disk_present = true, .write_protected = false, }; } pub fn initialize(interface: *fatfs.Disk) fatfs.Disk.Error!fatfs.Disk.Status { const self: *Disk = @fieldParentPtr("interface", interface); - if (self.backing_file != null) { - return fatfs.Disk.Status{ - .initialized = true, - .disk_present = true, - .write_protected = false, - }; - } - self.backing_file = std.fs.cwd().openFile("disk.img", .{ .mode = .read_write }) catch return error.DiskNotReady; - return fatfs.Disk.Status{ - .initialized = (self.backing_file != null), - .disk_present = (self.backing_file != null), - .write_protected = false, - }; + return getStatus(&self.interface); } pub fn read(interface: *fatfs.Disk, buff: [*]u8, sector: fatfs.LBA, count: c_uint) fatfs.Disk.Error!void { @@ -101,9 +68,9 @@ pub const Disk = struct { std.log.info("read({*}, {}, {})", .{ buff, sector, count }); - var file = self.backing_file orelse return error.IoError; - file.seekTo(sector * sector_size) catch return error.IoError; - file.reader().readNoEof(buff[0 .. sector_size * count]) catch return error.IoError; + var sectors = std.io.fixedBufferStream(std.mem.sliceAsBytes(self.sectors)); + sectors.seekTo(sector * sector_size) catch return error.IoError; + sectors.reader().readNoEof(buff[0 .. sector_size * count]) catch return error.IoError; } pub fn write(interface: *fatfs.Disk, buff: [*]const u8, sector: fatfs.LBA, count: c_uint) fatfs.Disk.Error!void { @@ -111,24 +78,23 @@ pub const Disk = struct { std.log.info("write({*}, {}, {})", .{ buff, sector, count }); - var file = self.backing_file orelse return error.IoError; - file.seekTo(sector * sector_size) catch return error.IoError; - file.writer().writeAll(buff[0 .. sector_size * count]) catch return error.IoError; + var sectors = std.io.fixedBufferStream(std.mem.sliceAsBytes(self.sectors)); + sectors.seekTo(sector * sector_size) catch return error.IoError; + sectors.writer().writeAll(buff[0 .. sector_size * count]) catch return error.IoError; } pub fn ioctl(interface: *fatfs.Disk, cmd: fatfs.IoCtl, buff: [*]u8) fatfs.Disk.Error!void { const self: *Disk = @fieldParentPtr("interface", interface); - if (self.backing_file) |file| { - _ = buff; - switch (cmd) { - .sync => { - file.sync() catch return error.IoError; - }, - - else => return error.InvalidParameter, - } - } else { - return error.DiskNotReady; + + switch (cmd) { + .sync => {}, + .get_sector_count => { + @as(*align(1) fatfs.LBA, @ptrCast(buff)).* = @intCast(self.sectors.len); + }, + else => { + std.log.err("invalid ioctl: {}", .{cmd}); + return error.InvalidParameter; + }, } } }; diff --git a/src/fatfs.zig b/src/fatfs.zig index f4e967e..98a216e 100644 --- a/src/fatfs.zig +++ b/src/fatfs.zig @@ -713,7 +713,7 @@ fn ErrorSet(comptime options: []const anyerror) type { }, }); - pub fn throw(error_code: c.FRESULT) Error!void { + pub inline fn throw(error_code: c.FRESULT) Error!void { const mapped_error = if (mapGenericError(error_code)) |_| { return; } else |err| err; @@ -728,7 +728,7 @@ fn ErrorSet(comptime options: []const anyerror) type { }; } -pub fn mapGenericError(code: c.FRESULT) GlobalError!void { +pub inline fn mapGenericError(code: c.FRESULT) GlobalError!void { return switch (code) { c.FR_OK => {}, c.FR_DISK_ERR => error.DiskErr,