234 lines
5.3 KiB
C
234 lines
5.3 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
|
|
#include "event.h"
|
|
#include "lcd.h"
|
|
#include "logging.h"
|
|
|
|
#define _LCD_DEBUG 1
|
|
|
|
#define A125_CMD_GETBUTTON { 0x4D, 0x06 } // not tried
|
|
#define A125_CMD_SETLINE { 0x4D, 0x0C, 0x00, 0x10 } // [2] is line, append 16 chars
|
|
#define A125_CMD_CLEAR { 0x4D, 0x0D } // works, but slow
|
|
#define A125_CMD_BACKLIGHT { 0x4D, 0x5E, 0x00 } // [2] is on/off
|
|
#define A125_CMD_RESET { 0x4D, 0xFF }
|
|
|
|
#define A125_EVENT_ID { 0x53, 0x01 } // never seen
|
|
#define A125_EVENT_BUTTON1 { 0x53, 0x05, 0x00, 0x01 }
|
|
#define A125_EVENT_BUTTON2 { 0x53, 0x05, 0x00, 0x02 }
|
|
#define A125_EVENT_VERSION { 0x53, 0x08 } // never seen
|
|
#define A125_EVENT_ACTINFO { 0x53, 0xAA }
|
|
#define A125_EVENT_INVALID { 0x53, 0xFB } // never seen
|
|
|
|
struct lcddev {
|
|
int fd;
|
|
char device[32];
|
|
struct termios oldtio;
|
|
|
|
int backlight_timeout;
|
|
int (*button_cb)(struct lcddev *dev, int button, void *privdata);
|
|
void *privdata;
|
|
|
|
struct event_fd *read_event;
|
|
struct event_timeout *backlight_event;
|
|
};
|
|
|
|
void lcd_close(struct lcddev *dev)
|
|
{
|
|
tcsetattr(dev->fd, TCSANOW, &dev->oldtio);
|
|
close(dev->fd);
|
|
}
|
|
|
|
static int lcd_open(struct lcddev *dev, const char *device)
|
|
{
|
|
int fd = open(device, O_RDWR);
|
|
if (fd < 0) {
|
|
log_print(LOG_ERROR, "failed to open '%s'", device);
|
|
return -1;
|
|
}
|
|
|
|
strncpy(dev->device, device, sizeof(dev->device));
|
|
dev->fd = fd;
|
|
|
|
tcgetattr(dev->fd, &dev->oldtio);
|
|
|
|
struct termios newtio;
|
|
memset(&newtio, 0, sizeof(newtio));
|
|
newtio.c_iflag |= IGNBRK;
|
|
newtio.c_lflag &= ~(ISIG | ICANON | ECHO);
|
|
newtio.c_cflag = B1200 | CS8 | CLOCAL | CREAD;
|
|
newtio.c_cc[VMIN] = 1;
|
|
newtio.c_cc[VTIME] = 0;
|
|
cfsetospeed(&newtio, B1200);
|
|
cfsetispeed(&newtio, B1200);
|
|
|
|
int err = tcsetattr(dev->fd, TCSAFLUSH, &newtio);
|
|
if (err < 0) {
|
|
log_print(LOG_ERROR, "failed to set termios");
|
|
close(dev->fd);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lcd_read(struct lcddev *dev, const char *buf, int len)
|
|
{
|
|
int retval = 0, i, cnt = 0;
|
|
while (cnt < len) {
|
|
retval = read(dev->fd, (char *)buf + cnt, len - cnt);
|
|
if (retval <= 0)
|
|
break;
|
|
|
|
cnt += retval;
|
|
}
|
|
#if _LCD_DEBUG
|
|
fprintf(stderr, "lcd_read[%d/%d]: ", cnt, len);
|
|
for (i = 0; i < cnt; i++)
|
|
fprintf(stderr, "0x%X ", (unsigned char)buf[i]);
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
return (cnt != 0) ? cnt : retval;
|
|
}
|
|
|
|
static int lcd_write(struct lcddev *dev, char *buf, int len)
|
|
{
|
|
int retval, i;
|
|
|
|
retval = write(dev->fd, buf, len);
|
|
#if _LCD_DEBUG
|
|
fprintf(stderr, "lcd_write[%d/%d]: ", retval, len);
|
|
for (i = 0; i < retval; i++)
|
|
fprintf(stderr, "0x%X ", (unsigned char)buf[i]);
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
return retval;
|
|
}
|
|
|
|
static int lcd_reset(struct lcddev *dev)
|
|
{
|
|
char cmd[] = A125_CMD_RESET;
|
|
lcd_write(dev, cmd, sizeof(cmd));
|
|
|
|
char event[2], expect[] = A125_EVENT_ACTINFO;
|
|
if (lcd_read(dev, event, sizeof(event)) != sizeof(event))
|
|
return -1;
|
|
|
|
return !memcmp(event, expect, sizeof(event));
|
|
}
|
|
|
|
static void lcd_backlight(struct lcddev *dev, int mode)
|
|
{
|
|
char cmd[] = A125_CMD_BACKLIGHT;
|
|
|
|
cmd[2] = (mode) ? 0x01 : 0x00;
|
|
lcd_write(dev, cmd, sizeof(cmd));
|
|
}
|
|
|
|
int lcd_setline(struct lcddev *dev, int line, const char *buf)
|
|
{
|
|
char cmd[20] = A125_CMD_SETLINE;
|
|
cmd[2] = (line) ? 0x01 : 0x00;
|
|
|
|
memset(cmd +4, ' ', 16);
|
|
int len = strlen(buf);
|
|
memcpy(cmd +4, buf, (len > 16) ? 16 : len);
|
|
|
|
lcd_write(dev, cmd, sizeof(cmd));
|
|
return 0;
|
|
}
|
|
|
|
static int lcd_backlight_timeout_cb(void *privdata)
|
|
{
|
|
struct lcddev *dev = (struct lcddev *)privdata;
|
|
dev->backlight_event = NULL;
|
|
|
|
lcd_backlight(dev, 0);
|
|
return -1; /* singleshot */
|
|
}
|
|
|
|
static void lcd_trigger_backlight(struct lcddev *dev)
|
|
{
|
|
if (dev->backlight_event != NULL) {
|
|
event_remove_timeout(dev->backlight_event);
|
|
dev->backlight_event = NULL;
|
|
|
|
} else {
|
|
lcd_backlight(dev, 1);
|
|
}
|
|
|
|
struct timeval tv = { .tv_sec = dev->backlight_timeout };
|
|
dev->backlight_event = event_add_timeout(&tv, lcd_backlight_timeout_cb, dev);
|
|
}
|
|
|
|
static int lcd_read_cb(int fd, void *privdata)
|
|
{
|
|
struct lcddev *dev = (struct lcddev *)privdata;
|
|
char buf[4];
|
|
|
|
if (lcd_read(dev, buf, sizeof(buf)) != sizeof(buf)) {
|
|
log_print(LOG_WARN, "lcd_read_cb(): invalid read");
|
|
return 0;
|
|
}
|
|
|
|
int retval = 0;
|
|
char expect1[] = A125_EVENT_BUTTON1;
|
|
char expect2[] = A125_EVENT_BUTTON2;
|
|
|
|
if (memcmp(buf, expect1, sizeof(buf)) == 0) {
|
|
retval = dev->button_cb(dev, LCD_BUTTON1, dev->privdata);
|
|
|
|
} else if (memcmp(buf, expect2, sizeof(buf)) == 0) {
|
|
retval = dev->button_cb(dev, LCD_BUTTON2, dev->privdata);
|
|
}
|
|
|
|
if (retval) {
|
|
lcd_trigger_backlight(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct lcddev * lcd_init(const char *devicename,
|
|
int backlight_timeout,
|
|
int (*button_cb)(struct lcddev *dev, int button, void *privdata),
|
|
void *privdata)
|
|
{
|
|
struct lcddev *dev = malloc(sizeof(struct lcddev));
|
|
if (dev == NULL) {
|
|
log_print(LOG_ERROR, "lcd_init(): out of memory");
|
|
return NULL;
|
|
}
|
|
|
|
memset(dev, 0, sizeof(struct lcddev));
|
|
|
|
if (lcd_open(dev, devicename) < 0) {
|
|
free(dev);
|
|
return NULL;
|
|
}
|
|
|
|
if (lcd_reset(dev) < 0) {
|
|
log_print(LOG_ERROR, "lcd_init(): could not reset LCD");
|
|
lcd_close(dev);
|
|
free(dev);
|
|
return NULL;
|
|
}
|
|
|
|
dev->backlight_timeout = backlight_timeout;
|
|
lcd_trigger_backlight(dev);
|
|
|
|
dev->button_cb = button_cb;
|
|
dev->privdata = privdata;
|
|
button_cb(dev, LCD_BUTTON0, dev->privdata);
|
|
|
|
dev->read_event = event_add_readfd(NULL, dev->fd, lcd_read_cb, dev);
|
|
return dev;
|
|
}
|