/*lcdm001.c*/
/* This is the LCDproc driver for the "LCDM001" device from kernelconcepts.de
Copyright (C) 2001 Rene Wagner <reenoo@gmx.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; either version 2 of the License, or
any later version.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */
/* This driver is mostly based on the MtxOrb driver.
See the file MtxOrb.c for copyright details */
/* The heartbeat workaround has been taken from the curses driver
See the file curses_drv.c for copyright details */
/* The function calls needed for reporting and getting settings from the
configfile have been written taking the calls in
sed1330.c ((C) by Joris Robijn) as examples*/
/* (Hopefully I have NOT forgotten any file I have stolen code from.
If so send me an e-mail or add your copyright here!) */
/* LCDM001 does NOT support custom chars
Most of the displaying problems have been fixed
using ASCII workarounds*/
/* You can modify the characters that are displayed instead of
the normal icons for the heartbeat in lcdm001.h*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
# if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
# else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
# endif
#include "lcd.h"
#include "lcdm001.h"
#include "report.h"
//#include "configfile.h"
typedef struct driver_private_data {
char device[200];
int fd;
int speed;
char icon_char;
char pause_key;
char back_key;
char forward_key;
char main_menu_key;
char *framebuf;
int width;
int height;
} PrivateData;
// Vars for the server core
MODULE_EXPORT char *api_version = API_VERSION;
MODULE_EXPORT int stay_in_foreground = 0;
MODULE_EXPORT int supports_multiple = 0;
MODULE_EXPORT char *symbol_prefix = "lcdm001_";
static void lcdm001_cursorblink(Driver *drvthis, int on);
static char lcdm001_parse_keypad_setting(Driver *drvthis, char * keyname, char * default_value);
// Parse one key from the configfile
static char lcdm001_parse_keypad_setting(Driver *drvthis, char * keyname, char * default_value)
{
char return_val = 0;
if (strcmp(drvthis->config_get_string(drvthis->name, keyname, 0, default_value), "LeftKey") == 0) {
return_val = LEFT_KEY;
} else if (strcmp(drvthis->config_get_string(drvthis->name, keyname, 0, default_value), "RightKey") == 0) {
return_val = RIGHT_KEY;
} else if (strcmp(drvthis->config_get_string(drvthis->name, keyname, 0, default_value), "UpKey") == 0) {
return_val = UP_KEY;
} else if (strcmp(drvthis->config_get_string(drvthis->name, keyname, 0, default_value), "DownKey") == 0) {
return_val = DOWN_KEY;
} else {
report(RPT_WARNING, "%s: invalid config setting for %s; using default %s",
drvthis->name, keyname, default_value);
if (strcmp(default_value, "LeftKey") == 0) {
return_val = LEFT_KEY;
} else if (strcmp(default_value, "RightKey") == 0) {
return_val = RIGHT_KEY;
} else if (strcmp(default_value, "UpKey") == 0) {
return_val = UP_KEY;
} else if (strcmp(default_value, "DownKey") == 0) {
return_val = DOWN_KEY;
}
}
return return_val;
}
/* Set cursorblink on/off */
static void
lcdm001_cursorblink(Driver *drvthis, int on)
{
PrivateData *p = drvthis->private_data;
if (on) {
write(p->fd, "~K1", 3);
debug(RPT_INFO, "%s: cursorblink turned on", drvthis->name);
} else {
write(p->fd, "~K0", 3);
debug(RPT_INFO, "%s: cursorblink turned off", drvthis->name);
}
}
/* TODO: Get lcd.framebuf to properly work as whatever driver is running...*/
/*********************************************************************
* init() should set up any device-specific stuff, and
* point all the function pointers.
*/
MODULE_EXPORT int
lcdm001_init (Driver *drvthis)
{
PrivateData *p;
struct termios portset;
/* Allocate and store private data */
p = (PrivateData *) calloc(1, sizeof(PrivateData));
if (p == NULL)
return -1;
if (drvthis->store_private_ptr(drvthis, p))
return -1;
/* initialize private data */
p->speed = B38400;
p->icon_char = '@';
p->pause_key = DOWN_KEY;
p->back_key = LEFT_KEY;
p->forward_key = RIGHT_KEY;
p->main_menu_key = UP_KEY;
p->framebuf = NULL;
p->width = LCD_DEFAULT_WIDTH;
p->height = LCD_DEFAULT_HEIGHT;
debug(RPT_INFO, "LCDM001: init(%p)", drvthis);
p->framebuf = calloc(1, p->width * p->height);
if (p->framebuf == NULL) {
report(RPT_ERR, "%s: unable to create framebuffer", drvthis->name);
return -1;
}
memset(p->framebuf, ' ', p->width * p->height);
// READ CONFIG FILE:
// which serial device should be used
strncpy(p->device, drvthis->config_get_string(drvthis->name, "Device", 0, "/dev/lcd"), sizeof(p->device));
p->device[sizeof(p->device)-1] = '\0';
report(RPT_INFO, "%s: using Device %s", drvthis->name, p->device);
// keypad settings
p->pause_key = lcdm001_parse_keypad_setting(drvthis, "PauseKey", "DownKey");
p->back_key = lcdm001_parse_keypad_setting(drvthis, "BackKey", "LeftKey");
p->forward_key = lcdm001_parse_keypad_setting(drvthis, "ForwardKey", "RightKey");
p->main_menu_key = lcdm001_parse_keypad_setting(drvthis, "MainMenuKey", "UpKey");
// Set up io port correctly, and open it...
debug(RPT_DEBUG, "%s: opening serial device: %s", __FUNCTION__, p->device);
p->fd = open(p->device, O_RDWR | O_NOCTTY | O_NDELAY);
if (p->fd == -1) {
report(RPT_ERR, "%s: open(%d) failed (%s)",
drvthis->name, p->device, strerror(errno));
if (errno == EACCES)
report(RPT_ERR, "%s: make sure you have rw access to %s!",
drvthis->name, p->device);
return -1;
}
report(RPT_INFO, "%s: opened display on %s", drvthis->name, p->device);
tcgetattr(p->fd, &portset);
#ifdef HAVE_CFMAKERAW
/* The easy way: */
cfmakeraw(&portset);
#else
/* The hard way: */
portset.c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON );
portset.c_oflag &= ~OPOST;
portset.c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
portset.c_cflag &= ~( CSIZE | PARENB | CRTSCTS );
portset.c_cflag |= CS8 | CREAD | CLOCAL;
#endif
cfsetospeed(&portset, p->speed);
cfsetispeed(&portset, p->speed);
tcsetattr(p->fd, TCSANOW, &portset);
tcflush(p->fd, TCIOFLUSH);
// Reset and clear the LCDM001
write(p->fd, "~C", 2);
//Set cursorblink default
lcdm001_cursorblink(drvthis, DEFAULT_CURSORBLINK);
// Turn all LEDs off
lcdm001_output(drvthis, 0);
report(RPT_DEBUG, "%s: init() done", drvthis->name);
return 1;
}
/* Below here, you may use either lcd.framebuf or driver->framebuf..
* lcd.framebuf will be set to the appropriate buffer before calling
* your driver.
*/
MODULE_EXPORT void
lcdm001_close (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
if (p != NULL) {
if (p->framebuf != NULL)
free(p->framebuf);
p->framebuf = NULL;
if (p->fd >= 0) {
//switch off all LEDs
lcdm001_output(drvthis, 0);
close(p->fd);
}
p->fd = -1;
free(p);
}
drvthis->store_private_ptr(drvthis, NULL);
report(RPT_INFO, "%s: closed", drvthis->name);
}
/////////////////////////////////////////////////////////////////
// Returns the display width
//
MODULE_EXPORT int
lcdm001_width (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->width;
}
/////////////////////////////////////////////////////////////////
// Returns the display height
//
MODULE_EXPORT int
lcdm001_height (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->height;
}
/////////////////////////////////////////////////////////////////
// Clears the LCD screen
//
MODULE_EXPORT void
lcdm001_clear (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
memset(p->framebuf, ' ', p->width * p->height);
/* instant clear is NOT neccessary, it only makes the display flicker */
//write(fd, "~C", 2);
debug(RPT_DEBUG, "LCDM001: cleared screen");
}
//////////////////////////////////////////////////////////////////
// Flushes all output to the lcd...
//
MODULE_EXPORT void
lcdm001_flush (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
//TODO: Check whether this is still correct
write(p->fd, p->framebuf, p->width * p->height);
debug(RPT_DEBUG, "LCDM001: frame buffer flushed");
}
/////////////////////////////////////////////////////////////////
// Prints a character on the lcd display, at position (x,y). The
// upper-left is (1,1), and the lower right should be (20,4).
//
MODULE_EXPORT void
lcdm001_chr (Driver *drvthis, int x, int y, char c)
{
PrivateData *p = drvthis->private_data;
if (c == '\0')
c = p->icon_char; //heartbeat workaround
y--; x--; // translate to 0-coords
if ((x >= 0) && (y >= 0) && (x < p->width) && (y < p->height))
p->framebuf[(y * p->width) + x] = c;
debug(RPT_DEBUG, "LCDM001: writing character %02X to position (%d,%d)", c, x, y);
}
/////////////////////////////////////////////////////////////////
// Prints a string on the lcd display, at position (x,y). The
// upper-left is (1,1), and the lower right should be (20,4).
//
MODULE_EXPORT void
lcdm001_string (Driver *drvthis, int x, int y, const char string[])
{
PrivateData *p = drvthis->private_data;
int i;
x--; y--; // Convert 1-based coords to 0-based...
if ((y < 0) || (y >= p->height))
return;
for (i = 0; (string[i] != '\0') && (x < p->width); i++, x++) {
if (x >= 0) // no write left of left border
p->framebuf[(y * p->width) + x] = string[i];
}
debug(RPT_DEBUG, "LCDM001: printed string at (%d,%d)", x, y);
}
/////////////////////////////////////////////////////////////////
// Controls LEDs
MODULE_EXPORT void
lcdm001_output (Driver *drvthis, int state)
{
PrivateData *p = drvthis->private_data;
char out[5];
int one = (state & 0xFF);
int two = (state > 255) ? ((state >> 8) & 0xFF) : 0;
snprintf(out, sizeof(out), "~L%c%c", one, two);
write(p->fd, out, 4);
debug(RPT_DEBUG, "LCDM001: current LED state %d", state);
}
/////////////////////////////////////////////////////////////////
// Draws a vertical bar, from the bottom of the screen up.
//
MODULE_EXPORT void
lcdm001_old_vbar(Driver *drvthis, int x, int len)
{
PrivateData *p = drvthis->private_data;
int y = p->height;
debug(RPT_DEBUG , "LCDM001: vertical bar at %d set to %d", x, len);
while (len >= LCD_DEFAULT_CELLHEIGHT) {
lcdm001_chr(drvthis, x, y, 0xFF);
len -= LCD_DEFAULT_CELLHEIGHT;
y--;
}
if (!len)
return;
//TODO: Distinguish between len>=4 and len<4
}
/////////////////////////////////////////////////////////////////
// Draws a horizontal bar to the right.
//
MODULE_EXPORT void
lcdm001_old_hbar(Driver *drvthis, int x, int y, int len)
{
PrivateData *p = drvthis->private_data;
if ((y <= 0) || (y > p->height))
return;
debug(RPT_DEBUG, "LCDM001: horizontal bar at %d set to %d", x, len);
//TODO: Improve this function
while ((x <= p->width) && (len > 0)) {
if (len < LCD_DEFAULT_CELLWIDTH) {
//lcdm001_chr(x, y, 0x98 + len);
break;
}
lcdm001_chr(drvthis, x, y, 0xFF);
len -= LCD_DEFAULT_CELLWIDTH;
x++;
}
return;
}
/////////////////////////////////////////////////////////////////
// Sets character 0 to an icon...
//
MODULE_EXPORT void
lcdm001_old_icon (Driver *drvthis, int which, char dest)
{
PrivateData *p = drvthis->private_data;
/*Heartbeat workaround:
As custom chars are not supported OPEN_HEART
and FILLED_HEART are displayed instead.
Change them in lcdm001.h*/
if (dest == 0)
switch (which) {
case 0:
p->icon_char = OPEN_HEART;
break;
case 1:
p->icon_char = FILLED_HEART;
break;
default:
p->icon_char = PAD;
break;
}
}
//////////////////////////////////////////////////////////////////////
// Tries to read a character from an input device...
//
// Return NULL for "nothing available".
//
MODULE_EXPORT const char *
lcdm001_get_key (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
char in = '\0';
const char *key = NULL;
read(p->fd, &in, 1);
if (in == p->pause_key) {
key = "Enter";
} else if (in == p->back_key) {
key = "Left";
} else if (in == p->forward_key) {
key = "Right";
} else if (in == p->main_menu_key) {
key = "Escape";
}
debug(RPT_DEBUG, "%s, get_key: %s",
drvthis->name, (key != NULL) ? key : "<null>");
return key;
}
syntax highlighted by Code2HTML, v. 0.9.1