|
|
@ -30,11 +30,13 @@ |
|
|
|
|
|
|
|
#include "event.h" |
|
|
|
#include "lcd.h" |
|
|
|
#include "list.h" |
|
|
|
#include "logging.h" |
|
|
|
|
|
|
|
#define _LCD_DEBUG 1 |
|
|
|
#define _LCD_DUMMY 1 |
|
|
|
|
|
|
|
#define LCD_SCROLL_SPEED 750 /* 750ms */ |
|
|
|
#define LCD_RESET_TIMEOUT 250 /* 250ms */ |
|
|
|
#define LCD_RESET_RETRY_TIMEOUT 10000 /* 10s */ |
|
|
|
|
|
|
@ -51,6 +53,14 @@ |
|
|
|
#define A125_EVENT_ACTINFO { 0x53, 0xAA } |
|
|
|
#define A125_EVENT_INVALID { 0x53, 0xFB } // never seen |
|
|
|
|
|
|
|
struct lcdpage { |
|
|
|
struct list_head list; |
|
|
|
int priority; |
|
|
|
|
|
|
|
int (*callback)(struct lcddev *dev, int event, void *privdata); |
|
|
|
void *privdata; |
|
|
|
}; |
|
|
|
|
|
|
|
enum lcdstate { |
|
|
|
LCD_STATE_NOT_INITIALIZED = 0x00, |
|
|
|
LCD_STATE_INITIALIZING, |
|
|
@ -67,14 +77,14 @@ struct lcddev { |
|
|
|
|
|
|
|
enum lcdstate state; |
|
|
|
|
|
|
|
int backlight_state; |
|
|
|
int backlight_enabled; |
|
|
|
int backlight_timeout_ms; |
|
|
|
|
|
|
|
int (*event_callback)(struct lcddev *dev, int event, void *privdata); |
|
|
|
void *event_privdata; |
|
|
|
struct list_head page_list; |
|
|
|
struct lcdpage *current_page; |
|
|
|
|
|
|
|
const char *line_data[2]; |
|
|
|
int line_length[2]; |
|
|
|
int scroll_speed; |
|
|
|
int scroll_pos; |
|
|
|
|
|
|
|
struct event_fd *read_event; |
|
|
@ -84,8 +94,20 @@ struct lcddev { |
|
|
|
struct event_timeout *update_timeout; |
|
|
|
}; |
|
|
|
|
|
|
|
static void lcd_pagecallback(struct lcddev *dev, int event); |
|
|
|
|
|
|
|
void lcd_close(struct lcddev *dev) |
|
|
|
{ |
|
|
|
if (dev->current_page) { |
|
|
|
dev->current_page->callback(dev, LCDPAGE_EVENT_EXIT, dev->current_page->privdata); |
|
|
|
dev->current_page = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
struct lcdpage *search, *tmp; |
|
|
|
list_for_each_entry_safe(search, tmp, &dev->page_list, list) { |
|
|
|
free(search); |
|
|
|
} |
|
|
|
|
|
|
|
if (dev->reset_timeout) { |
|
|
|
event_remove_timeout(dev->reset_timeout); |
|
|
|
dev->reset_timeout = NULL; |
|
|
@ -130,12 +152,18 @@ void lcd_close(struct lcddev *dev) |
|
|
|
|
|
|
|
static int lcd_realdevice_open(struct lcddev *dev, const char *device) |
|
|
|
{ |
|
|
|
dev->fd = open(device, O_RDWR | O_NOCTTY | O_CLOEXEC); |
|
|
|
dev->fd = open(device, O_RDWR | O_NOCTTY); |
|
|
|
if (dev->fd < 0) { |
|
|
|
log_print(LOG_ERROR, "%s(): failed to open '%s'", __FUNCTION__, device); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
if (fcntl(dev->fd, F_SETFD, FD_CLOEXEC) < 0) { |
|
|
|
log_print(LOG_ERROR, "%s(): fcntl(FD_CLOEXEC)", __FUNCTION__); |
|
|
|
close(dev->fd); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
tcgetattr(dev->fd, &dev->oldtio); |
|
|
|
|
|
|
|
struct termios newtio; |
|
|
@ -208,14 +236,14 @@ static int lcd_fakedevice_open(struct lcddev *dev) |
|
|
|
#endif /* (_LCD_DUMMY) */ |
|
|
|
|
|
|
|
#if (_LCD_DEBUG > 1) |
|
|
|
static void lcd_dump(const char *prefix, int len, int size, const char *buf) |
|
|
|
static void lcd_dump(const char *prefix, int len, int size, const char *data) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
int pos = 0; |
|
|
|
char buf[256]; |
|
|
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
|
|
pos += snprintf(buf, sizeof(buf) - pos, "0x%X ", (unsigned char)buf[i]); |
|
|
|
pos += snprintf(buf, sizeof(buf) - pos, "0x%X ", (unsigned char)data[i]); |
|
|
|
} |
|
|
|
|
|
|
|
log_print(LOG_DEBUG, "%s:[%d/%d]: %s", prefix, len, size, buf); |
|
|
@ -248,6 +276,8 @@ static int lcd_write(struct lcddev *dev, char *buf, int len) |
|
|
|
return retval; |
|
|
|
} |
|
|
|
|
|
|
|
static void lcd_reset(struct lcddev *dev); |
|
|
|
|
|
|
|
static int lcd_reset_retry_timeout_cb(int timerid, void *privdata) |
|
|
|
{ |
|
|
|
struct lcddev *dev = (struct lcddev *)privdata; |
|
|
@ -269,7 +299,7 @@ static int lcd_reset_timeout_cb(int timerid, void *privdata) |
|
|
|
return -1; /* singleshot */ |
|
|
|
} |
|
|
|
|
|
|
|
void lcd_reset(struct lcddev *dev) |
|
|
|
static void lcd_reset(struct lcddev *dev) |
|
|
|
{ |
|
|
|
#if (_LCD_DEBUG > 0) |
|
|
|
log_print(LOG_DEBUG, "%s()", __FUNCTION__); |
|
|
@ -278,29 +308,32 @@ void lcd_reset(struct lcddev *dev) |
|
|
|
char cmd[] = A125_CMD_RESET; |
|
|
|
lcd_write(dev, cmd, sizeof(cmd)); |
|
|
|
|
|
|
|
dev->backlight_state = -1; |
|
|
|
/* force next backlight command */ |
|
|
|
dev->backlight_enabled = -1; |
|
|
|
|
|
|
|
dev->state = LCD_STATE_INITIALIZING; |
|
|
|
|
|
|
|
dev->reset_timeout = event_add_timeout_ms(LCD_RESET_TIMEOUT, lcd_reset_timeout_cb, 0, dev); |
|
|
|
} |
|
|
|
|
|
|
|
static int lcd_backlight(struct lcddev *dev, int mode) |
|
|
|
static int lcd_backlight(struct lcddev *dev, int enable) |
|
|
|
{ |
|
|
|
if (dev->state != LCD_STATE_READY) |
|
|
|
return -1; |
|
|
|
|
|
|
|
#if (_LCD_DEBUG > 0) |
|
|
|
log_print(LOG_DEBUG, "%s(%d)", __FUNCTION__, mode); |
|
|
|
log_print(LOG_DEBUG, "%s(%d)", __FUNCTION__, enable); |
|
|
|
#endif /* (_LCD_DEBUG > 0) */ |
|
|
|
|
|
|
|
if (dev->backlight_state != mode) { |
|
|
|
if (dev->backlight_enabled != enable) { |
|
|
|
char cmd[] = A125_CMD_BACKLIGHT; |
|
|
|
cmd[2] = (mode) ? 0x01 : 0x00; |
|
|
|
|
|
|
|
cmd[2] = (enable) ? 0x01 : 0x00; |
|
|
|
lcd_write(dev, cmd, sizeof(cmd)); |
|
|
|
|
|
|
|
dev->backlight_enabled = enable; |
|
|
|
} |
|
|
|
|
|
|
|
dev->backlight_state = mode; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
@ -309,22 +342,27 @@ static int lcd_backlight_timeout_cb(int timerid, void *privdata) |
|
|
|
struct lcddev *dev = (struct lcddev *)privdata; |
|
|
|
dev->backlight_timeout = NULL; |
|
|
|
|
|
|
|
lcd_backlight(dev, 0); |
|
|
|
dev->event_callback(dev, LCD_EVENT_BACKLIGHT, dev->event_privdata); |
|
|
|
lcd_pagecallback(dev, LCDPAGE_EVENT_BACKLIGHT); |
|
|
|
|
|
|
|
if (dev->backlight_timeout == NULL) { |
|
|
|
lcd_backlight(dev, 0); |
|
|
|
} |
|
|
|
|
|
|
|
return -1; /* singleshot */ |
|
|
|
} |
|
|
|
|
|
|
|
int lcd_trigger_backlight(struct lcddev *dev, int timeout) |
|
|
|
int lcd_trigger_backlight(struct lcddev *dev, int enable, int timeout_ms) |
|
|
|
{ |
|
|
|
if (dev->backlight_timeout != NULL) { |
|
|
|
event_remove_timeout(dev->backlight_timeout); |
|
|
|
dev->backlight_timeout = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
int retval = lcd_backlight(dev, (timeout == 0) ? 0 : 1); |
|
|
|
int retval = lcd_backlight(dev, enable); |
|
|
|
|
|
|
|
if (timeout != -1) { |
|
|
|
dev->backlight_timeout = event_add_timeout_ms(timeout * 1000, lcd_backlight_timeout_cb, 0, dev); |
|
|
|
if (enable && (timeout_ms != -1)) { |
|
|
|
dev->backlight_timeout_ms = timeout_ms; |
|
|
|
dev->backlight_timeout = event_add_timeout_ms(timeout_ms * 1000, lcd_backlight_timeout_cb, 0, dev); |
|
|
|
} |
|
|
|
|
|
|
|
return retval; |
|
|
@ -333,14 +371,9 @@ int lcd_trigger_backlight(struct lcddev *dev, int timeout) |
|
|
|
static int lcd_update_timeout_cb(int timerid, void *privdata) |
|
|
|
{ |
|
|
|
struct lcddev *dev = (struct lcddev *)privdata; |
|
|
|
dev->update_timeout = NULL; |
|
|
|
|
|
|
|
int update = dev->event_callback(dev, LCD_EVENT_UPDATE, dev->event_privdata); |
|
|
|
|
|
|
|
/* update only if backlight is on (display visible) */ |
|
|
|
if ((update != 0) && (dev->backlight_state != 0)) { |
|
|
|
dev->update_timeout = event_add_timeout_ms(update, lcd_update_timeout_cb, 0, dev); |
|
|
|
} |
|
|
|
dev->update_timeout = NULL; |
|
|
|
lcd_pagecallback(dev, LCDPAGE_EVENT_UPDATE); |
|
|
|
|
|
|
|
return -1; /* singleshot */ |
|
|
|
} |
|
|
@ -350,7 +383,6 @@ static int lcd_read_cb(int fd, void *privdata) |
|
|
|
struct lcddev *dev = (struct lcddev *)privdata; |
|
|
|
|
|
|
|
char buf[4]; |
|
|
|
int update = 0; |
|
|
|
int size = (dev->state != LCD_STATE_INITIALIZING) ? sizeof(buf) : 2; |
|
|
|
int len = lcd_read(dev, buf, size); |
|
|
|
|
|
|
@ -367,7 +399,7 @@ static int lcd_read_cb(int fd, void *privdata) |
|
|
|
dev->state = LCD_STATE_READY; |
|
|
|
|
|
|
|
/* trigger application to set data */ |
|
|
|
update = dev->event_callback(dev, LCD_EVENT_INIT, dev->event_privdata); |
|
|
|
lcd_pagecallback(dev, LCDPAGE_EVENT_ENTER); |
|
|
|
} |
|
|
|
|
|
|
|
} else if (dev->state == LCD_STATE_READY) { |
|
|
@ -378,23 +410,13 @@ static int lcd_read_cb(int fd, void *privdata) |
|
|
|
return 0; |
|
|
|
|
|
|
|
if (memcmp(buf, expect1, sizeof(buf)) == 0) { |
|
|
|
update = dev->event_callback(dev, LCD_EVENT_BUTTON1, dev->event_privdata); |
|
|
|
lcd_pagecallback(dev, LCDPAGE_EVENT_BUTTON1); |
|
|
|
|
|
|
|
} else if (memcmp(buf, expect2, sizeof(buf)) == 0) { |
|
|
|
update = dev->event_callback(dev, LCD_EVENT_BUTTON2, dev->event_privdata); |
|
|
|
lcd_pagecallback(dev, LCDPAGE_EVENT_BUTTON2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (dev->update_timeout != NULL) { |
|
|
|
event_remove_timeout(dev->update_timeout); |
|
|
|
dev->update_timeout = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
/* update only if backlight is on (display visible) */ |
|
|
|
if ((update != 0) && (dev->backlight_state != 0)) { |
|
|
|
dev->update_timeout = event_add_timeout_ms(update, lcd_update_timeout_cb, 0, dev); |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
@ -423,6 +445,12 @@ static int lcd_scroll_timeout_cb(int timerid, void *privdata) |
|
|
|
struct lcddev *dev = (struct lcddev *)privdata; |
|
|
|
int i, reset_pos = 0; |
|
|
|
|
|
|
|
/* disable scrolling if backlight is off (display is not visible) */ |
|
|
|
if (dev->backlight_enabled == 0) { |
|
|
|
dev->scroll_timeout = NULL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
for (i = 0; i < 2; i++) { |
|
|
|
int line_pos = dev->scroll_pos; |
|
|
|
if (line_pos < 0) |
|
|
@ -446,19 +474,14 @@ static int lcd_scroll_timeout_cb(int timerid, void *privdata) |
|
|
|
if (reset_pos == 2) |
|
|
|
dev->scroll_pos = -1; |
|
|
|
|
|
|
|
/* disable scrolling if backlight is off (display is not visible) */ |
|
|
|
if (dev->backlight_state == 0) { |
|
|
|
dev->scroll_timeout = NULL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
/* periodic */ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int lcd_setlines(struct lcddev *dev, int scroll_speed, const char *line1, const char *line2) |
|
|
|
int lcd_setlines(struct lcddev *dev, const char *line1, const char *line2) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
|
|
|
|
for (i = 0; i < 2; i++) { |
|
|
|
if (dev->line_data[i] != NULL) { |
|
|
|
free((void *)dev->line_data[i]); |
|
|
@ -487,25 +510,123 @@ int lcd_setlines(struct lcddev *dev, int scroll_speed, const char *line1, const |
|
|
|
lcd_setline(dev, 0, dev->line_length[0], dev->line_data[0]); |
|
|
|
lcd_setline(dev, 1, dev->line_length[1], dev->line_data[1]); |
|
|
|
|
|
|
|
int scroll_enable = (dev->line_length[0] > 16) || (dev->line_length[1] > 16); |
|
|
|
if (dev->scroll_timeout && ((dev->scroll_speed != scroll_speed) || !scroll_enable)) { |
|
|
|
event_remove_timeout(dev->scroll_timeout); |
|
|
|
dev->scroll_timeout = NULL; |
|
|
|
if ((dev->line_length[0] > 16) || (dev->line_length[1] > 16)) { |
|
|
|
if (dev->scroll_timeout == NULL) { |
|
|
|
dev->scroll_pos = 0; |
|
|
|
dev->scroll_timeout = event_add_timeout_ms(LCD_SCROLL_SPEED, lcd_scroll_timeout_cb, 0, dev); |
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
if (dev->scroll_timeout != NULL) { |
|
|
|
event_remove_timeout(dev->scroll_timeout); |
|
|
|
dev->scroll_timeout = NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
dev->scroll_speed = scroll_speed; |
|
|
|
dev->scroll_pos = 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
if ((dev->scroll_timeout == NULL) && scroll_enable && (scroll_speed > 0)) { |
|
|
|
dev->scroll_timeout = event_add_timeout_ms(dev->scroll_speed, lcd_scroll_timeout_cb, 0, dev); |
|
|
|
static void lcd_pagecallback(struct lcddev *dev, int event) |
|
|
|
{ |
|
|
|
if (dev->state != LCD_STATE_READY) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (dev->current_page == NULL) { |
|
|
|
if (list_empty(&dev->page_list)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
dev->current_page = list_entry(dev->page_list.next, struct lcdpage, list); |
|
|
|
} |
|
|
|
|
|
|
|
if ((dev->backlight_enabled == 0x00) && ((event == LCDPAGE_EVENT_BUTTON1) || (event == LCDPAGE_EVENT_BUTTON2))) { |
|
|
|
lcd_trigger_backlight(dev, 1, dev->backlight_timeout_ms); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
int next; |
|
|
|
do { |
|
|
|
log_print(LOG_DEBUG, "%s: cb(%p, 0x%x)", __FUNCTION__, dev->current_page, event); |
|
|
|
int retval = dev->current_page->callback(dev, event, dev->current_page->privdata); |
|
|
|
log_print(LOG_DEBUG, "%s: cb(%p, 0x%x) => 0x%x", __FUNCTION__, dev->current_page, event, retval); |
|
|
|
|
|
|
|
next = 0; |
|
|
|
if ((event == LCDPAGE_EVENT_ENTER) && (retval == -1)) { |
|
|
|
next = 1; |
|
|
|
|
|
|
|
} else if (retval == LCDPAGE_COMMAND_NEXT) { |
|
|
|
next = 1; |
|
|
|
} |
|
|
|
|
|
|
|
if (dev->update_timeout != NULL) { |
|
|
|
event_remove_timeout(dev->update_timeout); |
|
|
|
dev->update_timeout = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
if (next) { |
|
|
|
struct lcdpage *page = dev->current_page; |
|
|
|
|
|
|
|
if (page->list.next != &dev->page_list) { |
|
|
|
page = list_entry(page->list.next, struct lcdpage, list); |
|
|
|
} else { |
|
|
|
page = list_entry(page->list.next->next, struct lcdpage, list); |
|
|
|
} |
|
|
|
|
|
|
|
if (page == dev->current_page) { |
|
|
|
next = 0; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
if (next) { |
|
|
|
dev->current_page->callback(dev, LCDPAGE_EVENT_EXIT, dev->current_page->privdata); |
|
|
|
dev->current_page = page; |
|
|
|
event = LCDPAGE_EVENT_ENTER; |
|
|
|
} |
|
|
|
|
|
|
|
} else if ((retval != 0x00) && (dev->backlight_enabled != 0)) { |
|
|
|
dev->update_timeout = event_add_timeout_ms(retval, lcd_update_timeout_cb, 0, dev); |
|
|
|
} |
|
|
|
} while (next); |
|
|
|
} |
|
|
|
|
|
|
|
int lcd_addpage_cb(struct lcddev *dev, |
|
|
|
int priority, |
|
|
|
int (*event_callback)(struct lcddev *dev, int event, void *privdata), |
|
|
|
void *event_privdata) |
|
|
|
{ |
|
|
|
struct lcdpage *page = malloc(sizeof(struct lcdpage)); |
|
|
|
if (page == NULL) { |
|
|
|
log_print(LOG_ERROR, "%s(): out of memory", __FUNCTION__); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
page->priority = priority; |
|
|
|
page->callback = event_callback; |
|
|
|
page->privdata = event_privdata; |
|
|
|
|
|
|
|
int inserted = 0; |
|
|
|
struct lcdpage *search; |
|
|
|
list_for_each_entry(search, &dev->page_list, list) { |
|
|
|
if (page->priority > search->priority) { |
|
|
|
list_add_tail(&page->list, &search->list); |
|
|
|
inserted = 1; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!inserted) { |
|
|
|
list_add_tail(&page->list, &dev->page_list); |
|
|
|
} |
|
|
|
|
|
|
|
if (dev->current_page == NULL) { |
|
|
|
lcd_pagecallback(dev, LCDPAGE_EVENT_ENTER); |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
struct lcddev * lcd_open(const char *devicename, |
|
|
|
int (*event_callback)(struct lcddev *dev, int button, void *privdata), |
|
|
|
void *event_privdata) |
|
|
|
struct lcddev * lcd_open(const char *devicename) |
|
|
|
{ |
|
|
|
struct lcddev *dev = malloc(sizeof(struct lcddev)); |
|
|
|
if (dev == NULL) { |
|
|
@ -515,6 +636,8 @@ struct lcddev * lcd_open(const char *devicename, |
|
|
|
|
|
|
|
memset(dev, 0, sizeof(struct lcddev)); |
|
|
|
|
|
|
|
INIT_LIST_HEAD(&dev->page_list); |
|
|
|
|
|
|
|
int retval; |
|
|
|
#if (_LCD_DUMMY) |
|
|
|
if (strncmp(devicename, "dummy", 5) == 0) { |
|
|
@ -530,9 +653,6 @@ struct lcddev * lcd_open(const char *devicename, |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
dev->event_callback = event_callback; |
|
|
|
dev->event_privdata = event_privdata; |
|
|
|
|
|
|
|
dev->read_event = event_add_readfd(NULL, dev->fd, lcd_read_cb, dev); |
|
|
|
|
|
|
|
lcd_reset(dev); |
|
|
|