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