Something went wrong. Try again.
Completely reimplimented in zig (eventually)
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156const std = @import("std");const usb = @import("usb.zig");const Allocator = std.mem.Allocator;const t = std.testing;const Io = std.Io;const ArrayList = std.ArrayList;const Transport = usb.Transport;
const Session = @This();
io: Io,gpa: Allocator,ep_size: u16,sequence: u16,transport: Transport,
// Bulk-frame message ids (RI4ToolsController ICD4CommsUsb):// requests carry the command id, every response carries RESULT.const msg_result: u32 = 0x0000000D;const job_side_channel: u32 = 0;
const read_timeout_ms: u32 = 5000;const max_response: usize = 4096;
pub fn init(io: Io, allocator: Allocator, path: []const u8) !Session { return .{ .io = io, .gpa = allocator, .sequence = 0, .ep_size = 0, .transport = try .open(path, allocator, io), };}
pub fn deinit(self: *Session) void { self.transport.close(); self.* = undefined;}
/// Send one bulk-frame request and return the response's data section.////// Request: [msg_id u32][job u32][total u32][plen u32][payload]/// Response: [RESULT u32][job u32][total u32][plen u32][status u32]/// [dlen u32][data...]pub fn request(self: *Session, msg_id: u32, payload: []const u8) ![]u8 { var frame: ArrayList(u8) = .empty; defer frame.deinit(self.gpa); var word: [4]u8 = undefined;
std.mem.writeInt(u32, &word, msg_id, .little); try frame.appendSlice(self.gpa, &word); std.mem.writeInt(u32, &word, job_side_channel, .little); try frame.appendSlice(self.gpa, &word); std.mem.writeInt(u32, &word, @intCast(Transport.header_size + payload.len), .little); try frame.appendSlice(self.gpa, &word); std.mem.writeInt(u32, &word, @intCast(payload.len), .little); try frame.appendSlice(self.gpa, &word); try frame.appendSlice(self.gpa, payload);
try self.transport.write(frame.items, read_timeout_ms);
var raw: [max_response]u8 = undefined; const n = try self.transport.read(&raw, read_timeout_ms);
// PTG-style ops answer with the bare op payload ([op][code][len u16] // [data][chk][status]); other message classes use the RESULT // envelope. Distinguish by the first word. const first = if (n >= 4) std.mem.readInt(u32, raw[0..4], .little) else 0; if (first != msg_result) { if (n == 0) return error.ToolProtocol; return self.gpa.dupe(u8, raw[0..n]); }
if (n < Transport.header_size + 8) return error.ToolProtocol;
const total = std.mem.readInt(u32, raw[8..12], .little); if (total == Transport.header_size) { // Bare ack: status 0, no data. return self.gpa.alloc(u8, 0); } if (n < Transport.header_size + 8) return error.ToolProtocol; const status = std.mem.readInt(u32, raw[Transport.header_size..][0..4], .little); const dlen = std.mem.readInt(u32, raw[Transport.header_size + 4 ..][0..4], .little); if (dlen > n - Transport.header_size - 8) return error.ToolProtocol; if (status != 0) return error.ToolFailure; return self.gpa.dupe(u8, raw[Transport.header_size + 8 ..][0..dlen]);}
const protocol_ok: u8 = 0x80;const protocol_list: u8 = 0x81;const protocol_data: u8 = 0x84;const protocol_failed: u8 = 0xA0;
const Peeled = struct { ok: bool, data: []u8,
pub fn deinit(self: Peeled, gpa: Allocator) void { gpa.free(self.data); }};
fn peelResponse(gpa: Allocator, payload: []const u8, trailing_status: bool) !Peeled { switch (payload[0]) { protocol_ok => return .{ .ok = true, .data = try gpa.alloc(u8, 0) }, protocol_list => return .{ .ok = true, .data = try gpa.dupe(u8, payload[1..]) }, protocol_data => { if (trailing_status and payload[payload.len - 1] == 0) return .{ .ok = true, .data = try gpa.dupe(u8, payload[1 .. payload.len - 1]) }; const code = try gpa.alloc(u8, 1); code[0] = payload[payload.len - 1]; return .{ .ok = false, .data = code }; }, else => { if (payload.len < 2) return error.ToolProtocol; const code = try gpa.alloc(u8, 1); code[0] = payload[1]; return .{ .ok = false, .data = code }; }, }}
test "peelResponse" { { const p = [_]u8{Session.protocol_ok}; var r = try Session.peelResponse(t.allocator, &p, true); defer r.deinit(t.allocator); try t.expect(r.ok); try t.expectEqual(@as(usize, 0), r.data.len); }
{ const p = [_]u8{ Session.protocol_data, 0xAB, 0xCD, 0x00 }; var r = try Session.peelResponse(t.allocator, &p, true); defer r.deinit(t.allocator); try t.expect(r.ok); try t.expectEqualSlices(u8, &.{ 0xAB, 0xCD }, r.data); }
{ const p = [_]u8{ Session.protocol_data, 0xAB, 0xCD, 0x21 }; var r = try Session.peelResponse(t.allocator, &p, true); defer r.deinit(t.allocator); try t.expect(!r.ok); try t.expectEqualSlices(u8, &.{0x21}, r.data); }
{ const p = [_]u8{ Session.protocol_failed, 0x30 }; var r = try Session.peelResponse(t.allocator, &p, true); defer r.deinit(t.allocator); try t.expect(!r.ok); try t.expectEqualSlices(u8, &.{0x30}, r.data); }}