start: initial minimal implementation

3 tabs: bitcoin, lightning and settings.
only settings is populated, with wifi info and shutdown.

see build.zig for how everything ties up together.
add-custom-font
alex 2 years ago
parent 68dd23525e
commit bfc71fec92

@ -0,0 +1,5 @@
build for rpi:
zig build -Dtarget=aarch64-linux-musl -Ddriver=fbev -Drelease-safe -Dstrip
otherwise just `zig build` on dev host

@ -1,34 +1,283 @@
const std = @import("std");
const nifbuild = @import("lib/nif/build.zig");
pub fn build(b: *std.build.Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
b.use_stage1 = true;
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();
const strip = b.option(bool, "strip", "strip output binary; default: false") orelse false;
const drv = b.option(DriverTarget, "driver", "display and input drivers combo; default: sdl2") orelse .sdl2;
const disp_horiz = b.option(u32, "horiz", "display horizontal pixels count; default: 800") orelse 800;
const disp_vert = b.option(u32, "vert", "display vertical pixels count; default: 480") orelse 480;
const exe = b.addExecutable("ngui", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
// gui build
const ngui = b.addExecutable("ngui", "src/ngui.zig");
ngui.setTarget(target);
ngui.setBuildMode(mode);
ngui.pie = true;
ngui.strip = strip;
const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
ngui.addIncludePath("lib");
ngui.addIncludePath("src/ui/c");
ngui.linkLibC();
const lvgl_flags = &.{
"-std=c11",
"-fstack-protector",
"-Wall",
"-Wextra",
"-Wformat",
"-Wformat-security",
"-Wundef",
};
ngui.addCSourceFiles(lvgl_generic_src, lvgl_flags);
const ngui_cflags: []const []const u8 = &.{
"-std=c11",
"-Wall",
"-Wextra",
"-Wshadow",
"-Wundef",
"-Wunused-parameter",
"-Werror",
};
ngui.addCSourceFiles(&.{
"src/ui/c/ui.c",
"src/ui/c/lv_font_courierprimecode_14.c",
//"src/ui/c/lv_font_courierprimecode_16.c",
"src/ui/c/lv_font_courierprimecode_24.c",
}, ngui_cflags);
ngui.defineCMacroRaw(b.fmt("NM_DISP_HOR={}", .{disp_horiz}));
ngui.defineCMacroRaw(b.fmt("NM_DISP_VER={}", .{disp_vert}));
ngui.defineCMacro("LV_CONF_INCLUDE_SIMPLE", null);
ngui.defineCMacro("LV_TICK_CUSTOM", "1");
ngui.defineCMacro("LV_TICK_CUSTOM_INCLUDE", "\"ui.h\"");
ngui.defineCMacro("LV_TICK_CUSTOM_SYS_TIME_EXPR", "(nm_get_curr_tick())");
switch (drv) {
.sdl2 => {
ngui.addCSourceFiles(lvgl_sdl2_src, lvgl_flags);
ngui.addCSourceFile("src/ui/c/drv_sdl2.c", ngui_cflags);
ngui.defineCMacro("NM_DRV_SDL2", null);
ngui.defineCMacro("USE_SDL", null);
ngui.linkSystemLibrary("SDL2");
},
.fbev => {
ngui.addCSourceFiles(lvgl_fbev_src, lvgl_flags);
ngui.addCSourceFile("src/ui/c/drv_fbev.c", ngui_cflags);
ngui.defineCMacro("NM_DRV_FBEV", null);
ngui.defineCMacro("USE_FBDEV", null);
ngui.defineCMacro("USE_EVDEV", null);
},
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const ngui_build_step = b.step("ngui", "build ngui (nakamochi gui)");
ngui_build_step.dependOn(&b.addInstallArtifact(ngui).step);
// daemon build
const nd = b.addExecutable("nd", "src/nd.zig");
nd.setTarget(target);
nd.setBuildMode(mode);
nd.pie = true;
nd.strip = strip;
nifbuild.addPkg(b, nd, "lib/nif");
const niflib = nifbuild.library(b, "lib/nif");
niflib.setTarget(target);
niflib.setBuildMode(mode);
nd.linkLibrary(niflib);
const exe_tests = b.addTest("src/main.zig");
exe_tests.setTarget(target);
exe_tests.setBuildMode(mode);
const nd_build_step = b.step("nd", "build nd (nakamochi daemon)");
nd_build_step.dependOn(&b.addInstallArtifact(nd).step);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);
// default build
const build_all_step = b.step("all", "build everything");
build_all_step.dependOn(ngui_build_step);
build_all_step.dependOn(nd_build_step);
b.default_step.dependOn(build_all_step);
{
const tests = b.addTest("src/test.zig");
tests.setTarget(target);
tests.setBuildMode(mode);
tests.linkLibC();
if (b.args) |args| {
for (args) |a, i| {
if (std.mem.eql(u8, a, "--test-filter")) {
tests.setFilter(args[i + 1]); // don't care about OOB
break;
}
}
}
const test_step = b.step("test", "run tests");
test_step.dependOn(&tests.step);
}
}
const DriverTarget = enum {
sdl2,
fbev, // framebuffer + evdev
};
const lvgl_sdl2_src: []const []const u8 = &.{
"lib/lv_drivers/sdl/sdl.c",
"lib/lv_drivers/sdl/sdl_common.c",
};
const lvgl_fbev_src: []const []const u8 = &.{
"lib/lv_drivers/display/fbdev.c",
"lib/lv_drivers/indev/evdev.c",
};
const lvgl_generic_src: []const []const u8 = &.{
"lib/lvgl/src/core/lv_disp.c",
"lib/lvgl/src/core/lv_event.c",
"lib/lvgl/src/core/lv_group.c",
"lib/lvgl/src/core/lv_indev.c",
"lib/lvgl/src/core/lv_indev_scroll.c",
"lib/lvgl/src/core/lv_obj.c",
"lib/lvgl/src/core/lv_obj_class.c",
"lib/lvgl/src/core/lv_obj_draw.c",
"lib/lvgl/src/core/lv_obj_pos.c",
"lib/lvgl/src/core/lv_obj_scroll.c",
"lib/lvgl/src/core/lv_obj_style.c",
"lib/lvgl/src/core/lv_obj_style_gen.c",
"lib/lvgl/src/core/lv_obj_tree.c",
"lib/lvgl/src/core/lv_refr.c",
"lib/lvgl/src/core/lv_theme.c",
"lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.c",
"lib/lvgl/src/draw/lv_draw.c",
"lib/lvgl/src/draw/lv_draw_arc.c",
"lib/lvgl/src/draw/lv_draw_img.c",
"lib/lvgl/src/draw/lv_draw_label.c",
"lib/lvgl/src/draw/lv_draw_layer.c",
"lib/lvgl/src/draw/lv_draw_line.c",
"lib/lvgl/src/draw/lv_draw_mask.c",
"lib/lvgl/src/draw/lv_draw_rect.c",
"lib/lvgl/src/draw/lv_draw_transform.c",
"lib/lvgl/src/draw/lv_draw_triangle.c",
"lib/lvgl/src/draw/lv_img_buf.c",
"lib/lvgl/src/draw/lv_img_cache.c",
"lib/lvgl/src/draw/lv_img_decoder.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_arc.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_bg.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_img.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_label.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_line.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_polygon.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c",
"lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.c",
"lib/lvgl/src/draw/sw/lv_draw_sw.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_arc.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_blend.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_dither.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_gradient.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_img.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_layer.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_letter.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_line.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_polygon.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_rect.c",
"lib/lvgl/src/draw/sw/lv_draw_sw_transform.c",
"lib/lvgl/src/extra/layouts/flex/lv_flex.c",
"lib/lvgl/src/extra/layouts/grid/lv_grid.c",
"lib/lvgl/src/extra/libs/bmp/lv_bmp.c",
"lib/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.c",
"lib/lvgl/src/extra/libs/freetype/lv_freetype.c",
"lib/lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.c",
"lib/lvgl/src/extra/libs/fsdrv/lv_fs_posix.c",
"lib/lvgl/src/extra/libs/fsdrv/lv_fs_stdio.c",
"lib/lvgl/src/extra/libs/fsdrv/lv_fs_win32.c",
"lib/lvgl/src/extra/libs/gif/gifdec.c",
"lib/lvgl/src/extra/libs/gif/lv_gif.c",
"lib/lvgl/src/extra/libs/png/lodepng.c",
"lib/lvgl/src/extra/libs/png/lv_png.c",
"lib/lvgl/src/extra/libs/qrcode/lv_qrcode.c",
"lib/lvgl/src/extra/libs/qrcode/qrcodegen.c",
"lib/lvgl/src/extra/libs/rlottie/lv_rlottie.c",
"lib/lvgl/src/extra/libs/sjpg/lv_sjpg.c",
"lib/lvgl/src/extra/libs/sjpg/tjpgd.c",
"lib/lvgl/src/extra/lv_extra.c",
"lib/lvgl/src/extra/others/fragment/lv_fragment.c",
"lib/lvgl/src/extra/others/fragment/lv_fragment_manager.c",
"lib/lvgl/src/extra/others/gridnav/lv_gridnav.c",
"lib/lvgl/src/extra/others/ime/lv_ime_pinyin.c",
"lib/lvgl/src/extra/others/imgfont/lv_imgfont.c",
"lib/lvgl/src/extra/others/monkey/lv_monkey.c",
"lib/lvgl/src/extra/others/msg/lv_msg.c",
"lib/lvgl/src/extra/others/snapshot/lv_snapshot.c",
"lib/lvgl/src/extra/themes/basic/lv_theme_basic.c",
"lib/lvgl/src/extra/themes/default/lv_theme_default.c",
"lib/lvgl/src/extra/themes/mono/lv_theme_mono.c",
"lib/lvgl/src/extra/widgets/animimg/lv_animimg.c",
"lib/lvgl/src/extra/widgets/calendar/lv_calendar.c",
"lib/lvgl/src/extra/widgets/calendar/lv_calendar_header_arrow.c",
"lib/lvgl/src/extra/widgets/calendar/lv_calendar_header_dropdown.c",
"lib/lvgl/src/extra/widgets/chart/lv_chart.c",
"lib/lvgl/src/extra/widgets/colorwheel/lv_colorwheel.c",
"lib/lvgl/src/extra/widgets/imgbtn/lv_imgbtn.c",
"lib/lvgl/src/extra/widgets/keyboard/lv_keyboard.c",
"lib/lvgl/src/extra/widgets/led/lv_led.c",
"lib/lvgl/src/extra/widgets/list/lv_list.c",
"lib/lvgl/src/extra/widgets/menu/lv_menu.c",
"lib/lvgl/src/extra/widgets/meter/lv_meter.c",
"lib/lvgl/src/extra/widgets/msgbox/lv_msgbox.c",
"lib/lvgl/src/extra/widgets/span/lv_span.c",
"lib/lvgl/src/extra/widgets/spinbox/lv_spinbox.c",
"lib/lvgl/src/extra/widgets/spinner/lv_spinner.c",
"lib/lvgl/src/extra/widgets/tabview/lv_tabview.c",
"lib/lvgl/src/extra/widgets/tileview/lv_tileview.c",
"lib/lvgl/src/extra/widgets/win/lv_win.c",
"lib/lvgl/src/font/lv_font.c",
"lib/lvgl/src/font/lv_font_fmt_txt.c",
"lib/lvgl/src/font/lv_font_loader.c",
"lib/lvgl/src/hal/lv_hal_disp.c",
"lib/lvgl/src/hal/lv_hal_indev.c",
"lib/lvgl/src/hal/lv_hal_tick.c",
"lib/lvgl/src/misc/lv_anim.c",
"lib/lvgl/src/misc/lv_anim_timeline.c",
"lib/lvgl/src/misc/lv_area.c",
"lib/lvgl/src/misc/lv_async.c",
"lib/lvgl/src/misc/lv_bidi.c",
"lib/lvgl/src/misc/lv_color.c",
"lib/lvgl/src/misc/lv_fs.c",
"lib/lvgl/src/misc/lv_gc.c",
"lib/lvgl/src/misc/lv_ll.c",
"lib/lvgl/src/misc/lv_log.c",
"lib/lvgl/src/misc/lv_lru.c",
"lib/lvgl/src/misc/lv_math.c",
"lib/lvgl/src/misc/lv_mem.c",
"lib/lvgl/src/misc/lv_printf.c",
"lib/lvgl/src/misc/lv_style.c",
"lib/lvgl/src/misc/lv_style_gen.c",
"lib/lvgl/src/misc/lv_templ.c",
"lib/lvgl/src/misc/lv_timer.c",
"lib/lvgl/src/misc/lv_tlsf.c",
"lib/lvgl/src/misc/lv_txt.c",
"lib/lvgl/src/misc/lv_txt_ap.c",
"lib/lvgl/src/misc/lv_utils.c",
"lib/lvgl/src/widgets/lv_arc.c",
"lib/lvgl/src/widgets/lv_bar.c",
"lib/lvgl/src/widgets/lv_btn.c",
"lib/lvgl/src/widgets/lv_btnmatrix.c",
"lib/lvgl/src/widgets/lv_canvas.c",
"lib/lvgl/src/widgets/lv_checkbox.c",
"lib/lvgl/src/widgets/lv_dropdown.c",
"lib/lvgl/src/widgets/lv_img.c",
"lib/lvgl/src/widgets/lv_label.c",
"lib/lvgl/src/widgets/lv_line.c",
"lib/lvgl/src/widgets/lv_objx_templ.c",
"lib/lvgl/src/widgets/lv_roller.c",
"lib/lvgl/src/widgets/lv_slider.c",
"lib/lvgl/src/widgets/lv_switch.c",
"lib/lvgl/src/widgets/lv_table.c",
"lib/lvgl/src/widgets/lv_textarea.c",
};

@ -0,0 +1,73 @@
the trouble is fontawesome now supplies multiple files,
fa-brands-400.woff2, fa-regular-400.woff2 and fa-solid-900.woff2.
don't know which symbol is in which file.
list all defined symbols into a file:
grep 0x ../ngui/lib/lvgl/src/font/lv_symbol_def.h | grep -v 0x2022 | \
cut -d, -f2 | cut -c4-7 | tr 'A-F' 'a-f' \
> /tmp/sym.txt
download and unzip fontawesome. expect to find metadata/icons.yml file.
grep metadata to find out which set each icon is in:
for c in $(cat /tmp/sym.txt); do
t=$(grep -B3 "unicode: $c" metadata/icons.yml | grep -- '- ' | head -n1 | tr -d ' -')
echo "$c\t$t"
done
some icons are in multiple styles. search for the code on https://fontawesome.com/icons/
and compare to the image on https://docs.lvgl.io/8.3/overview/font.html.
the command above takes the first one listed in icons.yml, which is usually "solid".
when searching on fontawesome, make sure it's a free icon, as opposed to their pro version.
not all icons might be present. at the time of writing, the following codes are amiss:
- 0xf067 `LV_SYMBOL_PLUS`; actually exists but listed as unicode:2b in icons.yml
- 0xf8a2 `LV_SYMBOL_NEW_LINE`; looks like fontawesome removed `level-down-alt` from v6
so i picked an alternative 0xf177 `arrow-left-long`
dump previous command output into an fa-icon-style.txt file. add missing "solid" style
in the second column and replace f8a2 with `f177=>0xf8a2` mapping. the latter is
the syntax for when running [lvgl font convertion tool](https://github.com/lvgl/lv_font_conv).
while there, add more codes to the file, separating columns with a single tab:
- 0xf379 brands (bitcoin)
- 0xe0b4 solid (bitcoin-sign)
- 0xf0e7 solid (lightning bolt)
split the previously generated fa-icon-style.txt file into chunks suitable for
constructing lvgl's font converter arguments.
first, check which styles are present. at the moment, only "brands" and "solid"
are used:
$ cut -f2 fa-icon-style.txt | sort | uniq -c
3 brands
61 solid
then split the file, for each style from the previous command. example for "solid":
grep solid fa-icon-style.txt | cut -f1 | tr 'a-f' 'A-F' | \
while IFS= read -r line; do printf "0x$line\n"; done | \
paste -s -d, | tr -d '\n' > fa-solid.txt
typically, you'll want to bundle the symbols from fontawesome with a regular font.
i'll use [courier prime code](https://github.com/quoteunquoteapps/courierprimecode)
as an example.
install the font converter tool; requires nodejs:
npm i lvgl/lv_font_conv
finally, convert and bundle all fonts, for 14px size as an example:
./node_modules/.bin/lv_font_conv --no-compress --no-prefilter --bpp 4 --size 14 \
--font courier-prime-code.ttf -r 0x20-0x7F,0xB0,0x2022 \
--font fa-brands-400.ttf -r $(cat fa-brands.txt) \
--font fa-solid-900.ttf -r $(cat fa-solid.txt) \
--format lvgl --force-fast-kern-format \
-o lv_font_courierprimecode_14.c
the arguments are similar to those in the header of any LVGL font in `lib/lvgl/src/font/lv_font/xxx.c`.

@ -0,0 +1,29 @@
const build = @import("std").build;
pub fn addPkg(b: *build.Builder, obj: *build.LibExeObjStep, prefix: []const u8) void {
obj.addPackagePath("nif", pkgPath(b, prefix));
}
pub fn pkgPath(b: *build.Builder, prefix: []const u8) []const u8 {
return b.pathJoin(&.{prefix, "nif.zig"});
}
pub fn library(b: *build.Builder, prefix: []const u8) *build.LibExeObjStep {
const lib = b.addStaticLibrary("nif", b.pathJoin(&.{prefix, "nif.zig"}));
lib.addIncludePath(b.pathJoin(&.{prefix, "wpa_supplicant"}));
lib.defineCMacro("CONFIG_CTRL_IFACE", null);
lib.defineCMacro("CONFIG_CTRL_IFACE_UNIX", null);
lib.addCSourceFiles(&.{
b.pathJoin(&.{prefix, "wpa_supplicant/wpa_ctrl.c"}),
b.pathJoin(&.{prefix, "wpa_supplicant/os_unix.c"}),
}, &.{
"-Wall",
"-Wextra",
"-Wshadow",
"-Wundef",
"-Wunused-parameter",
"-Werror",
});
lib.linkLibC();
return lib;
}

@ -0,0 +1,63 @@
const std = @import("std");
const mem = std.mem;
const net = std.net;
const os = std.os;
pub const wpa = @import("wpa.zig");
const IFF_UP = 1 << 0; //0b1;
const IFF_LOOPBACK = 1 << 3; //0b1000;
const ifaddrs = extern struct {
next: ?*ifaddrs,
name: [*:0]const u8,
flags: c_uint, // see IFF_xxx SIOCGIFFLAGS in netdevice(7)
addr: ?*std.os.sockaddr,
netmask: ?*std.os.sockaddr,
ifu: extern union {
broad: *os.sockaddr, // flags & IFF_BROADCAST
dst: *os.sockaddr, // flags & IFF_POINTOPOINT
},
data: ?*anyopaque,
};
extern "c" fn getifaddrs(ptrp: **ifaddrs) c_int;
extern "c" fn freeifaddrs(ptr: *ifaddrs) void;
/// retrieves a list of all public IP addresses assigned to the network interfaces,
/// optionally filtering by the interface name.
/// caller owns the returned value.
pub fn pubAddresses(allocator: mem.Allocator, ifname: ?[]const u8) ![]net.Address {
var res: *ifaddrs = undefined;
if (getifaddrs(&res) != 0) {
return error.Getifaddrs;
}
defer freeifaddrs(res);
var list = std.ArrayList(net.Address).init(allocator);
var it: ?*ifaddrs = res;
while (it) |ifa| : (it = ifa.next) {
const sa: *os.sockaddr = ifa.addr orelse continue;
if (sa.family != os.AF.INET and sa.family != os.AF.INET6) {
// not an IP address
continue;
}
if (ifa.flags & IFF_UP == 0 or ifa.flags & IFF_LOOPBACK != 0) {
// skip loopbacks and those which are not "up"
continue;
}
const ipaddr = net.Address.initPosix(@alignCast(4, sa)); // initPosix makes a copy
if (ipaddr.any.family == os.AF.INET6 and ipaddr.in6.sa.scope_id > 0) {
// want only global, with 0 scope
// non-zero scopes make sense for link-local addr only.
continue;
}
if (ifname) |name| {
if (!mem.eql(u8, name, mem.sliceTo(ifa.name, 0))) {
continue;
}
}
try list.append(ipaddr);
}
return list.toOwnedSlice();
}

@ -0,0 +1,384 @@
const std = @import("std");
const mem = std.mem;
const Thread = std.Thread;
const WPACtrl = opaque {};
const WPAReqCallback = *const fn ([*:0]const u8, usize) callconv(.C) void;
extern fn wpa_ctrl_open(ctrl_path: [*:0]const u8) ?*WPACtrl;
extern fn wpa_ctrl_close(ctrl: *WPACtrl) void;
extern fn wpa_ctrl_request(ctrl: *WPACtrl, cmd: [*:0]const u8, clen: usize, reply: [*:0]u8, rlen: *usize, cb: ?WPAReqCallback) c_int;
extern fn wpa_ctrl_pending(ctrl: *WPACtrl) c_int;
extern fn wpa_ctrl_recv(ctrl: *WPACtrl, reply: [*:0]u8, reply_len: *usize) c_int;
pub const Control = struct {
//mu: Thread.Mutext = .{},
wpa_ctrl: *WPACtrl,
attached: bool = false,
const Self = @This();
pub const Error = error{
NameTooLong,
WpaCtrlFailure,
WpaCtrlTimeout,
WpaCtrlAttach,
WpaCtrlDetach,
WpaCtrlScanStart,
WpaCtrlSaveConfig,
WpaCtrlAddNetwork,
WpaCtrlRemoveNetwork,
WpaCtrlSelectNetwork,
WpaCtrlEnableNetwork,
WpaCtrlSetNetworkParam,
} || std.fmt.BufPrintError;
// TODO: using this in Self.request
pub const RequestCallback = *const fn (msg: [:0]const u8) void;
fn wpaErr(i: c_int) Error {
return switch (i) {
-2 => error.WpaCtrlTimeout,
else => error.WpaCtrlFailure,
};
}
/// open a WPA control interface identified by the path.
/// the returned instance must be close'd when done to free resources.
/// TODO: describe @android: and @abstract: prefixes
/// TODO: what about UDP, on windows?
pub fn open(path: [:0]const u8) Error!Self {
const ctrl = wpa_ctrl_open(path);
if (ctrl == null) {
return error.WpaCtrlFailure;
}
return Self{ .wpa_ctrl = ctrl.? };
}
/// release all associated resources, including detach'ing a monitor.
pub fn close(self: *Self) Error!void {
//self.mu.lock();
//defer self.mu.unlock();
if (self.attached) {
try self.detach();
}
wpa_ctrl_close(self.wpa_ctrl);
}
/// start control interface events monitoring.
/// presence of events is reported by self.pending; then can be read using self.receive.
pub fn attach(self: *Self) Error!void {
self.reqOK("ATTACH") catch return error.WpaCtrlAttach;
self.attached = true;
}
/// stop control interface events monitoring.
pub fn detach(self: *Self) Error!void {
self.reqOK("DETACH") catch return error.WpaCtrlDetach;
self.attached = false;
}
/// request wifi scan
pub fn scan(self: *Self) Error!void {
self.reqOK("SCAN") catch return error.WpaCtrlScanStart;
}
/// dump in-memory config to a file, typically /etc/wpa_supplicant/wpa_supplicant.conf.
/// fails if update_config set to 0.
pub fn saveConfig(self: *Self) Error!void {
self.reqOK("SAVE_CONFIG") catch return error.WpaCtrlSaveConfig;
}
/// add a new blank network, returning its ID.
/// the newly added network can be configured with self.setNetworkParam.
pub fn addNetwork(self: *Self) (Error||std.fmt.ParseIntError)!u32 {
var buf: [10:0]u8 = undefined;
const resp = self.request("ADD_NETWORK", &buf, null) catch return error.WpaCtrlAddNetwork;
return std.fmt.parseUnsigned(u32, mem.trim(u8, resp, "\n "), 10);
}
pub fn removeNetwork(self: *Self, id: u32) Error!void {
var buf: [48:0]u8 = undefined;
const cmd = try std.fmt.bufPrintZ(&buf, "REMOVE_NETWORK {d}", .{id});
return self.reqOK(cmd) catch return error.WpaCtrlRemoveNetwork;
}
pub fn selectNetwork(self: *Self, id: u32) Error!void {
var buf: [48:0]u8 = undefined;
const cmd = try std.fmt.bufPrintZ(&buf, "SELECT_NETWORK {d}", .{id});
return self.reqOK(cmd) catch return error.WpaCtrlSelectNetwork;
}
pub fn enableNetwork(self: *Self, id: u32) Error!void {
var buf: [48:0]u8 = undefined;
const cmd = try std.fmt.bufPrintZ(&buf, "ENABLE_NETWORK {d}", .{id});
return self.reqOK(cmd) catch return error.WpaCtrlEnableNetwork;
}
pub fn setNetworkParam(self: *Self, id: u32, name: []const u8, value: []const u8) Error!void {
var buf: [512:0]u8 = undefined;
const cmd = try std.fmt.bufPrintZ(&buf, "SET_NETWORK {d} {s} {s}", .{id, name, value});
return self.reqOK(cmd) catch return error.WpaCtrlSetNetworkParam;
}
fn reqOK(self: *Self, cmd: [:0]const u8) Error!void {
var buf: [10:0]u8 = undefined;
const resp = try self.request(cmd, &buf, null);
if (!mem.startsWith(u8, resp, "OK\n")) {
return error.WpaCtrlFailure;
}
}
/// send a command to the control interface, returning a response owned by buf.
/// callback receives a message from the same buf.
pub fn request(self: Self, cmd: [:0]const u8, buf: [:0]u8, callback: ?WPAReqCallback) Error![]const u8 {
//self.mu.lock();
//defer self.mu.unlock();
var n: usize = buf.len;
const e = wpa_ctrl_request(self.wpa_ctrl, cmd, cmd.len, buf, &n, callback);
if (e != 0) {
return wpaErr(e);
}
return buf[0..n];
}
/// reports whether pending messages are waiting to be read using self.receive.
/// requires self to be attach'ed.
pub fn pending(self: Self) Error!bool {
//self.mu.lock();
//defer self.mu.unlock();
const n = wpa_ctrl_pending(self.wpa_ctrl);
if (n < 0) {
return wpaErr(n);
}
return n > 0;
}
/// retrieve a pending message using the provided buf.
/// returned slice is owned by the buf.
/// requires self to be attach'ed.
pub fn receive(self: Self, buf: [:0]u8) Error![]const u8 {
//self.mu.lock();
//defer self.mu.unlock();
var n: usize = buf.len;
const e = wpa_ctrl_recv(self.wpa_ctrl, buf, &n);
if (e != 0) {
return wpaErr(e);
}
return buf[0..n];
}
};
//pub const WPA_CTRL_REQ = "CTRL-REQ-";
//pub const WPA_CTRL_RSP = "CTRL-RSP-";
//pub const WPA_EVENT_CONNECTED = "CTRL-EVENT-CONNECTED ";
//pub const WPA_EVENT_DISCONNECTED = "CTRL-EVENT-DISCONNECTED ";
//pub const WPA_EVENT_ASSOC_REJECT = "CTRL-EVENT-ASSOC-REJECT ";
//pub const WPA_EVENT_AUTH_REJECT = "CTRL-EVENT-AUTH-REJECT ";
//pub const WPA_EVENT_TERMINATING = "CTRL-EVENT-TERMINATING ";
//pub const WPA_EVENT_PASSWORD_CHANGED = "CTRL-EVENT-PASSWORD-CHANGED ";
//pub const WPA_EVENT_EAP_NOTIFICATION = "CTRL-EVENT-EAP-NOTIFICATION ";
//pub const WPA_EVENT_EAP_STARTED = "CTRL-EVENT-EAP-STARTED ";
//pub const WPA_EVENT_EAP_PROPOSED_METHOD = "CTRL-EVENT-EAP-PROPOSED-METHOD ";
//pub const WPA_EVENT_EAP_METHOD = "CTRL-EVENT-EAP-METHOD ";
//pub const WPA_EVENT_EAP_PEER_CERT = "CTRL-EVENT-EAP-PEER-CERT ";
//pub const WPA_EVENT_EAP_PEER_ALT = "CTRL-EVENT-EAP-PEER-ALT ";
//pub const WPA_EVENT_EAP_TLS_CERT_ERROR = "CTRL-EVENT-EAP-TLS-CERT-ERROR ";
//pub const WPA_EVENT_EAP_STATUS = "CTRL-EVENT-EAP-STATUS ";
//pub const WPA_EVENT_EAP_RETRANSMIT = "CTRL-EVENT-EAP-RETRANSMIT ";
//pub const WPA_EVENT_EAP_RETRANSMIT2 = "CTRL-EVENT-EAP-RETRANSMIT2 ";
//pub const WPA_EVENT_EAP_SUCCESS = "CTRL-EVENT-EAP-SUCCESS ";
//pub const WPA_EVENT_EAP_SUCCESS2 = "CTRL-EVENT-EAP-SUCCESS2 ";
//pub const WPA_EVENT_EAP_FAILURE = "CTRL-EVENT-EAP-FAILURE ";
//pub const WPA_EVENT_EAP_FAILURE2 = "CTRL-EVENT-EAP-FAILURE2 ";
//pub const WPA_EVENT_EAP_TIMEOUT_FAILURE = "CTRL-EVENT-EAP-TIMEOUT-FAILURE ";
//pub const WPA_EVENT_EAP_TIMEOUT_FAILURE2 = "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 ";
//pub const WPA_EVENT_EAP_ERROR_CODE = "EAP-ERROR-CODE ";
//pub const WPA_EVENT_TEMP_DISABLED = "CTRL-EVENT-SSID-TEMP-DISABLED ";
//pub const WPA_EVENT_REENABLED = "CTRL-EVENT-SSID-REENABLED ";
//pub const WPA_EVENT_SCAN_STARTED = "CTRL-EVENT-SCAN-STARTED ";
//pub const WPA_EVENT_SCAN_RESULTS = "CTRL-EVENT-SCAN-RESULTS ";
//pub const WPA_EVENT_SCAN_FAILED = "CTRL-EVENT-SCAN-FAILED ";
//pub const WPA_EVENT_STATE_CHANGE = "CTRL-EVENT-STATE-CHANGE ";
//pub const WPA_EVENT_BSS_ADDED = "CTRL-EVENT-BSS-ADDED ";
//pub const WPA_EVENT_BSS_REMOVED = "CTRL-EVENT-BSS-REMOVED ";
//pub const WPA_EVENT_NETWORK_NOT_FOUND = "CTRL-EVENT-NETWORK-NOT-FOUND ";
//pub const WPA_EVENT_SIGNAL_CHANGE = "CTRL-EVENT-SIGNAL-CHANGE ";
//pub const WPA_EVENT_BEACON_LOSS = "CTRL-EVENT-BEACON-LOSS ";
//pub const WPA_EVENT_REGDOM_CHANGE = "CTRL-EVENT-REGDOM-CHANGE ";
//pub const WPA_EVENT_CHANNEL_SWITCH_STARTED = "CTRL-EVENT-STARTED-CHANNEL-SWITCH ";
//pub const WPA_EVENT_CHANNEL_SWITCH = "CTRL-EVENT-CHANNEL-SWITCH ";
//pub const WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER = "CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER ";
//pub const WPA_EVENT_UNPROT_BEACON = "CTRL-EVENT-UNPROT-BEACON ";
//pub const WPA_EVENT_DO_ROAM = "CTRL-EVENT-DO-ROAM ";
//pub const WPA_EVENT_SKIP_ROAM = "CTRL-EVENT-SKIP-ROAM ";
//pub const WPA_EVENT_SUBNET_STATUS_UPDATE = "CTRL-EVENT-SUBNET-STATUS-UPDATE ";
//pub const IBSS_RSN_COMPLETED = "IBSS-RSN-COMPLETED ";
//pub const WPA_EVENT_FREQ_CONFLICT = "CTRL-EVENT-FREQ-CONFLICT ";
//pub const WPA_EVENT_AVOID_FREQ = "CTRL-EVENT-AVOID-FREQ ";
//pub const WPA_EVENT_NETWORK_ADDED = "CTRL-EVENT-NETWORK-ADDED ";
//pub const WPA_EVENT_NETWORK_REMOVED = "CTRL-EVENT-NETWORK-REMOVED ";
//pub const WPA_EVENT_MSCS_RESULT = "CTRL-EVENT-MSCS-RESULT ";
//pub const WPS_EVENT_OVERLAP = "WPS-OVERLAP-DETECTED ";
//pub const WPS_EVENT_AP_AVAILABLE_PBC = "WPS-AP-AVAILABLE-PBC ";
//pub const WPS_EVENT_AP_AVAILABLE_AUTH = "WPS-AP-AVAILABLE-AUTH ";
//pub const WPS_EVENT_AP_AVAILABLE_PIN = "WPS-AP-AVAILABLE-PIN ";
//pub const WPS_EVENT_AP_AVAILABLE = "WPS-AP-AVAILABLE ";
//pub const WPS_EVENT_CRED_RECEIVED = "WPS-CRED-RECEIVED ";
//pub const WPS_EVENT_M2D = "WPS-M2D ";
//pub const WPS_EVENT_FAIL = "WPS-FAIL ";
//pub const WPS_EVENT_SUCCESS = "WPS-SUCCESS ";
//pub const WPS_EVENT_TIMEOUT = "WPS-TIMEOUT ";
//pub const WPS_EVENT_ACTIVE = "WPS-PBC-ACTIVE ";
//pub const WPS_EVENT_DISABLE = "WPS-PBC-DISABLE ";
//pub const WPS_EVENT_ENROLLEE_SEEN = "WPS-ENROLLEE-SEEN ";
//pub const WPS_EVENT_OPEN_NETWORK = "WPS-OPEN-NETWORK ";
//pub const WPA_EVENT_SCS_RESULT = "CTRL-EVENT-SCS-RESULT ";
//pub const WPA_EVENT_DSCP_POLICY = "CTRL-EVENT-DSCP-POLICY ";
//pub const WPS_EVENT_ER_AP_ADD = "WPS-ER-AP-ADD ";
//pub const WPS_EVENT_ER_AP_REMOVE = "WPS-ER-AP-REMOVE ";
//pub const WPS_EVENT_ER_ENROLLEE_ADD = "WPS-ER-ENROLLEE-ADD ";
//pub const WPS_EVENT_ER_ENROLLEE_REMOVE = "WPS-ER-ENROLLEE-REMOVE ";
//pub const WPS_EVENT_ER_AP_SETTINGS = "WPS-ER-AP-SETTINGS ";
//pub const WPS_EVENT_ER_SET_SEL_REG = "WPS-ER-AP-SET-SEL-REG ";
//pub const DPP_EVENT_AUTH_SUCCESS = "DPP-AUTH-SUCCESS ";
//pub const DPP_EVENT_AUTH_INIT_FAILED = "DPP-AUTH-INIT-FAILED ";
//pub const DPP_EVENT_NOT_COMPATIBLE = "DPP-NOT-COMPATIBLE ";
//pub const DPP_EVENT_RESPONSE_PENDING = "DPP-RESPONSE-PENDING ";
//pub const DPP_EVENT_SCAN_PEER_QR_CODE = "DPP-SCAN-PEER-QR-CODE ";
//pub const DPP_EVENT_AUTH_DIRECTION = "DPP-AUTH-DIRECTION ";
//pub const DPP_EVENT_CONF_RECEIVED = "DPP-CONF-RECEIVED ";
//pub const DPP_EVENT_CONF_SENT = "DPP-CONF-SENT ";
//pub const DPP_EVENT_CONF_FAILED = "DPP-CONF-FAILED ";
//pub const DPP_EVENT_CONN_STATUS_RESULT = "DPP-CONN-STATUS-RESULT ";
//pub const DPP_EVENT_CONFOBJ_AKM = "DPP-CONFOBJ-AKM ";
//pub const DPP_EVENT_CONFOBJ_SSID = "DPP-CONFOBJ-SSID ";
//pub const DPP_EVENT_CONFOBJ_SSID_CHARSET = "DPP-CONFOBJ-SSID-CHARSET ";
//pub const DPP_EVENT_CONFOBJ_PASS = "DPP-CONFOBJ-PASS ";
//pub const DPP_EVENT_CONFOBJ_PSK = "DPP-CONFOBJ-PSK ";
//pub const DPP_EVENT_CONNECTOR = "DPP-CONNECTOR ";
//pub const DPP_EVENT_C_SIGN_KEY = "DPP-C-SIGN-KEY ";
//pub const DPP_EVENT_PP_KEY = "DPP-PP-KEY ";
//pub const DPP_EVENT_NET_ACCESS_KEY = "DPP-NET-ACCESS-KEY ";
//pub const DPP_EVENT_SERVER_NAME = "DPP-SERVER-NAME ";
//pub const DPP_EVENT_CERTBAG = "DPP-CERTBAG ";
//pub const DPP_EVENT_CACERT = "DPP-CACERT ";
//pub const DPP_EVENT_MISSING_CONNECTOR = "DPP-MISSING-CONNECTOR ";
//pub const DPP_EVENT_NETWORK_ID = "DPP-NETWORK-ID ";
//pub const DPP_EVENT_CONFIGURATOR_ID = "DPP-CONFIGURATOR-ID ";
//pub const DPP_EVENT_RX = "DPP-RX ";
//pub const DPP_EVENT_TX = "DPP-TX ";
//pub const DPP_EVENT_TX_STATUS = "DPP-TX-STATUS ";
//pub const DPP_EVENT_FAIL = "DPP-FAIL ";
//pub const DPP_EVENT_PKEX_T_LIMIT = "DPP-PKEX-T-LIMIT ";
//pub const DPP_EVENT_INTRO = "DPP-INTRO ";
//pub const DPP_EVENT_CONF_REQ_RX = "DPP-CONF-REQ-RX ";
//pub const DPP_EVENT_CHIRP_STOPPED = "DPP-CHIRP-STOPPED ";
//pub const DPP_EVENT_MUD_URL = "DPP-MUD-URL ";
//pub const DPP_EVENT_BAND_SUPPORT = "DPP-BAND-SUPPORT ";
//pub const DPP_EVENT_CSR = "DPP-CSR ";
//pub const DPP_EVENT_CHIRP_RX = "DPP-CHIRP-RX ";
//pub const MESH_GROUP_STARTED = "MESH-GROUP-STARTED ";
//pub const MESH_GROUP_REMOVED = "MESH-GROUP-REMOVED ";
//pub const MESH_PEER_CONNECTED = "MESH-PEER-CONNECTED ";
//pub const MESH_PEER_DISCONNECTED = "MESH-PEER-DISCONNECTED ";
//pub const MESH_SAE_AUTH_FAILURE = "MESH-SAE-AUTH-FAILURE ";
//pub const MESH_SAE_AUTH_BLOCKED = "MESH-SAE-AUTH-BLOCKED ";
//pub const WMM_AC_EVENT_TSPEC_ADDED = "TSPEC-ADDED ";
//pub const WMM_AC_EVENT_TSPEC_REMOVED = "TSPEC-REMOVED ";
//pub const WMM_AC_EVENT_TSPEC_REQ_FAILED = "TSPEC-REQ-FAILED ";
//pub const P2P_EVENT_DEVICE_FOUND = "P2P-DEVICE-FOUND ";
//pub const P2P_EVENT_DEVICE_LOST = "P2P-DEVICE-LOST ";
//pub const P2P_EVENT_GO_NEG_REQUEST = "P2P-GO-NEG-REQUEST ";
//pub const P2P_EVENT_GO_NEG_SUCCESS = "P2P-GO-NEG-SUCCESS ";
//pub const P2P_EVENT_GO_NEG_FAILURE = "P2P-GO-NEG-FAILURE ";
//pub const P2P_EVENT_GROUP_FORMATION_SUCCESS = "P2P-GROUP-FORMATION-SUCCESS ";
//pub const P2P_EVENT_GROUP_FORMATION_FAILURE = "P2P-GROUP-FORMATION-FAILURE ";
//pub const P2P_EVENT_GROUP_STARTED = "P2P-GROUP-STARTED ";
//pub const P2P_EVENT_GROUP_REMOVED = "P2P-GROUP-REMOVED ";
//pub const P2P_EVENT_CROSS_CONNECT_ENABLE = "P2P-CROSS-CONNECT-ENABLE ";
//pub const P2P_EVENT_CROSS_CONNECT_DISABLE = "P2P-CROSS-CONNECT-DISABLE ";
//pub const P2P_EVENT_PROV_DISC_SHOW_PIN = "P2P-PROV-DISC-SHOW-PIN ";
//pub const P2P_EVENT_PROV_DISC_ENTER_PIN = "P2P-PROV-DISC-ENTER-PIN ";
//pub const P2P_EVENT_PROV_DISC_PBC_REQ = "P2P-PROV-DISC-PBC-REQ ";
//pub const P2P_EVENT_PROV_DISC_PBC_RESP = "P2P-PROV-DISC-PBC-RESP ";
//pub const P2P_EVENT_PROV_DISC_FAILURE = "P2P-PROV-DISC-FAILURE";
//pub const P2P_EVENT_SERV_DISC_REQ = "P2P-SERV-DISC-REQ ";
//pub const P2P_EVENT_SERV_DISC_RESP = "P2P-SERV-DISC-RESP ";
//pub const P2P_EVENT_SERV_ASP_RESP = "P2P-SERV-ASP-RESP ";
//pub const P2P_EVENT_INVITATION_RECEIVED = "P2P-INVITATION-RECEIVED ";
//pub const P2P_EVENT_INVITATION_RESULT = "P2P-INVITATION-RESULT ";
//pub const P2P_EVENT_INVITATION_ACCEPTED = "P2P-INVITATION-ACCEPTED ";
//pub const P2P_EVENT_FIND_STOPPED = "P2P-FIND-STOPPED ";
//pub const P2P_EVENT_PERSISTENT_PSK_FAIL = "P2P-PERSISTENT-PSK-FAIL id=";
//pub const P2P_EVENT_PRESENCE_RESPONSE = "P2P-PRESENCE-RESPONSE ";
//pub const P2P_EVENT_NFC_BOTH_GO = "P2P-NFC-BOTH-GO ";
//pub const P2P_EVENT_NFC_PEER_CLIENT = "P2P-NFC-PEER-CLIENT ";
//pub const P2P_EVENT_NFC_WHILE_CLIENT = "P2P-NFC-WHILE-CLIENT ";
//pub const P2P_EVENT_FALLBACK_TO_GO_NEG = "P2P-FALLBACK-TO-GO-NEG ";
//pub const P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED = "P2P-FALLBACK-TO-GO-NEG-ENABLED ";
//pub const ESS_DISASSOC_IMMINENT = "ESS-DISASSOC-IMMINENT ";
//pub const P2P_EVENT_REMOVE_AND_REFORM_GROUP = "P2P-REMOVE-AND-REFORM-GROUP ";
//pub const P2P_EVENT_P2PS_PROVISION_START = "P2PS-PROV-START ";
//pub const P2P_EVENT_P2PS_PROVISION_DONE = "P2PS-PROV-DONE ";
//pub const INTERWORKING_AP = "INTERWORKING-AP ";
//pub const INTERWORKING_EXCLUDED = "INTERWORKING-BLACKLISTED ";
//pub const INTERWORKING_NO_MATCH = "INTERWORKING-NO-MATCH ";
//pub const INTERWORKING_ALREADY_CONNECTED = "INTERWORKING-ALREADY-CONNECTED ";
//pub const INTERWORKING_SELECTED = "INTERWORKING-SELECTED ";
//pub const CRED_ADDED = "CRED-ADDED ";
//pub const CRED_MODIFIED = "CRED-MODIFIED ";
//pub const CRED_REMOVED = "CRED-REMOVED ";
//pub const GAS_RESPONSE_INFO = "GAS-RESPONSE-INFO ";
//pub const GAS_QUERY_START = "GAS-QUERY-START ";
//pub const GAS_QUERY_DONE = "GAS-QUERY-DONE ";
//pub const ANQP_QUERY_DONE = "ANQP-QUERY-DONE ";
//pub const RX_ANQP = "RX-ANQP ";
//pub const RX_HS20_ANQP = "RX-HS20-ANQP ";
//pub const RX_HS20_ANQP_ICON = "RX-HS20-ANQP-ICON ";
//pub const RX_HS20_ICON = "RX-HS20-ICON ";
//pub const RX_MBO_ANQP = "RX-MBO-ANQP ";
//pub const RX_VENUE_URL = "RX-VENUE-URL ";
//pub const HS20_SUBSCRIPTION_REMEDIATION = "HS20-SUBSCRIPTION-REMEDIATION ";
//pub const HS20_DEAUTH_IMMINENT_NOTICE = "HS20-DEAUTH-IMMINENT-NOTICE ";
//pub const HS20_T_C_ACCEPTANCE = "HS20-T-C-ACCEPTANCE ";
//pub const EXT_RADIO_WORK_START = "EXT-RADIO-WORK-START ";
//pub const EXT_RADIO_WORK_TIMEOUT = "EXT-RADIO-WORK-TIMEOUT ";
//pub const RRM_EVENT_NEIGHBOR_REP_RXED = "RRM-NEIGHBOR-REP-RECEIVED ";
//pub const RRM_EVENT_NEIGHBOR_REP_FAILED = "RRM-NEIGHBOR-REP-REQUEST-FAILED ";
//pub const WPS_EVENT_PIN_NEEDED = "WPS-PIN-NEEDED ";
//pub const WPS_EVENT_NEW_AP_SETTINGS = "WPS-NEW-AP-SETTINGS ";
//pub const WPS_EVENT_REG_SUCCESS = "WPS-REG-SUCCESS ";
//pub const WPS_EVENT_AP_SETUP_LOCKED = "WPS-AP-SETUP-LOCKED ";
//pub const WPS_EVENT_AP_SETUP_UNLOCKED = "WPS-AP-SETUP-UNLOCKED ";
//pub const WPS_EVENT_AP_PIN_ENABLED = "WPS-AP-PIN-ENABLED ";
//pub const WPS_EVENT_AP_PIN_DISABLED = "WPS-AP-PIN-DISABLED ";
//pub const WPS_EVENT_PIN_ACTIVE = "WPS-PIN-ACTIVE ";
//pub const WPS_EVENT_CANCEL = "WPS-CANCEL ";
//pub const AP_STA_CONNECTED = "AP-STA-CONNECTED ";
//pub const AP_STA_DISCONNECTED = "AP-STA-DISCONNECTED ";
//pub const AP_STA_POSSIBLE_PSK_MISMATCH = "AP-STA-POSSIBLE-PSK-MISMATCH ";
//pub const AP_STA_POLL_OK = "AP-STA-POLL-OK ";
//pub const AP_REJECTED_MAX_STA = "AP-REJECTED-MAX-STA ";
//pub const AP_REJECTED_BLOCKED_STA = "AP-REJECTED-BLOCKED-STA ";
//pub const HS20_T_C_FILTERING_ADD = "HS20-T-C-FILTERING-ADD ";
//pub const HS20_T_C_FILTERING_REMOVE = "HS20-T-C-FILTERING-REMOVE ";
//pub const AP_EVENT_ENABLED = "AP-ENABLED ";
//pub const AP_EVENT_DISABLED = "AP-DISABLED ";
//pub const INTERFACE_ENABLED = "INTERFACE-ENABLED ";
//pub const INTERFACE_DISABLED = "INTERFACE-DISABLED ";
//pub const ACS_EVENT_STARTED = "ACS-STARTED ";
//pub const ACS_EVENT_COMPLETED = "ACS-COMPLETED ";
//pub const ACS_EVENT_FAILED = "ACS-FAILED ";
//pub const DFS_EVENT_RADAR_DETECTED = "DFS-RADAR-DETECTED ";
//pub const DFS_EVENT_NEW_CHANNEL = "DFS-NEW-CHANNEL ";
//pub const DFS_EVENT_CAC_START = "DFS-CAC-START ";
//pub const DFS_EVENT_CAC_COMPLETED = "DFS-CAC-COMPLETED ";
//pub const DFS_EVENT_NOP_FINISHED = "DFS-NOP-FINISHED ";
//pub const DFS_EVENT_PRE_CAC_EXPIRED = "DFS-PRE-CAC-EXPIRED ";
//pub const AP_CSA_FINISHED = "AP-CSA-FINISHED ";
//pub const P2P_EVENT_LISTEN_OFFLOAD_STOP = "P2P-LISTEN-OFFLOAD-STOPPED ";
//pub const P2P_LISTEN_OFFLOAD_STOP_REASON = "P2P-LISTEN-OFFLOAD-STOP-REASON ";
//pub const BSS_TM_RESP = "BSS-TM-RESP ";
//pub const COLOC_INTF_REQ = "COLOC-INTF-REQ ";
//pub const COLOC_INTF_REPORT = "COLOC-INTF-REPORT ";
//pub const MBO_CELL_PREFERENCE = "MBO-CELL-PREFERENCE ";

@ -0,0 +1,22 @@
wpa_supplicant and hostapd
--------------------------
Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
See the README file for the current license terms.
This software was previously distributed under BSD/GPL v2 dual license
terms that allowed either of those license alternatives to be
selected. As of February 11, 2012, the project has chosen to use only
the BSD license option for future distribution. As such, the GPL v2
license option is no longer used. It should be noted that the BSD
license option (the one with advertisement clause removed) is compatible
with GPL and as such, does not prevent use of this software in projects
that use GPL.
Some of the files may still include pointers to GPL version 2 license
terms. However, such copyright and license notifications are maintained
only for attribution purposes and any distribution of this software
after February 11, 2012 is no longer under the GPL v2 option.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,56 @@
wpa_supplicant and hostapd
--------------------------
Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
These programs are licensed under the BSD license (the one with
advertisement clause removed).
If you are submitting changes to the project, please see CONTRIBUTIONS
file for more instructions.
This package may include either wpa_supplicant, hostapd, or both. See
README file respective subdirectories (wpa_supplicant/README or
hostapd/README) for more details.
Source code files were moved around in v0.6.x releases and compared to
earlier releases, the programs are now built by first going to a
subdirectory (wpa_supplicant or hostapd) and creating build
configuration (.config) and running 'make' there (for Linux/BSD/cygwin
builds).
License
-------
This software may be distributed, used, and modified under the terms of
BSD license:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name(s) of the above-listed copyright holder(s) nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,50 @@
/*
* wpa_supplicant/hostapd - Build time configuration defines
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This header file can be used to define configuration defines that were
* originally defined in Makefile. This is mainly meant for IDE use or for
* systems that do not have suitable 'make' tool. In these cases, it may be
* easier to have a single place for defining all the needed C pre-processor
* defines.
*/
#ifndef BUILD_CONFIG_H
#define BUILD_CONFIG_H
/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */
#ifdef CONFIG_WIN32_DEFAULTS
#define CONFIG_NATIVE_WINDOWS
#define CONFIG_ANSI_C_EXTRA
#define CONFIG_WINPCAP
#define IEEE8021X_EAPOL
#define PKCS12_FUNCS
#define PCSC_FUNCS
#define CONFIG_CTRL_IFACE
#define CONFIG_CTRL_IFACE_NAMED_PIPE
#define CONFIG_DRIVER_NDIS
#define CONFIG_NDIS_EVENTS_INTEGRATED
#define CONFIG_DEBUG_FILE
#define EAP_MD5
#define EAP_TLS
#define EAP_MSCHAPv2
#define EAP_PEAP
#define EAP_TTLS
#define EAP_GTC
#define EAP_OTP
#define EAP_LEAP
#define EAP_TNC
#define _CRT_SECURE_NO_DEPRECATE
#ifdef USE_INTERNAL_CRYPTO
#define CONFIG_TLS_INTERNAL_CLIENT
#define CONFIG_INTERNAL_LIBTOMMATH
#define CONFIG_CRYPTO_INTERNAL
#endif /* USE_INTERNAL_CRYPTO */
#endif /* CONFIG_WIN32_DEFAULTS */
#endif /* BUILD_CONFIG_H */

@ -0,0 +1,598 @@
/*
* wpa_supplicant/hostapd / common helper functions, etc.
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef COMMON_H
#define COMMON_H
#include "os.h"
#if defined(__linux__) || defined(__GLIBC__)
#include <endian.h>
#include <byteswap.h>
#endif /* __linux__ */
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
defined(__OpenBSD__)
#include <sys/types.h>
#include <sys/endian.h>
#define __BYTE_ORDER _BYTE_ORDER
#define __LITTLE_ENDIAN _LITTLE_ENDIAN
#define __BIG_ENDIAN _BIG_ENDIAN
#ifdef __OpenBSD__
#define bswap_16 swap16
#define bswap_32 swap32
#define bswap_64 swap64
#else /* __OpenBSD__ */
#define bswap_16 bswap16
#define bswap_32 bswap32
#define bswap_64 bswap64
#endif /* __OpenBSD__ */
#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
* defined(__DragonFly__) || defined(__OpenBSD__) */
#ifdef __APPLE__
#include <sys/types.h>
#include <machine/endian.h>
#define __BYTE_ORDER _BYTE_ORDER
#define __LITTLE_ENDIAN _LITTLE_ENDIAN
#define __BIG_ENDIAN _BIG_ENDIAN
static inline unsigned short bswap_16(unsigned short v)
{
return ((v & 0xff) << 8) | (v >> 8);
}
static inline unsigned int bswap_32(unsigned int v)
{
return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
((v & 0xff0000) >> 8) | (v >> 24);
}
#endif /* __APPLE__ */
#ifdef __rtems__
#include <rtems/endian.h>
#define __BYTE_ORDER BYTE_ORDER
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#define __BIG_ENDIAN BIG_ENDIAN
#define bswap_16 CPU_swap_u16
#define bswap_32 CPU_swap_u32
#endif /* __rtems__ */
#ifdef CONFIG_NATIVE_WINDOWS
#include <winsock.h>
typedef int socklen_t;
#ifndef MSG_DONTWAIT
#define MSG_DONTWAIT 0 /* not supported */
#endif
#endif /* CONFIG_NATIVE_WINDOWS */
#ifdef _MSC_VER
#define inline __inline
#undef vsnprintf
#define vsnprintf _vsnprintf
#undef close
#define close closesocket
#endif /* _MSC_VER */
/* Define platform specific integer types */
#ifdef _MSC_VER
typedef UINT64 u64;
typedef UINT32 u32;
typedef UINT16 u16;
typedef UINT8 u8;
typedef INT64 s64;
typedef INT32 s32;
typedef INT16 s16;
typedef INT8 s8;
#define WPA_TYPES_DEFINED
#endif /* _MSC_VER */
#ifdef __vxworks
typedef unsigned long long u64;
typedef UINT32 u32;
typedef UINT16 u16;
typedef UINT8 u8;
typedef long long s64;
typedef INT32 s32;
typedef INT16 s16;
typedef INT8 s8;
#define WPA_TYPES_DEFINED
#endif /* __vxworks */
#ifndef WPA_TYPES_DEFINED
#ifdef CONFIG_USE_INTTYPES_H
#include <inttypes.h>
#else
#include <stdint.h>
#endif
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef int64_t s64;
typedef int32_t s32;
typedef int16_t s16;
typedef int8_t s8;
#define WPA_TYPES_DEFINED
#endif /* !WPA_TYPES_DEFINED */
/* Define platform specific byte swapping macros */
#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
static inline unsigned short wpa_swap_16(unsigned short v)
{
return ((v & 0xff) << 8) | (v >> 8);
}
static inline unsigned int wpa_swap_32(unsigned int v)
{
return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
((v & 0xff0000) >> 8) | (v >> 24);
}
#define le_to_host16(n) (n)
#define host_to_le16(n) (n)
#define be_to_host16(n) wpa_swap_16(n)
#define host_to_be16(n) wpa_swap_16(n)
#define le_to_host32(n) (n)
#define host_to_le32(n) (n)
#define be_to_host32(n) wpa_swap_32(n)
#define host_to_be32(n) wpa_swap_32(n)
#define host_to_le64(n) (n)
#define WPA_BYTE_SWAP_DEFINED
#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
#ifndef WPA_BYTE_SWAP_DEFINED
#ifndef __BYTE_ORDER
#ifndef __LITTLE_ENDIAN
#ifndef __BIG_ENDIAN
#define __LITTLE_ENDIAN 1234
#define __BIG_ENDIAN 4321
#if defined(sparc)
#define __BYTE_ORDER __BIG_ENDIAN
#endif
#endif /* __BIG_ENDIAN */
#endif /* __LITTLE_ENDIAN */
#endif /* __BYTE_ORDER */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define le_to_host16(n) ((__force u16) (le16) (n))
#define host_to_le16(n) ((__force le16) (u16) (n))
#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
#define host_to_be16(n) ((__force be16) bswap_16((n)))
#define le_to_host32(n) ((__force u32) (le32) (n))
#define host_to_le32(n) ((__force le32) (u32) (n))
#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
#define host_to_be32(n) ((__force be32) bswap_32((n)))
#define le_to_host64(n) ((__force u64) (le64) (n))
#define host_to_le64(n) ((__force le64) (u64) (n))
#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
#define host_to_be64(n) ((__force be64) bswap_64((n)))
#elif __BYTE_ORDER == __BIG_ENDIAN
#define le_to_host16(n) bswap_16(n)
#define host_to_le16(n) bswap_16(n)
#define be_to_host16(n) (n)
#define host_to_be16(n) (n)
#define le_to_host32(n) bswap_32(n)
#define host_to_le32(n) bswap_32(n)
#define be_to_host32(n) (n)
#define host_to_be32(n) (n)
#define le_to_host64(n) bswap_64(n)
#define host_to_le64(n) bswap_64(n)
#define be_to_host64(n) (n)
#define host_to_be64(n) (n)
#ifndef WORDS_BIGENDIAN
#define WORDS_BIGENDIAN
#endif
#else
#error Could not determine CPU byte order
#endif
#define WPA_BYTE_SWAP_DEFINED
#endif /* !WPA_BYTE_SWAP_DEFINED */
/* Macros for handling unaligned memory accesses */
static inline u16 WPA_GET_BE16(const u8 *a)
{
return (a[0] << 8) | a[1];
}
static inline void WPA_PUT_BE16(u8 *a, u16 val)
{
a[0] = val >> 8;
a[1] = val & 0xff;
}
static inline u16 WPA_GET_LE16(const u8 *a)
{
return (a[1] << 8) | a[0];
}
static inline void WPA_PUT_LE16(u8 *a, u16 val)
{
a[1] = val >> 8;
a[0] = val & 0xff;
}
static inline u32 WPA_GET_BE24(const u8 *a)
{
return (a[0] << 16) | (a[1] << 8) | a[2];
}
static inline void WPA_PUT_BE24(u8 *a, u32 val)
{
a[0] = (val >> 16) & 0xff;
a[1] = (val >> 8) & 0xff;
a[2] = val & 0xff;
}
static inline u32 WPA_GET_BE32(const u8 *a)
{
return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
}
static inline void WPA_PUT_BE32(u8 *a, u32 val)
{
a[0] = (val >> 24) & 0xff;
a[1] = (val >> 16) & 0xff;
a[2] = (val >> 8) & 0xff;
a[3] = val & 0xff;
}
static inline u32 WPA_GET_LE32(const u8 *a)
{
return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
}
static inline void WPA_PUT_LE32(u8 *a, u32 val)
{
a[3] = (val >> 24) & 0xff;
a[2] = (val >> 16) & 0xff;
a[1] = (val >> 8) & 0xff;
a[0] = val & 0xff;
}
static inline u64 WPA_GET_BE64(const u8 *a)
{
return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
(((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
(((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
(((u64) a[6]) << 8) | ((u64) a[7]);
}
static inline void WPA_PUT_BE64(u8 *a, u64 val)
{
a[0] = val >> 56;
a[1] = val >> 48;
a[2] = val >> 40;
a[3] = val >> 32;
a[4] = val >> 24;
a[5] = val >> 16;
a[6] = val >> 8;
a[7] = val & 0xff;
}
static inline u64 WPA_GET_LE64(const u8 *a)
{
return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
(((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
(((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
(((u64) a[1]) << 8) | ((u64) a[0]);
}
static inline void WPA_PUT_LE64(u8 *a, u64 val)
{
a[7] = val >> 56;
a[6] = val >> 48;
a[5] = val >> 40;
a[4] = val >> 32;
a[3] = val >> 24;
a[2] = val >> 16;
a[1] = val >> 8;
a[0] = val & 0xff;
}
#ifndef ETH_ALEN
#define ETH_ALEN 6
#endif
#ifndef ETH_HLEN
#define ETH_HLEN 14
#endif
#ifndef IFNAMSIZ
#define IFNAMSIZ 16
#endif
#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif
#ifndef ETH_P_IP
#define ETH_P_IP 0x0800
#endif
#ifndef ETH_P_80211_ENCAP
#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
#endif
#ifndef ETH_P_PAE
#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
#endif /* ETH_P_PAE */
#ifndef ETH_P_EAPOL
#define ETH_P_EAPOL ETH_P_PAE
#endif /* ETH_P_EAPOL */
#ifndef ETH_P_RSN_PREAUTH
#define ETH_P_RSN_PREAUTH 0x88c7
#endif /* ETH_P_RSN_PREAUTH */
#ifndef ETH_P_RRB
#define ETH_P_RRB 0x890D
#endif /* ETH_P_RRB */
#ifndef ETH_P_OUI
#define ETH_P_OUI 0x88B7
#endif /* ETH_P_OUI */
#ifndef ETH_P_8021Q
#define ETH_P_8021Q 0x8100
#endif /* ETH_P_8021Q */
#ifdef __GNUC__
#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b))))
#define STRUCT_PACKED __attribute__ ((packed))
#else
#define PRINTF_FORMAT(a,b)
#define STRUCT_PACKED
#endif
#ifdef CONFIG_ANSI_C_EXTRA
#if !defined(_MSC_VER) || _MSC_VER < 1400
/* snprintf - used in number of places; sprintf() is _not_ a good replacement
* due to possible buffer overflow; see, e.g.,
* http://www.ijs.si/software/snprintf/ for portable implementation of
* snprintf. */
int snprintf(char *str, size_t size, const char *format, ...);
/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */
/* getopt - only used in main.c */
int getopt(int argc, char *const argv[], const char *optstring);
extern char *optarg;
extern int optind;
#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF
#ifndef __socklen_t_defined
typedef int socklen_t;
#endif
#endif
/* inline - define as __inline or just define it to be empty, if needed */
#ifdef CONFIG_NO_INLINE
#define inline
#else
#define inline __inline
#endif
#ifndef __func__
#define __func__ "__func__ not defined"
#endif
#ifndef bswap_16
#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff))
#endif
#ifndef bswap_32
#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \
(((u32) (a) << 8) & 0xff0000) | \
(((u32) (a) >> 8) & 0xff00) | \
(((u32) (a) >> 24) & 0xff))
#endif
#ifndef MSG_DONTWAIT
#define MSG_DONTWAIT 0
#endif
#ifdef _WIN32_WCE
void perror(const char *s);
#endif /* _WIN32_WCE */
#endif /* CONFIG_ANSI_C_EXTRA */
#ifndef MAC2STR
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
/*
* Compact form for string representation of MAC address
* To be used, e.g., for constructing dbus paths for P2P Devices
*/
#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
#endif
#ifndef BIT
#define BIT(x) (1U << (x))
#endif
/*
* Definitions for sparse validation
* (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
*/
#ifdef __CHECKER__
#define __force __attribute__((force))
#undef __bitwise
#define __bitwise __attribute__((bitwise))
#else
#define __force
#undef __bitwise
#define __bitwise
#endif
typedef u16 __bitwise be16;
typedef u16 __bitwise le16;
typedef u32 __bitwise be32;
typedef u32 __bitwise le32;
typedef u64 __bitwise be64;
typedef u64 __bitwise le64;
#ifndef __must_check
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
#define __must_check __attribute__((__warn_unused_result__))
#else
#define __must_check
#endif /* __GNUC__ */
#endif /* __must_check */
#ifndef __maybe_unused
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
#define __maybe_unused __attribute__((unused))
#else
#define __maybe_unused
#endif /* __GNUC__ */
#endif /* __must_check */
#define SSID_MAX_LEN 32
struct wpa_ssid_value {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
};
int hwaddr_aton(const char *txt, u8 *addr);
int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
int hwaddr_compact_aton(const char *txt, u8 *addr);
int hwaddr_aton2(const char *txt, u8 *addr);
int hex2byte(const char *hex);
int hexstr2bin(const char *hex, u8 *buf, size_t len);
void inc_byte_array(u8 *counter, size_t len);
void buf_shift_right(u8 *buf, size_t len, size_t bits);
void wpa_get_ntp_timestamp(u8 *buf);
int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
PRINTF_FORMAT(3, 4);
int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
char sep);
int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
size_t len);
int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
int ssid_parse(const char *buf, struct wpa_ssid_value *ssid);
#ifdef CONFIG_NATIVE_WINDOWS
void wpa_unicode2ascii_inplace(TCHAR *str);
TCHAR * wpa_strdup_tchar(const char *str);
#else /* CONFIG_NATIVE_WINDOWS */
#define wpa_unicode2ascii_inplace(s) do { } while (0)
#define wpa_strdup_tchar(s) strdup((s))
#endif /* CONFIG_NATIVE_WINDOWS */
void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
char * wpa_config_parse_string(const char *value, size_t *len);
int is_hex(const u8 *data, size_t len);
int has_ctrl_char(const u8 *data, size_t len);
int has_newline(const char *str);
size_t merge_byte_arrays(u8 *res, size_t res_len,
const u8 *src1, size_t src1_len,
const u8 *src2, size_t src2_len);
char * dup_binstr(const void *src, size_t len);
static inline int is_zero_ether_addr(const u8 *a)
{
return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
}
static inline int is_broadcast_ether_addr(const u8 *a)
{
return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
}
static inline int is_multicast_ether_addr(const u8 *a)
{
return a[0] & 0x01;
}
#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
#include "wpa_debug.h"
struct wpa_freq_range_list {
struct wpa_freq_range {
unsigned int min;
unsigned int max;
} *range;
unsigned int num;
};
int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value);
int freq_range_list_includes(const struct wpa_freq_range_list *list,
unsigned int freq);
char * freq_range_list_str(const struct wpa_freq_range_list *list);
size_t int_array_len(const int *a);
void int_array_concat(int **res, const int *a);
void int_array_sort_unique(int *a);
void int_array_add_unique(int **res, int a);
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
void str_clear_free(char *str);
void bin_clear_free(void *bin, size_t len);
int random_mac_addr(u8 *addr);
int random_mac_addr_keep_oui(u8 *addr);
const char * cstr_token(const char *str, const char *delim, const char **last);
char * str_token(char *str, const char *delim, char **context);
size_t utf8_escape(const char *inp, size_t in_size,
char *outp, size_t out_size);
size_t utf8_unescape(const char *inp, size_t in_size,
char *outp, size_t out_size);
int is_ctrl_char(char c);
int str_starts(const char *str, const char *start);
u8 rssi_to_rcpi(int rssi);
char * get_param(const char *cmd, const char *param);
void forced_memzero(void *ptr, size_t len);
/*
* gcc 4.4 ends up generating strict-aliasing warnings about some very common
* networking socket uses that do not really result in a real problem and
* cannot be easily avoided with union-based type-punning due to struct
* definitions including another struct in system header files. To avoid having
* to fully disable strict-aliasing warnings, provide a mechanism to hide the
* typecast from aliasing for now. A cleaner solution will hopefully be found
* in the future to handle these cases.
*/
void * __hide_aliasing_typecast(void *foo);
#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
#ifdef CONFIG_VALGRIND
#include <valgrind/memcheck.h>
#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
#else /* CONFIG_VALGRIND */
#define WPA_MEM_DEFINED(ptr, len) do { } while (0)
#endif /* CONFIG_VALGRIND */
#endif /* COMMON_H */

@ -0,0 +1,46 @@
/*
* wpa_supplicant/hostapd - Default include files
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This header file is included into all C files so that commonly used header
* files can be selected with OS specific ifdef blocks in one place instead of
* having to have OS/C library specific selection in many files.
*/
#ifndef INCLUDES_H
#define INCLUDES_H
/* Include possible build time configuration before including anything else */
#include "build_config.h"
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#ifndef _WIN32_WCE
#include <signal.h>
#include <sys/types.h>
#include <errno.h>
#endif /* _WIN32_WCE */
#include <ctype.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif /* _MSC_VER */
#ifndef CONFIG_NATIVE_WINDOWS
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef __vxworks
#include <sys/uio.h>
#include <sys/time.h>
#endif /* __vxworks */
#endif /* CONFIG_NATIVE_WINDOWS */
#endif /* INCLUDES_H */

@ -0,0 +1,680 @@
/*
* OS specific functions
* Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef OS_H
#define OS_H
typedef long os_time_t;
/**
* os_sleep - Sleep (sec, usec)
* @sec: Number of seconds to sleep
* @usec: Number of microseconds to sleep
*/
void os_sleep(os_time_t sec, os_time_t usec);
struct os_time {
os_time_t sec;
os_time_t usec;
};
struct os_reltime {
os_time_t sec;
os_time_t usec;
};
/**
* os_get_time - Get current time (sec, usec)
* @t: Pointer to buffer for the time
* Returns: 0 on success, -1 on failure
*/
int os_get_time(struct os_time *t);
/**
* os_get_reltime - Get relative time (sec, usec)
* @t: Pointer to buffer for the time
* Returns: 0 on success, -1 on failure
*/
int os_get_reltime(struct os_reltime *t);
/* Helpers for handling struct os_time */
static inline int os_time_before(struct os_time *a, struct os_time *b)
{
return (a->sec < b->sec) ||
(a->sec == b->sec && a->usec < b->usec);
}
static inline void os_time_sub(struct os_time *a, struct os_time *b,
struct os_time *res)
{
res->sec = a->sec - b->sec;
res->usec = a->usec - b->usec;
if (res->usec < 0) {
res->sec--;
res->usec += 1000000;
}
}
/* Helpers for handling struct os_reltime */
static inline int os_reltime_before(struct os_reltime *a,
struct os_reltime *b)
{
return (a->sec < b->sec) ||
(a->sec == b->sec && a->usec < b->usec);
}
static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b,
struct os_reltime *res)
{
res->sec = a->sec - b->sec;
res->usec = a->usec - b->usec;
if (res->usec < 0) {
res->sec--;
res->usec += 1000000;
}
}
static inline void os_reltime_age(struct os_reltime *start,
struct os_reltime *age)
{
struct os_reltime now;
os_get_reltime(&now);
os_reltime_sub(&now, start, age);
}
static inline int os_reltime_expired(struct os_reltime *now,
struct os_reltime *ts,
os_time_t timeout_secs)
{
struct os_reltime age;
os_reltime_sub(now, ts, &age);
return (age.sec > timeout_secs) ||
(age.sec == timeout_secs && age.usec > 0);
}
static inline int os_reltime_initialized(struct os_reltime *t)
{
return t->sec != 0 || t->usec != 0;
}
/**
* os_mktime - Convert broken-down time into seconds since 1970-01-01
* @year: Four digit year
* @month: Month (1 .. 12)
* @day: Day of month (1 .. 31)
* @hour: Hour (0 .. 23)
* @min: Minute (0 .. 59)
* @sec: Second (0 .. 60)
* @t: Buffer for returning calendar time representation (seconds since
* 1970-01-01 00:00:00)
* Returns: 0 on success, -1 on failure
*
* Note: The result is in seconds from Epoch, i.e., in UTC, not in local time
* which is used by POSIX mktime().
*/
int os_mktime(int year, int month, int day, int hour, int min, int sec,
os_time_t *t);
struct os_tm {
int sec; /* 0..59 or 60 for leap seconds */
int min; /* 0..59 */
int hour; /* 0..23 */
int day; /* 1..31 */
int month; /* 1..12 */
int year; /* Four digit year */
};
int os_gmtime(os_time_t t, struct os_tm *tm);
/**
* os_daemonize - Run in the background (detach from the controlling terminal)
* @pid_file: File name to write the process ID to or %NULL to skip this
* Returns: 0 on success, -1 on failure
*/
int os_daemonize(const char *pid_file);
/**
* os_daemonize_terminate - Stop running in the background (remove pid file)
* @pid_file: File name to write the process ID to or %NULL to skip this
*/
void os_daemonize_terminate(const char *pid_file);
/**
* os_get_random - Get cryptographically strong pseudo random data
* @buf: Buffer for pseudo random data
* @len: Length of the buffer
* Returns: 0 on success, -1 on failure
*/
int os_get_random(unsigned char *buf, size_t len);
/**
* os_random - Get pseudo random value (not necessarily very strong)
* Returns: Pseudo random value
*/
unsigned long os_random(void);
/**
* os_rel2abs_path - Get an absolute path for a file
* @rel_path: Relative path to a file
* Returns: Absolute path for the file or %NULL on failure
*
* This function tries to convert a relative path of a file to an absolute path
* in order for the file to be found even if current working directory has
* changed. The returned value is allocated and caller is responsible for
* freeing it. It is acceptable to just return the same path in an allocated
* buffer, e.g., return strdup(rel_path). This function is only used to find
* configuration files when os_daemonize() may have changed the current working
* directory and relative path would be pointing to a different location.
*/
char * os_rel2abs_path(const char *rel_path);
/**
* os_program_init - Program initialization (called at start)
* Returns: 0 on success, -1 on failure
*
* This function is called when a programs starts. If there are any OS specific
* processing that is needed, it can be placed here. It is also acceptable to
* just return 0 if not special processing is needed.
*/
int os_program_init(void);
/**
* os_program_deinit - Program deinitialization (called just before exit)
*
* This function is called just before a program exists. If there are any OS
* specific processing, e.g., freeing resourced allocated in os_program_init(),
* it should be done here. It is also acceptable for this function to do
* nothing.
*/
void os_program_deinit(void);
/**
* os_setenv - Set environment variable
* @name: Name of the variable
* @value: Value to set to the variable
* @overwrite: Whether existing variable should be overwritten
* Returns: 0 on success, -1 on error
*
* This function is only used for wpa_cli action scripts. OS wrapper does not
* need to implement this if such functionality is not needed.
*/
int os_setenv(const char *name, const char *value, int overwrite);
/**
* os_unsetenv - Delete environent variable
* @name: Name of the variable
* Returns: 0 on success, -1 on error
*
* This function is only used for wpa_cli action scripts. OS wrapper does not
* need to implement this if such functionality is not needed.
*/
int os_unsetenv(const char *name);
/**
* os_readfile - Read a file to an allocated memory buffer
* @name: Name of the file to read
* @len: For returning the length of the allocated buffer
* Returns: Pointer to the allocated buffer or %NULL on failure
*
* This function allocates memory and reads the given file to this buffer. Both
* binary and text files can be read with this function. The caller is
* responsible for freeing the returned buffer with os_free().
*/
char * os_readfile(const char *name, size_t *len);
/**
* os_file_exists - Check whether the specified file exists
* @fname: Path and name of the file
* Returns: 1 if the file exists or 0 if not
*/
int os_file_exists(const char *fname);
/**
* os_fdatasync - Sync a file's (for a given stream) state with storage device
* @stream: the stream to be flushed
* Returns: 0 if the operation succeeded or -1 on failure
*/
int os_fdatasync(FILE *stream);
/**
* os_zalloc - Allocate and zero memory
* @size: Number of bytes to allocate
* Returns: Pointer to allocated and zeroed memory or %NULL on failure
*
* Caller is responsible for freeing the returned buffer with os_free().
*/
void * os_zalloc(size_t size);
/**
* os_calloc - Allocate and zero memory for an array
* @nmemb: Number of members in the array
* @size: Number of bytes in each member
* Returns: Pointer to allocated and zeroed memory or %NULL on failure
*
* This function can be used as a wrapper for os_zalloc(nmemb * size) when an
* allocation is used for an array. The main benefit over os_zalloc() is in
* having an extra check to catch integer overflows in multiplication.
*
* Caller is responsible for freeing the returned buffer with os_free().
*/
static inline void * os_calloc(size_t nmemb, size_t size)
{
if (size && nmemb > (~(size_t) 0) / size)
return NULL;
return os_zalloc(nmemb * size);
}
/*
* The following functions are wrapper for standard ANSI C or POSIX functions.
* By default, they are just defined to use the standard function name and no
* os_*.c implementation is needed for them. This avoids extra function calls
* by allowing the C pre-processor take care of the function name mapping.
*
* If the target system uses a C library that does not provide these functions,
* build_config.h can be used to define the wrappers to use a different
* function name. This can be done on function-by-function basis since the
* defines here are only used if build_config.h does not define the os_* name.
* If needed, os_*.c file can be used to implement the functions that are not
* included in the C library on the target system. Alternatively,
* OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case
* these functions need to be implemented in os_*.c file for the target system.
*/
#ifdef OS_NO_C_LIB_DEFINES
/**
* os_malloc - Allocate dynamic memory
* @size: Size of the buffer to allocate
* Returns: Allocated buffer or %NULL on failure
*
* Caller is responsible for freeing the returned buffer with os_free().
*/
void * os_malloc(size_t size);
/**
* os_realloc - Re-allocate dynamic memory
* @ptr: Old buffer from os_malloc() or os_realloc()
* @size: Size of the new buffer
* Returns: Allocated buffer or %NULL on failure
*
* Caller is responsible for freeing the returned buffer with os_free().
* If re-allocation fails, %NULL is returned and the original buffer (ptr) is
* not freed and caller is still responsible for freeing it.
*/
void * os_realloc(void *ptr, size_t size);
/**
* os_free - Free dynamic memory
* @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL
*/
void os_free(void *ptr);
/**
* os_memcpy - Copy memory area
* @dest: Destination
* @src: Source
* @n: Number of bytes to copy
* Returns: dest
*
* The memory areas src and dst must not overlap. os_memmove() can be used with
* overlapping memory.
*/
void * os_memcpy(void *dest, const void *src, size_t n);
/**
* os_memmove - Copy memory area
* @dest: Destination
* @src: Source
* @n: Number of bytes to copy
* Returns: dest
*
* The memory areas src and dst may overlap.
*/
void * os_memmove(void *dest, const void *src, size_t n);
/**
* os_memset - Fill memory with a constant byte
* @s: Memory area to be filled
* @c: Constant byte
* @n: Number of bytes started from s to fill with c
* Returns: s
*/
void * os_memset(void *s, int c, size_t n);
/**
* os_memcmp - Compare memory areas
* @s1: First buffer
* @s2: Second buffer
* @n: Maximum numbers of octets to compare
* Returns: An integer less than, equal to, or greater than zero if s1 is
* found to be less than, to match, or be greater than s2. Only first n
* characters will be compared.
*/
int os_memcmp(const void *s1, const void *s2, size_t n);
/**
* os_strdup - Duplicate a string
* @s: Source string
* Returns: Allocated buffer with the string copied into it or %NULL on failure
*
* Caller is responsible for freeing the returned buffer with os_free().
*/
char * os_strdup(const char *s);
/**
* os_strlen - Calculate the length of a string
* @s: '\0' terminated string
* Returns: Number of characters in s (not counting the '\0' terminator)
*/
size_t os_strlen(const char *s);
/**
* os_strcasecmp - Compare two strings ignoring case
* @s1: First string
* @s2: Second string
* Returns: An integer less than, equal to, or greater than zero if s1 is
* found to be less than, to match, or be greatred than s2
*/
int os_strcasecmp(const char *s1, const char *s2);
/**
* os_strncasecmp - Compare two strings ignoring case
* @s1: First string
* @s2: Second string
* @n: Maximum numbers of characters to compare
* Returns: An integer less than, equal to, or greater than zero if s1 is
* found to be less than, to match, or be greater than s2. Only first n
* characters will be compared.
*/
int os_strncasecmp(const char *s1, const char *s2, size_t n);
/**
* os_strchr - Locate the first occurrence of a character in string
* @s: String
* @c: Character to search for
* Returns: Pointer to the matched character or %NULL if not found
*/
char * os_strchr(const char *s, int c);
/**
* os_strrchr - Locate the last occurrence of a character in string
* @s: String
* @c: Character to search for
* Returns: Pointer to the matched character or %NULL if not found
*/
char * os_strrchr(const char *s, int c);
/**
* os_strcmp - Compare two strings
* @s1: First string
* @s2: Second string
* Returns: An integer less than, equal to, or greater than zero if s1 is
* found to be less than, to match, or be greatred than s2
*/
int os_strcmp(const char *s1, const char *s2);
/**
* os_strncmp - Compare two strings
* @s1: First string
* @s2: Second string
* @n: Maximum numbers of characters to compare
* Returns: An integer less than, equal to, or greater than zero if s1 is
* found to be less than, to match, or be greater than s2. Only first n
* characters will be compared.
*/
int os_strncmp(const char *s1, const char *s2, size_t n);
/**
* os_strstr - Locate a substring
* @haystack: String (haystack) to search from
* @needle: Needle to search from haystack
* Returns: Pointer to the beginning of the substring or %NULL if not found
*/
char * os_strstr(const char *haystack, const char *needle);
/**
* os_snprintf - Print to a memory buffer
* @str: Memory buffer to print into
* @size: Maximum length of the str buffer
* @format: printf format
* Returns: Number of characters printed (not including trailing '\0').
*
* If the output buffer is truncated, number of characters which would have
* been written is returned. Since some C libraries return -1 in such a case,
* the caller must be prepared on that value, too, to indicate truncation.
*
* Note: Some C library implementations of snprintf() may not guarantee null
* termination in case the output is truncated. The OS wrapper function of
* os_snprintf() should provide this guarantee, i.e., to null terminate the
* output buffer if a C library version of the function is used and if that
* function does not guarantee null termination.
*
* If the target system does not include snprintf(), see, e.g.,
* http://www.ijs.si/software/snprintf/ for an example of a portable
* implementation of snprintf.
*/
int os_snprintf(char *str, size_t size, const char *format, ...);
#else /* OS_NO_C_LIB_DEFINES */
#ifdef WPA_TRACE
void * os_malloc(size_t size);
void * os_realloc(void *ptr, size_t size);
void os_free(void *ptr);
char * os_strdup(const char *s);
#else /* WPA_TRACE */
#ifndef os_malloc
#define os_malloc(s) malloc((s))
#endif
#ifndef os_realloc
#define os_realloc(p, s) realloc((p), (s))
#endif
#ifndef os_free
#define os_free(p) free((p))
#endif
#ifndef os_strdup
#ifdef _MSC_VER
#define os_strdup(s) _strdup(s)
#else
#define os_strdup(s) strdup(s)
#endif
#endif
#endif /* WPA_TRACE */
#ifndef os_memcpy
#define os_memcpy(d, s, n) memcpy((d), (s), (n))
#endif
#ifndef os_memmove
#define os_memmove(d, s, n) memmove((d), (s), (n))
#endif
#ifndef os_memset
#define os_memset(s, c, n) memset(s, c, n)
#endif
#ifndef os_memcmp
#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
#endif
#ifndef os_strlen
#define os_strlen(s) strlen(s)
#endif
#ifndef os_strcasecmp
#ifdef _MSC_VER
#define os_strcasecmp(s1, s2) _stricmp((s1), (s2))
#else
#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
#endif
#endif
#ifndef os_strncasecmp
#ifdef _MSC_VER
#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
#else
#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
#endif
#endif
#ifndef os_strchr
#define os_strchr(s, c) strchr((s), (c))
#endif
#ifndef os_strcmp
#define os_strcmp(s1, s2) strcmp((s1), (s2))
#endif
#ifndef os_strncmp
#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
#endif
#ifndef os_strrchr
#define os_strrchr(s, c) strrchr((s), (c))
#endif
#ifndef os_strstr
#define os_strstr(h, n) strstr((h), (n))
#endif
#ifndef os_snprintf
#ifdef _MSC_VER
#define os_snprintf _snprintf
#else
#define os_snprintf snprintf
#endif
#endif
#endif /* OS_NO_C_LIB_DEFINES */
static inline int os_snprintf_error(size_t size, int res)
{
return res < 0 || (unsigned int) res >= size;
}
static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
{
if (size && nmemb > (~(size_t) 0) / size)
return NULL;
return os_realloc(ptr, nmemb * size);
}
/**
* os_remove_in_array - Remove a member from an array by index
* @ptr: Pointer to the array
* @nmemb: Current member count of the array
* @size: The size per member of the array
* @idx: Index of the member to be removed
*/
static inline void os_remove_in_array(void *ptr, size_t nmemb, size_t size,
size_t idx)
{
if (idx < nmemb - 1)
os_memmove(((unsigned char *) ptr) + idx * size,
((unsigned char *) ptr) + (idx + 1) * size,
(nmemb - idx - 1) * size);
}
/**
* os_strlcpy - Copy a string with size bound and NUL-termination
* @dest: Destination
* @src: Source
* @siz: Size of the target buffer
* Returns: Total length of the target string (length of src) (not including
* NUL-termination)
*
* This function matches in behavior with the strlcpy(3) function in OpenBSD.
*/
size_t os_strlcpy(char *dest, const char *src, size_t siz);
/**
* os_memcmp_const - Constant time memory comparison
* @a: First buffer to compare
* @b: Second buffer to compare
* @len: Number of octets to compare
* Returns: 0 if buffers are equal, non-zero if not
*
* This function is meant for comparing passwords or hash values where
* difference in execution time could provide external observer information
* about the location of the difference in the memory buffers. The return value
* does not behave like os_memcmp(), i.e., os_memcmp_const() cannot be used to
* sort items into a defined order. Unlike os_memcmp(), execution time of
* os_memcmp_const() does not depend on the contents of the compared memory
* buffers, but only on the total compared length.
*/
int os_memcmp_const(const void *a, const void *b, size_t len);
/**
* os_memdup - Allocate duplicate of passed memory chunk
* @src: Source buffer to duplicate
* @len: Length of source buffer
* Returns: %NULL if allocation failed, copy of src buffer otherwise
*
* This function allocates a memory block like os_malloc() would, and
* copies the given source buffer into it.
*/
void * os_memdup(const void *src, size_t len);
/**
* os_exec - Execute an external program
* @program: Path to the program
* @arg: Command line argument string
* @wait_completion: Whether to wait until the program execution completes
* Returns: 0 on success, -1 on error
*/
int os_exec(const char *program, const char *arg, int wait_completion);
#ifdef OS_REJECT_C_LIB_FUNCTIONS
#define malloc OS_DO_NOT_USE_malloc
#define realloc OS_DO_NOT_USE_realloc
#define free OS_DO_NOT_USE_free
#define memcpy OS_DO_NOT_USE_memcpy
#define memmove OS_DO_NOT_USE_memmove
#define memset OS_DO_NOT_USE_memset
#define memcmp OS_DO_NOT_USE_memcmp
#undef strdup
#define strdup OS_DO_NOT_USE_strdup
#define strlen OS_DO_NOT_USE_strlen
#define strcasecmp OS_DO_NOT_USE_strcasecmp
#define strncasecmp OS_DO_NOT_USE_strncasecmp
#undef strchr
#define strchr OS_DO_NOT_USE_strchr
#undef strcmp
#define strcmp OS_DO_NOT_USE_strcmp
#undef strncmp
#define strncmp OS_DO_NOT_USE_strncmp
#undef strncpy
#define strncpy OS_DO_NOT_USE_strncpy
#define strrchr OS_DO_NOT_USE_strrchr
#define strstr OS_DO_NOT_USE_strstr
#undef snprintf
#define snprintf OS_DO_NOT_USE_snprintf
#define strcpy OS_DO_NOT_USE_strcpy
#endif /* OS_REJECT_C_LIB_FUNCTIONS */
#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
#define TEST_FAIL() testing_test_fail()
int testing_test_fail(void);
extern char wpa_trace_fail_func[256];
extern unsigned int wpa_trace_fail_after;
extern char wpa_trace_test_fail_func[256];
extern unsigned int wpa_trace_test_fail_after;
#else
#define TEST_FAIL() 0
#endif
#endif /* OS_H */

@ -0,0 +1,853 @@
/*
* OS specific functions for UNIX/POSIX systems
* Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <time.h>
#include <sys/wait.h>
#ifdef ANDROID
#include <sys/capability.h>
#include <sys/prctl.h>
#include <private/android_filesystem_config.h>
#endif /* ANDROID */
#ifdef __MACH__
#include <CoreServices/CoreServices.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif /* __MACH__ */
#include "os.h"
#include "common.h"
#ifdef WPA_TRACE
#include "wpa_debug.h"
#include "trace.h"
#include "list.h"
static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
#define ALLOC_MAGIC 0xa84ef1b2
#define FREED_MAGIC 0x67fd487a
struct os_alloc_trace {
unsigned int magic;
struct dl_list list __attribute__((aligned(16)));
size_t len;
WPA_TRACE_INFO
} __attribute__((aligned(16)));
#endif /* WPA_TRACE */
void os_sleep(os_time_t sec, os_time_t usec)
{
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
const struct timespec req = { sec, usec * 1000 };
nanosleep(&req, NULL);
#else
if (sec)
sleep(sec);
if (usec)
usleep(usec);
#endif
}
int os_get_time(struct os_time *t)
{
int res;
struct timeval tv;
res = gettimeofday(&tv, NULL);
t->sec = tv.tv_sec;
t->usec = tv.tv_usec;
return res;
}
int os_get_reltime(struct os_reltime *t)
{
#ifndef __MACH__
#if defined(CLOCK_BOOTTIME)
static clockid_t clock_id = CLOCK_BOOTTIME;
#elif defined(CLOCK_MONOTONIC)
static clockid_t clock_id = CLOCK_MONOTONIC;
#else
static clockid_t clock_id = CLOCK_REALTIME;
#endif
struct timespec ts;
int res;
if (TEST_FAIL())
return -1;
while (1) {
res = clock_gettime(clock_id, &ts);
if (res == 0) {
t->sec = ts.tv_sec;
t->usec = ts.tv_nsec / 1000;
return 0;
}
switch (clock_id) {
#ifdef CLOCK_BOOTTIME
case CLOCK_BOOTTIME:
clock_id = CLOCK_MONOTONIC;
break;
#endif
#ifdef CLOCK_MONOTONIC
case CLOCK_MONOTONIC:
clock_id = CLOCK_REALTIME;
break;
#endif
case CLOCK_REALTIME:
return -1;
}
}
#else /* __MACH__ */
uint64_t abstime, nano;
static mach_timebase_info_data_t info = { 0, 0 };
if (!info.denom) {
if (mach_timebase_info(&info) != KERN_SUCCESS)
return -1;
}
abstime = mach_absolute_time();
nano = (abstime * info.numer) / info.denom;
t->sec = nano / NSEC_PER_SEC;
t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
return 0;
#endif /* __MACH__ */
}
int os_mktime(int year, int month, int day, int hour, int min, int sec,
os_time_t *t)
{
struct tm tm, *tm1;
time_t t_local, t1, t2;
os_time_t tz_offset;
if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
sec > 60)
return -1;
memset(&tm, 0, sizeof(tm));
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
t_local = mktime(&tm);
/* figure out offset to UTC */
tm1 = localtime(&t_local);
if (tm1) {
t1 = mktime(tm1);
tm1 = gmtime(&t_local);
if (tm1) {
t2 = mktime(tm1);
tz_offset = t2 - t1;
} else
tz_offset = 0;
} else
tz_offset = 0;
*t = (os_time_t) t_local - tz_offset;
return 0;
}
int os_gmtime(os_time_t t, struct os_tm *tm)
{
struct tm *tm2;
time_t t2 = t;
tm2 = gmtime(&t2);
if (tm2 == NULL)
return -1;
tm->sec = tm2->tm_sec;
tm->min = tm2->tm_min;
tm->hour = tm2->tm_hour;
tm->day = tm2->tm_mday;
tm->month = tm2->tm_mon + 1;
tm->year = tm2->tm_year + 1900;
return 0;
}
#ifdef __APPLE__
#include <fcntl.h>
static int os_daemon(int nochdir, int noclose)
{
int devnull;
if (chdir("/") < 0)
return -1;
devnull = open("/dev/null", O_RDWR);
if (devnull < 0)
return -1;
if (dup2(devnull, STDIN_FILENO) < 0) {
close(devnull);
return -1;
}
if (dup2(devnull, STDOUT_FILENO) < 0) {
close(devnull);
return -1;
}
if (dup2(devnull, STDERR_FILENO) < 0) {
close(devnull);
return -1;
}
return 0;
}
#else /* __APPLE__ */
#define os_daemon daemon
#endif /* __APPLE__ */
int os_daemonize(const char *pid_file)
{
#if defined(__uClinux__) || defined(__sun__)
return -1;
#else /* defined(__uClinux__) || defined(__sun__) */
if (os_daemon(0, 0)) {
perror("daemon");
return -1;
}
if (pid_file) {
FILE *f = fopen(pid_file, "w");
if (f) {
fprintf(f, "%u\n", getpid());
fclose(f);
}
}
return -0;
#endif /* defined(__uClinux__) || defined(__sun__) */
}
void os_daemonize_terminate(const char *pid_file)
{
if (pid_file)
unlink(pid_file);
}
int os_get_random(unsigned char *buf, size_t len)
{
#ifdef TEST_FUZZ
size_t i;
for (i = 0; i < len; i++)
buf[i] = i & 0xff;
return 0;
#else /* TEST_FUZZ */
FILE *f;
size_t rc;
if (TEST_FAIL())
return -1;
f = fopen("/dev/urandom", "rb");
if (f == NULL) {
printf("Could not open /dev/urandom.\n");
return -1;
}
rc = fread(buf, 1, len, f);
fclose(f);
return rc != len ? -1 : 0;
#endif /* TEST_FUZZ */
}
unsigned long os_random(void)
{
return random();
}
char * os_rel2abs_path(const char *rel_path)
{
char *buf = NULL, *cwd, *ret;
size_t len = 128, cwd_len, rel_len, ret_len;
int last_errno;
if (!rel_path)
return NULL;
if (rel_path[0] == '/')
return os_strdup(rel_path);
for (;;) {
buf = os_malloc(len);
if (buf == NULL)
return NULL;
cwd = getcwd(buf, len);
if (cwd == NULL) {
last_errno = errno;
os_free(buf);
if (last_errno != ERANGE)
return NULL;
len *= 2;
if (len > 2000)
return NULL;
} else {
buf[len - 1] = '\0';
break;
}
}
cwd_len = os_strlen(cwd);
rel_len = os_strlen(rel_path);
ret_len = cwd_len + 1 + rel_len + 1;
ret = os_malloc(ret_len);
if (ret) {
os_memcpy(ret, cwd, cwd_len);
ret[cwd_len] = '/';
os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
ret[ret_len - 1] = '\0';
}
os_free(buf);
return ret;
}
int os_program_init(void)
{
unsigned int seed;
#ifdef ANDROID
/*
* We ignore errors here since errors are normal if we
* are already running as non-root.
*/
#ifdef ANDROID_SETGROUPS_OVERRIDE
gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
#else /* ANDROID_SETGROUPS_OVERRIDE */
gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
#endif /* ANDROID_SETGROUPS_OVERRIDE */
struct __user_cap_header_struct header;
struct __user_cap_data_struct cap;
setgroups(ARRAY_SIZE(groups), groups);
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
setgid(AID_WIFI);
setuid(AID_WIFI);
header.version = _LINUX_CAPABILITY_VERSION;
header.pid = 0;
cap.effective = cap.permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
cap.inheritable = 0;
capset(&header, &cap);
#endif /* ANDROID */
if (os_get_random((unsigned char *) &seed, sizeof(seed)) == 0)
srandom(seed);
return 0;
}
void os_program_deinit(void)
{
#ifdef WPA_TRACE
struct os_alloc_trace *a;
unsigned long total = 0;
dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
total += a->len;
if (a->magic != ALLOC_MAGIC) {
wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
"len %lu",
a, a->magic, (unsigned long) a->len);
continue;
}
wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
a, (unsigned long) a->len);
wpa_trace_dump("memleak", a);
}
if (total)
wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
(unsigned long) total);
wpa_trace_deinit();
#endif /* WPA_TRACE */
}
int os_setenv(const char *name, const char *value, int overwrite)
{
return setenv(name, value, overwrite);
}
int os_unsetenv(const char *name)
{
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
defined(__OpenBSD__)
unsetenv(name);
return 0;
#else
return unsetenv(name);
#endif
}
char * os_readfile(const char *name, size_t *len)
{
FILE *f;
char *buf;
long pos;
f = fopen(name, "rb");
if (f == NULL)
return NULL;
if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
fclose(f);
return NULL;
}
*len = pos;
if (fseek(f, 0, SEEK_SET) < 0) {
fclose(f);
return NULL;
}
buf = os_malloc(*len);
if (buf == NULL) {
fclose(f);
return NULL;
}
if (fread(buf, 1, *len, f) != *len) {
fclose(f);
os_free(buf);
return NULL;
}
fclose(f);
return buf;
}
int os_file_exists(const char *fname)
{
return access(fname, F_OK) == 0;
}
int os_fdatasync(FILE *stream)
{
if (!fflush(stream)) {
#if defined __FreeBSD__ || defined __linux__
return fdatasync(fileno(stream));
#else /* !__linux__ && !__FreeBSD__ */
#ifdef F_FULLFSYNC
/* OS X does not implement fdatasync(). */
return fcntl(fileno(stream), F_FULLFSYNC);
#else /* F_FULLFSYNC */
return fsync(fileno(stream));
#endif /* F_FULLFSYNC */
#endif /* __linux__ */
}
return -1;
}
#ifndef WPA_TRACE
void * os_zalloc(size_t size)
{
return calloc(1, size);
}
#endif /* WPA_TRACE */
size_t os_strlcpy(char *dest, const char *src, size_t siz)
{
const char *s = src;
size_t left = siz;
if (left) {
/* Copy string up to the maximum size of the dest buffer */
while (--left != 0) {
if ((*dest++ = *s++) == '\0')
break;
}
}
if (left == 0) {
/* Not enough room for the string; force NUL-termination */
if (siz != 0)
*dest = '\0';
while (*s++)
; /* determine total src string length */
}
return s - src - 1;
}
int os_memcmp_const(const void *a, const void *b, size_t len)
{
const u8 *aa = a;
const u8 *bb = b;
size_t i;
u8 res;
for (res = 0, i = 0; i < len; i++)
res |= aa[i] ^ bb[i];
return res;
}
void * os_memdup(const void *src, size_t len)
{
void *r = os_malloc(len);
if (r && src)
os_memcpy(r, src, len);
return r;
}
#ifdef WPA_TRACE
#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
char wpa_trace_fail_func[256] = { 0 };
unsigned int wpa_trace_fail_after;
static int testing_fail_alloc(void)
{
const char *func[WPA_TRACE_LEN];
size_t i, res, len;
char *pos, *next;
int match;
if (!wpa_trace_fail_after)
return 0;
res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
i = 0;
if (i < res && os_strcmp(func[i], __func__) == 0)
i++;
if (i < res && os_strcmp(func[i], "os_malloc") == 0)
i++;
if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
i++;
if (i < res && os_strcmp(func[i], "os_calloc") == 0)
i++;
if (i < res && os_strcmp(func[i], "os_realloc") == 0)
i++;
if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
i++;
if (i < res && os_strcmp(func[i], "os_strdup") == 0)
i++;
if (i < res && os_strcmp(func[i], "os_memdup") == 0)
i++;
pos = wpa_trace_fail_func;
match = 0;
while (i < res) {
int allow_skip = 1;
int maybe = 0;
if (*pos == '=') {
allow_skip = 0;
pos++;
} else if (*pos == '?') {
maybe = 1;
pos++;
}
next = os_strchr(pos, ';');
if (next)
len = next - pos;
else
len = os_strlen(pos);
if (os_memcmp(pos, func[i], len) != 0) {
if (maybe && next) {
pos = next + 1;
continue;
}
if (allow_skip) {
i++;
continue;
}
return 0;
}
if (!next) {
match = 1;
break;
}
pos = next + 1;
i++;
}
if (!match)
return 0;
wpa_trace_fail_after--;
if (wpa_trace_fail_after == 0) {
wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
wpa_trace_fail_func);
for (i = 0; i < res; i++)
wpa_printf(MSG_INFO, "backtrace[%d] = %s",
(int) i, func[i]);
return 1;
}
return 0;
}
char wpa_trace_test_fail_func[256] = { 0 };
unsigned int wpa_trace_test_fail_after;
int testing_test_fail(void)
{
const char *func[WPA_TRACE_LEN];
size_t i, res, len;
char *pos, *next;
int match;
if (!wpa_trace_test_fail_after)
return 0;
res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
i = 0;
if (i < res && os_strcmp(func[i], __func__) == 0)
i++;
pos = wpa_trace_test_fail_func;
match = 0;
while (i < res) {
int allow_skip = 1;
int maybe = 0;
if (*pos == '=') {
allow_skip = 0;
pos++;
} else if (*pos == '?') {
maybe = 1;
pos++;
}
next = os_strchr(pos, ';');
if (next)
len = next - pos;
else
len = os_strlen(pos);
if (os_memcmp(pos, func[i], len) != 0) {
if (maybe && next) {
pos = next + 1;
continue;
}
if (allow_skip) {
i++;
continue;
}
return 0;
}
if (!next) {
match = 1;
break;
}
pos = next + 1;
i++;
}
if (!match)
return 0;
wpa_trace_test_fail_after--;
if (wpa_trace_test_fail_after == 0) {
wpa_printf(MSG_INFO, "TESTING: fail at %s",
wpa_trace_test_fail_func);
for (i = 0; i < res; i++)
wpa_printf(MSG_INFO, "backtrace[%d] = %s",
(int) i, func[i]);
return 1;
}
return 0;
}
#else
static inline int testing_fail_alloc(void)
{
return 0;
}
#endif
void * os_malloc(size_t size)
{
struct os_alloc_trace *a;
if (testing_fail_alloc())
return NULL;
a = malloc(sizeof(*a) + size);
if (a == NULL)
return NULL;
a->magic = ALLOC_MAGIC;
dl_list_add(&alloc_list, &a->list);
a->len = size;
wpa_trace_record(a);
return a + 1;
}
void * os_realloc(void *ptr, size_t size)
{
struct os_alloc_trace *a;
size_t copy_len;
void *n;
if (ptr == NULL)
return os_malloc(size);
a = (struct os_alloc_trace *) ptr - 1;
if (a->magic != ALLOC_MAGIC) {
wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
a, a->magic,
a->magic == FREED_MAGIC ? " (already freed)" : "");
wpa_trace_show("Invalid os_realloc() call");
abort();
}
n = os_malloc(size);
if (n == NULL)
return NULL;
copy_len = a->len;
if (copy_len > size)
copy_len = size;
os_memcpy(n, a + 1, copy_len);
os_free(ptr);
return n;
}
void os_free(void *ptr)
{
struct os_alloc_trace *a;
if (ptr == NULL)
return;
a = (struct os_alloc_trace *) ptr - 1;
if (a->magic != ALLOC_MAGIC) {
wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
a, a->magic,
a->magic == FREED_MAGIC ? " (already freed)" : "");
wpa_trace_show("Invalid os_free() call");
abort();
}
dl_list_del(&a->list);
a->magic = FREED_MAGIC;
wpa_trace_check_ref(ptr);
free(a);
}
void * os_zalloc(size_t size)
{
void *ptr = os_malloc(size);
if (ptr)
os_memset(ptr, 0, size);
return ptr;
}
char * os_strdup(const char *s)
{
size_t len;
char *d;
len = os_strlen(s);
d = os_malloc(len + 1);
if (d == NULL)
return NULL;
os_memcpy(d, s, len);
d[len] = '\0';
return d;
}
#endif /* WPA_TRACE */
int os_exec(const char *program, const char *arg, int wait_completion)
{
pid_t pid;
int pid_status;
pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}
if (pid == 0) {
/* run the external command in the child process */
const int MAX_ARG = 30;
char *_program, *_arg, *pos;
char *argv[MAX_ARG + 1];
int i;
_program = os_strdup(program);
_arg = os_strdup(arg);
argv[0] = _program;
i = 1;
pos = _arg;
while (i < MAX_ARG && pos && *pos) {
while (*pos == ' ')
pos++;
if (*pos == '\0')
break;
argv[i++] = pos;
pos = os_strchr(pos, ' ');
if (pos)
*pos++ = '\0';
}
argv[i] = NULL;
execv(program, argv);
perror("execv");
os_free(_program);
os_free(_arg);
exit(0);
return -1;
}
if (wait_completion) {
/* wait for the child process to complete in the parent */
waitpid(pid, &pid_status, 0);
}
return 0;
}

