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