root/branches/0.10.1/drv_Crystalfontz.c

Revision 781, 24.2 kB (checked in by michael, 22 months ago)

backport r780 from trunk

  • Property svn:keywords set to Id URL Rev
Line 
1/* $Id$
2 * $URL$
3 *
4 * new style driver for Crystalfontz display modules
5 *
6 * Copyright (C) 1999, 2000 Michael Reinelt <reinelt@eunet.at>
7 * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
8 *
9 * This file is part of LCD4Linux.
10 *
11 * LCD4Linux is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2, or (at your option)
14 * any later version.
15 *
16 * LCD4Linux is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28 *
29 * exported fuctions:
30 *
31 * struct DRIVER drv_Crystalfontz
32 *
33 */
34
35#include "config.h"
36
37#include <stdlib.h>
38#include <stdio.h>
39#include <string.h>
40#include <unistd.h>
41#include <sys/time.h>
42
43#include "debug.h"
44#include "cfg.h"
45#include "qprintf.h"
46#include "thread.h"
47#include "timer.h"
48#include "plugin.h"
49#include "widget.h"
50#include "widget_text.h"
51#include "widget_icon.h"
52#include "widget_bar.h"
53#include "widget_keypad.h"
54#include "drv.h"
55#include "drv_generic_text.h"
56#include "drv_generic_gpio.h"
57#include "drv_generic_serial.h"
58#include "drv_generic_keypad.h"
59
60
61static char Name[] = "Crystalfontz";
62
63static int Model;
64static int Protocol;
65static int Payload;
66
67/* ring buffer for bytes received from the display */
68static unsigned char RingBuffer[256];
69static unsigned int RingRPos = 0;
70static unsigned int RingWPos = 0;
71
72/* packet from the display */
73struct {
74    unsigned char type;
75    unsigned char code;
76    unsigned char size;
77    unsigned char data[16 + 1]; /* trailing '\0' */
78} Packet;
79
80/* Line Buffer for 633 displays */
81static unsigned char Line[2 * 16];
82
83/* Fan RPM */
84static double Fan_RPM[4] = { 0.0, };
85
86/* Temperature sensors */
87static double Temperature[32] = { 0.0, };
88
89
90typedef struct {
91    int type;
92    char *name;
93    int rows;
94    int cols;
95    int gpis;
96    int gpos;
97    int protocol;
98    int payload;
99} MODEL;
100
101/* Fixme #1: number of GPI's & GPO's should be verified */
102/* Fixme #2: protocol should be verified */
103/* Fixme #3: number of keys on the keypad should be verified */
104
105static MODEL Models[] = {
106    {626, "626", 2, 16, 0, 0, 1, 0},
107    {631, "631", 2, 20, 4, 0, 3, 22},
108    {632, "632", 2, 16, 0, 0, 1, 0},
109    {633, "633", 2, 16, 4, 4, 2, 18},
110    {634, "634", 4, 20, 0, 0, 1, 0},
111    {635, "635", 4, 20, 4, 12, 3, 22},
112    {636, "636", 2, 16, 0, 0, 1, 0},
113    {-1, "Unknown", -1, -1, 0, 0, 0, 0}
114};
115
116
117/****************************************/
118/***  hardware dependant functions    ***/
119/****************************************/
120
121/* x^0 + x^5 + x^12 */
122#define CRCPOLY 0x8408
123
124static unsigned short CRC(const unsigned char *p, size_t len, unsigned short seed)
125{
126    int i;
127    while (len--) {
128  seed ^= *p++;
129  for (i = 0; i < 8; i++)
130      seed = (seed >> 1) ^ ((seed & 1) ? CRCPOLY : 0);
131    }
132    return ~seed;
133}
134
135static unsigned char LSB(const unsigned short word)
136{
137    return word & 0xff;
138}
139
140static unsigned char MSB(const unsigned short word)
141{
142    return word >> 8;
143}
144
145
146static unsigned char byte(unsigned int pos)
147{
148    pos += RingRPos;
149    if (pos >= sizeof(RingBuffer))
150  pos -= sizeof(RingBuffer);
151    return RingBuffer[pos];
152}
153
154
155static void drv_CF_process_packet(void)
156{
157
158    switch (Packet.type) {
159
160    case 0x02:
161
162  /* async response from display to host */
163  switch (Packet.code) {
164
165  case 0x00:
166      /* Key Activity */
167      debug("Key Activity: %d", Packet.data[0]);
168      drv_generic_keypad_press(Packet.data[0]);
169      break;
170
171  case 0x01:
172      /* Fan Speed Report */
173      if (Packet.data[1] == 0xff) {
174    Fan_RPM[Packet.data[0]] = -1.0;
175      } else if (Packet.data[1] < 4) {
176    Fan_RPM[Packet.data[0]] = 0.0;
177      } else {
178    Fan_RPM[Packet.data[0]] =
179        (double) 27692308L *(Packet.data[1] - 3) / (Packet.data[2] + 256 * Packet.data[3]);
180      }
181      break;
182
183  case 0x02:
184      /* Temperature Sensor Report */
185      switch (Packet.data[3]) {
186      case 0:
187    error("%s: 1-Wire device #%d: CRC error", Name, Packet.data[0]);
188    break;
189      case 1:
190      case 2:
191    Temperature[Packet.data[0]] = (Packet.data[1] + 256 * Packet.data[2]) / 16.0;
192    break;
193      default:
194    error("%s: 1-Wire device #%d: unknown CRC status %d", Name, Packet.data[0], Packet.data[3]);
195    break;
196      }
197      break;
198
199  default:
200      /* this should not happen */
201      error("%s: unexpected response type=0x%02x code=0x%02x size=%d", Name, Packet.type, Packet.code,
202      Packet.size);
203      break;
204  }
205
206  break;
207
208    case 0x03:
209  /* error response from display to host */
210  error("%s: error response type=0x%02x code=0x%02x size=%d", Name, Packet.type, Packet.code, Packet.size);
211  break;
212
213    default:
214  /* these should not happen: */
215  /* type 0x00: command from host to display: should never come back */
216  /* type 0x01: command response from display to host: are processed within send() */
217  error("%s: unexpected packet type=0x%02x code=0x%02x size=%d", Name, Packet.type, Packet.code, Packet.size);
218  break;
219    }
220
221}
222
223
224static int drv_CF_poll(void)
225{
226    /* read into RingBuffer */
227    while (1) {
228  char buffer[32];
229  int num, n;
230  num = drv_generic_serial_poll(buffer, sizeof(buffer));
231  if (num <= 0)
232      break;
233  /* put result into RingBuffer */
234  for (n = 0; n < num; n++) {
235      RingBuffer[RingWPos++] = (unsigned char) buffer[n];
236      if (RingWPos >= sizeof(RingBuffer))
237    RingWPos = 0;
238  }
239    }
240
241    /* process RingBuffer */
242    while (1) {
243  unsigned char buffer[32];
244  int n, num, size;
245  unsigned short crc;
246  /* packet size */
247  num = RingWPos - RingRPos;
248  if (num < 0)
249      num += sizeof(RingBuffer);
250  /* minimum packet size=4 */
251  if (num < 4)
252      return 0;
253  /* valid response types: 01xxxxx 10.. 11.. */
254  /* therefore: 00xxxxxx is invalid */
255  if (byte(0) >> 6 == 0)
256      goto GARBAGE;
257  /* command length */
258  size = byte(1);
259  /* valid command length is 0 to 16 */
260  if (size > 16)
261      goto GARBAGE;
262  /* all bytes available? */
263  if (num < size + 4)
264      return 0;
265  /* check CRC */
266  for (n = 0; n < size + 4; n++)
267      buffer[n] = byte(n);
268  crc = CRC(buffer, size + 2, 0xffff);
269  if (LSB(crc) != buffer[size + 2])
270      goto GARBAGE;
271  if (MSB(crc) != buffer[size + 3])
272      goto GARBAGE;
273  /* process packet */
274  Packet.type = buffer[0] >> 6;
275  Packet.code = buffer[0] & 0x3f;
276  Packet.size = size;
277  memcpy(Packet.data, buffer + 2, size);
278  Packet.data[size] = '\0'; /* trailing zero */
279  /* increment read pointer */
280  RingRPos += size + 4;
281  if (RingRPos >= sizeof(RingBuffer))
282      RingRPos -= sizeof(RingBuffer);
283  /* a packet arrived */
284  return 1;
285      GARBAGE:
286  debug("dropping garbage byte %02x", byte(0));
287  RingRPos++;
288  if (RingRPos >= sizeof(RingBuffer))
289      RingRPos = 0;
290  continue;
291    }
292
293    /* not reached */
294    return 0;
295}
296
297
298static void drv_CF_timer(void __attribute__ ((unused)) * notused)
299{
300    while (drv_CF_poll()) {
301  drv_CF_process_packet();
302    }
303}
304
305
306static void drv_CF_send(const unsigned char cmd, const unsigned char len, const unsigned char *data)
307{
308    /* 1 cmd + 1 len + 22 payload + 2 crc = 26 */
309    unsigned char buffer[26];
310    unsigned short crc;
311    struct timeval now, end;
312
313    if (len > Payload) {
314  error("%s: internal error: packet length %d exceeds payload size %d", Name, len, Payload);
315  return;
316    }
317
318    buffer[0] = cmd;
319    buffer[1] = len;
320    memcpy(buffer + 2, data, len);
321    crc = CRC(buffer, len + 2, 0xffff);
322    buffer[len + 2] = LSB(crc);
323    buffer[len + 3] = MSB(crc);
324
325    drv_generic_serial_write((char *) buffer, len + 4);
326
327    /* wait for acknowledge packet */
328    gettimeofday(&now, NULL);
329    while (1) {
330  /* delay 1 msec */
331  usleep(1 * 1000);
332  if (drv_CF_poll()) {
333      if (Packet.type == 0x01 && Packet.code == cmd) {
334    /* this is the ack we're waiting for */
335    if (0) {
336        gettimeofday(&end, NULL);
337        debug("%s: ACK after %ld usec", Name,
338        1000000 * (end.tv_sec - now.tv_sec) + end.tv_usec - now.tv_usec);
339    }
340    break;
341      } else {
342    /* some other (maybe async) packet, just process it */
343    drv_CF_process_packet();
344      }
345  }
346  gettimeofday(&end, NULL);
347  /* don't wait more than 250 msec */
348  if ((1000000 * (end.tv_sec - now.tv_sec) + end.tv_usec - now.tv_usec) > 250 * 1000) {
349      error("%s: timeout waiting for response to cmd 0x%02x", Name, cmd);
350      break;
351  }
352    }
353}
354
355
356static void drv_CF_write1(const int row, const int col, const char *data, const int len)
357{
358    char cmd[3] = "\021xy"; /* set cursor position */
359
360    if (row == 0 && col == 0) {
361  drv_generic_serial_write("\001", 1);  /* cursor home */
362    } else {
363  cmd[1] = (char) col;
364  cmd[2] = (char) row;
365  drv_generic_serial_write(cmd, 3);
366    }
367
368    drv_generic_serial_write(data, len);
369}
370
371
372static void drv_CF_write2(const int row, const int col, const char *data, const int len)
373{
374    int l = len;
375
376    /* limit length */
377    if (col + l > 16)
378  l = 16 - col;
379    if (l < 0)
380  l = 0;
381
382    /* sanity check */
383    if (row >= 2 || col + l > 16) {
384  error("%s: internal error: write outside linebuffer bounds!", Name);
385  return;
386    }
387    memcpy(Line + 16 * row + col, data, l);
388    drv_CF_send(7 + row, 16, (unsigned char *) (Line + 16 * row));
389}
390
391
392static void drv_CF_write3(const int row, const int col, const char *data, const int len)
393{
394    int l = len;
395    unsigned char cmd[23];
396
397    /* limit length */
398    if (col + l > DCOLS)
399  l = DCOLS - col;
400    if (l < 0)
401  l = 0;
402
403    /* sanity check */
404    if (row >= DROWS || col + l > DCOLS) {
405  error("%s: internal error: write outside display bounds!", Name);
406  return;
407    }
408
409    cmd[0] = col;
410    cmd[1] = row;
411    memcpy(cmd + 2, data, l);
412
413    drv_CF_send(31, l + 2, cmd);
414
415}
416
417
418static void drv_CF_defchar1(const int ascii, const unsigned char *matrix)
419{
420    int i;
421    char cmd[10] = "\031n"; /* set custom char bitmap */
422
423    /* user-defineable chars start at 128, but are defined at 0 */
424    cmd[1] = (char) (ascii - CHAR0);
425    for (i = 0; i < 8; i++) {
426  cmd[i + 2] = matrix[i] & 0x3f;
427    }
428    drv_generic_serial_write(cmd, 10);
429}
430
431
432static void drv_CF_defchar23(const int ascii, const unsigned char *matrix)
433{
434    int i;
435    unsigned char buffer[9];
436
437    /* user-defineable chars start at 128, but are defined at 0 */
438    buffer[0] = (char) (ascii - CHAR0);
439
440    /* clear bit 6 and 7 of the bitmap (blinking) */
441    for (i = 0; i < 8; i++) {
442  buffer[i + 1] = matrix[i] & 0x3f;
443    }
444
445    drv_CF_send(9, 9, buffer);
446}
447
448
449static int drv_CF_contrast(int contrast)
450{
451    static unsigned char Contrast = 0;
452    char buffer[2];
453
454    /* -1 is used to query the current contrast */
455    if (contrast == -1)
456  return Contrast;
457
458    if (contrast < 0)
459  contrast = 0;
460    if (contrast > 255)
461  contrast = 255;
462    Contrast = contrast;
463
464    switch (Protocol) {
465
466    case 1:
467  /* contrast range 0 to 100 */
468  if (Contrast > 100)
469      Contrast = 100;
470  buffer[0] = 15;   /* Set LCD Contrast */
471  buffer[1] = Contrast;
472  drv_generic_serial_write(buffer, 2);
473  break;
474
475    case 2:
476  /* contrast range 0 to 50 */
477  if (Contrast > 50)
478      Contrast = 50;
479  drv_CF_send(13, 1, &Contrast);
480  break;
481
482    case 3:
483  /* contrast range 0 to 255 */
484  drv_CF_send(13, 1, &Contrast);
485  break;
486    }
487
488    return Contrast;
489}
490
491
492static int drv_CF_backlight(int backlight)
493{
494    static unsigned char Backlight = 0;
495    char buffer[2];
496
497    /* -1 is used to query the current backlight */
498    if (backlight == -1)
499  return Backlight;
500
501    if (backlight < 0)
502  backlight = 0;
503    if (backlight > 100)
504  backlight = 100;
505    Backlight = backlight;
506
507    switch (Protocol) {
508
509    case 1:
510  buffer[0] = 14;   /* Set LCD Backlight */
511  buffer[1] = Backlight;
512  drv_generic_serial_write(buffer, 2);
513  break;
514
515    case 2:
516    case 3:
517  drv_CF_send(14, 1, &Backlight);
518  break;
519    }
520
521    return Backlight;
522}
523
524
525static int drv_CF_keypad(const int num)
526{
527    int val = 0;
528
529    switch (Protocol) {
530    case 1:
531    case 2:
532  break;
533    case 3:
534  if ((num < 8) || ((num > 12) && (num < 17)))
535      val = WIDGET_KEY_PRESSED;
536  else
537      val = WIDGET_KEY_RELEASED;
538  switch (num) {
539  case 1:
540  case 7:
541      val += WIDGET_KEY_UP;
542      break;
543  case 2:
544  case 8:
545      val += WIDGET_KEY_DOWN;
546      break;
547  case 3:
548  case 9:
549      val += WIDGET_KEY_LEFT;
550      break;
551  case 4:
552  case 10:
553      val += WIDGET_KEY_RIGHT;
554      break;
555  case 5:
556  case 11:
557      val += WIDGET_KEY_CONFIRM;
558      break;
559  case 6:
560  case 12:
561      val += WIDGET_KEY_CANCEL;
562      break;
563
564      /* added for 631, too lazy to make new WIDGET_KEY defines */
565  case 13:
566  case 17:
567      val += WIDGET_KEY_UP; /* really UPLEFT */
568      break;
569  case 14:
570  case 18:
571      val += WIDGET_KEY_RIGHT;  /* really UPRIGHT */
572      break;
573  case 15:
574  case 19:
575      val += WIDGET_KEY_LEFT; /* really DOWNLEFT */
576      break;
577  case 16:
578  case 20:
579      val += WIDGET_KEY_DOWN; /* really DOWNRIGHT */
580      break;
581  }
582  break;
583    }
584
585    return val;
586}
587
588
589static int drv_CF_GPI(const int num)
590{
591    if (num < 0 || num > 3) {
592  return 0;
593    }
594    return Fan_RPM[num];
595}
596
597
598static int drv_CF_GPO(const int num, const int val)
599{
600    static unsigned char PWM2[4] = { 0, 0, 0, 0 };
601    static unsigned char PWM3[2];
602
603    int v = val;
604
605    if (v < 0)
606  v = 0;
607    if (v > 100)
608  v = 100;
609
610    switch (Protocol) {
611    case 2:
612  PWM2[num] = v;
613  drv_CF_send(17, 4, PWM2);
614  break;
615    case 3:
616  PWM3[0] = num + 1;
617  PWM3[1] = v;
618  drv_CF_send(34, 2, PWM3);
619  break;
620    }
621
622    return v;
623}
624
625
626static int drv_CF_autodetect(void)
627{
628    int m;
629
630    /* only autodetect newer displays */
631    if (Protocol < 2)
632  return -1;
633
634    /* read display type */
635    drv_CF_send(1, 0, NULL);
636
637    /* send() did already wait for response packet */
638    if (Packet.type == 0x01 && Packet.code == 0x01) {
639  char t[7], c;
640  float h, v;
641  info("%s: display identifies itself as '%s'", Name, Packet.data);
642  if (sscanf((char *) Packet.data, "%6s:h%f,%c%f", t, &h, &c, &v) != 4) {
643      error("%s: error parsing display identification string", Name);
644      return -1;
645  }
646  info("%s: display type '%s', hardware version %3.1f, firmware version %c%3.1f", Name, t, h, c, v);
647  if (strncmp(t, "CFA", 3) == 0) {
648      for (m = 0; Models[m].type != -1; m++) {
649    /* omit the 'CFA' */
650    if (strcasecmp(Models[m].name, t + 3) == 0)
651        return m;
652      }
653  }
654  error("%s: display type '%s' may be not supported!", Name, t);
655  return -1;
656    }
657
658    error("%s: display detection failed!", Name);
659
660    return -1;
661}
662
663
664static char *drv_CF_print_ROM(void)
665{
666    static char buffer[17];
667
668    snprintf(buffer, sizeof(buffer), "0x%02x%02x%02x%02x%02x%02x%02x%02x",
669       Packet.data[1], Packet.data[2], Packet.data[3], Packet.data[4], Packet.data[5], Packet.data[6],
670       Packet.data[7], Packet.data[8]);
671
672    return buffer;
673}
674
675
676static int drv_CF_scan_DOW(unsigned char index)
677{
678    int i;
679
680    /* Read DOW Device Information */
681    drv_CF_send(18, 1, &index);
682
683    i = 0;
684    while (1) {
685  /* wait 10 msec */
686  usleep(10 * 1000);
687  /* packet available? */
688  if (drv_CF_poll()) {
689      /* DOW Device Info */
690      if (Packet.type == 0x01 && Packet.code == 0x12) {
691    switch (Packet.data[1]) {
692    case 0x00:
693        /* no device found */
694        return 0;
695    case 0x22:
696        info("%s: 1-Wire device #%d: DS1822 temperature sensor found at %s", Name, Packet.data[0],
697       drv_CF_print_ROM());
698        return 1;
699    case 0x28:
700        info("%s: 1-Wire device #%d: DS18B20 temperature sensor found at %s", Name, Packet.data[0],
701       drv_CF_print_ROM());
702        return 1;
703    default:
704        info("%s: 1-Wire device #%d: unknown device found at %s", Name, Packet.data[0], drv_CF_print_ROM());
705        return 0;
706    }
707      } else {
708    drv_CF_process_packet();
709      }
710  }
711  /* wait no longer than 300 msec */
712  if (++i > 30) {
713      error("%s: 1-Wire device #%d detection timed out", Name, index);
714      return -1;
715  }
716    }
717
718    /* not reached */
719    return -1;
720}
721
722
723/* clear display */
724static void drv_CF_clear(void)
725{
726    switch (Protocol) {
727    case 1:
728  drv_generic_serial_write("\014", 1);
729  break;
730    case 2:
731    case 3:
732  drv_CF_send(6, 0, NULL);
733  break;
734    }
735}
736
737
738/* init sequences for 626, 632, 634, 636  */
739static void drv_CF_start_1(void)
740{
741    drv_generic_serial_write("\014", 1);  /* Form Feed (Clear Display) */
742    drv_generic_serial_write("\004", 1);  /* hide cursor */
743    drv_generic_serial_write("\024", 1);  /* scroll off */
744    drv_generic_serial_write("\030", 1);  /* wrap off */
745}
746
747
748/* init sequences for 633 */
749static void drv_CF_start_2(void)
750{
751    int i;
752    unsigned long mask;
753    unsigned char buffer[4];
754
755    /* Clear Display */
756    drv_CF_send(6, 0, NULL);
757
758    /* Set LCD Cursor Style */
759    buffer[0] = 0;
760    drv_CF_send(12, 1, buffer);
761
762    /* enable Fan Reporting */
763    buffer[0] = 15;
764    drv_CF_send(16, 1, buffer);
765
766    /* Set Fan Power to 100% */
767    buffer[0] = buffer[1] = buffer[2] = buffer[3] = 100;
768    drv_CF_send(17, 4, buffer);
769
770    /* Read DOW Device Information */
771    mask = 0;
772    for (i = 0; i < 32; i++) {
773  if (drv_CF_scan_DOW(i) == 1) {
774      mask |= 1 << i;
775  }
776    }
777
778    /* enable Temperature Reporting */
779    buffer[0] = mask &