@ -0,0 +1,766 @@
/*
* wpa_supplicant/hostapd control interface library
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#ifdef CONFIG_CTRL_IFACE
#ifdef CONFIG_CTRL_IFACE_UNIX
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
#endif /* CONFIG_CTRL_IFACE_UNIX */
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
#include <netdb.h>
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#ifdef ANDROID
#include <dirent.h>
#include <sys/stat.h>
#include <cutils/sockets.h>
#include "private/android_filesystem_config.h"
#endif /* ANDROID */
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
#include <net/if.h>
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#include "wpa_ctrl.h"
#include "common.h"
#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
#define CTRL_IFACE_SOCKET
#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */
/**
* struct wpa_ctrl - Internal structure for control interface library
*
* This structure is used by the wpa_supplicant/hostapd control interface
* library to store internal data. Programs using the library should not touch
* this data directly. They can only use the pointer to the data structure as
* an identifier for the control interface connection and use this as one of
* the arguments for most of the control interface library functions.
*/
struct wpa_ctrl {
#ifdef CONFIG_CTRL_IFACE_UDP
int s;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 local;
struct sockaddr_in6 dest;
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in local;
struct sockaddr_in dest;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
char *cookie;
char *remote_ifname;
char *remote_ip;
#endif /* CONFIG_CTRL_IFACE_UDP */
#ifdef CONFIG_CTRL_IFACE_UNIX
int s;
struct sockaddr_un local;
struct sockaddr_un dest;
#endif /* CONFIG_CTRL_IFACE_UNIX */
#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
HANDLE pipe;
#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
};
#ifdef CONFIG_CTRL_IFACE_UNIX
#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR
#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp"
#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */
#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX
#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_"
#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
{
return wpa_ctrl_open2(ctrl_path, NULL);
}
struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path,
const char *cli_path)
{
struct wpa_ctrl *ctrl;
static int counter = 0;
int ret;
size_t res;
int tries = 0;
int flags;
if (ctrl_path == NULL)
return NULL;
ctrl = os_zalloc(sizeof(*ctrl));
if (ctrl == NULL)
return NULL;
ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
if (ctrl->s < 0) {
os_free(ctrl);
return NULL;
}
ctrl->local.sun_family = AF_UNIX;
counter++;
try_again:
if (cli_path && cli_path[0] == '/') {
ret = os_snprintf(ctrl->local.sun_path,
sizeof(ctrl->local.sun_path),
"%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
cli_path, (int) getpid(), counter);
} else {
ret = os_snprintf(ctrl->local.sun_path,
sizeof(ctrl->local.sun_path),
CONFIG_CTRL_IFACE_CLIENT_DIR "/"
CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
(int) getpid(), counter);
}
if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) {
close(ctrl->s);
os_free(ctrl);
return NULL;
}
tries++;
#ifdef ANDROID
/* Set client socket file permissions so that bind() creates the client
* socket with these permissions and there is no need to try to change
* them with chmod() after bind() which would have potential issues with
* race conditions. These permissions are needed to make sure the server
* side (wpa_supplicant or hostapd) can reply to the control interface
* messages.
*
* The lchown() calls below after bind() are also part of the needed
* operations to allow the response to go through. Those are using the
* no-deference-symlinks version to avoid races. */
fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
#endif /* ANDROID */
if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
sizeof(ctrl->local)) < 0) {
if (errno == EADDRINUSE && tries < 2) {
/*
* getpid() returns unique identifier for this instance
* of wpa_ctrl, so the existing socket file must have
* been left by unclean termination of an earlier run.
* Remove the file and try again.
*/
unlink(ctrl->local.sun_path);
goto try_again;
}
close(ctrl->s);
os_free(ctrl);
return NULL;
}
#ifdef ANDROID
/* Set group even if we do not have privileges to change owner */
lchown(ctrl->local.sun_path, -1, AID_WIFI);
lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
if (os_strncmp(ctrl_path, "@android:", 9) == 0) {
if (socket_local_client_connect(
ctrl->s, ctrl_path + 9,
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_DGRAM) < 0) {
close(ctrl->s);
unlink(ctrl->local.sun_path);
os_free(ctrl);
return NULL;
}
return ctrl;
}
/*
* If the ctrl_path isn't an absolute pathname, assume that
* it's the name of a socket in the Android reserved namespace.
* Otherwise, it's a normal UNIX domain socket appearing in the
* filesystem.
*/
if (*ctrl_path != '/') {
char buf[21];
os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
if (socket_local_client_connect(
ctrl->s, buf,
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_DGRAM) < 0) {
close(ctrl->s);
unlink(ctrl->local.sun_path);
os_free(ctrl);
return NULL;
}
return ctrl;
}
#endif /* ANDROID */
ctrl->dest.sun_family = AF_UNIX;
if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) {
ctrl->dest.sun_path[0] = '\0';
os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10,
sizeof(ctrl->dest.sun_path) - 1);
} else {
res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
sizeof(ctrl->dest.sun_path));
if (res >= sizeof(ctrl->dest.sun_path)) {
close(ctrl->s);
os_free(ctrl);
return NULL;
}
}
if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
sizeof(ctrl->dest)) < 0) {
close(ctrl->s);
unlink(ctrl->local.sun_path);
os_free(ctrl);
return NULL;
}
/*
* Make socket non-blocking so that we don't hang forever if
* target dies unexpectedly.
*/
flags = fcntl(ctrl->s, F_GETFL);
if (flags >= 0) {
flags |= O_NONBLOCK;
if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
perror("fcntl(ctrl->s, O_NONBLOCK)");
/* Not fatal, continue on.*/
}
}
return ctrl;
}
void wpa_ctrl_close(struct wpa_ctrl *ctrl)
{
if (ctrl == NULL)
return;
unlink(ctrl->local.sun_path);
if (ctrl->s >= 0)
close(ctrl->s);
os_free(ctrl);
}
#ifdef ANDROID
/**
* wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
* may be left over from clients that were previously connected to
* wpa_supplicant. This keeps these files from being orphaned in the
* event of crashes that prevented them from being removed as part
* of the normal orderly shutdown.
*/
void wpa_ctrl_cleanup(void)
{
DIR *dir;
struct dirent *result;
size_t dirnamelen;
size_t maxcopy;
char pathname[PATH_MAX];
char *namep;
if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL)
return;
dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/",
CONFIG_CTRL_IFACE_CLIENT_DIR);
if (dirnamelen >= sizeof(pathname)) {
closedir(dir);
return;
}
namep = pathname + dirnamelen;
maxcopy = PATH_MAX - dirnamelen;
while ((result = readdir(dir)) != NULL) {
if (os_strlcpy(namep, result->d_name, maxcopy) < maxcopy)
unlink(pathname);
}
closedir(dir);
}
#endif /* ANDROID */
#else /* CONFIG_CTRL_IFACE_UNIX */
#ifdef ANDROID
void wpa_ctrl_cleanup(void)
{
}
#endif /* ANDROID */
#endif /* CONFIG_CTRL_IFACE_UNIX */
#ifdef CONFIG_CTRL_IFACE_UDP
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
{
struct wpa_ctrl *ctrl;
char buf[128];
size_t len;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
struct hostent *h;
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
ctrl = os_zalloc(sizeof(*ctrl));
if (ctrl == NULL)
return NULL;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0);
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (ctrl->s < 0) {
perror("socket");
os_free(ctrl);
return NULL;
}
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
ctrl->local.sin6_family = AF_INET6;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
ctrl->local.sin6_addr = in6addr_any;
#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
inet_pton(AF_INET6, "::1", &ctrl->local.sin6_addr);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
ctrl->local.sin_family = AF_INET;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
ctrl->local.sin_addr.s_addr = INADDR_ANY;
#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
sizeof(ctrl->local)) < 0) {
close(ctrl->s);
os_free(ctrl);
return NULL;
}
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
ctrl->dest.sin6_family = AF_INET6;
inet_pton(AF_INET6, "::1", &ctrl->dest.sin6_addr);
ctrl->dest.sin6_port = htons(WPA_CTRL_IFACE_PORT);
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
ctrl->dest.sin_family = AF_INET;
ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
if (ctrl_path) {
char *port, *name;
int port_id;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
char *scope;
int scope_id = 0;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
name = os_strdup(ctrl_path);
if (name == NULL) {
close(ctrl->s);
os_free(ctrl);
return NULL;
}
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
port = os_strchr(name, ',');
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
port = os_strchr(name, ':');
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (port) {
port_id = atoi(&port[1]);
port[0] = '\0';
} else
port_id = WPA_CTRL_IFACE_PORT;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
scope = os_strchr(name, '%');
if (scope) {
scope_id = if_nametoindex(&scope[1]);
scope[0] = '\0';
}
h = gethostbyname2(name, AF_INET6);
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
h = gethostbyname(name);
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
ctrl->remote_ip = os_strdup(name);
os_free(name);
if (h == NULL) {
perror("gethostbyname");
close(ctrl->s);
os_free(ctrl->remote_ip);
os_free(ctrl);
return NULL;
}
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
ctrl->dest.sin6_scope_id = scope_id;
ctrl->dest.sin6_port = htons(port_id);
os_memcpy(&ctrl->dest.sin6_addr, h->h_addr, h->h_length);
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
ctrl->dest.sin_port = htons(port_id);
os_memcpy(&ctrl->dest.sin_addr.s_addr, h->h_addr, h->h_length);
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
} else
ctrl->remote_ip = os_strdup("localhost");
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
sizeof(ctrl->dest)) < 0) {
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
char addr[INET6_ADDRSTRLEN];
wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s",
inet_ntop(AF_INET6, &ctrl->dest.sin6_addr, addr,
sizeof(ctrl->dest)),
ntohs(ctrl->dest.sin6_port),
strerror(errno));
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s",
inet_ntoa(ctrl->dest.sin_addr),
ntohs(ctrl->dest.sin_port),
strerror(errno));
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
close(ctrl->s);
os_free(ctrl->remote_ip);
os_free(ctrl);
return NULL;
}
len = sizeof(buf) - 1;
if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) {
buf[len] = '\0';
ctrl->cookie = os_strdup(buf);
}
if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) {
buf[len] = '\0';
ctrl->remote_ifname = os_strdup(buf);
}
return ctrl;
}
char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl)
{
#define WPA_CTRL_MAX_PS_NAME 100
static char ps[WPA_CTRL_MAX_PS_NAME] = {};
os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s",
ctrl->remote_ip, ctrl->remote_ifname);
return ps;
}
void wpa_ctrl_close(struct wpa_ctrl *ctrl)
{
close(ctrl->s);
os_free(ctrl->cookie);
os_free(ctrl->remote_ifname);
os_free(ctrl->remote_ip);
os_free(ctrl);
}
#endif /* CONFIG_CTRL_IFACE_UDP */
#ifdef CTRL_IFACE_SOCKET
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
char *reply, size_t *reply_len,
void (*msg_cb)(char *msg, size_t len))
{
struct timeval tv;
struct os_reltime started_at;
int res;
fd_set rfds;
const char *_cmd;
char *cmd_buf = NULL;
size_t _cmd_len;
#ifdef CONFIG_CTRL_IFACE_UDP
if (ctrl->cookie) {
char *pos;
_cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
cmd_buf = os_malloc(_cmd_len);
if (cmd_buf == NULL)
return -1;
_cmd = cmd_buf;
pos = cmd_buf;
os_strlcpy(pos, ctrl->cookie, _cmd_len);
pos += os_strlen(ctrl->cookie);
*pos++ = ' ';
os_memcpy(pos, cmd, cmd_len);
} else
#endif /* CONFIG_CTRL_IFACE_UDP */
{
_cmd = cmd;
_cmd_len = cmd_len;
}
errno = 0;
started_at.sec = 0;
started_at.usec = 0;
retry_send:
if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
{
/*
* Must be a non-blocking socket... Try for a bit
* longer before giving up.
*/
if (started_at.sec == 0)
os_get_reltime(&started_at);
else {
struct os_reltime n;
os_get_reltime(&n);
/* Try for a few seconds. */
if (os_reltime_expired(&n, &started_at, 5))
goto send_err;
}
os_sleep(1, 0);
goto retry_send;
}
send_err:
os_free(cmd_buf);
return -1;
}
os_free(cmd_buf);
for (;;) {
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(ctrl->s, &rfds);
res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
if (res < 0 && errno == EINTR)
continue;
if (res < 0)
return res;
if (FD_ISSET(ctrl->s, &rfds)) {
res = recv(ctrl->s, reply, *reply_len, 0);
if (res < 0)
return res;
if ((res > 0 && reply[0] == '<') ||
(res > 6 && strncmp(reply, "IFNAME=", 7) == 0)) {
/* This is an unsolicited message from
* wpa_supplicant, not the reply to the
* request. Use msg_cb to report this to the
* caller. */
if (msg_cb) {
/* Make sure the message is nul
* terminated. */
if ((size_t) res == *reply_len)
res = (*reply_len) - 1;
reply[res] = '\0';
msg_cb(reply, res);
}
continue;
}
*reply_len = res;
break;
} else {
return -2;
}
}
return 0;
}
#endif /* CTRL_IFACE_SOCKET */
static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
{
char buf[10];
int ret;
size_t len = 10;
ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6,
buf, &len, NULL);
if (ret < 0)
return ret;
if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0)
return 0;
return -1;
}
int wpa_ctrl_attach(struct wpa_ctrl *ctrl)
{
return wpa_ctrl_attach_helper(ctrl, 1);
}
int wpa_ctrl_detach(struct wpa_ctrl *ctrl)
{
return wpa_ctrl_attach_helper(ctrl, 0);
}
#ifdef CTRL_IFACE_SOCKET
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
{
int res;
res = recv(ctrl->s, reply, *reply_len, 0);
if (res < 0)
return res;
*reply_len = res;
return 0;
}
int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
{
struct timeval tv;
fd_set rfds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(ctrl->s, &rfds);
select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
return FD_ISSET(ctrl->s, &rfds);
}
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
{
return ctrl->s;
}
#endif /* CTRL_IFACE_SOCKET */
#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
#ifndef WPA_SUPPLICANT_NAMED_PIPE
#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
#endif
#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
{
struct wpa_ctrl *ctrl;
DWORD mode;
TCHAR name[256];
int i, ret;
ctrl = os_malloc(sizeof(*ctrl));
if (ctrl == NULL)
return NULL;
os_memset(ctrl, 0, sizeof(*ctrl));
#ifdef UNICODE
if (ctrl_path == NULL)
ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX);
else
ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
ctrl_path);
#else /* UNICODE */
if (ctrl_path == NULL)
ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX);
else
ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
ctrl_path);
#endif /* UNICODE */
if (os_snprintf_error(256, ret)) {
os_free(ctrl);
return NULL;
}
for (i = 0; i < 10; i++) {
ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, 0, NULL);
/*
* Current named pipe server side in wpa_supplicant is
* re-opening the pipe for new clients only after the previous
* one is taken into use. This leaves a small window for race
* conditions when two connections are being opened at almost
* the same time. Retry if that was the case.
*/
if (ctrl->pipe != INVALID_HANDLE_VALUE ||
GetLastError() != ERROR_PIPE_BUSY)
break;
WaitNamedPipe(name, 1000);
}
if (ctrl->pipe == INVALID_HANDLE_VALUE) {
os_free(ctrl);
return NULL;
}
mode = PIPE_READMODE_MESSAGE;
if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) {
CloseHandle(ctrl->pipe);
os_free(ctrl);
return NULL;
}
return ctrl;
}
void wpa_ctrl_close(struct wpa_ctrl *ctrl)
{
CloseHandle(ctrl->pipe);
os_free(ctrl);
}
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
char *reply, size_t *reply_len,
void (*msg_cb)(char *msg, size_t len))
{
DWORD written;
DWORD readlen = *reply_len;
if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL))
return -1;
if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL))
return -1;
*reply_len = readlen;
return 0;
}
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
{
DWORD len = *reply_len;
if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL))
return -1;
*reply_len = len;
return 0;
}
int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
{
DWORD left;
if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL))
return -1;
return left ? 1 : 0;
}
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
{
return -1;
}
#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
#endif /* CONFIG_CTRL_IFACE */

