]> git.webhop.me Git - lcd4linux.git/commitdiff
driver for TeakLCM by Andreas Thienemann
authormichael <michael@3ae390bd-cb1e-0410-b409-cd5a39f66f1f>
Tue, 14 Feb 2012 03:17:04 +0000 (03:17 +0000)
committermichael <michael@3ae390bd-cb1e-0410-b409-cd5a39f66f1f>
Tue, 14 Feb 2012 03:17:04 +0000 (03:17 +0000)
git-svn-id: https://ssl.bulix.org/svn/lcd4linux/trunk@1173 3ae390bd-cb1e-0410-b409-cd5a39f66f1f

Makefile.am
Makefile.in
aclocal.m4
config.h.in
configure
drivers.m4
drv.c
drv_TeakLCM.c [new file with mode: 0644]
lcd4linux.conf.sample
smoketest.sh

index b1a4ed20366460d6324ff7ed76bf5728ed982586..894fbd80f5d9bb8da4a3b4850384d60455650ef1 100644 (file)
@@ -114,6 +114,7 @@ drv_serdisplib.c              \
 drv_ShuttleVFD.c              \
 drv_SimpleLCD.c               \
 drv_T6963.c                   \
+drv_TeakLCM.c                 \
 drv_Trefon.c                  \
 drv_ula200.c                  \
 drv_USBHUB.c                  \
index 8f2fb2d3bd58099bf2fcb62c5b4bcd31ef4c9fe7..95306f1ea54f1cf69ba4cb78aa14d703e7bf465c 100644 (file)
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.11.2 from Makefile.am.
+# Makefile.in generated by automake 1.11.3 from Makefile.am.
 # @configure_input@
 
 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@@ -97,9 +97,11 @@ DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
 am__remove_distdir = \
-  { test ! -d "$(distdir)" \
-    || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
-         && rm -fr "$(distdir)"; }; }
+  if test -d "$(distdir)"; then \
+    find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+      && rm -rf "$(distdir)" \
+      || { sleep 5 && rm -rf "$(distdir)"; }; \
+  else :; fi
 DIST_ARCHIVES = $(distdir).tar.gz
 GZIP_ENV = --best
 distuninstallcheck_listfiles = find . -type f -print
@@ -348,6 +350,7 @@ drv_serdisplib.c              \
 drv_ShuttleVFD.c              \
 drv_SimpleLCD.c               \
 drv_T6963.c                   \
+drv_TeakLCM.c                 \
 drv_Trefon.c                  \
 drv_ula200.c                  \
 drv_USBHUB.c                  \
@@ -516,7 +519,7 @@ clean-binPROGRAMS:
        list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
        echo " rm -f" $$list; \
        rm -f $$list
-lcd4linux$(EXEEXT): $(lcd4linux_OBJECTS) $(lcd4linux_DEPENDENCIES) 
+lcd4linux$(EXEEXT): $(lcd4linux_OBJECTS) $(lcd4linux_DEPENDENCIES) $(EXTRA_lcd4linux_DEPENDENCIES) 
        @rm -f lcd4linux$(EXEEXT)
        $(lcd4linux_LINK) $(lcd4linux_OBJECTS) $(lcd4linux_LDADD) $(LIBS)
 
@@ -566,6 +569,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_ShuttleVFD.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_SimpleLCD.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_T6963.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_TeakLCM.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_Trefon.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_USBHUB.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_USBLCD.Po@am__quote@
@@ -782,6 +786,10 @@ dist-bzip2: distdir
        tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
        $(am__remove_distdir)
 
+dist-lzip: distdir
+       tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
+       $(am__remove_distdir)
+
 dist-lzma: distdir
        tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
        $(am__remove_distdir)
@@ -818,6 +826,8 @@ distcheck: dist
          bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
        *.tar.lzma*) \
          lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\
+       *.tar.lz*) \
+         lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
        *.tar.xz*) \
          xz -dc $(distdir).tar.xz | $(am__untar) ;;\
        *.tar.Z*) \
@@ -1006,20 +1016,20 @@ uninstall-am: uninstall-binPROGRAMS
 
 .PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
        clean-binPROGRAMS clean-generic clean-libtool ctags dist \
-       dist-all dist-bzip2 dist-gzip dist-lzma dist-shar dist-tarZ \
-       dist-xz dist-zip distcheck distclean distclean-compile \
-       distclean-generic distclean-hdr distclean-libtool \
-       distclean-tags distcleancheck distdir distuninstallcheck dvi \
-       dvi-am html html-am info info-am install install-am \
-       install-binPROGRAMS install-data install-data-am install-dvi \
-       install-dvi-am install-exec install-exec-am install-html \
-       install-html-am install-info install-info-am install-man \
-       install-pdf install-pdf-am install-ps install-ps-am \
-       install-strip installcheck installcheck-am installdirs \
-       maintainer-clean maintainer-clean-generic mostlyclean \
-       mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
-       pdf pdf-am ps ps-am tags uninstall uninstall-am \
-       uninstall-binPROGRAMS
+       dist-all dist-bzip2 dist-gzip dist-lzip dist-lzma dist-shar \
+       dist-tarZ dist-xz dist-zip distcheck distclean \
+       distclean-compile distclean-generic distclean-hdr \
+       distclean-libtool distclean-tags distcleancheck distdir \
+       distuninstallcheck dvi dvi-am html html-am info info-am \
+       install install-am install-binPROGRAMS install-data \
+       install-data-am install-dvi install-dvi-am install-exec \
+       install-exec-am install-html install-html-am install-info \
+       install-info-am install-man install-pdf install-pdf-am \
+       install-ps install-ps-am install-strip installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-binPROGRAMS
 
 
 # create subversion version
index e901394fa2204acdfd43df30a0852846be1b8002..7b632e4d9f168f9ee1821aea9dec371b56170a1d 100644 (file)
@@ -1,4 +1,4 @@
-# generated automatically by aclocal 1.11.2 -*- Autoconf -*-
+# generated automatically by aclocal 1.11.3 -*- Autoconf -*-
 
 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
 # 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
