Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bulk transfer #2

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
9 changes: 8 additions & 1 deletion src/context.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const c = @import("c.zig");
const std = @import("std");
const DeviceHandle = @import("device_handle.zig").DeviceHandle;
const DeviceList = @import("device_list.zig").DeviceList;
const fromLibusb = @import("constructor.zig").fromLibusb;
const LogLevel = @import("options.zig").LogLevel;

const err = @import("error.zig");

Expand All @@ -15,6 +17,10 @@ pub const Context = struct {
return Context{ .raw = ctx.? };
}

pub fn setLogLevel(self: Context, log_level: LogLevel) err.Error!void {
try err.failable(c.libusb_set_option(self.raw, c.LIBUSB_OPTION_LOG_LEVEL, @intFromEnum(log_level)));
}

pub fn deinit(self: Context) void {
_ = c.libusb_exit(self.raw);
}
Expand All @@ -24,7 +30,8 @@ pub const Context = struct {
}

pub fn handleEvents(self: Context) err.Error!void {
try err.failable(c.libusb_handle_events_completed(self.raw, null));
var tv = std.c.timeval{ .sec = 0, .usec = 100_000 };
try err.failable(c.libusb_handle_events_timeout(self.raw, @ptrCast(&tv)));
}

pub fn openDeviceWithFd(self: *Context, fd: isize) err.Error!DeviceHandle {
Expand Down
12 changes: 12 additions & 0 deletions src/options.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,15 @@ const err = @import("error.zig");
pub fn disableDeviceDiscovery() err.Error!void {
try err.failable(c.libusb_set_option(null, c.LIBUSB_OPTION_NO_DEVICE_DISCOVERY));
}

pub const LogLevel = enum(u8) {
None = 0,
Error,
Warning,
Info,
Debug,
};

pub fn setLogLevel(log_level: LogLevel) err.Error!void {
try err.failable(c.libusb_set_option(null, c.LIBUSB_OPTION_LOG_LEVEL, @intFromEnum(log_level)));
}
150 changes: 110 additions & 40 deletions src/transfer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,84 @@ const PacketDescriptors = @import("packet_descriptor.zig").PacketDescriptors;

const err = @import("error.zig");

/// WIP
pub const TransferFlags = packed struct(u8) {
shortNotOk: bool = false,
freeBuffer: bool = false,
freeTransfer: bool = false,
addZeroPacket: bool = false,
_padding: u4 = 0,
};

pub const TransferStatus = enum(u8) {
Completed = 0,
Error,
Timeout,
Cancelled,
Stall,
Overflow,
NoDevice,
_,
};

fn transferStatusFromLibusb(transfer_status: c_uint) TransferStatus {
return @enumFromInt(transfer_status);
}

pub fn Transfer(comptime T: type) type {
return struct {
const Self = @This();

allocator: Allocator,
buf: []u8,
transfer: *c.libusb_transfer,
callback: *const fn (*T, []const u8) void,
user_data: *T,
active: bool,
should_resubmit: bool = true,
callback: *const fn (*Self) void,
user_data: ?*T,
active: bool = false,

pub fn deinit(self: *const Self) void {
c.libusb_free_transfer(self.transfer);
pub fn deinit(self: *Self) void {
if (self.active) {
@panic("Can't deinit an active transfer");
}
const flags = self.transferFlags();
if (!flags.freeTransfer) {
c.libusb_free_transfer(self.transfer);
}
self.allocator.free(self.buf);
self.allocator.destroy(self);
}

pub fn transferFlags(self: Self) TransferFlags {
return @bitCast(self.transfer.*.flags);
}

pub fn transferStatus(self: Self) TransferStatus {
return transferStatusFromLibusb(self.transfer.*.status);
}

pub fn isoPackets(self: Self) PacketDescriptors {
const num_iso_packets: usize = @intCast(self.transfer.*.num_iso_packets);
return PacketDescriptors.init(self.transfer, self.transfer.*.iso_packet_desc()[0..num_iso_packets]);
}

pub fn submit(self: *Self) err.Error!void {
if (!self.should_resubmit) {
return;
}
self.active = true;
try err.failable(c.libusb_submit_transfer(self.transfer));
}

pub fn cancel(self: *Self) err.Error!void {
self.should_resubmit = false;
try err.failable(c.libusb_cancel_transfer(self.transfer));
}

pub fn buffer(self: Self) []u8 {
const length = std.math.cast(usize, self.transfer.length) orelse @panic("Buffer length too large");
pub fn getData(self: Self) []u8 {
const length = std.math.cast(usize, self.transfer.actual_length) orelse @panic("Buffer length too large");
return self.transfer.buffer[0..length];
}

pub fn setData(self: *Self, data: []const u8) void {
@memcpy(self.buf, data);
self.transfer.length = std.math.cast(c_int, data.len) orelse @panic("Buffer length too large");
}

pub fn isActive(self: Self) bool {
return self.active;
}
Expand All @@ -54,9 +95,10 @@ pub fn Transfer(comptime T: type) type {
endpoint: u8,
packet_size: u16,
num_packets: u16,
callback: *const fn (*T, []const u8) void,
callback: *const fn (*Self) void,
user_data: *T,
timeout: u64,
flags: TransferFlags,
) !*Self {
const buf = try allocator.alloc(u8, packet_size * num_packets);
const opt_transfer: ?*c.libusb_transfer = c.libusb_alloc_transfer(num_packets);
Expand All @@ -78,9 +120,10 @@ pub fn Transfer(comptime T: type) type {
transfer.*.buffer = buf.ptr;
transfer.*.length = std.math.cast(c_int, buf.len) orelse @panic("Length too large");
transfer.*.num_iso_packets = std.math.cast(c_int, num_packets) orelse @panic("Number of packets too large");
transfer.*.callback = callbackRawIso;
transfer.*.callback = callbackRaw;
transfer.*.user_data = @ptrCast(self);
transfer.*.timeout = std.math.cast(c_uint, timeout) orelse @panic("Timeout too large");
transfer.*.flags = @bitCast(flags);

c.libusb_set_iso_packet_lengths(transfer, packet_size);

Expand All @@ -90,46 +133,62 @@ pub fn Transfer(comptime T: type) type {
}
}

export fn callbackRawIso(transfer: [*c]c.libusb_transfer) void {
const self: *Self = @alignCast(@ptrCast(transfer.*.user_data.?));
self.active = false;
if (transfer.*.status != c.LIBUSB_TRANSFER_COMPLETED) {
return;
}
const num_iso_packets: usize = @intCast(transfer.*.num_iso_packets);
var isoPackets = PacketDescriptors.init(transfer, transfer.*.iso_packet_desc()[0..num_iso_packets]);
while (isoPackets.next()) |pack| {
if (!pack.isCompleted()) {
std.log.info("Isochronous transfer failed, status: {}", .{pack.status()});
continue;
}
self.callback(self.user_data, pack.buffer());
}
self.submit() catch |e| std.log.err("Failed to resubmit isochronous transfer: {}", .{e});
}

pub fn fillInterrupt(
allocator: *Allocator,
allocator: Allocator,
handle: *DeviceHandle,
endpoint: u8,
buffer_size: usize,
callback: fn (*Self) void,
user_data: T,
callback: *const fn (*T, []const u8) anyerror!void,
user_data: *T,
timeout: u64,
) (Allocator.err.Error || err.Error)!*Self {
flags: TransferFlags,
) (Allocator.Error || err.Error)!*Self {
const buf = try allocator.alloc(u8, buffer_size);

const opt_transfer: ?*c.libusb_transfer = c.libusb_alloc_transfer(0);

if (opt_transfer) |transfer| {
transfer.*.dev_handle = handle.handle;
const self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.transfer = transfer,
.user_data = user_data,
.buf = buf,
.callback = callback,
.active = true,
};

transfer.*.dev_handle = handle.raw;
transfer.*.endpoint = endpoint;
transfer.*.type = c.LIBUSB_TRANSFER_TYPE_INTERRUPT;
transfer.*.timeout = std.math.cast(c_uint, timeout) orelse @panic("Timeout too large");
transfer.*.buffer = buf.ptr;
transfer.*.length = std.math.cast(c_int, buf.len) orelse @panic("Length too large");
transfer.*.user_data = @ptrCast(self);
transfer.*.callback = callbackRaw;
transfer.*.flags = @bitCast(flags);

return self;
} else {
return error.OutOfMemory;
}
}

pub fn fillBulk(
allocator: Allocator,
handle: *DeviceHandle,
endpoint: u8,
buffer_size: usize,
callback: *const fn (*Self) void,
user_data: ?*T,
timeout: u64,
flags: TransferFlags,
) (Allocator.Error || err.Error)!*Self {
const buf = try allocator.alloc(u8, buffer_size);

const opt_transfer: ?*c.libusb_transfer = c.libusb_alloc_transfer(0);

if (opt_transfer) |transfer| {
const self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
Expand All @@ -140,15 +199,26 @@ pub fn Transfer(comptime T: type) type {
.active = true,
};

transfer.*.dev_handle = handle.raw;
transfer.*.endpoint = endpoint;
transfer.*.type = c.LIBUSB_TRANSFER_TYPE_BULK;
transfer.*.timeout = std.math.cast(c_uint, timeout) orelse @panic("Timeout too large");
transfer.*.buffer = buf.ptr;
transfer.*.length = std.math.cast(c_int, buf.len) orelse @panic("Length too large");
transfer.*.user_data = @ptrCast(self);
transfer.*.callback = callbackRaw;
transfer.*.flags = @bitCast(flags);

return self;
} else {
return error.OutOfMemory;
}
}

export fn callbackRaw(transfer: [*c]c.libusb_transfer) void {
fn callbackRaw(transfer: [*c]c.libusb_transfer) callconv(.C) void {
const self: *Self = @alignCast(@ptrCast(transfer.*.user_data.?));
self.callback(self.user_data, self.buffer());
self.active = false;
self.callback(self);
}
};
}
1 change: 1 addition & 0 deletions zusb.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub usingnamespace @import("src/fields.zig");
pub usingnamespace @import("src/interface_descriptor.zig");
pub usingnamespace @import("src/transfer.zig");
pub usingnamespace @import("src/packet_descriptor.zig");
pub usingnamespace @import("src/options.zig");

comptime {
@import("std").testing.refAllDecls(@This());
Expand Down