braille_console.c 8.0 KB

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