488 lines
13 KiB
C
488 lines
13 KiB
C
/** \file server/drivers/imon.c
|
|
* LCDd \c imon driver for the Soundgraph iMON IR/VFD module.
|
|
*/
|
|
|
|
/*
|
|
* Driver for Soundgraph/Ahanix/Silverstone/Uneed/Accent iMON IR/VFD Module
|
|
*
|
|
* In order to be able to use it, you have to get and install one of
|
|
* the following kernel modules:
|
|
* - standalone iMON VFD driver from http://venky.ws/projects/imon/
|
|
* - the iMON module included with LIRC ver. 0.7.1 or newer
|
|
* from http://www.lirc.org/
|
|
*
|
|
* Copyright (c) 2004, Venky Raju <dev@venky.ws>, original author of
|
|
* the LCDproc 0.4.5 iMON driver, the standalone and the LIRC kernel
|
|
* modules for the iMON IR/VFD at http://venky.ws/projects/imon/
|
|
* Inspired by:
|
|
* TextMode driver (LCDproc authors?)
|
|
* Sasem driver (Oliver Stabel)
|
|
*
|
|
* Copyright (c) 2005 Lucian Muresan <lucianm AT users.sourceforge.net>,
|
|
* porting the LCDproc 0.4.5 code to LCDproc 0.5
|
|
* Copyright (c) 2006 John Saunders, use graphics characters
|
|
*
|
|
* This source code is being released under the GPL.
|
|
* Please see the file COPYING in this package for details.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "lcd.h"
|
|
#include "lcd_lib.h"
|
|
#include "shared/debug.h"
|
|
//#define DEBUG
|
|
#include "report.h"
|
|
|
|
|
|
#include "imon.h"
|
|
|
|
// iMon reserves the first 8 locations for the
|
|
// special bargraph characters
|
|
#define IMON_CHAR_1_BAR 0x00
|
|
#define IMON_CHAR_2_BARS 0x01
|
|
#define IMON_CHAR_3_BARS 0x02
|
|
#define IMON_CHAR_4_BARS 0x03
|
|
#define IMON_CHAR_5_BARS 0x04
|
|
#define IMON_CHAR_6_BARS 0x05
|
|
#define IMON_CHAR_7_BARS 0x06
|
|
#define IMON_CHAR_8_BARS 0x07
|
|
|
|
// Standard music control characters
|
|
#define IMON_CHAR_PLAY 0x10 // > Play
|
|
#define IMON_CHAR_RPLAY 0x11 // < Reverse Play
|
|
#define IMON_CHAR_PAUSE 0xA0 // || Pause
|
|
#define IMON_CHAR_RECORD 0x16 // O Record
|
|
|
|
#define IMON_CHAR_TRI_UP 0x1E // ^
|
|
#define IMON_CHAR_TRI_DOWN 0x1F // V
|
|
#define IMON_CHAR_DLB_TRI_UP 0x14
|
|
#define IMON_CHAR_DBL_TRI_DOWN 0x15
|
|
|
|
#define IMON_CHAR_ARROW_UP 0x18
|
|
#define IMON_CHAR_ARROW_DOWN 0x19
|
|
#define IMON_CHAR_ARROW_RIGHT 0x1A
|
|
#define IMON_CHAR_ARROW_LEFT 0x1B
|
|
#define IMON_CHAR_ENTER 0x17
|
|
|
|
#define IMON_CHAR_HOUSE 0x7F
|
|
|
|
#define IMON_CHAR_HEART 0x9D
|
|
|
|
#define IMON_CHAR_BLOCK_FILLED IMON_CHAR_8_BARS
|
|
#define IMON_CHAR_BLOCK_EMPTY ' '
|
|
|
|
#define DEFAULT_DEVICE "/dev/usb/lcd"
|
|
#define DEFAULT_SIZE "16x2"
|
|
|
|
#define VFD_DEFAULT_CELL_WIDTH 5
|
|
#define VFD_DEFAULT_CELL_HEIGHT 8
|
|
|
|
|
|
// 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 = "imon_";
|
|
|
|
|
|
/** private data for the \c imon driver */
|
|
typedef struct imon_private_data {
|
|
char info[255]; /**< info string contents */
|
|
int imon_fd; /**< file descriptor to the display */
|
|
unsigned char *framebuf; /**< fram buffer */
|
|
int height; /**< display height in characters */
|
|
int width; /**< display width in characters */
|
|
int cellwidth; /**< character cell width */
|
|
int cellheight; /**< character cell height */
|
|
} PrivateData;
|
|
|
|
|
|
/**
|
|
* driver initialization
|
|
*/
|
|
/**
|
|
* Initialize the driver.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \return Information of success (1) or failure (< 0).
|
|
*/
|
|
MODULE_EXPORT int imon_init (Driver *drvthis)
|
|
{
|
|
PrivateData *p = NULL;
|
|
|
|
// Alocate, initialize and store private p
|
|
p = (PrivateData *) calloc(1, sizeof(PrivateData));
|
|
if (p == NULL) {
|
|
debug(RPT_ERR, "%s: failed to allocate private data", drvthis->name);
|
|
return -1;
|
|
}
|
|
|
|
if (drvthis->store_private_ptr(drvthis, p)) {
|
|
debug(RPT_ERR, "%s: failed to store private data pointer", drvthis->name);
|
|
return -1;
|
|
}
|
|
|
|
char buf[256];
|
|
p->imon_fd = -1;
|
|
p->width = 0;
|
|
p->height = 0;
|
|
p->cellwidth = VFD_DEFAULT_CELL_WIDTH;
|
|
p->cellheight = VFD_DEFAULT_CELL_HEIGHT;
|
|
|
|
|
|
/* Get settings from config file*/
|
|
|
|
/* Get device */
|
|
strncpy(buf, drvthis->config_get_string(drvthis->name, "Device", 0, DEFAULT_DEVICE), sizeof(buf));
|
|
buf[sizeof(buf)-1] = '\0';
|
|
report(RPT_INFO, "%s: using Device %s", drvthis->name, buf);
|
|
|
|
/* Open device for writing */
|
|
if ((p->imon_fd = open(buf, O_WRONLY)) < 0) {
|
|
report(RPT_ERR, "%s: ERROR opening %s (%s)", drvthis->name, buf, strerror(errno));
|
|
report(RPT_ERR, "%s: Did you load the iMON VFD kernel module?", drvthis->name);
|
|
report(RPT_ERR, "%s: More info in lcdproc/docs/README.imon", drvthis->name);
|
|
return -1;
|
|
}
|
|
|
|
/* Get size settings*/
|
|
strncpy(buf, drvthis->config_get_string(drvthis->name, "Size", 0, 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, DEFAULT_SIZE);
|
|
sscanf(DEFAULT_SIZE , "%dx%d", &p->width, &p->height);
|
|
}
|
|
|
|
/* Make sure the frame buffer is there... */
|
|
p->framebuf = (unsigned char *) malloc(p->width * p->height);
|
|
if (p->framebuf == NULL) {
|
|
report(RPT_ERR, "%s: unable to allocate framebuffer", drvthis->name);
|
|
return -1;
|
|
}
|
|
memset(p->framebuf, ' ', p->width * p->height);
|
|
|
|
report(RPT_DEBUG, "%s: init() done", drvthis->name);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Close the driver (do necessary clean-up).
|
|
* \param drvthis Pointer to driver structure.
|
|
*/
|
|
MODULE_EXPORT void imon_close (Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
if (p != NULL) {
|
|
if (p->imon_fd >= 0)
|
|
close(p->imon_fd);
|
|
|
|
if (p->framebuf != NULL)
|
|
free(p->framebuf);
|
|
p->framebuf = NULL;
|
|
|
|
free(p);
|
|
}
|
|
drvthis->store_private_ptr(drvthis, NULL);
|
|
}
|
|
|
|
|
|
/**
|
|
* Provide some information about this driver.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \return Constant string with information.
|
|
*/
|
|
MODULE_EXPORT const char * imon_get_info (Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
strcpy(p->info, "Soundgraph/Ahanix/Silverstone/Uneed/Accent iMON IR/VFD driver");
|
|
return p->info;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clear the screen.
|
|
* \param drvthis Pointer to driver structure.
|
|
*/
|
|
MODULE_EXPORT void imon_clear (Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
memset(p->framebuf, ' ', p->width * p->height);
|
|
}
|
|
|
|
|
|
/**
|
|
* Flush data on screen to the LCD.
|
|
* \param drvthis Pointer to driver structure.
|
|
*/
|
|
MODULE_EXPORT void imon_flush (Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
write(p->imon_fd, p->framebuf, p->width * p->height);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 imon_string (Driver *drvthis, int x, int y, const char string[])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; string[i] != '\0'; i++)
|
|
imon_chr(drvthis, x+i, y, string[i]);
|
|
}
|
|
|
|
|
|
/**
|
|
* Print a character on the screen at position (x,y).
|
|
* The upper-left corner is (1,1), the lower-right corner is (p->width, p->height).
|
|
* \param drvthis Pointer to driver structure.
|
|
* \param x Horizontal character position (column).
|
|
* \param y Vertical character position (row).
|
|
* \param c Character that gets written.
|
|
*/
|
|
MODULE_EXPORT void imon_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))
|
|
return;
|
|
|
|
p->framebuf[(y * p->width) + x] = c;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 <0 Server core shall define/write the icon.
|
|
*/
|
|
MODULE_EXPORT int imon_icon (Driver *drvthis, int x, int y, int icon)
|
|
{
|
|
switch (icon) {
|
|
case ICON_BLOCK_FILLED:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_BLOCK_FILLED);
|
|
break;
|
|
case ICON_HEART_OPEN:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_BLOCK_EMPTY);
|
|
break;
|
|
case ICON_HEART_FILLED:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_HEART);
|
|
break;
|
|
case ICON_ARROW_UP:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_ARROW_UP);
|
|
break;
|
|
case ICON_ARROW_DOWN:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_ARROW_DOWN);
|
|
break;
|
|
case ICON_ARROW_LEFT:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_ARROW_LEFT);
|
|
break;
|
|
case ICON_ARROW_RIGHT:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_ARROW_RIGHT);
|
|
break;
|
|
case ICON_STOP:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_BLOCK_FILLED);
|
|
imon_chr(drvthis, x+1, y, ' ');
|
|
break;
|
|
case ICON_PAUSE:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_PAUSE);
|
|
imon_chr(drvthis, x+1, y, ' ');
|
|
break;
|
|
case ICON_PLAY:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_PLAY);
|
|
imon_chr(drvthis, x+1, y, ' ');
|
|
break;
|
|
case ICON_PLAYR:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_RPLAY);
|
|
imon_chr(drvthis, x+1, y, ' ');
|
|
break;
|
|
case ICON_FF:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_PLAY);
|
|
imon_chr(drvthis, x+1, y, IMON_CHAR_PLAY);
|
|
break;
|
|
case ICON_FR:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_RPLAY);
|
|
imon_chr(drvthis, x+1, y, IMON_CHAR_RPLAY);
|
|
break;
|
|
case ICON_NEXT:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_PLAY);
|
|
imon_chr(drvthis, x+1, y, '|');
|
|
break;
|
|
case ICON_PREV:
|
|
imon_chr(drvthis, x, y, '|');
|
|
imon_chr(drvthis, x+1, y, IMON_CHAR_RPLAY);
|
|
break;
|
|
case ICON_REC:
|
|
imon_chr(drvthis, x, y, IMON_CHAR_RECORD);
|
|
imon_chr(drvthis, x+1, y, ' ');
|
|
break;
|
|
default:
|
|
/* let the server core do the rest */
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Draw a vertical bar bottom-up.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \param x Horizontal character position (column) of the starting point.
|
|
* \param y Vertical character position (row) of the starting point.
|
|
* \param len Number of characters that the bar is high at 100%
|
|
* \param promille Current height level of the bar in promille.
|
|
* \param options Options (currently unused).
|
|
*/
|
|
MODULE_EXPORT void imon_vbar (Driver *drvthis, int x, int y, int len, int promille, int options)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
// Special characters start at 0 not 1, so pass -1 as first char.
|
|
// This can be safely done as heartbeat icon is not 0
|
|
lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, -1);
|
|
}
|
|
|
|
|
|
/**
|
|
* Draw a horizontal bar to the right.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \param x Horizontal character position (column) of the starting point.
|
|
* \param y Vertical character position (row) of the starting point.
|
|
* \param len Number of characters that the bar is long at 100%
|
|
* \param promille Current length level of the bar in promille.
|
|
* \param options Options (currently unused).
|
|
*/
|
|
MODULE_EXPORT void imon_hbar (Driver *drvthis, int x, int y, int len, int promille, int options)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
int pixels = ((long) 2 * len * p->cellwidth) * promille / 2000;
|
|
int pos;
|
|
|
|
if ((x <= 0) || (y <= 0) || (y > p->height))
|
|
return;
|
|
|
|
for (pos = 0; pos < len; pos++) {
|
|
|
|
if (x + pos > p->width)
|
|
return;
|
|
|
|
#ifndef IMON_HBARS_OLD
|
|
if (pixels >= p->cellwidth ) {
|
|
/* write a "full" block to the screen... */
|
|
imon_chr(drvthis, x+pos, y, IMON_CHAR_BLOCK_FILLED);
|
|
}
|
|
else if (pixels >= 1) {
|
|
/* write a partial block, albeit vertically... */
|
|
imon_chr(drvthis, x+pos, y, pixels * p->cellheight / p->cellwidth);
|
|
}
|
|
#else
|
|
if (pixels >= p->cellwidth * 3/4) {
|
|
/* write a "full" block to the screen... */
|
|
imon_chr(drvthis, x+pos, y, IMON_CHAR_BLOCK_FILLED);
|
|
}
|
|
else if (pixels >= p->cellwidth * 2/4) {
|
|
/* write a partial block... */
|
|
imon_chr(drvthis, x+pos, y, IMON_CHAR_PLAY);
|
|
break;
|
|
}
|
|
else if (pixels >= p->cellwidth * 1/4) {
|
|
/* write a partial block... */
|
|
imon_chr(drvthis, x+pos, y, '>');
|
|
break;
|
|
}
|
|
#endif
|
|
else {
|
|
; // write nothing (not even a space)
|
|
}
|
|
|
|
pixels -= p->cellwidth;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the display width in characters.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \return Number of characters the display is wide.
|
|
*/
|
|
MODULE_EXPORT int imon_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 imon_height (Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
return p->height;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the width of a character in pixels.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \return Number of pixel columns a character cell is wide.
|
|
*/
|
|
MODULE_EXPORT int imon_cellwidth (Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
return p->cellwidth;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the height of a character in pixels.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \return Number of pixel lines a character cell is high.
|
|
*/
|
|
MODULE_EXPORT int imon_cellheight (Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
return p->cellheight;
|
|
}
|
|
|
|
// EOF
|