@ -0,0 +1,643 @@
/*
* wpa_supplicant/hostapd control interface library
* Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_CTRL_H
#define WPA_CTRL_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
/* wpa_supplicant control interface - fixed message prefixes */
/** Interactive request for identity/password/pin */
#define WPA_CTRL_REQ "CTRL-REQ-"
/** Response to identity/password/pin request */
#define WPA_CTRL_RSP "CTRL-RSP-"
/* Event messages with fixed prefix */
/** Authentication completed successfully and data connection enabled */
#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
/** Disconnected, data connection is not available */
#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
/** Association rejected during connection attempt */
#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
/** Authentication rejected during connection attempt */
#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT "
/** wpa_supplicant is exiting */
#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
/** Password change was completed successfully */
#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED "
/** EAP-Request/Notification received */
#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION "
/** EAP authentication started (EAP-Request/Identity received) */
#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED "
/** EAP method proposed by the server */
#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD "
/** EAP method selected */
#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
/** EAP peer certificate from TLS */
#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
/** EAP peer certificate alternative subject name component from TLS */
#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT "
/** EAP TLS certificate chain validation error */
#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
/** EAP status */
#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS "
/** Retransmit the previous request packet */
#define WPA_EVENT_EAP_RETRANSMIT "CTRL-EVENT-EAP-RETRANSMIT "
#define WPA_EVENT_EAP_RETRANSMIT2 "CTRL-EVENT-EAP-RETRANSMIT2 "
/** EAP authentication completed successfully */
#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
#define WPA_EVENT_EAP_SUCCESS2 "CTRL-EVENT-EAP-SUCCESS2 "
/** EAP authentication failed (EAP-Failure received) */
#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
#define WPA_EVENT_EAP_FAILURE2 "CTRL-EVENT-EAP-FAILURE2 "
/** EAP authentication failed due to no response received */
#define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE "
#define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 "
#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE "
/** Network block temporarily disabled (e.g., due to authentication failure) */
#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
/** Temporarily disabled network block re-enabled */
#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED "
/** New scan started */
#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED "
/** New scan results available */
#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
/** Scan command failed */
#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED "
/** wpa_supplicant state change */
#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE "
/** A new BSS entry was added (followed by BSS entry id and BSSID) */
#define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
/** A BSS entry was removed (followed by BSS entry id and BSSID) */
#define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
/** No suitable network was found */
#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
/** Change in the signal level was reported by the driver */
#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
/** Beacon loss reported by the driver */
#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS "
/** Regulatory domain channel */
#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
/** Channel switch started (followed by freq=<MHz> and other channel parameters)
*/
#define WPA_EVENT_CHANNEL_SWITCH_STARTED "CTRL-EVENT-STARTED-CHANNEL-SWITCH "
/** Channel switch (followed by freq=<MHz> and other channel parameters) */
#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
/** SAE authentication failed due to unknown password identifier */
#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
"CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
/** Unprotected Beacon frame dropped */
#define WPA_EVENT_UNPROT_BEACON "CTRL-EVENT-UNPROT-BEACON "
/** Decision made to do a within-ESS roam */
#define WPA_EVENT_DO_ROAM "CTRL-EVENT-DO-ROAM "
/** Decision made to skip a within-ESS roam */
#define WPA_EVENT_SKIP_ROAM "CTRL-EVENT-SKIP-ROAM "
/** IP subnet status change notification
*
* When using an offloaded roaming mechanism where driver/firmware takes care
* of roaming and IP subnet validation checks post-roaming, this event can
* indicate whether IP subnet has changed.
*
* The event has a status=<0/1/2> parameter where
* 0 = unknown
* 1 = IP subnet unchanged (can continue to use the old IP address)
* 2 = IP subnet changed (need to get a new IP address)
*/
#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE "
/** RSN IBSS 4-way handshakes completed with specified peer */
#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
/** Notification of frequency conflict due to a concurrent operation.
*
* The indicated network is disabled and needs to be re-enabled before it can
* be used again.
*/
#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
/** Frequency ranges that the driver recommends to avoid */
#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
/** A new network profile was added (followed by network entry id) */
#define WPA_EVENT_NETWORK_ADDED "CTRL-EVENT-NETWORK-ADDED "
/** A network profile was removed (followed by prior network entry id) */
#define WPA_EVENT_NETWORK_REMOVED "CTRL-EVENT-NETWORK-REMOVED "
/** Result of MSCS setup */
#define WPA_EVENT_MSCS_RESULT "CTRL-EVENT-MSCS-RESULT "
/** WPS overlap detected in PBC mode */
#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
/** Available WPS AP with active PBC found in scan results */
#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC "
/** Available WPS AP with our address as authorized in scan results */
#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH "
/** Available WPS AP with recently selected PIN registrar found in scan results
*/
#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN "
/** Available WPS AP found in scan results */
#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE "
/** A new credential received */
#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED "
/** M2D received */
#define WPS_EVENT_M2D "WPS-M2D "
/** WPS registration failed after M2/M2D */
#define WPS_EVENT_FAIL "WPS-FAIL "
/** WPS registration completed successfully */
#define WPS_EVENT_SUCCESS "WPS-SUCCESS "
/** WPS enrollment attempt timed out and was terminated */
#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT "
/* PBC mode was activated */
#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE "
/* PBC mode was disabled */
#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE "
#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN "
#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK "
/** Result of SCS setup */
#define WPA_EVENT_SCS_RESULT "CTRL-EVENT-SCS-RESULT "
/* Event indicating DSCP policy */
#define WPA_EVENT_DSCP_POLICY "CTRL-EVENT-DSCP-POLICY "
/* WPS ER events */
#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD "
#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE "
#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD "
#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
/* DPP events */
#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED "
#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
#define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION "
#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
#define DPP_EVENT_CONN_STATUS_RESULT "DPP-CONN-STATUS-RESULT "
#define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM "
#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
#define DPP_EVENT_CONFOBJ_SSID_CHARSET "DPP-CONFOBJ-SSID-CHARSET "
#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
#define DPP_EVENT_PP_KEY "DPP-PP-KEY "
#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
#define DPP_EVENT_SERVER_NAME "DPP-SERVER-NAME "
#define DPP_EVENT_CERTBAG "DPP-CERTBAG "
#define DPP_EVENT_CACERT "DPP-CACERT "
#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
#define DPP_EVENT_CONFIGURATOR_ID "DPP-CONFIGURATOR-ID "
#define DPP_EVENT_RX "DPP-RX "
#define DPP_EVENT_TX "DPP-TX "
#define DPP_EVENT_TX_STATUS "DPP-TX-STATUS "
#define DPP_EVENT_FAIL "DPP-FAIL "
#define DPP_EVENT_PKEX_T_LIMIT "DPP-PKEX-T-LIMIT "
#define DPP_EVENT_INTRO "DPP-INTRO "
#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
#define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED "
#define DPP_EVENT_MUD_URL "DPP-MUD-URL "
#define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT "
#define DPP_EVENT_CSR "DPP-CSR "
#define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX "
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED "
#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED "
/** Mesh SAE authentication failure. Wrong password suspected. */
#define MESH_SAE_AUTH_FAILURE "MESH-SAE-AUTH-FAILURE "
#define MESH_SAE_AUTH_BLOCKED "MESH-SAE-AUTH-BLOCKED "
/* WMM AC events */
#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED "
#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED "
#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED "
/** P2P device found */
#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
/** P2P device lost */
#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST "
/** A P2P device requested GO negotiation, but we were not ready to start the
* negotiation */
#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE "
#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE "
/* parameters: <peer address> <PIN> */
#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
/* parameters: <peer address> <status> */
#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE"
/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
/* parameters: <src addr> <update indicator> <TLVs> */
#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
#define P2P_EVENT_INVITATION_ACCEPTED "P2P-INVITATION-ACCEPTED "
#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
#define P2P_EVENT_FALLBACK_TO_GO_NEG "P2P-FALLBACK-TO-GO-NEG "
#define P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED "P2P-FALLBACK-TO-GO-NEG-ENABLED "
/* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START "
#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE "
#define INTERWORKING_AP "INTERWORKING-AP "
#define INTERWORKING_EXCLUDED "INTERWORKING-BLACKLISTED "
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED "
#define INTERWORKING_SELECTED "INTERWORKING-SELECTED "
/* Credential block added; parameters: <id> */
#define CRED_ADDED "CRED-ADDED "
/* Credential block modified; parameters: <id> <field> */
#define CRED_MODIFIED "CRED-MODIFIED "
/* Credential block removed; parameters: <id> */
#define CRED_REMOVED "CRED-REMOVED "
#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
/* parameters: <addr> <dialog_token> <freq> */
#define GAS_QUERY_START "GAS-QUERY-START "
/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
#define GAS_QUERY_DONE "GAS-QUERY-DONE "
/* parameters: <addr> <result> */
#define ANQP_QUERY_DONE "ANQP-QUERY-DONE "
#define RX_ANQP "RX-ANQP "
#define RX_HS20_ANQP "RX-HS20-ANQP "
#define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON "
#define RX_HS20_ICON "RX-HS20-ICON "
#define RX_MBO_ANQP "RX-MBO-ANQP "
/* parameters: <Venue Number> <Venue URL> */
#define RX_VENUE_URL "RX-VENUE-URL "
#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE "
#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED "
#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED "
/* hostapd control interface - fixed message prefixes */
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
#define WPS_EVENT_PIN_ACTIVE "WPS-PIN-ACTIVE "
#define WPS_EVENT_CANCEL "WPS-CANCEL "
#define AP_STA_CONNECTED "AP-STA-CONNECTED "
#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
#define AP_STA_POLL_OK "AP-STA-POLL-OK "
#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD "
#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE "
#define AP_EVENT_ENABLED "AP-ENABLED "
#define AP_EVENT_DISABLED "AP-DISABLED "
#define INTERFACE_ENABLED "INTERFACE-ENABLED "
#define INTERFACE_DISABLED "INTERFACE-DISABLED "
#define ACS_EVENT_STARTED "ACS-STARTED "
#define ACS_EVENT_COMPLETED "ACS-COMPLETED "
#define ACS_EVENT_FAILED "ACS-FAILED "
#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED "
#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL "
#define DFS_EVENT_CAC_START "DFS-CAC-START "
#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
#define AP_CSA_FINISHED "AP-CSA-FINISHED "
#define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED "
#define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON "
/* BSS Transition Management Response frame received */
#define BSS_TM_RESP "BSS-TM-RESP "
/* Collocated Interference Request frame received;
* parameters: <dialog token> <automatic report enabled> <report timeout> */
#define COLOC_INTF_REQ "COLOC-INTF-REQ "
/* Collocated Interference Report frame received;
* parameters: <STA address> <dialog token> <hexdump of report elements> */
#define COLOC_INTF_REPORT "COLOC-INTF-REPORT "
/* MBO IE with cellular data connection preference received */
#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE "
/* BSS Transition Management Request received with MBO transition reason */
#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON "
/* parameters: <STA address> <dialog token> <ack=0/1> */
#define BEACON_REQ_TX_STATUS "BEACON-REQ-TX-STATUS "
/* parameters: <STA address> <dialog token> <report mode> <beacon report> */
#define BEACON_RESP_RX "BEACON-RESP-RX "
/* PMKSA cache entry added; parameters: <BSSID> <network_id> */
#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED "
/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */
#define PMKSA_CACHE_REMOVED "PMKSA-CACHE-REMOVED "
/* FILS HLP Container receive; parameters: dst=<addr> src=<addr> frame=<hexdump>
*/
#define FILS_HLP_RX "FILS-HLP-RX "
/* Event to indicate Probe Request frame;
* parameters: sa=<STA MAC address> signal=<signal> */
#define RX_PROBE_REQUEST "RX-PROBE-REQUEST "
/* Event to indicate station's HT/VHT operation mode change information */
#define STA_OPMODE_MAX_BW_CHANGED "STA-OPMODE-MAX-BW-CHANGED "
#define STA_OPMODE_SMPS_MODE_CHANGED "STA-OPMODE-SMPS-MODE-CHANGED "
#define STA_OPMODE_N_SS_CHANGED "STA-OPMODE-N_SS-CHANGED "
/* New interface addition or removal for 4addr WDS SDA */
#define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED "
#define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED "
/* Transition mode disabled indication - followed by bitmap */
#define TRANSITION_DISABLE "TRANSITION-DISABLE "
/* OCV validation failure; parameters: addr=<src addr>
* frame=<saqueryreq/saqueryresp> error=<error string> */
#define OCV_FAILURE "OCV-FAILURE "
/* Event triggered for received management frame */
#define AP_MGMT_FRAME_RECEIVED "AP-MGMT-FRAME-RECEIVED "
#ifndef BIT
#define BIT(x) (1U << (x))
#endif
/* PASN authentication status */
#define PASN_AUTH_STATUS "PASN-AUTH-STATUS "
/* BSS command information masks */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF
#define WPA_BSS_MASK_ID BIT(0)
#define WPA_BSS_MASK_BSSID BIT(1)
#define WPA_BSS_MASK_FREQ BIT(2)
#define WPA_BSS_MASK_BEACON_INT BIT(3)
#define WPA_BSS_MASK_CAPABILITIES BIT(4)
#define WPA_BSS_MASK_QUAL BIT(5)
#define WPA_BSS_MASK_NOISE BIT(6)
#define WPA_BSS_MASK_LEVEL BIT(7)
#define WPA_BSS_MASK_TSF BIT(8)
#define WPA_BSS_MASK_AGE BIT(9)
#define WPA_BSS_MASK_IE BIT(10)
#define WPA_BSS_MASK_FLAGS BIT(11)
#define WPA_BSS_MASK_SSID BIT(12)
#define WPA_BSS_MASK_WPS_SCAN BIT(13)
#define WPA_BSS_MASK_P2P_SCAN BIT(14)
#define WPA_BSS_MASK_INTERNETW BIT(15)
#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16)
#define WPA_BSS_MASK_DELIM BIT(17)
#define WPA_BSS_MASK_MESH_SCAN BIT(18)
#define WPA_BSS_MASK_SNR BIT(19)
#define WPA_BSS_MASK_EST_THROUGHPUT BIT(20)
#define WPA_BSS_MASK_FST BIT(21)
#define WPA_BSS_MASK_UPDATE_IDX BIT(22)
#define WPA_BSS_MASK_BEACON_IE BIT(23)
#define WPA_BSS_MASK_FILS_INDICATION BIT(24)
/* VENDOR_ELEM_* frame id values */
enum wpa_vendor_elem_frame {
VENDOR_ELEM_PROBE_REQ_P2P = 0,
VENDOR_ELEM_PROBE_RESP_P2P = 1,
VENDOR_ELEM_PROBE_RESP_P2P_GO = 2,
VENDOR_ELEM_BEACON_P2P_GO = 3,
VENDOR_ELEM_P2P_PD_REQ = 4,
VENDOR_ELEM_P2P_PD_RESP = 5,
VENDOR_ELEM_P2P_GO_NEG_REQ = 6,
VENDOR_ELEM_P2P_GO_NEG_RESP = 7,
VENDOR_ELEM_P2P_GO_NEG_CONF = 8,
VENDOR_ELEM_P2P_INV_REQ = 9,
VENDOR_ELEM_P2P_INV_RESP = 10,
VENDOR_ELEM_P2P_ASSOC_REQ = 11,
VENDOR_ELEM_P2P_ASSOC_RESP = 12,
VENDOR_ELEM_ASSOC_REQ = 13,
VENDOR_ELEM_PROBE_REQ = 14,
NUM_VENDOR_ELEM_FRAMES
};
/* wpa_supplicant/hostapd control interface access */
/**
* wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd
* @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
* Returns: Pointer to abstract control interface data or %NULL on failure
*
* This function is used to open a control interface to wpa_supplicant/hostapd.
* ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path
* is configured in wpa_supplicant/hostapd and other programs using the control
* interface need to use matching path configuration.
*/
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
/**
* wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd
* @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
* @cli_path: Path for client UNIX domain sockets; ignored if UDP socket
* is used.
* Returns: Pointer to abstract control interface data or %NULL on failure
*
* This function is used to open a control interface to wpa_supplicant/hostapd
* when the socket path for client need to be specified explicitly. Default
* ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client
* socket path is /tmp.
*/
struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);
/**
* wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
* @ctrl: Control interface data from wpa_ctrl_open()
*
* This function is used to close a control interface.
*/
void wpa_ctrl_close(struct wpa_ctrl *ctrl);
/**
* wpa_ctrl_request - Send a command to wpa_supplicant/hostapd
* @ctrl: Control interface data from wpa_ctrl_open()
* @cmd: Command; usually, ASCII text, e.g., "PING"
* @cmd_len: Length of the cmd in bytes
* @reply: Buffer for the response
* @reply_len: Reply buffer length
* @msg_cb: Callback function for unsolicited messages or %NULL if not used
* Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
*
* This function is used to send commands to wpa_supplicant/hostapd. Received
* response will be written to reply and reply_len is set to the actual length
* of the reply. This function will block for up to 10 seconds while waiting
* for the reply. If unsolicited messages are received, the blocking time may
* be longer.
*
* msg_cb can be used to register a callback function that will be called for
* unsolicited messages received while waiting for the command response. These
* messages may be received if wpa_ctrl_request() is called at the same time as
* wpa_supplicant/hostapd is sending such a message. This can happen only if
* the program has used wpa_ctrl_attach() to register itself as a monitor for
* event messages. Alternatively to msg_cb, programs can register two control
* interface connections and use one of them for commands and the other one for
* receiving event messages, in other words, call wpa_ctrl_attach() only for
* the control interface connection that will be used for event messages.
*/
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
char *reply, size_t *reply_len,
void (*msg_cb)(char *msg, size_t len));
/**
* wpa_ctrl_attach - Register as an event monitor for the control interface
* @ctrl: Control interface data from wpa_ctrl_open()
* Returns: 0 on success, -1 on failure, -2 on timeout
*
* This function registers the control interface connection as a monitor for
* wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the
* control interface connection starts receiving event messages that can be
* read with wpa_ctrl_recv().
*/
int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
/**
* wpa_ctrl_detach - Unregister event monitor from the control interface
* @ctrl: Control interface data from wpa_ctrl_open()
* Returns: 0 on success, -1 on failure, -2 on timeout
*
* This function unregisters the control interface connection as a monitor for
* wpa_supplicant/hostapd events, i.e., cancels the registration done with
* wpa_ctrl_attach().
*/
int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
/**
* wpa_ctrl_recv - Receive a pending control interface message
* @ctrl: Control interface data from wpa_ctrl_open()
* @reply: Buffer for the message data
* @reply_len: Length of the reply buffer
* Returns: 0 on success, -1 on failure
*
* This function will receive a pending control interface message. The received
* response will be written to reply and reply_len is set to the actual length
* of the reply.
* wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach()
* must have been used to register the control interface as an event monitor.
*/
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
/**
* wpa_ctrl_pending - Check whether there are pending event messages
* @ctrl: Control interface data from wpa_ctrl_open()
* Returns: 1 if there are pending messages, 0 if no, or -1 on error
*
* This function will check whether there are any pending control interface
* message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is
* only used for event messages, i.e., wpa_ctrl_attach() must have been used to
* register the control interface as an event monitor.
*/
int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
/**
* wpa_ctrl_get_fd - Get file descriptor used by the control interface
* @ctrl: Control interface data from wpa_ctrl_open()
* Returns: File descriptor used for the connection
*
* This function can be used to get the file descriptor that is used for the
* control interface connection. The returned value can be used, e.g., with
* select() while waiting for multiple events.
*
* The returned file descriptor must not be used directly for sending or
* receiving packets; instead, the library functions wpa_ctrl_request() and
* wpa_ctrl_recv() must be used for this.
*/
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
#ifdef ANDROID
/**
* wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
* may be left over from clients that were previously connected to
* wpa_supplicant. This keeps these files from being orphaned in the
* event of crashes that prevented them from being removed as part
* of the normal orderly shutdown.
*/
void wpa_ctrl_cleanup(void);
#endif /* ANDROID */
#ifdef CONFIG_CTRL_IFACE_UDP
/* Port range for multiple wpa_supplicant instances and multiple VIFs */
#define WPA_CTRL_IFACE_PORT 9877
#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */
#define WPA_GLOBAL_CTRL_IFACE_PORT 9878
#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */
char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
#endif /* CONFIG_CTRL_IFACE_UDP */
#ifdef __cplusplus
}
#endif
#endif /* WPA_CTRL_H */

