From 1f523c6438da33a373e36da4b72c4c338705a17b Mon Sep 17 00:00:00 2001 From: marschap Date: Sat, 11 Feb 2006 18:50:14 +0000 Subject: [PATCH] add driver for the ULA-200 device from ELV, --- acinclude.m4 | 20 +- server/drivers/Makefile.am | 4 +- server/drivers/ula200.c | 986 +++++++++++++++++++++++++++++++++++++ server/drivers/ula200.h | 46 ++ 4 files changed, 1053 insertions(+), 3 deletions(-) create mode 100644 server/drivers/ula200.c create mode 100644 server/drivers/ula200.h diff --git a/acinclude.m4 b/acinclude.m4 index 316e3f8..ff31cc8 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -9,13 +9,13 @@ AC_ARG_ENABLE(drivers, [ glcdlib,glk,hd44780,icp_a106,imon,IOWarrior,irman,] [ joy,lb216,lcdm001,lcterm,lirc,ms6931,mtc_s16209x,] [ MtxOrb,NoritakeVFD,pylcd,sed1330,sed1520,serialVFD,] - [ sli,stv5730,svga,t6963,text,tyan,xosd] + [ sli,stv5730,svga,t6963,text,tyan,ula200,xosd] [ \"all\" compiles all drivers], drivers="$enableval", drivers=[bayrad,CFontz,CFontz633,curses,CwLnx,glk,lb216,lcdm001,MtxOrb,pylcd,text]) if test "$drivers" = "all"; then - drivers=[bayrad,CFontz,CFontz633,CFontzPacket,curses,CwLnx,glcdlib,glk,hd44780,icp_a106,imon,IOWarrior,irman,joy,lb216,lcdm001,lcterm,lirc,ms6931,mtc_s16209x,MtxOrb,NoritakeVFD,pylcd,sed1330,sed1520,serialVFD,sli,stv5730,svga,t6963,text,tyan,xosd] + drivers=[bayrad,CFontz,CFontz633,CFontzPacket,curses,CwLnx,glcdlib,glk,hd44780,icp_a106,imon,IOWarrior,irman,joy,lb216,lcdm001,lcterm,lirc,ms6931,mtc_s16209x,MtxOrb,NoritakeVFD,pylcd,sed1330,sed1520,serialVFD,sli,stv5730,svga,t6963,text,tyan,ula200,xosd] fi drivers=`echo $drivers | sed 's/,/ /g'` @@ -285,6 +285,21 @@ dnl else DRIVERS="$DRIVERS tyan${SO}" actdrivers=["$actdrivers tyan"] ;; + ula200) + AC_CHECK_HEADERS([usb.h ftdi.h],[ + AC_CHECK_LIB(ftdi, main,[ + LIBFTDI="-lusb -lftdi" + DRIVERS="$DRIVERS ula200${SO}" + actdrivers=["$actdrivers ula200"] + ],[ +dnl else + AC_MSG_WARN([The ula200 driver needs the ftdi library]) + ]) + ],[ +dnl else + AC_MSG_WARN([The ula200 driver needs ftdi.h and usb.h]) + ]) + ;; xosd) AC_CHECK_HEADERS([xosd.h],[ AC_CHECK_LIB(xosd, main,[ @@ -316,6 +331,7 @@ AC_SUBST(LIBSVGA) AC_SUBST(DRIVERS) AC_SUBST(HD44780_DRIVERS) AC_SUBST(LIBGLCD) +AC_SUBST(LIBFTDI) AC_SUBST(LIBXOSD) ]) diff --git a/server/drivers/Makefile.am b/server/drivers/Makefile.am index b048a26..5be90d9 100644 --- a/server/drivers/Makefile.am +++ b/server/drivers/Makefile.am @@ -19,7 +19,7 @@ AM_LDFLAGS = @LDSHARED@ LIBS = pkglib_PROGRAMS = @DRIVERS@ -EXTRA_PROGRAMS = bayrad CFontz CFontz633 CFontzPacket curses CwLnx glcdlib glk hd44780 icp_a106 imon IOWarrior irman joy lb216 lcdm001 lcterm lirc ms6931 mtc_s16209x MtxOrb NoritakeVFD pylcd sed1330 sed1520 serialVFD stv5730 svga t6963 text tyan wirz_sli xosd +EXTRA_PROGRAMS = bayrad CFontz CFontz633 CFontzPacket curses CwLnx glcdlib glk hd44780 icp_a106 imon IOWarrior irman joy lb216 lcdm001 lcterm lirc ms6931 mtc_s16209x MtxOrb NoritakeVFD pylcd sed1330 sed1520 serialVFD stv5730 svga t6963 text tyan wirz_sli ula200 xosd noinst_LIBRARIES = libLCD.a libbignum.a IOWarrior_CFLAGS = @libusb_cflags@ $(AM_CFLAGS) @@ -45,6 +45,7 @@ serialVFD_LDADD = libLCD.a libbignum.a svga_LDADD = @LIBSVGA@ t6963_LDADD = libLCD.a tyan_LDADD = libLCD.a +ula200_LDADD = libLCD.a @LIBFTDI@ xosd_LDADD = @LIBXOSD@ libLCD_a_SOURCES = lcd_lib.h lcd_lib.c @@ -82,6 +83,7 @@ svga_SOURCES = lcd.h svgalib_drv.c svgalib_drv.h report.h t6963_SOURCES = lcd.h t6963.c t6963.h t6963_font.h report.h text_SOURCES = lcd.h text.h text.c report.h tyan_SOURCES = lcd.h tyan_lcdm.h tyan_lcdm.c report.h +ula200_SOURCES = lcd.h ula200.h ula200.c report.h wirz_sli_SOURCES = lcd.h wirz-sli.h wirz-sli.c report.h xosd_SOURCES = lcd.h xosdlib_drv.c xosdlib_drv.h report.h diff --git a/server/drivers/ula200.c b/server/drivers/ula200.c new file mode 100644 index 0000000..ad3e24d --- /dev/null +++ b/server/drivers/ula200.c @@ -0,0 +1,986 @@ +// Description: +// +// This is a driver for the ULA-200 from ELV (http://www.elv.de). It's a small +// board which can be connected to the PC via USB interface and that allows to +// connect a HD44780-compatible display. It also has a connection for up to 6 +// input buttons. +// +// It uses libftdi (which uses libusb) for communication, so no kernel driver +// is needed. You have to take care that ftdi_sio module doesn't claim the ELV +// device. If you didn't change the ids in the driver (ftdi_sio.c), you don't +// have to matter. +// +// +// Features: +// +// - text display is supported +// - standard symbols (heart, checkbox) are supported +// - backlight is supported +// - input buttons are supported +// - bars are NOT supported +// +// +// Known problems: +// +// Sometimes the display hangs (the ACK response is not received) on shutdown. +// Reconnect the display in that case. The same if it hangs while starting +// up (only happens if it was not the first time lcdproc talked to the +// display). +// +// I tried several hours to fix this, I simply find the reason for this problem. +// +// +// Implementation note: +// +// The ULA-200 talks a text protocol which allows to display text using a +// high-level language, i.e. STX 's' ... ETX. It also +// allows low-level register access to the HD44780. So in theory, it would be +// possible to write a hd44780 extension and let the hd44780 core do the rest. +// I tried this. It was slow and didn't work with user-specific characters +// (the hd44780 frequently changes this characters which seems to confuse the +// microcontroller, at least I cannot explain why it didn't work, there was +// garbare). +// +// So I wrote a own driver (this here) which uses the high-level language and +// should work for displays with all sizes. I only tested 20x4, so maybe for +// other sizes the positioning code may be adapted. +// +// As I mentioned, there were problems with frequently changing the +// user-definable characters. I also tried to implement bar code in the ula200 +// driver with similar effects. I gave it up because I don't need it personally +// and it can be done later. However, standard icons are implemented. The +// user-definable characters are set in startup and are not changed. This +// works like a charm. It is not possible to use character 0 with the +// high-level language (or at least it isn't documented how to escape it). +// It could be done with hd44780 code, but I replaced the character with +// a standard character which looks good. +// +// +// Author +// +// Bernhard Walle +// Feel free to contact me if there are problems with this driver. +// + +/* Copyright (C) 2006 Bernhard Walle + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "hd44780-charmap.h" +#include "lcd.h" +#include "ula200.h" +#include "report.h" +#include "lcd_lib.h" +#include "timing.h" + + +/* Vars for the server core */ +MODULE_EXPORT char *api_version = API_VERSION; +MODULE_EXPORT int stay_in_foreground = 0; +MODULE_EXPORT int supports_multiple = 1; +MODULE_EXPORT char *symbol_prefix = "ula200_"; + + +/////////////////////////////////////////////////////////////////////////////// +// constants +// +#define DISPLAY_VENDOR_ID 0x0403 +#define DISPLAY_PRODUCT_ID 0xf06d + +#define CH_STX 0x02 +#define CH_ETX 0x03 +#define CH_ENQ 0x05 +#define CH_ACK 0x06 +#define CH_NAK 0x15 +#define CH_DC2 0x12 +#define CH_DC3 0x13 + +#define ULA200_KEY_UP (1 << 0) +#define ULA200_KEY_DOWN (1 << 1) +#define ULA200_KEY_LEFT (1 << 2) +#define ULA200_KEY_RIGHT (1 << 3) +#define ULA200_KEY_ENTER (1 << 4) +#define ULA200_KEY_ESCAPE (1 << 5) + +#define CELLWIDTH 5 +#define CELLHEIGHT 8 + +/* repeat conts for responses */ +#define MAX_REPEATS 20 + + +/////////////////////////////////////////////////////////////////////////////// +// Key ring implementation from CFontz633io +// + +/* KeyRing management */ +#define KEYRINGSIZE 16 + +typedef struct { + unsigned char contents[KEYRINGSIZE]; + int head; + int tail; +} KeyRing; + + + +/* + * KeyRing handling functions. + * This separates the producer from the consumer. + * It is just a small fifo of unsigned char. + */ + +/** initialize/empty key ring by resetting its read & write pointers */ +void EmptyKeyRing(KeyRing *kr) +{ + kr->head = kr->tail = 0; +} + + +/** add byte to key ring; return success (byte added) / failure (key ring is full) */ +int AddKeyToKeyRing(KeyRing *kr, unsigned char key) +{ + if (((kr->head + 1) % KEYRINGSIZE) != (kr->tail % KEYRINGSIZE)) { + /* fprintf(stderr, "We add key: %d\n", key); */ + + kr->contents[kr->head % KEYRINGSIZE] = key; + kr->head = (kr->head + 1) % KEYRINGSIZE; + return 1; + } + + /* KeyRing overflow: do not accept extra key */ + return 0; +} + + +/** get byte from key ring (or '\\0' if key ring is empty) */ +unsigned char GetKeyFromKeyRing(KeyRing *kr) +{ + unsigned char retval = '\0'; + + kr->tail %= KEYRINGSIZE; + + if ((kr->head % KEYRINGSIZE) != kr->tail) { + retval = kr->contents[kr->tail]; + kr->tail = (kr->tail + 1) % KEYRINGSIZE; + } + + /* if (retval) fprintf(stderr, "We remove key: %d\n", retval); */ + return retval; +} + +/////////////////////////////////////////////////////////////////////////////// +// for HD 44780 compatibility +// + +// commands for senddata +#define RS_DATA 0x00 +#define RS_INSTR 0x01 + +#define SETCHAR 0x40 /* Only reachable with EXTREG clear */ + + +/////////////////////////////////////////////////////////////////////////////// +// private data types +// +typedef struct { + + // the handle for the USB FTDI library + struct ftdi_context ftdic; + + // the width and the height (in number of characters) of the library + int width, height; + + // The framebuffer and the framebuffer for the last contents (incr. update) + unsigned char *framebuf, *lcd_contents; + + // first time => all is dirty + unsigned char all_dirty; + + // backlight (-1 = unset, 0 = off, 1 = on) + int backlight; + + // the keyring + KeyRing keyring; + + +} PrivateData; + + +/////////////////////////////////////////////////////////////////////////////// +// Reads a USB characters +static inline int +ula200_ftdi_usb_read(PrivateData *p) +{ + unsigned char buffer[1]; + int err; + + while ((err = ftdi_read_data(&p->ftdic, buffer, 1)) == 0); + + if (err < 0) + { + return -1; + } + else + { + return buffer[0]; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// Reads the response +// +// @return true if ACK was responded, false if NACk was responded +// +static bool +ula200_ftdi_read_response(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + bool result = false; + bool answer_read = false; + int ret; + int ch; + + while (!answer_read) + { + /* wait until STX */ + while (((ret = ula200_ftdi_usb_read(p)) != CH_STX) && (ret > 0)); + if (ret < 0) + { + return false; + } + + /* read next char */ + ch = ula200_ftdi_usb_read(p); + + switch (ch) + { + case 't': + ch = ula200_ftdi_usb_read(p); + AddKeyToKeyRing(&p->keyring, (unsigned char)(ch - 0x40)); + break; + + case CH_ACK: + answer_read = true; + result = true; + break; + + case CH_NAK: + answer_read = true; + break; + + default: + answer_read = true; + report(RPT_INFO, "Read invalid answer (0x%x)", ch); + } + + /* wait until ETX */ + while (((ret = ula200_ftdi_usb_read(p)) != CH_ETX) && (ret > 0)); + if (ret < 0) + { + return false; + } + } + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// Write a command to the display. Adds the STX and ETX header/trailer. +// +// @param p the private data +// @param data the data bytes +// @param length the number of bytes in @p data which are valid +// @param escape if the data should be escaped (see the User's Guide of the +// ULA-200) +// @return 0 on success, negative value on error +// +static int +ula200_ftdi_write_command(Driver *drvthis, unsigned char *data, int length, bool escape) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + int i, err; + int repeat_count = 0; + int pos = 0; + unsigned char buffer[1024]; + + if (length > 512) + { + return -EINVAL; + } + + /* fill the array */ + buffer[pos++] = CH_STX; + for (i = 0; i < length; i++) + { + if (escape) + { + if (data[i] == CH_STX) + { + buffer[pos++] = CH_ENQ; + buffer[pos++] = CH_DC2; + } + else if (data[i] == CH_ETX) + { + buffer[pos++] = CH_ENQ; + buffer[pos++] = CH_DC3; + } + else if (data[i] == CH_ENQ) + { + buffer[pos++] = CH_ENQ; + buffer[pos++] = CH_NAK; + } + else + { + buffer[pos++] = data[i]; + } + } + else + { + buffer[pos++] = data[i]; + } + } + buffer[pos++] = CH_ETX; + + do + { + /* bytes */ + err = ftdi_write_data(&p->ftdic, buffer, pos); + if (err < 0) + { + report(RPT_WARNING, "ftdi_write_data failed"); + return -1; + } + } + while (!ula200_ftdi_read_response(drvthis) && (repeat_count++ < MAX_REPEATS)); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Clear the display +// +static int +ula200_ftdi_clear(Driver *drvthis) +{ + //PrivateData *p = (PrivateData *) drvthis->private_data; + unsigned char command[1]; + int err; + + command[0] = 'l'; + err = ula200_ftdi_write_command(drvthis, command, 1, true); + if (err < 0) + { + report(RPT_WARNING, "ula200_ftdi_clear: " + "ula200_ftdi_write_command failed"); + } + + return err; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Set the position +// +static int +ula200_ftdi_position(Driver *drvthis, int x, int y) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + unsigned char command[5]; + int err; + + if (y >= 2) + { + y -= 2; + x += p->width; + } + + command[0] = 'p'; + command[1] = x; + command[2] = y; + err = ula200_ftdi_write_command(drvthis, command, 3, true); + if (err < 0) + { + report(RPT_WARNING, "ula200_ftdi_position(%d,%d): " + "ula200_ftdi_write_command failed", x, y); + } + + return err; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Send raw data +// +static int +ula200_ftdi_rawdata(Driver *drvthis, unsigned char flags, unsigned char ch) +{ + unsigned char command[3]; + unsigned int err; + //PrivateData *p = (PrivateData *) drvthis->private_data; + + command[0] = 'R'; + command[1] = flags == RS_DATA ? '2' : '0'; + command[2] = ch; + err = ula200_ftdi_write_command(drvthis, command, 3, false); + if (err < 0) + { + report(RPT_ERR, "ULA-200: Cannot read size: %s"); + } + + return err; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Displays a string +// +static int +ula200_ftdi_string(Driver *drvthis, unsigned char *string, int len) +{ + //PrivateData *p = (PrivateData *) drvthis->private_data; + unsigned char buffer[128]; + int err; + int i; + + if (len > 80) + { + return -EINVAL; + } + + buffer[0] = 's'; + buffer[1] = len; + for (i = 0; i < len; i++) + { + buffer[i+2] = HD44780_charmap[(unsigned char)string[i]]; + } + + err = ula200_ftdi_write_command(drvthis, buffer, len+2, true); + if (err < 0) + { + report(RPT_WARNING, "ula200_ftdi_string: " + "ula200_ftdi_write_command failed"); + } + + return err; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Enables the raw register access mode. +// +static int +ula200_ftdi_enable_raw_mode(Driver *drvthis) +{ + unsigned char command[3]; + + report(RPT_DEBUG, "Enable the raw mode"); + + command[0] = 'R'; + command[1] = 'E'; + command[2] = '1'; + return ula200_ftdi_write_command(drvthis, command, 3, false); +} + +/////////////////////////////////////////////////////////////////////////////// +// Loads custom characters in the display +// +static int +ula200_load_curstom_chars(Driver *drvthis) +{ + int i, col, row; + int err = 0; + char custom_chars[8][CELLHEIGHT*CELLHEIGHT] = { + { 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, + 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, + 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, + 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, + 1, 0, 1, 0, 1, + 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1 }, + { 0, 0, 1, 0, 0, + 0, 1, 1, 1, 0, + 1, 0, 1, 0, 1, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0 }, + { 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 1, 0, 1, 0, 1, + 0, 1, 1, 1, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0 }, + { 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 1, 1, 1, 0, 1, + 1, 0, 1, 1, 0, + 1, 0, 1, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 0, 1, 0, 1, + 1, 1, 0, 1, 1, + 1, 0, 1, 0, 1, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0 }}; + + for (i = 0; i < 8 && err == 0; i++) + { + /* Tell the HD44780 we will redefine char number i */ + ula200_ftdi_rawdata(drvthis, RS_INSTR, SETCHAR | i * 8); + if (err < 0) + { + report(RPT_WARNING, "ula200_ftdi_rawdata failed"); + break; + } + + /* Send the subsequent rows */ + for (row = 0; row < CELLHEIGHT; row++) + { + int value = 0; + for (col = 0; col < CELLWIDTH; col++) + { + value <<= 1; + value |= (custom_chars[i][(row * CELLWIDTH) + col] > 0) ? 1 : 0; + } + err = ula200_ftdi_rawdata(drvthis, RS_DATA, value); + if (err < 0) + { + report(RPT_WARNING, "ula200_ftdi_rawdata failed"); + break; + } + } + } + + return err; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Init the driver and display +// +MODULE_EXPORT int +ula200_init(Driver *drvthis) +{ + PrivateData *p; + int err; + char *s; + + // Alocate and store private data + p = (PrivateData *) malloc( sizeof( PrivateData) ); + if (!p) + { + return -1; + } + if (drvthis->store_private_ptr(drvthis, p)) + { + return -1; + } + + p->backlight = -1; + p->all_dirty = 1; + EmptyKeyRing(&p->keyring); + + // Get and parse size + s = drvthis->config_get_string( drvthis->name, "size", 0, "20x4"); + if (sscanf( s, "%dx%d", &(p->width), &(p->height) ) != 2 + || (p->width <= 0) || (p->width > LCD_MAX_WIDTH) + || (p->height <= 0) || (p->height > LCD_MAX_HEIGHT)) + { + report(RPT_ERR, "ULA-200: Cannot read size: %s", s ); + } + + // Allocate framebuffer + p->framebuf = (unsigned char *)malloc(p->width * p->height); + if (!p->framebuf) + { + goto err_begin; + } + + // Allocate and clear the buffer for incremental updates + p->lcd_contents = (unsigned char *) malloc (p->width * p->height); + if (!p->lcd_contents) + { + goto err_framebuf; + } + memset(p->lcd_contents, 0, p->width * p->height); + + // open the FTDI library + ftdi_init(&p->ftdic); + (&p->ftdic)->usb_write_timeout = 20; + (&p->ftdic)->usb_read_timeout = 20; + + // open the device + err = ftdi_usb_open(&p->ftdic, DISPLAY_VENDOR_ID, DISPLAY_PRODUCT_ID); + if (err < 0) + { + report(RPT_ERR, "Couldn't open USB device"); + goto err_lcd; + } + + // set the baudrate + err = ftdi_set_baudrate(&p->ftdic, 19200); + if (err < 0) + { + report(RPT_ERR, "ULA-200: Cannot set baudrate"); + goto err_ftdi; + } + + // set communication parameters + err = ftdi_set_line_property(&p->ftdic, BITS_8, STOP_BIT_1, EVEN); + if (err < 0) + { + report(RPT_ERR, "ULA-200: Cannot set line properties"); + goto err_ftdi; + } + + // user is able to write commands + err = ula200_ftdi_enable_raw_mode(drvthis); + if (err < 0) + { + report(RPT_ERR, "Was not able to enable the raw mode"); + goto err_ftdi; + } + + // load the chars + err = ula200_load_curstom_chars(drvthis); + if (err < 0) + { + report(RPT_ERR, "Was not able to write the custom characters"); + goto err_ftdi; + } + + return 0; + +err_ftdi: + ftdi_usb_close(&p->ftdic); + ftdi_deinit(&p->ftdic); +err_framebuf: + free(p->framebuf); +err_lcd: + free(p->lcd_contents); +err_begin: + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// Clean-up +// +MODULE_EXPORT void +ula200_close(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + char buffer[150]; + + ftdi_usb_purge_buffers(&p->ftdic); + ftdi_usb_close(&p->ftdic); + ftdi_deinit(&p->ftdic); + + if (p->framebuf) + { + free(p->framebuf); + } + + if (p->lcd_contents) + { + free(p->lcd_contents); + } + + free(p); + drvthis->store_private_ptr(drvthis, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +// Returns the display width +// +MODULE_EXPORT int +ula200_width (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + return p->width; +} + +/////////////////////////////////////////////////////////////////////////////// +// Returns the display height +// +MODULE_EXPORT int +ula200_height (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + return p->height; +} + +/////////////////////////////////////////////////////////////////////////////// +// Clear the framebuffer +// +MODULE_EXPORT void +ula200_clear (Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + memset(p->framebuf, ' ', p->width * p->height); +} + +/////////////////////////////////////////////////////////////////////////////// +// Place a character in the framebuffer +// +MODULE_EXPORT void +ula200_chr (Driver *drvthis, int x, int y, char ch) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + y--; + x--; + + p->framebuf[ (y * p->width) + x] = ch; +} + +/////////////////////////////////////////////////////////////////////////////// +// Place a string in the framebuffer +// +MODULE_EXPORT void +ula200_string (Driver *drvthis, int x, int y, char *s) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + int i; + + x --; // Convert 1-based coords to 0-based + y --; + + for (i = 0; s[i]; i++) { + // Check for buffer overflows... + if ((y * p->width) + x + i > (p->width * p->height)) + break; + p->framebuf[(y*p->width) + x + i] = s[i]; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Sets the backlight on or off +// +MODULE_EXPORT void +ula200_backlight (Driver *drvthis, int on) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + unsigned char command[2]; + int err; + + if (p->backlight != on) + { + p->backlight = on; + + command[0] = 'h'; + command[1] = on ? '1' : '0'; + err = ula200_ftdi_write_command(drvthis, command, 2, false); + if (err < 0) + { + report(RPT_WARNING, "ula200_backlight: Error in ula200_ftdi_write_command"); + } + + report(RPT_INFO, "Turn backlight on"); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Flush the framebuffer to the display +// +MODULE_EXPORT void +ula200_flush(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + int x, y; + int wid = p->width; + char ch; + char drawing; + int count; + int firstdiff; + int lastdiff; + + if (p->all_dirty) + { + ula200_ftdi_clear(drvthis); + p->all_dirty = 0; + } + + // Update LCD incrementally by comparing with last contents + count = 0; + for (y = 0; y < p->height; y++) + { + drawing = 0; + firstdiff = -1; + lastdiff = 0; + for (x = 0 ; x < wid; x++) + { + ch = p->framebuf[(y * wid) + x]; + if (ch != p->lcd_contents[(y*wid)+x]) + { + p->lcd_contents[(y*wid)+x] = ch; + if (firstdiff == -1) + { + firstdiff = x; + } + lastdiff = x; + } + } + + if (firstdiff >= 0) + { + ula200_ftdi_position(drvthis, firstdiff, y); + ula200_ftdi_string(drvthis, p->framebuf + (y*wid) + firstdiff, + lastdiff - firstdiff + 1); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// Set default icon into a userdef char +// +MODULE_EXPORT int +ula200_icon (Driver *drvthis, int x, int y, int icon) +{ + + /* Yes I know, this is a VERY BAD implementation */ + switch( icon ) + { + case ICON_BLOCK_FILLED: + ula200_chr( drvthis, x, y, 0xff); + break; + case ICON_HEART_FILLED: + ula200_chr( drvthis, x, y, 2); + break; + case ICON_HEART_OPEN: + ula200_chr( drvthis, x, y, 1); + break; + case ICON_ARROW_UP: + ula200_chr( drvthis, x, y, 3); + break; + case ICON_ARROW_DOWN: + ula200_chr( drvthis, x, y, 4); + break; + case ICON_ARROW_LEFT: + ula200_chr( drvthis, x, y, 0x7F); + break; + case ICON_ARROW_RIGHT: + ula200_chr( drvthis, x, y, 0x7E); + break; + case ICON_CHECKBOX_OFF: + ula200_chr( drvthis, x, y, 5); + break; + case ICON_CHECKBOX_ON: + ula200_chr( drvthis, x, y, 6); + break; + case ICON_CHECKBOX_GRAY: + ula200_chr( drvthis, x, y, 7); + break; + default: + return -1; /* Let the core do other icons */ + } + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Set default icon into a userdef char +// +MODULE_EXPORT char * +ula200_get_key (Driver *drvthis) +{ + PrivateData *p = drvthis->private_data; + unsigned char key; + + key = GetKeyFromKeyRing(&p->keyring); + + switch (key) + { + case ULA200_KEY_LEFT: + return "Left"; + break; + case ULA200_KEY_UP: + return "Up"; + break; + case ULA200_KEY_DOWN: + return "Down"; + break; + case ULA200_KEY_RIGHT: + return "Right"; + break; + case ULA200_KEY_ENTER: + return "Enter"; /* Is this correct ? */ + break; + case ULA200_KEY_ESCAPE: + return "Escape"; + break; + default: + if (key != '\0') + { + report( RPT_INFO, "ula200: Untreated key 0x%2x", key); + } + return NULL; + break; + } + return NULL; +} + + +/* vim: set sw=4 ts=4 et: */ diff --git a/server/drivers/ula200.h b/server/drivers/ula200.h new file mode 100644 index 0000000..5fa7bd4 --- /dev/null +++ b/server/drivers/ula200.h @@ -0,0 +1,46 @@ +/* This is the LCDproc driver header for ULA-200 (http://www.elv.de) + + Author: bernhard.walle@gmx.de + + Copyright (C) 2006, Bernhard Walle + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +#ifndef ULA200_H +#define ULA200_H + +#include "lcd.h" + +#ifndef bool +# define bool short +# define true 1 +# define false 0 +#endif + +MODULE_EXPORT int ula200_init(Driver *drvthis); +MODULE_EXPORT void ula200_close (Driver *drvthis); +MODULE_EXPORT int ula200_width (Driver *drvthis); +MODULE_EXPORT int ula200_height (Driver *drvthis); +MODULE_EXPORT void ula200_clear (Driver * drvthis); +MODULE_EXPORT void ula200_string (Driver * drvthis, int x, int y, char string[]); +MODULE_EXPORT void ula200_chr (Driver * drvthis, int x, int y, char c); +MODULE_EXPORT void ula200_set_char (Driver * drvthis, int n, char *dat); +MODULE_EXPORT void ula200_backlight (Driver * drvthis, int on); +MODULE_EXPORT void ula200_flush (Driver * drvthis); +MODULE_EXPORT void ula200_vbar (Driver * drvthis, int x, int y, int len, int promille, int options); +MODULE_EXPORT void ula200_hbar (Driver * drvthis, int x, int y, int len, int promille, int options); +MODULE_EXPORT void ula200_num (Driver * drvthis, int x, int num); +MODULE_EXPORT int ula200_icon(Driver * drvthis, int x, int y, int icon); + +#endif /* ULA200_H */