* Patches by Pantelis Antoniou, 30 Mar 2004:
[platform/kernel/u-boot.git] / board / netphone / phone_console.c
1 /*
2  * (C) Copyright 2004 Intracom S.A.
3  * Pantelis Antoniou <panto@intracom.gr>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 /*
25  * phone_console.c
26  *
27  * A phone based console
28  *
29  * Virtual display of 80x24 characters.
30  * The actual display is much smaller and panned to show the virtual one.
31  * Input is made by a numeric keypad utilizing the input method of
32  * mobile phones. Sorry no T9 lexicons...
33  *
34  */
35
36 #include <common.h>
37
38 #include <version.h>
39 #include <linux/types.h>
40 #include <devices.h>
41
42 #include <sed156x.h>
43
44 /*************************************************************************************************/
45
46 #define ROWS    24
47 #define COLS    80
48
49 #define REFRESH_HZ              (CFG_HZ/50)     /* refresh every 20ms */
50 #define BLINK_HZ                (CFG_HZ/2)      /* cursor blink every 500ms */
51
52 /*************************************************************************************************/
53
54 #define DISPLAY_BACKLIT_PORT    ((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat
55 #define DISPLAY_BACKLIT_MASK    0x0010
56
57 /*************************************************************************************************/
58
59 #define KP_STABLE_HZ            (CFG_HZ/100)    /* stable for 10ms */
60 #define KP_REPEAT_DELAY_HZ      (CFG_HZ/4)      /* delay before repeat 250ms */
61 #define KP_REPEAT_HZ            (CFG_HZ/20)     /* repeat every 50ms */
62 #define KP_FORCE_DELAY_HZ       (CFG_HZ/2)      /* key was force pressed */
63 #define KP_IDLE_DELAY_HZ        (CFG_HZ/2)      /* key was released and idle */
64
65 #define KP_SPI_RXD_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
66 #define KP_SPI_RXD_MASK 0x0008
67
68 #define KP_SPI_TXD_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
69 #define KP_SPI_TXD_MASK 0x0004
70
71 #define KP_SPI_CLK_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
72 #define KP_SPI_CLK_MASK 0x0001
73
74 #define KP_CS_PORT      (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pedat)
75 #define KP_CS_MASK      0x00000010
76
77 #define KP_SPI_RXD() (KP_SPI_RXD_PORT & KP_SPI_RXD_MASK)
78
79 #define KP_SPI_TXD(x) \
80         do { \
81                 if (x) \
82                         KP_SPI_TXD_PORT |=  KP_SPI_TXD_MASK; \
83                 else \
84                         KP_SPI_TXD_PORT &= ~KP_SPI_TXD_MASK; \
85         } while(0)
86
87 #define KP_SPI_CLK(x) \
88         do { \
89                 if (x) \
90                         KP_SPI_CLK_PORT |=  KP_SPI_CLK_MASK; \
91                 else \
92                         KP_SPI_CLK_PORT &= ~KP_SPI_CLK_MASK; \
93         } while(0)
94
95 #define KP_SPI_CLK_TOGGLE() (KP_SPI_CLK_PORT ^= KP_SPI_CLK_MASK)
96
97 #define KP_SPI_BIT_DELAY()      /* no delay */
98
99 #define KP_CS(x) \
100         do { \
101                 if (x) \
102                         KP_CS_PORT |=  KP_CS_MASK; \
103                 else \
104                         KP_CS_PORT &= ~KP_CS_MASK; \
105         } while(0)
106
107 #define KP_ROWS 7
108 #define KP_COLS 4
109
110 #define KP_ROWS_MASK    ((1 << KP_ROWS) - 1)
111 #define KP_COLS_MASK    ((1 << KP_COLS) - 1)
112
113 #define SCAN            0
114 #define SCAN_FILTER     1
115 #define SCAN_COL        2
116 #define SCAN_COL_FILTER 3
117 #define PRESSED         4
118
119 #define KP_F1   0       /* leftmost dot (tab) */
120 #define KP_F2   1       /* middle left dot    */
121 #define KP_F3   2       /* up                 */
122 #define KP_F4   3       /* middle right dot   */
123 #define KP_F5   4       /* rightmost dot      */
124 #define KP_F6   5       /* C                  */
125 #define KP_F7   6       /* left               */
126 #define KP_F8   7       /* down               */
127 #define KP_F9   8       /* right              */
128 #define KP_F10  9       /* enter              */
129 #define KP_F11  10      /* R                  */
130 #define KP_F12  11      /* save               */
131 #define KP_F13  12      /* redial             */
132 #define KP_F14  13      /* speaker            */
133 #define KP_F15  14      /* unused             */
134 #define KP_F16  15      /* unused             */
135
136 #define KP_RELEASE              -1      /* key depressed                          */
137 #define KP_FORCE                -2      /* key was pressed for more than force hz */
138 #define KP_IDLE                 -3      /* key was released and idle              */
139
140 #define KP_1    '1'
141 #define KP_2    '2'
142 #define KP_3    '3'
143 #define KP_4    '4'
144 #define KP_5    '5'
145 #define KP_6    '6'
146 #define KP_7    '7'
147 #define KP_8    '8'
148 #define KP_9    '9'
149 #define KP_0    '0'
150 #define KP_STAR '*'
151 #define KP_HASH '#'
152
153 /*************************************************************************************************/
154
155 static int curs_disabled;
156 static int curs_col, curs_row;
157 static int disp_col, disp_row;
158
159 static int width, height;
160
161 /* the simulated vty buffer */
162 static char vty_buf[ROWS * COLS];
163 static char last_visible_buf[ROWS * COLS];      /* worst case */
164 static char *last_visible_curs_ptr;
165 static int last_visible_curs_rev;
166 static int blinked_state;
167 static int last_input_mode;
168 static int refresh_time;
169 static int blink_time;
170 static char last_fast_punct;
171 static int last_tab_indicator = -1;
172
173 /*************************************************************************************************/
174
175 #define IM_SMALL        0
176 #define IM_CAPITAL      1
177 #define IM_NUMBER       2
178
179 static int input_mode;
180 static char fast_punct;
181 static int tab_indicator;
182 static const char *fast_punct_list = ",.:;*";
183
184 static const char *input_mode_txt[] = { "abc", "ABC", "123" };
185
186 static const char *punct = ".,!;?'\"-()@/:_+&%*=<>$[]{}\\~^#|";
187 static const char *whspace = " 0\n";
188 /* per mode character select (for 2-9) */
189 static const char *digits_sel[2][8] = {
190         {       /* small */
191                 "abc2",                                 /* 2 */
192                 "def3",                                 /* 3 */
193                 "ghi4",                                 /* 4 */
194                 "jkl5",                                 /* 5 */
195                 "mno6",                                 /* 6 */
196                 "pqrs7",                                /* 7 */
197                 "tuv8",                                 /* 8 */
198                 "wxyz9",                                /* 9 */
199         }, {    /* capital */
200                 "ABC2",                                 /* 2 */
201                 "DEF3",                                 /* 3 */
202                 "GHI4",                                 /* 4 */
203                 "JKL5",                                 /* 5 */
204                 "MNO6",                                 /* 6 */
205                 "PQRS7",                                /* 7 */
206                 "TUV8",                                 /* 8 */
207                 "WXYZ9",                                /* 9 */
208         }
209 };
210
211 /*****************************************************************************/
212
213 static void update(void);
214 static void ensure_visible(int col, int row, int dx, int dy);
215
216 static void console_init(void)
217 {
218         curs_disabled = 0;
219         curs_col = 0;
220         curs_row = 0;
221
222         disp_col = 0;
223         disp_row = 0;
224
225         input_mode = IM_SMALL;
226         fast_punct = ',';
227         last_fast_punct = '\0';
228         refresh_time = REFRESH_HZ;
229         blink_time = BLINK_HZ;
230
231         tab_indicator = 1;
232
233         memset(vty_buf, ' ', sizeof(vty_buf));
234
235         memset(last_visible_buf, ' ', sizeof(last_visible_buf));
236         last_visible_curs_ptr = NULL;
237         last_input_mode = -1;
238         last_visible_curs_rev = 0;
239
240         blinked_state = 0;
241
242         sed156x_init();
243         width = sed156x_text_width;
244         height = sed156x_text_height - 1;
245 }
246
247 /*****************************************************************************/
248
249 void phone_putc(const char c);
250
251 /*****************************************************************************/
252
253 static int  queued_char = -1;
254 static int  enabled = 0;
255
256 /*****************************************************************************/
257
258 /* flush buffers */
259 int phone_start(void)
260 {
261         console_init();
262
263         update();
264         sed156x_sync();
265
266         enabled = 1;
267         queued_char = 'U' - '@';
268
269         /* backlit on */
270         DISPLAY_BACKLIT_PORT &= ~DISPLAY_BACKLIT_MASK;
271
272         return 0;
273 }
274
275 int phone_stop(void)
276 {
277         enabled = 0;
278
279         sed156x_clear();
280         sed156x_sync();
281
282         /* backlit off */
283         DISPLAY_BACKLIT_PORT |= DISPLAY_BACKLIT_MASK;
284
285         return 0;
286 }
287
288 void phone_puts(const char *s)
289 {
290         int count = strlen(s);
291
292         while (count--)
293                 phone_putc(*s++);
294 }
295
296 int phone_tstc(void)
297 {
298         return queued_char >= 0 ? 1 : 0;
299 }
300
301 int phone_getc(void)
302 {
303         int r;
304
305         if (queued_char < 0)
306                 return -1;
307
308         r = queued_char;
309         queued_char = -1;
310
311         return r;
312 }
313
314 /*****************************************************************************/
315
316 int drv_phone_init(void)
317 {
318         device_t console_dev;
319         char *penv;
320
321         /*
322          * Force console i/o to serial ?
323          */
324         if ((penv = getenv("console")) != NULL && strcmp(penv, "serial") == 0)
325                 return 0;
326
327         console_init();
328
329         memset(&console_dev, 0, sizeof(console_dev));
330         strcpy(console_dev.name, "phone");
331         console_dev.ext = DEV_EXT_VIDEO;        /* Video extensions */
332         console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
333         console_dev.start = phone_start;
334         console_dev.stop = phone_stop;
335         console_dev.putc = phone_putc;  /* 'putc' function */
336         console_dev.puts = phone_puts;  /* 'puts' function */
337         console_dev.tstc = phone_tstc;  /* 'tstc' function */
338         console_dev.getc = phone_getc;  /* 'getc' function */
339
340         if (device_register(&console_dev) == 0)
341                 return 1;
342
343         return 0;
344 }
345
346 static int use_me;
347
348 int drv_phone_use_me(void)
349 {
350         return use_me;
351 }
352
353 static void kp_do_poll(void);
354
355 void phone_console_do_poll(void)
356 {
357         int i, x, y;
358
359         kp_do_poll();
360
361         if (enabled) {
362                 /* do the blink */
363                 blink_time -= PHONE_CONSOLE_POLL_HZ;
364                 if (blink_time <= 0) {
365                         blink_time += BLINK_HZ;
366                         if (last_visible_curs_ptr) {
367                                 i = last_visible_curs_ptr - last_visible_buf;
368                                 x = i % width; y = i / width;
369                                 sed156x_reverse_at(x, y, 1);
370                                 last_visible_curs_rev ^= 1;
371                         }
372                 }
373
374                 /* do the refresh */
375                 refresh_time -= PHONE_CONSOLE_POLL_HZ;
376                 if (refresh_time <= 0) {
377                         refresh_time += REFRESH_HZ;
378                         sed156x_sync();
379                 }
380         }
381
382 }
383
384 static int last_scancode = -1;
385 static int forced_scancode = 0;
386 static int input_state = -1;
387 static int input_scancode = -1;
388 static int input_selected_char = -1;
389 static char input_covered_char;
390
391 static void putchar_at_cursor(char c)
392 {
393         vty_buf[curs_row * COLS + curs_col] = c;
394         ensure_visible(curs_col, curs_row, 1, 1);
395 }
396
397 static char getchar_at_cursor(void)
398 {
399         return vty_buf[curs_row * COLS + curs_col];
400 }
401
402 static void queue_input_char(char c)
403 {
404         if (c <= 0)
405                 return;
406
407         queued_char = c;
408 }
409
410 static void terminate_input(void)
411 {
412         if (input_state < 0)
413                 return;
414
415         if (input_selected_char >= 0)
416                 queue_input_char(input_selected_char);
417
418         input_state = -1;
419         input_selected_char = -1;
420         putchar_at_cursor(input_covered_char);
421
422         curs_disabled = 0;
423         blink_time = BLINK_HZ;
424         update();
425 }
426
427 static void handle_enabled_scancode(int scancode)
428 {
429         char c;
430         int new_disp_col, new_disp_row;
431         const char *sel;
432
433
434         switch (scancode) {
435
436                         /* key was released */
437                 case KP_RELEASE:
438                         forced_scancode = 0;
439                         break;
440
441                         /* key was forced */
442                 case KP_FORCE:
443
444                         switch (last_scancode) {
445                                 case '#':
446                                         if (input_mode == IM_NUMBER) {
447                                                 input_mode = IM_CAPITAL;
448                                                 /* queue backspace to erase # */
449                                                 queue_input_char('\b');
450                                         } else {
451                                                 input_mode = IM_NUMBER;
452                                                 fast_punct = '*';
453                                         }
454                                         update();
455                                         break;
456
457                                 case '0': case '1':
458                                 case '2': case '3': case '4': case '5':
459                                 case '6': case '7': case '8': case '9':
460
461                                         if (input_state < 0)
462                                                 break;
463
464                                         input_selected_char = last_scancode;
465                                         putchar_at_cursor((char)input_selected_char);
466                                         terminate_input();
467
468                                         break;
469
470                                 default:
471                                         break;
472                         }
473
474                         break;
475
476                         /* release and idle */
477                 case KP_IDLE:
478                         input_scancode = -1;
479                         if (input_state < 0)
480                                 break;
481                         terminate_input();
482                         break;
483
484                         /* change input mode */
485                 case '#':
486                         if (last_scancode == '#')       /* no repeat */
487                                 break;
488
489                         if (input_mode == IM_NUMBER) {
490                                 input_scancode = scancode;
491                                 input_state = 0;
492                                 input_selected_char = scancode;
493                                 input_covered_char = getchar_at_cursor();
494                                 putchar_at_cursor((char)input_selected_char);
495                                 terminate_input();
496                                 break;
497                         }
498
499                         if (input_mode == IM_SMALL)
500                                 input_mode = IM_CAPITAL;
501                         else
502                                 input_mode = IM_SMALL;
503
504                         update();
505                         break;
506
507                 case '*':
508                         /* no repeat */
509                         if (last_scancode == scancode)
510                                 break;
511
512                         if (input_state >= 0)
513                                 terminate_input();
514
515                         input_scancode = fast_punct;
516                         input_state = 0;
517                         input_selected_char = input_scancode;
518                         input_covered_char = getchar_at_cursor();
519                         putchar_at_cursor((char)input_selected_char);
520                         terminate_input();
521
522                         break;
523
524                 case '0': case '1':
525                 case '2': case '3': case '4': case '5':
526                 case '6': case '7': case '8': case '9':
527
528                         /* no repeat */
529                         if (last_scancode == scancode)
530                                 break;
531
532                         if (input_mode == IM_NUMBER) {
533                                 input_scancode = scancode;
534                                 input_state = 0;
535                                 input_selected_char = scancode;
536                                 input_covered_char = getchar_at_cursor();
537                                 putchar_at_cursor((char)input_selected_char);
538                                 terminate_input();
539                                 break;
540                         }
541
542                         if (input_state >= 0 && input_scancode != scancode)
543                                 terminate_input();
544
545                         if (input_state < 0) {
546                                 curs_disabled = 1;
547                                 input_scancode = scancode;
548                                 input_state = 0;
549                                 input_covered_char = getchar_at_cursor();
550                         } else
551                                 input_state++;
552
553                         if (scancode == '0')
554                                 sel = whspace;
555                         else if (scancode == '1')
556                                 sel = punct;
557                         else
558                                 sel = digits_sel[input_mode][scancode - '2'];
559                         c = *(sel + input_state);
560                         if (c == '\0') {
561                                 input_state = 0;
562                                 c = *sel;
563                         }
564
565                         input_selected_char = (int)c;
566                         putchar_at_cursor((char)input_selected_char);
567                         update();
568
569                         break;
570
571                         /* move visible display */
572                 case KP_F3: case KP_F8: case KP_F7: case KP_F9:
573
574                         new_disp_col = disp_col;
575                         new_disp_row = disp_row;
576
577                         switch (scancode) {
578                                         /* up */
579                                 case KP_F3:
580                                         if (new_disp_row <= 0)
581                                                 break;
582                                         new_disp_row--;
583                                         break;
584
585                                         /* down */
586                                 case KP_F8:
587                                         if (new_disp_row >= ROWS - height)
588                                                 break;
589                                         new_disp_row++;
590                                         break;
591
592                                         /* left */
593                                 case KP_F7:
594                                         if (new_disp_col <= 0)
595                                                 break;
596                                         new_disp_col--;
597                                         break;
598
599                                         /* right */
600                                 case KP_F9:
601                                         if (new_disp_col >= COLS - width)
602                                                 break;
603                                         new_disp_col++;
604                                         break;
605                         }
606
607                         /* no change? */
608                         if (disp_col == new_disp_col && disp_row == new_disp_row)
609                                 break;
610
611                         disp_col = new_disp_col;
612                         disp_row = new_disp_row;
613                         update();
614
615                         break;
616
617                 case KP_F6:     /* backspace */
618                         /* inputing something; no backspace sent, just cancel input */
619                         if (input_state >= 0) {
620                                 input_selected_char = -1;       /* cancel */
621                                 terminate_input();
622                                 break;
623                         }
624                         queue_input_char('\b');
625                         break;
626
627                 case KP_F10:    /* enter */
628                         /* inputing something; first cancel input */
629                         if (input_state >= 0)
630                                 terminate_input();
631                         queue_input_char('\r');
632                         break;
633
634                 case KP_F11:    /* R -> Ctrl-C (abort) */
635                         if (input_state >= 0)
636                                 terminate_input();
637                         queue_input_char('C' - 'Q');    /* ctrl-c */
638                         break;
639
640                 case KP_F5:     /* F% -> Ctrl-U (clear line) */
641                         if (input_state >= 0)
642                                 terminate_input();
643                         queue_input_char('U' - 'Q');    /* ctrl-c */
644                         break;
645
646
647                 case KP_F1:     /* tab */
648                         /* inputing something; first cancel input */
649                         if (input_state >= 0)
650                                 terminate_input();
651                         queue_input_char('\t');
652                         break;
653
654                 case KP_F2:     /* change fast punct */
655                         sel = strchr(fast_punct_list, fast_punct);
656                         if (sel == NULL)
657                                 sel = &fast_punct_list[0];
658                         sel++;
659                         if (*sel == '\0')
660                                 sel = &fast_punct_list[0];
661                         fast_punct = *sel;
662                         update();
663                         break;
664
665
666         }
667
668         if (scancode != KP_FORCE && scancode != KP_IDLE)        /* don't record forced or idle scancode */
669                 last_scancode = scancode;
670 }
671
672 static void scancode_action(int scancode)
673 {
674 #if 0
675         if (scancode == KP_RELEASE)
676                 printf(" RELEASE\n");
677         else if (scancode == KP_FORCE)
678                 printf(" FORCE\n");
679         else if (scancode == KP_IDLE)
680                 printf(" IDLE\n");
681         else if (scancode < 32)
682                 printf(" F%d", scancode + 1);
683         else
684                 printf(" %c", (char)scancode);
685         printf("\n");
686 #endif
687
688         if (enabled) {
689                 handle_enabled_scancode(scancode);
690                 return;
691         }
692
693         if (scancode == KP_FORCE && last_scancode == '*')
694                 use_me = 1;
695
696         last_scancode = scancode;
697 }
698
699 /**************************************************************************************/
700
701 /* update the display; make sure to update only the differences */
702 static void update(void)
703 {
704         int i;
705         char *s, *e, *t, *r, *b, *cp;
706
707         if (input_mode != last_input_mode)
708                 sed156x_output_at(sed156x_text_width - 3, sed156x_text_height - 1, input_mode_txt[input_mode], 3);
709
710         if (tab_indicator != last_tab_indicator)
711                 sed156x_output_at(0, sed156x_text_height - 1, "\\t", 2);
712
713         if (fast_punct != last_fast_punct)
714                 sed156x_output_at(4, sed156x_text_height - 1, &fast_punct, 1);
715
716         if (curs_disabled ||
717                 curs_col < disp_col || curs_col >= (disp_col + width) ||
718                 curs_row < disp_row || curs_row >= (disp_row + height)) {
719                 cp = NULL;
720         } else
721                 cp = last_visible_buf + (curs_row - disp_row) * width + (curs_col - disp_col);
722
723
724         /* printf("(%d,%d) (%d,%d) %s\n", curs_col, curs_row, disp_col, disp_row, cp ? "YES" : "no"); */
725
726         /* clear previous cursor */
727         if (last_visible_curs_ptr && last_visible_curs_rev == 0) {
728                 i = last_visible_curs_ptr - last_visible_buf;
729                 sed156x_reverse_at(i % width, i / width, 1);
730         }
731
732         b = vty_buf + disp_row * COLS + disp_col;
733         t = last_visible_buf;
734         for (i = 0; i < height; i++) {
735                 s = b;
736                 e = b + width;
737                 /* update only the differences */
738                 do {
739                         while (s < e && *s == *t) {
740                                 s++;
741                                 t++;
742                         }
743                         if (s == e)     /* no more */
744                                 break;
745
746                         /* find run */
747                         r = s;
748                         while (s < e && *s != *t)
749                                 *t++ = *s++;
750
751                         /* and update */
752                         sed156x_output_at(r - b, i, r, s - r);
753
754                 } while (s < e);
755
756                 b += COLS;
757         }
758
759         /* set cursor */
760         if (cp) {
761                 last_visible_curs_ptr = cp;
762                 i = last_visible_curs_ptr - last_visible_buf;
763                 sed156x_reverse_at(i % width, i / width, 1);
764                 last_visible_curs_rev = 0;
765         } else {
766                 last_visible_curs_ptr = NULL;
767         }
768
769         last_input_mode = input_mode;
770         last_fast_punct = fast_punct;
771         last_tab_indicator = tab_indicator;
772 }
773
774 /* ensure visibility; the trick is to minimize the screen movement */
775 static void ensure_visible(int col, int row, int dx, int dy)
776 {
777         int x1, y1, x2, y2, a1, b1, a2, b2;
778
779         /* clamp visible region */
780         if (col < 0) {
781                 dx -= col;
782                 col = 0;
783                 if (dx <= 0)
784                         dx = 1;
785         }
786
787         if (row < 0) {
788                 dy -= row;
789                 row = 0;
790                 if (dy <= 0)
791                         dy = 1;
792         }
793
794         if (col + dx > COLS)
795                 dx = COLS - col;
796
797         if (row + dy > ROWS)
798                 dy = ROWS - row;
799
800
801         /* move to easier to use vars */
802         x1 = disp_col;   y1 = disp_row;
803         x2 = x1 + width; y2 = y1 + height;
804         a1 = col;        b1 = row;
805         a2 = a1 + dx;    b2 = b1 + dy;
806
807         /* printf("(%d,%d) - (%d,%d) : (%d, %d) - (%d, %d)\n", x1, y1, x2, y2, a1, b1, a2, b2); */
808
809         if (a2 > x2) {
810                 /* move to the right */
811                 x2 = a2;
812                 x1 = x2 - width;
813                 if (x1 < 0) {
814                         x1 = 0;
815                         x2 = width;
816                 }
817         } else if (a1 < x1) {
818                 /* move to the left */
819                 x1 = a1;
820                 x2 = x1 + width;
821                 if (x2 > COLS) {
822                         x2 = COLS;
823                         x1 = x2 - width;
824                 }
825         }
826
827         if (b2 > y2) {
828                 /* move down */
829                 y2 = b2;
830                 y1 = y2 - height;
831                 if (y1 < 0) {
832                         y1 = 0;
833                         y2 = height;
834                 }
835         } else if (b1 < y1) {
836                 /* move up */
837                 y1 = b1;
838                 y2 = y1 + width;
839                 if (y2 > ROWS) {
840                         y2 = ROWS;
841                         y1 = y2 - height;
842                 }
843         }
844
845         /* printf("(%d,%d) - (%d,%d) : (%d, %d) - (%d, %d)\n", x1, y1, x2, y2, a1, b1, a2, b2); */
846
847         /* no movement? */
848         if (disp_col == x1 && disp_row == y1)
849                 return;
850
851         disp_col = x1;
852         disp_row = y1;
853 }
854
855 /**************************************************************************************/
856
857 static void newline(void)
858 {
859         curs_col = 0;
860         if (curs_row + 1 < ROWS)
861                 curs_row++;
862         else {
863                 memmove(vty_buf, vty_buf + COLS, COLS * (ROWS - 1));
864                 memset(vty_buf + (ROWS - 1) * COLS, ' ', COLS);
865         }
866 }
867
868 void phone_putc(const char c)
869 {
870         int i;
871
872         if (input_mode != -1) {
873                 input_selected_char = -1;
874                 terminate_input();
875         }
876
877         curs_disabled = 1;
878         update();
879
880         blink_time = BLINK_HZ;
881
882         switch (c) {
883                 case 13:                /* ignore */
884                         break;
885
886                 case '\n':              /* next line */
887                         newline();
888                         ensure_visible(curs_col, curs_row, 1, 1);
889                         break;
890
891                 case 9: /* tab 8 */
892                         /* move to tab */
893                         i = curs_col;
894                         i |=  0x0008;
895                         i &= ~0x0007;
896
897                         if (i < COLS)
898                                 curs_col = i;
899                         else
900                                 newline();
901
902                         ensure_visible(curs_col, curs_row, 1, 1);
903                         break;
904
905                 case 8:         /* backspace */
906                         if (curs_col <= 0)
907                                 break;
908                         curs_col--;
909
910                         /* make sure that we see a couple of characters before */
911                         if (curs_col > 4)
912                                 ensure_visible(curs_col - 4, curs_row, 4, 1);
913                         else
914                                 ensure_visible(curs_col, curs_row, 1, 1);
915
916                         break;
917
918                 default:                /* draw the char */
919                         putchar_at_cursor(c);
920
921                         /*
922                          * check for newline
923                          */
924                         if (curs_col + 1 < COLS)
925                                 curs_col++;
926                         else
927                                 newline();
928
929                         ensure_visible(curs_col, curs_row, 1, 1);
930
931                         break;
932         }
933
934         curs_disabled = 0;
935         blink_time = BLINK_HZ;
936         update();
937 }
938
939 /**************************************************************************************/
940
941 static inline unsigned int kp_transfer(unsigned int val)
942 {
943         unsigned int rx;
944         int b;
945
946         rx = 0; b = 8;
947         while (--b >= 0) {
948                 KP_SPI_TXD(val & 0x80);
949                 val <<= 1;
950                 KP_SPI_CLK_TOGGLE();
951                 KP_SPI_BIT_DELAY();
952                 rx <<= 1;
953                 if (KP_SPI_RXD())
954                         rx |= 1;
955                 KP_SPI_CLK_TOGGLE();
956                 KP_SPI_BIT_DELAY();
957         }
958
959         return rx;
960 }
961
962 unsigned int kp_data_transfer(unsigned int val)
963 {
964         KP_SPI_CLK(1);
965         KP_CS(0);
966         val = kp_transfer(val);
967         KP_CS(1);
968
969         return val;
970 }
971
972 unsigned int kp_get_col_mask(unsigned int row_mask)
973 {
974         unsigned int val, col_mask;
975
976         val = 0x80 | (row_mask & 0x7F);
977         (void)kp_data_transfer(val);
978         col_mask = kp_data_transfer(val) & 0x0F;
979
980         /* printf("col_mask(row_mask = 0x%x) -> col_mask = 0x%x\n", row_mask, col_mask); */
981         return col_mask;
982 }
983
984 /**************************************************************************************/
985
986 static const int kp_scancodes[KP_ROWS * KP_COLS] = {
987         KP_F1,   KP_F3,   KP_F4,  KP_F2,
988         KP_F6,   KP_F8,   KP_F9,  KP_F7,
989         KP_1,    KP_3,    KP_F11, KP_2,
990         KP_4,    KP_6,    KP_F12, KP_5,
991         KP_7,    KP_9,    KP_F13, KP_8,
992         KP_STAR, KP_HASH, KP_F14, KP_0,
993         KP_F5,   KP_F15,  KP_F16, KP_F10,
994 };
995
996 static const int kp_repeats[KP_ROWS * KP_COLS] = {
997         0, 1, 0, 0,
998         0, 1, 1, 1,
999         1, 1, 0, 1,
1000         1, 1, 0, 1,
1001         1, 1, 0, 1,
1002         1, 1, 0, 1,
1003         0, 0, 0, 1,
1004 };
1005
1006 static int kp_state = SCAN;
1007 static int kp_last_col_mask;
1008 static int kp_cur_row, kp_cur_col;
1009 static int kp_scancode;
1010 static int kp_stable;
1011 static int kp_repeat;
1012 static int kp_repeat_time;
1013 static int kp_force_time;
1014 static int kp_idle_time;
1015
1016 static void kp_do_poll(void)
1017 {
1018         unsigned int col_mask;
1019         int col;
1020
1021         switch (kp_state) {
1022                 case SCAN:
1023                         if (kp_idle_time > 0) {
1024                                 kp_idle_time -= PHONE_CONSOLE_POLL_HZ;
1025                                 if (kp_idle_time <= 0)
1026                                         scancode_action(KP_IDLE);
1027                         }
1028
1029                         col_mask = kp_get_col_mask(KP_ROWS_MASK);
1030                         if (col_mask == KP_COLS_MASK)
1031                                 break;  /* nothing */
1032                         kp_last_col_mask = col_mask;
1033                         kp_stable = 0;
1034                         kp_state = SCAN_FILTER;
1035                         break;
1036
1037                 case SCAN_FILTER:
1038                         col_mask = kp_get_col_mask(KP_ROWS_MASK);
1039                         if (col_mask != kp_last_col_mask) {
1040                                 kp_state = SCAN;
1041                                 break;
1042                         }
1043
1044                         kp_stable += PHONE_CONSOLE_POLL_HZ;
1045                         if (kp_stable < KP_STABLE_HZ)
1046                                 break;
1047
1048                         kp_cur_row = 0;
1049                         kp_stable = 0;
1050                         kp_state = SCAN_COL;
1051
1052                         (void)kp_get_col_mask(1 << kp_cur_row);
1053                         break;
1054
1055                 case SCAN_COL:
1056                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1057                         if (col_mask == KP_COLS_MASK) {
1058                                 if (++kp_cur_row >= KP_ROWS) {
1059                                         kp_state = SCAN;
1060                                         break;
1061                                 }
1062                                 kp_get_col_mask(1 << kp_cur_row);
1063                                 break;
1064                         }
1065                         kp_last_col_mask = col_mask;
1066                         kp_stable = 0;
1067                         kp_state = SCAN_COL_FILTER;
1068                         break;
1069
1070                 case SCAN_COL_FILTER:
1071                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1072                         if (col_mask != kp_last_col_mask || col_mask == KP_COLS_MASK) {
1073                                 kp_state = SCAN;
1074                                 break;
1075                         }
1076
1077                         kp_stable += PHONE_CONSOLE_POLL_HZ;
1078                         if (kp_stable < KP_STABLE_HZ)
1079                                 break;
1080
1081                         for (col = 0; col < KP_COLS; col++)
1082                                 if ((col_mask & (1 << col)) == 0)
1083                                         break;
1084                         kp_cur_col = col;
1085                         kp_state = PRESSED;
1086                         kp_scancode = kp_scancodes[kp_cur_row * KP_COLS + kp_cur_col];
1087                         kp_repeat = kp_repeats[kp_cur_row * KP_COLS + kp_cur_col];
1088
1089                         if (kp_repeat)
1090                                 kp_repeat_time = KP_REPEAT_DELAY_HZ;
1091                         kp_force_time = KP_FORCE_DELAY_HZ;
1092
1093                         scancode_action(kp_scancode);
1094
1095                         break;
1096
1097                 case PRESSED:
1098                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1099                         if (col_mask != kp_last_col_mask) {
1100                                 kp_state = SCAN;
1101                                 scancode_action(KP_RELEASE);
1102                                 kp_idle_time = KP_IDLE_DELAY_HZ;
1103                                 break;
1104                         }
1105
1106                         if (kp_repeat) {
1107                                 kp_repeat_time -= PHONE_CONSOLE_POLL_HZ;
1108                                 if (kp_repeat_time <= 0) {
1109                                         kp_repeat_time += KP_REPEAT_HZ;
1110                                         scancode_action(kp_scancode);
1111                                 }
1112                         }
1113
1114                         if (kp_force_time > 0) {
1115                                 kp_force_time -= PHONE_CONSOLE_POLL_HZ;
1116                                 if (kp_force_time <= 0)
1117                                         scancode_action(KP_FORCE);
1118                         }
1119
1120                         break;
1121         }
1122 }