@ -0,0 +1,370 @@
/*
* wpa_supplicant/hostapd / Debug prints
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_DEBUG_H
#define WPA_DEBUG_H
#include "wpabuf.h"
extern int wpa_debug_level;
extern int wpa_debug_show_keys;
extern int wpa_debug_timestamp;
extern int wpa_debug_syslog;
/* Debugging function - conditional printf and hex dump. Driver wrappers can
* use these for debugging purposes. */
enum {
MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
};
#ifdef CONFIG_NO_STDOUT_DEBUG
#define wpa_debug_print_timestamp() do { } while (0)
#define wpa_printf(args...) do { } while (0)
#define wpa_hexdump(l,t,b,le) do { } while (0)
#define wpa_hexdump_buf(l,t,b) do { } while (0)
#define wpa_hexdump_key(l,t,b,le) do { } while (0)
#define wpa_hexdump_buf_key(l,t,b) do { } while (0)
#define wpa_hexdump_ascii(l,t,b,le) do { } while (0)
#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
#define wpa_debug_open_file(p) do { } while (0)
#define wpa_debug_close_file() do { } while (0)
#define wpa_debug_setup_stdout() do { } while (0)
#define wpa_dbg(args...) do { } while (0)
static inline int wpa_debug_reopen_file(void)
{
return 0;
}
#else /* CONFIG_NO_STDOUT_DEBUG */
int wpa_debug_open_file(const char *path);
int wpa_debug_reopen_file(void);
void wpa_debug_close_file(void);
void wpa_debug_setup_stdout(void);
/**
* wpa_debug_printf_timestamp - Print timestamp for debug output
*
* This function prints a timestamp in seconds_from_1970.microsoconds
* format if debug output has been configured to include timestamps in debug
* messages.
*/
void wpa_debug_print_timestamp(void);
/**
* wpa_printf - conditional printf
* @level: priority level (MSG_*) of the message
* @fmt: printf format string, followed by optional arguments
*
* This function is used to print conditional debugging and error messages. The
* output may be directed to stdout, stderr, and/or syslog based on
* configuration.
*
* Note: New line '\n' is added to the end of the text when printing to stdout.
*/
void wpa_printf(int level, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
/**
* wpa_hexdump - conditional hex dump
* @level: priority level (MSG_*) of the message
* @title: title of for the message
* @buf: data buffer to be dumped
* @len: length of the buf
*
* This function is used to print conditional debugging and error messages. The
* output may be directed to stdout, stderr, and/or syslog based on
* configuration. The contents of buf is printed out has hex dump.
*/
void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
static inline void wpa_hexdump_buf(int level, const char *title,
const struct wpabuf *buf)
{
wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
buf ? wpabuf_len(buf) : 0);
}
/**
* wpa_hexdump_key - conditional hex dump, hide keys
* @level: priority level (MSG_*) of the message
* @title: title of for the message
* @buf: data buffer to be dumped
* @len: length of the buf
*
* This function is used to print conditional debugging and error messages. The
* output may be directed to stdout, stderr, and/or syslog based on
* configuration. The contents of buf is printed out has hex dump. This works
* like wpa_hexdump(), but by default, does not include secret keys (passwords,
* etc.) in debug output.
*/
void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
static inline void wpa_hexdump_buf_key(int level, const char *title,
const struct wpabuf *buf)
{
wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL,
buf ? wpabuf_len(buf) : 0);
}
/**
* wpa_hexdump_ascii - conditional hex dump
* @level: priority level (MSG_*) of the message
* @title: title of for the message
* @buf: data buffer to be dumped
* @len: length of the buf
*
* This function is used to print conditional debugging and error messages. The
* output may be directed to stdout, stderr, and/or syslog based on
* configuration. The contents of buf is printed out has hex dump with both
* the hex numbers and ASCII characters (for printable range) are shown. 16
* bytes per line will be shown.
*/
void wpa_hexdump_ascii(int level, const char *title, const void *buf,
size_t len);
/**
* wpa_hexdump_ascii_key - conditional hex dump, hide keys
* @level: priority level (MSG_*) of the message
* @title: title of for the message
* @buf: data buffer to be dumped
* @len: length of the buf
*
* This function is used to print conditional debugging and error messages. The
* output may be directed to stdout, stderr, and/or syslog based on
* configuration. The contents of buf is printed out has hex dump with both
* the hex numbers and ASCII characters (for printable range) are shown. 16
* bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
* default, does not include secret keys (passwords, etc.) in debug output.
*/
void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
size_t len);
/*
* wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
* binary size. As such, it should be used with debugging messages that are not
* needed in the control interface while wpa_msg() has to be used for anything
* that needs to shown to control interface monitors.
*/
#define wpa_dbg(args...) wpa_msg(args)
#endif /* CONFIG_NO_STDOUT_DEBUG */
#ifdef CONFIG_NO_WPA_MSG
#define wpa_msg(args...) do { } while (0)
#define wpa_msg_ctrl(args...) do { } while (0)
#define wpa_msg_global(args...) do { } while (0)
#define wpa_msg_global_ctrl(args...) do { } while (0)
#define wpa_msg_no_global(args...) do { } while (0)
#define wpa_msg_global_only(args...) do { } while (0)
#define wpa_msg_register_cb(f) do { } while (0)
#define wpa_msg_register_ifname_cb(f) do { } while (0)
#else /* CONFIG_NO_WPA_MSG */
/**
* wpa_msg - Conditional printf for default target and ctrl_iface monitors
* @ctx: Pointer to context data; this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @level: priority level (MSG_*) of the message
* @fmt: printf format string, followed by optional arguments
*
* This function is used to print conditional debugging and error messages. The
* output may be directed to stdout, stderr, and/or syslog based on
* configuration. This function is like wpa_printf(), but it also sends the
* same message to all attached ctrl_iface monitors.
*
* Note: New line '\n' is added to the end of the text when printing to stdout.
*/
void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
/**
* wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
* @ctx: Pointer to context data; this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @level: priority level (MSG_*) of the message
* @fmt: printf format string, followed by optional arguments
*
* This function is used to print conditional debugging and error messages.
* This function is like wpa_msg(), but it sends the output only to the
* attached ctrl_iface monitors. In other words, it can be used for frequent
* events that do not need to be sent to syslog.
*/
void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
PRINTF_FORMAT(3, 4);
/**
* wpa_msg_global - Global printf for ctrl_iface monitors
* @ctx: Pointer to context data; this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @level: priority level (MSG_*) of the message
* @fmt: printf format string, followed by optional arguments
*
* This function is used to print conditional debugging and error messages.
* This function is like wpa_msg(), but it sends the output as a global event,
* i.e., without being specific to an interface. For backwards compatibility,
* an old style event is also delivered on one of the interfaces (the one
* specified by the context data).
*/
void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
PRINTF_FORMAT(3, 4);
/**
* wpa_msg_global_ctrl - Conditional global printf for ctrl_iface monitors
* @ctx: Pointer to context data; this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @level: priority level (MSG_*) of the message
* @fmt: printf format string, followed by optional arguments
*
* This function is used to print conditional debugging and error messages.
* This function is like wpa_msg_global(), but it sends the output only to the
* attached global ctrl_iface monitors. In other words, it can be used for
* frequent events that do not need to be sent to syslog.
*/
void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
PRINTF_FORMAT(3, 4);
/**
* wpa_msg_no_global - Conditional printf for ctrl_iface monitors
* @ctx: Pointer to context data; this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @level: priority level (MSG_*) of the message
* @fmt: printf format string, followed by optional arguments
*
* This function is used to print conditional debugging and error messages.
* This function is like wpa_msg(), but it does not send the output as a global
* event.
*/
void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
PRINTF_FORMAT(3, 4);
/**
* wpa_msg_global_only - Conditional printf for ctrl_iface monitors
* @ctx: Pointer to context data; this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @level: priority level (MSG_*) of the message
* @fmt: printf format string, followed by optional arguments
*
* This function is used to print conditional debugging and error messages.
* This function is like wpa_msg_global(), but it sends the output only as a
* global event.
*/
void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
PRINTF_FORMAT(3, 4);
enum wpa_msg_type {
WPA_MSG_PER_INTERFACE,
WPA_MSG_GLOBAL,
WPA_MSG_NO_GLOBAL,
WPA_MSG_ONLY_GLOBAL,
};
typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
const char *txt, size_t len);
/**
* wpa_msg_register_cb - Register callback function for wpa_msg() messages
* @func: Callback function (%NULL to unregister)
*/
void wpa_msg_register_cb(wpa_msg_cb_func func);
typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
#endif /* CONFIG_NO_WPA_MSG */
#ifdef CONFIG_NO_HOSTAPD_LOGGER
#define hostapd_logger(args...) do { } while (0)
#define hostapd_logger_register_cb(f) do { } while (0)
#else /* CONFIG_NO_HOSTAPD_LOGGER */
void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
const char *fmt, ...) PRINTF_FORMAT(5, 6);
typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr,
unsigned int module, int level,
const char *txt, size_t len);
/**
* hostapd_logger_register_cb - Register callback function for hostapd_logger()
* @func: Callback function (%NULL to unregister)
*/
void hostapd_logger_register_cb(hostapd_logger_cb_func func);
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
#define HOSTAPD_MODULE_IEEE80211 0x00000001
#define HOSTAPD_MODULE_IEEE8021X 0x00000002
#define HOSTAPD_MODULE_RADIUS 0x00000004
#define HOSTAPD_MODULE_WPA 0x00000008
#define HOSTAPD_MODULE_DRIVER 0x00000010
#define HOSTAPD_MODULE_MLME 0x00000040
enum hostapd_logger_level {
HOSTAPD_LEVEL_DEBUG_VERBOSE = 0,
HOSTAPD_LEVEL_DEBUG = 1,
HOSTAPD_LEVEL_INFO = 2,
HOSTAPD_LEVEL_NOTICE = 3,
HOSTAPD_LEVEL_WARNING = 4
};
#ifdef CONFIG_DEBUG_SYSLOG
void wpa_debug_open_syslog(void);
void wpa_debug_close_syslog(void);
#else /* CONFIG_DEBUG_SYSLOG */
static inline void wpa_debug_open_syslog(void)
{
}
static inline void wpa_debug_close_syslog(void)
{
}
#endif /* CONFIG_DEBUG_SYSLOG */
#ifdef CONFIG_DEBUG_LINUX_TRACING
int wpa_debug_open_linux_tracing(void);
void wpa_debug_close_linux_tracing(void);
#else /* CONFIG_DEBUG_LINUX_TRACING */
static inline int wpa_debug_open_linux_tracing(void)
{
return 0;
}
static inline void wpa_debug_close_linux_tracing(void)
{
}
#endif /* CONFIG_DEBUG_LINUX_TRACING */
#ifdef EAPOL_TEST
#define WPA_ASSERT(a) \
do { \
if (!(a)) { \
printf("WPA_ASSERT FAILED '" #a "' " \
"%s %s:%d\n", \
__FUNCTION__, __FILE__, __LINE__); \
exit(1); \
} \
} while (0)
#else
#define WPA_ASSERT(a) do { } while (0)
#endif
const char * debug_level_str(int level);
int str_to_debug_level(const char *s);
#endif /* WPA_DEBUG_H */

