aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--README105
-rw-r--r--TODO20
-rw-r--r--cfg.c9
-rw-r--r--exec.c89
-rw-r--r--exec.h17
-rw-r--r--lcd4linux.c9
-rw-r--r--lcd4linux.conf.sample12
-rw-r--r--parser.c7
-rw-r--r--processor.c39
9 files changed, 214 insertions, 93 deletions
diff --git a/README b/README
index 82dce69..4d014ce 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
#
-# $Id: README,v 1.13 2001/03/07 18:10:21 ltoetsch Exp $
+# $Id: README,v 1.14 2001/03/08 15:25:38 ltoetsch Exp $
#
This is the README file for lcd4linux
@@ -17,22 +17,23 @@ vertical bars, logarithmic bars, split bars (two independent bars in one row).
USAGE
lcd4linux -h
-print version number and a small help text, then exit
+ print version number and a small help text, then exit
lcd4linux -l
-list available drivers
+ list available drivers
lcd4linux -d
-calibrate delay loop (necessary for some drivers)
+ calibrate delay loop (necessary for some drivers)
lcd4linux [-c key=val] [-F] [-f config-file] [-o output] [-q] [-v]
-run lcd4linux
-overwrite entries from the config-file with '-c'
-do not fork and detach with '-F'
-use configuration from 'config-file' instead of /etc/lcd4linux.conf
-write picture to 'output' (raster driver only)
-suppress startup splash screen with '-q'
-generate debugging messages with '-v'
+ run lcd4linux
+ overwrite entries from the config-file with '-c'
+ do not fork and detach with '-F'
+ use configuration from 'config-file' instead of /etc/lcd4linux.conf
+ write picture to 'output' (raster driver only)
+ suppress startup splash screen with '-q'
+ generate info messages with '-v'
+ generate debugging messages with '-vv'
SUPPORTED DISPLAYS
@@ -65,10 +66,11 @@ SUPPORTED DISPLAYS
* X11
- thanks to Herbert Rosmanith <herp@wildsau.idv.uni-linz.ac.at> a driver for the
- X Window System is available. It supports any size at any resolution. A very
- small XLCD4Linux-Window can even swallow on the KDE Panel!
-
+ thanks to Herbert Rosmanith <herp@wildsau.idv.uni-linz.ac.at> a driver
+ for the X Window System is available. It supports any size at any
+ resolution. A very small XLCD4Linux-Window can even swallow on the KDE
+ Panel!
+
* Raster formats:
a generic raster driver (which is used by the X11-driver, too) is availiable,
@@ -77,53 +79,68 @@ SUPPORTED DISPLAYS
PNG (with libgd)
-* other displays: lcd4linux and especially the display driver code is very modular,
- so it should be quite easy to write a driver for any display. See README.driver
- for details. Contributors are welcome!!!
+* other displays: lcd4linux and especially the display driver code is very
+ modular, so it should be quite easy to write a driver for any display. See
+ README.driver for details. Contributors are welcome!!!
CONFIGURATION
-The configuration file (default: /etc/lcd4linux.conf) has a very simple format:
-Every line consists of a key and a value, seperated by whitespace (blanks or tabs).
-Values can contain whitespace, and can be enclosed in single or double quotes.
-A key must not contain whitespace. Keys are NOT case-sensitive. Order doesn't matter.
-Empty lines and all text on a line after a '#' will be ignored. If you want to
-use '#' in a value (think of X11-colors), you have to quote it with a backslash.
+The configuration file (default: /etc/lcd4linux.conf) has a very simple
+format: Every line consists of a key and a value, seperated by whitespace
+(blanks or tabs). Values can contain whitespace, and can be enclosed in
+single or double quotes. A key must not contain whitespace. Keys are NOT
+case-sensitive. Order doesn't matter. Empty lines and all text on a line
+after a '#' will be ignored. If you want to use '#' in a value (think of
+X11-colors), you have to quote it with a backslash.
NOTE:
Because of security reasons the config file is assured to be:
* - file is a normal file (or /dev/null)
* - file owner is owner of program
- * - file is not writeable by group
- * - file is not writeable by other
+ * - file is not accessible by group
+ * - file is not accessible by other
So if you run lcd4linux as root, /etc/lcd4linux has to be:
chmod 600
chown root.root
-The configuration file contains information for different modules of lcd4linux:
+The configuration file contains information for different modules of
+lcd4linux:
Global options:
tick: time in milliseconds between bar updates
- tack: time in milliseconds between text updates (text can be updated less often than
- bars, so you get a smooth bar display and readable text)
+ tack: time in milliseconds between text updates (text can be updated less
+ often than bars, so you get a smooth bar display and readable text)
tau: time constant (in milliseconds) for damping function (not used by now)
Data-specific options:
- overload: load average threshold and bar scaling. The '%L' token (see below) displays
- a '!' instead of a blank if the current load average exceeds this value.
+ overload: load average threshold and bar scaling. The '%L' token (see below)
+ displays a '!' instead of a blank if the current load average
+ exceeds this value.
load bars are scaled by this value (load=overload gives 100%)
fifo: path to fifo for communication with isdnlog (not yet implemented)
- sensor1: path to the 1st temperature file (e.g. /proc/sys/dev/sensors/w83781d-isa-0290/temp1)
- it is important that you use the isa sensors, because the i2c sensors are very slow!
+ sensor1: path to the 1st temperature file
+ (e.g. /proc/sys/dev/sensors/w83781d-isa-0290/temp1)
+ it is important that you use the isa sensors, because the i2c
+ sensors are very slow!
sensor1_min: temperature where the corresponding bar starts
sensor1_max: temperature where bar ends
sensor[2..9], -_min, -_max: entries for the 2nd to 9th temperature sensor
+
+ exec:
+ x1 ... x9: command to execute, PATH=/usr/local/bin:/usr/bin:/bin
+ $X1 ... $X8 is result of command 1..8 in environment
+ Tick_x1 .. 9 delay in ticks (overrides delay_x)
+ Delay_x1 ..9 delay in seconds (default 1)
+ Scale_x1 ..9 scale for bars (default 100)
+
+ battery: Battwarning 10 (default 10)
+
Driver-specific options:
@@ -131,26 +148,28 @@ Driver-specific options:
every driver has its own configuration options (e.g. 'Port', 'Speed', ...)
see README.<Drivername> for details!
+
Display options:
row1: Text to display in row 1
row[2-max]: Text to display in other rows
- The text to be displayed can contain specific directives, which will be replaced
- by the appropriate values, or will create bars:
+ The text to be displayed can contain specific directives, which will be
+ replaced by the appropriate values, or will create bars:
'\nnn` will write the ASCII-character nnn (octal)
'%<token>' will be replaced by the value of <token>
'%%' will write a '%'
'%$' will write a '$'
- '$<direction><length><token>[+<token>] will create a bar with the specified direction
- and length (in characters) with the value of <token>. If the driver supports dual bars,
- you can specify the second value with '+<token>'.
- <direction> can be 'l' (left), 'r' (right), 'u' (up) or 'd' (down).
- If you specify the direction in upper case, a logarithmic bar will be created.
- note that the space occupied by a bar always grows from left to right or from top to
- bottom, regardless of the direction!
-
+
+ '$<direction><length><token>[+<token>] will create a bar with the
+ specified direction and length (in characters) with the value of <token>.
+ If the driver supports dual bars, you can specify the second value with
+ '+<token>'. <direction> can be 'l' (left), 'r' (right), 'u' (up) or 'd'
+ (down). If you specify the direction in upper case, a logarithmic bar will
+ be created. note that the space occupied by a bar always grows from left
+ to right or from top to bottom, regardless of the direction!
+
Tokens:
'o', operating system name ('Linux')
diff --git a/TODO b/TODO
index bb6c9a0..9b7d638 100644
--- a/TODO
+++ b/TODO
@@ -6,7 +6,7 @@ can be filled (made up of bars) or not (needs raster graphics)
// 2000-04-04 Michael Reinelt <reinelt@eunet.at>
// write a driver for PNG. This should be the first step towards a WWW-driver.
-Done 2001-03-01 -lt.
+// Done 2001-03-01 -lt.
2000-04-15 Thomas Skyt Jessen <thskyt@foni.net>
show partition information (used, free, ...)
@@ -18,14 +18,16 @@ show process information
show other sensors than temperature
we have to use libsensors instead of parsing the proc files directly
-2000-12-03 Ghassan Matar <gmatar@hexapods.com>
-show contents of any text file
-the file should only contain one line, with a fixed format
-there are two possibilities: text and numbers
-numbers can be used for bars, too
+// 2000-12-03 Ghassan Matar <gmatar@hexapods.com>
+// show contents of any text file
+// the file should only contain one line, with a fixed format
+// there are two possibilities: text and numbers
+// numbers can be used for bars, too
+// Done 2000-03-08, look at %x -lt
-2000-12-03 Ghassan Matar <gmatar@hexapods.com>
-accept data from external sources (fifo?)
+// 2000-12-03 Ghassan Matar <gmatar@hexapods.com>
+// accept data from external sources (fifo?)
+// Done 2000-03-08, look at %x -lt
2001-02-11 Carsten Nau <info@cnau.de>
connect a LED to a spare pin of the parallel port and show if ISDN
@@ -34,7 +36,7 @@ is online
// 2001-01-27 Axel Ehnert <axel@ehnert.net>
// - display numer of emails in a mailbox
// - display seti@home values
-done.
+// done.
2001-03-05 Leo Tötsch <lt@toetsch.at>
rename some tokens: %o->%os, %v->%ov, %r->%or, %p->%op,
diff --git a/cfg.c b/cfg.c
index 89061a2..634448f 100644
--- a/cfg.c
+++ b/<
/* $Id: HD44780.c,v 1.25 2002/08/19 09:11:34 reinelt Exp $
 *
 * driver for display modules based on the HD44780 chip
 *
 * Copyright 1999, 2000 by Michael Reinelt (reinelt@eunet.at)
 *
 * 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, or (at your option)
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 * $Log: HD44780.c,v $
 * Revision 1.25  2002/08/19 09:11:34  reinelt
 * changed HD44780 to use generic bar functions
 *
 * Revision 1.24  2002/08/19 07:36:29  reinelt
 *
 * finished bar.c, USBLCD is the first driver that uses the generic bar functions
 *
 * Revision 1.23  2002/08/19 04:41:20  reinelt
 * introduced bar.c, moved bar stuff from display.h to bar.h
 *
 * Revision 1.22  2002/08/17 14:14:21  reinelt
 *
 * USBLCD fixes
 *
 * Revision 1.21  2002/04/30 07:20:15  reinelt
 *
 * implemented the new ndelay(nanoseconds) in all parallel port drivers
 *
 * Revision 1.20  2001/09/09 12:26:03  reinelt
 * GPO bug: INIT is _not_ inverted
 *
 * Revision 1.19  2001/03/15 15:49:22  ltoetsch
 * fixed compile HD44780.c, cosmetics
 *
 * Revision 1.18  2001/03/15 09:47:13  reinelt
 *
 * some fixes to ppdef
 * off-by-one bug in processor.c fixed
 *
 * Revision 1.17  2001/03/14 16:47:41  reinelt
 * minor cleanups
 *
 * Revision 1.16  2001/03/14 15:30:53  reinelt
 *
 * make ppdev compatible to earlier kernels
 *
 * Revision 1.15  2001/03/14 15:14:59  reinelt
 * added ppdev parallel port access
 *
 * Revision 1.14  2001/03/12 13:44:58  reinelt
 *
 * new udelay() using Time Stamp Counters
 *
 * Revision 1.13  2001/03/12 12:39:36  reinelt
 *
 * reworked autoconf a lot: drivers may be excluded, #define's went to config.h
 *
 * Revision 1.12  2001/02/14 07:40:16  reinelt
 *
 * first (incomplete) GPO implementation
 *
 * Revision 1.11  2001/02/13 12:43:24  reinelt
 *
 * HD_gpo() was missing
 *
 * Revision 1.10  2001/02/13 09:00:13  reinelt
 *
 * prepared framework for GPO's (general purpose outputs)
 *
 * Revision 1.9  2000/10/20 07:17:07  reinelt
 *
 *
 * corrected a bug in HD_goto()
 * Thanks to Gregor Szaktilla <gregor@szaktilla.de>
 *
 * Revision 1.8  2000/08/10 09:44:09  reinelt
 *
 * new debugging scheme: error(), info(), debug()
 * uses syslog if in daemon mode
 *
 * Revision 1.7  2000/08/09 09:50:29  reinelt
 *
 * opened 0.98 development
 * removed driver-specific signal-handlers
 * added 'quit'-function to driver structure
 * added global signal-handler
 *
 * Revision 1.6  2000/07/31 10:43:44  reinelt
 *
 * some changes to support kernel-2.4 (different layout of various files in /proc)
 *
 * Revision 1.5  2000/07/31 06:46:35  reinelt
 *
 * eliminated some compiler warnings with glibc
 *
 * Revision 1.4  2000/04/15 16:56:52  reinelt
 *
 * moved delay loops to udelay.c
 * renamed -d (debugging) switch to -v (verbose)
 * new switch -d to calibrate delay loop
 * 'Delay' entry for HD44780 back again
 * delay loops will not calibrate automatically, because this will fail with hich CPU load
 *
 * Revision 1.3  2000/04/15 11:13:54  reinelt
 *
 * added '-d' (debugging) switch
 * added several debugging messages
 * removed config entry 'Delay' for HD44780 driver
 * delay loop for HD44780 will be calibrated automatically
 *
 * Revision 1.2  2000/04/13 06:09:52  reinelt
 *
 * added BogoMips() to system.c (not used by now, maybe sometimes we can
 * calibrate our delay loop with this value)
 *
 * added delay loop to HD44780 driver. It seems to be quite fast now. Hopefully
 * no compiler will optimize away the delay loop!
 *
 * Revision 1.1  2000/04/12 08:05:45  reinelt
 *
 * first version of the HD44780 driver
 *
 */

