~siretart/lcd4linux/debian

0.1.6 by Jonathan McCrohan
Import upstream version 0.11.0~svn1180
1
/* $Id$
2
 * $URL$
3
 *
4
 * TeakLCM lcd4linux driver
5
 *
6
 * Copyright (C) 2005 Michael Reinelt <michael@reinelt.co.at>
7
 * Copyright (C) 2005, 2006, 2007 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
8
 * Copyright (C) 2011 Hans Ulrich Niedermann <hun@n-dimensional.de>
9
 * Copyright (C) 2011, 2012 Andreas Thienemann <andreas@bawue.net>
10
 *
11
 * This file is part of LCD4Linux.
12
 *
13
 * LCD4Linux is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2, or (at your option)
16
 * any later version.
17
 *
18
 * LCD4Linux is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26
 *
27
 */
28
29
/* 
30
 *
31
 * exported fuctions:
32
 *
33
 * struct DRIVER drv_TeakLCM
34
 *
35
 */
36
37
#include "config.h"
38
39
#include <stdlib.h>
40
#include <stdio.h>
41
#include <unistd.h>
42
#include <string.h>
43
#include <errno.h>
44
45
#include <assert.h>
46
47
#include "event.h"
48
#include "timer.h"
49
#include "debug.h"
50
#include "cfg.h"
51
#include "qprintf.h"
52
#include "udelay.h"
53
#include "plugin.h"
54
#include "widget.h"
55
#include "widget_text.h"
56
#include "widget_icon.h"
57
#include "widget_bar.h"
58
#include "drv.h"
59
60
#include "drv_generic_text.h"
61
#include "drv_generic_serial.h"
62
63
64
static char Name[] = "TeakLCM";
65
66
67
static int global_reset_rx_flag = 0;
68
69
70
#define HI8(value) ((u_int8_t)(((value)>>8) & 0xff))
71
#define LO8(value) ((u_int8_t)((value) & 0xff))
72
73
74
static u_int16_t CRC16(u_int8_t value, u_int16_t crcin)
75
{
76
    u_int16_t k = (((crcin >> 8) ^ value) & 255) << 8;
77
    u_int16_t crc = 0;
78
    int bits;
79
    for (bits = 8; bits; --bits) {
80
	if ((crc ^ k) & 0x8000)
81
	    crc = (crc << 1) ^ 0x1021;
82
	else
83
	    crc <<= 1;
84
	k <<= 1;
85
    }
86
    return ((crcin << 8) ^ crc);
87
}
88
89
90
/** Return a printable character */
91
static char printable(const char ch)
92
{
93
    if ((32 <= ch) && (ch < 127)) {
94
	return ch;
95
    } else {
96
	return '.';
97
    }
98
}
99
100
101
static void debug_data_int(const char *prefix, const void *data, const size_t size, const unsigned int delta)
102
{
103
    const u_int8_t *b = (const u_int8_t *) data;
104
    size_t y;
105
    assert(delta <= 24);
106
    for (y = 0; y < size; y += delta) {
107
	char buf[100];
108
	size_t x;
109
	ssize_t idx = 0;
110
	idx += sprintf(&(buf[idx]), "%04x ", y);
111
	for (x = 0; x < delta; x++) {
112
	    const size_t i = x + y;
113
	    if (i < size) {
114
		idx += sprintf(&(buf[idx]), " %02x", b[i]);
115
	    } else {
116
		idx += sprintf(&(buf[idx]), "   ");
117
	    }
118
	}
119
	idx += sprintf(&buf[idx], "  ");
120
	for (x = 0; x < delta; x++) {
121
	    const size_t i = x + y;
122
	    if (i < size) {
123
		idx += sprintf(&buf[idx], "%c", printable(b[i]));
124
	    } else {
125
		idx += sprintf(&buf[idx], " ");
126
	    }
127
	}
128
	debug("%s%s", prefix, buf);
129
    }
130
}
131
132
133
static void debug_data(const char *prefix, const void *data, const size_t size)
134
{
135
    debug_data_int(prefix, data, size, 16);
136
}
137
138
139
typedef enum {
140
    CMD_CONNECT = 0x05,
141
    CMD_DISCONNECT = 0x06,
142
    CMD_ALARM = 0x07,
143
    CMD_WRITE = 0x08,
144
    CMD_PRINT1 = 0x09,
145
    CMD_PRINT2 = 0x0A,
146
    CMD_ACK = 0x0B,
147
    CMD_NACK = 0x0C,
148
    CMD_CONFIRM = 0x0D,
149
    CMD_RESET = 0x0E,
150
151
    LCM_CLEAR = 0x21,
152
    LCM_HOME = 0x22,
153
    LCM_CURSOR_SHIFT_R = 0x23,
154
    LCM_CURSOR_SHIFT_L = 0x24,
155
    LCM_BACKLIGHT_ON = 0x25,
156
    LCM_BACKLIGHT_OFF = 0x26,
157
    LCM_LINE2 = 0x27,
158
    LCM_DISPLAY_SHIFT_R = 0x28,
159
    LCM_DISPLAY_SHIFT_L = 0x29,
160
    LCM_CURSOR_ON = 0x2A,
161
    LCM_CURSOR_OFF = 0x2B,
162
    LCM_CURSOR_BLINK = 0x2C,
163
    LCM_DISPLAY_ON = 0x2D,
164
    LCM_DISPLAY_OFF = 0x2E
165
} lcm_cmd_t;
166
167
168
static
169
const char *cmdstr(const lcm_cmd_t cmd)
170
{
171
    switch (cmd) {
172
#define D(CMD) case CMD_ ## CMD: return "CMD_" # CMD; break;
173
	D(CONNECT);
174
	D(DISCONNECT);
175
	D(ACK);
176
	D(NACK);
177
	D(CONFIRM);
178
	D(RESET);
179
	D(ALARM);
180
	D(WRITE);
181
	D(PRINT1);
182
	D(PRINT2);
183
#undef D
184
#define D(CMD) case LCM_ ## CMD: return "LCM_" # CMD; break;
185
	D(CLEAR);
186
	D(HOME);
187
	D(CURSOR_SHIFT_R);
188
	D(CURSOR_SHIFT_L);
189
	D(BACKLIGHT_ON);
190
	D(BACKLIGHT_OFF);
191
	D(LINE2);
192
	D(DISPLAY_SHIFT_R);
193
	D(DISPLAY_SHIFT_L);
194
	D(CURSOR_ON);
195
	D(CURSOR_OFF);
196
	D(CURSOR_BLINK);
197
	D(DISPLAY_ON);
198
	D(DISPLAY_OFF);
199
#undef D
200
    }
201
    return "CMD_UNKNOWN";
202
}
203
204
205
/*
206
 * Magic defines
207
 */
