#include #include #include #include #include #include #include #include #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); struct event_fd *read_event; struct event_timeout *backlight_event; }; static 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 ", 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 ", 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; } char expect1[] = A125_EVENT_BUTTON1; char expect2[] = A125_EVENT_BUTTON2; if (memcmp(buf, expect1, sizeof(buf)) == 0) { dev->button_cb(dev, LCD_BUTTON1); lcd_trigger_backlight(dev); } else if (memcmp(buf, expect2, sizeof(buf)) == 0) { dev->button_cb(dev, LCD_BUTTON2); lcd_trigger_backlight(dev); } return 0; } int lcd_init(const char *devicename, int backlight_timeout, int (*button_cb)(struct lcddev *dev, int button)) { struct lcddev *dev = malloc(sizeof(struct lcddev)); if (dev == NULL) { log_print(LOG_ERROR, "lcd_init(): out of memory"); return -1; } memset(dev, 0, sizeof(struct lcddev)); if (lcd_open(dev, devicename) < 0) { free(dev); return -1; } if (lcd_reset(dev) < 0) { log_print(LOG_ERROR, "lcd_init(): could not reset LCD"); lcd_close(dev); free(dev); return -1; } dev->backlight_timeout = backlight_timeout; lcd_trigger_backlight(dev); dev->button_cb = button_cb; button_cb(dev, LCD_BUTTON0); dev->read_event = event_add_readfd(NULL, dev->fd, lcd_read_cb, dev); return 0; }