braille_console.c 7.3 KB

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