Base: LCDproc 0.5.2
[lcdproc-de200c.git] / server / drivers / ula200.c
blob6fb7afaf06e91cd4d4fdcc0d1aec6510b2eee5ee
1 // Description:
2 //
3 // This is a driver for the ULA-200 from ELV (http://www.elv.de). It's a small
4 // board which can be connected to the PC via USB interface and that allows to
5 // connect a HD44780-compatible display. It also has a connection for up to 6
6 // input buttons.
7 //
8 // It uses libftdi (which uses libusb) for communication, so no kernel driver
9 // is needed. You have to take care that ftdi_sio module doesn't claim the ELV
10 // device. If you didn't change the ids in the driver (ftdi_sio.c), you don't
11 // have to matter.
14 // Features:
16 // - text display is supported
17 // - standard symbols (heart, checkbox) are supported
18 // - backlight is supported
19 // - input buttons are supported
20 // - bars are NOT supported
23 // Configuration options (the values are the default values):
25 // - Size=20x4
26 // - KeyMap_A=Up
27 // - KeyMap_B=Down
28 // - KeyMap_C=Left
29 // - KeyMap_D=Right
30 // - KeyMap_E=Enter
31 // - KeyMap_F=Escape
34 // Known problems:
36 // Sometimes the display hangs (the ACK response is not received) on shutdown.
37 // Reconnect the display in that case. The same if it hangs while starting
38 // up (only happens if it was not the first time lcdproc talked to the
39 // display).
41 // I tried several hours to fix this, I simply find the reason for this problem.
42 //
44 // Implementation note:
46 // The ULA-200 talks a text protocol which allows to display text using a
47 // high-level language, i.e. STX 's' <len> <char0> <char1> ... ETX. It also
48 // allows low-level register access to the HD44780. So in theory, it would be
49 // possible to write a hd44780 extension and let the hd44780 core do the rest.
50 // I tried this. It was slow and didn't work with user-specific characters
51 // (the hd44780 frequently changes this characters which seems to confuse the
52 // microcontroller, at least I cannot explain why it didn't work, there was
53 // garbare).
55 // So I wrote a own driver (this here) which uses the high-level language and
56 // should work for displays with all sizes. I only tested 20x4, so maybe for
57 // other sizes the positioning code may be adapted.
59 // As I mentioned, there were problems with frequently changing the
60 // user-definable characters. I also tried to implement bar code in the ula200
61 // driver with similar effects. I gave it up because I don't need it personally
62 // and it can be done later. However, standard icons are implemented. The
63 // user-definable characters are set in startup and are not changed. This
64 // works like a charm. It is not possible to use character 0 with the
65 // high-level language (or at least it isn't documented how to escape it).
66 // It could be done with hd44780 code, but I replaced the character with
67 // a standard character which looks good.
70 // Author
72 // Bernhard Walle <bernhard.walle@gmx.de>
73 // Feel free to contact me if there are problems with this driver.
76 /* Copyright (C) 2006 Bernhard Walle <bernhard.walle@gmx.de>
78 This program is free software; you can redistribute it and/or modify
79 it under the terms of the GNU General Public License as published by
80 the Free Software Foundation; either version 2 of the License, or
81 any later version.
83 This program is distributed in the hope that it will be useful,
84 but WITHOUT ANY WARRANTY; without even the implied warranty of
85 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
86 GNU General Public License for more details.
88 You should have received a copy of the GNU General Public License
89 along with this program; if not, write to the Free Software
90 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */
92 #include <stdlib.h>
93 #include <stdio.h>
94 #include <unistd.h>
95 #include <termios.h>
96 #include <fcntl.h>
97 #include <string.h>
98 #include <errno.h>
99 #include <syslog.h>
101 #include <usb.h>
102 #include <ftdi.h>
104 #ifdef HAVE_CONFIG_H
105 # include "config.h"
106 #endif
108 #include "hd44780-charmap.h"
109 #include "lcd.h"
110 #include "ula200.h"
111 #include "report.h"
112 #include "lcd_lib.h"
113 #include "timing.h"
116 /* Vars for the server core */
117 MODULE_EXPORT char *api_version = API_VERSION;
118 MODULE_EXPORT int stay_in_foreground = 0;
119 MODULE_EXPORT int supports_multiple = 1;
120 MODULE_EXPORT char *symbol_prefix = "ula200_";
123 ///////////////////////////////////////////////////////////////////////////////
124 // constants
126 #define DISPLAY_VENDOR_ID 0x0403
127 #define DISPLAY_PRODUCT_ID 0xf06d
129 #define CH_STX 0x02
130 #define CH_ETX 0x03
131 #define CH_ENQ 0x05
132 #define CH_ACK 0x06
133 #define CH_NAK 0x15
134 #define CH_DC2 0x12
135 #define CH_DC3 0x13
137 #define MAX_KEY_MAP 6
138 static char *default_key_map[MAX_KEY_MAP] = { "Up", "Down", "Left", "Right", "Enter", "Escape" };
140 #define CELLWIDTH 5
141 #define CELLHEIGHT 8
143 /* repeat conts for responses */
144 #define MAX_REPEATS 20
147 ///////////////////////////////////////////////////////////////////////////////
148 // Key ring implementation from CFontz633io
151 /* KeyRing management */
152 #define KEYRINGSIZE 16
154 typedef struct {
155 unsigned char contents[KEYRINGSIZE];
156 int head;
157 int tail;
158 } KeyRing;
163 * KeyRing handling functions.
164 * This separates the producer from the consumer.
165 * It is just a small fifo of unsigned char.
168 /** initialize/empty key ring by resetting its read & write pointers */
169 void EmptyKeyRing(KeyRing *kr)
171 kr->head = kr->tail = 0;
175 /** add byte to key ring; return success (byte added) / failure (key ring is full) */
176 int AddKeyToKeyRing(KeyRing *kr, unsigned char key)
178 if (((kr->head + 1) % KEYRINGSIZE) != (kr->tail % KEYRINGSIZE)) {
179 /* fprintf(stderr, "We add key: %d\n", key); */
181 kr->contents[kr->head % KEYRINGSIZE] = key;
182 kr->head = (kr->head + 1) % KEYRINGSIZE;
183 return 1;
186 /* KeyRing overflow: do not accept extra key */
187 return 0;
191 /** get byte from key ring (or '\\0' if key ring is empty) */
192 unsigned char GetKeyFromKeyRing(KeyRing *kr)
194 unsigned char retval = '\0';
196 kr->tail %= KEYRINGSIZE;
198 if ((kr->head % KEYRINGSIZE) != kr->tail) {
199 retval = kr->contents[kr->tail];
200 kr->tail = (kr->tail + 1) % KEYRINGSIZE;
203 /* if (retval) fprintf(stderr, "We remove key: %d\n", retval); */
204 return retval;
207 ///////////////////////////////////////////////////////////////////////////////
208 // for HD 44780 compatibility
211 // commands for senddata
212 #define RS_DATA 0x00
213 #define RS_INSTR 0x01
215 #define SETCHAR 0x40 /* Only reachable with EXTREG clear */
218 ///////////////////////////////////////////////////////////////////////////////
219 // private data types
221 typedef struct {
223 // the handle for the USB FTDI library
224 struct ftdi_context ftdic;
226 // the width and the height (in number of characters) of the library
227 int width, height;
229 // The framebuffer and the framebuffer for the last contents (incr. update)
230 unsigned char *framebuf, *lcd_contents;
232 // first time => all is dirty
233 unsigned char all_dirty;
235 // backlight (-1 = unset, 0 = off, 1 = on)
236 int backlight;
238 // the keyring
239 KeyRing keyring;
241 // the keymap
242 char *key_map[MAX_KEY_MAP];
244 } PrivateData;
247 ///////////////////////////////////////////////////////////////////////////////
248 // Reads a USB characters
249 static inline int
250 ula200_ftdi_usb_read(PrivateData *p)
252 unsigned char buffer[1];
253 int err;
255 while ((err = ftdi_read_data(&p->ftdic, buffer, 1)) == 0);
257 if (err < 0) {
258 return -1;
260 else {
261 return buffer[0];
266 ///////////////////////////////////////////////////////////////////////////////
267 // Reads the response
269 // @return true if ACK was responded, false if NACk was responded
271 static bool
272 ula200_ftdi_read_response(Driver *drvthis)
274 PrivateData *p = (PrivateData *) drvthis->private_data;
275 bool result = false;
276 bool answer_read = false;
277 int ret;
278 int ch;
280 while (!answer_read)
282 /* wait until STX */
283 while (((ret = ula200_ftdi_usb_read(p)) != CH_STX) && (ret > 0));
284 if (ret < 0) {
285 return false;
288 /* read next char */
289 ch = ula200_ftdi_usb_read(p);
291 switch (ch) {
292 case 't':
293 ch = ula200_ftdi_usb_read(p);
294 AddKeyToKeyRing(&p->keyring, (unsigned char)(ch - 0x40));
295 break;
297 case CH_ACK:
298 answer_read = true;
299 result = true;
300 break;
302 case CH_NAK:
303 answer_read = true;
304 break;
306 default:
307 answer_read = true;
308 report(RPT_INFO, "%s: read invalid answer (0x%02X)", drvthis->name, ch);
311 /* wait until ETX */
312 while (((ret = ula200_ftdi_usb_read(p)) != CH_ETX) && (ret > 0));
313 if (ret < 0) {
314 return false;
318 return result;
321 ///////////////////////////////////////////////////////////////////////////////
322 // Write a command to the display. Adds the STX and ETX header/trailer.
324 // @param p the private data
325 // @param data the data bytes
326 // @param length the number of bytes in @p data which are valid
327 // @param escape if the data should be escaped (see the User's Guide of the
328 // ULA-200)
329 // @return 0 on success, negative value on error
331 static int
332 ula200_ftdi_write_command(Driver *drvthis, unsigned char *data, int length, bool escape)
334 PrivateData *p = (PrivateData *) drvthis->private_data;
335 int i, err;
336 int repeat_count = 0;
337 int pos = 0;
338 unsigned char buffer[1024];
340 if (length > 512) {
341 return -EINVAL;
344 /* fill the array */
345 buffer[pos++] = CH_STX;
346 for (i = 0; i < length; i++) {
347 if (escape) {
348 if (data[i] == CH_STX) {
349 buffer[pos++] = CH_ENQ;
350 buffer[pos++] = CH_DC2;
352 else if (data[i] == CH_ETX) {
353 buffer[pos++] = CH_ENQ;
354 buffer[pos++] = CH_DC3;
356 else if (data[i] == CH_ENQ) {
357 buffer[pos++] = CH_ENQ;
358 buffer[pos++] = CH_NAK;
360 else {
361 buffer[pos++] = data[i];
364 else {
365 buffer[pos++] = data[i];
368 buffer[pos++] = CH_ETX;
370 do {
371 /* bytes */
372 err = ftdi_write_data(&p->ftdic, buffer, pos);
373 if (err < 0) {
374 report(RPT_WARNING, "%s: ftdi_write_data failed", drvthis->name);
375 return -1;
378 while (!ula200_ftdi_read_response(drvthis) && (repeat_count++ < MAX_REPEATS));
380 return 0;
383 ///////////////////////////////////////////////////////////////////////////////
384 // Clear the display
386 static int
387 ula200_ftdi_clear(Driver *drvthis)
389 //PrivateData *p = (PrivateData *) drvthis->private_data;
390 unsigned char command[1];
391 int err;
393 command[0] = 'l';
394 err = ula200_ftdi_write_command(drvthis, command, 1, true);
395 if (err < 0) {
396 report(RPT_WARNING, "%s: ula200_ftdi_clear: "
397 "ula200_ftdi_write_command failed", drvthis->name);
400 return err;
404 ///////////////////////////////////////////////////////////////////////////////
405 // Set the position
407 static int
408 ula200_ftdi_position(Driver *drvthis, int x, int y)
410 PrivateData *p = (PrivateData *) drvthis->private_data;
411 unsigned char command[5];
412 int err;
414 if (y >= 2) {
415 y -= 2;
416 x += p->width;
419 command[0] = 'p';
420 command[1] = x;
421 command[2] = y;
422 err = ula200_ftdi_write_command(drvthis, command, 3, true);
423 if (err < 0) {
424 report(RPT_WARNING, "%s: ula200_ftdi_position(%d,%d): "
425 "ula200_ftdi_write_command failed",
426 drvthis->name, x, y);
429 return err;
433 ///////////////////////////////////////////////////////////////////////////////
434 // Send raw data
436 static int
437 ula200_ftdi_rawdata(Driver *drvthis, unsigned char flags, unsigned char ch)
439 //PrivateData *p = (PrivateData *) drvthis->private_data;
440 unsigned char command[3];
441 unsigned int err;
443 command[0] = 'R';
444 command[1] = flags == (RS_DATA) ? '2' : '0';
445 command[2] = ch;
446 err = ula200_ftdi_write_command(drvthis, command, 3, false);
447 if (err < 0) {
448 report(RPT_ERR, "%s: ftdi_write_command() failed", drvthis->name);
451 return err;
455 ///////////////////////////////////////////////////////////////////////////////
456 // Displays a string
458 static int
459 ula200_ftdi_string(Driver *drvthis, const unsigned char *string, int len)
461 //PrivateData *p = (PrivateData *) drvthis->private_data;
462 unsigned char buffer[128];
463 int err;
464 int i;
466 if (len > 80) {
467 return -EINVAL;
470 buffer[0] = 's';
471 buffer[1] = len;
472 for (i = 0; i < len; i++) {
473 buffer[i+2] = HD44780_charmap[(unsigned char)string[i]];
476 err = ula200_ftdi_write_command(drvthis, buffer, len+2, true);
477 if (err < 0) {
478 report(RPT_WARNING, "%s: ula200_ftdi_string: "
479 "ula200_ftdi_write_command() failed", drvthis->name);
482 return err;
486 ///////////////////////////////////////////////////////////////////////////////
487 // Enables the raw register access mode.
489 static int
490 ula200_ftdi_enable_raw_mode(Driver *drvthis)
492 unsigned char command[3];
494 report(RPT_DEBUG, "%s: enable raw mode", drvthis->name);
496 command[0] = 'R';
497 command[1] = 'E';
498 command[2] = '1';
499 return ula200_ftdi_write_command(drvthis, command, 3, false);
502 ///////////////////////////////////////////////////////////////////////////////
503 // Loads custom characters in the display
505 static int
506 ula200_load_curstom_chars(Driver *drvthis)
508 int i, col, row;
509 int err = 0;
510 char custom_chars[8][CELLHEIGHT*CELLHEIGHT] = {
511 { 1, 1, 1, 1, 1,
512 1, 1, 1, 1, 1,
513 1, 1, 1, 1, 1,
514 1, 1, 1, 1, 1,
515 1, 1, 1, 1, 1,
516 1, 1, 1, 1, 1,
517 1, 1, 1, 1, 1,
518 1, 1, 1, 1, 1 },
519 { 1, 1, 1, 1, 1,
520 1, 0, 1, 0, 1,
521 0, 0, 0, 0, 0,
522 0, 0, 0, 0, 0,
523 0, 0, 0, 0, 0,
524 1, 0, 0, 0, 1,
525 1, 1, 0, 1, 1,
526 1, 1, 1, 1, 1 },
527 { 1, 1, 1, 1, 1,
528 1, 0, 1, 0, 1,
529 0, 1, 0, 1, 0,
530 0, 1, 1, 1, 0,
531 0, 1, 1, 1, 0,
532 1, 0, 1, 0, 1,
533 1, 1, 0, 1, 1,
534 1, 1, 1, 1, 1 },
535 { 0, 0, 1, 0, 0,
536 0, 1, 1, 1, 0,
537 1, 0, 1, 0, 1,
538 0, 0, 1, 0, 0,
539 0, 0, 1, 0, 0,
540 0, 0, 1, 0, 0,
541 0, 0, 1, 0, 0,
542 0, 0, 0, 0, 0 },
543 { 0, 0, 1, 0, 0,
544 0, 0, 1, 0, 0,
545 0, 0, 1, 0, 0,
546 0, 0, 1, 0, 0,
547 1, 0, 1, 0, 1,
548 0, 1, 1, 1, 0,
549 0, 0, 1, 0, 0,
550 0, 0, 0, 0, 0 },
551 { 0, 0, 0, 0, 0,
552 0, 0, 0, 0, 0,
553 1, 1, 1, 1, 1,
554 1, 0, 0, 0, 1,
555 1, 0, 0, 0, 1,
556 1, 0, 0, 0, 1,
557 1, 1, 1, 1, 1,
558 0, 0, 0, 0, 0 },
559 { 0, 0, 1, 0, 0,
560 0, 0, 1, 0, 0,
561 1, 1, 1, 0, 1,
562 1, 0, 1, 1, 0,
563 1, 0, 1, 0, 1,
564 1, 0, 0, 0, 1,
565 1, 1, 1, 1, 1,
566 0, 0, 0, 0, 0 },
567 { 0, 0, 0, 0, 0,
568 0, 0, 0, 0, 0,
569 1, 1, 1, 1, 1,
570 1, 0, 1, 0, 1,
571 1, 1, 0, 1, 1,
572 1, 0, 1, 0, 1,
573 1, 1, 1, 1, 1,
574 0, 0, 0, 0, 0 }};
576 for (i = 0; i < 8 && err == 0; i++)
578 /* Tell the HD44780 we will redefine char number i */
579 ula200_ftdi_rawdata(drvthis, RS_INSTR, SETCHAR | i * 8);
580 if (err < 0) {
581 report(RPT_WARNING, "%s: ula200_ftdi_rawdata failed", drvthis->name);
582 break;
585 /* Send the subsequent rows */
586 for (row = 0; row < CELLHEIGHT; row++) {
587 int value = 0;
589 for (col = 0; col < CELLWIDTH; col++) {
590 value <<= 1;
591 value |= (custom_chars[i][(row * CELLWIDTH) + col] > 0) ? 1 : 0;
593 err = ula200_ftdi_rawdata(drvthis, RS_DATA, value);
594 if (err < 0) {
595 report(RPT_WARNING, "%s: ula200_ftdi_rawdata failed", drvthis->name);
596 break;
601 return err;
605 ///////////////////////////////////////////////////////////////////////////////
606 // Init the driver and display
608 MODULE_EXPORT int
609 ula200_init(Driver *drvthis)
611 PrivateData *p;
612 int err, i;
613 const char *s;
615 // Alocate and store private data
616 p = (PrivateData *) malloc( sizeof( PrivateData) );
617 if (p == NULL) {
618 return -1;
620 if (drvthis->store_private_ptr(drvthis, p)) {
621 return -1;
624 p->backlight = -1;
625 p->all_dirty = 1;
626 EmptyKeyRing(&p->keyring);
628 // Get and parse size
629 s = drvthis->config_get_string(drvthis->name, "size", 0, "20x4");
630 if ((sscanf(s, "%dx%d", &(p->width), &(p->height)) != 2)
631 || (p->width <= 0) || (p->width > LCD_MAX_WIDTH)
632 || (p->height <= 0) || (p->height > LCD_MAX_HEIGHT)) {
633 report(RPT_ERR, "%s: cannot read Size %s", drvthis->name, s);
634 return -1;
637 // read the keymap
638 for (i = 0; i < MAX_KEY_MAP; i++) {
639 char buf[40];
641 // First fill with default value
642 p->key_map[i] = default_key_map[i];
644 // Read config value
645 sprintf(buf, "KeyMap_%c", i+'A');
646 s = drvthis->config_get_string(drvthis->name, buf, 0, NULL);
648 // Was a key specified in the config file ?
649 if (s != NULL) {
650 p->key_map[i] = strdup(s);
651 report(RPT_INFO, "%s: Key '%c' mapped to \"%s\"",
652 drvthis->name, i+'A', s );
656 /* End of config file parsing */
659 // Allocate framebuffer
660 p->framebuf = (unsigned char *) malloc(p->width * p->height);
661 if (p->framebuf == NULL) {
662 report(RPT_ERR, "%s: unable to allocate framebuffer", drvthis->name);
663 goto err_begin;
666 // Allocate and clear the buffer for incremental updates
667 p->lcd_contents = (unsigned char *) malloc(p->width * p->height);
668 if (p->lcd_contents == NULL) {
669 report(RPT_ERR, "%s: unable to allocate framebuffer backing store", drvthis->name);
670 goto err_framebuf;
672 memset(p->lcd_contents, 0, p->width * p->height);
674 // open the FTDI library
675 ftdi_init(&p->ftdic);
676 (&p->ftdic)->usb_write_timeout = 20;
677 (&p->ftdic)->usb_read_timeout = 20;
679 // open the device
680 err = ftdi_usb_open(&p->ftdic, DISPLAY_VENDOR_ID, DISPLAY_PRODUCT_ID);
681 if (err < 0) {
682 report(RPT_ERR, "%s: cannot open USB device", drvthis->name);
683 goto err_lcd;
686 // set the baudrate
687 err = ftdi_set_baudrate(&p->ftdic, 19200);
688 if (err < 0) {
689 report(RPT_ERR, "%s: cannot set baudrate", drvthis->name);
690 goto err_ftdi;
693 // set communication parameters
694 err = ftdi_set_line_property(&p->ftdic, BITS_8, STOP_BIT_1, EVEN);
695 if (err < 0) {
696 report(RPT_ERR, "%s: cannot set line properties", drvthis->name);
697 goto err_ftdi;
700 // user is able to write commands
701 err = ula200_ftdi_enable_raw_mode(drvthis);
702 if (err < 0) {
703 report(RPT_ERR, "%s: unable to enable the raw mode", drvthis->name);
704 goto err_ftdi;
707 // load the chars
708 err = ula200_load_curstom_chars(drvthis);
709 if (err < 0) {
710 report(RPT_ERR, "%s: unable to write the custom characters", drvthis->name);
711 goto err_ftdi;
714 report(RPT_DEBUG, "%s: init() done", drvthis->name);
716 return 0;
718 err_ftdi:
719 ftdi_usb_close(&p->ftdic);
720 ftdi_deinit(&p->ftdic);
721 err_framebuf:
722 free(p->framebuf);
723 err_lcd:
724 free(p->lcd_contents);
725 err_begin:
727 return -1;
730 ///////////////////////////////////////////////////////////////////////////////
731 // Clean-up
733 MODULE_EXPORT void
734 ula200_close(Driver *drvthis)
736 PrivateData *p = (PrivateData *) drvthis->private_data;
738 if (p != NULL) {
739 ftdi_usb_purge_buffers(&p->ftdic);
740 ftdi_usb_close(&p->ftdic);
741 ftdi_deinit(&p->ftdic);
743 if (p->framebuf != NULL)
744 free(p->framebuf);
746 if (p->lcd_contents != NULL)
747 free(p->lcd_contents);
749 free(p);
751 drvthis->store_private_ptr(drvthis, NULL);
754 ///////////////////////////////////////////////////////////////////////////////
755 // Returns the display width
757 MODULE_EXPORT int
758 ula200_width (Driver *drvthis)
760 PrivateData *p = (PrivateData *) drvthis->private_data;
761 return p->width;
764 ///////////////////////////////////////////////////////////////////////////////
765 // Returns the display height
767 MODULE_EXPORT int
768 ula200_height (Driver *drvthis)
770 PrivateData *p = (PrivateData *) drvthis->private_data;
771 return p->height;
774 ///////////////////////////////////////////////////////////////////////////////
775 // Clear the framebuffer
777 MODULE_EXPORT void
778 ula200_clear (Driver *drvthis)
780 PrivateData *p = (PrivateData *) drvthis->private_data;
781 memset(p->framebuf, ' ', p->width * p->height);
784 ///////////////////////////////////////////////////////////////////////////////
785 // Place a character in the framebuffer
787 MODULE_EXPORT void
788 ula200_chr (Driver *drvthis, int x, int y, char ch)
790 PrivateData *p = (PrivateData *) drvthis->private_data;
791 y--;
792 x--;
794 p->framebuf[ (y * p->width) + x] = ch;
797 ///////////////////////////////////////////////////////////////////////////////
798 // Place a string in the framebuffer
800 MODULE_EXPORT void
801 ula200_string (Driver *drvthis, int x, int y, const char string[])
803 PrivateData *p = (PrivateData *) drvthis->private_data;
804 int i;
806 x --; // Convert 1-based coords to 0-based
807 y --;
809 for (i = 0; string[i] != '\0'; i++) {
810 // Check for buffer overflows...
811 if ((y * p->width) + x + i > (p->width * p->height))
812 break;
813 p->framebuf[(y*p->width) + x + i] = string[i];
817 ///////////////////////////////////////////////////////////////////////////////
818 // Sets the backlight on or off
820 MODULE_EXPORT void
821 ula200_backlight (Driver *drvthis, int on)
823 PrivateData *p = (PrivateData *) drvthis->private_data;
824 unsigned char command[2];
825 int err;
827 if (p->backlight != on) {
828 p->backlight = on;
830 command[0] = 'h';
831 command[1] = (on) ? '1' : '0';
832 err = ula200_ftdi_write_command(drvthis, command, 2, false);
833 if (err < 0)
834 report(RPT_WARNING, "%s: error in ula200_ftdi_write_command",
835 drvthis->name);
836 else
837 report(RPT_INFO, "%s: turn backlight %s",
838 drvthis->name, (on) ? "on" : "off");
842 ///////////////////////////////////////////////////////////////////////////////
843 // Flush the framebuffer to the display
845 MODULE_EXPORT void
846 ula200_flush(Driver *drvthis)
848 PrivateData *p = (PrivateData *) drvthis->private_data;
849 int x, y;
850 int wid = p->width;
851 char ch;
852 char drawing;
853 int count;
854 int firstdiff;
855 int lastdiff;
857 if (p->all_dirty) {
858 ula200_ftdi_clear(drvthis);
859 p->all_dirty = 0;
862 // Update LCD incrementally by comparing with last contents
863 count = 0;
864 for (y = 0; y < p->height; y++) {
865 drawing = 0;
866 firstdiff = -1;
867 lastdiff = 0;
868 for (x = 0 ; x < wid; x++) {
869 ch = p->framebuf[(y * wid) + x];
870 if (ch != p->lcd_contents[(y*wid)+x]) {
871 p->lcd_contents[(y*wid)+x] = ch;
872 if (firstdiff == -1) {
873 firstdiff = x;
875 lastdiff = x;
879 if (firstdiff >= 0) {
880 ula200_ftdi_position(drvthis, firstdiff, y);
881 ula200_ftdi_string(drvthis, p->framebuf + (y*wid) + firstdiff,
882 lastdiff - firstdiff + 1);
888 ///////////////////////////////////////////////////////////////////////////////
889 // Set default icon into a userdef char
891 MODULE_EXPORT int
892 ula200_icon (Driver *drvthis, int x, int y, int icon)
894 /* Yes I know, this is a VERY BAD implementation */
895 switch (icon) {
896 case ICON_BLOCK_FILLED:
897 ula200_chr(drvthis, x, y, 0xff);
898 break;
899 case ICON_HEART_FILLED:
900 ula200_chr(drvthis, x, y, 2);
901 break;
902 case ICON_HEART_OPEN:
903 ula200_chr(drvthis, x, y, 1);
904 break;
905 case ICON_ARROW_UP:
906 ula200_chr(drvthis, x, y, 3);
907 break;
908 case ICON_ARROW_DOWN:
909 ula200_chr(drvthis, x, y, 4);
910 break;
911 case ICON_ARROW_LEFT:
912 ula200_chr(drvthis, x, y, 0x7F);
913 break;
914 case ICON_ARROW_RIGHT:
915 ula200_chr(drvthis, x, y, 0x7E);
916 break;
917 case ICON_CHECKBOX_OFF:
918 ula200_chr(drvthis, x, y, 5);
919 break;
920 case ICON_CHECKBOX_ON:
921 ula200_chr(drvthis, x, y, 6);
922 break;
923 case ICON_CHECKBOX_GRAY:
924 ula200_chr(drvthis, x, y, 7);
925 break;
926 default:
927 return -1; /* Let the core do other icons */
929 return 0;
933 ///////////////////////////////////////////////////////////////////////////////
934 // Set default icon into a userdef char
936 MODULE_EXPORT const char *
937 ula200_get_key (Driver *drvthis)
939 PrivateData *p = drvthis->private_data;
940 unsigned char key;
941 int i;
943 // The libftdi has no non-blocking read (`select' system call), so we force
944 // a read that could not block by updating one character on the display.
945 // As long as lcdproc is single-threaded, we can write to the display because
946 // we're not inside a read here.
947 ula200_ftdi_position(drvthis, 0, 0);
948 ula200_ftdi_string(drvthis, p->lcd_contents, 1);
950 key = GetKeyFromKeyRing(&p->keyring);
952 // search the bit that was set by the hardware
953 for (i = 0; i < MAX_KEY_MAP; i++) {
954 if (key & (1 << i))
955 return p->key_map[i];
958 if (key != '\0') {
959 report(RPT_INFO, "%s: Untreated key 0x%02X", drvthis->name, key);
961 return NULL;