New Driver: picolcdgraph (Mini-box.com picoLCD 256x64 Sideshow)
Laurence Withers <[email protected]> Sun, 26 Feb 2012 20:33:35 +0000
| Newsgroups | gmane.comp.sysutils.lcdproc |
|---|---|
| Message-ID | <[email protected]> |
Hi, I've recently completed a driver for the picoLCD 256x64 Sideshow display available from Mini-box.com: http://www.mini-box.com/picoLCD-256x64-Sideshow-CDROM-Bay This is actually a 256x64 graphical display. The driver uses a 6x8 font which I derived from the t6963 driver to give a 42x8 character display. It also supports very nice looking horizontal and vertical bars! A lot of the code was derived by looking at the existing picolcd driver (which could not be reused, as it is too different) and the lcd4linux driver for this display. I have of course preserved copyright/license information in submission. A lot of this work seems to come from Nicu Pavel who I have added to CC. I originally developed my driver against v0.5.3 which is I know bad practice, but it's what my company are currently using. The submitted patch below is against CVS and I have verified operation of the driver with the CVS version. That said, there are two post-0.5.3 changes which I think need to be considered by me: the (possible) switch to libusb-1.0, and the consolidation of the 5x8 font. For now, I'd like any feedback on this driver, and perhaps if all is well it could be added to CVS and the above issues addressed in the medium term. Many thanks, and bye for now, -- Laurence Withers, <[email protected]> http://www.lwithers.me.uk/ _______________________________________________ LCDproc mailing list [email protected] http://lists.omnipotent.net/mailman/listinfo/lcdproc
lcdproc-add-picolcdgraph.patch
(text/x-diff, 49 KB)
diff -ruN lcdproc.orig/acinclude.m4 lcdproc/acinclude.m4
--- lcdproc.orig/acinclude.m4 2012-02-18 15:24:43.000000000 +0000
+++ lcdproc/acinclude.m4 2012-02-26 19:26:22.016883157 +0000
@@ -13,16 +13,16 @@
[ icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,]
[ joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,]
[ ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,]
- [ picolcd,pyramid,sdeclcd,sed1330,sed1520,serialPOS,]
- [ serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,]
- [ t6963,text,tyan,ula200,vlsys_m428,xosd]
+ [ picolcd,picolcdgraph,pyramid,sdeclcd,sed1330,sed1520,]
+ [ serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,]
+ [ svga,t6963,text,tyan,ula200,vlsys_m428,xosd]
[ ]
[ 'all' compiles all drivers;]
[ 'all,!xxx,!yyy' de-selects previously selected drivers],
drivers="$enableval",
drivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,glk,lb216,lcdm001,MtxOrb,pyramid,text])
-allDrivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,pyramid,sdeclcd,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,t6963,text,tyan,ula200,vlsys_m428,xosd]
+allDrivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,picolcdgraph,pyramid,sdeclcd,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,t6963,text,tyan,ula200,vlsys_m428,xosd]
if test "$debug" = yes; then
allDrivers=["${allDrivers},debug"]
fi
@@ -390,6 +390,19 @@
AC_MSG_WARN([The picolcd driver needs the libusb library.])
fi
;;
+ picolcdgraph)
+ if test "$enable_libusb" = yes ; then
+ if test "$enable_libusb_1_0" = yes ; then
+ AC_MSG_WARN([The picolcdgraph requires libusb-0.1, not libusb-1.0.])
+ else
+ DRIVERS="$DRIVERS picolcdgraph${SO}"
+ actdrivers=["$actdrivers picolcdgraph"]
+ AC_MSG_RESULT([The picolcdgraph driver is using the libusb-0.1 library.])
+ fi
+ else
+ AC_MSG_WARN([The picolcdgraph driver needs the libusb library.])
+ fi
+ ;;
pyramid)
DRIVERS="$DRIVERS pyramid${SO}"
actdrivers=["$actdrivers pyramid"]
diff -ruN lcdproc.orig/docs/LCDd.8.in lcdproc/docs/LCDd.8.in
--- lcdproc.orig/docs/LCDd.8.in 2012-02-18 15:24:45.000000000 +0000
+++ lcdproc/docs/LCDd.8.in 2012-02-26 19:17:24.028883148 +0000
@@ -262,6 +262,9 @@
.B picolcd
Mini-box.com USB LCD (PicoLCD 20x4 & picoLCD 20x2)
.TP
+.B picolcdgraph
+Mini-box.com USB LCD (PicoLCD 256x64 graphical)
+.TP
.B pyramid
LCD displays from Pyramid (http://www.pyramid.de)
.TP
diff -ruN lcdproc.orig/docs/lcdproc-user/drivers/picolcd.docbook lcdproc/docs/lcdproc-user/drivers/picolcd.docbook
--- lcdproc.orig/docs/lcdproc-user/drivers/picolcd.docbook 2011-12-10 15:39:47.000000000 +0000
+++ lcdproc/docs/lcdproc-user/drivers/picolcd.docbook 2012-02-26 19:17:24.028883148 +0000
@@ -9,7 +9,8 @@
<title>Displays</title>
<para>
<ulink url="http://www.mini-box.com/">Mini-Box.com</ulink> offers two types of
- USB LCD displays:
+ textual USB LCD displays. There is also a 256x64 graphical display, that is
+ supported by the <code>picolcdgraph</code> driver.
</para>
<variablelist>
diff -ruN lcdproc.orig/docs/lcdproc-user/drivers/picolcdgraph.docbook lcdproc/docs/lcdproc-user/drivers/picolcdgraph.docbook
--- lcdproc.orig/docs/lcdproc-user/drivers/picolcdgraph.docbook 1970-01-01 00:00:00.000000000 +0000
+++ lcdproc/docs/lcdproc-user/drivers/picolcdgraph.docbook 2012-02-26 19:17:24.028883148 +0000
@@ -0,0 +1,151 @@
+<sect1 id="picolcdgraph">
+<title>The Mini-Box.com USB LCD picoLCD 256x64 graphical Driver</title>
+
+<para>
+ This section covers the installation and use of the Mini-Box USB LCD displays.
+</para>
+
+<sect2 id="picolcdgraph-displays">
+<title>Displays</title>
+<para>
+ <ulink url="http://www.mini-box.com/">Mini-Box.com</ulink> offers a 256×64
+ graphical LCD display, known as the picoLCD 256×64 Sideshow. It is an external
+ USB 2.0 full speed device that comes either as an OEM board for system
+ integrators or ready-mounted in a 5.25" CDROM bay.
+</para>
+
+<para>
+ The picoLCD 256×64 supports software-controlled backlight brightness, contrast
+ and has a small number of keys. The <application>LCDd</application> driver supports
+ icons, clean and continuous bar graphs, big numbers, and the keypad.
+</para>
+
+</sect2>
+
+
+<sect2 id="picolcdgraph-requirements">
+<title>Requirements</title>
+
+<para>
+ The driver is based on the
+ <ulink url="http://libusb.sourceforge.net/"><filename>libusb</filename></ulink>
+ USB library, which should make it work with Linux, the different BSD variants
+ as well as Darwin/MacOS X.
+
+<note>
+ <para>
+ When using a <filename>libusb</filename> based driver like
+ <code>picolcdgraph</code>, <application>LCDd</application>
+ needs to be started as root.
+ </para>
+</note>
+</para>
+
+<para>
+ On Linux, the only kernel module required is the USB host controller
+ driver (<filename>uhci_hcd</filename> on the M300) to fire up the USB bus
+ to which the LCD is attached.
+ For other operating systems, analogous requirements apply.
+</para>
+
+<para>
+ Lastly, for <filename>libusb</filename> to work correctly,
+ the <filename>usbfs</filename> file system must be mounted on
+ <filename>/proc/bus/usb</filename>, e.g. using the command
+ <code>mount -t usbfs usbfs /proc/bus/usb</code> or by your system's
+ default configuration.
+</para>
+</sect2>
+
+
+
+<sect2 id="picolcdgraph-config">
+<title>Configuration in LCDd.conf</title>
+
+<sect3 id="picolcdgraph-config-section">
+<title>[picolcdgraph]</title>
+
+<variablelist>
+
+<varlistentry>
+ <term>
+ <property>BrightnessOn</property> =
+ <parameter><replaceable>BRIGHTNESS</replaceable></parameter>
+ </term>
+ <listitem>
+ <para>
+ Set the backlight brightness value when the backlight is switched on.
+ Legal values are: <literal>0</literal> – <literal>1000</literal>.
+ If not given, it defaults to <literal>1000</literal>.
+ </para>
+ </listitem>
+</varlistentry>
+
+<varlistentry>
+ <term>
+ <property>BrightnessOn</property> =
+ <parameter><replaceable>BRIGHTNESS</replaceable></parameter>
+ </term>
+ <listitem>
+ <para>
+ Set the backlight brightness value when the backlight is switched off.
+ Legal values are: <literal>0</literal> – <literal>1000</literal>.
+ If not given, it defaults to <literal>0</literal>.
+ </para>
+ </listitem>
+</varlistentry>
+
+<varlistentry>
+ <term>
+ <property>Contrast</property> =
+ <parameter><replaceable>CONTRAST</replaceable></parameter>
+ </term>
+ <listitem><para>
+ Contrast: <literal>0</literal> – <literal>1000</literal>.
+ Default is <literal>850</literal>.
+ </para></listitem>
+</varlistentry>
+
+<varlistentry>
+ <term>
+ <property>KeyTimeout</property> =
+ <parameter><replaceable>DURATION</replaceable></parameter>
+ </term>
+ <listitem><para>
+ (Advanced) This value controls how long <application>LCDd</application> waits
+ for a key press when get_key() is called. The default is <literal>20</literal>
+ milliseconds. Lowering this value will make LCDd more responsive but also
+ causes LCDd to use more CPU time and, as the timeout grows shorter, key
+ presses become harder to detect.
+ </para></listitem>
+</varlistentry>
+</variablelist>
+
+</sect3>
+</sect2>
+
+
+<sect2 id="picolcdgraph-status">
+<title>picolcdgraph driver status</title>
+
+<para>
+ The biggest issue is likely to be the key timeout algorithm.
+</para>
+
+</sect2>
+
+
+<sect2 id="picolcdgraph-copy">
+<title>Copyright</title>
+
+<para>
+ The lcdproc picolcdgraph driver was written by Laurence Withers
+ ([email protected]) and uses code and information gleaned from the
+ <filename>picolcd</filename> driver as well as the
+ <filename>picoLCDgraph</filename> driver in
+ <application>lcd4linux</application>.
+</para>
+
+</sect2>
+
+</sect1>
diff -ruN lcdproc.orig/docs/lcdproc-user/drivers.docbook lcdproc/docs/lcdproc-user/drivers.docbook
--- lcdproc.orig/docs/lcdproc-user/drivers.docbook 2012-02-01 22:42:59.000000000 +0000
+++ lcdproc/docs/lcdproc-user/drivers.docbook 2012-02-26 19:17:24.028883148 +0000
@@ -40,6 +40,7 @@
&mx5000;
&NoritakeVFD;
&picolcd;
+&picolcdgraph;
&pylcd;
&sdeclcd;
&sed1330;
diff -ruN lcdproc.orig/docs/lcdproc-user/lcdproc-user.docbook lcdproc/docs/lcdproc-user/lcdproc-user.docbook
--- lcdproc.orig/docs/lcdproc-user/lcdproc-user.docbook 2012-02-01 22:42:59.000000000 +0000
+++ lcdproc/docs/lcdproc-user/lcdproc-user.docbook 2012-02-26 19:17:24.028883148 +0000
@@ -47,6 +47,7 @@
<!ENTITY mx5000 SYSTEM "drivers/mx5000.docbook">
<!ENTITY NoritakeVFD SYSTEM "drivers/NoritakeVFD.docbook">
<!ENTITY picolcd SYSTEM "drivers/picolcd.docbook">
+ <!ENTITY picolcdgraph SYSTEM "drivers/picolcgraph .docbook">
<!ENTITY pylcd SYSTEM "drivers/pylcd.docbook">
<!ENTITY sdeclcd SYSTEM "drivers/sdeclcd.docbook">
<!ENTITY sed1330 SYSTEM "drivers/sed1330.docbook">
diff -ruN lcdproc.orig/LCDd.conf lcdproc/LCDd.conf
--- lcdproc.orig/LCDd.conf 2012-02-18 15:24:43.000000000 +0000
+++ lcdproc/LCDd.conf 2012-02-26 19:20:03.180883436 +0000
@@ -48,8 +48,9 @@
# 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, sdeclcd, sed1330, sed1520, serialPOS, serialVFD, shuttleVFD, sli,
-# stv5730, svga, t6963, text, tyan, ula200, vlsys_m428, xosd
+# picolcdgraph, pyramid, sdeclcd, sed1330, sed1520, serialPOS,
+# serialVFD, shuttleVFD, sli, stv5730, svga, t6963, text, tyan, ula200,
+# vlsys_m428, xosd
Driver=curses
# Tells the driver to bind to the given interface
@@ -1004,6 +1005,27 @@
+## Mini-box.com picoLCD 256x64 graphical driver ##
+[picolcdgraph]
+
+# KeyTimeout is the time (in milliseconds) that LCDd spends waiting for a key
+# press before cycling through other duties. Higher values make LCDd use less
+# CPU time and make key presses more detectable. Lower values make LCDd more
+# responsive but a little prone to missing key presses. Trial and error showed
+# 20ms to be a balanced value.
+KeyTimeout=20
+
+# Set the backlight brightness when on [default: 1000; legal: 0 - 1000]
+BrightnessOn=1000
+
+# Set the backlight brightness when off [default: 0; legal: 0 - 1000]
+BrightnessOff=0
+
+# Set the initial contrast [default: 850; legal: 0 - 1000]
+Contrast=850
+
+
+
## Pyramid LCD driver ##
[pyramid]
diff -ruN lcdproc.orig/server/drivers/Makefile.am lcdproc/server/drivers/Makefile.am
--- lcdproc.orig/server/drivers/Makefile.am 2012-02-18 15:24:45.000000000 +0000
+++ lcdproc/server/drivers/Makefile.am 2012-02-26 19:24:47.708882969 +0000
@@ -22,7 +22,7 @@
## Keep the lists sorted!
pkglib_PROGRAMS = @DRIVERS@
-EXTRA_PROGRAMS = bayrad CFontz CFontzPacket curses debug CwLnx ea65 EyeboxOne g15 glcd glcdlib glk hd44780 icp_a106 imon imonlcd IOWarrior irman joy lb216 lcdm001 lcterm lirc lis MD8800 mdm166a ms6931 mtc_s16209x MtxOrb mx5000 NoritakeVFD picolcd pyramid sdeclcd sed1330 sed1520 serialPOS serialVFD shuttleVFD stv5730 SureElec svga t6963 text tyan sli ula200 vlsys_m428 xosd i2500vfd irtrans
+EXTRA_PROGRAMS = bayrad CFontz CFontzPacket curses debug CwLnx ea65 EyeboxOne g15 glcd glcdlib glk hd44780 icp_a106 imon imonlcd IOWarrior irman joy lb216 lcdm001 lcterm lirc lis MD8800 mdm166a ms6931 mtc_s16209x MtxOrb mx5000 NoritakeVFD picolcd picolcdgraph pyramid sdeclcd sed1330 sed1520 serialPOS serialVFD shuttleVFD stv5730 SureElec svga t6963 text tyan sli ula200 vlsys_m428 xosd i2500vfd irtrans
noinst_LIBRARIES = libLCD.a libbignum.a
g15_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
@@ -33,6 +33,7 @@
lis_CFLAGS = @LIBUSB_CFLAGS@ @LIBFTDI_CFLAGS@ $(AM_CFLAGS)
mdm166a_CFLAGS = @LIBHID_CFLAGS@ $(AM_CFLAGS)
picolcd_CFLAGS = @LIBUSB_CFLAGS@ @LIBUSB_1_0_CFLAGS@ $(AM_CFLAGS)
+picolcdgraph_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
shuttleVFD_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
ula200_CFLAGS = @LIBFTDI_CFLAGS@ $(AM_CFLAGS)
xosd_CFLAGS = @LIBXOSD_CFLAGS@ $(AM_CFLAGS)
@@ -64,6 +65,7 @@
mx5000_LDADD = @LIBMX5000@
NoritakeVFD_LDADD = libbignum.a
picolcd_LDADD = @LIBUSB_LIBS@ @LIBUSB_1_0_LIBS@ libLCD.a libbignum.a
+picolcdgraph_LDADD = @LIBUSB_LIBS@ libLCD.a libbignum.a
pyramid_LDADD = libLCD.a libbignum.a
sdeclcd_LDADD = libLCD.a libbignum.a
serialPOS_LDADD = libbignum.a
@@ -116,6 +118,7 @@
mx5000_SOURCES = lcd.h mx5000.c mx5000.h report.h
NoritakeVFD_SOURCES = lcd.h lcd_lib.h NoritakeVFD.c NoritakeVFD.h report.h adv_bignum.h
picolcd_SOURCES = lcd.h picolcd.h picolcd.c report.h
+picolcdgraph_SOURCES = lcd.h picolcdgraph.h picolcdgraph.c report.h
pyramid_SOURCES = lcd.h pylcd.c pylcd.h
sdeclcd_SOURCES = lcd.h sdeclcd.h sdeclcd.c lcd_lib.h report.h adv_bignum.h port.h lpt-port.h timing.h
sed1330_SOURCES = lcd.h sed1330.h sed1330.c port.h lpt-port.h timing.h report.h
diff -ruN lcdproc.orig/server/drivers/picolcdgraph.c lcdproc/server/drivers/picolcdgraph.c
--- lcdproc.orig/server/drivers/picolcdgraph.c 1970-01-01 00:00:00.000000000 +0000
+++ lcdproc/server/drivers/picolcdgraph.c 2012-02-26 19:17:24.032883041 +0000
@@ -0,0 +1,1060 @@
+/** \file server/drivers/picolcdgraph.c
+ *
+ * This uses libusb to talk to a picoLCD 256×64 graphic display, using a 6×8
+ * font, giving 42×8 characters. The picoLCD may be purchased from Mini-Box.com
+ * here: http://www.mini-box.com/picoLCD-256x64-Sideshow-CDROM-Bay .
+ *
+ * This driver implements a frame buffer and has an embedded 6×8 font which
+ * it draws onto the frame buffer. It supports a large set of icons as well
+ * as bignum support (through custom characters), clean vertical and horizontal
+ * bars with pixel-level scaling and keypad interaction.
+ *
+ * As only 252 pixels (42×6) are required for character text, the leftmost and
+ * rightmost pixels are set to draw thin horizontal bars. This was originally a
+ * verification while writing the driver but the effect is quite pleasing.
+ *
+ * \todo It might be possible to keep 'dirty' flags to only redraw those parts
+ * of the framebuffer that really need it, but performance seems great anyway.
+ *
+ * \todo Keypress polling is a bit iffy. Increasing the timeout makes it more
+ * likely a keypress will be picked up, but makes the display much more jerky.
+ * Decreasing it (or setting it to -1, for non-blocking) makes it unlikely
+ * keypresses will be picked up. This may be a limitation of the blocking
+ * nature of the libusb-0.1 API? The default timeout has been chosen to work
+ * effectively on my test system, but may need to be tuned.
+ */
+
+/*-
+ * Copyright: ©2012, Laurence Withers <[email protected]>
+ * Released under the GNU GPLv2.
+ *
+ * This driver was written with reference to, and borrowing some code from, the
+ * following drivers:
+ *
+ * lcdproc/server/drivers/picolcd.c
+ * Original license: GPLv2
+ * Original copyright: ©2007 NitroSecurity, Inc. <[email protected]>
+ * ©2007–2008 Peter Marschall
+ * ©2007–2008 Mini-Box.com, Nicu Pavel <[email protected]>
+ * ©2008 Jack Cleaver
+ *
+ * lcd4linux/drv_picoLCDGraphic.c
+ * Original license: GPLv2
+ * Original copyright: ©2005 Michael Reinelt <[email protected]>
+ * ©2005, 2006, 2007, The LCD4Linux Team
+ * <[email protected]>
+ * ©2009 Nicu Pavel, Mini-Box.com <[email protected]>
+ *
+ * The driver also uses font data from:
+ * lcdproc/server/drivers/t6963_font.h
+ * Original license: GPLv2
+ * Original copyright: ©2001 Manuel Stahl <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <usb.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "lcd.h"
+#include "report.h"
+#include "picolcdgraph.h"
+
+
+/* Constants */
+enum {
+ /** Used to match the device on the USB bus. */
+ USB_VENDOR_ID = 0x04d8,
+ /** Used to match the device on the USB bus. */
+ USB_PRODUCT_ID = 0xc002,
+
+ /** Endpoint for writing commands. */
+ PICOLCD_ENDPOINT_OUT = USB_ENDPOINT_OUT + 1,
+ /** Timeout (in ms) for writing a command. */
+ PICOLCD_WRITE_TIMEOUT = 1000,
+ /** Endpoint for polling keyboard. */
+ PICOLCD_ENDPOINT_IN = USB_ENDPOINT_IN + 1,
+
+ /** Width of a character, in pixels. */
+ CELL_WIDTH = 6,
+ /** Height of a character, in pixels. */
+ CELL_HEIGHT = 8,
+ /** Width of the entire display, in pixels. */
+ DISPLAY_WIDTH_PIXELS = 256,
+ /** Height of the entire display, in pixels. */
+ DISPLAY_HEIGHT_PIXELS = 64,
+ /** Width of the display, in characters. */
+ DISPLAY_WIDTH_CHARS = 42,
+ /** Height of the display, in characters. */
+ DISPLAY_HEIGHT_CHARS = 8,
+
+ /** Number of custom characters we support. */
+ CUSTOM_CHARS_NUM = 32,
+
+ /** The default contrast setting if not specified in the config file. */
+ DEFAULT_CONTRAST = 825,
+ /** Scales lcdproc's 0–1000 to a number the hardware supports. */
+ CONTRAST_SCALE = 255,
+ /** The default brightness setting (backlight on) if not specified. */
+ DEFAULT_BRIGHTNESS_ON = 1000,
+ /** The default brightness setting (backlight off) if not specified. */
+ DEFAULT_BRIGHTNESS_OFF = 0,
+ /** Scales lcdproc's 0–1000 into a number the hardware supports. */
+ BRIGHTNESS_SCALE = 200,
+ /** Key scan timeout (in ms) if not specified. */
+ DEFAULT_KEY_TIMEOUT = 20,
+};
+
+
+/** Get frame buffer address for character.
+ * Convert 1-based (x,y)/chars into frame buffer position; +2 so that we leave
+ * 2 pixels at the left for a bar.
+ */
+#define chr_fbpos(x, y) \
+ ( 2 + ((x) - 1) * CELL_WIDTH + ((y) - 1) * DISPLAY_WIDTH_PIXELS )
+
+
+/** picoLCDgraph driver's private data.
+ * Mainly holds the USB device handle and the framebuffer, with a couple of
+ * variables to keep track of certain state.
+ */
+typedef struct picolcdgraph_data {
+ /** USB device handle. */
+ usb_dev_handle* lcd;
+
+ /** Framebuffer.
+ * The framebuffer is laid out to support both the hardware requirements
+ * and efficient writing of characters. Each byte in the buffer
+ * represents a column of 8 pixels (the least significant bit being at
+ * the top). The framebuffer is then in row-major layout, with each row
+ * comprising columns of 8 pixels:
+ * <pre>
+ * 0x000 0x001 0x002 … 0x0FE 0x0FF
+ * 0x100 0x101 0x102 … 0x1FE 0x1FF
+ * …
+ * 0x700 0x701 0x702 … 0x7FE 0x7FF
+ * </pre>
+ * Where the byte offset \c 0xRCC refers to row \c (R*8) and column
+ * \c CC.
+ *
+ * This means that drawing a character is simply a case of copying in
+ * six bytes. See the macro \ref chr_fbpos for the resulting address
+ * conversion.
+ *
+ * The hardware is actually organised as four display controllers, each
+ * with a chip select, and each responsible for a 64×64 portion of the
+ * display. The smallest write we can do is actually a 64×8 section,
+ * with each byte corresponding to an 8-pixel column (conveniently
+ * matching our cell height).
+ */
+ char framebuf[DISPLAY_WIDTH_PIXELS * DISPLAY_HEIGHT_PIXELS / 8];
+
+ /** Contrast shadow register.
+ * We can't query the hardware, but maintain a shadow register in ‰.
+ */
+ int contrast;
+
+ /** Contrast brightness register (on state).
+ * We can't query the hardware, but maintain a shadow register in ‰.
+ */
+ int brightness_on;
+
+ /** Contrast brightness register (off state).
+ * We can't query the hardware, but maintain a shadow register in ‰.
+ */
+ int brightness_off;
+
+ /** Key timeout in milliseconds. */
+ int key_timeout;
+
+ /** Keeps track of whether bignum set its chars up yet or not. */
+ int bignum_ready;
+}PrivateData;
+
+
+/* Font data.
+ * This is an array of characters (each character is a simple char[6]
+ * array). It is indexed either by custom character number (0–31), ASCII
+ * code (32–126) or icon number.
+ *
+ * The ASCII character glyphs were transformed from t6963_font.h with
+ * the following program:
+
+#include <stdio.h>
+#include "t6963_font.h"
+
+int
+main(void)
+{
+ int ch, col, row, x;
+ unsigned char* data;
+
+ fputs("static unsigned char picoLCDgraph_font[][6] = {\n", stdout);
+ for (ch = 32; ch < 128; ++ch) {
+ printf("\t[%d] = { ", ch);
+ data = fontdata_6x8 + (ch * 48);
+
+ for (col = 0; col < 6; ++col) {
+ x = 0;
+ for (row = 0; row < 8; ++row) {
+ x |= data[row * 6 + col] << row;
+ }
+
+ if (col) fputs(", ", stdout);
+ printf("0x%02X", x);
+ }
+
+ printf(" }, /""* ASCII '%c' *""/\n", ch);
+ }
+ fputs("};\n", stdout);
+
+ return 0;
+}
+
+ */
+static unsigned char fontdata[][6] = {
+ [32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, /* ASCII ' ' */
+ [33] = { 0x00, 0x00, 0x06, 0x5F, 0x06, 0x00, }, /* ASCII '!' */
+ [34] = { 0x00, 0x07, 0x03, 0x00, 0x07, 0x03, }, /* ASCII '"' */
+ [35] = { 0x00, 0x24, 0x7E, 0x24, 0x7E, 0x24, }, /* ASCII '#' */
+ [36] = { 0x00, 0x24, 0x2B, 0x6A, 0x12, 0x00, }, /* ASCII '$' */
+ [37] = { 0x00, 0x63, 0x13, 0x08, 0x64, 0x63, }, /* ASCII '%' */
+ [38] = { 0x00, 0x36, 0x49, 0x56, 0x20, 0x50, }, /* ASCII '&' */
+ [39] = { 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, }, /* ASCII ''' */
+ [40] = { 0x00, 0x00, 0x3E, 0x41, 0x00, 0x00, }, /* ASCII '(' */
+ [41] = { 0x00, 0x00, 0x41, 0x3E, 0x00, 0x00, }, /* ASCII ')' */
+ [42] = { 0x00, 0x08, 0x3E, 0x1C, 0x3E, 0x08, }, /* ASCII '*' */
+ [43] = { 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, }, /* ASCII '+' */
+ [44] = { 0x00, 0x00, 0xE0, 0x60, 0x00, 0x00, }, /* ASCII ',' */
+ [45] = { 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, }, /* ASCII '-' */
+ [46] = { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, }, /* ASCII '.' */
+ [47] = { 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, }, /* ASCII '/' */
+ [48] = { 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, }, /* ASCII '0' */
+ [49] = { 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, }, /* ASCII '1' */
+ [50] = { 0x00, 0x62, 0x51, 0x49, 0x49, 0x46, }, /* ASCII '2' */
+ [51] = { 0x00, 0x22, 0x49, 0x49, 0x49, 0x36, }, /* ASCII '3' */
+ [52] = { 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, }, /* ASCII '4' */
+ [53] = { 0x00, 0x2F, 0x49, 0x49, 0x49, 0x31, }, /* ASCII '5' */
+ [54] = { 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, }, /* ASCII '6' */
+ [55] = { 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, }, /* ASCII '7' */
+ [56] = { 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, }, /* ASCII '8' */
+ [57] = { 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, }, /* ASCII '9' */
+ [58] = { 0x00, 0x00, 0x6C, 0x6C, 0x00, 0x00, }, /* ASCII ':' */
+ [59] = { 0x00, 0x00, 0xEC, 0x6C, 0x00, 0x00, }, /* ASCII ';' */
+ [60] = { 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, }, /* ASCII '<' */
+ [61] = { 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, }, /* ASCII '=' */
+ [62] = { 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, }, /* ASCII '>' */
+ [63] = { 0x00, 0x02, 0x01, 0x59, 0x09, 0x06, }, /* ASCII '?' */
+ [64] = { 0x00, 0x3E, 0x41, 0x5D, 0x55, 0x1E, }, /* ASCII '@' */
+ [65] = { 0x00, 0x7E, 0x11, 0x11, 0x11, 0x7E, }, /* ASCII 'A' */
+ [66] = { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, }, /* ASCII 'B' */
+ [67] = { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, }, /* ASCII 'C' */
+ [68] = { 0x00, 0x7F, 0x41, 0x41, 0x41, 0x3E, }, /* ASCII 'D' */
+ [69] = { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, }, /* ASCII 'E' */
+ [70] = { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, }, /* ASCII 'F' */
+ [71] = { 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, }, /* ASCII 'G' */
+ [72] = { 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, }, /* ASCII 'H' */
+ [73] = { 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, }, /* ASCII 'I' */
+ [74] = { 0x00, 0x30, 0x40, 0x40, 0x40, 0x3F, }, /* ASCII 'J' */
+ [75] = { 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, }, /* ASCII 'K' */
+ [76] = { 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, }, /* ASCII 'L' */
+ [77] = { 0x00, 0x7F, 0x02, 0x04, 0x02, 0x7F, }, /* ASCII 'M' */
+ [78] = { 0x00, 0x7F, 0x02, 0x04, 0x08, 0x7F, }, /* ASCII 'N' */
+ [79] = { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, }, /* ASCII 'O' */
+ [80] = { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, }, /* ASCII 'P' */
+ [81] = { 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, }, /* ASCII 'Q' */
+ [82] = { 0x00, 0x7F, 0x09, 0x09, 0x19, 0x66, }, /* ASCII 'R' */
+ [83] = { 0x00, 0x26, 0x49, 0x49, 0x49, 0x32, }, /* ASCII 'S' */
+ [84] = { 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, }, /* ASCII 'T' */
+ [85] = { 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, }, /* ASCII 'U' */
+ [86] = { 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, }, /* ASCII 'V' */
+ [87] = { 0x00, 0x3F, 0x40, 0x3C, 0x40, 0x3F, }, /* ASCII 'W' */
+ [88] = { 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, }, /* ASCII 'X' */
+ [89] = { 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, }, /* ASCII 'Y' */
+ [90] = { 0x00, 0x71, 0x49, 0x45, 0x43, 0x00, }, /* ASCII 'Z' */
+ [91] = { 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, }, /* ASCII '[' */
+ [92] = { 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, }, /* ASCII '\' */
+ [93] = { 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, }, /* ASCII ']' */
+ [94] = { 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, }, /* ASCII '^' */
+ [95] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }, /* ASCII '_' */
+ [96] = { 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, }, /* ASCII '`' */
+ [97] = { 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, }, /* ASCII 'a' */
+ [98] = { 0x00, 0x7F, 0x44, 0x44, 0x44, 0x38, }, /* ASCII 'b' */
+ [99] = { 0x00, 0x38, 0x44, 0x44, 0x44, 0x28, }, /* ASCII 'c' */
+ [100] = { 0x00, 0x38, 0x44, 0x44, 0x44, 0x7F, }, /* ASCII 'd' */
+ [101] = { 0x00, 0x38, 0x54, 0x54, 0x54, 0x08, }, /* ASCII 'e' */
+ [102] = { 0x00, 0x08, 0x7E, 0x09, 0x09, 0x00, }, /* ASCII 'f' */
+ [103] = { 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, }, /* ASCII 'g' */
+ [104] = { 0x00, 0x7F, 0x04, 0x04, 0x78, 0x00, }, /* ASCII 'h' */
+ [105] = { 0x00, 0x00, 0x00, 0x7D, 0x40, 0x00, }, /* ASCII 'i' */
+ [106] = { 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, }, /* ASCII 'j' */
+ [107] = { 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, }, /* ASCII 'k' */
+ [108] = { 0x00, 0x00, 0x00, 0x7F, 0x40, 0x00, }, /* ASCII 'l' */
+ [109] = { 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, }, /* ASCII 'm' */
+ [110] = { 0x00, 0x7C, 0x04, 0x04, 0x78, 0x00, }, /* ASCII 'n' */
+ [111] = { 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, }, /* ASCII 'o' */
+ [112] = { 0x00, 0xFC, 0x44, 0x44, 0x44, 0x38, }, /* ASCII 'p' */
+ [113] = { 0x00, 0x38, 0x44, 0x44, 0x44, 0xFC, }, /* ASCII 'q' */
+ [114] = { 0x00, 0x44, 0x78, 0x44, 0x04, 0x08, }, /* ASCII 'r' */
+ [115] = { 0x00, 0x08, 0x54, 0x54, 0x54, 0x20, }, /* ASCII 's' */
+ [116] = { 0x00, 0x04, 0x3E, 0x44, 0x24, 0x00, }, /* ASCII 't' */
+ [117] = { 0x00, 0x3C, 0x40, 0x20, 0x7C, 0x00, }, /* ASCII 'u' */
+ [118] = { 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, }, /* ASCII 'v' */
+ [119] = { 0x00, 0x3C, 0x60, 0x30, 0x60, 0x3C, }, /* ASCII 'w' */
+ [120] = { 0x00, 0x6C, 0x10, 0x10, 0x6C, 0x00, }, /* ASCII 'x' */
+ [121] = { 0x00, 0x9C, 0xA0, 0x60, 0x3C, 0x00, }, /* ASCII 'y' */
+ [122] = { 0x00, 0x64, 0x54, 0x54, 0x4C, 0x00, }, /* ASCII 'z' */
+ [123] = { 0x00, 0x08, 0x3E, 0x41, 0x41, 0x00, }, /* ASCII '{' */
+ [124] = { 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, }, /* ASCII '|' */
+ [125] = { 0x00, 0x00, 0x41, 0x41, 0x3E, 0x08, }, /* ASCII '}' */
+ [126] = { 0x00, 0x02, 0x01, 0x02, 0x01, 0x00, }, /* ASCII '~' */
+ [ICON_BLOCK_FILLED] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ },
+ [ICON_HEART_OPEN] = {
+ /* ......
+ * ..X.X.
+ * .X.X.X
+ * .X...X
+ * ..X.X.
+ * ...X..
+ * ......
+ * ......
+ */
+ 0x00, 0x06, 0x12, 0x24, 0x12, 0x06,
+ },
+ [ICON_HEART_FILLED] = {
+ /* ......
+ * ..X.X.
+ * .XXXXX
+ * .XXXXX
+ * ..XXX.
+ * ...X..
+ * ......
+ * ......
+ */
+ 0x00, 0x06, 0x1E, 0x3C, 0x1E, 0x06,
+ },
+ [ICON_ARROW_UP] = {
+ /* ......
+ * ...X..
+ * ..XXX.
+ * .X.X.X
+ * ...X..
+ * ...X..
+ * ...X..
+ * ......
+ */
+ 0x00, 0x08, 0x04, 0x7E, 0x04, 0x08,
+ },
+ [ICON_ARROW_DOWN] = {
+ /* ......
+ * ...X..
+ * ...X..
+ * ...X..
+ * .X.X.X
+ * ..XXX.
+ * ...X..
+ * ......
+ */
+ 0x00, 0x10, 0x20, 0x7E, 0x20, 0x10,
+ },
+ [ICON_ARROW_LEFT] = {
+ /* ......
+ * ...X..
+ * ....X.
+ * .XXXXX
+ * ....X.
+ * ...X..
+ * ......
+ * ......
+ */
+ 0x00, 0x08, 0x08, 0x2A, 0x1C, 0x08,
+ },
+ [ICON_ARROW_RIGHT] = {
+ /* ......
+ * ...X..
+ * ..X...
+ * .XXXXX
+ * ..X...
+ * ...X..
+ * ......
+ * ......
+ */
+ 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08,
+ },
+ [ICON_CHECKBOX_OFF] = {
+ /* ......
+ * .XXXXX
+ * .X...X
+ * .X...X
+ * .X...X
+ * .XXXXX
+ * ......
+ * ......
+ */
+ 0x00, 0x3E, 0x22, 0x22, 0x22, 0x3E,
+ },
+ [ICON_CHECKBOX_ON] = {
+ /* ......
+ * .XXXXX
+ * .XX.XX
+ * .X.X.X
+ * .XX.XX
+ * .XXXXX
+ * ......
+ * ......
+ */
+ 0x00, 0x3E, 0x36, 0x2A, 0x36, 0x3E,
+ },
+ [ICON_CHECKBOX_GRAY] = {
+ /* ......
+ * .X.X.X
+ * ......
+ * .X...X
+ * ......
+ * .X.X.X
+ * ......
+ * ......
+ */
+ 0x00, 0x2A, 0x00, 0x22, 0x00, 0x2A,
+ },
+ [ICON_SELECTOR_AT_LEFT] = {
+ /* inverse of ASCII '[' */
+ 0xFF, 0xFF, 0x80, 0xBE, 0xBE, 0xFF,
+ },
+ [ICON_SELECTOR_AT_RIGHT] = {
+ /* inverse of ASCII ']' */
+ 0xFF, 0xFF, 0xBE, 0xBE, 0x80, 0xFF,
+ },
+ [ICON_ELLIPSIS] = {
+ /* ......
+ * ......
+ * ......
+ * ......
+ * ......
+ * .X.X.X
+ * .X.X.X
+ * ......
+ */
+ 0x00, 0x60, 0x00, 0x60, 0x00, 0x60,
+ },
+};
+
+
+/** Write command to USB device.
+ * \param lcd USB device handle.
+ * \param data Command data to write.
+ * \param size Number of bytes to write.
+ */
+static void
+write_command(usb_dev_handle *lcd, const char *data, int size)
+{
+ usb_interrupt_write(lcd, PICOLCD_ENDPOINT_OUT, (char*)data, size,
+ PICOLCD_WRITE_TIMEOUT);
+}
+
+
+/** Write character to framebuffer.
+ * \param drvthis Driver state.
+ * \param x Text column (1-based, 1=left).
+ * \param y Text column (1-based, 1=top).
+ * \param c Character to write. Index into font.
+ *
+ * Writes a character at the given \a x, \a y position (checking it is within
+ * bounds first). The character \a c is any index into the font array, so it may
+ * be a custom char or icon as well as an ASCII char.
+ */
+static void
+do_chr(Driver *drvthis, int x, int y, int c)
+{
+ PrivateData *p = drvthis->private_data;
+
+ if (x < 1 || x > DISPLAY_WIDTH_CHARS
+ || y < 1 || y > DISPLAY_HEIGHT_CHARS)
+ {
+ return;
+ }
+
+ memcpy(p->framebuf + chr_fbpos(x, y), fontdata[c], CELL_WIDTH);
+}
+
+
+/* lcd_logical_driver mandatory functions */
+
+/** Initialize the driver.
+ * \param drvthis Pointer to driver structure.
+ * \retval 0 Success.
+ * \retval -1 Error.
+ *
+ * Scans the USB bus looking for a device with \ref USB_VENDOR_ID and
+ * \ref USB_PRODUCT_ID. It will disassociate the HID driver (if bound, and
+ * if the function is available), create an empty framebuffer, and set the
+ * initial backlight/contrast states as per the configuration file (or the
+ * defaults, if not configured).
+ *
+ * This routine is almost exactly the same as the picoLCD driver's, so if the
+ * original has some bugfixes, they should also be applied here.
+ */
+MODULE_EXPORT int
+picolcdgraph_init(Driver *drvthis)
+{
+ PrivateData *p;
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ p = malloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+
+ if (drvthis->store_private_ptr(drvthis, p))
+ return -1;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+ p->lcd = NULL;
+
+ report(RPT_INFO, "%s: looking for device", drvthis->name);
+ for (bus = usb_get_busses(); bus != NULL; bus = bus->next) {
+ for (dev = bus->devices; dev != NULL; dev = dev->next) {
+ if ((dev->descriptor.idVendor == USB_VENDOR_ID) &&
+ (dev->descriptor.idProduct == USB_PRODUCT_ID)) {
+ report(RPT_INFO, "%s: found device on bus %s "
+ "(device %s)", drvthis->name,
+ bus->dirname, dev->filename);
+
+ p->lcd = usb_open(dev);
+ goto done;
+ }
+ }
+ }
+ done:
+
+ if (!p->lcd) {
+ report(RPT_ERR, "%s: no device found", drvthis->name);
+ return -1;
+ }
+
+#ifdef LIBUSB_HAS_GET_DRIVER_NP
+ char driver[1024];
+
+ if (usb_get_driver_np(p->lcd, 0, driver, sizeof(driver)) == 0) {
+ report(RPT_WARNING, "%s: interface 0 already claimed by '%s'"
+ " - detaching", drvthis->name, driver);
+#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
+ if ((usb_detach_kernel_driver_np(p->lcd, 0) < 0)) {
+ report(RPT_ERR, "%s: unable to detach %s driver",
+ drvthis->name, driver);
+ }
+#endif
+ }
+#endif
+ if (usb_claim_interface(p->lcd, 0) < 0) {
+ report(RPT_ERR, "%s: cannot claim interface!", drvthis->name);
+ usb_close(p->lcd);
+ return -1;
+ }
+ usleep(100);
+ if (usb_set_altinterface(p->lcd, 0) < 0) {
+ report(RPT_WARNING, "%s: unable to set alternate configuration",
+ drvthis->name);
+ }
+
+ p->contrast = drvthis->config_get_int(drvthis->name,
+ "Contrast", 0, DEFAULT_CONTRAST);
+ p->brightness_on = drvthis->config_get_int(drvthis->name,
+ "BrightnessOn", 0, DEFAULT_BRIGHTNESS_ON);
+ p->brightness_off = drvthis->config_get_int(drvthis->name,
+ "BrightnessOff", 0, DEFAULT_BRIGHTNESS_OFF);
+ p->key_timeout = drvthis->config_get_int(drvthis->name,
+ "KeyTimeout", 0, DEFAULT_KEY_TIMEOUT);
+
+ picolcdgraph_clear(drvthis);
+ picolcdgraph_backlight(drvthis,
+ drvthis->config_get_bool(drvthis->name, "Backlight", 0, 1));
+ picolcdgraph_set_contrast(drvthis, p->contrast);
+
+ report(RPT_INFO, "%s: init complete", drvthis->name);
+
+ return 0;
+}
+
+
+/** Close the driver.
+ * \param drvthis Pointer to driver structure.
+ */
+MODULE_EXPORT void
+picolcdgraph_close(Driver *drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+
+ if (p) {
+ usb_release_interface(p->lcd, 0);
+ usb_close(p->lcd);
+ free(p);
+ }
+}
+
+
+/* lcd_logical_driver Essential output functions */
+
+/** Return the display width in characters.
+ * \param drvthis Pointer to driver structure.
+ * \returns Number of characters the display is wide.
+ */
+MODULE_EXPORT int
+picolcdgraph_width(Driver *drvthis)
+{
+ return DISPLAY_WIDTH_CHARS;
+}
+
+
+/** Return the display height in characters.
+ * \param drvthis Pointer to driver structure.
+ * \returns Number of characters the display is high.
+ */
+MODULE_EXPORT int
+picolcdgraph_height(Driver *drvthis)
+{
+ return DISPLAY_HEIGHT_CHARS;
+}
+
+
+/** Clear the screen.
+ * \param drvthis Pointer to driver structure.
+ *
+ * Clears the framebuffer, leaving thin bars in the left- and right-most pixels
+ * (which are otherwise unused).
+ */
+MODULE_EXPORT void
+picolcdgraph_clear(Driver *drvthis)
+{
+ int i;
+ PrivateData *p = drvthis->private_data;
+
+ memset(p->framebuf, 0, sizeof(p->framebuf));
+
+ /* draw vertical bars in the unused left- and right-most pixels */
+ for (i = 0; i < DISPLAY_HEIGHT_CHARS; ++i) {
+ p->framebuf[i * DISPLAY_WIDTH_PIXELS + 0] = 0xFF;
+ p->framebuf[(i + 1) * DISPLAY_WIDTH_PIXELS - 1] = 0xFF;
+ }
+}
+
+
+/** Flush data on screen to the display.
+ * \param drvthis Pointer to driver structure.
+ *
+ * This routine is derived from \c lcd4linux/drv_picoLCDGraphic.c
+ * \c drv_pLG_update_img() . It is responsible for actually writing data via USB
+ * to the display.
+ */
+MODULE_EXPORT void
+picolcdgraph_flush(Driver *drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ int cs, line, bufpos;
+ char cmd1[44] = {
+ 0x96, /* write command and data */
+ 0xFF, /* chip select */
+ 0x02,
+ 0x00,
+ 0x00,
+ 0xFF, /* row offset */
+ 0x00,
+ 0x00,
+ 0x40,
+ 0x00,
+ 0x00,
+ 32, /* number of data bytes */
+ }, cmd2[38] = {
+ 0x95, /* write data */
+ 0xFF, /* chip select */
+ 0x00,
+ 0x00,
+ 32, /* number of data bytes */
+ };
+
+ /* The display is driven by four controllers (chip select 0-3). Each
+ * controller drives a 64x64 segment of the display, divided into eight
+ * 64x8 lines, which we transmit as two commands of 32x8 lines.
+ */
+ for (cs = 0; cs < 4; ++cs) {
+ for (line = 0; line < 8; ++line) {
+ cmd1[1] = (cs << 2);
+ cmd1[5] = 0xb8 | line;
+ cmd2[1] = (cs << 2) | 0x01;
+
+ bufpos = 32 * ((cs << 1) + (line << 3));
+
+ memcpy(cmd1 + 12, p->framebuf + bufpos, 32);
+ memcpy(cmd2 + 5, p->framebuf + bufpos + 32, 32);
+ write_command(p->lcd, cmd1, sizeof(cmd1));
+ write_command(p->lcd, cmd2, sizeof(cmd2));
+ }
+ }
+}
+
+
+/** 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
+picolcdgraph_string(Driver *drvthis, int x, int y, unsigned char string[])
+{
+ unsigned char* rd;
+ for (rd = string; *rd; ++rd) {
+ picolcdgraph_chr(drvthis, x++, y, *rd);
+ }
+}
+
+
+/** 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
+picolcdgraph_chr(Driver *drvthis, int x, int y, unsigned char c)
+{
+ do_chr(drvthis, x, y, c);
+}
+
+
+/** Define a custom character and write it to the LCD.
+ * \param drvthis Pointer to driver structure.
+ * \param n Custom character to define [0 - (NUM_CCs-1)].
+ * \param dat Array of 8 (=cellheight) bytes, each representing a pixel row
+ * starting from the top to bottom.
+ * The bits in each byte represent the pixels where the LSB
+ * (least significant bit) is the rightmost pixel in each pixel
+ * row.
+ *
+ * See \ref picolcdgraph_get_free_chars() for an explanation of how the custom
+ * characters are assigned.
+ */
+MODULE_EXPORT void
+picolcdgraph_set_char(Driver *drvthis, int n, unsigned char *dat)
+{
+ unsigned char* chr;
+ int row, col;
+
+ chr = fontdata[n];
+
+ for (col = 0; col < CELL_WIDTH; ++col) {
+ chr[col] = 0;
+
+ for (row = 0; row < CELL_HEIGHT; ++row) {
+ if (dat[row] & (1 << (CELL_WIDTH - col - 1))) {
+ chr[col] |= 1 << row;
+ }
+ }
+ }
+}
+
+
+/** Get total number of custom characters available.
+ * \param drvthis Pointer to driver structure.
+ * \return Number of custom characters.
+ *
+ * Our custom characters run from 0–31 inclusive, and we simply use \a n as an
+ * index into the font characters. Technically we could have any number of such
+ * characters, but in practice they are only used by the \c adv_bignum code and
+ * it only needs 8.
+ */
+MODULE_EXPORT int
+picolcdgraph_get_free_chars(Driver *drvthis)
+{
+ return CUSTOM_CHARS_NUM;
+}
+
+
+/** Draw a vertical bar.
+ * \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 1000‰.
+ * \param promille Current height of the bar in ‰.
+ * \param options Options (currently unused).
+ *
+ * Draws a clean vertical bar, starting at the bottom (character position
+ * \a x, \a y) and extending upwards. Has pixel-level precision. Bars are four
+ * pixels wide, leaving one pixel of space either side.
+ */
+MODULE_EXPORT void
+picolcdgraph_vbar(Driver *drvthis, int x, int y, int len, int promille, int options)
+{
+ PrivateData *p = drvthis->private_data;
+ int ypx, fbpos, fbbit, i;
+
+ for (ypx = 0; ypx < (len * CELL_HEIGHT); ++ypx) {
+ if (((ypx * 1000) / (len * CELL_HEIGHT)) < promille) {
+ fbpos = chr_fbpos(x, y - (ypx / CELL_HEIGHT));
+ fbbit = 1 << (7 - (ypx & 7));
+
+ /* 1-5 leaves a 1-pixel empty border */
+ for (i = 1; i < 5; ++i) {
+ p->framebuf[fbpos + i] |= fbbit;
+ }
+ }
+ else {
+ break;
+ }
+ }
+}
+
+
+/** Draw a horizontal bar.
+ * \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 1000‰.
+ * \param promille Current length of the bar in ‰.
+ * \param options Options (currently unused).
+ *
+ * Draws a clean horizontal bar, starting at the left (character position
+ * \a x, \a y) and extending to the right. Has pixel-level precision. Bars are
+ * six pixels high, leaving one pixel of space above and one below.
+ */
+MODULE_EXPORT void
+picolcdgraph_hbar(Driver *drvthis, int x, int y, int len, int promille,
+ int options)
+{
+ PrivateData *p = drvthis->private_data;
+ int xpx, fbpos;
+
+ fbpos = chr_fbpos(x, y);
+ for (xpx = 0; xpx < (len * CELL_WIDTH); ++xpx) {
+ if ((xpx * 1000) / (len * CELL_WIDTH) < promille) {
+ /* leaves a 1-pixel empty border */
+ p->framebuf[fbpos] = 0x7E;
+ }
+ else {
+ break;
+ }
+ ++fbpos;
+ }
+}
+
+
+/** Write a big number to the screen.
+ * \param drvthis Pointer to driver structure.
+ * \param x Horizontal character position (column).
+ * \param num Character to write (0 - 10 with 10 representing ':')
+ *
+ * Uses \ref lib_adv_bignum() to draw big numbers. It's the only thing using
+ * custom chars, so start it from offset 0. Only initialise the chars once.
+ */
+MODULE_EXPORT void
+picolcdgraph_num(Driver *drvthis, int x, int num)
+{
+ PrivateData *p = drvthis->private_data;
+
+ lib_adv_bignum(drvthis, x, num, 0, p->bignum_ready);
+ p->bignum_ready = 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 synbolic value representing the icon.
+ * \retval 0 Icon has been successfully defined/written.
+ * \retval -1 Server core shall define/write the icon.
+ *
+ * We draw icons as though they were characters, since all that involves is
+ * copying a bit pattern to the frame buffer. All the main icons are supported.
+ *
+ * \todo What about the double-sized icons? There's no reason we couldn't
+ * support them too.
+ */
+MODULE_EXPORT int
+picolcdgraph_icon(Driver *drvthis, int x, int y, int icon)
+{
+ switch(icon) {
+ case ICON_BLOCK_FILLED:
+ case ICON_HEART_OPEN:
+ case ICON_HEART_FILLED:
+ case ICON_ARROW_UP:
+ case ICON_ARROW_DOWN:
+ case ICON_ARROW_LEFT:
+ case ICON_ARROW_RIGHT:
+ case ICON_CHECKBOX_OFF:
+ case ICON_CHECKBOX_ON:
+ case ICON_CHECKBOX_GRAY:
+ case ICON_SELECTOR_AT_LEFT:
+ case ICON_SELECTOR_AT_RIGHT:
+ case ICON_ELLIPSIS:
+ do_chr(drvthis, x, y, icon);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+/** Poll panel keys.
+ * \param drvthis Pointer to driver structure.
+ * \return String representation of the key;
+ * \c NULL if nothing available / unmapped key
+ *
+ * The picoLCD 256×64 CDROM bay module has five keys, which are mapped below.
+ * It returns the keycode on key down, and 0x00 on key up. It performs
+ * debouncing for us.
+ *
+ * \todo is there an OEM version where other keys can come out? If so, we might
+ * need to make the key mapping configurable.
+ */
+MODULE_EXPORT const char *
+picolcdgraph_get_key(Driver *drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ char buf[24];
+ int ret;
+
+ ret = usb_interrupt_read(p->lcd, PICOLCD_ENDPOINT_IN, buf, sizeof(buf),
+ p->key_timeout);
+ if ((ret > 0) && buf[0] == 0x11) {
+ switch(buf[1]) {
+ case 0x01:
+ return "Back";
+ case 0x02:
+ return "Escape";
+ case 0x05:
+ return "Up";
+ case 0x06:
+ return "Enter";
+ case 0x07:
+ return "Down";
+ }
+ }
+
+ return NULL;
+}
+
+
+/** Get current display contrast.
+ * \param drvthis Pointer to driver structure.
+ * \return Stored contrast in ‰.
+ *
+ * Returns shadow register contents for contrast.
+ */
+MODULE_EXPORT int
+picolcdgraph_get_contrast(Driver *drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+
+ return p->contrast;
+}
+
+
+/** Change display contrast.
+ * \param drvthis Pointer to driver structure.
+ * \param promille New contrast value in ‰.
+ */
+MODULE_EXPORT void
+picolcdgraph_set_contrast(Driver *drvthis, int promille)
+{
+ PrivateData *p = drvthis->private_data;
+ char cmd[2] = { 0x92 }; /* set contrast id */
+
+ cmd[1] = (promille * CONTRAST_SCALE) / 1000;
+
+ write_command(p->lcd, cmd, sizeof(cmd));
+}
+
+
+/** Retrieve brightness.
+ * \param drvthis Pointer to driver structure.
+ * \param state Brightness state (on/off) for which we want the value.
+ * \return Stored brightness in ‰.
+ *
+ * Returns shadow register contents for brightness.
+ */
+MODULE_EXPORT int
+picolcdgraph_get_brightness(Driver *drvthis, int state)
+{
+ PrivateData *p = drvthis->private_data;
+
+ return state ? p->brightness_on : p->brightness_off;
+}
+
+
+/** Set on/off brightness.
+ * \param drvthis Pointer to driver structure.
+ * \param state Brightness state (on/off) for which we want to store the
+ * new value.
+ * \param promille New brightness in ‰.
+ *
+ * Updates the stored (shadow) register contents for brightness on/off state,
+ * but does not effect any change on the hardware.
+ * \ref picolcdgraph_backlight() is responsible for that.
+ */
+MODULE_EXPORT void
+picolcdgraph_set_brightness(Driver *drvthis, int state, int promille)
+{
+ PrivateData *p = drvthis->private_data;
+
+ if (promille < 0 || promille > 1000)
+ return;
+
+ if (state)
+ p->brightness_on = promille;
+ else
+ p->brightness_off = promille;
+
+ return;
+}
+
+
+/** Turn the backlight on or off.
+ * \param drvthis Pointer to driver structure.
+ * \param state New backlight status.
+ *
+ * Writes the stored brightness level to the hardware.
+ */
+MODULE_EXPORT void
+picolcdgraph_backlight(Driver *drvthis, int state)
+{
+ PrivateData *p = drvthis->private_data;
+ char cmd[2] = { 0x91 }; /* set backlight id */
+
+ cmd[1] = ((state ? p->brightness_on : p->brightness_off)
+ * BRIGHTNESS_SCALE) / 1000;
+
+ write_command(p->lcd, cmd, sizeof(cmd));
+}
+
+
+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 = "picolcdgraph_";
diff -ruN lcdproc.orig/server/drivers/picolcdgraph.h lcdproc/server/drivers/picolcdgraph.h
--- lcdproc.orig/server/drivers/picolcdgraph.h 1970-01-01 00:00:00.000000000 +0000
+++ lcdproc/server/drivers/picolcdgraph.h 2012-02-26 19:17:24.032883041 +0000
@@ -0,0 +1,57 @@
+/*
+ * picolcdgraph (graphical panel) driver for lcdPROC
+ *
+ * Copyright: ©2012, Laurence Withers <[email protected]>
+ * Released under the GNU GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * The picolcd is usb connected and is driven via userspace using libusb library.
+ *
+ * libusb: http://libusb.sf.net
+ *
+ */
+
+#ifndef PICOLCDGRAPH_H
+#define PCIOLCDGRAPH_H
+
+#include "lcd.h"
+#include "adv_bignum.h"
+
+#include <usb.h>
+
+MODULE_EXPORT int picolcdgraph_init(Driver *drvthis);
+MODULE_EXPORT void picolcdgraph_close(Driver *drvthis);
+MODULE_EXPORT int picolcdgraph_width(Driver *drvthis);
+MODULE_EXPORT int picolcdgraph_height(Driver *drvthis);
+MODULE_EXPORT void picolcdgraph_clear(Driver *drvthis);
+MODULE_EXPORT void picolcdgraph_flush(Driver *drvthis);
+MODULE_EXPORT void picolcdgraph_string(Driver *drvthis, int x, int y, unsigned char string[]);
+MODULE_EXPORT void picolcdgraph_chr(Driver *drvthis, int x, int y, unsigned char c);
+MODULE_EXPORT void picolcdgraph_set_char (Driver *drvthis, int n, unsigned char *dat);
+MODULE_EXPORT const char *picolcdgraph_get_key(Driver *drvthis);
+
+MODULE_EXPORT void picolcdgraph_vbar(Driver *drvthis, int x, int y, int len, int promille, int options);
+MODULE_EXPORT void picolcdgraph_hbar(Driver *drvthis, int x, int y, int len, int promille, int options);
+MODULE_EXPORT void picolcdgraph_num(Driver *drvthis, int x, int num);
+MODULE_EXPORT int picolcdgraph_icon(Driver *drvthis, int x, int y, int icon);
+MODULE_EXPORT void picolcdgraph_cursor(Driver *drvthis, int x, int y, int type);
+
+MODULE_EXPORT int picolcdgraph_get_contrast(Driver *drvthis);
+MODULE_EXPORT void picolcdgraph_set_contrast(Driver *drvthis, int promille);
+MODULE_EXPORT int picolcdgraph_get_brightness(Driver *drvthis, int state);
+MODULE_EXPORT void picolcdgraph_set_brightness(Driver *drvthis, int state, int promille);
+MODULE_EXPORT void picolcdgraph_backlight(Driver *drvthis, int state);
+
+#endif