Module Name: src Committed By: mbalmer Date: Fri Oct 7 21:14:19 UTC 2011
Added Files: src/lib/lua/gpio: gpio.c Log Message: Interface gpio(4) from Lua. Not linked to the build. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/lib/lua/gpio/gpio.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Added files: Index: src/lib/lua/gpio/gpio.c diff -u /dev/null src/lib/lua/gpio/gpio.c:1.1 --- /dev/null Fri Oct 7 21:14:19 2011 +++ src/lib/lua/gpio/gpio.c Fri Oct 7 21:14:19 2011 @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2011 Marc Balmer <m...@msys.ch> + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* GPIO interface for Lua */ + +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> + +#include <sys/gpio.h> +#include <sys/ioctl.h> + +#define GPIO_METATABLE "GPIO object methods" + +static void +gpio_error(lua_State *L, const char *fmt, ...) +{ + va_list ap; + int len; + char *msg; + + va_start(ap, fmt); + len = vasprintf(&msg, fmt, ap); + va_end(ap); + + if (len != -1) { + lua_pushstring(L, msg); + free(msg); + } else + lua_pushstring(L, "vasprintf failed"); + lua_error(L); +} + +static int +gpio_open(lua_State *L) +{ + int *fd; + + fd = lua_newuserdata(L, sizeof(int)); + *fd = open(luaL_checkstring(L, -2), O_RDWR); + if (*fd == -1) { + gpio_error(L, "%s", strerror(errno)); + /* NOTREACHED */ + return 0; + } + luaL_getmetatable(L, GPIO_METATABLE); + lua_setmetatable(L, -2); + return 1; +} + +static int +gpio_close(lua_State *L) +{ + int *fd; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + if (*fd != -1) { + close(*fd); + *fd = -1; + } + return 0; +} + +static int +gpio_info(lua_State *L) +{ + struct gpio_info info; + int *fd; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + if (ioctl(*fd, GPIOINFO, &info) == -1) + gpio_error(L, "GPIOINFO"); + lua_pushinteger(L, info.gpio_npins); + return 1; +} + +static void +gpio_get_pin(lua_State *L, int n, struct gpio_req *req) +{ + switch (lua_type(L, n)) { + case LUA_TNUMBER: + req->gp_pin = lua_tointeger(L, n) - 1; /* 1 based! */ + break; + case LUA_TSTRING: + strlcpy(req->gp_name, lua_tostring(L, n), sizeof(req->gp_name)); + break; + default: + luaL_argerror(L, n, "expected string or integer"); + /* NOTREACHED */ + } +} + +static int +gpio_set(lua_State *L) +{ + struct gpio_set set; + int *fd; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + memset(&set, 0, sizeof(set)); + gpio_get_pin(L, 2, (struct gpio_req *)&set); + set.gp_flags = luaL_checkinteger(L, 3); + if (ioctl(*fd, GPIOSET, &set) == -1) + gpio_error(L, "GPIOSET"); + return 0; +} + +static int +gpio_unset(lua_State *L) +{ + struct gpio_set set; + int *fd; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + memset(&set, 0, sizeof(set)); + gpio_get_pin(L, 2, (struct gpio_req *)&set); + if (ioctl(*fd, GPIOUNSET, &set) == -1) + gpio_error(L, "GPIOUNSET"); + return 0; +} + +static int +gpio_read(lua_State *L) +{ + struct gpio_req req; + int *fd; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + memset(&req, 0, sizeof(req)); + gpio_get_pin(L, 2, &req); + if (ioctl(*fd, GPIOREAD, &req) == -1) + gpio_error(L, "GPIOREAD"); + lua_pushinteger(L, req.gp_value); + return 1; +} + + +static int +gpio_write(lua_State *L) +{ + struct gpio_req req; + int *fd, val; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + val = luaL_checkinteger(L, 3); + if (val != GPIO_PIN_HIGH && val != GPIO_PIN_LOW) + gpio_error(L, "%d: invalid value", val); + memset(&req, 0, sizeof(req)); + gpio_get_pin(L, 2, &req); + req.gp_value = val; + if (ioctl(*fd, GPIOWRITE, &req) == -1) + gpio_error(L, "GPIOWRITE"); + lua_pushinteger(L, req.gp_value); + return 1; +} + + +static int +gpio_toggle(lua_State *L) +{ + struct gpio_req req; + int *fd, val; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + memset(&req, 0, sizeof(req)); + gpio_get_pin(L, 2, &req); + if (ioctl(*fd, GPIOTOGGLE, &req) == -1) + gpio_error(L, "GPIOTOGGLE"); + lua_pushinteger(L, req.gp_value); + return 1; +} + + +static int +gpio_attach(lua_State *L) +{ + struct gpio_attach attach; + int *fd; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + memset(&attach, 0, sizeof(attach)); + strlcpy(attach.ga_dvname, luaL_checkstring(L, 2), + sizeof(attach.ga_dvname)); + attach.ga_offset = luaL_checkinteger(L, 3); + attach.ga_mask = luaL_checkinteger(L, 4); + if (lua_gettop(L) > 4) + attach.ga_flags = luaL_checkinteger(L, 5); + else + attach.ga_flags = 0; + + if (ioctl(*fd, GPIOATTACH, &attach) == -1) + gpio_error(L, "GPIOATTACH"); + return 0; +} + + +static int +gpio_pulse(lua_State *L) +{ + struct gpio_pulse pulse; + suseconds_t period, on, off; + double freq, dc; + int *fd; + + fd = luaL_checkudata(L, 1, GPIO_METATABLE); + freq = luaL_checknumber(L, 3); + dc = luaL_checknumber(L, 4); + + if (freq < 0.0 || (dc < 0.0 || dc >= 100.0)) + gpio_error(L, "%.f Hz, %.f%% duty cycle: invalid value", + freq, dc); + + memset(&pulse, 0, sizeof(pulse)); + gpio_get_pin(L, 2, (struct gpio_req *)&pulse); + + if (freq > 0.0 && dc > 0.0) { + period = 1000000 / freq; + on = period * dc / 100; + off = period - on; + + if (on >= 1000000) { + pulse.gp_pulse_on.tv_sec = on / 1000000; + on -= pulse.gp_pulse_on.tv_sec * 1000000; + pulse.gp_pulse_on.tv_usec = on; + } else { + pulse.gp_pulse_on.tv_sec = 0; + pulse.gp_pulse_on.tv_usec = on; + } + if (off >= 1000000) { + pulse.gp_pulse_off.tv_sec = off / 1000000; + off -= pulse.gp_pulse_off.tv_sec * 1000000; + pulse.gp_pulse_off.tv_usec = off; + } else { + pulse.gp_pulse_off.tv_sec = 0; + pulse.gp_pulse_off.tv_usec = off; + } + } else { /* gpio(4) defaults */ + freq = 1.0; + dc = 50.0; + } + + if (ioctl(*fd, GPIOPULSE, &pulse) == -1) + gpio_error(L, "GPIOPULSE"); +} + +struct constant { + char *name; + int value; +}; + +static struct constant gpio_constant[] = { + /* GPIO pin states */ + { "PIN_LOW", GPIO_PIN_LOW }, + { "PIN_HIGH", GPIO_PIN_HIGH }, + { "PIN_PULSE", GPIO_PIN_PULSE }, + + /* GPIO pin configuration flags */ + { "PIN_INPUT", GPIO_PIN_INPUT }, + { "PIN_OUTPUT", GPIO_PIN_OUTPUT }, + { "PIN_INOUT", GPIO_PIN_INOUT }, + { "PIN_OPENDRAIN", GPIO_PIN_OPENDRAIN }, + { "PIN_PUSHPULL", GPIO_PIN_PUSHPULL }, + { "PIN_TRISTATE", GPIO_PIN_TRISTATE }, + { "PIN_PULLUP", GPIO_PIN_PULLUP }, + { "PIN_PULLDOWN", GPIO_PIN_PULLDOWN }, + { "PIN_INVIN", GPIO_PIN_INVIN }, + { "PIN_INVOUT", GPIO_PIN_INVOUT }, + { "PIN_USER", GPIO_PIN_USER }, + { "PIN_PULSATE", GPIO_PIN_PULSATE }, + { "PIN_INTR", GPIO_PIN_INTR }, + { "PIN_INTR_HIGH", GPIO_PIN_INTR_HIGH }, + { "PIN_SET", GPIO_PIN_SET }, + { NULL, 0 } +}; + +static void +gpio_set_info(lua_State *L) +{ + lua_pushliteral(L, "_COPYRIGHT"); + lua_pushliteral(L, "Copyright (C) 2011 Marc Balmer <m...@msys.ch>"); + lua_settable(L, -3); + lua_pushliteral(L, "_DESCRIPTION"); + lua_pushliteral(L, "GPIO interface for Lua"); + lua_settable(L, -3); + lua_pushliteral(L, "_VERSION"); + lua_pushliteral(L, "gpio 1.0.0"); + lua_settable(L, -3); +} + +int +luaopen_gpio(lua_State* L) +{ + static const struct luaL_Reg methods[] = { + { "open", gpio_open }, + { NULL, NULL } + }; + static const struct luaL_Reg gpio_methods[] = { + { "info", gpio_info }, + { "close", gpio_close }, + { "set", gpio_set }, + { "unset", gpio_unset }, + { "read", gpio_read }, + { "write", gpio_write }, + { "toggle", gpio_toggle }, + { "attach", gpio_attach }, + { "pulse", gpio_pulse }, + { NULL, NULL } + }; + int n; + + luaL_register(L, "gpio", methods); + gpio_set_info(L); + + /* The gpio metatable */ + if (luaL_newmetatable(L, GPIO_METATABLE)) { + luaL_register(L, NULL, gpio_methods); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gpio_close); + lua_settable(L, -3); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); + lua_settable(L, -3); + + lua_pushliteral(L, "__metatable"); + lua_pushliteral(L, "must not access this metatable"); + lua_settable(L, -3); + } + lua_pop(L, 1); + + for (n = 0; gpio_constant[n].name != NULL; n++) { + lua_pushinteger(L, gpio_constant[n].value); + lua_setfield(L, -2, gpio_constant[n].name); + }; + return 1; +}