diff --git a/ChangeLog b/ChangeLog index fdfc466..eff6c40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,7 @@ v0.5dev (ongoing development) * sed1520: Make it work without using an external inverter * lis driver: Make the last pixel row work (add lastline option parsing) * hd44780/ftdi: Fix error in setting backlight and add backlight for 4bit mode + + new driver: sdeclcd for Watchguard FireBox appliances (F. Mertz) v0.5.5 + sed1330 driver: Add support for HG25504 (L. Lagendijk) diff --git a/LCDd.conf b/LCDd.conf index 301480d..e37ac42 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -48,7 +48,7 @@ DriverPath=server/drivers/ # EyeboxOne, g15, glcdlib, glk, hd44780, icp_a106, imon, imonlcd, # IOWarrior, irman, joy, lb216, lcdm001, lcterm, lirc, lis, MD8800, # mdm166a, ms6931, mtc_s16209x, MtxOrb, mx5000, NoritakeVFD, picolcd, -# pyramid, sed1330, sed1520, serialPOS, serialVFD, shuttleVFD, sli, +# pyramid, sdeclcd, sed1330, sed1520, serialPOS, serialVFD, shuttleVFD, sli, # stv5730, svga, t6963, text, tyan, ula200, vlsys_m428, xosd Driver=curses @@ -1006,6 +1006,12 @@ Device=/dev/ttyUSB0 +## SDEC driver for Watchguard Firebox ## +[sdeclcd] +# No options + + + ## Seiko Epson 1330 driver ## [sed1330] diff --git a/acinclude.m4 b/acinclude.m4 index dde0eb8..1586b9b 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -13,7 +13,7 @@ AC_ARG_ENABLE(drivers, [ icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,] [ joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,] [ ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,] - [ picolcd,pyramid,sed1330,sed1520,serialPOS,] + [ picolcd,pyramid,sdeclcd,sed1330,sed1520,serialPOS,] [ serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,] [ t6963,text,tyan,ula200,vlsys_m428,xosd] [ ] @@ -22,7 +22,7 @@ AC_ARG_ENABLE(drivers, drivers="$enableval", drivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,glk,lb216,lcdm001,MtxOrb,pyramid,text]) -allDrivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,pyramid,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,t6963,text,tyan,ula200,vlsys_m428,xosd] +allDrivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,pyramid,sdeclcd,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,t6963,text,tyan,ula200,vlsys_m428,xosd] if test "$debug" = yes; then allDrivers=["${allDrivers},debug"] fi @@ -394,6 +394,15 @@ dnl else DRIVERS="$DRIVERS pyramid${SO}" actdrivers=["$actdrivers pyramid"] ;; + sdeclcd) + if test "$ac_cv_port_have_lpt" = yes + then + DRIVERS="$DRIVERS sdeclcd${SO}" + actdrivers=["$actdrivers sdeclcd"] + else + AC_MSG_WARN([The sdeclcd driver needs a parallel port.]) + fi + ;; sed1330) if test "$ac_cv_port_have_lpt" = yes then diff --git a/docs/LCDd.8.in b/docs/LCDd.8.in index 6512977..4a8a86b 100644 --- a/docs/LCDd.8.in +++ b/docs/LCDd.8.in @@ -262,6 +262,9 @@ Mini-box.com USB LCD (PicoLCD 20x4 & picoLCD 20x2) .B pyramid LCD displays from Pyramid (http://www.pyramid.de) .TP +.B sdeclcd +Watchguard Firebox LCD display based on SDEC LMC-S2D20 (http://www.sdec.com.tw) +.TP .B sed1330 SED1330/SED1335 (aka S1D13300/S1D13305) based graphical displays .TP diff --git a/docs/lcdproc-user/drivers.docbook b/docs/lcdproc-user/drivers.docbook index 509ceb6..a21ea85 100644 --- a/docs/lcdproc-user/drivers.docbook +++ b/docs/lcdproc-user/drivers.docbook @@ -41,6 +41,7 @@ well as the configuration of LCDd. &NoritakeVFD; &picolcd; &pylcd; +&sdeclcd; &sed1330; &sed1520; &serialPOS; diff --git a/docs/lcdproc-user/drivers/Makefile.am b/docs/lcdproc-user/drivers/Makefile.am index 278926a..e545e40 100644 --- a/docs/lcdproc-user/drivers/Makefile.am +++ b/docs/lcdproc-user/drivers/Makefile.am @@ -35,6 +35,7 @@ EXTRA_DIST = bayrad.docbook \ picolcd.docbook \ ppttrouble.docbook \ pylcd.docbook \ + sdeclcd.docbook \ sed1330.docbook \ sed1520.docbook \ serialPOS.docbook \ diff --git a/docs/lcdproc-user/drivers/sdeclcd.docbook b/docs/lcdproc-user/drivers/sdeclcd.docbook new file mode 100644 index 0000000..b0576f9 --- /dev/null +++ b/docs/lcdproc-user/drivers/sdeclcd.docbook @@ -0,0 +1,29 @@ + +The SDEC LCD Driver + +The SDEC LCD modules are fitted to the Watchguard Firebox firewall appliances. +Watchguard identifies these units by the X-Core, X-e and X-Peak product lines. +The model number indicates a software license level, and has no bearing on the +hardware configuration. + + +Features + + +The SDEC LCDs are interfaced to the appliance using a parallel port. All lines +are hard-wired by Watchguard. +The LCD displays are 20 characters wide and 2 lines high, and +have 4 buttons: Up, Down, +Left, and Right. The SDEC model number is +LMC-S2D20-01. There is a back light that can be controlled +by software. Due to its low half-life, the driver tries to keep the light off a +short while after buttons have been pressed. + + + +For more detailed information about the SDEC LCD, locate the technical and +programming guide LMC-S2D20-01.pdf. The manufacturer web site is: +SDEC home page + + + diff --git a/docs/lcdproc-user/lcdproc-user.docbook b/docs/lcdproc-user/lcdproc-user.docbook index d23716f..5374dff 100644 --- a/docs/lcdproc-user/lcdproc-user.docbook +++ b/docs/lcdproc-user/lcdproc-user.docbook @@ -48,6 +48,7 @@ + diff --git a/server/drivers/Makefile.am b/server/drivers/Makefile.am index 349e880..b1eca3a 100644 --- a/server/drivers/Makefile.am +++ b/server/drivers/Makefile.am @@ -22,7 +22,7 @@ AM_LDFLAGS = @LDSHARED@ ## Keep the lists sorted! pkglib_PROGRAMS = @DRIVERS@ -EXTRA_PROGRAMS = bayrad CFontz CFontzPacket curses debug CwLnx ea65 EyeboxOne g15 glcd glcdlib glk hd44780 icp_a106 imon imonlcd IOWarrior irman joy lb216 lcdm001 lcterm lirc lis MD8800 mdm166a ms6931 mtc_s16209x MtxOrb mx5000 NoritakeVFD picolcd pyramid sed1330 sed1520 serialPOS serialVFD shuttleVFD stv5730 SureElec svga t6963 text tyan sli ula200 vlsys_m428 xosd i2500vfd irtrans +EXTRA_PROGRAMS = bayrad CFontz CFontzPacket curses debug CwLnx ea65 EyeboxOne g15 glcd glcdlib glk hd44780 icp_a106 imon imonlcd IOWarrior irman joy lb216 lcdm001 lcterm lirc lis MD8800 mdm166a ms6931 mtc_s16209x MtxOrb mx5000 NoritakeVFD picolcd pyramid sdeclcd sed1330 sed1520 serialPOS serialVFD shuttleVFD stv5730 SureElec svga t6963 text tyan sli ula200 vlsys_m428 xosd i2500vfd irtrans noinst_LIBRARIES = libLCD.a libbignum.a g15_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS) @@ -65,6 +65,7 @@ mx5000_LDADD = @LIBMX5000@ NoritakeVFD_LDADD = libbignum.a picolcd_LDADD = @LIBUSB_LIBS@ @LIBUSB_1_0_LIBS@ libLCD.a libbignum.a pyramid_LDADD = libLCD.a libbignum.a +sdeclcd_LDADD = libLCD.a libbignum.a serialPOS_LDADD = libbignum.a serialVFD_LDADD = libLCD.a libbignum.a shuttleVFD_LDADD = @LIBUSB_LIBS@ @@ -116,6 +117,7 @@ mx5000_SOURCES = lcd.h mx5000.c mx5000.h report.h NoritakeVFD_SOURCES = lcd.h lcd_lib.h NoritakeVFD.c NoritakeVFD.h report.h adv_bignum.h picolcd_SOURCES = lcd.h picolcd.h picolcd.c report.h pyramid_SOURCES = lcd.h pylcd.c pylcd.h +sdeclcd_SOURCES = lcd.h sdeclcd.h sdeclcd.c lcd_lib.h report.h adv_bignum.h port.h lpt-port.h timing.h sed1330_SOURCES = lcd.h sed1330.h sed1330.c port.h lpt-port.h timing.h report.h sed1520_SOURCES = lcd.h sed1520.c sed1520.h port.h report.h glcd_font5x8.h sed1520fm.h serialPOS_SOURCES = lcd.h lcd_lib.h serialPOS.c serialPOS.h report.h adv_bignum.h diff --git a/server/drivers/sdeclcd.c b/server/drivers/sdeclcd.c new file mode 100644 index 0000000..ab0cd9e --- /dev/null +++ b/server/drivers/sdeclcd.c @@ -0,0 +1,723 @@ +/** \file server/drivers/sdeclcd.c + * This is the LCDproc driver for SDEC LCD Devices. + * They are found in the Watchguard FireBox firewall appliances. + * They are interfaced through the parallel port. + * + * The code is based on the spec file LMC-S2D20-01.pdf, + * a technical hardware and programming guide for this LCD. + */ + +/*- + * Copyright(C) 2011, Francois Mertz + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif + +#include "port.h" +#include "lpt-port.h" +#include "timing.h" +#include "lcd.h" +#include "report.h" +#include "adv_bignum.h" +#include "lcd_lib.h" + +#include "sdeclcd.h" + +/* + * The following definitions are specific for the SDEC LCD device, + * and the way it is wired in the Watchguard Firebox. There is a + * mapping between the parallel port Control register and the + * LCD control lines, as follows: (see lpt-port.h for defs) + */ +#define SDEC_BACKLIGHT STRB /* Strobe, bit 0 */ +#define SDEC_ENABLE LF /* Linefeed, bit */ +#define SDEC_CONTRAST INIT /* Init, bit 2 <--FIXME, Untested */ +#define SDEC_REG_SEL SEL /* Select Printer, bit 3 */ + +/* + * The following definitions are specific for the SDEC LCD device + */ +#define SDEC_DISP_W 20 /* Display Width */ +#define SDEC_DISP_H 2 /* Display Height */ +#define SDEC_ADD_LINE1 0x80 /* Address of beginning of Line 1 */ +#define SDEC_ADD_LINE2 0xC0 /* Address of beginning of Line 2 */ +#define SDEC_CELL_W 5 /* Character cell Width */ +#define SDEC_CELL_H 8 /* Character cell Height */ +#define SDEC_NUM_CC 8 /* Number of Custom Characters */ +#define SDEC_IDENT "SDEC LCD Driver with Keyboard, Version " VERSION + +/* + * Instruction Set for the SDEC LCD device + */ +#define SDEC_FN_CLEAR_DISPLAY 0x01 /* Clears the LCD */ +#define SDEC_FN_RETURN_HOME 0x02 /* Returns cursor home */ +#define SDEC_FN_SET_ENTRY_MODE 0x04 /* Sets Entry Mode */ +#define SDEC_OPT_INCREMENT 0x02 /* Cursor moves right */ +#define SDEC_FN_DISPLAY 0x08 /* Display: */ +#define SDEC_OPT_DISPLAY_ON 0x04 /* Display ON option */ +#define SDEC_OPT_CURSOR_ON 0x02 /* Cursor ON option */ +#define SDEC_OPT_BLINK_ON 0x01 /* Blinking Cursor option */ +#define SDEC_FN_CURSOR_R 0x14 /* Move cursor right 1 position */ +#define SDEC_FN_CURSOR_L 0x10 /* Move cursor left 1 position */ +#define SDEC_FN_FUNCTION_SET 0x20 /* Function Set: */ +#define SDEC_OPT_8_BIT 0x10 /* 8bit option (vs 4 bit) */ +#define SDEC_OPT_2_LINES 0x08 /* 2 Lines (vs 1 Line) */ +#define SDEC_OPT_5X10 0x04 /* Each char is 5x10 (vs 5x7) */ +#define SDEC_FN_CG_ADD 0x40 /* Set Custom Char address */ +#define SDEC_FN_DD_ADD 0x80 /* Set Display address (cursor) */ +#define SDEC_DATA SDEC_REG_SEL /* For data, RS is ON */ +#define SDEC_EXEC 0x00 /* For exec, RS is OFF */ +#define SDEC_HOLD 40 /* Min exec time in micro sec */ +#define SDEC_HOLD_ENABLE 1 /* Min time to hold ENABLE in micro + * sec, should be 450 nano sec */ + +/* + * Basic PC parallel port constants. At this point, the driver only supports a + * SDEC LCD device on port 0, as all Fireboxes seem to be wired that way. + * + * http://en.wikipedia.org/wiki/Parallel_port + */ +#define LPT_PORT_0 0x0378 +#define LPT_PORT_1 0x0278 +#define LPT_PORT_2 0x03BC + +#define LPT_DEFAULT LPT_PORT_0 +#define LPT_STATUS (LPT_DEFAULT+1) +#define LPT_CONTROL (LPT_DEFAULT+2) +/* Control port hardware inversion */ +#define LPT_CTRL_MASK OUTMASK +/* Bits actually used by Status port */ +#define LPT_STUS_MASK (SELIN|PAPEREND|ACK|BUSY|FAULT) + +/* + * Driver implementation choices. + * + * Heart beat location on screen, and corresponding address, char, + * and backlight timeout + */ +#define SDEC_HB_LOC (SDEC_DISP_W-1) +#define SDEC_HB_ADD (SDEC_ADD_LINE1+SDEC_HB_LOC) +#define SDEC_HB_CHAR ':' +#define SDEC_BKLT_DFT 30 + +/** + * This structure will hold data private to this Driver + * As the driver is implemented as a Shared Library, persistent data needs to + * be put in a dynamically allocated struct. Otherwise, all instances of the + * library would share the same values. + */ +typedef struct driver_private_data { + CGmode ccmode; /* Current custom character mode */ + unsigned char bklgt; /* Backlight status */ + unsigned char lastkbd; /* Last keyboard status */ + unsigned char hb_stus; /* Heart Beat status */ + unsigned bklgt_timer; /* Seconds to keep the backlight on */ + time_t bklgt_lasttime; /* When back light was turned on */ + time_t hrbt_lasttime; /* When heart beat was output */ + char *framebuf; /* Frame buffer */ + char *framelcd; /* Frame buffer on the LCD */ + char *vbar_cg; /* Vertical Bar bitmaps */ + char *hbar_cg; /* Horizontal Bar bitmaps */ + char *bignum_cg; /* Big Numbers bitmaps */ +} PrivateData; + +/* Variables assisting the driver loader */ +MODULE_EXPORT char *api_version = API_VERSION; +MODULE_EXPORT int stay_in_foreground = 0; /* Foreground is not required */ +MODULE_EXPORT int supports_multiple = 0; /* No support for mult. LCDs */ +MODULE_EXPORT char *symbol_prefix = "sdeclcd_"; /* Prefix for API entries */ + +/** + * Main LCD control routine + * \param register_select What to set the Register Select line to + * \param backlight What to set the Backlight line to + * \param data Data to send to LCD (char or instruction) + * \param usec Wait time in micro seconds + * + * This is the basic routine that controls the LCD. It interfaces with the + * parallel ports and contains the logic for flipping and holding the lines + * as required. It tries and be comprehensive enough to avoid spreading + * LPT port logic all over the code. + * + * The Register Select line determines if an output is to be understood as an + * instruction, or as data to be displayed. + * The Backlight line controls the backlight in a direct manner. + * + * The sequence is: + * Bring Enable up + * Output the data (command or character) + * Hold Enable for 450 nano sec (.45 micro sec) + * Bring Enable down + * Wait for the LCD to execute the command, typically 40 micro sec + * + * Take into account the hardware inversion of some control port bits. + */ +static inline void +_sdec_control_wait(unsigned char register_select, unsigned char backlight, + unsigned char data, int usec) +{ + port_out(LPT_CONTROL, (register_select | backlight | SDEC_ENABLE) ^ LPT_CTRL_MASK); + port_out(LPT_DEFAULT, data); + timing_uPause(SDEC_HOLD_ENABLE); + port_out(LPT_CONTROL, (register_select | backlight) ^ LPT_CTRL_MASK); + timing_uPause(usec); +} + +/** + * These macros are meant to be used by the driver code. They are wrappers + * around the low level routine, and capture the Register Select logic. + */ +#define sdec_write(a,b) _sdec_control_wait(SDEC_DATA, b, a, SDEC_HOLD) +#define sdec_exec(a,b) _sdec_control_wait(SDEC_EXEC, b, a, SDEC_HOLD) +#define sdec_exec_wait(a,b) _sdec_control_wait(SDEC_EXEC, 0, a,b) + +/** + * This is the mandatory initialization sequence for the LCD, as per the SDEC + * programming guide. Note the required execution times. + */ +static void +sdec_init() +{ + /* Mandatory reset sequence */ + sdec_exec_wait(SDEC_FN_FUNCTION_SET | SDEC_OPT_8_BIT, 15000); + sdec_exec_wait(SDEC_FN_FUNCTION_SET | SDEC_OPT_8_BIT, 4100); + sdec_exec_wait(SDEC_FN_FUNCTION_SET | SDEC_OPT_8_BIT, 100); + sdec_exec_wait(SDEC_FN_FUNCTION_SET | SDEC_OPT_8_BIT | SDEC_OPT_2_LINES, 100); + sdec_exec_wait(SDEC_FN_DISPLAY, 40); + sdec_exec_wait(SDEC_FN_CLEAR_DISPLAY, 1640); + sdec_exec_wait(SDEC_FN_SET_ENTRY_MODE | SDEC_OPT_INCREMENT, 40); + + /* Additional command */ + sdec_exec(SDEC_FN_DISPLAY | SDEC_OPT_DISPLAY_ON, 0); +} + +/** + * API: Initialize the driver. + */ +MODULE_EXPORT int +sdeclcd_init(Driver *drvthis) +{ + PrivateData *p; + int i, j; + /* + * Compact version of big num character bitmaps. Stack each line + * separately to visualize each char. + */ + static char bignum_bitmaps[] = { + b___XXXX, b__XX___, b__XX___, b__XX___, b__XX___, b__XX___, b__XX___, b__XX___, + b__XXXX_, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, + b__XX___, b__XX___, b__XX___, b__XX___, b__XX___, b__XX___, b__XX___, b___XXXX, + b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b__XXXX_, + b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, + b__XXXXX, b_______, b_______, b_______, b_______, b_______, b_______, b__XXXXX, + b__XXXX_, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b_____XX, b__XXXX_, + b___XXXX, b__XX___, b__XX___, b__XX___, b__XX___, b__XX___, b__XX___, b___XXXX + }; + + /* Allocate and store private data */ + p = (PrivateData *) malloc(sizeof(PrivateData)); + if (p == NULL) + return -1; + if (drvthis->store_private_ptr(drvthis, p)) + return -1; + + /* Initialize private data and read config */ + p->ccmode = standard; + p->bklgt = BACKLIGHT_ON; + p->bklgt_timer = SDEC_BKLT_DFT; + p->bklgt_lasttime = time(NULL); + p->hrbt_lasttime = time(NULL); + p->hb_stus = HEARTBEAT_OFF; + p->framebuf = (char *)malloc(SDEC_DISP_W * SDEC_DISP_H); + p->framelcd = (char *)malloc(SDEC_DISP_W * SDEC_DISP_H); + p->vbar_cg = (char *)malloc(SDEC_CELL_H * SDEC_NUM_CC); + p->hbar_cg = (char *)malloc(SDEC_CELL_H * SDEC_NUM_CC); + p->bignum_cg = (char *)bignum_bitmaps; + + if (NULL == p->framebuf || NULL == p->framelcd || + NULL == p->vbar_cg || NULL == p->hbar_cg) { + report(RPT_ERR, "%s: unable to allocate framebuffer", + drvthis->name); + return -1; + } + + memset(p->framebuf, ' ', SDEC_DISP_W * SDEC_DISP_H); + memset(p->framelcd, ' ', SDEC_DISP_W * SDEC_DISP_H); + + /* Prepare custom character bitmaps:vbar, hbar */ + for (i = 0; i < SDEC_NUM_CC; i++) { + for (j = 0; j < SDEC_CELL_H; j++) { + /* Visualize this: */ + p->vbar_cg[i * SDEC_CELL_H + SDEC_CELL_H - j - 1] = (j <= i) ? 0xFF : 0; + p->hbar_cg[i * SDEC_CELL_H + j] = (~(0x0F >> i)) & 0x1F; + } + } + + p->bignum_cg = bignum_bitmaps; + + /* Initializes the scheduler, see timing.h */ + timing_init(); + + /* Initialize access to the ports */ + if (port_access_multiple(LPT_DEFAULT, 3)) { + report(RPT_ERR, "%s: cannot get IO-permission for 0x%03X! Are we root?", + drvthis->name, LPT_DEFAULT); + return -1; + } + sdec_init(); + return 0; +} + +/** + * API: Close the driver (do necessary clean-up). + */ +MODULE_EXPORT void +sdeclcd_close(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + if (p) { + if (p->framebuf) + free(p->framebuf); + if (p->framelcd) + free(p->framelcd); + if (p->vbar_cg) + free(p->vbar_cg); + if (p->hbar_cg) + free(p->hbar_cg); + free(p); + } + + if (!port_deny_multiple(LPT_DEFAULT, 3)) { + report(RPT_WARNING, "%s: cannot release IO-permission for 0x%03X!", + drvthis->name, LPT_DEFAULT); + } + drvthis->store_private_ptr(drvthis, NULL); +} + +/** + * API: Return the display width in characters. + */ +MODULE_EXPORT int +sdeclcd_width(Driver *drvthis) +{ + return SDEC_DISP_W; +} + +/** + * API: Return the display height in characters. + */ +MODULE_EXPORT int +sdeclcd_height(Driver *drvthis) +{ + return SDEC_DISP_H; +} + +/** + * API: Flush the contents of the framebuffer out to the LCD + * + * The idea is that we only write to the LCD what we must. Therefore we compare + * the content of the framebuffer to the content of the LCD as we last wrote it + * and write when there is a diff. Writing involves setting the cursor (at a + * cost of 40 micro sec), and then write the framebuffer char (another 40 micro + * sec). As the Entry Mode was set to auto-increment the cursor on every write, + * we only set the cursor when we must. + */ +MODULE_EXPORT void +sdeclcd_flush(Driver *drvthis) +{ + PrivateData *p = (PrivateData *)drvthis->private_data; + int i, c; + unsigned char addr; + + for (i = 0, c = -1; i < SDEC_DISP_H * SDEC_DISP_W; i++) { + if (p->framelcd[i] == p->framebuf[i]) + continue; + /* Found diff: cursor ok ? */ + if (c != i) { + /* Set cursor address to where the change is */ + addr = (i < SDEC_DISP_W) ? + (SDEC_ADD_LINE1 + i) : + (SDEC_ADD_LINE2 + i - SDEC_DISP_W); + /* This costs 40 micro sec. */ + sdec_exec(SDEC_FN_DD_ADD | addr, p->bklgt); + c = i; + } + /* Write to LCD, 40 micro sec */ + sdec_write(p->framebuf[i], p->bklgt); + /* LCD Entry Mode auto increments cursor, so keep track */ + if (SDEC_DISP_W - 1 == c++) + c = -1; + /* Force cursor set */ + p->framelcd[i] = p->framebuf[i]; + } +} + +/** + * API: Print a string on the screen at position (x,y). + */ +MODULE_EXPORT void +sdeclcd_string(Driver *drvthis, int x, int y, char string[]) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + int len; + + if (y < 1 || y > SDEC_DISP_H || x < 1 || x > SDEC_DISP_W) + return; + + x--; /* convert coordinates to 0-base */ + y--; + + len = strlen(string); + memcpy(p->framebuf + x + y * SDEC_DISP_W, string, + (x + len > SDEC_DISP_W) ? SDEC_DISP_W - x : len); +} + +/** + * API: Print a character on the screen at position (x,y). + */ +MODULE_EXPORT void +sdeclcd_chr(Driver *drvthis, int x, int y, char c) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + if (y < 1 || y > SDEC_DISP_H || x < 1 || x > SDEC_DISP_W) + return; + p->framebuf[(y - 1) * SDEC_DISP_W + x - 1] = c; +} + +/** + * API: Clear the screen. + */ +MODULE_EXPORT void +sdeclcd_clear(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + memset(p->framebuf, ' ', SDEC_DISP_W * SDEC_DISP_H); +} + +/** + * API: Return the width of a character in pixels. + */ +MODULE_EXPORT int +sdeclcd_cellwidth(Driver *drvthis) +{ + return SDEC_CELL_W; +} + +/** + * API: Return the height of a character in pixels. + */ +MODULE_EXPORT int +sdeclcd_cellheight(Driver *drvthis) +{ + return SDEC_CELL_H; +} + +/** + * API: Get total number of custom characters available. + */ +MODULE_EXPORT int +sdeclcd_get_free_char(Driver *drvthis) +{ + return SDEC_NUM_CC; +} + +/** + * API: + * Implementation routine for Icons. The server asks for an icon by code, and + * this routine does the job. Simply updates the frame buffer with a suitable + * char based on the icon code requested. As the LCD has its own character + * map with latin, chinese, greek and extra symbols, it is only a matter of + * mapping to some character on the LCD that (loosely) resembles the + * requested code. This implementation does NOT use custom characters. + */ +MODULE_EXPORT int +sdeclcd_icon(Driver *drvthis, int x, int y, int icon_code) +{ + char icon_char; + + switch (icon_code) { + case ICON_BLOCK_FILLED: + icon_char = 0xFF; + break; + case ICON_ARROW_UP: + icon_char = '|'; + break; + case ICON_ARROW_DOWN: + icon_char = '|'; + break; + case ICON_ARROW_LEFT: + icon_char = 0x7F; + break; + case ICON_ARROW_RIGHT: + icon_char = 0x7E; + break; + case ICON_CHECKBOX_OFF: + icon_char = 0xDB; + break; + case ICON_CHECKBOX_ON: + icon_char = 0xE8; + break; + case ICON_CHECKBOX_GRAY: + icon_char = 0xA5; + break; + case ICON_SELECTOR_AT_LEFT: + icon_char = 0x7E; + break; + case ICON_SELECTOR_AT_RIGHT: + icon_char = 0x7F; + break; + case ICON_ELLIPSIS: + icon_char = 0xD0; + break; + default: + return -1; + } + + /* Update frame buffer */ + sdeclcd_chr(drvthis, x, y, icon_char); + return 0; +} + +/** + * API: + * Implements heart beat. This routine implements a visual cue that the whole + * LCDproc server/driver and host are still alive. We could update the frame + * buffer, but we chose to go straight to the hardware instead. + * We do NOT rely on custom characters for this. Instead, we simply alternate + * the frame buffer value with a special char in the corner. This way, we keep + * the character under the heart beat somewhat visible. After all, this is only + * 20x2. This is called by the server very often, so we pace ourselves, and + * beat the heart on our schedule. + * OPTIONAL + */ +MODULE_EXPORT void +sdeclcd_heartbeat(Driver *drvthis, int type) +{ + PrivateData *p = drvthis->private_data; + + if (time(NULL) <= p->hrbt_lasttime) + return; + /* Move cursor */ + sdec_exec(SDEC_FN_DD_ADD | SDEC_HB_ADD, p->bklgt); + /* Write */ + if (HEARTBEAT_ON == type && HEARTBEAT_OFF == p->hb_stus) + sdec_write(SDEC_HB_CHAR, p->bklgt); + else + sdec_write(p->framelcd[SDEC_HB_LOC], p->bklgt); + /* Toggle status */ + p->hb_stus = (HEARTBEAT_ON == p->hb_stus) ? + HEARTBEAT_OFF : HEARTBEAT_ON; + /* Save time */ + p->hrbt_lasttime = time(NULL); +} + +/** + * API: Draw a horizontal bar to the right. + */ +MODULE_EXPORT void +sdeclcd_hbar(Driver *drvthis, int x, int y, int len, int promille, int options) +{ + PrivateData *p = drvthis->private_data; + int i, j; + + if (hbar != p->ccmode) { + for (i = 0; i < SDEC_CELL_W; i++) { + for (j = 0; j < SDEC_CELL_H; j++) { + /* Set Custom Char Address */ + sdec_exec(SDEC_FN_CG_ADD | (i * SDEC_CELL_H + j), p->bklgt); + /* Write bitmap at Address */ + sdec_write(p->hbar_cg[i * SDEC_CELL_H + j], p->bklgt); + } + } + p->ccmode = hbar; + } + /* 1 bar is at char 0 or 8, so offset is 7 (avoiding offset of -1) */ + lib_hbar_static(drvthis, x, y, len, promille, options, SDEC_CELL_W, 7); +} + +/** + * API: Draw a vertical bar bottom-up. + */ +MODULE_EXPORT void +sdeclcd_vbar(Driver *drvthis, int x, int y, int len, int promille, int options) +{ + PrivateData *p = drvthis->private_data; + int i, j; + + if (vbar != p->ccmode) { + for (i = 0; i < SDEC_NUM_CC; i++) { + for (j = 0; j < SDEC_CELL_H; j++) { + /* Set Custom Char Address */ + sdec_exec(SDEC_FN_CG_ADD | (i * SDEC_CELL_H + j), p->bklgt); + /* Write bitmap at Address */ + sdec_write(p->vbar_cg[i * SDEC_CELL_H + j], p->bklgt); + } + } + p->ccmode = vbar; + } + /* see note in hbar */ + lib_vbar_static(drvthis, x, y, len, promille, options, SDEC_CELL_H, 7); +} + +/** + * API: Write a big number to the screen. + */ +MODULE_EXPORT void +sdeclcd_num(Driver *drvthis, int x, int num) +{ + PrivateData *p = (PrivateData *)drvthis->private_data; + int i, j; + + /* Big Nums are made of these chunks: */ + static char bignum_map[11][2][2] = { + {{0, 1}, {2, 3}}, /* 0 */ + {{32, 4}, {32, 4}}, /* 1 */ + {{5, 6}, {7, 5}}, + {{5, 6}, {5, 6}}, + {{2, 3}, {32, 4}}, + {{7, 5}, {5, 6}}, + {{7, 5}, {7, 6}}, + {{0, 1}, {32, 4}}, + {{7, 6}, {7, 6}}, + {{7, 6}, {5, 6}}, /* 9 */ + {{0xA1, 32}, {0xDF, 32}} /* : */ + }; + + if (num < 0 || num > 10) + return; + + /* Check custom char in LCD memory */ + if (bignum != p->ccmode) { + for (i = 0; i < SDEC_NUM_CC; i++) { + for (j = 0; j < SDEC_CELL_H; j++) { + /* Set Custom Char Address */ + sdec_exec(SDEC_FN_CG_ADD | (i * SDEC_CELL_H + j), p->bklgt); + /* Write bitmap at Address */ + sdec_write(p->bignum_cg[i * SDEC_CELL_H + j], p->bklgt); + } + } + p->ccmode = bignum; + } + /* Update the frame buffer */ + for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) + if (bignum_map[num][j][i] != ' ') + sdeclcd_chr(drvthis, x + i, j + 1, bignum_map[num][j][i]); +} + +/** + * API: + * This routine implements the control of the back light. Here, + * we set the back light status variable only, and let the next update take + * care of the actual light control line. + * Try and keep the light off, unless someone pushed a button not too long ago. + * The light has a poor half life, so try and preserve it. + */ +MODULE_EXPORT void +sdeclcd_backlight(Driver *drvthis, int level) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + + if (time(NULL) - p->bklgt_lasttime >= p->bklgt_timer || 0 == level) + p->bklgt = BACKLIGHT_OFF; + else + p->bklgt = BACKLIGHT_ON; +} + +/** + * API: Get key from keypad. This routine implements the polling of the + * keypad. This is not part of the SDEC LCD, but is wired through the + * parallel port as well. It seems natural to include it in this driver. + * Different boxes have different mapping to keys, but all codes are + * fortunately unique. As this routine is called periodically by the server, + * this is where we added the logic to turn off the backlight after some + * time. + */ +MODULE_EXPORT const char * +sdeclcd_get_key(Driver *drvthis) +{ + PrivateData *p = (PrivateData *)drvthis->private_data; + unsigned kbd; + + /* + * check backlight timer. It is important to make sure we turn the + * light off when there is no activity. With a half life of only + * 3,000 hours, we need to preserve it. + */ + if (time(NULL) - p->bklgt_lasttime >= p->bklgt_timer) + p->bklgt = BACKLIGHT_OFF; + else + p->bklgt = BACKLIGHT_ON; + + /* read keyboard status */ + kbd = port_in(LPT_STATUS) & LPT_STUS_MASK; + /* check if keyboard changed */ + if (kbd == p->lastkbd) + return NULL; + /* reset backlight counter */ + p->bklgt_lasttime = time(NULL); + + p->lastkbd = kbd; + /*- + * Return key text according to status reg mapping: + * X-e U:70 R:F8 D:68 L:58 Rel:78 + * X-peak U:C8 R:E0 D:C0 L:E8 Rel: 88,80,A8,A0 + */ + switch (kbd) { + case 0x70: + case 0xC8: + return ("Up"); + case 0xF8: + case 0xE0: + return ("Right"); + case 0x68: + case 0xC0: + return ("Down"); + case 0x58: + case 0xE8: + return ("Left"); + case 0x78: /* button Releases */ + case 0x88: + case 0x80: + case 0xA8: + case 0xA0: + return (NULL); + default: /* Code not mapped: */ + report(RPT_DEBUG, "LCDd sdeclcd.c/sdeclcd_get_key() %2x unmapped", kbd); + return (NULL); + } +} + +/** + * API: Return info string about this driver. + */ +MODULE_EXPORT const char * +sdeclcd_get_info(Driver *drvthis) +{ + return SDEC_IDENT; +} diff --git a/server/drivers/sdeclcd.h b/server/drivers/sdeclcd.h new file mode 100644 index 0000000..0657da1 --- /dev/null +++ b/server/drivers/sdeclcd.h @@ -0,0 +1,51 @@ +/** \file server/drivers/sdeclcd.h + * This is the LCDproc driver for SDEC LCD Devices. + * They are found in the Watchguard FireBox firewall appliances. + * They are interfaced through the parallel port. + * + * The code is based on the spec file LMC-S2D20-01.pdf, + * a technical hardware and programming guide for this LCD. + */ + +/*- + * Copyright(C) 2011, Francois Mertz + * + * 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 + */ + +#ifndef SDECLCD_H +#define SDECLCD_H + +MODULE_EXPORT int sdeclcd_init(Driver *); +MODULE_EXPORT void sdeclcd_close(Driver *); +MODULE_EXPORT int sdeclcd_width(Driver *); +MODULE_EXPORT int sdeclcd_height(Driver *); +MODULE_EXPORT void sdeclcd_flush(Driver *); +MODULE_EXPORT void sdeclcd_string(Driver *, int, int, char *); +MODULE_EXPORT void sdeclcd_chr(Driver *, int, int, char); +MODULE_EXPORT void sdeclcd_clear(Driver *); +MODULE_EXPORT int sdeclcd_cellwidth(Driver *); +MODULE_EXPORT int sdeclcd_cellheight(Driver *); +MODULE_EXPORT int sdeclcd_get_free_char(Driver *); +MODULE_EXPORT int sdeclcd_icon(Driver *, int, int, int); +MODULE_EXPORT void sdeclcd_heartbeat(Driver *, int); +MODULE_EXPORT void sdeclcd_vbar(Driver *, int, int, int, int, int); +MODULE_EXPORT void sdeclcd_hbar(Driver *, int, int, int, int, int); +MODULE_EXPORT void sdeclcd_num(Driver *, int, int); +MODULE_EXPORT const char *sdeclcd_get_key(Driver *); +MODULE_EXPORT void sdeclcd_backlight(Driver *, int); +MODULE_EXPORT const char *sdeclcd_get_info(Driver *); + +#endif