/* 
 *
 * exported fuctions:
 *
 * struct LCD HD44780[]
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <sys/ioctl.h>

#ifdef HAVE_SYS_IO_H
#include <sys/io.h>
#define WITH_OUTB
#else
#ifdef HAVE_ASM_IO_H
#include <asm/io.h>
#define WITH_OUTB
#endif
#endif

#if defined (HAVE_LINUX_PARPORT_H) && defined (HAVE_LINUX_PPDEV_H)
#define WITH_PPDEV
#include <linux/parport.h>
#include <linux/ppdev.h>
#endif


#if !defined(WITH_OUTB) && !defined(WITH_PPDEV)
#error neither outb() nor ppdev() possible
#error cannot compile HD44780 driver
#endif

#include "debug.h"
#include "cfg.h"
#include "display.h"
#include "bar.h"
#include "udelay.h"

#define XRES 5
#define YRES 8
#define CHARS 8

static LCD Lcd;


static unsigned short Port=0;

static char *PPdev=NULL;

#ifdef WITH_PPDEV
static int PPfd=-1;
#endif

static char Txt[4][40];
static int  GPO=0;

#ifdef WITH_PPDEV
static void HD_toggle (int bit, int inv, int delay)
{
  struct ppdev_frob_struct frob;
  frob.mask=bit;

  // rise
  frob.val=inv?0:bit;
  ioctl (PPfd, PPFCONTROL, &frob);
  
  // pulse width
  ndelay(delay);      

  // lower
  frob.val=inv?bit:0;
  ioctl (PPfd, PPFCONTROL, &frob);
}
#endif

static void HD_command (unsigned char cmd, int delay)
{
#ifdef WITH_PPDEV
  if (PPdev) {

    struct ppdev_frob_struct frob;
    
    // put data on DB1..DB8
    ioctl(PPfd, PPWDATA, &cmd);
    
    // clear RS (inverted)
    frob.mask=PARPORT_CONTROL_AUTOFD;
    frob.val=PARPORT_CONTROL_AUTOFD;
    ioctl (PPfd, PPFCONTROL, &frob);
    
    // Address set-up time
    ndelay(40);

    // send command
    // Enable cycle time = 230ns
    HD_toggle(PARPORT_CONTROL_STROBE, 1, 230);
    
    // wait for command completion
    udelay(delay);

  } else

#endif

    {
      outb (cmd, Port);    // put data on DB1..DB8
      outb (0x03, Port+2); // clear RS = bit 2 invertet
      ndelay(40);          // Address set-up time
      outb (0x02, Port+2); // set Enable = bit 0 invertet
      ndelay(230);         // Enable cycle time
      outb (0x03, Port+2); // clear Enable
      udelay (delay);      // wait for command completion
    }
}

static void HD_write (char *string, int len, int delay)
{
#ifdef WITH_PPDEV
  if (PPdev) {

    struct ppdev_frob_struct frob;

    // set RS (inverted)
    frob.mask=PARPORT_CONTROL_AUTOFD;
    frob.val=0;
    ioctl (PPfd, PPFCONTROL, &frob);
    
    // Address set-up time
    ndelay(40);
    
    while (len--) {

      // put data on DB1..DB8
      ioctl(PPfd, PPWDATA, string++);
      
      // send command
      HD_toggle(PARPORT_CONTROL_STROBE, 1, 230);

      // wait for command completion
      udelay(delay);
    }
    
  } else

#endif

    {
      outb (0x01, Port+2); // set RS = bit 2 invertet
      ndelay(40);          // Address set-up time
      while (len--) {
	outb (*string++, Port); // put data on DB1..DB8
	outb (0x00, Port+2);    // set Enable = bit 0 invertet
	ndelay(230);            // Enable cycle time
	outb (0x01, Port+2);    // clear Enable
	udelay (delay);
      }
    }
}

static void HD_setGPO (int bits)
{
  if (Lcd.gpos>0) {

#ifdef WITH_PPDEV


    if (PPdev) {
      
      // put data on DB1..DB8
      ioctl(PPfd, PPWDATA, &bits);

      // 74HCT573 set-up time
      ndelay(20);
      
      // toggle INIT
      // 74HCT573 enable pulse width = 24ns
      HD_toggle(PARPORT_CONTROL_INIT, 0, 24);
      
    } else
      
#endif
      
      {
	outb (bits, Port);    // put data on DB1..DB8
	ndelay(20);           // 74HCT573 set-up time
	outb (0x05, Port+2);  // set INIT = bit 2 invertet
	ndelay(24);           // 74HCT573 enable pulse width
	outb (0x03, Port+2);  // clear INIT
      }
  }
}


static int HD_open (void)
{
#ifdef WITH_PPDEV

  if (PPdev) {
    debug ("using ppdev %s", PPdev);
    PPfd=open(PPdev, O_RDWR);
    if (PPfd==-1) {
      error ("HD44780: open(%s) failed: %s", PPdev, strerror(errno));
      return -1;
    }

#if 0
    if (ioctl(PPfd, PPEXCL)) {
      debug ("ioctl(%s, PPEXCL) failed: %s", PPdev, strerror(errno));
    } else {
      debug ("ioctl(%s, PPEXCL) succeded.");
    }
#endif

    if (ioctl(PPfd, PPCLAIM)) {
      error ("HD44780: ioctl(%s, PPCLAIM) failed: %d %s", PPdev, errno, strerror(errno));
      return -1;
    }
  } else

#endif

    {
      debug ("using raw port 0x%x", Port);
      if (ioperm(Port, 3, 1)!=0) {
	error ("HD44780: ioperm(0x%x) failed: %s", Port, strerror(errno));
	return -1;
      }
    }
  
  HD_command (0x30, 4100); // 8 Bit mode, wait 4.1 ms
  HD_command (0x30, 100);  // 8 Bit mode, wait 100 us
  HD_command (0x30, 4100); // 8 Bit mode, wait 4.1 ms
  HD_command (0x38, 40);   // 8 Bit mode, 1/16 duty cycle, 5x8 font
  HD_command (0x08, 40);   // Display off, cursor off, blink off
  HD_command (0x0c, 1640); // Display on, cursor off, blink off, wait 1.64 ms
  HD_command (0x06, 40);   // curser moves to right, no shift

  return 0;
}


static void HD_define_char (int ascii, char *buffer)
{
  HD_command (0x40|8*ascii, 40);
  HD_write (buffer, 8, 120); // 120 usec delay for CG RAM write
}


int HD_clear (void)
{
  int row, col;

  for (row=0; row<Lcd.rows; row++) {
    for (col=0; col<Lcd.cols; col++) {
      Txt[row][col]='\t';
    }
  }

  bar_clear();

  GPO=0;
  HD_setGPO (GPO);         // All GPO's off
  HD_command (0x01, 1640); // clear display
  return 0;
}


int HD_init (LCD *Self)
{
  int rows=-1, cols=-1, gpos=-1;
  char *s, *e;
  
  s=cfg_get ("Port");
  if (s==NULL || *s=='\0') {
    error ("HD44780: no 'Port' entry in %s", cfg_file());
    return -1;
  }
  PPdev=NULL;
  if ((Port=strtol(s, &e, 0))==0 || *e!='\0') {
#ifdef WITH_PPDEV
    Port=0;
    PPdev=s;
#else
    error ("HD44780: bad port '%s' in %s", s, cfg_file());
    return -1;
#endif
  }
  
#ifdef USE_OLD_UDELAY
  s=cfg_get ("Delay");
  if (s==NULL || *s=='\0') {
    error ("HD44780: no 'Delay' entry in %s", cfg_file());
    return -1;
  }
  if ((loops_per_usec=strtol(s, &e, 0))==0 || *e!='\0') {
    error ("HD44780: bad delay '%s' in %s", s, cfg_file());
    return -1;
  }    
#endif
  
  s=cfg_get("Size");
  if (s==NULL || *s=='\0') {
    error ("HD44780: no 'Size' entry in %s", cfg_file());
    return -1;
  }
  if (sscanf(s,"%dx%d",&cols,&rows)!=2 || rows<1 || cols<1) {
    error ("HD44780: bad size '%s'",s);
    return -1;
  }

  s=cfg_get ("GPOs");
  if (s==NULL) {
    gpos=0;
  }
  else if ((gpos=strtol(s, &e, 0))==0 || *e!='\0' || gpos<0 || gpos>8) {
    error ("HD44780: bad GPOs '%s' in %s", s, cfg_file());
    return -1;
  }    
  
  Self->rows=rows;
  Self->cols=cols;
  Self->gpos=gpos;
  Lcd=*Self;
  
#ifndef USE_OLD_UDELAY
  udelay_init();
#endif

  if (HD_open()!=0)
    return -1;
  
  bar_init(rows, cols, XRES, YRES, CHARS);
  bar_add_segment(  0,  0,255, 32); // ASCII  32 = blank
  bar_add_segment(255,255,255,255); // ASCII 255 = block
  
  HD_clear();
  
  return 0;
}


void HD_goto (int row, int col)
{
  int pos;

  pos=(row%2)*64+(row/2)*20+col;
  HD_command (0x80|pos, 40);
}


int HD_put (int row, int col, char *text)
{
  char *p=&Txt[row][col];
  char *t=text;
  
  while (*t && col++<=Lcd.cols) {
    *p++=*t++;
  }
  return 0;
}


int HD_bar (int type, int row, int col, int max, int len1, int len2)
{
  return bar_draw (type, row, col, max, len1, len2);
}


int HD_gpo (int num, int val)
{
  if (num>=Lcd.gpos) 
    return -1;

  if (val) {
    GPO |= 1<<num;     // set bit
  } else {
    GPO &= ~(1<<num);  // clear bit
  }
  return 0;
}


int HD_flush (void)
{
  char buffer[256];
  char *p;
  int c, row, col;
  
  bar_process(HD_define_char);

  for (row=0; row<Lcd.rows; row++) {
    for (col=0; col<Lcd.cols; col++) {
      c=bar_peek(row, col);
      if (c!=-1) {
	Txt[row][col]=(char)c;
      }
    }
    for (col=0; col<Lcd.cols; col++) {
      if (Txt[row][col]=='\t') continue;
      HD_goto (row, col);
      for (p=buffer; col<Lcd.cols; col++, p++) {
	if (Txt[row][col]=='\t') break;
	*p=Txt[row][col];
      }
      HD_write (buffer, p-buffer, 40); // 40 usec delay for write
    }
  }

  HD_setGPO(GPO);

  return 0;
}


int HD_quit (void)
{
#ifdef WITH_PPDEV
  if (PPdev) {
    debug ("closing ppdev %s", PPdev);
    if (ioctl(PPfd, PPRELEASE)) {
      error ("HD44780: ioctl(%s, PPRELEASE) failed: %s", PPdev, strerror(errno));
    }
    if (close(PPfd)==-1) {
      error ("HD44780: close(%s) failed: %s", PPdev, strerror(errno));
      return -1;
    }
  } else 
#endif    
   {
    debug ("closing raw port 0x%x", Port);
    if (ioperm(Port, 3, 0)!=0) {
      error ("HD44780: ioperm(0x%x) failed: %s", Port, strerror(errno));
      return -1;
    }
  }
  return 0;
}


LCD HD44780[] = {
  { name: "HD44780",
    rows:  0,
    cols:  0,
    xres:  XRES,
    yres:  YRES,
    bars:  BAR_L | BAR_R | BAR_U | BAR_D | BAR_H2,
    gpos:  0,
    init:  HD_init,
    clear: HD_clear,
    put:   HD_put,
    bar:   HD_bar,
    gpo:   HD_gpo,
    flush: HD_flush,
    quit:  HD_quit 
  },
  { NULL }
};