From cab1c1a4002c5967a26f1ecd1e6384c0f890d454 Mon Sep 17 00:00:00 2001 From: michael Date: Tue, 14 Feb 2012 03:17:04 +0000 Subject: driver for TeakLCM by Andreas Thienemann git-svn-id: https://ssl.bulix.org/svn/lcd4linux/trunk@1173 3ae390bd-cb1e-0410-b409-cd5a39f66f1f --- drv_TeakLCM.c | 1024 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1024 insertions(+) create mode 100644 drv_TeakLCM.c (limited to 'drv_TeakLCM.c') diff --git a/drv_TeakLCM.c b/drv_TeakLCM.c new file mode 100644 index 0000000..7517b40 --- /dev/null +++ b/drv_TeakLCM.c @@ -0,0 +1,1024 @@ +/* $Id$ + * $URL$ + * + * TeakLCM lcd4linux driver + * + * Copyright (C) 2005 Michael Reinelt + * Copyright (C) 2005, 2006, 2007 The LCD4Linux Team + * Copyright (C) 2011 Hans Ulrich Niedermann + * Copyright (C) 2011, 2012 Andreas Thienemann + * + * This file is part of LCD4Linux. + * + * LCD4Linux 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, or (at your option) + * any later version. + * + * LCD4Linux 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * + * exported fuctions: + * + * struct DRIVER drv_TeakLCM + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include + +#include "event.h" +#include "timer.h" +#include "debug.h" +#include "cfg.h" +#include "qprintf.h" +#include "udelay.h" +#include "plugin.h" +#include "widget.h" +#include "widget_text.h" +#include "widget_icon.h" +#include "widget_bar.h" +#include "drv.h" + +#include "drv_generic_text.h" +#include "drv_generic_serial.h" + + +static char Name[] = "TeakLCM"; + + +static int global_reset_rx_flag = 0; + + +#define HI8(value) ((u_int8_t)(((value)>>8) & 0xff)) +#define LO8(value) ((u_int8_t)((value) & 0xff)) + + +static u_int16_t CRC16(u_int8_t value, u_int16_t crcin) +{ + u_int16_t k = (((crcin >> 8) ^ value) & 255) << 8; + u_int16_t crc = 0; + int bits; + for (bits = 8; bits; --bits) { + if ((crc ^ k) & 0x8000) + crc = (crc << 1) ^ 0x1021; + else + crc <<= 1; + k <<= 1; + } + return ((crcin << 8) ^ crc); +} + + +/** Return a printable character */ +static char printable(const char ch) +{ + if ((32 <= ch) && (ch < 127)) { + return ch; + } else { + return '.'; + } +} + + +static void debug_data_int(const char *prefix, const void *data, const size_t size, const unsigned int delta) +{ + const u_int8_t *b = (const u_int8_t *) data; + size_t y; + assert(delta <= 24); + for (y = 0; y < size; y += delta) { + char buf[100]; + size_t x; + ssize_t idx = 0; + idx += sprintf(&(buf[idx]), "%04x ", y); + for (x = 0; x < delta; x++) { + const size_t i = x + y; + if (i < size) { + idx += sprintf(&(buf[idx]), " %02x", b[i]); + } else { + idx += sprintf(&(buf[idx]), " "); + } + } + idx += sprintf(&buf[idx], " "); + for (x = 0; x < delta; x++) { + const size_t i = x + y; + if (i < size) { + idx += sprintf(&buf[idx], "%c", printable(b[i])); + } else { + idx += sprintf(&buf[idx], " "); + } + } + debug("%s%s", prefix, buf); + } +} + + +static void debug_data(const char *prefix, const void *data, const size_t size) +{ + debug_data_int(prefix, data, size, 16); +} + + +typedef enum { + CMD_CONNECT = 0x05, + CMD_DISCONNECT = 0x06, + CMD_ALARM = 0x07, + CMD_WRITE = 0x08, + CMD_PRINT1 = 0x09, + CMD_PRINT2 = 0x0A, + CMD_ACK = 0x0B, + CMD_NACK = 0x0C, + CMD_CONFIRM = 0x0D, + CMD_RESET = 0x0E, + + LCM_CLEAR = 0x21, + LCM_HOME = 0x22, + LCM_CURSOR_SHIFT_R = 0x23, + LCM_CURSOR_SHIFT_L = 0x24, + LCM_BACKLIGHT_ON = 0x25, + LCM_BACKLIGHT_OFF = 0x26, + LCM_LINE2 = 0x27, + LCM_DISPLAY_SHIFT_R = 0x28, + LCM_DISPLAY_SHIFT_L = 0x29, + LCM_CURSOR_ON = 0x2A, + LCM_CURSOR_OFF = 0x2B, + LCM_CURSOR_BLINK = 0x2C, + LCM_DISPLAY_ON = 0x2D, + LCM_DISPLAY_OFF = 0x2E +} lcm_cmd_t; + + +static +const char *cmdstr(const lcm_cmd_t cmd) +{ + switch (cmd) { +#define D(CMD) case CMD_ ## CMD: return "CMD_" # CMD; break; + D(CONNECT); + D(DISCONNECT); + D(ACK); + D(NACK); + D(CONFIRM); + D(RESET); + D(ALARM); + D(WRITE); + D(PRINT1); + D(PRINT2); +#undef D +#define D(CMD) case LCM_ ## CMD: return "LCM_" # CMD; break; + D(CLEAR); + D(HOME); + D(CURSOR_SHIFT_R); + D(CURSOR_SHIFT_L); + D(BACKLIGHT_ON); + D(BACKLIGHT_OFF); + D(LINE2); + D(DISPLAY_SHIFT_R); + D(DISPLAY_SHIFT_L); + D(CURSOR_ON); + D(CURSOR_OFF); + D(CURSOR_BLINK); + D(DISPLAY_ON); + D(DISPLAY_OFF); +#undef D + } + return "CMD_UNKNOWN"; +} + + +/* + * Magic defines + */ + +#define LCM_FRAME_MASK 0xFF +#define LCM_TIMEOUT 2 +#define LCM_ESC 0x1B + +#define LCM_KEY1 0x31 +#define LCM_KEY2 0x32 +#define LCM_KEY3 0x33 +#define LCM_KEY4 0x34 +#define LCM_KEY12 0x35 +#define LCM_KEY13 0x36 +#define LCM_KEY14 0x37 +#define LCM_KEY23 0x38 +#define LCM_KEY24 0x39 +#define LCM_KEY34 0x3A + + +/****************************************/ +/*** hardware dependant functions ***/ +/****************************************/ + +/* global LCM state machine */ + + +struct _lcm_fsm_t; +typedef struct _lcm_fsm_t lcm_fsm_t; + + +typedef enum { + ST_IDLE, /* mode == 0, IDLE */ + ST_COMMAND, /* mode == 1, COMMAND */ + ST_CONNECTED /* mode == 2, CONNECTED */ +} lcm_state_t; + + +static +const char *state2str(const lcm_state_t state) +{ + switch (state) { + case ST_IDLE: + return "ST_IDLE (0)"; + break; + case ST_COMMAND: + return "ST_COMMAND (1)"; + break; + case ST_CONNECTED: + return "ST_CONNECTED (2)"; + break; + } + return "ST_UNKNOWN"; +} + + +#if 0 +static +void repeat_connect_to_display_callback(void *data); +#endif + +static +void lcm_send_cmd(lcm_cmd_t cmd); + +static +void drv_TeakLCM_clear(void); + + +static +void raw_send_cmd_frame(lcm_cmd_t cmd); + +static +void raw_send_data_frame(lcm_cmd_t cmd, const char *data, const unsigned int len); + + +static +lcm_state_t fsm_get_state(lcm_fsm_t * fsm); + +static +void fsm_handle_cmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd); + +static +void fsm_handle_datacmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const u_int8_t * payload, const unsigned int payload_len); + +static +void try_reset(void); + +static +void fsm_step(lcm_fsm_t * fsm); + +static +void fsm_trans_noop(lcm_fsm_t * fsm, const lcm_state_t next_state); + +static +void fsm_trans_cmd(lcm_fsm_t * fsm, const lcm_state_t next_state, const lcm_cmd_t cmd); + +static +void fsm_trans_data(lcm_fsm_t * fsm, + const lcm_state_t next_state, const lcm_cmd_t cmd, const char *data, const unsigned int len); + + +static +void fsm_handle_bytes(lcm_fsm_t * fsm, u_int8_t * rxbuf, const unsigned int buflen) +{ + if ((buflen >= 3) && (rxbuf[0] == LCM_FRAME_MASK) && (rxbuf[2] == LCM_FRAME_MASK)) { + const lcm_cmd_t cmd = rxbuf[1]; + debug("%s Received cmd frame (cmd=%d=%s)", __FUNCTION__, cmd, cmdstr(cmd)); + fsm_handle_cmd(fsm, cmd); + if (buflen > 3) { + /* recursively handle remaining bytes */ + fsm_handle_bytes(fsm, &rxbuf[3], buflen - 3); + } + return; + } else if ((buflen > 3) && (rxbuf[0] == LCM_FRAME_MASK)) { + unsigned int ri; /* raw indexed */ + unsigned int ci; /* cooked indexed, i.e. after unescaping */ + + debug("%s Received possible data frame", __FUNCTION__); + + /* unescape rxframe data in place */ + u_int16_t crc0 = 0, crc1 = 0, crc2 = 0, crc3 = 0; + for (ri = 1, ci = 1; ri < buflen; ri++) { + switch (rxbuf[ri]) { + case LCM_ESC: + ri++; + /* fall through */ + default: + rxbuf[ci++] = rxbuf[ri]; + crc3 = crc2; + crc2 = crc1; + crc1 = crc0; + crc0 = CRC16(rxbuf[ri], crc0); + break; + } + if ((rxbuf[ci - 1] == LCM_FRAME_MASK) && (rxbuf[ci - 2] == LO8(crc3)) && (rxbuf[ci - 3] == HI8(crc3))) { + /* looks like a complete data frame */ + lcm_cmd_t cmd = rxbuf[1]; + u_int16_t len = (rxbuf[3] << 8) + rxbuf[2]; + assert(ci == (unsigned int) (1 + 1 + 2 + len + 2 + 1)); + fsm_handle_datacmd(fsm, cmd, &rxbuf[4], len); + if (ri + 1 < buflen) { + /* recursively handle remaining bytes */ + fsm_handle_bytes(fsm, &rxbuf[ri + 1], buflen - ri); + } + return; + } + } + + fsm_trans_cmd(fsm, fsm_get_state(fsm), /* TODO: Is this a good next_state value? */ + CMD_NACK); + debug("%s checksum/framemask error", __FUNCTION__); + return; + } else { + debug("%s Received garbage data:", __FUNCTION__); + debug_data(" RXD ", rxbuf, buflen); + return; + } +} + + +static void fsm_handle_cmd(lcm_fsm_t * fsm, lcm_cmd_t cmd) +{ + // debug("fsm_handle_cmd: old state 0x%02x %s", lcm_mode, modestr(lcm_mode)); + const lcm_state_t old_state = fsm_get_state(fsm); + if (CMD_RESET == cmd) { + global_reset_rx_flag = 1; + } + switch (old_state) { + case ST_IDLE: + case ST_COMMAND: + switch (cmd) { + case CMD_CONNECT: + fsm_trans_cmd(fsm, ST_COMMAND, CMD_ACK); + break; + case CMD_ACK: + fsm_trans_cmd(fsm, ST_CONNECTED, CMD_CONFIRM); + break; + case CMD_NACK: + fsm_trans_cmd(fsm, ST_IDLE, CMD_CONFIRM); + break; + case CMD_CONFIRM: + fsm_trans_noop(fsm, ST_CONNECTED); + break; + case CMD_RESET: + fsm_trans_cmd(fsm, ST_COMMAND, CMD_CONNECT); + break; + default: + error("%s: Unhandled cmd %s in state %s", Name, cmdstr(cmd), state2str(old_state)); + fsm_trans_cmd(fsm, ST_IDLE, CMD_NACK); + break; + } + break; + case ST_CONNECTED: /* "if (mode == 2)" */ + switch (cmd) { + case CMD_ACK: + fsm_trans_cmd(fsm, ST_CONNECTED, CMD_CONFIRM); + break; + case CMD_CONNECT: + fsm_trans_cmd(fsm, ST_CONNECTED, CMD_NACK); + break; + case CMD_DISCONNECT: + fsm_trans_cmd(fsm, ST_CONNECTED, CMD_ACK); + break; + case CMD_RESET: + fsm_trans_cmd(fsm, ST_IDLE, CMD_CONNECT); + break; + default: + debug("%s: Ignoring unhandled cmd %s in state %s", Name, cmdstr(cmd), state2str(old_state)); + fsm_trans_noop(fsm, ST_CONNECTED); + break; + } + break; + } + fsm_step(fsm); +} + + +static +void fsm_handle_datacmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const u_int8_t * payload, unsigned int payload_len) +{ + const lcm_state_t old_state = fsm_get_state(fsm); + debug("fsm_handle_datacmd: old state 0x%02x %s", old_state, state2str(old_state)); + switch (old_state) { + case ST_CONNECTED: + switch (cmd) { + case CMD_WRITE: + assert(payload_len == 1); + debug("Got a key code 0x%02x", *payload); + fsm_trans_noop(fsm, ST_CONNECTED); + // lcm_send_cmd_frame(CMD_ACK); + break; + default: + debug("Got an unknown data frame: %d=%s", cmd, cmdstr(cmd)); + fsm_trans_noop(fsm, ST_CONNECTED); + // lcm_send_cmd_frame(CMD_NACK); + break; + } + break; + case ST_IDLE: + case ST_COMMAND: + fsm_trans_cmd(fsm, old_state, CMD_NACK); + break; + } + fsm_step(fsm); +} + + +struct _lcm_fsm_t { + lcm_state_t state; + lcm_state_t next_state; + enum { + ACTION_UNINITIALIZED, + ACTION_NOOP, + ACTION_CMD, + ACTION_DATA + } action_type; + union { + struct { + lcm_cmd_t cmd; + } cmd_frame; + struct { + lcm_cmd_t cmd; + const char *data; + unsigned int len; + } data_frame; + } action; +}; + + +static +lcm_state_t fsm_get_state(lcm_fsm_t * fsm) +{ + return fsm->state; +} + + +static +void flush_shadow(void); + + +static +void fsm_step(lcm_fsm_t * fsm) +{ + debug("fsm: old_state=%s new_state=%s", state2str(fsm->state), state2str(fsm->next_state)); + switch (fsm->action_type) { + case ACTION_UNINITIALIZED: + error("Uninitialized LCM FSM action"); + abort(); + break; + case ACTION_NOOP: + break; + case ACTION_CMD: + raw_send_cmd_frame(fsm->action.cmd_frame.cmd); + break; + case ACTION_DATA: + raw_send_data_frame(fsm->action.data_frame.cmd, fsm->action.data_frame.data, fsm->action.data_frame.len); + break; + } + fsm->action_type = ACTION_UNINITIALIZED; + switch (fsm->next_state) { + case ST_IDLE: + case ST_COMMAND: + fsm->state = fsm->next_state; + fsm->next_state = -1; + return; + break; + case ST_CONNECTED: + if (fsm->state != ST_CONNECTED) { + /* going from ST_IDLE or ST_COMMAND into ST_CONNECTED */ + if (!global_reset_rx_flag) { + try_reset(); +#if 0 + int timer_res = timer_add(repeat_connect_to_display_callback, NULL, 50 /*ms */ , 1); + debug("re-scheduled connect callback result: %d", timer_res); + + done by try_reset / fsm_init fsm->state = fsm->next_state; + fsm->next_state = -1; +#endif + return; + } else { + /* properly connected for the first time */ + debug("%s: %s NOW CONNECTED!!!", Name, __FUNCTION__); + + fsm->state = fsm->next_state; + fsm->next_state = -1; + + lcm_send_cmd(LCM_DISPLAY_ON); + flush_shadow(); + lcm_send_cmd(LCM_BACKLIGHT_ON); + return; + } + } else { + debug("no state change in ST_CONNECTED"); + fsm->state = fsm->next_state; + fsm->next_state = -1; + return; + } + error("we should never arrive here"); + abort(); + break; + } + error("LCM FSM: Illegal next_state"); + abort(); +} + + +#if 0 + +#endif + + +static +void fsm_trans_noop(lcm_fsm_t * fsm, const lcm_state_t next_state) +{ + fsm->next_state = next_state; + fsm->action_type = ACTION_NOOP; +} + + +static +void fsm_trans_cmd(lcm_fsm_t * fsm, const lcm_state_t next_state, const lcm_cmd_t cmd) +{ + fsm->next_state = next_state; + fsm->action_type = ACTION_CMD; + fsm->action.cmd_frame.cmd = cmd; +} + + +static +void fsm_trans_data(lcm_fsm_t * fsm, + const lcm_state_t next_state, const lcm_cmd_t cmd, const char *data, const unsigned int len) +{ + fsm->next_state = next_state; + fsm->action_type = ACTION_DATA; + fsm->action.data_frame.cmd = cmd; + fsm->action.data_frame.data = data; + fsm->action.data_frame.len = len; +} + + +static +void fsm_send(lcm_fsm_t * fsm, const lcm_cmd_t cmd) +{ + const lcm_state_t old_state = fsm_get_state(fsm); + switch (old_state) { + case ST_IDLE: + case ST_COMMAND: + debug("%s: %s, ignoring cmd 0x%02x=%s", __FUNCTION__, state2str(old_state), cmd, cmdstr(cmd)); + /* Silently ignore the command to send. */ + /* TODO: Would it be better to queue it and send it later? */ + break; + case ST_CONNECTED: + fsm_trans_cmd(fsm, ST_CONNECTED, cmd); + fsm_step(fsm); + break; + } +} + + +static +void fsm_send_data(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const void *data, const unsigned int len) +{ + const lcm_state_t old_state = fsm_get_state(fsm); + switch (old_state) { + case ST_IDLE: + case ST_COMMAND: + debug("%s: %s, ignoring data cmd 0x%02x=%s", __FUNCTION__, state2str(old_state), cmd, cmdstr(cmd)); + /* Silently ignore the command to send. */ + /* TODO: Would it be better to queue it and send it later? */ + break; + case ST_CONNECTED: + fsm_trans_data(fsm, ST_CONNECTED, cmd, data, len); + fsm_step(fsm); + break; + } +} + + +static lcm_fsm_t lcm_fsm; + + +static +void fsm_init(void) +{ + lcm_fsm.state = ST_IDLE; + lcm_fsm.next_state = -1; + lcm_fsm.action_type = ACTION_UNINITIALIZED; +} + + + +/* Send a command frame to the TCM board */ +static +void raw_send_cmd_frame(lcm_cmd_t cmd) +{ + // lcm_receive_check(); + char cmd_buf[3]; + cmd_buf[0] = LCM_FRAME_MASK; + cmd_buf[1] = cmd; + cmd_buf[2] = LCM_FRAME_MASK; + debug("%s sending cmd frame cmd=0x%02x=%s", __FUNCTION__, cmd, cmdstr(cmd)); + debug_data(" TX ", cmd_buf, 3); + drv_generic_serial_write(cmd_buf, 3); + usleep(100); +#if 0 + usleep(100000); + switch (cmd) { + case CMD_ACK: + //case CMD_CONFIRM: + case CMD_NACK: + lcm_receive_check(); + break; + default: + if (1) { + int i; + for (i = 0; i < 20; i++) { + usleep(100000); + if (lcm_receive_check()) { + break; + } + } + } + break; + } +#endif +} + + +/* Send a data frame to the TCM board */ +static +void raw_send_data_frame(lcm_cmd_t cmd, const char *data, const unsigned int len) +{ + unsigned int di; /* data index */ + unsigned int fi; /* frame index */ + static char frame[32]; + u_int16_t crc = 0; + + frame[0] = LCM_FRAME_MASK; + + frame[1] = cmd; + crc = CRC16(frame[1], crc); + + frame[2] = HI8(len); + crc = CRC16(frame[2], crc); + + frame[3] = LO8(len); + crc = CRC16(frame[3], crc); + +#define APPEND(value) \ + do { \ + const unsigned char v = (value); \ + if ((v == LCM_FRAME_MASK) || (v == LCM_ESC)) { \ + frame[fi++] = LCM_ESC; \ + } \ + frame[fi++] = v; \ + crc = CRC16(v, crc); \ + } while (0) + +#define APPEND_NOCRC(value) \ + do { \ + const unsigned char v = (value); \ + if ((v == LCM_FRAME_MASK) || (v == LCM_ESC)) { \ + frame[fi++] = LCM_ESC; \ + } \ + frame[fi++] = v; \ + } while (0) + + for (fi = 4, di = 0; di < len; di++) { + APPEND(data[di]); + } + + APPEND_NOCRC(HI8(crc)); + APPEND_NOCRC(LO8(crc)); + + frame[fi++] = LCM_FRAME_MASK; + + debug_data(" TXD ", frame, fi); + drv_generic_serial_write(frame, fi); + +#undef APPEND + + usleep(500); +} + + +static +void lcm_send_cmd(lcm_cmd_t cmd) +{ + fsm_send(&lcm_fsm, cmd); +} + + +static +void lcm_event_callback(event_flags_t flags, void *data) +{ + lcm_fsm_t *fsm = (lcm_fsm_t *) data; + debug("%s: flags=%d, data=%p", __FUNCTION__, flags, data); + if (flags & EVENT_READ) { + static u_int8_t rxbuf[32]; + const int readlen = drv_generic_serial_poll((void *) rxbuf, sizeof(rxbuf)); + if (readlen <= 0) { + debug("%s Received no data", __FUNCTION__); + } else { + debug("%s RECEIVED %d bytes", __FUNCTION__, readlen); + debug_data(" RX ", rxbuf, readlen); + fsm_handle_bytes(fsm, rxbuf, readlen); + } + } +} + + +static int drv_TeakLCM_open(const char *section) +{ + /* open serial port */ + /* don't mind about device, speed and stuff, this function will take care of */ + + const int fd = drv_generic_serial_open(section, Name, 0); + if (fd < 0) + return -1; + + return fd; +} + + +static int drv_TeakLCM_close(int fd) +{ + if (fd >= 0) { + event_del(fd); + } + + /* close whatever port you've opened */ + drv_generic_serial_close(); + + return 0; +} + + +/* shadow buffer */ +static +char *shadow; + + +static void debug_shadow(const char *prefix) +{ + debug_data_int(prefix, shadow, DCOLS * DROWS, 20); +} + + +static +void flush_shadow(void) +{ + debug("%s called", __FUNCTION__); + debug_shadow(" shadow "); + usleep(50000); + fsm_send_data(&lcm_fsm, CMD_PRINT1, &shadow[DCOLS * 0], DCOLS); + usleep(50000); + fsm_send_data(&lcm_fsm, CMD_PRINT2, &shadow[DCOLS * 1], DCOLS); + usleep(50000); +} + + +/* text mode displays only */ +static +void drv_TeakLCM_clear(void) +{ + /* do whatever is necessary to clear the display */ + memset(shadow, ' ', DROWS * DCOLS); + flush_shadow(); +} + + +/* text mode displays only */ +static void drv_TeakLCM_write(const int row, const int col, const char *data, int len) +{ + debug("%s row=%d col=%d len=%d data=\"%s\"", __FUNCTION__, row, col, len, data); + + memcpy(&shadow[DCOLS * row + col], data, len); + + debug_shadow(" shadow "); + + fsm_send_data(&lcm_fsm, (row == 0) ? CMD_PRINT1 : CMD_PRINT2, &shadow[DCOLS * row], DCOLS); +} + + +static +void try_reset(void) +{ + debug("%s called", __FUNCTION__); + fsm_init(); + raw_send_cmd_frame(CMD_RESET); +} + + +#if 0 +static +void repeat_connect_to_display_callback(void *data) +{ + static int already_called = 0; + if (!already_called) { + debug("%s(%p): called", __FUNCTION__, data); + + /* reset & initialize display */ + try_reset(); + already_called = 1; + } else { + debug("%s(%p): already called, ignoring", __FUNCTION__, data); + } +} +#endif + + +static +int global_fd = -1; + + +static +void initial_connect_to_display_callback(void *data) +{ + debug("%s(%p): called", __FUNCTION__, data); + + debug("Calling event_add for fd=%d", global_fd); + int ret = event_add(lcm_event_callback, &lcm_fsm, global_fd, 1, 0, 1); + debug("event_add result: %d", ret); + + /* reset & initialize display */ + try_reset(); +} + + +/* start text mode display */ +static int drv_TeakLCM_start(const char *section) +{ + int rows = -1, cols = -1; + char *s; + + s = cfg_get(section, "Size", NULL); + if (s == NULL || *s == '\0') { + error("%s: no '%s.Size' entry from %s", Name, section, cfg_source()); + return -1; + } + if (sscanf(s, "%dx%d", &cols, &rows) != 2 || rows < 1 || cols < 1) { + error("%s: bad %s.Size '%s' from %s", Name, section, s, cfg_source()); + free(s); + return -1; + } + + DROWS = rows; + DCOLS = cols; + shadow = malloc(DROWS * DCOLS); + memset(shadow, ' ', DROWS * DCOLS); + + /* open communication with the display */ + global_fd = drv_TeakLCM_open(section); + if (global_fd < 0) { + return -1; + } + debug("%s: %s opened", Name, __FUNCTION__); + + /* read initial garbage data */ + static u_int8_t rxbuf[32]; + const int readlen = drv_generic_serial_poll((void *) rxbuf, sizeof(rxbuf)); + if (readlen >= 0) { + debug_data(" initial RX garbage ", rxbuf, readlen); + } + + /* We need to do a delayed connect */ + int timer_res = timer_add(initial_connect_to_display_callback, NULL, 10 /*ms */ , 1); + debug("timer_add for connect callback result: %d", timer_res); + + debug("%s: %s done", Name, __FUNCTION__); + return 0; +} + + +/****************************************/ +/*** plugins ***/ +/****************************************/ + + +/****************************************/ +/*** widget callbacks ***/ +/****************************************/ + + +/* using drv_generic_text_draw(W) */ +/* using drv_generic_text_icon_draw(W) */ +/* using drv_generic_text_bar_draw(W) */ + + +/****************************************/ +/*** exported functions ***/ +/****************************************/ + + +/* list models */ +int drv_TeakLCM_list(void) +{ + printf("TeakLCM driver"); + return 0; +} + + +/* initialize driver & display */ +/* use this function for a text display */ +int drv_TeakLCM_init(const char *section, const int quiet) +{ + WIDGET_CLASS wc; + int ret; + + info("%s: %s (quiet=%d)", Name, "$Rev$", quiet); + + /* display preferences */ + XRES = 5; /* pixel width of one char */ + YRES = 8; /* pixel height of one char */ + CHARS = 0; /* number of user-defineable characters */ + CHAR0 = 0; /* ASCII of first user-defineable char */ + GOTO_COST = -1; /* number of bytes a goto command requires */ + + /* real worker functions */ + drv_generic_text_real_write = drv_TeakLCM_write; + + /* start display */ + if ((ret = drv_TeakLCM_start(section)) != 0) + return ret; + + /* initialize generic text driver */ + if ((ret = drv_generic_text_init(section, Name)) != 0) + return ret; + + /* register text widget */ + wc = Widget_Text; + wc.draw = drv_generic_text_draw; + widget_register(&wc); + + /* register plugins */ + + return 0; +} + + +/* close driver & display */ +/* use this function for a text display */ +int drv_TeakLCM_quit(const int quiet) +{ + + info("%s: shutting down. (quiet=%d)", Name, quiet); + + drv_generic_text_quit(); + + /* clear display */ + drv_TeakLCM_clear(); + + lcm_send_cmd(LCM_DISPLAY_OFF); + // lcm_send_cmd_frame(LCM_BACKLIGHT_OFF); + lcm_send_cmd(CMD_DISCONNECT); + + /* FIXME: consume final ack frame */ + usleep(100000); + + debug("closing connection"); + drv_TeakLCM_close(global_fd); + + return (0); +} + + +/* use this one for a text display */ +DRIVER drv_TeakLCM = { + .name = Name, + .list = drv_TeakLCM_list, + .init = drv_TeakLCM_init, + .quit = drv_TeakLCM_quit, +}; + + +/* + * Local Variables: + * c-basic-offset: 4 + * End: + */ -- cgit v1.2.3