Skip to content
Merged
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
117 changes: 103 additions & 14 deletions drivers/base/Stream_Device.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,12 @@ pub fn readv(sd: Stream_Device, bytes_vec: []const []u8) ReadError!usize {
return readv_fn(sd.ptr, bytes_vec);
}

pub const Reader = std.io.Reader(Stream_Device, ReadError, reader_read);
pub fn reader(sd: Stream_Device) Reader {
return .{ .context = sd };
pub fn writer(self: Stream_Device, buf: []u8) Writer {
return .init(self, buf);
}

fn reader_read(sd: Stream_Device, buf: []u8) ReadError!usize {
return sd.read(buf);
}

pub const Writer = std.io.Reader(Stream_Device, WriteError, writer_write);
pub fn writer(sd: Stream_Device) Writer {
return .{ .context = sd };
}

fn writer_write(sd: Stream_Device, buf: []const u8) WriteError!usize {
return sd.write(buf);
pub fn reader(self: Stream_Device, buf: []u8) Reader {
return .init(self, buf);
}

pub const VTable = struct {
Expand All @@ -84,6 +74,105 @@ pub const VTable = struct {
readv_fn: ?*const fn (*anyopaque, datagram: []const []u8) ReadError!usize,
};

/// microzig already has this function in SliceVector, but no other base driver include microzig as a dependency
/// so we duplicate it here for now
fn byte_sum(data: []const []const u8) usize {
var sum: usize = 0;
for (data) |bytes| {
sum += bytes.len;
}
return sum;
}

pub const Writer = struct {
dev: Stream_Device,
interface: std.Io.Writer,
err: ?WriteError = null,

fn drain(w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
const wt: *Writer = @alignCast(@fieldParentPtr("interface", w));
const total_size = byte_sum(data);
var ret: usize = 0;

const n = wt.dev.write(w.buffered()) catch |err| {
wt.err = err;
return error.WriteFailed;
};
_ = w.consume(n);

ret = wt.dev.writev(data) catch |err| {
wt.err = err;
return error.WriteFailed;
};

// NOTE: maybe check if ret is 0 across multiple calls to detect broken stream?
// check if we wrote everything we wanted before splatting
if (ret != total_size) {
return ret;
}

const pattern = data[data.len - 1];
for (0..splat) |_| {
const s_len = wt.dev.write(pattern) catch |err| {
wt.err = err;
return error.WriteFailed;
};
ret += s_len;
if (s_len < pattern.len) break; // if the previous pattern was not fully written, break the loop
}

return ret;
}

pub fn init(dev: Stream_Device, buf: []u8) Writer {
return Writer{
.dev = dev,
.interface = .{
.buffer = buf,
.vtable = &.{
.drain = drain,
},
},
};
}
};

pub const Reader = struct {
dev: Stream_Device,
interface: std.Io.Reader,
err: ?ReadError = null,

fn stream(r: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
const rd: *Reader = @alignCast(@fieldParentPtr("interface", r));
const w_buf = limit.slice(try w.writableSliceGreedy(1));

const n = rd.dev.read(w_buf) catch |err| {
rd.err = err;
return error.ReadFailed;
};

// NOTE: should we treat 0 as an EOF or check if 0 across multiple calls before returning EOF?
if (n == 0) return error.EndOfStream;

w.advance(n);
return n;
}

pub fn init(dev: Stream_Device, buf: []u8) Reader {
return Reader{
.dev = dev,
.interface = .{
.buffer = buf,
.seek = 0,
.end = 0,
.vtable = &.{
.stream = stream,
},
},
};
}
};

/// A device implementation that can be used to write unit tests for datagram devices.
pub const Test_Device = struct {
input: ?std.io.FixedBufferStream([]const u8),
Expand Down
8 changes: 4 additions & 4 deletions examples/stmicro/stm32/src/stm32f1xx/uart_echo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ pub fn main() !void {
var byte: [100]u8 = undefined;

//simple USART echo
try uart.write_blocking("START UART ECHO\n", null);
_ = try uart.write_blocking("START UART ECHO\n", null);
while (true) {
@memset(&byte, 0);
uart.read_blocking(&byte, Duration.from_ms(100)) catch |err| {
const len = uart.read_blocking(&byte, Duration.from_ms(100)) catch |err| {
if (err != error.Timeout) {
uart.writer().print("Got error {any}\n", .{err}) catch unreachable;
uart.clear_errors();
continue;
}
continue;
};

uart.write_blocking(&byte, null) catch unreachable;
_ = uart.write_blocking(byte[0..len], null) catch unreachable;
}
}
26 changes: 15 additions & 11 deletions port/stmicro/stm32/src/hals/STM32F103/uart.zig
Original file line number Diff line number Diff line change
Expand Up @@ -225,26 +225,30 @@ pub const UART = struct {
return (0 != uart.regs.SR.read().TXE);
}

pub fn writev_blocking(uart: *const UART, payloads: []const []const u8, timeout: ?Duration) TransmitError!void {
pub fn writev_blocking(uart: *const UART, payloads: []const []const u8, timeout: ?Duration) TransmitError!usize {
const deadline = Deadline.init_relative(time.get_time_since_boot(), timeout);
const regs = uart.regs;
var n: usize = 0;
for (payloads) |pkgs| {
for (pkgs) |byte| {
while (!uart.is_writeable()) {
if (deadline.is_reached_by(time.get_time_since_boot())) return error.Timeout;
}
regs.DR.raw = @intCast(byte);
n += 1;
}
}
return n;
}

pub fn readv_blocking(uart: *const UART, buffers: []const []u8, timeout: ?Duration) ReceiveError!void {
pub fn readv_blocking(uart: *const UART, buffers: []const []u8, timeout: ?Duration) ReceiveError!usize {
const deadline = Deadline.init_relative(time.get_time_since_boot(), timeout);
const regs = uart.regs;
var n: usize = 0;
for (buffers) |buf| {
for (buf) |*bytes| {
while (!uart.is_readable()) {
if (deadline.is_reached_by(time.get_time_since_boot())) return error.Timeout;
if (deadline.is_reached_by(time.get_time_since_boot())) return n;
}
const SR = regs.SR.read();

Expand All @@ -260,8 +264,10 @@ pub const UART = struct {
const rx = regs.DR.raw;

bytes.* = @intCast(0xFF & rx);
n += 1;
}
}
return n;
}

pub fn get_errors(uart: *const UART) ErrorStates {
Expand All @@ -281,12 +287,12 @@ pub const UART = struct {
std.mem.doNotOptimizeAway(regs.DR.raw);
}

pub fn write_blocking(uart: *const UART, data: []const u8, timeout: ?Duration) TransmitError!void {
try uart.writev_blocking(&.{data}, timeout);
pub fn write_blocking(uart: *const UART, data: []const u8, timeout: ?Duration) TransmitError!usize {
return uart.writev_blocking(&.{data}, timeout);
}

pub fn read_blocking(uart: *const UART, data: []u8, timeout: ?Duration) ReceiveError!void {
try uart.readv_blocking(&.{data}, timeout);
pub fn read_blocking(uart: *const UART, data: []u8, timeout: ?Duration) ReceiveError!usize {
return uart.readv_blocking(&.{data}, timeout);
}

pub fn writer(uart: *const UART) Writer {
Expand All @@ -297,13 +303,11 @@ pub const UART = struct {
return .{ .context = uart };
}
fn generic_writer_fn(uart: *const UART, buffer: []const u8) TransmitError!usize {
try uart.write_blocking(buffer, null);
return buffer.len;
return uart.write_blocking(buffer, null);
}

fn generic_reader_fn(uart: *const UART, buffer: []u8) ReceiveError!usize {
try uart.read_blocking(buffer, null);
return buffer.len;
return uart.read_blocking(buffer, null);
}

pub fn init(comptime uart: Instances) UART {
Expand Down
Loading