208
209
#define LCM_FRAME_MASK      0xFF
210
#define LCM_TIMEOUT         2
211
#define LCM_ESC             0x1B
212
213
#define LCM_KEY1            0x31
214
#define LCM_KEY2            0x32
215
#define LCM_KEY3            0x33
216
#define LCM_KEY4            0x34
217
#define LCM_KEY12           0x35
218
#define LCM_KEY13           0x36
219
#define LCM_KEY14           0x37
220
#define LCM_KEY23           0x38
221
#define LCM_KEY24           0x39
222
#define LCM_KEY34           0x3A
223
224
225
/****************************************/
226
/***  hardware dependant functions    ***/
227
/****************************************/
228
229
/* global LCM state machine */
230
231
232
struct _lcm_fsm_t;
233
typedef struct _lcm_fsm_t lcm_fsm_t;
234
235
236
typedef enum {
237
    ST_IDLE,			/* mode == 0, IDLE */
238
    ST_COMMAND,			/* mode == 1, COMMAND */
239
    ST_CONNECTED		/* mode == 2, CONNECTED */
240
} lcm_state_t;
241
242
243
static
244
const char *state2str(const lcm_state_t state)
245
{
246
    switch (state) {
247
    case ST_IDLE:
248
	return "ST_IDLE (0)";
249
	break;
250
    case ST_COMMAND:
251
	return "ST_COMMAND (1)";
252
	break;
253
    case ST_CONNECTED:
254
	return "ST_CONNECTED (2)";
255
	break;
256
    }
257
    return "ST_UNKNOWN";
258
}
259
260
261
#if 0
262
static
263
void repeat_connect_to_display_callback(void *data);
264
#endif
265
266
static
267
void lcm_send_cmd(lcm_cmd_t cmd);
268
269
static
270
void drv_TeakLCM_clear(void);
271
272
273
static
274
void raw_send_cmd_frame(lcm_cmd_t cmd);
275
276
static
277
void raw_send_data_frame(lcm_cmd_t cmd, const char *data, const unsigned int len);
278
279
280
static
281
lcm_state_t fsm_get_state(lcm_fsm_t * fsm);
282
283
static
284
void fsm_handle_cmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd);
285
286
static
287
void fsm_handle_datacmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const u_int8_t * payload, const unsigned int payload_len);
288
289
static
290
void try_reset(void);
291
292
static
293
void fsm_step(lcm_fsm_t * fsm);
294
295
static
296
void fsm_trans_noop(lcm_fsm_t * fsm, const lcm_state_t next_state);
297
298
static
299
void fsm_trans_cmd(lcm_fsm_t * fsm, const lcm_state_t next_state, const lcm_cmd_t cmd);
300
301
static
302
void fsm_trans_data(lcm_fsm_t * fsm,
303
		    const lcm_state_t next_state, const lcm_cmd_t cmd, const char *data, const unsigned int len);
