diff --git a/CREDITS b/CREDITS
index bf7e599..b3bb74a 100644
--- a/CREDITS
+++ b/CREDITS
@@ -270,6 +270,9 @@ Jonathan Kyler:
Laurent Latil
- SureElec driver for devices made by SURE Electronics
+Christoph Rasim
+ - MDM166A driver for Futaba/Targa USB VFD
+
Further developers in the sourceforge lcdproc team:
Mindaugas Idzelis aim4min
diff --git a/ChangeLog b/ChangeLog
index e54ec2a..d6a5289 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -31,6 +31,7 @@ v.0.5dev (ongoing development)
* picolcd: Use module output function to control key LEDs (Martin Jones)
+ hd44780: Added 'usbtiny' connection type (Siarhei Herasimchuk)
* SureElec: Fix stall on startup
+ + new driver: MDM166A for Futaba/Targa USB VFD (Christoph Rasim)
v0.5.3
+ lcdexec: notification when called program finishes
diff --git a/LCDd.conf b/LCDd.conf
index 20748a7..ef490d1 100644
--- a/LCDd.conf
+++ b/LCDd.conf
@@ -45,11 +45,11 @@ DriverPath=server/drivers/
#
# The following drivers are supported:
# 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, sli, stv5730, svga, t6963, text, tyan, ula200,
-# xosd
+# 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,
+# stv5730, svga, t6963, text, tyan, ula200, xosd
Driver=curses
# Tells the driver to bind to the given interface
@@ -736,6 +736,18 @@ OffBrightness=50
+## Futuba MDM166A Display
+[mdm166a]
+# Show self-running clock after LCDd shutdown
+# Possible values: [default: no; legal: no, small, big]
+Clock=big
+# Dimm display, no dimming gives full brightness [default: no, legal: yes, no]
+Dimming=no
+# Dimm display in case LCDd is inactive [default: no, legal: yes, no]
+OffDimming=yes
+
+
+
## MSI MS-6931 driver for displays in 1HU servers ##
[ms6931]
diff --git a/acinclude.m4 b/acinclude.m4
index 2dfdd65..e1c8a86 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -11,18 +11,18 @@ AC_ARG_ENABLE(drivers,
[ 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]
+ [ 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,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,SureElec,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,mdm166a,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'`
@@ -318,6 +318,14 @@ dnl else
DRIVERS="$DRIVERS MD8800${SO}"
actdrivers=["$actdrivers MD8800"]
;;
+ mdm166a)
+ if test "$enable_libhid" = yes ; then
+ DRIVERS="$DRIVERS mdm166a${SO}"
+ actdrivers=["$actdrivers mdm166a"]
+ else
+ AC_MSG_WARN([The mdm166a driver needs the hid library])
+ fi
+ ;;
ms6931)
DRIVERS="$DRIVERS ms6931${SO}"
actdrivers=["$actdrivers ms6931"]
diff --git a/configure.in b/configure.in
index cf0b026..c70876e 100644
--- a/configure.in
+++ b/configure.in
@@ -292,6 +292,29 @@ fi
AC_SUBST(LIBFTDI_LIBS)
AC_SUBST(LIBFTDI_CFLAGS)
+dnl ######################################################################
+dnl libhid support
+dnl ######################################################################
+AC_MSG_CHECKING([if libhid support has been enabled]);
+AC_ARG_ENABLE(libhid,
+ [AS_HELP_STRING([--disable-libhid],[disable HID support using libhid])],
+ [ if test "$enableval" != "no"; then
+ enable_libhid=yes
+ fi ],
+ [ enable_libhid=yes ]
+)
+AC_MSG_RESULT($enable_libhid)
+
+if test "$enable_libhid" = "yes"; then
+ ifdef([PKG_CHECK_MODULES],
+ [PKG_CHECK_MODULES(LIBHID, libhid >= 0.2.16,
+ [AC_DEFINE(HAVE_LIBHID, [1], [Define to 1 if you have libhid])],
+ [ enable_libhid=no ])],
+ [AC_MSG_WARN([pkg-config not (fully) installed; drivers requiring libhid may not be built])])
+fi
+AC_SUBST(LIBHID_LIBS)
+AC_SUBST(LIBHID_CFLAGS)
+
dnl ######################################################################
dnl ethlcd support
dnl ######################################################################
diff --git a/docs/lcdproc-user/drivers.docbook b/docs/lcdproc-user/drivers.docbook
index bc320da..a706c35 100644
--- a/docs/lcdproc-user/drivers.docbook
+++ b/docs/lcdproc-user/drivers.docbook
@@ -33,6 +33,7 @@ well as the configuration of LCDd.
&lirc;
&lis;
&MD8800;
+&mdm166a;
&ms6931;
&mtc_s16209x;
&MtxOrb;
diff --git a/docs/lcdproc-user/drivers/Makefile.am b/docs/lcdproc-user/drivers/Makefile.am
index bb62b57..936d352 100644
--- a/docs/lcdproc-user/drivers/Makefile.am
+++ b/docs/lcdproc-user/drivers/Makefile.am
@@ -25,6 +25,7 @@ EXTRA_DIST = bayrad.docbook \
lircin.docbook \
lis.docbook \
MD8800.docbook \
+ mdm166a.docbook \
ms6931.docbook \
mtc_s16209x.docbook \
mtxorb.docbook \
diff --git a/docs/lcdproc-user/drivers/mdm166a.docbook b/docs/lcdproc-user/drivers/mdm166a.docbook
new file mode 100644
index 0000000..6424620
--- /dev/null
+++ b/docs/lcdproc-user/drivers/mdm166a.docbook
@@ -0,0 +1,197 @@
+
+
+
+ MarkusDolze
+
+
+The mdm166a driver
+
+This section talks about using LCDproc with
+the Futaba / Targa USB Graphic Vacuum Fluorescent Display (MDM166A; USB
+VID=0x19c2, PID=0x6a11).
+
+The MDM166A is a graphical VFD with a 96x16 pixel dot matrix area which
+is used for 16x2 characters with a 6x8 pixel font. It features several icons,
+volume level and WLAN strength indicator which are all software controllable
+using the output function.
+
+The mdm166a driver builts on top of libhid which in
+turn uses libusb.
+
+The driver was developed by Christoph Rasim
+() without any available documentation
+from the vendor, but with a good protocol description from Thomas Koos
+() created by reverse engineering the
+protocol.
+
+
+
+Configuration in LCDd.conf
+
+
+[mdm166a]
+
+
+
+
+ Clock =
+ TYPE
+
+
+ Show self-running clock after LCDd shutdown. Possible values for
+ TYPE are no,
+ small and big. If not given no clock
+ is shown.
+
+
+
+
+
+ Dimming = ¶meters.yesnodef;
+
+
+ Dimm display, no dimming gives full brightness.
+
+
+
+
+
+ OffDimming = ¶meters.yesnodef;
+
+
+ Dimm display in case LCDd is inactive.
+
+
+
+
+
+
+
+
+
+
+Using the icons
+
+The icons, the volume bar and the WLAN indicator supported by the VFD
+can be controlled by use of the output function.
+
+The on parameter is used as a
+bitmask to control which elements to display. Setting an icon bit to
+1 enables the icon, setting the bit to 0
+disables an icon. Volume and WLAN strength indicator accept an numeric value
+in the given bit mask.
+
+
+ mdm166a_output bitmask
+
+
+
+ bit(s)
+ Icon
+
+
+
+
+ 0
+ Play
+
+
+ 1
+ Pause
+
+
+ 2
+ Record
+
+
+ 3
+ Message
+
+
+ 4
+ Mail (at-symbol)
+
+
+ 5
+ Mute
+
+
+ 6
+ WLAN tower
+
+
+ 7
+ Volume (the word)
+
+
+ 8…12
+ Volume (decimal 0…28)
+
+
+ 13…14
+ WLAN strength (0…3)
+
+
+
+
+
+
+
+
+
+Special device hints
+
+This display may appear as a HID device in your
+system which may prevent libhid from being able to open
+the USB device. In this case you have to create some OS-specific
+configuration to prevent the HID driver to take control
+of this display.
+
+
+Configuring FreeBSD 7.x to exclude this device from uhid
+
+To make the uhid driver ignore this device you have to apply the
+following patch to your kernel source and recompile and install your kernel:
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/lcdproc-user/lcdproc-user.docbook b/docs/lcdproc-user/lcdproc-user.docbook
index dabee2f..bf6ef68 100644
--- a/docs/lcdproc-user/lcdproc-user.docbook
+++ b/docs/lcdproc-user/lcdproc-user.docbook
@@ -40,6 +40,7 @@
+
diff --git a/server/drivers/Makefile.am b/server/drivers/Makefile.am
index 0d1733f..925fab1 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 SureElec 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 mdm166a 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
g15_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
@@ -29,6 +29,7 @@ hd44780_CFLAGS = @LIBUSB_CFLAGS@ @LIBFTDI_CFLAGS@ $(AM_CFLAGS)
i2500vfd_CFLAGS = @LIBFTDI_CFLAGS@ $(AM_CFLAGS)
IOWarrior_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
lis_CFLAGS = @LIBUSB_CFLAGS@ @LIBFTDI_CFLAGS@ $(AM_CFLAGS)
+mdm166a_CFLAGS = @LIBHID_CFLAGS@ $(AM_CFLAGS)
picolcd_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
shuttleVFD_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
ula200_CFLAGS = @LIBFTDI_CFLAGS@ $(AM_CFLAGS)
@@ -52,6 +53,7 @@ irman_LDADD = @LIBIRMAN@
lcterm_LDADD = libLCD.a
lirc_LDADD = @LIBLIRC_CLIENT@
lis_LDADD = libLCD.a @LIBFTDI_LIBS@ @LIBUSB_LIBS@ @LIBPTHREAD_LIBS@ libbignum.a
+mdm166a_LDADD = @LIBHID_LIBS@ libLCD.a
mtc_s16209x_LDADD = libLCD.a
MtxOrb_LDADD = libLCD.a libbignum.a
mx5000_LDADD = @LIBMX5000@
@@ -99,6 +101,7 @@ lcterm_SOURCES = lcd.h lcd_lib.h lcterm.c lcterm.h report.h
lirc_SOURCES = lcd.h lircin.c lircin.h report.h
lis_SOURCES = lcd.h lcd_lib.h lis.h lis.c report.h
MD8800_SOURCES = lcd.h lcd_lib.h MD8800.c MD8800.h report.h
+mdm166a_SOURCES = lcd.h mdm166a.c mdm166a.h i2500vfdfm.c i2500vfdfm.h report.h
ms6931_SOURCES = lcd.h lcd_lib.h ms6931.h ms6931.c report.h
mtc_s16209x_SOURCES = lcd.h lcd_lib.h mtc_s16209x.c mtc_s16209x.h report.h
MtxOrb_SOURCES = lcd.h lcd_lib.h MtxOrb.c MtxOrb.h report.h adv_bignum.h
diff --git a/server/drivers/mdm166a.c b/server/drivers/mdm166a.c
new file mode 100644
index 0000000..9c45d1a
--- /dev/null
+++ b/server/drivers/mdm166a.c
@@ -0,0 +1,738 @@
+/** \file server/drivers/mdm166a.c
+ * LCDd \c mdm166a driver for Futuba MDM166A displays.
+ *
+ * The 96x16 pixel dot matrix area is used for 2x16 characters with a
+ * 6x8 pixel font. The icons on the display are supported by using the
+ * output-function.
+ *
+ * The display features a self-running clock mode, which can be
+ * activated in the config file and will be shown on LCDproc shutdown.
+ */
+
+/*-
+ * (C) 2010 Christoph Rasim (christoph@rasim.net)
+ *
+ * Code here is basend on i2500vfd.c:
+ * (C) 2003,2007 Intra2net AG
+ *
+ * i2500vfdfm.c is used for the font, no need for another copy of it.
+ * The HD44780 font in i2500vfdfm.c was taken from Michael Reinelt /
+ * lcd4linux and is (C) 2000 by him.
+ *
+ * This driver is released under the GPL. See file COPYING in this
+ * package for further details.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#include "lcd.h"
+#include "mdm166a.h"
+#include "i2500vfdfm.h"
+#include "report.h"
+
+/*
+ * The display itself stores eight pixels in one byte We waste a little
+ * memory as we store one pixel per byte as we want to keep the drawing code
+ * simple. Take a look at mdm166a_flush for the conversion.
+ */
+
+#define MDM166A_XSIZE 96
+#define MDM166A_YSIZE 16
+#define MDM166A_SCREENSIZE MDM166A_XSIZE*MDM166A_YSIZE
+#define MDM166A_PACKEDSIZE 96*2
+
+#define WIDTH 16
+#define HEIGHT 2
+#define CELLWIDTH 6
+#define CELLHEIGHT 8
+
+#define DEFAULT_CLOCK "no"
+
+#define CMD_PREFIX 0x1b /**< Command prefix, indicates a command follows */
+#define CMD_SETCLOCK 0x00 /**< Send current time to display */
+#define CMD_SETSYMBOL 0x30 /**< Enable or disable a symbol */
+#define CMD_SETDIMM 0x40 /**< Set the dimming level of the display */
+#define CMD_RESET 0x50 /**< Reset all configuration data to default and clear */
+#define CMD_SETRAM 0x60 /**< Set the actual graphics RAM offset for next data write */
+#define CMD_SETPIXEL 0x70 /**< Write pixel data to RAM of the display */
+
+
+/** private data for the \c mdm166a driver */
+typedef struct mdm166a_private_data {
+ HIDInterface *hid; /**< Pointer to HID interface */
+ int showClock; /**< What clock to show on close */
+ bool dimm; /**< Brightness level */
+ bool offDimm; /**< Brightness level on close */
+ unsigned char *framebuf; /**< Pointer to internal framebuffer */
+ int changed; /**< Indicator for framebuffer changes */
+ int last_output; /**< Icon states after last update */
+ char info[255]; /**< Pointer to driver description */
+} PrivateData;
+
+/* Vars 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 = "mdm166a_";
+
+/**
+ * Helper function for clock (conversion to BCD)
+ * \param x int_value
+ * \return BCD_value
+ */
+inline byte
+toBCD(int x)
+{
+ return (byte) (((x) / 10 * 16) + ((x) % 10));
+}
+
+/**
+ * Draws char z from fontmap to the framebuffer at position
+ * x,y. These are zero-based textmode positions.
+ * The Fontmap is stored in rows while the framebuffer is stored
+ * in columns, so we need a little conversion.
+ *
+ * \param drvthis Pointer to driver structure.
+ * \param x Horizontal character position (column).
+ * \param y Vertical character position (row).
+ * \param z Character that gets written.
+ */
+void
+drawchar2fb(Driver *drvthis, int x, int y, unsigned char z)
+{
+ int font_x, font_y;
+ PrivateData *p = drvthis->private_data;
+
+ if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT)
+ return;
+
+ x++;
+
+ for (font_y = 0; font_y < 8; font_y++) {
+ for (font_x = 5; font_x > -1; font_x--) {
+ if ((i2500vfd_fontmap[z][font_y] & 1 << font_x) == 1 << font_x)
+ p->framebuf[x * 6 - 1 - font_x + (y * 8 + font_y) * MDM166A_XSIZE] = 1;
+ else
+ p->framebuf[x * 6 - 1 - font_x + (y * 8 + font_y) * MDM166A_XSIZE] = 0;
+ }
+ }
+
+ p->changed = 1;
+}
+
+
+/**
+ * Initialize the driver.
+ * \param drvthis Pointer to driver structure.
+ * \retval 0 Success.
+ * \retval <0 Error.
+ */
+MODULE_EXPORT int
+mdm166a_init(Driver *drvthis)
+{
+ char clock[256] = DEFAULT_CLOCK;
+ PrivateData *p;
+ int const PATH_OUT[1] = {0xff7f0004};
+ HIDInterfaceMatcher matcher = {0x19c2, 0x6a11, NULL, NULL, 0};
+ hid_return ret;
+ char Cmd[5];
+
+ /* Allocate and store private data */
+ p = (PrivateData *) calloc(1, sizeof(PrivateData));
+ if (p == NULL)
+ return -1;
+ if (drvthis->store_private_ptr(drvthis, p))
+ return -1;
+ strcpy(p->info, "Targa USB Graphic Vacuum Fluorescent Display (mdm166a) driver v0.1 : 19c2:6a11");
+
+ /* Get clock setting from config */
+ strncpy(clock, drvthis->config_get_string(drvthis->name, "Clock", 0, DEFAULT_CLOCK), sizeof(clock));
+ clock[sizeof(clock) - 1] = '\0';
+ p->showClock = 0;
+ if (strcmp(clock, "small") == 0)
+ p->showClock = 1;
+ else if (strcmp(clock, "big") == 0)
+ p->showClock = 2;
+
+ /* Get dimming setting from config */
+ p->dimm = drvthis->config_get_bool(drvthis->name, "Dimming", 0, 0);
+ p->offDimm = drvthis->config_get_bool(drvthis->name, "OffDimming", 0, 0);
+
+ /* Silence libhid and libusb */
+ hid_set_debug(HID_DEBUG_NONE);
+ hid_set_debug_stream(0);
+ hid_set_usb_debug(0);
+
+ /*
+ * We use one central error exit point utilizing forward goto below!
+ */
+
+ /* Init HID device */
+ ret = hid_init();
+ if (ret != HID_RET_SUCCESS) {
+ report(RPT_ERR, "%s: hid_init failed: %s", drvthis->name, hid_strerror(ret));
+ goto error;
+ }
+
+ p->hid = hid_new_HIDInterface();
+ if (p->hid == 0) {
+ report(RPT_ERR, "%s: hid_new_HIDInterface() failed, out of memory?", drvthis->name);
+ goto error;
+ }
+
+ ret = hid_force_open(p->hid, 0, &matcher, 3);
+ if (ret != HID_RET_SUCCESS) {
+ report(RPT_ERR, "%s: hid_force_open failed: %s", drvthis->name, hid_strerror(ret));
+ goto error;
+ }
+
+ /* Allocate our framebuffer */
+ p->framebuf = (unsigned char *)malloc(MDM166A_SCREENSIZE + MDM166A_PACKEDSIZE + 1);
+ if (p->framebuf == NULL) {
+ report(RPT_ERR, "%s: unable to allocate framebuffer", drvthis->name);
+ goto error;
+ }
+
+ /* Reset and clear display */
+ Cmd[0] = 0x02;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = CMD_RESET;
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 3);
+ p->last_output = 0;
+
+ /* Set dimming */
+ Cmd[0] = 0x03;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = CMD_SETDIMM;
+ if (p->dimm)
+ Cmd[3] = 1;
+ else
+ Cmd[3] = 2;
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 4);
+
+ /* Clear internal screen */
+ mdm166a_clear(drvthis);
+
+ report(RPT_DEBUG, "%s: init() done", drvthis->name);
+ return 0;
+
+ /* Central error exit point */
+error:
+ mdm166a_close(drvthis);
+ return -1;
+}
+
+
+/**
+ * Set the clock mode and close the driver (do necessary clean-up).
+ * \param drvthis Pointer to driver structure.
+ */
+MODULE_EXPORT void
+mdm166a_close(Driver *drvthis)
+{
+ int const PATH_OUT[1] = {0xff7f0004};
+ char Cmd[5];
+ time_t tt; /* clock */
+ struct tm l; /* time */
+
+ PrivateData *p = drvthis->private_data;
+
+ if (p != NULL) {
+ if (p->hid != 0) {
+ /*
+ * XXX: I don't know if it is a good idea to talk to
+ * the display here as this function will be called
+ * on errors during init. p->hid may not be NULL even
+ * if the device could not be open!
+ */
+
+ /* Set clock */
+ if (p->showClock > 0) {
+ tt = time(NULL);
+ localtime_r(&tt, &l);
+ /* Set time */
+ Cmd[0] = 0x04;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = CMD_SETCLOCK;
+ Cmd[3] = toBCD(l.tm_min);
+ Cmd[4] = toBCD(l.tm_hour);
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 5);
+ /* Show it */
+ Cmd[0] = 0x03;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = p->showClock;
+ Cmd[3] = 0x01; /* 24 hour mode */
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 4);
+ }
+ /* Set dimming */
+ Cmd[0] = 0x03;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = CMD_SETDIMM;
+ if (p->offDimm)
+ Cmd[3] = 1;
+ else
+ Cmd[3] = 2;
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 4);
+
+ hid_close(p->hid);
+ hid_delete_HIDInterface(&p->hid);
+ p->hid = 0;
+ }
+ hid_cleanup();
+
+ if (p->framebuf)
+ free(p->framebuf);
+
+ free(p);
+ }
+ drvthis->store_private_ptr(drvthis, NULL);
+}
+
+
+/**
+ * Return the display width in characters.
+ * \param drvthis Pointer to driver structure.
+ * \return Number of characters the display is wide.
+ */
+MODULE_EXPORT int
+mdm166a_width(Driver *drvthis)
+{
+ return WIDTH;
+}
+
+
+/**
+ * Return the display height in characters.
+ * \param drvthis Pointer to driver structure.
+ * \return Number of characters the display is high.
+ */
+MODULE_EXPORT int
+mdm166a_height(Driver *drvthis)
+{
+ return 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
+mdm166a_cellwidth(Driver *drvthis)
+{
+ return CELLWIDTH;
+}
+
+
+/**
+ * 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
+mdm166a_cellheight(Driver *drvthis)
+{
+ return CELLHEIGHT;
+}
+
+
+/**
+ * Clear the screen.
+ * \param drvthis Pointer to driver structure.
+ */
+MODULE_EXPORT void
+mdm166a_clear(Driver *drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+
+ memset(p->framebuf, 0, MDM166A_SCREENSIZE);
+ p->changed = 1;
+}
+
+
+/**
+ * Flush data on screen to the VFD.
+ * \param drvthis Pointer to driver structure.
+ */
+MODULE_EXPORT void
+mdm166a_flush(Driver *drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ int const PATH_OUT[1] = {0xff7f0004};
+ char Cmd[64];
+ int packed_begin = MDM166A_SCREENSIZE;
+ int xpos = 0, ypos = 0, i, j;
+
+ if (!p->changed)
+ return;
+
+ memset(p->framebuf + packed_begin, 0, MDM166A_PACKEDSIZE);
+
+ /* Convert framebuffer */
+ for (xpos = 0; xpos < MDM166A_XSIZE; xpos++)
+ for (ypos = 0; ypos < MDM166A_YSIZE; ypos++)
+ if (p->framebuf[ypos * MDM166A_XSIZE + xpos])
+ p->framebuf[packed_begin + 2 * xpos + (ypos / 8)] |= (1 << (7 - (ypos % 8)));
+
+ /* Write data to display */
+ /* Set position (0,0) */
+ Cmd[0] = 0x03;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = CMD_SETRAM;
+ Cmd[3] = 0;
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 4);
+
+ /* Display accepts max. 64 bytes at a time -> Send 4 x 24 columns */
+ Cmd[0] = 51;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = CMD_SETPIXEL;
+ Cmd[3] = 48;
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 24; j++) {
+ Cmd[4 + 2 * j] = p->framebuf[packed_begin + i * 48 + 2 * j];
+ Cmd[5 + 2 * j] = p->framebuf[packed_begin + i * 48 + 2 * j + 1];
+ }
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 52);
+ }
+
+ p->changed = 0;
+}
+
+
+/**
+ * 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
+mdm166a_string(Driver *drvthis, int x, int y, const char string[])
+{
+ int i;
+ x--; /* Convert 1-based coords to 0-based */
+ y--;
+
+ for (i = 0; string[i]; i++)
+ drawchar2fb(drvthis, x + i, y, string[i]);
+}
+
+
+/**
+ * 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
+mdm166a_chr(Driver *drvthis, int x, int y, char c)
+{
+ y--; /* Convert 1-based coords to 0-based */
+ x--;
+ drawchar2fb(drvthis, x, y, c);
+}
+
+
+/**
+ * Define a custom character and write it to the VFD.
+ * \param drvthis Pointer to driver structure.
+ * \param n Custom character to define [0 - (p->customchars)].
+ * \param dat Array of 7(=cellheight) bytes, each representing a pixel row
+ * starting from the top to bottom. \n
+ * 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
+mdm166a_set_char(Driver *drvthis, int n, char *dat)
+{
+ int row, col;
+
+ if (n < 0 || n > 255)
+ return;
+ if (!dat)
+ return;
+
+ for (row = 0; row < CELLHEIGHT; row++) {
+ int i = 0;
+
+ for (col = 0; col < CELLWIDTH; col++)
+ i = (i << 1) | (dat[(row * CELLWIDTH) + col] > 0);
+
+ i2500vfd_fontmap[n][row] = i;
+ }
+}
+
+
+/**
+ * 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 pattern Options (currently unused).
+ */
+MODULE_EXPORT void
+mdm166a_vbar(Driver *drvthis, int x, int y, int len, int promille, int pattern)
+{
+ PrivateData *p = drvthis->private_data;
+ unsigned int offset;
+ int i, j, pixels;
+
+ debug(RPT_INFO, "%s: x=%i, y=%i, len=%i, promille=%i, options=%i",
+ __FUNCTION__, x, y, len, promille, pattern);
+
+ x--;
+ /* don't do y-- as we draw bottom up */
+
+ if (x < 0 || y < 1 || x >= WIDTH || y > HEIGHT || len > HEIGHT) {
+ return;
+ }
+
+ offset = x * CELLWIDTH + y * MDM166A_XSIZE * CELLHEIGHT;
+ pixels = len * CELLHEIGHT * promille / 1000;
+
+ for (i = 0; i < pixels; i++) {
+ for (j = 0; j < CELLWIDTH; j++) {
+ p->framebuf[offset + j] = 1;
+ }
+ /* go to next y-line */
+ offset -= MDM166A_XSIZE;
+ }
+
+ p->changed = 1;
+}
+
+
+/**
+ * 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 pattern Options (currently unused).
+ */
+MODULE_EXPORT void
+mdm166a_hbar(Driver *drvthis, int x, int y, int len, int promille, int pattern)
+{
+ PrivateData *p = drvthis->private_data;
+ unsigned int offset;
+ int i, j, pixels;
+
+ debug(RPT_INFO, "%s: x=%i, y=%i, len=%i, promille=%i, options=%i",
+ __FUNCTION__, x, y, len, promille, pattern);
+
+ x--;
+ y--;
+
+ if (y < 0 || y >= HEIGHT || x < 0 || len < 0 || x + len > WIDTH) {
+ return;
+ }
+
+ offset = 2 + x * CELLWIDTH + y * MDM166A_XSIZE * CELLHEIGHT;
+
+ /* calculate length of bar */
+ pixels = len * CELLWIDTH * promille / 1000;
+
+ for (i = 0; i < CELLHEIGHT - 1; i++) {
+ for (j = 0; j < pixels; j++) {
+ p->framebuf[offset + j] = 1;
+ }
+ /* go to next y-line */
+ offset += MDM166A_XSIZE;
+ }
+
+ p->changed = 1;
+}
+
+
+/**
+ * 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 symbolic value representing the icon.
+ * \retval 0 Icon has been successfully defined/written.
+ * \retval <0 Server core shall define/write the icon.
+ */
+MODULE_EXPORT int
+mdm166a_icon(Driver *drvthis, int x, int y, int icon)
+{
+ static char heart_open[] = {
+ 0, 1, 1, 1, 1, 1,
+ 0, 1, 0, 1, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 1,
+ 0, 1, 1, 0, 1, 1,
+ 0, 1, 1, 1, 1, 1};
+
+ static char heart_filled[] = {
+ 0, 1, 1, 1, 1, 1,
+ 0, 1, 0, 1, 0, 1,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 0,
+ 0, 1, 0, 1, 0, 1,
+ 0, 1, 1, 0, 1, 1,
+ 0, 1, 1, 1, 1, 1};
+
+ switch (icon) {
+ case ICON_BLOCK_FILLED:
+ mdm166a_chr(drvthis, x, y, 255);
+ break;
+ case ICON_HEART_FILLED:
+ mdm166a_set_char(drvthis, 0, heart_filled);
+ mdm166a_chr(drvthis, x, y, 0);
+ break;
+ case ICON_HEART_OPEN:
+ mdm166a_set_char(drvthis, 0, heart_open);
+ mdm166a_chr(drvthis, x, y, 0);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Turn display backlight on or off.
+ * This does not really toggle the backlight, but tries to handle it more
+ * intelligently: It sets the brightness to the value defined for the related
+ * backlight state.
+ *
+ * \param drvthis Pointer to driver structure.
+ * \param on New backlight status.
+ */
+MODULE_EXPORT void
+mdm166a_backlight(Driver *drvthis, int on)
+{
+ int const PATH_OUT[1] = {0xff7f0004};
+ char Cmd[5];
+ PrivateData *p = drvthis->private_data;
+ /* Set dimming */
+ Cmd[0] = 0x03;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = CMD_SETDIMM;
+ if ((p->dimm && (on == BACKLIGHT_ON)) || (p->offDimm && (on == BACKLIGHT_OFF)))
+ Cmd[3] = 1;
+ else
+ Cmd[3] = 2;
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 4);
+}
+
+
+/**
+ * Controls the custom icons.
+ *
+ * The bits of the \c on value map to icons as this (a bit set '1' means the
+ * icon is switched on, a bit set '0' means the icon is turned off):
+ * \verbatim
+ * Bit Funktion
+ * 0 Play
+ * 1 Pause
+ * 2 Record
+ * 3 Message
+ * 4 @
+ * 5 Mute
+ * 6 WLAN tower
+ * 7 Volume (the word)
+ * 8..12 Volume level bar (decimal 0..28)
+ * 13..14 WLAN strength (decimal 0..3)
+ * \endverbatim
+ *
+ * \param drvthis Pointer to driver structure.
+ * \param on Integer with bits representing LED states.
+ */
+MODULE_EXPORT void
+mdm166a_output(Driver *drvthis, int on)
+{
+ char const Icon[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0a};
+ PrivateData *p = drvthis->private_data;
+ int const PATH_OUT[1] = {0xff7f0004};
+ char Cmd[64];
+ int icons_changed = on ^ p->last_output;
+ int i, volume, wlan;
+
+ /*
+ * Icons, volume bar or WLAN indicator are only changed if different
+ * from previous call.
+ */
+
+ /* Set Icons. */
+ Cmd[0] = 0x04;
+ Cmd[1] = CMD_PREFIX;
+ Cmd[2] = CMD_SETSYMBOL;
+ for (i = 0; i < 8; i++) {
+ if (icons_changed & (1 << i)) {
+ Cmd[3] = Icon[i];
+ Cmd[4] = ((on & (1 << i)) < 1) ? 0 : 1;
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 5);
+ }
+ }
+
+ /*
+ * Volume (0..28). Note: Each bar element can individually be set to
+ * one of three states: off, dimmed or fully on.
+ */
+ volume = (on >> 8) & 0x1F;
+ if (volume != ((p->last_output >> 8) & 0x1F)) {
+ Cmd[0] = 56;
+ for (i = 0; i < 14; i++) {
+ Cmd[i * 4 + 1] = CMD_PREFIX;
+ Cmd[i * 4 + 2] = CMD_SETSYMBOL;
+ Cmd[i * 4 + 3] = 0x0B + i;
+ if (i < (volume / 2))
+ Cmd[i * 4 + 4] = 2;
+ else if (i == (volume / 2))
+ Cmd[i * 4 + 4] = volume % 2;
+ else
+ Cmd[i * 4 + 4] = 0;
+ }
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 57);
+ }
+
+ /* Wlan Strength (0..3) */
+ wlan = (on >> 13) & 0x03;
+ if (wlan != ((p->last_output >> 13) & 0x03)) {
+ Cmd[0] = 12;
+ for (i = 0; i < 3; i++) {
+ Cmd[i * 4 + 1] = CMD_PREFIX;
+ Cmd[i * 4 + 2] = CMD_SETSYMBOL;
+ Cmd[i * 4 + 3] = 0x07 + i;
+ Cmd[i * 4 + 4] = (i < wlan) ? 1 : 0;
+ }
+ hid_set_output_report(p->hid, PATH_OUT, sizeof(PATH_OUT), Cmd, 13);
+ }
+
+ p->last_output = on;
+}
+
+
+/**
+ * Provide general information about the LCD/VFD display/driver.
+ * \param drvthis Pointer to driver structure.
+ * \return Constant string with information.
+ */
+MODULE_EXPORT const char *
+mdm166a_get_info(Driver *drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ return p->info;
+}
diff --git a/server/drivers/mdm166a.h b/server/drivers/mdm166a.h
new file mode 100644
index 0000000..8c25988
--- /dev/null
+++ b/server/drivers/mdm166a.h
@@ -0,0 +1,26 @@
+#ifndef MDM166A_H
+#define MDM166A_H
+
+#include "lcd.h"
+
+MODULE_EXPORT int mdm166a_init (Driver *drvthis);
+MODULE_EXPORT void mdm166a_close (Driver *drvthis);
+MODULE_EXPORT int mdm166a_width (Driver *drvthis);
+MODULE_EXPORT int mdm166a_height (Driver *drvthis);
+MODULE_EXPORT int mdm166a_cellwidth (Driver *drvthis);
+MODULE_EXPORT int mdm166a_cellheight (Driver *drvthis);
+MODULE_EXPORT void mdm166a_clear (Driver *drvthis);
+MODULE_EXPORT void mdm166a_flush (Driver *drvthis);
+MODULE_EXPORT void mdm166a_string (Driver *drvthis, int x, int y, const char string[]);
+MODULE_EXPORT void mdm166a_chr (Driver *drvthis, int x, int y, char c);
+
+MODULE_EXPORT void mdm166a_vbar(Driver *drvthis, int x, int y, int len, int promille, int pattern);
+MODULE_EXPORT void mdm166a_hbar(Driver *drvthis, int x, int y, int len, int promille, int pattern);
+MODULE_EXPORT int mdm166a_icon (Driver *drvthis, int x, int y, int icon);
+
+MODULE_EXPORT void mdm166a_set_char (Driver *drvthis, int n, char *dat);
+MODULE_EXPORT void mdm166a_backlight (Driver *drvthis, int on);
+MODULE_EXPORT const char * mdm166a_get_info (Driver *drvthis);
+MODULE_EXPORT void mdm166a_output (Driver *drvthis, int on);
+
+#endif