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