@ -0,0 +1,39 @@
/*
* wpa_supplicant ctrl_iface helpers
* Copyright (c) 2010-2011, Atheros Communications, Inc.
* Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_HELPERS_H
#define WPA_HELPERS_H
#include <stddef.h>
int wpa_command(const char *ifname, const char *cmd);
int wpa_command_resp(const char *ifname, const char *cmd,
char *resp, size_t resp_size);
int get_wpa_status(const char *ifname, const char *field, char *obuf,
size_t obuf_size);
struct wpa_ctrl * open_wpa_mon(const char *ifname);
int wait_ip_addr(const char *ifname, int timeout);
int get_wpa_cli_event(struct wpa_ctrl *mon,
const char *event, char *buf, size_t buf_size);
int get_wpa_cli_event2(struct wpa_ctrl *mon,
const char *event, const char *event2,
char *buf, size_t buf_size);
int add_network(const char *ifname);
int set_network(const char *ifname, int id, const char *field,
const char *value);
int set_network_quoted(const char *ifname, int id, const char *field,
const char *value);
int add_cred(const char *ifname);
int set_cred(const char *ifname, int id, const char *field, const char *value);
int set_cred_quoted(const char *ifname, int id, const char *field,
const char *value);
#endif /* WPA_HELPERS_H */

