]>
Commit | Line | Data |
---|---|---|
04a85b3b WD |
1 | /* |
2 | * (C) Copyright 2004 Intracom S.A. | |
3 | * Pantelis Antoniou <[email protected]> | |
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 | |
79fa88f3 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
04a85b3b WD |
16 | * GNU General Public License for more details. |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | * MA 02111-1307 USA | |
22 | */ | |
23 | ||
24 | /* | |
25 | * phone_console.c | |
26 | * | |
27 | * A phone based console | |
28 | * | |
29 | * Virtual display of 80x24 characters. | |
30 | * The actual display is much smaller and panned to show the virtual one. | |
31 | * Input is made by a numeric keypad utilizing the input method of | |
32 | * mobile phones. Sorry no T9 lexicons... | |
33 | * | |
34 | */ | |
35 | ||
36 | #include <common.h> | |
37 | ||
38 | #include <version.h> | |
39 | #include <linux/types.h> | |
40 | #include <devices.h> | |
41 | ||
42 | #include <sed156x.h> | |
43 | ||
44 | /*************************************************************************************************/ | |
45 | ||
46 | #define ROWS 24 | |
47 | #define COLS 80 | |
48 | ||
49 | #define REFRESH_HZ (CFG_HZ/50) /* refresh every 20ms */ | |
50 | #define BLINK_HZ (CFG_HZ/2) /* cursor blink every 500ms */ | |
51 | ||
52 | /*************************************************************************************************/ | |
53 | ||
54 | #define DISPLAY_BACKLIT_PORT ((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat | |
79fa88f3 | 55 | #define DISPLAY_BACKLIT_MASK 0x0010 |
04a85b3b WD |
56 | |
57 | /*************************************************************************************************/ | |
58 | ||
59 | #define KP_STABLE_HZ (CFG_HZ/100) /* stable for 10ms */ | |
60 | #define KP_REPEAT_DELAY_HZ (CFG_HZ/4) /* delay before repeat 250ms */ | |
61 | #define KP_REPEAT_HZ (CFG_HZ/20) /* repeat every 50ms */ | |
62 | #define KP_FORCE_DELAY_HZ (CFG_HZ/2) /* key was force pressed */ | |
63 | #define KP_IDLE_DELAY_HZ (CFG_HZ/2) /* key was released and idle */ | |
64 | ||
c26e454d | 65 | #if CONFIG_NETPHONE_VERSION == 1 |
79fa88f3 WD |
66 | #define KP_SPI_RXD_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat) |
67 | #define KP_SPI_RXD_MASK 0x0008 | |
04a85b3b | 68 | |
79fa88f3 WD |
69 | #define KP_SPI_TXD_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat) |
70 | #define KP_SPI_TXD_MASK 0x0004 | |
04a85b3b | 71 | |
79fa88f3 WD |
72 | #define KP_SPI_CLK_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat) |
73 | #define KP_SPI_CLK_MASK 0x0001 | |
c26e454d | 74 | #elif CONFIG_NETPHONE_VERSION == 2 |
79fa88f3 WD |
75 | #define KP_SPI_RXD_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat) |
76 | #define KP_SPI_RXD_MASK 0x00000008 | |
c26e454d | 77 | |
79fa88f3 WD |
78 | #define KP_SPI_TXD_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat) |
79 | #define KP_SPI_TXD_MASK 0x00000004 | |
c26e454d | 80 | |
79fa88f3 WD |
81 | #define KP_SPI_CLK_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat) |
82 | #define KP_SPI_CLK_MASK 0x00000002 | |
c26e454d | 83 | #endif |
04a85b3b WD |
84 | |
85 | #define KP_CS_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pedat) | |
86 | #define KP_CS_MASK 0x00000010 | |
87 | ||
88 | #define KP_SPI_RXD() (KP_SPI_RXD_PORT & KP_SPI_RXD_MASK) | |
89 | ||
90 | #define KP_SPI_TXD(x) \ | |
91 | do { \ | |
92 | if (x) \ | |
93 | KP_SPI_TXD_PORT |= KP_SPI_TXD_MASK; \ | |
94 | else \ | |
95 | KP_SPI_TXD_PORT &= ~KP_SPI_TXD_MASK; \ | |
96 | } while(0) | |
97 | ||
98 | #define KP_SPI_CLK(x) \ | |
99 | do { \ | |
100 | if (x) \ | |
101 | KP_SPI_CLK_PORT |= KP_SPI_CLK_MASK; \ | |
102 | else \ | |
103 | KP_SPI_CLK_PORT &= ~KP_SPI_CLK_MASK; \ | |
104 | } while(0) | |
105 | ||
106 | #define KP_SPI_CLK_TOGGLE() (KP_SPI_CLK_PORT ^= KP_SPI_CLK_MASK) | |
107 | ||
108 | #define KP_SPI_BIT_DELAY() /* no delay */ | |
109 | ||
110 | #define KP_CS(x) \ | |
111 | do { \ | |
112 | if (x) \ | |
113 | KP_CS_PORT |= KP_CS_MASK; \ | |
114 | else \ | |
115 | KP_CS_PORT &= ~KP_CS_MASK; \ | |
116 | } while(0) | |
117 | ||
79fa88f3 WD |
118 | #define KP_ROWS 7 |
119 | #define KP_COLS 4 | |
04a85b3b WD |
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 | |
79fa88f3 | 127 | #define SCAN_COL_FILTER 3 |
04a85b3b WD |
128 | #define PRESSED 4 |
129 | ||
79fa88f3 WD |
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 */ | |
04a85b3b WD |
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' | |
79fa88f3 WD |
161 | #define KP_STAR '*' |
162 | #define KP_HASH '#' | |
04a85b3b WD |
163 | |
164 | /*************************************************************************************************/ | |
165 | ||
166 | static int curs_disabled; | |
167 | static int curs_col, curs_row; | |
168 | static int disp_col, disp_row; | |
169 | ||
170 | static int width, height; | |
171 | ||
172 | /* the simulated vty buffer */ | |
173 | static char vty_buf[ROWS * COLS]; | |
174 | static char last_visible_buf[ROWS * COLS]; /* worst case */ | |
175 | static char *last_visible_curs_ptr; | |
176 | static int last_visible_curs_rev; | |
177 | static int blinked_state; | |
178 | static int last_input_mode; | |
179 | static int refresh_time; | |
180 | static int blink_time; | |
181 | static char last_fast_punct; | |
04a85b3b WD |
182 | |
183 | /*************************************************************************************************/ | |
184 | ||
185 | #define IM_SMALL 0 | |
186 | #define IM_CAPITAL 1 | |
187 | #define IM_NUMBER 2 | |
188 | ||
189 | static int input_mode; | |
190 | static char fast_punct; | |
191 | static int tab_indicator; | |
192 | static const char *fast_punct_list = ",.:;*"; | |
193 | ||
194 | static const char *input_mode_txt[] = { "abc", "ABC", "123" }; | |
195 | ||
196 | static const char *punct = ".,!;?'\"-()@/:_+&%*=<>$[]{}\\~^#|"; | |
197 | static const char *whspace = " 0\n"; | |
198 | /* per mode character select (for 2-9) */ | |
199 | static const char *digits_sel[2][8] = { | |
200 | { /* small */ | |
79fa88f3 | 201 | "abc2", /* 2 */ |
04a85b3b WD |
202 | "def3", /* 3 */ |
203 | "ghi4", /* 4 */ | |
204 | "jkl5", /* 5 */ | |
205 | "mno6", /* 6 */ | |
206 | "pqrs7", /* 7 */ | |
207 | "tuv8", /* 8 */ | |
208 | "wxyz9", /* 9 */ | |
79fa88f3 WD |
209 | }, { /* capital */ |
210 | "ABC2", /* 2 */ | |
04a85b3b WD |
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 | ||
223 | static void update(void); | |
224 | static void ensure_visible(int col, int row, int dx, int dy); | |
225 | ||
226 | static 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 | ||
04a85b3b WD |
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; | |
5cf91d6b WD |
253 | |
254 | tab_indicator = 0; | |
04a85b3b WD |
255 | } |
256 | ||
257 | /*****************************************************************************/ | |
258 | ||
259 | void phone_putc(const char c); | |
260 | ||
261 | /*****************************************************************************/ | |
262 | ||
263 | static int queued_char = -1; | |
264 | static int enabled = 0; | |
265 | ||
266 | /*****************************************************************************/ | |
267 | ||
268 | /* flush buffers */ | |
269 | int 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 | ||
285 | int 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 | ||
298 | void phone_puts(const char *s) | |
299 | { | |
300 | int count = strlen(s); | |
301 | ||
302 | while (count--) | |
303 | phone_putc(*s++); | |
304 | } | |
305 | ||
306 | int phone_tstc(void) | |
307 | { | |
308 | return queued_char >= 0 ? 1 : 0; | |
309 | } | |
310 | ||
311 | int 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 | ||
326 | int drv_phone_init(void) | |
327 | { | |
328 | device_t console_dev; | |
04a85b3b | 329 | |
04a85b3b WD |
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 (device_register(&console_dev) == 0) | |
344 | return 1; | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
349 | static int use_me; | |
350 | ||
351 | int drv_phone_use_me(void) | |
352 | { | |
353 | return use_me; | |
354 | } | |
355 | ||
356 | static void kp_do_poll(void); | |
357 | ||
358 | void 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 | ||
387 | static int last_scancode = -1; | |
388 | static int forced_scancode = 0; | |
389 | static int input_state = -1; | |
390 | static int input_scancode = -1; | |
391 | static int input_selected_char = -1; | |
392 | static char input_covered_char; | |
393 | ||
394 | static 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 | ||
400 | static char getchar_at_cursor(void) | |
401 | { | |
402 | return vty_buf[curs_row * COLS + curs_col]; | |
403 | } | |
404 | ||
405 | static void queue_input_char(char c) | |
406 | { | |
407 | if (c <= 0) | |
408 | return; | |
409 | ||
410 | queued_char = c; | |
411 | } | |
412 | ||
413 | static 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 | ||
430 | static 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 | ||
675 | static 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 */ | |
705 | static 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 | ||
5cf91d6b | 713 | if (tab_indicator == 0) { |
04a85b3b | 714 | sed156x_output_at(0, sed156x_text_height - 1, "\\t", 2); |
5cf91d6b WD |
715 | tab_indicator = 1; |
716 | } | |
04a85b3b WD |
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; | |
04a85b3b WD |
776 | } |
777 | ||
778 | /* ensure visibility; the trick is to minimize the screen movement */ | |
779 | static 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 */ | |
79fa88f3 | 806 | x1 = disp_col; y1 = disp_row; |
04a85b3b | 807 | x2 = x1 + width; y2 = y1 + height; |
79fa88f3 WD |
808 | a1 = col; b1 = row; |
809 | a2 = a1 + dx; b2 = b1 + dy; | |
04a85b3b WD |
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 | ||
861 | static 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 | ||
872 | void 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) { | |
79fa88f3 | 887 | case '\a': /* ignore bell */ |
5cf91d6b | 888 | case '\r': /* ignore carriage return */ |
04a85b3b WD |
889 | break; |
890 | ||
891 | case '\n': /* next line */ | |
892 | newline(); | |
893 | ensure_visible(curs_col, curs_row, 1, 1); | |
894 | break; | |
895 | ||
79fa88f3 | 896 | case 9: /* tab 8 */ |
04a85b3b WD |
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 | ||
946 | static 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 | ||
967 | unsigned 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 | ||
977 | unsigned 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); | |
c26e454d | 983 | #if CONFIG_NETPHONE_VERSION == 1 |
04a85b3b | 984 | col_mask = kp_data_transfer(val) & 0x0F; |
c26e454d WD |
985 | #elif CONFIG_NETPHONE_VERSION == 2 |
986 | col_mask = ((volatile immap_t *)CFG_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 */ | |
04a85b3b | 992 | |
c26e454d | 993 | #endif |
04a85b3b | 994 | /* printf("col_mask(row_mask = 0x%x) -> col_mask = 0x%x\n", row_mask, col_mask); */ |
c26e454d | 995 | |
04a85b3b WD |
996 | return col_mask; |
997 | } | |
998 | ||
999 | /**************************************************************************************/ | |
1000 | ||
1001 | static const int kp_scancodes[KP_ROWS * KP_COLS] = { | |
79fa88f3 WD |
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, | |
04a85b3b | 1007 | KP_STAR, KP_HASH, KP_F14, KP_0, |
79fa88f3 | 1008 | KP_F5, KP_F15, KP_F16, KP_F10, |
04a85b3b WD |
1009 | }; |
1010 | ||
1011 | static 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 | ||
1021 | static int kp_state = SCAN; | |
1022 | static int kp_last_col_mask; | |
1023 | static int kp_cur_row, kp_cur_col; | |
1024 | static int kp_scancode; | |
1025 | static int kp_stable; | |
1026 | static int kp_repeat; | |
1027 | static int kp_repeat_time; | |
1028 | static int kp_force_time; | |
1029 | static int kp_idle_time; | |
1030 | ||
1031 | static 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 | } | |
5cf91d6b WD |
1138 | |
1139 | /**************************************************************************************/ | |
1140 | ||
1141 | int drv_phone_is_idle(void) | |
1142 | { | |
1143 | return kp_state == SCAN; | |
1144 | } |