2009-11-29 09:09:02 +00:00
/** \file server/drivers/SureElec.c
*
* Driver for the LCD modules (actually the controller board) available from
* the 'SURE electronics' shop (http://www.sureelectronics.net).
*
* These devices are PIC based controlled, using a serial communication
* protocol with the host. The actual connection to host is done through
* USB through a serial <-> USB converter (CP2102 USB to UART Bridge)
* integrated on the board.
*/
2010-11-25 07:11:41 +00:00
/*-
2009-11-29 09:09:02 +00:00
* Copyright (C) 2009, Laurent Latil
2010-10-30 18:04:21 +00:00
*
2009-11-29 09:09:02 +00:00
* 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.
2010-10-30 18:04:21 +00:00
*
2009-11-29 09:09:02 +00:00
* 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.
2010-10-30 18:04:21 +00:00
*
2009-11-29 09:09:02 +00:00
* 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
*/
/*
* It should be noted that the communication protocol, while somewhat
* documented in the LCD user's manual, is not complete (ie. the
* initialization sequence is not described), and inacurate on some points.
* This driver is so based on the spying of serial communication using the
* officially supported client softwares (LCD Smarties & SureLCD).
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "lcd.h"
#include "lcd_lib.h"
#include "SureElec.h"
#include "adv_bignum.h"
#include "report.h"
/* API: variables 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 = "SureElec_" ;
typedef enum {
SURE_ELEC_EDITION1 = 1 ,
SURE_ELEC_EDITION2 = 2 ,
SURE_ELEC_EDITION3 = 3 ,
} SureElec_edition ;
/** private data for the \c SureElec driver */
typedef struct SureElec_private_data {
int fd ; /* Serial port file descriptor */
int width , height ; /* Screen size */
int cellwidth , cellheight ; /* Cell size */
/* framebuffer and buffer for old LCD contents */
unsigned char * framebuf ;
unsigned char * backingstore ;
2011-03-28 18:17:58 +00:00
/* definable characters */
2009-11-29 09:09:02 +00:00
CGmode ccmode ;
int output_state ; /**< current output state */
int contrast ; /**< current contrast */
int brightness ;
int offbrightness ;
int backlight ;
SureElec_edition edition ;
} PrivateData ;
static int open_port ( Driver * drvthis , const char * device );
static void get_device_screen_size ( Driver * drvthis , int * width , int * height );
static int write_ ( Driver * drvthis , const unsigned char * buf , size_t count );
static int read_ ( Driver * drvthis , void * buf , size_t count );
/**
* Initialize the driver.
* \param drvthis Pointer to driver structure.
* \retval 0 Success.
* \retval <0 Error.
2011-06-26 13:23:56 +00:00
*
* \todo This function returns at several points without properly freeing
* file descriptors and allocated memory.
2009-11-29 09:09:02 +00:00
*/
MODULE_EXPORT int
SureElec_init ( Driver * drvthis )
{
char device [ 256 ] = DEFAULT_DEVICE ;
char size [ 256 ] = DEFAULT_SIZE ;
int tmp ;
int user_width = - 1 , user_height = - 1 ;
int device_width = - 1 , device_height = - 1 ;
const char * sedition ;
PrivateData * p ;
/* Allocate and store private data */
p = ( PrivateData * ) malloc ( sizeof ( PrivateData ));
if ( p == NULL )
return - 1 ;
if ( drvthis -> store_private_ptr ( drvthis , p ))
return - 1 ;
/* Initialise the PrivateData structure */
p -> fd = - 1 ;
p -> edition = SURE_ELEC_EDITION2 ; /* Assume an edition 2
* version */
p -> cellwidth = CELL_WIDTH ;
p -> cellheight = CELL_HEIGHT ;
p -> framebuf = NULL ;
p -> backingstore = NULL ;
p -> backlight = 0 ;
debug ( RPT_INFO , "SureElec driver: init(%p)" , drvthis );
/* READ CONFIG FILE */
/* Get serial device to use */
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" , drvthis -> name , device );
if ( open_port ( drvthis , device ) == - 1 ) {
return - 1 ;
}
2011-06-26 13:23:56 +00:00
2009-11-29 09:09:02 +00:00
/* Get edition version */
sedition = drvthis -> config_get_string ( drvthis -> name , "Edition" , 0 , "" );
if ( strlen ( sedition ) != 0 ) {
int n = sedition [ 0 ] - '0' ;
if ( strlen ( sedition ) != 1 || n < 1 || n > 3 ) {
report ( RPT_ERR , "%s: unknown edition version %s; must be one of 1, 2, or 3" ,
drvthis -> name , sedition );
return - 1 ;
}
p -> edition = ( SureElec_edition ) n ;
}
/* Get the user-specified display size */
strncpy ( size , drvthis -> config_get_string ( drvthis -> name , "Size" , 0 , "" ), sizeof ( size ));
size [ sizeof ( size ) - 1 ] = '\0' ;
if ( strlen ( size ) != 0 ) {
if (( sscanf ( size , "%dx%d" , & user_width , & user_height ) != 2 )
|| ( user_width <= 0 ) || ( user_width > LCD_MAX_WIDTH )
|| ( user_height <= 0 ) || ( user_height > LCD_MAX_HEIGHT )) {
report ( RPT_ERR , "%s: invalid screen size: %s" ,
drvthis -> name , size );
return - 1 ;
}
}
if ( p -> edition != SURE_ELEC_EDITION1 ) {
/* For edition >1 the device can report screen size */
get_device_screen_size ( drvthis , & device_width , & device_height );
if ( user_width != - 1 ) {
if ( user_width != device_width || user_height != device_height ) {
report ( RPT_WARNING , "%s: device and config settings disagree for screen size: %d x %d <> %d x %d" , drvthis -> name , device_width , device_height , user_width , user_height );
report ( RPT_WARNING , "%s: config specified size applied." , drvthis -> name );
}
device_width = user_width ;
device_height = user_height ;
}
} else {
device_width = user_width ;
device_height = user_height ;
}
if ( device_width == - 1 ) {
report ( RPT_ERR , "%s: screen size must be specified" ,
drvthis -> name );
return - 1 ;
}
/* set screen size */
p -> width = device_width ;
p -> height = device_height ;
report ( RPT_INFO , "%s: screen size %d x %d." , drvthis -> name , p -> width , p -> height );
/* Get contrast */
tmp = drvthis -> config_get_int ( drvthis -> name , "Contrast" , 0 , DEFAULT_CONTRAST );
debug ( RPT_INFO , "%s: Contrast (in config) is '%d'" , drvthis -> name , tmp );
if (( tmp < 0 ) || ( tmp > 1000 )) {
report ( RPT_WARNING , "%s: Contrast value must be between 0 and 1000; using default %d" ,
drvthis -> name , DEFAULT_CONTRAST );
tmp = DEFAULT_CONTRAST ;
}
SureElec_set_contrast ( drvthis , tmp );
/* Get backlight */
tmp = drvthis -> config_get_int ( drvthis -> name , "Brightness" , 0 , DEFAULT_BRIGHTNESS );
debug ( RPT_INFO , "%s: Brightness (in config) is '%d'" , drvthis -> name , tmp );
if (( tmp < 0 ) || ( tmp > 1000 )) {
report ( RPT_WARNING , "%s: Brightness must be between 0 and 1000; using default %d" ,
drvthis -> name , DEFAULT_BRIGHTNESS );
tmp = DEFAULT_BRIGHTNESS ;
}
p -> brightness = tmp ;
/* Which backlight-off "brightness" */
tmp = drvthis -> config_get_int ( drvthis -> name , "OffBrightness" , 0 , DEFAULT_OFFBRIGHTNESS );
debug ( RPT_INFO , "%s: OffBrightness (in config) is '%d'" , __FUNCTION__ , tmp );
if (( tmp < 0 ) || ( tmp > 1000 )) {
report ( RPT_WARNING , "%s: OffBrightness must be between 0 and 1000; using default %d" ,
drvthis -> name , DEFAULT_OFFBRIGHTNESS );
tmp = DEFAULT_OFFBRIGHTNESS ;
}
p -> offbrightness = tmp ;
/* Allocate the frame buffer. */
p -> framebuf = ( unsigned char * ) calloc ( p -> width * p -> height , 1 );
if ( p -> framebuf == NULL ) {
report ( RPT_ERR , "%s: unable to create framebuffer" , drvthis -> name );
return - 1 ;
}
memset ( p -> framebuf , ' ' , p -> width * p -> height );
/* Allocate the backing store. */
p -> backingstore = ( unsigned char * ) malloc ( p -> width * p -> height );
if ( p -> backingstore == NULL ) {
report ( RPT_ERR , "%s: unable to create framebuffer backing store" , drvthis -> name );
return - 1 ;
}
memset ( p -> backingstore , '#' , p -> width * p -> height );
SureElec_clear ( drvthis );
SureElec_flush ( drvthis );
report ( RPT_DEBUG , "%s: init() done" , drvthis -> name );
return 0 ;
}
/**
* Close the driver (do necessary clean-up).
* \param drvthis Pointer to driver structure.
*/
MODULE_EXPORT void
SureElec_close ( Driver * drvthis )
{
PrivateData * p = drvthis -> private_data ;
if ( p != NULL ) {
if ( p -> fd >= 0 )
close ( p -> fd );
/* Free allocated framebuffers */
if ( p -> framebuf )
free ( p -> framebuf );
if ( p -> backingstore )
free ( p -> backingstore );
/* Free our private data */
free ( p );
}
drvthis -> store_private_ptr ( drvthis , NULL );
debug ( RPT_DEBUG , "SureElec: closed" );
}
/**
* Return the display width in characters.
* \param drvthis Pointer to driver structure.
* \return Number of characters the display is wide.
*/
MODULE_EXPORT int
SureElec_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
SureElec_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
SureElec_cellwidth ( Driver * drvthis )
{
return CELL_WIDTH ;
}
/**
* 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
SureElec_cellheight ( Driver * drvthis )
{
return CELL_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
SureElec_string ( Driver * drvthis , int x , int y , const char string [])
{
PrivateData * p = drvthis -> private_data ;
int i ;
/* Convert 1-based coords to 0-based... */
x -- ;
y -- ;
/* Check coord. validity */
if (( y < 0 ) || ( y >= p -> height ))
return ;
for ( i = 0 ; ( string [ i ] != '\0' ) && ( x < p -> width ); i ++ , x ++ ) {
p -> framebuf [( y * p -> width ) + x ] = string [ i ];
}
debug ( RPT_DEBUG , "SureElec: printed string at (%d,%d)" , x , y );
}
/**
* Clear the screen.
* \param drvthis Pointer to driver structure.
*/
MODULE_EXPORT void
SureElec_clear ( Driver * drvthis )
{
PrivateData * p = drvthis -> private_data ;
/* set hbar/vbar/bignum mode back to normal character display */
p -> ccmode = standard ;
/* replace all chars in framebuf with spaces */
memset ( p -> framebuf , ' ' , ( p -> width * p -> height ));
debug ( RPT_DEBUG , "SureElec: cleared screen" );
}
/**
* 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
SureElec_chr ( Driver * drvthis , int x , int y , char c )
{
PrivateData * p = drvthis -> private_data ;
/* Convert 1-based coords to 0-based... */
x -- ;
y -- ;
if (( x >= 0 ) && ( y >= 0 ) && ( x < p -> width ) && ( y < p -> height ))
p -> framebuf [( y * p -> width ) + x ] = c ;
debug ( RPT_DEBUG , "SureElec: writing character %02X to position (%d,%d)" , c , x , y );
}
/**
* Flush data on screen to the LCD.
* \param drvthis Pointer to driver structure.
*/
MODULE_EXPORT void
SureElec_flush ( Driver * drvthis )
{
PrivateData * p = drvthis -> private_data ;
int modified = 0 ;
int i , j ;
unsigned char cmd [ 4 ] = { '\xFE' , '\x47' , '\x01' , /* line */ 0 };
for ( i = 0 ; i < p -> height ; i ++ ) {
/* Check if line content changed */
for ( j = 0 ; j < p -> width && p -> framebuf [ p -> width * i + j ] ==
p -> backingstore [ p -> width * i + j ]; j ++ ) {
;
}
if ( j < p -> width ) {
/*
* Line content has been changed, need to output the
* line on screen
*/
cmd [ 3 ] = i + 1 ;
if ( write_ ( drvthis , cmd , sizeof ( cmd )) == - 1 ||
write_ ( drvthis , & ( p -> framebuf [ p -> width * i ]), p -> width ) == - 1 ) {
return ;
}
modified = 1 ;
}
}
if ( modified ) {
/* If something changed on screen, update the backingstore */
memcpy ( p -> backingstore , p -> framebuf , p -> width * p -> height );
}
debug ( RPT_DEBUG , "SureElec: frame buffer flushed" );
}
/**
* Get current LCD contrast.
* This is only the locally stored contrast, the contrast value
* cannot be retrieved from the LCD.
* \param drvthis Pointer to driver structure.
* \return Stored contrast in promille.
*/
MODULE_EXPORT int
SureElec_get_contrast ( Driver * drvthis )
{
PrivateData * p = drvthis -> private_data ;
return p -> contrast ;
}
/**
* Set the LCD contrast.
* \param drvthis Pointer to driver structure.
* \param promille New contrast value in promille.
*/
MODULE_EXPORT void
SureElec_set_contrast ( Driver * drvthis , int promille )
{
unsigned char cmd [ 3 ] = { '\xFE' , '\x50' , 0 };
PrivateData * p = drvthis -> private_data ;
int real_contrast ;
/* Check it */
if (( promille < 0 ) || ( promille > 1000 ))
return ;
/* Store it */
p -> contrast = promille ;
real_contrast = ( int )(( long ) promille * MAX_CONTRAST / 1000 );
/* And do it */
cmd [ 2 ] = real_contrast ;
write_ ( drvthis , 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 promille.
*/
MODULE_EXPORT int
SureElec_get_brightness ( Driver * drvthis , int state )
{
PrivateData * p = drvthis -> private_data ;
return ( state == BACKLIGHT_ON ) ? p -> brightness : 0 ;
}
/**
* Set on/off brightness.
* \param drvthis Pointer to driver structure.
* \param state Brightness state (on/off) for which we want to store the value.
* \param promille New brightness in promille.
*/
MODULE_EXPORT void
SureElec_set_brightness ( Driver * drvthis , int state , int promille )
{
PrivateData * p = drvthis -> private_data ;
/* Check value */
if ( promille < 0 || promille > 1000 )
return ;
/* store the software value */
if ( state == BACKLIGHT_ON )
p -> brightness = promille ;
else
p -> offbrightness = promille ;
}
/**
* Turn the LCD backlight on or off.
* \param drvthis Pointer to driver structure.
* \param on New backlight status.
*/
MODULE_EXPORT void
SureElec_backlight ( Driver * drvthis , int on )
{
PrivateData * p = drvthis -> private_data ;
int promille ;
if ( BACKLIGHT_ON == on ) {
promille = p -> brightness ;
} else {
promille = p -> offbrightness ;
}
if ( promille == 0 && p -> backlight ) {
/* Turn BL off */
unsigned char cmd [ 2 ] = { '\xFE' , '\x46' };
if ( write_ ( drvthis , cmd , sizeof ( cmd )) == - 1 ) {
return ;
}
report ( RPT_INFO , "SureElec: BL turned off" );
p -> backlight = 0 ;
}
if ( promille > 0 ) {
unsigned char cmd_bl [ 3 ] = { '\xFE' , '\x42' , '\x0' };
unsigned char cmd_brightness [ 3 ] = { '\xFE' , '\x98' , 0 };
int level ;
/* Turn BL on & set brightness level */
if ( ! p -> backlight ) {
if ( write_ ( drvthis , cmd_bl , sizeof ( cmd_bl )) == - 1 ) {
return ;
}
report ( RPT_INFO , "SureElec: BL turned on" );
p -> backlight = 1 ;
}
level = ( int )(( long ) promille * MAX_BACKLIGHT / 1000 );
cmd_brightness [ 2 ] = level ;
write_ ( drvthis , cmd_brightness , sizeof ( cmd_brightness ));
}
}
/**
* 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
SureElec_vbar ( Driver * drvthis , int x , int y , int len , int promille , int options )
{
PrivateData * p = drvthis -> private_data ;
if ( p -> ccmode != vbar ) {
unsigned char vBar [ p -> cellheight ];
int i ;
if ( p -> ccmode != standard ) {
/* Not supported(yet) */
report ( RPT_WARNING , "%s: vbar: cannot combine two modes using user-defined characters" ,
drvthis -> name );
return ;
}
p -> ccmode = vbar ;
memset ( vBar , 0x00 , sizeof ( vBar ));
for ( i = 1 ; i < p -> cellheight ; i ++ ) {
/* add pixel line per pixel line */
vBar [ p -> cellheight - i ] = 0xFF ;
SureElec_set_char ( drvthis , i , vBar );
}
}
lib_vbar_static ( drvthis , x , y , len , promille , options , p -> cellheight , 0 );
}
/**
* 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
SureElec_hbar ( Driver * drvthis , int x , int y , int len , int promille , int options )
{
PrivateData * p = drvthis -> private_data ;
if ( p -> ccmode != hbar ) {
unsigned char hBar [ p -> cellheight ];
int i ;
if ( p -> ccmode != standard ) {
/* Not supported(yet) */
report ( RPT_WARNING , "%s: hbar: cannot combine two modes using user-defined characters" ,
drvthis -> name );
return ;
}
p -> ccmode = hbar ;
memset ( hBar , 0x00 , sizeof ( hBar ));
for ( i = 1 ; i <= p -> cellwidth ; i ++ ) {
/* fill pixel columns from left to right. */
memset ( hBar , 0xFF & ~ (( 1 << ( p -> cellwidth - i )) - 1 ), sizeof ( hBar ));
SureElec_set_char ( drvthis , i , hBar );
}
}
lib_hbar_static ( drvthis , x , y , len , promille , options , p -> cellwidth , 0 );
}
/**
* 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 ':')
*/
MODULE_EXPORT void
SureElec_num ( Driver * drvthis , int x , int num )
{
PrivateData * p = drvthis -> private_data ;
int do_init = 0 ;
if (( num < 0 ) || ( num > 10 ))
return ;
if ( p -> ccmode != bignum ) {
if ( p -> ccmode != standard ) {
/* Not supported (yet) */
report ( RPT_WARNING , "%s: num: cannot combine two modes using user-defined characters" ,
drvthis -> name );
return ;
}
p -> ccmode = bignum ;
do_init = 1 ;
}
/* Lib_adv_bignum does everything needed to show the bignumbers. */
lib_adv_bignum ( drvthis , x , num , 0 , do_init );
}
/**
* Get total number of custom characters available.
* \param drvthis Pointer to driver structure.
* \return Number of custom characters (always NUM_CCs).
*/
MODULE_EXPORT int
SureElec_get_free_chars ( Driver * drvthis )
{
return NUM_CC ;
}
/**
* 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.
*/
MODULE_EXPORT void
SureElec_set_char ( Driver * drvthis , int n , unsigned char * dat )
{
PrivateData * p = drvthis -> private_data ;
unsigned char cmd [ 11 ] = { '\xFE' , '\x4E' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 };
unsigned char mask = ( 1 << CELL_WIDTH ) - 1 ;
int row ;
if (( unsigned int ) n >= NUM_CC )
return ;
if ( ! dat )
return ;
cmd [ 2 ] = n ; /* Custom char to define. */
for ( row = 0 ; row < p -> cellheight ; row ++ ) {
cmd [ row + 3 ] = dat [ row ] & mask ;
}
write_ ( drvthis , cmd , sizeof ( cmd ));
}
/**
* 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
SureElec_icon ( Driver * drvthis , int x , int y , int icon )
{
static unsigned char heart_open [] =
{
b__XXXXX ,
b__X_X_X ,
b_______ ,
b_______ ,
b_______ ,
b__X___X ,
b__XX_XX ,
b__XXXXX
};
static unsigned char heart_filled [] =
{
b__XXXXX ,
b__X_X_X ,
b___X_X_ ,
b___XXX_ ,
b___XXX_ ,
b__X_X_X ,
b__XX_XX ,
b__XXXXX
};
static unsigned char arrow_up [] =
{
b____X__ ,
b___XXX_ ,
b__X_X_X ,
b____X__ ,
b____X__ ,
b____X__ ,
b____X__ ,
b_______
};
static unsigned char arrow_down [] =
{
b____X__ ,
b____X__ ,
b____X__ ,
b____X__ ,
b__X_X_X ,
b___XXX_ ,
b____X__ ,
b_______
};
static unsigned char checkbox_off [] =
{
b_______ ,
b_______ ,
b__XXXXX ,
b__X___X ,
b__X___X ,
b__X___X ,
b__XXXXX ,
b_______
};
static unsigned char checkbox_on [] =
{
b____X__ ,
b____X__ ,
b__XXX_X ,
b__X_XX_ ,
b__X_X_X ,
b__X___X ,
b__XXXXX ,
b_______
};
static unsigned char checkbox_gray [] =
{
b_______ ,
b_______ ,
b__XXXXX ,
b__X_X_X ,
b__XX_XX ,
b__X_X_X ,
b__XXXXX ,
b_______
};
unsigned char * pattern = NULL ;
char charnum = 0 ;
switch ( icon ) {
case ICON_BLOCK_FILLED :
charnum = 0xFF ;
break ;
case ICON_ARROW_LEFT :
charnum = 0x7F ;
break ;
case ICON_ARROW_RIGHT :
charnum = 0x7E ;
break ;
case ICON_ARROW_UP :
charnum = 1 ;
pattern = arrow_up ;
break ;
case ICON_ARROW_DOWN :
charnum = 2 ;
pattern = arrow_down ;
break ;
case ICON_HEART_FILLED :
pattern = heart_filled ;
break ;
case ICON_HEART_OPEN :
pattern = heart_open ;
break ;
case ICON_CHECKBOX_OFF :
charnum = 3 ;
pattern = checkbox_off ;
break ;
case ICON_CHECKBOX_ON :
charnum = 4 ;
pattern = checkbox_on ;
break ;
case ICON_CHECKBOX_GRAY :
charnum = 5 ;
pattern = checkbox_gray ;
break ;
default :
return - 1 ;
}
if ( NULL != pattern ) {
SureElec_set_char ( drvthis , charnum , pattern );
}
SureElec_chr ( drvthis , x , y , charnum );
return 0 ;
}
/**
* Open the communication port and sends the initialization 'magic' sequence
* to the device
* \param drvthis Pointer to driver structure.
* \param device Name of the device to use.
* \retval 0 Success
* \retval <0 Error opening port or writing init sequence
*/
static int
open_port ( Driver * drvthis , const char * device )
{
PrivateData * p = drvthis -> private_data ;
struct termios portset ;
unsigned char cmd [ 3 ] = { '\xFE' , '\x56' , 0 };
unsigned char init_seq [ 7 ] = { '\x54' , '\x58' , '\x4B' , '\x52' , '\x44' , '\x41' , '\x60' };
int i ;
2011-06-26 13:23:56 +00:00
/* Set up io port correctly, and open it... */
p -> fd = open ( device , O_RDWR | O_NOCTTY );
if ( p -> fd == - 1 ) {
2009-11-29 09:09:02 +00:00
report ( RPT_ERR , "%s: open(%s) failed (%s)" , drvthis -> name , device , strerror ( errno ));
2011-06-26 13:23:56 +00:00
if ( errno == EACCES )
report ( RPT_ERR , "%s: %s device could not be opened" , drvthis -> name , device );
2009-11-29 09:09:02 +00:00
return - 1 ;
}
2011-06-26 13:23:56 +00:00
report ( RPT_INFO , "%s: opened display on %s" , drvthis -> name , device );
2009-11-29 09:09:02 +00:00
2011-06-26 13:23:56 +00:00
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 );
2009-11-29 09:09:02 +00:00
portset . c_oflag &= ~ OPOST ;
2011-06-26 13:23:56 +00:00
portset . c_lflag &= ~ ( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
portset . c_cflag &= ~ ( CSIZE | PARENB | CRTSCTS );
2009-11-29 09:09:02 +00:00
portset . c_cflag |= CS8 | CREAD | CLOCAL ;
2011-06-26 13:23:56 +00:00
#endif
/* Set timeouts */
2009-11-29 09:09:02 +00:00
portset . c_cc [ VMIN ] = 1 ;
2011-06-26 13:23:56 +00:00
portset . c_cc [ VTIME ] = 3 ;
2009-11-29 09:09:02 +00:00
/* Set port speed */
cfsetospeed ( & portset , B9600 );
2011-06-26 13:23:56 +00:00
cfsetispeed ( & portset , B0 );
2009-11-29 09:09:02 +00:00
/* Do it... */
2011-06-26 13:23:56 +00:00
if ( tcsetattr ( p -> fd , TCSANOW , & portset ) == - 1 ) {
report ( RPT_ERR , "%s: failed to configure port (%s)" , drvthis -> name , strerror ( errno ));
return - 1 ;
}
2009-11-29 09:09:02 +00:00
/* Send initialization sequence */
for ( i = 1 ; i <= 8 ; i ++ ) {
cmd [ 2 ] = i ;
if ( write_ ( drvthis , cmd , sizeof ( cmd )) == - 1 )
return - 1 ;
}
for ( i = 0 ; i < sizeof ( init_seq ); i ++ ) {
if ( write_ ( drvthis , ( const unsigned char * ) " \x0FE " , 1 ) == - 1
|| write_ ( drvthis , & ( init_seq [ i ]), 1 ) == - 1 )
return - 1 ;
}
return 0 ;
}
/**
* Retrieves the display size from the device.
* \param drvthis Pointer to driver structure.
* \param width Pointer to variable storing display width
* \param height Pointer to variable storing display height
*/
static void
get_device_screen_size ( Driver * drvthis , int * width , int * height )
{
unsigned char cmd [ 2 ] = { '\xFE' , '\x76' };
char result [ 11 ];
if ( write_ ( drvthis , cmd , sizeof ( cmd )) == - 1 )
return ;
if ( read_ ( drvthis , result , sizeof ( result )) != - 1 ) {
* width = ( int )( result [ 0 ] - '0' ) * 10 + ( int )( result [ 1 ] - '0' );
* height = ( int )( result [ 2 ] - '0' ) * 10 + ( int )( result [ 3 ] - '0' );
}
}
/**
* Writes count bytes from a buffer to the device.
* \param drvthis Pointer to driver structure
* \param buf Pointer to the buffer
* \param count Number of bytes to write
* \retval -1 upon error
*/
static int
write_ ( Driver * drvthis , const unsigned char * buf , size_t count )
{
int write_count = 0 , written ;
PrivateData * p = drvthis -> private_data ;
while ( write_count < count ) {
written = write ( p -> fd , & buf [ write_count ], count - write_count );
if ( written == - 1 ) {
report ( RPT_ERR , "SureElec: cannot write to port" );
return - 1 ;
}
write_count += written ;
}
return write_count ;
}
/**
* Reads bytes in a buffer from the device.
* \param drvthis Pointer to driver structure
* \param buf Pointer to the buffer
* \param count Number of bytes to read
* \retval -1 upon error
*/
static int
read_ ( Driver * drvthis , void * buf , size_t count )
{
fd_set rfds ;
struct timeval tv ;
PrivateData * p = drvthis -> private_data ;
int read_count ;
/* Watch fd to see when it has input. */
FD_ZERO ( & rfds );
FD_SET ( p -> fd , & rfds );
read_count = 0 ;
while ( read_count < count ) {
int retval ;
/*
* For compatibility, better to reset the timeout delay (may
* be modified in select() call).
*/
tv . tv_sec = 1 ; /* seconds */
tv . tv_usec = 0 ; /* microseconds */
retval = select ( p -> fd + 1 , & rfds , NULL , NULL , & tv );
if ( retval ) {
int read_result = read ( p -> fd , (( char * ) buf ) + read_count , count - read_count );
if ( read_result < 0 ) {
return - 1 ;
} else {
read_count += read_result ;
}
} else {
report ( RPT_ERR , "SureElec: No answer from device" );
return - 1 ;
}
}
return read_count ;
}