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