Add rawserial driver (by C. Wolf).
This commit is contained in:
@@ -21,6 +21,7 @@ v0.5dev (ongoing development)
|
||||
+ hd44780: Added 'spi' connection type (S. Dawson)
|
||||
* hd55780/serial: Change data escape handling (G. Smith)
|
||||
+ hd44780: New connection type 'pifacecad' (T. Preston)
|
||||
+ new driver: rawserial - dump framebuffer to serial port (C. Wolf)
|
||||
|
||||
v0.5.6
|
||||
- Remove deprecated CFontz633 driver. Use CFontzPacket with Model=633 instead!
|
||||
|
||||
@@ -44,12 +44,12 @@ DriverPath=server/drivers/
|
||||
# driver specific section.
|
||||
#
|
||||
# The following drivers are supported:
|
||||
# bayrad, CFontz, CFontzPacket, curses, 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, sli,
|
||||
# stv5730, svga, t6963, text, tyan, ula200, vlsys_m428, xosd
|
||||
# bayrad, CFontz, CFontzPacket, curses, 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, rawserial, 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. [default: 127.0.0.1]
|
||||
@@ -1056,6 +1056,27 @@ Device=/dev/ttyUSB0
|
||||
|
||||
|
||||
|
||||
## rawserial driver ##
|
||||
[rawserial]
|
||||
|
||||
# Select the output device to use [default: /dev/cuaU0]
|
||||
Device=/dev/ttyS0
|
||||
|
||||
# Serial port baudrate [default: 9600]
|
||||
Speed=9600
|
||||
|
||||
# Specifies the size of the LCD. If this driver is loaded as a secondary driver
|
||||
# it always adopts to the size of the primary driver. If loaded as the only
|
||||
# (or primary) driver, the size can be set. [default: 40x4]
|
||||
#Size=16x2
|
||||
|
||||
# How often to dump the LCD contents out the port, in Hertz (times per second)
|
||||
# 1 = once per second, 4 is 4 times per second, 0.1 is once every 10 seconds.
|
||||
# [default: 1; legal: 0.0005 - 10]
|
||||
UpdateRate=1
|
||||
|
||||
|
||||
|
||||
## SDEC driver for Watchguard Firebox ##
|
||||
[sdeclcd]
|
||||
# No options
|
||||
|
||||
+10
-6
@@ -8,21 +8,21 @@ AC_ARG_ENABLE(drivers,
|
||||
[ --enable-drivers=<list> compile drivers for LCDs in <list>,]
|
||||
[ which is a comma-separated list of drivers.]
|
||||
[ Possible drivers are:]
|
||||
[ bayrad,CFontz,CFontzPacket,curses,CwLnx,]
|
||||
[ ea65,EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,]
|
||||
[ 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]
|
||||
[ picolcd,pyramid,rawserial,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,pyramid,sdeclcd,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,t6963,text,tyan,ula200,vlsys_m428,xosd,rawserial]
|
||||
if test "$debug" = yes; then
|
||||
allDrivers=["${allDrivers},debug"]
|
||||
fi
|
||||
@@ -393,6 +393,10 @@ dnl else
|
||||
DRIVERS="$DRIVERS NoritakeVFD${SO}"
|
||||
actdrivers=["$actdrivers NoritakeVFD"]
|
||||
;;
|
||||
rawserial)
|
||||
DRIVERS="$DRIVERS rawserial${SO}"
|
||||
actdrivers=["$actdrivers rawserial"]
|
||||
;;
|
||||
picolcd)
|
||||
if test "$enable_libusb" = yes ; then
|
||||
DRIVERS="$DRIVERS picolcd${SO}"
|
||||
|
||||
@@ -274,6 +274,9 @@ LCD display on the Logitech MX5000 keyboard
|
||||
.B NoritakeVFD
|
||||
Noritake VFD Device CU20045SCPB-T28A
|
||||
.TP
|
||||
.B rawserial
|
||||
Dumps the entire framebuffer to the serial port at a configurable rate.
|
||||
.TP
|
||||
.B picolcd
|
||||
Mini-box.com USB LCD (PicoLCD 20x4 & picoLCD 20x2)
|
||||
.TP
|
||||
|
||||
@@ -39,6 +39,7 @@ well as the configuration of LCDd.
|
||||
&MtxOrb;
|
||||
&mx5000;
|
||||
&NoritakeVFD;
|
||||
&rawserial;
|
||||
&picolcd;
|
||||
&pylcd;
|
||||
&sdeclcd;
|
||||
|
||||
@@ -35,6 +35,7 @@ EXTRA_DIST = bayrad.docbook \
|
||||
picolcd.docbook \
|
||||
ppttrouble.docbook \
|
||||
pylcd.docbook \
|
||||
rawserial.docbook \
|
||||
sdeclcd.docbook \
|
||||
sed1330.docbook \
|
||||
sed1520.docbook \
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
<sect1 id="howto">
|
||||
<sect1info>
|
||||
<author>
|
||||
<firstname>Connor</firstname>
|
||||
<surname>Wolf</surname>
|
||||
</author>
|
||||
</sect1info>
|
||||
<!-- Only as a comment, as inline presentation is awful in Docbook 4.5
|
||||
Imaginary Industries
|
||||
imaginaryindustries.com
|
||||
Email: lcdproc@imaginaryindustries.com
|
||||
-->
|
||||
<title>The *Really* Simple Serial interface (rawserial)</title>
|
||||
|
||||
<para>
|
||||
This section talks about using LCDproc with custom hardware.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This driver is intended for using LCDproc with custom hardware. In the
|
||||
original implementation, this was for a PFsense router with a homemade
|
||||
arduino-style circuit that controlled analog panel meters to display network
|
||||
traffic. It is intended for this sort of application, where the LCD control
|
||||
characters emitted normally, as well as the partial screen drawing, make
|
||||
parsing the serial stream a considerably complex affair, particularly on
|
||||
small (generally 8 bit) microprocessors.
|
||||
</para>
|
||||
|
||||
|
||||
<sect2 id="features">
|
||||
<title>Features</title>
|
||||
|
||||
<para>
|
||||
The driver emulates a 40x4 display, and simply dumps the entire framebuffer
|
||||
out the serial port at a rate configured in the config file (default of 1
|
||||
Hz), post-pended with a single "\n" character. At 1 Hz, it therefore
|
||||
generates 161 serial characters per second.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The maximum update-rate is limited by the internal update-rate of LCDproc,
|
||||
which is currently 8 Hz, so speeds greater then 8 Hz will be simply limited
|
||||
to 8 Hz. The update-rate is also granular at 1 second/8 time-intervals (125
|
||||
ms), so interesting fractional update-rates will behave oddly. The
|
||||
event-loop is rigidly deterministic, so fractional rates will *average* out
|
||||
to the desired rate, but that will be achieved by dithering between nearby
|
||||
available intervals.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The baud rate is configurable, with a default of 9600 baud. Baud rate will
|
||||
have to be chosen with the required data-per-second in mind, since at 1 Hz,
|
||||
there are 161 characters (40x4+1) per second, which requires that's a
|
||||
theoretical minimum baud of 161*10 = 1610 baud. Higher frame-rates will
|
||||
require higher baud rates.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="requirements">
|
||||
<title>Requirements</title>
|
||||
|
||||
<para>
|
||||
None. The only requirement is a serial port of some sort.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="config">
|
||||
<title>Configuration in LCDd.conf</title>
|
||||
|
||||
<sect3 id="config-section">
|
||||
<title>[rawserial]</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<property>Device</property> =
|
||||
<parameter><replaceable>DEVICE</replaceable></parameter>
|
||||
</term>
|
||||
<listitem><para>
|
||||
Serial port to use. Default: <filename>/dev/cuaU0</filename>
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<property>Speed</property> =
|
||||
<parameter><replaceable>NUMBER</replaceable></parameter>
|
||||
</term>
|
||||
<listitem><para>
|
||||
Desired baud-rate. Possible values:
|
||||
<literal>1200</literal>,
|
||||
<literal>2400</literal>,
|
||||
<literal>9600</literal> (default),
|
||||
<literal>19200</literal>, and
|
||||
<literal>115200</literal>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<property>Size</property> = ¶meters.size;
|
||||
</term>
|
||||
<listitem><para>
|
||||
Specifies the size of the LCD. If this driver is loaded as a secondary
|
||||
driver it always adopts to the size of the primary driver. If loaded as
|
||||
the only (or primary) driver, the size can be set.
|
||||
Default: <literal>40x4</literal>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<property>UpdateRate</property> =
|
||||
<parameter><replaceable>NUMBER</replaceable></parameter>
|
||||
</term>
|
||||
<listitem><para>
|
||||
Desired update-rate in Hertz (e.g. updates per second). Fractional
|
||||
values and values less than one are valid (e.g. 0.5). Legal values are
|
||||
<literal>0.0005</literal> (equals 2000 seconds) - <literal>10</literal>,
|
||||
with <literal>1.0 </literal> being the default.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
@@ -46,6 +46,7 @@
|
||||
<!ENTITY MtxOrb SYSTEM "drivers/mtxorb.docbook">
|
||||
<!ENTITY mx5000 SYSTEM "drivers/mx5000.docbook">
|
||||
<!ENTITY NoritakeVFD SYSTEM "drivers/NoritakeVFD.docbook">
|
||||
<!ENTITY rawserial SYSTEM "drivers/rawserial.docbook">
|
||||
<!ENTITY picolcd SYSTEM "drivers/picolcd.docbook">
|
||||
<!ENTITY pylcd SYSTEM "drivers/pylcd.docbook">
|
||||
<!ENTITY sdeclcd SYSTEM "drivers/sdeclcd.docbook">
|
||||
|
||||
@@ -23,7 +23,7 @@ AM_LDFLAGS = @LDSHARED@
|
||||
|
||||
lcdexecbindir = $(pkglibdir)
|
||||
lcdexecbin_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 CwLnx debug 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 rawserial sdeclcd sed1330 sed1520 serialPOS serialVFD shuttleVFD sli stv5730 SureElec svga t6963 text tyan ula200 vlsys_m428 xosd
|
||||
noinst_LIBRARIES = libLCD.a libbignum.a
|
||||
|
||||
g15_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
|
||||
@@ -116,6 +116,7 @@ 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
|
||||
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
|
||||
rawserial_SOURCES = lcd.h rawserial.c rawserial.h
|
||||
picolcd_SOURCES = lcd.h picolcd.h picolcd.c report.h
|
||||
pyramid_SOURCES = lcd.h pylcd.c pylcd.h
|
||||
sdeclcd_SOURCES = lcd.h sdeclcd.h sdeclcd.c lcd_lib.h report.h adv_bignum.h port.h lpt-port.h timing.h
|
||||
|
||||
@@ -0,0 +1,425 @@
|
||||
/** \file server/drivers/rawserial.c
|
||||
* LCDd \c rawserial driver.
|
||||
* This is primarily of use when you want to use LCDproc as a data-source for
|
||||
* an external MCU or other similar hardware, in a situation where the LCD
|
||||
* control characters normally emitted are actually obnoxious, rather then
|
||||
* beneficial.
|
||||
* As such, it simply dumps the ENTIRE (no deltas or such) framebuffer at a
|
||||
* configurable rate (default 1 Hz).
|
||||
*/
|
||||
|
||||
/*-
|
||||
* Copyright (C) 2014 Connor Wolf <lcdproc@imaginaryindustries.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "lcd.h"
|
||||
#include "rawserial.h"
|
||||
#include "report.h"
|
||||
|
||||
|
||||
/** private data for the \c rawserial driver */
|
||||
typedef struct rawserial_private_data {
|
||||
int width; /**< display width in characters */
|
||||
int height; /**< display height in characters */
|
||||
char *framebuf; /**< frame buffer */
|
||||
int fd; /**< handle to the device */
|
||||
|
||||
/** \name Event loop timing. refresh_time and refresh_delta form the
|
||||
* event loop timing mechanism for configurable update rates.
|
||||
*@{*/
|
||||
uint refresh_time; /**< time at the last screen update */
|
||||
uint refresh_delta; /**< time step to next screen update */
|
||||
/**@}*/
|
||||
} PrivateData;
|
||||
|
||||
/* Local prototypes */
|
||||
static uint get_millisecond_time(void);
|
||||
|
||||
|
||||
/* 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 = "rawserial_";
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the driver.
|
||||
* \param drvthis Pointer to driver structure.
|
||||
* \retval 0 Success.
|
||||
* \retval <0 Error.
|
||||
*/
|
||||
MODULE_EXPORT int
|
||||
rawserial_init(Driver *drvthis)
|
||||
{
|
||||
PrivateData *p;
|
||||
char buf[256];
|
||||
char device[200];
|
||||
|
||||
int speed = DEFAULT_SPEED;
|
||||
int tmp;
|
||||
double tmpf;
|
||||
|
||||
struct termios portset;
|
||||
|
||||
/* Allocate and store private data */
|
||||
p = (PrivateData *) calloc(1, sizeof(PrivateData));
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
if (drvthis->store_private_ptr(drvthis, p) != 0)
|
||||
return -1;
|
||||
|
||||
/* initialize private data */
|
||||
tmpf = drvthis->config_get_float(drvthis->name, "UpdateRate", 0, DEFAULT_UPDATE_RATE);
|
||||
if (tmpf < 0.0005 || tmpf > 10) {
|
||||
report(RPT_WARNING, "%s: UpdateRate out of range; using default %g",
|
||||
drvthis->name, DEFAULT_UPDATE_RATE);
|
||||
tmpf = DEFAULT_UPDATE_RATE;
|
||||
}
|
||||
p->refresh_delta = (int) ((double) SECOND_GRANULARITY / tmpf);
|
||||
|
||||
/* Subtract refresh delta so we update immediately on start */
|
||||
p->refresh_time = get_millisecond_time() - p->refresh_delta;
|
||||
report(RPT_INFO, "%s: start-up time: %u, refresh delta: %u ms per update", drvthis->name,
|
||||
p->refresh_time, p->refresh_delta);
|
||||
|
||||
/* Which speed */
|
||||
tmp = drvthis->config_get_int(drvthis->name, "Speed", 0, DEFAULT_SPEED);
|
||||
switch (tmp) {
|
||||
case 1200:
|
||||
speed = B1200;
|
||||
break;
|
||||
case 2400:
|
||||
speed = B2400;
|
||||
break;
|
||||
case 9600:
|
||||
speed = B9600;
|
||||
break;
|
||||
case 19200:
|
||||
speed = B19200;
|
||||
break;
|
||||
case 115200:
|
||||
speed = B115200;
|
||||
break;
|
||||
default:
|
||||
report(RPT_WARNING,
|
||||
"%s: Speed must be 1200, 2400, 9600, 19200 or 115200; using default %d",
|
||||
drvthis->name, DEFAULT_SPEED);
|
||||
speed = DEFAULT_SPEED;
|
||||
}
|
||||
|
||||
/* which serial device should be used */
|
||||
strncpy(device, drvthis->config_get_string(drvthis->name, "Device", 0, DEFAULT_DEVICE),
|
||||
sizeof(device));
|
||||
device[sizeof(device) - 1] = '\0';
|
||||
report(RPT_INFO, "%s: using Device %s at baud rate: %d (configured = %d)",
|
||||
drvthis->name, device, speed, tmp);
|
||||
|
||||
/* Set display sizes */
|
||||
if ((drvthis->request_display_width() > 0)
|
||||
&& (drvthis->request_display_height() > 0)) {
|
||||
/* Use size from primary driver */
|
||||
p->width = drvthis->request_display_width();
|
||||
p->height = drvthis->request_display_height();
|
||||
}
|
||||
else {
|
||||
/* Use our own size from config file */
|
||||
strncpy(buf,
|
||||
drvthis->config_get_string(drvthis->name, "Size", 0, DISPLAY_DEFAULT_SIZE),
|
||||
sizeof(buf));
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
if ((sscanf(buf, "%dx%d", &p->width, &p->height) != 2)
|
||||
|| (p->width <= 0) || (p->width > LCD_MAX_WIDTH)
|
||||
|| (p->height <= 0) || (p->height > LCD_MAX_HEIGHT)) {
|
||||
report(RPT_WARNING, "%s: cannot read Size: %s; using default %s",
|
||||
drvthis->name, buf, DISPLAY_DEFAULT_SIZE);
|
||||
sscanf(DISPLAY_DEFAULT_SIZE, "%dx%d", &p->width, &p->height);
|
||||
}
|
||||
}
|
||||
report(RPT_INFO, "%s: using Size %dx%d", drvthis->name, p->width, p->height);
|
||||
|
||||
/* Allocate the framebuffer */
|
||||
p->framebuf = malloc(p->width * p->height);
|
||||
if (p->framebuf == NULL) {
|
||||
report(RPT_ERR, "%s: unable to create framebuffer", drvthis->name);
|
||||
goto err_out;
|
||||
}
|
||||
memset(p->framebuf, ' ', p->width * p->height);
|
||||
|
||||
/* Set up I/O port correctly, and open it... */
|
||||
p->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (p->fd == -1) {
|
||||
report(RPT_ERR, "%s: open(%s) failed (%s)", drvthis->name, device, strerror(errno));
|
||||
if (errno == EACCES)
|
||||
report(RPT_ERR, "%s: device %s could not be opened", drvthis->name, device);
|
||||
goto err_out;
|
||||
}
|
||||
report(RPT_INFO, "%s: opened display on %s", drvthis->name, device);
|
||||
|
||||
tcgetattr(p->fd, &portset);
|
||||
|
||||
/* We use RAW mode */
|
||||
#ifdef HAVE_CFMAKERAW
|
||||
/* The easy way */
|
||||
cfmakeraw(&portset);
|
||||
#else
|
||||
/* The hard way */
|
||||
portset.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
portset.c_oflag &= ~OPOST;
|
||||
portset.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
portset.c_cflag &= ~(CSIZE | PARENB | CRTSCTS);
|
||||
portset.c_cflag |= CS8 | CREAD | CLOCAL;
|
||||
#endif
|
||||
/* Set timeouts */
|
||||
portset.c_cc[VMIN] = 1;
|
||||
portset.c_cc[VTIME] = 3;
|
||||
|
||||
/* Set port speed */
|
||||
cfsetospeed(&portset, speed);
|
||||
cfsetispeed(&portset, B0);
|
||||
|
||||
/* Do it... */
|
||||
tcsetattr(p->fd, TCSANOW, &portset);
|
||||
|
||||
report(RPT_DEBUG, "%s: init() done", drvthis->name);
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
rawserial_close(drvthis);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close the driver (do necessary clean-up).
|
||||
* \param drvthis Pointer to driver structure.
|
||||
*/
|
||||
MODULE_EXPORT void
|
||||
rawserial_close(Driver *drvthis)
|
||||
{
|
||||
PrivateData *p = drvthis->private_data;
|
||||
|
||||
if (p != NULL) {
|
||||
if (p->fd >= 0)
|
||||
close(p->fd);
|
||||
if (p->framebuf != NULL)
|
||||
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
|
||||
rawserial_width(Driver *drvthis)
|
||||
{
|
||||
PrivateData *p = drvthis->private_data;
|
||||
|
||||
return p->width;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the display height in characters.
|
||||
* \param drvthis Pointer to driver structure.
|
||||
* \return Number of characters the display is high.
|
||||
*/
|
||||
MODULE_EXPORT int
|
||||
rawserial_height(Driver *drvthis)
|
||||
{
|
||||
PrivateData *p = drvthis->private_data;
|
||||
|
||||
return p->height;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear the screen.
|
||||
* \param drvthis Pointer to driver structure.
|
||||
*/
|
||||
MODULE_EXPORT void
|
||||
rawserial_clear(Driver *drvthis)
|
||||
{
|
||||
PrivateData *p = drvthis->private_data;
|
||||
|
||||
memset(p->framebuf, ' ', p->width * p->height);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flush data on screen to the display.
|
||||
*
|
||||
* Output is written at every \c refresh_delta milliseconds to the device.
|
||||
* However, updates do not occur more often than every 125 ms (8 Hz) which is
|
||||
* the default rendering rate in LCDd.
|
||||
*
|
||||
* This function implements its own update scheduler which is independent of
|
||||
* the main rendering loop to achieve a more constant output rate.
|
||||
*
|
||||
* \param drvthis Pointer to driver structure.
|
||||
*/
|
||||
MODULE_EXPORT void
|
||||
rawserial_flush(Driver *drvthis)
|
||||
{
|
||||
PrivateData *p = drvthis->private_data;
|
||||
char out[LCD_MAX_WIDTH * LCD_MAX_HEIGHT];
|
||||
|
||||
uint currentTime = get_millisecond_time();
|
||||
int t_delta = currentTime - p->refresh_time;
|
||||
|
||||
/*
|
||||
* Sanity checking in case of major time-shifts due to NTP updates or
|
||||
* daylight savings time. (INT_MAX / 1e3) is 2147483 milliseconds, 2147
|
||||
* seconds, or 35 minutes.
|
||||
*/
|
||||
if (((t_delta + 1) > (INT_MAX / 1e3)) || (t_delta < 0)) {
|
||||
report(RPT_WARNING,
|
||||
"%s: Major time-delta between flush calls! Old time: %d, new time: %d",
|
||||
drvthis->name, p->refresh_time, currentTime);
|
||||
p->refresh_time = currentTime;
|
||||
}
|
||||
|
||||
if (currentTime > (p->refresh_time + p->refresh_delta)) {
|
||||
int dataEnd = p->height * p->width;
|
||||
|
||||
/* Dump the contents of the framebuffer out the serial port.
|
||||
* There is no processing and no control chars are emitted,
|
||||
* just a plain-old newline at the end of the record. */
|
||||
memcpy(out, p->framebuf, dataEnd);
|
||||
write(p->fd, out, dataEnd);
|
||||
write(p->fd, "\n", 1);
|
||||
|
||||
report(RPT_DEBUG,
|
||||
"%s: flush exec time: %u, refresh delta: %u, current clock: %u, rendering loop overshoot: %d ms",
|
||||
drvthis->name, p->refresh_time, p->refresh_delta, currentTime,
|
||||
currentTime - p->refresh_time - p->refresh_delta);
|
||||
|
||||
/* Update the event timer so we'll trigger after the next
|
||||
* p->refresh_delta milliseconds */
|
||||
p->refresh_time += p->refresh_delta;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
rawserial_string(Driver *drvthis, int x, int y, const char string[])
|
||||
{
|
||||
PrivateData *p = drvthis->private_data;
|
||||
int i;
|
||||
|
||||
x--;
|
||||
y--; /* Convert 1-based coords to 0-based */
|
||||
|
||||
if ((y < 0) || (y >= p->height))
|
||||
return;
|
||||
|
||||
for (i = 0; (string[i] != '\0') && (x < p->width); i++, x++) {
|
||||
if (x >= 0) /* no write left of left border */
|
||||
p->framebuf[(y * p->width) + x] = 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
|
||||
rawserial_chr(Driver *drvthis, int x, int y, char c)
|
||||
{
|
||||
PrivateData *p = drvthis->private_data;
|
||||
|
||||
y--;
|
||||
x--;
|
||||
|
||||
if ((x >= 0) && (y >= 0) && (x < p->width) && (y < p->height))
|
||||
p->framebuf[(y * p->width) + x] = c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provide some information about this driver.
|
||||
* \param drvthis Pointer to driver structure.
|
||||
* \return Constant string with information.
|
||||
*/
|
||||
MODULE_EXPORT const char *
|
||||
rawserial_get_info(Driver *drvthis)
|
||||
{
|
||||
static char *info_string =
|
||||
"Raw Text mode driver for use as a source for sending data to external hardware";
|
||||
|
||||
return info_string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get current time in milliseconds, used for determining update event timing
|
||||
*
|
||||
* This WILL wrap fairly often, but we don't care, since we're just comparing
|
||||
* two values, both of which will wrap at the same time. Note that gettimeofday
|
||||
* is the wrong thing to use here, since it's not monotonic at all. We *really*
|
||||
* should use `clock_gettime(CLOCK_MONOTONIC, &ts);` since that's both
|
||||
* monotonic and immune to NTP updates, DST time changes, etc. However, that
|
||||
* would require linking librealtime.
|
||||
*
|
||||
* \return int with current millisecond time.
|
||||
*/
|
||||
static uint
|
||||
get_millisecond_time(void)
|
||||
{
|
||||
struct timeval ts;
|
||||
gettimeofday(&ts, NULL);
|
||||
return ((ts.tv_sec * SECOND_GRANULARITY) +
|
||||
(ts.tv_usec / (1000000.0 / SECOND_GRANULARITY))) + 0.5;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef LCD_rawserial_H
|
||||
#define LCD_rawserial_H
|
||||
|
||||
#define DEFAULT_DEVICE "/dev/cuaU0"
|
||||
#define DISPLAY_DEFAULT_SIZE "40x4"
|
||||
|
||||
/* Default 9600 baud, 1 hz update rate, time resolution of 1 ms */
|
||||
#define DEFAULT_SPEED B9600
|
||||
#define DEFAULT_UPDATE_RATE 1.0
|
||||
#define SECOND_GRANULARITY 1000
|
||||
|
||||
MODULE_EXPORT int rawserial_init(Driver *drvthis);
|
||||
MODULE_EXPORT void rawserial_close(Driver *drvthis);
|
||||
MODULE_EXPORT int rawserial_width(Driver *drvthis);
|
||||
MODULE_EXPORT int rawserial_height(Driver *drvthis);
|
||||
MODULE_EXPORT void rawserial_clear(Driver *drvthis);
|
||||
MODULE_EXPORT void rawserial_flush(Driver *drvthis);
|
||||
MODULE_EXPORT void rawserial_string(Driver *drvthis, int x, int y, const char string[]);
|
||||
MODULE_EXPORT void rawserial_chr(Driver *drvthis, int x, int y, char c);
|
||||
MODULE_EXPORT const char *rawserial_get_info(Driver *drvthis);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user