blob: d9b0ad3768ddfcd5563d32c34c957f8eeae6f4cd [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
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020049#define REFRESH_HZ (CONFIG_SYS_HZ/50) /* refresh every 20ms */
50#define BLINK_HZ (CONFIG_SYS_HZ/2) /* cursor blink every 500ms */
wdenk3902d702004-04-15 18:22:41 +000051
52/*************************************************************************************************/
53
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020054#define DISPLAY_BACKLIT_PORT ((volatile immap_t *)CONFIG_SYS_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
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020059#define KP_STABLE_HZ (CONFIG_SYS_HZ/100) /* stable for 10ms */
60#define KP_REPEAT_DELAY_HZ (CONFIG_SYS_HZ/4) /* delay before repeat 250ms */
61#define KP_REPEAT_HZ (CONFIG_SYS_HZ/20) /* repeat every 50ms */
62#define KP_FORCE_DELAY_HZ (CONFIG_SYS_HZ/2) /* key was force pressed */
63#define KP_IDLE_DELAY_HZ (CONFIG_SYS_HZ/2) /* key was released and idle */
wdenk3902d702004-04-15 18:22:41 +000064
wdenk6203e402004-04-18 10:13:26 +000065#if CONFIG_NETPHONE_VERSION == 1
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020066#define KP_SPI_RXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat)
wdenkc4e854f2004-06-07 23:46:25 +000067#define KP_SPI_RXD_MASK 0x0008
wdenk3902d702004-04-15 18:22:41 +000068
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020069#define KP_SPI_TXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat)
wdenkc4e854f2004-06-07 23:46:25 +000070#define KP_SPI_TXD_MASK 0x0004
wdenk3902d702004-04-15 18:22:41 +000071
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020072#define KP_SPI_CLK_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat)
wdenkc4e854f2004-06-07 23:46:25 +000073#define KP_SPI_CLK_MASK 0x0001
wdenk6203e402004-04-18 10:13:26 +000074#elif CONFIG_NETPHONE_VERSION == 2
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020075#define KP_SPI_RXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
wdenkc4e854f2004-06-07 23:46:25 +000076#define KP_SPI_RXD_MASK 0x00000008
wdenk6203e402004-04-18 10:13:26 +000077
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020078#define KP_SPI_TXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
wdenkc4e854f2004-06-07 23:46:25 +000079#define KP_SPI_TXD_MASK 0x00000004
wdenk6203e402004-04-18 10:13:26 +000080
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020081#define KP_SPI_CLK_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
wdenkc4e854f2004-06-07 23:46:25 +000082#define KP_SPI_CLK_MASK 0x00000002
wdenk6203e402004-04-18 10:13:26 +000083#endif
wdenk3902d702004-04-15 18:22:41 +000084
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020085#define KP_CS_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pedat)
wdenk3902d702004-04-15 18:22:41 +000086#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;
wdenk3902d702004-04-15 18:22:41 +0000329
wdenk3902d702004-04-15 18:22:41 +0000330 console_init();
331
332 memset(&console_dev, 0, sizeof(console_dev));
333 strcpy(console_dev.name, "phone");
334 console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */
335 console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
336 console_dev.start = phone_start;
337 console_dev.stop = phone_stop;
338 console_dev.putc = phone_putc; /* 'putc' function */
339 console_dev.puts = phone_puts; /* 'puts' function */
340 console_dev.tstc = phone_tstc; /* 'tstc' function */
341 console_dev.getc = phone_getc; /* 'getc' function */
342
343 if (device_register(&console_dev) == 0)
344 return 1;
345
346 return 0;
347}
348
349static int use_me;
350
351int drv_phone_use_me(void)
352{
353 return use_me;
354}
355
356static void kp_do_poll(void);
357
358void phone_console_do_poll(void)
359{
360 int i, x, y;
361
362 kp_do_poll();
363
364 if (enabled) {
365 /* do the blink */
366 blink_time -= PHONE_CONSOLE_POLL_HZ;
367 if (blink_time <= 0) {
368 blink_time += BLINK_HZ;
369 if (last_visible_curs_ptr) {
370 i = last_visible_curs_ptr - last_visible_buf;
371 x = i % width; y = i / width;
372 sed156x_reverse_at(x, y, 1);
373 last_visible_curs_rev ^= 1;
374 }
375 }
376
377 /* do the refresh */
378 refresh_time -= PHONE_CONSOLE_POLL_HZ;
379 if (refresh_time <= 0) {
380 refresh_time += REFRESH_HZ;
381 sed156x_sync();
382 }
383 }
384
385}
386
387static int last_scancode = -1;
388static int forced_scancode = 0;
389static int input_state = -1;
390static int input_scancode = -1;
391static int input_selected_char = -1;
392static char input_covered_char;
393
394static void putchar_at_cursor(char c)
395{
396 vty_buf[curs_row * COLS + curs_col] = c;
397 ensure_visible(curs_col, curs_row, 1, 1);
398}
399
400static char getchar_at_cursor(void)
401{
402 return vty_buf[curs_row * COLS + curs_col];
403}
404
405static void queue_input_char(char c)
406{
407 if (c <= 0)
408 return;
409
410 queued_char = c;
411}
412
413static void terminate_input(void)
414{
415 if (input_state < 0)
416 return;
417
418 if (input_selected_char >= 0)
419 queue_input_char(input_selected_char);
420
421 input_state = -1;
422 input_selected_char = -1;
423 putchar_at_cursor(input_covered_char);
424
425 curs_disabled = 0;
426 blink_time = BLINK_HZ;
427 update();
428}
429
430static void handle_enabled_scancode(int scancode)
431{
432 char c;
433 int new_disp_col, new_disp_row;
434 const char *sel;
435
436
437 switch (scancode) {
438
439 /* key was released */
440 case KP_RELEASE:
441 forced_scancode = 0;
442 break;
443
444 /* key was forced */
445 case KP_FORCE:
446
447 switch (last_scancode) {
448 case '#':
449 if (input_mode == IM_NUMBER) {
450 input_mode = IM_CAPITAL;
451 /* queue backspace to erase # */
452 queue_input_char('\b');
453 } else {
454 input_mode = IM_NUMBER;
455 fast_punct = '*';
456 }
457 update();
458 break;
459
460 case '0': case '1':
461 case '2': case '3': case '4': case '5':
462 case '6': case '7': case '8': case '9':
463
464 if (input_state < 0)
465 break;
466
467 input_selected_char = last_scancode;
468 putchar_at_cursor((char)input_selected_char);
469 terminate_input();
470
471 break;
472
473 default:
474 break;
475 }
476
477 break;
478
479 /* release and idle */
480 case KP_IDLE:
481 input_scancode = -1;
482 if (input_state < 0)
483 break;
484 terminate_input();
485 break;
486
487 /* change input mode */
488 case '#':
489 if (last_scancode == '#') /* no repeat */
490 break;
491
492 if (input_mode == IM_NUMBER) {
493 input_scancode = scancode;
494 input_state = 0;
495 input_selected_char = scancode;
496 input_covered_char = getchar_at_cursor();
497 putchar_at_cursor((char)input_selected_char);
498 terminate_input();
499 break;
500 }
501
502 if (input_mode == IM_SMALL)
503 input_mode = IM_CAPITAL;
504 else
505 input_mode = IM_SMALL;
506
507 update();
508 break;
509
510 case '*':
511 /* no repeat */
512 if (last_scancode == scancode)
513 break;
514
515 if (input_state >= 0)
516 terminate_input();
517
518 input_scancode = fast_punct;
519 input_state = 0;
520 input_selected_char = input_scancode;
521 input_covered_char = getchar_at_cursor();
522 putchar_at_cursor((char)input_selected_char);
523 terminate_input();
524
525 break;
526
527 case '0': case '1':
528 case '2': case '3': case '4': case '5':
529 case '6': case '7': case '8': case '9':
530
531 /* no repeat */
532 if (last_scancode == scancode)
533 break;
534
535 if (input_mode == IM_NUMBER) {
536 input_scancode = scancode;
537 input_state = 0;
538 input_selected_char = scancode;
539 input_covered_char = getchar_at_cursor();
540 putchar_at_cursor((char)input_selected_char);
541 terminate_input();
542 break;
543 }
544
545 if (input_state >= 0 && input_scancode != scancode)
546 terminate_input();
547
548 if (input_state < 0) {
549 curs_disabled = 1;
550 input_scancode = scancode;
551 input_state = 0;
552 input_covered_char = getchar_at_cursor();
553 } else
554 input_state++;
555
556 if (scancode == '0')
557 sel = whspace;
558 else if (scancode == '1')
559 sel = punct;
560 else
561 sel = digits_sel[input_mode][scancode - '2'];
562 c = *(sel + input_state);
563 if (c == '\0') {
564 input_state = 0;
565 c = *sel;
566 }
567
568 input_selected_char = (int)c;
569 putchar_at_cursor((char)input_selected_char);
570 update();
571
572 break;
573
574 /* move visible display */
575 case KP_F3: case KP_F8: case KP_F7: case KP_F9:
576
577 new_disp_col = disp_col;
578 new_disp_row = disp_row;
579
580 switch (scancode) {
581 /* up */
582 case KP_F3:
583 if (new_disp_row <= 0)
584 break;
585 new_disp_row--;
586 break;
587
588 /* down */
589 case KP_F8:
590 if (new_disp_row >= ROWS - height)
591 break;
592 new_disp_row++;
593 break;
594
595 /* left */
596 case KP_F7:
597 if (new_disp_col <= 0)
598 break;
599 new_disp_col--;
600 break;
601
602 /* right */
603 case KP_F9:
604 if (new_disp_col >= COLS - width)
605 break;
606 new_disp_col++;
607 break;
608 }
609
610 /* no change? */
611 if (disp_col == new_disp_col && disp_row == new_disp_row)
612 break;
613
614 disp_col = new_disp_col;
615 disp_row = new_disp_row;
616 update();
617
618 break;
619
620 case KP_F6: /* backspace */
621 /* inputing something; no backspace sent, just cancel input */
622 if (input_state >= 0) {
623 input_selected_char = -1; /* cancel */
624 terminate_input();
625 break;
626 }
627 queue_input_char('\b');
628 break;
629
630 case KP_F10: /* enter */
631 /* inputing something; first cancel input */
632 if (input_state >= 0)
633 terminate_input();
634 queue_input_char('\r');
635 break;
636
637 case KP_F11: /* R -> Ctrl-C (abort) */
638 if (input_state >= 0)
639 terminate_input();
640 queue_input_char('C' - 'Q'); /* ctrl-c */
641 break;
642
643 case KP_F5: /* F% -> Ctrl-U (clear line) */
644 if (input_state >= 0)
645 terminate_input();
646 queue_input_char('U' - 'Q'); /* ctrl-c */
647 break;
648
649
650 case KP_F1: /* tab */
651 /* inputing something; first cancel input */
652 if (input_state >= 0)
653 terminate_input();
654 queue_input_char('\t');
655 break;
656
657 case KP_F2: /* change fast punct */
658 sel = strchr(fast_punct_list, fast_punct);
659 if (sel == NULL)
660 sel = &fast_punct_list[0];
661 sel++;
662 if (*sel == '\0')
663 sel = &fast_punct_list[0];
664 fast_punct = *sel;
665 update();
666 break;
667
668
669 }
670
671 if (scancode != KP_FORCE && scancode != KP_IDLE) /* don't record forced or idle scancode */
672 last_scancode = scancode;
673}
674
675static void scancode_action(int scancode)
676{
677#if 0
678 if (scancode == KP_RELEASE)
679 printf(" RELEASE\n");
680 else if (scancode == KP_FORCE)
681 printf(" FORCE\n");
682 else if (scancode == KP_IDLE)
683 printf(" IDLE\n");
684 else if (scancode < 32)
685 printf(" F%d", scancode + 1);
686 else
687 printf(" %c", (char)scancode);
688 printf("\n");
689#endif
690
691 if (enabled) {
692 handle_enabled_scancode(scancode);
693 return;
694 }
695
696 if (scancode == KP_FORCE && last_scancode == '*')
697 use_me = 1;
698
699 last_scancode = scancode;
700}
701
702/**************************************************************************************/
703
704/* update the display; make sure to update only the differences */
705static void update(void)
706{
707 int i;
708 char *s, *e, *t, *r, *b, *cp;
709
710 if (input_mode != last_input_mode)
711 sed156x_output_at(sed156x_text_width - 3, sed156x_text_height - 1, input_mode_txt[input_mode], 3);
712
wdenk20c98a62004-04-23 20:32:05 +0000713 if (tab_indicator == 0) {
wdenk3902d702004-04-15 18:22:41 +0000714 sed156x_output_at(0, sed156x_text_height - 1, "\\t", 2);
wdenk20c98a62004-04-23 20:32:05 +0000715 tab_indicator = 1;
716 }
wdenk3902d702004-04-15 18:22:41 +0000717
718 if (fast_punct != last_fast_punct)
719 sed156x_output_at(4, sed156x_text_height - 1, &fast_punct, 1);
720
721 if (curs_disabled ||
722 curs_col < disp_col || curs_col >= (disp_col + width) ||
723 curs_row < disp_row || curs_row >= (disp_row + height)) {
724 cp = NULL;
725 } else
726 cp = last_visible_buf + (curs_row - disp_row) * width + (curs_col - disp_col);
727
728
729 /* printf("(%d,%d) (%d,%d) %s\n", curs_col, curs_row, disp_col, disp_row, cp ? "YES" : "no"); */
730
731 /* clear previous cursor */
732 if (last_visible_curs_ptr && last_visible_curs_rev == 0) {
733 i = last_visible_curs_ptr - last_visible_buf;
734 sed156x_reverse_at(i % width, i / width, 1);
735 }
736
737 b = vty_buf + disp_row * COLS + disp_col;
738 t = last_visible_buf;
739 for (i = 0; i < height; i++) {
740 s = b;
741 e = b + width;
742 /* update only the differences */
743 do {
744 while (s < e && *s == *t) {
745 s++;
746 t++;
747 }
748 if (s == e) /* no more */
749 break;
750
751 /* find run */
752 r = s;
753 while (s < e && *s != *t)
754 *t++ = *s++;
755
756 /* and update */
757 sed156x_output_at(r - b, i, r, s - r);
758
759 } while (s < e);
760
761 b += COLS;
762 }
763
764 /* set cursor */
765 if (cp) {
766 last_visible_curs_ptr = cp;
767 i = last_visible_curs_ptr - last_visible_buf;
768 sed156x_reverse_at(i % width, i / width, 1);
769 last_visible_curs_rev = 0;
770 } else {
771 last_visible_curs_ptr = NULL;
772 }
773
774 last_input_mode = input_mode;
775 last_fast_punct = fast_punct;
wdenk3902d702004-04-15 18:22:41 +0000776}
777
778/* ensure visibility; the trick is to minimize the screen movement */
779static void ensure_visible(int col, int row, int dx, int dy)
780{
781 int x1, y1, x2, y2, a1, b1, a2, b2;
782
783 /* clamp visible region */
784 if (col < 0) {
785 dx -= col;
786 col = 0;
787 if (dx <= 0)
788 dx = 1;
789 }
790
791 if (row < 0) {
792 dy -= row;
793 row = 0;
794 if (dy <= 0)
795 dy = 1;
796 }
797
798 if (col + dx > COLS)
799 dx = COLS - col;
800
801 if (row + dy > ROWS)
802 dy = ROWS - row;
803
804
805 /* move to easier to use vars */
wdenkc4e854f2004-06-07 23:46:25 +0000806 x1 = disp_col; y1 = disp_row;
wdenk3902d702004-04-15 18:22:41 +0000807 x2 = x1 + width; y2 = y1 + height;
wdenkc4e854f2004-06-07 23:46:25 +0000808 a1 = col; b1 = row;
809 a2 = a1 + dx; b2 = b1 + dy;
wdenk3902d702004-04-15 18:22:41 +0000810
811 /* printf("(%d,%d) - (%d,%d) : (%d, %d) - (%d, %d)\n", x1, y1, x2, y2, a1, b1, a2, b2); */
812
813 if (a2 > x2) {
814 /* move to the right */
815 x2 = a2;
816 x1 = x2 - width;
817 if (x1 < 0) {
818 x1 = 0;
819 x2 = width;
820 }
821 } else if (a1 < x1) {
822 /* move to the left */
823 x1 = a1;
824 x2 = x1 + width;
825 if (x2 > COLS) {
826 x2 = COLS;
827 x1 = x2 - width;
828 }
829 }
830
831 if (b2 > y2) {
832 /* move down */
833 y2 = b2;
834 y1 = y2 - height;
835 if (y1 < 0) {
836 y1 = 0;
837 y2 = height;
838 }
839 } else if (b1 < y1) {
840 /* move up */
841 y1 = b1;
842 y2 = y1 + width;
843 if (y2 > ROWS) {
844 y2 = ROWS;
845 y1 = y2 - height;
846 }
847 }
848
849 /* printf("(%d,%d) - (%d,%d) : (%d, %d) - (%d, %d)\n", x1, y1, x2, y2, a1, b1, a2, b2); */
850
851 /* no movement? */
852 if (disp_col == x1 && disp_row == y1)
853 return;
854
855 disp_col = x1;
856 disp_row = y1;
857}
858
859/**************************************************************************************/
860
861static void newline(void)
862{
863 curs_col = 0;
864 if (curs_row + 1 < ROWS)
865 curs_row++;
866 else {
867 memmove(vty_buf, vty_buf + COLS, COLS * (ROWS - 1));
868 memset(vty_buf + (ROWS - 1) * COLS, ' ', COLS);
869 }
870}
871
872void phone_putc(const char c)
873{
874 int i;
875
876 if (input_mode != -1) {
877 input_selected_char = -1;
878 terminate_input();
879 }
880
881 curs_disabled = 1;
882 update();
883
884 blink_time = BLINK_HZ;
885
886 switch (c) {
wdenkc4e854f2004-06-07 23:46:25 +0000887 case '\a': /* ignore bell */
wdenk20c98a62004-04-23 20:32:05 +0000888 case '\r': /* ignore carriage return */
wdenk3902d702004-04-15 18:22:41 +0000889 break;
890
891 case '\n': /* next line */
892 newline();
893 ensure_visible(curs_col, curs_row, 1, 1);
894 break;
895
wdenkc4e854f2004-06-07 23:46:25 +0000896 case 9: /* tab 8 */
wdenk3902d702004-04-15 18:22:41 +0000897 /* move to tab */
898 i = curs_col;
899 i |= 0x0008;
900 i &= ~0x0007;
901
902 if (i < COLS)
903 curs_col = i;
904 else
905 newline();
906
907 ensure_visible(curs_col, curs_row, 1, 1);
908 break;
909
910 case 8: /* backspace */
911 if (curs_col <= 0)
912 break;
913 curs_col--;
914
915 /* make sure that we see a couple of characters before */
916 if (curs_col > 4)
917 ensure_visible(curs_col - 4, curs_row, 4, 1);
918 else
919 ensure_visible(curs_col, curs_row, 1, 1);
920
921 break;
922
923 default: /* draw the char */
924 putchar_at_cursor(c);
925
926 /*
927 * check for newline
928 */
929 if (curs_col + 1 < COLS)
930 curs_col++;
931 else
932 newline();
933
934 ensure_visible(curs_col, curs_row, 1, 1);
935
936 break;
937 }
938
939 curs_disabled = 0;
940 blink_time = BLINK_HZ;
941 update();
942}
943
944/**************************************************************************************/
945
946static inline unsigned int kp_transfer(unsigned int val)
947{
948 unsigned int rx;
949 int b;
950
951 rx = 0; b = 8;
952 while (--b >= 0) {
953 KP_SPI_TXD(val & 0x80);
954 val <<= 1;
955 KP_SPI_CLK_TOGGLE();
956 KP_SPI_BIT_DELAY();
957 rx <<= 1;
958 if (KP_SPI_RXD())
959 rx |= 1;
960 KP_SPI_CLK_TOGGLE();
961 KP_SPI_BIT_DELAY();
962 }
963
964 return rx;
965}
966
967unsigned int kp_data_transfer(unsigned int val)
968{
969 KP_SPI_CLK(1);
970 KP_CS(0);
971 val = kp_transfer(val);
972 KP_CS(1);
973
974 return val;
975}
976
977unsigned int kp_get_col_mask(unsigned int row_mask)
978{
979 unsigned int val, col_mask;
980
981 val = 0x80 | (row_mask & 0x7F);
982 (void)kp_data_transfer(val);
wdenk6203e402004-04-18 10:13:26 +0000983#if CONFIG_NETPHONE_VERSION == 1
wdenk3902d702004-04-15 18:22:41 +0000984 col_mask = kp_data_transfer(val) & 0x0F;
wdenk6203e402004-04-18 10:13:26 +0000985#elif CONFIG_NETPHONE_VERSION == 2
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200986 col_mask = ((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pedat & 0x0f;
wdenk6203e402004-04-18 10:13:26 +0000987 /* XXX FUCK FUCK FUCK FUCK FUCK!!!! */
988 col_mask = ((col_mask & 0x08) >> 3) | /* BKBR1 */
989 ((col_mask & 0x04) << 1) | /* BKBR2 */
990 (col_mask & 0x02) | /* BKBR3 */
991 ((col_mask & 0x01) << 2); /* BKBR4 */
wdenk3902d702004-04-15 18:22:41 +0000992
wdenk6203e402004-04-18 10:13:26 +0000993#endif
wdenk3902d702004-04-15 18:22:41 +0000994 /* printf("col_mask(row_mask = 0x%x) -> col_mask = 0x%x\n", row_mask, col_mask); */
wdenk6203e402004-04-18 10:13:26 +0000995
wdenk3902d702004-04-15 18:22:41 +0000996 return col_mask;
997}
998
999/**************************************************************************************/
1000
1001static const int kp_scancodes[KP_ROWS * KP_COLS] = {
wdenkc4e854f2004-06-07 23:46:25 +00001002 KP_F1, KP_F3, KP_F4, KP_F2,
1003 KP_F6, KP_F8, KP_F9, KP_F7,
1004 KP_1, KP_3, KP_F11, KP_2,
1005 KP_4, KP_6, KP_F12, KP_5,
1006 KP_7, KP_9, KP_F13, KP_8,
wdenk3902d702004-04-15 18:22:41 +00001007 KP_STAR, KP_HASH, KP_F14, KP_0,
wdenkc4e854f2004-06-07 23:46:25 +00001008 KP_F5, KP_F15, KP_F16, KP_F10,
wdenk3902d702004-04-15 18:22:41 +00001009};
1010
1011static const int kp_repeats[KP_ROWS * KP_COLS] = {
1012 0, 1, 0, 0,
1013 0, 1, 1, 1,
1014 1, 1, 0, 1,
1015 1, 1, 0, 1,
1016 1, 1, 0, 1,
1017 1, 1, 0, 1,
1018 0, 0, 0, 1,
1019};
1020
1021static int kp_state = SCAN;
1022static int kp_last_col_mask;
1023static int kp_cur_row, kp_cur_col;
1024static int kp_scancode;
1025static int kp_stable;
1026static int kp_repeat;
1027static int kp_repeat_time;
1028static int kp_force_time;
1029static int kp_idle_time;
1030
1031static void kp_do_poll(void)
1032{
1033 unsigned int col_mask;
1034 int col;
1035
1036 switch (kp_state) {
1037 case SCAN:
1038 if (kp_idle_time > 0) {
1039 kp_idle_time -= PHONE_CONSOLE_POLL_HZ;
1040 if (kp_idle_time <= 0)
1041 scancode_action(KP_IDLE);
1042 }
1043
1044 col_mask = kp_get_col_mask(KP_ROWS_MASK);
1045 if (col_mask == KP_COLS_MASK)
1046 break; /* nothing */
1047 kp_last_col_mask = col_mask;
1048 kp_stable = 0;
1049 kp_state = SCAN_FILTER;
1050 break;
1051
1052 case SCAN_FILTER:
1053 col_mask = kp_get_col_mask(KP_ROWS_MASK);
1054 if (col_mask != kp_last_col_mask) {
1055 kp_state = SCAN;
1056 break;
1057 }
1058
1059 kp_stable += PHONE_CONSOLE_POLL_HZ;
1060 if (kp_stable < KP_STABLE_HZ)
1061 break;
1062
1063 kp_cur_row = 0;
1064 kp_stable = 0;
1065 kp_state = SCAN_COL;
1066
1067 (void)kp_get_col_mask(1 << kp_cur_row);
1068 break;
1069
1070 case SCAN_COL:
1071 col_mask = kp_get_col_mask(1 << kp_cur_row);
1072 if (col_mask == KP_COLS_MASK) {
1073 if (++kp_cur_row >= KP_ROWS) {
1074 kp_state = SCAN;
1075 break;
1076 }
1077 kp_get_col_mask(1 << kp_cur_row);
1078 break;
1079 }
1080 kp_last_col_mask = col_mask;
1081 kp_stable = 0;
1082 kp_state = SCAN_COL_FILTER;
1083 break;
1084
1085 case SCAN_COL_FILTER:
1086 col_mask = kp_get_col_mask(1 << kp_cur_row);
1087 if (col_mask != kp_last_col_mask || col_mask == KP_COLS_MASK) {
1088 kp_state = SCAN;
1089 break;
1090 }
1091
1092 kp_stable += PHONE_CONSOLE_POLL_HZ;
1093 if (kp_stable < KP_STABLE_HZ)
1094 break;
1095
1096 for (col = 0; col < KP_COLS; col++)
1097 if ((col_mask & (1 << col)) == 0)
1098 break;
1099 kp_cur_col = col;
1100 kp_state = PRESSED;
1101 kp_scancode = kp_scancodes[kp_cur_row * KP_COLS + kp_cur_col];
1102 kp_repeat = kp_repeats[kp_cur_row * KP_COLS + kp_cur_col];
1103
1104 if (kp_repeat)
1105 kp_repeat_time = KP_REPEAT_DELAY_HZ;
1106 kp_force_time = KP_FORCE_DELAY_HZ;
1107
1108 scancode_action(kp_scancode);
1109
1110 break;
1111
1112 case PRESSED:
1113 col_mask = kp_get_col_mask(1 << kp_cur_row);
1114 if (col_mask != kp_last_col_mask) {
1115 kp_state = SCAN;
1116 scancode_action(KP_RELEASE);
1117 kp_idle_time = KP_IDLE_DELAY_HZ;
1118 break;
1119 }
1120
1121 if (kp_repeat) {
1122 kp_repeat_time -= PHONE_CONSOLE_POLL_HZ;
1123 if (kp_repeat_time <= 0) {
1124 kp_repeat_time += KP_REPEAT_HZ;
1125 scancode_action(kp_scancode);
1126 }
1127 }
1128
1129 if (kp_force_time > 0) {
1130 kp_force_time -= PHONE_CONSOLE_POLL_HZ;
1131 if (kp_force_time <= 0)
1132 scancode_action(KP_FORCE);
1133 }
1134
1135 break;
1136 }
1137}
wdenk20c98a62004-04-23 20:32:05 +00001138
1139/**************************************************************************************/
1140
1141int drv_phone_is_idle(void)
1142{
1143 return kp_state == SCAN;
1144}