@@ -10130,7 +10130,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
 [am__api_version='1.11'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.11.2], [],
+m4_if([$1], [1.11.3], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -10146,7 +10146,7 @@ m4_define([_AM_AUTOCONF_VERSION], [])
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.11.2])dnl
+[AM_AUTOMAKE_VERSION([1.11.3])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
@@ -11005,7 +11005,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004, 2005  Free Software Foundation, Inc.
+# Copyright (C) 2004, 2005, 2012 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -11027,10 +11027,11 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 # a tarball read from stdin.
 #     $(am__untar) < result.tar
 AC_DEFUN([_AM_PROG_TAR],
-[# Always define AMTAR for backward compatibility.
-AM_MISSING_PROG([AMTAR], [tar])
+[# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
 m4_if([$1], [v7],
-     [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+     [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
      [m4_case([$1], [ustar],, [pax],,
               [m4_fatal([Unknown tar format])])
 AC_MSG_CHECKING([how to create a $1 tar archive])
index 97c3e84b49e4a2d339a83184e2aa3241c7cba211..7fdebfcbdb7f93c70714c07365b9b9ce99c62f8e 100644 (file)
 /* T6963 driver */
 #undef WITH_T6963
 
+/* TeakLCM driver */
+#undef WITH_TEAK_LCM
+
 /* TREFON driver */
 #undef WITH_TREFON
 
index 40be1492dea404209daee6dd9ece7182935dda75..1e1e1f6c05f8b05ed8ab3ac1ddf60c5977b430ac 100755 (executable)
--- a/configure
+++ b/configure
@@ -1449,7 +1449,7 @@ Optional Packages:
                           Newhaven, Noritake, NULL, Pertelian, PHAnderson,
                           PICGraphic, picoLCD, picoLCDGraphic, PNG, PPM, RouterBoard,
                           Sample, serdisplib, ShuttleVFD, SimpleLCD, st2205, T6963,
-                          Trefon, ULA200, USBHUB, USBLCD, VNC, WincorNixdorf, X11
+                          TeakLCM, Trefon, ULA200, USBHUB, USBLCD, VNC, WincorNixdorf, X11
   --with-plugins=<list>   choose which plugins to compile.
                           type --with-plugins=list for a list
                           of avaible plugins
@@ -2821,11 +2821,11 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
 
 # We need awk for the "check" target.  The system "awk" is bad on
 # some platforms.
-# Always define AMTAR for backward compatibility.
+# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
 
-AMTAR=${AMTAR-"${am_missing_run}tar"}
-
-am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
 
 
 
@@ -6317,6 +6317,7 @@ for driver in $drivers; do
         SHUTTLEVFD="yes"
          SIMPLELCD="yes"
          T6963="yes"
+         TeakLCM="yes"
          Trefon="yes"
          ULA200="yes"
         USBHUB="yes"
@@ -6460,6 +6461,9 @@ for driver in $drivers; do
       T6963)
          T6963=$val
          ;;
+      TeakLCM)
+         TeakLCM=$val
+         ;;
       Trefon)
          Trefon=$val
          ;;
@@ -7081,6 +7085,16 @@ $as_echo "$as_me: WARNING: asm/io.h or {linux/parport.h and linux/ppdev.h} not f
    fi
 fi
 
+if test "$TeakLCM" = "yes"; then
+   TEXT="yes"
+   GPIO="no"
+   SERIAL="yes"
+   DRIVERS="$DRIVERS drv_TeakLCM.o"
+
+$as_echo "#define WITH_TEAK_LCM 1" >>confdefs.h
+
+fi
+
 if test "$Trefon" = "yes"; then
    if test "$has_usb" = "true"; then
       TEXT="yes"
index 09bd152fc53e334da006e58d602a59f460d08819..f9f289d66024d3ec4c22d72f7f3999a04db60e47 100644 (file)
@@ -39,7 +39,7 @@ AC_ARG_WITH(
   [                        Newhaven, Noritake, NULL, Pertelian, PHAnderson,]
   [                        PICGraphic, picoLCD, picoLCDGraphic, PNG, PPM, RouterBoard,]
   [                        Sample, serdisplib, ShuttleVFD, SimpleLCD, st2205, T6963,]
-  [                        Trefon, ULA200, USBHUB, USBLCD, VNC, WincorNixdorf, X11],
+  [                        TeakLCM, Trefon, ULA200, USBHUB, USBLCD, VNC, WincorNixdorf, X11],
   drivers=$withval,
   drivers=all
 )
@@ -105,6 +105,7 @@ for driver in $drivers; do
         SHUTTLEVFD="yes"
          SIMPLELCD="yes"
          T6963="yes"
+         TeakLCM="yes"
          Trefon="yes"
          ULA200="yes"
         USBHUB="yes"
@@ -248,6 +249,9 @@ for driver in $drivers; do
       T6963)
          T6963=$val
          ;;
+      TeakLCM)
+         TeakLCM=$val
+         ;;
       Trefon)
          Trefon=$val
          ;;
@@ -752,6 +756,14 @@ if test "$T6963" = "yes"; then
    fi
 fi
 
+if test "$TeakLCM" = "yes"; then
+   TEXT="yes"
+   GPIO="no"
+   SERIAL="yes"
+   DRIVERS="$DRIVERS drv_TeakLCM.o"
+   AC_DEFINE(WITH_TEAK_LCM,1,[TeakLCM driver])
+fi
+
 if test "$Trefon" = "yes"; then
    if test "$has_usb" = "true"; then
       TEXT="yes"
diff --git a/drv.c b/drv.c
index 32883a80d34db1f821315d4ca8a0d441571dd019..9ce8a390a0d50078002a455ba3d315d74954f7fe 100644 (file)
--- a/drv.c
+++ b/drv.c
@@ -90,6 +90,7 @@ extern DRIVER drv_serdisplib;
 extern DRIVER drv_ShuttleVFD;
 extern DRIVER drv_SimpleLCD;
 extern DRIVER drv_T6963;
+extern DRIVER drv_TeakLCM;
 extern DRIVER drv_Trefon;
 extern DRIVER drv_ula200;
 extern DRIVER drv_USBHUB;
@@ -236,6 +237,9 @@ DRIVER *Driver[] = {
 #ifdef WITH_T6963
     &drv_T6963,
 #endif
+#ifdef WITH_TEAK_LCM
+    &drv_TeakLCM,
+#endif
 #ifdef WITH_TREFON
     &drv_Trefon,
 #endif
diff --git a/drv_TeakLCM.c b/drv_TeakLCM.c
new file mode 100644 (file)
index 0000000..7517b40
--- /dev/null
@@ -0,0 +1,1024 @@
+/* $Id$
+ * $URL$
+ *
+ * TeakLCM lcd4linux driver
+ *
+ * Copyright (C) 2005 Michael Reinelt <michael@reinelt.co.at>
+ * Copyright (C) 2005, 2006, 2007 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
+ * Copyright (C) 2011 Hans Ulrich Niedermann <hun@n-dimensional.de>
+ * Copyright (C) 2011, 2012 Andreas Thienemann <andreas@bawue.net>
+ *
+ * This file is part of LCD4Linux.
+ *
+ * LCD4Linux 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.
+ *
+ * LCD4Linux 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.
+ *
+ */
+
+/* 
+ *
+ * exported fuctions:
+ *
+ * struct DRIVER drv_TeakLCM
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <assert.h>
+
+#include "event.h"
+#include "timer.h"
+#include "debug.h"
+#include "cfg.h"
+#include "qprintf.h"
+#include "udelay.h"
+#include "plugin.h"
+#include "widget.h"
+#include "widget_text.h"
+#include "widget_icon.h"
+#include "widget_bar.h"
+#include "drv.h"
+
+#include "drv_generic_text.h"
+#include "drv_generic_serial.h"
+
+
+static char Name[] = "TeakLCM";
+
+
+static int global_reset_rx_flag = 0;
+
+
+#define HI8(value) ((u_int8_t)(((value)>>8) & 0xff))
+#define LO8(value) ((u_int8_t)((value) & 0xff))
+
+
+static u_int16_t CRC16(u_int8_t value, u_int16_t crcin)
+{
+    u_int16_t k = (((crcin >> 8) ^ value) & 255) << 8;
+    u_int16_t crc = 0;
+    int bits;
+    for (bits = 8; bits; --bits) {
+       if ((crc ^ k) & 0x8000)
+           crc = (crc << 1) ^ 0x1021;
+       else
+           crc <<= 1;
+       k <<= 1;
+    }
+    return ((crcin << 8) ^ crc);
+}
+
+
+/** Return a printable character */
+static char printable(const char ch)
+{
+    if ((32 <= ch) && (ch < 127)) {
+       return ch;
+    } else {
+       return '.';
+    }
+}
+
+
+static void debug_data_int(const char *prefix, const void *data, const size_t size, const unsigned int delta)
+{
+    const u_int8_t *b = (const u_int8_t *) data;
+    size_t y;
+    assert(delta <= 24);
+    for (y = 0; y < size; y += delta) {
+       char buf[100];
+       size_t x;
+       ssize_t idx = 0;
+       idx += sprintf(&(buf[idx]), "%04x ", y);
+       for (x = 0; x < delta; x++) {
+           const size_t i = x + y;
+           if (i < size) {
+               idx += sprintf(&(buf[idx]), " %02x", b[i]);
+           } else {
+               idx += sprintf(&(buf[idx]), "   ");
+           }
+       }
+       idx += sprintf(&buf[idx], "  ");
+       for (x = 0; x < delta; x++) {
+           const size_t i = x + y;
+           if (i < size) {
+               idx += sprintf(&buf[idx], "%c", printable(b[i]));
+           } else {
+               idx += sprintf(&buf[idx], " ");
+           }
+       }
+       debug("%s%s", prefix, buf);
+    }
+}
+
+
+static void debug_data(const char *prefix, const void *data, const size_t size)
+{
+    debug_data_int(prefix, data, size, 16);
+}
+
+
+typedef enum {
+    CMD_CONNECT = 0x05,
+    CMD_DISCONNECT = 0x06,
+    CMD_ALARM = 0x07,
+    CMD_WRITE = 0x08,
+    CMD_PRINT1 = 0x09,
+    CMD_PRINT2 = 0x0A,
+    CMD_ACK = 0x0B,
+    CMD_NACK = 0x0C,
+    CMD_CONFIRM = 0x0D,
+    CMD_RESET = 0x0E,
+
+    LCM_CLEAR = 0x21,
+    LCM_HOME = 0x22,
+    LCM_CURSOR_SHIFT_R = 0x23,
+    LCM_CURSOR_SHIFT_L = 0x24,
+    LCM_BACKLIGHT_ON = 0x25,
+    LCM_BACKLIGHT_OFF = 0x26,
+    LCM_LINE2 = 0x27,
+    LCM_DISPLAY_SHIFT_R = 0x28,
+    LCM_DISPLAY_SHIFT_L = 0x29,
+    LCM_CURSOR_ON = 0x2A,
+    LCM_CURSOR_OFF = 0x2B,
+    LCM_CURSOR_BLINK = 0x2C,
+    LCM_DISPLAY_ON = 0x2D,
+    LCM_DISPLAY_OFF = 0x2E
+} lcm_cmd_t;
+
+
+static
+const char *cmdstr(const lcm_cmd_t cmd)
+{
+    switch (cmd) {
+#define D(CMD) case CMD_ ## CMD: return "CMD_" # CMD; break;
+       D(CONNECT);
+       D(DISCONNECT);
+       D(ACK);
+       D(NACK);
+       D(CONFIRM);
+       D(RESET);
+       D(ALARM);
+       D(WRITE);
+       D(PRINT1);
+       D(PRINT2);
+#undef D
+#define D(CMD) case LCM_ ## CMD: return "LCM_" # CMD; break;
+       D(CLEAR);
+       D(HOME);
+       D(CURSOR_SHIFT_R);
+       D(CURSOR_SHIFT_L);
+       D(BACKLIGHT_ON);
+       D(BACKLIGHT_OFF);
+       D(LINE2);
+       D(DISPLAY_SHIFT_R);
+       D(DISPLAY_SHIFT_L);
+       D(CURSOR_ON);
+       D(CURSOR_OFF);
+       D(CURSOR_BLINK);
+       D(DISPLAY_ON);
+       D(DISPLAY_OFF);
+#undef D
+    }
+    return "CMD_UNKNOWN";
+}
+
+
+/*
+ * Magic defines
+ */
+
+#define LCM_FRAME_MASK      0xFF
+#define LCM_TIMEOUT         2
+#define LCM_ESC             0x1B
+
+#define LCM_KEY1            0x31
+#define LCM_KEY2            0x32
+#define LCM_KEY3            0x33
+#define LCM_KEY4            0x34
+#define LCM_KEY12           0x35
+#define LCM_KEY13           0x36
+#define LCM_KEY14           0x37
+#define LCM_KEY23           0x38
+#define LCM_KEY24           0x39
+#define LCM_KEY34           0x3A
+
+
+/****************************************/
+/***  hardware dependant functions    ***/
+/****************************************/
+
+/* global LCM state machine */
+
+
+struct _lcm_fsm_t;
+typedef struct _lcm_fsm_t lcm_fsm_t;
+
+
+typedef enum {
+    ST_IDLE,                   /* mode == 0, IDLE */
+    ST_COMMAND,                        /* mode == 1, COMMAND */
+    ST_CONNECTED               /* mode == 2, CONNECTED */
+} lcm_state_t;
+
+
+static
+const char *state2str(const lcm_state_t state)
+{
+    switch (state) {
+    case ST_IDLE:
+       return "ST_IDLE (0)";
+       break;
+    case ST_COMMAND:
+       return "ST_COMMAND (1)";
+       break;
+    case ST_CONNECTED:
+       return "ST_CONNECTED (2)";
+       break;
+    }
+    return "ST_UNKNOWN";
+}
+
+
+#if 0
+static
+void repeat_connect_to_display_callback(void *data);
+#endif
+
+static
+void lcm_send_cmd(lcm_cmd_t cmd);
+
+static
+void drv_TeakLCM_clear(void);
+
+
+static
+void raw_send_cmd_frame(lcm_cmd_t cmd);
+
+static
+void raw_send_data_frame(lcm_cmd_t cmd, const char *data, const unsigned int len);
+
+
+static
+lcm_state_t fsm_get_state(lcm_fsm_t * fsm);
+
+static
+void fsm_handle_cmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd);
+
+static
+void fsm_handle_datacmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const u_int8_t * payload, const unsigned int payload_len);
+
+static
+void try_reset(void);
+
+static
+void fsm_step(lcm_fsm_t * fsm);
+
+static
+void fsm_trans_noop(lcm_fsm_t * fsm, const lcm_state_t next_state);
+
+static
+void fsm_trans_cmd(lcm_fsm_t * fsm, const lcm_state_t next_state, const lcm_cmd_t cmd);
+
+static
+void fsm_trans_data(lcm_fsm_t * fsm,
+                   const lcm_state_t next_state, const lcm_cmd_t cmd, const char *data, const unsigned int len);
+
+
+static
+void fsm_handle_bytes(lcm_fsm_t * fsm, u_int8_t * rxbuf, const unsigned int buflen)
+{
+    if ((buflen >= 3) && (rxbuf[0] == LCM_FRAME_MASK) && (rxbuf[2] == LCM_FRAME_MASK)) {
+       const lcm_cmd_t cmd = rxbuf[1];
+       debug("%s Received cmd frame (cmd=%d=%s)", __FUNCTION__, cmd, cmdstr(cmd));
+       fsm_handle_cmd(fsm, cmd);
+       if (buflen > 3) {
+           /* recursively handle remaining bytes */
+           fsm_handle_bytes(fsm, &rxbuf[3], buflen - 3);
+       }
+       return;
+    } else if ((buflen > 3) && (rxbuf[0] == LCM_FRAME_MASK)) {
+       unsigned int ri;        /* raw indexed */
+       unsigned int ci;        /* cooked indexed, i.e. after unescaping */
+
+       debug("%s Received possible data frame", __FUNCTION__);
+
+       /* unescape rxframe data in place */
+       u_int16_t crc0 = 0, crc1 = 0, crc2 = 0, crc3 = 0;
+       for (ri = 1, ci = 1; ri < buflen; ri++) {
+           switch (rxbuf[ri]) {
+           case LCM_ESC:
+               ri++;
+               /* fall through */
+           default:
+               rxbuf[ci++] = rxbuf[ri];
+               crc3 = crc2;
+               crc2 = crc1;
+               crc1 = crc0;
+               crc0 = CRC16(rxbuf[ri], crc0);
+               break;
+           }
+           if ((rxbuf[ci - 1] == LCM_FRAME_MASK) && (rxbuf[ci - 2] == LO8(crc3)) && (rxbuf[ci - 3] == HI8(crc3))) {
+               /* looks like a complete data frame */
+               lcm_cmd_t cmd = rxbuf[1];
+               u_int16_t len = (rxbuf[3] << 8) + rxbuf[2];
+               assert(ci == (unsigned int) (1 + 1 + 2 + len + 2 + 1));
+               fsm_handle_datacmd(fsm, cmd, &rxbuf[4], len);
+               if (ri + 1 < buflen) {
+                   /* recursively handle remaining bytes */
+                   fsm_handle_bytes(fsm, &rxbuf[ri + 1], buflen - ri);
+               }
+               return;
+           }
+       }
+
+       fsm_trans_cmd(fsm, fsm_get_state(fsm),  /* TODO: Is this a good next_state value? */
+                     CMD_NACK);
+       debug("%s checksum/framemask error", __FUNCTION__);
+       return;
+    } else {
+       debug("%s Received garbage data:", __FUNCTION__);
+       debug_data(" RXD ", rxbuf, buflen);
+       return;
+    }
+}
+
+
+static void fsm_handle_cmd(lcm_fsm_t * fsm, lcm_cmd_t cmd)
+{
+    // debug("fsm_handle_cmd: old state 0x%02x %s", lcm_mode, modestr(lcm_mode));
+    const lcm_state_t old_state = fsm_get_state(fsm);
+    if (CMD_RESET == cmd) {
+       global_reset_rx_flag = 1;
+    }
+    switch (old_state) {
+    case ST_IDLE:
+    case ST_COMMAND:
+       switch (cmd) {
+       case CMD_CONNECT:
+           fsm_trans_cmd(fsm, ST_COMMAND, CMD_ACK);
+           break;
+       case CMD_ACK:
+           fsm_trans_cmd(fsm, ST_CONNECTED, CMD_CONFIRM);
+           break;
+       case CMD_NACK:
+           fsm_trans_cmd(fsm, ST_IDLE, CMD_CONFIRM);
+           break;
+       case CMD_CONFIRM:
+           fsm_trans_noop(fsm, ST_CONNECTED);
+           break;
+       case CMD_RESET:
+           fsm_trans_cmd(fsm, ST_COMMAND, CMD_CONNECT);
+           break;
+       default:
+           error("%s: Unhandled cmd %s in state %s", Name, cmdstr(cmd), state2str(old_state));
+           fsm_trans_cmd(fsm, ST_IDLE, CMD_NACK);
+           break;
+       }
+       break;
+    case ST_CONNECTED:         /* "if (mode == 2)" */
+       switch (cmd) {
+       case CMD_ACK:
+           fsm_trans_cmd(fsm, ST_CONNECTED, CMD_CONFIRM);
+           break;
+       case CMD_CONNECT:
+           fsm_trans_cmd(fsm, ST_CONNECTED, CMD_NACK);
+           break;
+       case CMD_DISCONNECT:
+           fsm_trans_cmd(fsm, ST_CONNECTED, CMD_ACK);
+           break;
+       case CMD_RESET:
+           fsm_trans_cmd(fsm, ST_IDLE, CMD_CONNECT);
+           break;
+       default:
+           debug("%s: Ignoring unhandled cmd %s in state %s", Name, cmdstr(cmd), state2str(old_state));
+           fsm_trans_noop(fsm, ST_CONNECTED);
+           break;
+       }
+       break;
+    }
+    fsm_step(fsm);
+}
+
+
+static
+void fsm_handle_datacmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const u_int8_t * payload, unsigned int payload_len)
+{
+    const lcm_state_t old_state = fsm_get_state(fsm);
+    debug("fsm_handle_datacmd: old state 0x%02x %s", old_state, state2str(old_state));
+    switch (old_state) {
+    case ST_CONNECTED:
+       switch (cmd) {
+       case CMD_WRITE:
+           assert(payload_len == 1);
+           debug("Got a key code 0x%02x", *payload);
+           fsm_trans_noop(fsm, ST_CONNECTED);
+           // lcm_send_cmd_frame(CMD_ACK);
+           break;
+       default:
+           debug("Got an unknown data frame: %d=%s", cmd, cmdstr(cmd));
+           fsm_trans_noop(fsm, ST_CONNECTED);
+           // lcm_send_cmd_frame(CMD_NACK);
+           break;
+       }
+       break;
+    case ST_IDLE:
+    case ST_COMMAND:
+       fsm_trans_cmd(fsm, old_state, CMD_NACK);
+       break;
+    }
+    fsm_step(fsm);
+}
+
+
+struct _lcm_fsm_t {
+    lcm_state_t state;
+    lcm_state_t next_state;
+    enum {
+       ACTION_UNINITIALIZED,
+       ACTION_NOOP,
+       ACTION_CMD,
+       ACTION_DATA
+    } action_type;
+    union {
+       struct {
+           lcm_cmd_t cmd;
+       } cmd_frame;
+       struct {
+           lcm_cmd_t cmd;
+           const char *data;
+           unsigned int len;
+       } data_frame;
+    } action;
+};
+
+
+static
+lcm_state_t fsm_get_state(lcm_fsm_t * fsm)
+{
+    return fsm->state;
+}
+
+
+static
+void flush_shadow(void);
+
+
+static
+void fsm_step(lcm_fsm_t * fsm)
+{
+    debug("fsm: old_state=%s new_state=%s", state2str(fsm->state), state2str(fsm->next_state));
+    switch (fsm->action_type) {
+    case ACTION_UNINITIALIZED:
+       error("Uninitialized LCM FSM action");
+       abort();
+       break;
+    case ACTION_NOOP:
+       break;
+    case ACTION_CMD:
+       raw_send_cmd_frame(fsm->action.cmd_frame.cmd);
+       break;
+    case ACTION_DATA:
+       raw_send_data_frame(fsm->action.data_frame.cmd, fsm->action.data_frame.data, fsm->action.data_frame.len);
+       break;
+    }
+    fsm->action_type = ACTION_UNINITIALIZED;
+    switch (fsm->next_state) {
+    case ST_IDLE:
+    case ST_COMMAND:
+       fsm->state = fsm->next_state;
+       fsm->next_state = -1;
+       return;
+       break;
+    case ST_CONNECTED:
+       if (fsm->state != ST_CONNECTED) {
+           /* going from ST_IDLE or ST_COMMAND into ST_CONNECTED */
+           if (!global_reset_rx_flag) {
+               try_reset();
+#if 0
+               int timer_res = timer_add(repeat_connect_to_display_callback, NULL, 50 /*ms */ , 1);
+               debug("re-scheduled connect callback result: %d", timer_res);
+
+               done by try_reset / fsm_init fsm->state = fsm->next_state;
+               fsm->next_state = -1;
+#endif
+               return;
+           } else {
+               /* properly connected for the first time */
+               debug("%s: %s NOW CONNECTED!!!", Name, __FUNCTION__);
+
+               fsm->state = fsm->next_state;
+               fsm->next_state = -1;
+
+               lcm_send_cmd(LCM_DISPLAY_ON);
+               flush_shadow();
+               lcm_send_cmd(LCM_BACKLIGHT_ON);
+               return;
+           }
+       } else {
+           debug("no state change in ST_CONNECTED");
+           fsm->state = fsm->next_state;
+           fsm->next_state = -1;
+           return;
+       }
+       error("we should never arrive here");
+       abort();
+       break;
+    }
+    error("LCM FSM: Illegal next_state");
+    abort();
+}
+
+
+#if 0
+
+#endif
+
+
+static
+void fsm_trans_noop(lcm_fsm_t * fsm, const lcm_state_t next_state)
+{
+    fsm->next_state = next_state;
+    fsm->action_type = ACTION_NOOP;
+}
+
+
+static
+void fsm_trans_cmd(lcm_fsm_t * fsm, const lcm_state_t next_state, const lcm_cmd_t cmd)
+{
+    fsm->next_state = next_state;
+    fsm->action_type = ACTION_CMD;
+    fsm->action.cmd_frame.cmd = cmd;
+}
+
+
+static
+void fsm_trans_data(lcm_fsm_t * fsm,
+                   const lcm_state_t next_state, const lcm_cmd_t cmd, const char *data, const unsigned int len)
+{
+    fsm->next_state = next_state;
+    fsm->action_type = ACTION_DATA;
+    fsm->action.data_frame.cmd = cmd;
+    fsm->action.data_frame.data = data;
+    fsm->action.data_frame.len = len;
+}
+
+
+static
+void fsm_send(lcm_fsm_t * fsm, const lcm_cmd_t cmd)
+{
+    const lcm_state_t old_state = fsm_get_state(fsm);
+    switch (old_state) {
+    case ST_IDLE:
+    case ST_COMMAND:
+       debug("%s: %s, ignoring cmd 0x%02x=%s", __FUNCTION__, state2str(old_state), cmd, cmdstr(cmd));
+       /* Silently ignore the command to send. */
+       /* TODO: Would it be better to queue it and send it later? */
+       break;
+    case ST_CONNECTED:
+       fsm_trans_cmd(fsm, ST_CONNECTED, cmd);
+       fsm_step(fsm);
+       break;
+    }
+}
+
+
+static
+void fsm_send_data(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const void *data, const unsigned int len)
+{
+    const lcm_state_t old_state = fsm_get_state(fsm);
+    switch (old_state) {
+    case ST_IDLE:
+    case ST_COMMAND:
+       debug("%s: %s, ignoring data cmd 0x%02x=%s", __FUNCTION__, state2str(old_state), cmd, cmdstr(cmd));
+       /* Silently ignore the command to send. */
+       /* TODO: Would it be better to queue it and send it later? */
+       break;
+    case ST_CONNECTED:
+       fsm_trans_data(fsm, ST_CONNECTED, cmd, data, len);
+       fsm_step(fsm);
+       break;
+    }
+}
+
+
+static lcm_fsm_t lcm_fsm;
+
+
+static
+void fsm_init(void)
+{
+    lcm_fsm.state = ST_IDLE;
+    lcm_fsm.next_state = -1;
+    lcm_fsm.action_type = ACTION_UNINITIALIZED;
+}
+
+
+
+/* Send a command frame to the TCM board */
+static
+void raw_send_cmd_frame(lcm_cmd_t cmd)
+{
+    // lcm_receive_check();
+    char cmd_buf[3];
+    cmd_buf[0] = LCM_FRAME_MASK;
+    cmd_buf[1] = cmd;
+    cmd_buf[2] = LCM_FRAME_MASK;
+    debug("%s sending cmd frame cmd=0x%02x=%s", __FUNCTION__, cmd, cmdstr(cmd));
+    debug_data(" TX ", cmd_buf, 3);
+    drv_generic_serial_write(cmd_buf, 3);
+    usleep(100);
+#if 0
+    usleep(100000);
+    switch (cmd) {
+    case CMD_ACK:
+       //case CMD_CONFIRM:
+    case CMD_NACK:
+       lcm_receive_check();
+       break;
+    default:
+       if (1) {
+           int i;
+           for (i = 0; i < 20; i++) {
+               usleep(100000);
+               if (lcm_receive_check()) {
+                   break;
+               }
+           }
+       }
+       break;
+    }
+#endif
+}
+
+
+/* Send a data frame to the TCM board */
+static
+void raw_send_data_frame(lcm_cmd_t cmd, const char *data, const unsigned int len)
+{
+    unsigned int di;           /* data index */
+    unsigned int fi;           /* frame index */
+    static char frame[32];
+    u_int16_t crc = 0;
+
+    frame[0] = LCM_FRAME_MASK;
+
+    frame[1] = cmd;
+    crc = CRC16(frame[1], crc);
+
+    frame[2] = HI8(len);
+    crc = CRC16(frame[2], crc);
+
+    frame[3] = LO8(len);
+    crc = CRC16(frame[3], crc);
+
+#define APPEND(value)                                 \
+    do {                                              \
+       const unsigned char v = (value);               \
+       if ((v == LCM_FRAME_MASK) || (v == LCM_ESC)) { \
+           frame[fi++] = LCM_ESC;                     \
+       }                                              \
+       frame[fi++] = v;                               \
+       crc = CRC16(v, crc);                           \
+    } while (0)
+
+#define APPEND_NOCRC(value)                           \
+    do {                                              \
+       const unsigned char v = (value);               \
+       if ((v == LCM_FRAME_MASK) || (v == LCM_ESC)) { \
+           frame[fi++] = LCM_ESC;                     \
+       }                                              \
+       frame[fi++] = v;                               \
+    } while (0)
+
+    for (fi = 4, di = 0; di < len; di++) {
+       APPEND(data[di]);
+    }
+
+    APPEND_NOCRC(HI8(crc));
+    APPEND_NOCRC(LO8(crc));
+
+    frame[fi++] = LCM_FRAME_MASK;
+
+    debug_data(" TXD ", frame, fi);
+    drv_generic_serial_write(frame, fi);
+
+#undef APPEND
+
+    usleep(500);
+}
+
+
+static
+void lcm_send_cmd(lcm_cmd_t cmd)
+{
+    fsm_send(&lcm_fsm, cmd);
+}
+
+
+static
+void lcm_event_callback(event_flags_t flags, void *data)
+{
+    lcm_fsm_t *fsm = (lcm_fsm_t *) data;
+    debug("%s: flags=%d, data=%p", __FUNCTION__, flags, data);
+    if (flags & EVENT_READ) {
+       static u_int8_t rxbuf[32];
+       const int readlen = drv_generic_serial_poll((void *) rxbuf, sizeof(rxbuf));
+       if (readlen <= 0) {
+           debug("%s Received no data", __FUNCTION__);
+       } else {
+           debug("%s RECEIVED %d bytes", __FUNCTION__, readlen);
+           debug_data(" RX ", rxbuf, readlen);
+           fsm_handle_bytes(fsm, rxbuf, readlen);
+       }
+    }
+}
+
+
+static int drv_TeakLCM_open(const char *section)
+{
+    /* open serial port */
+    /* don't mind about device, speed and stuff, this function will take care of */
+
+    const int fd = drv_generic_serial_open(section, Name, 0);
+    if (fd < 0)
+       return -1;
+
+    return fd;
+}
+
+
+static int drv_TeakLCM_close(int fd)
+{
+    if (fd >= 0) {
+       event_del(fd);
+    }
+
+    /* close whatever port you've opened */
+    drv_generic_serial_close();
+
+    return 0;
+}
+
+
+/* shadow buffer */
+static
+char *shadow;
+
+
+static void debug_shadow(const char *prefix)
+{
+    debug_data_int(prefix, shadow, DCOLS * DROWS, 20);
+}
+
+
+static
+void flush_shadow(void)
+{
+    debug("%s called", __FUNCTION__);
+    debug_shadow(" shadow ");
+    usleep(50000);
+    fsm_send_data(&lcm_fsm, CMD_PRINT1, &shadow[DCOLS * 0], DCOLS);
+    usleep(50000);
+    fsm_send_data(&lcm_fsm, CMD_PRINT2, &shadow[DCOLS * 1], DCOLS);
+    usleep(50000);
+}
+
+
+/* text mode displays only */
+static
+void drv_TeakLCM_clear(void)
+{
+    /* do whatever is necessary to clear the display */
+    memset(shadow, ' ', DROWS * DCOLS);
+    flush_shadow();
+}
+
+
+/* text mode displays only */
+static void drv_TeakLCM_write(const int row, const int col, const char *data, int len)
+{
+    debug("%s row=%d col=%d len=%d data=\"%s\"", __FUNCTION__, row, col, len, data);
+
+    memcpy(&shadow[DCOLS * row + col], data, len);
+
+    debug_shadow(" shadow ");
+
+    fsm_send_data(&lcm_fsm, (row == 0) ? CMD_PRINT1 : CMD_PRINT2, &shadow[DCOLS * row], DCOLS);
+}
+
+
+static
+void try_reset(void)
+{
+    debug("%s called", __FUNCTION__);
+    fsm_init();
+    raw_send_cmd_frame(CMD_RESET);
+}
+
+
+#if 0
+static
+void repeat_connect_to_display_callback(void *data)
+{
+    static int already_called = 0;
+    if (!already_called) {
+       debug("%s(%p): called", __FUNCTION__, data);
+
+       /* reset & initialize display */
+       try_reset();
+       already_called = 1;
+    } else {
+       debug("%s(%p): already called, ignoring", __FUNCTION__, data);
+    }
+}
+#endif
+
+
+static
+int global_fd = -1;
+
+
+static
+void initial_connect_to_display_callback(void *data)
+{
+    debug("%s(%p): called", __FUNCTION__, data);
+
+    debug("Calling event_add for fd=%d", global_fd);
+    int ret = event_add(lcm_event_callback, &lcm_fsm, global_fd, 1, 0, 1);
+    debug("event_add result: %d", ret);
+
+    /* reset & initialize display */
+    try_reset();
+}
+
+
+/* start text mode display */
+static int drv_TeakLCM_start(const char *section)
+{
+    int rows = -1, cols = -1;
+    char *s;
+
+    s = cfg_get(section, "Size", NULL);
+    if (s == NULL || *s == '\0') {
+       error("%s: no '%s.Size' entry from %s", Name, section, cfg_source());
+       return -1;
+    }
+    if (sscanf(s, "%dx%d", &cols, &rows) != 2 || rows < 1 || cols < 1) {
+       error("%s: bad %s.Size '%s' from %s", Name, section, s, cfg_source());
+       free(s);
+       return -1;
+    }
+
+    DROWS = rows;
+    DCOLS = cols;
+    shadow = malloc(DROWS * DCOLS);
+    memset(shadow, ' ', DROWS * DCOLS);
+
+    /* open communication with the display */
+    global_fd = drv_TeakLCM_open(section);
+    if (global_fd < 0) {
+       return -1;
+    }
+    debug("%s: %s opened", Name, __FUNCTION__);
+
+    /* read initial garbage data */
+    static u_int8_t rxbuf[32];
+    const int readlen = drv_generic_serial_poll((void *) rxbuf, sizeof(rxbuf));
+    if (readlen >= 0) {
+       debug_data(" initial RX garbage ", rxbuf, readlen);
+    }
+
+    /* We need to do a delayed connect */
+    int timer_res = timer_add(initial_connect_to_display_callback, NULL, 10 /*ms */ , 1);
+    debug("timer_add for connect callback result: %d", timer_res);
+
+    debug("%s: %s done", Name, __FUNCTION__);
+    return 0;
+}
+
+
+/****************************************/
+/***            plugins               ***/
+/****************************************/
+
+
+/****************************************/
+/***        widget callbacks          ***/
+/****************************************/
+
+
+/* using drv_generic_text_draw(W) */
+/* using drv_generic_text_icon_draw(W) */
+/* using drv_generic_text_bar_draw(W) */
+
+
+/****************************************/
+/***        exported functions        ***/
+/****************************************/
+
+
+/* list models */
+int drv_TeakLCM_list(void)
+{
+    printf("TeakLCM driver");
+    return 0;
+}
+
+
+/* initialize driver & display */
+/* use this function for a text display */
+int drv_TeakLCM_init(const char *section, const int quiet)
+{
+    WIDGET_CLASS wc;
+    int ret;
+
+    info("%s: %s (quiet=%d)", Name, "$Rev$", quiet);
+
+    /* display preferences */
+    XRES = 5;                  /* pixel width of one char  */
+    YRES = 8;                  /* pixel height of one char  */
+    CHARS = 0;                 /* number of user-defineable characters */
+    CHAR0 = 0;                 /* ASCII of first user-defineable char */
+    GOTO_COST = -1;            /* number of bytes a goto command requires */
+
+    /* real worker functions */
+    drv_generic_text_real_write = drv_TeakLCM_write;
+
+    /* start display */
+    if ((ret = drv_TeakLCM_start(section)) != 0)
+       return ret;
+
+    /* initialize generic text driver */
+    if ((ret = drv_generic_text_init(section, Name)) != 0)
+       return ret;
+
+    /* register text widget */
+    wc = Widget_Text;
+    wc.draw = drv_generic_text_draw;
+    widget_register(&wc);
+
+    /* register plugins */
+
+    return 0;
+}
+
+
+/* close driver & display */
+/* use this function for a text display */
+int drv_TeakLCM_quit(const int quiet)
+{
+
+    info("%s: shutting down. (quiet=%d)", Name, quiet);
+
+    drv_generic_text_quit();
+
+    /* clear display */
+    drv_TeakLCM_clear();
+
+    lcm_send_cmd(LCM_DISPLAY_OFF);
+    // lcm_send_cmd_frame(LCM_BACKLIGHT_OFF);
+    lcm_send_cmd(CMD_DISCONNECT);
+
+    /* FIXME: consume final ack frame */
+    usleep(100000);
+
+    debug("closing connection");
+    drv_TeakLCM_close(global_fd);
+
+    return (0);
+}
+
+
+/* use this one for a text display */
+DRIVER drv_TeakLCM = {
+    .name = Name,
+    .list = drv_TeakLCM_list,
+    .init = drv_TeakLCM_init,
+    .quit = drv_TeakLCM_quit,
+};
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * End:
+ */
index 0e4da6c0937e85c7b49c3e4737fa4a368b8cd999..9c312bdbfcbf8014be00b75327dc1e7aa49fcc8d 100644 (file)
@@ -27,6 +27,16 @@ Display SerDispLib {
 }
 
 
