From cfe1bd1a1e3ade8ac49ab369829bdcf7b542eeba Mon Sep 17 00:00:00 2001 From: robijn Date: Sat, 27 Apr 2002 12:52:18 +0000 Subject: [PATCH] Added drivers icp_a106 and lcterm written by Michael Schwingen. --- LCDd.conf | 15 + acinclude.m4 | 13 +- server/drivers/Makefile.am | 8 +- server/drivers/icp_a106.c | 381 ++++++++++++++++++ server/drivers/icp_a106.h | 53 +++ server/drivers/lcterm.c | 786 +++++++++++++++++++++++++++++++++++++ server/drivers/lcterm.h | 49 +++ 7 files changed, 1301 insertions(+), 4 deletions(-) create mode 100644 server/drivers/icp_a106.c create mode 100644 server/drivers/icp_a106.h create mode 100644 server/drivers/lcterm.c create mode 100644 server/drivers/lcterm.h diff --git a/LCDd.conf b/LCDd.conf index 1ad86c6..f084ec7 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -285,6 +285,21 @@ MainMenuKey=RightKey # You can rearrange the settings here. # If your device is broken, have a look at server/drivers/lcdm001.h + + +[lcterm] +# HNE LCTerm driver +Device=/dev/ttyS1 +size=16x2 + + + +[icp_a106] +# ICP A106 driver +Device=/dev/ttyS1 + + + [lirc] # Lirc input driver diff --git a/acinclude.m4 b/acinclude.m4 index 2ed512b..2fe3ed1 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -8,12 +8,13 @@ AC_ARG_ENABLE(drivers, [ mtxorb,cfontz,cfontz633,curses,text,lb216,] [ hd44780,joy,irman,lirc,bayrad,glk,] [ stv5730,sed1330,sed1520,svga,lcdm001,t6963] + [ lcterm,icp_a106] [ \"all\" compiles all drivers], drivers="$enableval", - drivers=[lcdm001,mtxorb,cfontz,cfontz633,,curses,text,lb216,bayrad,glk]) + drivers=[lcdm001,mtxorb,cfontz,cfontz633,curses,text,lb216,bayrad,glk]) if test "$drivers" = "all"; then - drivers=[mtxorb,cfontz,cfontz633,curses,text,lb216,hd44780,joy,irman,lirc,bayrad,glk,stv5730,sed1330,sed1520,svga,lcdm001,t6963] + drivers=[mtxorb,cfontz,cfontz633,curses,text,lb216,hd44780,joy,irman,lirc,bayrad,glk,stv5730,sed1330,sed1520,svga,lcdm001,t6963,lcterm,icp_a106] fi drivers=`echo $drivers | sed 's/,/ /g'` @@ -45,6 +46,14 @@ fi DRIVERS="$DRIVERS CFontz633${SO}" actdrivers=["$actdrivers cfontz633"] ;; + lcterm) + DRIVERS="$DRIVERS lcterm${SO}" + actdrivers=["$actdrivers lcterm"] + ;; + icp_a106) + DRIVERS="$DRIVERS icp_a106${SO}" + actdrivers=["$actdrivers icp_a106"] + ;; sli) DRIVERS="$DRIVERS wirz-sli${SO}" actdrivers=["$actdrivers sli"] diff --git a/server/drivers/Makefile.am b/server/drivers/Makefile.am index 5e4e63a..0fbdb92 100644 --- a/server/drivers/Makefile.am +++ b/server/drivers/Makefile.am @@ -18,19 +18,21 @@ LDFLAGS += @LDSHARED@ LIBS = pkglib_PROGRAMS = @DRIVERS@ -EXTRA_PROGRAMS = bayrad CFontz CFontz633 curses glk hd44780 irman joy lb216 lcdm001 lirc MtxOrb sed1330 sed1520 stv5730 svga t6963 text wirz_sli +EXTRA_PROGRAMS = bayrad CFontz CFontz633 curses glk hd44780 irman icp_a106 joy lb216 lcdm001 lcterm lirc MtxOrb sed1330 sed1520 stv5730 svga t6963 text wirz_sli noinst_LIBRARIES = libLCD.a CFontz_LDADD = libLCD.a CFontz633_LDADD = libLCD.a curses_LDADD = @LIBCURSES@ hd44780_LDADD = libLCD.a +icp_a106_LDADD = libLCD.a irman_LDADD = @LIBIRMAN@ lirc_LDADD = @LIBLIRC_CLIENT@ +lcterm_LDADD = libLCD.a svga_LDADD = @LIBSVGA@ t6963_LDADD = libLCD.a -libLCD_a_SOURCES = lcd_lib.h lcd_lib.c +libLCD_a_SOURCES = lcd_lib.h lcd_lib.c bayrad_SOURCES = lcd.h bayrad.h bayrad.c report.h CFontz_SOURCES = lcd.h CFontz.c CFontz.h report.h @@ -38,9 +40,11 @@ CFontz633_SOURCES = lcd.h CFontz633.c CFontz633.h CFontz633io.c CFontz633io.h r curses_SOURCES = lcd.h curses_drv.h curses_drv.c report.h glk_SOURCES = lcd.h glk.c glk.h glkproto.c glkproto.h report.h hd44780_SOURCES = lcd.h hd44780.h hd44780.c hd44780-drivers.h hd44780-low.h hd44780-charmap.h hd44780-4bit.c hd44780-4bit.h hd44780-ext8bit.c hd44780-ext8bit.h lcd_sem.c lcd_sem.h hd44780-serialLpt.c hd44780-serialLpt.h hd44780-winamp.c hd44780-winamp.h hd44780-picanlcd.c hd44780-picanlcd.h port.h lpt-port.h timing.h report.h +icp_a106_SOURCES = lcd.h icp_a106.c icp_a106.h report.h irman_SOURCES = lcd.h irmanin.c irmanin.h report.h joy_SOURCES = lcd.h joy.c joy.h port.h report.h lb216_SOURCES = lcd.h lb216.c lb216.h report.h +lcterm_SOURCES = lcd.h lcterm.c lcterm.h report.h lcdm001_SOURCES = lcd.h lcdm001.c lcdm001.h report.h lirc_SOURCES = lcd.h lircin.c lircin.h report.h MtxOrb_SOURCES = lcd.h MtxOrb.c MtxOrb.h report.h bigfont.h diff --git a/server/drivers/icp_a106.c b/server/drivers/icp_a106.c new file mode 100644 index 0000000..7cf9d18 --- /dev/null +++ b/server/drivers/icp_a106.c @@ -0,0 +1,381 @@ +/*icp_a106.c*/ +/* + This is the LCDproc driver for the ICP A106 alarm/LCD board, + used in 19" rack cases by ICP + + Both LCD and alarm functions are accessed via one serial port, using + separate commands. Unfortunately, the device runs at slow 1200bps and the + LCD does not allow user-defined characters, so the bargraphs do not look + very nice. + + Copyright (C) 2002 Michael Schwingen + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + + This driver is mostly based on the HD44780 and the LCDM001 driver. + (Hopefully I have NOT forgotten any file I have stolen code from. + If so send me an e-mail or add your copyright here!) +*/ + +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "lcd.h" +#include "lcd_lib.h" +#include "icp_a106.h" +#include "report.h" + +typedef struct +{ + unsigned char *framebuf; + unsigned char *last_framebuf; + int width; + int height; + int fd; +}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 = "icp_a106_"; + + +///////////////////////////////////////////////////////////////// +// Opens com port and sets baud correctly... +// +MODULE_EXPORT int +icp_a106_init (Driver *drvthis, char *args) +{ + char device[200]; + int speed=B1200; + struct termios portset; + + PrivateData *p; + + debug( RPT_INFO, "ICP_A106: init(%p,%s)", drvthis, args ); + + // Alocate and store private data + p = (PrivateData *) calloc( 1, sizeof( PrivateData) ); + if( ! p ) + return -1; + if( drvthis->store_private_ptr( drvthis, p ) ) + return -1; + + p->width = 20; + p->height = 2; + + // READ CONFIG FILE: + // which serial device should be used + strncpy(device, drvthis->config_get_string ( drvthis->name , "Device" , 0 , DEFAULT_DEVICE), + sizeof(device)); + device[sizeof(device)-1]=0; + report (RPT_INFO,"ICP_A106: Using device: %s", device); + + p->framebuf = malloc (p->width * p->height); + p->last_framebuf = malloc (p->width * p->height); + if (!p->framebuf || !p->last_framebuf) { + report(RPT_ERR, "\nError: unable to create ICP_A106 framebuffer.\n"); + return -1; + } + memset (p->framebuf, ' ', p->width * p->height); + memset (p->last_framebuf, ' ', p->width * p->height); + + // Set up io port correctly, and open it... + debug( RPT_DEBUG, "ICP_A106: Opening serial device: %s", device); + p->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (p->fd == -1) + { + switch (errno) + { + case ENOENT: report( RPT_ERR, "ICP_A106: icp_a106_init() failed: Device file missing: %s\n", device); + break; + case EACCES: report( RPT_ERR, "ICP_A106: icp_a106_init() failed: Could not open device: %s\n", device); + report( RPT_ERR, "ICP_A106: icp_a106_init() failed: Make sure you have rw access to %s!\n", device); + break; + default: report( RPT_ERR, "ICP_A106: icp_a106_init() failed (%s)\n", strerror (errno)); + break; + } + return -1; + } else { + report (RPT_INFO, "opened ICP_A106 display on %s\n", 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, speed); + cfsetispeed(&portset, speed); + tcsetattr(p->fd, TCSANOW, &portset); + tcflush(p->fd, TCIOFLUSH); + + // stop auto clock display, clear display + write (p->fd, "\x4D\x28\x4D\x0D", 4); + return 0; +} + +///////////////////////////////////////////////////////////////// +// Clean-up +// +MODULE_EXPORT void +icp_a106_close (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + if(p->framebuf) + free (p->framebuf); + if(p->last_framebuf) + free (p->last_framebuf); + + // clear display, start auto display + if (p->fd) + { + write (p->fd, "\x4D\x0D\x4D\x29", 4); + close (p->fd); + } + drvthis->store_private_ptr( drvthis, NULL ); + report (RPT_INFO, "ICP_A106: closed"); +} + +///////////////////////////////////////////////////////////////// +// Returns the display width +// +MODULE_EXPORT int +icp_a106_width (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + return p->width; +} + +///////////////////////////////////////////////////////////////// +// Returns the display height +// +MODULE_EXPORT int +icp_a106_height (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + return p->height; +} + +///////////////////////////////////////////////////////////////// +// Clears the LCD screen +// +MODULE_EXPORT void +icp_a106_clear (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + memset(p->framebuf, ' ', p->width * p->height); +} + +////////////////////////////////////////////////////////////////// +// Flushes all output to the lcd... +// +MODULE_EXPORT void +icp_a106_flush (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + /* + The ICP A106 is a bit difficult to handle - 1200bps, ho handshake, and + the controller is easily overrun and displays garbage when it has too much + of work to do. It seems we can handle two full updates per second, so if + the last update was less than 0.5 seconds ago, we simply skip this one + and update the display the next time. + */ + struct timeval tv, tv2; + static struct timeval tv_old; /* time of last update */ + int line; + static char cmd[] = "\x4D\x0c\x00\x14"; + + gettimeofday(&tv, 0); + timersub(&tv, &tv_old, &tv2); + + if (tv2.tv_sec ==0 && tv2.tv_usec < 500000) // less than 0.5s + return; + tv_old = tv; + + for(line=0; line < p->height; line++) + { + if(memcmp(p->framebuf + line * p->width, + p->last_framebuf + line * p->width, p->width) != 0) + { + cmd[2] = line; + write(p->fd, cmd, 4); + write(p->fd, p->framebuf + line * p->width, 20); + } + } + memcpy(p->last_framebuf, p->framebuf, p->width * p->height); +} + +///////////////////////////////////////////////////////////////// +// Prints a character on the lcd display, at position (x,y). The +// upper-left is (1,1), and the lower right should be (16,2). +// +MODULE_EXPORT void +icp_a106_chr (Driver *drvthis, int x, int y, char ch) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + y--; + x--; + //debug (RPT_DEBUG, "icp_a106_chr: x=%d, y=%d, chr=%x", x,y,ch); + if (x >= 0 && x < p->width && y >= 0 && y < p->height) + p->framebuf[ y * p->width + x] = ch; +} + +///////////////////////////////////////////////////////////////// +// Prints a string on the lcd display, at position (x,y). The +// upper-left is (1,1), and the lower right should be (16,2). +// +MODULE_EXPORT void +icp_a106_string (Driver *drvthis, int x, int y, char *s) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + x --; // Convert 1-based coords to 0-based + y --; + + for (; *s && x < p->width; x++) + p->framebuf[y * p->width + x] = *s++; +} + + +///////////////////////////////////////////////////////////////// +// Draws a vertical bar, from the bottom of the screen up. +// +MODULE_EXPORT void +icp_a106_vbar (Driver *drvthis, int x, int y, int len, int promille, int options) +{ + int total_pixels = ((long) 2 * len * LCD_DEFAULT_CELLHEIGHT + 1 ) * promille / 2000; + int pos; + static char map[] = " __---=#"; + + for (pos = 0; pos < len; pos ++ ) { + + int pixels = total_pixels - LCD_DEFAULT_CELLHEIGHT * pos; + + if( pixels >= LCD_DEFAULT_CELLHEIGHT ) + { + /* write a "full" block to the screen... */ + icp_a106_icon (drvthis, x, y-pos, ICON_BLOCK_FILLED); + } + else + { + /* write a partial block... */ + icp_a106_chr (drvthis, x, y-pos, map[pixels]); + break; + } + } +} + + +///////////////////////////////////////////////////////////////// +// Draws a horizontal bar to the right. +// +MODULE_EXPORT void +icp_a106_hbar(Driver *drvthis, int x, int y, int len, int promille, int options) +{ + int total_pixels = ((long) 2 * len * LCD_DEFAULT_CELLWIDTH + 1 ) * promille / 2000; + int pos; + + for (pos = 0; pos < len; pos ++ ) + { + int pixels = total_pixels - LCD_DEFAULT_CELLWIDTH * pos; + + if( pixels >= LCD_DEFAULT_CELLWIDTH ) + { + /* write a "full" block to the screen... */ + icp_a106_icon(drvthis, x+pos, y, ICON_BLOCK_FILLED); + } + else if( pixels > 0 ) + { + /* write a partial block... */ + icp_a106_chr(drvthis, x+pos, y, '|'); + break; + } + else { + ; /* write nothing (not even a space) */ + } + } +} + + +///////////////////////////////////////////////////////////////// +// Writes a big number. +// +MODULE_EXPORT void +icp_a106_num (Driver *drvthis, int x, int num) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + if( num < 0 || num > 10 ) return; + + { + int y = ( p->height - 1 ) / 2 + 1; + if( num == 11 ) + icp_a106_chr( drvthis, x, y, ':' ); + else + icp_a106_chr( drvthis, x, y, num + '0' ); + } +} + + +///////////////////////////////////////////////////////////////// +// Sets character 0 to an icon... +// +MODULE_EXPORT void +icp_a106_icon (Driver *drvthis, int x, int y, int icon) +{ + switch( icon ) + { + case ICON_BLOCK_FILLED: + icp_a106_chr( drvthis, x, y, 255 ); + break; + case ICON_HEART_FILLED: + break; + case ICON_HEART_OPEN: + break; + default: + report( RPT_WARNING, "icp_a106_icon: unknown or unsupported icon: %d", icon ); + } +} + +///////////////////////////////////////////////////////////// +// Does the heartbeat... +// +MODULE_EXPORT void +icp_a106_heartbeat (Driver *drvthis, int type) +{ +} + + diff --git a/server/drivers/icp_a106.h b/server/drivers/icp_a106.h new file mode 100644 index 0000000..79c4155 --- /dev/null +++ b/server/drivers/icp_a106.h @@ -0,0 +1,53 @@ +/* + This is the LCDproc driver for the ICP A106 alarm/LCD board, + used in 19" rack cases by ICP + + Both LCD and alarm functions are accessed via one serial port, using + separate commands. Unfortunately, the device runs at slow 1200bps and the + LCD does not allow user-defined characters, so the bargraphs do not look + very nice. + + Copyright (C) 2002 Michael Schwingen + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + + This driver is mostly based on the HD44780 and the LCDM001 driver. + (Hopefully I have NOT forgotten any file I have stolen code from. + If so send me an e-mail or add your copyright here!) +*/ + +#ifndef ICP_A106_H +#define ICP_A106_H + +#include "lcd.h" +MODULE_EXPORT int icp_a106_init (struct lcd_logical_driver *driver, char *args); +MODULE_EXPORT void icp_a106_close (Driver *drvthis); +MODULE_EXPORT int icp_a106_width (Driver *drvthis); +MODULE_EXPORT int icp_a106_height (Driver *drvthis); +MODULE_EXPORT void icp_a106_clear (Driver *drvthis); +MODULE_EXPORT void icp_a106_flush (Driver *drvthis); +MODULE_EXPORT void icp_a106_string (Driver *drvthis, int x, int y, char *s); +MODULE_EXPORT void icp_a106_chr (Driver *drvthis, int x, int y, char ch); + +MODULE_EXPORT void icp_a106_vbar (Driver *drvthis, int x, int y, int len, int promille, int options); +MODULE_EXPORT void icp_a106_hbar (Driver *drvthis, int x, int y, int len, int promille, int options); +MODULE_EXPORT void icp_a106_num (Driver *drvthis, int x, int num); +MODULE_EXPORT void icp_a106_heartbeat (Driver *drvthis, int type); +MODULE_EXPORT void icp_a106_icon (Driver *drvthis, int x, int y, int icon); + +#define DEFAULT_DEVICE "/dev/lcd" + +#endif + diff --git a/server/drivers/lcterm.c b/server/drivers/lcterm.c new file mode 100644 index 0000000..5d7a2c1 --- /dev/null +++ b/server/drivers/lcterm.c @@ -0,0 +1,786 @@ +/*lcterm.c*/ +/* + This is the LCDproc driver for the "LCTerm" serial LCD terminal + from Helmut Neumark Elektronik, www.neumark.de + + Copyright (C) 2002 Michael Schwingen + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + + This driver is mostly based on the HD44780 and the LCDM001 driver. + (Hopefully I have NOT forgotten any file I have stolen code from. + If so send me an e-mail or add your copyright here!) + + TODO: support keyboard input +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "lcd.h" +#include "lcd_lib.h" +#include "lcterm.h" +#include "report.h" + +typedef enum +{ + CCMODE_STANDARD, /* only char 0 is used for heartbeat */ + CCMODE_VBAR, + CCMODE_HBAR, + CCMODE_BIGNUM +} CCMode; + +typedef struct +{ + CCMode ccmode; /* custom character mode for current display */ + CCMode last_ccmode; /* custom character set that is loaded in the display */ + unsigned char *framebuf; + unsigned char *last_framebuf; + int width; + int height; + int fd; +}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 = "lcterm_"; + + +///////////////////////////////////////////////////////////////// +// Opens com port and sets baud correctly... +// +MODULE_EXPORT int +lcterm_init (Driver *drvthis, char *args) +{ + char device[200]; + int speed=B9600; + struct termios portset; + PrivateData *p; + + debug( RPT_INFO, "LCTERM: init(%p,%s)", drvthis, args ); + + // Alocate and store private data + p = (PrivateData *) calloc( 1, sizeof( PrivateData) ); + if( ! p ) + return -1; + if( drvthis->store_private_ptr( drvthis, p ) ) + return -1; + + p->ccmode = p->last_ccmode = CCMODE_STANDARD; + + // READ CONFIG FILE: + // which serial device should be used + strncpy(device, drvthis->config_get_string ( drvthis->name , "Device" , 0 , DEFAULT_DEVICE), + sizeof(device)); + device[sizeof(device)-1]=0; + report (RPT_INFO,"LCTERM: Using device: %s", device); + + { + char *s; + int w,h; + + // Get and parse size + s = drvthis->config_get_string( drvthis->name, "size", 0, "16x2" ); + fprintf( stderr, "lcterm_init: size: %s\n", s ); + + if( sscanf( s, "%dx%d", &w, &h) == 2 + && (w > 0) && (w <= LCD_MAX_WIDTH) + && (h > 0) && (h <= LCD_MAX_HEIGHT)) + { + p->width = w; + p->height = h; + } + else + fprintf( stderr, "lcterm_init: Cannot parse size: %s\n", s ); + } + fprintf(stderr,"size: %dx%d\n",p->width, p->height); + + p->framebuf = malloc (p->width * p->height); + p->last_framebuf = malloc (p->width * p->height); + if (!p->framebuf || !p->last_framebuf) { + report(RPT_ERR, "\nError: unable to create LCTERM framebuffer.\n"); + return -1; + } + memset (p->framebuf, ' ', p->width * p->height); + memset (p->last_framebuf, ' ', p->width * p->height); + + // Set up io port correctly, and open it... + debug( RPT_DEBUG, "LCTERM: Opening serial device: %s", device); + p->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (p->fd == -1) + { + switch (errno) + { + case ENOENT: report( RPT_ERR, "LCTERM: lcterm_init() failed: Device file missing: %s\n", device); + break; + case EACCES: report( RPT_ERR, "LCTERM: lcterm_init() failed: Could not open device: %s\n", device); + report( RPT_ERR, "LCTERM: lcterm_init() failed: Make sure you have rw access to %s!\n", device); + break; + default: report( RPT_ERR, "LCTERM: lcterm_init() failed (%s)\n", strerror (errno)); + break; + } + return -1; + } else { + report (RPT_INFO, "opened LCTERM display on %s\n", 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, speed); + cfsetispeed(&portset, speed); + tcsetattr(p->fd, TCSANOW, &portset); + tcflush(p->fd, TCIOFLUSH); + + // clear the display, disable cursor, disable key scanning + write (p->fd, "\x1a\x16\x1bK", 4); + return 0; +} + +///////////////////////////////////////////////////////////////// +// Clean-up +// +MODULE_EXPORT void +lcterm_close (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + if(p->framebuf) + free (p->framebuf); + if(p->last_framebuf) + free (p->last_framebuf); + + // clear the display, disable key scanning + if (p->fd) + { + write (p->fd, "\x1a\x1bK", 3); + close (p->fd); + } + drvthis->store_private_ptr( drvthis, NULL ); + report (RPT_INFO, "LCTERM: closed"); +} + +///////////////////////////////////////////////////////////////// +// Returns the display width +// +MODULE_EXPORT int +lcterm_width (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + return p->width; +} + +///////////////////////////////////////////////////////////////// +// Returns the display height +// +MODULE_EXPORT int +lcterm_height (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + return p->height; +} + +///////////////////////////////////////////////////////////////// +// Clears the LCD screen +// +MODULE_EXPORT void +lcterm_clear (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + memset(p->framebuf, ' ', p->width * p->height); + p->ccmode = CCMODE_STANDARD; +} + +////////////////////////////////////////////////////////////////// +// Flushes all output to the lcd... +// +MODULE_EXPORT void +lcterm_flush (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + int i,line; + unsigned char *buf, *sp, *dp, c; + + if(memcmp(p->framebuf, p->last_framebuf, p->width * p->height) == 0) + return; + + buf = alloca(p->width * p->height * 2 + 5); /* worst case: we need to + escape *every* character */ + dp = buf; + sp = p->framebuf; + + *dp++ = 0x1E; // cursor home + + for(line = p->height; line>0; line--) + { + for(i=p->width; i>0; i--) + { + if((c = *sp++) < 0x08) // need to escape used-defined characters + *dp++ = 0x1B; + *dp++ = c; + } + *dp++ = 0x0a; + *dp++ = 0x0d; + } + write(p->fd,buf,dp-buf); + memcpy(p->last_framebuf, p->framebuf, p->width * p->height); +} + +///////////////////////////////////////////////////////////////// +// Prints a character on the lcd display, at position (x,y). The +// upper-left is (1,1), and the lower right should be (16,2). +// +MODULE_EXPORT void +lcterm_chr (Driver *drvthis, int x, int y, char ch) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + y--; + x--; + //debug (RPT_DEBUG, "lcterm_chr: x=%d, y=%d, chr=%x", x,y,ch); + if (x >= 0 && x < p->width && y >= 0 && y < p->height) + p->framebuf[ y * p->width + x] = ch; +} + +///////////////////////////////////////////////////////////////// +// Prints a string on the lcd display, at position (x,y). The +// upper-left is (1,1), and the lower right should be (16,2). +// +MODULE_EXPORT void +lcterm_string (Driver *drvthis, int x, int y, char *s) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + x --; // Convert 1-based coords to 0-based + y --; + + for (; *s && x < p->width; x++) + p->framebuf[y * p->width + x] = *s++; +} + + +///////////////////////////////////////////////////////////////// +// Sets a custom character from 0-7... +// +// For input, values > 0 mean "on" and values <= 0 are "off". +// +// The input is just an array of characters... +// +MODULE_EXPORT void +lcterm_set_char (Driver *drvthis, int n, char *dat) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + int row, col; + int data; + unsigned char buf[11]; + + if (n < 0 || n > 7 || !dat) + return; + + buf[0] = 0x1F; + buf[1] = 8*n; // CG RAM address */ + for (row = 0; row < 8; row++) + { + data = 0; + for (col = 0; col < 5; col++) + { + data <<= 1; + data |= (*dat++ != 0); + } + buf[2+row] = data | 0x80; + } + buf[10] = 0x1E; // Cursor Home - exit CG-RAM mode + write(p->fd,buf,11); +} + +///////////////////////////////////////////////////////////////// +// Sets up for vertical bars. Call before lcterm->vbar() +// +static void +lcterm_init_vbar (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + static char vbar_1[] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }; + static char vbar_2[] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + }; + static char vbar_3[] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + }; + static char vbar_4[] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + }; + static char vbar_5[] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + }; + static char vbar_6[] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + }; + static char vbar_7[] = { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + }; + + if( p->last_ccmode == CCMODE_VBAR ) /* Work already done */ + return; + + if( p->ccmode != CCMODE_STANDARD ) + { + /* Not supported (yet) */ + report( RPT_WARNING, "lcterm_init_vbar: Cannot combine two modes using user defined characters" ); + return; + } + p->ccmode = p->last_ccmode = CCMODE_VBAR; + + lcterm_set_char (drvthis, 1, vbar_1); + lcterm_set_char (drvthis, 2, vbar_2); + lcterm_set_char (drvthis, 3, vbar_3); + lcterm_set_char (drvthis, 4, vbar_4); + lcterm_set_char (drvthis, 5, vbar_5); + lcterm_set_char (drvthis, 6, vbar_6); + lcterm_set_char (drvthis, 7, vbar_7); +} + +///////////////////////////////////////////////////////////////// +// Inits horizontal bars... +// +static void +lcterm_init_hbar (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + static char hbar_1[] = { + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + }; + static char hbar_2[] = { + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + }; + static char hbar_3[] = { + 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, + }; + static char hbar_4[] = { + 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, + }; + + if( p->last_ccmode == CCMODE_HBAR ) /* Work already done */ + return; + + if( p->ccmode != CCMODE_STANDARD ) + { + /* Not supported (yet) */ + report( RPT_WARNING, "lcterm_init_hbar: Cannot combine two modes using user defined characters" ); + return; + } + p->ccmode = p->last_ccmode = CCMODE_HBAR; + + lcterm_set_char (drvthis, 1, hbar_1); + lcterm_set_char (drvthis, 2, hbar_2); + lcterm_set_char (drvthis, 3, hbar_3); + lcterm_set_char (drvthis, 4, hbar_4); +} + + +///////////////////////////////////////////////////////////////// +// Draws a vertical bar, from the bottom of the screen up. +// +MODULE_EXPORT void +lcterm_vbar (Driver *drvthis, int x, int y, int len, int promille, int options) +{ + lcterm_init_vbar(drvthis); + lib_vbar_static(drvthis, x, y, len, promille, options, LCD_DEFAULT_CELLHEIGHT, 0); +} + + +///////////////////////////////////////////////////////////////// +// Draws a horizontal bar to the right. +// +MODULE_EXPORT void +lcterm_hbar(Driver *drvthis, int x, int y, int len, int promille, int options) +{ + lcterm_init_hbar(drvthis); + lib_hbar_static(drvthis, x, y, len, promille, options, LCD_DEFAULT_CELLWIDTH, 0); +} + + +///////////////////////////////////////////////////////////////// +// Sets up for big numbers. +// +MODULE_EXPORT void +lcterm_init_num (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + int i; + + static char bignum_ccs[8][5*8] = {{ + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + }, { + 0, 0, 0, 1, 1, + 0, 0, 0, 1, 1, + 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0 + }, { + 1, 1, 0, 1, 1, + 1, 1, 0, 1, 1, + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + }, { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0 + }, { + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, + 0, 0, 0, 1, 1, + 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0 + }, { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + 1, 1, 0, 1, 1, + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0 + }, { + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0 + }, { + 0, 0, 0, 1, 1, + 0, 0, 0, 1, 1, + 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + }}; + + if( p->last_ccmode == CCMODE_BIGNUM ) { + /* Work already done */ + return; + } + + if( p->ccmode != CCMODE_STANDARD ) { + /* Not supported (yet) */ + report( RPT_WARNING, "lcterm_init_num: Cannot combine two modes using user defined characters" ); + return; + } + + for( i=0; i<8; i++ ) + lcterm_set_char (drvthis, i, bignum_ccs[i]); + p->ccmode = p->last_ccmode = CCMODE_BIGNUM; +} + +///////////////////////////////////////////////////////////////// +// Writes a big number. +// +MODULE_EXPORT void +lcterm_num (Driver *drvthis, int x, int num) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + static char bignum_map[11][4][3] = { + { /* 0: */ + {1,2,3}, + {6,32,6}, + {6,32,6}, + {7,2,32} + }, + { /* 1: */ + {7,6,32}, + {32,6,32}, + {32,6,32}, + {7,2,32}, + }, + { /* 2: */ + {1,2,3}, + {32,5,0}, + {1,32,32}, + {2,2,0}, + }, + { /* 3: */ + {1,2,3}, + {32,5,0}, + {3,32,6}, + {7,2,32} + }, + { /* 4: */ + {32,3,6}, + {1,32,6}, + {2,2,6}, + {32,32,0} + }, + { /* 5: */ + {1,2,0}, + {2,2,3}, + {3,32,6}, + {7,2,32} + }, + { /* 6: */ + {1,2,32}, + {6,5,32}, + {6,32,6}, + {7,2,32} + }, + { /* 7: */ + {2,2,6}, + {32,1,32}, + {32,6,32}, + {32,0,32} + }, + { /* 8: */ + {1,2,3}, + {4,5,0}, + {6,32,6}, + {7,2,32} + }, + { /* 9: */ + {1,2,3}, + {4,3,6}, + {32,1,32}, + {7,32,32} + }, + { /* colon: */ + {32}, + {7}, + {7}, + {32} + }}; + + if( num < 0 || num > 10 ) return; + + lcterm_init_num(drvthis); + + if( p->width >= 16 && p->height >= 4 ) + { + int y = 1 + ( p->height - 2 ) / 2; + int x2, y2; + + for( x2 = 0; x2 <= 2; x2 ++ ) + { + for( y2 = 0; y2 <= 3; y2 ++ ) + lcterm_chr( drvthis, x+x2, y+y2, bignum_map[num][y2][x2] ); + if( num == 10 ) + x2 = 2; /* =break, for colon only */ + } + } + else + { + int y = ( p->height - 1 ) / 2 + 1; + if( num == 11 ) + lcterm_chr( drvthis, x, y, ':' ); + else + lcterm_chr( drvthis, x, y, num + '0' ); + } +} + + +///////////////////////////////////////////////////////////////// +// Sets character 0 to an icon... +// +MODULE_EXPORT void +lcterm_icon (Driver *drvthis, int x, int y, int icon) +{ + static char heart_open[] = { + 1, 1, 1, 1, 1, + 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1 }; + + static char heart_filled[] = { + 1, 1, 1, 1, 1, + 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, + 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, + 1, 0, 1, 0, 1, + 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1 }; + + switch( icon ) + { + case ICON_BLOCK_FILLED: + lcterm_chr( drvthis, x, y, 255 ); + break; + case ICON_HEART_FILLED: + lcterm_set_char( drvthis, 0, heart_filled ); + lcterm_chr( drvthis, x, y, 0 ); + break; + case ICON_HEART_OPEN: + lcterm_set_char( drvthis, 0, heart_open ); + lcterm_chr( drvthis, x, y, 0 ); + break; + default: + report( RPT_WARNING, "lcterm_icon: unknown or unsupported icon: %d", icon ); + } +} + +///////////////////////////////////////////////////////////// +// Does the heartbeat... +// +MODULE_EXPORT void +lcterm_heartbeat (Driver *drvthis, int type) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + static int timer = 0; + int whichIcon; + + if (type == HEARTBEAT_ON) + { + /* Set this to pulsate like a real heart beat... */ + if( ((timer + 4) & 5)) + whichIcon = ICON_HEART_OPEN; + else + whichIcon = ICON_HEART_FILLED; + + /* place the icon */ + lcterm_icon (drvthis, p->width, 1, whichIcon); + } + timer++; +} + diff --git a/server/drivers/lcterm.h b/server/drivers/lcterm.h new file mode 100644 index 0000000..bb4bf0b --- /dev/null +++ b/server/drivers/lcterm.h @@ -0,0 +1,49 @@ +/* + This is the LCDproc driver for the "LCTerm" serial LCD terminal + from Helmut Neumark Elektronik, www.neumark.de + + Copyright (C) 2002 Michael Schwingen + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + + This driver is mostly based on the HD44780 and the LCDM001 driver. + (Hopefully I have NOT forgotten any file I have stolen code from. + If so send me an e-mail or add your copyright here!) +*/ + +#ifndef LCTERM_H +#define LCTERM_H + +#include "lcd.h" +MODULE_EXPORT int lcterm_init (struct lcd_logical_driver *driver, char *args); +MODULE_EXPORT void lcterm_close (Driver *drvthis); +MODULE_EXPORT int lcterm_width (Driver *drvthis); +MODULE_EXPORT int lcterm_height (Driver *drvthis); +MODULE_EXPORT void lcterm_clear (Driver *drvthis); +MODULE_EXPORT void lcterm_flush (Driver *drvthis); +MODULE_EXPORT void lcterm_string (Driver *drvthis, int x, int y, char *s); +MODULE_EXPORT void lcterm_chr (Driver *drvthis, int x, int y, char ch); + +MODULE_EXPORT void lcterm_vbar (Driver *drvthis, int x, int y, int len, int promille, int options); +MODULE_EXPORT void lcterm_hbar (Driver *drvthis, int x, int y, int len, int promille, int options); +MODULE_EXPORT void lcterm_num (Driver *drvthis, int x, int num); +MODULE_EXPORT void lcterm_heartbeat (Driver *drvthis, int type); +MODULE_EXPORT void lcterm_icon (Driver *drvthis, int x, int y, int icon); +MODULE_EXPORT void lcterm_set_char (Driver *drvthis, int n, char *dat); + +#define DEFAULT_DEVICE "/dev/lcd" + +#endif +