#include #include #include #include #include #include #include #include #include #include "util.h" #define GD_ADDR_US 0x00 #define GD_ADDR_PANEL 0x11 #define GD_FN_POLL 0x01 #define GD_FN_PRESS_KEY 0x02 //Acked in the LCD data #define GD_FN_GET_LCD 0x03 #define GD_FN_LCD 0x83 #define GD_FN_GET_PANEL_VER 0x04 #define GD_FN_PANEL_VER 0x84 #define GD_FN_05 0x05 #define GD_FN_05_RESP 0x85 #define GD_FN_06 0x06 #define GD_FN_06_RESP 0x86 #define GD_FN_ERR 0xf2 #define GD_FN_ACK 0xfe #define GD_FN_BUSY 0x81 #define GD_MAX_PKT_LEN 128 #define GD_AK_RECEIVED 0x01 #define GD_AK_DUPLICATE 0x02 #define GD_LCD_LINE_LEN 16 #define GD_TIMEOUT 10 // Packets are // AA FN D0 D1 D2 CK typedef struct { union { unsigned char pkt[GD_MAX_PKT_LEN]; struct { unsigned char address; unsigned char fn; unsigned char data[]; }; }; size_t data_len; } GD_PKT; typedef struct { unsigned char line0[GD_LCD_LINE_LEN + 1]; unsigned char line1[GD_LCD_LINE_LEN + 1]; int cx, cy; unsigned char flags; } GD_LCD; static WINDOW *win, *dwin = NULL; static FILE *dfile = NULL; static void debug_print (const char *fmt, ...) { va_list va; if (dwin) { va_start (va, fmt); vw_printw (dwin, fmt, va); va_end (va); wrefresh (dwin); } if (dfile) { va_start (va, fmt); vfprintf (dfile, fmt, va); va_end (va); } } static void endecode (unsigned char *p, size_t l) { unsigned char ks[] = { 0x86, 0x93, 0xb5, 0x37, 0x12, 0xd6, 0xe4, 0x77}; size_t i; for (i = 2; i < l; ++i) p[i] ^= ks[i & 7]; } static unsigned char checksum (unsigned char *p, size_t l) { unsigned s = 0; unsigned r = 0; unsigned i; for (i = 0; i < l; ++i) s += p[i]; s += 0xaa; while (s > 0xff) { r = s & 0xff; r += (s >> 8); r += (s >> 16); s = r; } return s; } static void hexdump_pkt (const char *prefix, GD_PKT *p) { unsigned i, j, k; unsigned char *d = p->pkt; size_t l = p->data_len + 2; if (!dwin && !dfile) return; for (i = 0; i < l; i += 16) { debug_print ("%s %04x ", prefix, i); for (j = 0; j < 16; ++j) { k = i + j; if (k < l) debug_print (" %02x", d[k]); else debug_print (" "); if (j == 7) debug_print (" "); } debug_print (" "); for (j = 0; j < 16; ++j) { k = i + j; if (k < l) { if ((d[k] > 32) && (d[k] < 127)) debug_print ("%c", d[k]); else debug_print ("."); } else debug_print (" "); if (j == 7) debug_print (" "); } debug_print ("\n"); } } static int send_pkt (int fd, GD_PKT *tx) { unsigned char buf[GD_MAX_PKT_LEN]; size_t len = tx->data_len + 2; hexdump_pkt (" >", tx); memcpy (buf, tx->pkt, len); endecode (buf, len); buf[len] = checksum (buf, len) ; len++; if (write (fd, buf, len) == len) return 1; return -1; } static int recv_pkt (int fd, GD_PKT *rx, int timeout) { size_t len; unsigned char sum; len = read_with_timeout (fd, rx->pkt, sizeof (rx->pkt), timeout); if (!len) return 0; if (len < 3) return -1; len--; sum = checksum (rx->pkt, len) ; if (rx->pkt[len] != sum) { rx->data_len = len - 1; hexdump_pkt ("!<", rx); debug_print ("Bad checksum expected %02x got %02x\n", sum, rx->pkt[len]); return -1; } endecode (rx->pkt, len); rx->data_len = len - 2; hexdump_pkt (" <", rx); return 1; } static int gd_transact (int fd, GD_PKT *tx, GD_PKT *rx, unsigned char rx_addr, unsigned char rx_fn, int timeout) { unsigned tries = 8; while (tries--) { if (send_pkt (fd, tx) != 1) return -1; if (recv_pkt (fd, rx, timeout) != 1) continue; if (rx->address != rx_addr) continue; if (rx->fn != rx_fn) continue; return 0; } return -1; } static int gd_get_panel_ver (int fd, unsigned char a, int *v) { GD_PKT o, i; o.address = GD_ADDR_US; o.fn = GD_FN_GET_PANEL_VER; o.data[0] = a; o.data_len = 1; if (gd_transact (fd, &o, &i, GD_ADDR_PANEL, GD_FN_PANEL_VER, GD_TIMEOUT)) return -1; switch (i.data[2]) { case 0: //? *v = 2; break; case 1: //Galaxy Dimension 48 *v = 3; break; case 2: *v = 3; break; case 3: *v = 3; break; default: return -1; } return 0; } static int gd_fn_05 (int fd, unsigned char a, unsigned char b, unsigned char c) { GD_PKT o, i; o.address = GD_ADDR_US; o.fn = GD_FN_05; o.data[0] = a; o.data[1] = b; o.data[2] = c; o.data_len = 3; if (gd_transact (fd, &o, &i, GD_ADDR_PANEL, GD_FN_05_RESP, GD_TIMEOUT)) return -1; return 0; } static int gd_fn_06 (int fd, unsigned char a, unsigned char b, unsigned char c, unsigned char *data, size_t len) { GD_PKT o, i; o.address = GD_ADDR_US; o.fn = GD_FN_06; o.data[0] = len; o.data[1] = a; o.data[2] = b; o.data[3] = c; memcpy (&o.data[4], data, len); o.data_len = 4 + len; if (gd_transact (fd, &o, &i, GD_ADDR_PANEL, GD_FN_06_RESP, GD_TIMEOUT)) return -1; return 0; } static void gd_to_ascii (unsigned char *c, size_t l) { for (; l; l--, c++) { if (*c == 0xdb) *c = '+'; else if (*c == 0xb0) *c = '-'; else if ((*c > 126) || (*c < 32)) *c = '?'; } } static void gd_extract_lcd (GD_PKT *i, GD_LCD *lcd) { memcpy (lcd->line0, &i->data[3], GD_LCD_LINE_LEN); lcd->line0[GD_LCD_LINE_LEN] = 0; memcpy (lcd->line1, &i->data[3 + GD_LCD_LINE_LEN], GD_LCD_LINE_LEN); lcd->line1[GD_LCD_LINE_LEN] = 0; gd_to_ascii (lcd->line0, GD_LCD_LINE_LEN); gd_to_ascii (lcd->line1, GD_LCD_LINE_LEN); lcd->flags = i->data[2]; lcd->cx = i->data[0] - 1; while (lcd->cx >= GD_LCD_LINE_LEN) { lcd->cx -= GD_LCD_LINE_LEN; lcd->cy++; } } static int gd_fn_get_lcd (int fd, GD_LCD *lcd) { GD_PKT o, i; o.address = GD_ADDR_US; o.fn = GD_FN_GET_LCD; o.data_len = 0; if (gd_transact (fd, &o, &i, GD_ADDR_PANEL, GD_FN_LCD, GD_TIMEOUT)) return -1; if (lcd) gd_extract_lcd (&i, lcd); return 0; } static int gd_fn_key (int fd, char key, int dedup, GD_LCD *lcd) { int tries = 5; GD_PKT o, i; switch (key) { case '\e': key = 0xd; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': key -= '0'; break; case '*': key = 0xe; break; case '#': key = 0xf; break; case 'a': case 'A': key = 0xb; break; case 'b': case 'B': key = 0xa; break; case '\n': key = 0xc; break; default: return -1; } if (dedup) key |= 0x80; o.address = GD_ADDR_US; o.fn = GD_FN_PRESS_KEY; o.data[0] = key; o.data_len = 1; do { if (gd_transact (fd, &o, &i, GD_ADDR_PANEL, GD_FN_LCD, GD_TIMEOUT)) return -1; } while ((! (i.data[2] & 0x80)) && (tries--)); if (!tries) return -1; if (lcd) gd_extract_lcd (&i, lcd); return 0; } static int gd_fn_poll (int fd, GD_LCD *lcd) { GD_PKT o, i; o.address = GD_ADDR_US; o.fn = GD_FN_POLL; o.data_len = 0; if (gd_transact (fd, &o, &i, GD_ADDR_PANEL, GD_FN_LCD, GD_TIMEOUT)) return -1; if (lcd) gd_extract_lcd (&i, lcd); return 0; } static int gd_fn_key_press (int fd, char key, GD_LCD *lcd) { static int alternate = 0; int ret; ret = gd_fn_key (fd, key, alternate, lcd); alternate = !alternate; return ret; } static int gd_init (int fd) { int gv = 0; if (gd_get_panel_ver (fd, 0x30, &gv)) return -1; if (gv == 2) { if (gd_fn_05 (fd, 0x18, 0x00, 0x07)) return -1; } else if (gv == 3) { if (gd_fn_05 (fd, 0x17, 0x00, 0x00)) return -1; } if (gd_fn_06 (fd, 0x18, 0x7f, 0x03, NULL, 0)) return -1; if (gd_fn_06 (fd, 0x18, 0x7f, 0x02, NULL, 0)) return -1; if (gd_fn_06 (fd, 0x18, 0x7f, 0x01, NULL, 0)) return -1; if (gd_get_panel_ver (fd, 0x30, &gv)) return -1; { unsigned char data[2] = {0x01, 0x00}; if (gd_fn_06 (fd, 0x18, 0x00, 0x00, data, sizeof (data))) return -1; } (void) gd_fn_get_lcd (fd, NULL); if (gd_fn_05 (fd, 0x18, 0x00, 0x00)) return -1; if (gd_fn_05 (fd, 0x9c, 0x00, 0x01)) return -1; if (gd_fn_05 (fd, 0x02, 0xff, 0xff)) return -1; #if 0 if (gd_fn_key_press (fd, '\e', NULL)) return -1; if (gd_fn_key_press (fd, '\e', NULL)) return -1; #endif return 0; } static void curses_init (int debug) { initscr(); /* Start curses mode */ raw(); /* Line buffering disabled */ keypad (stdscr, TRUE); /* We get F1, F2 etc.. */ noecho(); /* Don't echo() while we do getch */ // printw ("fish"); move (15, 1); addstr ("q to quit"); refresh(); win = newwin (15, 20, 0, 0); box (win, 0, 0); wmove (win, 6, 3); waddstr (win, "1 2 3 A->"); wmove (win, 8, 3); waddstr (win, "4 5 6 B<-"); wmove (win, 10, 3); waddstr (win, "7 8 9 Ent"); wmove (win, 12, 3); waddstr (win, "* 0 # Esc"); wrefresh (win); win = newwin (4, 18, 1, 1); box (win, 0, 0); wrefresh (win); if (debug) { dwin = newwin (20, 80, 16, 0); box (dwin, 0, 0); wrefresh (dwin); dwin = newwin (18, 78, 17, 1); scrollok (dwin, TRUE); wrefresh (dwin); } timeout (0); } static void curses_end (void) { endwin(); } static void curses_show (GD_LCD *lcd) { wmove (win, 1, 1); waddstr (win, (char *) lcd->line0); wmove (win, 2, 1); waddstr (win, (char *) lcd->line1); wmove (win, lcd->cy + 1, lcd->cx + 1); wrefresh (win); } int gd_keypad (int fd, const char *dfn, int debug) { GD_LCD l; int ch; if (dfn) { dfile = fopen (dfn, "w"); if (!dfile) { perror ("open debug log"); return -1; } } if (gd_init (fd)) { printf ("Failed to init comms with panel\n"); return -1; } curses_init (debug); while ((ch = getch()) != 'q') { if (ch == KEY_LEFT) ch = 'b'; if (ch == KEY_RIGHT) ch = 'a'; if (ch != ERR && !gd_fn_key_press (fd, ch, &l)) curses_show (&l); else { gd_fn_poll (fd, &l); curses_show (&l); } } curses_end(); return 0; }