From 6588d33321e46331a226844ca5fe553f9329e3a3 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Sun, 17 Apr 2011 16:59:51 +0200 Subject: [PATCH] inital commit --- .gitignore | 2 + Makefile | 48 +++++++++ TODO | 13 +++ event.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++ event.h | 42 ++++++++ lcd.c | 224 ++++++++++++++++++++++++++++++++++++++ lcd.h | 13 +++ list.h | 268 +++++++++++++++++++++++++++++++++++++++++++++ logging.c | 114 ++++++++++++++++++++ logging.h | 19 ++++ qnaplcd.c | 71 ++++++++++++ 11 files changed, 1124 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 TODO create mode 100644 event.c create mode 100644 event.h create mode 100644 lcd.c create mode 100644 lcd.h create mode 100644 list.h create mode 100644 logging.c create mode 100644 logging.h create mode 100644 qnaplcd.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..607be09 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +qnaplcd diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..17fb142 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +DESTDIR = +BINARY_DIR = /usr/local/bin +CONFIG_DIR = /usr/local/etc +LOG_DIR = /var/log +PID_DIR = /var/run + +# ############################ + +SRC = $(wildcard *.c) +TARGET = qnaplcd +BUILD_DIR = build +CFLAGS = -O2 -g -pipe -Wall -Wno-unused-result +CFLAGS += -MMD -MF $(BUILD_DIR)/$(*D)/$(*F).d +LDFLAGS = + +# ############################ + +ifeq ($(strip $(wildcard $(DESTDIR)$(CONFIG_DIR)/$(TARGET).conf)),) + NEWCONF=$(TARGET).conf +else + NEWCONF=$(TARGET).conf.dist +endif + +# ############################ + +all: $(TARGET) + +$(TARGET): $(patsubst %,$(BUILD_DIR)/%, $(SRC:.c=.o)) + @echo " Linking file: $@" + @$(CC) $(LDFLAGS) $^ -o $@ + +$(BUILD_DIR)/%.o: %.c $(MAKEFILE_LIST) + @echo " Building file: $<" + @$(shell test -d $(BUILD_DIR)/$(*D) || mkdir -p $(BUILD_DIR)/$(*D)) + @$(CC) $(CFLAGS) -o $@ -c $< + +install: $(TARGET) + install -D -m 755 -s $(TARGET) $(DESTDIR)$(BINARY_DIR)/$(TARGET) + install -D -m 644 $(TARGET).conf $(DESTDIR)$(CONFIG_DIR)/$(NEWCONF) + sed -i -e "s:^logfile .*$$:logfile $(LOG_DIR)/$(TARGET).log:" \ + -e "s:^pidfile .*$$:pidfile $(PID_DIR)/$(TARGET).pid:" \ + $(DESTDIR)$(CONFIG_DIR)/$(NEWCONF) + install -d -m 755 $(DESTDIR)$(LOG_DIR) + +clean: + rm -rf $(BUILD_DIR) $(TARGET) + +include $(shell find $(BUILD_DIR) -name \*.d 2> /dev/null) diff --git a/TODO b/TODO new file mode 100644 index 0000000..331696b --- /dev/null +++ b/TODO @@ -0,0 +1,13 @@ +- dynamic pages +- refresh of dynamic pages (clock, loadavg) +- scrolling +- daemonize: pid/log/config-file + +- disk utilization (MB/GB) + - mntpoints given in config + +- disk temperature / idle state + - devicenames / timeoutvalue given in config + +- network usage? +- cpu usage? diff --git a/event.c b/event.c new file mode 100644 index 0000000..4e52c31 --- /dev/null +++ b/event.c @@ -0,0 +1,310 @@ +/*************************************************************************** + * Copyright (C) 03/2010 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include + +#include +#include +#include + +#include "list.h" +#include "logging.h" + +#include "event.h" + +static LIST_HEAD(event_fd_list); +static LIST_HEAD(event_timeout_list); + +struct event_fd { + struct list_head list; + unsigned int flags; + int fd; + int (*read_cb)(int fd, void *privdata); + int (*write_cb)(int fd, void *privdata); + void *read_priv; + void *write_priv; +}; + +struct event_timeout { + struct list_head list; + unsigned int flags; + struct timeval intervall; + struct timeval nextrun; + int (*callback)(void *privdata); + void *privdata; +}; + +struct event_fd * event_add_fd( + struct event_fd *entry, + int fd, + int type, + int (*callback)(int fd, void *privdata), + void *privdata) +{ + /* check valid filediskriptor */ + if (fd < 0 || fd > FD_SETSIZE) { + log_print(LOG_ERROR, "event_add_fd(): invalid fd"); + return NULL; + } + + /* check valid type (read/write) */ + if (!(type & FD_TYPES)) { + log_print(LOG_ERROR, "event_add_fd(): invalid type"); + return NULL; + } + + /* create new entry */ + if (entry == NULL) { + entry = malloc(sizeof(struct event_fd)); + if (entry == NULL) { + log_print(LOG_ERROR, "event_add_fd(): out of memory"); + return NULL; + } + + memset(entry, 0, sizeof(struct event_fd)); + entry->flags |= EVENT_NEW; + entry->fd = fd; + + /* put it on the list */ + list_add_tail(&entry->list, &event_fd_list); + } + + if (type & FD_READ) { + entry->flags = (callback != NULL) ? (entry->flags | FD_READ | EVENT_NEW) : (entry->flags & ~FD_READ); + entry->read_cb = callback; + entry->read_priv = privdata; + + } else if (type & FD_WRITE) { + entry->flags = (callback != NULL) ? (entry->flags | FD_WRITE | EVENT_NEW) : (entry->flags & ~FD_WRITE); + entry->write_cb = callback; + entry->write_priv = privdata; + } + + return entry; +} + +int event_get_fd(struct event_fd *entry) +{ + return (entry != NULL) ? entry->fd: -1; +} + +void event_remove_fd(struct event_fd *entry) +{ + /* mark the event as deleted -> remove in select() loop */ + entry->flags |= EVENT_DELETE; +} + +static void add_timeval(struct timeval *ret, struct timeval *a, struct timeval *b) +{ + ret->tv_usec = a->tv_usec + b->tv_usec; + ret->tv_sec = a->tv_sec + b->tv_sec; + + if (ret->tv_usec >= 1000000) { + ret->tv_usec -= 1000000; + ret->tv_sec++; + } +} + +static void sub_timeval(struct timeval *ret, struct timeval *a, struct timeval *b) +{ + ret->tv_usec = a->tv_usec - b->tv_usec; + ret->tv_sec = a->tv_sec - b->tv_sec; + + if (ret->tv_usec < 0) { + ret->tv_usec += 1000000; + ret->tv_sec--; + } +} + +static int cmp_timeval(struct timeval *a, struct timeval *b) +{ + if (a->tv_sec > b->tv_sec) + return -1; + + if (a->tv_sec < b->tv_sec) + return 1; + + if (a->tv_usec > b->tv_usec) + return -1; + + if (a->tv_usec < b->tv_usec) + return 1; + + return 0; +} + +static void schedule_nextrun(struct event_timeout *entry, struct timeval *now) +{ + add_timeval(&entry->nextrun, now, &entry->intervall); + + struct event_timeout *search; + list_for_each_entry(search, &event_timeout_list, list) { + if (search->nextrun.tv_sec > entry->nextrun.tv_sec) { + list_add_tail(&entry->list, &search->list); + return; + + } else if (search->nextrun.tv_sec == entry->nextrun.tv_sec && + search->nextrun.tv_usec > entry->nextrun.tv_usec) { + list_add_tail(&entry->list, &search->list); + return; + } + } + list_add_tail(&entry->list, &event_timeout_list); +} + +struct event_timeout * event_add_timeout( + struct timeval *timeout, + int (*callback)(void *privdata), + void *privdata) +{ + struct event_timeout *entry; + entry = malloc(sizeof(struct event_timeout)); + if (entry == NULL) { + log_print(LOG_ERROR, "event_add_timeout(): out of memory"); + return NULL; + } + + entry->flags = 0; + memcpy(&entry->intervall, timeout, sizeof(entry->intervall)); + entry->callback = callback; + entry->privdata = privdata; + + struct timeval now; + gettimeofday(&now, NULL); + + schedule_nextrun(entry, &now); + return entry; +} + +void event_remove_timeout(struct event_timeout *entry) +{ + /* mark the event as deleted -> remove in select() loop */ + entry->flags |= EVENT_DELETE; +} + +int event_loop(int (*callback)(void *privdata), void *privdata) +{ + fd_set *fdsets = malloc(sizeof(fd_set) * 2); + if (fdsets == NULL) { + log_print(LOG_ERROR, "event_loop(): out of memory"); + return -1; + } + + while (1) { + struct timeval timeout, *timeout_p = NULL; + if (!list_empty(&event_timeout_list)) { + struct timeval now; + gettimeofday(&now, NULL); + + struct event_timeout *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &event_timeout_list, list) { + if (entry->flags & EVENT_DELETE) { + list_del(&entry->list); + free(entry); + continue; + } + + /* first timeout not elapsed, exit search (since list is sorted) */ + if (cmp_timeval(&entry->nextrun, &now) == -1) + break; + + /* remove event from list */ + list_del(&entry->list); + + /* execute callback, when callback returns 0 -> schedule event again */ + if (entry->callback(entry->privdata)) { + free(entry); + + } else { + schedule_nextrun(entry, &now); + } + } + + if (!list_empty(&event_timeout_list)) { + entry = list_entry(event_timeout_list.next, typeof(*entry), list); + + /* calc select() timeout */ + sub_timeval(&timeout, &entry->nextrun, &now); + timeout_p = &timeout; + } + } + + fd_set *readfds = NULL, *writefds = NULL; + struct event_fd *entry, *tmp; + int maxfd = -1; + + list_for_each_entry_safe(entry, tmp, &event_fd_list, list) { + entry->flags &= ~EVENT_NEW; + + if (entry->flags & EVENT_DELETE) { + list_del(&entry->list); + free(entry); + continue; + } + + if (entry->flags & FD_READ) { + if (readfds == NULL) { + readfds = &fdsets[0]; + FD_ZERO(readfds); + } + FD_SET(entry->fd, readfds); + } + + if (entry->flags & FD_WRITE) { + if (writefds == NULL) { + writefds = &fdsets[1]; + FD_ZERO(writefds); + } + FD_SET(entry->fd, writefds); + } + + maxfd = (entry->fd > maxfd) ? entry->fd : maxfd; + } + + /* exit loop if callback returns true */ + if (callback != NULL && callback(privdata) != 0) + break; + + int i = select(maxfd +1, readfds, writefds, NULL, timeout_p); + if (i < 0 && errno == EINTR) { + errno = 0; + continue; + + } else if (i < 0) { + log_print(LOG_ERROR, "event_loop(): select():"); + continue; + + } else if (i == 0) { + continue; + } + + list_for_each_entry(entry, &event_fd_list, list) { + if (((entry->flags & (FD_READ | EVENT_NEW)) == FD_READ) && FD_ISSET(entry->fd, readfds)) + if (entry->read_cb(entry->fd, entry->read_priv) != 0) + entry->flags |= EVENT_DELETE; + + if (((entry->flags & (FD_WRITE | EVENT_NEW)) == FD_WRITE) && FD_ISSET(entry->fd, writefds)) + if (entry->write_cb(entry->fd, entry->write_priv) != 0) + entry->flags |= EVENT_DELETE; + } + } + free(fdsets); + return 0; +} diff --git a/event.h b/event.h new file mode 100644 index 0000000..cb8b0d4 --- /dev/null +++ b/event.h @@ -0,0 +1,42 @@ +#ifndef _EVENT_H_ +#define _EVENT_H_ + +#include + +#define EVENT_NEW 0x1000 +#define EVENT_DELETE 0x2000 + +#define FD_READ 0x0001 +#define FD_WRITE 0x0002 +#define FD_TYPES (FD_READ | FD_WRITE) + +#define event_add_readfd(entry, fd, callback, privdata) \ + event_add_fd(entry, fd, FD_READ, callback, privdata) + +#define event_add_writefd(entry, fd, callback, privdata) \ + event_add_fd(entry, fd, FD_WRITE, callback, privdata) + +/* inner details are not visible to external users (TODO: size unknown) */ +struct event_fd; +struct event_timeout; + +struct event_fd * event_add_fd( + struct event_fd *entry, + int fd, + int type, + int (*callback)(int fd, void *privdata), + void *privdata); + +int event_get_fd(struct event_fd *entry); +void event_remove_fd(struct event_fd *entry); + +struct event_timeout * event_add_timeout( + struct timeval *timeout, + int (*callback)(void *privdata), + void *privdata); + +void event_remove_timeout(struct event_timeout *entry); + +int event_loop(int (*callback)(void *privdata), void *privdata); + +#endif /* _EVENT_H_ */ diff --git a/lcd.c b/lcd.c new file mode 100644 index 0000000..0245e0c --- /dev/null +++ b/lcd.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "event.h" +#include "lcd.h" +#include "logging.h" + +#define _LCD_DEBUG 1 + +#define A125_CMD_GETBUTTON { 0x4D, 0x06 } // not tried +#define A125_CMD_SETLINE { 0x4D, 0x0C, 0x00, 0x10 } // [2] is line, append 16 chars +#define A125_CMD_CLEAR { 0x4D, 0x0D } // works, but slow +#define A125_CMD_BACKLIGHT { 0x4D, 0x5E, 0x00 } // [2] is on/off +#define A125_CMD_RESET { 0x4D, 0xFF } + +#define A125_EVENT_ID { 0x53, 0x01 } // never seen +#define A125_EVENT_BUTTON1 { 0x53, 0x05, 0x00, 0x01 } +#define A125_EVENT_BUTTON2 { 0x53, 0x05, 0x00, 0x02 } +#define A125_EVENT_VERSION { 0x53, 0x08 } // never seen +#define A125_EVENT_ACTINFO { 0x53, 0xAA } +#define A125_EVENT_INVALID { 0x53, 0xFB } // never seen + +struct lcddev { + int fd; + char device[32]; + struct termios oldtio; + + int backlight_timeout; + int (*button_cb)(struct lcddev *dev, int button); + + struct event_fd *read_event; + struct event_timeout *backlight_event; +}; + +static void lcd_close(struct lcddev *dev) +{ + tcsetattr(dev->fd, TCSANOW, &dev->oldtio); + close(dev->fd); +} + +static int lcd_open(struct lcddev *dev, const char *device) +{ + int fd = open(device, O_RDWR); + if (fd < 0) { + log_print(LOG_ERROR, "failed to open '%s'", device); + return -1; + } + + strncpy(dev->device, device, sizeof(dev->device)); + dev->fd = fd; + + tcgetattr(dev->fd, &dev->oldtio); + + struct termios newtio; + memset(&newtio, 0, sizeof(newtio)); + newtio.c_iflag |= IGNBRK; + newtio.c_lflag &= ~(ISIG | ICANON | ECHO); + newtio.c_cflag = B1200 | CS8 | CLOCAL | CREAD; + newtio.c_cc[VMIN] = 1; + newtio.c_cc[VTIME] = 0; + cfsetospeed(&newtio, B1200); + cfsetispeed(&newtio, B1200); + + int err = tcsetattr(dev->fd, TCSAFLUSH, &newtio); + if (err < 0) { + log_print(LOG_ERROR, "failed to set termios"); + close(dev->fd); + return -1; + } + + return 0; +} + +static int lcd_read(struct lcddev *dev, const char *buf, int len) +{ + int retval = 0, i, cnt = 0; + while (cnt < len) { + retval = read(dev->fd, (char *)buf + cnt, len - cnt); + if (retval <= 0) + break; + + cnt += retval; + } +#if _LCD_DEBUG + fprintf(stderr, "lcd_read[%d/%d]: ", cnt, len); + for (i = 0; i < cnt; i++) + fprintf(stderr, "0x%X ", buf[i]); + fprintf(stderr, "\n"); +#endif + return (cnt != 0) ? cnt : retval; +} + +static int lcd_write(struct lcddev *dev, char *buf, int len) +{ + int retval, i; + + retval = write(dev->fd, buf, len); +#if _LCD_DEBUG + fprintf(stderr, "lcd_write[%d/%d]: ", retval, len); + for (i = 0; i < retval; i++) + fprintf(stderr, "0x%X ", buf[i]); + fprintf(stderr, "\n"); +#endif + return retval; +} + +static int lcd_reset(struct lcddev *dev) +{ + char cmd[] = A125_CMD_RESET; + lcd_write(dev, cmd, sizeof(cmd)); + + char event[2], expect[] = A125_EVENT_ACTINFO; + if (lcd_read(dev, event, sizeof(event)) != sizeof(event)) + return -1; + + return !memcmp(event, expect, sizeof(event)); +} + +static void lcd_backlight(struct lcddev *dev, int mode) +{ + char cmd[] = A125_CMD_BACKLIGHT; + + cmd[2] = (mode) ? 0x01 : 0x00; + lcd_write(dev, cmd, sizeof(cmd)); +} + +int lcd_setline(struct lcddev *dev, int line, const char *buf) +{ + char cmd[20] = A125_CMD_SETLINE; + cmd[2] = (line) ? 0x01 : 0x00; + + memset(cmd +4, ' ', 16); + int len = strlen(buf); + memcpy(cmd +4, buf, (len > 16) ? 16 : len); + + lcd_write(dev, cmd, sizeof(cmd)); + return 0; +} + +static int lcd_backlight_timeout_cb(void *privdata) +{ + struct lcddev *dev = (struct lcddev *)privdata; + dev->backlight_event = NULL; + + lcd_backlight(dev, 0); + return -1; /* singleshot */ +} + +static void lcd_trigger_backlight(struct lcddev *dev) +{ + if (dev->backlight_event != NULL) { + event_remove_timeout(dev->backlight_event); + dev->backlight_event = NULL; + + } else { + lcd_backlight(dev, 1); + } + + struct timeval tv = { .tv_sec = dev->backlight_timeout }; + dev->backlight_event = event_add_timeout(&tv, lcd_backlight_timeout_cb, dev); +} + +static int lcd_read_cb(int fd, void *privdata) +{ + struct lcddev *dev = (struct lcddev *)privdata; + char buf[4]; + + if (lcd_read(dev, buf, sizeof(buf)) != sizeof(buf)) { + log_print(LOG_WARN, "lcd_read_cb(): invalid read"); + return 0; + } + + char expect1[] = A125_EVENT_BUTTON1; + char expect2[] = A125_EVENT_BUTTON2; + if (memcmp(buf, expect1, sizeof(buf)) == 0) { + dev->button_cb(dev, LCD_BUTTON1); + lcd_trigger_backlight(dev); + + } else if (memcmp(buf, expect2, sizeof(buf)) == 0) { + dev->button_cb(dev, LCD_BUTTON2); + lcd_trigger_backlight(dev); + } + + return 0; +} + +int lcd_init(const char *devicename, int backlight_timeout, int (*button_cb)(struct lcddev *dev, int button)) +{ + struct lcddev *dev = malloc(sizeof(struct lcddev)); + if (dev == NULL) { + log_print(LOG_ERROR, "lcd_init(): out of memory"); + return -1; + } + + memset(dev, 0, sizeof(struct lcddev)); + + if (lcd_open(dev, devicename) < 0) { + free(dev); + return -1; + } + + if (lcd_reset(dev) < 0) { + log_print(LOG_ERROR, "lcd_init(): could not reset LCD"); + lcd_close(dev); + free(dev); + return -1; + } + + dev->backlight_timeout = backlight_timeout; + lcd_trigger_backlight(dev); + + dev->button_cb = button_cb; + button_cb(dev, LCD_BUTTON0); + + dev->read_event = event_add_readfd(NULL, dev->fd, lcd_read_cb, dev); + return 0; +} diff --git a/lcd.h b/lcd.h new file mode 100644 index 0000000..811f8fa --- /dev/null +++ b/lcd.h @@ -0,0 +1,13 @@ +#ifndef _LCD_H_ +#define _LCD_H_ + +#define LCD_BUTTON0 0x00 +#define LCD_BUTTON1 0x01 +#define LCD_BUTTON2 0x02 + +struct lcddev; /* private data */ + +int lcd_init(const char *device, int backlight_timeout, int (*button_callback)(struct lcddev *dev, int button)); +int lcd_setline(struct lcddev *dev, int line, const char *text); + +#endif /* _LCD_H_ */ diff --git a/list.h b/list.h new file mode 100644 index 0000000..61f8d93 --- /dev/null +++ b/list.h @@ -0,0 +1,268 @@ +#ifndef _LIST_H_ +#define _LIST_H_ + +/* + * stolen from linux kernel 2.6.11 (http://kernel.org/) + * linux/include/linux/stddef.h (offsetoff) + * linux/include/linux/kernel.h (container_of) + * linux/include/linux/list.h (*list*) + * linux/include/linux/netfilter_ipv4/listhelp.h (LIST_FIND) + * + * modified by Olaf Rempel + */ +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/* + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/* + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/* + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +/* + * list_del_init - deletes entry from list and reinitialize it. + * entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/* + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/* + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/* + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/* + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/* + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/* + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/* + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/* + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/* + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/* + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/* + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/* + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + + +/* Return pointer to first true entry, if any, or NULL. A macro + required to allow inlining of cmpfn. */ +#define LIST_FIND(head, cmpfn, type, args...) \ +({ \ + const struct list_head *__i, *__j = NULL; \ + \ + list_for_each(__i, (head)) \ + if (cmpfn((const type)__i , ## args)) { \ + __j = __i; \ + break; \ + } \ + (type)__j; \ +}) + +#endif /* _LIST_H_ */ diff --git a/logging.c b/logging.c new file mode 100644 index 0000000..ddc1128 --- /dev/null +++ b/logging.c @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 07/2007 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include + +#include +#include +#include +#include + +#include "logging.h" + +#define BUFSIZE 8192 + +static FILE *log_fd = NULL; +static int log_prio = LOG_EVERYTIME; +static char *buffer = NULL; + +int log_print(int prio, const char *fmt, ...) +{ + va_list az; + int len = 0, retval; + + if (prio < log_prio) + return 0; + + if (buffer == NULL) { + buffer = malloc(BUFSIZE); + if (buffer == NULL) { + fprintf(stderr, "log_print(): out of memory\n"); + return -1; + } + } + + if (log_fd != NULL) { + time_t tzgr; + time(&tzgr); + + len += strftime(buffer, BUFSIZE, "%b %d %H:%M:%S :", localtime(&tzgr)); + } + + va_start(az, fmt); + len += vsnprintf(buffer + len, BUFSIZE - len, fmt, az); + va_end(az); + + if (len < 0 || len >= BUFSIZE) { + errno = 0; + return log_print(LOG_ERROR, "log_print: arguments too long"); + } + + if (errno) { + len += snprintf(buffer + len, BUFSIZE - len, ": %s", strerror(errno)); + errno = 0; + } + + retval = fprintf((log_fd ? log_fd : stderr), "%s\n", buffer); + fflush(log_fd); + return retval; +} + +void log_close(void) +{ + if (buffer) { + free(buffer); + buffer = NULL; + } + + if (log_fd) { + fclose(log_fd); + log_fd = NULL; + } +} + +int log_init(const char *logfile) +{ + if (log_fd != NULL) + log_close(); + + log_fd = fopen(logfile, "a"); + if (log_fd == NULL) { + fprintf(stderr, "log_init(): can not open logfile"); + return -1; + } + + if (fcntl(fileno(log_fd), F_SETFD, FD_CLOEXEC) < 0) { + fprintf(stderr, "log_init(): fcntl(FD_CLOEXEC)"); + return -1; + } + + log_prio = LOG_EVERYTIME; + return 0; +} + +void log_setprio(int prio) +{ + log_prio = prio; +} diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..82d028d --- /dev/null +++ b/logging.h @@ -0,0 +1,19 @@ +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#define LOG_DEBUG 5 +#define LOG_INFO 4 +#define LOG_NOTICE 3 +#define LOG_WARN 2 +#define LOG_ERROR 1 +#define LOG_CRIT 0 + +#define LOG_EVERYTIME 0 + +int log_init(const char *logfile); +void log_close(void); +void log_setprio(int prio); + +int log_print(int prio, const char *fmt, ... ); + +#endif /* _LOGGING_H_ */ diff --git a/qnaplcd.c b/qnaplcd.c new file mode 100644 index 0000000..209e6ec --- /dev/null +++ b/qnaplcd.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +#include "event.h" +#include "lcd.h" +#include "logging.h" + +#define LCD_DEVICE "/dev/ttyS0" +#define LCD_TIMEOUT 10 + +#define PAGE_MAX 7 + +static int page; + +static int button_callback(struct lcddev *dev, int button) +{ + if (button == LCD_BUTTON2) + page = (page < PAGE_MAX) ? page +1 : 0; + + else if (button == LCD_BUTTON1) + page = (page > 0) ? page -1 : PAGE_MAX; + + switch (page) { + case 0: + lcd_setline(dev, 0, "Debian 5.0 Lenny"); + lcd_setline(dev, 1, "2.6.34-kirkwood"); + break; + case 1: + lcd_setline(dev, 0, "Hostname:"); + lcd_setline(dev, 1, "storenix.lan"); + break; + case 2: + lcd_setline(dev, 0, "Address(br0):"); + lcd_setline(dev, 1, "10.10.250.135"); + break; + case 3: + lcd_setline(dev, 0, "Netmask(br0):"); + lcd_setline(dev, 1, "255.255.0.0"); + break; + case 4: + lcd_setline(dev, 0, "Gateway(br0):"); + lcd_setline(dev, 1, "10.10.250.250"); + break; + case 5: + lcd_setline(dev, 0, "LoadAVG:"); + lcd_setline(dev, 1, "x.xx x.xx x.xx"); + break; + case 6: + lcd_setline(dev, 0, "Uptime:"); + lcd_setline(dev, 1, " XXXd XX:XX:XX"); + break; + case 7: + lcd_setline(dev, 0, "Time: XX:XX:XX"); + lcd_setline(dev, 1, "Date: XX-XX-XXXX"); + break; + } + + return 0; +} + +int main(int argc, const char *argv[]) +{ + if (lcd_init(LCD_DEVICE, LCD_TIMEOUT, &button_callback) < 0) + return -1; + + event_loop(NULL, NULL); + + return 0; +}