304
305
306
static
307
void fsm_handle_bytes(lcm_fsm_t * fsm, u_int8_t * rxbuf, const unsigned int buflen)
308
{
309
    if ((buflen >= 3) && (rxbuf[0] == LCM_FRAME_MASK) && (rxbuf[2] == LCM_FRAME_MASK)) {
310
	const lcm_cmd_t cmd = rxbuf[1];
311
	debug("%s Received cmd frame (cmd=%d=%s)", __FUNCTION__, cmd, cmdstr(cmd));
312
	fsm_handle_cmd(fsm, cmd);
313
	if (buflen > 3) {
314
	    /* recursively handle remaining bytes */
315
	    fsm_handle_bytes(fsm, &rxbuf[3], buflen - 3);
316
	}
317
	return;
318
    } else if ((buflen > 3) && (rxbuf[0] == LCM_FRAME_MASK)) {
319
	unsigned int ri;	/* raw indexed */
320
	unsigned int ci;	/* cooked indexed, i.e. after unescaping */
321
322
	debug("%s Received possible data frame", __FUNCTION__);
323
324
	/* unescape rxframe data in place */
325
	u_int16_t crc0 = 0, crc1 = 0, crc2 = 0, crc3 = 0;
326
	for (ri = 1, ci = 1; ri < buflen; ri++) {
327
	    switch (rxbuf[ri]) {
328
	    case LCM_ESC:
329
		ri++;
330
		/* fall through */
331
	    default:
332
		rxbuf[ci++] = rxbuf[ri];
333
		crc3 = crc2;
334
		crc2 = crc1;
335
		crc1 = crc0;
336
		crc0 = CRC16(rxbuf[ri], crc0);
337
		break;
338
	    }
339
	    if ((rxbuf[ci - 1] == LCM_FRAME_MASK) && (rxbuf[ci - 2] == LO8(crc3)) && (rxbuf[ci - 3] == HI8(crc3))) {
340
		/* looks like a complete data frame */
341
		lcm_cmd_t cmd = rxbuf[1];
342
		u_int16_t len = (rxbuf[3] << 8) + rxbuf[2];
343
		assert(ci == (unsigned int) (1 + 1 + 2 + len + 2 + 1));
344
		fsm_handle_datacmd(fsm, cmd, &rxbuf[4], len);
345
		if (ri + 1 < buflen) {
346
		    /* recursively handle remaining bytes */
347
		    fsm_handle_bytes(fsm, &rxbuf[ri + 1], buflen - ri);
348
		}
349
		return;
350
	    }
351
	}
352
353
	fsm_trans_cmd(fsm, fsm_get_state(fsm),	/* TODO: Is this a good next_state value? */
354
		      CMD_NACK);
355
	debug("%s checksum/framemask error", __FUNCTION__);
356
	return;
357
    } else {
358
	debug("%s Received garbage data:", __FUNCTION__);
359
	debug_data(" RXD ", rxbuf, buflen);
360
	return;
361
    }
362
}
363
364
365
static void fsm_handle_cmd(lcm_fsm_t * fsm, lcm_cmd_t cmd)
366
{
367
    // debug("fsm_handle_cmd: old state 0x%02x %s", lcm_mode, modestr(lcm_mode));
368
    const lcm_state_t old_state = fsm_get_state(fsm);
369
    if (CMD_RESET == cmd) {
370
	global_reset_rx_flag = 1;
371
    }
372
    switch (old_state) {
373
    case ST_IDLE:
374
    case ST_COMMAND:
375
	switch (cmd) {
376
	case CMD_CONNECT:
377
	    fsm_trans_cmd(fsm, ST_COMMAND, CMD_ACK);
378
	    break;
379
	case CMD_ACK:
380
	    fsm_trans_cmd(fsm, ST_CONNECTED, CMD_CONFIRM);
381
	    break;
382
	case CMD_NACK:
383
	    fsm_trans_cmd(fsm, ST_IDLE, CMD_CONFIRM);
384
	    break;
385
	case CMD_CONFIRM:
386
	    fsm_trans_noop(fsm, ST_CONNECTED);
387
	    break;
388
	case CMD_RESET:
389
	    fsm_trans_cmd(fsm, ST_COMMAND, CMD_CONNECT);
390
	    break;
391
	default:
392
	    error("%s: Unhandled cmd %s in state %s", Name, cmdstr(cmd), state2str(old_state));
393
	    fsm_trans_cmd(fsm, ST_IDLE, CMD_NACK);
394
	    break;
395
	}
396
	break;
397
    case ST_CONNECTED:		/* "if (mode == 2)" */
398
	switch (cmd) {
399
	case CMD_ACK:
400
	    fsm_trans_cmd(fsm, ST_CONNECTED, CMD_CONFIRM);
401
	    break;
402
	case CMD_CONNECT:
403
	    fsm_trans_cmd(fsm, ST_CONNECTED, CMD_NACK);
404
	    break;
405
	case CMD_DISCONNECT:
406
	    fsm_trans_cmd(fsm, ST_CONNECTED, CMD_ACK);
407
	    break;
408
	case CMD_RESET:
409
	    fsm_trans_cmd(fsm, ST_IDLE, CMD_CONNECT);
410
	    break;
411
	default:
412
	    debug("%s: Ignoring unhandled cmd %s in state %s", Name, cmdstr(cmd), state2str(old_state));
413
	    fsm_trans_noop(fsm, ST_CONNECTED);
414
	    break;
415
	}
416
	break;
417
    }
418
    fsm_step(fsm);
419
}
420
421
422
static
423
void fsm_handle_datacmd(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const u_int8_t * payload, unsigned int payload_len)
424
{
425
    const lcm_state_t old_state = fsm_get_state(fsm);
426
    debug("fsm_handle_datacmd: old state 0x%02x %s", old_state, state2str(old_state));
427
    switch (old_state) {
428
    case ST_CONNECTED:
429
	switch (cmd) {
430
	case CMD_WRITE:
431
	    assert(payload_len == 1);
432
	    debug("Got a key code 0x%02x", *payload);
433
	    fsm_trans_noop(fsm, ST_CONNECTED);
434
	    // lcm_send_cmd_frame(CMD_ACK);
435
	    break;
436
	default:
437
	    debug("Got an unknown data frame: %d=%s", cmd, cmdstr(cmd));
438
	    fsm_trans_noop(fsm, ST_CONNECTED);
439
	    // lcm_send_cmd_frame(CMD_NACK);
440
	    break;
441
	}
442
	break;
443
    case ST_IDLE:
444
    case ST_COMMAND:
445
	fsm_trans_cmd(fsm, old_state, CMD_NACK);
446
	break;
447
    }
448
    fsm_step(fsm);
449
}
450
451
452
struct _lcm_fsm_t {
453
    lcm_state_t state;
454
    lcm_state_t next_state;
455
    enum {
456
	ACTION_UNINITIALIZED,
457
	ACTION_NOOP,
458
	ACTION_CMD,
459
	ACTION_DATA
460
    } action_type;
461
    union {
462
	struct {
463
	    lcm_cmd_t cmd;
464
	} cmd_frame;
465
	struct {
466
	    lcm_cmd_t cmd;
467
	    const char *data;
468
	    unsigned int len;
469
	} data_frame;
470
    } action;
471
};
472
473
474
static
475
lcm_state_t fsm_get_state(lcm_fsm_t * fsm)
476
{
477
    return fsm->state;
478
}
479
480
481
static
482
void flush_shadow(void);
483
484
485
static
486
void fsm_step(lcm_fsm_t * fsm)
487
{
488
    debug("fsm: old_state=%s new_state=%s", state2str(fsm->state), state2str(fsm->next_state));
489
    switch (fsm->action_type) {
490
    case ACTION_UNINITIALIZED:
491
	error("Uninitialized LCM FSM action");
492
	abort();
493
	break;
494
    case ACTION_NOOP:
495
	break;
496
    case ACTION_CMD:
497
	raw_send_cmd_frame(fsm->action.cmd_frame.cmd);
498
	break;
499
    case ACTION_DATA:
500
	raw_send_data_frame(fsm->action.data_frame.cmd, fsm->action.data_frame.data, fsm->action.data_frame.len);
501
	break;
502
    }
503
    fsm->action_type = ACTION_UNINITIALIZED;
504
    switch (fsm->next_state) {
505
    case ST_IDLE:
506
    case ST_COMMAND:
507
	fsm->state = fsm->next_state;
508
	fsm->next_state = -1;
509
	return;
510
	break;
511
    case ST_CONNECTED:
512
	if (fsm->state != ST_CONNECTED) {
513
	    /* going from ST_IDLE or ST_COMMAND into ST_CONNECTED */
514
	    if (!global_reset_rx_flag) {
515
		try_reset();
516
#if 0
517
		int timer_res = timer_add(repeat_connect_to_display_callback, NULL, 50 /*ms */ , 1);
518
		debug("re-scheduled connect callback result: %d", timer_res);
519
520
		done by try_reset / fsm_init fsm->state = fsm->next_state;
521
		fsm->next_state = -1;
522
#endif
523
		return;
524
	    } else {
525
		/* properly connected for the first time */
526
		debug("%s: %s NOW CONNECTED!!!", Name, __FUNCTION__);
527
528
		fsm->state = fsm->next_state;
529
		fsm->next_state = -1;
530
531
		lcm_send_cmd(LCM_DISPLAY_ON);
532
		flush_shadow();
533
		lcm_send_cmd(LCM_BACKLIGHT_ON);
534
		return;
535
	    }
536
	} else {
537
	    debug("no state change in ST_CONNECTED");
538
	    fsm->state = fsm->next_state;
539
	    fsm->next_state = -1;
540
	    return;
541
	}
542
	error("we should never arrive here");
543
	abort();
544
	break;
545
    }
546
    error("LCM FSM: Illegal next_state");
547
    abort();
548
}
549
550
551
#if 0
552
553
#endif
554
555
556
static
557
void fsm_trans_noop(lcm_fsm_t * fsm, const lcm_state_t next_state)
558
{
559
    fsm->next_state = next_state;
560
    fsm->action_type = ACTION_NOOP;
561
}
562
563
564
static
565
void fsm_trans_cmd(lcm_fsm_t * fsm, const lcm_state_t next_state, const lcm_cmd_t cmd)
566
{
567
    fsm->next_state = next_state;
568
    fsm->action_type = ACTION_CMD;
569
    fsm->action.cmd_frame.cmd = cmd;
570
}
571
572
573
static
574
void fsm_trans_data(lcm_fsm_t * fsm,
575
		    const lcm_state_t next_state, const lcm_cmd_t cmd, const char *data, const unsigned int len)