@ -0,0 +1,191 @@
/*
* Dynamic data buffer
* Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPABUF_H
#define WPABUF_H
/* wpabuf::buf is a pointer to external data */
#define WPABUF_FLAG_EXT_DATA BIT(0)
/*
* Internal data structure for wpabuf. Please do not touch this directly from
* elsewhere. This is only defined in header file to allow inline functions
* from this file to access data.
*/
struct wpabuf {
size_t size; /* total size of the allocated buffer */
size_t used; /* length of data in the buffer */
u8 *buf; /* pointer to the head of the buffer */
unsigned int flags;
/* optionally followed by the allocated buffer */
};
int wpabuf_resize(struct wpabuf **buf, size_t add_len);
struct wpabuf * wpabuf_alloc(size_t len);
struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
struct wpabuf * wpabuf_dup(const struct wpabuf *src);
void wpabuf_free(struct wpabuf *buf);
void wpabuf_clear_free(struct wpabuf *buf);
void * wpabuf_put(struct wpabuf *buf, size_t len);
struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
struct wpabuf * wpabuf_parse_bin(const char *buf);
/**
* wpabuf_size - Get the currently allocated size of a wpabuf buffer
* @buf: wpabuf buffer
* Returns: Currently allocated size of the buffer
*/
static inline size_t wpabuf_size(const struct wpabuf *buf)
{
return buf->size;
}
/**
* wpabuf_len - Get the current length of a wpabuf buffer data
* @buf: wpabuf buffer
* Returns: Currently used length of the buffer
*/
static inline size_t wpabuf_len(const struct wpabuf *buf)
{
return buf->used;
}
/**
* wpabuf_tailroom - Get size of available tail room in the end of the buffer
* @buf: wpabuf buffer
* Returns: Tail room (in bytes) of available space in the end of the buffer
*/
static inline size_t wpabuf_tailroom(const struct wpabuf *buf)
{
return buf->size - buf->used;
}
/**
* wpabuf_cmp - Check if two buffers contain the same data
* @a: wpabuf buffer
* @b: wpabuf buffer
* Returns: 0 if the two buffers contain the same data and non-zero otherwise
*/
static inline int wpabuf_cmp(const struct wpabuf *a, const struct wpabuf *b)
{
if (!a && !b)
return 0;
if (a && b && wpabuf_size(a) == wpabuf_size(b))
return os_memcmp(a->buf, b->buf, wpabuf_size(a));
return -1;
}
/**
* wpabuf_head - Get pointer to the head of the buffer data
* @buf: wpabuf buffer
* Returns: Pointer to the head of the buffer data
*/
static inline const void * wpabuf_head(const struct wpabuf *buf)
{
return buf->buf;
}
static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
{
return (const u8 *) wpabuf_head(buf);
}
/**
* wpabuf_mhead - Get modifiable pointer to the head of the buffer data
* @buf: wpabuf buffer
* Returns: Pointer to the head of the buffer data
*/
static inline void * wpabuf_mhead(struct wpabuf *buf)
{
return buf->buf;
}
static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
{
return (u8 *) wpabuf_mhead(buf);
}
static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 1);
*pos = data;
}
static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 2);
WPA_PUT_LE16(pos, data);
}
static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 4);
WPA_PUT_LE32(pos, data);
}
static inline void wpabuf_put_le64(struct wpabuf *buf, u64 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 8);
WPA_PUT_LE64(pos, data);
}
static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 2);
WPA_PUT_BE16(pos, data);
}
static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 3);
WPA_PUT_BE24(pos, data);
}
static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 4);
WPA_PUT_BE32(pos, data);
}
static inline void wpabuf_put_be64(struct wpabuf *buf, u64 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 8);
WPA_PUT_BE64(pos, data);
}
static inline void wpabuf_put_data(struct wpabuf *buf, const void *data,
size_t len)
{
if (data)
os_memcpy(wpabuf_put(buf, len), data, len);
}
static inline void wpabuf_put_buf(struct wpabuf *dst,
const struct wpabuf *src)
{
wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src));
}
static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len)
{
buf->buf = (u8 *) data;
buf->flags = WPABUF_FLAG_EXT_DATA;
buf->size = buf->used = len;
}
static inline void wpabuf_put_str(struct wpabuf *dst, const char *str)
{
wpabuf_put_data(dst, str, os_strlen(str));
}
#endif /* WPABUF_H */

@ -0,0 +1,198 @@
///! daemon <-> gui communication
const std = @import("std");
const json = std.json;
const mem = std.mem;
pub const Message = union(MessageTag) {
ping: void,
pong: void,
poweroff: void,
wifi_connect: WifiConnect,
network_report: NetworkReport,
get_network_report: GetNetworkReport,
pub const WifiConnect = struct {
ssid: []const u8,
password: []const u8,
};
pub const NetworkReport = struct {
ipaddrs: []const []const u8,
wifi_ssid: ?[]const u8, // null indicates disconnected from wifi
wifi_scan_networks: []const []const u8,
};
pub const GetNetworkReport = struct {
scan: bool, // true starts a wifi scan and send NetworkReport only after completion
};
};
pub const MessageTag = enum(u8) {
ping,
pong,
poweroff,
wifi_connect,
network_report,
get_network_report,
};
const Header = extern struct {
tag: MessageTag,
len: usize,
};
/// reads and parses a single message from the input stream reader.
/// callers must deallocate resources with free when done.
pub fn read(allocator: mem.Allocator, reader: anytype) anyerror!Message {
const h = try reader.readStruct(Header);
if (h.len == 0) {
const m = switch (h.tag) {
.ping => Message{ .ping = {} },
.pong => Message{ .pong = {} },
.poweroff => Message{ .poweroff = {} },
else => error.ZeroLenInNonVoidTag,
};
return m;
}
// TODO: limit h.len to some max value
var bytes = try allocator.alloc(u8, h.len);
defer allocator.free(bytes);
try reader.readNoEof(bytes);
const jopt = json.ParseOptions{ .allocator = allocator, .ignore_unknown_fields = true };
var jstream = json.TokenStream.init(bytes);
return switch (h.tag) {
.ping, .pong, .poweroff => unreachable, // void
.wifi_connect => Message{
.wifi_connect = try json.parse(Message.WifiConnect, &jstream, jopt),
},
.network_report => Message{
.network_report = try json.parse(Message.NetworkReport, &jstream, jopt),
},
.get_network_report => Message{
.get_network_report = try json.parse(Message.GetNetworkReport, &jstream, jopt),
},
};
}
/// outputs the message msg using writer.
/// all allocated resources are freed upon return.
pub fn write(allocator: mem.Allocator, writer: anytype, msg: Message) !void {
var header = Header{ .tag = msg, .len = 0 };
switch (msg) {
.ping, .pong, .poweroff => return writer.writeStruct(header),
else => {}, // non-zero payload; continue
}
var data = std.ArrayList(u8).init(allocator);
defer data.deinit();
const jopt = .{ .whitespace = null };
switch (msg) {
.ping, .pong, .poweroff => unreachable,
.wifi_connect => try json.stringify(msg.wifi_connect, jopt, data.writer()),
.network_report => try json.stringify(msg.network_report, jopt, data.writer()),
.get_network_report => try json.stringify(msg.get_network_report, jopt, data.writer()),
}
header.len = data.items.len;
try writer.writeStruct(header);
try writer.writeAll(data.items);
}
pub fn free(allocator: mem.Allocator, m: Message) void {
switch (m) {
.ping, .pong, .poweroff => {},
else => |v| {
json.parseFree(@TypeOf(v), v, .{ .allocator = allocator });
},
}
}
test "read" {
const t = std.testing;
var data = std.ArrayList(u8).init(t.allocator);
defer data.deinit();
const msg = Message{ .wifi_connect = .{ .ssid = "hello", .password = "world" } };
try json.stringify(msg.wifi_connect, .{}, data.writer());
var buf = std.ArrayList(u8).init(t.allocator);
defer buf.deinit();
try buf.writer().writeStruct(Header{ .tag = msg, .len = data.items.len });
try buf.writer().writeAll(data.items);
var bs = std.io.fixedBufferStream(buf.items);
const res = try read(t.allocator, bs.reader());
defer free(t.allocator, res);
try t.expectEqualStrings(msg.wifi_connect.ssid, res.wifi_connect.ssid);
try t.expectEqualStrings(msg.wifi_connect.password, res.wifi_connect.password);
}
test "write" {
const t = std.testing;
var buf = std.ArrayList(u8).init(t.allocator);
defer buf.deinit();
const msg = Message{ .wifi_connect = .{ .ssid = "wlan", .password = "secret" } };
try write(t.allocator, buf.writer(), msg);
const payload = "{\"ssid\":\"wlan\",\"password\":\"secret\"}";
var js = std.ArrayList(u8).init(t.allocator);
defer js.deinit();
try js.writer().writeStruct(Header{ .tag = msg, .len = payload.len });
try js.appendSlice(payload);
try t.expectEqualSlices(u8, js.items, buf.items);
}
test "write/read void tags" {
const t = std.testing;
var buf = std.ArrayList(u8).init(t.allocator);
defer buf.deinit();
const msg = [_]Message{
Message.ping,
Message.pong,
Message.poweroff,
};
for (msg) |m| {
buf.clearAndFree();
try write(t.allocator, buf.writer(), m);
var bs = std.io.fixedBufferStream(buf.items);
const res = try read(t.allocator, bs.reader());
free(t.allocator, res); // noop
try t.expectEqual(m, res);
}
}
test "msg sequence" {
const t = std.testing;
var buf = std.ArrayList(u8).init(t.allocator);
defer buf.deinit();
const msgs = [_]Message{
Message.ping,
Message{ .wifi_connect = .{ .ssid = "wlan", .password = "secret" } },
Message.pong,
Message{ .network_report = .{
.ipaddrs = &.{},
.wifi_ssid = null,
.wifi_scan_networks = &.{ "foo", "bar" },
} },
};
for (msgs) |m| {
try write(t.allocator, buf.writer(), m);
}
var bs = std.io.fixedBufferStream(buf.items);
for (msgs) |m| {
const res = try read(t.allocator, bs.reader());
defer free(t.allocator, res);
try t.expectEqual(@as(MessageTag, m), @as(MessageTag, res));
}
}

@ -1,11 +0,0 @@
const std = @import("std");
pub fn main() anyerror!void {
// Note that info level log messages are by default printed only in Debug
// and ReleaseSafe build modes.
std.log.info("All your codebase are belong to us.", .{});
}
test "basic test" {
try std.testing.expectEqual(10, 3 + 7);
}

@ -0,0 +1,225 @@
const std = @import("std");
const os = std.os;
const sys = os.system;
const time = std.time;
const Address = std.net.Address;
const nif = @import("nif");
const comm = @import("comm.zig");
const Daemon = @import("nd/Daemon.zig");
const logger = std.log.scoped(.nd);
const stderr = std.io.getStdErr().writer();
/// prints usage help text to stderr.
fn usage(prog: []const u8) !void {
try stderr.print(
\\usage: {s} -gui path/to/ngui -gui-user username -wpa path
\\
\\nd is a short for nakamochi daemon.
\\the daemon executes ngui as a child process and runs until
\\TERM or INT signal is received.
\\
\\nd logs messages to stderr.
\\
, .{prog});
}
/// prints messages in the same way std.fmt.format does and exits the process
/// with a non-zero code.
fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
stderr.print(fmt, args) catch {};
if (fmt[fmt.len - 1] != '\n') {
stderr.writeByte('\n') catch {};
}
std.process.exit(1);
}
/// nd program args. see usage.
const NdArgs = struct {
gui: ?[:0]const u8 = null, // = "ngui",
gui_user: ?[:0]const u8 = null, // u8 = "uiuser",
wpa: ?[:0]const u8 = null, // = "/var/run/wpa_supplicant/wlan0",
fn deinit(self: @This(), allocator: std.mem.Allocator) void {
if (self.gui) |p| allocator.free(p);
if (self.gui_user) |p| allocator.free(p);
if (self.wpa) |p| allocator.free(p);
}
};
/// parses and validates program args.
fn parseArgs(gpa: std.mem.Allocator) !NdArgs {
var flags: NdArgs = .{};
var args = try std.process.ArgIterator.initWithAllocator(gpa);
defer args.deinit();
const prog = args.next() orelse return error.NoProgName;
var lastarg: enum {
none,
gui,
gui_user,
wpa,
} = .none;
while (args.next()) |a| {
switch (lastarg) {
.gui => {
flags.gui = try gpa.dupeZ(u8, a);
lastarg = .none;
continue;
},
.gui_user => {
flags.gui_user = try gpa.dupeZ(u8, a);
lastarg = .none;
continue;
},
.wpa => {
flags.wpa = try gpa.dupeZ(u8, a);
lastarg = .none;
continue;
},
.none => {},
}
if (std.mem.eql(u8, a, "-h") or std.mem.eql(u8, a, "-help") or std.mem.eql(u8, a, "--help")) {
usage(prog) catch {};
std.process.exit(1);
} else if (std.mem.eql(u8, a, "-gui")) {
lastarg = .gui;
} else if (std.mem.eql(u8, a, "-gui-user")) {
lastarg = .gui_user;
} else if (std.mem.eql(u8, a, "-wpa")) {
lastarg = .wpa;
} else {
fatal("unknown arg name {s}", .{a});
}
}
if (lastarg != .none) {
fatal("invalid arg: {s} requires a value", .{@tagName(lastarg)});
}
if (flags.gui == null) fatal("missing -gui arg", .{});
if (flags.gui_user == null) fatal("missing -gui-user arg", .{});
if (flags.wpa == null) fatal("missing -wpa arg", .{});
return flags;
}
/// quit signals nd to exit.
/// TODO: thread-safety?
var quit = false;
fn sighandler(sig: c_int) callconv(.C) void {
logger.info("got signal {}; exiting...\n", .{sig});
quit = true;
}
pub fn main() !void {
// main heap allocator used throughout the lifetime of nd
var gpa_state = std.heap.GeneralPurposeAllocator(.{}){};
defer if (gpa_state.deinit()) {
logger.err("memory leaks detected", .{});
};
const gpa = gpa_state.allocator();
// parse program args first thing and fail fast if invalid
const args = try parseArgs(gpa);
defer args.deinit(gpa);
// start ngui, unless -nogui mode
var ngui = std.ChildProcess.init(&.{args.gui.?}, gpa);
ngui.stdin_behavior = .Pipe;
ngui.stdout_behavior = .Pipe;
ngui.stderr_behavior = .Inherit;
// fix zig std: child_process.zig:125:33: error: container 'std.os' has no member called 'getUserInfo'
//ngui.setUserName(args.gui_user) catch |err| {
// fatal("unable to set gui username to {s}: {s}", .{args.gui_user.?, err});
//};
// TODO: the following fails with "cannot open framebuffer device: Permission denied"
// but works with "doas -u uiuser ngui"
// ftr, zig uses setreuid and setregid
//const uiuser = std.process.getUserInfo(args.gui_user.?) catch |err| {
// fatal("unable to set gui username to {s}: {any}", .{ args.gui_user.?, err });
//};
//ngui.uid = uiuser.uid;
//ngui.gid = uiuser.gid;
// ngui.env_map = ...
ngui.spawn() catch |err| {
fatal("unable to start ngui: {any}", .{err});
};
// TODO: thread-safety, esp. uiwriter
const uireader = ngui.stdout.?.reader();
const uiwriter = ngui.stdin.?.writer();
// graceful shutdown; see sigaction(2)
const sa = os.Sigaction{
.handler = .{ .handler = sighandler },
.mask = os.empty_sigset,
.flags = 0,
};
try os.sigaction(os.SIG.INT, &sa, null);
//TODO: try os.sigaction(os.SIG.TERM, &sa, null);
// start network monitor
var ctrl = try nif.wpa.Control.open(args.wpa.?);
defer ctrl.close() catch {};
var nd: Daemon = .{
.allocator = gpa,
.uiwriter = uiwriter,
.wpa_ctrl = ctrl,
};
try nd.start();
// send the UI network report right away, without scanning wifi
nd.reportNetworkStatus(.{ .scan = false });
// comm with ui loop; run until exit is requested
var poweroff = false;
while (!quit) {
time.sleep(100 * time.ns_per_ms);
// note: uireader.read is blocking
// TODO: handle error.EndOfStream - ngui exited
const msg = comm.read(gpa, uireader) catch |err| {
logger.err("comm.read: {any}", .{err});
continue;
};
logger.debug("got ui msg tagged {s}", .{@tagName(msg)});
switch (msg) {
.pong => {},
.poweroff => {
logger.info("poweroff requested; terminating", .{});
quit = true;
poweroff = true;
},
.get_network_report => |req| {
nd.reportNetworkStatus(.{ .scan = req.scan });
},
.wifi_connect => |req| {
nd.startConnectWifi(req.ssid, req.password) catch |err| {
logger.err("startConnectWifi: {any}", .{err});
};
},
else => logger.warn("unhandled msg tag {s}", .{@tagName(msg)}),
}
comm.free(gpa, msg);
}
// shutdown
_ = ngui.kill() catch |err| logger.err("ngui.kill: {any}", .{err});
nd.stop();
if (poweroff) {
svShutdown(gpa);
var off = std.ChildProcess.init(&.{"poweroff"}, gpa);
_ = try off.spawnAndWait();
}
}
/// shut down important services manually.
/// TODO: make this OS-agnostic
fn svShutdown(allocator: std.mem.Allocator) void {
// sv waits 7sec by default but bitcoind needs more
// http://smarden.org/runit/
var stop_lnd = std.ChildProcess.init(&.{"sv", "-w", "25", "stop", "lnd"}, allocator);
_ = stop_lnd.spawnAndWait() catch |err| logger.err("stop lnd: {any}", .{err});
var stop_btc = std.ChildProcess.init(&.{"sv", "-w", "30", "stop", "bitcoind"}, allocator);
_ = stop_btc.spawnAndWait() catch |err| logger.err("stop bitcoind: {any}", .{err});
}

@ -0,0 +1,401 @@
///! daemon watches network status and communicates updates to the gui
///! using uiwriter
const std = @import("std");
const mem = std.mem;
const time = std.time;
const nif = @import("nif");
const comm = @import("../comm.zig");
//const ioq = @import("../ioq.zig");
const logger = std.log.scoped(.netmon);
// pub fields
allocator: mem.Allocator,
uiwriter: std.fs.File.Writer, // ngui stdin
wpa_ctrl: nif.wpa.Control, // guarded by mu once start'ed
// private fields
mu: std.Thread.Mutex = .{},
quit: bool = false, // tells daemon to quit
main_thread: ?std.Thread = null, // non-nill if started
want_report: bool = false,
want_wifi_scan: bool = false,
wifi_scan_in_progress: bool = false,
report_ready: bool = true, // no need to scan for an immediate report
wpa_save_config_on_connected: bool = false,
const Daemon = @This();
pub fn start(self: *Daemon) !void {
// TODO: return error if already started
self.main_thread = try std.Thread.spawn(.{}, mainThreadLoop, .{self});
}
pub fn stop(self: *Daemon) void {
self.mu.lock();
self.quit = true;
self.mu.unlock();
if (self.main_thread) |th| {
th.join();
}
}
/// main thread entry point.
fn mainThreadLoop(self: *Daemon) !void {
try self.wpa_ctrl.attach();
defer self.wpa_ctrl.detach() catch |err| logger.err("wpa_ctrl.detach failed on exit: {any}", .{err});
while (true) {
time.sleep(1 * time.ns_per_s);
self.mainThreadLoopCycle();
self.mu.lock();
const do_quit = self.quit;
self.mu.unlock();
if (do_quit) {
break;
}
}
}
/// run one cycle of the main thread loop iteration.
/// holds self.mu for the whole duration.
fn mainThreadLoopCycle(self: *Daemon) void {
self.mu.lock();
defer self.mu.unlock();
self.readWPACtrlMsg() catch |err| logger.err("readWPACtrlMsg: {any}", .{err});
if (self.want_wifi_scan) {
if (self.startWifiScan()) {
self.want_wifi_scan = false;
} else |err| {
logger.err("startWifiScan: {any}", .{err});
}
}
if (self.want_report and self.report_ready) {
if (self.sendNetworkReport()) {
self.want_report = false;
} else |err| {
logger.err("sendNetworkReport: {any}", .{err});
}
}
}
/// caller must hold self.mu.
fn startWifiScan(self: *Daemon) !void {
try self.wpa_ctrl.scan();
self.wifi_scan_in_progress = true;
self.report_ready = false;
}
/// invoked when CTRL-EVENT-SCAN-RESULTS event is seen.
/// caller must hold self.mu.
fn wifiScanComplete(self: *Daemon) void {
self.wifi_scan_in_progress = false;
self.report_ready = true;
}
/// invoked when CTRL-EVENT-CONNECTED event is seen.
/// caller must hold self.mu.
fn wifiConnected(self: *Daemon) void {
if (self.wpa_save_config_on_connected) {
// fails if update_config=0 in wpa_supplicant.conf
const ok_saved = self.wpa_ctrl.saveConfig();
if (ok_saved) {
self.wpa_save_config_on_connected = false;
} else |err| {
logger.err("wifiConnected: {any}", .{err});
}
}
// always send a network report when connected
self.want_report = true;
}
/// invoked when CTRL-EVENT-SSID-TEMP-DISABLED event with authentication failures is seen.
/// caller must hold self.mu.
fn wifiInvalidKey(self: *Daemon) void {
self.wpa_save_config_on_connected = false;
self.want_report = true;
self.report_ready = true;
}
pub const ReportNetworkStatusOpt = struct {
scan: bool,
};
pub fn reportNetworkStatus(self: *Daemon, opt: ReportNetworkStatusOpt) void {
self.mu.lock();
defer self.mu.unlock();
self.want_report = true;
self.want_wifi_scan = opt.scan and !self.wifi_scan_in_progress;
if (self.want_wifi_scan and self.report_ready) {
self.report_ready = false;
}
}
pub fn startConnectWifi(self: *Daemon, ssid: []const u8, password: []const u8) !void {
if (ssid.len == 0) {
return error.ConnectWifiEmptySSID;
}
const ssid_copy = try self.allocator.dupe(u8, ssid);
const pwd_copy = try self.allocator.dupe(u8, password);
const th = try std.Thread.spawn(.{}, connectWifiThread, .{ self, ssid_copy, pwd_copy });
th.detach();
}
fn connectWifiThread(self: *Daemon, ssid: []const u8, password: []const u8) void {
defer {
self.allocator.free(ssid);
self.allocator.free(password);
}
// https://hostap.epitest.fi/wpa_supplicant/devel/ctrl_iface_page.html
// https://wiki.archlinux.org/title/WPA_supplicant
// unfortunately, this prevents main thread from looping until released.
// but the following commands and expected to be pretty quick.
self.mu.lock();
defer self.mu.unlock();
const id = self.addWifiNetwork(ssid, password) catch |err| {
logger.err("addWifiNetwork: {any}; exiting", .{err});
return;
};
// SELECT_NETWORK <id> - this disables others
// ENABLE_NETWORK <id>
self.wpa_ctrl.selectNetwork(id) catch |err| {
logger.err("selectNetwork({d}): {any}", .{ id, err });
// non-critical; can try to continue
};
self.wpa_ctrl.enableNetwork(id) catch |err| {
logger.err("enableNetwork({d}): {any}; cannot continue", .{ id, err });
self.wpa_ctrl.removeNetwork(id) catch {};
return;
};
// wait for CTRL-EVENT-CONNECTED, SAVE_CONFIG and send network report.
self.wpa_save_config_on_connected = true;
}
/// adds a new network and configures its parameters.
/// caller must hold self.mu.
fn addWifiNetwork(self: *Daemon, ssid: []const u8, password: []const u8) !u32 {
// - ADD_NETWORK -> get id and set parameters
// - SET_NETWORK <id> ssid "ssid"
// - if password:
// SET_NETWORK <id> psk "password"
// else:
// SET_NETWORK <id> key_mgmt NONE
const newWifiId = try self.wpa_ctrl.addNetwork();
errdefer self.wpa_ctrl.removeNetwork(newWifiId) catch |err| {
logger.err("addWifiNetwork cleanup: {any}", .{err});
};
var buf: [128:0]u8 = undefined;
// TODO: convert ssid to hex string, to support special characters
const ssidZ = try std.fmt.bufPrintZ(&buf, "\"{s}\"", .{ssid});
try self.wpa_ctrl.setNetworkParam(newWifiId, "ssid", ssidZ);
if (password.len > 0) {
// TODO: switch to wpa_passphrase
const v = try std.fmt.bufPrintZ(&buf, "\"{s}\"", .{password});
try self.wpa_ctrl.setNetworkParam(newWifiId, "psk", v);
} else {
try self.wpa_ctrl.setNetworkParam(newWifiId, "key_mgmt", "NONE");
}
// - LIST_NETWORKS: network id / ssid / bssid / flags
// - for each matching ssid unless it's newly created: REMOVE_NETWORK <id>
if (self.queryWifiNetworksList(.{ .ssid = ssid })) |res| {
defer self.allocator.free(res);
for (res) |id| {
if (id == newWifiId) {
continue;
}
self.wpa_ctrl.removeNetwork(id) catch |err| {
logger.err("wpa_ctrl.removeNetwork({}): {any}", .{ id, err });
};
}
} else |err| {
logger.err("queryWifiNetworksList({s}): {any}; won't remove existing, if any", .{ ssid, err });
}
return newWifiId;
}
/// caller must hold self.mu.
fn readWPACtrlMsg(self: *Daemon) !void {
var buf: [512:0]u8 = undefined;
while (try self.wpa_ctrl.pending()) {
const m = try self.wpa_ctrl.receive(&buf);
logger.debug("wpa_ctrl msg: {s}", .{m});
if (mem.indexOf(u8, m, "CTRL-EVENT-SCAN-RESULTS") != null) {
self.wifiScanComplete();
}
if (mem.indexOf(u8, m, "CTRL-EVENT-CONNECTED") != null) {
self.wifiConnected();
}
if (mem.indexOf(u8, m, "CTRL-EVENT-SSID-TEMP-DISABLED") != null) {
// TODO: what about CTRL-EVENT-DISCONNECTED bssid=xx:xx:xx:xx:xx:xx reason=15
// CTRL-EVENT-SSID-TEMP-DISABLED id=1 ssid="<ssid>" auth_failures=3 duration=49 reason=WRONG_KEY
var it = mem.tokenize(u8, m, " ");
while (it.next()) |kv_str| {
var kv = mem.split(u8, kv_str, "=");
if (mem.eql(u8, kv.first(), "auth_failures")) {
const v = kv.next();
if (v != null and !mem.eql(u8, v.?, "0")) {
self.wifiInvalidKey();
break;
}
}
}
}
// TODO: CTRL-EVENT-DISCONNECTED
}
}
/// report network status to ngui.
/// caller must hold self.mu.
fn sendNetworkReport(self: *Daemon) !void {
var report = comm.Message.NetworkReport{
.ipaddrs = undefined,
.wifi_ssid = null,
.wifi_scan_networks = undefined,
};
// fetch all public IP addresses using getifaddrs
const pubaddr = try nif.pubAddresses(self.allocator, null);
defer self.allocator.free(pubaddr);
//var addrs = std.ArrayList([]).init(t.allocator);
var ipaddrs = try self.allocator.alloc([]const u8, pubaddr.len);
for (pubaddr) |a, i| {
ipaddrs[i] = try std.fmt.allocPrint(self.allocator, "{s}", .{a});
}
defer {
for (ipaddrs) |a| self.allocator.free(a);
self.allocator.free(ipaddrs);
}
report.ipaddrs = ipaddrs;
// get currently connected SSID, if any, from WPA ctrl
const ssid = self.queryWifiSSID() catch |err| blk: {
logger.err("queryWifiSsid: {any}", .{err});
break :blk null;
};
defer if (ssid) |v| self.allocator.free(v);
report.wifi_ssid = ssid;
// fetch available wifi networks from scan results using WPA ctrl
var wifi_networks: ?StringList = if (self.queryWifiScanResults()) |v| v else |err| blk: {
logger.err("queryWifiScanResults: {any}", .{err});
break :blk null;
};
defer if (wifi_networks) |*list| list.deinit();
if (wifi_networks) |list| {
report.wifi_scan_networks = list.items();
}
// report everything back to ngui
return comm.write(self.allocator, self.uiwriter, comm.Message{ .network_report = report });
}
fn queryWifiSSID(self: *Daemon) !?[]const u8 {
var buf: [512:0]u8 = undefined;
const resp = try self.wpa_ctrl.request("STATUS", &buf, null);
const ssid = "ssid=";
var it = mem.tokenize(u8, resp, "\n");
while (it.next()) |line| {
if (mem.startsWith(u8, line, ssid)) {
// TODO: check line.len vs ssid.len
const v = try self.allocator.dupe(u8, line[ssid.len..]);
return v;
}
}
return null;
}
/// callers must free with StringList.deinit.
fn queryWifiScanResults(self: *Daemon) !StringList {
var buf: [8192:0]u8 = undefined; // TODO: what if isn't enough?
// first line is banner: "bssid / frequency / signal level / flags / ssid"
const resp = try self.wpa_ctrl.request("SCAN_RESULTS", &buf, null);
var it = mem.tokenize(u8, resp, "\n");
if (it.next() == null) {
return error.MissingWifiScanHeader;
}
var seen = std.BufSet.init(self.allocator);
defer seen.deinit();
var list = StringList.init(self.allocator);
errdefer list.deinit();
while (it.next()) |line| {
// TODO: wpactrl's text protocol won't work for names with control characters
if (mem.lastIndexOfScalar(u8, line, '\t')) |i| {
const s = mem.trim(u8, line[i..], "\t\n");
if (s.len == 0 or seen.contains(s)) {
continue;
}
try seen.insert(s);
try list.append(s);
}
}
return list;
}
const WifiNetworksListFilter = struct {
ssid: ?[]const u8, // ignore networks whose ssid doesn't match
};
/// caller must release results with allocator.free.
fn queryWifiNetworksList(self: *Daemon, filter: WifiNetworksListFilter) ![]u32 {
var buf: [8192:0]u8 = undefined; // TODO: is this enough?
// first line is banner: "network id / ssid / bssid / flags"
const resp = try self.wpa_ctrl.request("LIST_NETWORKS", &buf, null);
var it = mem.tokenize(u8, resp, "\n");
if (it.next() == null) {
return error.MissingWifiNetworksListHeader;
}
var list = std.ArrayList(u32).init(self.allocator);
while (it.next()) |line| {
var cols = mem.tokenize(u8, line, "\t");
const id_str = cols.next() orelse continue; // bad line format?
const ssid = cols.next() orelse continue; // bad line format?
const id = std.fmt.parseUnsigned(u32, id_str, 10) catch continue; // skip bad line
if (filter.ssid != null and !mem.eql(u8, filter.ssid.?, ssid)) {
continue;
}
list.append(id) catch {}; // grab anything we can
}
return list.toOwnedSlice();
}
// TODO: turns this into a UniqStringList backed by StringArrayHashMap; also see std.BufSet
const StringList = struct {
l: std.ArrayList([]const u8),
allocator: mem.Allocator,
const Self = @This();
pub fn init(allocator: mem.Allocator) Self {
return Self{
.l = std.ArrayList([]const u8).init(allocator),
.allocator = allocator,
};
}
pub fn deinit(self: *Self) void {
for (self.l.items) |a| {
self.allocator.free(a);
}
self.l.deinit();
}
pub fn append(self: *Self, s: []const u8) !void {
const item = try self.allocator.dupe(u8, s);
errdefer self.allocator.free(item);
try self.l.append(item);
}
pub fn items(self: Self) []const []const u8 {
return self.l.items;
}
};

