diff --git a/CREDITS b/CREDITS
index f65a991..4e9c348 100644
--- a/CREDITS
+++ b/CREDITS
@@ -265,6 +265,9 @@ Christian Leuschen,
Jonathan Kyler:
- driver for SoundGraph iMON OEM LCD Modules
+Laurent Latil
+ - SureElec driver for devices made by SURE Electronics
+
Further developers in the sourceforge lcdproc team:
Mindaugas Idzelis aim4min
diff --git a/ChangeLog b/ChangeLog
index 6c167e7..c4c336b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -14,6 +14,7 @@ v.0.5dev (ongoing development)
* Build system: Add a check testing if SA_RESTART flag is available.
* picolcd driver: Fix RC-5 for picoLCD 20x2 (A. van Schie)
* Introduce shared/defines.h to collect commonly used macros
+ + new driver: SureElec for devices made by SURE Electronics (Laurent Latil)
v0.5.3
+ lcdexec: notification when called program finishes
diff --git a/LCDd.conf b/LCDd.conf
index ff28e25..2ac2520 100644
--- a/LCDd.conf
+++ b/LCDd.conf
@@ -987,6 +987,29 @@ Speed=9600
Port=0x378
+[SureElec]
+
+# Port the device is connected to (by default first USB serial port)
+Port=/dev/ttyUSB0
+
+# Edition level of the device (can be 1, 2 or 3) [default: 2]
+#Edition=1
+
+# set display size
+# Note: The size can be obtained directly from device for edition 2 & 3.
+#Size=16x2
+
+# Set the initial contrast [default: 480; legal: 0 - 1000]
+#Contrast=200
+
+# Set the initial brightness [default: 480; legal: 1 - 1000]
+#Brightness=480
+
+# Set the initial off-brightness [default: 100; legal: 1 - 1000]
+# This value is used when the display is normally
+# switched off in case LCDd is inactive
+#OffBrightness=100
+
## SVGAlib driver ##
[svga]
diff --git a/acinclude.m4 b/acinclude.m4
index a47a88e..f56474e 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -13,13 +13,13 @@ AC_ARG_ENABLE(drivers,
[ IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,]
[ MD8800,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,]
[ pyramid,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,]
- [ stv5730,svga,t6963,text,tyan,ula200,xosd]
+ [ stv5730,SureElec,svga,t6963,text,tyan,ula200,xosd]
[ 'all' compiles all drivers;]
[ 'all,!xxx,!yyy' de-selects previously selected drivers],
drivers="$enableval",
drivers=[bayrad,CFontz,CFontz633,curses,CwLnx,glk,lb216,lcdm001,MtxOrb,pyramid,text])
-allDrivers=[bayrad,CFontz,CFontz633,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcdlib,glk,hd44780,i2500vfd,icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,pyramid,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,svga,t6963,text,tyan,ula200,xosd]
+allDrivers=[bayrad,CFontz,CFontz633,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcdlib,glk,hd44780,i2500vfd,icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,pyramid,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,t6963,text,tyan,ula200,xosd]
drivers=`echo $drivers | sed -e 's/,/ /g'`
@@ -406,6 +406,10 @@ dnl else
AC_MSG_WARN([The stv5730 driver needs a parallel port.])
fi
;;
+ SureElec)
+ DRIVERS="$DRIVERS SureElec${SO}"
+ actdrivers=["$actdrivers SureElec"]
+ ;;
svga)
AC_CHECK_HEADERS([vga.h vgagl.h],[
AC_CHECK_LIB(vga, main,[
diff --git a/docs/LCDd.8.in b/docs/LCDd.8.in
index 115e309..62cc6f4 100644
--- a/docs/LCDd.8.in
+++ b/docs/LCDd.8.in
@@ -262,6 +262,9 @@ Wirz SLI driver (unknown)
.B stv5730
STV5730A on-screen display chip
.TP
+.B SureElec
+LCD devices from SURE electronics (http://www.sureelectronics.net)
+.TP
.B svga
VGA monitors using svgalib
.TP
diff --git a/docs/lcdproc-user/drivers.docbook b/docs/lcdproc-user/drivers.docbook
index 4a00505..35acd0a 100644
--- a/docs/lcdproc-user/drivers.docbook
+++ b/docs/lcdproc-user/drivers.docbook
@@ -46,6 +46,7 @@ well as the configuration of LCDd.
&shuttleVFD;
&sli;
&stv5730;
+&SureElec;
&svga;
&t6963;
&text;
diff --git a/docs/lcdproc-user/drivers/Makefile.am b/docs/lcdproc-user/drivers/Makefile.am
index fcb6074..bb62b57 100644
--- a/docs/lcdproc-user/drivers/Makefile.am
+++ b/docs/lcdproc-user/drivers/Makefile.am
@@ -40,6 +40,7 @@ EXTRA_DIST = bayrad.docbook \
shuttleVFD.docbook \
sli.docbook \
stv5730.docbook \
+ SureElec.docbook \
svga.docbook \
t6963.docbook \
text.docbook \
diff --git a/docs/lcdproc-user/drivers/SureElec.docbook b/docs/lcdproc-user/drivers/SureElec.docbook
new file mode 100644
index 0000000..8d90ec7
--- /dev/null
+++ b/docs/lcdproc-user/drivers/SureElec.docbook
@@ -0,0 +1,94 @@
+
+The SureElec Driver
+
+
+ Driver for the LCD modules (actually the controller board) available from
+ the 'SURE electronics' shop ().
+
+
+ These devices are PIC based controlled, using a serial communication
+ protocol with the host. The actual connection to host is done through
+ USB through a serial-to-USB converter (CP2102 USB to UART Bridge)
+ integrated on the board.
+
+
+
+Configuration in LCDd.conf
+
+
+[SureElec]
+
+
+
+
+ Port =
+ PORT
+
+
+ Port the device is connected to. By default first USB serial port
+ /dev/ttyUSB0 is used.
+
+
+
+
+
+ Edition =
+ EDITION
+
+
+ Edition level of the device (can be 1, 2 or 3). The default is 2.
+
+
+
+
+
+ Size = ¶meters.size;
+
+
+ Set the display size in characters. This is required for edition 1 devices.
+ For edition 2 & 3 devices this value, if defined, overrides the size
+ read directly from the device.
+
+
+
+
+
+ Contrast =
+ CONTRAST
+
+
+ Select the display's contrast, 480 is the default.
+ Permissible values are in the range of 0-1000.
+
+
+
+
+
+ Brightness =
+ BRIGHTNESS
+
+
+ Select the display's brightness, 480 is the default.
+ Permissible values are in the range of 0-1000.
+
+
+
+
+
+ OffBrightness =
+ OFFBRIGHTNESS
+
+
+ Select the display's when the display is normally switched off in case LCDd
+ is inactive, 100 is the default.
+ Permissible values are in the range of 0-1000.
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/lcdproc-user/lcdproc-user.docbook b/docs/lcdproc-user/lcdproc-user.docbook
index 64d1d21..e81ccc9 100644
--- a/docs/lcdproc-user/lcdproc-user.docbook
+++ b/docs/lcdproc-user/lcdproc-user.docbook
@@ -54,6 +54,7 @@
+
diff --git a/server/drivers/Makefile.am b/server/drivers/Makefile.am
index 7533e62..02b59ae 100644
--- a/server/drivers/Makefile.am
+++ b/server/drivers/Makefile.am
@@ -21,7 +21,7 @@ AM_LDFLAGS = @LDSHARED@
## Keep the lists sorted!
pkglib_PROGRAMS = @DRIVERS@
-EXTRA_PROGRAMS = bayrad CFontz CFontz633 CFontzPacket curses CwLnx ea65 EyeboxOne g15 glcdlib glk hd44780 icp_a106 imon imonlcd IOWarrior irman joy lb216 lcdm001 lcterm lirc lis MD8800 ms6931 mtc_s16209x MtxOrb mx5000 NoritakeVFD picolcd pyramid sed1330 sed1520 serialPOS serialVFD shuttleVFD stv5730 svga t6963 text tyan sli ula200 xosd i2500vfd irtrans
+EXTRA_PROGRAMS = bayrad CFontz CFontz633 CFontzPacket curses CwLnx ea65 EyeboxOne g15 glcdlib glk hd44780 icp_a106 imon imonlcd IOWarrior irman joy lb216 lcdm001 lcterm lirc lis MD8800 ms6931 mtc_s16209x MtxOrb mx5000 NoritakeVFD picolcd pyramid sed1330 sed1520 serialPOS serialVFD shuttleVFD stv5730 SureElec svga t6963 text tyan sli ula200 xosd i2500vfd irtrans
noinst_LIBRARIES = libLCD.a libbignum.a
IOWarrior_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
@@ -60,6 +60,7 @@ pyramid_LDADD = libLCD.a
serialPOS_LDADD = libbignum.a
serialVFD_LDADD = libLCD.a libbignum.a
shuttleVFD_LDADD = @LIBUSB_LIBS@
+SureElec_LDADD = libLCD.a libbignum.a
svga_LDADD = @LIBSVGA@
t6963_LDADD = libLCD.a
tyan_LDADD = libLCD.a libbignum.a
@@ -110,6 +111,7 @@ serialPOS_SOURCES = lcd.h lcd_lib.h serialPOS.c serialPOS.h report.h adv_bignum
serialVFD_SOURCES = lcd.h lcd_lib.h serialVFD.c serialVFD.h report.h adv_bignum.h serialVFD_displays.c serialVFD_displays.h serialVFD_io.c serialVFD_io.h
shuttleVFD_SOURCES = lcd.h shuttleVFD.c shuttleVFD.h report.h
stv5730_SOURCES = lcd.h stv5730.c stv5730.h report.h
+SureElec_SOURCES = lcd.h lcd_lib.h SureElec.c SureElec.h report.h adv_bignum.h
svga_SOURCES = lcd.h svgalib_drv.c svgalib_drv.h report.h
t6963_SOURCES = lcd.h lcd_lib.h t6963.c t6963.h t6963_font.h report.h
text_SOURCES = lcd.h text.h text.c report.h
diff --git a/server/drivers/SureElec.c b/server/drivers/SureElec.c
new file mode 100644
index 0000000..432c39b
--- /dev/null
+++ b/server/drivers/SureElec.c
@@ -0,0 +1,1030 @@
+/** \file server/drivers/SureElec.c
+ *
+ * Driver for the LCD modules (actually the controller board) available from
+ * the 'SURE electronics' shop (http://www.sureelectronics.net).
+ *
+ * These devices are PIC based controlled, using a serial communication
+ * protocol with the host. The actual connection to host is done through
+ * USB through a serial <-> USB converter (CP2102 USB to UART Bridge)
+ * integrated on the board.
+ */
+
+/*
+ * Copyright (C) 2009, Laurent Latil
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ */
+
+/*
+ * It should be noted that the communication protocol, while somewhat
+ * documented in the LCD user's manual, is not complete (ie. the
+ * initialization sequence is not described), and inacurate on some points.
+ * This driver is so based on the spying of serial communication using the
+ * officially supported client softwares (LCD Smarties & SureLCD).
+ */
+
+#include
+#include
+#include
+#include
+#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 "SureElec.h"
+#include "adv_bignum.h"
+
+#include "report.h"
+
+/* API: variables 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 = "SureElec_";
+
+
+typedef enum {
+ SURE_ELEC_EDITION1 = 1,
+ SURE_ELEC_EDITION2 = 2,
+ SURE_ELEC_EDITION3 = 3,
+} SureElec_edition;
+
+typedef enum {
+ standard, /* only char 0 is used for heartbeat */
+ vbar, /* vertical bars */
+ hbar, /* horizontal bars */
+ custom, /* custom settings */
+ bignum, /* big numbers */
+ bigchar /* big characters */
+} CGmode;
+
+/** private data for the \c SureElec driver */
+typedef struct SureElec_private_data {
+ int fd; /* Serial port file descriptor */
+
+ int width, height; /* Screen size */
+ int cellwidth, cellheight; /* Cell size */
+
+ /* framebuffer and buffer for old LCD contents */
+ unsigned char *framebuf;
+ unsigned char *backingstore;
+
+ /* defineable characters */
+ CGmode ccmode;
+
+ int output_state; /**< current output state */
+ int contrast; /**< current contrast */
+ int brightness;
+ int offbrightness;
+ int backlight;
+
+ SureElec_edition edition;
+
+} PrivateData;
+
+static int open_port(Driver * drvthis, const char *device);
+static void get_device_screen_size(Driver * drvthis, int *width, int *height);
+static int write_(Driver * drvthis, const unsigned char *buf, size_t count);
+static int read_(Driver * drvthis, void *buf, size_t count);
+
+
+/**
+ * Initialize the driver.
+ * \param drvthis Pointer to driver structure.
+ * \retval 0 Success.
+ * \retval <0 Error.
+ */
+MODULE_EXPORT int
+SureElec_init(Driver * drvthis)
+{
+ char device[256] = DEFAULT_DEVICE;
+ char size[256] = DEFAULT_SIZE;
+ int tmp;
+ int user_width = -1, user_height = -1;
+ int device_width = -1, device_height = -1;
+ const char *sedition;
+
+ PrivateData *p;
+
+ /* Allocate and store private data */
+ p = (PrivateData *) malloc(sizeof(PrivateData));
+ if (p == NULL)
+ return -1;
+ if (drvthis->store_private_ptr(drvthis, p))
+ return -1;
+
+ /* Initialise the PrivateData structure */
+ p->fd = -1;
+ p->edition = SURE_ELEC_EDITION2; /* Assume an edition 2
+ * version */
+
+ p->cellwidth = CELL_WIDTH;
+ p->cellheight = CELL_HEIGHT;
+
+ p->framebuf = NULL;
+ p->backingstore = NULL;
+
+ p->backlight = 0;
+
+ debug(RPT_INFO, "SureElec driver: init(%p)", drvthis);
+
+ /* READ CONFIG FILE */
+
+ /* Get serial device to use */
+ strncpy(device, drvthis->config_get_string(drvthis->name, "Device", 0, DEFAULT_DEVICE), sizeof(device));
+ device[sizeof(device) - 1] = '\0';
+ report(RPT_INFO, "%s: using Device %s", drvthis->name, device);
+
+ if (open_port(drvthis, device) == -1) {
+ return -1;
+ }
+ /* Get edition version */
+ sedition = drvthis->config_get_string(drvthis->name, "Edition", 0, "");
+
+ if (strlen(sedition) != 0) {
+ int n = sedition[0] - '0';
+ if (strlen(sedition) != 1 || n < 1 || n > 3) {
+ report(RPT_ERR, "%s: unknown edition version %s; must be one of 1, 2, or 3",
+ drvthis->name, sedition);
+ return -1;
+ }
+ p->edition = (SureElec_edition) n;
+ }
+
+ /* Get the user-specified display size */
+ strncpy(size, drvthis->config_get_string(drvthis->name, "Size", 0, ""), sizeof(size));
+ size[sizeof(size) - 1] = '\0';
+ if (strlen(size) != 0) {
+ if ((sscanf(size, "%dx%d", &user_width, &user_height) != 2)
+ || (user_width <= 0) || (user_width > LCD_MAX_WIDTH)
+ || (user_height <= 0) || (user_height > LCD_MAX_HEIGHT)) {
+ report(RPT_ERR, "%s: invalid screen size: %s",
+ drvthis->name, size);
+ return -1;
+ }
+ }
+ if (p->edition != SURE_ELEC_EDITION1) {
+ /* For edition >1 the device can report screen size */
+ get_device_screen_size(drvthis, &device_width, &device_height);
+
+ if (user_width != -1) {
+ if (user_width != device_width || user_height != device_height) {
+ report(RPT_WARNING, "%s: device and config settings disagree for screen size: %d x %d <> %d x %d", drvthis->name, device_width, device_height, user_width, user_height);
+ report(RPT_WARNING, "%s: config specified size applied.", drvthis->name);
+ }
+ device_width = user_width;
+ device_height = user_height;
+ }
+ } else {
+ device_width = user_width;
+ device_height = user_height;
+ }
+
+ if (device_width == -1) {
+ report(RPT_ERR, "%s: screen size must be specified",
+ drvthis->name);
+ return -1;
+ }
+ /* set screen size */
+ p->width = device_width;
+ p->height = device_height;
+ report(RPT_INFO, "%s: screen size %d x %d.", drvthis->name, p->width, p->height);
+
+ /* Get contrast */
+ tmp = drvthis->config_get_int(drvthis->name, "Contrast", 0, DEFAULT_CONTRAST);
+ debug(RPT_INFO, "%s: Contrast (in config) is '%d'", drvthis->name, tmp);
+ if ((tmp < 0) || (tmp > 1000)) {
+ report(RPT_WARNING, "%s: Contrast value must be between 0 and 1000; using default %d",
+ drvthis->name, DEFAULT_CONTRAST);
+ tmp = DEFAULT_CONTRAST;
+ }
+ SureElec_set_contrast(drvthis, tmp);
+
+ /* Get backlight */
+ tmp = drvthis->config_get_int(drvthis->name, "Brightness", 0, DEFAULT_BRIGHTNESS);
+ debug(RPT_INFO, "%s: Brightness (in config) is '%d'", drvthis->name, tmp);
+ if ((tmp < 0) || (tmp > 1000)) {
+ report(RPT_WARNING, "%s: Brightness must be between 0 and 1000; using default %d",
+ drvthis->name, DEFAULT_BRIGHTNESS);
+ tmp = DEFAULT_BRIGHTNESS;
+ }
+ p->brightness = tmp;
+
+ /* Which backlight-off "brightness" */
+ tmp = drvthis->config_get_int(drvthis->name, "OffBrightness", 0, DEFAULT_OFFBRIGHTNESS);
+ debug(RPT_INFO, "%s: OffBrightness (in config) is '%d'", __FUNCTION__, tmp);
+ if ((tmp < 0) || (tmp > 1000)) {
+ report(RPT_WARNING, "%s: OffBrightness must be between 0 and 1000; using default %d",
+ drvthis->name, DEFAULT_OFFBRIGHTNESS);
+ tmp = DEFAULT_OFFBRIGHTNESS;
+ }
+ p->offbrightness = tmp;
+
+ /* Allocate the frame buffer. */
+ p->framebuf = (unsigned char *)calloc(p->width * p->height, 1);
+ if (p->framebuf == NULL) {
+ report(RPT_ERR, "%s: unable to create framebuffer", drvthis->name);
+ return -1;
+ }
+ memset(p->framebuf, ' ', p->width * p->height);
+
+ /* Allocate the backing store. */
+ p->backingstore = (unsigned char *)malloc(p->width * p->height);
+ if (p->backingstore == NULL) {
+ report(RPT_ERR, "%s: unable to create framebuffer backing store", drvthis->name);
+ return -1;
+ }
+ memset(p->backingstore, '#', p->width * p->height);
+
+ SureElec_clear(drvthis);
+ SureElec_flush(drvthis);
+
+ report(RPT_DEBUG, "%s: init() done", drvthis->name);
+ return 0;
+}
+
+/**
+ * Close the driver (do necessary clean-up).
+ * \param drvthis Pointer to driver structure.
+ */
+MODULE_EXPORT void
+SureElec_close(Driver * drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+
+ if (p != NULL) {
+ if (p->fd >= 0)
+ close(p->fd);
+
+ /* Free allocated framebuffers */
+ if (p->framebuf)
+ free(p->framebuf);
+ if (p->backingstore)
+ free(p->backingstore);
+
+ /* Free our private data */
+ free(p);
+ }
+ drvthis->store_private_ptr(drvthis, NULL);
+ debug(RPT_DEBUG, "SureElec: closed");
+}
+
+/**
+ * Return the display width in characters.
+ * \param drvthis Pointer to driver structure.
+ * \return Number of characters the display is wide.
+ */
+MODULE_EXPORT int
+SureElec_width(Driver * drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ return p->width;
+}
+
+
+/**
+ * Return the display height in characters.
+ * \param drvthis Pointer to driver structure.
+ * \return Number of characters the display is high.
+ */
+MODULE_EXPORT int
+SureElec_height(Driver * drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ return p->height;
+}
+
+
+/**
+ * Return the width of a character in pixels.
+ * \param drvthis Pointer to driver structure.
+ * \return Number of pixel columns a character cell is wide.
+ */
+MODULE_EXPORT int
+SureElec_cellwidth(Driver * drvthis)
+{
+ return CELL_WIDTH;
+}
+
+
+/**
+ * Return the height of a character in pixels.
+ * \param drvthis Pointer to driver structure.
+ * \return Number of pixel lines a character cell is high.
+ */
+MODULE_EXPORT int
+SureElec_cellheight(Driver * drvthis)
+{
+ return CELL_HEIGHT;
+}
+
+/**
+ * Print a string on the screen at position (x,y).
+ * The upper-left corner is (1,1), the lower-right corner is (p->width, p->height).
+ * \param drvthis Pointer to driver structure.
+ * \param x Horizontal character position (column).
+ * \param y Vertical character position (row).
+ * \param string String that gets written.
+ */
+MODULE_EXPORT void
+SureElec_string(Driver * drvthis, int x, int y, const char string[])
+{
+ PrivateData *p = drvthis->private_data;
+ int i;
+
+ /* Convert 1-based coords to 0-based... */
+ x--;
+ y--;
+
+ /* Check coord. validity */
+ if ((y < 0) || (y >= p->height))
+ return;
+
+ for (i = 0; (string[i] != '\0') && (x < p->width); i++, x++) {
+ p->framebuf[(y * p->width) + x] = string[i];
+ }
+ debug(RPT_DEBUG, "SureElec: printed string at (%d,%d)", x, y);
+}
+
+
+/**
+ * Clear the screen.
+ * \param drvthis Pointer to driver structure.
+ */
+MODULE_EXPORT void
+SureElec_clear(Driver * drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+
+ /* set hbar/vbar/bignum mode back to normal character display */
+ p->ccmode = standard;
+
+ /* replace all chars in framebuf with spaces */
+ memset(p->framebuf, ' ', (p->width * p->height));
+
+ debug(RPT_DEBUG, "SureElec: cleared screen");
+}
+
+/**
+ * Print a character on the screen at position (x,y).
+ * The upper-left corner is (1,1), the lower-right corner is (p->width, p->height).
+ * \param drvthis Pointer to driver structure.
+ * \param x Horizontal character position (column).
+ * \param y Vertical character position (row).
+ * \param c Character that gets written.
+ */
+MODULE_EXPORT void
+SureElec_chr(Driver * drvthis, int x, int y, char c)
+{
+ PrivateData *p = drvthis->private_data;
+
+ /* Convert 1-based coords to 0-based... */
+ x--;
+ y--;
+
+ if ((x >= 0) && (y >= 0) && (x < p->width) && (y < p->height))
+ p->framebuf[(y * p->width) + x] = c;
+
+ debug(RPT_DEBUG, "SureElec: writing character %02X to position (%d,%d)", c, x, y);
+}
+
+/**
+ * Flush data on screen to the LCD.
+ * \param drvthis Pointer to driver structure.
+ */
+MODULE_EXPORT void
+SureElec_flush(Driver * drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ int modified = 0;
+ int i, j;
+ unsigned char cmd[4] = {'\xFE', '\x47', '\x01', /* line */ 0};
+
+ for (i = 0; i < p->height; i++) {
+ /* Check if line content changed */
+ for (j = 0; j < p->width && p->framebuf[p->width * i + j] ==
+ p->backingstore[p->width * i + j]; j++) {
+ ;
+ }
+
+ if (j < p->width) {
+ /*
+ * Line content has been changed, need to output the
+ * line on screen
+ */
+ cmd[3] = i + 1;
+ if (write_(drvthis, cmd, sizeof(cmd)) == -1 ||
+ write_(drvthis, &(p->framebuf[p->width * i]), p->width) == -1) {
+ return;
+ }
+ modified = 1;
+ }
+ }
+
+ if (modified) {
+ /* If something changed on screen, update the backingstore */
+ memcpy(p->backingstore, p->framebuf, p->width * p->height);
+ }
+ debug(RPT_DEBUG, "SureElec: frame buffer flushed");
+}
+
+/**
+ * Get current LCD contrast.
+ * This is only the locally stored contrast, the contrast value
+ * cannot be retrieved from the LCD.
+ * \param drvthis Pointer to driver structure.
+ * \return Stored contrast in promille.
+ */
+
+MODULE_EXPORT int
+SureElec_get_contrast(Driver * drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ return p->contrast;
+}
+
+
+/**
+ * Set the LCD contrast.
+ * \param drvthis Pointer to driver structure.
+ * \param promille New contrast value in promille.
+ */
+MODULE_EXPORT void
+SureElec_set_contrast(Driver * drvthis, int promille)
+{
+ unsigned char cmd[3] = {'\xFE', '\x50', 0};
+ PrivateData *p = drvthis->private_data;
+ int real_contrast;
+
+ /* Check it */
+ if ((promille < 0) || (promille > 1000))
+ return;
+
+ /* Store it */
+ p->contrast = promille;
+
+ real_contrast = (int)((long)promille * MAX_CONTRAST / 1000);
+
+ /* And do it */
+ cmd[2] = real_contrast;
+
+ write_(drvthis, cmd, sizeof(cmd));
+}
+
+/**
+ * Retrieve brightness.
+ * \param drvthis Pointer to driver structure.
+ * \param state Brightness state (on/off) for which we want the value.
+ * \return Stored brightness in promille.
+ */
+MODULE_EXPORT int
+SureElec_get_brightness(Driver * drvthis, int state)
+{
+ PrivateData *p = drvthis->private_data;
+ return (state == BACKLIGHT_ON) ? p->brightness : 0;
+}
+
+
+/**
+ * Set on/off brightness.
+ * \param drvthis Pointer to driver structure.
+ * \param state Brightness state (on/off) for which we want to store the value.
+ * \param promille New brightness in promille.
+ */
+MODULE_EXPORT void
+SureElec_set_brightness(Driver * drvthis, int state, int promille)
+{
+ PrivateData *p = drvthis->private_data;
+
+ /* Check value */
+ if (promille < 0 || promille > 1000)
+ return;
+
+ /* store the software value */
+ if (state == BACKLIGHT_ON)
+ p->brightness = promille;
+ else
+ p->offbrightness = promille;
+}
+
+/**
+ * Turn the LCD backlight on or off.
+ * \param drvthis Pointer to driver structure.
+ * \param on New backlight status.
+ */
+MODULE_EXPORT void
+SureElec_backlight(Driver * drvthis, int on)
+{
+ PrivateData *p = drvthis->private_data;
+ int promille;
+
+ if (BACKLIGHT_ON == on) {
+ promille = p->brightness;
+ } else {
+ promille = p->offbrightness;
+ }
+
+ if (promille == 0 && p->backlight) {
+ /* Turn BL off */
+ unsigned char cmd[2] = {'\xFE', '\x46'};
+ if (write_(drvthis, cmd, sizeof(cmd)) == -1) {
+ return;
+ }
+ report(RPT_INFO, "SureElec: BL turned off");
+ p->backlight = 0;
+ }
+ if (promille > 0) {
+ unsigned char cmd_bl[3] = {'\xFE', '\x42', '\x0'};
+ unsigned char cmd_brightness[3] = {'\xFE', '\x98', 0};
+ int level;
+
+ /* Turn BL on & set brightness level */
+ if (!p->backlight) {
+ if (write_(drvthis, cmd_bl, sizeof(cmd_bl)) == -1) {
+ return;
+ }
+ report(RPT_INFO, "SureElec: BL turned on");
+ p->backlight = 1;
+ }
+ level = (int)((long)promille * MAX_BACKLIGHT / 1000);
+ cmd_brightness[2] = level;
+
+ write_(drvthis, cmd_brightness, sizeof(cmd_brightness));
+ }
+}
+
+/**
+ * Draw a vertical bar bottom-up.
+ * \param drvthis Pointer to driver structure.
+ * \param x Horizontal character position (column) of the starting point.
+ * \param y Vertical character position (row) of the starting point.
+ * \param len Number of characters that the bar is high at 100%
+ * \param promille Current height level of the bar in promille.
+ * \param options Options (currently unused).
+ */
+MODULE_EXPORT void
+SureElec_vbar(Driver * drvthis, int x, int y, int len, int promille, int options)
+{
+ PrivateData *p = drvthis->private_data;
+ if (p->ccmode != vbar) {
+ unsigned char vBar[p->cellheight];
+ int i;
+
+ if (p->ccmode != standard) {
+ /* Not supported(yet) */
+ report(RPT_WARNING, "%s: vbar: cannot combine two modes using user-defined characters",
+ drvthis->name);
+ return;
+ }
+ p->ccmode = vbar;
+
+ memset(vBar, 0x00, sizeof(vBar));
+
+ for (i = 1; i < p->cellheight; i++) {
+ /* add pixel line per pixel line */
+ vBar[p->cellheight - i] = 0xFF;
+ SureElec_set_char(drvthis, i, vBar);
+ }
+ }
+ lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, 0);
+}
+
+
+/**
+ * Draw a horizontal bar to the right.
+ * \param drvthis Pointer to driver structure.
+ * \param x Horizontal character position (column) of the starting point.
+ * \param y Vertical character position (row) of the starting point.
+ * \param len Number of characters that the bar is long at 100%
+ * \param promille Current length level of the bar in promille.
+ * \param options Options (currently unused).
+ */
+MODULE_EXPORT void
+SureElec_hbar(Driver * drvthis, int x, int y, int len, int promille, int options)
+{
+ PrivateData *p = drvthis->private_data;
+
+ if (p->ccmode != hbar) {
+ unsigned char hBar[p->cellheight];
+ int i;
+
+ if (p->ccmode != standard) {
+ /* Not supported(yet) */
+ report(RPT_WARNING, "%s: hbar: cannot combine two modes using user-defined characters",
+ drvthis->name);
+ return;
+ }
+ p->ccmode = hbar;
+
+ memset(hBar, 0x00, sizeof(hBar));
+
+ for (i = 1; i <= p->cellwidth; i++) {
+ /* fill pixel columns from left to right. */
+ memset(hBar, 0xFF & ~((1 << (p->cellwidth - i)) - 1), sizeof(hBar));
+ SureElec_set_char(drvthis, i, hBar);
+ }
+ }
+ lib_hbar_static(drvthis, x, y, len, promille, options, p->cellwidth, 0);
+}
+
+
+/**
+ * Write a big number to the screen.
+ * \param drvthis Pointer to driver structure.
+ * \param x Horizontal character position (column).
+ * \param num Character to write (0 - 10 with 10 representing ':')
+ */
+MODULE_EXPORT void
+SureElec_num(Driver * drvthis, int x, int num)
+{
+ PrivateData *p = drvthis->private_data;
+ int do_init = 0;
+
+ if ((num < 0) || (num > 10))
+ return;
+
+ if (p->ccmode != bignum) {
+ if (p->ccmode != standard) {
+ /* Not supported (yet) */
+ report(RPT_WARNING, "%s: num: cannot combine two modes using user-defined characters",
+ drvthis->name);
+ return;
+ }
+ p->ccmode = bignum;
+ do_init = 1;
+ }
+ /* Lib_adv_bignum does everything needed to show the bignumbers. */
+ lib_adv_bignum(drvthis, x, num, 0, do_init);
+}
+
+
+/**
+ * Get total number of custom characters available.
+ * \param drvthis Pointer to driver structure.
+ * \return Number of custom characters (always NUM_CCs).
+ */
+MODULE_EXPORT int
+SureElec_get_free_chars(Driver * drvthis)
+{
+ return NUM_CC;
+}
+
+
+/**
+ * Define a custom character and write it to the LCD.
+ * \param drvthis Pointer to driver structure.
+ * \param n Custom character to define [0 - (NUM_CCs-1)].
+ * \param dat Array of 8(=cellheight) bytes, each representing a pixel row
+ * starting from the top to bottom.
+ * The bits in each byte represent the pixels where the LSB
+ * (least significant bit) is the rightmost pixel in each pixel row.
+ */
+MODULE_EXPORT void
+SureElec_set_char(Driver * drvthis, int n, unsigned char *dat)
+{
+ PrivateData *p = drvthis->private_data;
+ unsigned char cmd[11] = {'\xFE', '\x4E', 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ unsigned char mask = (1 << CELL_WIDTH) - 1;
+ int row;
+
+ if ((unsigned int) n >= NUM_CC)
+ return;
+ if (!dat)
+ return;
+
+ cmd[2] = n; /* Custom char to define. */
+
+ for (row = 0; row < p->cellheight; row++) {
+ cmd[row + 3] = dat[row] & mask;
+ }
+ write_(drvthis, cmd, sizeof(cmd));
+}
+
+
+/**
+ * Place an icon on the screen.
+ * \param drvthis Pointer to driver structure.
+ * \param x Horizontal character position (column).
+ * \param y Vertical character position (row).
+ * \param icon synbolic value representing the icon.
+ * \retval 0 Icon has been successfully defined/written.
+ * \retval <0 Server core shall define/write the icon.
+ */
+MODULE_EXPORT int
+SureElec_icon(Driver * drvthis, int x, int y, int icon)
+{
+ static unsigned char heart_open[] =
+ {
+ b__XXXXX,
+ b__X_X_X,
+ b_______,
+ b_______,
+ b_______,
+ b__X___X,
+ b__XX_XX,
+ b__XXXXX
+ };
+
+ static unsigned char heart_filled[] =
+ {
+ b__XXXXX,
+ b__X_X_X,
+ b___X_X_,
+ b___XXX_,
+ b___XXX_,
+ b__X_X_X,
+ b__XX_XX,
+ b__XXXXX
+ };
+
+ static unsigned char arrow_up[] =
+ {
+ b____X__,
+ b___XXX_,
+ b__X_X_X,
+ b____X__,
+ b____X__,
+ b____X__,
+ b____X__,
+ b_______
+ };
+
+ static unsigned char arrow_down[] =
+ {
+ b____X__,
+ b____X__,
+ b____X__,
+ b____X__,
+ b__X_X_X,
+ b___XXX_,
+ b____X__,
+ b_______
+ };
+
+ static unsigned char checkbox_off[] =
+ {
+ b_______,
+ b_______,
+ b__XXXXX,
+ b__X___X,
+ b__X___X,
+ b__X___X,
+ b__XXXXX,
+ b_______
+ };
+
+ static unsigned char checkbox_on[] =
+ {
+ b____X__,
+ b____X__,
+ b__XXX_X,
+ b__X_XX_,
+ b__X_X_X,
+ b__X___X,
+ b__XXXXX,
+ b_______
+ };
+
+ static unsigned char checkbox_gray[] =
+ {
+ b_______,
+ b_______,
+ b__XXXXX,
+ b__X_X_X,
+ b__XX_XX,
+ b__X_X_X,
+ b__XXXXX,
+ b_______
+ };
+
+ unsigned char *pattern = NULL;
+ char charnum = 0;
+
+ switch (icon) {
+ case ICON_BLOCK_FILLED:
+ charnum = 0xFF;
+ break;
+ case ICON_ARROW_LEFT:
+ charnum = 0x7F;
+ break;
+ case ICON_ARROW_RIGHT:
+ charnum = 0x7E;
+ break;
+ case ICON_ARROW_UP:
+ charnum = 1;
+ pattern = arrow_up;
+ break;
+ case ICON_ARROW_DOWN:
+ charnum = 2;
+ pattern = arrow_down;
+ break;
+ case ICON_HEART_FILLED:
+ pattern = heart_filled;
+ break;
+ case ICON_HEART_OPEN:
+ pattern = heart_open;
+ break;
+ case ICON_CHECKBOX_OFF:
+ charnum = 3;
+ pattern = checkbox_off;
+ break;
+ case ICON_CHECKBOX_ON:
+ charnum = 4;
+ pattern = checkbox_on;
+ break;
+ case ICON_CHECKBOX_GRAY:
+ charnum = 5;
+ pattern = checkbox_gray;
+ break;
+
+ default:
+ return -1;
+ }
+
+ if (NULL != pattern) {
+ SureElec_set_char(drvthis, charnum, pattern);
+ }
+ SureElec_chr(drvthis, x, y, charnum);
+ return 0;
+}
+
+/**
+ * Open the communication port and sends the initialization 'magic' sequence
+ * to the device
+ * \param drvthis Pointer to driver structure.
+ * \param device Name of the device to use.
+ * \retval 0 Success
+ * \retval <0 Error opening port or writing init sequence
+ */
+static int
+open_port(Driver * drvthis, const char *device)
+{
+ PrivateData *p = drvthis->private_data;
+ int fd;
+ struct termios portset;
+
+ unsigned char cmd[3] = {'\xFE', '\x56', 0};
+ unsigned char init_seq[7] = {'\x54', '\x58', '\x4B', '\x52', '\x44', '\x41', '\x60'};
+ int i;
+
+
+ fd = open(device, O_RDWR | O_NOCTTY);
+ if (fd == -1) {
+ report(RPT_ERR, "%s: open(%s) failed (%s)", drvthis->name, device, strerror(errno));
+ if (errno == EACCES) {
+ report(RPT_ERR, "%s: %s device could not be opened... (check your device and / or config file)", drvthis->name, device);
+ }
+ return -1;
+ }
+ tcgetattr(fd, &portset);
+
+ /* Set port parameters */
+ 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 | CSTOPB);
+ portset.c_cflag |= CS8 | CREAD | CLOCAL;
+ portset.c_cc[VMIN] = 1;
+ portset.c_cc[VTIME] = 10;
+
+ /* Set port speed */
+ cfsetospeed(&portset, B9600);
+ cfsetispeed(&portset, B9600);
+
+ /* Do it... */
+ tcsetattr(fd, TCSANOW, &portset);
+
+ /* Set port file desc. in private data */
+ p->fd = fd;
+
+ /* Send initialization sequence */
+ for (i = 1; i <= 8; i++) {
+ cmd[2] = i;
+ if (write_(drvthis, cmd, sizeof(cmd)) == -1)
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(init_seq); i++) {
+ if (write_(drvthis, (const unsigned char *)"\x0FE", 1) == -1
+ || write_(drvthis, &(init_seq[i]), 1) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Retrieves the display size from the device.
+ * \param drvthis Pointer to driver structure.
+ * \param width Pointer to variable storing display width
+ * \param height Pointer to variable storing display height
+ */
+static void
+get_device_screen_size(Driver * drvthis, int *width, int *height)
+{
+ unsigned char cmd[2] = {'\xFE', '\x76'};
+ char result[11];
+
+ if (write_(drvthis, cmd, sizeof(cmd)) == -1)
+ return;
+
+ if (read_(drvthis, result, sizeof(result)) != -1) {
+ *width = (int)(result[0] - '0') * 10 + (int)(result[1] - '0');
+ *height = (int)(result[2] - '0') * 10 + (int)(result[3] - '0');
+ }
+}
+
+/**
+ * Writes count bytes from a buffer to the device.
+ * \param drvthis Pointer to driver structure
+ * \param buf Pointer to the buffer
+ * \param count Number of bytes to write
+ * \retval -1 upon error
+ */
+static int
+write_(Driver * drvthis, const unsigned char *buf, size_t count)
+{
+ int write_count = 0, written;
+ PrivateData *p = drvthis->private_data;
+
+ while (write_count < count) {
+ written = write(p->fd, &buf[write_count], count - write_count);
+ if (written == -1) {
+ report(RPT_ERR, "SureElec: cannot write to port");
+ return -1;
+ }
+ write_count += written;
+ }
+ return write_count;
+}
+
+/**
+ * Reads bytes in a buffer from the device.
+ * \param drvthis Pointer to driver structure
+ * \param buf Pointer to the buffer
+ * \param count Number of bytes to read
+ * \retval -1 upon error
+ */
+static int
+read_(Driver * drvthis, void *buf, size_t count)
+{
+ fd_set rfds;
+ struct timeval tv;
+ PrivateData *p = drvthis->private_data;
+ int read_count;
+
+ /* Watch fd to see when it has input. */
+ FD_ZERO(&rfds);
+ FD_SET(p->fd, &rfds);
+
+ read_count = 0;
+ while (read_count < count) {
+ int retval;
+
+ /*
+ * For compatibility, better to reset the timeout delay (may
+ * be modified in select() call).
+ */
+ tv.tv_sec = 1; /* seconds */
+ tv.tv_usec = 0; /* microseconds */
+
+ retval = select(p->fd + 1, &rfds, NULL, NULL, &tv);
+ if (retval) {
+ int read_result = read(p->fd, ((char *)buf) + read_count, count - read_count);
+ if (read_result < 0) {
+ return -1;
+ } else {
+ read_count += read_result;
+ }
+ } else {
+ report(RPT_ERR, "SureElec: No answer from device");
+ return -1;
+ }
+ }
+ return read_count;
+}
diff --git a/server/drivers/SureElec.h b/server/drivers/SureElec.h
new file mode 100644
index 0000000..f845ca4
--- /dev/null
+++ b/server/drivers/SureElec.h
@@ -0,0 +1,56 @@
+/*
+ * SureElec.h
+ */
+#ifndef SUREELEC_H
+#define SUREELEC_H
+
+#include "lcd.h"
+
+#define DEFAULT_DEVICE "/dev/ttyUSB0"
+#define DEFAULT_SPEED 9600
+#define DEFAULT_LINEWRAP 1
+#define DEFAULT_AUTOSCROLL 0
+#define DEFAULT_CURSORBLINK 0
+#define DEFAULT_SIZE "16x2"
+#define DEFAULT_CONTRAST 480
+#define DEFAULT_BRIGHTNESS 480
+#define DEFAULT_OFFBRIGHTNESS 100
+
+#define MIN_CONTRAST 0x1
+#define MAX_CONTRAST 0xFD
+
+#define MIN_BACKLIGHT 0x1
+#define MAX_BACKLIGHT 0xFD
+
+#define CELL_WIDTH 5
+#define CELL_HEIGHT 8
+
+#define NUM_CC 8
+
+
+MODULE_EXPORT int SureElec_init (Driver *drvthis);
+MODULE_EXPORT void SureElec_close (Driver *drvthis);
+MODULE_EXPORT int SureElec_width (Driver *drvthis);
+MODULE_EXPORT int SureElec_height (Driver *drvthis);
+MODULE_EXPORT int SureElec_cellwidth (Driver *drvthis);
+MODULE_EXPORT int SureElec_cellheight (Driver *drvthis);
+MODULE_EXPORT void SureElec_clear (Driver *drvthis);
+MODULE_EXPORT void SureElec_flush (Driver *drvthis);
+MODULE_EXPORT void SureElec_string (Driver *drvthis, int x, int y, const char string[]);
+MODULE_EXPORT void SureElec_chr (Driver *drvthis, int x, int y, char c);
+
+MODULE_EXPORT void SureElec_vbar (Driver *drvthis, int x, int y, int len, int promille, int options);
+MODULE_EXPORT void SureElec_hbar (Driver *drvthis, int x, int y, int len, int promille, int options);
+MODULE_EXPORT int SureElec_icon (Driver *drvthis, int x, int y, int icon);
+MODULE_EXPORT void SureElec_num (Driver *drvthis, int x, int num);
+
+MODULE_EXPORT int SureElec_get_free_chars (Driver *drvthis);
+MODULE_EXPORT void SureElec_set_char (Driver *drvthis, int n, unsigned char *dat);
+
+MODULE_EXPORT int SureElec_get_contrast (Driver *drvthis);
+MODULE_EXPORT void SureElec_set_contrast (Driver *drvthis, int promille);
+MODULE_EXPORT int SureElec_get_brightness(Driver *drvthis, int state);
+MODULE_EXPORT void SureElec_set_brightness(Driver *drvthis, int state, int promille);
+MODULE_EXPORT void SureElec_backlight(struct lcd_logical_driver *drvthis, int on);
+
+#endif // SUREELEC_H