2019-05-20 09:19:11 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-04-30 00:54:51 -07:00
/*
* Minimalistic braille device kernel support .
*
* By default , shows console messages on the braille device .
* Pressing Insert switches to VC browsing .
*
* Copyright ( C ) Samuel Thibault < samuel . thibault @ ens - lyon . org >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/console.h>
# include <linux/notifier.h>
# include <linux/selection.h>
# include <linux/vt_kern.h>
# include <linux/consolemap.h>
# include <linux/keyboard.h>
# include <linux/kbd_kern.h>
# include <linux/input.h>
MODULE_AUTHOR ( " samuel.thibault@ens-lyon.org " ) ;
MODULE_DESCRIPTION ( " braille device " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* Braille device support part .
*/
/* Emit various sounds */
2012-01-13 09:32:20 +10:30
static bool sound ;
2008-04-30 00:54:51 -07:00
module_param ( sound , bool , 0 ) ;
MODULE_PARM_DESC ( sound , " emit sounds " ) ;
static void beep ( unsigned int freq )
{
if ( sound )
kd_mksound ( freq , HZ / 10 ) ;
}
/* mini console */
# define WIDTH 40
# define BRAILLE_KEY KEY_INSERT
static u16 console_buf [ WIDTH ] ;
static int console_cursor ;
/* mini view of VC */
static int vc_x , vc_y , lastvc_x , lastvc_y ;
/* show console ? (or show VC) */
static int console_show = 1 ;
/* pending newline ? */
static int console_newline = 1 ;
static int lastVC = - 1 ;
static struct console * braille_co ;
/* Very VisioBraille-specific */
static void braille_write ( u16 * buf )
{
static u16 lastwrite [ WIDTH ] ;
unsigned char data [ 1 + 1 + 2 * WIDTH + 2 + 1 ] , csum = 0 , * c ;
u16 out ;
int i ;
if ( ! braille_co )
return ;
if ( ! memcmp ( lastwrite , buf , WIDTH * sizeof ( * buf ) ) )
return ;
memcpy ( lastwrite , buf , WIDTH * sizeof ( * buf ) ) ;
# define SOH 1
# define STX 2
# define ETX 2
# define EOT 4
# define ENQ 5
data [ 0 ] = STX ;
data [ 1 ] = ' > ' ;
csum ^ = ' > ' ;
c = & data [ 2 ] ;
for ( i = 0 ; i < WIDTH ; i + + ) {
out = buf [ i ] ;
if ( out > = 0x100 )
out = ' ? ' ;
else if ( out = = 0x00 )
out = ' ' ;
csum ^ = out ;
if ( out < = 0x05 ) {
* c + + = SOH ;
out | = 0x40 ;
}
* c + + = out ;
}
if ( csum < = 0x05 ) {
* c + + = SOH ;
csum | = 0x40 ;
}
* c + + = csum ;
* c + + = ETX ;
braille_co - > write ( braille_co , data , c - data ) ;
}
/* Follow the VC cursor*/
static void vc_follow_cursor ( struct vc_data * vc )
{
2020-06-15 09:48:33 +02:00
vc_x = vc - > state . x - ( vc - > state . x % WIDTH ) ;
vc_y = vc - > state . y ;
lastvc_x = vc - > state . x ;
lastvc_y = vc - > state . y ;
2008-04-30 00:54:51 -07:00
}
/* Maybe the VC cursor moved, if so follow it */
static void vc_maybe_cursor_moved ( struct vc_data * vc )
{
2020-06-15 09:48:33 +02:00
if ( vc - > state . x ! = lastvc_x | | vc - > state . y ! = lastvc_y )
2008-04-30 00:54:51 -07:00
vc_follow_cursor ( vc ) ;
}
/* Show portion of VC at vc_x, vc_y */
static void vc_refresh ( struct vc_data * vc )
{
u16 buf [ WIDTH ] ;
int i ;
for ( i = 0 ; i < WIDTH ; i + + ) {
u16 glyph = screen_glyph ( vc ,
2 * ( vc_x + i ) + vc_y * vc - > vc_size_row ) ;
buf [ i ] = inverse_translate ( vc , glyph , 1 ) ;
}
braille_write ( buf ) ;
}
/*
* Link to keyboard
*/
static int keyboard_notifier_call ( struct notifier_block * blk ,
unsigned long code , void * _param )
{
struct keyboard_notifier_param * param = _param ;
struct vc_data * vc = param - > vc ;
int ret = NOTIFY_OK ;
if ( ! param - > down )
return ret ;
switch ( code ) {
case KBD_KEYCODE :
if ( console_show ) {
if ( param - > value = = BRAILLE_KEY ) {
console_show = 0 ;
beep ( 880 ) ;
vc_maybe_cursor_moved ( vc ) ;
vc_refresh ( vc ) ;
ret = NOTIFY_STOP ;
}
} else {
ret = NOTIFY_STOP ;
switch ( param - > value ) {
case KEY_INSERT :
beep ( 440 ) ;
console_show = 1 ;
lastVC = - 1 ;
braille_write ( console_buf ) ;
break ;
case KEY_LEFT :
if ( vc_x > 0 ) {
vc_x - = WIDTH ;
if ( vc_x < 0 )
vc_x = 0 ;
} else if ( vc_y > = 1 ) {
beep ( 880 ) ;
vc_y - - ;
vc_x = vc - > vc_cols - WIDTH ;
} else
beep ( 220 ) ;
break ;
case KEY_RIGHT :
if ( vc_x + WIDTH < vc - > vc_cols ) {
vc_x + = WIDTH ;
} else if ( vc_y + 1 < vc - > vc_rows ) {
beep ( 880 ) ;
vc_y + + ;
vc_x = 0 ;
} else
beep ( 220 ) ;
break ;
case KEY_DOWN :
if ( vc_y + 1 < vc - > vc_rows )
vc_y + + ;
else
beep ( 220 ) ;
break ;
case KEY_UP :
if ( vc_y > = 1 )
vc_y - - ;
else
beep ( 220 ) ;
break ;
case KEY_HOME :
vc_follow_cursor ( vc ) ;
break ;
case KEY_PAGEUP :
vc_x = 0 ;
vc_y = 0 ;
break ;
case KEY_PAGEDOWN :
vc_x = 0 ;
vc_y = vc - > vc_rows - 1 ;
break ;
default :
ret = NOTIFY_OK ;
break ;
}
if ( ret = = NOTIFY_STOP )
vc_refresh ( vc ) ;
}
break ;
case KBD_POST_KEYSYM :
{
unsigned char type = KTYP ( param - > value ) - 0xf0 ;
2021-06-13 14:30:32 -06:00
2008-04-30 00:54:51 -07:00
if ( type = = KT_SPEC ) {
unsigned char val = KVAL ( param - > value ) ;
int on_off = - 1 ;
switch ( val ) {
case KVAL ( K_CAPS ) :
2012-02-28 14:49:23 +00:00
on_off = vt_get_leds ( fg_console , VC_CAPSLOCK ) ;
2008-04-30 00:54:51 -07:00
break ;
case KVAL ( K_NUM ) :
2012-02-28 14:49:23 +00:00
on_off = vt_get_leds ( fg_console , VC_NUMLOCK ) ;
2008-04-30 00:54:51 -07:00
break ;
case KVAL ( K_HOLD ) :
2012-02-28 14:49:23 +00:00
on_off = vt_get_leds ( fg_console , VC_SCROLLOCK ) ;
2008-04-30 00:54:51 -07:00
break ;
}
if ( on_off = = 1 )
beep ( 880 ) ;
else if ( on_off = = 0 )
beep ( 440 ) ;
}
}
2020-11-20 12:34:35 -06:00
break ;
2008-04-30 00:54:51 -07:00
case KBD_UNBOUND_KEYCODE :
case KBD_UNICODE :
case KBD_KEYSYM :
/* Unused */
break ;
}
return ret ;
}
static struct notifier_block keyboard_notifier_block = {
. notifier_call = keyboard_notifier_call ,
} ;
static int vt_notifier_call ( struct notifier_block * blk ,
unsigned long code , void * _param )
{
struct vt_notifier_param * param = _param ;
struct vc_data * vc = param - > vc ;
2021-06-13 14:30:32 -06:00
2008-04-30 00:54:51 -07:00
switch ( code ) {
case VT_ALLOCATE :
break ;
case VT_DEALLOCATE :
break ;
case VT_WRITE :
{
unsigned char c = param - > c ;
2021-06-13 14:30:32 -06:00
2008-04-30 00:54:51 -07:00
if ( vc - > vc_num ! = fg_console )
break ;
switch ( c ) {
case ' \b ' :
case 127 :
if ( console_cursor > 0 ) {
console_cursor - - ;
console_buf [ console_cursor ] = ' ' ;
}
break ;
case ' \n ' :
case ' \v ' :
case ' \f ' :
case ' \r ' :
console_newline = 1 ;
break ;
case ' \t ' :
c = ' ' ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2008-04-30 00:54:51 -07:00
default :
if ( c < 32 )
/* Ignore other control sequences */
break ;
if ( console_newline ) {
memset ( console_buf , 0 , sizeof ( console_buf ) ) ;
console_cursor = 0 ;
console_newline = 0 ;
}
if ( console_cursor = = WIDTH )
memmove ( console_buf , & console_buf [ 1 ] ,
( WIDTH - 1 ) * sizeof ( * console_buf ) ) ;
else
console_cursor + + ;
console_buf [ console_cursor - 1 ] = c ;
break ;
}
if ( console_show )
braille_write ( console_buf ) ;
else {
vc_maybe_cursor_moved ( vc ) ;
vc_refresh ( vc ) ;
}
break ;
}
case VT_UPDATE :
/* Maybe a VT switch, flush */
if ( console_show ) {
if ( vc - > vc_num ! = lastVC ) {
lastVC = vc - > vc_num ;
memset ( console_buf , 0 , sizeof ( console_buf ) ) ;
console_cursor = 0 ;
braille_write ( console_buf ) ;
}
} else {
vc_maybe_cursor_moved ( vc ) ;
vc_refresh ( vc ) ;
}
break ;
}
return NOTIFY_OK ;
}
static struct notifier_block vt_notifier_block = {
. notifier_call = vt_notifier_call ,
} ;
/*
* Called from printk . c when console = brl is given
*/
int braille_register_console ( struct console * console , int index ,
char * console_options , char * braille_options )
{
int ret ;
2013-07-31 13:53:45 -07:00
2008-04-30 00:54:51 -07:00
if ( ! console_options )
/* Only support VisioBraille for now */
console_options = " 57600o8 " ;
if ( braille_co )
return - ENODEV ;
if ( console - > setup ) {
ret = console - > setup ( console , console_options ) ;
if ( ret ! = 0 )
return ret ;
}
console - > flags | = CON_ENABLED ;
console - > index = index ;
braille_co = console ;
2008-10-02 14:50:13 -07:00
register_keyboard_notifier ( & keyboard_notifier_block ) ;
register_vt_notifier ( & vt_notifier_block ) ;
2013-07-31 13:53:45 -07:00
return 1 ;
2008-04-30 00:54:51 -07:00
}
int braille_unregister_console ( struct console * console )
{
if ( braille_co ! = console )
return - EINVAL ;
2008-10-02 14:50:13 -07:00
unregister_keyboard_notifier ( & keyboard_notifier_block ) ;
unregister_vt_notifier ( & vt_notifier_block ) ;
2008-04-30 00:54:51 -07:00
braille_co = NULL ;
2013-07-31 13:53:45 -07:00
return 1 ;
2008-04-30 00:54:51 -07:00
}