@ -0,0 +1,225 @@
const std = @import("std");
const mem = std.mem;
const time = std.time;
const Thread = std.Thread;
const comm = @import("comm.zig");
const types = @import("types.zig");
const symbol = @import("ui/symbol.zig");
/// SIGPIPE is triggered when a process attempts to write to a broken pipe.
/// by default, SIGPIPE terminates the process without invoking a panic handler.
/// this declaration makes such writes result in EPIPE (error.BrokenPipe) to let
/// the program can handle it.
pub const keep_sigpipe = true;
const stdin = std.io.getStdIn().reader();
const stdout = std.io.getStdOut().writer();
const logger = std.log.scoped(.ngui);
const lvgl_logger = std.log.scoped(.lvgl); // logs LV_LOG_xxx messages
extern "c" fn lv_timer_handler() u32;
extern "c" fn lv_log_register_print_cb(fn (msg: [*:0]const u8) callconv(.C) void) void;
const LvTimer = opaque {};
//const LvTimerCallback = *const fn (timer: *LvTimer) callconv(.C) void; // stage2
const LvTimerCallback = fn (timer: *LvTimer) callconv(.C) void;
extern "c" fn lv_timer_create(callback: LvTimerCallback, period_ms: u32, userdata: ?*anyopaque) *LvTimer;
extern "c" fn lv_timer_del(timer: *LvTimer) void;
extern "c" fn lv_timer_set_repeat_count(timer: *LvTimer, n: i32) void;
extern "c" fn ui_init() c_int;
extern "c" fn ui_update_network_status(text: [*:0]const u8, wifi_list: ?[*:0]const u8) void;
/// global heap allocator used throughout the gui program.
/// TODO: thread-safety?
var gpa: mem.Allocator = undefined;
/// the mutex must be held before any call reaching into lv_xxx functions.
/// all nm_xxx functions assume it is the case since they are invoked from lvgl c code.
var ui_mutex: Thread.Mutex = .{};
/// the program runs until quit is true.
var quit: bool = false;
/// a monotonic clock for reporting elapsed ticks to LVGL.
/// the timer runs throughout the whole duration of the UI program.
var tick_timer: types.Timer = undefined;
/// reports elapsed time in ms since the program start, overflowing at u32 max.
/// it is defined as LVGL custom tick.
export fn nm_get_curr_tick() u32 {
const ms = tick_timer.read() / time.ns_per_ms;
const over = ms >> 32;
if (over > 0) {
return @truncate(u32, over); // LVGL deals with overflow correctly
}
return @truncate(u32, ms);
}
export fn nm_lvgl_log(msg: [*:0]const u8) void {
const s = mem.span(msg);
lvgl_logger.debug("{s}", .{mem.trimRight(u8, s, "\n")});
}
/// initiate system shutdown.
export fn nm_sys_shutdown() void {
logger.info("initiating system shutdown", .{});
const msg = comm.Message.poweroff;
comm.write(gpa, stdout, msg) catch |err| logger.err("nm_sys_shutdown: {any}", .{err});
quit = true;
}
export fn nm_tab_settings_active() void {
logger.info("starting wifi scan", .{});
const msg = comm.Message{ .get_network_report = .{ .scan = true } };
comm.write(gpa, stdout, msg) catch |err| logger.err("nm_tab_settings_active: {any}", .{err});
}
export fn nm_request_network_status(t: *LvTimer) void {
lv_timer_del(t);
const msg: comm.Message = .{ .get_network_report = .{ .scan = false } };
comm.write(gpa, stdout, msg) catch |err| logger.err("nm_request_network_status: {any}", .{err});
}
/// ssid and password args must not outlive this function.
export fn nm_wifi_start_connect(ssid: [*:0]const u8, password: [*:0]const u8) void {
const msg = comm.Message{ .wifi_connect = .{
.ssid = mem.span(ssid),
.password = mem.span(password),
} };
logger.info("connect to wifi [{s}]", .{msg.wifi_connect.ssid});
comm.write(gpa, stdout, msg) catch |err| {
logger.err("comm.write: {any}", .{err});
};
}
fn updateNetworkStatus(report: comm.Message.NetworkReport) !void {
ui_mutex.lock();
defer ui_mutex.unlock();
var wifi_list: ?[:0]const u8 = null;
var wifi_list_ptr: ?[*:0]const u8 = null;
if (report.wifi_scan_networks.len > 0) {
wifi_list = try mem.joinZ(gpa, "\n", report.wifi_scan_networks);
wifi_list_ptr = wifi_list.?.ptr;
}
defer if (wifi_list) |v| gpa.free(v);
var status = std.ArrayList(u8).init(gpa); // free'd as owned slice below
const w = status.writer();
if (report.wifi_ssid) |ssid| {
try w.writeAll(symbol.Ok);
try w.print(" connected to {s}", .{ssid});
} else {
try w.writeAll(symbol.Warning);
try w.print(" disconnected", .{});
}
if (report.ipaddrs.len > 0) {
const ipaddrs = try mem.join(gpa, "\n", report.ipaddrs);
defer gpa.free(ipaddrs);
try w.print("\n\nIP addresses:\n{s}", .{ipaddrs});
}
const text = try status.toOwnedSliceSentinel(0);
defer gpa.free(text);
ui_update_network_status(text, wifi_list_ptr);
// request network status again if we're connected but IP addr list is empty.
// can happen with a fresh connection while dhcp is still in progress.
if (report.wifi_ssid != null and report.ipaddrs.len == 0) {
// TODO: sometimes this is too fast, not all ip addrs are avail (ipv4 vs ipv6)
var t = lv_timer_create(nm_request_network_status, 1000, null);
lv_timer_set_repeat_count(t, 1);
}
}
/// reads messages from nd; loops indefinitely until program exit
fn commThread() void {
while (true) {
commThreadLoopCycle() catch |err| logger.err("commThreadLoopCycle: {any}", .{err});
ui_mutex.lock();
const do_quit = quit;
ui_mutex.unlock();
if (do_quit) {
return;
}
}
}
fn commThreadLoopCycle() !void {
const msg = comm.read(gpa, stdin) catch |err| {
if (err == error.EndOfStream) {
// pointless to continue running if comms is broken
ui_mutex.lock();
quit = true;
ui_mutex.unlock();
}
return err;
};
defer comm.free(gpa, msg);
logger.debug("got msg tagged {s}", .{@tagName(msg)});
switch (msg) {
.ping => try comm.write(gpa, stdout, comm.Message.pong),
.network_report => |report| {
updateNetworkStatus(report) catch |err| logger.err("updateNetworkStatus: {any}", .{err});
},
else => logger.warn("unhandled msg tag {s}", .{@tagName(msg)}),
}
}
/// nakamochi UI program entry point.
pub fn main() anyerror!void {
// ensure timer is available on this platform before doing anything else;
// the UI is unusable otherwise.
tick_timer = try time.Timer.start();
// main heap allocator used through the lifetime of nd
var gpa_state = std.heap.GeneralPurposeAllocator(.{}){};
defer if (gpa_state.deinit()) {
logger.err("memory leaks detected", .{});
};
gpa = gpa_state.allocator();
// info level log messages are by default printed only in Debug and ReleaseSafe build modes.
lv_log_register_print_cb(nm_lvgl_log);
const c_res = ui_init();
if (c_res != 0) {
logger.err("ui_init failed with code {}", .{c_res});
std.process.exit(1);
}
const th = try Thread.spawn(.{}, commThread, .{});
th.detach();
// TODO: handle sigterm
while (true) {
ui_mutex.lock();
var till_next_ms = lv_timer_handler();
const do_quit = quit;
ui_mutex.unlock();
if (do_quit) {
return;
}
// sleep at least 1ms
time.sleep(@maximum(1, till_next_ms) * time.ns_per_ms);
}
}
test "tick" {
const t = std.testing;
tick_timer = types.Timer{ .value = 0 };
try t.expectEqual(@as(u32, 0), nm_get_curr_tick());
tick_timer.value = 1 * time.ns_per_ms;
try t.expectEqual(@as(u32, 1), nm_get_curr_tick());
tick_timer.value = 13 * time.ns_per_ms;
try t.expectEqual(@as(u32, 13), nm_get_curr_tick());
tick_timer.value = @as(u64, ~@as(u32, 0)) * time.ns_per_ms;
try t.expectEqual(@as(u32, std.math.maxInt(u32)), nm_get_curr_tick());
tick_timer.value = (1 << 32) * time.ns_per_ms;
try t.expectEqual(@as(u32, 1), nm_get_curr_tick());
}

@ -0,0 +1,16 @@
const std = @import("std");
export fn wifi_ssid_add_network(name: [*:0]const u8) void {
_ = name;
}
export fn lv_timer_del(timer: *opaque{}) void {
_ = timer;
}
test {
std.testing.refAllDecls(@This());
_ = @import("comm.zig");
_ = @import("ngui.zig");
}

@ -0,0 +1,14 @@
const std = @import("std");
const builtin = @import("builtin");
pub const Timer = if (builtin.is_test) TestTimer else std.time.Timer;
/// TestTimer always reports the same fixed value.
pub const TestTimer = if (!builtin.is_test) @compileError("TestTimer is for tests only") else struct {
value: u64,
pub fn read(self: *Timer) u64 {
return self.value;
}
};

@ -0,0 +1,56 @@
/**
* framebuffer display + evdev touchpad drivers init
*/
#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/evdev.h"
#define DISP_BUF_SIZE NM_DISP_HOR * NM_DISP_VER / 10
lv_disp_t* drv_init(void)
{
fbdev_init();
static lv_disp_draw_buf_t buf;
static lv_color_t cb[DISP_BUF_SIZE];
lv_disp_draw_buf_init(&buf, cb, NULL, DISP_BUF_SIZE);
uint32_t hor, vert;
fbdev_get_sizes(&hor, &vert, NULL);
if (hor != NM_DISP_HOR || vert != NM_DISP_VER) {
LV_LOG_WARN("framebuffer display mismatch; expected %dx%d", NM_DISP_HOR, NM_DISP_VER);
}
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &buf;
disp_drv.hor_res = NM_DISP_HOR;
disp_drv.ver_res = NM_DISP_VER;
disp_drv.antialiasing = 1;
disp_drv.flush_cb = fbdev_flush;
lv_disp_t* disp = lv_disp_drv_register(&disp_drv);
if (disp == NULL) {
return NULL;
}
/* keypad input devices default group;
* future-proof: don't have any atm */
lv_group_t* g = lv_group_create();
if (g == NULL) {
return NULL;
}
lv_group_set_default(g);
evdev_init();
static lv_indev_drv_t touchpad_drv;
lv_indev_drv_init(&touchpad_drv);
touchpad_drv.type = LV_INDEV_TYPE_POINTER;
touchpad_drv.read_cb = evdev_read;
lv_indev_t* touchpad = lv_indev_drv_register(&touchpad_drv);
if (touchpad == NULL) {
/* TODO: or continue without the touchpad? */
return NULL;
}
return disp;
}

@ -0,0 +1,73 @@
/**
* SDL2 drivers init for display, keyboard and mouse
*/
#include "lvgl/lvgl.h"
#include "lvgl/src/misc/lv_log.h"
#include "lv_drivers/sdl/sdl.h"
#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/
#include SDL_INCLUDE_PATH
lv_disp_t* drv_init(void)
{
sdl_init();
SDL_DisplayMode dm;
int dm_err = SDL_GetDesktopDisplayMode(0, &dm);
if (dm_err != 0) {
LV_LOG_WARN("SDL_GetDesktopDisplayMode: %i", dm_err);
} else {
unsigned char bpp = SDL_BITSPERPIXEL(dm.format);
LV_LOG_INFO("%ix%i %dbpp %s", dm.w, dm.h, bpp, SDL_GetPixelFormatName(dm.format));
if (dm.w != NM_DISP_HOR || dm.h != NM_DISP_VER || bpp != LV_COLOR_DEPTH) {
LV_LOG_WARN("SDL display mismatch; expected %dx%d %dbpp", NM_DISP_HOR, NM_DISP_VER, LV_COLOR_DEPTH);
}
}
static lv_disp_draw_buf_t buf;
static lv_color_t cb1[NM_DISP_HOR * 100];
static lv_color_t cb2[NM_DISP_HOR * 100];
lv_disp_draw_buf_init(&buf, cb1, cb2, NM_DISP_HOR * 100);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &buf;
disp_drv.flush_cb = sdl_display_flush;
disp_drv.hor_res = NM_DISP_HOR;
disp_drv.ver_res = NM_DISP_VER;
disp_drv.antialiasing = 1;
lv_disp_t* disp = lv_disp_drv_register(&disp_drv);
if (disp == NULL) {
return NULL;
}
static lv_indev_drv_t mouse_drv;
lv_indev_drv_init(&mouse_drv);
mouse_drv.type = LV_INDEV_TYPE_POINTER;
mouse_drv.read_cb = sdl_mouse_read;
lv_indev_t* mouse = lv_indev_drv_register(&mouse_drv);
if (mouse == NULL) {
LV_LOG_WARN("lv_indev_drv_register(&mouse_drv) returned NULL");
}
/* keypad input devices default group */
lv_group_t * g = lv_group_create();
if (g == NULL) {
LV_LOG_WARN("lv_group_create returned NULL; won't set default group");
} else {
lv_group_set_default(g);
}
static lv_indev_drv_t keyboard_drv;
lv_indev_drv_init(&keyboard_drv);
keyboard_drv.type = LV_INDEV_TYPE_KEYPAD;
keyboard_drv.read_cb = sdl_keyboard_read;
lv_indev_t *kb = lv_indev_drv_register(&keyboard_drv);
if (kb == NULL) {
LV_LOG_WARN("lv_indev_drv_register(&keyboard_drv) returned NULL");
} else if (g) {
lv_indev_set_group(kb, g);
}
return disp;
}

@ -0,0 +1,718 @@
/**
* LVGL config file for v8.3.1
* see lib/lvgl/lv_conf_template.h for initial values
*/
#ifndef LV_CONF_H
#define LV_CONF_H
#include <stdint.h>
/*====================
COLOR SETTINGS
*====================*/
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 16
/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 0
/*Enable features to draw on transparent background.
*It's required if opa, and transform_* style properties are used.
*Can be also used if the UI is above another layer, e.g. an OSD menu or video player.*/
#define LV_COLOR_SCREEN_TRANSP 0
/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.
* 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */
#define LV_COLOR_MIX_ROUND_OFS 0
/*Images pixels with this color will not be drawn if they are chroma keyed)*/
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
/*=========================
MEMORY SETTINGS
*=========================*/
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
// TODO: research benefits of lv_mem_alloc
#define LV_MEM_CUSTOM 1
#define LV_MEM_CUSTOM_INCLUDE <stdlib.h>
#define LV_MEM_CUSTOM_ALLOC malloc
#define LV_MEM_CUSTOM_FREE free
#define LV_MEM_CUSTOM_REALLOC realloc
/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.
*You will see an error log message if there wasn't enough buffers. */
#define LV_MEM_BUF_MAX_NUM 16
/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
#define LV_MEMCPY_MEMSET_STD 0
/*====================
HAL SETTINGS
*====================*/
/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
/*Input device read period in milliseconds*/
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
*(Not so important, you can adjust it to modify default sizes and spaces)*/
#define LV_DPI_DEF 130 /*[px/inch]*/
/*=======================
* FEATURE CONFIGURATION
*=======================*/
/*-------------
* Drawing
*-----------*/
/*Enable complex draw engine.
*Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
#define LV_DRAW_COMPLEX 1
/*Allow buffering some shadow calculation.
*LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`
*Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
#define LV_SHADOW_CACHE_SIZE 0
/* Set number of maximally cached circle data.
* The circumference of 1/4 circle are saved for anti-aliasing
* radius * 4 bytes are used per circle (the most often used radiuses are saved)
* 0: to disable caching */
#define LV_CIRCLE_CACHE_SIZE 4
/**
* "Simple layers" are used when a widget has `style_opa < 255` to buffer the widget into a layer
* and blend it as an image with the given opacity.
* Note that `bg_opa`, `text_opa` etc don't require buffering into layer)
* The widget can be buffered in smaller chunks to avoid using large buffers.
*
* - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it
* - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated.
*
* Both buffer sizes are in bytes.
* "Transformed layers" (where transform_angle/zoom properties are used) use larger buffers
* and can't be drawn in chunks. So these settings affects only widgets with opacity.
*/
#define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024)
#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024)
/*Default image cache size. Image caching keeps the images opened.
*If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added)
*With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
*However the opened images might consume additional RAM.
*0: to disable caching*/
#define LV_IMG_CACHE_DEF_SIZE 0
/*Number of stops allowed per gradient. Increase this to allow more stops.
*This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
#define LV_GRADIENT_MAX_STOPS 2
/*Default gradient buffer size.
*When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again.
*LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes.
*If the cache is too small the map will be allocated only while it's required for the drawing.
*0 mean no caching.*/
#define LV_GRAD_CACHE_DEF_SIZE 0
/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display)
*LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface
*The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
#define LV_DITHER_GRADIENT 0
/*Maximum buffer size to allocate for rotation.
*Only used if software rotation is enabled in the display driver.*/
#define LV_DISP_ROT_MAX_BUF (10*1024)
/*-------------
* GPU
*-----------*/
/*Use Arm's 2D acceleration library Arm-2D */
/* TODO: consider enabling for rpi? */
#define LV_USE_GPU_ARM2D 0
/*Use STM32's DMA2D (aka Chrom Art) GPU*/
#define LV_USE_GPU_STM32_DMA2D 0
#if LV_USE_GPU_STM32_DMA2D
/*Must be defined to include path of CMSIS header of target processor
e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
#define LV_GPU_DMA2D_CMSIS_INCLUDE
#endif
/*Use SWM341's DMA2D GPU*/
#define LV_USE_GPU_SWM341_DMA2D 0
#if LV_USE_GPU_SWM341_DMA2D
#define LV_GPU_SWM341_DMA2D_INCLUDE "SWM341.h"
#endif
/*Use NXP's PXP GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_PXP 0
#if LV_USE_GPU_NXP_PXP
/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)
* and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS
* has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.
*0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()
*/
#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0
#endif
/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_VG_LITE 0
/*Use SDL renderer API*/
#define LV_USE_GPU_SDL 0
#if LV_USE_GPU_SDL
#define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>
/*Texture cache size, 8MB by default*/
#define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8)
/*Custom blend mode for mask drawing, disable if you need to link with older SDL2 lib*/
#define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
#endif
/*-------------
* Logging
*-----------*/
/*Enable the log module*/
#define LV_USE_LOG 1
#if LV_USE_LOG
/*How important log should be added:
*LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
*LV_LOG_LEVEL_INFO Log important events
*LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/
#define LV_LOG_LEVEL LV_LOG_LEVEL_INFO
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
#define LV_LOG_PRINTF 0
/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
#define LV_LOG_TRACE_MEM 0
#define LV_LOG_TRACE_TIMER 0
#define LV_LOG_TRACE_INDEV 1
#define LV_LOG_TRACE_DISP_REFR 1
#define LV_LOG_TRACE_EVENT 1
#define LV_LOG_TRACE_OBJ_CREATE 0
#define LV_LOG_TRACE_LAYOUT 0
#define LV_LOG_TRACE_ANIM 0
#endif /*LV_USE_LOG*/
/*-------------
* Asserts
*-----------*/
/*Enable asserts if an operation is failed or an invalid data is found.
*If LV_USE_LOG is enabled an error message will be printed on failure*/
#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/
#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/
#define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/
#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/
#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/
/*Add a custom handler when assert happens e.g. to restart the MCU*/
#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>
#define LV_ASSERT_HANDLER while(1); /*Halt by default*/
/*-------------
* Others
*-----------*/
/*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 0
#if LV_USE_PERF_MONITOR
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif
/*1: Show the used memory and the memory fragmentation
* Requires LV_MEM_CUSTOM = 0*/
#define LV_USE_MEM_MONITOR 0
#if LV_USE_MEM_MONITOR
#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#endif
/*1: Draw random colored rectangles over the redrawn areas*/
#define LV_USE_REFR_DEBUG 0
/*Change the built in (v)snprintf functions*/
#define LV_SPRINTF_CUSTOM 0
#if LV_SPRINTF_CUSTOM
#define LV_SPRINTF_INCLUDE <stdio.h>
#define lv_snprintf snprintf
#define lv_vsnprintf vsnprintf
#else /*LV_SPRINTF_CUSTOM*/
#define LV_SPRINTF_USE_FLOAT 0
#endif /*LV_SPRINTF_CUSTOM*/
#define LV_USE_USER_DATA 1
/*Garbage Collector settings
*Used if lvgl is bound to higher level language and the memory is managed by that language*/
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0
#define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif /*LV_ENABLE_GC*/
/*=====================
* COMPILER SETTINGS
*====================*/
/*For big endian systems set to 1*/
#define LV_BIG_ENDIAN_SYSTEM 0
/*Define a custom attribute to `lv_tick_inc` function*/
#define LV_ATTRIBUTE_TICK_INC
/*Define a custom attribute to `lv_timer_handler` function*/
#define LV_ATTRIBUTE_TIMER_HANDLER
/*Define a custom attribute to `lv_disp_flush_ready` function*/
#define LV_ATTRIBUTE_FLUSH_READY
/*Required alignment size for buffers*/
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).
* E.g. __attribute__((aligned(4)))*/
#define LV_ATTRIBUTE_MEM_ALIGN
/*Attribute to mark large constant arrays for example font's bitmaps*/
#define LV_ATTRIBUTE_LARGE_CONST
/*Compiler prefix for a big array declaration in RAM*/
#define LV_ATTRIBUTE_LARGE_RAM_ARRAY
/*Place performance critical functions into a faster memory (e.g RAM)*/
#define LV_ATTRIBUTE_FAST_MEM
/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
#define LV_ATTRIBUTE_DMA
/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that
*should also appear on LVGL binding API such as Micropython.*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/
/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
#define LV_USE_LARGE_COORD 0
/*==================
* FONT USAGE
*===================*/
/*Montserrat fonts with ASCII range and some symbols using bpp = 4
*https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_10 0
#define LV_FONT_MONTSERRAT_12 0
#define LV_FONT_MONTSERRAT_14 0
#define LV_FONT_MONTSERRAT_16 0
#define LV_FONT_MONTSERRAT_18 0
#define LV_FONT_MONTSERRAT_20 0
#define LV_FONT_MONTSERRAT_22 0
#define LV_FONT_MONTSERRAT_24 0
#define LV_FONT_MONTSERRAT_26 0
#define LV_FONT_MONTSERRAT_28 0
#define LV_FONT_MONTSERRAT_30 0
#define LV_FONT_MONTSERRAT_32 0
#define LV_FONT_MONTSERRAT_34 0
#define LV_FONT_MONTSERRAT_36 0
#define LV_FONT_MONTSERRAT_38 0
#define LV_FONT_MONTSERRAT_40 0
#define LV_FONT_MONTSERRAT_42 0
#define LV_FONT_MONTSERRAT_44 0
#define LV_FONT_MONTSERRAT_46 0
#define LV_FONT_MONTSERRAT_48 0
/*Demonstrate special features*/
#define LV_FONT_MONTSERRAT_12_SUBPX 0
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
/*Pixel perfect monospace fonts*/
#define LV_FONT_UNSCII_8 0
#define LV_FONT_UNSCII_16 0
/*Optionally declare custom fonts here.
*You can use these fonts as default font too and they will be available globally.
*E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/
#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(lv_font_courierprimecode_14) LV_FONT_DECLARE(lv_font_courierprimecode_24)
/*Always set a default font*/
//#define LV_FONT_DEFAULT &lv_font_montserrat_14
#define LV_FONT_DEFAULT &lv_font_courierprimecode_14
/* additional fontawesome symbols to complement LV_SYMBOL_xxx */
#define NM_SYMBOL_BITCOIN "\xEF\x8D\xB9" /* 0xF379 */
#define NM_SYMBOL_BITCOIN_SIGN "\xEE\x82\xB4" /* 0xE0B4 */
#define NM_SYMBOL_BOLT "\xEF\x83\xA7" /* 0xF0E7 */
/*Enable handling large font and/or fonts with a lot of characters.
*The limit depends on the font size, font face and bpp.
*Compiler error will be triggered if a font needs it.*/
#define LV_FONT_FMT_TXT_LARGE 0
/*Enables/disables support for compressed fonts.*/
#define LV_USE_FONT_COMPRESSED 0
/*Enable subpixel rendering*/
#define LV_USE_FONT_SUBPX 0
#if LV_USE_FONT_SUBPX
/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/
#define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/
#endif
/*Enable drawing placeholders when glyph dsc is not found*/
#define LV_USE_FONT_PLACEHOLDER 1
/*=================
* TEXT SETTINGS
*=================*/
/**
* Select a character encoding for strings.
* Your IDE or editor should have the same character encoding
* - LV_TXT_ENC_UTF8
* - LV_TXT_ENC_ASCII
*/
#define LV_TXT_ENC LV_TXT_ENC_UTF8
/*Can break (wrap) texts on these chars*/
#define LV_TXT_BREAK_CHARS " ,.;:-_"
/*If a word is at least this long, will break wherever "prettiest"
*To disable, set to a value <= 0*/
#define LV_TXT_LINE_BREAK_LONG_LEN 0
/*Minimum number of characters in a long word to put on a line before a break.
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
/*Minimum number of characters in a long word to put on a line after a break.
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
/*The control character to use for signalling text recoloring.*/
#define LV_TXT_COLOR_CMD "#"
/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts.
*The direction will be processed according to the Unicode Bidirectional Algorithm:
*https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
#define LV_USE_BIDI 1
#if LV_USE_BIDI
/*Set the default direction. Supported values:
*`LV_BASE_DIR_LTR` Left-to-Right
*`LV_BASE_DIR_RTL` Right-to-Left
*`LV_BASE_DIR_AUTO` detect texts base direction*/
#define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
#endif
/*Enable Arabic/Persian processing
*In these languages characters should be replaced with an other form based on their position in the text*/
#define LV_USE_ARABIC_PERSIAN_CHARS 0
/*==================
* WIDGET USAGE
*================*/
/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/
#define LV_USE_ARC 1
#define LV_USE_BAR 1
#define LV_USE_BTN 1
#define LV_USE_BTNMATRIX 1
#define LV_USE_CANVAS 1
#define LV_USE_CHECKBOX 1
#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/
#define LV_USE_IMG 1 /*Requires: lv_label*/
#define LV_USE_LABEL 1
#if LV_USE_LABEL
#define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/
#define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/
#endif
#define LV_USE_LINE 1
#define LV_USE_ROLLER 1 /*Requires: lv_label*/
#if LV_USE_ROLLER
#define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
#endif
#define LV_USE_SLIDER 1 /*Requires: lv_bar*/
#define LV_USE_SWITCH 1
#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/
#define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
#define LV_USE_TABLE 1
/*==================
* EXTRA COMPONENTS
*==================*/
/*-----------
* Widgets
*----------*/
#define LV_USE_ANIMIMG 1
#define LV_USE_CALENDAR 1
#define LV_CALENDAR_WEEK_STARTS_MONDAY 1
#if LV_CALENDAR_WEEK_STARTS_MONDAY
#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
#else
#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}
#endif
#define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
#define LV_USE_CALENDAR_HEADER_ARROW 1
#define LV_USE_CALENDAR_HEADER_DROPDOWN 1
#define LV_USE_CHART 1
#define LV_USE_COLORWHEEL 1
#define LV_USE_IMGBTN 1
#define LV_USE_KEYBOARD 1
#define LV_USE_LED 1
#define LV_USE_LIST 1
#define LV_USE_MENU 1
#define LV_USE_METER 1
#define LV_USE_MSGBOX 1
#define LV_USE_SPAN 1
/*A line text can contain maximum num of span descriptor */
#define LV_SPAN_SNIPPET_STACK_SIZE 64
#define LV_USE_SPINBOX 1
#define LV_USE_SPINNER 1
#define LV_USE_TABVIEW 1
#define LV_USE_TILEVIEW 1
#define LV_USE_WIN 1
/*-----------
* Themes
*----------*/
/*A simple, impressive and very complete theme*/
#define LV_USE_THEME_DEFAULT 1
#if LV_USE_THEME_DEFAULT
/*0: Light mode; 1: Dark mode*/
#define LV_THEME_DEFAULT_DARK 0
/*1: Enable grow on press*/
#define LV_THEME_DEFAULT_GROW 1
/*Default transition time in [ms]*/
#define LV_THEME_DEFAULT_TRANSITION_TIME 80
#endif /*LV_USE_THEME_DEFAULT*/
/*A very simple theme that is a good starting point for a custom theme*/
#define LV_USE_THEME_BASIC 1
/*A theme designed for monochrome displays*/
#define LV_USE_THEME_MONO 1
/*-----------
* Layouts
*----------*/
/*A layout similar to Flexbox in CSS.*/
#define LV_USE_FLEX 1
/*A layout similar to Grid in CSS.*/
#define LV_USE_GRID 1
/*---------------------
* 3rd party libraries
*--------------------*/
/*File system interfaces for common APIs */
/*API for fopen, fread, etc*/
#define LV_USE_FS_STDIO 0
#if LV_USE_FS_STDIO
#define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for open, read, etc*/
#define LV_USE_FS_POSIX 0
#if LV_USE_FS_POSIX
#define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for CreateFile, ReadFile, etc*/
#define LV_USE_FS_WIN32 0
#if LV_USE_FS_WIN32
#define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/
#define LV_USE_FS_FATFS 0
#if LV_USE_FS_FATFS
#define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*PNG decoder library*/
#define LV_USE_PNG 0
/*BMP decoder library*/
#define LV_USE_BMP 0
/* JPG + split JPG decoder library.
* Split JPG is a custom format optimized for embedded systems. */
#define LV_USE_SJPG 0
/*GIF decoder library*/
#define LV_USE_GIF 0
/*QR code library*/
#define LV_USE_QRCODE 0
/*FreeType library*/
#define LV_USE_FREETYPE 0
#if LV_USE_FREETYPE
/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/
#define LV_FREETYPE_CACHE_SIZE (16 * 1024)
#if LV_FREETYPE_CACHE_SIZE >= 0
/* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */
/* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */
/* if font size >= 256, must be configured as image cache */
#define LV_FREETYPE_SBIT_CACHE 0
/* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */
/* (0:use system defaults) */
#define LV_FREETYPE_CACHE_FT_FACES 0
#define LV_FREETYPE_CACHE_FT_SIZES 0
#endif
#endif
/*Rlottie library*/
#define LV_USE_RLOTTIE 0
/*FFmpeg library for image decoding and playing videos
*Supports all major image formats so do not enable other image decoder with it*/
#define LV_USE_FFMPEG 0
#if LV_USE_FFMPEG
/*Dump input information to stderr*/
#define LV_FFMPEG_DUMP_FORMAT 0
#endif
/*-----------
* Others
*----------*/
/*1: Enable API to take snapshot for object*/
#define LV_USE_SNAPSHOT 0
/*1: Enable Monkey test*/
#define LV_USE_MONKEY 0
/*1: Enable grid navigation*/
#define LV_USE_GRIDNAV 0
/*1: Enable lv_obj fragment*/
#define LV_USE_FRAGMENT 0
/*1: Support using images as font in label or span widgets */
#define LV_USE_IMGFONT 0
/*1: Enable a published subscriber based messaging system */
#define LV_USE_MSG 0
/*1: Enable Pinyin input method*/
/*Requires: lv_keyboard*/
#define LV_USE_IME_PINYIN 0
#if LV_USE_IME_PINYIN
/*1: Use default thesaurus*/
/*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/
#define LV_IME_PINYIN_USE_DEFAULT_DICT 1
/*Set the maximum number of candidate panels that can be displayed*/
/*This needs to be adjusted according to the size of the screen*/
#define LV_IME_PINYIN_CAND_TEXT_NUM 6
/*Use 9 key input(k9)*/
#define LV_IME_PINYIN_USE_K9_MODE 1
#if LV_IME_PINYIN_USE_K9_MODE == 1
#define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3
#endif // LV_IME_PINYIN_USE_K9_MODE
#endif
/*==================
* EXAMPLES
*==================*/
/*Enable the examples to be built with the library*/
#define LV_BUILD_EXAMPLES 0
/*===================
* DEMO USAGE
====================*/
/*Show some widget. It might be required to increase `LV_MEM_SIZE` */
#define LV_USE_DEMO_WIDGETS 0
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 0
#endif
/*Demonstrate the usage of encoder and keyboard*/
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
/*Benchmark your system*/
#define LV_USE_DEMO_BENCHMARK 0
#if LV_USE_DEMO_BENCHMARK
/*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/
#define LV_DEMO_BENCHMARK_RGB565A8 0
#endif
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 0
/*Music player demo*/
#define LV_USE_DEMO_MUSIC 0
#if LV_USE_DEMO_MUSIC
#define LV_DEMO_MUSIC_SQUARE 0
#define LV_DEMO_MUSIC_LANDSCAPE 0
#define LV_DEMO_MUSIC_ROUND 0
#define LV_DEMO_MUSIC_LARGE 0
#define LV_DEMO_MUSIC_AUTO_PLAY 0
#endif
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/

