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