This still needs lots of work before it can hit mainline... Signed-off-by: Greg Kroah-Hartman MAINTAINERS | 7 arch/arm/Kconfig | 1 drivers/Kconfig | 2 drivers/Makefile | 4 drivers/char/Makefile | 1 drivers/char/consolemap.c | 1 drivers/char/keyboard.c | 44 drivers/char/speakup/Kconfig | 211 ++ drivers/char/speakup/Makefile | 36 drivers/char/speakup/cvsversion.h | 1 drivers/char/speakup/dtload.c | 554 +++++++ drivers/char/speakup/dtload.h | 57 drivers/char/speakup/dtpc_reg.h | 132 + drivers/char/speakup/genmap.c | 204 ++ drivers/char/speakup/keyinfo.h | 119 + drivers/char/speakup/makemapdata.c | 156 ++ drivers/char/speakup/mapdata.h | 238 +++ drivers/char/speakup/mod_code.c | 25 drivers/char/speakup/serialio.h | 18 drivers/char/speakup/speakup.c | 2281 +++++++++++++++++++++++++++++++ drivers/char/speakup/speakup_acnt.h | 16 drivers/char/speakup/speakup_acntpc.c | 160 ++ drivers/char/speakup/speakup_acntsa.c | 184 ++ drivers/char/speakup/speakup_apollo.c | 195 ++ drivers/char/speakup/speakup_audptr.c | 201 ++ drivers/char/speakup/speakup_bns.c | 174 ++ drivers/char/speakup/speakup_decext.c | 205 ++ drivers/char/speakup/speakup_decpc.c | 242 +++ drivers/char/speakup/speakup_dectlk.c | 221 +++ drivers/char/speakup/speakup_drvcommon.c | 879 +++++++++++ drivers/char/speakup/speakup_dtlk.c | 219 ++ drivers/char/speakup/speakup_dtlk.h | 54 drivers/char/speakup/speakup_keyhelp.c | 294 +++ drivers/char/speakup/speakup_keypc.c | 189 ++ drivers/char/speakup/speakup_ltlk.c | 215 ++ drivers/char/speakup/speakup_sftsyn.c | 175 ++ drivers/char/speakup/speakup_spkout.c | 188 ++ drivers/char/speakup/speakup_txprt.c | 195 ++ drivers/char/speakup/speakupconf | 51 drivers/char/speakup/speakupmap.h | 64 drivers/char/speakup/speakupmap.map | 91 + drivers/char/speakup/spk_con_module.h | 43 drivers/char/speakup/spk_priv.h | 258 +++ drivers/char/speakup/synthlist.h | 54 drivers/char/vt.c | 16 include/linux/keyboard.h | 2 include/linux/speakup.h | 33 47 files changed, 8701 insertions(+), 9 deletions(-) --- --- /dev/null +++ gregkh-2.6/drivers/char/speakup/cvsversion.h @@ -0,0 +1 @@ +#define CVSVERSION " CVS: Wed Mar 2 20:22:02 EST 2005 " --- /dev/null +++ gregkh-2.6/drivers/char/speakup/dtload.c @@ -0,0 +1,554 @@ +/* + * This is the DECtalk PC firmware loader for the Linux kernel, version 1.0 + * + * Original 386BSD source: + * Copyright ( c ) 1996 Brian Buhrow + * + * Adapted for Linux: + * Copyright ( c ) 1997 Nicolas Pitre + * + * Adapted for speakup: + * Copyright ( c ) 2003 David Borowski + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * ( at your option ) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dtload.h" +#include "dtpc_reg.h" + +#define dt_delay(x) usleep(x) +int verbose = 0, intest = 0,infd = -1; +int image_len, total_paras; +int dt_stat, dma_state = 0, has_kernel = 0; +struct dos_reloc fixups[512]; +char *read_buff = NULL; +struct dos_exe_header header; +u_short iobase = 0x350; + +static inline int dt_getstatus( ) +{ + dt_stat = inb_p( iobase )|(inb_p( iobase+1 )<<8); + return dt_stat; +} + +static void dt_sendcmd( u_int cmd ) +{ + outb_p( cmd & 0xFF, iobase ); + outb_p( (cmd>>8) & 0xFF, iobase+1 ); +} + +static int dt_waitbit( int bit ) +{ + int timeout = 100; + while ( --timeout > 0 ) { + if( (dt_getstatus( ) & bit ) == bit ) return 1; + usleep( 1000 ); + } + return 0; +} + +static int dt_sendcmd_wait( u_int cmd, int bit ) +{ + int timeout = 1000; + outb_p( cmd & 0xFF, iobase ); + outb_p( (cmd>>8) & 0xFF, iobase+1 ); + while ( --timeout > 0 ) { + if( (dt_getstatus( ) & bit ) == bit ) return 1; + usleep( 1000 ); + } + return 0; +} + +static int dt_waitmode( int pattern ) +{ + int timeout = 1000; + while ( --timeout > 0 ) { + if( dt_getstatus( ) == pattern ) return 1; + usleep( 1000 ); + } + fprintf( stderr, "waitmode p=%x s = %x\n", pattern, dt_stat ); + return 0; +} + +static int dt_wait_dma( ) +{ + int timeout = 1000, state = dma_state; + if( !has_kernel ){ + dt_delay( 500 ); + return( dt_waitbit( STAT_dma_ready ) ); + } + if( ! dt_waitbit( STAT_dma_ready ) ) return 0; + while ( --timeout > 0 ) { + if( (dt_getstatus()&STAT_dma_state) == state ) return 1; + usleep( 1000 ); + } + dma_state = dt_getstatus( ) & STAT_dma_state; + return 1; +} + +dt_ctrl( u_int cmd ) +{ + while ( ! dt_waitbit( STAT_cmd_ready ) ) dt_delay( 100 ); + outb_p( 0, iobase+2 ); + outb_p( 0, iobase+3 ); + dt_getstatus( ); + dt_sendcmd( CMD_control|cmd ); + outb_p( 0, iobase+6 ); + dt_delay( 100 ); + dt_sendcmd( CMD_null ); + while ( ! dt_waitbit( STAT_cmd_ready ) ) dt_delay( 100 ); +} + +int dt_flush( void ) +{ + dt_ctrl( CTRL_flush ); + dt_waitbit( STAT_dma_ready ); + outb_p( DMA_sync, iobase+4 ); + outb_p( 0, iobase+4 ); + dma_state ^= STAT_dma_state; + while( dt_getstatus( ) & STAT_flushing ) dt_delay( 100 ); + return 0; +} + +static int dt_sendbuff( char *src, int len ) +{ + while( len-- ){ + if( ! dt_wait_dma( ) ) return -1; + if( ! (dt_getstatus( ) & STAT_rr_char) ) break; + outb_p( DMA_single_in, iobase+4 ); + outb_p( *src++, iobase+4 ); + dma_state ^= STAT_dma_state; + } + return 0; +} + +unsigned long dt_allocmem( unsigned long paras ) +{ + unsigned long addr; + if( ! dt_wait_dma( ) ) return 0; + outb_p( DMA_control, iobase+4 ); + outb_p( DT_MEM_ALLOC, iobase+4 ); + dma_state ^= STAT_dma_state; + if( ! dt_wait_dma( ) ) return 0; + outb_p( paras & 0xFF, iobase+4 ); + outb_p( (paras>>8) & 0xFF, iobase+4 ); + dma_state ^= STAT_dma_state; + if( ! dt_wait_dma( ) ) return 0; + addr = inb_p( iobase+4 ); + addr |= (inb_p( iobase+4 )<<8); + addr += (inb_p( iobase+4 )<<4); + addr += (inb_p( iobase+4 )<<12); + dma_state ^= STAT_dma_state; + return addr; +} + +static int testkernel( void ) +{ + dt_sendcmd( CMD_sync ); + if( ! dt_waitbit( STAT_cmd_ready ) ) return -10; + has_kernel = ( dt_stat&0x8000 ) ? 1 : 0; + if ( verbose ) printf( "testkernel got %x\n", dt_stat ); + if ( has_kernel ) return 0; + dt_delay( 100 ); + return 1; +} + +static int dt_loadmem( int addr, int len, char *src ) +{ + char c; + int l; + if ( verbose ) printf( "dt_loadmem: addr = %08X size = %d\n", addr, len ); + do { + l = len; + if ( l >= 0xc000 ) l = 0xc000; + len -= l; + if( ! dt_wait_dma( ) ) return -1; + outb_p( DMA_control, iobase+4 ); + outb_p( DT_LOAD_MEM, iobase+4 ); + dma_state ^= STAT_dma_state; + if( ! dt_wait_dma( ) ) return -2; + outb_p( addr & 0xFF, iobase+4 ); + outb_p( (addr>>8) & 0xFF, iobase+4 ); + outb_p( (addr>>16) & 0xFF, iobase+4 ); + outb_p( (addr>>24) & 0xFF, iobase+4 ); + outb_p( l & 0xFF, iobase+4 ); + outb_p( (l>>8) & 0xFF, iobase+4 ); + dma_state ^= STAT_dma_state; + if( ! dt_wait_dma( ) ) return -3; + addr += l; + while( l-- ){ + c = *src++; + outb_p( c, iobase+4 ); + } + dma_state ^= STAT_dma_state; + } while ( len > 0 ); + return 0; +} + +unsigned int loadfile ( char *filename ) +{ + int i, header_size; + unsigned int total_paras; + long fix; + infd = open ( filename, O_RDONLY ); + if ( infd == -1 ) { + perror ( "Opening file: " ); + return 0; + } + read ( infd, &header, sizeof ( struct dos_exe_header ) ); + if ( header.id != 0x5a4d ) { + fprintf ( stderr, "Invalid header file format\n" ); + fprintf ( stderr, "Want 0x5a4d, got 0x%x\n", header.id ); + return 0; + } + if ( header.relen > MAX_FIXUPS ) { + fprintf ( stderr, "Too many fixups\n" ); + return 0; + } + lseek ( infd, ( long ) header.reloc, SEEK_SET ); + read ( infd, fixups, sizeof ( struct dos_reloc ) * header.relen ); + header_size = header.hsize * 16; + lseek ( infd, ( long )header_size, SEEK_SET ); + image_len = ( ( header.pages-1 )*512 ) + ( header.rem- header_size ); + total_paras = ( image_len >> 4 ) + header.hmin + 16; + read ( infd, read_buff, image_len ); + close( infd ); + return total_paras; +} + +static int loadkernel( char *filename ) +{ + int segfix = 0x40, fix, i; + int ipval, csval; + if ( has_kernel ) return 0; + if ( !loadfile( filename ) ) return -1; + header.csval += segfix; + header.ssval += segfix; + if ( verbose ) { + printf ( "Loading kernel of %ld bytes ( %d relocs )\n", + image_len, header.relen ); + printf ( " cs:ip == %04x:%04x ss:sp == %04x:%04x\n", + header.csval, header.ipval, header.ssval, header.spval ); + } + for ( i = 0; i < header.relen; i++ ) { + fix = ( fixups[i].segment << 4 ) + fixups[i].offset; + ( *( unsigned int * ) &read_buff[fix] ) += segfix; + } + csval = header.csval; + ipval = header.ipval; + dt_sendcmd_wait( MODULE_reset, MODULE_init ); + dt_sendcmd( CMD_reset ); + if( dt_getstatus( ) == MODULE_self_test ){ + if( ! dt_waitmode( MODULE_init ) ) return -1; + } + if ( !dt_sendcmd_wait( CMD_reset, MODE_status ) ) return -2; + if ( !dt_sendcmd_wait( CMD_sync, MODE_error ) ) return -3; + if ( !dt_sendcmd_wait( CMD_reset, MODE_status ) ) return -4; + if ( verbose ) printf( "card is ready\n" ); + dt_sendcmd( CMD_dma ); + if( ! dt_waitbit( STAT_dma_ready ) ) return -5; + if( ( i = dt_loadmem( 0x00000400, image_len, read_buff ) ) ) { + fprintf( stderr, "kernel load failed, status %d\n", i ); + return -6; + } + dt_delay( 100 ); + /* the kernel is loaded, start it */ + if ( !dt_sendcmd_wait( CMD_reset, MODE_status ) ) return -7; + dt_sendcmd( CMD_dma+1 ); /**xxx**/ + dt_delay( 100 ); + if( ! dt_waitbit( STAT_dma_ready ) ) return-8; + outb_p( DMA_control, iobase+4 ); + outb_p( DT_START_TASK, iobase+4 ); + dt_delay( 100 ); + outb_p( ipval & 0xFF, iobase+4 ); + outb_p( (ipval>>8) & 0xFF, iobase+4 ); + outb_p( csval & 0xFF, iobase+4 ); + outb_p( (csval>>8) & 0xFF, iobase+4 ); + if( ! dt_waitmode( 0xc001 ) ) return -9; + if ( verbose ) { + printf( "done loading kernel\n" ); + } + return testkernel( ); +} + +int loaddict ( char *filename, char *name, int type ) +{ + int i, read_index, read_size, act_size; + unsigned short *index_fix, seg_fix; + unsigned long entries, index, dic_bytes, dic_addr; + unsigned int total_paras; + unsigned long param, l; + infd = open ( filename, O_RDONLY ); + if ( infd == -1 ) { + perror ( filename ); + return -1; + } +/* read in the entry count and the actual entry size excluding the + * index table ( which is entries * 4 ) ... */ + read ( infd, &entries, 4 ); + read ( infd, &dic_bytes, 4 ); + if ( verbose ) + printf ( "Loading %s dictionary of %lu entries, %lu bytes.\n", + name, entries, dic_bytes ); + total_paras = ( ( ( entries * 4 ) + dic_bytes ) >> 4 ) + 2; + if ( verbose ) + printf ( "Allocating %d paragraphs of free ram ...\n", total_paras ); + l = dt_allocmem( total_paras ); + if ( l == 0 ) { + perror ( "Error requesting memory from speech device" ); + return -1; + } + seg_fix = ( l >> 4 ) & 0xffff; + dic_addr = l; + index = entries; + index_fix = ( unsigned short * ) &read_buff[0]; + if ( verbose ) + printf ( "Index table starts at %lx\n", l ); + read_index = index*4; + act_size = read ( infd, read_buff, read_index ); + if ( act_size != read_index ) { + fprintf ( stderr, "\nError reading indexes\n" ); + fprintf ( stderr, " exp : %d act : %d\n", read_index * 4, act_size ); + return -1; + } + for ( i = 1; i < index * 2; i += 2 ) + index_fix[i] += seg_fix; + if( ( i = dt_loadmem( l, read_index, read_buff ) ) ) { + fprintf ( stderr, "\nError loading indexes at 0x%lX: i %d\n", + l, i ); + return -1; + } + l += read_index; +/* now, load up the dictionary bytes ... */ + if ( verbose ) + printf ( "Dictionary text starts at %lx\n", l ); + read_size = dic_bytes; + if ( ( act_size = read ( infd, read_buff, read_size ) ) != read_size ) { + fprintf ( stderr, "\nError reading dictionary text!\n" ); + fprintf ( stderr, "asked : %d actual : %d\n", act_size, read_size ); + return -1; + } + if( ( i = dt_loadmem( l, read_size, read_buff ) ) ) { + fprintf ( stderr, "\nError loading dictionary at 0x%lX: status %d\n", + l, i ); + return -1; + } + if( ! dt_wait_dma( ) ) return -1; + outb_p( DMA_control, iobase+4 ); + outb_p( DT_SET_DIC, iobase+4 ); + dma_state ^= STAT_dma_state; + if( ! dt_wait_dma( ) ) return -1; + l = dic_addr; + l = ((l << 12) & 0xFFFF0000) + (l & 0x0000000F); + outb_p( l & 0xFF, iobase+4 ); + outb_p( (l>>8) & 0xFF, iobase+4 ); + outb_p( (l>>16) & 0xFF, iobase+4 ); + outb_p( (l>>24) & 0xFF, iobase+4 ); + l = entries; + outb_p( l & 0xFF, iobase+4 ); + outb_p( (l>>8) & 0xFF, iobase+4 ); + outb_p( (l>>16) & 0xFF, iobase+4 ); + outb_p( (l>>24) & 0xFF, iobase+4 ); + l = type; + outb_p( l & 0xFF, iobase+4 ); + outb_p( (l>>8) & 0xFF, iobase+4 ); + dma_state ^= STAT_dma_state; + close ( infd ); + if ( verbose ) printf( "dictionary load complete\n" ); + return 0; +} + +int loadexe ( char *filename ) +{ + unsigned int load_addr = 0, seg_fix; + int i, read_size; + int ipval, csval; + long fix; + unsigned long total_paras; + total_paras = loadfile ( filename ); + if ( total_paras == 0 ) return -1; + load_addr = dt_allocmem( total_paras ); + if ( load_addr == 0 ) { + fprintf ( stderr, "Error allocating memory on card: " ); + return -1; + } + seg_fix = ( load_addr >> 4 ) & 0xffff; + if ( verbose ) { + printf ( "Loading %s %ld bytes ( %d relocs )\n", + filename, image_len, header.relen ); + printf ( "Allocating %ld bytes of free ram at %05x\n", + ( long ) header.hmin * 16, load_addr ); + printf ( "Total memory taken is %ld bytes\n", ( long ) total_paras * 16 ); + printf ( " cs:ip == %04x:%04x ss:sp == %04x:%04x\n", + header.csval + seg_fix, header.ipval, header.ssval + seg_fix, header.spval ); + } + for ( i = 0; i < header.relen; i++ ) { + fix = ( ( long ) fixups[i].segment << 4 ) + ( long ) fixups[i].offset; + ( *( unsigned int * ) &read_buff[fix] ) += seg_fix; + } + if( ( i = dt_loadmem( load_addr, image_len, read_buff ) ) ) { + fprintf ( stderr, "Error loading speech device at 0x%lX: status %d\n", + load_addr, i ); + return -1; + } + csval = header.csval + seg_fix; + ipval = header.ipval; + if( ! dt_wait_dma( ) ) return -1; + outb_p( DMA_control, iobase+4 ); + outb_p( DT_START_TASK, iobase+4 ); + dma_state ^= STAT_dma_state; + if( ! dt_wait_dma( ) ) return -1; + outb_p( ipval & 0xFF, iobase+4 ); + outb_p( (ipval>>8) & 0xFF, iobase+4 ); + outb_p( csval & 0xFF, iobase+4 ); + outb_p( (csval>>8) & 0xFF, iobase+4 ); + dma_state ^= STAT_dma_state; + return 0; +} + +void release_io( void ) +{ + ioperm( (long)iobase, 8, 0 ); + ioperm( (long)0x0080, 1, 0 ); + if ( read_buff ) free( read_buff ); +} + +parseparm( char *parm, char *value ) +{ + char *cp = parm+strlen( parm ); + while ( --cp > parm ) if ( *cp > ' ' ) break; + cp[1] = '\0'; + if ( !strcmp( parm, "io" ) ) { + long io = strtol( value, 0, 0 ); + if ( io >= 0x100 && io <= 0x350 ) { + iobase = (u_short)io; + return; + } + fprintf( stderr, "invalid io value %s\n", value ); + exit( 1 ); + } else if ( !strcmp( parm,"verbose" ) ) { + verbose = atoi( value ); + } +} + +do_test( void ) +{ + char buffer[512]; + int len; + dma_state = dt_getstatus( ) & STAT_dma_state; + while ( fgets( buffer, 510, stdin ) ) { + len = strlen( buffer ); + if ( len == 1 ) dt_flush( ); + else { + if ( buffer[len-1] == '\n' ) buffer[len-1] = '\013'; + dt_sendbuff( buffer, len ); + } + } + *buffer = '\013'; + dt_sendbuff( buffer, 1 ); +} + +int main ( int argc, char **argv ) +{ + char name[80], *cp; + char *dirname = 0, *confname = "dec_pc.conf"; + char *init_msg = "[:ra 360] dec pc initialized\011"; + FILE *confile; + struct stat statbuf; + int maxsize = 0, status = 0; + while ( --argc > 0 ) { + argv++; + if ( !strcmp( *argv, "-v" ) ) verbose = 1; + else if ( !strcmp( *argv, "-t" ) ) intest = 1; + else dirname = *argv; + } + if ( !dirname ) dirname = "/usr/local/lib/dec_pc"; + if ( chdir( dirname ) != 0 ) { + fprintf( stderr, "cannot chdir to %s\n", dirname ); + exit( 1 ); + } + if ( !( confile = fopen( confname, "r" ) ) ) { + fprintf( stderr, "could not open %s", confname ); + exit( 1 ); + } + while ( fgets( name, 80, confile ) ) { + cp = strchr( name, '\n' ); + if ( cp ) *cp = '\0'; + if ( ( cp = strchr( name, '=' ) ) ) { + *cp++ = '\0'; + parseparm( name, cp ); + continue; + } + if ( stat( name, &statbuf ) != 0 ) { + fprintf( stderr, "cannot stat %s\n", name ); + exit( 1 ); + } + if ( statbuf.st_size > maxsize ) maxsize = statbuf.st_size; + } + rewind( confile ); + if ( ioperm( (long)0x0080, 1, 1 ) || ioperm( (long)iobase, 8, 1 ) ) { + fprintf( stderr, "could not get ioperm\n" ); + exit( 1 ); + } + atexit( release_io ); + if ( testkernel( ) == 0 ) { + if ( intest ) do_test( ); + else fprintf( stderr, "kernel already loaded\n" ); + exit( 0 ); + } + read_buff = malloc( maxsize ); + if ( !read_buff ) { + fprintf( stderr, "cannot malloc %d bytes\n", maxsize ); + exit( 1 ); + } + while ( fgets( name, 80, confile ) && !status ) { + cp = strchr( name, '\n' ); + if ( cp ) *cp = '\0'; + if ( strchr( name, '=' ) ) continue; /* a parameter */ + if ( !( cp = strchr( name, '.' ) ) ) continue; + cp++; + if ( !strcmp ( cp, "dic" ) ) { + status = loaddict ( name, "primary", PRIMARY_DIC ); + } else if ( !strcmp ( cp, "dtu" ) ) { + status = loaddict ( name, "user", USER_DIC ); + } else if ( !strcmp ( cp, "dta" ) ) { + status = loaddict ( name, "abbreviation file", ABBREV_DIC ); + } else if ( !strcmp ( cp, "exe" ) ) { + status = loadexe ( name ); + } else if ( !strcmp ( cp, "sys" ) ) { + status = loadkernel ( name ); + } + } + if ( status ) fprintf( stderr, "status %d\n", status ); + fclose( confile ); + if ( status ) exit( status ); + dt_sendbuff( init_msg, strlen( init_msg ) ); + sleep( 1 ); + if ( intest ) do_test( ); + exit( 0 ); +} --- /dev/null +++ gregkh-2.6/drivers/char/speakup/dtload.h @@ -0,0 +1,57 @@ +/* + * This is the DECtalk PC firmware loader for the Linux kernel, version 1.0 + * + * Original 386BSD source: + * Copyright (c) 1996 Brian Buhrow + * + * Adapted for Linux: + * Copyright (c) 1997 Nicolas Pitre + * + * Adapted for speakup: + * Copyright (c) 2003 David Borowski + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define MAX_FIXUPS 512 /* maximum fixups per exe */ +/* + * msdos .exe files will look like ... + */ + +struct dos_exe_header { + unsigned short id; /* Linker's signature, must be 0x5a4d */ + unsigned short rem; /* length of image mod 512 */ + unsigned short pages; /* length of image in pages of 512 bytes */ + unsigned short relen; /* number of relocation items */ + unsigned short hsize; /* header size in paragraphs of 16 bytes */ + unsigned short hmin; /* min # of paragraphs above prog end */ + unsigned short hmax; + unsigned short ssval; + unsigned short spval; /* to be loaded in sp */ + unsigned short checksum; + unsigned short ipval; /* to be loaded in ip */ + unsigned short csval; /* segment offset to code */ + unsigned short reloc; /* location of relocation items */ + unsigned short ovrlay; /* overlay number */ +}; + +/* a dos relocation element looks like */ + +struct dos_reloc { + short int offset, segment; +}; --- /dev/null +++ gregkh-2.6/drivers/char/speakup/dtpc_reg.h @@ -0,0 +1,132 @@ +/* + * This is the DECtalk PC register constants (from DEC's DOS driver) + * + * Original code: + * Copyright (c) by Digital Equipment Corp. + * + * 386BSD DECtalk PC driver: + * Copyright (c) 1996 Brian Buhrow + * + * Linux DECtalk PC driver: + * Copyright (c) 1997 Nicolas Pitre + * + * speakup DECtalk PC driver: + * Copyright (c) 2003 David Borowski + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * port interface defs ... used by dtpc.c + */ + +#define MODULE_init 0x0dec /* module in boot code */ +#define MODULE_self_test 0x8800 /* module in self-test */ +#define MODULE_reset 0xffff /* reinit the whole module */ + +#define MODE_mask 0xf000 /* mode bits in high nibble */ +#define MODE_null 0x0000 +#define MODE_test 0x2000 /* in testing mode */ +#define MODE_status 0x8000 +#define STAT_int 0x0001 /* running in interrupt mode */ +#define STAT_tr_char 0x0002 /* character data to transmit */ +#define STAT_rr_char 0x0004 /* ready to receive char data */ +#define STAT_cmd_ready 0x0008 /* ready to accept commands */ +#define STAT_dma_ready 0x0010 /* dma command ready */ +#define STAT_digitized 0x0020 /* spc in digitized mode */ +#define STAT_new_index 0x0040 /* new last index ready */ +#define STAT_new_status 0x0080 /* new status posted */ +#define STAT_dma_state 0x0100 /* dma state toggle */ +#define STAT_index_valid 0x0200 /* indexs are valid */ +#define STAT_flushing 0x0400 /* flush in progress */ +#define STAT_self_test 0x0800 /* module in self test */ +#define MODE_ready 0xc000 /* module ready for next phase */ +#define READY_boot 0x0000 +#define READY_kernel 0x0001 +#define MODE_error 0xf000 + +#define CMD_mask 0xf000 /* mask for command nibble */ +#define CMD_null 0x0000 /* post status */ +#define CMD_control 0x1000 /* hard control command */ +#define CTRL_mask 0x0F00 /* mask off control nibble */ +#define CTRL_data 0x00FF /* madk to get data byte */ +#define CTRL_null 0x0000 /* null control */ +#define CTRL_vol_up 0x0100 /* increase volume */ +#define CTRL_vol_down 0x0200 /* decrease volume */ +#define CTRL_vol_set 0x0300 /* set volume */ +#define CTRL_pause 0x0400 /* pause spc */ +#define CTRL_resume 0x0500 /* resume spc clock */ +#define CTRL_resume_spc 0x0001 /* resume spc soft pause */ +#define CTRL_flush 0x0600 /* flush all buffers */ +#define CTRL_int_enable 0x0700 /* enable status change ints */ +#define CTRL_buff_free 0x0800 /* buffer remain count */ +#define CTRL_buff_used 0x0900 /* buffer in use */ +#define CTRL_speech 0x0a00 /* immediate speech change */ +#define CTRL_SP_voice 0x0001 /* voice change */ +#define CTRL_SP_rate 0x0002 /* rate change */ +#define CTRL_SP_comma 0x0003 /* comma pause change */ +#define CTRL_SP_period 0x0004 /* period pause change */ +#define CTRL_SP_rate_delta 0x0005 /* delta rate change */ +#define CTRL_SP_get_param 0x0006 /* return the desired parameter */ +#define CTRL_last_index 0x0b00 /* get last index spoken */ +#define CTRL_io_priority 0x0c00 /* change i/o priority */ +#define CTRL_free_mem 0x0d00 /* get free paragraphs on module */ +#define CTRL_get_lang 0x0e00 /* return bit mask of loaded languages */ +#define CMD_test 0x2000 /* self-test request */ +#define TEST_mask 0x0F00 /* isolate test field */ +#define TEST_null 0x0000 /* no test requested */ +#define TEST_isa_int 0x0100 /* assert isa irq */ +#define TEST_echo 0x0200 /* make data in == data out */ +#define TEST_seg 0x0300 /* set peek/poke segment */ +#define TEST_off 0x0400 /* set peek/poke offset */ +#define TEST_peek 0x0500 /* data out == *peek */ +#define TEST_poke 0x0600 /* *peek == data in */ +#define TEST_sub_code 0x00FF /* user defined test sub codes */ +#define CMD_id 0x3000 /* return software id */ +#define ID_null 0x0000 /* null id */ +#define ID_kernel 0x0100 /* kernel code executing */ +#define ID_boot 0x0200 /* boot code executing */ +#define CMD_dma 0x4000 /* force a dma start */ +#define CMD_reset 0x5000 /* reset module status */ +#define CMD_sync 0x6000 /* kernel sync command */ +#define CMD_char_in 0x7000 /* single character send */ +#define CMD_char_out 0x8000 /* single character get */ +#define CHAR_count_1 0x0100 /* one char in cmd_low */ +#define CHAR_count_2 0x0200 /* the second in data_low */ +#define CHAR_count_3 0x0300 /* the third in data_high */ +#define CMD_spc_mode 0x9000 /* change spc mode */ +#define CMD_spc_to_text 0x0100 /* set to text mode */ +#define CMD_spc_to_digit 0x0200 /* set to digital mode */ +#define CMD_spc_rate 0x0400 /* change spc data rate */ +#define CMD_error 0xf000 /* severe error */ + +enum { PRIMARY_DIC = 0, USER_DIC, COMMAND_DIC, ABBREV_DIC }; + +#define DMA_single_in 0x01 +#define DMA_single_out 0x02 +#define DMA_buff_in 0x03 +#define DMA_buff_out 0x04 +#define DMA_control 0x05 +#define DT_MEM_ALLOC 0x03 +#define DT_SET_DIC 0x04 +#define DT_START_TASK 0x05 +#define DT_LOAD_MEM 0x06 +#define DT_READ_MEM 0x07 +#define DT_DIGITAL_IN 0x08 +#define DMA_sync 0x06 +#define DMA_sync_char 0x07 --- /dev/null +++ gregkh-2.6/drivers/char/speakup/genmap.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include + +int get_define(void); + +#define MAXKEYS 512 +#define MAXKEYVAL 160 +#define HASHSIZE 101 +#define is_shift -3 +#define is_spk -2 +#define is_input -1 +typedef struct st_key_init t_key_init; +struct st_key_init { + char *name; + int value, shift; +}; +typedef struct st_key t_key; +struct st_key { + char *name; + t_key *next; + int value, shift; +}; +unsigned char key_data[MAXKEYVAL][16], *kp; + +#include "mapdata.h" +t_key key_table[MAXKEYS]; +t_key *extra_keys = key_table+HASHSIZE; +char buffer[256], filename[256]; +FILE *infile; +char delims[] = "\t\n "; +char *def_name, *def_val, *cp; +int map_ver = 119; /* an arbitrary number so speakup can check */ +int lc, shift_table[17]; +int max_states = 1, flags = 0; +/* flags reserved for later, maybe for individual console maps */ + +void open_input( char *name ) +{ + strcpy( filename, name ); + if ( ( infile = fopen( filename, "r" ) ) == 0 ) { + fprintf( stderr, "can't open %s, version %d\n", filename, LINUX_VERSION_CODE ); + exit( 1 ); + } + lc = 0; +} + +int +oops( char *msg, char *info ) +{ + if ( info == NULL ) info = " "; + fprintf( stderr, "error: file %s line %d\n", filename, lc ); + fprintf( stderr, "%s %s\n", msg, info ); + exit( 1 ); +} + +t_key *hash_name( char *name ) +{ + u_char *pn = (u_char *)name; + int hash = 0; + while ( *pn ) { + hash = ( hash * 17 ) & 0xfffffff; + if ( isupper( *pn ) ) *pn = tolower( *pn ); + hash += ( int )*pn; + pn++; + } + hash %= HASHSIZE; + return &key_table[hash]; +} + +t_key *find_key( char *name ) +{ + t_key *this = hash_name( name ); + while ( this ) { + if ( !strcmp( name, this->name ) ) return this; + this = this->next; + } + return this; +} + +t_key *add_key( char *name, int value, int shift ) +{ + t_key *this = hash_name( name ); + if ( extra_keys-key_table >= MAXKEYS ) + oops( "out of key table space, enlarge MAXKEYS", NULL ); + if ( this->name != NULL ) { + while ( this->next ) { + if ( !strcmp( name, this->name ) ) + oops( "attempt to add duplicate key", name ); + this = this->next; + } + this->next = extra_keys++; + this = this->next; + } + this->name = strdup( name ); + this->value = value; + this->shift = shift; + return this; +} + +int get_shift_value( int state ) +{ + int i; + for ( i = 0; shift_table[i] != state; i++ ) { + if ( shift_table[i] == -1 ) { + if ( i >= 16 ) + oops( "too many shift states", NULL ); + shift_table[i] = state; + max_states = i+1; + break; + } + } + return i; +} + +int +main( int argc, char *argv[] ) +{ + int value, shift_state, i, spk_val = 0, lock_val = 0; + int max_key_used = 0, num_keys_used = 0; + t_key *this; + t_key_init *p_init; +char *argpath, *argname, *argcopy; + + bzero( key_table, sizeof( key_table ) ); + bzero( key_data, sizeof( key_data ) ); + shift_table[0] = 0; + for ( i = 1; i <= 16; i++ ) shift_table[i] = -1; + if ( argc < 2 ) { + fputs( "usage: genmap filename\n", stderr ); + exit( 1 ); + } + for ( p_init = init_key_data; p_init->name[0] != '.'; p_init++ ) + add_key( p_init->name, p_init->value, p_init->shift ); + open_input( argv[1] ); + while ( fgets( buffer, 250, infile ) ) { + lc++; + value = shift_state = 0; + cp = strtok( buffer, delims ); + if ( *cp == '#' ) continue; + while ( cp ) { + if ( *cp == '=' ) break; + this = find_key( cp ); + if ( this == NULL ) + oops( "unknown key/modifier", cp ); + if ( this->shift == is_shift ) { + if ( value ) + oops( "modifiers must come first", cp ); + shift_state += this->value; + } else if ( this->shift == is_input ) + value = this->value; + else oops( "bad modifier or key", cp ); + cp = strtok( 0, delims ); + } + if ( !cp ) oops( "no = found", NULL ); + cp = strtok( 0, delims ); + if ( !cp ) oops( "no speakup function after =", NULL ); + this = find_key( cp ); + if ( this == NULL || this->shift != is_spk ) + oops( "invalid speakup function", cp ); + i = get_shift_value( shift_state ); + if ( key_data[value][i] ) { + while ( --cp > buffer ) + if ( !*cp ) *cp = ' '; + oops( "two functions on same key combination", cp ); + } + key_data[value][i] = (char)this->value; + if ( value > max_key_used ) max_key_used = value; + } + fclose( infile ); + this = find_key( "spk_key" ); + if ( this ) spk_val = this->value; + this = find_key( "spk_lock" ); + if ( this ) lock_val = this->value; + for ( lc = 1; lc <= max_key_used; lc++ ) { + kp = key_data[lc]; + if ( !memcmp( key_data[0], kp, 16 ) ) continue; + num_keys_used++; + for ( i = 0; i < max_states; i++ ) { + if ( kp[i] != spk_val&& kp[i] != lock_val ) continue; + shift_state = shift_table[i]; + if ( ( shift_state&16 ) ) continue; + shift_state = get_shift_value( shift_state+16 ); + kp[shift_state] = kp[i]; +/* fill in so we can process the key up, as spk bit will be set */ + } + } + printf( "\t%d, %d, %d,\n\t", map_ver, num_keys_used, max_states ); + for ( i = 0; i < max_states; i++ ) + printf( "%d, ", shift_table[i] ); + printf( "%d,", flags ); + for ( lc = 1; lc <= max_key_used; lc++ ) { + kp = key_data[lc]; + if ( !memcmp( key_data[0], kp, 16 ) ) continue; + printf( "\n\t%d,", lc ); + for ( i = 0; i < max_states; i++ ) + printf( " %d,", (unsigned int)kp[i] ); + } + printf( "\n\t0, %d\n", map_ver ); + exit( 0 ); +} --- /dev/null +++ gregkh-2.6/drivers/char/speakup/Kconfig @@ -0,0 +1,211 @@ +menu "Speakup console speech" +config SPEAKUP + tristate "Build speakup console speech" + depends on !PPC64 + ---help--- + + This is the Speakup screen reader. Think of it as a + video console for blind people. If built in to the + kernel, it can speak evrything on the text console from + boot up to shutdown. For more information on Speakup, + point your browser at http://www.linux-speakup.org/. + There is also a mailing list at the above url that you + can subscribe to. + + Supported synthesizers are accent sa, accent pc, appollo + II., Auddapter, Braille 'n Speak, Dectalk external + (old), Dectalk PC (full length isa board), Dectalk + express, Doubletalk, Doubletalk LT or Litetalk, + Keynote + Gold internal PC, software synthesizers, Speakout, and transport. + + Speakup can either be built in or compiled as a module + by answering y or m. If you answer y here, then you + must answer either y or m to at least one of the + synthesizer drivers below. If you answer m here, then + the synthesizer drivers below can only be built as + modules. + + These drivers are not standalone drivers, but must be + used in conjunction with Speakup. Think of them as + video cards for blind people. + + + The Dectalk pc driver can only be built as a module, and + requires software to be pre-loaded on to the card before + the module can be loaded. See the decpc choice below + for more details. + + If you are not a blind person, or don't have access to + one of the listed synthesizers, you should say n. + +config SPEAKUP_ACNTSA + depends on SPEAKUP + tristate "Accent SA, acntsa" + ---help--- + + This is the Speakup driver for the accent sa + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_ACNTPC + depends on SPEAKUP + tristate "Accent PC, acntpc" + ---help--- + + This is the Speakup driver for the accent pc + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_APOLLO + depends on SPEAKUP + tristate "Apollo, apollo" + ---help--- + + This is the Speakup driver for the Apollo II + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_AUDPTR + depends on SPEAKUP + tristate "Audapter, audptr" + ---help--- + + This is the Speakup driver for the Audapter synthesizer. + You can say y to build it into the kernel, or m to + build it as a module. See the configuration help on the + Speakup choice above for more info. + +config SPEAKUP_BNS + depends on SPEAKUP + tristate "Braille 'n' Speak, bns" + ---help--- + + This is the Speakup driver for the Braille 'n' Speak + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_DECTLK + depends on SPEAKUP + tristate "DECtalk Express, dectlk" + ---help--- + + This is the Speakup driver for the DecTalk Express + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_DECEXT + depends on SPEAKUP + tristate "DECtalk External (old), decext" + ---help--- + + This is the Speakup driver for the DecTalk External + (old) synthesizer. You can say y to build it into the + kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more + info. + +config SPEAKUP_DECPC + depends on SPEAKUP + tristate "DECtalk PC (big ISA card), decpc" + ---help--- + + This is the Speakup driver for the DecTalk PC (full + length ISA) synthesizer. You can say m to build it as + a module. See the configuration help on the Speakup + choice above for more info. + + In order to use the DecTalk PC driver, you must download + the dec_pc.tgz file from linux-speakup.org. It is in + the pub/linux/goodies directory. The dec_pc.tgz file + contains the software which must be pre-loaded on to the + DecTalk PC board in order to use it with this driver. + This driver must be built as a module, and can not be + loaded until the file system is mounted and the DecTalk + PC software has been pre-loaded on to the board. + + See the README file in the dec_pc.tgz file for more + details. + +config SPEAKUP_DTLK + depends on SPEAKUP + tristate "DoubleTalk PC, dtlk" + ---help--- + + This is the Speakup driver for the internal DoubleTalk + PC synthesizer. You can say y to build it into the + kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more + info. + +config SPEAKUP_KEYPC + depends on SPEAKUP + tristate "Keynote Gold PC, keypc" + ---help--- + + This is the Speakup driver for the Keynote Gold + PC synthesizer. You can say y to build it into the + kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more + info. + +config SPEAKUP_LTLK + depends on SPEAKUP + tristate "DoubleTalk LT or LiteTalk, ltlk" +---help--- + + This is the Speakup driver for the LiteTalk/DoubleTalk + LT synthesizer. You can say y to build it into the + kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more + info. + +config SPEAKUP_SFTSYN + depends on SPEAKUP + tristate "Software synthesizers, sftsyn" +---help--- + + This is the software synthesizer device node. It will + register a device /dev/sftsyn which midware programs + and speech + daemons may open and read to provide kernel output to + software synths + such as festival, flite, tuxtalk and so forth. You + can select 'y' or + 'm' to have it built-in to the kernel or loaded as a module. + +config SPEAKUP_SPKOUT + depends on SPEAKUP + tristate "Speak Out, spkout" + ---help--- + + This is the Speakup driver for the Speakout synthesizer. + You can say y to build it into the kernel, or m to + build it as a module. See the configuration help on the + Speakup choice above for more info. + +config SPEAKUP_TXPRT + depends on SPEAKUP + tristate "Transport, txprt" + ---help--- + + This is the Speakup driver for the Transport + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +if SPEAKUP != n + comment 'Enter the 3 to 6 character keyword from the list above, or none for no default synthesizer on boot up.' + depends on SPEAKUP +endif + +config SPEAKUP_DEFAULT + string "Choose Default synthesizer for Speakup" + default "none" + +endmenu --- /dev/null +++ gregkh-2.6/drivers/char/speakup/keyinfo.h @@ -0,0 +1,119 @@ +/* spk_priv.h + review functions for the speakup screen review package. + originally written by: Kirk Reiser and Andy Berdan. + + extensively modified by David Borowski. + + Copyright (C ) 1998 Kirk Reiser. + Copyright (C ) 2003 David Borowski. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option ) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +enum { /* var_ids */ + VERSION = 0, SYNTH, SILENT, SYNTH_DIRECT, + KEYMAP, CHARS, + PUNC_SOME, PUNC_MOST, PUNC_ALL, + DELIM, REPEATS, EXNUMBER, + DELAY, TRIGGER, JIFFY, FULL, /* all timers must be together */ + BLEEP_TIME, CURSOR_TIME, BELL_POS, +SAY_CONTROL, SAY_WORD_CTL, NO_INTERRUPT, KEY_ECHO, + SPELL_DELAY, PUNC_LEVEL, READING_PUNC, + ATTRIB_BLEEP, BLEEPS, + RATE, PITCH, VOL, TONE, PUNCT, VOICE, FREQ, LANG, + CAPS_START, CAPS_STOP, + MAXVARS +}; + +#define FIRST_SYNTH_VAR RATE +/* 0 is reserved for no remap */ +#define SPEAKUP_GOTO 0x01 +#define SPEECH_KILL 0x02 +#define SPEAKUP_QUIET 0x03 +#define SPEAKUP_CUT 0x04 +#define SPEAKUP_PASTE 0x05 +#define SAY_FIRST_CHAR 0x06 +#define SAY_LAST_CHAR 0x07 +#define SAY_CHAR 0x08 +#define SAY_PREV_CHAR 0x09 +#define SAY_NEXT_CHAR 0x0a +#define SAY_WORD 0x0b +#define SAY_PREV_WORD 0x0c +#define SAY_NEXT_WORD 0x0d +#define SAY_LINE 0x0e +#define SAY_PREV_LINE 0x0f +#define SAY_NEXT_LINE 0x10 +#define TOP_EDGE 0x11 +#define BOTTOM_EDGE 0x12 +#define LEFT_EDGE 0x13 +#define RIGHT_EDGE 0x14 +#define SPELL_PHONETIC 0x15 +#define SPELL_WORD 0x16 +#define SAY_SCREEN 0x17 +#define SAY_POSITION 0x18 +#define SAY_ATTRIBUTES 0x19 +#define SPEAKUP_OFF 0x1a +#define SPEAKUP_PARKED 0x1b +#define SAY_LINE_INDENT 0x1c +#define SAY_FROM_TOP 0x1d +#define SAY_TO_BOTTOM 0x1e +#define SAY_FROM_LEFT 0x1f +#define SAY_TO_RIGHT 0x20 +#define SAY_CHAR_NUM 0x21 +#define EDIT_SOME 0x22 +#define EDIT_MOST 0x23 +#define SAY_PHONETIC_CHAR 0x24 +#define EDIT_DELIM 0x25 +#define EDIT_REPEAT 0x26 +#define EDIT_EXNUM 0x27 +#define SET_WIN 0x28 +#define CLEAR_WIN 0x29 +#define ENABLE_WIN 0x2a +#define SAY_WIN 0x2b +#define SPK_LOCK 0x2c +#define SPEAKUP_HELP 0x2d +#define TOGGLE_CURSORING 0x2e +#define SPKUP_MAX_FUNC 0x2f /* one greater than the last func handler */ + +#define SPK_KEY 0x80 +#define FIRST_EDIT_BITS 0x22 + +#define FIRST_SET_VAR SPELL_DELAY +#define VAR_START 0x40 /* increase if adding more than 0x3f functions */ + +/* keys for setting variables, must be ordered same as the enum for var_ids */ +/* with dec being even and inc being 1 greater */ +#define SPELL_DELAY_DEC VAR_START+0 +#define SPELL_DELAY_INC SPELL_DELAY_DEC+1 +#define PUNC_LEVEL_DEC SPELL_DELAY_DEC+2 +#define PUNC_LEVEL_INC PUNC_LEVEL_DEC+1 +#define READING_PUNC_DEC PUNC_LEVEL_DEC+2 +#define READING_PUNC_INC READING_PUNC_DEC+1 +#define ATTRIB_BLEEP_DEC READING_PUNC_DEC+2 +#define ATTRIB_BLEEP_INC ATTRIB_BLEEP_DEC+1 +#define BLEEPS_DEC ATTRIB_BLEEP_DEC+2 +#define BLEEPS_INC BLEEPS_DEC+1 +#define RATE_DEC BLEEPS_DEC+2 +#define RATE_INC RATE_DEC+1 +#define PITCH_DEC RATE_DEC+2 +#define PITCH_INC PITCH_DEC+1 +#define VOL_DEC PITCH_DEC+2 +#define VOL_INC VOL_DEC+1 +#define TONE_DEC VOL_DEC+2 +#define TONE_INC TONE_DEC+1 +#define PUNCT_DEC TONE_DEC+2 +#define PUNCT_INC PUNCT_DEC+1 +#define VOICE_DEC PUNCT_DEC+2 +#define VOICE_INC VOICE_DEC+1 --- /dev/null +++ gregkh-2.6/drivers/char/speakup/Makefile @@ -0,0 +1,36 @@ +# +# Makefile for the speakup speech output system. +# + +obj-m = speakup_keyhelp.o +speakupmain-objs := speakup.o speakup_drvcommon.o + +obj-$(CONFIG_SPEAKUP) += speakupmain.o +obj-$(CONFIG_SPEAKUP_ACNTPC) += speakup_acntpc.o +obj-$(CONFIG_SPEAKUP_ACNTSA) += speakup_acntsa.o +obj-$(CONFIG_SPEAKUP_APOLLO) += speakup_apollo.o +obj-$(CONFIG_SPEAKUP_AUDPTR) += speakup_audptr.o +obj-$(CONFIG_SPEAKUP_BNS) += speakup_bns.o +obj-$(CONFIG_SPEAKUP_DECEXT) += speakup_decext.o +obj-$(CONFIG_SPEAKUP_DECPC) += speakup_decpc.o +obj-$(CONFIG_SPEAKUP_DECTLK) += speakup_dectlk.o +obj-$(CONFIG_SPEAKUP_DTLK) += speakup_dtlk.o +obj-$(CONFIG_SPEAKUP_KEYPC) += speakup_keypc.o +obj-$(CONFIG_SPEAKUP_LTLK) += speakup_ltlk.o +obj-$(CONFIG_SPEAKUP_SFTSYN) += speakup_sftsyn.o +obj-$(CONFIG_SPEAKUP_SPKOUT) += speakup_spkout.o +obj-$(CONFIG_SPEAKUP_TXPRT) += speakup_txprt.o + +#speakupmain.o:speakup.o speakup_drvcommon.o +# ld -r -o speakupmain.o speakup.o speakup_drvcommon.o + +$(obj)/speakupmap.h: $(src)/speakupmap.map $(src)/genmap + $(src)/genmap $(src)/speakupmap.map >$@ + +$(obj)/mapdata.h: $(src)/keyinfo.h $(src)/makemapdata + $(src)/makemapdata >$@ + +$(obj)/genmap: $(obj)/mapdata.h + +HOSTCFLAGS := -Iinclude -I/usr/include +hostprogs-y := makemapdata genmap --- /dev/null +++ gregkh-2.6/drivers/char/speakup/makemapdata.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include + +int get_define(void); + +#define MAXKEYS 512 +#define MAXKEYVAL 160 +#define HASHSIZE 101 +#define is_shift -3 +#define is_spk -2 +#define is_input -1 +typedef struct st_key t_key; +struct st_key { + char *name; + t_key *next; + int value, shift; +}; + +t_key key_table[MAXKEYS]; +t_key *extra_keys = key_table+HASHSIZE; +char buffer[256], filename[256]; +FILE *infile; +char delims[] = "\t\n "; +char *dir_name, *def_name, *def_val, *cp; +int lc; + +void open_input( char *name ) +{ + sprintf( filename, "%s/%s", dir_name, name ); + if ( ( infile = fopen( filename, "r" ) ) == 0 ) { + fprintf( stderr, "can't open %s\n", filename ); + exit( 1 ); + } + lc = 0; +} + +int +oops( char *msg, char *info ) +{ + if ( info == NULL ) info = " "; + fprintf( stderr, "error: file %s line %d\n", filename, lc ); + fprintf( stderr, "%s %s\n", msg, info ); + exit( 1 ); +} + +int get_define( ) +{ + while ( fgets( buffer, 250, infile ) ) { + lc++; + if ( strncmp( buffer, "#define", 7 ) ) continue; + strtok( buffer, delims ); + def_name = strtok( 0, delims ); + def_val = strtok( 0, delims ); + if ( def_val != NULL ) return 1; + } + fclose( infile ); + infile = 0; + return 0; +} + +t_key *hash_name( char *name ) +{ + u_char *pn = (u_char *)name; + int hash = 0; + while ( *pn ) { + hash = ( hash * 17 ) & 0xfffffff; + if ( isupper( *pn ) ) *pn = tolower( *pn ); + hash += ( int )*pn; + pn++; + } + hash %= HASHSIZE; + return &key_table[hash]; +} + +t_key *find_key( char *name ) +{ + t_key *this = hash_name( name ); + while ( this ) { + if ( !strcmp( name, this->name ) ) return this; + this = this->next; + } + return this; +} + +t_key *add_key( char *name, int value, int shift ) +{ + t_key *this = hash_name( name ); + if ( extra_keys-key_table >= MAXKEYS ) + oops( "out of key table space, enlarge MAXKEYS", NULL ); + if ( this->name != NULL ) { + while ( this->next ) { + if ( !strcmp( name, this->name ) ) + oops( "attempt to add duplicate key", name ); + this = this->next; + } + this->next = extra_keys++; + this = this->next; + } + this->name = strdup( name ); + this->value = value; + this->shift = shift; + return this; +} + +int +main( int argc, char *argv[] ) +{ + int value, i; + t_key *this; + dir_name = getenv( "TOPDIR" ); + if ( !dir_name ) dir_name = "/usr/src/linux"; + bzero( key_table, sizeof( key_table ) ); + add_key( "shift", 1, is_shift ); + add_key( "altgr", 2, is_shift ); + add_key( "ctrl", 4, is_shift ); + add_key( "alt", 8, is_shift ); + add_key( "spk", 16, is_shift ); + add_key( "double", 32, is_shift ); + open_input( "include/linux/input.h" ); + while ( get_define( ) ) { + if ( strncmp( def_name, "KEY_", 4 ) ) continue; + value = atoi( def_val ); + if ( value > 0 && value < MAXKEYVAL ) + add_key( def_name, value, is_input ); + } + open_input( "drivers/char/speakup/keyinfo.h" ); + while ( get_define( ) ) { + if ( strlen( def_val ) > 5 ) { + if ( !( cp = strchr( def_val, '+' ) ) ) continue; + *cp++ = '\0'; + this = find_key( def_val ); + if ( !this || *cp < '0' || *cp > '9' ) continue; + value = this->value+atoi( cp ); + } else if ( !strncmp( def_val, "0x", 2 ) ) + sscanf( def_val+2, "%x", &value ); + else if ( *def_val >= '0' && *def_val <= '9' ) + value = atoi( def_val ); + else continue; + add_key( def_name, value, is_spk ); + } + printf( "t_key_init init_key_data[] = {\n" ); + for ( i = 0; i < HASHSIZE; i++ ) { + this = &key_table[i]; + if ( !this->name ) continue; + do { + printf( "\t\"%s\", %d, %d,\n", this->name, this->value, this->shift ); + this = this->next; + } while ( this ); + } + printf( "\t\".\", 0, 0\n};\n" ); + exit( 0 ); +} --- /dev/null +++ gregkh-2.6/drivers/char/speakup/mapdata.h @@ -0,0 +1,238 @@ +t_key_init init_key_data[] = { + "key_s", 31, -1, + "top_edge", 17, -2, + "key_t", 20, -1, + "say_first_char", 6, -2, + "attrib_bleep_inc", 71, -2, + "key_u", 22, -1, + "key_grave", 41, -1, + "key_v", 47, -1, + "key_w", 17, -1, + "key_kpasterisk", 55, -1, + "key_minus", 12, -1, + "key_x", 45, -1, + "key_down", 108, -1, + "key_y", 21, -1, + "key_kpdot", 83, -1, + "key_leftmeta", 125, -1, + "key_z", 44, -1, + "key_volumedown", 114, -1, + "say_next_word", 13, -2, + "clear_win", 41, -2, + "key_rightbrace", 27, -1, + "right_edge", 20, -2, + "key_scrolllock", 70, -1, + "key_comma", 51, -1, + "altgr", 2, -3, + "say_screen", 23, -2, + "key_cut", 137, -1, + "say_to_bottom", 30, -2, + "edit_most", 35, -2, + "key_sleep", 142, -1, + "say_phonetic_char", 36, -2, + "speakup_quiet", 3, -2, + "key_hanja", 123, -1, + "say_next_line", 16, -2, + "vol_dec", 78, -2, + "key_help", 138, -1, + "key_xfer", 147, -1, + "speakup_goto", 1, -2, + "punct_inc", 83, -2, + "key_rightctrl", 97, -1, + "attrib_bleep_dec", 70, -2, + "key_rightshift", 54, -1, + "key_linefeed", 101, -1, + "key_wakeup", 143, -1, + "key_enter", 28, -1, + "key_again", 129, -1, + "key_file", 144, -1, + "key_tab", 15, -1, + "speakup_off", 26, -2, + "set_win", 40, -2, + "key_insert", 110, -1, + "key_setup", 141, -1, + "key_equal", 13, -1, + "tone_inc", 81, -2, + "key_hiragana", 91, -1, + "key_kpjpcomma", 95, -1, + "key_pause", 119, -1, + "key_volumeup", 115, -1, + "key_f1", 59, -1, + "key_hanguel", 122, -1, + "double", 32, -3, + "key_f2", 60, -1, + "key_computer", 157, -1, + "key_back", 158, -1, + "key_leftctrl", 29, -1, + "key_f3", 61, -1, + "key_f4", 62, -1, + "key_0", 11, -1, + "key_f5", 63, -1, + "key_yen", 124, -1, + "key_copy", 133, -1, + "speakup_paste", 5, -2, + "key_1", 2, -1, + "key_f6", 64, -1, + "toggle_cursoring", 46, -2, + "key_esc", 1, -1, + "key_2", 3, -1, + "key_f7", 65, -1, + "speakup_parked", 27, -2, + "punct_dec", 82, -2, + "key_3", 4, -1, + "key_f8", 66, -1, + "key_4", 5, -1, + "key_f9", 67, -1, + "key_5", 6, -1, + "key_calc", 140, -1, + "spell_phonetic", 21, -2, + "key_6", 7, -1, + "key_7", 8, -1, + "key_coffee", 152, -1, + "key_mail", 155, -1, + "say_attributes", 25, -2, + "say_to_right", 32, -2, + "key_8", 9, -1, + "key_mute", 113, -1, + "key_9", 10, -1, + "key_katakana", 90, -1, + "key_zenkakuhankaku", 85, -1, + "key_forward", 159, -1, + "key_up", 103, -1, + "tone_dec", 80, -2, + "key_leftalt", 56, -1, + "say_next_char", 10, -2, + "reading_punc_inc", 69, -2, + "rate_inc", 75, -2, + "key_backspace", 14, -1, + "bottom_edge", 18, -2, + "key_kp0", 82, -1, + "key_delete", 111, -1, + "key_prog1", 148, -1, + "say_char_num", 33, -2, + "key_kp1", 79, -1, + "key_end", 107, -1, + "key_prog2", 149, -1, + "key_kp2", 80, -1, + "say_prev_word", 12, -2, + "left_edge", 19, -2, + "key_kp3", 81, -1, + "key_katakanahiragana", 93, -1, + "key_right", 106, -1, + "key_kp4", 75, -1, + "key_find", 136, -1, + "key_kp5", 76, -1, + "speakup_cut", 4, -2, + "key_kp6", 77, -1, + "key_kp7", 71, -1, + "spkup_max_func", 47, -2, + "key_kp8", 72, -1, + "key_deletefile", 146, -1, + "key_f10", 68, -1, + "key_kp9", 73, -1, + "key_f11", 87, -1, + "key_sendfile", 145, -1, + "say_word", 11, -2, + "edit_repeat", 38, -2, + "key_leftbrace", 26, -1, + "key_f12", 88, -1, + "say_prev_line", 15, -2, + "say_from_top", 29, -2, + "var_start", 64, -2, + "reading_punc_dec", 68, -2, + "rate_dec", 74, -2, + "key_backslash", 43, -1, + "edit_exnum", 39, -2, + "key_kpslash", 98, -1, + "key_pagedown", 109, -1, + "key_kpplusminus", 118, -1, + "key_stop", 128, -1, + "key_props", 130, -1, + "pitch_inc", 77, -2, + "key_semicolon", 39, -1, + "key_rightalt", 100, -1, + "key_pageup", 104, -1, + "key_kpplus", 78, -1, + "say_line", 14, -2, + "bleeps_inc", 73, -2, + "key_leftshift", 42, -1, + "key_kpminus", 74, -1, + "key_paste", 135, -1, + "spell_delay_inc", 65, -2, + "punc_level_inc", 67, -2, + "key_www", 150, -1, + "edit_delim", 37, -2, + "key_capslock", 58, -1, + "key_muhenkan", 94, -1, + "key_compose", 127, -1, + "spk_key", 128, -2, + "key_sysrq", 99, -1, + "key_apostrophe", 40, -1, + "key_left", 105, -1, + "key_power", 116, -1, + "key_menu", 139, -1, + "voice_inc", 85, -2, + "key_kpcomma", 121, -1, + "spell_word", 22, -2, + "enable_win", 42, -2, + "key_a", 30, -1, + "key_henkan", 92, -1, + "key_bookmarks", 156, -1, + "say_line_indent", 28, -2, + "key_b", 48, -1, + "key_space", 57, -1, + "key_c", 46, -1, + "edit_some", 34, -2, + "key_d", 32, -1, + "key_numlock", 69, -1, + "key_home", 102, -1, + "shift", 1, -3, + "key_e", 18, -1, + "key_msdos", 151, -1, + "say_position", 24, -2, + "pitch_dec", 76, -2, + "key_f", 33, -1, + "key_cyclewindows", 154, -1, + "speakup_help", 45, -2, + "first_edit_bits", 34, -2, + "alt", 8, -3, + "key_g", 34, -1, + "key_kpenter", 96, -1, + "key_macro", 112, -1, + "bleeps_dec", 72, -2, + "ctrl", 4, -3, + "key_h", 35, -1, + "key_direction", 153, -1, + "key_i", 23, -1, + "spk_lock", 44, -2, + "spell_delay_dec", 64, -2, + "punc_level_dec", 66, -2, + "key_j", 36, -1, + "say_prev_char", 9, -2, + "key_k", 37, -1, + "key_102nd", 86, -1, + "key_l", 38, -1, + "key_ro", 89, -1, + "key_rightmeta", 126, -1, + "key_m", 50, -1, + "key_kpequal", 117, -1, + "key_undo", 131, -1, + "speech_kill", 2, -2, + "key_n", 49, -1, + "key_front", 132, -1, + "key_o", 24, -1, + "key_open", 134, -1, + "voice_dec", 84, -2, + "spk", 16, -3, + "key_p", 25, -1, + "say_last_char", 7, -2, + "say_from_left", 31, -2, + "key_q", 16, -1, + "key_slash", 53, -1, + "say_win", 43, -2, + "key_r", 19, -1, + "key_dot", 52, -1, + "say_char", 8, -2, + "vol_inc", 79, -2, + ".", 0, 0 +}; --- /dev/null +++ gregkh-2.6/drivers/char/speakup/mod_code.c @@ -0,0 +1,25 @@ +/* this code is to modularize a synth specific file, included at the end */ + +static void __exit mod_synth_exit( void ) +{ + if ( synth == &MY_SYNTH ) + synth_release( ); + synth_remove( &MY_SYNTH ); +} + +static int __init mod_synth_init( void ) +{ + int status = do_synth_init( &MY_SYNTH ); + if ( status != 0 ) return status; + synth_add( &MY_SYNTH ); +#if (LINUX_VERSION_CODE < 132419) +// MOD_INC_USE_COUNT; +#endif + return 0; +} + +module_init( mod_synth_init ); +module_exit( mod_synth_exit ); +MODULE_AUTHOR("Kirk Reiser "); +MODULE_DESCRIPTION("Synthesizer driver module for speakup for the synth->long_name"); +MODULE_LICENSE( "GPL" ); --- /dev/null +++ gregkh-2.6/drivers/char/speakup/serialio.h @@ -0,0 +1,18 @@ +#ifndef SSPK_SERIAL +#define SSPK_SERIAL + +#include /* for rs_table, serial constants & + serial_uart_config */ +#include /* for more serial constants */ +#include /* for struct serial_state */ +#include + +#define SPK_SERIAL_TIMEOUT 1000000 /* countdown values for serial timeouts */ +#define SPK_XMITR_TIMEOUT 1000000 /* countdown values transmitter/dsr timeouts */ +#define SPK_LO_TTY 0 /* check ttyS0 ... ttyS3 */ +#define SPK_HI_TTY 3 +#define NUM_DISABLE_TIMEOUTS 3 /* # of timeouts permitted before disable */ +#define SPK_TIMEOUT 100 /* buffer timeout in ms */ +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +#endif --- /dev/null +++ gregkh-2.6/drivers/char/speakup/speakup_acnt.h @@ -0,0 +1,16 @@ +/* speakup_acntpc.h - header file for speakups Accent-PC driver. */ + +#define SYNTH_IO_EXTENT 0x02 + +#define SYNTH_CLEAR 0x18 /* stops speech */ + + /* Port Status Flags */ +#define SYNTH_READABLE 0x01 /* mask for bit which is nonzero if a + byte can be read from the data port */ +#define SYNTH_WRITABLE 0x02 /* mask for RDY bit, which when set to + 1, indicates the data port is ready + to accept a byte of data. */ +#define SYNTH_QUIET 'S' /* synth is not speaking */ +#define SYNTH_FULL 'F' /* synth is full. */ +#define SYNTH_ALMOST_EMPTY 'M' /* synth has les than 2 seconds of text left */ +#define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */ --- /dev/null +++ gregkh-2.6/drivers/char/speakup/speakup_acntpc.c @@ -0,0 +1,160 @@ +/* + * originially written by: Kirk Reiser +* this version considerably modified by David Borowski, david575@rogers.com + + Copyright (C) 1998-99 Kirk Reiser. + Copyright (C) 2003 David Borowski. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ +#include "spk_priv.h" +#include "speakup_acnt.h" /* local header file for Accent values */ + +#define MY_SYNTH synth_acntpc +#define synth_readable( ) ( inb_p( synth_port_control ) & SYNTH_READABLE ) +#define synth_writable( ) ( inb_p( synth_port_control ) & SYNTH_WRITABLE ) +#define synth_full( ) ( inb_p( synth_port_tts ) == 'F' ) +#define PROCSPEECH '\r' + + +static int synth_port_control; +static unsigned int synth_portlist[] = + { 0x2a8, 0 }; + +static char *synth_immediate ( char *buf ) +{ + u_char ch; + while ( ( ch = *buf ) ) { + if ( ch == 0x0a ) ch = PROCSPEECH; + if ( synth_full( ) ) + return buf; + while ( synth_writable( ) ); + outb_p( ch, synth_port_tts ); + buf++; + } + return 0; +} + +static void do_catch_up( unsigned long data ) +{ + unsigned long jiff_max = jiffies+synth_jiffy_delta; + u_char ch; + synth_stop_timer( ); + while ( synth_buff_out < synth_buff_in ) { + if ( synth_full( ) ) { + synth_delay( synth_full_time ); + return; + } + while ( synth_writable( ) ); + ch = *synth_buff_out++; + if ( ch == 0x0a ) ch = PROCSPEECH; + outb_p( ch, synth_port_tts ); + if ( jiffies >= jiff_max && ch == SPACE ) { + while ( synth_writable( ) ); + outb_p( PROCSPEECH, synth_port_tts ); + synth_delay( synth_delay_time ); + return; + } + } + while ( synth_writable( ) ); + outb_p( PROCSPEECH, synth_port_tts ); + synth_done( ); +} + +static void synth_flush( void ) +{ + outb_p( SYNTH_CLEAR, synth_port_tts ); +} + +static int synth_probe( void ) +{ + unsigned int port_val = 0; + int i = 0; + pr_info( "Probing for %s.\n", synth->long_name ); + if ( synth_port_forced ) { + synth_port_tts = synth_port_forced; + pr_info( "probe forced to %x by kernel command line\n", synth_port_tts ); + if ( synth_request_region( synth_port_tts-1, SYNTH_IO_EXTENT ) ) { + pr_warn( "sorry, port already reserved\n" ); + return -EBUSY; + } + port_val = inw( synth_port_tts-1 ); + synth_port_control = synth_port_tts-1; + } else { + for( i=0; synth_portlist[i]; i++ ) { + if ( synth_request_region( synth_portlist[i], SYNTH_IO_EXTENT ) ) { + pr_warn( "request_region: failed with 0x%x, %d\n", + synth_portlist[i], SYNTH_IO_EXTENT ); + continue; + } + port_val = inw( synth_portlist[i] ); + if ( ( port_val &= 0xfffc ) == 0x53fc ) { /* 'S' and out&input bits */ + synth_port_control = synth_portlist[i]; + synth_port_tts = synth_port_control+1; + break; + } + } + } + if ( ( port_val &= 0xfffc ) != 0x53fc ) { /* 'S' and out&input bits */ + pr_info( "%s: not found\n", synth->long_name ); + synth_release_region( synth_portlist[i], SYNTH_IO_EXTENT ); + synth_port_control = 0; + return -ENODEV; + } + pr_info( "%s: %03x-%03x, driver version %s,\n", synth->long_name, + synth_port_control, synth_port_control+SYNTH_IO_EXTENT-1, + synth->version ); + return 0; +} + +static void accent_release( void ) +{ + if ( synth_port_tts ) + synth_release_region( synth_port_tts-1, SYNTH_IO_EXTENT ); + synth_port_tts = 0; +} + +static int synth_is_alive( void ) +{ + synth_alive = 1; + return 1; +} + +static const char init_string[] = "\033=X \033Oi\033T2\033=M\033N1\n"; + +static string_var stringvars[] = { + { CAPS_START, "\033P8" }, + { CAPS_STOP, "\033P5" }, + V_LAST_STRING +}; +static num_var numvars[] = { + { RATE, "\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" }, + { PITCH, "\033P%d", 5, 0, 9, 0, 0, 0 }, + { VOL, "\033A%d", 5, 0, 9, 0, 0, 0 }, + { TONE, "\033V%d", 5, 0, 9, 0, 0, 0 }, + V_LAST_NUM +}; + +struct spk_synth synth_acntpc = {"acntpc", "1.1", "Accent PC", + init_string, 500, 50, 50, 1000, 0, 0, SYNTH_CHECK, + stringvars, numvars, synth_probe, accent_release, synth_immediate, + do_catch_up, NULL, synth_flush, synth_is_alive, NULL }; + +#ifdef MODULE +#include "mod_code.c" +#endif --- /dev/null +++ gregkh-2.6/drivers/char/speakup/speakup_acntsa.c @@ -0,0 +1,184 @@ +/* + * originially written by: Kirk Reiser +* this version considerably modified by David Borowski, david575@rogers.com + + Copyright (C) 1998-99 Kirk Reiser. + Copyright (C) 2003 David Borowski. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ +#include "spk_priv.h" +#include "serialio.h" +#include "speakup_acnt.h" /* local header file for Accent values */ + +#define MY_SYNTH synth_acntsa +#define synth_full( ) ( inb_p( synth_port_tts ) == 'F' ) +#define PROCSPEECH '\r' + +static int timeouts = 0; /* sequential number of timeouts */ + +static int +wait_for_xmitr ( void ) +{ + int check, tmout = SPK_XMITR_TIMEOUT; + if ( ( synth_alive ) && ( timeouts >= NUM_DISABLE_TIMEOUTS ) ) { + synth_alive = 0; + return 0; + } + do { /* holding register empty? */ + check = inb_p ( synth_port_tts + UART_LSR ); + if ( --tmout == 0 ) { + pr_warn ( "%s: timed out\n", synth->long_name ); + timeouts++; + return 0; + } + } while ( ( check & BOTH_EMPTY ) != BOTH_EMPTY ); + tmout = SPK_XMITR_TIMEOUT; + do { /* CTS */ + check = inb_p ( synth_port_tts + UART_MSR ); + if ( --tmout == 0 ) { + timeouts++; + return 0; + } + } while ( ( check & UART_MSR_CTS ) != UART_MSR_CTS ); + timeouts = 0; + return 1; +} + +static inline int +spk_serial_out ( const char ch ) +{ + if ( synth_alive && wait_for_xmitr ( ) ) { + outb_p ( ch, synth_port_tts ); + return 1; + } + return 0; +} + +static void +do_catch_up ( unsigned long data ) +{ + unsigned long jiff_max = jiffies+synth_jiffy_delta; + u_char ch; + synth_stop_timer ( ); + while ( synth_buff_out < synth_buff_in ) { + ch = *synth_buff_out; + if ( ch == 0x0a ) ch = 0x0D; + if ( !spk_serial_out ( ch ) ) { + synth_delay ( synth_full_time ); + return; + } + synth_buff_out++; + if ( jiffies >= jiff_max && ch == ' ' ) { + spk_serial_out ( PROCSPEECH ); + synth_delay ( synth_delay_time ); + return; + } + } + spk_serial_out ( PROCSPEECH ); + synth_done( ); +} + +static char *synth_immediate ( char *buff ) +{ + u_char ch; + while ( ( ch = *buff ) ) { + if ( ch == 0x0a ) ch = PROCSPEECH; + if ( wait_for_xmitr( ) ) + outb( ch, synth_port_tts ); + else return buff; + buff++; + } + return 0; +} + +static void synth_flush ( void ) +{ + spk_serial_out ( SYNTH_CLEAR ); +} + +static int serprobe ( int index ) +{ + struct serial_state *ser = spk_serial_init( index ); + if ( ser == NULL ) return -1; + outb ( 0x0d, ser->port ); + // mdelay ( 1 ); + /* ignore any error results, if port was forced */ + if ( synth_port_forced ) return 0; + /* check for accent s.a now... */ + if ( !synth_immediate( "\x18" ) ) + return 0; + spk_serial_release( ); + timeouts = synth_alive = 0; /* not ignoring */ + return -1; +} + +static int synth_probe ( void ) +{ + int i = 0, failed=0; + pr_info ( "Probing for %s.\n", synth->long_name ); + for ( i = SPK_LO_TTY; i <= SPK_HI_TTY; i++ ) { + if (( failed = serprobe( i )) == 0 ) break; /* found it */ + } + if ( failed ) { + pr_info ( "%s: not found\n", synth->long_name ); + return -ENODEV; + } + pr_info ( "%s: %03x-%03x, Driver Version %s,\n", synth->long_name, + synth_port_tts, synth_port_tts + 7, synth->version ); + synth_immediate( "\033=R\r" ); + mdelay( 100 ); + return 0; +} + +static int +synth_is_alive ( void ) +{ + if ( synth_alive ) return 1; + if ( !synth_alive && wait_for_xmitr ( ) > 0 ) { /* restart */ + synth_alive = 1; + synth_write_string ( synth->init ); + return 2; + } + pr_warn ( "%s: can't restart synth\n", synth->long_name ); + return 0; +} + +static const char init_string[] = "\033T2\033=M\033Oi\033N1\n"; + +static string_var stringvars[] = { + { CAPS_START, "\033P8" }, + { CAPS_STOP, "\033P5" }, + V_LAST_STRING +}; +static num_var numvars[] = { + { RATE, "\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" }, + { PITCH, "\033P%d", 5, 0, 9, 0, 0, 0 }, + { VOL, "\033A%d", 9, 0, 9, 0, 0, 0 }, + { TONE, "\033V%d", 5, 0, 9, 0, 0, 0 }, + V_LAST_NUM +}; + +struct spk_synth synth_acntsa = { "acntsa", "1.1", "Accent-SA", + init_string, 400, 5, 30, 1000, 0, 0, SYNTH_CHECK, + stringvars, numvars, synth_probe, spk_serial_release, synth_immediate, + do_catch_up, NULL, synth_flush, synth_is_alive, NULL}; + +#ifdef MODULE +#include "mod_code.c" +#endif --- /dev/null +++ gregkh-2.6/drivers/char/speakup/speakup_apollo.c @@ -0,0 +1,195 @@ +/* + * originially written by: Kirk Reiser +* this version considerably modified by David Borowski, david575@rogers.com + + Copyright (C) 1998-99 Kirk Reiser. + Copyright (C) 2003 David Borowski. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ +#include "spk_priv.h" +#include "serialio.h" + +#define MY_SYNTH synth_apollo +#define SYNTH_CLEAR 0x18 +#define PROCSPEECH '\r' + +static int timeouts = 0; /* sequential number of timeouts */ + +static int wait_for_xmitr( void ) +{ + int check, tmout = SPK_XMITR_TIMEOUT; + if ( ( synth_alive ) && ( timeouts >= NUM_DISABLE_TIMEOUTS ) ) { + synth_alive = 0; + timeouts = 0; + return 0; + } + do { + check = inb( synth_port_tts + UART_LSR ); + if ( --tmout == 0 ) { + pr_warn( "APOLLO: timed out\n" ); + timeouts++; + return 0; + } + } while ( ( check & BOTH_EMPTY ) != BOTH_EMPTY ); + tmout = SPK_XMITR_TIMEOUT; + do { + check = inb( synth_port_tts + UART_MSR ); + if ( --tmout == 0 ) { + timeouts++; + return 0; + } + } while ( ( check & UART_MSR_CTS ) != UART_MSR_CTS ); + timeouts = 0; + return 1; +} + +static inline int spk_serial_out( const char ch ) +{ + // int timer = 9000000; + if ( synth_alive && wait_for_xmitr( ) ) { + outb( ch, synth_port_tts ); + /*while ( inb( synth_port_tts+UART_MSR ) & UART_MSR_CTS ) if ( --timer == 0 ) break;*/ + /* outb( UART_MCR_DTR, synth_port_tts + UART_MCR );*/ + return 1; + } + return 0; +} + +/* +static unsigned char spk_serial_in( void ) +{ + int c, lsr, tmout = SPK_SERIAL_TIMEOUT; + do { + lsr = inb( synth_port_tts + UART_LSR ); + if ( --tmout == 0 ) return 0xff; + } while ( !( lsr & UART_LSR_DR ) ); + c = inb( synth_port_tts + UART_RX ); + return ( unsigned char ) c; +} +*/ + +static void do_catch_up( unsigned long data ) +{ + unsigned long jiff_max = jiffies+synth_jiffy_delta; + u_char ch; +synth_stop_timer( ); + while ( synth_buff_out < synth_buff_in ) { + ch = *synth_buff_out; + if ( !spk_serial_out( ch ) ) { + outb( UART_MCR_DTR, synth_port_tts + UART_MCR ); + outb( UART_MCR_DTR | UART_MCR_RTS, synth_port_tts + UART_MCR ); + synth_delay( synth_full_time ); + return; + } + synth_buff_out++; + if ( jiffies >= jiff_max && synth_buff_out-synth_buffer > 10 ) { + spk_serial_out( PROCSPEECH ); + synth_delay( synth_delay_time ); + return; + } + } + spk_serial_out( PROCSPEECH ); + synth_done( ); +} + +static char *synth_immediate ( char *buf ) +{ + u_char ch; + while ( ( ch = *buf ) ) { + if ( ch == 0x0a ) ch = PROCSPEECH; + if ( wait_for_xmitr( ) ) + outb( ch, synth_port_tts ); + else return buf; + buf++; + } + return 0; +} + +static void synth_flush ( void ) +{ + spk_serial_out ( SYNTH_CLEAR ); +} + +static int serprobe( int index ) +{ + struct serial_state *ser = spk_serial_init( index ); + if ( ser == NULL ) return -1; + outb( 0x0d, ser->port ); /* wake it up if older BIOS */ + mdelay( 1 ); + synth_port_tts = ser->port; + if ( synth_port_forced ) return 0; + /* check for apollo now... */ + if ( !synth_immediate( "\x18" ) ) return 0; + pr_warn( "port %x failed\n", synth_port_tts ); + spk_serial_release( ); + timeouts = synth_alive = synth_port_tts = 0; + return -1; +} + +static int synth_probe( void ) +{ +int i, failed=0; + pr_info( "Probing for %s.\n", synth->long_name ); + for ( i=SPK_LO_TTY; i <= SPK_HI_TTY; i++ ) { + if (( failed = serprobe( i )) == 0 ) break; /* found it */ + } + if ( failed ) { + pr_info( "%s: not found\n", synth->long_name ); + return -ENODEV; + } + pr_info( "%s: %03x-%03x, Driver version %s,\n", synth->long_name, + synth_port_tts, synth_port_tts + 7, synth->version ); + return 0; +} + +static int synth_is_alive( void ) +{ + if ( synth_alive ) return 1; + if ( !synth_alive && wait_for_xmitr( ) > 0 ) { /* restart */ + synth_alive = 1; + synth_write_string( synth->init ); + return 2; /* reenabled */ + } else pr_warn( "%s: can't restart synth\n", synth->long_name ); + return 0; +} + +static const char init_string[] = "@R3@D0@K1\r"; + +static string_var stringvars[] = { + { CAPS_START, "cap, " }, + { CAPS_STOP, "" }, + V_LAST_STRING +}; +static num_var numvars[] = { + { RATE, "@W%d", 6, 1, 9, 0, 0, 0 }, + { PITCH, "@F%x", 10, 0, 15, 0, 0, 0 }, + { VOL, "@A%x", 10, 0, 15, 0, 0, 0 }, + { VOICE, "@V%d", 1, 1, 6, 0, 0, 0 }, + { LANG, "@=%d,", 1, 1, 4, 0, 0, 0 }, + V_LAST_NUM +}; + +struct spk_synth synth_apollo = {"apollo", "1.2", "Apollo", + init_string, 500, 50, 50, 5000, 0, 0, SYNTH_CHECK, + stringvars, numvars, synth_probe, spk_serial_release, synth_immediate, + do_catch_up, NULL, synth_flush, synth_is_alive, NULL}; + +#ifdef MODULE +#include "mod_code.c" +#endif --- /dev/null +++ gregkh-2.6/drivers/char/speakup/speakup_audptr.c @@ -0,0 +1,201 @@ +/* + * originially written by: Kirk Reiser +* this version considerably modified by David Borowski, david575@rogers.com + + Copyright (C) 1998-99 Kirk Reiser. + Copyright (C) 2003 David Borowski. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ +#include "spk_priv.h" +#include "serialio.h" + +#define MY_SYNTH synth_audptr +#define SYNTH_CLEAR 0x18 /* flush synth buffer */ +#define PROCSPEECH '\r' /* start synth processing speech char */ + +static int timeouts = 0; /* sequential number of timeouts */ + +static int wait_for_xmitr( void ) +{ + int check, tmout = SPK_XMITR_TIMEOUT; + if ( ( synth_alive ) && ( timeouts >= NUM_DISABLE_TIMEOUTS ) ) { + synth_alive = 0; + timeouts = 0; + return 0; + } + do { /* holding register empty? */ + check = inb( synth_port_tts + UART_LSR ); + if ( --tmout == 0 ) { + pr_warn( "%s: timed out\n", synth->long_name ); + timeouts++; + return 0; + } + } while ( ( check & BOTH_EMPTY ) != BOTH_EMPTY ); + tmout = SPK_XMITR_TIMEOUT; + do { /* CTS */ + check = inb( synth_port_tts + UART_MSR ); + if ( --tmout == 0 ) { + timeouts++; + return 0; + } + } while ( ( check & UART_MSR_CTS ) != UART_MSR_CTS ); + timeouts = 0; + return 1; +} + +static inline int spk_serial_out( const char ch ) +{ + if ( synth_alive && wait_for_xmitr( ) ) { + outb( ch, synth_port_tts ); + return 1; + } + return 0; +} + +static unsigned char spk_serial_in( void ) +{ + int c, lsr, tmout = SPK_SERIAL_TIMEOUT; + do { + lsr = inb( synth_port_tts + UART_LSR ); + if ( --tmout == 0 ) return 0xff; + } while ( !( lsr & UART_LSR_DR ) ); + c = inb( synth_port_tts + UART_RX ); + return ( unsigned char ) c; +} + +static void do_catch_up( unsigned long data ) +{ + unsigned long jiff_max = jiffies+synth_jiffy_delta; + u_char ch; +synth_stop_timer( ); + while ( synth_buff_out < synth_buff_in ) { + ch = *synth_buff_out; + if ( ch == 0x0a ) ch = PROCSPEECH; + if ( !spk_serial_out( ch ) ) { + synth_delay( synth_full_time ); + return; + } + synth_buff_out++; + if ( jiffies >= jiff_max && ch == SPACE ) { + spk_serial_out( PROCSPEECH ); + synth_delay( synth_delay_time ); + return; + } + } + spk_serial_out( PROCSPEECH ); + synth_done( ); +} + +static char *synth_immediate ( char *buf ) +{ + u_char ch; + while ( ( ch = *buf ) ) { + if ( ch == 0x0a ) ch = PROCSPEECH; + if ( wait_for_xmitr( ) ) + outb( ch, synth_port_tts ); + else return buf; + buf++; + } + return 0; +} + +static void synth_flush( void ) +{ + while ( ( inb( synth_port_tts + UART_LSR ) & BOTH_EMPTY ) != BOTH_EMPTY ); + outb( SYNTH_CLEAR, synth_port_tts ); + spk_serial_out( PROCSPEECH ); + } + +static char synth_id[40] = ""; + +static int serprobe( int index ) +{ + u_char test = 0; + struct serial_state *ser = spk_serial_init( index ); + if ( ser == NULL ) return -1; + /* ignore any error results, if port was forced */ + if ( synth_port_forced ) + return 0; + synth_immediate( "\x05[Q]" ); + if ( ( synth_id[test] = spk_serial_in( ) ) == 'A' ) { + do { /* read version string from synth */ + synth_id[++test] = spk_serial_in( ); + } while ( synth_id[test] != '\n' && test < 32 ); + synth_id[++test] = 0x00; + if ( test != 32 ) + return 0; + } + spk_serial_release( ); + timeouts = synth_alive = 0; /* not ignoring */ + return -1; +} + +static int synth_probe( void ) +{ +int i=0, failed=0; + pr_info( "Probing for %s.\n", synth->long_name ); + for ( i=SPK_LO_TTY; i <= SPK_HI_TTY; i++ ) { + if (( failed = serprobe( i )) == 0 ) break; /* found it */ + } + if ( failed ) { + pr_info( "%s: not found\n", synth->long_name ); + return -ENODEV; + } + pr_info( "%s: %03x-%03x, Driver %s,\n", synth->long_name, + synth_port_tts, synth_port_tts + 7, synth->version ); + if ( synth_id[0] == 'A' ) + pr_info( "%s version: %s", synth->long_name, synth_id ); + return 0; +} + +static int synth_is_alive( void ) +{ + if ( synth_alive ) return 1; + if ( !synth_alive && wait_for_xmitr( ) > 0 ) { /* restart */ + synth_alive = 1; + synth_write_string( synth->init ); + return 2; + } + return 0; +} + +static const char init_string[] = "\x05[D1]\x05[Ol]"; + +static string_var stringvars[] = { + { CAPS_START, "\x05[f99]" }, + { CAPS_STOP, "\x05[f80]" }, + V_LAST_STRING +}; +static num_var numvars[] = { + { RATE, "\x05[r%d]", 10, 0, 20, -100, 10, 0 }, + { PITCH, "\x05[f%d]", 80, 39, 4500, 0, 0, 0 }, + { VOL, "\x05[g%d]", 21, 0, 40, 0, 0, 0 }, + { TONE, "\x05[s%d]", 9, 0,63, 0, 0, 0 }, + { PUNCT, "\x05[A%c]", 0, 0, 3, 0, 0, "nmsa" }, + V_LAST_NUM +}; + +struct spk_synth synth_audptr = {"audptr", "1.1", "Audapter", + init_string, 400, 5, 30, 5000, 0, 0, SYNTH_CHECK, + stringvars, numvars, synth_probe, spk_serial_release, synth_immediate, + do_catch_up, NULL, synth_flush, synth_is_alive, NULL}; + +#ifdef MODULE +#include "mod_code.c" +#endif --- /dev/null +++ gregkh-2.6/drivers/char/speakup/speakup_bns.c @@ -0,0 +1,174 @@ +/* + * originially written by: Kirk Reiser +* this version considerably modified by David Borowski, david575@rogers.com + + Copyright (C) 1998-99 Kirk Reiser. + Copyright (C) 2003 David Borowski. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ +#include "spk_priv.h" +#include "serialio.h" + +#define MY_SYNTH synth_bns +#define SYNTH_CLEAR 0x18 +#define PROCSPEECH '\r' + +static int wait_for_xmitr( void ) +{ + static int timeouts = 0; /* sequential number of timeouts */ + int check, tmout = SPK_XMITR_TIMEOUT; + if ( ( synth_alive ) && ( timeouts >= NUM_DISABLE_TIMEOUTS ) ) { + synth_alive = 0; + timeouts = 0; + return 0; + } + do { + check = inb( synth_port_tts + UART_LSR ); + if ( --tmout == 0 ) { + pr_warn( "BNS: timed out\n" ); + timeouts++; + return 0; + } + } while ( ( check & BOTH_EMPTY ) != BOTH_EMPTY ); + tmout = SPK_XMITR_TIMEOUT; + do { + check = inb( synth_port_tts + UART_MSR ); + if ( --tmout == 0 ) { + timeouts++; + return 0; + } + } while ( ( check & UART_MSR_CTS ) != UART_MSR_CTS ); + timeouts = 0; + return 1; +} + +static inline int spk_serial_out( const char ch ) +{ + if ( synth_alive && wait_for_xmitr( ) ) { + outb( ch, synth_port_tts ); + return 1; + } + return 0; +} + +static void do_catch_up( unsigned long data ) +{ + unsigned long jiff_max = jiffies+synth_jiffy_delta; + u_char ch; + synth_stop_timer( ); + while ( synth_buff_out < synth_buff_in ) { + ch = *synth_buff_out; + if ( ch == '\n' ) ch = PROCSPEECH; + if ( !spk_serial_out( ch ) ) { + synth_delay( synth_full_time ); + return; + } + synth_buff_out++; + if ( jiffies >= jiff_max && ch == ' ' ) { + spk_serial_out( PROCSPEECH ); + synth_delay( synth_delay_time ); + return; + } + } + spk_serial_out( PROCSPEECH ); + synth_done( ); +} + +static char *synth_immediate ( char *buf ) +{ + u_char ch; + while ( ( ch = *buf ) ) { + if ( ch == 0x0a ) ch = PROCSPEECH; + if ( wait_for_xmitr( ) ) + outb( ch, synth_port_tts ); + else return buf; + buf++; + } + return 0; +} + +static void synth_flush ( void ) +{ + spk_serial_out ( SYNTH_CLEAR ); +} + +static int serprobe( int index ) +{ + struct serial_state *ser = spk_serial_init( index ); + if ( ser == NULL ) return -1; + outb( '\r', ser->port ); + if ( synth_port_forced ) return 0; + /* check for bns now... */ + if ( !synth_immediate( "\x18" ) ) return 0; + spk_serial_release( ); + synth_alive = 0; + return -1; +} + +static int synth_probe( void ) +{ +int i=0, failed=0; + pr_info( "Probing for %s.\n", synth->long_name ); + for ( i=SPK_LO_TTY; i <= SPK_HI_TTY; i++ ) { + if (( failed = serprobe( i )) == 0 ) break; /* found it */ + } + if ( failed ) { + pr_info( "%s: not found\n", synth->long_name ); + return -ENODEV; + } + pr_info( "%s: %03x-%03x, Driver version %s,\n", synth->long_name, + synth_port_tts, synth_port_tts + 7, synth->version ); + return 0; +} + +static int synth_is_alive( void ) +{ + if ( synth_alive ) return 1; + if ( !synth_alive && wait_for_xmitr( ) > 0 ) { /* restart */ + synth_alive = 1; + synth_write_string( synth->init ); + return 2; + } + pr_warn( "%s: can't restart synth\n", synth->long_name ); + return 0; +} + +static const char init_string[] = "\x05Z\x05\x43"; + +static string_var stringvars[] = { + { CAPS_START, "\x05\x31\x32P" }, + { CAPS_STOP, "\x05\x38P" }, + V_LAST_STRING +}; +static num_var numvars[] = { + { RATE, "\x05%dE", 8, 1, 16, 0, 0, 0 }, + { PITCH, "\x05%dP", 8, 0, 16, 0, 0, 0 }, + { VOL, "\x05%dV", 8, 0, 16, 0, 0, 0 }, + { TONE, "\x05%dT", 8, 0, 16, 0, 0, 0 }, + V_LAST_NUM +}; + +struct spk_synth synth_bns = {"bns", "1.1", "Braille 'N Speak", + init_string, 500, 50, 50, 5000, 0, 0, SYNTH_CHECK, + stringvars, numvars, synth_probe, spk_serial_release, synth_immediate, + do_catch_up, NULL, synth_flush, synth_is_alive, NULL}; + +#ifdef MODULE +#include "mod_code.c" +#endif --- /dev/null +++ gregkh-2.6/drivers/char/speakup/speakup.c @@ -0,0 +1,2281 @@ +/* speakup.c + review functions for the speakup screen review package. + originally written by: Kirk Reiser and Andy Berdan. + + extensively modified by David Borowski. + + Copyright (C ) 1998 Kirk Reiser. + Copyright (C ) 2003 David Borowski. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option ) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define __KERNEL_SYSCALLS__ + +#include +#include +#include +#include +#include /* __get_free_page( ) and friends */ +#include +#if (LINUX_VERSION_CODE < 132419) +#include +#include +#include +#endif +#include +#include +#include /* copy_from|to|user( ) and others */ +#include + +#include /* for KT_SHIFT */ +#include /* for vc_kbd_* and friends */ +#include +#include +#include +#include + +#include "cvsversion.h" +#include "spk_priv.h" +#include /* for alloc_bootmem */ + +/* speakup_*_selection */ +#include +#include +#include +#include +#include +#include + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define SPEAKUP_VERSION "Speakup v-2.00" CVSVERSION +#define MAX_DELAY ( (500 * HZ ) / 1000 ) +#define KEY_MAP_VER 119 +#define MINECHOCHAR SPACE + +/* these are globals from the kernel code */ +extern void *kmalloc (size_t, unsigned int ); +extern void kfree (const void * ); +extern struct kbd_struct * kbd; +extern int fg_console; +extern short punc_masks[]; + +static special_func special_handler = NULL; +special_func help_handler = NULL; + +static int errno; +int synth_file_inuse = 0; +short pitch_shift = 0, synth_flags = 0; +static char buf[256]; +short attrib_bleep = 0, bleeps = 0, bleep_time = 1; +short no_intr = 0, spell_delay = 0; +short key_echo = 0, cursor_timeout = 120, say_word_ctl = 0; +short say_ctrl = 0, bell_pos = 0; +short punc_mask = 0, punc_level = 0, reading_punc = 0; +char str_caps_start[MAXVARLEN+1] = "\0", str_caps_stop[MAXVARLEN+1] = "\0"; +bits_data punc_info[] = { + { "none", "", 0 }, + { "some", "/$%&@", SOME }, + { "most", "$%&#()=+*/@^<>|\\", MOST }, + { "all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC }, + { "delimiters", "", B_WDLM }, + { "repeats", "()", CH_RPT }, + { "extended numeric", "", B_EXNUM }, + { "symbols", "", B_SYM }, + { 0, 0 } +}; +char mark_cut_flag = 0; +u_short mark_x = 0, mark_y = 0; +static char synth_name[10] = CONFIG_SPEAKUP_DEFAULT; +#define MAX_KEY 160 +u_char *our_keys[MAX_KEY], *shift_table; +static u_char key_buf[600]; +static u_char key_defaults[] = { +#include "speakupmap.h" +}; + +#if (LINUX_VERSION_CODE < 132419) +extern struct tty_struct *tty; +typedef void (*k_handler_fn)(unsigned char value, char up_flag); +#define KBD_PROTO u_char value, char up_flag +#define KBD_ARGS value, up_flag +#else +struct tty_struct *tty; +#define key_handler k_handler +typedef void (*k_handler_fn)(struct vc_data *vc, unsigned char value, + char up_flag, struct pt_regs *regs); +#define KBD_PROTO struct vc_data *vc, u_char value, char up_flag, struct pt_regs *regs +#define KBD_ARGS vc, value, up_flag, regs +#endif +extern k_handler_fn key_handler[16]; +static k_handler_fn do_shift, do_spec, do_latin, do_cursor; +EXPORT_SYMBOL( help_handler ); +EXPORT_SYMBOL( special_handler ); +EXPORT_SYMBOL( our_keys ); + +static void speakup_date (struct vc_data *vc ); +static void spkup_write (const char *in_buf, int count ); +int set_mask_bits( const char *input, const int which, const int how ); + +char str_ctl[] = "control-"; +char *colors[] = { + "black", "blue", "green", "cyan", "red", "magenta", "yellow", "white", + "grey" +}; + +char *phonetic[] = { + "alpha", "beta", "charley", "delta", "echo", "fox", "gamma", "hotel", + "india", "juleiet", "keelo", "leema", "mike", "november", "oscar", + "papa", + "quebec", "romeo", "seeara", "tango", "uniform", "victer", "wiskey", + "x ray", "yankee", "zooloo" +}; + +// array of 256 char pointers (one for each ASCII character description ) +// initialized to default_chars and user selectable via /proc/speakup/characters +char *characters[256]; + +char *default_chars[256] = { + "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g", + "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o", + "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w", + "^x", "^y", "^z", NULL, NULL, NULL, NULL, NULL, + "space", "bang!", "quote", "number", "dollar", "percent", "and", + "tick", + "left paren", "right paren", "star", "plus", "comma", "dash", "dot", + "slash", + "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", + "colon", "semmy", "less", "equals", "greater", "question", "at", + "eigh", "b", "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", "x", + "y", "zehd", "left bracket", "backslash", "right bracket", "caret", + "line", + "accent", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "left brace", "bar", "right brace", "tihlduh", + "delta", "see cedilla", "u oomlout", "e acute", /* 128 */ + "eigh circumflex", "eigh oomlout", "eigh grave", "eigh ring", /* 132 */ + "see cedilla", "e circumflex", "e oomlout", "e grave", /* 136 */ + "i oomlout", "i circumflex", "i grave", "eigh oomlout", /* 140 */ + "eigh ring", "e acute", "eigh e dipthong", "eigh e dipthong", /* 144 */ + "o circumflex", "o oomlout", "o grave", "u circumflex", /* 148 */ + "u grave", "y oomlout", "o oomlout", "u oomlout", /* 152 */ + "cents", "pounds", "yen", "peseta", /* 156 */ + "florin", "eigh acute", "i acute", "o acute", /* 160 */ + "u acute", "n tilde", "n tilde", "feminine ordinal", /* 164 */ + "masculin ordinal", "inverted question", "reversed not", "not", /* 168 */ + "half", "quarter", "inverted bang", "much less than", /* 172 */ + "much greater than", "dark shading", "medium shading", /* 176 */ + "light shading", "verticle line", "left tee", /* 179 */ + "double left tee", "left double tee", "double top right", /* 182 */ + "top double right", "double left double tee", /* 185 */ + "double vertical line", "double top double right", /* 187 */ + "double bottom double right", "double bottom right", /* 189 */ + "bottom double right", "top right", "left bottom", /* 191 */ + "up tee", "tee down", "tee right", "horizontal line", /* 194 */ + "cross bars", "tee double right", "double tee right", /* 198 */ + "double left double bottom", "double left double top", /* 201 */ + "double up double tee", "double tee double down", /* 203 */ + "double tee double right", "double horizontal line", /* 205 */ + "double cross bars", "up double tee", "double up tee", /* 207 */ + "double tee down", "tee double down", /* 210 */ + "double left bottom", "left double bottom", /* 212 */ + "double left top", "left double top", /* 214 */ + "double vertical cross", "double horizontal cross", /* 216 */ + "bottom right", "left top", "solid square", /* 218 */ + "solid lower half", "solid left half", "solid right half", /* 221 */ + "solid upper half", "alpha", "beta", "gamma", /* 224 */ + "pie", "sigma", "sigma", "mu", /* 228 */ + "tou", "phigh", "thayta", "ohmega", /* 232 */ + "delta", "infinity", "phigh", "epsilaun", /* 236 */ +"intersection", "identical to", "plus or minus", "equal grater than", /* 240 */ + "less than equal", "upper integral", "lower integral", /* 244 */ + "divided by", "almost equal", "degrees", /* 247 */ + "centre dot", "bullet", "square root", /* 250 */ + "power", "squared", "black square", "white space" /* 252 */ +}; + +u_short spk_chartab[256] = { + B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */ + B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */ + B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */ + B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */ +WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */ +PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ( )*+, -./ */ +NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */ +NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */ +PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */ +A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */ +A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */ +A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */ +PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */ +ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */ +ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */ +ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */ +B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-135 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 136-143 */ +B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 144-151 */ +B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 152-159 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, B_SYM, /* 160-167 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 192-199 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 200-207 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 208-215 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 216-223 */ +B_SYM, B_SYM, B_SYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, /* 224-231 */ +B_SYM, B_CAPSYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 232-239 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 240-247 */ +B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM /* 248-255 */ +}; + +int spk_keydown = 0; +static u_char spk_lastkey = 0, spk_close_press = 0, keymap_flags = 0; +static u_char last_keycode = 0, this_speakup_key = 0; +static u_long last_spk_jiffy = 0; + +spk_t *speakup_console[MAX_NR_CONSOLES]; + +int spk_setup (char *str ) +{ + int ints[4]; + str = get_options (str, ARRAY_SIZE (ints ), ints ); + if (ints[0] > 0 && ints[1] >= 0 ) + synth_port_forced = ints[1]; + return 1; +} + +int spk_ser_setup (char *str ) +{ + int lookup[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + int ints[4]; + str = get_options (str, ARRAY_SIZE (ints ), ints ); + if (ints[0] > 0 && ints[1] >= 0 ) + synth_port_forced = lookup[ints[1]]; + return 1; +} + +int spk_synth_setup (char *str ) +{ + size_t len = MIN (strlen (str ), 9 ); + memcpy (synth_name, str, len ); + synth_name[len] = '\0'; + return 1; +} + +__setup ("speakup_port=", spk_setup ); +__setup ("speakup_ser=", spk_ser_setup ); +__setup ("speakup_synth=", spk_synth_setup ); + +char * +strlwr (char *s ) +{ + char *p; + for (p = s; *p; p++ ) { + if (*p >= CAP_A && *p <= CAP_Z ) *p |= 32; + } + return s; +} + +static void +bleep (u_short val ) +{ + static short vals[] = { 350, 370, 392, 414, 440, 466, 491, 523, +554, 587, 619, 659 }; + short freq; + int time = bleep_time; + freq = vals[val%12]; + if (val > 11 ) + freq *= (1<<(val/12 ) ); + kd_mksound (freq, time ); +} + +void +speakup_shut_up (struct vc_data *vc ) +{ + if (spk_killed ) return; + spk_shut_up |= 0x01; + spk_parked &= 0xfe; + speakup_date (vc ); + if (synth == NULL ) return; + do_flush( ); +} + +void +speech_kill (struct vc_data *vc ) +{ + char val = synth->is_alive ( ); + if (val == 0 ) return; + /* re-enables synth, if disabled */ + if (val == 2 || spk_killed ) { /* dead */ + spk_shut_up &= ~0x40; + synth_write_msg ("Eyem a Lighve!" ); + } else { + synth_write_msg ("You killed speak up!" ); + spk_shut_up |= 0x40; + } +} + +static void +speakup_off (struct vc_data *vc ) +{ + if (spk_shut_up & 0x80 ) { + spk_shut_up &= 0x7f; + synth_write_msg ("hey. That's better!" ); + } else { + spk_shut_up |= 0x80; + synth_write_msg ("You turned me off!" ); + } + speakup_date (vc ); +} + +static void +speakup_parked (struct vc_data *vc ) +{ + if (spk_parked & 0x80 ) { + spk_parked = 0; + synth_write_msg ("unparked!" ); + } else { + spk_parked |= 0x80; + synth_write_msg ("parked!" ); + } +} + +/* ------ cut and paste ----- */ +/* Don't take this from : 011-015 on the screen aren't spaces */ +#undef isspace +#define isspace(c) ((c) == ' ') +/* Variables for selection control. */ +struct vc_data *spk_sel_cons; /* defined in selection.c must not be disallocated */ +static volatile int sel_start = -1; /* cleared by clear_selection */ +static int sel_end; +static int sel_buffer_lth; +static char *sel_buffer; + +static unsigned char +sel_pos(int n) +{ + return inverse_translate(spk_sel_cons, screen_glyph(spk_sel_cons, n)); +} + +static void +speakup_clear_selection(void) +{ + sel_start = -1; +} + +/* does screen address p correspond to character at LH/RH edge of screen? */ +static inline int atedge(const int p, int size_row) +{ + return (!(p % size_row) || !((p + 2) % size_row)); +} + +/* constrain v such that v <= u */ +static inline unsigned short limit(const unsigned short v, const unsigned short u) +{ + return (v > u) ? u : v; +} + +unsigned short xs, ys, xe, ye; /* our region points */ + +static int +speakup_set_selection( struct tty_struct *tty) +{ + int new_sel_start, new_sel_end; + char *bp, *obp; + int i, ps, pe; + struct vc_data *vc = vc_cons[fg_console].d; + + xs = limit(xs, vc->vc_cols - 1); + ys = limit(ys, vc->vc_rows - 1); + xe = limit(xe, vc->vc_cols - 1); + ye = limit(ye, vc->vc_rows - 1); + ps = ys * vc->vc_size_row + (xs << 1); + pe = ye * vc->vc_size_row + (xe << 1); + + if (ps > pe) { /* make sel_start <= sel_end */ + int tmp = ps; + ps = pe; + pe = tmp; + } + + if (spk_sel_cons != vc_cons[fg_console].d) { + speakup_clear_selection(); + spk_sel_cons = vc_cons[fg_console].d; + printk(KERN_WARNING "Selection: mark console not the same as cut\n"); + return -EINVAL; + } + + new_sel_start = ps; + new_sel_end = pe; + + /* select to end of line if on trailing space */ + if (new_sel_end > new_sel_start && + !atedge(new_sel_end, vc->vc_size_row) && + isspace(sel_pos(new_sel_end))) { + for (pe = new_sel_end + 2; ; pe += 2) + if (!isspace(sel_pos(pe)) || + atedge(pe, vc->vc_size_row)) + break; + if (isspace(sel_pos(pe))) + new_sel_end = pe; + } + if ((new_sel_start == sel_start) + && (new_sel_end == sel_end)) /* no action required */ + return 0; + + sel_start = new_sel_start; + sel_end = new_sel_end; + /* Allocate a new buffer before freeing the old one ... */ + bp = kmalloc((sel_end-sel_start)/2+1, GFP_ATOMIC); + if (!bp) { + printk(KERN_WARNING "selection: kmalloc() failed\n"); + speakup_clear_selection(); + return -ENOMEM; + } + if (sel_buffer) + kfree(sel_buffer); + sel_buffer = bp; + + obp = bp; + for (i = sel_start; i <= sel_end; i += 2) { + *bp = sel_pos(i); + if (!isspace(*bp++)) + obp = bp; + if (! ((i + 2) % vc->vc_size_row)) { + /* strip trailing blanks from line and add newline, + unless non-space at end of line. */ + if (obp != bp) { + bp = obp; + *bp++ = '\r'; + } + obp = bp; + } + } + sel_buffer_lth = bp - sel_buffer; + return 0; +} + +static int +speakup_paste_selection(struct tty_struct *tty) +{ + struct vc_data *vc = (struct vc_data *) tty->driver_data; + int pasted = 0, count; + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(&vc->paste_wait, &wait); + while (sel_buffer && sel_buffer_lth > pasted) { + set_current_state(TASK_INTERRUPTIBLE); + if (test_bit(TTY_THROTTLED, &tty->flags)) { + schedule(); + continue; + } + count = sel_buffer_lth - pasted; + count = MIN(count, tty->ldisc.receive_room(tty)); + tty->ldisc.receive_buf(tty, sel_buffer + pasted, 0, count); + pasted += count; + } + remove_wait_queue(&vc->paste_wait, &wait); + current->state = TASK_RUNNING; + return 0; +} + +static void +speakup_cut (struct vc_data *vc ) +{ + static const char err_buf[] = "set selection failed"; + int ret; + + if (!mark_cut_flag ) { + mark_cut_flag = 1; + xs = spk_x; + ys = spk_y; + spk_sel_cons = vc; + synth_write_msg ("mark" ); + return; + } + xe = (u_short ) spk_x; + ye = (u_short )spk_y; + mark_cut_flag = 0; + synth_write_msg ("cut" ); + + speakup_clear_selection( ); + ret = speakup_set_selection ( tty ); + + switch (ret ) { + case 0: + break; /* no error */ + case -EFAULT : + pr_warn( "%sEFAULT\n", err_buf ); + break; + case -EINVAL : + pr_warn( "%sEINVAL\n", err_buf ); + break; + case -ENOMEM : + pr_warn( "%sENOMEM\n", err_buf ); + break; + } +} + +static void +speakup_paste (struct vc_data *vc ) +{ + if (mark_cut_flag ) { + mark_cut_flag = 0; + synth_write_msg ("mark, cleared" ); + } else { + synth_write_msg ("paste" ); + speakup_paste_selection (tty ); + } +} + +static void +say_attributes (struct vc_data *vc ) +{ + int fg= spk_attr&0x0f, bg = spk_attr>>4; + if (fg > 8 ) { + synth_write_string("bright " ); + fg -= 8; + } + synth_write_string(colors[fg] ); + if (bg > 7 ) { + synth_write_string(" on blinking " ); + bg -= 8; + } else + synth_write_string(" on " ); + synth_write_msg(colors[bg] ); +} + +static char *blank_msg = "blank"; +static char *edges[] = { "top, ", "bottom, ", "left, ", "right, ", "" }; +enum { edge_top = 1, edge_bottom, edge_left, edge_right, edge_quiet }; + +static void +announce_edge (struct vc_data *vc, int msg_id ) +{ + if (bleeps&1 ) + bleep (spk_y ); + if (bleeps&2 ) + synth_write_msg (edges[msg_id-1] ); +} + +static void +speak_char( u_char ch ) +{ + char *cp = characters[ ch]; + synth_buffer_add( SPACE ); + if (IS_CHAR(ch, B_CAP ) ) { + pitch_shift++; + synth_write_string(str_caps_start ); + synth_write_string(cp ); + synth_write_string(str_caps_stop ); + } else { + if (*cp == '^' ) { + synth_write_string(str_ctl ); + cp++; + } + synth_write_string(cp ); + } + synth_buffer_add( SPACE ); +} + +static void +say_char (struct vc_data *vc ) +{ + u_short ch; + spk_old_attr = spk_attr; + ch = scr_readw ((u_short * ) spk_pos ); + spk_attr = ((ch & 0xff00 ) >> 8 ); + if (spk_attr != spk_old_attr ) { + if ( attrib_bleep&1 ) bleep (spk_y ); + if ( attrib_bleep&2 ) say_attributes( vc ); + } + speak_char( ch&0xff ); +} + +static void +say_phonetic_char (struct vc_data *vc ) +{ + u_short ch; + spk_old_attr = spk_attr; + ch = scr_readw ((u_short * ) spk_pos ); + spk_attr = ((ch & 0xff00 ) >> 8 ); + if ( IS_CHAR(ch, B_ALPHA ) ) { + ch &= 0x1f; + synth_write_msg(phonetic[--ch] ); + } else { + if ( IS_CHAR(ch, B_NUM ) ) + synth_write_string( "number " ); + speak_char( ch ); + } +} + +static void +say_prev_char (struct vc_data *vc ) +{ + spk_parked |= 0x01; + if (spk_x == 0 ) { + announce_edge(vc, edge_left ); + return; + } + spk_x--; + spk_pos -= 2; + say_char (vc ); +} + +static void +say_next_char (struct vc_data *vc ) +{ + spk_parked |= 0x01; + if (spk_x == vc->vc_cols - 1 ) { + announce_edge(vc, edge_right ); + return; + } + spk_x++; + spk_pos += 2; + say_char (vc ); +} + +/* get_word - will first check to see if the character under the + reading cursor is a space and if say_word_ctl is true it will + return the word space. If say_word_ctl is not set it will check to + see if there is a word starting on the next position to the right + and return that word if it exists. If it does not exist it will + move left to the beginning of any previous word on the line or the + beginning off the line whichever comes first.. */ + +static u_long +get_word (struct vc_data *vc ) +{ + u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos; + char ch; + u_short attr_ch; + spk_old_attr = spk_attr; + ch = (char ) scr_readw ((u_short * ) tmp_pos ); + +/* decided to take out the sayword if on a space (mis-information */ + if ( say_word_ctl && ch == SPACE ) { + *buf = '\0'; + synth_write_msg( "space" ); + return 0; + } else if ((tmpx < vc->vc_cols-2 ) + && (ch == SPACE || IS_WDLM(ch )) + && ((char) scr_readw ((u_short * ) tmp_pos+1 ) > SPACE)) { + tmp_pos += 2; + tmpx++; + } else + while (tmpx > 0 ) { + if (((ch = (char ) scr_readw ((u_short * ) tmp_pos-1 )) == SPACE + || IS_WDLM(ch )) + && ((char) scr_readw ((u_short * ) tmp_pos ) > SPACE)) + break; + tmp_pos -= 2; + tmpx--; + } + attr_ch = scr_readw ((u_short * ) tmp_pos ); + spk_attr = attr_ch >> 8; + buf[cnt++] = attr_ch&0xff; + while (tmpx < vc->vc_cols-1 ) { + tmp_pos += 2; + tmpx++; + ch = (char ) scr_readw ((u_short * ) tmp_pos ); + if ((ch == SPACE ) + || (IS_WDLM(buf[cnt-1] ) && ( ch > SPACE ))) + break; + buf[cnt++] = ch; + } + buf[cnt] = '\0'; + return cnt; +} + +static void +say_word (struct vc_data *vc ) +{ + u_long cnt = get_word(vc ); + u_short saved_punc_mask = punc_mask; + if ( cnt == 0 ) return; + punc_mask = PUNC; + buf[cnt++] = SPACE; + spkup_write (buf, cnt ); + punc_mask = saved_punc_mask; +} + +static void +say_prev_word (struct vc_data *vc ) +{ + char ch; + u_short edge_said = 0, last_state = 0, state = 0; + spk_parked |= 0x01; + if (spk_x == 0 ) { + if ( spk_y == 0 ) { + announce_edge(vc, edge_top ); + return; + } + spk_y--; + spk_x = vc->vc_cols; + edge_said = edge_quiet; + } + while ( 1 ) { + if (spk_x == 0 ) { + if (spk_y == 0 ) { + edge_said = edge_top; + break; + } + if ( edge_said != edge_quiet ) edge_said = edge_left; + if ( state > 0 ) break; + spk_y--; + spk_x = vc->vc_cols-1; + } else spk_x--; + spk_pos -= 2; + ch = (char ) scr_readw ((u_short * ) spk_pos ); + if ( ch == SPACE ) state = 0; + else if (IS_WDLM(ch ) ) state = 1; + else state = 2; + if (state < last_state ) { + spk_pos += 2; + spk_x++; + break; + } + last_state = state; + } + if ( spk_x == 0 && edge_said == edge_quiet ) + edge_said = edge_left; + if ( edge_said > 0 && edge_said < edge_quiet ) + announce_edge( vc, edge_said ); + say_word (vc ); +} + +static void +say_next_word (struct vc_data *vc ) +{ + char ch; + u_short edge_said = 0, last_state = 2, state = 0; + spk_parked |= 0x01; + if ( spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows-1 ) { + announce_edge(vc, edge_bottom ); + return; + } + while ( 1 ) { + ch = (char ) scr_readw ((u_short * ) spk_pos ); + if ( ch == SPACE ) state = 0; + else if (IS_WDLM(ch ) ) state = 1; + else state = 2; + if ( state > last_state ) break; + if (spk_x >= vc->vc_cols-1 ) { + if (spk_y == vc->vc_rows-1 ) { + edge_said = edge_bottom; + break; + } + state = 0; + spk_y++; + spk_x = 0; + edge_said = edge_right; + } else spk_x++; + spk_pos += 2; + last_state = state; + } + if ( edge_said > 0 ) + announce_edge( vc, edge_said ); + say_word (vc ); +} + +static void +spell_word (struct vc_data *vc ) +{ + static char *delay_str[] = { " ", ", ", ". ", ". . ", ". . . " }; + char *cp = buf, *str_cap= str_caps_stop; + char *cp1, *last_cap = str_caps_stop; + u_char ch; + if ( !get_word(vc ) ) return; + while ((ch = (u_char )*cp ) ) { + if ( cp != buf ) + synth_write_string (delay_str[spell_delay] ); + if (IS_CHAR(ch, B_CAP ) ) { + str_cap = str_caps_start; + if ( *str_caps_stop ) pitch_shift++; + else last_cap = str_caps_stop; /* synth has no pitch */ + } else str_cap = str_caps_stop; + if ( str_cap !=last_cap ) { + synth_write_string( str_cap ); + last_cap = str_cap; + } + if ( this_speakup_key == SPELL_PHONETIC && ( IS_CHAR( ch, B_ALPHA ) ) ) { + ch &= 31; + cp1 = phonetic[--ch]; + } else { + cp1 = characters[ch]; + if (*cp1 == '^' ) { + synth_write_string(str_ctl ); + cp1++; + } + } + synth_write_string(cp1 ); + cp++; + } + if ( str_cap != str_caps_stop ) + synth_write_string( str_caps_stop ); +} + +static int +get_line (struct vc_data *vc ) +{ + u_long tmp = spk_pos - (spk_x * 2 ); + int i = 0; + spk_old_attr = spk_attr; + spk_attr = (u_char ) (scr_readw ((u_short * ) spk_pos ) >> 8 ); + for (i = 0; i < vc->vc_cols; i++ ) { + buf[i] = (u_char ) scr_readw ((u_short * ) tmp ); + tmp += 2; + } + for (--i; i >= 0; i-- ) + if (buf[i] != SPACE ) break; + return ++i; +} + +static void +say_line (struct vc_data *vc ) +{ + int i = get_line( vc ); + char *cp; + char num_buf[8]; + u_short saved_punc_mask = punc_mask; + if (i == 0 ) { + synth_write_msg (blank_msg ); + return; + } + buf[i++] = '\n'; + if ( this_speakup_key == SAY_LINE_INDENT ) { + for ( cp = buf; *cp == SPACE; cp++ ); + sprintf( num_buf, "%d, ", ( cp-buf )+1 ); + synth_write_string( num_buf ); + } + punc_mask = punc_masks[reading_punc]; + spkup_write (buf, i ); + punc_mask = saved_punc_mask; +} + +static void +say_prev_line (struct vc_data *vc ) +{ + spk_parked |= 0x01; + if (spk_y == 0 ) { + announce_edge (vc, edge_top ); + return; + } + spk_y--; + spk_pos -= vc->vc_size_row; + say_line (vc ); +} + +static void +say_next_line (struct vc_data *vc ) +{ + spk_parked |= 0x01; + if (spk_y == vc->vc_rows - 1 ) { + announce_edge (vc, edge_bottom ); + return; + } + spk_y++; + spk_pos += vc->vc_size_row; + say_line (vc ); +} + +static int +say_from_to (struct vc_data *vc, u_long from, u_long to, int read_punc ) +{ + int i = 0; + u_short saved_punc_mask = punc_mask; + spk_old_attr = spk_attr; + spk_attr = (u_char ) (scr_readw ((u_short * ) from ) >> 8 ); + while (from < to ) { + buf[i++] = (char ) scr_readw ((u_short * ) from ); + from += 2; + if ( i >= vc->vc_size_row ) break; + } + for (--i; i >= 0; i-- ) + if (buf[i] != SPACE ) break; + buf[++i] = SPACE; + buf[++i] = '\0'; + if ( i < 1 ) return i; + if ( read_punc ) punc_mask = punc_info[reading_punc].mask; + spkup_write (buf, i ); + if ( read_punc ) punc_mask = saved_punc_mask; + return i-1; +} + +static void +say_line_from_to (struct vc_data *vc, u_long from, u_long to, int read_punc ) +{ + u_long start = vc->vc_origin+(spk_y*vc->vc_size_row ); + u_long end = start+( to * 2 ); + start += from*2; + if ( say_from_to( vc, start, end, read_punc ) <= 0 ) + synth_write_msg (blank_msg ); +} + +static void +say_screen_from_to (struct vc_data *vc, u_long from, u_long to ) +{ + u_long start = vc->vc_origin, end; + if ( from > 0 ) start += from * vc->vc_size_row; + if ( to > vc->vc_rows ) to = vc->vc_rows; + end = vc->vc_origin + ( to * vc->vc_size_row); + for ( from = start; from < end; from = to ) { + to = from + vc->vc_size_row; + say_from_to( vc, from, to, 1 ); + } +} + +static void +say_screen (struct vc_data *vc ) +{ + say_screen_from_to( vc, 0, vc->vc_rows ); +} + +static void +speakup_win_say (struct vc_data *vc ) +{ + u_long start, end, from, to; + if ( win_start < 2 ) { + synth_write_msg( "no window" ); + return; + } + start = vc->vc_origin + ( win_top * vc->vc_size_row ); + end = vc->vc_origin + ( win_bottom * vc->vc_size_row ); + while ( start <= end ) { + from = start + ( win_left * 2 ); + to = start + ( win_right * 2 ); + say_from_to( vc, from, to, 1 ); + start += vc->vc_size_row; + } +} + +static void +top_edge (struct vc_data *vc ) +{ + spk_parked |= 0x01; + spk_pos = vc->vc_origin + 2 * spk_x; + spk_y = 0; + say_line (vc ); +} + +static void +bottom_edge (struct vc_data *vc ) +{ + spk_parked |= 0x01; + spk_pos += (vc->vc_rows - spk_y - 1 ) * vc->vc_size_row; + spk_y = vc->vc_rows - 1; + say_line (vc ); +} + +static void +left_edge (struct vc_data *vc ) +{ + spk_parked |= 0x01; + spk_pos -= spk_x * 2; + spk_x = 0; + say_char (vc ); +} + +static void +right_edge (struct vc_data *vc ) +{ + spk_parked |= 0x01; + spk_pos += (vc->vc_cols - spk_x - 1 ) * 2; + spk_x = vc->vc_cols - 1; + say_char (vc ); +} + +static void +say_first_char (struct vc_data *vc ) +{ + int i, len = get_line( vc ); + u_char ch; + spk_parked |= 0x01; + if ( len == 0 ) { + synth_write_msg( blank_msg ); + return; + } + for ( i = 0; i < len; i++ ) if ( buf[i] != SPACE ) break; + ch = buf[i]; + spk_pos -= ( spk_x-i ) * 2; + spk_x = i; + sprintf (buf, "%d, ", ++i ); + synth_write_string (buf ); + speak_char( ch ); +} + +static void +say_last_char (struct vc_data *vc ) +{ + int len = get_line( vc ); + u_char ch; + spk_parked |= 0x01; + if ( len == 0 ) { + synth_write_msg( blank_msg ); + return; + } + ch = buf[--len]; + spk_pos -= ( spk_x-len ) * 2; + spk_x = len; + sprintf (buf, "%d, ", ++len ); + synth_write_string (buf ); + speak_char( ch ); +} + +static void +say_position (struct vc_data *vc ) +{ + sprintf (buf, "line %ld, col %ld, t t y %d\n", spk_y + 1, + spk_x + 1, vc->vc_num + 1 ); + synth_write_string (buf ); +} + +// Added by brianb +static void +say_char_num (struct vc_data *vc ) +{ + u_short ch = scr_readw ((u_short * ) spk_pos ); + ch &= 0x0ff; + sprintf (buf, "hex %02x, decimal %d", ch, ch ); + synth_write_msg (buf ); +} + +/* these are stub functions to keep keyboard.c happy. */ + +static void +say_from_top (struct vc_data *vc ) +{ + say_screen_from_to (vc, 0, spk_y ); +} + +static void +say_to_bottom (struct vc_data *vc ) +{ + say_screen_from_to (vc, spk_y, vc->vc_rows ); +} + +static void +say_from_left (struct vc_data *vc ) +{ + say_line_from_to (vc, 0, spk_x, 1 ); +} + +static void +say_to_right (struct vc_data *vc ) +{ + say_line_from_to (vc, spk_x, vc->vc_cols, 1 ); +} + +/* end of stub functions. */ + +static void +spkup_write (const char *in_buf, int count ) +{ + static int rep_count = 0; + static u_char ch = '\0', old_ch = '\0'; + static u_short char_type = 0, last_type = 0; + static u_char *exn_ptr = NULL; + int in_count = count; + char rpt_buf[32]; + spk_keydown = 0; + while ( count-- ) { + ch = (u_char )*in_buf++; + char_type = spk_chartab[ch]; + if (ch == old_ch && !(char_type&B_NUM ) ) { + if (++rep_count > 2 ) continue; + } else { + if ( (last_type&CH_RPT) && rep_count > 2 ) { + sprintf (rpt_buf, " times %d . ", ++rep_count ); + synth_write_string (rpt_buf ); + } + rep_count = 0; + } + if ( !( char_type&B_NUM ) ) + exn_ptr = NULL; + if (ch == spk_lastkey ) { + rep_count = 0; + if ( key_echo == 1 && ch >= MINECHOCHAR ) + speak_char( ch ); + } else if ( ( char_type&B_ALPHA ) ) { + if ( (synth_flags&SF_DEC) && (last_type&PUNC) ) + synth_buffer_add ( SPACE ); + synth_write( &ch, 1 ); + } else if ( ( char_type&B_NUM ) ) { + rep_count = 0; + if ( (last_type&B_EXNUM) && synth_buff_in == exn_ptr+1 ) { + synth_buff_in--; + synth_buffer_add( old_ch ); + exn_ptr = NULL; + } + synth_write( &ch, 1 ); + } else if ( (char_type&punc_mask) ) { + speak_char( ch ); + char_type &= ~PUNC; /* for dec nospell processing */ + } else if ( ( char_type&SYNTH_OK ) ) { +/* these are usually puncts like . and , which synth needs for expression. + * suppress multiple to get rid of long pausesand clear repeat count so if + *someone has repeats on you don't get nothing repeated count */ + if ( ch != old_ch ) + synth_write( &ch, 1 ); + else rep_count = 0; + } else { + if ( ( char_type&B_EXNUM ) ) + exn_ptr = (u_char *)synth_buff_in; +/* send space and record position, if next is num overwrite space */ + if ( old_ch != ch ) synth_buffer_add ( SPACE ); + else rep_count = 0; + } + old_ch = ch; + last_type = char_type; + } + spk_lastkey = 0; + if (in_count > 2 && rep_count > 2 ) { + if ( (last_type&CH_RPT) ) { + sprintf (rpt_buf, " repeated %d . ", ++rep_count ); + synth_write_string (rpt_buf ); + } + rep_count = 0; + } +} + +static char *ctl_key_ids[] = { + "shift", "altgr", "control", "ault", "l shift", "speakup", +"l control", "r control" +}; +#define NUM_CTL_LABELS 8 + +static void +handle_shift( KBD_PROTO ) +{ + (*do_shift)( KBD_ARGS ); + if ( synth == NULL || up_flag || spk_killed ) return; + spk_shut_up &= 0xfe; + do_flush( ); + if ( say_ctrl && value < NUM_CTL_LABELS ) + synth_write_string ( ctl_key_ids[value] ); +} + +static void +handle_latin( KBD_PROTO ) +{ + (*do_latin)( KBD_ARGS ); + if ( up_flag ) { + spk_lastkey = spk_keydown = 0; + return; + } + if ( synth == NULL || spk_killed ) return; + spk_shut_up &= 0xfe; + spk_lastkey = value; + spk_keydown++; + spk_parked &= 0xfe; + if ( key_echo == 2 && value >= MINECHOCHAR ) + speak_char( value ); +} + +static int +set_key_info( u_char *key_info, u_char *k_buffer ) +{ + int i = 0, states, key_data_len; + u_char *cp = key_info, *cp1 = k_buffer; + u_char ch, version, num_keys; + version = *cp++; + if ( version != KEY_MAP_VER ) return -1; + num_keys = *cp; + states = (int)cp[1]; + key_data_len = ( states+1 ) * ( num_keys+1 ); + if ( key_data_len+SHIFT_TBL_SIZE+4 >= sizeof(key_buf ) ) return -2; + memset( k_buffer, 0, SHIFT_TBL_SIZE ); + memset( our_keys, 0, sizeof( our_keys ) ); + shift_table = k_buffer; + our_keys[0] = shift_table; + cp1 += SHIFT_TBL_SIZE; + memcpy( cp1, cp, key_data_len+3 ); +/* get num_keys, states and data*/ + cp1 += 2; /* now pointing at shift states */ + for ( i = 1; i <= states; i++ ) { + ch = *cp1++; + if ( ch >= SHIFT_TBL_SIZE ) return -3; + shift_table[ch] = i; + } + keymap_flags = *cp1++; + while ( ( ch = *cp1 ) ) { + if ( ch >= MAX_KEY ) return -4; + our_keys[ch] = cp1; + cp1 += states+1; + } + return 0; +} + +num_var spk_num_vars[] = { /* bell must be first to set high limit */ + { BELL_POS, 0, 0, 0, 0, 0, 0, 0 }, + { SPELL_DELAY, 0, 0, 0, 5, 0, 0, 0 }, + { ATTRIB_BLEEP, 0, 1, 0, 3, 0, 0, 0 }, + { BLEEPS, 0, 3, 0, 3, 0, 0, 0 }, + { BLEEP_TIME, 0, 4, 1, 20, 0, 0, 0 }, + { PUNC_LEVEL, 0, 1, 0, 4, 0, 0, 0 }, + { READING_PUNC, 0, 1, 0, 4, 0, 0, 0 }, + { CURSOR_TIME, 0, 120, 50, 600, 0, 0, 0 }, + { SAY_CONTROL, TOGGLE_0 }, + { SAY_WORD_CTL, TOGGLE_0 }, + { NO_INTERRUPT, TOGGLE_0 }, + { KEY_ECHO, 0, 1, 0, 2, 0, 0, 0 }, + V_LAST_NUM +}; + +static int cursor_track = 1; +static char *cursor_msgs[] = { "cursoring off", "cursoring on", + "attribute cursor" }; +#define MAXCURSORTRACK 1 +/* increase when we add more cursor modes */ +/* attribute cursor code coming soon */ + +static void +toggle_cursoring( struct vc_data *vc ) +{ + cursor_track++; + if ( cursor_track > MAXCURSORTRACK ) + cursor_track = 0; + synth_write_msg (cursor_msgs[cursor_track] ); +} + +static void +reset_default_chars (void ) +{ + int i; + if (default_chars[(int )'a'] == NULL ) { /* lowers are null first time */ + for (i = (int )'a'; default_chars[i] == NULL; i++ ) + default_chars[i] = default_chars[i-32]; + } else { /* free any non-default */ + for (i = 0; i < 256; i++ ) { + if (characters[i] != default_chars[i] ) + kfree (characters[i] ); + } + } + memcpy( characters, default_chars, sizeof( default_chars ) ); +} + +static void +handle_cursor( KBD_PROTO ); +static void +handle_spec( KBD_PROTO ); +static void +cursor_done(u_long data ); +declare_timer( cursor_timer ); + +void __init speakup_open (struct vc_data *vc, spk_t *first_console ) +{ + int i; + num_var *n_var; + reset_default_chars ( ); + memset( speakup_console, 0, sizeof( speakup_console ) ); + if ( first_console == NULL ) return; + memset( first_console, 0, spk_size ); + speakup_console[vc->vc_num] = first_console; + speakup_date( vc); + pr_info ("%s: initialized\n", SPEAKUP_VERSION ); + init_timer (&cursor_timer ); +#if (LINUX_VERSION_CODE >= 132419) + cursor_timer.entry.prev=NULL; +#endif + cursor_timer.function = cursor_done; + init_sleeper ( synth_sleeping_list ); + strlwr (synth_name ); + synth_init ( synth_name ); + spk_num_vars[0].high = vc->vc_cols; + for ( n_var = spk_num_vars; n_var->var_id >= 0; n_var++ ) + speakup_register_var( n_var ); + for (i = 1; punc_info[i].mask != 0; i++ ) + set_mask_bits( 0, i, 2 ); + do_latin = key_handler[KT_LATIN]; + key_handler[KT_LATIN] = handle_latin; + do_spec = key_handler[KT_SPEC]; + key_handler[KT_SPEC] = handle_spec; + do_cursor = key_handler[KT_CUR]; + key_handler[KT_CUR] = handle_cursor; + do_shift = key_handler[KT_SHIFT]; + key_handler[KT_SHIFT] = handle_shift; + set_key_info( key_defaults, key_buf ); +} + +#ifdef CONFIG_PROC_FS + +// speakup /proc interface code + +/* Usage: +cat /proc/speakup/version + +cat /proc/speakup/characters > foo +less /proc/speakup/characters +vi /proc/speakup/characters + +cat foo > /proc/speakup/characters +cat > /proc/speakup/characters +echo 39 apostrophe > /proc/speakup/characters +echo 87 w > /proc/speakup/characters +echo 119 w > /proc/speakup/characters +echo defaults > /proc/speakup/characters +echo reset > /proc/speakup/characters +*/ + +// keymap handlers + +static int +keys_read_proc (PROC_READ_PROTOTYPE ) +{ + char *cp = page; + int i, n, num_keys, nstates; + u_char *cp1 = key_buf + SHIFT_TBL_SIZE, ch; + num_keys = (int)(*cp1); + nstates = (int)cp1[1]; + cp += sprintf( cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates ); + cp1 += 2; /* now pointing at shift states */ +/* dump num_keys+1 as first row is shift states + flags, + each subsequent row is key + states */ + for ( n = 0; n <= num_keys; n++ ) { + for ( i = 0; i <= nstates; i++ ) { + ch = *cp1++; + cp += sprintf( cp, "%d,", (int)ch ); + *cp++ = ( i < nstates ) ? SPACE : '\n'; + } + } + cp += sprintf( cp, "0, %d\n", KEY_MAP_VER ); + *start = 0; + *eof = 1; + return (int)(cp-page); +} + +static char * +s2uchar ( char *start, char *dest ) +{ + int val = 0; + while ( *start && *start <= SPACE ) start++; + while ( *start >= '0' && *start <= '9' ) { + val *= 10; + val += ( *start ) - '0'; + start++; + } + if ( *start == ',' ) start++; + *dest = (u_char)val; + return start; +} + +static int +keys_write_proc (PROC_WRITE_PROTOTYPE ) +{ + int i, ret = count; + char *in_buff, *cp; + u_char *cp1; + if (count < 1 || count > 1800 ) + return -EINVAL; + in_buff = ( char * ) __get_free_page ( GFP_KERNEL ); + if ( !in_buff ) return -ENOMEM; + if (copy_from_user (in_buff, buffer, count ) ) { + free_page ( ( unsigned long ) in_buff ); + return -EFAULT; + } + if (in_buff[count - 1] == '\n' ) count--; + in_buff[count] = '\0'; + if ( count == 1 && *in_buff == 'd' ) { + free_page ( ( unsigned long ) in_buff ); + set_key_info( key_defaults, key_buf ); + return ret; + } + cp = in_buff; + cp1 = (u_char *)in_buff; + for ( i = 0; i < 3; i++ ) { + cp = s2uchar( cp, cp1 ); + cp1++; + } + i = (int)cp1[-2]+1; + i *= (int)cp1[-1]+1; + i+= 2; /* 0 and last map ver */ + if ( cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 || + i+SHIFT_TBL_SIZE+4 >= sizeof(key_buf ) ) { +pr_warn( "i %d %d %d %d\n", i, (int)cp1[-3], (int)cp1[-2], (int)cp1[-1] ); + free_page ( ( unsigned long ) in_buff ); + return -EINVAL; + } + while ( --i >= 0 ) { + cp = s2uchar( cp, cp1 ); + cp1++; + if ( !(*cp) ) break; + } + if ( i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0 ) { + ret = -EINVAL; +pr_warn( "end %d %d %d %d\n", i, (int)cp1[-3], (int)cp1[-2], (int)cp1[-1] ); + } else { + if ( set_key_info( in_buff, key_buf ) ) { + set_key_info( key_defaults, key_buf ); + ret = -EINVAL; +pr_warn( "set key failed\n" ); + } + } + free_page ( ( unsigned long ) in_buff ); + return ret; +} + +// this is the handler for /proc/speakup/version +static int +version_read_proc (PROC_READ_PROTOTYPE ) +{ + int len = sprintf (page, "%s\n", SPEAKUP_VERSION ); + if ( synth != NULL ) + len += sprintf( page+len, "synth %s version %s\n", + synth->name, synth->version ); + *start = 0; + *eof = 1; + return len; +} + +// this is the read handler for /proc/speakup/characters +static int +chars_read_proc (PROC_READ_PROTOTYPE ) +{ + int i, len = 0; + off_t begin = 0; + char *cp; + for (i = 0; i < 256; i++ ) { + cp = (characters[i] ) ? characters[i] : "NULL"; + len += sprintf (page + len, "%d\t%s\n", i, cp ); + if (len + begin > off + count ) + break; + if (len + begin < off ) { + begin += len; + len = 0; + } + } + if (i >= 256 ) + *eof = 1; + if (off >= len + begin ) + return 0; + *start = page + (off - begin ); + return ((count < begin + len - off ) ? count : begin + len - off ); +} + +static volatile int chars_timer_active = 0; // indicates when timer is set +static declare_timer( chars_timer ); + +static inline void +chars_stop_timer (void ) +{ + if (chars_timer_active ) + stop_timer ( chars_timer ); +} + +static int strings, rejects, updates; + +static void +show_char_results (u_long data ) +{ + int len; + char buf[80]; + chars_stop_timer ( ); + len = sprintf (buf, " updated %d of %d character descriptions\n", + updates, strings ); + if (rejects ) + sprintf (buf + (len-1), " with %d reject%s\n", + rejects, rejects > 1 ? "s" : "" ); + printk( buf ); +} + +/* this is the write handler for /proc/speakup/silent */ +static int +silent_write_proc (PROC_WRITE_PROTOTYPE ) +{ + struct vc_data *vc = vc_cons[fg_console].d; + char ch = 0, shut; + if (count > 0 || count < 3 ) { + get_user (ch, buffer ); + if ( ch == '\n' ) ch = '0'; + } + if ( ch < '0' || ch > '7' ) { + pr_warn ( "silent value not in range (0,7)\n" ); + return count; + } + if ( (ch&2) ) { + shut = 1; + do_flush( ); + } else shut = 0; + if ( (ch&4) ) shut |= 0x40; + if ( (ch&1) ) + spk_shut_up |= shut; + else spk_shut_up &= ~shut; + return count; +} + +// this is the write handler for /proc/speakup/characters +static int +chars_write_proc (PROC_WRITE_PROTOTYPE ) +{ +#define max_desc_len 72 + static int cnt = 0, state = 0; + static char desc[max_desc_len + 1]; + static u_long jiff_last = 0; + short i = 0, num; + int len; + char ch, *cp, *p_new; + // reset certain vars if enough time has elapsed since last called + if (jiffies - jiff_last > 10 ) { + cnt = state = strings = rejects = updates = 0; + } + jiff_last = jiffies; +get_more: + desc[cnt] = '\0'; + state = 0; + for (; i < count && state < 2; i++ ) { + get_user (ch, buffer + i ); + if ( ch == '\n' ) { + desc[cnt] = '\0'; + state = 2; + } else if (cnt < max_desc_len ) + desc[cnt++] = ch; + } + if (state < 2 ) return count; + cp = desc; + while ( *cp && *cp <= SPACE ) cp++; + if ((!cnt ) || strchr ("dDrR", *cp ) ) { + reset_default_chars ( ); + pr_info( "character descriptions reset to defaults\n" ); + cnt = 0; + return count; + } + cnt = 0; + if (*cp == '#' ) goto get_more; + num = -1; + cp = speakup_s2i(cp, &num ); + while ( *cp && *cp <= SPACE ) cp++; + if (num < 0 || num > 255 ) { // not in range + rejects++; + strings++; + goto get_more; + } + if (num >= 27 && num <= 31 ) goto get_more; + if (!strcmp(cp, characters[num] ) ) { + strings++; + goto get_more; + } + len = strlen(cp ); + if (characters[num] == default_chars[num] ) + p_new = (char * ) kmalloc (sizeof (char ) * len+1, GFP_KERNEL ); + else if ( strlen(characters[num] ) >= len ) + p_new = characters[num]; + else { + kfree(characters[num] ); + characters[num] = default_chars[num]; + p_new = (char * ) kmalloc (sizeof (char ) * len+1, GFP_KERNEL ); + } + if (!p_new ) return -ENOMEM; + strcpy ( p_new, cp ); + characters[num] = p_new; + updates++; + strings++; + if (i < count ) goto get_more; + chars_stop_timer ( ); + init_timer (&chars_timer ); + chars_timer.function = show_char_results; + chars_timer.expires = jiffies + 5; + start_timer (chars_timer ); + chars_timer_active++; + return count; +} + +static int +bits_read_proc (PROC_READ_PROTOTYPE ) +{ + int i; + var_header *p_header = (var_header * )data; + proc_var *var = p_header->data; + bits_data *pb = &punc_info[var->value]; + short mask = pb->mask; + char *cp = page; + *start = 0; + *eof = 1; + for ( i = 33; i < 128; i++ ) { + if ( !(spk_chartab[i]&mask ) ) continue; + *cp++ = (char )i; + } + *cp++ = '\n'; + return cp-page; +} + +/* set_mask_bits sets or clears the punc/delim/repeat bits, + * if input is null uses the defaults. + * values for how: 0 clears bits of chars supplied, + * 1 clears allk, 2 sets bits for chars */ + +int +set_mask_bits( const char *input, const int which, const int how ) +{ + u_char *cp; + short mask = punc_info[which].mask; + if ( how&1 ) { + for ( cp = (u_char * )punc_info[3].value; *cp; cp++ ) + spk_chartab[*cp] &= ~mask; + } + cp = (u_char * )input; + if ( cp == 0 ) cp = punc_info[which].value; + else { + for ( ; *cp; cp++ ) { + if ( *cp < SPACE ) break; + if ( mask < PUNC ) { + if ( !(spk_chartab[*cp]&PUNC) ) break; + } else if ( (spk_chartab[*cp]&B_NUM) ) break; + } + if ( *cp ) return -EINVAL; + cp = (u_char * )input; + } + if ( how&2 ) { + for ( ; *cp; cp++ ) + if ( *cp > SPACE ) spk_chartab[*cp] |= mask; + } else { + for ( ; *cp; cp++ ) + if ( *cp > SPACE ) spk_chartab[*cp] &= ~mask; + } + return 0; +} + +static bits_data *pb_edit = NULL; + +static int edit_bits (struct vc_data *vc, u_char type, u_char ch, u_short key ) +{ + short mask = pb_edit->mask, ch_type = spk_chartab[ch]; + if ( type != KT_LATIN || (ch_type&B_NUM ) || ch < SPACE ) return -1; + if ( ch == SPACE ) { + synth_write_msg( "edit done" ); + special_handler = NULL; + return 1; + } + if ( mask < PUNC && !(ch_type&PUNC) ) return -1; + spk_chartab[ch] ^= mask; + speak_char( ch ); + synth_write_msg( (spk_chartab[ch]&mask ) ? " on" : " off" ); + return 1; +} + +static int +bits_write_proc (PROC_WRITE_PROTOTYPE ) +{ + var_header *p_header = (var_header * )data; + proc_var *var = p_header->data; + int ret = count; + char punc_buf[100]; + if (count < 1 || count > 99 ) + return -EINVAL; + if (copy_from_user (punc_buf, buffer, count ) ) + return -EFAULT; + if (punc_buf[count - 1] == '\n' ) + count--; + punc_buf[count] = '\0'; + if ( *punc_buf == 'd' || *punc_buf == 'r' ) + count = set_mask_bits( 0, var->value, 3 ); + else + count = set_mask_bits( punc_buf, var->value, 3 ); + if ( count < 0 ) return count; + return ret; +} + +// this is the read handler for /proc/speakup/synth +static int +synth_read_proc (PROC_READ_PROTOTYPE ) +{ + int len; + if ( synth == NULL ) strcpy( synth_name, "none" ); + else strcpy( synth_name, synth->name ); + len = sprintf (page, "%s\n", synth_name ); + *start = 0; + *eof = 1; + return len; +} + +// this is the write handler for /proc/speakup/synth +static int +synth_write_proc (PROC_WRITE_PROTOTYPE ) +{ + int ret = count; + char new_synth_name[10]; + const char *old_name = ( synth != NULL ) ? synth->name : "none"; + if (count < 2 || count > 9 ) + return -EINVAL; + if (copy_from_user (new_synth_name, buffer, count ) ) + return -EFAULT; + if (new_synth_name[count - 1] == '\n' ) + count--; + new_synth_name[count] = '\0'; + strlwr (new_synth_name ); + if (!strcmp (new_synth_name, old_name ) ) { + pr_warn ( "%s already in use\n", new_synt