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