+Display TeakLCM {
+    Driver 'TeakLCM'
+    Size '20x2'
+    Port '/dev/ttyS1'
+    Speed 38400
+    Backlight 1
+    Icons 0
+}
+
+
 Display Trefon {
     Driver 'TREFON'
     Size '16x2'
@@ -1313,6 +1323,7 @@ Layout Debug {
 #Display 'USBLCD'
 #Display 'BWCT'
 #Display 'Image'
+#Display 'TeakLCD'
 #Display 'Trefon'
 #Display 'LCD2USB'
 #Display 'LPH7508-serdisplib'
index 0077b8659639ab2fe6d342f0b5bbc9c936f542b1..afa92eba4dde1e7f79da9e0895636cead9f60b0e 100755 (executable)
@@ -9,7 +9,7 @@ rm -f smoketest.log lcd4linux
 make distclean
 ./bootstrap
 
-for driver in ASTUSB BeckmannEgle BWCT CrystalFontz Curses Cwlinux D4D DPF EA232graphic EFN G15 GLCD2USB HD44780 IRLCD LCD2USB LCDLinux LCDTerm LEDMatrix LPH7508 LUIse LW_ABP M50530 MatrixOrbital MatrixOrbitalGX MilfordInstruments Noritake NULL Pertelian PHAnderson picoLCD picoLCDGraphic PNG PPM RouterBoard Sample serdisplib SimpleLCD T6963 Trefon ULA200 USBHUB USBLCD WincorNixdorf X11; do
+for driver in ASTUSB BeckmannEgle BWCT CrystalFontz Curses Cwlinux D4D DPF EA232graphic EFN G15 GLCD2USB HD44780 IRLCD LCD2USB LCDLinux LCDTerm LEDMatrix LPH7508 LUIse LW_ABP M50530 MatrixOrbital MatrixOrbitalGX MilfordInstruments Noritake NULL Pertelian PHAnderson picoLCD picoLCDGraphic PNG PPM RouterBoard Sample serdisplib SimpleLCD T6963 TeakLCM Trefon ULA200 USBHUB USBLCD WincorNixdorf X11; do
 
     make distclean
     ./configure --with-drivers=$driver