check for correct device response; add timeouts for socket (fixes LCDd hang up
e.g. when device disappear) (all by M. Bialonczyk)
This commit is contained in:
@@ -30,6 +30,7 @@ v.0.5dev (ongoing development)
|
||||
* ula200: convert custom characters to 8 byte bitmap
|
||||
* Build system: Fix trying to install svga driver twice
|
||||
+ imon: Add bignum support (Y. Scheglyuk)
|
||||
* hd44780/ethlcd: fix LCDd hang up on network error (exits now) (M. Bialonczyk)
|
||||
|
||||
v0.5.4
|
||||
* Update driver includes and LDFLAGS (fixed glk and bayrad binding error)
|
||||
|
||||
+128
-57
@@ -1,40 +1,46 @@
|
||||
/** \file server/drivers/hd44780-ethlcd.c
|
||||
* \c ethlcd connection type of \c hd44780 driver for Hitachi HD44780 based LCD displays.
|
||||
* \c ethlcd connection type of \c hd44780 driver for Hitachi HD44780 based
|
||||
* LCD displays.
|
||||
*
|
||||
* ethlcd is an open source project based on Atmel's ATmega microcontroller
|
||||
* and ENC28J60 ethernet controller. The device is connected via ethernet, has
|
||||
* its own IP address and is available via TCP protocol. More info at project
|
||||
* homepage: http://manio.skyboo.net/ethlcd/
|
||||
*/
|
||||
|
||||
/*
|
||||
* ethlcd ethernet driver module for Hitachi HD44780 based LCD displays
|
||||
* ethlcd is an opensource project based on atmel's atmega microcontroller
|
||||
* and enc28j60 ethernet device. The device is connected via ethernet, has own
|
||||
* IP address and is available via TCP protocol. More info at project homepage:
|
||||
* http://manio.skyboo.net/ethlcd/
|
||||
*
|
||||
/*-
|
||||
* Copyright (c) 2008 Mariusz Bialonczyk <manio@skyboo.net>
|
||||
*
|
||||
* This file is released under the GNU General Public License. Refer to the
|
||||
* COPYING file distributed with this package.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lcd.h"
|
||||
#include "hd44780-ethlcd.h"
|
||||
#include "hd44780-low.h"
|
||||
#include "shared/sockets.h"
|
||||
#include "report.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
void ethlcd_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch);
|
||||
unsigned char ethlcd_HD44780_scankeypad(PrivateData *p);
|
||||
void ethlcd_HD44780_backlight(PrivateData *p, unsigned char state);
|
||||
void ethlcd_HD44780_close(PrivateData *p);
|
||||
// fake pause function (pausing is handled by ethlcd device itself)
|
||||
void ethlcd_HD44780_uPause(PrivateData *p, int usecs) {}
|
||||
|
||||
/* helper function */
|
||||
static void ethlcd_send_low(PrivateData *p, unsigned char *data, int length);
|
||||
|
||||
/* fake pause function (pausing is handled by ethlcd device itself) */
|
||||
void
|
||||
ethlcd_HD44780_uPause(PrivateData *p, int usecs)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the driver.
|
||||
@@ -42,12 +48,14 @@ void ethlcd_HD44780_uPause(PrivateData *p, int usecs) {}
|
||||
* \retval 0 Success.
|
||||
* \retval -1 Error.
|
||||
*/
|
||||
int hd_init_ethlcd(Driver *drvthis)
|
||||
int
|
||||
hd_init_ethlcd(Driver *drvthis)
|
||||
{
|
||||
char hostname[256];
|
||||
unsigned long flags = 0;
|
||||
struct timeval tv;
|
||||
|
||||
PrivateData *p = (PrivateData*) drvthis->private_data;
|
||||
PrivateData *p = (PrivateData *) drvthis->private_data;
|
||||
HD44780_functions *hd44780_functions = p->hd44780_functions;
|
||||
|
||||
hd44780_functions->senddata = ethlcd_HD44780_senddata;
|
||||
@@ -56,7 +64,7 @@ int hd_init_ethlcd(Driver *drvthis)
|
||||
hd44780_functions->uPause = ethlcd_HD44780_uPause;
|
||||
hd44780_functions->close = ethlcd_HD44780_close;
|
||||
|
||||
//reading configuration file
|
||||
/* reading configuration file */
|
||||
strncpy(hostname, drvthis->config_get_string(drvthis->name, "Device", 0, "ethlcd"), sizeof(hostname));
|
||||
hostname[sizeof(hostname) - 1] = '\0';
|
||||
|
||||
@@ -67,28 +75,40 @@ int hd_init_ethlcd(Driver *drvthis)
|
||||
return -1;
|
||||
}
|
||||
|
||||
//we need to have a blocking read back again:
|
||||
if (fcntl(p->sock, F_GETFL, &flags) < 0)
|
||||
{
|
||||
/* we need to have a blocking read back again: */
|
||||
if (fcntl(p->sock, F_GETFL, &flags) < 0) {
|
||||
report(RPT_ERR, "%s[%s]: Cannot obtain current flags: %s",
|
||||
drvthis->name, ETHLCD_DRV_NAME, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
flags &= ~O_NONBLOCK;
|
||||
if (fcntl(p->sock, F_SETFL, flags) < 0)
|
||||
{
|
||||
if (fcntl(p->sock, F_SETFL, flags) < 0) {
|
||||
report(RPT_ERR, "%s[%s]: Unable to change socket to O_NONBLOCK: %s",
|
||||
drvthis->name, ETHLCD_DRV_NAME, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set up two-line, small character (5x8) mode
|
||||
/* setting timeouts */
|
||||
tv.tv_sec = ETHLCD_TIMEOUT;
|
||||
tv.tv_usec = 0;
|
||||
if (setsockopt(p->sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(struct timeval)) < 0) {
|
||||
report(RPT_ERR, "%s[%s]: Cannot set receive timeout: %s",
|
||||
drvthis->name, ETHLCD_DRV_NAME, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(p->sock, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, sizeof(struct timeval)) < 0) {
|
||||
report(RPT_ERR, "%s[%s]: Cannot set send timeout: %s",
|
||||
drvthis->name, ETHLCD_DRV_NAME, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set up two-line, small character (5x8) mode */
|
||||
hd44780_functions->senddata(p, 0, RS_INSTR, FUNCSET | IF_4BIT | TWOLINE | SMALLCHAR);
|
||||
|
||||
common_init(p, IF_4BIT);
|
||||
|
||||
if (p->have_keypad) {
|
||||
// Remember which input lines are stuck
|
||||
/* Remember which input lines are stuck */
|
||||
p->stuckinputs = 0;
|
||||
}
|
||||
|
||||
@@ -103,51 +123,55 @@ int hd_init_ethlcd(Driver *drvthis)
|
||||
* \param flags Defines whether to end a command or data.
|
||||
* \param ch The value to send.
|
||||
*/
|
||||
void ethlcd_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch)
|
||||
void
|
||||
ethlcd_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch)
|
||||
{
|
||||
static char buff[2];
|
||||
static unsigned char buff[2];
|
||||
|
||||
if (flags == RS_INSTR)
|
||||
buff[0] = ETHLCD_SEND_INSTR;
|
||||
else //if (flags == RS_DATA)
|
||||
else /* RS_DATA */
|
||||
buff[0] = ETHLCD_SEND_DATA;
|
||||
buff[1] = ch;
|
||||
|
||||
sock_send(p->sock, buff, 2); //send data to device
|
||||
sock_recv(p->sock, &buff, 1); //wait for reply
|
||||
ethlcd_send_low(p, buff, 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read keypress.
|
||||
* Read key press.
|
||||
* \param p Pointer to driver's private data structure.
|
||||
* \return Bitmap of the pressed keys.
|
||||
*/
|
||||
unsigned char ethlcd_HD44780_scankeypad(PrivateData *p)
|
||||
unsigned char
|
||||
ethlcd_HD44780_scankeypad(PrivateData *p)
|
||||
{
|
||||
unsigned char readval;
|
||||
static char buff[2];
|
||||
static unsigned char buff[2];
|
||||
|
||||
buff[0] = ETHLCD_GET_BUTTONS;
|
||||
|
||||
sock_send(p->sock, buff, 1); //send data to device
|
||||
sock_recv(p->sock, &buff, 2); //wait for answer
|
||||
ethlcd_send_low(p, buff, 1);
|
||||
|
||||
if (buff[0] != ETHLCD_GET_BUTTONS) //check if this is true reply for our packet
|
||||
return 0;
|
||||
//answer should be in second byte on bits 0-6 in negative logic:
|
||||
/* answer should be in second byte on bits 0-6 in negative logic: */
|
||||
readval = buff[1];
|
||||
|
||||
readval = ~readval & 0x3F; //reverse logic
|
||||
switch (readval)
|
||||
{
|
||||
case 1: return(0x34); //C
|
||||
case 2: return(0x24); //B
|
||||
case 4: return(0x14); //A
|
||||
case 8: return(0x25); //F
|
||||
case 16: return(0x15); //E
|
||||
case 32: return(0x44); //D
|
||||
default: return('\0');
|
||||
readval = ~readval & 0x3F; /* reverse logic */
|
||||
switch (readval) {
|
||||
case 1:
|
||||
return (0x34); /* C */
|
||||
case 2:
|
||||
return (0x24); /* B */
|
||||
case 4:
|
||||
return (0x14); /* A */
|
||||
case 8:
|
||||
return (0x25); /* F */
|
||||
case 16:
|
||||
return (0x15); /* E */
|
||||
case 32:
|
||||
return (0x44); /* D */
|
||||
default:
|
||||
return ('\0');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,14 +181,14 @@ unsigned char ethlcd_HD44780_scankeypad(PrivateData *p)
|
||||
* \param p Pointer to driver's private data structure.
|
||||
* \param state New backlight status.
|
||||
*/
|
||||
void ethlcd_HD44780_backlight(PrivateData *p, unsigned char state)
|
||||
void
|
||||
ethlcd_HD44780_backlight(PrivateData *p, unsigned char state)
|
||||
{
|
||||
static char buff[2];
|
||||
static unsigned char buff[2];
|
||||
|
||||
buff[0] = ETHLCD_SET_BACKLIGHT;
|
||||
|
||||
if (state == BACKLIGHT_ON)
|
||||
{
|
||||
if (state == BACKLIGHT_ON) {
|
||||
if (p->brightness >= 500)
|
||||
buff[1] = ETHLCD_BACKLIGHT_ON;
|
||||
else
|
||||
@@ -173,8 +197,7 @@ void ethlcd_HD44780_backlight(PrivateData *p, unsigned char state)
|
||||
else
|
||||
buff[1] = ETHLCD_BACKLIGHT_OFF;
|
||||
|
||||
sock_send(p->sock, buff, 2); //send data to device
|
||||
sock_recv(p->sock, &buff, 1); //wait for reply
|
||||
ethlcd_send_low(p, buff, 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +205,55 @@ void ethlcd_HD44780_backlight(PrivateData *p, unsigned char state)
|
||||
* Close the driver (do necessary clean-up).
|
||||
* \param p Pointer to driver's private data structure.
|
||||
*/
|
||||
void ethlcd_HD44780_close(PrivateData *p)
|
||||
void
|
||||
ethlcd_HD44780_close(PrivateData *p)
|
||||
{
|
||||
sock_close(p->sock);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send the data to ethlcd device.
|
||||
* \param p Pointer to driver's private data structure.
|
||||
* \param data Pointer to buffer with data to send.
|
||||
* \param length Number of bytes to send.
|
||||
*
|
||||
* \todo This functions makes LCDd exit on fatal error without any clean-up.
|
||||
* There is currently no way to make LCDd exit cleanly in those cases.
|
||||
*/
|
||||
static void
|
||||
ethlcd_send_low(PrivateData *p, unsigned char *data, int length)
|
||||
{
|
||||
int response_len, len;
|
||||
unsigned char cmd;
|
||||
|
||||
/* Send data to device */
|
||||
cmd = data[0]; /* store command byte for verification */
|
||||
len = sock_send(p->sock, data, length);
|
||||
if (len <= 0) {
|
||||
p->hd44780_functions->drv_report(RPT_CRIT, "%s: Write to socket failed: %s. Exiting",
|
||||
ETHLCD_DRV_NAME, strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Check if this is a command with reply */
|
||||
if (cmd == ETHLCD_GET_BUTTONS)
|
||||
response_len = 2;
|
||||
else
|
||||
response_len = 1;
|
||||
|
||||
/* Wait for reply */
|
||||
len = sock_recv(p->sock, data, response_len);
|
||||
if (len <= 0) {
|
||||
p->hd44780_functions->drv_report(RPT_CRIT, "%s: Read from socket failed: %s. Exiting",
|
||||
ETHLCD_DRV_NAME, strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Check reply */
|
||||
if (data[0] != cmd) {
|
||||
p->hd44780_functions->drv_report(RPT_CRIT, "%s: Invalid device response (want 0x%02X, got 0x%02X). Exiting",
|
||||
ETHLCD_DRV_NAME, cmd, data[0]);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
#ifndef HD_ETHLCD_H
|
||||
#define HD_ETHLCD_H
|
||||
|
||||
#include "lcd.h" /* for Driver */
|
||||
|
||||
// initialise this particular driver
|
||||
/* initialise this particular driver */
|
||||
int hd_init_ethlcd(Driver *drvthis);
|
||||
|
||||
#define ETHLCD_DRV_NAME "ethlcd"
|
||||
#define DEFAULT_ETHLCD_PORT 2425
|
||||
#define ETHLCD_DRV_NAME "ethlcd"
|
||||
#define DEFAULT_ETHLCD_PORT 2425
|
||||
#define ETHLCD_TIMEOUT 5
|
||||
|
||||
// ethlcd protocol constants:
|
||||
/* ethlcd protocol constants: */
|
||||
#define ETHLCD_SEND_INSTR 0x01
|
||||
#define ETHLCD_SEND_DATA 0x02
|
||||
#define ETHLCD_GET_BUTTONS 0x03
|
||||
|
||||
Reference in New Issue
Block a user