braille_console.c 7.3 KB

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