576
{
577
    fsm->next_state = next_state;
578
    fsm->action_type = ACTION_DATA;
579
    fsm->action.data_frame.cmd = cmd;
580
    fsm->action.data_frame.data = data;
581
    fsm->action.data_frame.len = len;
582
}
583
584
585
static
586
void fsm_send(lcm_fsm_t * fsm, const lcm_cmd_t cmd)
587
{
588
    const lcm_state_t old_state = fsm_get_state(fsm);
589
    switch (old_state) {
590
    case ST_IDLE:
591
    case ST_COMMAND:
592
	debug("%s: %s, ignoring cmd 0x%02x=%s", __FUNCTION__, state2str(old_state), cmd, cmdstr(cmd));
593
	/* Silently ignore the command to send. */
594
	/* TODO: Would it be better to queue it and send it later? */
595
	break;
596
    case ST_CONNECTED:
597
	fsm_trans_cmd(fsm, ST_CONNECTED, cmd);
598
	fsm_step(fsm);
599
	break;
600
    }
601
}
602
603
604
static
605
void fsm_send_data(lcm_fsm_t * fsm, const lcm_cmd_t cmd, const void *data, const unsigned int len)
606
{
607
    const lcm_state_t old_state = fsm_get_state(fsm);
608
    switch (old_state) {
609
    case ST_IDLE:
610
    case ST_COMMAND:
611
	debug("%s: %s, ignoring data cmd 0x%02x=%s", __FUNCTION__, state2str(old_state), cmd, cmdstr(cmd));
612
	/* Silently ignore the command to send. */
613
	/* TODO: Would it be better to queue it and send it later? */
614
	break;
615
    case ST_CONNECTED:
616
	fsm_trans_data(fsm, ST_CONNECTED, cmd, data, len);
617
	fsm_step(fsm);
618
	break;
619
    }
620
}
621
622
623
static lcm_fsm_t lcm_fsm;
624
625
626
static
627
void fsm_init(void)
628
{
629
    lcm_fsm.state = ST_IDLE;
630
    lcm_fsm.next_state = -1;
631
    lcm_fsm.action_type = ACTION_UNINITIALIZED;
632
}
633
634
635
636
/* Send a command frame to the TCM board */
637
static
638
void raw_send_cmd_frame(lcm_cmd_t cmd)
639
{
640
    // lcm_receive_check();
641
    char cmd_buf[3];
642
    cmd_buf[0] = LCM_FRAME_MASK;
643
    cmd_buf[1] = cmd;
644
    cmd_buf[2] = LCM_FRAME_MASK;
645
    debug("%s sending cmd frame cmd=0x%02x=%s", __FUNCTION__, cmd, cmdstr(cmd));
646
    debug_data(" TX ", cmd_buf, 3);
647
    drv_generic_serial_write(cmd_buf, 3);
648
    usleep(100);
649
#if 0
650
    usleep(100000);
651
    switch (cmd) {
652
    case CMD_ACK:
653
	//case CMD_CONFIRM:
654
    case CMD_NACK:
655
	lcm_receive_check();
656
	break;
657
    default:
658
	if (1) {
659
	    int i;
660
	    for (i = 0; i < 20; i++) {
661
		usleep(100000);
662
		if (lcm_receive_check()) {
663
		    break;
664
		}
665
	    }
666
	}
667
	break;
668
    }
669
#endif
670
}
671
672
673
/* Send a data frame to the TCM board */
674
static
675
void raw_send_data_frame(lcm_cmd_t cmd, const char *data, const unsigned int len)
676
{
677
    unsigned int di;		/* data index */
678
    unsigned int fi;		/* frame index */
679
    static char frame[32];
680
    u_int16_t crc = 0;
681
682
    frame[0] = LCM_FRAME_MASK;
683
684
    frame[1] = cmd;
685
    crc = CRC16(frame[1], crc);
686
687
    frame[2] = HI8(len);
688
    crc = CRC16(frame[2], crc);
689
690
    frame[3] = LO8(len);
691
    crc = CRC16(frame[3], crc);
692
693
#define APPEND(value)				       \
694
    do {					       \
695
	const unsigned char v = (value);	       \
696
	if ((v == LCM_FRAME_MASK) || (v == LCM_ESC)) { \
697
	    frame[fi++] = LCM_ESC;		       \
698
	}					       \
699
	frame[fi++] = v;			       \
700
	crc = CRC16(v, crc);			       \
701
    } while (0)
702
703
#define APPEND_NOCRC(value)			       \
704
    do {					       \
705
	const unsigned char v = (value);	       \
706
	if ((v == LCM_FRAME_MASK) || (v == LCM_ESC)) { \
707
	    frame[fi++] = LCM_ESC;		       \
708
	}					       \
709
	frame[fi++] = v;			       \
710
    } while (0)
711
712
    for (fi = 4, di = 0; di < len; di++) {
713
	APPEND(data[di]);
714
    }
715
716
    APPEND_NOCRC(HI8(crc));
717
    APPEND_NOCRC(LO8(crc));
718
719
    frame[fi++] = LCM_FRAME_MASK;
720
721
    debug_data(" TXD ", frame, fi);
722
    drv_generic_serial_write(frame, fi);
723
724
#undef APPEND
725
726
    usleep(500);
727
}
728
729
730
static
731
void lcm_send_cmd(lcm_cmd_t cmd)
732
{
733
    fsm_send(&lcm_fsm, cmd);
734
}
735
736
737
static
738
void lcm_event_callback(event_flags_t flags, void *data)
739
{
740
    lcm_fsm_t *fsm = (lcm_fsm_t *) data;
741
    debug("%s: flags=%d, data=%p", __FUNCTION__, flags, data);
742
    if (flags & EVENT_READ) {
743
	static u_int8_t rxbuf[32];
744
	const int readlen = drv_generic_serial_poll((void *) rxbuf, sizeof(rxbuf));
745
	if (readlen <= 0) {
746
	    debug("%s Received no data", __FUNCTION__);
747
	} else {
748
	    debug("%s RECEIVED %d bytes", __FUNCTION__, readlen);
749
	    debug_data(" RX ", rxbuf, readlen);
750
	    fsm_handle_bytes(fsm, rxbuf, readlen);
751
	}
752
    }
753
}
754
755
756
static int drv_TeakLCM_open(const char *section)
757
{
758
    /* open serial port */
759
    /* don't mind about device, speed and stuff, this function will take care of */
760
761
    const int fd = drv_generic_serial_open(section, Name, 0);
762
    if (fd < 0)
763
	return -1;
764
765
    return fd;
766
}
767
768
769
static int drv_TeakLCM_close(int fd)
770
{
771
    if (fd >= 0) {
772
	event_del(fd);
773
    }
774
775
    /* close whatever port you've opened */
776
    drv_generic_serial_close();
777
778
    return 0;
779
}
780
781
782
/* shadow buffer */
783
static
784
char *shadow;
785
786
787
static void debug_shadow(const char *prefix)
788
{
789
    debug_data_int(prefix, shadow, DCOLS * DROWS, 20);
790
}
791
792
793
static
794
void flush_shadow(void)
795
{
796
    debug("%s called", __FUNCTION__);
797
    debug_shadow(" shadow ");
798
    usleep(50000);
799
    fsm_send_data(&lcm_fsm, CMD_PRINT1, &shadow[DCOLS * 0], DCOLS);
800
    usleep(50000);
801
    fsm_send_data(&lcm_fsm, CMD_PRINT2, &shadow[DCOLS * 1], DCOLS);
802
    usleep(50000);
803
}
804
805
806
/* text mode displays only */
807
static
808
void drv_TeakLCM_clear(void)
809
{
810
    /* do whatever is necessary to clear the display */
811
    memset(shadow, ' ', DROWS * DCOLS);
812
    flush_shadow();
813
}
814
815
816
/* text mode displays only */
817
static void drv_TeakLCM_write(const int row, const int col, const char *data, int len)
818
{
819
    debug("%s row=%d col=%d len=%d data=\"%s\"", __FUNCTION__, row, col, len, data);
820
821
    memcpy(&shadow[DCOLS * row + col], data, len);
822
823
    debug_shadow(" shadow ");
824
825
    fsm_send_data(&lcm_fsm, (row == 0) ? CMD_PRINT1 : CMD_PRINT2, &shadow[DCOLS * row], DCOLS);
826
}
827
828
829
static
830
void try_reset(void)
831
{
832
    debug("%s called", __FUNCTION__);
833
    fsm_init();
834
    raw_send_cmd_frame(CMD_RESET);
835
}
836
837
838
#if 0
839
static
840
void repeat_connect_to_display_callback(void *data)
841
{
842
    static int already_called = 0;
843
    if (!already_called) {
844
	debug("%s(%p): called", __FUNCTION__, data);
845
846
	/* reset & initialize display */
847
	try_reset();
848
	already_called = 1;
849
    } else {
850
	debug("%s(%p): already called, ignoring", __FUNCTION__, data);
851
    }
852
}
853
#endif
854
855
856
static
857
int global_fd = -1;
858
859
860
static
861
void initial_connect_to_display_callback(void *data)
862
{
863
    debug("%s(%p): called", __FUNCTION__, data);
864
865
    debug("Calling event_add for fd=%d", global_fd);
866
    int ret = event_add(lcm_event_callback, &lcm_fsm, global_fd, 1, 0, 1);
867
    debug("event_add result: %d", ret);
868
869
    /* reset & initialize display */
870
    try_reset();
871
}
872
873
874
/* start text mode display */
875
static int drv_TeakLCM_start(const char *section)
876
{
877
    int rows = -1, cols = -1;
878
    char *s;
879
880
    s = cfg_get(section, "Size", NULL);
881
    if (s == NULL || *s == '\0') {
882
	error("%s: no '%s.Size' entry from %s", Name, section, cfg_source());
883
	return -1;
884
    }
885
    if (sscanf(s, "%dx%d", &cols, &rows) != 2 || rows < 1 || cols < 1) {
886
	error("%s: bad %s.Size '%s' from %s", Name, section, s, cfg_source());
887
	free(s);
888
	return -1;
889
    }
890
891
    DROWS = rows;
892
    DCOLS = cols;
893
    shadow = malloc(DROWS * DCOLS);
894
    memset(shadow, ' ', DROWS * DCOLS);
895
896
    /* open communication with the display */
897
    global_fd = drv_TeakLCM_open(section);
898
    if (global_fd < 0) {
899
	return -1;
900
    }
901
    debug("%s: %s opened", Name, __FUNCTION__);
902
903
    /* read initial garbage data */
904
    static u_int8_t rxbuf[32];
905
    const int readlen = drv_generic_serial_poll((void *) rxbuf, sizeof(rxbuf));
906
    if (readlen >= 0) {
907
	debug_data(" initial RX garbage ", rxbuf, readlen);
908
    }
909
910
    /* We need to do a delayed connect */
911
    int timer_res = timer_add(initial_connect_to_display_callback, NULL, 10 /*ms */ , 1);
912
    debug("timer_add for connect callback result: %d", timer_res);
913
914
    debug("%s: %s done", Name, __FUNCTION__);
915
    return 0;
916
}
917
918
919
/****************************************/
920
/***            plugins               ***/
921
/****************************************/
922
923
924
/****************************************/
925
/***        widget callbacks          ***/
926
/****************************************/
927
928
929
/* using drv_generic_text_draw(W) */
930
/* using drv_generic_text_icon_draw(W) */
931
/* using drv_generic_text_bar_draw(W) */
932
933
934
/****************************************/
935
/***        exported functions        ***/
936
/****************************************/
937
938
939
/* list models */
940
int drv_TeakLCM_list(void)
941
{
942
    printf("TeakLCM driver");
943
    return 0;
944
}
945
946
947
/* initialize driver & display */
948
/* use this function for a text display */
949
int drv_TeakLCM_init(const char *section, const int quiet)
950
{
951
    WIDGET_CLASS wc;
952
    int ret;
953
954
    info("%s: %s (quiet=%d)", Name, "$Rev$", quiet);
955
956
    /* display preferences */
957
    XRES = 5;			/* pixel width of one char  */
958
    YRES = 8;			/* pixel height of one char  */
959
    CHARS = 0;			/* number of user-defineable characters */
960
    CHAR0 = 0;			/* ASCII of first user-defineable char */
961
    GOTO_COST = -1;		/* number of bytes a goto command requires */
962
963
    /* real worker functions */
964
    drv_generic_text_real_write = drv_TeakLCM_write;
965
966
    /* start display */
967
    if ((ret = drv_TeakLCM_start(section)) != 0)
968
	return ret;
969
970
    /* initialize generic text driver */
971
    if ((ret = drv_generic_text_init(section, Name)) != 0)
972
	return ret;
973
974
    /* register text widget */
975
    wc = Widget_Text;
976
    wc.draw = drv_generic_text_draw;
977
    widget_register(&wc);
978
979
    /* register plugins */
980
981
    return 0;
982
}
983
984
985
/* close driver & display */
986
/* use this function for a text display */
987
int drv_TeakLCM_quit(const int quiet)
988
{
989
990
    info("%s: shutting down. (quiet=%d)", Name, quiet);
991
992
    drv_generic_text_quit();
993
994
    /* clear display */
995
    drv_TeakLCM_clear();
996
997
    lcm_send_cmd(LCM_DISPLAY_OFF);
998
    // lcm_send_cmd_frame(LCM_BACKLIGHT_OFF);
999
    lcm_send_cmd(CMD_DISCONNECT);
1000
1001
    /* FIXME: consume final ack frame */
1002
    usleep(100000);
1003
1004
    debug("closing connection");
1005
    drv_TeakLCM_close(global_fd);
1006
1007
    return (0);
1008
}
1009
1010
1011
/* use this one for a text display */
1012
DRIVER drv_TeakLCM = {
1013
    .name = Name,
1014
    .list = drv_TeakLCM_list,
1015
    .init = drv_TeakLCM_init,
1016
    .quit = drv_TeakLCM_quit,
1017
};
1018
1019
1020
/*
1021
 * Local Variables:
1022
 * c-basic-offset: 4
1023
 * End:
1024
 */