braille_console.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /*
  2. * Minimalistic braille device kernel support.
  3. *
  4. * By default, shows console messages on the braille device.
  5. * Pressing Insert switches to VC browsing.
  6. *
  7. * Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org>
  8. *
  9. * This program is free software ; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation ; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY ; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with the program ; if not, write to the Free Software
  21. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22. */
  23. #include <linux/autoconf.h>
  24. #include <linux/kernel.h>
  25. #include <linux/module.h>
  26. #include <linux/moduleparam.h>
  27. #include <linux/console.h>
  28. #include <linux/notifier.h>
  29. #include <linux/selection.h>
  30. #include <linux/vt_kern.h>
  31. #include <linux/consolemap.h>
  32. #include <linux/keyboard.h>
  33. #include <linux/kbd_kern.h>
  34. #include <linux/input.h>
  35. MODULE_AUTHOR("samuel.thibault@ens-lyon.org");
  36. MODULE_DESCRIPTION("braille device");
  37. MODULE_LICENSE("GPL");
  38. /*
  39. * Braille device support part.
  40. */
  41. /* Emit various sounds */
  42. static int sound;
  43. module_param(sound, bool, 0);
  44. MODULE_PARM_DESC(sound, "emit sounds");
  45. static void beep(unsigned int freq)
  46. {
  47. if (sound)
  48. kd_mksound(freq, HZ/10);
  49. }
  50. /* mini console */
  51. #define WIDTH 40
  52. #define BRAILLE_KEY KEY_INSERT
  53. static u16 console_buf[WIDTH];
  54. static int console_cursor;
  55. /* mini view of VC */
  56. static int vc_x, vc_y, lastvc_x, lastvc_y;
  57. /* show console ? (or show VC) */
  58. static int console_show = 1;
  59. /* pending newline ? */
  60. static int console_newline = 1;
  61. static int lastVC = -1;
  62. static struct console *braille_co;
  63. /* Very VisioBraille-specific */
  64. static void braille_write(u16 *buf)
  65. {
  66. static u16 lastwrite[WIDTH];
  67. unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c;
  68. u16 out;
  69. int i;
  70. if (!braille_co)
  71. return;
  72. if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf)))
  73. return;
  74. memcpy(lastwrite, buf, WIDTH * sizeof(*buf));
  75. #define SOH 1
  76. #define STX 2
  77. #define ETX 2
  78. #define EOT 4
  79. #define ENQ 5
  80. data[0] = STX;
  81. data[1] = '>';
  82. csum ^= '>';
  83. c = &data[2];
  84. for (i = 0; i < WIDTH; i++) {
  85. out = buf[i];
  86. if (out >= 0x100)
  87. out = '?';
  88. else if (out == 0x00)
  89. out = ' ';
  90. csum ^= out;
  91. if (out <= 0x05) {
  92. *c++ = SOH;
  93. out |= 0x40;
  94. }
  95. *c++ = out;
  96. }
  97. if (csum <= 0x05) {
  98. *c++ = SOH;
  99. csum |= 0x40;
  100. }
  101. *c++ = csum;
  102. *c++ = ETX;
  103. braille_co->write(braille_co, data, c - data);
  104. }
  105. /* Follow the VC cursor*/
  106. static void vc_follow_cursor(struct vc_data *vc)
  107. {
  108. vc_x = vc->vc_x - (vc->vc_x % WIDTH);
  109. vc_y = vc->vc_y;
  110. lastvc_x = vc->vc_x;
  111. lastvc_y = vc->vc_y;
  112. }
  113. /* Maybe the VC cursor moved, if so follow it */
  114. static void vc_maybe_cursor_moved(struct vc_data *vc)
  115. {
  116. if (vc->vc_x != lastvc_x || vc->vc_y != lastvc_y)
  117. vc_follow_cursor(vc);
  118. }
  119. /* Show portion of VC at vc_x, vc_y */
  120. static void vc_refresh(struct vc_data *vc)
  121. {
  122. u16 buf[WIDTH];
  123. int i;
  124. for (i = 0; i < WIDTH; i++) {
  125. u16 glyph = screen_glyph(vc,
  126. 2 * (vc_x + i) + vc_y * vc->vc_size_row);
  127. buf[i] = inverse_translate(vc, glyph, 1);
  128. }
  129. braille_write(buf);
  130. }
  131. /*
  132. * Link to keyboard
  133. */
  134. static int keyboard_notifier_call(struct notifier_block *blk,
  135. unsigned long code, void *_param)
  136. {
  137. struct keyboard_notifier_param *param = _param;
  138. struct vc_data *vc = param->vc;
  139. int ret = NOTIFY_OK;
  140. if (!param->down)
  141. return ret;
  142. switch (code) {
  143. case KBD_KEYCODE:
  144. if (console_show) {
  145. if (param->value == BRAILLE_KEY) {
  146. console_show = 0;
  147. beep(880);
  148. vc_maybe_cursor_moved(vc);
  149. vc_refresh(vc);
  150. ret = NOTIFY_STOP;
  151. }
  152. } else {
  153. ret = NOTIFY_STOP;
  154. switch (param->value) {
  155. case KEY_INSERT:
  156. beep(440);
  157. console_show = 1;
  158. lastVC = -1;
  159. braille_write(console_buf);
  160. break;
  161. case KEY_LEFT:
  162. if (vc_x > 0) {
  163. vc_x -= WIDTH;
  164. if (vc_x < 0)
  165. vc_x = 0;
  166. } else if (vc_y >= 1) {
  167. beep(880);
  168. vc_y--;
  169. vc_x = vc->vc_cols-WIDTH;
  170. } else
  171. beep(220);
  172. break;
  173. case KEY_RIGHT:
  174. if (vc_x + WIDTH < vc->vc_cols) {
  175. vc_x += WIDTH;
  176. } else if (vc_y + 1 < vc->vc_rows) {
  177. beep(880);
  178. vc_y++;
  179. vc_x = 0;
  180. } else
  181. beep(220);
  182. break;
  183. case KEY_DOWN:
  184. if (vc_y + 1 < vc->vc_rows)
  185. vc_y++;
  186. else
  187. beep(220);
  188. break;
  189. case KEY_UP:
  190. if (vc_y >= 1)
  191. vc_y--;
  192. else
  193. beep(220);
  194. break;
  195. case KEY_HOME:
  196. vc_follow_cursor(vc);
  197. break;
  198. case KEY_PAGEUP:
  199. vc_x = 0;
  200. vc_y = 0;
  201. break;
  202. case KEY_PAGEDOWN:
  203. vc_x = 0;
  204. vc_y = vc->vc_rows-1;
  205. break;
  206. default:
  207. ret = NOTIFY_OK;
  208. break;
  209. }
  210. if (ret == NOTIFY_STOP)
  211. vc_refresh(vc);
  212. }
  213. break;
  214. case KBD_POST_KEYSYM:
  215. {
  216. unsigned char type = KTYP(param->value) - 0xf0;
  217. if (type == KT_SPEC) {
  218. unsigned char val = KVAL(param->value);
  219. int on_off = -1;
  220. switch (val) {
  221. case KVAL(K_CAPS):
  222. on_off = vc_kbd_led(kbd_table + fg_console,
  223. VC_CAPSLOCK);
  224. break;
  225. case KVAL(K_NUM):
  226. on_off = vc_kbd_led(kbd_table + fg_console,
  227. VC_NUMLOCK);
  228. break;
  229. case KVAL(K_HOLD):
  230. on_off = vc_kbd_led(kbd_table + fg_console,
  231. VC_SCROLLOCK);
  232. break;
  233. }
  234. if (on_off == 1)
  235. beep(880);
  236. else if (on_off == 0)
  237. beep(440);
  238. }
  239. }
  240. case KBD_UNBOUND_KEYCODE:
  241. case KBD_UNICODE:
  242. case KBD_KEYSYM:
  243. /* Unused */
  244. break;
  245. }
  246. return ret;
  247. }
  248. static struct notifier_block keyboard_notifier_block = {
  249. .notifier_call = keyboard_notifier_call,
  250. };
  251. static int vt_notifier_call(struct notifier_block *blk,
  252. unsigned long code, void *_param)
  253. {
  254. struct vt_notifier_param *param = _param;
  255. struct vc_data *vc = param->vc;
  256. switch (code) {
  257. case VT_ALLOCATE:
  258. break;
  259. case VT_DEALLOCATE:
  260. break;
  261. case VT_WRITE:
  262. {
  263. unsigned char c = param->c;
  264. if (vc->vc_num != fg_console)
  265. break;
  266. switch (c) {
  267. case '\b':
  268. case 127:
  269. if (console_cursor > 0) {
  270. console_cursor--;
  271. console_buf[console_cursor] = ' ';
  272. }
  273. break;
  274. case '\n':
  275. case '\v':
  276. case '\f':
  277. case '\r':
  278. console_newline = 1;
  279. break;
  280. case '\t':
  281. c = ' ';
  282. /* Fallthrough */
  283. default:
  284. if (c < 32)
  285. /* Ignore other control sequences */
  286. break;
  287. if (console_newline) {
  288. memset(console_buf, 0, sizeof(console_buf));
  289. console_cursor = 0;
  290. console_newline = 0;
  291. }
  292. if (console_cursor == WIDTH)
  293. memmove(console_buf, &console_buf[1],
  294. (WIDTH-1) * sizeof(*console_buf));
  295. else
  296. console_cursor++;
  297. console_buf[console_cursor-1] = c;
  298. break;
  299. }
  300. if (console_show)
  301. braille_write(console_buf);
  302. else {
  303. vc_maybe_cursor_moved(vc);
  304. vc_refresh(vc);
  305. }
  306. break;
  307. }
  308. case VT_UPDATE:
  309. /* Maybe a VT switch, flush */
  310. if (console_show) {
  311. if (vc->vc_num != lastVC) {
  312. lastVC = vc->vc_num;
  313. memset(console_buf, 0, sizeof(console_buf));
  314. console_cursor = 0;
  315. braille_write(console_buf);
  316. }
  317. } else {
  318. vc_maybe_cursor_moved(vc);
  319. vc_refresh(vc);
  320. }
  321. break;
  322. }
  323. return NOTIFY_OK;
  324. }
  325. static struct notifier_block vt_notifier_block = {
  326. .notifier_call = vt_notifier_call,
  327. };
  328. /*
  329. * Called from printk.c when console=brl is given
  330. */
  331. int braille_register_console(struct console *console, int index,
  332. char *console_options, char *braille_options)
  333. {
  334. int ret;
  335. if (!console_options)
  336. /* Only support VisioBraille for now */
  337. console_options = "57600o8";
  338. if (braille_co)
  339. return -ENODEV;
  340. if (console->setup) {
  341. ret = console->setup(console, console_options);
  342. if (ret != 0)
  343. return ret;
  344. }
  345. console->flags |= CON_ENABLED;
  346. console->index = index;
  347. braille_co = console;
  348. register_keyboard_notifier(&keyboard_notifier_block);
  349. register_vt_notifier(&vt_notifier_block);
  350. return 0;
  351. }
  352. int braille_unregister_console(struct console *console)
  353. {
  354. if (braille_co != console)
  355. return -EINVAL;
  356. unregister_keyboard_notifier(&keyboard_notifier_block);
  357. unregister_vt_notifier(&vt_notifier_block);
  358. braille_co = NULL;
  359. return 0;
  360. }