@ -0,0 +1,487 @@
/**
* LVGL display/touch drivers config file for v8.3.0
* see lib/lvgl/lv_conf_template.h for initial values
*/
#ifndef LV_DRV_CONF_H
#define LV_DRV_CONF_H
#include "lv_conf.h"
/*********************
* DELAY INTERFACE
*********************/
#define LV_DRV_DELAY_INCLUDE <stdint.h> /*Dummy include by default*/
#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/
#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/
/*********************
* DISPLAY INTERFACE
*********************/
/*------------
* Common
*------------*/
#define LV_DRV_DISP_INCLUDE <stdint.h> /*Dummy include by default*/
#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/
#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/
/*---------
* SPI
*---------*/
#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/
#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/
#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/
/*------------------
* Parallel port
*-----------------*/
#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/
#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/
#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/
#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/
#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/
/***************************
* INPUT DEVICE INTERFACE
***************************/
/*----------
* Common
*----------*/
#define LV_DRV_INDEV_INCLUDE <stdint.h> /*Dummy include by default*/
#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/
#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/
/*---------
* SPI
*---------*/
#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/
#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/
/*---------
* I2C
*---------*/
#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/
#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/
#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/
#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/
#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/
/*********************
* DISPLAY DRIVERS
*********************/
/*-------------------
* SDL
*-------------------*/
/* SDL based drivers for display, mouse, mousewheel and keyboard*/
#ifndef USE_SDL
# define USE_SDL 0
#endif
/* Hardware accelerated SDL driver */
#ifndef USE_SDL_GPU
# define USE_SDL_GPU 0
#endif
#if USE_SDL || USE_SDL_GPU
# define SDL_HOR_RES 800
# define SDL_VER_RES 480
/* Scale window by this factor (useful when simulating small screens) */
# define SDL_ZOOM 1
/* Used to test true double buffering with only address changing.
* Use 2 draw buffers, bith with SDL_HOR_RES x SDL_VER_RES size*/
# define SDL_DOUBLE_BUFFERED 0
/*Eclipse: <SDL2/SDL.h> Visual Studio: <SDL.h>*/
# define SDL_INCLUDE_PATH <SDL2/SDL.h>
/*Open two windows to test multi display support*/
# define SDL_DUAL_DISPLAY 0
#endif
/*-------------------
* Monitor of PC
*-------------------*/
/*DEPRECATED: Use the SDL driver instead. */
#ifndef USE_MONITOR
# define USE_MONITOR 0
#endif
#if USE_MONITOR
# define MONITOR_HOR_RES 480
# define MONITOR_VER_RES 320
/* Scale window by this factor (useful when simulating small screens) */
# define MONITOR_ZOOM 1
/* Used to test true double buffering with only address changing.
* Use 2 draw buffers, bith with MONITOR_HOR_RES x MONITOR_VER_RES size*/
# define MONITOR_DOUBLE_BUFFERED 0
/*Eclipse: <SDL2/SDL.h> Visual Studio: <SDL.h>*/
# define MONITOR_SDL_INCLUDE_PATH <SDL2/SDL.h>
/*Open two windows to test multi display support*/
# define MONITOR_DUAL 0
#endif
/*-----------------------------------
* Native Windows (including mouse)
*----------------------------------*/
#ifndef USE_WINDOWS
# define USE_WINDOWS 0
#endif
#if USE_WINDOWS
# define WINDOW_HOR_RES 480
# define WINDOW_VER_RES 320
#endif
/*----------------------------
* Native Windows (win32drv)
*---------------------------*/
#ifndef USE_WIN32DRV
# define USE_WIN32DRV 0
#endif
#if USE_WIN32DRV
/* Scale window by this factor (useful when simulating small screens) */
# define WIN32DRV_MONITOR_ZOOM 1
#endif
/*----------------------------------------
* GTK drivers (monitor, mouse, keyboard
*---------------------------------------*/
#ifndef USE_GTK
# define USE_GTK 0
#endif
/*----------------------------------------
* Wayland drivers (monitor, mouse, keyboard, touchscreen)
*---------------------------------------*/
#ifndef USE_WAYLAND
# define USE_WAYLAND 0
#endif
#if USE_WAYLAND
/* Support for client-side decorations */
# ifndef LV_WAYLAND_CLIENT_SIDE_DECORATIONS
# define LV_WAYLAND_CLIENT_SIDE_DECORATIONS 1
# endif
/* Support for (deprecated) wl-shell protocol */
# ifndef LV_WAYLAND_WL_SHELL
# define LV_WAYLAND_WL_SHELL 1
# endif
/* Support for xdg-shell protocol */
# ifndef LV_WAYLAND_XDG_SHELL
# define LV_WAYLAND_XDG_SHELL 0
# endif
#endif
/*----------------
* SSD1963
*--------------*/
#ifndef USE_SSD1963
# define USE_SSD1963 0
#endif
#if USE_SSD1963
# define SSD1963_HOR_RES LV_HOR_RES
# define SSD1963_VER_RES LV_VER_RES
# define SSD1963_HT 531
# define SSD1963_HPS 43
# define SSD1963_LPS 8
# define SSD1963_HPW 10
# define SSD1963_VT 288
# define SSD1963_VPS 12
# define SSD1963_FPS 4
# define SSD1963_VPW 10
# define SSD1963_HS_NEG 0 /*Negative hsync*/
# define SSD1963_VS_NEG 0 /*Negative vsync*/
# define SSD1963_ORI 0 /*0, 90, 180, 270*/
# define SSD1963_COLOR_DEPTH 16
#endif
/*----------------
* R61581
*--------------*/
#ifndef USE_R61581
# define USE_R61581 0
#endif
#if USE_R61581
# define R61581_HOR_RES LV_HOR_RES
# define R61581_VER_RES LV_VER_RES
# define R61581_HSPL 0 /*HSYNC signal polarity*/
# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/
# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/
# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */
# define R61581_VSPL 0 /*VSYNC signal polarity*/
# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/
# define R61581_VFP 8 /*Vertical Front poarch*/
# define R61581_VBP 8 /*Vertical Back poarch */
# define R61581_DPL 0 /*DCLK signal polarity*/
# define R61581_EPL 1 /*ENABLE signal polarity*/
# define R61581_ORI 0 /*0, 180*/
# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/
#endif
/*------------------------------
* ST7565 (Monochrome, low res.)
*-----------------------------*/
#ifndef USE_ST7565
# define USE_ST7565 0
#endif
#if USE_ST7565
/*No settings*/
#endif /*USE_ST7565*/
/*------------------------------
* GC9A01 (color, low res.)
*-----------------------------*/
#ifndef USE_GC9A01
# define USE_GC9A01 0
#endif
#if USE_GC9A01
/*No settings*/
#endif /*USE_GC9A01*/
/*------------------------------------------
* UC1610 (4 gray 160*[104|128])
* (EA DOGXL160 160x104 tested)
*-----------------------------------------*/
#ifndef USE_UC1610
# define USE_UC1610 0
#endif
#if USE_UC1610
# define UC1610_HOR_RES LV_HOR_RES
# define UC1610_VER_RES LV_VER_RES
# define UC1610_INIT_CONTRAST 33 /* init contrast, values in [%] */
# define UC1610_INIT_HARD_RST 0 /* 1 : hardware reset at init, 0 : software reset */
# define UC1610_TOP_VIEW 0 /* 0 : Bottom View, 1 : Top View */
#endif /*USE_UC1610*/
/*-------------------------------------------------
* SHARP memory in pixel monochrome display series
* LS012B7DD01 (184x38 pixels.)
* LS013B7DH03 (128x128 pixels.)
* LS013B7DH05 (144x168 pixels.)
* LS027B7DH01 (400x240 pixels.) (tested)
* LS032B7DD02 (336x536 pixels.)
* LS044Q7DH01 (320x240 pixels.)
*------------------------------------------------*/
#ifndef USE_SHARP_MIP
# define USE_SHARP_MIP 0
#endif
#if USE_SHARP_MIP
# define SHARP_MIP_HOR_RES LV_HOR_RES
# define SHARP_MIP_VER_RES LV_VER_RES
# define SHARP_MIP_SOFT_COM_INVERSION 0
# define SHARP_MIP_REV_BYTE(b) /*((uint8_t) __REV(__RBIT(b)))*/ /*Architecture / compiler dependent byte bits order reverse*/
#endif /*USE_SHARP_MIP*/
/*-------------------------------------------------
* ILI9341 240X320 TFT LCD
*------------------------------------------------*/
#ifndef USE_ILI9341
# define USE_ILI9341 0
#endif
#if USE_ILI9341
# define ILI9341_HOR_RES LV_HOR_RES
# define ILI9341_VER_RES LV_VER_RES
# define ILI9341_GAMMA 1
# define ILI9341_TEARING 0
#endif /*USE_ILI9341*/
/*-----------------------------------------
* Linux frame buffer device (/dev/fbx)
*-----------------------------------------*/
#ifndef USE_FBDEV
# define USE_FBDEV 0
#endif
#if USE_FBDEV
# define FBDEV_PATH "/dev/fb0"
#endif
/*-----------------------------------------
* FreeBSD frame buffer device (/dev/fbx)
*.........................................*/
#ifndef USE_BSD_FBDEV
# define USE_BSD_FBDEV 0
#endif
#if USE_BSD_FBDEV
# define FBDEV_PATH "/dev/fb0"
#endif
/*-----------------------------------------
* DRM/KMS device (/dev/dri/cardX)
*-----------------------------------------*/
// TODO: consider switching to DRM?
// see https://forum.lvgl.io/t/drm-driver-is-slow/3479
#ifndef USE_DRM
# define USE_DRM 0
#endif
#if USE_DRM
# define DRM_CARD "/dev/dri/card0"
# define DRM_CONNECTOR_ID -1 /* -1 for the first connected one */
#endif
/*********************
* INPUT DEVICES
*********************/
/*--------------
* XPT2046
*--------------*/
#ifndef USE_XPT2046
# define USE_XPT2046 0
#endif
#if USE_XPT2046
# define XPT2046_HOR_RES 480
# define XPT2046_VER_RES 320
# define XPT2046_X_MIN 200
# define XPT2046_Y_MIN 200
# define XPT2046_X_MAX 3800
# define XPT2046_Y_MAX 3800
# define XPT2046_AVG 4
# define XPT2046_X_INV 0
# define XPT2046_Y_INV 0
# define XPT2046_XY_SWAP 0
#endif
/*-----------------
* FT5406EE8
*-----------------*/
#ifndef USE_FT5406EE8
# define USE_FT5406EE8 0
#endif
#if USE_FT5406EE8
# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/
#endif
/*---------------
* AD TOUCH
*--------------*/
#ifndef USE_AD_TOUCH
# define USE_AD_TOUCH 0
#endif
#if USE_AD_TOUCH
/*No settings*/
#endif
/*---------------------------------------
* Mouse or touchpad on PC (using SDL)
*-------------------------------------*/
/*DEPRECATED: Use the SDL driver instead. */
#ifndef USE_MOUSE
# define USE_MOUSE 0
#endif
#if USE_MOUSE
/*No settings*/
#endif
/*-------------------------------------------
* Mousewheel as encoder on PC (using SDL)
*------------------------------------------*/
/*DEPRECATED: Use the SDL driver instead. */
#ifndef USE_MOUSEWHEEL
# define USE_MOUSEWHEEL 0
#endif
#if USE_MOUSEWHEEL
/*No settings*/
#endif
/*-------------------------------------------------
* Touchscreen, mouse/touchpad or keyboard as libinput interface (for Linux based systems)
*------------------------------------------------*/
#ifndef USE_LIBINPUT
# define USE_LIBINPUT 0
#endif
#ifndef USE_BSD_LIBINPUT
# define USE_BSD_LIBINPUT 0
#endif
#if USE_LIBINPUT || USE_BSD_LIBINPUT
/*If only a single device of the same type is connected, you can also auto detect it, e.g.:
*#define LIBINPUT_NAME libinput_find_dev(LIBINPUT_CAPABILITY_TOUCH, false)*/
# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/
#endif /*USE_LIBINPUT || USE_BSD_LIBINPUT*/
/*-------------------------------------------------
* Mouse or touchpad as evdev interface (for Linux based systems)
*------------------------------------------------*/
#ifndef USE_EVDEV
# define USE_EVDEV 0
#endif
#ifndef USE_BSD_EVDEV
# define USE_BSD_EVDEV 0
#endif
#if USE_EVDEV || USE_BSD_EVDEV
# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/
# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/
# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/
# if EVDEV_CALIBRATE
# define EVDEV_HOR_MIN 0 /*to invert axis swap EVDEV_XXX_MIN by EVDEV_XXX_MAX*/
# define EVDEV_HOR_MAX 4096 /*"evtest" Linux tool can help to get the correct calibraion values>*/
# define EVDEV_VER_MIN 0
# define EVDEV_VER_MAX 4096
# endif /*EVDEV_CALIBRATE*/
#endif /*USE_EVDEV*/
/*-------------------------------------------------
* Full keyboard support for evdev and libinput interface
*------------------------------------------------*/
# ifndef USE_XKB
# define USE_XKB 0
# endif
#if USE_LIBINPUT || USE_BSD_LIBINPUT || USE_EVDEV || USE_BSD_EVDEV
# if USE_XKB
# define XKB_KEY_MAP { .rules = NULL, \
.model = "pc101", \
.layout = "us", \
.variant = NULL, \
.options = NULL } /*"setxkbmap -query" can help find the right values for your keyboard*/
# endif /*USE_XKB*/
#endif /*USE_LIBINPUT || USE_BSD_LIBINPUT || USE_EVDEV || USE_BSD_EVDEV*/
/*-------------------------------
* Keyboard of a PC (using SDL)
*------------------------------*/
/*DEPRECATED: Use the SDL driver instead. */
#ifndef USE_KEYBOARD
# define USE_KEYBOARD 0
#endif
#if USE_KEYBOARD
/*No settings*/
#endif
#endif /*LV_DRV_CONF_H*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,327 @@
#define _DEFAULT_SOURCE /* needed for usleep() */
#include "lvgl/lvgl.h"
#include "ui.h"
#include <stdlib.h>
#include <unistd.h>
lv_disp_t* drv_init(void);
static lv_style_t style_title;
static lv_style_t style_text_muted;
static lv_style_t style_btn_red;
static const lv_font_t* font_large;
static lv_obj_t* virt_keyboard;
static lv_obj_t* tabview; /* main tabs content parent; lv_tabview_create */
static void textarea_event_cb(lv_event_t* e)
{
lv_obj_t* textarea = lv_event_get_target(e);
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_FOCUSED) {
if (lv_indev_get_type(lv_indev_get_act()) != LV_INDEV_TYPE_KEYPAD) {
lv_keyboard_set_textarea(virt_keyboard, textarea);
lv_obj_set_style_max_height(virt_keyboard, NM_DISP_HOR * 2 / 3, 0);
lv_obj_update_layout(tabview); /* make sure sizes are recalculated */
lv_obj_set_height(tabview, NM_DISP_VER - lv_obj_get_height(virt_keyboard));
lv_obj_clear_flag(virt_keyboard, LV_OBJ_FLAG_HIDDEN);
lv_obj_scroll_to_view_recursive(textarea, LV_ANIM_OFF);
}
} else if (code == LV_EVENT_DEFOCUSED) {
lv_keyboard_set_textarea(virt_keyboard, NULL);
lv_obj_set_height(tabview, NM_DISP_VER);
lv_obj_add_flag(virt_keyboard, LV_OBJ_FLAG_HIDDEN);
lv_indev_reset(NULL, textarea);
} else if (code == LV_EVENT_READY || code == LV_EVENT_CANCEL) {
lv_obj_set_height(tabview, NM_DISP_VER);
lv_obj_add_flag(virt_keyboard, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_state(textarea, LV_STATE_FOCUSED);
lv_indev_reset(NULL, textarea); /* forget the last clicked object to make it focusable again */
}
}
static void create_bitcoin_panel(lv_obj_t* parent)
{
lv_obj_t* label = lv_label_create(parent);
lv_label_set_text_static(label, "bitcoin tab isn't designed yet\nfollow https://nakamochi.io");
lv_obj_center(label);
}
static void create_lnd_panel(lv_obj_t* parent)
{
lv_obj_t* label = lv_label_create(parent);
lv_label_set_text_static(label, "lightning tab isn't designed yet\nfollow https://nakamochi.io");
lv_obj_center(label);
}
static struct {
lv_obj_t* wifi_spinner_obj; /* lv_spinner_create */
lv_obj_t* wifi_status_obj; /* lv_label_create */
lv_obj_t* wifi_connect_btn_obj; /* lv_btn_create */
lv_obj_t* wifi_ssid_list_obj; /* lv_dropdown_create */
lv_obj_t* wifi_pwd_obj; /* lv_textarea_create */
lv_obj_t* power_halt_btn_obj; /* lv_btn_create */
} settings;
/**
* update the UI with network connection info, placing text into wifi_status_obj as is.
* wifi_list is optional; items must be delimited by '\n' if non-null.
* args are alloc-copied and owned by lvgl.
*/
extern void ui_update_network_status(const char *text, const char *wifi_list)
{
if (wifi_list) {
lv_dropdown_set_options(settings.wifi_ssid_list_obj, wifi_list);
}
lv_obj_clear_state(settings.wifi_connect_btn_obj, LV_STATE_DISABLED);
lv_obj_add_flag(settings.wifi_spinner_obj, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text(settings.wifi_status_obj, text);
}
static void wifi_connect_btn_callback(lv_event_t* e)
{
(void)e; /* unused */
lv_obj_add_state(settings.wifi_connect_btn_obj, LV_STATE_DISABLED);
lv_obj_clear_flag(settings.wifi_spinner_obj, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text(settings.wifi_status_obj, "connecting ...");
char buf[100];
lv_dropdown_get_selected_str(settings.wifi_ssid_list_obj, buf, sizeof(buf));
nm_wifi_start_connect(buf, lv_textarea_get_text(settings.wifi_pwd_obj));
}
static void power_halt_btn_callback(lv_event_t *e)
{
if (e->user_data) { /* ptr to msgbox */
lv_obj_t *msgbox = e->user_data;
/* first button is "proceed", do shutdown */
if (lv_msgbox_get_active_btn(msgbox) == 0) {
/* shutdown confirmed */
nm_sys_shutdown();
}
/* shutdown aborted or passthrough from confirmed shutdown.
* in the latter case, ui is still running for a brief moment,
* until ngui terminates. */
lv_msgbox_close(msgbox);
return;
}
/* first button must always be a "proceed", do shutdown;
* text is irrelevant */
static const char *btns[] = {"PROCEED", "ABORT", NULL};
lv_obj_t *msgbox = lv_msgbox_create(
NULL /* modal */,
"SHUTDOWN", /* title */
"are you sure?", /* text */
btns,
false /* close btn */);
lv_obj_center(msgbox);
lv_obj_add_event_cb(msgbox, power_halt_btn_callback, LV_EVENT_VALUE_CHANGED, msgbox);
return;
}
static void create_settings_panel(lv_obj_t* parent)
{
/********************
* wifi panel
********************/
lv_obj_t* wifi_panel = lv_obj_create(parent);
lv_obj_set_height(wifi_panel, LV_SIZE_CONTENT);
lv_obj_t * wifi_panel_title = lv_label_create(wifi_panel);
lv_label_set_text_static(wifi_panel_title, LV_SYMBOL_WIFI " WIFI");
lv_obj_add_style(wifi_panel_title, &style_title, 0);
lv_obj_t* wifi_spinner = lv_spinner_create(wifi_panel, 1000 /* speed */, 60 /* arc in deg */);
settings.wifi_spinner_obj = wifi_spinner;
lv_obj_add_flag(wifi_spinner, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_size(wifi_spinner, 20, 20);
lv_obj_set_style_arc_width(wifi_spinner, 4, LV_PART_INDICATOR);
lv_obj_t* wifi_status = lv_label_create(wifi_panel);
settings.wifi_status_obj = wifi_status;
lv_label_set_text_static(wifi_status, "unknown status");
lv_label_set_long_mode(wifi_status, LV_LABEL_LONG_WRAP);
lv_obj_set_height(wifi_status, LV_SIZE_CONTENT);
lv_label_set_recolor(wifi_status, true);
lv_obj_t* wifi_ssid_label = lv_label_create(wifi_panel);
lv_label_set_text_static(wifi_ssid_label, "network name");
lv_obj_add_style(wifi_ssid_label, &style_text_muted, 0);
lv_obj_t* wifi_ssid = lv_dropdown_create(wifi_panel);
settings.wifi_ssid_list_obj = wifi_ssid;
lv_dropdown_clear_options(wifi_ssid);
lv_obj_t* wifi_pwd_label = lv_label_create(wifi_panel);
lv_label_set_text_static(wifi_pwd_label, "password");
lv_obj_add_style(wifi_pwd_label, &style_text_muted, 0);
lv_obj_t* wifi_pwd = lv_textarea_create(wifi_panel);
settings.wifi_pwd_obj = wifi_pwd;
lv_textarea_set_one_line(wifi_pwd, true);
lv_textarea_set_password_mode(wifi_pwd, true);
lv_obj_add_event_cb(wifi_pwd, textarea_event_cb, LV_EVENT_ALL, NULL);
lv_obj_t* wifi_connect_btn = lv_btn_create(wifi_panel);
settings.wifi_connect_btn_obj = wifi_connect_btn;
lv_obj_set_height(wifi_connect_btn, LV_SIZE_CONTENT);
lv_obj_add_event_cb(wifi_connect_btn, wifi_connect_btn_callback, LV_EVENT_CLICKED, NULL);
lv_obj_t* wifi_connect_btn_label = lv_label_create(wifi_connect_btn);
lv_label_set_text_static(wifi_connect_btn_label, "CONNECT");
lv_obj_center(wifi_connect_btn_label);
/********************
* power panel
********************/
lv_obj_t* power_panel = lv_obj_create(parent);
lv_obj_set_height(power_panel, LV_SIZE_CONTENT);
lv_obj_t * power_panel_title = lv_label_create(power_panel);
lv_label_set_text_static(power_panel_title, LV_SYMBOL_POWER " POWER");
lv_obj_add_style(power_panel_title, &style_title, 0);
lv_obj_t* poweroff_text = lv_label_create(power_panel);
lv_label_set_text_static(poweroff_text, "once shut down, the power cord\ncan be removed.");
lv_label_set_long_mode(poweroff_text, LV_LABEL_LONG_WRAP);
lv_obj_set_height(poweroff_text, LV_SIZE_CONTENT);
lv_label_set_recolor(poweroff_text, true);
lv_obj_t* power_halt_btn = lv_btn_create(power_panel);
settings.power_halt_btn_obj = power_halt_btn;
lv_obj_set_height(power_halt_btn, LV_SIZE_CONTENT);
lv_obj_add_style(power_halt_btn, &style_btn_red, 0);
lv_obj_add_event_cb(power_halt_btn, power_halt_btn_callback, LV_EVENT_CLICKED, NULL);
lv_obj_t* power_halt_btn_label = lv_label_create(power_halt_btn);
lv_label_set_text_static(power_halt_btn_label, "SHUTDOWN");
lv_obj_center(power_halt_btn_label);
/********************
* layout
********************/
static lv_coord_t parent_grid_cols[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static lv_coord_t parent_grid_rows[] = {
LV_GRID_CONTENT, /* wifi panel */
LV_GRID_CONTENT, /* power panel */
LV_GRID_TEMPLATE_LAST
};
lv_obj_set_grid_dsc_array(parent, parent_grid_cols, parent_grid_rows);
lv_obj_set_grid_cell(wifi_panel, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
lv_obj_set_grid_cell(power_panel, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 1, 1);
static lv_coord_t wifi_grid_cols[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static lv_coord_t wifi_grid_rows[] = {
LV_GRID_CONTENT, /* title */
5, /* separator */
LV_GRID_CONTENT, /* wifi status text */
30, /* wifi selector */
5, /* separator */
LV_GRID_CONTENT, /* password label */
30, /* password input */
5, /* separator */
LV_GRID_CONTENT, /* connect btn */
LV_GRID_TEMPLATE_LAST
};
lv_obj_set_grid_dsc_array(wifi_panel, wifi_grid_cols, wifi_grid_rows);
lv_obj_set_grid_cell(wifi_panel_title, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
lv_obj_set_grid_cell(wifi_spinner, LV_GRID_ALIGN_END, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1);
/* column 0 */
lv_obj_set_grid_cell(wifi_status, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 2, 7);
/* column 1 */
lv_obj_set_grid_cell(wifi_ssid_label, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_START, 2, 1);
lv_obj_set_grid_cell(wifi_ssid, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_CENTER, 3, 1);
lv_obj_set_grid_cell(wifi_pwd_label, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_START, 5, 1);
lv_obj_set_grid_cell(wifi_pwd, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_CENTER, 6, 1);
lv_obj_set_grid_cell(wifi_connect_btn, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_CENTER, 8, 1);
static lv_coord_t power_grid_cols[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static lv_coord_t power_grid_rows[] = {
LV_GRID_CONTENT, /* title */
5, /* separator */
LV_GRID_CONTENT, /* power off text and btn*/
LV_GRID_TEMPLATE_LAST
};
lv_obj_set_grid_dsc_array(power_panel, power_grid_cols, power_grid_rows);
lv_obj_set_grid_cell(power_panel_title, LV_GRID_ALIGN_STRETCH, 0, 2, LV_GRID_ALIGN_CENTER, 0, 1);
/* column 0 */
lv_obj_set_grid_cell(poweroff_text, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 2, 1);
/* column 1 */
lv_obj_set_grid_cell(power_halt_btn, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_CENTER, 2, 1);
}
static void tab_changed_event_cb(lv_event_t* e)
{
(void)e; /* unused */
uint16_t n = lv_tabview_get_tab_act(tabview);
switch (n) {
case 2:
nm_tab_settings_active();
break;
default:
LV_LOG_INFO("unhandled tab index %i", n);
}
}
extern int ui_init()
{
lv_init();
lv_disp_t* disp = drv_init();
if (disp == NULL) {
return -1;
}
/* default theme is static */
lv_theme_t* theme = lv_theme_default_init(
disp,
lv_palette_main(LV_PALETTE_BLUE), /* primary */
lv_palette_main(LV_PALETTE_RED), /* secondary */
true /*LV_THEME_DEFAULT_DARK*/,
LV_FONT_DEFAULT);
lv_disp_set_theme(disp, theme);
font_large = &lv_font_courierprimecode_24; /* static */
lv_style_init(&style_title);
lv_style_set_text_font(&style_title, font_large);
lv_style_init(&style_text_muted);
lv_style_set_text_opa(&style_text_muted, LV_OPA_50);
lv_style_init(&style_btn_red);
lv_style_set_bg_color(&style_btn_red, lv_palette_main(LV_PALETTE_RED));
/* global virtual keyboard */
virt_keyboard = lv_keyboard_create(lv_scr_act());
if (virt_keyboard == NULL) {
/* TODO: or continue without keyboard? */
return -1;
}
lv_obj_add_flag(virt_keyboard, LV_OBJ_FLAG_HIDDEN);
const lv_coord_t tabh = 60;
tabview = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, tabh);
if (tabview == NULL) {
return -1;
}
/**
* tab_changed_event_cb relies on the specific tab order, 0-based index:
* 0: bitcoin
* 1: lightning
* 2: settings
*/
lv_obj_t* tab_btc = lv_tabview_add_tab(tabview, NM_SYMBOL_BITCOIN " BITCOIN");
if (tab_btc == NULL) {
return -1;
}
create_bitcoin_panel(tab_btc);
lv_obj_t* tab_lnd = lv_tabview_add_tab(tabview, NM_SYMBOL_BOLT " LIGHTNING");
if (tab_lnd == NULL) {
return -1;
}
create_lnd_panel(tab_lnd);
lv_obj_t* tab_settings = lv_tabview_add_tab(tabview, LV_SYMBOL_SETTINGS " SETTINGS");
if (tab_settings == NULL) {
return -1;
}
create_settings_panel(tab_settings);
lv_obj_add_event_cb(tabview, tab_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
return 0;
}

@ -0,0 +1,28 @@
#ifndef NM_UI_H
#define NM_UI_H
#include <stdint.h>
/**
* returns elapsed time since program start, in ms.
* rolls over when overflow occurs.
*/
uint32_t nm_get_curr_tick();
/**
* initiates system shutdown leading to poweroff.
*/
void nm_sys_shutdown();
/**
* invoken when the UI is switched to the network settings tab.
*/
void nm_tab_settings_active();
/**
* initiate connection to a wifi network with the given SSID and a password.
* connection, if successful, is persisted in wpa_supplicant config.
*/
int nm_wifi_start_connect(const char* ssid, const char* password);
#endif

@ -0,0 +1,3 @@
///! see lv_symbols_def.h
pub const Warning = &[_]u8{ 0xef, 0x81, 0xb1 };
pub const Ok = &[_]u8{ 0xef, 0x80, 0x8c };