From d61430decdb2764682c496d0cde04b4c437e967f Mon Sep 17 00:00:00 2001 From: mmdolze Date: Wed, 5 Mar 2014 18:46:39 +0000 Subject: [PATCH] Add rawserial driver (by C. Wolf). --- ChangeLog | 1 + LCDd.conf | 33 +- acinclude.m4 | 16 +- docs/LCDd.8.in | 3 + docs/lcdproc-user/drivers.docbook | 1 + docs/lcdproc-user/drivers/Makefile.am | 1 + docs/lcdproc-user/drivers/rawserial.docbook | 134 ++++++ docs/lcdproc-user/lcdproc-user.docbook | 1 + server/drivers/Makefile.am | 3 +- server/drivers/rawserial.c | 425 ++++++++++++++++++++ server/drivers/rawserial.h | 22 + 11 files changed, 627 insertions(+), 13 deletions(-) create mode 100644 docs/lcdproc-user/drivers/rawserial.docbook create mode 100644 server/drivers/rawserial.c create mode 100644 server/drivers/rawserial.h diff --git a/ChangeLog b/ChangeLog index 74333dd..b744a5c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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! diff --git a/LCDd.conf b/LCDd.conf index 20ca8ac..f0ac69b 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -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 diff --git a/acinclude.m4 b/acinclude.m4 index e8d0532..76ca6c5 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -8,21 +8,21 @@ AC_ARG_ENABLE(drivers, [ --enable-drivers= compile drivers for LCDs in ,] [ 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}" diff --git a/docs/LCDd.8.in b/docs/LCDd.8.in index 84da491..977e22d 100644 --- a/docs/LCDd.8.in +++ b/docs/LCDd.8.in @@ -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 diff --git a/docs/lcdproc-user/drivers.docbook b/docs/lcdproc-user/drivers.docbook index a21ea85..72a4244 100644 --- a/docs/lcdproc-user/drivers.docbook +++ b/docs/lcdproc-user/drivers.docbook @@ -39,6 +39,7 @@ well as the configuration of LCDd. &MtxOrb; &mx5000; &NoritakeVFD; +&rawserial; &picolcd; &pylcd; &sdeclcd; diff --git a/docs/lcdproc-user/drivers/Makefile.am b/docs/lcdproc-user/drivers/Makefile.am index e545e40..2efa0a9 100644 --- a/docs/lcdproc-user/drivers/Makefile.am +++ b/docs/lcdproc-user/drivers/Makefile.am @@ -35,6 +35,7 @@ EXTRA_DIST = bayrad.docbook \ picolcd.docbook \ ppttrouble.docbook \ pylcd.docbook \ + rawserial.docbook \ sdeclcd.docbook \ sed1330.docbook \ sed1520.docbook \ diff --git a/docs/lcdproc-user/drivers/rawserial.docbook b/docs/lcdproc-user/drivers/rawserial.docbook new file mode 100644 index 0000000..0963659 --- /dev/null +++ b/docs/lcdproc-user/drivers/rawserial.docbook @@ -0,0 +1,134 @@ + + + + Connor + Wolf + + + +The *Really* Simple Serial interface (rawserial) + + +This section talks about using LCDproc with custom hardware. + + + +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. + + + + +Features + + +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. + + + +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. + + + +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. + + + + + + +Requirements + + +None. The only requirement is a serial port of some sort. + + + + + + +Configuration in LCDd.conf + + +[rawserial] + + + + + Device = + DEVICE + + + Serial port to use. Default: /dev/cuaU0 + + + + + + Speed = + NUMBER + + + Desired baud-rate. Possible values: + 1200, + 2400, + 9600 (default), + 19200, and + 115200. + + + + + + Size = ¶meters.size; + + + 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. + + + + + + UpdateRate = + NUMBER + + + 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 + 0.0005 (equals 2000 seconds) - 10, + with 1.0 being the default. + + + + + + + + + + diff --git a/docs/lcdproc-user/lcdproc-user.docbook b/docs/lcdproc-user/lcdproc-user.docbook index 5374dff..1c73ed6 100644 --- a/docs/lcdproc-user/lcdproc-user.docbook +++ b/docs/lcdproc-user/lcdproc-user.docbook @@ -46,6 +46,7 @@ + diff --git a/server/drivers/Makefile.am b/server/drivers/Makefile.am index 33ca155..0fa38f2 100644 --- a/server/drivers/Makefile.am +++ b/server/drivers/Makefile.am @@ -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 diff --git a/server/drivers/rawserial.c b/server/drivers/rawserial.c new file mode 100644 index 0000000..433273a --- /dev/null +++ b/server/drivers/rawserial.c @@ -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 + * + * 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 +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef HAVE_SYS_TIME_H +# include +#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; +} diff --git a/server/drivers/rawserial.h b/server/drivers/rawserial.h new file mode 100644 index 0000000..66fd27c --- /dev/null +++ b/server/drivers/rawserial.h @@ -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