blob: aa31ed3a70def9c16afbbe5e621f0d7abd0f2592 [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
wdenkc4e854f2004-06-07 23:46:25 +000015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
wdenk3902d702004-04-15 18:22:41 +000016 * 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
wdenkc4e854f2004-06-07 23:46:25 +000055#define DISPLAY_BACKLIT_MASK 0x0010
wdenk3902d702004-04-15 18:22:41 +000056
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
wdenkc4e854f2004-06-07 23:46:25 +000066#define KP_SPI_RXD_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
67#define KP_SPI_RXD_MASK 0x0008
wdenk3902d702004-04-15 18:22:41 +000068
wdenkc4e854f2004-06-07 23:46:25 +000069#define KP_SPI_TXD_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
70#define KP_SPI_TXD_MASK 0x0004
wdenk3902d702004-04-15 18:22:41 +000071
wdenkc4e854f2004-06-07 23:46:25 +000072#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
wdenkc4e854f2004-06-07 23:46:25 +000075#define KP_SPI_RXD_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat)
76#define KP_SPI_RXD_MASK 0x00000008
wdenk6203e402004-04-18 10:13:26 +000077
wdenkc4e854f2004-06-07 23:46:25 +000078#define KP_SPI_TXD_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat)
79#define KP_SPI_TXD_MASK 0x00000004
wdenk6203e402004-04-18 10:13:26 +000080
wdenkc4e854f2004-06-07 23:46:25 +000081#define KP_SPI_CLK_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat)
82#define KP_SPI_CLK_MASK 0x00000002
wdenk6203e402004-04-18 10:13:26 +000083#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
wdenkc4e854f2004-06-07 23:46:25 +0000118#define KP_ROWS 7
119#define KP_COLS 4
wdenk3902d702004-04-15 18:22:41 +0000120
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
wdenkc4e854f2004-06-07 23:46:25 +0000127#define SCAN_COL_FILTER 3
wdenk3902d702004-04-15 18:22:41 +0000128#define PRESSED 4
129
wdenkc4e854f2004-06-07 23:46:25 +0000130#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 */
wdenk3902d702004-04-15 18:22:41 +0000146
wdenkc4e854f2004-06-07 23:46:25 +0000147#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 */
wdenk3902d702004-04-15 18:22:41 +0000150
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'
wdenkc4e854f2004-06-07 23:46:25 +0000161#define KP_STAR '*'
162#define KP_HASH '#'
wdenk3902d702004-04-15 18:22:41 +0000163
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;
wdenk3902d702004-04-15 18:22:41 +0000182
183/*************************************************************************************************/
184
185#define IM_SMALL 0
186#define IM_CAPITAL 1
187#define IM_NUMBER 2
188
189static int input_mode;
190static char fast_punct;
191static int tab_indicator;
192static const char *fast_punct_list = ",.:;*";
193
194static const char *input_mode_txt[] = { "abc", "ABC", "123" };
195
196static const char *punct = ".,!;?'\"-()@/:_+&%*=<>$[]{}\\~^#|";
197static const char *whspace = " 0\n";
198/* per mode character select (for 2-9) */
199static const char *digits_sel[2][8] = {
200 { /* small */
wdenkc4e854f2004-06-07 23:46:25 +0000201 "abc2", /* 2 */
wdenk3902d702004-04-15 18:22:41 +0000202 "def3", /* 3 */
203 "ghi4", /* 4 */
204 "jkl5", /* 5 */
205 "mno6", /* 6 */
206 "pqrs7", /* 7 */
207 "tuv8", /* 8 */
208 "wxyz9", /* 9 */
wdenkc4e854f2004-06-07 23:46:25 +0000209 }, { /* capital */
210 "ABC2", /* 2 */
wdenk3902d702004-04-15 18:22:41 +0000211 "DEF3", /* 3 */
212 "GHI4", /* 4 */
213 "JKL5", /* 5 */
214 "MNO6", /* 6 */
215 "PQRS7", /* 7 */
216 "TUV8", /* 8 */
217 "WXYZ9", /* 9 */
218 }
219};
220
221/*****************************************************************************/
222
223static void update(void);
224static void ensure_visible(int col, int row, int dx, int dy);
225
226static void console_init(void)
227{
228 curs_disabled = 0;
229 curs_col = 0;
230 curs_row = 0;
231
232 disp_col = 0;
233 disp_row = 0;
234
235 input_mode = IM_SMALL;
236 fast_punct = ',';
237 last_fast_punct = '\0';
238 refresh_time = REFRESH_HZ;
239 blink_time = BLINK_HZ;
240
wdenk3902d702004-04-15 18:22:41 +0000241 memset(vty_buf, ' ', sizeof(vty_buf));
242
243 memset(last_visible_buf, ' ', sizeof(last_visible_buf));
244 last_visible_curs_ptr = NULL;
245 last_input_mode = -1;
246 last_visible_curs_rev = 0;
247
248 blinked_state = 0;
249
250 sed156x_init();
251 width = sed156x_text_width;
252 height = sed156x_text_height - 1;
wdenk20c98a62004-04-23 20:32:05 +0000253
254 tab_indicator = 0;
wdenk3902d702004-04-15 18:22:41 +0000255}
256
257/*****************************************************************************/
258
259void phone_putc(const char c);
260
261/*****************************************************************************/
262
263static int queued_char = -1;
264static int enabled = 0;
265
266/*****************************************************************************/
267
268/* flush buffers */
269int phone_start(void)
270{
271 console_init();
272
273 update();
274 sed156x_sync();
275
276 enabled = 1;
277 queued_char = 'U' - '@';
278
279 /* backlit on */
280 DISPLAY_BACKLIT_PORT &= ~DISPLAY_BACKLIT_MASK;
281
282 return 0;
283}
284
285int phone_stop(void)
286{
287 enabled = 0;
288
289 sed156x_clear();
290 sed156x_sync();
291
292 /* backlit off */
293 DISPLAY_BACKLIT_PORT |= DISPLAY_BACKLIT_MASK;
294
295 return 0;
296}
297
298void phone_puts(const char *s)
299{
300 int count = strlen(s);
301
302 while (count--)
303 phone_putc(*s++);
304}
305
306int phone_tstc(void)
307{
308 return queued_char >= 0 ? 1 : 0;
309}
310
311int phone_getc(void)
312{
313 int r;
314
315 if (queued_char < 0)
316 return -1;
317
318 r = queued_char;
319 queued_char = -1;
320
321 return r;
322}
323
324/*****************************************************************************/
325
326int drv_phone_init(void)
327{
328 device_t console_dev;
329 char *penv;
330
331 /*
332 * Force console i/o to serial ?
333 */
334 if ((penv = getenv("console")) != NULL && strcmp(penv, "serial") == 0)
335 return 0;
336
337 console_init();
338
339 memset(&console_dev, 0, sizeof(console_dev));
340 strcpy(console_dev.name, "phone");
341 console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */
342 console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
343 console_dev.start = phone_start;
344 console_dev.stop = phone_stop;
345 console_dev.putc = phone_putc; /* 'putc' function */
346 console_dev.puts = phone_puts; /* 'puts' function */
347 console_dev.tstc = phone_tstc; /* 'tstc' function */
348 console_dev.getc = phone_getc; /* 'getc' function */
349
350 if (device_register(&console_dev) == 0)
351 return 1;
352
353 return 0;
354}
355
356static int use_me;
357
358int drv_phone_use_me(void)
359{
360 return use_me;
361}
362
363static void kp_do_poll(void);
364
365void phone_console_do_poll(void)
366{
367 int i, x, y;
368
369 kp_do_poll();
370
371 if (enabled) {
372 /* do the blink */
373 blink_time -= PHONE_CONSOLE_POLL_HZ;
374 if (blink_time <= 0) {
375 blink_time += BLINK_HZ;
376 if (last_visible_curs_ptr) {
377 i = last_visible_curs_ptr - last_visible_buf;
378 x = i % width; y = i / width;
379 sed156x_reverse_at(x, y, 1);
380 last_visible_curs_rev ^= 1;
381 }
382 }
383
384 /* do the refresh */
385 refresh_time -= PHONE_CONSOLE_POLL_HZ;
386 if (refresh_time <= 0) {
387 refresh_time += REFRESH_HZ;
388 sed156x_sync();
389 }
390 }
391
392}
393
394static int last_scancode = -1;
395static int forced_scancode = 0;
396static int input_state = -1;
397static int input_scancode = -1;
398static int input_selected_char = -1;
399static char input_covered_char;
400
401static void putchar_at_cursor(char c)
402{
403 vty_buf[curs_row * COLS + curs_col] = c;
404 ensure_visible(curs_col, curs_row, 1, 1);
405}
406
407static char getchar_at_cursor(void)
408{
409 return vty_buf[curs_row * COLS + curs_col];
410}
411
412static void queue_input_char(char c)
413{
414 if (c <= 0)
415 return;
416
417 queued_char = c;
418}
419
420static void terminate_input(void)
421{
422 if (input_state < 0)
423 return;
424
425 if (input_selected_char >= 0)
426 queue_input_char(input_selected_char);
427
428 input_state = -1;
429 input_selected_char = -1;
430 putchar_at_cursor(input_covered_char);
431
432 curs_disabled = 0;
433 blink_time = BLINK_HZ;
434 update();
435}
436
437static void handle_enabled_scancode(int scancode)
438{
439 char c;
440 int new_disp_col, new_disp_row;
441 const char *sel;
442
443
444 switch (scancode) {
445
446 /* key was released */
447 case KP_RELEASE:
448 forced_scancode = 0;
449 break;
450
451 /* key was forced */
452 case KP_FORCE:
453
454 switch (last_scancode) {
455 case '#':
456 if (input_mode == IM_NUMBER) {
457 input_mode = IM_CAPITAL;
458 /* queue backspace to erase # */
459 queue_input_char('\b');
460 } else {
461 input_mode = IM_NUMBER;
462 fast_punct = '*';
463 }
464 update();
465 break;
466
467 case '0': case '1':
468 case '2': case '3': case '4': case '5':
469 case '6': case '7': case '8': case '9':
470
471 if (input_state < 0)
472 break;
473
474 input_selected_char = last_scancode;
475 putchar_at_cursor((char)input_selected_char);
476 terminate_input();
477
478 break;
479
480 default:
481 break;
482 }
483
484 break;
485
486 /* release and idle */
487 case KP_IDLE:
488 input_scancode = -1;
489 if (input_state < 0)
490 break;
491 terminate_input();
492 break;
493
494 /* change input mode */
495 case '#':
496 if (last_scancode == '#') /* no repeat */
497 break;
498
499 if (input_mode == IM_NUMBER) {
500 input_scancode = scancode;
501 input_state = 0;
502 input_selected_char = scancode;
503 input_covered_char = getchar_at_cursor();
504 putchar_at_cursor((char)input_selected_char);
505 terminate_input();
506 break;
507 }
508
509 if (input_mode == IM_SMALL)
510 input_mode = IM_CAPITAL;
511 else
512 input_mode = IM_SMALL;
513
514 update();
515 break;
516
517 case '*':
518 /* no repeat */
519 if (last_scancode == scancode)
520 break;
521
522 if (input_state >= 0)
523 terminate_input();
524
525 input_scancode = fast_punct;
526 input_state = 0;
527 input_selected_char = input_scancode;
528 input_covered_char = getchar_at_cursor();
529 putchar_at_cursor((char)input_selected_char);
530 terminate_input();
531
532 break;
533
534 case '0': case '1':
535 case '2': case '3': case '4': case '5':
536 case '6': case '7': case '8': case '9':
537
538 /* no repeat */
539 if (last_scancode == scancode)
540 break;
541
542 if (input_mode == IM_NUMBER) {
543 input_scancode = scancode;
544 input_state = 0;
545 input_selected_char = scancode;
546 input_covered_char = getchar_at_cursor();
547 putchar_at_cursor((char)input_selected_char);
548 terminate_input();
549 break;
550 }
551
552 if (input_state >= 0 && input_scancode != scancode)
553 terminate_input();
554
555 if (input_state < 0) {
556 curs_disabled = 1;
557 input_scancode = scancode;
558 input_state = 0;
559 input_covered_char = getchar_at_cursor();
560 } else
561 input_state++;
562
563 if (scancode == '0')
564 sel = whspace;
565 else if (scancode == '1')
566 sel = punct;
567 else
568 sel = digits_sel[input_mode][scancode - '2'];
569 c = *(sel + input_state);
570 if (c == '\0') {
571 input_state = 0;
572 c = *sel;
573 }
574
575 input_selected_char = (int)c;
576 putchar_at_cursor((char)input_selected_char);
577 update();
578
579 break;
580
581 /* move visible display */
582 case KP_F3: case KP_F8: case KP_F7: case KP_F9:
583
584 new_disp_col = disp_col;
585 new_disp_row = disp_row;
586
587 switch (scancode) {
588 /* up */
589 case KP_F3:
590 if (new_disp_row <= 0)
591 break;
592 new_disp_row--;
593 break;
594
595 /* down */
596 case KP_F8:
597 if (new_disp_row >= ROWS - height)
598 break;
599 new_disp_row++;
600 break;
601
602 /* left */
603 case KP_F7:
604 if (new_disp_col <= 0)
605 break;
606 new_disp_col--;
607 break;
608
609 /* right */
610 case KP_F9:
611 if (new_disp_col >= COLS - width)
612 break;
613 new_disp_col++;
614 break;
615 }
616
617 /* no change? */
618 if (disp_col == new_disp_col && disp_row == new_disp_row)
619 break;
620
621 disp_col = new_disp_col;
622 disp_row = new_disp_row;
623 update();
624
625 break;
626
627 case KP_F6: /* backspace */
628 /* inputing something; no backspace sent, just cancel input */
629 if (input_state >= 0) {
630 input_selected_char = -1; /* cancel */
631 terminate_input();
632 break;
633 }
634 queue_input_char('\b');
635 break;
636
637 case KP_F10: /* enter */
638 /* inputing something; first cancel input */
639 if (input_state >= 0)
640 terminate_input();
641 queue_input_char('\r');
642 break;
643
644 case KP_F11: /* R -> Ctrl-C (abort) */
645 if (input_state >= 0)
646 terminate_input();
647 queue_input_char('C' - 'Q'); /* ctrl-c */
648 break;
649
650 case KP_F5: /* F% -> Ctrl-U (clear line) */
651 if (input_state >= 0)
652 terminate_input();
653 queue_input_char('U' - 'Q'); /* ctrl-c */
654 break;
655
656
657 case KP_F1: /* tab */
658 /* inputing something; first cancel input */
659 if (input_state >= 0)
660 terminate_input();
661 queue_input_char('\t');
662 break;
663
664 case KP_F2: /* change fast punct */
665 sel = strchr(fast_punct_list, fast_punct);
666 if (sel == NULL)
667 sel = &fast_punct_list[0];
668 sel++;
669 if (*sel == '\0')
670 sel = &fast_punct_list[0];
671 fast_punct = *sel;
672 update();
673 break;
674
675
676 }
677
678 if (scancode != KP_FORCE && scancode != KP_IDLE) /* don't record forced or idle scancode */
679 last_scancode = scancode;
680}
681
682static void scancode_action(int scancode)
683{
684#if 0
685 if (scancode == KP_RELEASE)
686 printf(" RELEASE\n");
687 else if (scancode == KP_FORCE)
688 printf(" FORCE\n");
689 else if (scancode == KP_IDLE)
690 printf(" IDLE\n");
691 else if (scancode < 32)
692 printf(" F%d", scancode + 1);
693 else
694 printf(" %c", (char)scancode);
695 printf("\n");
696#endif
697
698 if (enabled) {
699 handle_enabled_scancode(scancode);
700 return;
701 }
702
703 if (scancode == KP_FORCE && last_scancode == '*')
704 use_me = 1;
705
706 last_scancode = scancode;
707}
708
709/**************************************************************************************/
710
711/* update the display; make sure to update only the differences */
712static void update(void)
713{
714 int i;
715 char *s, *e, *t, *r, *b, *cp;
716
717 if (input_mode != last_input_mode)
718 sed156x_output_at(sed156x_text_width - 3, sed156x_text_height - 1, input_mode_txt[input_mode], 3);
719
wdenk20c98a62004-04-23 20:32:05 +0000720 if (tab_indicator == 0) {
wdenk3902d702004-04-15 18:22:41 +0000721 sed156x_output_at(0, sed156x_text_height - 1, "\\t", 2);
wdenk20c98a62004-04-23 20:32:05 +0000722 tab_indicator = 1;
723 }
wdenk3902d702004-04-15 18:22:41 +0000724
725 if (fast_punct != last_fast_punct)
726 sed156x_output_at(4, sed156x_text_height - 1, &fast_punct, 1);
727
728 if (curs_disabled ||
729 curs_col < disp_col || curs_col >= (disp_col + width) ||
730 curs_row < disp_row || curs_row >= (disp_row + height)) {
731 cp = NULL;
732 } else
733 cp = last_visible_buf + (curs_row - disp_row) * width + (curs_col - disp_col);
734
735
736 /* printf("(%d,%d) (%d,%d) %s\n", curs_col, curs_row, disp_col, disp_row, cp ? "YES" : "no"); */
737
738 /* clear previous cursor */
739 if (last_visible_curs_ptr && last_visible_curs_rev == 0) {
740 i = last_visible_curs_ptr - last_visible_buf;
741 sed156x_reverse_at(i % width, i / width, 1);
742 }
743
744 b = vty_buf + disp_row * COLS + disp_col;
745 t = last_visible_buf;
746 for (i = 0; i < height; i++) {
747 s = b;
748 e = b + width;
749 /* update only the differences */
750 do {
751 while (s < e && *s == *t) {
752 s++;
753 t++;
754 }
755 if (s == e) /* no more */
756 break;
757
758 /* find run */
759 r = s;
760 while (s < e && *s != *t)
761 *t++ = *s++;
762
763 /* and update */
764 sed156x_output_at(r - b, i, r, s - r);
765
766 } while (s < e);
767
768 b += COLS;
769 }
770
771 /* set cursor */
772 if (cp) {
773 last_visible_curs_ptr = cp;
774 i = last_visible_curs_ptr - last_visible_buf;
775 sed156x_reverse_at(i % width, i / width, 1);
776 last_visible_curs_rev = 0;
777 } else {
778 last_visible_curs_ptr = NULL;
779 }
780
781 last_input_mode = input_mode;
782 last_fast_punct = fast_punct;
wdenk3902d702004-04-15 18:22:41 +0000783}
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 */
wdenkc4e854f2004-06-07 23:46:25 +0000813 x1 = disp_col; y1 = disp_row;
wdenk3902d702004-04-15 18:22:41 +0000814 x2 = x1 + width; y2 = y1 + height;
wdenkc4e854f2004-06-07 23:46:25 +0000815 a1 = col; b1 = row;
816 a2 = a1 + dx; b2 = b1 + dy;
wdenk3902d702004-04-15 18:22:41 +0000817
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) {
wdenkc4e854f2004-06-07 23:46:25 +0000894 case '\a': /* ignore bell */
wdenk20c98a62004-04-23 20:32:05 +0000895 case '\r': /* ignore carriage return */
wdenk3902d702004-04-15 18:22:41 +0000896 break;
897
898 case '\n': /* next line */
899 newline();
900 ensure_visible(curs_col, curs_row, 1, 1);
901 break;
902
wdenkc4e854f2004-06-07 23:46:25 +0000903 case 9: /* tab 8 */
wdenk3902d702004-04-15 18:22:41 +0000904 /* move to tab */
905 i = curs_col;
906 i |= 0x0008;
907 i &= ~0x0007;
908
909 if (i < COLS)
910 curs_col = i;
911 else
912 newline();
913
914 ensure_visible(curs_col, curs_row, 1, 1);
915 break;
916
917 case 8: /* backspace */
918 if (curs_col <= 0)
919 break;
920 curs_col--;
921
922 /* make sure that we see a couple of characters before */
923 if (curs_col > 4)
924 ensure_visible(curs_col - 4, curs_row, 4, 1);
925 else
926 ensure_visible(curs_col, curs_row, 1, 1);
927
928 break;
929
930 default: /* draw the char */
931 putchar_at_cursor(c);
932
933 /*
934 * check for newline
935 */
936 if (curs_col + 1 < COLS)
937 curs_col++;
938 else
939 newline();
940
941 ensure_visible(curs_col, curs_row, 1, 1);
942
943 break;
944 }
945
946 curs_disabled = 0;
947 blink_time = BLINK_HZ;
948 update();
949}
950
951/**************************************************************************************/
952
953static inline unsigned int kp_transfer(unsigned int val)
954{
955 unsigned int rx;
956 int b;
957
958 rx = 0; b = 8;
959 while (--b >= 0) {
960 KP_SPI_TXD(val & 0x80);
961 val <<= 1;
962 KP_SPI_CLK_TOGGLE();
963 KP_SPI_BIT_DELAY();
964 rx <<= 1;
965 if (KP_SPI_RXD())
966 rx |= 1;
967 KP_SPI_CLK_TOGGLE();
968 KP_SPI_BIT_DELAY();
969 }
970
971 return rx;
972}
973
974unsigned int kp_data_transfer(unsigned int val)
975{
976 KP_SPI_CLK(1);
977 KP_CS(0);
978 val = kp_transfer(val);
979 KP_CS(1);
980
981 return val;
982}
983
984unsigned int kp_get_col_mask(unsigned int row_mask)
985{
986 unsigned int val, col_mask;
987
988 val = 0x80 | (row_mask & 0x7F);
989 (void)kp_data_transfer(val);
wdenk6203e402004-04-18 10:13:26 +0000990#if CONFIG_NETPHONE_VERSION == 1
wdenk3902d702004-04-15 18:22:41 +0000991 col_mask = kp_data_transfer(val) & 0x0F;
wdenk6203e402004-04-18 10:13:26 +0000992#elif CONFIG_NETPHONE_VERSION == 2
993 col_mask = ((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pedat & 0x0f;
994 /* XXX FUCK FUCK FUCK FUCK FUCK!!!! */
995 col_mask = ((col_mask & 0x08) >> 3) | /* BKBR1 */
996 ((col_mask & 0x04) << 1) | /* BKBR2 */
997 (col_mask & 0x02) | /* BKBR3 */
998 ((col_mask & 0x01) << 2); /* BKBR4 */
wdenk3902d702004-04-15 18:22:41 +0000999
wdenk6203e402004-04-18 10:13:26 +00001000#endif
wdenk3902d702004-04-15 18:22:41 +00001001 /* printf("col_mask(row_mask = 0x%x) -> col_mask = 0x%x\n", row_mask, col_mask); */
wdenk6203e402004-04-18 10:13:26 +00001002
wdenk3902d702004-04-15 18:22:41 +00001003 return col_mask;
1004}
1005
1006/**************************************************************************************/
1007
1008static const int kp_scancodes[KP_ROWS * KP_COLS] = {
wdenkc4e854f2004-06-07 23:46:25 +00001009 KP_F1, KP_F3, KP_F4, KP_F2,
1010 KP_F6, KP_F8, KP_F9, KP_F7,
1011 KP_1, KP_3, KP_F11, KP_2,
1012 KP_4, KP_6, KP_F12, KP_5,
1013 KP_7, KP_9, KP_F13, KP_8,
wdenk3902d702004-04-15 18:22:41 +00001014 KP_STAR, KP_HASH, KP_F14, KP_0,
wdenkc4e854f2004-06-07 23:46:25 +00001015 KP_F5, KP_F15, KP_F16, KP_F10,
wdenk3902d702004-04-15 18:22:41 +00001016};
1017
1018static const int kp_repeats[KP_ROWS * KP_COLS] = {
1019 0, 1, 0, 0,
1020 0, 1, 1, 1,
1021 1, 1, 0, 1,
1022 1, 1, 0, 1,
1023 1, 1, 0, 1,
1024 1, 1, 0, 1,
1025 0, 0, 0, 1,
1026};
1027
1028static int kp_state = SCAN;
1029static int kp_last_col_mask;
1030static int kp_cur_row, kp_cur_col;
1031static int kp_scancode;
1032static int kp_stable;
1033static int kp_repeat;
1034static int kp_repeat_time;
1035static int kp_force_time;
1036static int kp_idle_time;
1037
1038static void kp_do_poll(void)
1039{
1040 unsigned int col_mask;
1041 int col;
1042
1043 switch (kp_state) {
1044 case SCAN:
1045 if (kp_idle_time > 0) {
1046 kp_idle_time -= PHONE_CONSOLE_POLL_HZ;
1047 if (kp_idle_time <= 0)
1048 scancode_action(KP_IDLE);
1049 }
1050
1051 col_mask = kp_get_col_mask(KP_ROWS_MASK);
1052 if (col_mask == KP_COLS_MASK)
1053 break; /* nothing */
1054 kp_last_col_mask = col_mask;
1055 kp_stable = 0;
1056 kp_state = SCAN_FILTER;
1057 break;
1058
1059 case SCAN_FILTER:
1060 col_mask = kp_get_col_mask(KP_ROWS_MASK);
1061 if (col_mask != kp_last_col_mask) {
1062 kp_state = SCAN;
1063 break;
1064 }
1065
1066 kp_stable += PHONE_CONSOLE_POLL_HZ;
1067 if (kp_stable < KP_STABLE_HZ)
1068 break;
1069
1070 kp_cur_row = 0;
1071 kp_stable = 0;
1072 kp_state = SCAN_COL;
1073
1074 (void)kp_get_col_mask(1 << kp_cur_row);
1075 break;
1076
1077 case SCAN_COL:
1078 col_mask = kp_get_col_mask(1 << kp_cur_row);
1079 if (col_mask == KP_COLS_MASK) {
1080 if (++kp_cur_row >= KP_ROWS) {
1081 kp_state = SCAN;
1082 break;
1083 }
1084 kp_get_col_mask(1 << kp_cur_row);
1085 break;
1086 }
1087 kp_last_col_mask = col_mask;
1088 kp_stable = 0;
1089 kp_state = SCAN_COL_FILTER;
1090 break;
1091
1092 case SCAN_COL_FILTER:
1093 col_mask = kp_get_col_mask(1 << kp_cur_row);
1094 if (col_mask != kp_last_col_mask || col_mask == KP_COLS_MASK) {
1095 kp_state = SCAN;
1096 break;
1097 }
1098
1099 kp_stable += PHONE_CONSOLE_POLL_HZ;
1100 if (kp_stable < KP_STABLE_HZ)
1101 break;
1102
1103 for (col = 0; col < KP_COLS; col++)
1104 if ((col_mask & (1 << col)) == 0)
1105 break;
1106 kp_cur_col = col;
1107 kp_state = PRESSED;
1108 kp_scancode = kp_scancodes[kp_cur_row * KP_COLS + kp_cur_col];
1109 kp_repeat = kp_repeats[kp_cur_row * KP_COLS + kp_cur_col];
1110
1111 if (kp_repeat)
1112 kp_repeat_time = KP_REPEAT_DELAY_HZ;
1113 kp_force_time = KP_FORCE_DELAY_HZ;
1114
1115 scancode_action(kp_scancode);
1116
1117 break;
1118
1119 case PRESSED:
1120 col_mask = kp_get_col_mask(1 << kp_cur_row);
1121 if (col_mask != kp_last_col_mask) {
1122 kp_state = SCAN;
1123 scancode_action(KP_RELEASE);
1124 kp_idle_time = KP_IDLE_DELAY_HZ;
1125 break;
1126 }
1127
1128 if (kp_repeat) {
1129 kp_repeat_time -= PHONE_CONSOLE_POLL_HZ;
1130 if (kp_repeat_time <= 0) {
1131 kp_repeat_time += KP_REPEAT_HZ;
1132 scancode_action(kp_scancode);
1133 }
1134 }
1135
1136 if (kp_force_time > 0) {
1137 kp_force_time -= PHONE_CONSOLE_POLL_HZ;
1138 if (kp_force_time <= 0)
1139 scancode_action(KP_FORCE);
1140 }
1141
1142 break;
1143 }
1144}
wdenk20c98a62004-04-23 20:32:05 +00001145
1146/**************************************************************************************/
1147
1148int drv_phone_is_idle(void)
1149{
1150 return kp_state == SCAN;
1151}