uboot/board/netphone/phone_console.c
<<
>>
Prefs
   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 <stdio_dev.h>
  41
  42#include <sed156x.h>
  43
  44/*************************************************************************************************/
  45
  46#define ROWS    24
  47#define COLS    80
  48
  49#define REFRESH_HZ              (CONFIG_SYS_HZ/50)      /* refresh every 20ms */
  50#define BLINK_HZ                (CONFIG_SYS_HZ/2)       /* cursor blink every 500ms */
  51
  52/*************************************************************************************************/
  53
  54#define DISPLAY_BACKLIT_PORT    ((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat
  55#define DISPLAY_BACKLIT_MASK    0x0010
  56
  57/*************************************************************************************************/
  58
  59#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 */
  64
  65#if CONFIG_NETPHONE_VERSION == 1
  66#define KP_SPI_RXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat)
  67#define KP_SPI_RXD_MASK 0x0008
  68
  69#define KP_SPI_TXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat)
  70#define KP_SPI_TXD_MASK 0x0004
  71
  72#define KP_SPI_CLK_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat)
  73#define KP_SPI_CLK_MASK 0x0001
  74#elif CONFIG_NETPHONE_VERSION == 2
  75#define KP_SPI_RXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
  76#define KP_SPI_RXD_MASK 0x00000008
  77
  78#define KP_SPI_TXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
  79#define KP_SPI_TXD_MASK 0x00000004
  80
  81#define KP_SPI_CLK_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
  82#define KP_SPI_CLK_MASK 0x00000002
  83#endif
  84
  85#define KP_CS_PORT      (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pedat)
  86#define KP_CS_MASK      0x00000010
  87
  88#define KP_SPI_RXD() (KP_SPI_RXD_PORT & KP_SPI_RXD_MASK)
  89
  90#define KP_SPI_TXD(x) \
  91        do { \
  92                if (x) \
  93                        KP_SPI_TXD_PORT |=  KP_SPI_TXD_MASK; \
  94                else \
  95                        KP_SPI_TXD_PORT &= ~KP_SPI_TXD_MASK; \
  96        } while(0)
  97
  98#define KP_SPI_CLK(x) \
  99        do { \
 100                if (x) \
 101                        KP_SPI_CLK_PORT |=  KP_SPI_CLK_MASK; \
 102                else \
 103                        KP_SPI_CLK_PORT &= ~KP_SPI_CLK_MASK; \
 104        } while(0)
 105
 106#define KP_SPI_CLK_TOGGLE() (KP_SPI_CLK_PORT ^= KP_SPI_CLK_MASK)
 107
 108#define KP_SPI_BIT_DELAY()      /* no delay */
 109
 110#define KP_CS(x) \
 111        do { \
 112                if (x) \
 113                        KP_CS_PORT |=  KP_CS_MASK; \
 114                else \
 115                        KP_CS_PORT &= ~KP_CS_MASK; \
 116        } while(0)
 117
 118#define KP_ROWS 7
 119#define KP_COLS 4
 120
 121#define KP_ROWS_MASK    ((1 << KP_ROWS) - 1)
 122#define KP_COLS_MASK    ((1 << KP_COLS) - 1)
 123
 124#define SCAN            0
 125#define SCAN_FILTER     1
 126#define SCAN_COL        2
 127#define SCAN_COL_FILTER 3
 128#define PRESSED         4
 129
 130#define KP_F1   0       /* leftmost dot (tab)   */
 131#define KP_F2   1       /* middle left dot      */
 132#define KP_F3   2       /* up                   */
 133#define KP_F4   3       /* middle right dot     */
 134#define KP_F5   4       /* rightmost dot        */
 135#define KP_F6   5       /* C                    */
 136#define KP_F7   6       /* left                 */
 137#define KP_F8   7       /* down                 */
 138#define KP_F9   8       /* right                */
 139#define KP_F10  9       /* enter                */
 140#define KP_F11  10      /* R                    */
 141#define KP_F12  11      /* save                 */
 142#define KP_F13  12      /* redial               */
 143#define KP_F14  13      /* speaker              */
 144#define KP_F15  14      /* unused               */
 145#define KP_F16  15      /* unused               */
 146
 147#define KP_RELEASE              -1      /* key depressed                                */
 148#define KP_FORCE                -2      /* key was pressed for more than force hz       */
 149#define KP_IDLE                 -3      /* key was released and idle                    */
 150
 151#define KP_1    '1'
 152#define KP_2    '2'
 153#define KP_3    '3'
 154#define KP_4    '4'
 155#define KP_5    '5'
 156#define KP_6    '6'
 157#define KP_7    '7'
 158#define KP_8    '8'
 159#define KP_9    '9'
 160#define KP_0    '0'
 161#define KP_STAR '*'
 162#define KP_HASH '#'
 163
 164/*************************************************************************************************/
 165
 166static int curs_disabled;
 167static int curs_col, curs_row;
 168static int disp_col, disp_row;
 169
 170static int width, height;
 171
 172/* the simulated vty buffer */
 173static char vty_buf[ROWS * COLS];
 174static char last_visible_buf[ROWS * COLS];      /* worst case */
 175static char *last_visible_curs_ptr;
 176static int last_visible_curs_rev;
 177static int blinked_state;
 178static int last_input_mode;
 179static int refresh_time;
 180static int blink_time;
 181static char last_fast_punct;
 182
 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 */
 201                "abc2",                                 /* 2 */
 202                "def3",                                 /* 3 */
 203                "ghi4",                                 /* 4 */
 204                "jkl5",                                 /* 5 */
 205                "mno6",                                 /* 6 */
 206                "pqrs7",                                /* 7 */
 207                "tuv8",                                 /* 8 */
 208                "wxyz9",                                /* 9 */
 209        }, {    /* capital */
 210                "ABC2",                                 /* 2 */
 211                "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
 241        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;
 253
 254        tab_indicator = 0;
 255}
 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        struct stdio_dev console_dev;
 329
 330        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 (stdio_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
 713        if (tab_indicator == 0) {
 714                sed156x_output_at(0, sed156x_text_height - 1, "\\t", 2);
 715                tab_indicator = 1;
 716        }
 717
 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;
 776}
 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 */
 806        x1 = disp_col;   y1 = disp_row;
 807        x2 = x1 + width; y2 = y1 + height;
 808        a1 = col;        b1 = row;
 809        a2 = a1 + dx;    b2 = b1 + dy;
 810
 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) {
 887                case '\a':              /* ignore bell            */
 888                case '\r':              /* ignore carriage return */
 889                        break;
 890
 891                case '\n':              /* next line */
 892                        newline();
 893                        ensure_visible(curs_col, curs_row, 1, 1);
 894                        break;
 895
 896                case 9: /* tab 8 */
 897                        /* 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);
 983#if CONFIG_NETPHONE_VERSION == 1
 984        col_mask = kp_data_transfer(val) & 0x0F;
 985#elif CONFIG_NETPHONE_VERSION == 2
 986        col_mask = ((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pedat & 0x0f;
 987        /* 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 */
 992
 993#endif
 994        /* printf("col_mask(row_mask = 0x%x) -> col_mask = 0x%x\n", row_mask, col_mask); */
 995
 996        return col_mask;
 997}
 998
 999/**************************************************************************************/
1000
1001static const int kp_scancodes[KP_ROWS * KP_COLS] = {
1002        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,
1007        KP_STAR, KP_HASH, KP_F14, KP_0,
1008        KP_F5,   KP_F15,  KP_F16, KP_F10,
1009};
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}
1138
1139/**************************************************************************************/
1140
1141int drv_phone_is_idle(void)
1142{
1143        return kp_state == SCAN;
1144}
1145