1// Copyright © 2021 Sören Tempel
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16const zoap = @import("zoap");
17const crc = @import("crc.zig");
18const std = @import("std");
19const console = @import("console.zig");
20
21const Plic = @import("plic.zig").Plic;
22const Uart = @import("uart.zig").Uart;
23
24const FrameHandler = fn (ctx: ?*anyopaque, buf: []const u8) void;
25const CoapHandler = fn (req: *zoap.Request) void;
26
27pub 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,
35
36 // 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;
41
42 // SLIP (as defined in RFC 1055) doesn't specify an MTU.
43 const MTU: u32 = 1500;
44
45 fn writeByte(self: *Slip, byte: u8) void {
46 self.rcvbuf[self.rcvpos] = byte;
47 self.rcvpos += 1;
48 }
49
50 fn handleByte(self: *Slip, byte: u8) !void {
51 if (self.rcvpos >= self.rcvbuf.len) {
52 self.prev_esc = false;
53 return error.FrameTooLarge;
54 }
55
56 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 }
77
78 self.writeByte(c);
79 },
80 else => {
81 self.writeByte(byte);
82 },
83 }
84
85 self.prev_esc = false;
86 }
87
88 fn rxIrqHandler(self: *Slip) !void {
89 while (self.uart.readByte()) |byte| {
90 try self.handleByte(byte);
91 }
92 }
93
94 fn irqHandler(ctx: ?*anyopaque) void {
95 var self: *Slip = @ptrCast(*Slip, @alignCast(@alignOf(*Slip), ctx.?));
96
97 const ip = self.uart.readIp();
98 if (ip.rxwm) {
99 rxIrqHandler(self) catch {
100 @panic("rx handler failed");
101 };
102 }
103 }
104
105 pub fn registerHandler(self: *Slip, func: FrameHandler, ctx: ?*anyopaque) !void {
106 // Enable RX interrupt, dissable TX interrupt.
107 self.uart.writeIe(false, true);
108
109 self.handler = func;
110 self.context = ctx;
111
112 try self.plic.registerHandler(self.uart.irq, irqHandler, self);
113 }
114};
115
116pub const FrameType = enum(u8) {
117 diagnostic = 0x0a,
118 coap = 0xa9,
119};
120
121pub const Frame = struct {
122 slip: *const Slip,
123 ftype: FrameType,
124 csum: crc.Incremental,
125
126 const WriteError = error{};
127 const FrameWriter = std.io.Writer(*Frame, WriteError, write);
128
129 fn init(slip: *const Slip, ftype: FrameType) Frame {
130 var frame = Frame{
131 .slip = slip,
132 .ftype = ftype,
133 .csum = .{},
134 };
135
136 frame.pushByte(@enumToInt(ftype));
137 return frame;
138 }
139
140 fn pushByteRaw(self: *Frame, byte: u8) void {
141 const uart = self.slip.uart;
142
143 // Busy wait for TX fifo to empty.
144 while (uart.isTxFull()) {}
145 uart.writeByte(byte);
146 }
147
148 fn pushByte(self: *Frame, byte: u8) void {
149 self.pushByteRaw(byte);
150 if (self.ftype == FrameType.coap)
151 self.csum.add(byte);
152 }
153
154 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 }
170
171 return data.len;
172 }
173
174 pub fn close(self: *Frame) void {
175 if (self.ftype == FrameType.coap) {
176 var fcs16 = self.csum.csum();
177 fcs16 ^= 0xffff; // complement
178
179 // XXX: Use @truncate instead?
180 self.pushByteRaw(@intCast(u8, fcs16 & @as(u16, 0x00ff)));
181 self.pushByteRaw(@intCast(u8, fcs16 >> 8 & @as(u16, 0x00ff)));
182 }
183
184 self.pushByteRaw(Slip.END);
185 }
186
187 pub fn writer(self: *Frame) FrameWriter {
188 return .{ .context = self };
189 }
190};
191
192pub const SlipMux = struct {
193 slip: *Slip,
194 handler: ?CoapHandler = null,
195
196 fn handleCoAP(self: *SlipMux, buf: []const u8) !void {
197 // 1 byte (frame type) + 4 byte (coap message) + 2 byte CRC
198 if (buf.len <= 7)
199 return error.CoAPFrameTooShort;
200 if (!crc.validCsum(buf))
201 return error.InvalidChecksum;
202
203 // Strip frame identifier and 16-bit CRC FCS.
204 const msgBuf = buf[1..(buf.len - @sizeOf(u16))];
205
206 var req = try zoap.Request.init(msgBuf);
207 self.handler.?(&req);
208 }
209
210 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 }
223
224 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;
228
229 self.dispatchFrame(buf) catch |err| {
230 console.print("handleFrame failed: {s}\n", .{@errorName(err)});
231 };
232 }
233
234 pub fn newFrame(self: *SlipMux, ftype: FrameType) Frame {
235 return Frame.init(self.slip, ftype);
236 }
237
238 pub fn registerHandler(self: *SlipMux, handler: CoapHandler) !void {
239 self.handler = handler;
240 try self.slip.registerHandler(handleFrame, self);
241 }
242};