blob: a0485b0927002e8e15f0b2e7bae3c1ddbfe0f069 [file] [log] [blame]
wdenk3902d702004-04-15 18:22:41 +00001/*
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
wdenk6203e402004-04-18 10:13:26 +000065#if CONFIG_NETPHONE_VERSION == 1
wdenk3902d702004-04-15 18:22:41 +000066#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
wdenk6203e402004-04-18 10:13:26 +000074#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
wdenk3902d702004-04-15 18:22:41 +000084
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
166static int curs_disabled;
167static int curs_col, curs_row;
168static int disp_col, disp_row;
169
170static int width, height;
171
172/* the simulated vty buffer */
173static char vty_buf[ROWS * COLS];
174static char last_visible_buf[ROWS * COLS]; /* worst case */
175static char *last_visible_curs_ptr;
176static int last_visible_curs_rev;
177static int blinked_state;
178static int last_input_mode;
179static int refresh_time;
180static int blink_time;
181static char last_fast_punct;
182static int last_tab_indicator = -1;
183
184/*************************************************************************************************/
185
186#define IM_SMALL 0
187#define IM_CAPITAL 1
188#define IM_NUMBER 2
189
190static int input_mode;
191static char fast_punct;
192static int tab_indicator;
193static const char *fast_punct_list = ",.:;*";
194
195static const char *input_mode_txt[] = { "abc", "ABC", "123" };
196
197static const char *punct = ".,!;?'\"-()@/:_+&%*=<>$[]{}\\~^#|";
198static const char *whspace = " 0\n";
199/* per mode character select (for 2-9) */
200static 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
224static void update(void);
225static void ensure_visible(int col, int row, int dx, int dy);
226
227static 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
260void phone_putc(const char c);
261
262/*****************************************************************************/
263
264static int queued_char = -1;
265static int enabled = 0;
266
267/*****************************************************************************/
268
269/* flush buffers */
270int 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
286int 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
299void phone_puts(const char *s)
300{
301 int count = strlen(s);
302
303 while (count--)
304 phone_putc(*s++);
305}
306
307int phone_tstc(void)
308{
309 return queued_char >= 0 ? 1 : 0;
310}
311
312int 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
327int 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
357static int use_me;
358
359int drv_phone_use_me(void)
360{
361 return use_me;
362}
363
364static void kp_do_poll(void);
365
366void 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
395static int last_scancode = -1;
396static int forced_scancode = 0;
397static int input_state = -1;
398static int input_scancode = -1;
399static int input_selected_char = -1;
400static char input_covered_char;
401
402static 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
408static char getchar_at_cursor(void)
409{
410 return vty_buf[curs_row * COLS + curs_col];
411}
412
413static void queue_input_char(char c)
414{
415 if (c <= 0)
416 return;
417
418 queued_char = c;
419}
420
421static 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
438static 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
683static 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 */
713static 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 */
786static 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
868static 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
879void 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
952static 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
973unsigned 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
983unsigned 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);
wdenk6203e402004-04-18 10:13:26 +0000989#if CONFIG_NETPHONE_VERSION == 1
wdenk3902d702004-04-15 18:22:41 +0000990 col_mask = kp_data_transfer(val) & 0x0F;
wdenk6203e402004-04-18 10:13:26 +0000991#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 */
wdenk3902d702004-04-15 18:22:41 +0000998
wdenk6203e402004-04-18 10:13:26 +0000999#endif
wdenk3902d702004-04-15 18:22:41 +00001000 /* printf("col_mask(row_mask = 0x%x) -> col_mask = 0x%x\n", row_mask, col_mask); */
wdenk6203e402004-04-18 10:13:26 +00001001
wdenk3902d702004-04-15 18:22:41 +00001002 return col_mask;
1003}
1004
1005/**************************************************************************************/
1006
1007static 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
1017static 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
1027static int kp_state = SCAN;
1028static int kp_last_col_mask;
1029static int kp_cur_row, kp_cur_col;
1030static int kp_scancode;
1031static int kp_stable;
1032static int kp_repeat;
1033static int kp_repeat_time;
1034static int kp_force_time;
1035static int kp_idle_time;
1036
1037static 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}