diff --git a/examples/gpio_zig/Kconfig b/examples/gpio_zig/Kconfig new file mode 100644 index 00000000000..d3189a8f2e0 --- /dev/null +++ b/examples/gpio_zig/Kconfig @@ -0,0 +1,30 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_GPIO_ZIG + tristate "GPIO driver example" + default n + depends on DEV_GPIO + ---help--- + Enable the GPIO in Zig driver example + +if EXAMPLES_GPIO_ZIG + +config EXAMPLES_GPIO_ZIG_PROGNAME + string "Program name" + default "gpio_zig" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_GPIO_ZIG_PRIORITY + int "GPIO task priority" + default 100 + +config EXAMPLES_GPIO_ZIG_STACKSIZE + int "GPIO stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/examples/gpio_zig/Make.defs b/examples/gpio_zig/Make.defs new file mode 100644 index 00000000000..1cc532451f1 --- /dev/null +++ b/examples/gpio_zig/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/examples/hello_zig/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_GPIO_ZIG),) +CONFIGURED_APPS += $(APPDIR)/examples/gpio_zig +endif diff --git a/examples/gpio_zig/Makefile b/examples/gpio_zig/Makefile new file mode 100644 index 00000000000..5c831d9d899 --- /dev/null +++ b/examples/gpio_zig/Makefile @@ -0,0 +1,35 @@ +############################################################################ +# apps/examples/GPIO_zig/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# GPIO in Zig Example + +MAINSRC = gpio_zig.zig + +ZIG := /home/kassane/Downloads/zig-linux-x86_64-0.13.0/zig +# GPIO_zig built-in application info +ZIGFLAGS += -fPIE -O ReleaseSmall -lc -I$(TOPDIR)/include +PROGNAME = $(CONFIG_EXAMPLES_GPIO_ZIG_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_GPIO_ZIG_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_GPIO_ZIG_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_GPIO_ZIG) + +include $(APPDIR)/Application.mk diff --git a/examples/gpio_zig/gpio_zig.zig b/examples/gpio_zig/gpio_zig.zig new file mode 100644 index 00000000000..8e1f77c4c8b --- /dev/null +++ b/examples/gpio_zig/gpio_zig.zig @@ -0,0 +1,356 @@ +///************************************************************************** +// examples/gpio_zig/gpio_zig.zig +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. The +// ASF licenses this file to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance with the +// License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +///************************************************************************** +///************************************************************************** +// Included Files +///************************************************************************** +const std = @import("std"); +const posix = std.posix; + +// C types not working with Zig comptime, need allocation +const Allocator = std.mem.Allocator; + +// comptime_int +const stderr = posix.STDERR_FILENO; + +/// NuttX namespace +const NuttX = struct { + // public constant + pub const c = @cImport({ + @cInclude("nuttx/config.h"); + @cInclude("sys/mount.h"); + @cInclude("sys/ioctl.h"); + @cInclude("signal.h"); + @cInclude("stdio.h"); + @cInclude("stdlib.h"); + @cInclude("fcntl.h"); + @cInclude("debug.h"); + @cInclude("unistd.h"); + + @cInclude("nuttx/ioexpander/gpio.h"); + }); + pub fn fprint(allocator: Allocator, fd: isize, comptime fmt: [*:0]const u8, args: anytype) void { + const output = std.fmt.allocPrintZ(allocator, std.mem.span(fmt), args) catch unreachable; + _ = c.fprintf(fd, output); + } + pub fn print(allocator: Allocator, comptime fmt: [*:0]const u8, args: anytype) void { + const output = std.fmt.allocPrintZ(allocator, std.mem.span(fmt), args) catch unreachable; + _ = c.printf(output); + } +}; + +// C enum to Zig enum +const pinType = enum(NuttX.c.gpio_pintype_e) { + GPIO_INPUT_PIN = 0, + GPIO_INPUT_PIN_PULLUP = 1, + GPIO_INPUT_PIN_PULLDOWN = 2, + GPIO_OUTPUT_PIN = 3, + GPIO_OUTPUT_PIN_OPENDRAIN = 4, + GPIO_INTERRUPT_PIN = 5, + GPIO_INTERRUPT_HIGH_PIN = 6, + GPIO_INTERRUPT_LOW_PIN = 7, + GPIO_INTERRUPT_RISING_PIN = 8, + GPIO_INTERRUPT_FALLING_PIN = 9, + GPIO_INTERRUPT_BOTH_PIN = 10, + GPIO_NPINTYPES = 11, +}; + +export fn main(argc: c_int, argv: [*][*:0]u8) c_int { + var ndx: usize = 0; + var havenewtype: bool = false; + var haveout: bool = false; + var signo: c_int = 0; + var havesigno: bool = false; + var outvalue: bool = false; + var pintype: pinType = undefined; + var newpintype: pinType = undefined; + var invalue: bool = false; + var devpath: [*:0]u8 = undefined; + + // Allocate an arena allocator (raw_c_allocator uses malloc/free) + var arena = std.heap.ArenaAllocator.init(std.heap.raw_c_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + var args = std.process.argsWithAllocator(allocator) catch unreachable; + defer args.deinit(); + + if (argc < 2) { + NuttX.print(allocator, "ERROR: Missing required arguments\n", .{}); + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + + while (args.next()) |arg| { + if (std.mem.eql(u8, arg, "-h")) { + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + if (std.mem.eql(u8, arg, "-t")) { + havenewtype = true; + if (ndx >= argc) { + NuttX.print(allocator, "ERROR: Missing argument to -t\n", .{}); + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + const number = std.fmt.parseInt(c_uint, arg, 10) catch |err| { + NuttX.print(allocator, "ERROR: {s} isn't a number\n", .{@errorName(err)}); + return NuttX.c.EXIT_FAILURE; + }; + newpintype = @enumFromInt(number); + if (ndx >= argc) { + NuttX.print(allocator, "ERROR: Missing required \n", .{}); + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + } + + if (std.mem.eql(u8, arg, "-w")) { + havesigno = true; + if (ndx >= argc) { + NuttX.print(allocator, "ERROR: Missing argument to -w\n", .{}); + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + + signo = std.fmt.parseInt(c_int, arg, 10) catch |err| { + NuttX.print(allocator, "ERROR: {s} isn't a number\n", .{@errorName(err)}); + return NuttX.c.EXIT_FAILURE; + }; + if (ndx >= argc) { + NuttX.print(allocator, "ERROR: Missing required \n", .{}); + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + } + + if (ndx < argc and std.mem.eql(u8, arg, "-o")) { + if (ndx >= argc) { + NuttX.print(allocator, "ERROR: Missing argument to -o\n", .{}); + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + + if (std.mem.eql(u8, arg, "0")) { + outvalue = false; + haveout = true; + } else if (std.mem.eql(u8, arg, "1")) { + outvalue = true; + haveout = true; + } else { + NuttX.print(allocator, "ERROR: Invalid argument to -o\n", .{}); + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + + if (ndx >= argc) { + NuttX.print(allocator, "ERROR: Missing required \n", .{}); + show_usage(allocator, argv[0]); + return NuttX.c.EXIT_FAILURE; + } + } + ndx += 1; + } + + devpath = argv[ndx]; + NuttX.print(allocator, "Driver: {s}\n", .{devpath}); + + // Open the pin driver + const fd = NuttX.c.open(devpath, NuttX.c.O_RDWR); + if (fd < 0) { + NuttX.print(allocator, "ERROR: Failed to open {s}\n", .{devpath}); + return NuttX.c.EXIT_FAILURE; + } + + // Set the new pintype + + if (havenewtype) { + const ret = NuttX.c.ioctl(fd, NuttX.c.GPIOC_SETPINTYPE, @intFromEnum(newpintype)); + if (ret < 0) { + // const errcode = NuttX.c.errno; + NuttX.print(allocator, "ERROR: Failed to set pintype on {s}\n", .{devpath}); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_FAILURE; + } + } + + // Get the pin type + + var ret = NuttX.c.ioctl(fd, NuttX.c.GPIOC_PINTYPE, &pintype); + if (ret < 0) { + // const errcode = NuttX.c.errno; + NuttX.print(allocator, "ERROR: Failed to read pintype from {s}\n", .{devpath}); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_FAILURE; + } + + // Read the pin value + + ret = NuttX.c.ioctl(fd, NuttX.c.GPIOC_READ, &invalue); + if (ret < 0) { + // const errcode = NuttX.c.errno; + NuttX.print(allocator, "ERROR: Failed to read value from {s}\n", .{devpath}); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_FAILURE; + } + + // Perform the test based on the pintype and on command line options + + switch (pintype) { + .GPIO_INPUT_PIN => NuttX.print(allocator, " Input pin: Value={}\n", .{invalue}), + + .GPIO_INPUT_PIN_PULLUP => NuttX.print(allocator, " Input pin (pull-up): Value={}\n", .{invalue}), + + .GPIO_INPUT_PIN_PULLDOWN => NuttX.print(allocator, " Input pin (pull-down): Value={}\n", .{invalue}), + + .GPIO_OUTPUT_PIN, + .GPIO_OUTPUT_PIN_OPENDRAIN, + => { + NuttX.print(allocator, " Output pin: Value={}\n", .{invalue}); + + if (haveout) { + NuttX.print(allocator, " Writing: Value={}\n", .{outvalue}); + + // Write the pin value + + ret = NuttX.c.ioctl(fd, NuttX.c.GPIOC_WRITE, outvalue); + if (ret < 0) { + // const errcode = NuttX.c.errno; + NuttX.print(allocator, "ERROR: Failed to write value {} from {s}\n", .{ outvalue, devpath }); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_FAILURE; + } + + // Re-read the pin value + + ret = NuttX.c.ioctl(fd, NuttX.c.GPIOC_READ, &invalue); + if (ret < 0) { + // const errcode = NuttX.c.errno; + NuttX.print(allocator, "ERROR: Failed to re-read value from {s}\n", .{devpath}); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_FAILURE; + } + + NuttX.print(allocator, " Verify: Value={}\n", .{invalue}); + } + }, + + .GPIO_INTERRUPT_PIN, + .GPIO_INTERRUPT_HIGH_PIN, + .GPIO_INTERRUPT_LOW_PIN, + .GPIO_INTERRUPT_RISING_PIN, + .GPIO_INTERRUPT_FALLING_PIN, + .GPIO_INTERRUPT_BOTH_PIN, + => { + NuttX.print(allocator, " Interrupt pin: Value={}\n", .{invalue}); + + if (havesigno) { + // struct sigevent notify; + // struct timespec ts; + // sigset_t set; + + // notify.sigev_notify = SIGEV_SIGNAL; + // notify.sigev_signo = signo; + + // // Set up to receive signal + + // ret = ioctl(fd, GPIOC_REGISTER, ¬ify); + // if (ret < 0) + // { + // const errcode = errno; + + // fprintf(stderr, + // "ERROR: Failed to setup for signal from {s}: {d}\n", + // devpath, errcode); + + // close(fd); + // return EXIT_FAILURE; + // } + + // // Wait up to 5 seconds for the signal + + // sigemptyset(&set); + // sigaddset(&set, signo); + + // ts.tv_sec = 5; + // ts.tv_nsec = 0; + + // ret = sigtimedwait(&set, NULL, &ts); + // ioctl(fd, GPIOC_UNREGISTER, 0); + + if (ret < 0) { + // const errcode = errno; + if (signo == 0) { + NuttX.print(allocator, " [Five second timeout with no signal]\n", .{}); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_SUCCESS; + } else { + NuttX.print(allocator, "ERROR: Failed to wait signal {d} from {s}\n", .{ signo, devpath }); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_FAILURE; + } + } + + // Re-read the pin value + + ret = NuttX.c.ioctl(fd, NuttX.c.GPIOC_READ, &invalue); + if (ret < 0) { + // const errcode = NuttX.c.errno; + NuttX.print(allocator, "ERROR: Failed to re-read value from {s}\n", .{devpath}); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_FAILURE; + } + + NuttX.print(allocator, " Verify: Value={}\n", .{invalue}); + } + }, + + else => { + NuttX.print(allocator, "ERROR: Unrecognized pintype: {}\n", .{pintype}); + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_FAILURE; + }, + } + + _ = NuttX.c.close(fd); + return NuttX.c.EXIT_SUCCESS; +} + +fn show_usage(allocator: Allocator, progname: [*:0]const u8) void { + NuttX.print(allocator, "USAGE: {s} [-t ] [-w ] [-o ] \n", .{progname}); + NuttX.print(allocator, "\t\t{s} -h\n", .{progname}); + NuttX.print(allocator, "Where:\n", .{}); + NuttX.print(allocator, "\t: The full path to the GPIO pin driver.\n", .{}); + NuttX.print(allocator, "\t-t : Change the pin to this pintype (0-10):\n", .{}); + NuttX.print(allocator, "\t-w : Wait for a signal if this is an interrupt pin.\n", .{}); + NuttX.print(allocator, "\t-o : Write this value (0 or 1) if this is an output pin.\n", .{}); + NuttX.print(allocator, "\t-h: Print this usage information and exit.\n", .{}); + NuttX.print(allocator, "Pintypes:\n", .{}); + NuttX.print(allocator, "\t 0: GPIO_INPUT_PIN\n", .{}); + NuttX.print(allocator, "\t 1: GPIO_INPUT_PIN_PULLUP\n", .{}); + NuttX.print(allocator, "\t 2: GPIO_INPUT_PIN_PULLDOWN\n", .{}); + NuttX.print(allocator, "\t 3: GPIO_OUTPUT_PIN\n", .{}); + NuttX.print(allocator, "\t 4: GPIO_OUTPUT_PIN_OPENDRAIN\n", .{}); + NuttX.print(allocator, "\t 5: GPIO_INTERRUPT_PIN\n", .{}); + NuttX.print(allocator, "\t 6: GPIO_INTERRUPT_HIGH_PIN\n", .{}); + NuttX.print(allocator, "\t 7: GPIO_INTERRUPT_LOW_PIN\n", .{}); + NuttX.print(allocator, "\t 8: GPIO_INTERRUPT_RISING_PIN\n", .{}); + NuttX.print(allocator, "\t 9: GPIO_INTERRUPT_FALLING_PIN\n", .{}); + NuttX.print(allocator, "\t10: GPIO_INTERRUPT_BOTH_PIN\n", .{}); +} diff --git a/examples/hello_zig/hello_zig.zig b/examples/hello_zig/hello_zig.zig index 7d59a9ef5ed..4c8fbcbcadb 100644 --- a/examples/hello_zig/hello_zig.zig +++ b/examples/hello_zig/hello_zig.zig @@ -26,18 +26,21 @@ const std = @import("std"); //**************************************************************************** //* C API - need libc linking (freestanding libc is stubs only) //**************************************************************************** -// nuttx namespace -const nuttx = struct { +// NuttX namespace +const NuttX = struct { pub const c = @cImport({ @cInclude("nuttx/config.h"); @cInclude("stdio.h"); }); + pub fn print(comptime fmt: [*:0]const u8, args: anytype) void { + _ = printf(std.fmt.comptimePrint(std.mem.span(fmt), args)); + } }; // or (optional) const c = std.c; // from std library (non-full libc) // typedef alias -const printf = nuttx.c.printf; +const printf = NuttX.c.printf; //**************************************************************************** //* hello_zig_main //**************************************************************************** @@ -50,10 +53,6 @@ comptime { } fn hello_zig(_: c_int, _: ?[*]const [*]const u8) callconv(.C) c_int { - print("[{s}]: Hello, Zig!\n", .{nuttx.c.CONFIG_ARCH_BOARD}); + NuttX.print("[{s}]: Hello, Zig!\n", .{NuttX.c.CONFIG_ARCH_BOARD}); return 0; } - -fn print(comptime fmt: [*:0]const u8, args: anytype) void { - _ = printf(std.fmt.comptimePrint(std.mem.span(fmt), args)); -}