1// Copyright © 2021 Sören Tempel2//3// This program is free software: you can redistribute it and/or modify4// it under the terms of the GNU Affero General Public License as5// published by the Free Software Foundation, either version 3 of the6// License, or (at your option) any later version.7//8// This program is distributed in the hope that it will be useful, but9// WITHOUT ANY WARRANTY; without even the implied warranty of10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU11// Affero General Public License for more details.12//13// You should have received a copy of the GNU Affero General Public License14// along with this program. If not, see <https://www.gnu.org/licenses/>.1516const zoap = @import("zoap");17const crc = @import("crc.zig");18const std = @import("std");19const console = @import("console.zig");2021const Plic = @import("plic.zig").Plic;22const Uart = @import("uart.zig").Uart;2324const FrameHandler = fn (ctx: ?*anyopaque, buf: []const u8) void;25const CoapHandler = fn (req: *zoap.Request) void;2627pub const Slip = struct {28 uart: *const Uart,29 plic: *const Plic,30 handler: ?FrameHandler = null,31 context: ?*anyopaque = null,32 rcvbuf: [MTU]u8 = undefined,33 rcvpos: usize = 0,34 prev_esc: bool = false,3536 // SLIP control bytes from RFC 1055.37 const END: u8 = 0o300;38 const ESC: u8 = 0o333;39 const ESC_END: u8 = 0o334;40 const ESC_ESC: u8 = 0o335;4142 // SLIP (as defined in RFC 1055) doesn't specify an MTU.43 const MTU: u32 = 1500;4445 fn writeByte(self: *Slip, byte: u8) void {46 self.rcvbuf[self.rcvpos] = byte;47 self.rcvpos += 1;48 }4950 fn handleByte(self: *Slip, byte: u8) !void {51 if (self.rcvpos >= self.rcvbuf.len) {52 self.prev_esc = false;53 return error.FrameTooLarge;54 }5556 switch (byte) {57 ESC => {58 self.prev_esc = true;59 return;60 },61 END => {62 if (self.handler != null)63 self.handler.?(self.context, self.rcvbuf[0..self.rcvpos]);64 self.rcvpos = 0;65 },66 ESC_END, ESC_ESC => {67 var c: u8 = undefined;68 if (self.prev_esc) {69 switch (byte) {70 ESC_END => c = END,71 ESC_ESC => c = ESC,72 else => return error.UnknownEscapeSequence,73 }74 } else {75 c = byte;76 }7778 self.writeByte(c);79 },80 else => {81 self.writeByte(byte);82 },83 }8485 self.prev_esc = false;86 }8788 fn rxIrqHandler(self: *Slip) !void {89 while (self.uart.readByte()) |byte| {90 try self.handleByte(byte);91 }92 }9394 fn irqHandler(ctx: ?*anyopaque) void {95 var self: *Slip = @ptrCast(*Slip, @alignCast(@alignOf(*Slip), ctx.?));9697 const ip = self.uart.readIp();98 if (ip.rxwm) {99 rxIrqHandler(self) catch {100 @panic("rx handler failed");101 };102 }103 }104105 pub fn registerHandler(self: *Slip, func: FrameHandler, ctx: ?*anyopaque) !void {106 // Enable RX interrupt, dissable TX interrupt.107 self.uart.writeIe(false, true);108109 self.handler = func;110 self.context = ctx;111112 try self.plic.registerHandler(self.uart.irq, irqHandler, self);113 }114};115116pub const FrameType = enum(u8) {117 diagnostic = 0x0a,118 coap = 0xa9,119};120121pub const Frame = struct {122 slip: *const Slip,123 ftype: FrameType,124 csum: crc.Incremental,125126 const WriteError = error{};127 const FrameWriter = std.io.Writer(*Frame, WriteError, write);128129 fn init(slip: *const Slip, ftype: FrameType) Frame {130 var frame = Frame{131 .slip = slip,132 .ftype = ftype,133 .csum = .{},134 };135136 frame.pushByte(@enumToInt(ftype));137 return frame;138 }139140 fn pushByteRaw(self: *Frame, byte: u8) void {141 const uart = self.slip.uart;142143 // Busy wait for TX fifo to empty.144 while (uart.isTxFull()) {}145 uart.writeByte(byte);146 }147148 fn pushByte(self: *Frame, byte: u8) void {149 self.pushByteRaw(byte);150 if (self.ftype == FrameType.coap)151 self.csum.add(byte);152 }153154 fn write(self: *Frame, data: []const u8) WriteError!usize {155 for (data) |c| {156 switch (c) {157 Slip.END => {158 self.pushByte(Slip.ESC);159 self.pushByte(Slip.ESC_END);160 },161 Slip.ESC => {162 self.pushByte(Slip.ESC);163 self.pushByte(Slip.ESC_ESC);164 },165 else => {166 self.pushByte(c);167 },168 }169 }170171 return data.len;172 }173174 pub fn close(self: *Frame) void {175 if (self.ftype == FrameType.coap) {176 var fcs16 = self.csum.csum();177 fcs16 ^= 0xffff; // complement178179 // XXX: Use @truncate instead?180 self.pushByteRaw(@intCast(u8, fcs16 & @as(u16, 0x00ff)));181 self.pushByteRaw(@intCast(u8, fcs16 >> 8 & @as(u16, 0x00ff)));182 }183184 self.pushByteRaw(Slip.END);185 }186187 pub fn writer(self: *Frame) FrameWriter {188 return .{ .context = self };189 }190};191192pub const SlipMux = struct {193 slip: *Slip,194 handler: ?CoapHandler = null,195196 fn handleCoAP(self: *SlipMux, buf: []const u8) !void {197 // 1 byte (frame type) + 4 byte (coap message) + 2 byte CRC198 if (buf.len <= 7)199 return error.CoAPFrameTooShort;200 if (!crc.validCsum(buf))201 return error.InvalidChecksum;202203 // Strip frame identifier and 16-bit CRC FCS.204 const msgBuf = buf[1..(buf.len - @sizeOf(u16))];205206 var req = try zoap.Request.init(msgBuf);207 self.handler.?(&req);208 }209210 fn dispatchFrame(self: *SlipMux, buf: []const u8) !void {211 switch (buf[0]) {212 @enumToInt(FrameType.diagnostic) => {213 return error.NoDiagnosticSupport;214 },215 @enumToInt(FrameType.coap) => {216 try self.handleCoAP(buf);217 },218 else => {219 return error.UnsupportedFrameType;220 },221 }222 }223224 fn handleFrame(ctx: ?*anyopaque, buf: []const u8) void {225 var self: *SlipMux = @ptrCast(*SlipMux, @alignCast(@alignOf(*SlipMux), ctx.?));226 if (buf.len == 0)227 return;228229 self.dispatchFrame(buf) catch |err| {230 console.print("handleFrame failed: {s}\n", .{@errorName(err)});231 };232 }233234 pub fn newFrame(self: *SlipMux, ftype: FrameType) Frame {235 return Frame.init(self.slip, ftype);236 }237238 pub fn registerHandler(self: *SlipMux, handler: CoapHandler) !void {239 self.handler = handler;240 try self.slip.registerHandler(handleFrame, self);241 }242};