diff options
44 files changed, 12518 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index a39f04f9a98..809673b60a7 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig | |||
@@ -173,5 +173,7 @@ source "drivers/staging/ft1000/Kconfig" | |||
173 | 173 | ||
174 | source "drivers/staging/intel_sst/Kconfig" | 174 | source "drivers/staging/intel_sst/Kconfig" |
175 | 175 | ||
176 | source "drivers/staging/speakup/Kconfig" | ||
177 | |||
176 | endif # !STAGING_EXCLUDE_BUILD | 178 | endif # !STAGING_EXCLUDE_BUILD |
177 | endif # STAGING | 179 | endif # STAGING |
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index d071a19d831..a81af6d4948 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile | |||
@@ -67,3 +67,4 @@ obj-$(CONFIG_USB_ENESTORAGE) += keucr/ | |||
67 | obj-$(CONFIG_BCM_WIMAX) += bcm/ | 67 | obj-$(CONFIG_BCM_WIMAX) += bcm/ |
68 | obj-$(CONFIG_FT1000) += ft1000/ | 68 | obj-$(CONFIG_FT1000) += ft1000/ |
69 | obj-$(CONFIG_SND_INTEL_SST) += intel_sst/ | 69 | obj-$(CONFIG_SND_INTEL_SST) += intel_sst/ |
70 | obj-$(CONFIG_SPEAKUP) += speakup/ | ||
diff --git a/drivers/staging/speakup/DefaultKeyAssignments b/drivers/staging/speakup/DefaultKeyAssignments new file mode 100644 index 00000000000..101c803b21f --- /dev/null +++ b/drivers/staging/speakup/DefaultKeyAssignments | |||
@@ -0,0 +1,46 @@ | |||
1 | This file is intended to give you an overview of the default keys used | ||
2 | by speakup for it's review functions. You may change them to be | ||
3 | anything you want but that will take some familiarity with key | ||
4 | mapping. | ||
5 | |||
6 | We have remapped the insert or zero key on the keypad to act as a | ||
7 | shift key. Well, actually as an altgr key. So in the following list | ||
8 | InsKeyPad-period means hold down the insert key like a shift key and | ||
9 | hit the keypad period. | ||
10 | |||
11 | KeyPad-8 Say current Line | ||
12 | InsKeyPad-8 say from top of screen to reading cursor. | ||
13 | KeyPad-7 Say Previous Line (UP one line) | ||
14 | KeyPad-9 Say Next Line (down one line) | ||
15 | KeyPad-5 Say Current Word | ||
16 | InsKeyPad-5 Spell Current Word | ||
17 | KeyPad-4 Say Previous Word (left one word) | ||
18 | InsKeyPad-4 say from left edge of line to reading cursor. | ||
19 | KeyPad-6 Say Next Word (right one word) | ||
20 | InsKeyPad-6 Say from reading cursor to right edge of line. | ||
21 | KeyPad-2 Say Current Letter | ||
22 | InsKeyPad-2 say current letter phonetically | ||
23 | KeyPad-1 Say Previous Character (left one letter) | ||
24 | KeyPad-3 Say Next Character (right one letter) | ||
25 | KeyPad-plus Say Entire Screen | ||
26 | InsKeyPad-plus Say from reading cursor line to bottom of screen. | ||
27 | KeyPad-Minus Park reading cursor (toggle) | ||
28 | InsKeyPad-minus Say character hex and decimal value. | ||
29 | KeyPad-period Say Position (current line, position and console) | ||
30 | InsKeyPad-period say colour attributes of current position. | ||
31 | InsKeyPad-9 Move reading cursor to top of screen (insert pgup) | ||
32 | InsKeyPad-3 Move reading cursor to bottom of screen (insert pgdn) | ||
33 | InsKeyPad-7 Move reading cursor to left edge of screen (insert home) | ||
34 | InsKeyPad-1 Move reading cursor to right edge of screen (insert end) | ||
35 | ControlKeyPad-1 Move reading cursor to last character on current line. | ||
36 | KeyPad-Enter Shut Up (until another key is hit) and sync reading cursor | ||
37 | InsKeyPad-Enter Shut Up (until toggled back on). | ||
38 | InsKeyPad-star n<x|y> go to line (y) or column (x). Where 'n' is any | ||
39 | allowed value for the row or column for your current screen. | ||
40 | KeyPad-/ Mark and Cut screen region. | ||
41 | InsKeyPad-/ Paste screen region into any console. | ||
42 | |||
43 | Hitting any key while speakup is outputting speech will quiet the | ||
44 | synth until it has caught up with what is being printed on the | ||
45 | console. | ||
46 | |||
diff --git a/drivers/staging/speakup/Kconfig b/drivers/staging/speakup/Kconfig new file mode 100644 index 00000000000..d288cf03e14 --- /dev/null +++ b/drivers/staging/speakup/Kconfig | |||
@@ -0,0 +1,195 @@ | |||
1 | menu "Speakup console speech" | ||
2 | |||
3 | config SPEAKUP | ||
4 | depends on VT | ||
5 | tristate "Speakup core" | ||
6 | ---help--- | ||
7 | This is the Speakup screen reader. Think of it as a | ||
8 | video console for blind people. If built in to the | ||
9 | kernel, it can speak everything on the text console from | ||
10 | boot up to shutdown. For more information on Speakup, | ||
11 | point your browser at http://www.linux-speakup.org/. | ||
12 | There is also a mailing list at the above url that you | ||
13 | can subscribe to. | ||
14 | |||
15 | Supported synthesizers are accent sa, accent pc, | ||
16 | appollo II., Auddapter, Braille 'n Speak, Dectalk | ||
17 | external (old), Dectalk PC (full length isa board), | ||
18 | Dectalk express, Doubletalk, Doubletalk LT or | ||
19 | Litetalk, Keynote Gold internal PC, software | ||
20 | synthesizers, Speakout, transport, and a dummy module | ||
21 | that can be used with a plain text terminal. | ||
22 | |||
23 | Speakup can either be built in or compiled as a module | ||
24 | by answering y or m. If you answer y here, then you | ||
25 | must answer either y or m to at least one of the | ||
26 | synthesizer drivers below. If you answer m here, then | ||
27 | the synthesizer drivers below can only be built as | ||
28 | modules. | ||
29 | |||
30 | These drivers are not standalone drivers, but must be | ||
31 | used in conjunction with Speakup. Think of them as | ||
32 | video cards for blind people. | ||
33 | |||
34 | |||
35 | The Dectalk pc driver can only be built as a module, and | ||
36 | requires software to be pre-loaded on to the card before | ||
37 | the module can be loaded. See the decpc choice below | ||
38 | for more details. | ||
39 | |||
40 | If you are not a blind person, or don't have access to | ||
41 | one of the listed synthesizers, you should say n. | ||
42 | |||
43 | if SPEAKUP | ||
44 | config SPEAKUP_SYNTH_ACNTSA | ||
45 | tristate "Accent SA synthesizer support" | ||
46 | ---help--- | ||
47 | This is the Speakup driver for the Accent SA | ||
48 | synthesizer. You can say y to build it into the kernel, | ||
49 | or m to build it as a module. See the configuration | ||
50 | help on the Speakup choice above for more info. | ||
51 | |||
52 | config SPEAKUP_SYNTH_ACNTPC | ||
53 | tristate "Accent PC synthesizer support" | ||
54 | ---help--- | ||
55 | This is the Speakup driver for the accent pc | ||
56 | synthesizer. You can say y to build it into the kernel, | ||
57 | or m to build it as a module. See the configuration | ||
58 | help on the Speakup choice above for more info. | ||
59 | |||
60 | config SPEAKUP_SYNTH_APOLLO | ||
61 | tristate "Apollo II synthesizer support" | ||
62 | ---help--- | ||
63 | This is the Speakup driver for the Apollo II | ||
64 | synthesizer. You can say y to build it into the kernel, | ||
65 | or m to build it as a module. See the configuration | ||
66 | help on the Speakup choice above for more info. | ||
67 | |||
68 | config SPEAKUP_SYNTH_AUDPTR | ||
69 | tristate "Audapter synthesizer support" | ||
70 | ---help--- | ||
71 | This is the Speakup driver for the Audapter synthesizer. | ||
72 | You can say y to build it into the kernel, or m to | ||
73 | build it as a module. See the configuration help on the | ||
74 | Speakup choice above for more info. | ||
75 | |||
76 | config SPEAKUP_SYNTH_BNS | ||
77 | tristate "Braille 'n' Speak synthesizer support" | ||
78 | ---help--- | ||
79 | This is the Speakup driver for the Braille 'n' Speak | ||
80 | synthesizer. You can say y to build it into the kernel, | ||
81 | or m to build it as a module. See the configuration | ||
82 | help on the Speakup choice above for more info. | ||
83 | |||
84 | config SPEAKUP_SYNTH_DECTLK | ||
85 | tristate "DECtalk Express synthesizer support" | ||
86 | ---help--- | ||
87 | |||
88 | This is the Speakup driver for the DecTalk Express | ||
89 | synthesizer. You can say y to build it into the kernel, | ||
90 | or m to build it as a module. See the configuration | ||
91 | help on the Speakup choice above for more info. | ||
92 | |||
93 | config SPEAKUP_SYNTH_DECEXT | ||
94 | tristate "DECtalk External (old) synthesizer support" | ||
95 | ---help--- | ||
96 | |||
97 | This is the Speakup driver for the DecTalk External | ||
98 | (old) synthesizer. You can say y to build it into the | ||
99 | kernel, or m to build it as a module. See the | ||
100 | configuration help on the Speakup choice above for more | ||
101 | info. | ||
102 | |||
103 | config SPEAKUP_SYNTH_DECPC | ||
104 | depends on m | ||
105 | tristate "DECtalk PC (big ISA card) synthesizer support" | ||
106 | ---help--- | ||
107 | |||
108 | This is the Speakup driver for the DecTalk PC (full | ||
109 | length ISA) synthesizer. You can say m to build it as | ||
110 | a module. See the configuration help on the Speakup | ||
111 | choice above for more info. | ||
112 | |||
113 | In order to use the DecTalk PC driver, you must download | ||
114 | the dec_pc.tgz file from linux-speakup.org. It is in | ||
115 | the pub/linux/goodies directory. The dec_pc.tgz file | ||
116 | contains the software which must be pre-loaded on to the | ||
117 | DecTalk PC board in order to use it with this driver. | ||
118 | This driver must be built as a module, and can not be | ||
119 | loaded until the file system is mounted and the DecTalk | ||
120 | PC software has been pre-loaded on to the board. | ||
121 | |||
122 | See the README file in the dec_pc.tgz file for more | ||
123 | details. | ||
124 | |||
125 | config SPEAKUP_SYNTH_DTLK | ||
126 | tristate "DoubleTalk PC synthesizer support" | ||
127 | ---help--- | ||
128 | |||
129 | This is the Speakup driver for the internal DoubleTalk | ||
130 | PC synthesizer. You can say y to build it into the | ||
131 | kernel, or m to build it as a module. See the | ||
132 | configuration help on the Speakup choice above for more | ||
133 | info. | ||
134 | |||
135 | config SPEAKUP_SYNTH_KEYPC | ||
136 | tristate "Keynote Gold PC synthesizer support" | ||
137 | ---help--- | ||
138 | |||
139 | This is the Speakup driver for the Keynote Gold | ||
140 | PC synthesizer. You can say y to build it into the | ||
141 | kernel, or m to build it as a module. See the | ||
142 | configuration help on the Speakup choice above for more | ||
143 | info. | ||
144 | |||
145 | config SPEAKUP_SYNTH_LTLK | ||
146 | tristate "DoubleTalk LT/LiteTalk synthesizer support" | ||
147 | ---help--- | ||
148 | |||
149 | This is the Speakup driver for the LiteTalk/DoubleTalk | ||
150 | LT synthesizer. You can say y to build it into the | ||
151 | kernel, or m to build it as a module. See the | ||
152 | configuration help on the Speakup choice above for more | ||
153 | info. | ||
154 | |||
155 | config SPEAKUP_SYNTH_SOFT | ||
156 | tristate "Userspace software synthesizer support" | ||
157 | ---help--- | ||
158 | |||
159 | This is the software synthesizer device node. It will | ||
160 | register a device /dev/softsynth which midware programs | ||
161 | and speech daemons may open and read to provide kernel | ||
162 | output to software synths such as espeak, festival, | ||
163 | flite and so forth. You can select 'y' or 'm' to have | ||
164 | it built-in to the kernel or loaded as a module. | ||
165 | |||
166 | config SPEAKUP_SYNTH_SPKOUT | ||
167 | tristate "Speak Out synthesizer support" | ||
168 | ---help--- | ||
169 | |||
170 | This is the Speakup driver for the Speakout synthesizer. | ||
171 | You can say y to build it into the kernel, or m to | ||
172 | build it as a module. See the configuration help on the | ||
173 | Speakup choice above for more info. | ||
174 | |||
175 | config SPEAKUP_SYNTH_TXPRT | ||
176 | tristate "Transport synthesizer support" | ||
177 | ---help--- | ||
178 | |||
179 | This is the Speakup driver for the Transport | ||
180 | synthesizer. You can say y to build it into the kernel, | ||
181 | or m to build it as a module. See the configuration | ||
182 | help on the Speakup choice above for more info. | ||
183 | |||
184 | config SPEAKUP_SYNTH_DUMMY | ||
185 | tristate "Dummy synthesizer driver (for testing)" | ||
186 | ---help--- | ||
187 | |||
188 | This is a dummy Speakup driver for plugging a mere serial | ||
189 | terminal. This is handy if you want to test speakup but | ||
190 | don't have the hardware. You can say y to build it into | ||
191 | the kernel, or m to build it as a module. See the | ||
192 | configuration help on the Speakup choice above for more info. | ||
193 | |||
194 | endif # SPEAKUP | ||
195 | endmenu | ||
diff --git a/drivers/staging/speakup/Makefile b/drivers/staging/speakup/Makefile new file mode 100644 index 00000000000..9923463687c --- /dev/null +++ b/drivers/staging/speakup/Makefile | |||
@@ -0,0 +1,30 @@ | |||
1 | obj-$(CONFIG_SPEAKUP_SYNTH_ACNTSA) += speakup_acntsa.o | ||
2 | obj-$(CONFIG_SPEAKUP_SYNTH_ACNTPC) += speakup_acntpc.o | ||
3 | obj-$(CONFIG_SPEAKUP_SYNTH_APOLLO) += speakup_apollo.o | ||
4 | obj-$(CONFIG_SPEAKUP_SYNTH_AUDPTR) += speakup_audptr.o | ||
5 | obj-$(CONFIG_SPEAKUP_SYNTH_BNS) += speakup_bns.o | ||
6 | obj-$(CONFIG_SPEAKUP_SYNTH_DECTLK) += speakup_dectlk.o | ||
7 | obj-$(CONFIG_SPEAKUP_SYNTH_DECEXT) += speakup_decext.o | ||
8 | obj-$(CONFIG_SPEAKUP_SYNTH_DECPC) += speakup_decpc.o | ||
9 | obj-$(CONFIG_SPEAKUP_SYNTH_DTLK) += speakup_dtlk.o | ||
10 | obj-$(CONFIG_SPEAKUP_SYNTH_KEYPC) += speakup_keypc.o | ||
11 | obj-$(CONFIG_SPEAKUP_SYNTH_LTLK) += speakup_ltlk.o | ||
12 | obj-$(CONFIG_SPEAKUP_SYNTH_SOFT) += speakup_soft.o | ||
13 | obj-$(CONFIG_SPEAKUP_SYNTH_SPKOUT) += speakup_spkout.o | ||
14 | obj-$(CONFIG_SPEAKUP_SYNTH_TXPRT) += speakup_txprt.o | ||
15 | obj-$(CONFIG_SPEAKUP_SYNTH_DUMMY) += speakup_dummy.o | ||
16 | |||
17 | obj-$(CONFIG_SPEAKUP) += speakup.o | ||
18 | speakup-objs := \ | ||
19 | buffers.o \ | ||
20 | devsynth.o \ | ||
21 | i18n.o \ | ||
22 | fakekey.o \ | ||
23 | main.o \ | ||
24 | keyhelp.o \ | ||
25 | kobjects.o \ | ||
26 | selection.o \ | ||
27 | serialio.o \ | ||
28 | synth.o \ | ||
29 | thread.o \ | ||
30 | varhandlers.o | ||
diff --git a/drivers/staging/speakup/TODO b/drivers/staging/speakup/TODO new file mode 100644 index 00000000000..c3612e4b8ac --- /dev/null +++ b/drivers/staging/speakup/TODO | |||
@@ -0,0 +1,47 @@ | |||
1 | Speakup project home: http://www.linux-speakup.org | ||
2 | |||
3 | Mailing List: speakup@braille.uwo.ca | ||
4 | |||
5 | Speakup is a kernel based screen review package for the linux operating | ||
6 | system. It allows blind users to interact with applications on the | ||
7 | linux console by means of synthetic speech. | ||
8 | |||
9 | Currently, speakup has several issues we know of. | ||
10 | |||
11 | The first issue has to do with the way speakup communicates with serial | ||
12 | ports. Currently, we communicate directly with the hardware | ||
13 | ports. This however conflicts with the standard serial port drivers, | ||
14 | which poses various problems. This is also not working for modern hardware | ||
15 | such as PCI-based serial ports. Also, there is not a way we can | ||
16 | communicate with USB devices. The current serial port handling code is | ||
17 | in serialio.c in this directory. | ||
18 | |||
19 | Some places are currently using in_atomic() because speakup functions | ||
20 | are called in various contexts, and a couple of things can't happen | ||
21 | in these cases. Pushing work to some worker thread would probably help, | ||
22 | as was already done for the serial port driving part. | ||
23 | |||
24 | There is a duplication of the selection functions in selections.c. These | ||
25 | functions should get exported from drivers/char/selection.c (clear_selection | ||
26 | notably) and used from there instead. | ||
27 | |||
28 | The kobjects may have to move to a more proper place in /sys. The | ||
29 | discussion on lkml resulted to putting speech synthesizers in the | ||
30 | "speech" class, and the speakup screen reader itself into | ||
31 | /sys/class/vtconsole/vtcon0/speakup, the nasty path being handled by | ||
32 | userland tools. | ||
33 | |||
34 | Another issue seems to only happen on SMP systems. It seems | ||
35 | that text in the output buffer gets garbled because a lock is not set. | ||
36 | This bug happens regularly, but no one has been able to find a situation | ||
37 | which produces it consistently. | ||
38 | |||
39 | Patches, suggestions, corrections, etc, are definitely welcome. | ||
40 | |||
41 | We prefer that you contact us on the mailing list; however, if you do | ||
42 | not want to subscribe to a mailing list, send your email to all of the | ||
43 | following: | ||
44 | |||
45 | w.d.hubbs@gmail.com, chris@the-brannons.com, kirk@braille.uwo.ca and | ||
46 | samuel.thibault@ens-lyon.org. | ||
47 | |||
diff --git a/drivers/staging/speakup/buffers.c b/drivers/staging/speakup/buffers.c new file mode 100644 index 00000000000..6dd53cf348f --- /dev/null +++ b/drivers/staging/speakup/buffers.c | |||
@@ -0,0 +1,108 @@ | |||
1 | #include <linux/console.h> | ||
2 | #include <linux/smp_lock.h> | ||
3 | #include <linux/types.h> | ||
4 | #include <linux/wait.h> | ||
5 | |||
6 | #include "speakup.h" | ||
7 | #include "spk_priv.h" | ||
8 | |||
9 | #define synthBufferSize 8192 /* currently 8K bytes */ | ||
10 | |||
11 | static u_char synth_buffer[synthBufferSize]; /* guess what this is for! */ | ||
12 | static u_char *buff_in = synth_buffer; | ||
13 | static u_char *buff_out = synth_buffer; | ||
14 | static u_char *buffer_end = synth_buffer+synthBufferSize-1; | ||
15 | |||
16 | /* These try to throttle applications by stopping the TTYs | ||
17 | * Note: we need to make sure that we will restart them eventually, which is | ||
18 | * usually not possible to do from the notifiers. TODO: it should be possible | ||
19 | * starting from linux 2.6.26. | ||
20 | * | ||
21 | * So we only stop when we know alive == 1 (else we discard the data anyway), | ||
22 | * and the alive synth will eventually call start_ttys from the thread context. | ||
23 | */ | ||
24 | void speakup_start_ttys(void) | ||
25 | { | ||
26 | int i; | ||
27 | |||
28 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | ||
29 | if (speakup_console[i] && speakup_console[i]->tty_stopped) | ||
30 | continue; | ||
31 | if ((vc_cons[i].d != NULL) && (vc_cons[i].d->vc_tty != NULL)) | ||
32 | start_tty(vc_cons[i].d->vc_tty); | ||
33 | } | ||
34 | } | ||
35 | EXPORT_SYMBOL_GPL(speakup_start_ttys); | ||
36 | |||
37 | static void speakup_stop_ttys(void) | ||
38 | { | ||
39 | int i; | ||
40 | |||
41 | for (i = 0; i < MAX_NR_CONSOLES; i++) | ||
42 | if ((vc_cons[i].d != NULL) && (vc_cons[i].d->vc_tty != NULL)) | ||
43 | stop_tty(vc_cons[i].d->vc_tty); | ||
44 | return; | ||
45 | } | ||
46 | |||
47 | static int synth_buffer_free(void) | ||
48 | { | ||
49 | int bytesFree; | ||
50 | |||
51 | if (buff_in >= buff_out) | ||
52 | bytesFree = synthBufferSize - (buff_in - buff_out); | ||
53 | else | ||
54 | bytesFree = buff_out - buff_in; | ||
55 | return bytesFree; | ||
56 | } | ||
57 | |||
58 | int synth_buffer_empty(void) | ||
59 | { | ||
60 | return (buff_in == buff_out); | ||
61 | } | ||
62 | EXPORT_SYMBOL_GPL(synth_buffer_empty); | ||
63 | |||
64 | void synth_buffer_add(char ch) | ||
65 | { | ||
66 | if (!synth->alive) { | ||
67 | /* This makes sure that we won't stop TTYs if there is no synth | ||
68 | * to restart them */ | ||
69 | return; | ||
70 | } | ||
71 | if (synth_buffer_free() <= 100) { | ||
72 | synth_start(); | ||
73 | speakup_stop_ttys(); | ||
74 | } | ||
75 | if (synth_buffer_free() <= 1) | ||
76 | return; | ||
77 | *buff_in++ = ch; | ||
78 | if (buff_in > buffer_end) | ||
79 | buff_in = synth_buffer; | ||
80 | } | ||
81 | |||
82 | char synth_buffer_getc(void) | ||
83 | { | ||
84 | char ch; | ||
85 | |||
86 | if (buff_out == buff_in) | ||
87 | return 0; | ||
88 | ch = *buff_out++; | ||
89 | if (buff_out > buffer_end) | ||
90 | buff_out = synth_buffer; | ||
91 | return ch; | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(synth_buffer_getc); | ||
94 | |||
95 | char synth_buffer_peek(void) | ||
96 | { | ||
97 | if (buff_out == buff_in) | ||
98 | return 0; | ||
99 | return *buff_out; | ||
100 | } | ||
101 | EXPORT_SYMBOL_GPL(synth_buffer_peek); | ||
102 | |||
103 | void synth_buffer_clear(void) | ||
104 | { | ||
105 | buff_in = buff_out = synth_buffer; | ||
106 | return; | ||
107 | } | ||
108 | EXPORT_SYMBOL_GPL(synth_buffer_clear); | ||
diff --git a/drivers/staging/speakup/devsynth.c b/drivers/staging/speakup/devsynth.c new file mode 100644 index 00000000000..a4656533dd5 --- /dev/null +++ b/drivers/staging/speakup/devsynth.c | |||
@@ -0,0 +1,92 @@ | |||
1 | #include <linux/errno.h> | ||
2 | #include <linux/miscdevice.h> /* for misc_register, and SYNTH_MINOR */ | ||
3 | #include <linux/types.h> | ||
4 | #include <linux/uaccess.h> | ||
5 | |||
6 | #include "speakup.h" | ||
7 | #include "spk_priv.h" | ||
8 | |||
9 | #ifndef SYNTH_MINOR | ||
10 | #define SYNTH_MINOR 25 | ||
11 | #endif | ||
12 | |||
13 | static int misc_registered; | ||
14 | static int dev_opened; | ||
15 | |||
16 | static ssize_t speakup_file_write(struct file *fp, const char *buffer, | ||
17 | size_t nbytes, loff_t *ppos) | ||
18 | { | ||
19 | size_t count = nbytes; | ||
20 | const char *ptr = buffer; | ||
21 | int bytes; | ||
22 | unsigned long flags; | ||
23 | u_char buf[256]; | ||
24 | if (synth == NULL) | ||
25 | return -ENODEV; | ||
26 | while (count > 0) { | ||
27 | bytes = min_t(size_t, count, sizeof(buf)); | ||
28 | if (copy_from_user(buf, ptr, bytes)) | ||
29 | return -EFAULT; | ||
30 | count -= bytes; | ||
31 | ptr += bytes; | ||
32 | spk_lock(flags); | ||
33 | synth_write(buf, bytes); | ||
34 | spk_unlock(flags); | ||
35 | } | ||
36 | return (ssize_t) nbytes; | ||
37 | } | ||
38 | |||
39 | static ssize_t speakup_file_read(struct file *fp, char *buf, size_t nbytes, loff_t *ppos) | ||
40 | { | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | static int speakup_file_open(struct inode *ip, struct file *fp) | ||
45 | { | ||
46 | if (synth == NULL) | ||
47 | return -ENODEV; | ||
48 | if (xchg(&dev_opened, 1)) | ||
49 | return -EBUSY; | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static int speakup_file_release(struct inode *ip, struct file *fp) | ||
54 | { | ||
55 | dev_opened = 0; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static struct file_operations synth_fops = { | ||
60 | .read = speakup_file_read, | ||
61 | .write = speakup_file_write, | ||
62 | .open = speakup_file_open, | ||
63 | .release = speakup_file_release, | ||
64 | }; | ||
65 | |||
66 | static struct miscdevice synth_device = { | ||
67 | .minor = SYNTH_MINOR, | ||
68 | .name = "synth", | ||
69 | .fops = &synth_fops, | ||
70 | }; | ||
71 | |||
72 | void speakup_register_devsynth(void) | ||
73 | { | ||
74 | if (misc_registered != 0) | ||
75 | return; | ||
76 | /* zero it so if register fails, deregister will not ref invalid ptrs */ | ||
77 | if (misc_register(&synth_device)) | ||
78 | pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); | ||
79 | else { | ||
80 | pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", MISC_MAJOR, SYNTH_MINOR); | ||
81 | misc_registered = 1; | ||
82 | } | ||
83 | } | ||
84 | |||
85 | void speakup_unregister_devsynth(void) | ||
86 | { | ||
87 | if (!misc_registered) | ||
88 | return; | ||
89 | pr_info("speakup: unregistering synth device /dev/synth\n"); | ||
90 | misc_deregister(&synth_device); | ||
91 | misc_registered = 0; | ||
92 | } | ||
diff --git a/drivers/staging/speakup/fakekey.c b/drivers/staging/speakup/fakekey.c new file mode 100644 index 00000000000..adb93f21c0d --- /dev/null +++ b/drivers/staging/speakup/fakekey.c | |||
@@ -0,0 +1,105 @@ | |||
1 | /* fakekey.c | ||
2 | * Functions for simulating keypresses. | ||
3 | * | ||
4 | * Copyright (C) 2010 the Speakup Team | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/preempt.h> | ||
23 | #include <linux/percpu.h> | ||
24 | #include <linux/input.h> | ||
25 | |||
26 | #include "speakup.h" | ||
27 | |||
28 | #define PRESSED 1 | ||
29 | #define RELEASED 0 | ||
30 | |||
31 | DEFINE_PER_CPU(bool, reporting_keystroke); | ||
32 | |||
33 | static struct input_dev *virt_keyboard; | ||
34 | |||
35 | int speakup_add_virtual_keyboard(void) | ||
36 | { | ||
37 | int err; | ||
38 | |||
39 | virt_keyboard = input_allocate_device(); | ||
40 | |||
41 | if (!virt_keyboard) | ||
42 | return -ENOMEM; | ||
43 | |||
44 | virt_keyboard->name = "Speakup"; | ||
45 | virt_keyboard->id.bustype = BUS_VIRTUAL; | ||
46 | virt_keyboard->phys = "speakup/input0"; | ||
47 | virt_keyboard->dev.parent = NULL; | ||
48 | |||
49 | __set_bit(EV_KEY, virt_keyboard->evbit); | ||
50 | __set_bit(KEY_DOWN, virt_keyboard->keybit); | ||
51 | |||
52 | err = input_register_device(virt_keyboard); | ||
53 | if (err) { | ||
54 | input_free_device(virt_keyboard); | ||
55 | virt_keyboard = NULL; | ||
56 | } | ||
57 | |||
58 | return err; | ||
59 | } | ||
60 | |||
61 | void speakup_remove_virtual_keyboard(void) | ||
62 | { | ||
63 | if (virt_keyboard != NULL) { | ||
64 | input_unregister_device(virt_keyboard); | ||
65 | input_free_device(virt_keyboard); | ||
66 | virt_keyboard = NULL; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Send a simulated down-arrow to the application. | ||
72 | */ | ||
73 | void speakup_fake_down_arrow(void) | ||
74 | { | ||
75 | unsigned long flags; | ||
76 | |||
77 | /* disable keyboard interrupts */ | ||
78 | local_irq_save(flags); | ||
79 | /* don't change CPU */ | ||
80 | preempt_disable(); | ||
81 | |||
82 | __get_cpu_var(reporting_keystroke) = true; | ||
83 | input_report_key(virt_keyboard, KEY_DOWN, PRESSED); | ||
84 | input_report_key(virt_keyboard, KEY_DOWN, RELEASED); | ||
85 | __get_cpu_var(reporting_keystroke) = false; | ||
86 | |||
87 | /* reenable preemption */ | ||
88 | preempt_enable(); | ||
89 | /* reenable keyboard interrupts */ | ||
90 | local_irq_restore(flags); | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Are we handling a simulated keypress on the current CPU? | ||
95 | * Returns a boolean. | ||
96 | */ | ||
97 | bool speakup_fake_key_pressed(void) | ||
98 | { | ||
99 | bool is_pressed; | ||
100 | |||
101 | is_pressed = get_cpu_var(reporting_keystroke); | ||
102 | put_cpu_var(reporting_keystroke); | ||
103 | |||
104 | return is_pressed; | ||
105 | } | ||
diff --git a/drivers/staging/speakup/i18n.c b/drivers/staging/speakup/i18n.c new file mode 100644 index 00000000000..4d12d0ae3bc --- /dev/null +++ b/drivers/staging/speakup/i18n.c | |||
@@ -0,0 +1,622 @@ | |||
1 | /* Internationalization implementation. Includes definitions of English | ||
2 | * string arrays, and the i18n pointer. */ | ||
3 | |||
4 | #include <linux/slab.h> /* For kmalloc. */ | ||
5 | #include <linux/ctype.h> | ||
6 | #include <linux/module.h> | ||
7 | #include <linux/string.h> | ||
8 | #include "speakup.h" | ||
9 | #include "spk_priv.h" | ||
10 | |||
11 | static char *speakup_msgs[MSG_LAST_INDEX]; | ||
12 | static char *speakup_default_msgs [MSG_LAST_INDEX] = { | ||
13 | [MSG_BLANK] = "blank", | ||
14 | [MSG_IAM_ALIVE] = "I'm aLive!", | ||
15 | [MSG_YOU_KILLED_SPEAKUP] = "You killed speakup!", | ||
16 | [MSG_HEY_THATS_BETTER] = "hey. That's better!", | ||
17 | [MSG_YOU_TURNED_ME_OFF] = "You turned me off!", | ||
18 | [MSG_PARKED] = "parked!", | ||
19 | [MSG_UNPARKED] = "unparked!", | ||
20 | [MSG_MARK] = "mark", | ||
21 | [MSG_CUT] = "cut", | ||
22 | [MSG_MARK_CLEARED] = "mark, cleared", | ||
23 | [MSG_PASTE] = "paste", | ||
24 | [MSG_BRIGHT] = "bright", | ||
25 | [MSG_ON_BLINKING] = "on blinking", | ||
26 | [MSG_OFF] = "off", | ||
27 | [MSG_ON] = "on", | ||
28 | [MSG_NO_WINDOW] = "no window", | ||
29 | [MSG_CURSORING_OFF] = "cursoring off", | ||
30 | [MSG_CURSORING_ON] = "cursoring on", | ||
31 | [MSG_HIGHLIGHT_TRACKING] = "highlight tracking", | ||
32 | [MSG_READ_WINDOW] = "read windo", | ||
33 | [MSG_READ_ALL] = "read all", | ||
34 | [MSG_EDIT_DONE] = "edit done", | ||
35 | [MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset", | ||
36 | [MSG_END_BEFORE_START] = "error end before start", | ||
37 | [MSG_WINDOW_CLEARED] = "window cleared", | ||
38 | [MSG_WINDOW_SILENCED] = "window silenced", | ||
39 | [MSG_WINDOW_SILENCE_DISABLED] = "window silence disabled", | ||
40 | [MSG_ERROR] = "error", | ||
41 | [MSG_GOTO_CANCELED] = "goto canceled", | ||
42 | [MSG_GOTO] = "go to?", | ||
43 | [MSG_LEAVING_HELP] = "leaving help", | ||
44 | [MSG_IS_UNASSIGNED] = "is unassigned", | ||
45 | [MSG_HELP_INFO] = "press space to leav help, cursor up or down to scroll, or a letter to go to commands in list", | ||
46 | [MSG_EDGE_TOP] = "top,", | ||
47 | [MSG_EDGE_BOTTOM] = "bottom,", | ||
48 | [MSG_EDGE_LEFT] = "left,", | ||
49 | [MSG_EDGE_RIGHT] = "right,", | ||
50 | [MSG_NUMBER] = "number", | ||
51 | [MSG_SPACE] = "space", | ||
52 | [MSG_START] = "start", | ||
53 | [MSG_END] = "end", | ||
54 | [MSG_CTRL] = "control-", | ||
55 | [MSG_DISJUNCTION] = "or", | ||
56 | |||
57 | /* Messages with embedded format specifiers. */ | ||
58 | [MSG_POS_INFO] = "line %ld, col %ld, t t y %d", | ||
59 | [MSG_CHAR_INFO] = "hex %02x, decimal %d", | ||
60 | [MSG_REPEAT_DESC] = "times %d .", | ||
61 | [MSG_REPEAT_DESC2] = "repeated %d .", | ||
62 | [MSG_WINDOW_LINE] = "window is line %d", | ||
63 | [MSG_WINDOW_BOUNDARY] = "%s at line %d, column %d", | ||
64 | [MSG_EDIT_PROMPT] = "edit %s, press space when done", | ||
65 | [MSG_NO_COMMAND] = "no commands for %c", | ||
66 | [MSG_KEYDESC] = "is %s", | ||
67 | |||
68 | /* Control keys. */ | ||
69 | /* Most of these duplicate the entries in state names. */ | ||
70 | [MSG_CTL_SHIFT] = "shift", | ||
71 | [MSG_CTL_ALTGR] = "altgr", | ||
72 | [MSG_CTL_CONTROL] = "control", | ||
73 | [MSG_CTL_ALT] = "ault", | ||
74 | [MSG_CTL_LSHIFT] = "l shift", | ||
75 | [MSG_CTL_SPEAKUP] = "speakup", | ||
76 | [MSG_CTL_LCONTROL] = "l control", | ||
77 | [MSG_CTL_RCONTROL] = "r control", | ||
78 | [MSG_CTL_CAPSSHIFT] = "caps shift", | ||
79 | |||
80 | /* Color names. */ | ||
81 | [MSG_COLOR_BLACK] = "black", | ||
82 | [MSG_COLOR_BLUE] = "blue", | ||
83 | [MSG_COLOR_GREEN] = "green", | ||
84 | [MSG_COLOR_CYAN] = "cyan", | ||
85 | [MSG_COLOR_RED] = "red", | ||
86 | [MSG_COLOR_MAGENTA] = "magenta", | ||
87 | [MSG_COLOR_YELLOW] = "yellow", | ||
88 | [MSG_COLOR_WHITE] = "white", | ||
89 | [MSG_COLOR_GREY] = "grey", | ||
90 | |||
91 | /* Names of key states. */ | ||
92 | [MSG_STATE_DOUBLE] = "double", | ||
93 | [MSG_STATE_SPEAKUP] = "speakup", | ||
94 | [MSG_STATE_ALT] = "alt", | ||
95 | [MSG_STATE_CONTROL] = "ctrl", | ||
96 | [MSG_STATE_ALTGR] = "altgr", | ||
97 | [MSG_STATE_SHIFT] = "shift", | ||
98 | |||
99 | /* Key names. */ | ||
100 | [MSG_KEYNAME_ESC] = "escape", | ||
101 | [MSG_KEYNAME_1] = "1", | ||
102 | [MSG_KEYNAME_2] = "2", | ||
103 | [MSG_KEYNAME_3] = "3", | ||
104 | [MSG_KEYNAME_4] = "4", | ||
105 | [MSG_KEYNAME_5] = "5", | ||
106 | [MSG_KEYNAME_6] = "6", | ||
107 | [MSG_KEYNAME_7] = "7", | ||
108 | [MSG_KEYNAME_8] = "8", | ||
109 | [MSG_KEYNAME_9] = "9", | ||
110 | [MSG_KEYNAME_0] = "0", | ||
111 | [MSG_KEYNAME_DASH] = "minus", | ||
112 | [MSG_KEYNAME_EQUAL] = "equal", | ||
113 | [MSG_KEYNAME_BS] = "back space", | ||
114 | [MSG_KEYNAME_TAB] = "tab", | ||
115 | [MSG_KEYNAME_Q] = "q", | ||
116 | [MSG_KEYNAME_W] = "w", | ||
117 | [MSG_KEYNAME_E] = "e", | ||
118 | [MSG_KEYNAME_R] = "r", | ||
119 | [MSG_KEYNAME_T] = "t", | ||
120 | [MSG_KEYNAME_Y] = "y", | ||
121 | [MSG_KEYNAME_U] = "u", | ||
122 | [MSG_KEYNAME_I] = "i", | ||
123 | [MSG_KEYNAME_O] = "o", | ||
124 | [MSG_KEYNAME_P] = "p", | ||
125 | [MSG_KEYNAME_LEFTBRACE] = "left brace", | ||
126 | [MSG_KEYNAME_RIGHTBRACE] = "right brace", | ||
127 | [MSG_KEYNAME_ENTER] = "enter", | ||
128 | [MSG_KEYNAME_LEFTCTRL] = "left control", | ||
129 | [MSG_KEYNAME_A] = "a", | ||
130 | [MSG_KEYNAME_S] = "s", | ||
131 | [MSG_KEYNAME_D] = "d", | ||
132 | [MSG_KEYNAME_F] = "f", | ||
133 | [MSG_KEYNAME_G] = "g", | ||
134 | [MSG_KEYNAME_H] = "h", | ||
135 | [MSG_KEYNAME_J] = "j", | ||
136 | [MSG_KEYNAME_K] = "k", | ||
137 | [MSG_KEYNAME_L] = "l", | ||
138 | [MSG_KEYNAME_SEMICOLON] = "semicolon", | ||
139 | [MSG_KEYNAME_SINGLEQUOTE] = "apostrophe", | ||
140 | [MSG_KEYNAME_GRAVE] = "accent", | ||
141 | [MSG_KEYNAME_LEFTSHFT] = "left shift", | ||
142 | [MSG_KEYNAME_BACKSLASH] = "back slash", | ||
143 | [MSG_KEYNAME_Z] = "z", | ||
144 | [MSG_KEYNAME_X] = "x", | ||
145 | [MSG_KEYNAME_C] = "c", | ||
146 | [MSG_KEYNAME_V] = "v", | ||
147 | [MSG_KEYNAME_B] = "b", | ||
148 | [MSG_KEYNAME_N] = "n", | ||
149 | [MSG_KEYNAME_M] = "m", | ||
150 | [MSG_KEYNAME_COMMA] = "comma", | ||
151 | [MSG_KEYNAME_DOT] = "dot", | ||
152 | [MSG_KEYNAME_SLASH] = "slash", | ||
153 | [MSG_KEYNAME_RIGHTSHFT] = "right shift", | ||
154 | [MSG_KEYNAME_KPSTAR] = "keypad asterisk", | ||
155 | [MSG_KEYNAME_LEFTALT] = "left alt", | ||
156 | [MSG_KEYNAME_SPACE] = "space", | ||
157 | [MSG_KEYNAME_CAPSLOCK] = "caps lock", | ||
158 | [MSG_KEYNAME_F1] = "f1", | ||
159 | [MSG_KEYNAME_F2] = "f2", | ||
160 | [MSG_KEYNAME_F3] = "f3", | ||
161 | [MSG_KEYNAME_F4] = "f4", | ||
162 | [MSG_KEYNAME_F5] = "f5", | ||
163 | [MSG_KEYNAME_F6] = "f6", | ||
164 | [MSG_KEYNAME_F7] = "f7", | ||
165 | [MSG_KEYNAME_F8] = "f8", | ||
166 | [MSG_KEYNAME_F9] = "f9", | ||
167 | [MSG_KEYNAME_F10] = "f10", | ||
168 | [MSG_KEYNAME_NUMLOCK] = "num lock", | ||
169 | [MSG_KEYNAME_SCROLLLOCK] = "scroll lock", | ||
170 | [MSG_KEYNAME_KP7] = "keypad 7", | ||
171 | [MSG_KEYNAME_KP8] = "keypad 8", | ||
172 | [MSG_KEYNAME_KP9] = "keypad 9", | ||
173 | [MSG_KEYNAME_KPMINUS] = "keypad minus", | ||
174 | [MSG_KEYNAME_KP4] = "keypad 4", | ||
175 | [MSG_KEYNAME_KP5] = "keypad 5", | ||
176 | [MSG_KEYNAME_KP6] = "keypad 6", | ||
177 | [MSG_KEYNAME_KPPLUS] = "keypad plus", | ||
178 | [MSG_KEYNAME_KP1] = "keypad 1", | ||
179 | [MSG_KEYNAME_KP2] = "keypad 2", | ||
180 | [MSG_KEYNAME_KP3] = "keypad 3", | ||
181 | [MSG_KEYNAME_KP0] = "keypad 0", | ||
182 | [MSG_KEYNAME_KPDOT] = "keypad dot", | ||
183 | [MSG_KEYNAME_103RD] = "103rd", | ||
184 | [MSG_KEYNAME_F13] = "f13", | ||
185 | [MSG_KEYNAME_102ND] = "102nd", | ||
186 | [MSG_KEYNAME_F11] = "f11", | ||
187 | [MSG_KEYNAME_F12] = "f12", | ||
188 | [MSG_KEYNAME_F14] = "f14", | ||
189 | [MSG_KEYNAME_F15] = "f15", | ||
190 | [MSG_KEYNAME_F16] = "f16", | ||
191 | [MSG_KEYNAME_F17] = "f17", | ||
192 | [MSG_KEYNAME_F18] = "f18", | ||
193 | [MSG_KEYNAME_F19] = "f19", | ||
194 | [MSG_KEYNAME_F20] = "f20", | ||
195 | [MSG_KEYNAME_KPENTER] = "keypad enter", | ||
196 | [MSG_KEYNAME_RIGHTCTRL] = "right control", | ||
197 | [MSG_KEYNAME_KPSLASH] = "keypad slash", | ||
198 | [MSG_KEYNAME_SYSRQ] = "sysrq", | ||
199 | [MSG_KEYNAME_RIGHTALT] = "right alt", | ||
200 | [MSG_KEYNAME_LF] = "line feed", | ||
201 | [MSG_KEYNAME_HOME] = "home", | ||
202 | [MSG_KEYNAME_UP] = "up", | ||
203 | [MSG_KEYNAME_PGUP] = "page up", | ||
204 | [MSG_KEYNAME_LEFT] = "left", | ||
205 | [MSG_KEYNAME_RIGHT] = "right", | ||
206 | [MSG_KEYNAME_END] = "end", | ||
207 | [MSG_KEYNAME_DOWN] = "down", | ||
208 | [MSG_KEYNAME_PGDN] = "page down", | ||
209 | [MSG_KEYNAME_INS] = "insert", | ||
210 | [MSG_KEYNAME_DEL] = "delete", | ||
211 | [MSG_KEYNAME_MACRO] = "macro", | ||
212 | [MSG_KEYNAME_MUTE] = "mute", | ||
213 | [MSG_KEYNAME_VOLDOWN] = "volume down", | ||
214 | [MSG_KEYNAME_VOLUP] = "volume up", | ||
215 | [MSG_KEYNAME_POWER] = "power", | ||
216 | [MSG_KEYNAME_KPEQUAL] = "keypad equal", | ||
217 | [MSG_KEYNAME_KPPLUSDASH] = "keypad plusminus", | ||
218 | [MSG_KEYNAME_PAUSE] = "pause", | ||
219 | [MSG_KEYNAME_F21] = "f21", | ||
220 | [MSG_KEYNAME_F22] = "f22", | ||
221 | [MSG_KEYNAME_F23] = "f23", | ||
222 | [MSG_KEYNAME_F24] = "f24", | ||
223 | [MSG_KEYNAME_KPCOMMA] = "keypad comma", | ||
224 | [MSG_KEYNAME_LEFTMETA] = "left meta", | ||
225 | [MSG_KEYNAME_RIGHTMETA] = "right meta", | ||
226 | [MSG_KEYNAME_COMPOSE] = "compose", | ||
227 | [MSG_KEYNAME_STOP] = "stop", | ||
228 | [MSG_KEYNAME_AGAIN] = "again", | ||
229 | [MSG_KEYNAME_PROPS] = "props", | ||
230 | [MSG_KEYNAME_UNDO] = "undo", | ||
231 | [MSG_KEYNAME_FRONT] = "front", | ||
232 | [MSG_KEYNAME_COPY] = "copy", | ||
233 | [MSG_KEYNAME_OPEN] = "open", | ||
234 | [MSG_KEYNAME_PASTE] = "paste", | ||
235 | [MSG_KEYNAME_FIND] = "find", | ||
236 | [MSG_KEYNAME_CUT] = "cut", | ||
237 | [MSG_KEYNAME_HELP] = "help", | ||
238 | [MSG_KEYNAME_MENU] = "menu", | ||
239 | [MSG_KEYNAME_CALC] = "calc", | ||
240 | [MSG_KEYNAME_SETUP] = "setup", | ||
241 | [MSG_KEYNAME_SLEEP] = "sleep", | ||
242 | [MSG_KEYNAME_WAKEUP] = "wakeup", | ||
243 | [MSG_KEYNAME_FILE] = "file", | ||
244 | [MSG_KEYNAME_SENDFILE] = "send file", | ||
245 | [MSG_KEYNAME_DELFILE] = "delete file", | ||
246 | [MSG_KEYNAME_XFER] = "transfer", | ||
247 | [MSG_KEYNAME_PROG1] = "prog1", | ||
248 | [MSG_KEYNAME_PROG2] = "prog2", | ||
249 | [MSG_KEYNAME_WWW] = "www", | ||
250 | [MSG_KEYNAME_MSDOS] = "msdos", | ||
251 | [MSG_KEYNAME_COFFEE] = "coffee", | ||
252 | [MSG_KEYNAME_DIRECTION] = "direction", | ||
253 | [MSG_KEYNAME_CYCLEWINDOWS] = "cycle windows", | ||
254 | [MSG_KEYNAME_MAIL] = "mail", | ||
255 | [MSG_KEYNAME_BOOKMARKS] = "bookmarks", | ||
256 | [MSG_KEYNAME_COMPUTER] = "computer", | ||
257 | [MSG_KEYNAME_BACK] = "back", | ||
258 | [MSG_KEYNAME_FORWARD] = "forward", | ||
259 | [MSG_KEYNAME_CLOSECD] = "close cd", | ||
260 | [MSG_KEYNAME_EJECTCD] = "eject cd", | ||
261 | [MSG_KEYNAME_EJECTCLOSE] = "eject close cd", | ||
262 | [MSG_KEYNAME_NEXTSONG] = "next song", | ||
263 | [MSG_KEYNAME_PLAYPAUSE] = "play pause", | ||
264 | [MSG_KEYNAME_PREVSONG] = "previous song", | ||
265 | [MSG_KEYNAME_STOPCD] = "stop cd", | ||
266 | [MSG_KEYNAME_RECORD] = "record", | ||
267 | [MSG_KEYNAME_REWIND] = "rewind", | ||
268 | [MSG_KEYNAME_PHONE] = "phone", | ||
269 | [MSG_KEYNAME_ISO] = "iso", | ||
270 | [MSG_KEYNAME_CONFIG] = "config", | ||
271 | [MSG_KEYNAME_HOMEPG] = "home page", | ||
272 | [MSG_KEYNAME_REFRESH] = "refresh", | ||
273 | [MSG_KEYNAME_EXIT] = "exit", | ||
274 | [MSG_KEYNAME_MOVE] = "move", | ||
275 | [MSG_KEYNAME_EDIT] = "edit", | ||
276 | [MSG_KEYNAME_SCROLLUP] = "scroll up", | ||
277 | [MSG_KEYNAME_SCROLLDN] = "scroll down", | ||
278 | [MSG_KEYNAME_KPLEFTPAR] = "keypad left paren", | ||
279 | [MSG_KEYNAME_KPRIGHTPAR] = "keypad right paren", | ||
280 | |||
281 | /* Function names. */ | ||
282 | [MSG_FUNCNAME_ATTRIB_BLEEP_DEC] = "attribute bleep decrement", | ||
283 | [MSG_FUNCNAME_ATTRIB_BLEEP_INC] = "attribute bleep increment", | ||
284 | [MSG_FUNCNAME_BLEEPS_DEC] = "bleeps decrement", | ||
285 | [MSG_FUNCNAME_BLEEPS_INC] = "bleeps increment", | ||
286 | [MSG_FUNCNAME_CHAR_FIRST] = "character, first", | ||
287 | [MSG_FUNCNAME_CHAR_LAST] = "character, last", | ||
288 | [MSG_FUNCNAME_CHAR_CURRENT] = "character, say current", | ||
289 | [MSG_FUNCNAME_CHAR_HEX_AND_DEC] = "character, say hex and decimal", | ||
290 | [MSG_FUNCNAME_CHAR_NEXT] = "character, say next", | ||
291 | [MSG_FUNCNAME_CHAR_PHONETIC] = "character, say phonetic", | ||
292 | [MSG_FUNCNAME_CHAR_PREVIOUS] = "character, say previous", | ||
293 | [MSG_FUNCNAME_CURSOR_PARK] = "cursor park", | ||
294 | [MSG_FUNCNAME_CUT] = "cut", | ||
295 | [MSG_FUNCNAME_EDIT_DELIM] = "edit delimiters", | ||
296 | [MSG_FUNCNAME_EDIT_EXNUM] = "edit exnum", | ||
297 | [MSG_FUNCNAME_EDIT_MOST] = "edit most", | ||
298 | [MSG_FUNCNAME_EDIT_REPEATS] = "edit repeats", | ||
299 | [MSG_FUNCNAME_EDIT_SOME] = "edit some", | ||
300 | [MSG_FUNCNAME_GOTO] = "go to", | ||
301 | [MSG_FUNCNAME_GOTO_BOTTOM] = "go to bottom edge", | ||
302 | [MSG_FUNCNAME_GOTO_LEFT] = "go to left edge", | ||
303 | [MSG_FUNCNAME_GOTO_RIGHT] = "go to right edge", | ||
304 | [MSG_FUNCNAME_GOTO_TOP] = "go to top edge", | ||
305 | [MSG_FUNCNAME_HELP] = "help", | ||
306 | [MSG_FUNCNAME_LINE_SAY_CURRENT] = "line, say current", | ||
307 | [MSG_FUNCNAME_LINE_SAY_NEXT] = "line, say next", | ||
308 | [MSG_FUNCNAME_LINE_SAY_PREVIOUS] = "line, say previous", | ||
309 | [MSG_FUNCNAME_LINE_SAY_WITH_INDENT] = "line, say with indent", | ||
310 | [MSG_FUNCNAME_PASTE] = "paste", | ||
311 | [MSG_FUNCNAME_PITCH_DEC] = "pitch decrement", | ||
312 | [MSG_FUNCNAME_PITCH_INC] = "pitch increment", | ||
313 | [MSG_FUNCNAME_PUNC_DEC] = "punctuation decrement", | ||
314 | [MSG_FUNCNAME_PUNC_INC] = "punctuation increment", | ||
315 | [MSG_FUNCNAME_PUNC_LEVEL_DEC] = "punc level decrement", | ||
316 | [MSG_FUNCNAME_PUNC_LEVEL_INC] = "punc level increment", | ||
317 | [MSG_FUNCNAME_QUIET] = "quiet", | ||
318 | [MSG_FUNCNAME_RATE_DEC] = "rate decrement", | ||
319 | [MSG_FUNCNAME_RATE_INC] = "rate increment", | ||
320 | [MSG_FUNCNAME_READING_PUNC_DEC] = "reading punctuation decrement", | ||
321 | [MSG_FUNCNAME_READING_PUNC_INC] = "reading punctuation increment", | ||
322 | [MSG_FUNCNAME_SAY_ATTRIBUTES] = "say attributes", | ||
323 | [MSG_FUNCNAME_SAY_FROM_LEFT] = "say from left", | ||
324 | [MSG_FUNCNAME_SAY_FROM_TOP] = "say from top", | ||
325 | [MSG_FUNCNAME_SAY_POSITION] = "say position", | ||
326 | [MSG_FUNCNAME_SAY_SCREEN] = "say screen", | ||
327 | [MSG_FUNCNAME_SAY_TO_BOTTOM] = "say to bottom", | ||
328 | [MSG_FUNCNAME_SAY_TO_RIGHT] = "say to right", | ||
329 | [MSG_FUNCNAME_SPEAKUP] = "speakup", | ||
330 | [MSG_FUNCNAME_SPEAKUP_LOCK] = "speakup lock", | ||
331 | [MSG_FUNCNAME_SPEAKUP_OFF] = "speakup off", | ||
332 | [MSG_FUNCNAME_SPEECH_KILL] = "speech kill", | ||
333 | [MSG_FUNCNAME_SPELL_DELAY_DEC] = "spell delay decrement", | ||
334 | [MSG_FUNCNAME_SPELL_DELAY_INC] = "spell delay increment", | ||
335 | [MSG_FUNCNAME_SPELL_WORD] = "spell word", | ||
336 | [MSG_FUNCNAME_SPELL_WORD_PHONETICALLY] = "spell word phoneticly", | ||
337 | [MSG_FUNCNAME_TONE_DEC] = "tone decrement", | ||
338 | [MSG_FUNCNAME_TONE_INC] = "tone increment", | ||
339 | [MSG_FUNCNAME_VOICE_DEC] = "voice decrement", | ||
340 | [MSG_FUNCNAME_VOICE_INC] = "voice increment", | ||
341 | [MSG_FUNCNAME_VOLUME_DEC] = "volume decrement", | ||
342 | [MSG_FUNCNAME_VOLUME_INC] = "volume increment", | ||
343 | [MSG_FUNCNAME_WINDOW_CLEAR] = "window, clear", | ||
344 | [MSG_FUNCNAME_WINDOW_SAY] = "window, say", | ||
345 | [MSG_FUNCNAME_WINDOW_SET] = "window, set", | ||
346 | [MSG_FUNCNAME_WINDOW_SILENCE] = "window, silence", | ||
347 | [MSG_FUNCNAME_WORD_SAY_CURRENT] = "word, say current", | ||
348 | [MSG_FUNCNAME_WORD_SAY_NEXT] = "word, say next", | ||
349 | [MSG_FUNCNAME_WORD_SAY_PREVIOUS] = "word, say previous", | ||
350 | }; | ||
351 | |||
352 | static struct msg_group_t all_groups [] = { | ||
353 | { | ||
354 | .name = "ctl_keys", | ||
355 | .start = MSG_CTL_START, | ||
356 | .end = MSG_CTL_END, | ||
357 | }, | ||
358 | { | ||
359 | .name = "colors", | ||
360 | .start = MSG_COLORS_START, | ||
361 | .end = MSG_COLORS_END, | ||
362 | }, | ||
363 | { | ||
364 | .name = "formatted", | ||
365 | .start = MSG_FORMATTED_START, | ||
366 | .end = MSG_FORMATTED_END, | ||
367 | }, | ||
368 | { | ||
369 | .name = "function_names", | ||
370 | .start = MSG_FUNCNAMES_START, | ||
371 | .end = MSG_FUNCNAMES_END, | ||
372 | }, | ||
373 | { | ||
374 | .name = "key_names", | ||
375 | .start = MSG_KEYNAMES_START, | ||
376 | .end = MSG_KEYNAMES_END, | ||
377 | }, | ||
378 | { | ||
379 | .name = "announcements", | ||
380 | .start = MSG_ANNOUNCEMENTS_START, | ||
381 | .end = MSG_ANNOUNCEMENTS_END, | ||
382 | }, | ||
383 | { | ||
384 | .name = "states", | ||
385 | .start = MSG_STATES_START, | ||
386 | .end = MSG_STATES_END, | ||
387 | }, | ||
388 | }; | ||
389 | |||
390 | static const int num_groups = sizeof(all_groups) / sizeof(struct msg_group_t); | ||
391 | |||
392 | char *msg_get(enum msg_index_t index) | ||
393 | { | ||
394 | char *ch; | ||
395 | |||
396 | ch = speakup_msgs[index]; | ||
397 | return ch; | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * Function: next_specifier | ||
402 | * Finds the start of the next format specifier in the argument string. | ||
403 | * Return value: pointer to start of format | ||
404 | * specifier, or NULL if no specifier exists. | ||
405 | */ | ||
406 | static char *next_specifier(char *input) | ||
407 | { | ||
408 | int found = 0; | ||
409 | char *next_percent = input; | ||
410 | |||
411 | while ((next_percent != NULL) && !found) { | ||
412 | next_percent = strchr(next_percent, '%'); | ||
413 | if (next_percent != NULL) { | ||
414 | while ((next_percent[0] == '%') | ||
415 | && (next_percent[1] == '%')) | ||
416 | next_percent += 2; /* Advance over doubled percent signs. */ | ||
417 | if (*next_percent == '%') | ||
418 | found = 1; | ||
419 | else if (*next_percent == '\0') | ||
420 | next_percent = NULL; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | return next_percent; | ||
425 | } | ||
426 | |||
427 | /* Skip over 0 or more flags. */ | ||
428 | static char *skip_flags(char *input) | ||
429 | { | ||
430 | while ((*input != '\0') && strchr(" 0+-#", *input)) | ||
431 | input++; | ||
432 | return input; | ||
433 | } | ||
434 | |||
435 | /* Skip over width.precision, if it exists. */ | ||
436 | static char *skip_width(char *input) | ||
437 | { | ||
438 | while (isdigit(*input)) | ||
439 | input++; | ||
440 | if (*input == '.') { | ||
441 | input++; | ||
442 | while (isdigit(*input)) | ||
443 | input++; | ||
444 | } | ||
445 | return input; | ||
446 | } | ||
447 | |||
448 | /* | ||
449 | * Skip past the end of the conversion part. | ||
450 | * Note that this code only accepts a handful of conversion specifiers: | ||
451 | * c d s x and ld. Not accidental; these are exactly the ones used in | ||
452 | * the default group of formatted messages. | ||
453 | */ | ||
454 | static char *skip_conversion(char *input) | ||
455 | { | ||
456 | if ((input[0] == 'l') && (input[1] == 'd')) | ||
457 | input += 2; | ||
458 | else if ((*input != '\0') && strchr("cdsx", *input)) | ||
459 | input++; | ||
460 | return input; | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * Function: find_specifier_end | ||
465 | * Return a pointer to the end of the format specifier. | ||
466 | */ | ||
467 | static char *find_specifier_end(char *input) | ||
468 | { | ||
469 | input++; /* Advance over %. */ | ||
470 | input = skip_flags(input); | ||
471 | input = skip_width(input); | ||
472 | input = skip_conversion(input); | ||
473 | return input; | ||
474 | } | ||
475 | |||
476 | /* | ||
477 | * Function: compare_specifiers | ||
478 | * Compare the format specifiers pointed to by *input1 and *input2. | ||
479 | * Return 1 if they are the same, 0 otherwise. Advance *input1 and *input2 | ||
480 | * so that they point to the character following the end of the specifier. | ||
481 | */ | ||
482 | static int compare_specifiers(char **input1, char **input2) | ||
483 | { | ||
484 | int same = 0; | ||
485 | char *end1 = find_specifier_end(*input1); | ||
486 | char *end2 = find_specifier_end(*input2); | ||
487 | size_t length1 = end1 - *input1; | ||
488 | size_t length2 = end2 - *input2; | ||
489 | |||
490 | if((length1 == length2) && !memcmp(*input1, *input2, length1)) | ||
491 | same = 1; | ||
492 | |||
493 | *input1 = end1; | ||
494 | *input2 = end2; | ||
495 | return same; | ||
496 | } | ||
497 | |||
498 | /* | ||
499 | * Function: fmt_validate | ||
500 | * Check that two format strings contain the same number of format specifiers, | ||
501 | * and that the order of specifiers is the same in both strings. | ||
502 | * Return 1 if the condition holds, 0 if it doesn't. | ||
503 | */ | ||
504 | static int fmt_validate(char *template, char *user) | ||
505 | { | ||
506 | int valid = 1; | ||
507 | int still_comparing = 1; | ||
508 | char *template_ptr = template; | ||
509 | char *user_ptr = user; | ||
510 | |||
511 | while (still_comparing && valid) { | ||
512 | template_ptr = next_specifier(template_ptr); | ||
513 | user_ptr = next_specifier(user_ptr); | ||
514 | if (template_ptr && user_ptr) { | ||
515 | /* Both have at least one more specifier. */ | ||
516 | valid = compare_specifiers(&template_ptr, &user_ptr); | ||
517 | } else { | ||
518 | /* No more format specifiers in one or both of the strings. */ | ||
519 | still_comparing = 0; | ||
520 | if (template_ptr || user_ptr) | ||
521 | valid = 0; /* One has more specifiers than the other. */ | ||
522 | } | ||
523 | } | ||
524 | return valid; | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * Function: msg_set | ||
529 | * Description: Add a user-supplied message to the user_messages array. | ||
530 | * The message text is copied to a memory area allocated with kmalloc. | ||
531 | * If the function fails, then user_messages is untouched. | ||
532 | * Arguments: | ||
533 | * - index: a message number, as found in i18n.h. | ||
534 | * - text: text of message. Not NUL-terminated. | ||
535 | * - length: number of bytes in text. | ||
536 | * Failure conditions: | ||
537 | * -EINVAL - Invalid format specifiers in formatted message or illegal index. | ||
538 | * -ENOMEM - Unable to allocate memory. | ||
539 | */ | ||
540 | ssize_t msg_set(enum msg_index_t index, char *text, size_t length) | ||
541 | { | ||
542 | int rc = 0; | ||
543 | char *newstr = NULL; | ||
544 | unsigned long flags; | ||
545 | |||
546 | if ((index >= MSG_FIRST_INDEX) && (index < MSG_LAST_INDEX)) { | ||
547 | newstr = kmalloc(length + 1, GFP_KERNEL); | ||
548 | if (newstr) { | ||
549 | memcpy(newstr, text, length); | ||
550 | newstr[length] = '\0'; | ||
551 | if ((index >= MSG_FORMATTED_START && index <= MSG_FORMATTED_END) | ||
552 | && ! fmt_validate(speakup_default_msgs[index], newstr)) { | ||
553 | return -EINVAL; | ||
554 | } | ||
555 | spk_lock(flags); | ||
556 | if (speakup_msgs[index] != speakup_default_msgs[index]) | ||
557 | kfree(speakup_msgs[index]); | ||
558 | speakup_msgs[index] = newstr; | ||
559 | spk_unlock(flags); | ||
560 | } else { | ||
561 | rc = -ENOMEM; | ||
562 | } | ||
563 | } else { | ||
564 | rc = -EINVAL; | ||
565 | } | ||
566 | return rc; | ||
567 | } | ||
568 | |||
569 | /* | ||
570 | * Find a message group, given its name. Return a pointer to the structure | ||
571 | * if found, or NULL otherwise. | ||
572 | */ | ||
573 | struct msg_group_t *find_msg_group(const char *group_name) | ||
574 | { | ||
575 | struct msg_group_t *group = NULL; | ||
576 | int i; | ||
577 | |||
578 | for (i = 0; i < num_groups; i++) { | ||
579 | if (!strcmp(all_groups[i].name, group_name)) { | ||
580 | group = &all_groups[i]; | ||
581 | break; | ||
582 | } | ||
583 | } | ||
584 | return group; | ||
585 | } | ||
586 | |||
587 | void reset_msg_group(struct msg_group_t *group) | ||
588 | { | ||
589 | unsigned long flags; | ||
590 | enum msg_index_t i; | ||
591 | |||
592 | spk_lock(flags); | ||
593 | |||
594 | for(i = group->start; i <= group->end; i++) { | ||
595 | if (speakup_msgs[i] != speakup_default_msgs[i]) | ||
596 | kfree(speakup_msgs[i]); | ||
597 | speakup_msgs[i] = speakup_default_msgs[i]; | ||
598 | } | ||
599 | spk_unlock(flags); | ||
600 | } | ||
601 | |||
602 | /* Called at initialization time, to establish default messages. */ | ||
603 | void initialize_msgs(void) | ||
604 | { | ||
605 | memcpy(speakup_msgs, speakup_default_msgs, sizeof(speakup_default_msgs)); | ||
606 | } | ||
607 | |||
608 | /* Free user-supplied strings when module is unloaded: */ | ||
609 | void free_user_msgs(void) | ||
610 | { | ||
611 | enum msg_index_t index; | ||
612 | unsigned long flags; | ||
613 | |||
614 | spk_lock(flags); | ||
615 | for(index = MSG_FIRST_INDEX; index < MSG_LAST_INDEX; index++) { | ||
616 | if (speakup_msgs[index] != speakup_default_msgs[index]) { | ||
617 | kfree(speakup_msgs[index]); | ||
618 | speakup_msgs[index] = speakup_default_msgs[index]; | ||
619 | } | ||
620 | } | ||
621 | spk_unlock(flags); | ||
622 | } | ||
diff --git a/drivers/staging/speakup/i18n.h b/drivers/staging/speakup/i18n.h new file mode 100644 index 00000000000..65caa801077 --- /dev/null +++ b/drivers/staging/speakup/i18n.h | |||
@@ -0,0 +1,228 @@ | |||
1 | #ifndef I18N_H | ||
2 | #define I18N_H | ||
3 | /* Internationalization declarations */ | ||
4 | |||
5 | enum msg_index_t { | ||
6 | MSG_FIRST_INDEX , | ||
7 | MSG_ANNOUNCEMENTS_START = MSG_FIRST_INDEX, | ||
8 | MSG_BLANK = MSG_ANNOUNCEMENTS_START, | ||
9 | MSG_IAM_ALIVE, | ||
10 | MSG_YOU_KILLED_SPEAKUP, | ||
11 | MSG_HEY_THATS_BETTER, | ||
12 | MSG_YOU_TURNED_ME_OFF, | ||
13 | MSG_PARKED, | ||
14 | MSG_UNPARKED, | ||
15 | MSG_MARK, | ||
16 | MSG_CUT, | ||
17 | MSG_MARK_CLEARED, | ||
18 | MSG_PASTE, | ||
19 | MSG_BRIGHT, | ||
20 | MSG_ON_BLINKING, | ||
21 | MSG_STATUS_START, | ||
22 | MSG_OFF = MSG_STATUS_START, | ||
23 | MSG_ON, | ||
24 | MSG_NO_WINDOW, | ||
25 | MSG_CURSOR_MSGS_START, | ||
26 | MSG_CURSORING_OFF = MSG_CURSOR_MSGS_START, | ||
27 | MSG_CURSORING_ON, | ||
28 | MSG_HIGHLIGHT_TRACKING, | ||
29 | MSG_READ_WINDOW, | ||
30 | MSG_READ_ALL, | ||
31 | MSG_EDIT_DONE, | ||
32 | MSG_WINDOW_ALREADY_SET, | ||
33 | MSG_END_BEFORE_START, | ||
34 | MSG_WINDOW_CLEARED, | ||
35 | MSG_WINDOW_SILENCED, | ||
36 | MSG_WINDOW_SILENCE_DISABLED, | ||
37 | MSG_ERROR, | ||
38 | MSG_GOTO_CANCELED, | ||
39 | MSG_GOTO, | ||
40 | MSG_LEAVING_HELP, | ||
41 | MSG_IS_UNASSIGNED, | ||
42 | MSG_HELP_INFO, | ||
43 | MSG_EDGE_MSGS_START, | ||
44 | MSG_EDGE_TOP = MSG_EDGE_MSGS_START, | ||
45 | MSG_EDGE_BOTTOM, | ||
46 | MSG_EDGE_LEFT, | ||
47 | MSG_EDGE_RIGHT, | ||
48 | MSG_NUMBER, | ||
49 | MSG_SPACE, | ||
50 | MSG_START, /* A little confusing, given our convention. */ | ||
51 | MSG_END, /* A little confusing, given our convention. */ | ||
52 | MSG_CTRL, | ||
53 | |||
54 | /* A message containing the single word "or". */ | ||
55 | MSG_DISJUNCTION, | ||
56 | MSG_ANNOUNCEMENTS_END = MSG_DISJUNCTION, | ||
57 | |||
58 | /* Messages with format specifiers. */ | ||
59 | MSG_FORMATTED_START, | ||
60 | MSG_POS_INFO = MSG_FORMATTED_START, | ||
61 | MSG_CHAR_INFO, | ||
62 | MSG_REPEAT_DESC, | ||
63 | MSG_REPEAT_DESC2, | ||
64 | MSG_WINDOW_LINE, | ||
65 | MSG_WINDOW_BOUNDARY, | ||
66 | MSG_EDIT_PROMPT, | ||
67 | MSG_NO_COMMAND, | ||
68 | MSG_KEYDESC, | ||
69 | MSG_FORMATTED_END = MSG_KEYDESC, | ||
70 | |||
71 | /* Control keys. */ | ||
72 | MSG_CTL_START, | ||
73 | MSG_CTL_SHIFT = MSG_CTL_START, | ||
74 | MSG_CTL_ALTGR, | ||
75 | MSG_CTL_CONTROL, | ||
76 | MSG_CTL_ALT, | ||
77 | MSG_CTL_LSHIFT, | ||
78 | MSG_CTL_SPEAKUP, | ||
79 | MSG_CTL_LCONTROL, | ||
80 | MSG_CTL_RCONTROL, | ||
81 | MSG_CTL_CAPSSHIFT, | ||
82 | MSG_CTL_END = MSG_CTL_CAPSSHIFT, | ||
83 | |||
84 | /* Colors. */ | ||
85 | MSG_COLORS_START, | ||
86 | MSG_COLOR_BLACK = MSG_COLORS_START, | ||
87 | MSG_COLOR_BLUE, | ||
88 | MSG_COLOR_GREEN, | ||
89 | MSG_COLOR_CYAN, | ||
90 | MSG_COLOR_RED, | ||
91 | MSG_COLOR_MAGENTA, | ||
92 | MSG_COLOR_YELLOW, | ||
93 | MSG_COLOR_WHITE, | ||
94 | MSG_COLOR_GREY, | ||
95 | MSG_COLORS_END = MSG_COLOR_GREY, | ||
96 | |||
97 | MSG_STATES_START, | ||
98 | MSG_STATE_DOUBLE = MSG_STATES_START, | ||
99 | MSG_STATE_SPEAKUP, | ||
100 | MSG_STATE_ALT, | ||
101 | MSG_STATE_CONTROL, | ||
102 | MSG_STATE_ALTGR, | ||
103 | MSG_STATE_SHIFT, | ||
104 | MSG_STATES_END = MSG_STATE_SHIFT, | ||
105 | |||
106 | MSG_KEYNAMES_START, | ||
107 | MSG_KEYNAME_ESC = MSG_KEYNAMES_START, | ||
108 | MSG_KEYNAME_1, MSG_KEYNAME_2, MSG_KEYNAME_3, MSG_KEYNAME_4, | ||
109 | MSG_KEYNAME_5, MSG_KEYNAME_6, MSG_KEYNAME_7, MSG_KEYNAME_8, MSG_KEYNAME_9, | ||
110 | MSG_KEYNAME_0, MSG_KEYNAME_DASH, MSG_KEYNAME_EQUAL, MSG_KEYNAME_BS, | ||
111 | MSG_KEYNAME_TAB, | ||
112 | MSG_KEYNAME_Q, MSG_KEYNAME_W, MSG_KEYNAME_E, MSG_KEYNAME_R, MSG_KEYNAME_T, | ||
113 | MSG_KEYNAME_Y, MSG_KEYNAME_U, MSG_KEYNAME_I, MSG_KEYNAME_O, MSG_KEYNAME_P, | ||
114 | MSG_KEYNAME_LEFTBRACE, MSG_KEYNAME_RIGHTBRACE, MSG_KEYNAME_ENTER, | ||
115 | MSG_KEYNAME_LEFTCTRL, MSG_KEYNAME_A, | ||
116 | MSG_KEYNAME_S, MSG_KEYNAME_D, MSG_KEYNAME_F, MSG_KEYNAME_G, MSG_KEYNAME_H, | ||
117 | MSG_KEYNAME_J, MSG_KEYNAME_K, MSG_KEYNAME_L, MSG_KEYNAME_SEMICOLON, | ||
118 | MSG_KEYNAME_SINGLEQUOTE, MSG_KEYNAME_GRAVE, | ||
119 | MSG_KEYNAME_LEFTSHFT, MSG_KEYNAME_BACKSLASH, MSG_KEYNAME_Z, MSG_KEYNAME_X, | ||
120 | MSG_KEYNAME_C, MSG_KEYNAME_V, MSG_KEYNAME_B, MSG_KEYNAME_N, MSG_KEYNAME_M, | ||
121 | MSG_KEYNAME_COMMA, MSG_KEYNAME_DOT, MSG_KEYNAME_SLASH, MSG_KEYNAME_RIGHTSHFT, | ||
122 | MSG_KEYNAME_KPSTAR, | ||
123 | MSG_KEYNAME_LEFTALT, MSG_KEYNAME_SPACE, MSG_KEYNAME_CAPSLOCK, | ||
124 | MSG_KEYNAME_F1, MSG_KEYNAME_F2, | ||
125 | MSG_KEYNAME_F3, MSG_KEYNAME_F4, MSG_KEYNAME_F5, MSG_KEYNAME_F6, | ||
126 | MSG_KEYNAME_F7, | ||
127 | MSG_KEYNAME_F8, MSG_KEYNAME_F9, MSG_KEYNAME_F10, MSG_KEYNAME_NUMLOCK, | ||
128 | MSG_KEYNAME_SCROLLLOCK, | ||
129 | MSG_KEYNAME_KP7, MSG_KEYNAME_KP8, MSG_KEYNAME_KP9, MSG_KEYNAME_KPMINUS, | ||
130 | MSG_KEYNAME_KP4, | ||
131 | MSG_KEYNAME_KP5, MSG_KEYNAME_KP6, MSG_KEYNAME_KPPLUS, MSG_KEYNAME_KP1, | ||
132 | MSG_KEYNAME_KP2, | ||
133 | MSG_KEYNAME_KP3, MSG_KEYNAME_KP0, MSG_KEYNAME_KPDOT, MSG_KEYNAME_103RD, | ||
134 | MSG_KEYNAME_F13, | ||
135 | MSG_KEYNAME_102ND, MSG_KEYNAME_F11, MSG_KEYNAME_F12, MSG_KEYNAME_F14, | ||
136 | MSG_KEYNAME_F15, | ||
137 | MSG_KEYNAME_F16, MSG_KEYNAME_F17, MSG_KEYNAME_F18, MSG_KEYNAME_F19, | ||
138 | MSG_KEYNAME_F20, | ||
139 | MSG_KEYNAME_KPENTER, MSG_KEYNAME_RIGHTCTRL, MSG_KEYNAME_KPSLASH, | ||
140 | MSG_KEYNAME_SYSRQ, MSG_KEYNAME_RIGHTALT, | ||
141 | MSG_KEYNAME_LF, MSG_KEYNAME_HOME, MSG_KEYNAME_UP, MSG_KEYNAME_PGUP, | ||
142 | MSG_KEYNAME_LEFT, | ||
143 | MSG_KEYNAME_RIGHT, MSG_KEYNAME_END, MSG_KEYNAME_DOWN, MSG_KEYNAME_PGDN, | ||
144 | MSG_KEYNAME_INS, | ||
145 | MSG_KEYNAME_DEL, MSG_KEYNAME_MACRO, MSG_KEYNAME_MUTE, | ||
146 | MSG_KEYNAME_VOLDOWN, MSG_KEYNAME_VOLUP, | ||
147 | MSG_KEYNAME_POWER, MSG_KEYNAME_KPEQUAL, MSG_KEYNAME_KPPLUSDASH, MSG_KEYNAME_PAUSE, MSG_KEYNAME_F21, | ||
148 | MSG_KEYNAME_F22, MSG_KEYNAME_F23, MSG_KEYNAME_F24, MSG_KEYNAME_KPCOMMA, MSG_KEYNAME_LEFTMETA, | ||
149 | MSG_KEYNAME_RIGHTMETA, MSG_KEYNAME_COMPOSE, MSG_KEYNAME_STOP, | ||
150 | MSG_KEYNAME_AGAIN, MSG_KEYNAME_PROPS, | ||
151 | MSG_KEYNAME_UNDO, MSG_KEYNAME_FRONT, MSG_KEYNAME_COPY, MSG_KEYNAME_OPEN, | ||
152 | MSG_KEYNAME_PASTE, | ||
153 | MSG_KEYNAME_FIND, MSG_KEYNAME_CUT, MSG_KEYNAME_HELP, MSG_KEYNAME_MENU, | ||
154 | MSG_KEYNAME_CALC, | ||
155 | MSG_KEYNAME_SETUP, MSG_KEYNAME_SLEEP, MSG_KEYNAME_WAKEUP, | ||
156 | MSG_KEYNAME_FILE, MSG_KEYNAME_SENDFILE, | ||
157 | MSG_KEYNAME_DELFILE, MSG_KEYNAME_XFER, MSG_KEYNAME_PROG1, | ||
158 | MSG_KEYNAME_PROG2, MSG_KEYNAME_WWW, | ||
159 | MSG_KEYNAME_MSDOS, MSG_KEYNAME_COFFEE, MSG_KEYNAME_DIRECTION, | ||
160 | MSG_KEYNAME_CYCLEWINDOWS, MSG_KEYNAME_MAIL, | ||
161 | MSG_KEYNAME_BOOKMARKS, MSG_KEYNAME_COMPUTER, MSG_KEYNAME_BACK, | ||
162 | MSG_KEYNAME_FORWARD, MSG_KEYNAME_CLOSECD, | ||
163 | MSG_KEYNAME_EJECTCD, MSG_KEYNAME_EJECTCLOSE, MSG_KEYNAME_NEXTSONG, | ||
164 | MSG_KEYNAME_PLAYPAUSE, MSG_KEYNAME_PREVSONG, | ||
165 | MSG_KEYNAME_STOPCD, MSG_KEYNAME_RECORD, MSG_KEYNAME_REWIND, | ||
166 | MSG_KEYNAME_PHONE, MSG_KEYNAME_ISO, | ||
167 | MSG_KEYNAME_CONFIG, MSG_KEYNAME_HOMEPG, MSG_KEYNAME_REFRESH, | ||
168 | MSG_KEYNAME_EXIT, MSG_KEYNAME_MOVE, | ||
169 | MSG_KEYNAME_EDIT, MSG_KEYNAME_SCROLLUP, MSG_KEYNAME_SCROLLDN, | ||
170 | MSG_KEYNAME_KPLEFTPAR, MSG_KEYNAME_KPRIGHTPAR, | ||
171 | MSG_KEYNAMES_END = MSG_KEYNAME_KPRIGHTPAR, | ||
172 | |||
173 | MSG_FUNCNAMES_START, | ||
174 | MSG_FUNCNAME_ATTRIB_BLEEP_DEC = MSG_FUNCNAMES_START, | ||
175 | MSG_FUNCNAME_ATTRIB_BLEEP_INC, | ||
176 | MSG_FUNCNAME_BLEEPS_DEC, MSG_FUNCNAME_BLEEPS_INC, | ||
177 | MSG_FUNCNAME_CHAR_FIRST, MSG_FUNCNAME_CHAR_LAST, | ||
178 | MSG_FUNCNAME_CHAR_CURRENT, MSG_FUNCNAME_CHAR_HEX_AND_DEC, | ||
179 | MSG_FUNCNAME_CHAR_NEXT, | ||
180 | MSG_FUNCNAME_CHAR_PHONETIC, MSG_FUNCNAME_CHAR_PREVIOUS, | ||
181 | MSG_FUNCNAME_CURSOR_PARK, MSG_FUNCNAME_CUT, | ||
182 | MSG_FUNCNAME_EDIT_DELIM, MSG_FUNCNAME_EDIT_EXNUM, | ||
183 | MSG_FUNCNAME_EDIT_MOST, MSG_FUNCNAME_EDIT_REPEATS, MSG_FUNCNAME_EDIT_SOME, | ||
184 | MSG_FUNCNAME_GOTO, MSG_FUNCNAME_GOTO_BOTTOM, MSG_FUNCNAME_GOTO_LEFT, | ||
185 | MSG_FUNCNAME_GOTO_RIGHT, MSG_FUNCNAME_GOTO_TOP, MSG_FUNCNAME_HELP, | ||
186 | MSG_FUNCNAME_LINE_SAY_CURRENT, MSG_FUNCNAME_LINE_SAY_NEXT, | ||
187 | MSG_FUNCNAME_LINE_SAY_PREVIOUS, MSG_FUNCNAME_LINE_SAY_WITH_INDENT, | ||
188 | MSG_FUNCNAME_PASTE, MSG_FUNCNAME_PITCH_DEC, MSG_FUNCNAME_PITCH_INC, | ||
189 | MSG_FUNCNAME_PUNC_DEC, MSG_FUNCNAME_PUNC_INC, | ||
190 | MSG_FUNCNAME_PUNC_LEVEL_DEC, MSG_FUNCNAME_PUNC_LEVEL_INC, | ||
191 | MSG_FUNCNAME_QUIET, | ||
192 | MSG_FUNCNAME_RATE_DEC, MSG_FUNCNAME_RATE_INC, | ||
193 | MSG_FUNCNAME_READING_PUNC_DEC, MSG_FUNCNAME_READING_PUNC_INC, | ||
194 | MSG_FUNCNAME_SAY_ATTRIBUTES, | ||
195 | MSG_FUNCNAME_SAY_FROM_LEFT, MSG_FUNCNAME_SAY_FROM_TOP, | ||
196 | MSG_FUNCNAME_SAY_POSITION, MSG_FUNCNAME_SAY_SCREEN, | ||
197 | MSG_FUNCNAME_SAY_TO_BOTTOM, MSG_FUNCNAME_SAY_TO_RIGHT, | ||
198 | MSG_FUNCNAME_SPEAKUP, MSG_FUNCNAME_SPEAKUP_LOCK, | ||
199 | MSG_FUNCNAME_SPEAKUP_OFF, MSG_FUNCNAME_SPEECH_KILL, | ||
200 | MSG_FUNCNAME_SPELL_DELAY_DEC, MSG_FUNCNAME_SPELL_DELAY_INC, | ||
201 | MSG_FUNCNAME_SPELL_WORD, MSG_FUNCNAME_SPELL_WORD_PHONETICALLY, | ||
202 | MSG_FUNCNAME_TONE_DEC, MSG_FUNCNAME_TONE_INC, | ||
203 | MSG_FUNCNAME_VOICE_DEC, MSG_FUNCNAME_VOICE_INC, | ||
204 | MSG_FUNCNAME_VOLUME_DEC, MSG_FUNCNAME_VOLUME_INC, | ||
205 | MSG_FUNCNAME_WINDOW_CLEAR, MSG_FUNCNAME_WINDOW_SAY, | ||
206 | MSG_FUNCNAME_WINDOW_SET, MSG_FUNCNAME_WINDOW_SILENCE, | ||
207 | MSG_FUNCNAME_WORD_SAY_CURRENT, MSG_FUNCNAME_WORD_SAY_NEXT, | ||
208 | MSG_FUNCNAME_WORD_SAY_PREVIOUS, | ||
209 | MSG_FUNCNAMES_END = MSG_FUNCNAME_WORD_SAY_PREVIOUS, | ||
210 | |||
211 | /* all valid indices must be above this */ | ||
212 | MSG_LAST_INDEX | ||
213 | }; | ||
214 | |||
215 | struct msg_group_t { | ||
216 | char *name; | ||
217 | enum msg_index_t start; | ||
218 | enum msg_index_t end; | ||
219 | }; | ||
220 | |||
221 | extern char *msg_get(enum msg_index_t index); | ||
222 | extern ssize_t msg_set(enum msg_index_t index, char *text, size_t length); | ||
223 | extern struct msg_group_t *find_msg_group(const char *group_name); | ||
224 | extern void reset_msg_group(struct msg_group_t *group); | ||
225 | extern void initialize_msgs(void); | ||
226 | extern void free_user_msgs(void); | ||
227 | |||
228 | #endif | ||
diff --git a/drivers/staging/speakup/keyhelp.c b/drivers/staging/speakup/keyhelp.c new file mode 100644 index 00000000000..81c627cfb3a --- /dev/null +++ b/drivers/staging/speakup/keyhelp.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /* speakup_keyhelp.c | ||
2 | help module for speakup | ||
3 | |||
4 | written by David Borowski. | ||
5 | |||
6 | Copyright (C) 2003 David Borowski. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2 of the License, or | ||
11 | (at your option) any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; if not, write to the Free Software | ||
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <linux/keyboard.h> | ||
24 | #include "spk_priv.h" | ||
25 | #include "speakup.h" | ||
26 | |||
27 | #define MAXFUNCS 130 | ||
28 | #define MAXKEYS 256 | ||
29 | static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1; | ||
30 | static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS]; | ||
31 | static u_short masks[] = { 32, 16, 8, 4, 2, 1 }; | ||
32 | |||
33 | static short letter_offsets[26] = | ||
34 | { -1, -1, -1, -1, -1, -1, -1, -1, | ||
35 | -1, -1, -1, -1, -1, -1, -1, -1, | ||
36 | -1, -1, -1, -1, -1, -1, -1, -1, | ||
37 | -1, -1 }; | ||
38 | |||
39 | static u_char funcvals[] = { | ||
40 | ATTRIB_BLEEP_DEC, ATTRIB_BLEEP_INC, BLEEPS_DEC, BLEEPS_INC, | ||
41 | SAY_FIRST_CHAR, SAY_LAST_CHAR, SAY_CHAR, SAY_CHAR_NUM, | ||
42 | SAY_NEXT_CHAR, SAY_PHONETIC_CHAR, SAY_PREV_CHAR, SPEAKUP_PARKED, | ||
43 | SPEAKUP_CUT, EDIT_DELIM, EDIT_EXNUM, EDIT_MOST, | ||
44 | EDIT_REPEAT, EDIT_SOME, SPEAKUP_GOTO, BOTTOM_EDGE, | ||
45 | LEFT_EDGE, RIGHT_EDGE, TOP_EDGE, SPEAKUP_HELP, | ||
46 | SAY_LINE, SAY_NEXT_LINE, SAY_PREV_LINE, SAY_LINE_INDENT, | ||
47 | SPEAKUP_PASTE, PITCH_DEC, PITCH_INC, PUNCT_DEC, | ||
48 | PUNCT_INC, PUNC_LEVEL_DEC, PUNC_LEVEL_INC, SPEAKUP_QUIET, | ||
49 | RATE_DEC, RATE_INC, READING_PUNC_DEC, READING_PUNC_INC, | ||
50 | SAY_ATTRIBUTES, SAY_FROM_LEFT, SAY_FROM_TOP, SAY_POSITION, | ||
51 | SAY_SCREEN, SAY_TO_BOTTOM, SAY_TO_RIGHT, SPK_KEY, | ||
52 | SPK_LOCK, SPEAKUP_OFF, SPEECH_KILL, SPELL_DELAY_DEC, | ||
53 | SPELL_DELAY_INC, SPELL_WORD, SPELL_PHONETIC, TONE_DEC, | ||
54 | TONE_INC, VOICE_DEC, VOICE_INC, VOL_DEC, | ||
55 | VOL_INC, CLEAR_WIN, SAY_WIN, SET_WIN, | ||
56 | ENABLE_WIN, SAY_WORD, SAY_NEXT_WORD, SAY_PREV_WORD, 0 | ||
57 | }; | ||
58 | |||
59 | static u_char *state_tbl; | ||
60 | static int cur_item, nstates; | ||
61 | |||
62 | static void build_key_data(void) | ||
63 | { | ||
64 | u_char *kp, counters[MAXFUNCS], ch, ch1; | ||
65 | u_short *p_key = key_data, key; | ||
66 | int i, offset = 1; | ||
67 | nstates = (int)(state_tbl[-1]); | ||
68 | memset(counters, 0, sizeof(counters)); | ||
69 | memset(key_offsets, 0, sizeof(key_offsets)); | ||
70 | kp = state_tbl + nstates + 1; | ||
71 | while (*kp++) { | ||
72 | /* count occurrances of each function */ | ||
73 | for (i = 0; i < nstates; i++, kp++) { | ||
74 | if (!*kp) | ||
75 | continue; | ||
76 | if ((state_tbl[i]&16) != 0 && *kp == SPK_KEY) | ||
77 | continue; | ||
78 | counters[*kp]++; | ||
79 | } | ||
80 | } | ||
81 | for (i = 0; i < MAXFUNCS; i++) { | ||
82 | if (counters[i] == 0) | ||
83 | continue; | ||
84 | key_offsets[i] = offset; | ||
85 | offset += (counters[i]+1); | ||
86 | if (offset >= MAXKEYS) | ||
87 | break; | ||
88 | } | ||
89 | /* leave counters set so high keycodes come first. | ||
90 | this is done so num pad and other extended keys maps are spoken before | ||
91 | the alpha with speakup type mapping. */ | ||
92 | kp = state_tbl + nstates + 1; | ||
93 | while ((ch = *kp++)) { | ||
94 | for (i = 0; i < nstates; i++) { | ||
95 | ch1 = *kp++; | ||
96 | if (!ch1) | ||
97 | continue; | ||
98 | if ((state_tbl[i]&16) != 0 && ch1 == SPK_KEY) | ||
99 | continue; | ||
100 | key = (state_tbl[i] << 8) + ch; | ||
101 | counters[ch1]--; | ||
102 | offset = key_offsets[ch1]; | ||
103 | if (!offset) | ||
104 | continue; | ||
105 | p_key = key_data + offset + counters[ch1]; | ||
106 | *p_key = key; | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | static void say_key(int key) | ||
112 | { | ||
113 | int i, state = key >> 8; | ||
114 | key &= 0xff; | ||
115 | for (i = 0; i < 6; i++) { | ||
116 | if (state & masks[i]) | ||
117 | synth_printf(" %s", msg_get(MSG_STATES_START + i)); | ||
118 | } | ||
119 | if ((key > 0) && (key <= num_key_names)) | ||
120 | synth_printf(" %s\n", msg_get(MSG_KEYNAMES_START + (key - 1))); | ||
121 | } | ||
122 | |||
123 | static int help_init(void) | ||
124 | { | ||
125 | char start = SPACE; | ||
126 | int i; | ||
127 | int num_funcs = MSG_FUNCNAMES_END - MSG_FUNCNAMES_START + 1; | ||
128 | state_tbl = our_keys[0]+SHIFT_TBL_SIZE+2; | ||
129 | for (i = 0; i < num_funcs; i++) { | ||
130 | char *cur_funcname = msg_get(MSG_FUNCNAMES_START + i); | ||
131 | if (start == *cur_funcname) | ||
132 | continue; | ||
133 | start = *cur_funcname; | ||
134 | letter_offsets[(start&31)-1] = i; | ||
135 | } | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | int handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key) | ||
140 | { | ||
141 | int i, n; | ||
142 | char *name; | ||
143 | u_char func, *kp; | ||
144 | u_short *p_keys, val; | ||
145 | if (letter_offsets[0] == -1) | ||
146 | help_init(); | ||
147 | if (type == KT_LATIN) { | ||
148 | if (ch == SPACE) { | ||
149 | special_handler = NULL; | ||
150 | synth_printf("%s\n", msg_get(MSG_LEAVING_HELP)); | ||
151 | return 1; | ||
152 | } | ||
153 | ch |= 32; /* lower case */ | ||
154 | if (ch < 'a' || ch > 'z') | ||
155 | return -1; | ||
156 | if (letter_offsets[ch-'a'] == -1) { | ||
157 | synth_printf(msg_get(MSG_NO_COMMAND), ch); | ||
158 | synth_printf("\n"); | ||
159 | return 1; | ||
160 | } | ||
161 | cur_item = letter_offsets[ch-'a']; | ||
162 | } else if (type == KT_CUR) { | ||
163 | if (ch == 0 && (cur_item + 1) <= MSG_FUNCNAMES_END) | ||
164 | cur_item++; | ||
165 | else if (ch == 3 && cur_item > 0) | ||
166 | cur_item--; | ||
167 | else | ||
168 | return -1; | ||
169 | } else if (type == KT_SPKUP && ch == SPEAKUP_HELP && !special_handler) { | ||
170 | special_handler = handle_help; | ||
171 | synth_printf("%s\n", msg_get(MSG_HELP_INFO)); | ||
172 | build_key_data(); /* rebuild each time in case new mapping */ | ||
173 | return 1; | ||
174 | } else { | ||
175 | name = NULL; | ||
176 | if ((type != KT_SPKUP) && (key > 0) && (key <= num_key_names)) { | ||
177 | synth_printf("%s\n", msg_get(MSG_KEYNAMES_START + key-1)); | ||
178 | return 1; | ||
179 | } | ||
180 | for (i = 0; funcvals[i] != 0 && !name; i++) { | ||
181 | if (ch == funcvals[i]) | ||
182 | name = msg_get(MSG_FUNCNAMES_START + i); | ||
183 | } | ||
184 | if (!name) | ||
185 | return -1; | ||
186 | kp = our_keys[key]+1; | ||
187 | for (i = 0; i < nstates; i++) { | ||
188 | if (ch == kp[i]) | ||
189 | break; | ||
190 | } | ||
191 | key += (state_tbl[i] << 8); | ||
192 | say_key(key); | ||
193 | synth_printf(msg_get(MSG_KEYDESC), name); | ||
194 | synth_printf("\n"); | ||
195 | return 1; | ||
196 | } | ||
197 | name = msg_get(MSG_FUNCNAMES_START + cur_item); | ||
198 | func = funcvals[cur_item]; | ||
199 | synth_printf("%s", name); | ||
200 | if (key_offsets[func] == 0) { | ||
201 | synth_printf(" %s\n", msg_get(MSG_IS_UNASSIGNED)); | ||
202 | return 1; | ||
203 | } | ||
204 | p_keys = key_data + key_offsets[func]; | ||
205 | for (n = 0; p_keys[n]; n++) { | ||
206 | val = p_keys[n]; | ||
207 | if (n > 0) | ||
208 | synth_printf("%s ", msg_get(MSG_DISJUNCTION)); | ||
209 | say_key(val); | ||
210 | } | ||
211 | return 1; | ||
212 | } | ||
diff --git a/drivers/staging/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c new file mode 100644 index 00000000000..1cd456670fc --- /dev/null +++ b/drivers/staging/speakup/kobjects.c | |||
@@ -0,0 +1,1007 @@ | |||
1 | /* | ||
2 | * Speakup kobject implementation | ||
3 | * | ||
4 | * Copyright (C) 2009 William Hubbs | ||
5 | * | ||
6 | * This code is based on kobject-example.c, which came with linux 2.6.x. | ||
7 | * | ||
8 | * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com> | ||
9 | * Copyright (C) 2007 Novell Inc. | ||
10 | * | ||
11 | * Released under the GPL version 2 only. | ||
12 | * | ||
13 | */ | ||
14 | #include <linux/slab.h> /* For kmalloc. */ | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/kobject.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/sysfs.h> | ||
19 | #include <linux/ctype.h> | ||
20 | |||
21 | #include "speakup.h" | ||
22 | #include "spk_priv.h" | ||
23 | |||
24 | /* | ||
25 | * This is called when a user reads the characters or chartab sys file. | ||
26 | */ | ||
27 | static ssize_t chars_chartab_show(struct kobject *kobj, | ||
28 | struct kobj_attribute *attr, char *buf) | ||
29 | { | ||
30 | int i; | ||
31 | int len = 0; | ||
32 | char *cp; | ||
33 | char *buf_pointer = buf; | ||
34 | size_t bufsize = PAGE_SIZE; | ||
35 | unsigned long flags; | ||
36 | |||
37 | spk_lock(flags); | ||
38 | *buf_pointer = '\0'; | ||
39 | for (i = 0; i < 256; i++) { | ||
40 | if (bufsize <= 1) | ||
41 | break; | ||
42 | if (strcmp("characters", attr->attr.name) == 0) { | ||
43 | len = scnprintf(buf_pointer, bufsize, "%d\t%s\n", | ||
44 | i, characters[i]); | ||
45 | } else { /* show chartab entry */ | ||
46 | if (IS_TYPE(i, B_CTL)) | ||
47 | cp = "B_CTL"; | ||
48 | else if (IS_TYPE(i, WDLM)) | ||
49 | cp = "WDLM"; | ||
50 | else if (IS_TYPE(i, A_PUNC)) | ||
51 | cp = "A_PUNC"; | ||
52 | else if (IS_TYPE(i, PUNC)) | ||
53 | cp = "PUNC"; | ||
54 | else if (IS_TYPE(i, NUM)) | ||
55 | cp = "NUM"; | ||
56 | else if (IS_TYPE(i, A_CAP)) | ||
57 | cp = "A_CAP"; | ||
58 | else if (IS_TYPE(i, ALPHA)) | ||
59 | cp = "ALPHA"; | ||
60 | else if (IS_TYPE(i, B_CAPSYM)) | ||
61 | cp = "B_CAPSYM"; | ||
62 | else if (IS_TYPE(i, B_SYM)) | ||
63 | cp = "B_SYM"; | ||
64 | else | ||
65 | cp = "0"; | ||
66 | len = | ||
67 | scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp); | ||
68 | } | ||
69 | bufsize -= len; | ||
70 | buf_pointer += len; | ||
71 | } | ||
72 | spk_unlock(flags); | ||
73 | return buf_pointer - buf; | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * Print informational messages or warnings after updating | ||
78 | * character descriptions or chartab entries. | ||
79 | */ | ||
80 | static void report_char_chartab_status(int reset, int received, int used, | ||
81 | int rejected, int do_characters) | ||
82 | { | ||
83 | char *object_type[] = { | ||
84 | "character class entries", | ||
85 | "character descriptions", | ||
86 | }; | ||
87 | int len; | ||
88 | char buf[80]; | ||
89 | |||
90 | if (reset) { | ||
91 | pr_info("%s reset to defaults\n", object_type[do_characters]); | ||
92 | } else if (received ) { | ||
93 | len = snprintf(buf, sizeof(buf), | ||
94 | " updated %d of %d %s\n", | ||
95 | used, received, object_type[do_characters]); | ||
96 | if (rejected) | ||
97 | snprintf(buf + (len - 1), sizeof(buf) - (len - 1), | ||
98 | " with %d reject%s\n", | ||
99 | rejected, rejected > 1 ? "s" : ""); | ||
100 | printk(buf); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * This is called when a user changes the characters or chartab parameters. | ||
106 | */ | ||
107 | static ssize_t chars_chartab_store(struct kobject *kobj, | ||
108 | struct kobj_attribute *attr, const char *buf, size_t count) | ||
109 | { | ||
110 | char *cp = (char *) buf; | ||
111 | char *end = cp + count; /* the null at the end of the buffer */ | ||
112 | char *linefeed = NULL; | ||
113 | char keyword[MAX_DESC_LEN + 1]; | ||
114 | char *outptr = NULL; /* Will hold keyword or desc. */ | ||
115 | char *temp = NULL; | ||
116 | char *desc = NULL; | ||
117 | ssize_t retval = count; | ||
118 | unsigned long flags; | ||
119 | unsigned long index = 0; | ||
120 | int charclass = 0; | ||
121 | int received = 0; | ||
122 | int used = 0; | ||
123 | int rejected = 0; | ||
124 | int reset = 0; | ||
125 | int do_characters = !strcmp(attr->attr.name, "characters"); | ||
126 | size_t desc_length = 0; | ||
127 | int i; | ||
128 | |||
129 | spk_lock(flags); | ||
130 | while (cp < end) { | ||
131 | |||
132 | while ((cp < end) && (*cp == ' ' || *cp == '\t')) | ||
133 | cp++; | ||
134 | |||
135 | if (cp == end) | ||
136 | break; | ||
137 | if ((*cp == '\n') || strchr("dDrR", *cp)) { | ||
138 | reset = 1; | ||
139 | break; | ||
140 | } | ||
141 | received++; | ||
142 | |||
143 | linefeed = strchr(cp, '\n'); | ||
144 | if (!linefeed) { | ||
145 | rejected++; | ||
146 | break; | ||
147 | } | ||
148 | |||
149 | if (! isdigit(*cp)) { | ||
150 | rejected++; | ||
151 | cp = linefeed + 1; | ||
152 | continue; | ||
153 | } | ||
154 | |||
155 | index = simple_strtoul(cp, &temp, 10); | ||
156 | if (index > 255) { | ||
157 | rejected++; | ||
158 | cp = linefeed + 1; | ||
159 | continue; | ||
160 | } | ||
161 | |||
162 | while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) | ||
163 | temp++; | ||
164 | |||
165 | desc_length = linefeed - temp; | ||
166 | if (desc_length > MAX_DESC_LEN) { | ||
167 | rejected++; | ||
168 | cp = linefeed + 1; | ||
169 | continue; | ||
170 | } | ||
171 | if (do_characters) { | ||
172 | desc = kmalloc(desc_length + 1, GFP_ATOMIC); | ||
173 | if (! desc) { | ||
174 | retval = -ENOMEM; | ||
175 | reset = 1; /* just reset on error. */ | ||
176 | break; | ||
177 | } | ||
178 | outptr = desc; | ||
179 | } else { | ||
180 | outptr = keyword; | ||
181 | } | ||
182 | |||
183 | for (i = 0; i < desc_length; i++) | ||
184 | outptr[i] = temp[i]; | ||
185 | outptr[desc_length] = '\0'; | ||
186 | |||
187 | if (do_characters) { | ||
188 | if (characters[index] != default_chars[index]) | ||
189 | kfree(characters[index]); | ||
190 | characters[index] = desc; | ||
191 | used++; | ||
192 | } else { | ||
193 | charclass = chartab_get_value(keyword); | ||
194 | if (charclass == 0) { | ||
195 | rejected++; | ||
196 | cp = linefeed + 1; | ||
197 | continue; | ||
198 | } | ||
199 | if (charclass != spk_chartab[index]) { | ||
200 | spk_chartab[index] = charclass; | ||
201 | used++; | ||
202 | } | ||
203 | } | ||
204 | cp = linefeed + 1; | ||
205 | } | ||
206 | |||
207 | if (reset) { | ||
208 | if (do_characters) | ||
209 | reset_default_chars(); | ||
210 | else | ||
211 | reset_default_chartab(); | ||
212 | } | ||
213 | |||
214 | spk_unlock(flags); | ||
215 | report_char_chartab_status(reset, received, used, rejected, do_characters); | ||
216 | return retval; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * This is called when a user reads the keymap parameter. | ||
221 | */ | ||
222 | static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
223 | char *buf) | ||
224 | { | ||
225 | char *cp = buf; | ||
226 | int i; | ||
227 | int n; | ||
228 | int num_keys; | ||
229 | int nstates; | ||
230 | u_char *cp1; | ||
231 | u_char ch; | ||
232 | unsigned long flags; | ||
233 | spk_lock(flags); | ||
234 | cp1 = key_buf + SHIFT_TBL_SIZE; | ||
235 | num_keys = (int)(*cp1); | ||
236 | nstates = (int)cp1[1]; | ||
237 | cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates); | ||
238 | cp1 += 2; /* now pointing at shift states */ | ||
239 | /* dump num_keys+1 as first row is shift states + flags, | ||
240 | each subsequent row is key + states */ | ||
241 | for (n = 0; n <= num_keys; n++) { | ||
242 | for (i = 0; i <= nstates; i++) { | ||
243 | ch = *cp1++; | ||
244 | cp += sprintf(cp, "%d,", (int)ch); | ||
245 | *cp++ = (i < nstates) ? SPACE : '\n'; | ||
246 | } | ||
247 | } | ||
248 | cp += sprintf(cp, "0, %d\n", KEY_MAP_VER); | ||
249 | spk_unlock(flags); | ||
250 | return (int)(cp-buf); | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * This is called when a user changes the keymap parameter. | ||
255 | */ | ||
256 | static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
257 | const char *buf, size_t count) | ||
258 | { | ||
259 | int i; | ||
260 | ssize_t ret = count; | ||
261 | char *in_buff = NULL; | ||
262 | char *cp; | ||
263 | u_char *cp1; | ||
264 | unsigned long flags; | ||
265 | |||
266 | spk_lock(flags); | ||
267 | in_buff = kmalloc(count + 1, GFP_ATOMIC); | ||
268 | if (! in_buff) { | ||
269 | spk_unlock(flags); | ||
270 | return -ENOMEM; | ||
271 | } | ||
272 | memcpy(in_buff, buf, count + 1); | ||
273 | if (strchr("dDrR", *in_buff)) { | ||
274 | set_key_info(key_defaults, key_buf); | ||
275 | pr_info("keymap set to default values\n"); | ||
276 | kfree(in_buff); | ||
277 | spk_unlock(flags); | ||
278 | return count; | ||
279 | } | ||
280 | if (in_buff[count - 1] == '\n') | ||
281 | in_buff[count - 1] = '\0'; | ||
282 | cp = in_buff; | ||
283 | cp1 = (u_char *)in_buff; | ||
284 | for (i = 0; i < 3; i++) { | ||
285 | cp = s2uchar(cp, cp1); | ||
286 | cp1++; | ||
287 | } | ||
288 | i = (int)cp1[-2]+1; | ||
289 | i *= (int)cp1[-1]+1; | ||
290 | i += 2; /* 0 and last map ver */ | ||
291 | if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 || | ||
292 | i+SHIFT_TBL_SIZE+4 >= sizeof(key_buf)) { | ||
293 | pr_warn("i %d %d %d %d\n", i, | ||
294 | (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); | ||
295 | kfree(in_buff); | ||
296 | spk_unlock(flags); | ||
297 | return -EINVAL; | ||
298 | } | ||
299 | while (--i >= 0) { | ||
300 | cp = s2uchar(cp, cp1); | ||
301 | cp1++; | ||
302 | if (!(*cp)) | ||
303 | break; | ||
304 | } | ||
305 | if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) { | ||
306 | ret = -EINVAL; | ||
307 | pr_warn("end %d %d %d %d\n", i, | ||
308 | (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); | ||
309 | } else { | ||
310 | if (set_key_info(in_buff, key_buf)) { | ||
311 | set_key_info(key_defaults, key_buf); | ||
312 | ret = -EINVAL; | ||
313 | pr_warn("set key failed\n"); | ||
314 | } | ||
315 | } | ||
316 | kfree(in_buff); | ||
317 | spk_unlock(flags); | ||
318 | return ret; | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * This is called when a user changes the value of the silent parameter. | ||
323 | */ | ||
324 | static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
325 | const char *buf, size_t count) | ||
326 | { | ||
327 | int len; | ||
328 | struct vc_data *vc = vc_cons[fg_console].d; | ||
329 | char ch = 0; | ||
330 | char shut; | ||
331 | unsigned long flags; | ||
332 | |||
333 | len = strlen(buf); | ||
334 | if (len > 0 || len < 3) { | ||
335 | ch = buf[0]; | ||
336 | if (ch == '\n') | ||
337 | ch = '0'; | ||
338 | } | ||
339 | if (ch < '0' || ch > '7') { | ||
340 | pr_warn("silent value '%c' not in range (0,7)\n", ch); | ||
341 | return -EINVAL; | ||
342 | } | ||
343 | spk_lock(flags); | ||
344 | if (ch&2) { | ||
345 | shut = 1; | ||
346 | do_flush(); | ||
347 | } else { | ||
348 | shut = 0; | ||
349 | } | ||
350 | if (ch&4) | ||
351 | shut |= 0x40; | ||
352 | if (ch&1) | ||
353 | spk_shut_up |= shut; | ||
354 | else | ||
355 | spk_shut_up &= ~shut; | ||
356 | spk_unlock(flags); | ||
357 | return count; | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * This is called when a user reads the synth setting. | ||
362 | */ | ||
363 | static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
364 | char *buf) | ||
365 | { | ||
366 | int rv; | ||
367 | |||
368 | if (synth == NULL) | ||
369 | rv = sprintf(buf, "%s\n", "none"); | ||
370 | else | ||
371 | rv = sprintf(buf, "%s\n", synth->name); | ||
372 | return rv; | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * This is called when a user requests to change synthesizers. | ||
377 | */ | ||
378 | static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
379 | const char *buf, size_t count) | ||
380 | { | ||
381 | int len; | ||
382 | char new_synth_name[10]; | ||
383 | |||
384 | len = strlen(buf); | ||
385 | if (len < 2 || len > 9) | ||
386 | return -EINVAL; | ||
387 | strncpy(new_synth_name, buf, len); | ||
388 | if (new_synth_name[len - 1] == '\n') | ||
389 | len--; | ||
390 | new_synth_name[len] = '\0'; | ||
391 | strlwr(new_synth_name); | ||
392 | if ((synth != NULL) && (!strcmp(new_synth_name, synth->name))) { | ||
393 | pr_warn("%s already in use\n", new_synth_name); | ||
394 | } else if (synth_init(new_synth_name) != 0) { | ||
395 | pr_warn("failed to init synth %s\n", new_synth_name); | ||
396 | return -ENODEV; | ||
397 | } | ||
398 | return count; | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * This is called when text is sent to the synth via the synth_direct file. | ||
403 | */ | ||
404 | static ssize_t synth_direct_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
405 | const char *buf, size_t count) | ||
406 | { | ||
407 | u_char tmp[256]; | ||
408 | int len; | ||
409 | int bytes; | ||
410 | const char *ptr = buf; | ||
411 | |||
412 | if (! synth) | ||
413 | return -EPERM; | ||
414 | |||
415 | len = strlen(buf); | ||
416 | while (len > 0) { | ||
417 | bytes = min_t(size_t, len, 250); | ||
418 | strncpy(tmp, ptr, bytes); | ||
419 | tmp[bytes] = '\0'; | ||
420 | xlate(tmp); | ||
421 | synth_printf("%s", tmp); | ||
422 | ptr += bytes; | ||
423 | len -= bytes; | ||
424 | } | ||
425 | return count; | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | * This function is called when a user reads the version. | ||
430 | */ | ||
431 | static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
432 | char *buf) | ||
433 | { | ||
434 | char *cp; | ||
435 | |||
436 | cp = buf; | ||
437 | cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION); | ||
438 | if (synth) | ||
439 | cp += sprintf(cp, "%s synthesizer driver version %s\n", | ||
440 | synth->name, synth->version); | ||
441 | return cp - buf; | ||
442 | } | ||
443 | |||
444 | /* | ||
445 | * This is called when a user reads the punctuation settings. | ||
446 | */ | ||
447 | static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
448 | char *buf) | ||
449 | { | ||
450 | int i; | ||
451 | char *cp = buf; | ||
452 | struct st_var_header *p_header; | ||
453 | struct punc_var_t *var; | ||
454 | struct st_bits_data *pb; | ||
455 | short mask; | ||
456 | unsigned long flags; | ||
457 | |||
458 | p_header = var_header_by_name(attr->attr.name); | ||
459 | if (p_header == NULL) { | ||
460 | pr_warn("p_header is null, attr->attr.name is %s\n", attr->attr.name); | ||
461 | return -EINVAL; | ||
462 | } | ||
463 | |||
464 | var = get_punc_var(p_header->var_id); | ||
465 | if (var == NULL) { | ||
466 | pr_warn("var is null, p_header->var_id is %i\n", | ||
467 | p_header->var_id); | ||
468 | return -EINVAL; | ||
469 | } | ||
470 | |||
471 | spk_lock(flags); | ||
472 | pb = (struct st_bits_data *) &punc_info[var->value]; | ||
473 | mask = pb->mask; | ||
474 | for (i = 33; i < 128; i++) { | ||
475 | if (!(spk_chartab[i]&mask)) | ||
476 | continue; | ||
477 | *cp++ = (char)i; | ||
478 | } | ||
479 | spk_unlock(flags); | ||
480 | return cp-buf; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * This is called when a user changes the punctuation settings. | ||
485 | */ | ||
486 | static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
487 | const char *buf, size_t count) | ||
488 | { | ||
489 | int x; | ||
490 | struct st_var_header *p_header; | ||
491 | struct punc_var_t *var; | ||
492 | char punc_buf[100]; | ||
493 | unsigned long flags; | ||
494 | |||
495 | x = strlen(buf); | ||
496 | if (x < 1 || x > 99) | ||
497 | return -EINVAL; | ||
498 | |||
499 | p_header = var_header_by_name(attr->attr.name); | ||
500 | if (p_header == NULL) { | ||
501 | pr_warn("p_header is null, attr->attr.name is %s\n", attr->attr.name); | ||
502 | return -EINVAL; | ||
503 | } | ||
504 | |||
505 | var = get_punc_var(p_header->var_id); | ||
506 | if (var == NULL) { | ||
507 | pr_warn("var is null, p_header->var_id is %i\n", | ||
508 | p_header->var_id); | ||
509 | return -EINVAL; | ||
510 | } | ||
511 | |||
512 | strncpy(punc_buf, buf, x); | ||
513 | |||
514 | while (x && punc_buf[x - 1] == '\n') | ||
515 | x--; | ||
516 | punc_buf[x] = '\0'; | ||
517 | |||
518 | spk_lock(flags); | ||
519 | |||
520 | if (*punc_buf == 'd' || *punc_buf == 'r') | ||
521 | x = set_mask_bits(0, var->value, 3); | ||
522 | else | ||
523 | x = set_mask_bits(punc_buf, var->value, 3); | ||
524 | |||
525 | spk_unlock(flags); | ||
526 | return count; | ||
527 | } | ||
528 | |||
529 | /* | ||
530 | * This function is called when a user reads one of the variable parameters. | ||
531 | */ | ||
532 | ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
533 | char *buf) | ||
534 | { | ||
535 | int rv = 0; | ||
536 | struct st_var_header *param; | ||
537 | struct var_t *var; | ||
538 | char *cp1; | ||
539 | char *cp; | ||
540 | char ch; | ||
541 | unsigned long flags; | ||
542 | |||
543 | param = var_header_by_name(attr->attr.name); | ||
544 | if (param == NULL) | ||
545 | return -EINVAL; | ||
546 | |||
547 | spk_lock(flags); | ||
548 | var = (struct var_t *) param->data; | ||
549 | switch (param->var_type) { | ||
550 | case VAR_NUM: | ||
551 | case VAR_TIME: | ||
552 | if (var) | ||
553 | rv = sprintf(buf, "%i\n", var->u.n.value); | ||
554 | else | ||
555 | rv = sprintf(buf, "0\n"); | ||
556 | break; | ||
557 | case VAR_STRING: | ||
558 | if (var) { | ||
559 | cp1 = buf; | ||
560 | *cp1++ = '"'; | ||
561 | for (cp = (char *)param->p_val; (ch = *cp); cp++) { | ||
562 | if (ch >= ' ' && ch < '~') | ||
563 | *cp1++ = ch; | ||
564 | else | ||
565 | cp1 += sprintf(cp1, "\\""x%02x", ch); | ||
566 | } | ||
567 | *cp1++ = '"'; | ||
568 | *cp1++ = '\n'; | ||
569 | *cp1 = '\0'; | ||
570 | rv = cp1-buf; | ||
571 | } else { | ||
572 | rv = sprintf(buf, "\"\"\n"); | ||
573 | } | ||
574 | break; | ||
575 | default: | ||
576 | rv = sprintf(buf, "Bad parameter %s, type %i\n", | ||
577 | param->name, param->var_type); | ||
578 | break; | ||
579 | } | ||
580 | spk_unlock(flags); | ||
581 | return rv; | ||
582 | } | ||
583 | EXPORT_SYMBOL_GPL(spk_var_show); | ||
584 | |||
585 | /* | ||
586 | * This function is called when a user echos a value to one of the | ||
587 | * variable parameters. | ||
588 | */ | ||
589 | ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
590 | const char *buf, size_t count) | ||
591 | { | ||
592 | struct st_var_header *param; | ||
593 | int ret; | ||
594 | int len; | ||
595 | char *cp; | ||
596 | struct var_t *var_data; | ||
597 | int value; | ||
598 | unsigned long flags; | ||
599 | |||
600 | param = var_header_by_name(attr->attr.name); | ||
601 | if (param == NULL) | ||
602 | return -EINVAL; | ||
603 | if (param->data == NULL) | ||
604 | return 0; | ||
605 | ret = 0; | ||
606 | cp = xlate((char *) buf); | ||
607 | |||
608 | spk_lock(flags); | ||
609 | switch (param->var_type) { | ||
610 | case VAR_NUM: | ||
611 | case VAR_TIME: | ||
612 | if (*cp == 'd' || *cp == 'r' || *cp == '\0') | ||
613 | len = E_DEFAULT; | ||
614 | else if (*cp == '+' || *cp == '-') | ||
615 | len = E_INC; | ||
616 | else | ||
617 | len = E_SET; | ||
618 | speakup_s2i(cp, &value); | ||
619 | ret = set_num_var(value, param, len); | ||
620 | if (ret == E_RANGE) { | ||
621 | var_data = param->data; | ||
622 | pr_warn("value for %s out of range, expect %d to %d\n", | ||
623 | attr->attr.name, | ||
624 | var_data->u.n.low, var_data->u.n.high); | ||
625 | } | ||
626 | break; | ||
627 | case VAR_STRING: | ||
628 | len = strlen(buf); | ||
629 | if ((len >= 1) && (buf[len - 1] == '\n')) | ||
630 | --len; | ||
631 | if ((len >= 2) && (buf[0] == '"') && (buf[len - 1] == '"')) { | ||
632 | ++buf; | ||
633 | len -= 2; | ||
634 | } | ||
635 | cp = (char *) buf; | ||
636 | cp[len] = '\0'; | ||
637 | ret = set_string_var(buf, param, len); | ||
638 | if (ret == E_TOOLONG) | ||
639 | pr_warn("value too long for %s\n", | ||
640 | attr->attr.name); | ||
641 | break; | ||
642 | default: | ||
643 | pr_warn("%s unknown type %d\n", | ||
644 | param->name, (int)param->var_type); | ||
645 | break; | ||
646 | } | ||
647 | /* | ||
648 | * If voice was just changed, we might need to reset our default | ||
649 | * pitch and volume. | ||
650 | */ | ||
651 | if (strcmp(attr->attr.name, "voice") == 0) { | ||
652 | if (synth && synth->default_pitch) { | ||
653 | param = var_header_by_name("pitch"); | ||
654 | if (param) { | ||
655 | set_num_var(synth->default_pitch[value], param, E_NEW_DEFAULT); | ||
656 | set_num_var(0, param, E_DEFAULT); | ||
657 | } | ||
658 | } | ||
659 | if (synth && synth->default_vol) { | ||
660 | param = var_header_by_name("vol"); | ||
661 | if (param) { | ||
662 | set_num_var(synth->default_vol[value], param, E_NEW_DEFAULT); | ||
663 | set_num_var(0, param, E_DEFAULT); | ||
664 | } | ||
665 | } | ||
666 | } | ||
667 | spk_unlock(flags); | ||
668 | |||
669 | if (ret == SET_DEFAULT) | ||
670 | pr_info("%s reset to default value\n", attr->attr.name); | ||
671 | return count; | ||
672 | } | ||
673 | EXPORT_SYMBOL_GPL(spk_var_store); | ||
674 | |||
675 | /* | ||
676 | * Functions for reading and writing lists of i18n messages. Incomplete. | ||
677 | */ | ||
678 | |||
679 | static ssize_t message_show_helper(char *buf, enum msg_index_t first, | ||
680 | enum msg_index_t last) | ||
681 | { | ||
682 | size_t bufsize = PAGE_SIZE; | ||
683 | char *buf_pointer = buf; | ||
684 | int printed; | ||
685 | enum msg_index_t cursor; | ||
686 | int index = 0; | ||
687 | *buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */ | ||
688 | |||
689 | for (cursor = first; cursor <= last; cursor++, index++) { | ||
690 | if (bufsize <= 1) | ||
691 | break; | ||
692 | printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n", | ||
693 | index, msg_get(cursor)); | ||
694 | buf_pointer += printed; | ||
695 | bufsize -= printed; | ||
696 | } | ||
697 | |||
698 | return buf_pointer - buf; | ||
699 | } | ||
700 | |||
701 | static void report_msg_status(int reset, int received, int used, | ||
702 | int rejected, char *groupname) | ||
703 | { | ||
704 | int len; | ||
705 | char buf[160]; | ||
706 | |||
707 | if (reset) { | ||
708 | pr_info("i18n messages from group %s reset to defaults\n", | ||
709 | groupname); | ||
710 | } else if (received ) { | ||
711 | len = snprintf(buf, sizeof(buf), | ||
712 | " updated %d of %d i18n messages from group %s\n", | ||
713 | used, received, groupname); | ||
714 | if (rejected) | ||
715 | snprintf(buf + (len - 1), sizeof(buf) - (len - 1), | ||
716 | " with %d reject%s\n", | ||
717 | rejected, rejected > 1 ? "s" : ""); | ||
718 | printk(buf); | ||
719 | } | ||
720 | } | ||
721 | |||
722 | static ssize_t message_store_helper(const char *buf, size_t count, | ||
723 | struct msg_group_t *group) | ||
724 | { | ||
725 | char *cp = (char *) buf; | ||
726 | char *end = cp + count; | ||
727 | char *linefeed = NULL; | ||
728 | char *temp = NULL; | ||
729 | ssize_t msg_stored = 0; | ||
730 | ssize_t retval = count; | ||
731 | size_t desc_length = 0; | ||
732 | unsigned long index = 0; | ||
733 | int received = 0; | ||
734 | int used = 0; | ||
735 | int rejected = 0; | ||
736 | int reset = 0; | ||
737 | enum msg_index_t firstmessage = group->start; | ||
738 | enum msg_index_t lastmessage = group->end; | ||
739 | enum msg_index_t curmessage; | ||
740 | |||
741 | while (cp < end) { | ||
742 | |||
743 | while ((cp < end) && (*cp == ' ' || *cp == '\t')) | ||
744 | cp++; | ||
745 | |||
746 | if (cp == end) | ||
747 | break; | ||
748 | if (strchr("dDrR", *cp)) { | ||
749 | reset = 1; | ||
750 | break; | ||
751 | } | ||
752 | received++; | ||
753 | |||
754 | linefeed = strchr(cp, '\n'); | ||
755 | if (!linefeed) { | ||
756 | rejected++; | ||
757 | break; | ||
758 | } | ||
759 | |||
760 | if (! isdigit(*cp)) { | ||
761 | rejected++; | ||
762 | cp = linefeed + 1; | ||
763 | continue; | ||
764 | } | ||
765 | |||
766 | index = simple_strtoul(cp, &temp, 10); | ||
767 | |||
768 | while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) | ||
769 | temp++; | ||
770 | |||
771 | desc_length = linefeed - temp; | ||
772 | curmessage = firstmessage + index; | ||
773 | |||
774 | /* | ||
775 | * Note the check (curmessage < firstmessage). It is not | ||
776 | * redundant. Suppose that the user gave us an index | ||
777 | * equal to ULONG_MAX - 1. If firstmessage > 1, then | ||
778 | * firstmessage + index < firstmessage! | ||
779 | */ | ||
780 | |||
781 | if ((curmessage < firstmessage) || (curmessage > lastmessage)) { | ||
782 | rejected++; | ||
783 | cp = linefeed + 1; | ||
784 | continue; | ||
785 | } | ||
786 | |||
787 | msg_stored = msg_set(curmessage, temp, desc_length); | ||
788 | if (msg_stored < 0) { | ||
789 | retval = msg_stored; | ||
790 | if (msg_stored == -ENOMEM) | ||
791 | reset = 1; | ||
792 | break; | ||
793 | } else { | ||
794 | used++; | ||
795 | } | ||
796 | |||
797 | cp = linefeed + 1; | ||
798 | } | ||
799 | |||
800 | if (reset) | ||
801 | reset_msg_group(group); | ||
802 | |||
803 | report_msg_status(reset, received, used, rejected, group->name); | ||
804 | return retval; | ||
805 | } | ||
806 | |||
807 | static ssize_t message_show(struct kobject *kobj, | ||
808 | struct kobj_attribute *attr, char *buf) | ||
809 | { | ||
810 | ssize_t retval = 0; | ||
811 | struct msg_group_t *group = find_msg_group(attr->attr.name); | ||
812 | unsigned long flags; | ||
813 | |||
814 | BUG_ON(! group); | ||
815 | spk_lock(flags); | ||
816 | retval = message_show_helper(buf, group->start, group->end); | ||
817 | spk_unlock(flags); | ||
818 | return retval; | ||
819 | } | ||
820 | |||
821 | static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
822 | const char *buf, size_t count) | ||
823 | { | ||
824 | ssize_t retval = 0; | ||
825 | struct msg_group_t *group = find_msg_group(attr->attr.name); | ||
826 | |||
827 | BUG_ON(! group); | ||
828 | retval = message_store_helper(buf, count, group); | ||
829 | return retval; | ||
830 | } | ||
831 | |||
832 | /* | ||
833 | * Declare the attributes. | ||
834 | */ | ||
835 | static struct kobj_attribute keymap_attribute = | ||
836 | __ATTR(keymap, ROOT_W, keymap_show, keymap_store); | ||
837 | static struct kobj_attribute silent_attribute = | ||
838 | __ATTR(silent, USER_W, NULL, silent_store); | ||
839 | static struct kobj_attribute synth_attribute = | ||
840 | __ATTR(synth, USER_RW, synth_show, synth_store); | ||
841 | static struct kobj_attribute synth_direct_attribute = | ||
842 | __ATTR(synth_direct, USER_W, NULL, synth_direct_store); | ||
843 | static struct kobj_attribute version_attribute = | ||
844 | __ATTR_RO(version); | ||
845 | |||
846 | static struct kobj_attribute delimiters_attribute = | ||
847 | __ATTR(delimiters, USER_RW, punc_show, punc_store); | ||
848 | static struct kobj_attribute ex_num_attribute = | ||
849 | __ATTR(ex_num, USER_RW, punc_show, punc_store); | ||
850 | static struct kobj_attribute punc_all_attribute = | ||
851 | __ATTR(punc_all, USER_RW, punc_show, punc_store); | ||
852 | static struct kobj_attribute punc_most_attribute = | ||
853 | __ATTR(punc_most, USER_RW, punc_show, punc_store); | ||
854 | static struct kobj_attribute punc_some_attribute = | ||
855 | __ATTR(punc_some, USER_RW, punc_show, punc_store); | ||
856 | static struct kobj_attribute repeats_attribute = | ||
857 | __ATTR(repeats, USER_RW, punc_show, punc_store); | ||
858 | |||
859 | static struct kobj_attribute attrib_bleep_attribute = | ||
860 | __ATTR(attrib_bleep, USER_RW, spk_var_show, spk_var_store); | ||
861 | static struct kobj_attribute bell_pos_attribute = | ||
862 | __ATTR(bell_pos, USER_RW, spk_var_show, spk_var_store); | ||
863 | static struct kobj_attribute bleep_time_attribute = | ||
864 | __ATTR(bleep_time, USER_RW, spk_var_show, spk_var_store); | ||
865 | static struct kobj_attribute bleeps_attribute = | ||
866 | __ATTR(bleeps, USER_RW, spk_var_show, spk_var_store); | ||
867 | static struct kobj_attribute cursor_time_attribute = | ||
868 | __ATTR(cursor_time, USER_RW, spk_var_show, spk_var_store); | ||
869 | static struct kobj_attribute key_echo_attribute = | ||
870 | __ATTR(key_echo, USER_RW, spk_var_show, spk_var_store); | ||
871 | static struct kobj_attribute no_interrupt_attribute = | ||
872 | __ATTR(no_interrupt, USER_RW, spk_var_show, spk_var_store); | ||
873 | static struct kobj_attribute punc_level_attribute = | ||
874 | __ATTR(punc_level, USER_RW, spk_var_show, spk_var_store); | ||
875 | static struct kobj_attribute reading_punc_attribute = | ||
876 | __ATTR(reading_punc, USER_RW, spk_var_show, spk_var_store); | ||
877 | static struct kobj_attribute say_control_attribute = | ||
878 | __ATTR(say_control, USER_RW, spk_var_show, spk_var_store); | ||
879 | static struct kobj_attribute say_word_ctl_attribute = | ||
880 | __ATTR(say_word_ctl, USER_RW, spk_var_show, spk_var_store); | ||
881 | static struct kobj_attribute spell_delay_attribute = | ||
882 | __ATTR(spell_delay, USER_RW, spk_var_show, spk_var_store); | ||
883 | |||
884 | /* | ||
885 | * These attributes are i18n related. | ||
886 | */ | ||
887 | static struct kobj_attribute announcements_attribute = | ||
888 | __ATTR(announcements, USER_RW, message_show, message_store); | ||
889 | static struct kobj_attribute characters_attribute = | ||
890 | __ATTR(characters, USER_RW, chars_chartab_show, chars_chartab_store); | ||
891 | static struct kobj_attribute chartab_attribute = | ||
892 | __ATTR(chartab, USER_RW, chars_chartab_show, chars_chartab_store); | ||
893 | static struct kobj_attribute ctl_keys_attribute = | ||
894 | __ATTR(ctl_keys, USER_RW, message_show, message_store); | ||
895 | static struct kobj_attribute colors_attribute = | ||
896 | __ATTR(colors, USER_RW, message_show, message_store); | ||
897 | static struct kobj_attribute formatted_attribute = | ||
898 | __ATTR(formatted, USER_RW, message_show, message_store); | ||
899 | static struct kobj_attribute function_names_attribute = | ||
900 | __ATTR(function_names, USER_RW, message_show, message_store); | ||
901 | static struct kobj_attribute key_names_attribute = | ||
902 | __ATTR(key_names, USER_RW, message_show, message_store); | ||
903 | static struct kobj_attribute states_attribute = | ||
904 | __ATTR(states, USER_RW, message_show, message_store); | ||
905 | |||
906 | /* | ||
907 | * Create groups of attributes so that we can create and destroy them all | ||
908 | * at once. | ||
909 | */ | ||
910 | static struct attribute *main_attrs[] = { | ||
911 | &keymap_attribute.attr, | ||
912 | &silent_attribute.attr, | ||
913 | &synth_attribute.attr, | ||
914 | &synth_direct_attribute.attr, | ||
915 | &version_attribute.attr, | ||
916 | &delimiters_attribute.attr, | ||
917 | &ex_num_attribute.attr, | ||
918 | &punc_all_attribute.attr, | ||
919 | &punc_most_attribute.attr, | ||
920 | &punc_some_attribute.attr, | ||
921 | &repeats_attribute.attr, | ||
922 | &attrib_bleep_attribute.attr, | ||
923 | &bell_pos_attribute.attr, | ||
924 | &bleep_time_attribute.attr, | ||
925 | &bleeps_attribute.attr, | ||
926 | &cursor_time_attribute.attr, | ||
927 | &key_echo_attribute.attr, | ||
928 | &no_interrupt_attribute.attr, | ||
929 | &punc_level_attribute.attr, | ||
930 | &reading_punc_attribute.attr, | ||
931 | &say_control_attribute.attr, | ||
932 | &say_word_ctl_attribute.attr, | ||
933 | &spell_delay_attribute.attr, | ||
934 | NULL, | ||
935 | }; | ||
936 | |||
937 | static struct attribute *i18n_attrs[] = { | ||
938 | &announcements_attribute.attr, | ||
939 | &characters_attribute.attr, | ||
940 | &chartab_attribute.attr, | ||
941 | &ctl_keys_attribute.attr, | ||
942 | &colors_attribute.attr, | ||
943 | &formatted_attribute.attr, | ||
944 | &function_names_attribute.attr, | ||
945 | &key_names_attribute.attr, | ||
946 | &states_attribute.attr, | ||
947 | NULL, | ||
948 | }; | ||
949 | |||
950 | /* | ||
951 | * An unnamed attribute group will put all of the attributes directly in | ||
952 | * the kobject directory. If we specify a name, a subdirectory will be | ||
953 | * created for the attributes with the directory being the name of the | ||
954 | * attribute group. | ||
955 | */ | ||
956 | static struct attribute_group main_attr_group = { | ||
957 | .attrs = main_attrs, | ||
958 | }; | ||
959 | |||
960 | static struct attribute_group i18n_attr_group = { | ||
961 | .attrs = i18n_attrs, | ||
962 | .name = "i18n", | ||
963 | }; | ||
964 | |||
965 | static struct kobject *accessibility_kobj; | ||
966 | struct kobject *speakup_kobj; | ||
967 | |||
968 | int speakup_kobj_init(void) | ||
969 | { | ||
970 | int retval; | ||
971 | |||
972 | /* | ||
973 | * Create a simple kobject with the name of "accessibility", | ||
974 | * located under /sys/ | ||
975 | * | ||
976 | * As this is a simple directory, no uevent will be sent to | ||
977 | * userspace. That is why this function should not be used for | ||
978 | * any type of dynamic kobjects, where the name and number are | ||
979 | * not known ahead of time. | ||
980 | */ | ||
981 | accessibility_kobj = kobject_create_and_add("accessibility", NULL); | ||
982 | if (!accessibility_kobj) | ||
983 | return -ENOMEM; | ||
984 | |||
985 | speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj); | ||
986 | if (!speakup_kobj) { | ||
987 | kobject_put(accessibility_kobj); | ||
988 | return -ENOMEM; | ||
989 | } | ||
990 | |||
991 | /* Create the files associated with this kobject */ | ||
992 | retval = sysfs_create_group(speakup_kobj, &main_attr_group); | ||
993 | if (retval) | ||
994 | speakup_kobj_exit(); | ||
995 | |||
996 | retval = sysfs_create_group(speakup_kobj, &i18n_attr_group); | ||
997 | if (retval) | ||
998 | speakup_kobj_exit(); | ||
999 | |||
1000 | return retval; | ||
1001 | } | ||
1002 | |||
1003 | void speakup_kobj_exit(void) | ||
1004 | { | ||
1005 | kobject_put(speakup_kobj); | ||
1006 | kobject_put(accessibility_kobj); | ||
1007 | } | ||
diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c new file mode 100644 index 00000000000..8c549650f21 --- /dev/null +++ b/drivers/staging/speakup/main.c | |||
@@ -0,0 +1,2304 @@ | |||
1 | /* speakup.c | ||
2 | review functions for the speakup screen review package. | ||
3 | originally written by: Kirk Reiser and Andy Berdan. | ||
4 | |||
5 | extensively modified by David Borowski. | ||
6 | |||
7 | Copyright (C) 1998 Kirk Reiser. | ||
8 | Copyright (C) 2003 David Borowski. | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | GNU General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program; if not, write to the Free Software | ||
22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/version.h> | ||
27 | #include <linux/vt.h> | ||
28 | #include <linux/tty.h> | ||
29 | #include <linux/mm.h> /* __get_free_page() and friends */ | ||
30 | #include <linux/vt_kern.h> | ||
31 | #include <linux/ctype.h> | ||
32 | #include <linux/selection.h> | ||
33 | #include <linux/unistd.h> | ||
34 | #include <linux/jiffies.h> | ||
35 | #include <linux/kthread.h> | ||
36 | #include <linux/keyboard.h> /* for KT_SHIFT */ | ||
37 | #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */ | ||
38 | #include <linux/input.h> | ||
39 | #include <linux/kmod.h> | ||
40 | |||
41 | #include <linux/bootmem.h> /* for alloc_bootmem */ | ||
42 | |||
43 | /* speakup_*_selection */ | ||
44 | #include <linux/module.h> | ||
45 | #include <linux/sched.h> | ||
46 | #include <linux/slab.h> | ||
47 | #include <linux/types.h> | ||
48 | #include <linux/consolemap.h> | ||
49 | |||
50 | #include <linux/spinlock.h> | ||
51 | #include <linux/notifier.h> | ||
52 | |||
53 | #include <linux/uaccess.h> /* copy_from|to|user() and others */ | ||
54 | |||
55 | #include "spk_priv.h" | ||
56 | #include "speakup.h" | ||
57 | |||
58 | #define MAX_DELAY msecs_to_jiffies(500) | ||
59 | #define MINECHOCHAR SPACE | ||
60 | |||
61 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
62 | MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>"); | ||
63 | MODULE_DESCRIPTION("Speakup console speech"); | ||
64 | MODULE_LICENSE("GPL"); | ||
65 | MODULE_VERSION(SPEAKUP_VERSION); | ||
66 | |||
67 | char *synth_name; | ||
68 | module_param_named(synth, synth_name, charp, S_IRUGO); | ||
69 | module_param_named(quiet, quiet_boot, bool, S_IRUGO); | ||
70 | |||
71 | MODULE_PARM_DESC(synth, "Synth to start if speakup is built in."); | ||
72 | MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found."); | ||
73 | |||
74 | special_func special_handler; | ||
75 | |||
76 | short pitch_shift, synth_flags; | ||
77 | static char buf[256]; | ||
78 | int attrib_bleep, bleeps, bleep_time = 10; | ||
79 | int no_intr, spell_delay; | ||
80 | int key_echo, say_word_ctl; | ||
81 | int say_ctrl, bell_pos; | ||
82 | short punc_mask; | ||
83 | int punc_level, reading_punc; | ||
84 | char str_caps_start[MAXVARLEN+1] = "\0", str_caps_stop[MAXVARLEN+1] = "\0"; | ||
85 | const struct st_bits_data punc_info[] = { | ||
86 | { "none", "", 0 }, | ||
87 | { "some", "/$%&@", SOME }, | ||
88 | { "most", "$%&#()=+*/@^<>|\\", MOST }, | ||
89 | { "all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC }, | ||
90 | { "delimiters", "", B_WDLM }, | ||
91 | { "repeats", "()", CH_RPT }, | ||
92 | { "extended numeric", "", B_EXNUM }, | ||
93 | { "symbols", "", B_SYM }, | ||
94 | { 0, 0 } | ||
95 | }; | ||
96 | static char mark_cut_flag; | ||
97 | #define MAX_KEY 160 | ||
98 | u_char *our_keys[MAX_KEY], *shift_table; | ||
99 | u_char key_buf[600]; | ||
100 | const u_char key_defaults[] = { | ||
101 | #include "speakupmap.h" | ||
102 | }; | ||
103 | |||
104 | /* Speakup Cursor Track Variables */ | ||
105 | static int cursor_track = 1, prev_cursor_track = 1; | ||
106 | |||
107 | /* cursor track modes, must be ordered same as cursor_msgs */ | ||
108 | enum { | ||
109 | CT_Off = 0, | ||
110 | CT_On, | ||
111 | CT_Highlight, | ||
112 | CT_Window, | ||
113 | CT_Max | ||
114 | }; | ||
115 | #define read_all_mode CT_Max | ||
116 | |||
117 | static struct tty_struct *tty; | ||
118 | |||
119 | static void spkup_write(const char *in_buf, int count); | ||
120 | |||
121 | |||
122 | static char *phonetic[] = { | ||
123 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", | ||
124 | "india", "juliett", "keelo", "leema", "mike", "november", "oscar", "papa", | ||
125 | "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey", | ||
126 | "x ray", "yankee", "zulu" | ||
127 | }; | ||
128 | |||
129 | /* array of 256 char pointers (one for each character description) | ||
130 | * initialized to default_chars and user selectable via | ||
131 | * /proc/speakup/characters */ | ||
132 | char *characters[256]; | ||
133 | |||
134 | char *default_chars[256] = { | ||
135 | /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g", | ||
136 | /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o", | ||
137 | /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w", | ||
138 | /*024*/ "^x", "^y", "^z", "control", "control", "control", "control", "control", | ||
139 | /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and", "tick", | ||
140 | /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash", "dot", | ||
141 | "slash", | ||
142 | /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven", | ||
143 | "eight", "nine", | ||
144 | /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at", | ||
145 | /*065*/ "EIGH", "B", "C", "D", "E", "F", "G", | ||
146 | /*072*/ "H", "I", "J", "K", "L", "M", "N", "O", | ||
147 | /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X", | ||
148 | /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket", "caret", | ||
149 | "line", | ||
150 | /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g", | ||
151 | /*104*/ "h", "i", "j", "k", "l", "m", "n", "o", | ||
152 | /*112*/ "p", "q", "r", "s", "t", "u", "v", "w", | ||
153 | /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh", | ||
154 | /*127*/ "del", "control", "control", "control", "control", "control", "control", "control", "control", "control", "control", | ||
155 | /*138*/ "control", "control", "control", "control", "control", "control", "control", "control", "control", "control", "control", "control", | ||
156 | /*150*/ "control", "control", "control", "control", "control", "control", "control", "control", "control", "control", | ||
157 | /*160*/ "nbsp", "inverted bang", | ||
158 | /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section", | ||
159 | /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle", | ||
160 | /*172*/ "not", "soft hyphen", "registered", "macron", | ||
161 | /*176*/ "degrees", "plus or minus", "super two", "super three", | ||
162 | /*180*/ "acute accent", "micro", "pilcrow", "middle dot", | ||
163 | /*184*/ "cedilla", "super one", "male ordinal", "double right angle", | ||
164 | /*188*/ "one quarter", "one half", "three quarters", "inverted question", | ||
165 | /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT", "A RING", | ||
166 | /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX", "E OOMLAUT", | ||
167 | /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH", "N TILDE", | ||
168 | /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT", | ||
169 | /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE", "U CIRCUMFLEX", | ||
170 | /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave", | ||
171 | /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring", | ||
172 | /*230*/ "ae", "c cidella", "e grave", "e acute", | ||
173 | /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute", "i circumflex", | ||
174 | /*239*/ "i oomlaut", "eth", "n tilde","o grave", "o acute", "o circumflex", | ||
175 | /*245*/"o tilde", "o oomlaut", "divided by", "o stroke", "u grave", "u acute", | ||
176 | /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut" | ||
177 | }; | ||
178 | |||
179 | /* array of 256 u_short (one for each character) | ||
180 | * initialized to default_chartab and user selectable via | ||
181 | * /sys/module/speakup/parameters/chartab */ | ||
182 | u_short spk_chartab[256]; | ||
183 | |||
184 | static u_short default_chartab[256] = { | ||
185 | B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */ | ||
186 | B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */ | ||
187 | B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */ | ||
188 | B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */ | ||
189 | WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */ | ||
190 | PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */ | ||
191 | NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */ | ||
192 | NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */ | ||
193 | PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */ | ||
194 | A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */ | ||
195 | A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */ | ||
196 | A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */ | ||
197 | PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */ | ||
198 | ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */ | ||
199 | ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */ | ||
200 | ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */ | ||
201 | B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-135 */ | ||
202 | B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 136-143 */ | ||
203 | B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 144-151 */ | ||
204 | B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 152-159 */ | ||
205 | WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, B_SYM, /* 160-167 */ | ||
206 | B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */ | ||
207 | B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */ | ||
208 | B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */ | ||
209 | A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */ | ||
210 | A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */ | ||
211 | A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */ | ||
212 | A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */ | ||
213 | ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */ | ||
214 | ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */ | ||
215 | ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */ | ||
216 | ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */ | ||
217 | }; | ||
218 | |||
219 | struct task_struct *speakup_task; | ||
220 | struct bleep unprocessed_sound; | ||
221 | static int spk_keydown; | ||
222 | static u_char spk_lastkey, spk_close_press, keymap_flags; | ||
223 | static u_char last_keycode, this_speakup_key; | ||
224 | static u_long last_spk_jiffy; | ||
225 | |||
226 | struct st_spk_t *speakup_console[MAX_NR_CONSOLES]; | ||
227 | |||
228 | DEFINE_MUTEX(spk_mutex); | ||
229 | |||
230 | static int keyboard_notifier_call(struct notifier_block *, | ||
231 | unsigned long code, void *param); | ||
232 | |||
233 | struct notifier_block keyboard_notifier_block = { | ||
234 | .notifier_call = keyboard_notifier_call, | ||
235 | }; | ||
236 | |||
237 | static int vt_notifier_call(struct notifier_block *, | ||
238 | unsigned long code, void *param); | ||
239 | |||
240 | struct notifier_block vt_notifier_block = { | ||
241 | .notifier_call = vt_notifier_call, | ||
242 | }; | ||
243 | |||
244 | static unsigned char get_attributes(u16 *pos) | ||
245 | { | ||
246 | return (u_char)(scr_readw(pos) >> 8); | ||
247 | } | ||
248 | |||
249 | static void speakup_date(struct vc_data *vc) | ||
250 | { | ||
251 | spk_x = spk_cx = vc->vc_x; | ||
252 | spk_y = spk_cy = vc->vc_y; | ||
253 | spk_pos = spk_cp = vc->vc_pos; | ||
254 | spk_old_attr = spk_attr; | ||
255 | spk_attr = get_attributes((u_short *) spk_pos); | ||
256 | } | ||
257 | |||
258 | static void bleep(u_short val) | ||
259 | { | ||
260 | static const short vals[] = { | ||
261 | 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659 | ||
262 | }; | ||
263 | short freq; | ||
264 | int time = bleep_time; | ||
265 | freq = vals[val%12]; | ||
266 | if (val > 11) | ||
267 | freq *= (1 << (val/12)); | ||
268 | unprocessed_sound.freq = freq; | ||
269 | unprocessed_sound.jiffies = msecs_to_jiffies(time); | ||
270 | unprocessed_sound.active = 1; | ||
271 | /* We can only have 1 active sound at a time. */ | ||
272 | } | ||
273 | |||
274 | static void speakup_shut_up(struct vc_data *vc) | ||
275 | { | ||
276 | if (spk_killed) | ||
277 | return; | ||
278 | spk_shut_up |= 0x01; | ||
279 | spk_parked &= 0xfe; | ||
280 | speakup_date(vc); | ||
281 | if (synth != NULL) | ||
282 | do_flush(); | ||
283 | } | ||
284 | |||
285 | static void speech_kill(struct vc_data *vc) | ||
286 | { | ||
287 | char val = synth->is_alive(synth); | ||
288 | if (val == 0) | ||
289 | return; | ||
290 | |||
291 | /* re-enables synth, if disabled */ | ||
292 | if (val == 2 || spk_killed) { | ||
293 | /* dead */ | ||
294 | spk_shut_up &= ~0x40; | ||
295 | synth_printf("%s\n", msg_get(MSG_IAM_ALIVE)); | ||
296 | } else { | ||
297 | synth_printf("%s\n", msg_get(MSG_YOU_KILLED_SPEAKUP)); | ||
298 | spk_shut_up |= 0x40; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | static void speakup_off(struct vc_data *vc) | ||
303 | { | ||
304 | if (spk_shut_up & 0x80) { | ||
305 | spk_shut_up &= 0x7f; | ||
306 | synth_printf("%s\n", msg_get(MSG_HEY_THATS_BETTER)); | ||
307 | } else { | ||
308 | spk_shut_up |= 0x80; | ||
309 | synth_printf("%s\n", msg_get(MSG_YOU_TURNED_ME_OFF)); | ||
310 | } | ||
311 | speakup_date(vc); | ||
312 | } | ||
313 | |||
314 | static void speakup_parked(struct vc_data *vc) | ||
315 | { | ||
316 | if (spk_parked & 0x80) { | ||
317 | spk_parked = 0; | ||
318 | synth_printf("%s\n", msg_get(MSG_UNPARKED)); | ||
319 | } else { | ||
320 | spk_parked |= 0x80; | ||
321 | synth_printf("%s\n", msg_get(MSG_PARKED)); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | static void speakup_cut(struct vc_data *vc) | ||
326 | { | ||
327 | static const char err_buf[] = "set selection failed"; | ||
328 | int ret; | ||
329 | |||
330 | if (!mark_cut_flag) { | ||
331 | mark_cut_flag = 1; | ||
332 | xs = (u_short) spk_x; | ||
333 | ys = (u_short) spk_y; | ||
334 | spk_sel_cons = vc; | ||
335 | synth_printf("%s\n", msg_get(MSG_MARK)); | ||
336 | return; | ||
337 | } | ||
338 | xe = (u_short) spk_x; | ||
339 | ye = (u_short) spk_y; | ||
340 | mark_cut_flag = 0; | ||
341 | synth_printf("%s\n", msg_get(MSG_CUT)); | ||
342 | |||
343 | speakup_clear_selection(); | ||
344 | ret = speakup_set_selection(tty); | ||
345 | |||
346 | switch (ret) { | ||
347 | case 0: | ||
348 | break; /* no error */ | ||
349 | case -EFAULT : | ||
350 | pr_warn("%sEFAULT\n", err_buf); | ||
351 | break; | ||
352 | case -EINVAL : | ||
353 | pr_warn("%sEINVAL\n", err_buf); | ||
354 | break; | ||
355 | case -ENOMEM : | ||
356 | pr_warn("%sENOMEM\n", err_buf); | ||
357 | break; | ||
358 | } | ||
359 | } | ||
360 | |||
361 | static void speakup_paste(struct vc_data *vc) | ||
362 | { | ||
363 | if (mark_cut_flag) { | ||
364 | mark_cut_flag = 0; | ||
365 | synth_printf("%s\n", msg_get(MSG_MARK_CLEARED)); | ||
366 | } else { | ||
367 | synth_printf("%s\n", msg_get(MSG_PASTE)); | ||
368 | speakup_paste_selection(tty); | ||
369 | } | ||
370 | } | ||
371 | |||
372 | static void say_attributes(struct vc_data *vc) | ||
373 | { | ||
374 | int fg = spk_attr & 0x0f; | ||
375 | int bg = spk_attr >> 4; | ||
376 | if (fg > 8) { | ||
377 | synth_printf("%s ", msg_get(MSG_BRIGHT)); | ||
378 | fg -= 8; | ||
379 | } | ||
380 | synth_printf("%s", msg_get(MSG_COLORS_START + fg)); | ||
381 | if (bg > 7) { | ||
382 | synth_printf(" %s ", msg_get(MSG_ON_BLINKING)); | ||
383 | bg -= 8; | ||
384 | } else | ||
385 | synth_printf(" %s ", msg_get(MSG_ON)); | ||
386 | synth_printf("%s\n", msg_get(MSG_COLORS_START + bg)); | ||
387 | } | ||
388 | |||
389 | enum { | ||
390 | edge_top = 1, | ||
391 | edge_bottom, | ||
392 | edge_left, | ||
393 | edge_right, | ||
394 | edge_quiet | ||
395 | }; | ||
396 | |||
397 | static void announce_edge(struct vc_data *vc, int msg_id) | ||
398 | { | ||
399 | if (bleeps & 1) | ||
400 | bleep(spk_y); | ||
401 | if ((bleeps & 2) && (msg_id < edge_quiet)) | ||
402 | synth_printf("%s\n", msg_get(MSG_EDGE_MSGS_START + msg_id - 1)); | ||
403 | } | ||
404 | |||
405 | static void speak_char(u_char ch) | ||
406 | { | ||
407 | char *cp = characters[ch]; | ||
408 | struct var_t *direct = get_var(DIRECT); | ||
409 | if (direct && direct->u.n.value) { | ||
410 | if (IS_CHAR(ch, B_CAP)) { | ||
411 | pitch_shift++; | ||
412 | synth_printf("%s", str_caps_start); | ||
413 | } | ||
414 | synth_printf("%c", ch); | ||
415 | if (IS_CHAR(ch, B_CAP)) | ||
416 | synth_printf("%s", str_caps_stop); | ||
417 | return; | ||
418 | } | ||
419 | if (cp == NULL) { | ||
420 | pr_info("speak_char: cp == NULL!\n"); | ||
421 | return; | ||
422 | } | ||
423 | synth_buffer_add(SPACE); | ||
424 | if (IS_CHAR(ch, B_CAP)) { | ||
425 | pitch_shift++; | ||
426 | synth_printf("%s", str_caps_start); | ||
427 | synth_printf("%s", cp); | ||
428 | synth_printf("%s", str_caps_stop); | ||
429 | } else { | ||
430 | if (*cp == '^') { | ||
431 | synth_printf("%s", msg_get(MSG_CTRL)); | ||
432 | cp++; | ||
433 | } | ||
434 | synth_printf("%s", cp); | ||
435 | } | ||
436 | synth_buffer_add(SPACE); | ||
437 | } | ||
438 | |||
439 | static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs) | ||
440 | { | ||
441 | u16 ch = ' '; | ||
442 | if (vc && pos) { | ||
443 | u16 w = scr_readw(pos); | ||
444 | u16 c = w & 0xff; | ||
445 | |||
446 | if (w & vc->vc_hi_font_mask) | ||
447 | c |= 0x100; | ||
448 | |||
449 | ch = inverse_translate(vc, c, 0); | ||
450 | *attribs = (w & 0xff00) >> 8; | ||
451 | } | ||
452 | return ch; | ||
453 | } | ||
454 | |||
455 | static void say_char(struct vc_data *vc) | ||
456 | { | ||
457 | u_short ch; | ||
458 | spk_old_attr = spk_attr; | ||
459 | ch = get_char(vc, (u_short *) spk_pos, &spk_attr); | ||
460 | if (spk_attr != spk_old_attr) { | ||
461 | if (attrib_bleep & 1) | ||
462 | bleep(spk_y); | ||
463 | if (attrib_bleep & 2) | ||
464 | say_attributes(vc); | ||
465 | } | ||
466 | speak_char(ch & 0xff); | ||
467 | } | ||
468 | |||
469 | static void say_phonetic_char(struct vc_data *vc) | ||
470 | { | ||
471 | u_short ch; | ||
472 | spk_old_attr = spk_attr; | ||
473 | ch = get_char(vc, (u_short *) spk_pos, &spk_attr); | ||
474 | if (isascii(ch) && isalpha(ch)) { | ||
475 | ch &= 0x1f; | ||
476 | synth_printf("%s\n", phonetic[--ch]); | ||
477 | } else { | ||
478 | if (IS_CHAR(ch, B_NUM)) | ||
479 | synth_printf("%s ", msg_get(MSG_NUMBER)); | ||
480 | speak_char(ch); | ||
481 | } | ||
482 | } | ||
483 | |||
484 | static void say_prev_char(struct vc_data *vc) | ||
485 | { | ||
486 | spk_parked |= 0x01; | ||
487 | if (spk_x == 0) { | ||
488 | announce_edge(vc, edge_left); | ||
489 | return; | ||
490 | } | ||
491 | spk_x--; | ||
492 | spk_pos -= 2; | ||
493 | say_char(vc); | ||
494 | } | ||
495 | |||
496 | static void say_next_char(struct vc_data *vc) | ||
497 | { | ||
498 | spk_parked |= 0x01; | ||
499 | if (spk_x == vc->vc_cols - 1) { | ||
500 | announce_edge(vc, edge_right); | ||
501 | return; | ||
502 | } | ||
503 | spk_x++; | ||
504 | spk_pos += 2; | ||
505 | say_char(vc); | ||
506 | } | ||
507 | |||
508 | /* get_word - will first check to see if the character under the | ||
509 | reading cursor is a space and if say_word_ctl is true it will | ||
510 | return the word space. If say_word_ctl is not set it will check to | ||
511 | see if there is a word starting on the next position to the right | ||
512 | and return that word if it exists. If it does not exist it will | ||
513 | move left to the beginning of any previous word on the line or the | ||
514 | beginning off the line whichever comes first.. */ | ||
515 | |||
516 | static u_long get_word(struct vc_data *vc) | ||
517 | { | ||
518 | u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos; | ||
519 | char ch; | ||
520 | u_short attr_ch; | ||
521 | u_char temp; | ||
522 | spk_old_attr = spk_attr; | ||
523 | ch = (char) get_char(vc, (u_short *) tmp_pos, &temp); | ||
524 | |||
525 | /* decided to take out the sayword if on a space (mis-information */ | ||
526 | if (say_word_ctl && ch == SPACE) { | ||
527 | *buf = '\0'; | ||
528 | synth_printf("%s\n", msg_get(MSG_SPACE)); | ||
529 | return 0; | ||
530 | } else if ((tmpx < vc->vc_cols - 2) | ||
531 | && (ch == SPACE || ch == 0 || IS_WDLM(ch)) | ||
532 | && ((char) get_char(vc, (u_short *) &tmp_pos+1, &temp) > SPACE)) { | ||
533 | tmp_pos += 2; | ||
534 | tmpx++; | ||
535 | } else | ||
536 | while (tmpx > 0) { | ||
537 | ch = (char) get_char(vc, (u_short *) tmp_pos - 1, &temp); | ||
538 | if ((ch == SPACE || ch == 0 || IS_WDLM(ch)) | ||
539 | && ((char) get_char(vc, (u_short *) tmp_pos, &temp) > | ||
540 | SPACE)) | ||
541 | break; | ||
542 | tmp_pos -= 2; | ||
543 | tmpx--; | ||
544 | } | ||
545 | attr_ch = get_char(vc, (u_short *) tmp_pos, &spk_attr); | ||
546 | buf[cnt++] = attr_ch & 0xff; | ||
547 | while (tmpx < vc->vc_cols - 1) { | ||
548 | tmp_pos += 2; | ||
549 | tmpx++; | ||
550 | ch = (char) get_char(vc, (u_short *) tmp_pos, &temp); | ||
551 | if ((ch == SPACE) || ch == 0 || (IS_WDLM(buf[cnt-1]) && (ch > SPACE))) | ||
552 | break; | ||
553 | buf[cnt++] = ch; | ||
554 | } | ||
555 | buf[cnt] = '\0'; | ||
556 | return cnt; | ||
557 | } | ||
558 | |||
559 | static void say_word(struct vc_data *vc) | ||
560 | { | ||
561 | u_long cnt = get_word(vc); | ||
562 | u_short saved_punc_mask = punc_mask; | ||
563 | if (cnt == 0) | ||
564 | return; | ||
565 | punc_mask = PUNC; | ||
566 | buf[cnt++] = SPACE; | ||
567 | spkup_write(buf, cnt); | ||
568 | punc_mask = saved_punc_mask; | ||
569 | } | ||
570 | |||
571 | static void say_prev_word(struct vc_data *vc) | ||
572 | { | ||
573 | u_char temp; | ||
574 | char ch; | ||
575 | u_short edge_said = 0, last_state = 0, state = 0; | ||
576 | spk_parked |= 0x01; | ||
577 | |||
578 | if (spk_x == 0) { | ||
579 | if (spk_y == 0) { | ||
580 | announce_edge(vc, edge_top); | ||
581 | return; | ||
582 | } | ||
583 | spk_y--; | ||
584 | spk_x = vc->vc_cols; | ||
585 | edge_said = edge_quiet; | ||
586 | } | ||
587 | while (1) { | ||
588 | if (spk_x == 0) { | ||
589 | if (spk_y == 0) { | ||
590 | edge_said = edge_top; | ||
591 | break; | ||
592 | } | ||
593 | if (edge_said != edge_quiet) | ||
594 | edge_said = edge_left; | ||
595 | if (state > 0) | ||
596 | break; | ||
597 | spk_y--; | ||
598 | spk_x = vc->vc_cols - 1; | ||
599 | } else | ||
600 | spk_x--; | ||
601 | spk_pos -= 2; | ||
602 | ch = (char) get_char(vc, (u_short *) spk_pos, &temp); | ||
603 | if (ch == SPACE || ch == 0) | ||
604 | state = 0; | ||
605 | else if (IS_WDLM(ch)) | ||
606 | state = 1; | ||
607 | else | ||
608 | state = 2; | ||
609 | if (state < last_state) { | ||
610 | spk_pos += 2; | ||
611 | spk_x++; | ||
612 | break; | ||
613 | } | ||
614 | last_state = state; | ||
615 | } | ||
616 | if (spk_x == 0 && edge_said == edge_quiet) | ||
617 | edge_said = edge_left; | ||
618 | if (edge_said > 0 && edge_said < edge_quiet) | ||
619 | announce_edge(vc, edge_said); | ||
620 | say_word(vc); | ||
621 | } | ||
622 | |||
623 | static void say_next_word(struct vc_data *vc) | ||
624 | { | ||
625 | u_char temp; | ||
626 | char ch; | ||
627 | u_short edge_said = 0, last_state = 2, state = 0; | ||
628 | spk_parked |= 0x01; | ||
629 | |||
630 | if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) { | ||
631 | announce_edge(vc, edge_bottom); | ||
632 | return; | ||
633 | } | ||
634 | while (1) { | ||
635 | ch = (char) get_char(vc, (u_short *) spk_pos, &temp); | ||
636 | if (ch == SPACE || ch == 0) | ||
637 | state = 0; | ||
638 | else if (IS_WDLM(ch)) | ||
639 | state = 1; | ||
640 | else | ||
641 | state = 2; | ||
642 | if (state > last_state) | ||
643 | break; | ||
644 | if (spk_x >= vc->vc_cols - 1) { | ||
645 | if (spk_y == vc->vc_rows - 1) { | ||
646 | edge_said = edge_bottom; | ||
647 | break; | ||
648 | } | ||
649 | state = 0; | ||
650 | spk_y++; | ||
651 | spk_x = 0; | ||
652 | edge_said = edge_right; | ||
653 | } else | ||
654 | spk_x++; | ||
655 | spk_pos += 2; | ||
656 | last_state = state; | ||
657 | } | ||
658 | if (edge_said > 0) | ||
659 | announce_edge(vc, edge_said); | ||
660 | say_word(vc); | ||
661 | } | ||
662 | |||
663 | static void spell_word(struct vc_data *vc) | ||
664 | { | ||
665 | static char *delay_str[] = { "", ",", ".", ". .", ". . ." }; | ||
666 | char *cp = buf, *str_cap = str_caps_stop; | ||
667 | char *cp1, *last_cap = str_caps_stop; | ||
668 | u_char ch; | ||
669 | if (!get_word(vc)) | ||
670 | return; | ||
671 | while ((ch = (u_char) *cp)) { | ||
672 | if (cp != buf) | ||
673 | synth_printf(" %s ", delay_str[spell_delay]); | ||
674 | if (IS_CHAR(ch, B_CAP)) { | ||
675 | str_cap = str_caps_start; | ||
676 | if (*str_caps_stop) | ||
677 | pitch_shift++; | ||
678 | else /* synth has no pitch */ | ||
679 | last_cap = str_caps_stop; | ||
680 | } else | ||
681 | str_cap = str_caps_stop; | ||
682 | if (str_cap != last_cap) { | ||
683 | synth_printf("%s", str_cap); | ||
684 | last_cap = str_cap; | ||
685 | } | ||
686 | if (this_speakup_key == SPELL_PHONETIC | ||
687 | && (isascii(ch) && isalpha(ch))) { | ||
688 | ch &= 31; | ||
689 | cp1 = phonetic[--ch]; | ||
690 | } else { | ||
691 | cp1 = characters[ch]; | ||
692 | if (*cp1 == '^') { | ||
693 | synth_printf("%s", msg_get(MSG_CTRL)); | ||
694 | cp1++; | ||
695 | } | ||
696 | } | ||
697 | synth_printf("%s", cp1); | ||
698 | cp++; | ||
699 | } | ||
700 | if (str_cap != str_caps_stop) | ||
701 | synth_printf("%s", str_caps_stop); | ||
702 | } | ||
703 | |||
704 | static int get_line(struct vc_data *vc) | ||
705 | { | ||
706 | u_long tmp = spk_pos - (spk_x * 2); | ||
707 | int i = 0; | ||
708 | u_char tmp2; | ||
709 | |||
710 | spk_old_attr = spk_attr; | ||
711 | spk_attr = get_attributes((u_short *) spk_pos); | ||
712 | for (i = 0; i < vc->vc_cols; i++) { | ||
713 | buf[i] = (u_char) get_char(vc, (u_short *) tmp, &tmp2); | ||
714 | tmp += 2; | ||
715 | } | ||
716 | for (--i; i >= 0; i--) | ||
717 | if (buf[i] != SPACE) | ||
718 | break; | ||
719 | return ++i; | ||
720 | } | ||
721 | |||
722 | static void say_line(struct vc_data *vc) | ||
723 | { | ||
724 | int i = get_line(vc); | ||
725 | char *cp; | ||
726 | u_short saved_punc_mask = punc_mask; | ||
727 | if (i == 0) { | ||
728 | synth_printf("%s\n", msg_get(MSG_BLANK)); | ||
729 | return; | ||
730 | } | ||
731 | buf[i++] = '\n'; | ||
732 | if (this_speakup_key == SAY_LINE_INDENT) { | ||
733 | for (cp = buf; *cp == SPACE; cp++) | ||
734 | ; | ||
735 | synth_printf("%d, ", (cp - buf) + 1); | ||
736 | } | ||
737 | punc_mask = punc_masks[reading_punc]; | ||
738 | spkup_write(buf, i); | ||
739 | punc_mask = saved_punc_mask; | ||
740 | } | ||
741 | |||
742 | static void say_prev_line(struct vc_data *vc) | ||
743 | { | ||
744 | spk_parked |= 0x01; | ||
745 | if (spk_y == 0) { | ||
746 | announce_edge(vc, edge_top); | ||
747 | return; | ||
748 | } | ||
749 | spk_y--; | ||
750 | spk_pos -= vc->vc_size_row; | ||
751 | say_line(vc); | ||
752 | } | ||
753 | |||
754 | static void say_next_line(struct vc_data *vc) | ||
755 | { | ||
756 | spk_parked |= 0x01; | ||
757 | if (spk_y == vc->vc_rows - 1) { | ||
758 | announce_edge(vc, edge_bottom); | ||
759 | return; | ||
760 | } | ||
761 | spk_y++; | ||
762 | spk_pos += vc->vc_size_row; | ||
763 | say_line(vc); | ||
764 | } | ||
765 | |||
766 | static int say_from_to(struct vc_data *vc, u_long from, u_long to, | ||
767 | int read_punc) | ||
768 | { | ||
769 | int i = 0; | ||
770 | u_char tmp; | ||
771 | u_short saved_punc_mask = punc_mask; | ||
772 | spk_old_attr = spk_attr; | ||
773 | spk_attr = get_attributes((u_short *) from); | ||
774 | while (from < to) { | ||
775 | buf[i++] = (char) get_char(vc, (u_short *) from, &tmp); | ||
776 | from += 2; | ||
777 | if (i >= vc->vc_size_row) | ||
778 | break; | ||
779 | } | ||
780 | for (--i; i >= 0; i--) | ||
781 | if (buf[i] != SPACE) | ||
782 | break; | ||
783 | buf[++i] = SPACE; | ||
784 | buf[++i] = '\0'; | ||
785 | if (i < 1) | ||
786 | return i; | ||
787 | if (read_punc) | ||
788 | punc_mask = punc_info[reading_punc].mask; | ||
789 | spkup_write(buf, i); | ||
790 | if (read_punc) | ||
791 | punc_mask = saved_punc_mask; | ||
792 | return i - 1; | ||
793 | } | ||
794 | |||
795 | static void say_line_from_to(struct vc_data *vc, u_long from, u_long to, | ||
796 | int read_punc) | ||
797 | { | ||
798 | u_long start = vc->vc_origin + (spk_y * vc->vc_size_row); | ||
799 | u_long end = start + (to * 2); | ||
800 | start += from * 2; | ||
801 | if (say_from_to(vc, start, end, read_punc) <= 0) | ||
802 | if (cursor_track != read_all_mode) | ||
803 | synth_printf("%s\n", msg_get(MSG_BLANK)); | ||
804 | } | ||
805 | |||
806 | /* Sentence Reading Commands */ | ||
807 | |||
808 | void synth_insert_next_index(int); | ||
809 | |||
810 | static int currsentence; | ||
811 | static int numsentences[2]; | ||
812 | static char *sentbufend[2]; | ||
813 | static char *sentmarks[2][10]; | ||
814 | static int currbuf; | ||
815 | static int bn; | ||
816 | static char sentbuf[2][256]; | ||
817 | |||
818 | static int say_sentence_num(int num , int prev) | ||
819 | { | ||
820 | bn = currbuf; | ||
821 | currsentence = num + 1; | ||
822 | if (prev && --bn == -1) | ||
823 | bn = 1; | ||
824 | |||
825 | if (num > numsentences[bn]) | ||
826 | return 0; | ||
827 | |||
828 | spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]); | ||
829 | return 1; | ||
830 | } | ||
831 | |||
832 | static int get_sentence_buf(struct vc_data *vc, int read_punc) | ||
833 | { | ||
834 | u_long start, end; | ||
835 | int i, bn; | ||
836 | u_char tmp; | ||
837 | |||
838 | currbuf++; | ||
839 | if (currbuf == 2) | ||
840 | currbuf = 0; | ||
841 | bn = currbuf; | ||
842 | start = vc->vc_origin + ((spk_y) * vc->vc_size_row); | ||
843 | end = vc->vc_origin+((spk_y) * vc->vc_size_row) + vc->vc_cols * 2; | ||
844 | |||
845 | numsentences[bn] = 0; | ||
846 | sentmarks[bn][0] = &sentbuf[bn][0]; | ||
847 | i = 0; | ||
848 | spk_old_attr = spk_attr; | ||
849 | spk_attr = get_attributes((u_short *) start); | ||
850 | |||
851 | while (start < end) { | ||
852 | sentbuf[bn][i] = (char) get_char(vc, (u_short *) start, &tmp); | ||
853 | if (i > 0) { | ||
854 | if (sentbuf[bn][i] == SPACE && sentbuf[bn][i-1] == '.' | ||
855 | && numsentences[bn] < 9) { | ||
856 | /* Sentence Marker */ | ||
857 | numsentences[bn]++; | ||
858 | sentmarks[bn][numsentences[bn]] = | ||
859 | &sentbuf[bn][i]; | ||
860 | } | ||
861 | } | ||
862 | i++; | ||
863 | start += 2; | ||
864 | if (i >= vc->vc_size_row) | ||
865 | break; | ||
866 | } | ||
867 | |||
868 | for (--i; i >= 0; i--) | ||
869 | if (sentbuf[bn][i] != SPACE) | ||
870 | break; | ||
871 | |||
872 | if (i < 1) | ||
873 | return -1; | ||
874 | |||
875 | sentbuf[bn][++i] = SPACE; | ||
876 | sentbuf[bn][++i] = '\0'; | ||
877 | |||
878 | sentbufend[bn] = &sentbuf[bn][i]; | ||
879 | return numsentences[bn]; | ||
880 | } | ||
881 | |||
882 | static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to) | ||
883 | { | ||
884 | u_long start = vc->vc_origin, end; | ||
885 | if (from > 0) | ||
886 | start += from * vc->vc_size_row; | ||
887 | if (to > vc->vc_rows) | ||
888 | to = vc->vc_rows; | ||
889 | end = vc->vc_origin + (to * vc->vc_size_row); | ||
890 | for (from = start; from < end; from = to) { | ||
891 | to = from + vc->vc_size_row; | ||
892 | say_from_to(vc, from, to, 1); | ||
893 | } | ||
894 | } | ||
895 | |||
896 | static void say_screen(struct vc_data *vc) | ||
897 | { | ||
898 | say_screen_from_to(vc, 0, vc->vc_rows); | ||
899 | } | ||
900 | |||
901 | static void speakup_win_say(struct vc_data *vc) | ||
902 | { | ||
903 | u_long start, end, from, to; | ||
904 | if (win_start < 2) { | ||
905 | synth_printf("%s\n", msg_get(MSG_NO_WINDOW)); | ||
906 | return; | ||
907 | } | ||
908 | start = vc->vc_origin + (win_top * vc->vc_size_row); | ||
909 | end = vc->vc_origin + (win_bottom * vc->vc_size_row); | ||
910 | while (start <= end) { | ||
911 | from = start + (win_left * 2); | ||
912 | to = start + (win_right * 2); | ||
913 | say_from_to(vc, from, to, 1); | ||
914 | start += vc->vc_size_row; | ||
915 | } | ||
916 | } | ||
917 | |||
918 | static void top_edge(struct vc_data *vc) | ||
919 | { | ||
920 | spk_parked |= 0x01; | ||
921 | spk_pos = vc->vc_origin + 2 * spk_x; | ||
922 | spk_y = 0; | ||
923 | say_line(vc); | ||
924 | } | ||
925 | |||
926 | static void bottom_edge(struct vc_data *vc) | ||
927 | { | ||
928 | spk_parked |= 0x01; | ||
929 | spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row; | ||
930 | spk_y = vc->vc_rows - 1; | ||
931 | say_line(vc); | ||
932 | } | ||
933 | |||
934 | static void left_edge(struct vc_data *vc) | ||
935 | { | ||
936 | spk_parked |= 0x01; | ||
937 | spk_pos -= spk_x * 2; | ||
938 | spk_x = 0; | ||
939 | say_char(vc); | ||
940 | } | ||
941 | |||
942 | static void right_edge(struct vc_data *vc) | ||
943 | { | ||
944 | spk_parked |= 0x01; | ||
945 | spk_pos += (vc->vc_cols - spk_x - 1) * 2; | ||
946 | spk_x = vc->vc_cols - 1; | ||
947 | say_char(vc); | ||
948 | } | ||
949 | |||
950 | static void say_first_char(struct vc_data *vc) | ||
951 | { | ||
952 | int i, len = get_line(vc); | ||
953 | u_char ch; | ||
954 | spk_parked |= 0x01; | ||
955 | if (len == 0) { | ||
956 | synth_printf("%s\n", msg_get(MSG_BLANK)); | ||
957 | return; | ||
958 | } | ||
959 | for (i = 0; i < len; i++) | ||
960 | if (buf[i] != SPACE) | ||
961 | break; | ||
962 | ch = buf[i]; | ||
963 | spk_pos -= (spk_x - i) * 2; | ||
964 | spk_x = i; | ||
965 | synth_printf("%d, ", ++i); | ||
966 | speak_char(ch); | ||
967 | } | ||
968 | |||
969 | static void say_last_char(struct vc_data *vc) | ||
970 | { | ||
971 | int len = get_line(vc); | ||
972 | u_char ch; | ||
973 | spk_parked |= 0x01; | ||
974 | if (len == 0) { | ||
975 | synth_printf("%s\n", msg_get(MSG_BLANK)); | ||
976 | return; | ||
977 | } | ||
978 | ch = buf[--len]; | ||
979 | spk_pos -= (spk_x - len) * 2; | ||
980 | spk_x = len; | ||
981 | synth_printf("%d, ", ++len); | ||
982 | speak_char(ch); | ||
983 | } | ||
984 | |||
985 | static void say_position(struct vc_data *vc) | ||
986 | { | ||
987 | synth_printf(msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1, | ||
988 | vc->vc_num + 1); | ||
989 | synth_printf("\n"); | ||
990 | } | ||
991 | |||
992 | /* Added by brianb */ | ||
993 | static void say_char_num(struct vc_data *vc) | ||
994 | { | ||
995 | u_char tmp; | ||
996 | u_short ch = get_char(vc, (u_short *) spk_pos, &tmp); | ||
997 | ch &= 0xff; | ||
998 | synth_printf(msg_get(MSG_CHAR_INFO), ch, ch); | ||
999 | } | ||
1000 | |||
1001 | /* these are stub functions to keep keyboard.c happy. */ | ||
1002 | |||
1003 | static void say_from_top(struct vc_data *vc) | ||
1004 | { | ||
1005 | say_screen_from_to(vc, 0, spk_y); | ||
1006 | } | ||
1007 | |||
1008 | static void say_to_bottom(struct vc_data *vc) | ||
1009 | { | ||
1010 | say_screen_from_to(vc, spk_y, vc->vc_rows); | ||
1011 | } | ||
1012 | |||
1013 | static void say_from_left(struct vc_data *vc) | ||
1014 | { | ||
1015 | say_line_from_to(vc, 0, spk_x, 1); | ||
1016 | } | ||
1017 | |||
1018 | static void say_to_right(struct vc_data *vc) | ||
1019 | { | ||
1020 | say_line_from_to(vc, spk_x, vc->vc_cols, 1); | ||
1021 | } | ||
1022 | |||
1023 | /* end of stub functions. */ | ||
1024 | |||
1025 | static void spkup_write(const char *in_buf, int count) | ||
1026 | { | ||
1027 | static int rep_count = 0; | ||
1028 | static u_char ch = '\0', old_ch = '\0'; | ||
1029 | static u_short char_type = 0, last_type = 0; | ||
1030 | int in_count = count; | ||
1031 | spk_keydown = 0; | ||
1032 | while (count--) { | ||
1033 | if (cursor_track == read_all_mode) { | ||
1034 | /* Insert Sentence Index */ | ||
1035 | if ((in_buf == sentmarks[bn][currsentence]) && | ||
1036 | (currsentence <= numsentences[bn])) | ||
1037 | synth_insert_next_index(currsentence++); | ||
1038 | } | ||
1039 | ch = (u_char)*in_buf++; | ||
1040 | char_type = spk_chartab[ch]; | ||
1041 | if (ch == old_ch && !(char_type&B_NUM)) { | ||
1042 | if (++rep_count > 2) | ||
1043 | continue; | ||
1044 | } else { | ||
1045 | if ((last_type&CH_RPT) && rep_count > 2) { | ||
1046 | synth_printf(" "); | ||
1047 | synth_printf(msg_get(MSG_REPEAT_DESC), ++rep_count); | ||
1048 | synth_printf(" "); | ||
1049 | } | ||
1050 | rep_count = 0; | ||
1051 | } | ||
1052 | if (ch == spk_lastkey) { | ||
1053 | rep_count = 0; | ||
1054 | if (key_echo == 1 && ch >= MINECHOCHAR) | ||
1055 | speak_char(ch); | ||
1056 | } else if (char_type & B_ALPHA) { | ||
1057 | if ((synth_flags & SF_DEC) && (last_type & PUNC)) | ||
1058 | synth_buffer_add(SPACE); | ||
1059 | synth_printf("%c", ch); | ||
1060 | } else if (char_type & B_NUM) { | ||
1061 | rep_count = 0; | ||
1062 | synth_printf("%c", ch); | ||
1063 | } else if (char_type&punc_mask) { | ||
1064 | speak_char(ch); | ||
1065 | char_type &= ~PUNC; /* for dec nospell processing */ | ||
1066 | } else if (char_type&SYNTH_OK) { | ||
1067 | /* these are usually puncts like . and , which synth needs for expression. | ||
1068 | * suppress multiple to get rid of long pausesand clear repeat count so if | ||
1069 | *someone has repeats on you don't get nothing repeated count */ | ||
1070 | if (ch != old_ch) | ||
1071 | synth_printf("%c", ch); | ||
1072 | else | ||
1073 | rep_count = 0; | ||
1074 | } else { | ||
1075 | /* send space and record position, if next is num overwrite space */ | ||
1076 | if (old_ch != ch) | ||
1077 | synth_buffer_add(SPACE); | ||
1078 | else | ||
1079 | rep_count = 0; | ||
1080 | } | ||
1081 | old_ch = ch; | ||
1082 | last_type = char_type; | ||
1083 | } | ||
1084 | spk_lastkey = 0; | ||
1085 | if (in_count > 2 && rep_count > 2) { | ||
1086 | if (last_type&CH_RPT) { | ||
1087 | synth_printf(" "); | ||
1088 | synth_printf(msg_get(MSG_REPEAT_DESC2), ++rep_count); | ||
1089 | synth_printf(" "); | ||
1090 | } | ||
1091 | rep_count = 0; | ||
1092 | } | ||
1093 | } | ||
1094 | |||
1095 | static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1); | ||
1096 | |||
1097 | static void read_all_doc(struct vc_data *vc); | ||
1098 | static void cursor_done(u_long data); | ||
1099 | static DEFINE_TIMER(cursor_timer, cursor_done, 0, 0); | ||
1100 | |||
1101 | static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag) | ||
1102 | { | ||
1103 | unsigned long flags; | ||
1104 | if (synth == NULL || up_flag || spk_killed) | ||
1105 | return; | ||
1106 | spk_lock(flags); | ||
1107 | if (cursor_track == read_all_mode) { | ||
1108 | switch (value) { | ||
1109 | case KVAL(K_SHIFT): | ||
1110 | del_timer(&cursor_timer); | ||
1111 | spk_shut_up &= 0xfe; | ||
1112 | do_flush(); | ||
1113 | read_all_doc(vc); | ||
1114 | break; | ||
1115 | case KVAL(K_CTRL): | ||
1116 | del_timer(&cursor_timer); | ||
1117 | cursor_track = prev_cursor_track; | ||
1118 | spk_shut_up &= 0xfe; | ||
1119 | do_flush(); | ||
1120 | break; | ||
1121 | } | ||
1122 | } else { | ||
1123 | spk_shut_up &= 0xfe; | ||
1124 | do_flush(); | ||
1125 | } | ||
1126 | if (say_ctrl && value < NUM_CTL_LABELS) | ||
1127 | synth_printf("%s", msg_get(MSG_CTL_START + value)); | ||
1128 | spk_unlock(flags); | ||
1129 | } | ||
1130 | |||
1131 | static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag) | ||
1132 | { | ||
1133 | unsigned long flags; | ||
1134 | spk_lock(flags); | ||
1135 | if (up_flag) { | ||
1136 | spk_lastkey = spk_keydown = 0; | ||
1137 | spk_unlock(flags); | ||
1138 | return; | ||
1139 | } | ||
1140 | if (synth == NULL || spk_killed) { | ||
1141 | spk_unlock(flags); | ||
1142 | return; | ||
1143 | } | ||
1144 | spk_shut_up &= 0xfe; | ||
1145 | spk_lastkey = value; | ||
1146 | spk_keydown++; | ||
1147 | spk_parked &= 0xfe; | ||
1148 | if (key_echo == 2 && value >= MINECHOCHAR) | ||
1149 | speak_char(value); | ||
1150 | spk_unlock(flags); | ||
1151 | } | ||
1152 | |||
1153 | int set_key_info(const u_char *key_info, u_char *k_buffer) | ||
1154 | { | ||
1155 | int i = 0, states, key_data_len; | ||
1156 | const u_char *cp = key_info; | ||
1157 | u_char *cp1 = k_buffer; | ||
1158 | u_char ch, version, num_keys; | ||
1159 | version = *cp++; | ||
1160 | if (version != KEY_MAP_VER) | ||
1161 | return -1; | ||
1162 | num_keys = *cp; | ||
1163 | states = (int) cp[1]; | ||
1164 | key_data_len = (states + 1) * (num_keys + 1); | ||
1165 | if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(key_buf)) | ||
1166 | return -2; | ||
1167 | memset(k_buffer, 0, SHIFT_TBL_SIZE); | ||
1168 | memset(our_keys, 0, sizeof(our_keys)); | ||
1169 | shift_table = k_buffer; | ||
1170 | our_keys[0] = shift_table; | ||
1171 | cp1 += SHIFT_TBL_SIZE; | ||
1172 | memcpy(cp1, cp, key_data_len + 3); | ||
1173 | /* get num_keys, states and data*/ | ||
1174 | cp1 += 2; /* now pointing at shift states */ | ||
1175 | for (i = 1; i <= states; i++) { | ||
1176 | ch = *cp1++; | ||
1177 | if (ch >= SHIFT_TBL_SIZE) | ||
1178 | return -3; | ||
1179 | shift_table[ch] = i; | ||
1180 | } | ||
1181 | keymap_flags = *cp1++; | ||
1182 | while ((ch = *cp1)) { | ||
1183 | if (ch >= MAX_KEY) | ||
1184 | return -4; | ||
1185 | our_keys[ch] = cp1; | ||
1186 | cp1 += states + 1; | ||
1187 | } | ||
1188 | return 0; | ||
1189 | } | ||
1190 | |||
1191 | static struct var_t spk_vars[] = { | ||
1192 | /* bell must be first to set high limit */ | ||
1193 | { BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL }}, | ||
1194 | { SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL }}, | ||
1195 | { ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL }}, | ||
1196 | { BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL }}, | ||
1197 | { BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL }}, | ||
1198 | { PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL }}, | ||
1199 | { READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL }}, | ||
1200 | { CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL }}, | ||
1201 | { SAY_CONTROL, TOGGLE_0 }, | ||
1202 | { SAY_WORD_CTL, TOGGLE_0 }, | ||
1203 | { NO_INTERRUPT, TOGGLE_0 }, | ||
1204 | { KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL }}, | ||
1205 | V_LAST_VAR | ||
1206 | }; | ||
1207 | |||
1208 | |||
1209 | static void toggle_cursoring(struct vc_data *vc) | ||
1210 | { | ||
1211 | if (cursor_track == read_all_mode) | ||
1212 | cursor_track = prev_cursor_track; | ||
1213 | if (++cursor_track >= CT_Max) | ||
1214 | cursor_track = 0; | ||
1215 | synth_printf("%s\n", msg_get(MSG_CURSOR_MSGS_START + cursor_track)); | ||
1216 | } | ||
1217 | |||
1218 | void reset_default_chars(void) | ||
1219 | { | ||
1220 | int i; | ||
1221 | |||
1222 | /* First, free any non-default */ | ||
1223 | for (i = 0; i < 256; i++) { | ||
1224 | if ((characters[i] != NULL) | ||
1225 | && (characters[i] != default_chars[i])) | ||
1226 | kfree(characters[i]); | ||
1227 | } | ||
1228 | |||
1229 | memcpy(characters, default_chars, sizeof(default_chars)); | ||
1230 | } | ||
1231 | |||
1232 | void reset_default_chartab(void) | ||
1233 | { | ||
1234 | memcpy(spk_chartab, default_chartab, sizeof(default_chartab)); | ||
1235 | } | ||
1236 | |||
1237 | static const struct st_bits_data *pb_edit = NULL; | ||
1238 | |||
1239 | static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key) | ||
1240 | { | ||
1241 | short mask = pb_edit->mask, ch_type = spk_chartab[ch]; | ||
1242 | if (type != KT_LATIN || (ch_type&B_NUM) || ch < SPACE) | ||
1243 | return -1; | ||
1244 | if (ch == SPACE) { | ||
1245 | synth_printf("%s\n", msg_get(MSG_EDIT_DONE)); | ||
1246 | special_handler = NULL; | ||
1247 | return 1; | ||
1248 | } | ||
1249 | if (mask < PUNC && !(ch_type&PUNC)) | ||
1250 | return -1; | ||
1251 | spk_chartab[ch] ^= mask; | ||
1252 | speak_char(ch); | ||
1253 | synth_printf(" %s\n", | ||
1254 | (spk_chartab[ch]&mask) ? msg_get(MSG_ON) : msg_get(MSG_OFF)); | ||
1255 | return 1; | ||
1256 | } | ||
1257 | |||
1258 | /* Allocation concurrency is protected by the console semaphore */ | ||
1259 | void speakup_allocate(struct vc_data *vc) | ||
1260 | { | ||
1261 | int vc_num; | ||
1262 | |||
1263 | vc_num = vc->vc_num; | ||
1264 | if (speakup_console[vc_num] == NULL) { | ||
1265 | speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]), | ||
1266 | GFP_ATOMIC); | ||
1267 | if (speakup_console[vc_num] == NULL) | ||
1268 | return; | ||
1269 | speakup_date(vc); | ||
1270 | } else if (!spk_parked) | ||
1271 | speakup_date(vc); | ||
1272 | } | ||
1273 | |||
1274 | void speakup_deallocate(struct vc_data *vc) | ||
1275 | { | ||
1276 | int vc_num; | ||
1277 | |||
1278 | vc_num = vc->vc_num; | ||
1279 | if (speakup_console[vc_num] != NULL) { | ||
1280 | kfree(speakup_console[vc_num]); | ||
1281 | speakup_console[vc_num] = NULL; | ||
1282 | } | ||
1283 | } | ||
1284 | |||
1285 | static u_char is_cursor; | ||
1286 | static u_long old_cursor_pos, old_cursor_x, old_cursor_y; | ||
1287 | static int cursor_con; | ||
1288 | |||
1289 | static void reset_highlight_buffers(struct vc_data *); | ||
1290 | |||
1291 | static int read_all_key; | ||
1292 | |||
1293 | void reset_index_count(int); | ||
1294 | void get_index_count(int *, int *); | ||
1295 | /*int synth_supports_indexing(void); */ | ||
1296 | static void start_read_all_timer(struct vc_data *vc, int command); | ||
1297 | |||
1298 | enum { | ||
1299 | RA_NOTHING, | ||
1300 | RA_NEXT_SENT, | ||
1301 | RA_PREV_LINE, | ||
1302 | RA_NEXT_LINE, | ||
1303 | RA_PREV_SENT, | ||
1304 | RA_DOWN_ARROW, | ||
1305 | RA_TIMER, | ||
1306 | RA_FIND_NEXT_SENT, | ||
1307 | RA_FIND_PREV_SENT, | ||
1308 | }; | ||
1309 | |||
1310 | static void | ||
1311 | kbd_fakekey2(struct vc_data *vc, int command) | ||
1312 | { | ||
1313 | del_timer(&cursor_timer); | ||
1314 | speakup_fake_down_arrow(); | ||
1315 | start_read_all_timer(vc, command); | ||
1316 | } | ||
1317 | |||
1318 | static void | ||
1319 | read_all_doc(struct vc_data *vc) | ||
1320 | { | ||
1321 | if ((vc->vc_num != fg_console) || synth == NULL || spk_shut_up) | ||
1322 | return; | ||
1323 | if (!synth_supports_indexing()) | ||
1324 | return; | ||
1325 | if (cursor_track != read_all_mode) | ||
1326 | prev_cursor_track = cursor_track; | ||
1327 | cursor_track = read_all_mode; | ||
1328 | reset_index_count(0); | ||
1329 | if (get_sentence_buf(vc, 0) == -1) | ||
1330 | kbd_fakekey2(vc, RA_DOWN_ARROW); | ||
1331 | else { | ||
1332 | say_sentence_num(0, 0); | ||
1333 | synth_insert_next_index(0); | ||
1334 | start_read_all_timer(vc, RA_TIMER); | ||
1335 | } | ||
1336 | } | ||
1337 | |||
1338 | static void | ||
1339 | stop_read_all(struct vc_data *vc) | ||
1340 | { | ||
1341 | del_timer(&cursor_timer); | ||
1342 | cursor_track = prev_cursor_track; | ||
1343 | spk_shut_up &= 0xfe; | ||
1344 | do_flush(); | ||
1345 | } | ||
1346 | |||
1347 | static void | ||
1348 | start_read_all_timer(struct vc_data *vc, int command) | ||
1349 | { | ||
1350 | struct var_t *cursor_timeout; | ||
1351 | |||
1352 | cursor_con = vc->vc_num; | ||
1353 | read_all_key = command; | ||
1354 | cursor_timeout = get_var(CURSOR_TIME); | ||
1355 | mod_timer(&cursor_timer, jiffies + msecs_to_jiffies(cursor_timeout->u.n.value)); | ||
1356 | } | ||
1357 | |||
1358 | static void | ||
1359 | handle_cursor_read_all(struct vc_data *vc, int command) | ||
1360 | { | ||
1361 | int indcount, sentcount, rv, sn; | ||
1362 | |||
1363 | switch (command) { | ||
1364 | case RA_NEXT_SENT: | ||
1365 | /* Get Current Sentence */ | ||
1366 | get_index_count(&indcount, &sentcount); | ||
1367 | /*printk("%d %d ", indcount, sentcount); */ | ||
1368 | reset_index_count(sentcount+1); | ||
1369 | if (indcount == 1) { | ||
1370 | if (!say_sentence_num(sentcount+1, 0)) { | ||
1371 | kbd_fakekey2(vc, RA_FIND_NEXT_SENT); | ||
1372 | return; | ||
1373 | } | ||
1374 | synth_insert_next_index(0); | ||
1375 | } else { | ||
1376 | sn = 0; | ||
1377 | if (!say_sentence_num(sentcount+1, 1)) { | ||
1378 | sn = 1; | ||
1379 | reset_index_count(sn); | ||
1380 | } else | ||
1381 | synth_insert_next_index(0); | ||
1382 | if (!say_sentence_num(sn, 0)) { | ||
1383 | kbd_fakekey2(vc, RA_FIND_NEXT_SENT); | ||
1384 | return; | ||
1385 | } | ||
1386 | synth_insert_next_index(0); | ||
1387 | } | ||
1388 | start_read_all_timer(vc, RA_TIMER); | ||
1389 | break; | ||
1390 | case RA_PREV_SENT: | ||
1391 | break; | ||
1392 | case RA_NEXT_LINE: | ||
1393 | read_all_doc(vc); | ||
1394 | break; | ||
1395 | case RA_PREV_LINE: | ||
1396 | break; | ||
1397 | case RA_DOWN_ARROW: | ||
1398 | if (get_sentence_buf(vc, 0) == -1) { | ||
1399 | kbd_fakekey2(vc, RA_DOWN_ARROW); | ||
1400 | } else { | ||
1401 | say_sentence_num(0, 0); | ||
1402 | synth_insert_next_index(0); | ||
1403 | start_read_all_timer(vc, RA_TIMER); | ||
1404 | } | ||
1405 | break; | ||
1406 | case RA_FIND_NEXT_SENT: | ||
1407 | rv = get_sentence_buf(vc, 0); | ||
1408 | if (rv == -1) | ||
1409 | read_all_doc(vc); | ||
1410 | if (rv == 0) | ||
1411 | kbd_fakekey2(vc, RA_FIND_NEXT_SENT); | ||
1412 | else { | ||
1413 | say_sentence_num(1, 0); | ||
1414 | synth_insert_next_index(0); | ||
1415 | start_read_all_timer(vc, RA_TIMER); | ||
1416 | } | ||
1417 | break; | ||
1418 | case RA_FIND_PREV_SENT: | ||
1419 | break; | ||
1420 | case RA_TIMER: | ||
1421 | get_index_count(&indcount, &sentcount); | ||
1422 | if (indcount < 2) | ||
1423 | kbd_fakekey2(vc, RA_DOWN_ARROW); | ||
1424 | else | ||
1425 | start_read_all_timer(vc, RA_TIMER); | ||
1426 | break; | ||
1427 | } | ||
1428 | } | ||
1429 | |||
1430 | static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag) | ||
1431 | { | ||
1432 | unsigned long flags; | ||
1433 | spk_lock(flags); | ||
1434 | if (cursor_track == read_all_mode) { | ||
1435 | spk_parked &= 0xfe; | ||
1436 | if (synth == NULL || up_flag || spk_shut_up) { | ||
1437 | spk_unlock(flags); | ||
1438 | return NOTIFY_STOP; | ||
1439 | } | ||
1440 | del_timer(&cursor_timer); | ||
1441 | spk_shut_up &= 0xfe; | ||
1442 | do_flush(); | ||
1443 | start_read_all_timer(vc, value+1); | ||
1444 | spk_unlock(flags); | ||
1445 | return NOTIFY_STOP; | ||
1446 | } | ||
1447 | spk_unlock(flags); | ||
1448 | return NOTIFY_OK; | ||
1449 | } | ||
1450 | |||
1451 | static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag) | ||
1452 | { | ||
1453 | unsigned long flags; | ||
1454 | struct var_t *cursor_timeout; | ||
1455 | |||
1456 | spk_lock(flags); | ||
1457 | spk_parked &= 0xfe; | ||
1458 | if (synth == NULL || up_flag || spk_shut_up || cursor_track == CT_Off) { | ||
1459 | spk_unlock(flags); | ||
1460 | return; | ||
1461 | } | ||
1462 | spk_shut_up &= 0xfe; | ||
1463 | if (no_intr) | ||
1464 | do_flush(); | ||
1465 | /* the key press flushes if !no_inter but we want to flush on cursor | ||
1466 | * moves regardless of no_inter state */ | ||
1467 | is_cursor = value + 1; | ||
1468 | old_cursor_pos = vc->vc_pos; | ||
1469 | old_cursor_x = vc->vc_x; | ||
1470 | old_cursor_y = vc->vc_y; | ||
1471 | speakup_console[vc->vc_num]->ht.cy = vc->vc_y; | ||
1472 | cursor_con = vc->vc_num; | ||
1473 | if (cursor_track == CT_Highlight) | ||
1474 | reset_highlight_buffers(vc); | ||
1475 | cursor_timeout = get_var(CURSOR_TIME); | ||
1476 | mod_timer(&cursor_timer, jiffies + msecs_to_jiffies(cursor_timeout->u.n.value)); | ||
1477 | spk_unlock(flags); | ||
1478 | } | ||
1479 | |||
1480 | static void | ||
1481 | update_color_buffer(struct vc_data *vc , const char *ic , int len) | ||
1482 | { | ||
1483 | int i, bi, hi; | ||
1484 | int vc_num = vc->vc_num; | ||
1485 | |||
1486 | bi = ((vc->vc_attr & 0x70) >> 4) ; | ||
1487 | hi = speakup_console[vc_num]->ht.highsize[bi]; | ||
1488 | |||
1489 | i = 0; | ||
1490 | if (speakup_console[vc_num]->ht.highsize[bi] == 0) { | ||
1491 | speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos; | ||
1492 | speakup_console[vc_num]->ht.rx[bi] = vc->vc_x; | ||
1493 | speakup_console[vc_num]->ht.ry[bi] = vc->vc_y; | ||
1494 | } | ||
1495 | while ((hi < COLOR_BUFFER_SIZE) && (i < len)) { | ||
1496 | if ((ic[i] > 32) && (ic[i] < 127)) { | ||
1497 | speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i]; | ||
1498 | hi++; | ||
1499 | } else if ((ic[i] == 32) && (hi != 0)) { | ||
1500 | if (speakup_console[vc_num]->ht.highbuf[bi][hi-1] != | ||
1501 | 32) { | ||
1502 | speakup_console[vc_num]->ht.highbuf[bi][hi] = | ||
1503 | ic[i]; | ||
1504 | hi++; | ||
1505 | } | ||
1506 | } | ||
1507 | i++; | ||
1508 | } | ||
1509 | speakup_console[vc_num]->ht.highsize[bi] = hi; | ||
1510 | } | ||
1511 | |||
1512 | static void | ||
1513 | reset_highlight_buffers(struct vc_data *vc) | ||
1514 | { | ||
1515 | int i; | ||
1516 | int vc_num = vc->vc_num; | ||
1517 | for (i = 0 ; i < 8 ; i++) | ||
1518 | speakup_console[vc_num]->ht.highsize[i] = 0; | ||
1519 | } | ||
1520 | |||
1521 | static int | ||
1522 | count_highlight_color(struct vc_data *vc) | ||
1523 | { | ||
1524 | int i, bg; | ||
1525 | int cc; | ||
1526 | int vc_num = vc->vc_num; | ||
1527 | u16 ch; | ||
1528 | u16 *start = (u16 *) vc->vc_origin; | ||
1529 | |||
1530 | for (i = 0; i < 8; i++) | ||
1531 | speakup_console[vc_num]->ht.bgcount[i] = 0; | ||
1532 | |||
1533 | for (i = 0; i < vc->vc_rows; i++) { | ||
1534 | u16 *end = start + vc->vc_cols*2; | ||
1535 | u16 *ptr; | ||
1536 | for (ptr = start; ptr < end; ptr++) { | ||
1537 | ch = get_attributes(ptr); | ||
1538 | bg = (ch & 0x70) >> 4; | ||
1539 | speakup_console[vc_num]->ht.bgcount[bg]++; | ||
1540 | } | ||
1541 | start += vc->vc_size_row; | ||
1542 | } | ||
1543 | |||
1544 | cc = 0; | ||
1545 | for (i = 0; i < 8; i++) | ||
1546 | if (speakup_console[vc_num]->ht.bgcount[i] > 0) | ||
1547 | cc++; | ||
1548 | return cc; | ||
1549 | } | ||
1550 | |||
1551 | static int | ||
1552 | get_highlight_color(struct vc_data *vc) | ||
1553 | { | ||
1554 | int i, j; | ||
1555 | unsigned int cptr[8], tmp; | ||
1556 | int vc_num = vc->vc_num; | ||
1557 | |||
1558 | for (i = 0; i < 8; i++) | ||
1559 | cptr[i] = i; | ||
1560 | |||
1561 | for (i = 0; i < 7; i++) | ||
1562 | for (j = i + 1; j < 8; j++) | ||
1563 | if (speakup_console[vc_num]->ht.bgcount[cptr[i]] > | ||
1564 | speakup_console[vc_num]->ht.bgcount[cptr[j]]) { | ||
1565 | tmp = cptr[i]; | ||
1566 | cptr[i] = cptr[j]; | ||
1567 | cptr[j] = tmp; | ||
1568 | } | ||
1569 | |||
1570 | for (i = 0; i < 8; i++) | ||
1571 | if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0) | ||
1572 | if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0) | ||
1573 | return cptr[i]; | ||
1574 | return -1; | ||
1575 | } | ||
1576 | |||
1577 | static int | ||
1578 | speak_highlight(struct vc_data *vc) | ||
1579 | { | ||
1580 | int hc, d; | ||
1581 | int vc_num = vc->vc_num; | ||
1582 | if (count_highlight_color(vc) == 1) | ||
1583 | return 0; | ||
1584 | hc = get_highlight_color(vc); | ||
1585 | if (hc != -1) { | ||
1586 | d = vc->vc_y-speakup_console[vc_num]->ht.cy; | ||
1587 | if ((d == 1) || (d == -1)) | ||
1588 | if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y) | ||
1589 | return 0; | ||
1590 | spk_parked |= 0x01; | ||
1591 | do_flush(); | ||
1592 | spkup_write(speakup_console[vc_num]->ht.highbuf[hc], | ||
1593 | speakup_console[vc_num]->ht.highsize[hc]); | ||
1594 | spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc]; | ||
1595 | spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc]; | ||
1596 | spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc]; | ||
1597 | return 1; | ||
1598 | } | ||
1599 | return 0; | ||
1600 | } | ||
1601 | |||
1602 | static void | ||
1603 | cursor_done(u_long data) | ||
1604 | { | ||
1605 | struct vc_data *vc = vc_cons[cursor_con].d; | ||
1606 | unsigned long flags; | ||
1607 | del_timer(&cursor_timer); | ||
1608 | spk_lock(flags); | ||
1609 | if (cursor_con != fg_console) { | ||
1610 | is_cursor = 0; | ||
1611 | goto out; | ||
1612 | } | ||
1613 | speakup_date(vc); | ||
1614 | if (win_enabled) { | ||
1615 | if (vc->vc_x >= win_left && vc->vc_x <= win_right && | ||
1616 | vc->vc_y >= win_top && vc->vc_y <= win_bottom) { | ||
1617 | spk_keydown = is_cursor = 0; | ||
1618 | goto out; | ||
1619 | } | ||
1620 | } | ||
1621 | if (cursor_track == read_all_mode) { | ||
1622 | handle_cursor_read_all(vc, read_all_key); | ||
1623 | goto out; | ||
1624 | } | ||
1625 | if (cursor_track == CT_Highlight) { | ||
1626 | if (speak_highlight(vc)) { | ||
1627 | spk_keydown = is_cursor = 0; | ||
1628 | goto out; | ||
1629 | } | ||
1630 | } | ||
1631 | if (cursor_track == CT_Window) | ||
1632 | speakup_win_say(vc); | ||
1633 | else if (is_cursor == 1 || is_cursor == 4) | ||
1634 | say_line_from_to(vc, 0, vc->vc_cols, 0); | ||
1635 | else | ||
1636 | say_char(vc); | ||
1637 | spk_keydown = is_cursor = 0; | ||
1638 | out: | ||
1639 | spk_unlock(flags); | ||
1640 | } | ||
1641 | |||
1642 | /* called by: vt_notifier_call() */ | ||
1643 | static void speakup_bs(struct vc_data *vc) | ||
1644 | { | ||
1645 | unsigned long flags; | ||
1646 | if (!speakup_console[vc->vc_num]) | ||
1647 | return; | ||
1648 | if (!spk_trylock(flags)) | ||
1649 | /* Speakup output, discard */ | ||
1650 | return; | ||
1651 | if (!spk_parked) | ||
1652 | speakup_date(vc); | ||
1653 | if (spk_shut_up || synth == NULL) { | ||
1654 | spk_unlock(flags); | ||
1655 | return; | ||
1656 | } | ||
1657 | if (vc->vc_num == fg_console && spk_keydown) { | ||
1658 | spk_keydown = 0; | ||
1659 | if (!is_cursor) | ||
1660 | say_char(vc); | ||
1661 | } | ||
1662 | spk_unlock(flags); | ||
1663 | } | ||
1664 | |||
1665 | /* called by: vt_notifier_call() */ | ||
1666 | static void speakup_con_write(struct vc_data *vc, const char *str, int len) | ||
1667 | { | ||
1668 | unsigned long flags; | ||
1669 | if ((vc->vc_num != fg_console) || spk_shut_up || synth == NULL) | ||
1670 | return; | ||
1671 | if (!spk_trylock(flags)) | ||
1672 | /* Speakup output, discard */ | ||
1673 | return; | ||
1674 | if (bell_pos && spk_keydown && (vc->vc_x == bell_pos - 1)) | ||
1675 | bleep(3); | ||
1676 | if ((is_cursor) || (cursor_track == read_all_mode)) { | ||
1677 | if (cursor_track == CT_Highlight) | ||
1678 | update_color_buffer(vc, str, len); | ||
1679 | spk_unlock(flags); | ||
1680 | return; | ||
1681 | } | ||
1682 | if (win_enabled) { | ||
1683 | if (vc->vc_x >= win_left && vc->vc_x <= win_right && | ||
1684 | vc->vc_y >= win_top && vc->vc_y <= win_bottom) { | ||
1685 | spk_unlock(flags); | ||
1686 | return; | ||
1687 | } | ||
1688 | } | ||
1689 | |||
1690 | spkup_write(str, len); | ||
1691 | spk_unlock(flags); | ||
1692 | } | ||
1693 | |||
1694 | void | ||
1695 | speakup_con_update(struct vc_data *vc) | ||
1696 | { | ||
1697 | unsigned long flags; | ||
1698 | if (speakup_console[vc->vc_num] == NULL || spk_parked) | ||
1699 | return; | ||
1700 | if (!spk_trylock(flags)) | ||
1701 | /* Speakup output, discard */ | ||
1702 | return; | ||
1703 | speakup_date(vc); | ||
1704 | spk_unlock(flags); | ||
1705 | } | ||
1706 | |||
1707 | static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag) | ||
1708 | { | ||
1709 | unsigned long flags; | ||
1710 | int on_off = 2; | ||
1711 | char *label; | ||
1712 | if (synth == NULL || up_flag || spk_killed) | ||
1713 | return; | ||
1714 | spk_lock(flags); | ||
1715 | spk_shut_up &= 0xfe; | ||
1716 | if (no_intr) | ||
1717 | do_flush(); | ||
1718 | switch (value) { | ||
1719 | case KVAL(K_CAPS): | ||
1720 | label = msg_get(MSG_KEYNAME_CAPSLOCK); | ||
1721 | on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_CAPSLOCK)); | ||
1722 | break; | ||
1723 | case KVAL(K_NUM): | ||
1724 | label = msg_get(MSG_KEYNAME_NUMLOCK); | ||
1725 | on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_NUMLOCK)); | ||
1726 | break; | ||
1727 | case KVAL(K_HOLD): | ||
1728 | label = msg_get(MSG_KEYNAME_SCROLLLOCK); | ||
1729 | on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_SCROLLOCK)); | ||
1730 | if (speakup_console[vc->vc_num]) | ||
1731 | speakup_console[vc->vc_num]->tty_stopped = on_off; | ||
1732 | break; | ||
1733 | default: | ||
1734 | spk_parked &= 0xfe; | ||
1735 | spk_unlock(flags); | ||
1736 | return; | ||
1737 | } | ||
1738 | if (on_off < 2) | ||
1739 | synth_printf("%s %s\n", | ||
1740 | label, msg_get(MSG_STATUS_START + on_off)); | ||
1741 | spk_unlock(flags); | ||
1742 | } | ||
1743 | |||
1744 | static int | ||
1745 | inc_dec_var(u_char value) | ||
1746 | { | ||
1747 | struct st_var_header *p_header; | ||
1748 | struct var_t *var_data; | ||
1749 | char num_buf[32]; | ||
1750 | char *cp = num_buf; | ||
1751 | char *pn; | ||
1752 | int var_id = (int)value - VAR_START; | ||
1753 | int how = (var_id&1) ? E_INC : E_DEC; | ||
1754 | var_id = var_id/2+FIRST_SET_VAR; | ||
1755 | p_header = get_var_header(var_id); | ||
1756 | if (p_header == NULL) | ||
1757 | return -1; | ||
1758 | if (p_header->var_type != VAR_NUM) | ||
1759 | return -1; | ||
1760 | var_data = p_header->data; | ||
1761 | if (set_num_var(1, p_header, how) != 0) | ||
1762 | return -1; | ||
1763 | if (!spk_close_press) { | ||
1764 | for (pn = p_header->name; *pn; pn++) { | ||
1765 | if (*pn == '_') | ||
1766 | *cp = SPACE; | ||
1767 | else | ||
1768 | *cp++ = *pn; | ||
1769 | } | ||
1770 | } | ||
1771 | snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ", | ||
1772 | var_data->u.n.value); | ||
1773 | synth_printf("%s", num_buf); | ||
1774 | return 0; | ||
1775 | } | ||
1776 | |||
1777 | static void | ||
1778 | speakup_win_set(struct vc_data *vc) | ||
1779 | { | ||
1780 | char info[40]; | ||
1781 | if (win_start > 1) { | ||
1782 | synth_printf("%s\n", msg_get(MSG_WINDOW_ALREADY_SET)); | ||
1783 | return; | ||
1784 | } | ||
1785 | if (spk_x < win_left || spk_y < win_top) { | ||
1786 | synth_printf("%s\n", msg_get(MSG_END_BEFORE_START)); | ||
1787 | return; | ||
1788 | } | ||
1789 | if (win_start && spk_x == win_left && spk_y == win_top) { | ||
1790 | win_left = 0; | ||
1791 | win_right = vc->vc_cols-1; | ||
1792 | win_bottom = spk_y; | ||
1793 | snprintf(info, sizeof(info), msg_get(MSG_WINDOW_LINE), | ||
1794 | (int)win_top+1); | ||
1795 | } else { | ||
1796 | if (!win_start) { | ||
1797 | win_top = spk_y; | ||
1798 | win_left = spk_x; | ||
1799 | } else { | ||
1800 | win_bottom = spk_y; | ||
1801 | win_right = spk_x; | ||
1802 | } | ||
1803 | snprintf(info, sizeof(info), msg_get(MSG_WINDOW_BOUNDARY), | ||
1804 | (win_start) ? msg_get(MSG_END) : msg_get(MSG_START), | ||
1805 | (int)spk_y+1, (int)spk_x+1); | ||
1806 | } | ||
1807 | synth_printf("%s\n", info); | ||
1808 | win_start++; | ||
1809 | } | ||
1810 | |||
1811 | static void | ||
1812 | speakup_win_clear(struct vc_data *vc) | ||
1813 | { | ||
1814 | win_top = win_bottom = 0; | ||
1815 | win_left = win_right = 0; | ||
1816 | win_start = 0; | ||
1817 | synth_printf("%s\n", msg_get(MSG_WINDOW_CLEARED)); | ||
1818 | } | ||
1819 | |||
1820 | static void | ||
1821 | speakup_win_enable(struct vc_data *vc) | ||
1822 | { | ||
1823 | if (win_start < 2) { | ||
1824 | synth_printf("%s\n", msg_get(MSG_NO_WINDOW)); | ||
1825 | return; | ||
1826 | } | ||
1827 | win_enabled ^= 1; | ||
1828 | if (win_enabled) | ||
1829 | synth_printf("%s\n", msg_get(MSG_WINDOW_SILENCED)); | ||
1830 | else | ||
1831 | synth_printf("%s\n", msg_get(MSG_WINDOW_SILENCE_DISABLED)); | ||
1832 | } | ||
1833 | |||
1834 | static void | ||
1835 | speakup_bits(struct vc_data *vc) | ||
1836 | { | ||
1837 | int val = this_speakup_key - (FIRST_EDIT_BITS - 1); | ||
1838 | if (special_handler != NULL || val < 1 || val > 6) { | ||
1839 | synth_printf("%s\n", msg_get(MSG_ERROR)); | ||
1840 | return; | ||
1841 | } | ||
1842 | pb_edit = &punc_info[val]; | ||
1843 | synth_printf(msg_get(MSG_EDIT_PROMPT), pb_edit->name); | ||
1844 | special_handler = edit_bits; | ||
1845 | } | ||
1846 | |||
1847 | static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key) | ||
1848 | { | ||
1849 | static u_char *goto_buf = "\0\0\0\0\0\0"; | ||
1850 | static int num = 0; | ||
1851 | int maxlen, go_pos; | ||
1852 | char *cp; | ||
1853 | if (type == KT_SPKUP && ch == SPEAKUP_GOTO) | ||
1854 | goto do_goto; | ||
1855 | if (type == KT_LATIN && ch == '\n') | ||
1856 | goto do_goto; | ||
1857 | if (type != 0) | ||
1858 | goto oops; | ||
1859 | if (ch == 8) { | ||
1860 | if (num == 0) | ||
1861 | return -1; | ||
1862 | ch = goto_buf[--num]; | ||
1863 | goto_buf[num] = '\0'; | ||
1864 | spkup_write(&ch, 1); | ||
1865 | return 1; | ||
1866 | } | ||
1867 | if (ch < '+' || ch > 'y') | ||
1868 | goto oops; | ||
1869 | goto_buf[num++] = ch; | ||
1870 | goto_buf[num] = '\0'; | ||
1871 | spkup_write(&ch, 1); | ||
1872 | maxlen = (*goto_buf >= '0') ? 3 : 4; | ||
1873 | if ((ch == '+' || ch == '-') && num == 1) | ||
1874 | return 1; | ||
1875 | if (ch >= '0' && ch <= '9' && num < maxlen) | ||
1876 | return 1; | ||
1877 | if (num < maxlen-1 || num > maxlen) | ||
1878 | goto oops; | ||
1879 | if (ch < 'x' || ch > 'y') { | ||
1880 | oops: | ||
1881 | if (!spk_killed) | ||
1882 | synth_printf(" %s\n", msg_get(MSG_GOTO_CANCELED)); | ||
1883 | goto_buf[num = 0] = '\0'; | ||
1884 | special_handler = NULL; | ||
1885 | return 1; | ||
1886 | } | ||
1887 | cp = speakup_s2i(goto_buf, &go_pos); | ||
1888 | goto_pos = (u_long)go_pos; | ||
1889 | if (*cp == 'x') { | ||
1890 | if (*goto_buf < '0') | ||
1891 | goto_pos += spk_x; | ||
1892 | else | ||
1893 | goto_pos--; | ||
1894 | if (goto_pos < 0) | ||
1895 | goto_pos = 0; | ||
1896 | if (goto_pos >= vc->vc_cols) | ||
1897 | goto_pos = vc->vc_cols-1; | ||
1898 | goto_x = 1; | ||
1899 | } else { | ||
1900 | if (*goto_buf < '0') | ||
1901 | goto_pos += spk_y; | ||
1902 | else | ||
1903 | goto_pos--; | ||
1904 | if (goto_pos < 0) | ||
1905 | goto_pos = 0; | ||
1906 | if (goto_pos >= vc->vc_rows) | ||
1907 | goto_pos = vc->vc_rows-1; | ||
1908 | goto_x = 0; | ||
1909 | } | ||
1910 | goto_buf[num = 0] = '\0'; | ||
1911 | do_goto: | ||
1912 | special_handler = NULL; | ||
1913 | spk_parked |= 0x01; | ||
1914 | if (goto_x) { | ||
1915 | spk_pos -= spk_x * 2; | ||
1916 | spk_x = goto_pos; | ||
1917 | spk_pos += goto_pos * 2; | ||
1918 | say_word(vc); | ||
1919 | } else { | ||
1920 | spk_y = goto_pos; | ||
1921 | spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row); | ||
1922 | say_line(vc); | ||
1923 | } | ||
1924 | return 1; | ||
1925 | } | ||
1926 | |||
1927 | static void | ||
1928 | speakup_goto(struct vc_data *vc) | ||
1929 | { | ||
1930 | if (special_handler != NULL) { | ||
1931 | synth_printf("%s\n", msg_get(MSG_ERROR)); | ||
1932 | return; | ||
1933 | } | ||
1934 | synth_printf("%s\n", msg_get(MSG_GOTO)); | ||
1935 | special_handler = handle_goto; | ||
1936 | return; | ||
1937 | } | ||
1938 | |||
1939 | static void speakup_help(struct vc_data *vc) | ||
1940 | { | ||
1941 | handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0); | ||
1942 | } | ||
1943 | |||
1944 | static void | ||
1945 | do_nothing(struct vc_data *vc) | ||
1946 | { | ||
1947 | return; /* flush done in do_spkup */ | ||
1948 | } | ||
1949 | static u_char key_speakup, spk_key_locked; | ||
1950 | |||
1951 | static void | ||
1952 | speakup_lock(struct vc_data *vc) | ||
1953 | { | ||
1954 | if (!spk_key_locked) | ||
1955 | spk_key_locked = key_speakup = 16; | ||
1956 | else | ||
1957 | spk_key_locked = key_speakup = 0; | ||
1958 | } | ||
1959 | |||
1960 | typedef void(*spkup_hand)(struct vc_data *); | ||
1961 | spkup_hand spkup_handler[] = { | ||
1962 | /* must be ordered same as defines in speakup.h */ | ||
1963 | do_nothing, speakup_goto, speech_kill, speakup_shut_up, | ||
1964 | speakup_cut, speakup_paste, say_first_char, say_last_char, | ||
1965 | say_char, say_prev_char, say_next_char, | ||
1966 | say_word, say_prev_word, say_next_word, | ||
1967 | say_line, say_prev_line, say_next_line, | ||
1968 | top_edge, bottom_edge, left_edge, right_edge, | ||
1969 | spell_word, spell_word, say_screen, | ||
1970 | say_position, say_attributes, | ||
1971 | speakup_off, speakup_parked, say_line, /* this is for indent */ | ||
1972 | say_from_top, say_to_bottom, | ||
1973 | say_from_left, say_to_right, | ||
1974 | say_char_num, speakup_bits, speakup_bits, say_phonetic_char, | ||
1975 | speakup_bits, speakup_bits, speakup_bits, | ||
1976 | speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say, | ||
1977 | speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL | ||
1978 | }; | ||
1979 | |||
1980 | static void do_spkup(struct vc_data *vc, u_char value) | ||
1981 | { | ||
1982 | if (spk_killed && value != SPEECH_KILL) | ||
1983 | return; | ||
1984 | spk_keydown = 0; | ||
1985 | spk_lastkey = 0; | ||
1986 | spk_shut_up &= 0xfe; | ||
1987 | this_speakup_key = value; | ||
1988 | if (value < SPKUP_MAX_FUNC && spkup_handler[value]) { | ||
1989 | do_flush(); | ||
1990 | (*spkup_handler[value])(vc); | ||
1991 | } else { | ||
1992 | if (inc_dec_var(value) < 0) | ||
1993 | bleep(9); | ||
1994 | } | ||
1995 | } | ||
1996 | |||
1997 | static const char *pad_chars = "0123456789+-*/\015,.?()"; | ||
1998 | |||
1999 | int | ||
2000 | speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym, | ||
2001 | int up_flag) | ||
2002 | { | ||
2003 | unsigned long flags; | ||
2004 | int kh; | ||
2005 | u_char *key_info; | ||
2006 | u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0; | ||
2007 | u_char shift_info, offset; | ||
2008 | int ret = 0; | ||
2009 | if (synth == NULL) | ||
2010 | return 0; | ||
2011 | |||
2012 | spk_lock(flags); | ||
2013 | tty = vc->vc_tty; | ||
2014 | if (type >= 0xf0) | ||
2015 | type -= 0xf0; | ||
2016 | if (type == KT_PAD && | ||
2017 | (vc_kbd_led(kbd_table + fg_console, VC_NUMLOCK))) { | ||
2018 | if (up_flag) { | ||
2019 | spk_keydown = 0; | ||
2020 | goto out; | ||
2021 | } | ||
2022 | value = spk_lastkey = pad_chars[value]; | ||
2023 | spk_keydown++; | ||
2024 | spk_parked &= 0xfe; | ||
2025 | goto no_map; | ||
2026 | } | ||
2027 | if (keycode >= MAX_KEY) | ||
2028 | goto no_map; | ||
2029 | key_info = our_keys[keycode]; | ||
2030 | if (key_info == 0) | ||
2031 | goto no_map; | ||
2032 | /* Check valid read all mode keys */ | ||
2033 | if ((cursor_track == read_all_mode) && (!up_flag)) { | ||
2034 | switch (value) { | ||
2035 | case KVAL(K_DOWN): | ||
2036 | case KVAL(K_UP): | ||
2037 | case KVAL(K_LEFT): | ||
2038 | case KVAL(K_RIGHT): | ||
2039 | case KVAL(K_PGUP): | ||
2040 | case KVAL(K_PGDN): | ||
2041 | break; | ||
2042 | default: | ||
2043 | stop_read_all(vc); | ||
2044 | break; | ||
2045 | } | ||
2046 | } | ||
2047 | shift_info = (shift_state&0x0f) + key_speakup; | ||
2048 | offset = shift_table[shift_info]; | ||
2049 | if (offset) { | ||
2050 | new_key = key_info[offset]; | ||
2051 | if (new_key) { | ||
2052 | ret = 1; | ||
2053 | if (new_key == SPK_KEY) { | ||
2054 | if (!spk_key_locked) | ||
2055 | key_speakup = (up_flag) ? 0 : 16; | ||
2056 | if (up_flag || spk_killed) | ||
2057 | goto out; | ||
2058 | spk_shut_up &= 0xfe; | ||
2059 | do_flush(); | ||
2060 | goto out; | ||
2061 | } | ||
2062 | if (up_flag) | ||
2063 | goto out; | ||
2064 | if (last_keycode == keycode && | ||
2065 | last_spk_jiffy+MAX_DELAY > jiffies) { | ||
2066 | spk_close_press = 1; | ||
2067 | offset = shift_table[shift_info+32]; | ||
2068 | /* double press? */ | ||
2069 | if (offset && key_info[offset]) | ||
2070 | new_key = key_info[offset]; | ||
2071 | } | ||
2072 | last_keycode = keycode; | ||
2073 | last_spk_jiffy = jiffies; | ||
2074 | type = KT_SPKUP; | ||
2075 | value = new_key; | ||
2076 | } | ||
2077 | } | ||
2078 | no_map: | ||
2079 | if (type == KT_SPKUP && special_handler == NULL) { | ||
2080 | do_spkup(vc, new_key); | ||
2081 | spk_close_press = 0; | ||
2082 | ret = 1; | ||
2083 | goto out; | ||
2084 | } | ||
2085 | if (up_flag || spk_killed || type == KT_SHIFT) | ||
2086 | goto out; | ||
2087 | spk_shut_up &= 0xfe; | ||
2088 | kh = (value == KVAL(K_DOWN)) | ||
2089 | || (value == KVAL(K_UP)) | ||
2090 | || (value == KVAL(K_LEFT)) | ||
2091 | || (value == KVAL(K_RIGHT)); | ||
2092 | if ((cursor_track != read_all_mode) || !kh) | ||
2093 | if (!no_intr) | ||
2094 | do_flush(); | ||
2095 | if (special_handler) { | ||
2096 | if (type == KT_SPEC && value == 1) { | ||
2097 | value = '\n'; | ||
2098 | type = KT_LATIN; | ||
2099 | } else if (type == KT_LETTER) | ||
2100 | type = KT_LATIN; | ||
2101 | else if (value == 0x7f) | ||
2102 | value = 8; /* make del = backspace */ | ||
2103 | ret = (*special_handler)(vc, type, value, keycode); | ||
2104 | spk_close_press = 0; | ||
2105 | if (ret < 0) | ||
2106 | bleep(9); | ||
2107 | goto out; | ||
2108 | } | ||
2109 | last_keycode = 0; | ||
2110 | out: | ||
2111 | spk_unlock(flags); | ||
2112 | return ret; | ||
2113 | } | ||
2114 | |||
2115 | static int keyboard_notifier_call(struct notifier_block *nb, | ||
2116 | unsigned long code, void *_param) | ||
2117 | { | ||
2118 | struct keyboard_notifier_param *param = _param; | ||
2119 | struct vc_data *vc = param->vc; | ||
2120 | int up = !param->down; | ||
2121 | int ret = NOTIFY_OK; | ||
2122 | static int keycode; /* to hold the current keycode */ | ||
2123 | |||
2124 | if (vc->vc_mode == KD_GRAPHICS) | ||
2125 | return ret; | ||
2126 | |||
2127 | /* | ||
2128 | * First, determine whether we are handling a fake keypress on | ||
2129 | * the current processor. If we are, then return NOTIFY_OK, | ||
2130 | * to pass the keystroke up the chain. This prevents us from | ||
2131 | * trying to take the Speakup lock while it is held by the | ||
2132 | * processor on which the simulated keystroke was generated. | ||
2133 | * Also, the simulated keystrokes should be ignored by Speakup. | ||
2134 | */ | ||
2135 | |||
2136 | if (speakup_fake_key_pressed()) | ||
2137 | return ret; | ||
2138 | |||
2139 | switch (code) { | ||
2140 | case KBD_KEYCODE: | ||
2141 | /* speakup requires keycode and keysym currently */ | ||
2142 | keycode = param->value; | ||
2143 | break; | ||
2144 | case KBD_UNBOUND_KEYCODE: | ||
2145 | /* not used yet */ | ||
2146 | break; | ||
2147 | case KBD_UNICODE: | ||
2148 | /* not used yet */ | ||
2149 | break; | ||
2150 | case KBD_KEYSYM: | ||
2151 | if (speakup_key(vc, param->shift, keycode, param->value, up)) | ||
2152 | ret = NOTIFY_STOP; | ||
2153 | else | ||
2154 | if (KTYP(param->value) == KT_CUR) | ||
2155 | ret = pre_handle_cursor(vc, | ||
2156 | KVAL(param->value), up); | ||
2157 | break; | ||
2158 | case KBD_POST_KEYSYM: { | ||
2159 | unsigned char type = KTYP(param->value) - 0xf0; | ||
2160 | unsigned char val = KVAL(param->value); | ||
2161 | switch (type) { | ||
2162 | case KT_SHIFT: | ||
2163 | do_handle_shift(vc, val, up); | ||
2164 | break; | ||
2165 | case KT_LATIN: | ||
2166 | case KT_LETTER: | ||
2167 | do_handle_latin(vc, val, up); | ||
2168 | break; | ||
2169 | case KT_CUR: | ||
2170 | do_handle_cursor(vc, val, up); | ||
2171 | break; | ||
2172 | case KT_SPEC: | ||
2173 | do_handle_spec(vc, val, up); | ||
2174 | break; | ||
2175 | } | ||
2176 | break; | ||
2177 | } | ||
2178 | } | ||
2179 | return ret; | ||
2180 | } | ||
2181 | |||
2182 | static int vt_notifier_call(struct notifier_block *nb, | ||
2183 | unsigned long code, void *_param) | ||
2184 | { | ||
2185 | struct vt_notifier_param *param = _param; | ||
2186 | struct vc_data *vc = param->vc; | ||
2187 | switch (code) { | ||
2188 | case VT_ALLOCATE: | ||
2189 | if (vc->vc_mode == KD_TEXT) | ||
2190 | speakup_allocate(vc); | ||
2191 | break; | ||
2192 | case VT_DEALLOCATE: | ||
2193 | speakup_deallocate(vc); | ||
2194 | break; | ||
2195 | case VT_WRITE: | ||
2196 | if (param->c == '\b') | ||
2197 | speakup_bs(vc); | ||
2198 | else | ||
2199 | if (param->c < 0x100) { | ||
2200 | char d = param->c; | ||
2201 | speakup_con_write(vc, &d, 1); | ||
2202 | } | ||
2203 | break; | ||
2204 | case VT_UPDATE: | ||
2205 | speakup_con_update(vc); | ||
2206 | break; | ||
2207 | } | ||
2208 | return NOTIFY_OK; | ||
2209 | } | ||
2210 | |||
2211 | /* called by: module_exit() */ | ||
2212 | static void __exit speakup_exit(void) | ||
2213 | { | ||
2214 | int i; | ||
2215 | |||
2216 | free_user_msgs(); | ||
2217 | unregister_keyboard_notifier(&keyboard_notifier_block); | ||
2218 | unregister_vt_notifier(&vt_notifier_block); | ||
2219 | speakup_unregister_devsynth(); | ||
2220 | del_timer(&cursor_timer); | ||
2221 | |||
2222 | kthread_stop(speakup_task); | ||
2223 | speakup_task = NULL; | ||
2224 | mutex_lock(&spk_mutex); | ||
2225 | synth_release(); | ||
2226 | mutex_unlock(&spk_mutex); | ||
2227 | |||
2228 | for (i = 0; i < MAXVARS; i++) | ||
2229 | speakup_unregister_var(i); | ||
2230 | |||
2231 | for (i = 0; i < 256; i++) { | ||
2232 | if (characters[i] != default_chars[i]) | ||
2233 | kfree(characters[i]); | ||
2234 | } | ||
2235 | for (i = 0; speakup_console[i]; i++) | ||
2236 | kfree(speakup_console[i]); | ||
2237 | speakup_kobj_exit(); | ||
2238 | speakup_remove_virtual_keyboard(); | ||
2239 | } | ||
2240 | |||
2241 | /* call by: module_init() */ | ||
2242 | static int __init speakup_init(void) | ||
2243 | { | ||
2244 | int i; | ||
2245 | int err; | ||
2246 | struct st_spk_t *first_console; | ||
2247 | struct vc_data *vc = vc_cons[fg_console].d; | ||
2248 | struct var_t *var; | ||
2249 | |||
2250 | err = speakup_add_virtual_keyboard(); | ||
2251 | if (err) | ||
2252 | return err; | ||
2253 | |||
2254 | initialize_msgs(); /* Initialize arrays for i18n. */ | ||
2255 | first_console = kzalloc(sizeof(*first_console), GFP_KERNEL); | ||
2256 | if (!first_console) | ||
2257 | return -ENOMEM; | ||
2258 | if (speakup_kobj_init() < 0) | ||
2259 | return -ENOMEM; | ||
2260 | |||
2261 | reset_default_chars(); | ||
2262 | reset_default_chartab(); | ||
2263 | |||
2264 | speakup_console[vc->vc_num] = first_console; | ||
2265 | speakup_date(vc); | ||
2266 | pr_info("speakup %s: initialized\n", SPEAKUP_VERSION); | ||
2267 | |||
2268 | strlwr(synth_name); | ||
2269 | spk_vars[0].u.n.high = vc->vc_cols; | ||
2270 | for (var = spk_vars; var->var_id !=MAXVARS; var++) | ||
2271 | speakup_register_var(var); | ||
2272 | for (var = synth_time_vars; (var->var_id >= 0) && (var->var_id < MAXVARS); var++) | ||
2273 | speakup_register_var(var); | ||
2274 | for (i = 1; punc_info[i].mask != 0; i++) | ||
2275 | set_mask_bits(0, i, 2); | ||
2276 | |||
2277 | set_key_info(key_defaults, key_buf); | ||
2278 | if (quiet_boot) | ||
2279 | spk_shut_up |= 0x01; | ||
2280 | |||
2281 | for (i = 0; i < MAX_NR_CONSOLES; i++) | ||
2282 | if (vc_cons[i].d) | ||
2283 | speakup_allocate(vc_cons[i].d); | ||
2284 | |||
2285 | pr_warn("synth name on entry is: %s\n", synth_name); | ||
2286 | synth_init(synth_name); | ||
2287 | speakup_register_devsynth(); | ||
2288 | |||
2289 | register_keyboard_notifier(&keyboard_notifier_block); | ||
2290 | register_vt_notifier(&vt_notifier_block); | ||
2291 | |||
2292 | speakup_task = kthread_create(speakup_thread, NULL, "speakup"); | ||
2293 | set_user_nice(speakup_task, 10); | ||
2294 | if ( ! IS_ERR(speakup_task)) | ||
2295 | wake_up_process(speakup_task); | ||
2296 | else | ||
2297 | return -ENOMEM; | ||
2298 | return 0; | ||
2299 | } | ||
2300 | |||
2301 | |||
2302 | module_init(speakup_init); | ||
2303 | module_exit(speakup_exit); | ||
2304 | |||
diff --git a/drivers/staging/speakup/selection.c b/drivers/staging/speakup/selection.c new file mode 100644 index 00000000000..1b865ff00bc --- /dev/null +++ b/drivers/staging/speakup/selection.c | |||
@@ -0,0 +1,155 @@ | |||
1 | #include <linux/slab.h> /* for kmalloc */ | ||
2 | #include <linux/consolemap.h> | ||
3 | #include <linux/interrupt.h> | ||
4 | #include <linux/sched.h> | ||
5 | #include <linux/selection.h> | ||
6 | |||
7 | #include "speakup.h" | ||
8 | |||
9 | /* ------ cut and paste ----- */ | ||
10 | /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */ | ||
11 | #define ishardspace(c) ((c) == ' ') | ||
12 | |||
13 | unsigned short xs, ys, xe, ye; /* our region points */ | ||
14 | |||
15 | /* Variables for selection control. */ | ||
16 | /* must not be disallocated */ | ||
17 | struct vc_data *spk_sel_cons; | ||
18 | /* cleared by clear_selection */ | ||
19 | static int sel_start = -1; | ||
20 | static int sel_end; | ||
21 | static int sel_buffer_lth; | ||
22 | static char *sel_buffer; | ||
23 | |||
24 | static unsigned char sel_pos(int n) | ||
25 | { | ||
26 | return inverse_translate(spk_sel_cons, screen_glyph(spk_sel_cons, n), 0); | ||
27 | } | ||
28 | |||
29 | void speakup_clear_selection(void) | ||
30 | { | ||
31 | sel_start = -1; | ||
32 | } | ||
33 | |||
34 | /* does screen address p correspond to character at LH/RH edge of screen? */ | ||
35 | static int atedge(const int p, int size_row) | ||
36 | { | ||
37 | return (!(p % size_row) || !((p + 2) % size_row)); | ||
38 | } | ||
39 | |||
40 | /* constrain v such that v <= u */ | ||
41 | static unsigned short limit(const unsigned short v, const unsigned short u) | ||
42 | { | ||
43 | return (v > u) ? u : v; | ||
44 | } | ||
45 | |||
46 | int speakup_set_selection(struct tty_struct *tty) | ||
47 | { | ||
48 | int new_sel_start, new_sel_end; | ||
49 | char *bp, *obp; | ||
50 | int i, ps, pe; | ||
51 | struct vc_data *vc = vc_cons[fg_console].d; | ||
52 | |||
53 | xs = limit(xs, vc->vc_cols - 1); | ||
54 | ys = limit(ys, vc->vc_rows - 1); | ||
55 | xe = limit(xe, vc->vc_cols - 1); | ||
56 | ye = limit(ye, vc->vc_rows - 1); | ||
57 | ps = ys * vc->vc_size_row + (xs << 1); | ||
58 | pe = ye * vc->vc_size_row + (xe << 1); | ||
59 | |||
60 | if (ps > pe) { | ||
61 | /* make sel_start <= sel_end */ | ||
62 | int tmp = ps; | ||
63 | ps = pe; | ||
64 | pe = tmp; | ||
65 | } | ||
66 | |||
67 | if (spk_sel_cons != vc_cons[fg_console].d) { | ||
68 | speakup_clear_selection(); | ||
69 | spk_sel_cons = vc_cons[fg_console].d; | ||
70 | printk(KERN_WARNING | ||
71 | "Selection: mark console not the same as cut\n"); | ||
72 | return -EINVAL; | ||
73 | } | ||
74 | |||
75 | new_sel_start = ps; | ||
76 | new_sel_end = pe; | ||
77 | |||
78 | /* select to end of line if on trailing space */ | ||
79 | if (new_sel_end > new_sel_start && | ||
80 | !atedge(new_sel_end, vc->vc_size_row) && | ||
81 | ishardspace(sel_pos(new_sel_end))) { | ||
82 | for (pe = new_sel_end + 2; ; pe += 2) | ||
83 | if (!ishardspace(sel_pos(pe)) || | ||
84 | atedge(pe, vc->vc_size_row)) | ||
85 | break; | ||
86 | if (ishardspace(sel_pos(pe))) | ||
87 | new_sel_end = pe; | ||
88 | } | ||
89 | if ((new_sel_start == sel_start) && (new_sel_end == sel_end)) | ||
90 | return 0; /* no action required */ | ||
91 | |||
92 | sel_start = new_sel_start; | ||
93 | sel_end = new_sel_end; | ||
94 | /* Allocate a new buffer before freeing the old one ... */ | ||
95 | bp = kmalloc((sel_end-sel_start)/2+1, GFP_ATOMIC); | ||
96 | if (!bp) { | ||
97 | printk(KERN_WARNING "selection: kmalloc() failed\n"); | ||
98 | speakup_clear_selection(); | ||
99 | return -ENOMEM; | ||
100 | } | ||
101 | kfree(sel_buffer); | ||
102 | sel_buffer = bp; | ||
103 | |||
104 | obp = bp; | ||
105 | for (i = sel_start; i <= sel_end; i += 2) { | ||
106 | *bp = sel_pos(i); | ||
107 | if (!ishardspace(*bp++)) | ||
108 | obp = bp; | ||
109 | if (!((i + 2) % vc->vc_size_row)) { | ||
110 | /* strip trailing blanks from line and add newline, | ||
111 | unless non-space at end of line. */ | ||
112 | if (obp != bp) { | ||
113 | bp = obp; | ||
114 | *bp++ = '\r'; | ||
115 | } | ||
116 | obp = bp; | ||
117 | } | ||
118 | } | ||
119 | sel_buffer_lth = bp - sel_buffer; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* TODO: move to some helper thread, probably. That'd fix having to check for | ||
124 | * in_atomic(). */ | ||
125 | int speakup_paste_selection(struct tty_struct *tty) | ||
126 | { | ||
127 | struct vc_data *vc = (struct vc_data *) tty->driver_data; | ||
128 | int pasted = 0, count; | ||
129 | DECLARE_WAITQUEUE(wait, current); | ||
130 | add_wait_queue(&vc->paste_wait, &wait); | ||
131 | while (sel_buffer && sel_buffer_lth > pasted) { | ||
132 | set_current_state(TASK_INTERRUPTIBLE); | ||
133 | if (test_bit(TTY_THROTTLED, &tty->flags)) { | ||
134 | if (in_atomic()) | ||
135 | /* can't be performed in an interrupt handler, abort */ | ||
136 | break; | ||
137 | schedule(); | ||
138 | continue; | ||
139 | } | ||
140 | count = sel_buffer_lth - pasted; | ||
141 | count = min_t(int, count, tty->receive_room); | ||
142 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) | ||
143 | tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, 0, count); | ||
144 | #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) | ||
145 | tty->ldisc.ops->receive_buf(tty, sel_buffer + pasted, 0, count); | ||
146 | #else | ||
147 | tty->ldisc.receive_buf(tty, sel_buffer + pasted, 0, count); | ||
148 | #endif | ||
149 | pasted += count; | ||
150 | } | ||
151 | remove_wait_queue(&vc->paste_wait, &wait); | ||
152 | current->state = TASK_RUNNING; | ||
153 | return 0; | ||
154 | } | ||
155 | |||
diff --git a/drivers/staging/speakup/serialio.c b/drivers/staging/speakup/serialio.c new file mode 100644 index 00000000000..f64eb364533 --- /dev/null +++ b/drivers/staging/speakup/serialio.c | |||
@@ -0,0 +1,212 @@ | |||
1 | #include <linux/interrupt.h> | ||
2 | #include <linux/ioport.h> | ||
3 | |||
4 | #include "spk_types.h" | ||
5 | #include "speakup.h" | ||
6 | #include "spk_priv.h" | ||
7 | #include "serialio.h" | ||
8 | |||
9 | static void start_serial_interrupt(int irq); | ||
10 | |||
11 | static struct serial_state rs_table[] = { | ||
12 | SERIAL_PORT_DFNS | ||
13 | }; | ||
14 | static struct serial_state *serstate; | ||
15 | static int timeouts; | ||
16 | |||
17 | struct serial_state *spk_serial_init(int index) | ||
18 | { | ||
19 | int baud = 9600, quot = 0; | ||
20 | unsigned int cval = 0; | ||
21 | int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8; | ||
22 | struct serial_state *ser = NULL; | ||
23 | int err; | ||
24 | |||
25 | ser = rs_table + index; | ||
26 | /* Divisor, bytesize and parity */ | ||
27 | quot = ser->baud_base / baud; | ||
28 | cval = cflag & (CSIZE | CSTOPB); | ||
29 | #if defined(__powerpc__) || defined(__alpha__) | ||
30 | cval >>= 8; | ||
31 | #else /* !__powerpc__ && !__alpha__ */ | ||
32 | cval >>= 4; | ||
33 | #endif /* !__powerpc__ && !__alpha__ */ | ||
34 | if (cflag & PARENB) | ||
35 | cval |= UART_LCR_PARITY; | ||
36 | if (!(cflag & PARODD)) | ||
37 | cval |= UART_LCR_EPAR; | ||
38 | if (synth_request_region(ser->port, 8)) { | ||
39 | /* try to take it back. */ | ||
40 | printk("Ports not available, trying to steal them\n"); | ||
41 | __release_region(&ioport_resource, ser->port, 8); | ||
42 | err = synth_request_region(ser->port, 8); | ||
43 | if (err) { | ||
44 | pr_warn("Unable to allocate port at %x, errno %i", ser->port, err); | ||
45 | return NULL; | ||
46 | } | ||
47 | } | ||
48 | |||
49 | /* Disable UART interrupts, set DTR and RTS high | ||
50 | * and set speed. */ | ||
51 | outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ | ||
52 | outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ | ||
53 | outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ | ||
54 | outb(cval, ser->port + UART_LCR); /* reset DLAB */ | ||
55 | |||
56 | /* Turn off Interrupts */ | ||
57 | outb(0, ser->port + UART_IER); | ||
58 | outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); | ||
59 | |||
60 | /* If we read 0xff from the LSR, there is no UART here. */ | ||
61 | if (inb(ser->port + UART_LSR) == 0xff) { | ||
62 | synth_release_region(ser->port, 8); | ||
63 | serstate = NULL; | ||
64 | return NULL; | ||
65 | } | ||
66 | |||
67 | mdelay(1); | ||
68 | speakup_info.port_tts = ser->port; | ||
69 | serstate = ser; | ||
70 | |||
71 | start_serial_interrupt(ser->irq); | ||
72 | |||
73 | return ser; | ||
74 | } | ||
75 | |||
76 | static irqreturn_t synth_readbuf_handler(int irq, void *dev_id) | ||
77 | { | ||
78 | unsigned long flags; | ||
79 | /*printk(KERN_ERR "in irq\n"); */ | ||
80 | /*pr_warn("in IRQ\n"); */ | ||
81 | int c; | ||
82 | spk_lock(flags); | ||
83 | while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) { | ||
84 | |||
85 | c = inb_p(speakup_info.port_tts+UART_RX); | ||
86 | synth->read_buff_add((u_char) c); | ||
87 | /*printk(KERN_ERR "c = %d\n", c); */ | ||
88 | /*pr_warn("C = %d\n", c); */ | ||
89 | } | ||
90 | spk_unlock(flags); | ||
91 | return IRQ_HANDLED; | ||
92 | } | ||
93 | |||
94 | static void start_serial_interrupt(int irq) | ||
95 | { | ||
96 | int rv; | ||
97 | |||
98 | if (synth->read_buff_add == NULL) | ||
99 | return; | ||
100 | |||
101 | rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED, | ||
102 | "serial", (void *) synth_readbuf_handler); | ||
103 | |||
104 | if (rv) | ||
105 | printk(KERN_ERR "Unable to request Speakup serial I R Q\n"); | ||
106 | /* Set MCR */ | ||
107 | outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, | ||
108 | speakup_info.port_tts + UART_MCR); | ||
109 | /* Turn on Interrupts */ | ||
110 | outb(UART_IER_MSI|UART_IER_RLSI|UART_IER_RDI, | ||
111 | speakup_info.port_tts + UART_IER); | ||
112 | inb(speakup_info.port_tts+UART_LSR); | ||
113 | inb(speakup_info.port_tts+UART_RX); | ||
114 | inb(speakup_info.port_tts+UART_IIR); | ||
115 | inb(speakup_info.port_tts+UART_MSR); | ||
116 | outb(1, speakup_info.port_tts + UART_FCR); /* Turn FIFO On */ | ||
117 | } | ||
118 | |||
119 | void stop_serial_interrupt(void) | ||
120 | { | ||
121 | if (speakup_info.port_tts == 0) | ||
122 | return; | ||
123 | |||
124 | if (synth->read_buff_add == NULL) | ||
125 | return; | ||
126 | |||
127 | /* Turn off interrupts */ | ||
128 | outb(0, speakup_info.port_tts+UART_IER); | ||
129 | /* Free IRQ */ | ||
130 | free_irq(serstate->irq, (void *) synth_readbuf_handler); | ||
131 | } | ||
132 | |||
133 | int wait_for_xmitr(void) | ||
134 | { | ||
135 | int tmout = SPK_XMITR_TIMEOUT; | ||
136 | if ((synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) { | ||
137 | pr_warn("%s: too many timeouts, deactivating speakup\n", synth->long_name); | ||
138 | synth->alive = 0; | ||
139 | /* No synth any more, so nobody will restart TTYs, and we thus | ||
140 | * need to do it ourselves. Now that there is no synth we can | ||
141 | * let application flood anyway */ | ||
142 | speakup_start_ttys(); | ||
143 | timeouts = 0; | ||
144 | return 0; | ||
145 | } | ||
146 | while (spk_serial_tx_busy()) { | ||
147 | if (--tmout == 0) { | ||
148 | pr_warn("%s: timed out (tx busy)\n", synth->long_name); | ||
149 | timeouts++; | ||
150 | return 0; | ||
151 | } | ||
152 | udelay(1); | ||
153 | } | ||
154 | tmout = SPK_CTS_TIMEOUT; | ||
155 | while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) { | ||
156 | /* CTS */ | ||
157 | if (--tmout == 0) { | ||
158 | // pr_warn("%s: timed out (cts)\n", synth->long_name); | ||
159 | timeouts++; | ||
160 | return 0; | ||
161 | } | ||
162 | udelay(1); | ||
163 | } | ||
164 | timeouts = 0; | ||
165 | return 1; | ||
166 | } | ||
167 | |||
168 | unsigned char spk_serial_in(void) | ||
169 | { | ||
170 | int tmout = SPK_SERIAL_TIMEOUT; | ||
171 | |||
172 | while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) { | ||
173 | if (--tmout == 0) { | ||
174 | pr_warn("time out while waiting for input.\n"); | ||
175 | return 0xff; | ||
176 | } | ||
177 | udelay(1); | ||
178 | } | ||
179 | return inb_p(speakup_info.port_tts + UART_RX); | ||
180 | } | ||
181 | EXPORT_SYMBOL_GPL(spk_serial_in); | ||
182 | |||
183 | unsigned char spk_serial_in_nowait(void) | ||
184 | { | ||
185 | unsigned char lsr; | ||
186 | |||
187 | lsr = inb_p(speakup_info.port_tts + UART_LSR); | ||
188 | if (!(lsr & UART_LSR_DR)) | ||
189 | return 0; | ||
190 | return inb_p(speakup_info.port_tts + UART_RX); | ||
191 | } | ||
192 | EXPORT_SYMBOL_GPL(spk_serial_in_nowait); | ||
193 | |||
194 | int spk_serial_out(const char ch) | ||
195 | { | ||
196 | if (synth->alive && wait_for_xmitr()) { | ||
197 | outb_p(ch, speakup_info.port_tts); | ||
198 | return 1; | ||
199 | } | ||
200 | return 0; | ||
201 | } | ||
202 | EXPORT_SYMBOL_GPL(spk_serial_out); | ||
203 | |||
204 | void spk_serial_release(void) | ||
205 | { | ||
206 | if (speakup_info.port_tts == 0) | ||
207 | return; | ||
208 | synth_release_region(speakup_info.port_tts, 8); | ||
209 | speakup_info.port_tts = 0; | ||
210 | } | ||
211 | EXPORT_SYMBOL_GPL(spk_serial_release); | ||
212 | |||
diff --git a/drivers/staging/speakup/serialio.h b/drivers/staging/speakup/serialio.h new file mode 100644 index 00000000000..d785b1f6a3b --- /dev/null +++ b/drivers/staging/speakup/serialio.h | |||
@@ -0,0 +1,55 @@ | |||
1 | #ifndef _SPEAKUP_SERIAL_H | ||
2 | #define _SPEAKUP_SERIAL_H | ||
3 | |||
4 | #include <linux/serial.h> /* for rs_table, serial constants & | ||
5 | serial_uart_config */ | ||
6 | #include <linux/serial_reg.h> /* for more serial constants */ | ||
7 | #include <linux/serialP.h> /* for struct serial_state */ | ||
8 | #ifndef __sparc__ | ||
9 | #include <asm/serial.h> | ||
10 | #endif | ||
11 | |||
12 | /* countdown values for serial timeouts in us */ | ||
13 | #define SPK_SERIAL_TIMEOUT 100000 | ||
14 | /* countdown values transmitter/dsr timeouts in us */ | ||
15 | #define SPK_XMITR_TIMEOUT 100000 | ||
16 | /* countdown values cts timeouts in us */ | ||
17 | #define SPK_CTS_TIMEOUT 100000 | ||
18 | /* check ttyS0 ... ttyS3 */ | ||
19 | #define SPK_LO_TTY 0 | ||
20 | #define SPK_HI_TTY 3 | ||
21 | /* # of timeouts permitted before disable */ | ||
22 | #define NUM_DISABLE_TIMEOUTS 3 | ||
23 | /* buffer timeout in ms */ | ||
24 | #define SPK_TIMEOUT 100 | ||
25 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) | ||
26 | |||
27 | #define spk_serial_tx_busy() ((inb(speakup_info.port_tts + UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY) | ||
28 | |||
29 | /* 2.6.22 doesn't have them any more, hardcode it for now (these values should | ||
30 | * be fine for 99% cases) */ | ||
31 | #ifndef BASE_BAUD | ||
32 | #define BASE_BAUD (1843200 / 16) | ||
33 | #endif | ||
34 | #ifndef STD_COM_FLAGS | ||
35 | #ifdef CONFIG_SERIAL_DETECT_IRQ | ||
36 | #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) | ||
37 | #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) | ||
38 | #else | ||
39 | #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) | ||
40 | #define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF | ||
41 | #endif | ||
42 | #endif | ||
43 | #ifndef SERIAL_PORT_DFNS | ||
44 | #define SERIAL_PORT_DFNS \ | ||
45 | /* UART CLK PORT IRQ FLAGS */ \ | ||
46 | { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \ | ||
47 | { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \ | ||
48 | { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \ | ||
49 | { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ | ||
50 | #endif | ||
51 | #ifndef IRQF_SHARED | ||
52 | #define IRQF_SHARED SA_SHIRQ | ||
53 | #endif | ||
54 | |||
55 | #endif | ||
diff --git a/drivers/staging/speakup/speakup.h b/drivers/staging/speakup/speakup.h new file mode 100644 index 00000000000..e6f3dfe38b9 --- /dev/null +++ b/drivers/staging/speakup/speakup.h | |||
@@ -0,0 +1,127 @@ | |||
1 | #ifndef _SPEAKUP_H | ||
2 | #define _SPEAKUP_H | ||
3 | #include <linux/version.h> | ||
4 | |||
5 | #include "spk_types.h" | ||
6 | #include "i18n.h" | ||
7 | |||
8 | #define SPEAKUP_VERSION "3.1.6" | ||
9 | #define KEY_MAP_VER 119 | ||
10 | #define SHIFT_TBL_SIZE 64 | ||
11 | #define MAX_DESC_LEN 72 | ||
12 | |||
13 | /* proc permissions */ | ||
14 | #define USER_R (S_IFREG|S_IRUGO) | ||
15 | #define USER_W (S_IFREG|S_IWUGO) | ||
16 | #define USER_RW (S_IFREG|S_IRUGO|S_IWUGO) | ||
17 | #define ROOT_W (S_IFREG|S_IRUGO|S_IWUSR) | ||
18 | |||
19 | #define TOGGLE_0 .u.n = {NULL, 0, 0, 1, 0, 0, NULL } | ||
20 | #define TOGGLE_1 .u.n = {NULL, 1, 0, 1, 0, 0, NULL } | ||
21 | #define MAXVARLEN 15 | ||
22 | |||
23 | #define SYNTH_OK 0x0001 | ||
24 | #define B_ALPHA 0x0002 | ||
25 | #define ALPHA 0x0003 | ||
26 | #define B_CAP 0x0004 | ||
27 | #define A_CAP 0x0007 | ||
28 | #define B_NUM 0x0008 | ||
29 | #define NUM 0x0009 | ||
30 | #define ALPHANUM (B_ALPHA|B_NUM) | ||
31 | #define SOME 0x0010 | ||
32 | #define MOST 0x0020 | ||
33 | #define PUNC 0x0040 | ||
34 | #define A_PUNC 0x0041 | ||
35 | #define B_WDLM 0x0080 | ||
36 | #define WDLM 0x0081 | ||
37 | #define B_EXNUM 0x0100 | ||
38 | #define CH_RPT 0x0200 | ||
39 | #define B_CTL 0x0400 | ||
40 | #define A_CTL (B_CTL+SYNTH_OK) | ||
41 | #define B_SYM 0x0800 | ||
42 | #define B_CAPSYM (B_CAP|B_SYM) | ||
43 | |||
44 | #define IS_WDLM(x) (spk_chartab[((u_char)x)]&B_WDLM) | ||
45 | #define IS_CHAR(x, type) (spk_chartab[((u_char)x)]&type) | ||
46 | #define IS_TYPE(x, type) ((spk_chartab[((u_char)x)]&type) == type) | ||
47 | |||
48 | #define SET_DEFAULT -4 | ||
49 | #define E_RANGE -3 | ||
50 | #define E_TOOLONG -2 | ||
51 | #define E_UNDEF -1 | ||
52 | |||
53 | extern int speakup_thread(void *data); | ||
54 | extern void reset_default_chars(void); | ||
55 | extern void reset_default_chartab(void); | ||
56 | extern void synth_start(void); | ||
57 | extern int set_key_info(const u_char *key_info, u_char *k_buffer); | ||
58 | extern char *strlwr(char *s); | ||
59 | extern char *speakup_s2i(char *start, int *dest); | ||
60 | extern char *s2uchar(char *start, char *dest); | ||
61 | extern char *xlate(char *s); | ||
62 | extern int speakup_kobj_init(void); | ||
63 | extern void speakup_kobj_exit(void); | ||
64 | extern int chartab_get_value(char *keyword); | ||
65 | extern void speakup_register_var(struct var_t *var); | ||
66 | extern void speakup_unregister_var(enum var_id_t var_id); | ||
67 | extern struct st_var_header *get_var_header(enum var_id_t var_id); | ||
68 | extern struct st_var_header *var_header_by_name(const char *name); | ||
69 | extern struct punc_var_t *get_punc_var(enum var_id_t var_id); | ||
70 | extern int set_num_var(int val, struct st_var_header *var, int how); | ||
71 | extern int set_string_var(const char *page, struct st_var_header *var, int len); | ||
72 | extern int set_mask_bits(const char *input, const int which, const int how); | ||
73 | extern special_func special_handler; | ||
74 | extern int handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key); | ||
75 | extern int synth_init(char *name); | ||
76 | extern void synth_release(void); | ||
77 | |||
78 | extern void do_flush(void); | ||
79 | extern void speakup_start_ttys(void); | ||
80 | extern void synth_buffer_add(char ch); | ||
81 | extern void synth_buffer_clear(void); | ||
82 | extern void speakup_clear_selection(void); | ||
83 | extern int speakup_set_selection(struct tty_struct *tty); | ||
84 | extern int speakup_paste_selection(struct tty_struct *tty); | ||
85 | extern void speakup_register_devsynth(void); | ||
86 | extern void speakup_unregister_devsynth(void); | ||
87 | extern void synth_write(const char *buf, size_t count); | ||
88 | extern int synth_supports_indexing(void); | ||
89 | |||
90 | extern struct vc_data *spk_sel_cons; | ||
91 | extern unsigned short xs, ys, xe, ye; /* our region points */ | ||
92 | |||
93 | extern wait_queue_head_t speakup_event; | ||
94 | extern struct kobject *speakup_kobj; | ||
95 | extern struct task_struct *speakup_task; | ||
96 | extern const u_char key_defaults[]; | ||
97 | |||
98 | /* Protect speakup synthesizer list */ | ||
99 | extern struct mutex spk_mutex; | ||
100 | extern struct st_spk_t *speakup_console[]; | ||
101 | extern struct spk_synth *synth; | ||
102 | extern char pitch_buff[]; | ||
103 | extern u_char *our_keys[]; | ||
104 | extern short punc_masks[]; | ||
105 | extern char str_caps_start[], str_caps_stop[]; | ||
106 | extern const struct st_bits_data punc_info[]; | ||
107 | extern u_char key_buf[600]; | ||
108 | extern char *characters[]; | ||
109 | extern char *default_chars[]; | ||
110 | extern u_short spk_chartab[]; | ||
111 | extern int no_intr, say_ctrl, say_word_ctl, punc_level; | ||
112 | extern int reading_punc, attrib_bleep, bleeps; | ||
113 | extern int bleep_time, bell_pos; | ||
114 | extern int spell_delay, key_echo; | ||
115 | extern short punc_mask; | ||
116 | extern short pitch_shift, synth_flags; | ||
117 | extern int quiet_boot; | ||
118 | extern char *synth_name; | ||
119 | extern struct bleep unprocessed_sound; | ||
120 | |||
121 | /* Prototypes from fakekey.c. */ | ||
122 | int speakup_add_virtual_keyboard(void); | ||
123 | void speakup_remove_virtual_keyboard(void); | ||
124 | void speakup_fake_down_arrow(void); | ||
125 | bool speakup_fake_key_pressed(void); | ||
126 | |||
127 | #endif | ||
diff --git a/drivers/staging/speakup/speakup_acnt.h b/drivers/staging/speakup/speakup_acnt.h new file mode 100644 index 00000000000..2d883654ffc --- /dev/null +++ b/drivers/staging/speakup/speakup_acnt.h | |||
@@ -0,0 +1,16 @@ | |||
1 | /* speakup_acntpc.h - header file for speakups Accent-PC driver. */ | ||
2 | |||
3 | #define SYNTH_IO_EXTENT 0x02 | ||
4 | |||
5 | #define SYNTH_CLEAR 0x18 /* stops speech */ | ||
6 | |||
7 | /* Port Status Flags */ | ||
8 | #define SYNTH_READABLE 0x01 /* mask for bit which is nonzero if a | ||
9 | byte can be read from the data port */ | ||
10 | #define SYNTH_WRITABLE 0x02 /* mask for RDY bit, which when set to | ||
11 | 1, indicates the data port is ready | ||
12 | to accept a byte of data. */ | ||
13 | #define SYNTH_QUIET 'S' /* synth is not speaking */ | ||
14 | #define SYNTH_FULL 'F' /* synth is full. */ | ||
15 | #define SYNTH_ALMOST_EMPTY 'M' /* synth has les than 2 seconds of text left */ | ||
16 | #define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */ | ||
diff --git a/drivers/staging/speakup/speakup_acntpc.c b/drivers/staging/speakup/speakup_acntpc.c new file mode 100644 index 00000000000..d2660e213be --- /dev/null +++ b/drivers/staging/speakup/speakup_acntpc.c | |||
@@ -0,0 +1,327 @@ | |||
1 | /* | ||
2 | * written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * this code is specificly written as a driver for the speakup screenreview | ||
23 | * package and is not a general device driver. | ||
24 | * This driver is for the Aicom Acent PC internal synthesizer. | ||
25 | */ | ||
26 | |||
27 | #include <linux/jiffies.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <linux/timer.h> | ||
30 | #include <linux/kthread.h> | ||
31 | |||
32 | #include "spk_priv.h" | ||
33 | #include "serialio.h" | ||
34 | #include "speakup.h" | ||
35 | #include "speakup_acnt.h" /* local header file for Accent values */ | ||
36 | |||
37 | #define DRV_VERSION "2.10" | ||
38 | #define synth_readable() (inb_p(synth_port_control) & SYNTH_READABLE) | ||
39 | #define synth_writable() (inb_p(synth_port_control) & SYNTH_WRITABLE) | ||
40 | #define synth_full() (inb_p(speakup_info.port_tts + UART_RX) == 'F') | ||
41 | #define PROCSPEECH '\r' | ||
42 | |||
43 | static int synth_probe(struct spk_synth *synth); | ||
44 | static void accent_release(void); | ||
45 | static const char *synth_immediate(struct spk_synth *synth, const char *buf); | ||
46 | static void do_catch_up(struct spk_synth *synth); | ||
47 | static void synth_flush(struct spk_synth *synth); | ||
48 | |||
49 | static int synth_port_control; | ||
50 | static int port_forced; | ||
51 | static unsigned int synth_portlist[] = { 0x2a8, 0 }; | ||
52 | |||
53 | static struct var_t vars[] = { | ||
54 | { CAPS_START, .u.s = {"\033P8" }}, | ||
55 | { CAPS_STOP, .u.s = {"\033P5" }}, | ||
56 | { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" }}, | ||
57 | { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL }}, | ||
58 | { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL }}, | ||
59 | { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL }}, | ||
60 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
61 | V_LAST_VAR | ||
62 | }; | ||
63 | |||
64 | /* | ||
65 | * These attributes will appear in /sys/accessibility/speakup/acntpc. | ||
66 | */ | ||
67 | static struct kobj_attribute caps_start_attribute = | ||
68 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
69 | static struct kobj_attribute caps_stop_attribute = | ||
70 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
71 | static struct kobj_attribute pitch_attribute = | ||
72 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
73 | static struct kobj_attribute rate_attribute = | ||
74 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
75 | static struct kobj_attribute tone_attribute = | ||
76 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
77 | static struct kobj_attribute vol_attribute = | ||
78 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
79 | |||
80 | static struct kobj_attribute delay_time_attribute = | ||
81 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
82 | static struct kobj_attribute direct_attribute = | ||
83 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
84 | static struct kobj_attribute full_time_attribute = | ||
85 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
86 | static struct kobj_attribute jiffy_delta_attribute = | ||
87 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
88 | static struct kobj_attribute trigger_time_attribute = | ||
89 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
90 | |||
91 | /* | ||
92 | * Create a group of attributes so that we can create and destroy them all | ||
93 | * at once. | ||
94 | */ | ||
95 | static struct attribute *synth_attrs[] = { | ||
96 | &caps_start_attribute.attr, | ||
97 | &caps_stop_attribute.attr, | ||
98 | &pitch_attribute.attr, | ||
99 | &rate_attribute.attr, | ||
100 | &tone_attribute.attr, | ||
101 | &vol_attribute.attr, | ||
102 | &delay_time_attribute.attr, | ||
103 | &direct_attribute.attr, | ||
104 | &full_time_attribute.attr, | ||
105 | &jiffy_delta_attribute.attr, | ||
106 | &trigger_time_attribute.attr, | ||
107 | NULL, /* need to NULL terminate the list of attributes */ | ||
108 | }; | ||
109 | |||
110 | static struct spk_synth synth_acntpc = { | ||
111 | .name = "acntpc", | ||
112 | .version = DRV_VERSION, | ||
113 | .long_name = "Accent PC", | ||
114 | .init = "\033=X \033Oi\033T2\033=M\033N1\n", | ||
115 | .procspeech = PROCSPEECH, | ||
116 | .clear = SYNTH_CLEAR, | ||
117 | .delay = 500, | ||
118 | .trigger = 50, | ||
119 | .jiffies = 50, | ||
120 | .full = 1000, | ||
121 | .startup = SYNTH_START, | ||
122 | .checkval = SYNTH_CHECK, | ||
123 | .vars = vars, | ||
124 | .probe = synth_probe, | ||
125 | .release = accent_release, | ||
126 | .synth_immediate = synth_immediate, | ||
127 | .catch_up = do_catch_up, | ||
128 | .flush = synth_flush, | ||
129 | .is_alive = spk_synth_is_alive_nop, | ||
130 | .synth_adjust = NULL, | ||
131 | .read_buff_add = NULL, | ||
132 | .get_index = NULL, | ||
133 | .indexing = { | ||
134 | .command = NULL, | ||
135 | .lowindex = 0, | ||
136 | .highindex = 0, | ||
137 | .currindex = 0, | ||
138 | }, | ||
139 | .attributes = { | ||
140 | .attrs = synth_attrs, | ||
141 | .name = "acntpc", | ||
142 | }, | ||
143 | }; | ||
144 | |||
145 | static const char *synth_immediate(struct spk_synth *synth, const char *buf) | ||
146 | { | ||
147 | u_char ch; | ||
148 | while ((ch = *buf)) { | ||
149 | int timeout = SPK_XMITR_TIMEOUT; | ||
150 | if (ch == '\n') | ||
151 | ch = PROCSPEECH; | ||
152 | if (synth_full()) | ||
153 | return buf; | ||
154 | while (synth_writable()) { | ||
155 | if (!--timeout) | ||
156 | return buf; | ||
157 | udelay(1); | ||
158 | } | ||
159 | outb_p(ch, speakup_info.port_tts); | ||
160 | buf++; | ||
161 | } | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static void do_catch_up(struct spk_synth *synth) | ||
166 | { | ||
167 | u_char ch; | ||
168 | unsigned long flags; | ||
169 | unsigned long jiff_max; | ||
170 | int timeout; | ||
171 | int delay_time_val; | ||
172 | int jiffy_delta_val; | ||
173 | int full_time_val; | ||
174 | struct var_t *delay_time; | ||
175 | struct var_t *full_time; | ||
176 | struct var_t *jiffy_delta; | ||
177 | |||
178 | jiffy_delta = get_var(JIFFY); | ||
179 | delay_time = get_var(DELAY); | ||
180 | full_time = get_var(FULL); | ||
181 | |||
182 | spk_lock(flags); | ||
183 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
184 | spk_unlock(flags); | ||
185 | |||
186 | jiff_max = jiffies + jiffy_delta_val; | ||
187 | while (!kthread_should_stop()) { | ||
188 | spk_lock(flags); | ||
189 | if (speakup_info.flushing) { | ||
190 | speakup_info.flushing = 0; | ||
191 | spk_unlock(flags); | ||
192 | synth->flush(synth); | ||
193 | continue; | ||
194 | } | ||
195 | if (synth_buffer_empty()) { | ||
196 | spk_unlock(flags); | ||
197 | break; | ||
198 | } | ||
199 | set_current_state(TASK_INTERRUPTIBLE); | ||
200 | full_time_val = full_time->u.n.value; | ||
201 | spk_unlock(flags); | ||
202 | if (synth_full()) { | ||
203 | schedule_timeout(msecs_to_jiffies(full_time_val)); | ||
204 | continue; | ||
205 | } | ||
206 | set_current_state(TASK_RUNNING); | ||
207 | timeout = SPK_XMITR_TIMEOUT; | ||
208 | while (synth_writable()) { | ||
209 | if (!--timeout) | ||
210 | break; | ||
211 | udelay(1); | ||
212 | } | ||
213 | spk_lock(flags); | ||
214 | ch = synth_buffer_getc(); | ||
215 | spk_unlock(flags); | ||
216 | if (ch == '\n') | ||
217 | ch = PROCSPEECH; | ||
218 | outb_p(ch, speakup_info.port_tts); | ||
219 | if (jiffies >= jiff_max && ch == SPACE) { | ||
220 | timeout = SPK_XMITR_TIMEOUT; | ||
221 | while (synth_writable()) { | ||
222 | if (!--timeout) | ||
223 | break; | ||
224 | udelay(1); | ||
225 | } | ||
226 | outb_p(PROCSPEECH, speakup_info.port_tts); | ||
227 | spk_lock(flags); | ||
228 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
229 | delay_time_val = delay_time->u.n.value; | ||
230 | spk_unlock(flags); | ||
231 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
232 | jiff_max = jiffies+jiffy_delta_val; | ||
233 | } | ||
234 | } | ||
235 | timeout = SPK_XMITR_TIMEOUT; | ||
236 | while (synth_writable()) { | ||
237 | if (!--timeout) | ||
238 | break; | ||
239 | udelay(1); | ||
240 | } | ||
241 | outb_p(PROCSPEECH, speakup_info.port_tts); | ||
242 | } | ||
243 | |||
244 | static void synth_flush(struct spk_synth *synth) | ||
245 | { | ||
246 | outb_p(SYNTH_CLEAR, speakup_info.port_tts); | ||
247 | } | ||
248 | |||
249 | static int synth_probe(struct spk_synth *synth) | ||
250 | { | ||
251 | unsigned int port_val = 0; | ||
252 | int i = 0; | ||
253 | pr_info("Probing for %s.\n", synth->long_name); | ||
254 | if (port_forced) { | ||
255 | speakup_info.port_tts = port_forced; | ||
256 | pr_info("probe forced to %x by kernel command line\n", | ||
257 | speakup_info.port_tts); | ||
258 | if (synth_request_region(speakup_info.port_tts-1, | ||
259 | SYNTH_IO_EXTENT)) { | ||
260 | pr_warn("sorry, port already reserved\n"); | ||
261 | return -EBUSY; | ||
262 | } | ||
263 | port_val = inw(speakup_info.port_tts-1); | ||
264 | synth_port_control = speakup_info.port_tts-1; | ||
265 | } else { | ||
266 | for (i = 0; synth_portlist[i]; i++) { | ||
267 | if (synth_request_region(synth_portlist[i], | ||
268 | SYNTH_IO_EXTENT)) { | ||
269 | pr_warn("request_region: failed with 0x%x, %d\n", | ||
270 | synth_portlist[i], SYNTH_IO_EXTENT); | ||
271 | continue; | ||
272 | } | ||
273 | port_val = inw(synth_portlist[i]) & 0xfffc; | ||
274 | if (port_val == 0x53fc) { | ||
275 | /* 'S' and out&input bits */ | ||
276 | synth_port_control = synth_portlist[i]; | ||
277 | speakup_info.port_tts = synth_port_control+1; | ||
278 | break; | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | port_val &= 0xfffc; | ||
283 | if (port_val != 0x53fc) { | ||
284 | /* 'S' and out&input bits */ | ||
285 | pr_info("%s: not found\n", synth->long_name); | ||
286 | synth_release_region(synth_port_control, SYNTH_IO_EXTENT); | ||
287 | synth_port_control = 0; | ||
288 | return -ENODEV; | ||
289 | } | ||
290 | pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name, | ||
291 | synth_port_control, synth_port_control+SYNTH_IO_EXTENT-1, | ||
292 | synth->version); | ||
293 | synth->alive = 1; | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static void accent_release(void) | ||
298 | { | ||
299 | if (speakup_info.port_tts) | ||
300 | synth_release_region(speakup_info.port_tts-1, SYNTH_IO_EXTENT); | ||
301 | speakup_info.port_tts = 0; | ||
302 | } | ||
303 | |||
304 | module_param_named(port, port_forced, int, S_IRUGO); | ||
305 | module_param_named(start, synth_acntpc.startup, short, S_IRUGO); | ||
306 | |||
307 | MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); | ||
308 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
309 | |||
310 | static int __init acntpc_init(void) | ||
311 | { | ||
312 | return synth_add(&synth_acntpc); | ||
313 | } | ||
314 | |||
315 | static void __exit acntpc_exit(void) | ||
316 | { | ||
317 | synth_remove(&synth_acntpc); | ||
318 | } | ||
319 | |||
320 | module_init(acntpc_init); | ||
321 | module_exit(acntpc_exit); | ||
322 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
323 | MODULE_AUTHOR("David Borowski"); | ||
324 | MODULE_DESCRIPTION("Speakup support for Accent PC synthesizer"); | ||
325 | MODULE_LICENSE("GPL"); | ||
326 | MODULE_VERSION(DRV_VERSION); | ||
327 | |||
diff --git a/drivers/staging/speakup/speakup_acntsa.c b/drivers/staging/speakup/speakup_acntsa.c new file mode 100644 index 00000000000..0418c9df947 --- /dev/null +++ b/drivers/staging/speakup/speakup_acntsa.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * this code is specificly written as a driver for the speakup screenreview | ||
23 | * package and is not a general device driver. | ||
24 | */ | ||
25 | |||
26 | #include "spk_priv.h" | ||
27 | #include "speakup.h" | ||
28 | #include "speakup_acnt.h" /* local header file for Accent values */ | ||
29 | |||
30 | #define DRV_VERSION "2.11" | ||
31 | #define synth_full() (inb_p(speakup_info.port_tts + UART_RX) == 'F') | ||
32 | #define PROCSPEECH '\r' | ||
33 | |||
34 | static int synth_probe(struct spk_synth *synth); | ||
35 | |||
36 | static struct var_t vars[] = { | ||
37 | { CAPS_START, .u.s = {"\033P8" }}, | ||
38 | { CAPS_STOP, .u.s = {"\033P5" }}, | ||
39 | { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" }}, | ||
40 | { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL }}, | ||
41 | { VOL, .u.n = {"\033A%d", 9, 0, 9, 0, 0, NULL }}, | ||
42 | { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL }}, | ||
43 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
44 | V_LAST_VAR | ||
45 | }; | ||
46 | |||
47 | /* | ||
48 | * These attributes will appear in /sys/accessibility/speakup/acntsa. | ||
49 | */ | ||
50 | static struct kobj_attribute caps_start_attribute = | ||
51 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
52 | static struct kobj_attribute caps_stop_attribute = | ||
53 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
54 | static struct kobj_attribute pitch_attribute = | ||
55 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
56 | static struct kobj_attribute rate_attribute = | ||
57 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
58 | static struct kobj_attribute tone_attribute = | ||
59 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
60 | static struct kobj_attribute vol_attribute = | ||
61 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
62 | |||
63 | static struct kobj_attribute delay_time_attribute = | ||
64 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
65 | static struct kobj_attribute direct_attribute = | ||
66 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
67 | static struct kobj_attribute full_time_attribute = | ||
68 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
69 | static struct kobj_attribute jiffy_delta_attribute = | ||
70 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
71 | static struct kobj_attribute trigger_time_attribute = | ||
72 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
73 | |||
74 | /* | ||
75 | * Create a group of attributes so that we can create and destroy them all | ||
76 | * at once. | ||
77 | */ | ||
78 | static struct attribute *synth_attrs[] = { | ||
79 | &caps_start_attribute.attr, | ||
80 | &caps_stop_attribute.attr, | ||
81 | &pitch_attribute.attr, | ||
82 | &rate_attribute.attr, | ||
83 | &tone_attribute.attr, | ||
84 | &vol_attribute.attr, | ||
85 | &delay_time_attribute.attr, | ||
86 | &direct_attribute.attr, | ||
87 | &full_time_attribute.attr, | ||
88 | &jiffy_delta_attribute.attr, | ||
89 | &trigger_time_attribute.attr, | ||
90 | NULL, /* need to NULL terminate the list of attributes */ | ||
91 | }; | ||
92 | |||
93 | static struct spk_synth synth_acntsa = { | ||
94 | .name = "acntsa", | ||
95 | .version = DRV_VERSION, | ||
96 | .long_name = "Accent-SA", | ||
97 | .init = "\033T2\033=M\033Oi\033N1\n", | ||
98 | .procspeech = PROCSPEECH, | ||
99 | .clear = SYNTH_CLEAR, | ||
100 | .delay = 400, | ||
101 | .trigger = 50, | ||
102 | .jiffies = 30, | ||
103 | .full = 40000, | ||
104 | .startup = SYNTH_START, | ||
105 | .checkval = SYNTH_CHECK, | ||
106 | .vars = vars, | ||
107 | .probe = synth_probe, | ||
108 | .release = spk_serial_release, | ||
109 | .synth_immediate = spk_synth_immediate, | ||
110 | .catch_up = spk_do_catch_up, | ||
111 | .flush = spk_synth_flush, | ||
112 | .is_alive = spk_synth_is_alive_restart, | ||
113 | .synth_adjust = NULL, | ||
114 | .read_buff_add = NULL, | ||
115 | .get_index = NULL, | ||
116 | .indexing = { | ||
117 | .command = NULL, | ||
118 | .lowindex = 0, | ||
119 | .highindex = 0, | ||
120 | .currindex = 0, | ||
121 | }, | ||
122 | .attributes = { | ||
123 | .attrs = synth_attrs, | ||
124 | .name = "acntsa", | ||
125 | }, | ||
126 | }; | ||
127 | |||
128 | static int synth_probe(struct spk_synth *synth) | ||
129 | { | ||
130 | int failed; | ||
131 | |||
132 | failed = serial_synth_probe(synth); | ||
133 | if (failed == 0) { | ||
134 | spk_synth_immediate(synth, "\033=R\r"); | ||
135 | mdelay(100); | ||
136 | } | ||
137 | synth->alive = !failed; | ||
138 | return failed; | ||
139 | } | ||
140 | |||
141 | module_param_named(ser, synth_acntsa.ser, int, S_IRUGO); | ||
142 | module_param_named(start, synth_acntsa.startup, short, S_IRUGO); | ||
143 | |||
144 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
145 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
146 | |||
147 | static int __init acntsa_init(void) | ||
148 | { | ||
149 | return synth_add(&synth_acntsa); | ||
150 | } | ||
151 | |||
152 | static void __exit acntsa_exit(void) | ||
153 | { | ||
154 | synth_remove(&synth_acntsa); | ||
155 | } | ||
156 | |||
157 | module_init(acntsa_init); | ||
158 | module_exit(acntsa_exit); | ||
159 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
160 | MODULE_AUTHOR("David Borowski"); | ||
161 | MODULE_DESCRIPTION("Speakup support for Accent SA synthesizer"); | ||
162 | MODULE_LICENSE("GPL"); | ||
163 | MODULE_VERSION(DRV_VERSION); | ||
164 | |||
diff --git a/drivers/staging/speakup/speakup_apollo.c b/drivers/staging/speakup/speakup_apollo.c new file mode 100644 index 00000000000..502c0324ad2 --- /dev/null +++ b/drivers/staging/speakup/speakup_apollo.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * this code is specificly written as a driver for the speakup screenreview | ||
23 | * package and is not a general device driver. | ||
24 | */ | ||
25 | #include <linux/jiffies.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/kthread.h> | ||
29 | |||
30 | #include "spk_priv.h" | ||
31 | #include "serialio.h" | ||
32 | #include "speakup.h" | ||
33 | |||
34 | #define DRV_VERSION "2.21" | ||
35 | #define SYNTH_CLEAR 0x18 | ||
36 | #define PROCSPEECH '\r' | ||
37 | |||
38 | static void do_catch_up(struct spk_synth *synth); | ||
39 | |||
40 | static struct var_t vars[] = { | ||
41 | { CAPS_START, .u.s = {"cap, " }}, | ||
42 | { CAPS_STOP, .u.s = {"" }}, | ||
43 | { RATE, .u.n = {"@W%d", 6, 1, 9, 0, 0, NULL }}, | ||
44 | { PITCH, .u.n = {"@F%x", 10, 0, 15, 0, 0, NULL }}, | ||
45 | { VOL, .u.n = {"@A%x", 10, 0, 15, 0, 0, NULL }}, | ||
46 | { VOICE, .u.n = {"@V%d", 1, 1, 6, 0, 0, NULL }}, | ||
47 | { LANG, .u.n = {"@=%d,", 1, 1, 4, 0, 0, NULL }}, | ||
48 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
49 | V_LAST_VAR | ||
50 | }; | ||
51 | |||
52 | /* | ||
53 | * These attributes will appear in /sys/accessibility/speakup/apollo. | ||
54 | */ | ||
55 | static struct kobj_attribute caps_start_attribute = | ||
56 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
57 | static struct kobj_attribute caps_stop_attribute = | ||
58 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
59 | static struct kobj_attribute lang_attribute = | ||
60 | __ATTR(lang, USER_RW, spk_var_show, spk_var_store); | ||
61 | static struct kobj_attribute pitch_attribute = | ||
62 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
63 | static struct kobj_attribute rate_attribute = | ||
64 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
65 | static struct kobj_attribute voice_attribute = | ||
66 | __ATTR(voice, USER_RW, spk_var_show, spk_var_store); | ||
67 | static struct kobj_attribute vol_attribute = | ||
68 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
69 | |||
70 | static struct kobj_attribute delay_time_attribute = | ||
71 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
72 | static struct kobj_attribute direct_attribute = | ||
73 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
74 | static struct kobj_attribute full_time_attribute = | ||
75 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
76 | static struct kobj_attribute jiffy_delta_attribute = | ||
77 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
78 | static struct kobj_attribute trigger_time_attribute = | ||
79 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
80 | |||
81 | /* | ||
82 | * Create a group of attributes so that we can create and destroy them all | ||
83 | * at once. | ||
84 | */ | ||
85 | static struct attribute *synth_attrs[] = { | ||
86 | &caps_start_attribute.attr, | ||
87 | &caps_stop_attribute.attr, | ||
88 | &lang_attribute.attr, | ||
89 | &pitch_attribute.attr, | ||
90 | &rate_attribute.attr, | ||
91 | &voice_attribute.attr, | ||
92 | &vol_attribute.attr, | ||
93 | &delay_time_attribute.attr, | ||
94 | &direct_attribute.attr, | ||
95 | &full_time_attribute.attr, | ||
96 | &jiffy_delta_attribute.attr, | ||
97 | &trigger_time_attribute.attr, | ||
98 | NULL, /* need to NULL terminate the list of attributes */ | ||
99 | }; | ||
100 | |||
101 | static struct spk_synth synth_apollo = { | ||
102 | .name = "apollo", | ||
103 | .version = DRV_VERSION, | ||
104 | .long_name = "Apollo", | ||
105 | .init = "@R3@D0@K1\r", | ||
106 | .procspeech = PROCSPEECH, | ||
107 | .clear = SYNTH_CLEAR, | ||
108 | .delay = 500, | ||
109 | .trigger = 50, | ||
110 | .jiffies = 50, | ||
111 | .full = 40000, | ||
112 | .startup = SYNTH_START, | ||
113 | .checkval = SYNTH_CHECK, | ||
114 | .vars = vars, | ||
115 | .probe = serial_synth_probe, | ||
116 | .release = spk_serial_release, | ||
117 | .synth_immediate = spk_synth_immediate, | ||
118 | .catch_up = do_catch_up, | ||
119 | .flush = spk_synth_flush, | ||
120 | .is_alive = spk_synth_is_alive_restart, | ||
121 | .synth_adjust = NULL, | ||
122 | .read_buff_add = NULL, | ||
123 | .get_index = NULL, | ||
124 | .indexing = { | ||
125 | .command = NULL, | ||
126 | .lowindex = 0, | ||
127 | .highindex = 0, | ||
128 | .currindex = 0, | ||
129 | }, | ||
130 | .attributes = { | ||
131 | .attrs = synth_attrs, | ||
132 | .name = "apollo", | ||
133 | }, | ||
134 | }; | ||
135 | |||
136 | static void do_catch_up(struct spk_synth *synth) | ||
137 | { | ||
138 | u_char ch; | ||
139 | unsigned long flags; | ||
140 | unsigned long jiff_max; | ||
141 | struct var_t *jiffy_delta; | ||
142 | struct var_t *delay_time; | ||
143 | struct var_t *full_time; | ||
144 | int full_time_val = 0; | ||
145 | int delay_time_val = 0; | ||
146 | int jiffy_delta_val = 0; | ||
147 | |||
148 | jiffy_delta = get_var(JIFFY); | ||
149 | delay_time = get_var(DELAY); | ||
150 | full_time = get_var(FULL); | ||
151 | spk_lock(flags); | ||
152 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
153 | spk_unlock(flags); | ||
154 | jiff_max = jiffies + jiffy_delta_val; | ||
155 | |||
156 | while (!kthread_should_stop()) { | ||
157 | spk_lock(flags); | ||
158 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
159 | full_time_val = full_time->u.n.value; | ||
160 | delay_time_val = delay_time->u.n.value; | ||
161 | if (speakup_info.flushing) { | ||
162 | speakup_info.flushing = 0; | ||
163 | spk_unlock(flags); | ||
164 | synth->flush(synth); | ||
165 | continue; | ||
166 | } | ||
167 | if (synth_buffer_empty()) { | ||
168 | spk_unlock(flags); | ||
169 | break; | ||
170 | } | ||
171 | ch = synth_buffer_peek(); | ||
172 | set_current_state(TASK_INTERRUPTIBLE); | ||
173 | full_time_val = full_time->u.n.value; | ||
174 | spk_unlock(flags); | ||
175 | if (!spk_serial_out(ch)) { | ||
176 | outb(UART_MCR_DTR, speakup_info.port_tts + UART_MCR); | ||
177 | outb(UART_MCR_DTR | UART_MCR_RTS, | ||
178 | speakup_info.port_tts + UART_MCR); | ||
179 | schedule_timeout(msecs_to_jiffies(full_time_val)); | ||
180 | continue; | ||
181 | } | ||
182 | if ((jiffies >= jiff_max) && (ch == SPACE)) { | ||
183 | spk_lock(flags); | ||
184 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
185 | full_time_val = full_time->u.n.value; | ||
186 | delay_time_val = delay_time->u.n.value; | ||
187 | spk_unlock(flags); | ||
188 | if (spk_serial_out(synth->procspeech)) | ||
189 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
190 | else | ||
191 | schedule_timeout(msecs_to_jiffies(full_time_val)); | ||
192 | jiff_max = jiffies + jiffy_delta_val; | ||
193 | } | ||
194 | set_current_state(TASK_RUNNING); | ||
195 | spk_lock(flags); | ||
196 | synth_buffer_getc(); | ||
197 | spk_unlock(flags); | ||
198 | } | ||
199 | spk_serial_out(PROCSPEECH); | ||
200 | } | ||
201 | |||
202 | module_param_named(ser, synth_apollo.ser, int, S_IRUGO); | ||
203 | module_param_named(start, synth_apollo.startup, short, S_IRUGO); | ||
204 | |||
205 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
206 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
207 | |||
208 | static int __init apollo_init(void) | ||
209 | { | ||
210 | return synth_add(&synth_apollo); | ||
211 | } | ||
212 | |||
213 | static void __exit apollo_exit(void) | ||
214 | { | ||
215 | synth_remove(&synth_apollo); | ||
216 | } | ||
217 | |||
218 | module_init(apollo_init); | ||
219 | module_exit(apollo_exit); | ||
220 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
221 | MODULE_AUTHOR("David Borowski"); | ||
222 | MODULE_DESCRIPTION("Speakup support for Apollo II synthesizer"); | ||
223 | MODULE_LICENSE("GPL"); | ||
224 | MODULE_VERSION(DRV_VERSION); | ||
225 | |||
diff --git a/drivers/staging/speakup/speakup_audptr.c b/drivers/staging/speakup/speakup_audptr.c new file mode 100644 index 00000000000..a194b6f2b5a --- /dev/null +++ b/drivers/staging/speakup/speakup_audptr.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * specificly written as a driver for the speakup screenreview | ||
23 | * s not a general device driver. | ||
24 | */ | ||
25 | #include "spk_priv.h" | ||
26 | #include "speakup.h" | ||
27 | #include "serialio.h" | ||
28 | |||
29 | #define DRV_VERSION "2.11" | ||
30 | #define SYNTH_CLEAR 0x18 /* flush synth buffer */ | ||
31 | #define PROCSPEECH '\r' /* start synth processing speech char */ | ||
32 | |||
33 | static int synth_probe(struct spk_synth *synth); | ||
34 | static void synth_flush(struct spk_synth *synth); | ||
35 | |||
36 | static struct var_t vars[] = { | ||
37 | { CAPS_START, .u.s = {"\x05[f99]" }}, | ||
38 | { CAPS_STOP, .u.s = {"\x05[f80]" }}, | ||
39 | { RATE, .u.n = {"\x05[r%d]", 10, 0, 20, 100, -10, NULL }}, | ||
40 | { PITCH, .u.n = {"\x05[f%d]", 80, 39, 4500, 0, 0, NULL }}, | ||
41 | { VOL, .u.n = {"\x05[g%d]", 21, 0, 40, 0, 0, NULL }}, | ||
42 | { TONE, .u.n = {"\x05[s%d]", 9, 0, 63, 0, 0, 0 }}, | ||
43 | { PUNCT, .u.n = {"\x05[A%c]", 0, 0, 3, 0, 0, "nmsa" }}, | ||
44 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
45 | V_LAST_VAR | ||
46 | }; | ||
47 | |||
48 | /* | ||
49 | * These attributes will appear in /sys/accessibility/speakup/audptr. | ||
50 | */ | ||
51 | static struct kobj_attribute caps_start_attribute = | ||
52 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
53 | static struct kobj_attribute caps_stop_attribute = | ||
54 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
55 | static struct kobj_attribute pitch_attribute = | ||
56 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
57 | static struct kobj_attribute punct_attribute = | ||
58 | __ATTR(punct, USER_RW, spk_var_show, spk_var_store); | ||
59 | static struct kobj_attribute rate_attribute = | ||
60 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
61 | static struct kobj_attribute tone_attribute = | ||
62 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
63 | static struct kobj_attribute vol_attribute = | ||
64 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
65 | |||
66 | static struct kobj_attribute delay_time_attribute = | ||
67 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
68 | static struct kobj_attribute direct_attribute = | ||
69 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
70 | static struct kobj_attribute full_time_attribute = | ||
71 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
72 | static struct kobj_attribute jiffy_delta_attribute = | ||
73 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
74 | static struct kobj_attribute trigger_time_attribute = | ||
75 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
76 | |||
77 | /* | ||
78 | * Create a group of attributes so that we can create and destroy them all | ||
79 | * at once. | ||
80 | */ | ||
81 | static struct attribute *synth_attrs[] = { | ||
82 | &caps_start_attribute.attr, | ||
83 | &caps_stop_attribute.attr, | ||
84 | &pitch_attribute.attr, | ||
85 | &punct_attribute.attr, | ||
86 | &rate_attribute.attr, | ||
87 | &tone_attribute.attr, | ||
88 | &vol_attribute.attr, | ||
89 | &delay_time_attribute.attr, | ||
90 | &direct_attribute.attr, | ||
91 | &full_time_attribute.attr, | ||
92 | &jiffy_delta_attribute.attr, | ||
93 | &trigger_time_attribute.attr, | ||
94 | NULL, /* need to NULL terminate the list of attributes */ | ||
95 | }; | ||
96 | |||
97 | static struct spk_synth synth_audptr = { | ||
98 | .name = "audptr", | ||
99 | .version = DRV_VERSION, | ||
100 | .long_name = "Audapter", | ||
101 | .init = "\x05[D1]\x05[Ol]", | ||
102 | .procspeech = PROCSPEECH, | ||
103 | .clear = SYNTH_CLEAR, | ||
104 | .delay = 400, | ||
105 | .trigger = 50, | ||
106 | .jiffies = 30, | ||
107 | .full = 18000, | ||
108 | .startup = SYNTH_START, | ||
109 | .checkval = SYNTH_CHECK, | ||
110 | .vars = vars, | ||
111 | .probe = synth_probe, | ||
112 | .release = spk_serial_release, | ||
113 | .synth_immediate = spk_synth_immediate, | ||
114 | .catch_up = spk_do_catch_up, | ||
115 | .flush = synth_flush, | ||
116 | .is_alive = spk_synth_is_alive_restart, | ||
117 | .synth_adjust = NULL, | ||
118 | .read_buff_add = NULL, | ||
119 | .get_index = NULL, | ||
120 | .indexing = { | ||
121 | .command = NULL, | ||
122 | .lowindex = 0, | ||
123 | .highindex = 0, | ||
124 | .currindex = 0, | ||
125 | }, | ||
126 | .attributes = { | ||
127 | .attrs = synth_attrs, | ||
128 | .name = "audptr", | ||
129 | }, | ||
130 | }; | ||
131 | |||
132 | static void synth_flush(struct spk_synth *synth) | ||
133 | { | ||
134 | int timeout = SPK_XMITR_TIMEOUT; | ||
135 | while (spk_serial_tx_busy()) { | ||
136 | if (!--timeout) | ||
137 | break; | ||
138 | udelay(1); | ||
139 | } | ||
140 | outb(SYNTH_CLEAR, speakup_info.port_tts); | ||
141 | spk_serial_out(PROCSPEECH); | ||
142 | } | ||
143 | |||
144 | static void synth_version(struct spk_synth *synth) | ||
145 | { | ||
146 | unsigned char test = 0; | ||
147 | char synth_id[40] = ""; | ||
148 | spk_synth_immediate(synth, "\x05[Q]"); | ||
149 | synth_id[test] = spk_serial_in(); | ||
150 | if (synth_id[test] == 'A') { | ||
151 | do { | ||
152 | /* read version string from synth */ | ||
153 | synth_id[++test] = spk_serial_in(); | ||
154 | } while (synth_id[test] != '\n' && test < 32); | ||
155 | synth_id[++test] = 0x00; | ||
156 | } | ||
157 | if (synth_id[0] == 'A') | ||
158 | pr_info("%s version: %s", synth->long_name, synth_id); | ||
159 | } | ||
160 | |||
161 | static int synth_probe(struct spk_synth *synth) | ||
162 | { | ||
163 | int failed = 0; | ||
164 | |||
165 | failed = serial_synth_probe(synth); | ||
166 | if (failed == 0) | ||
167 | synth_version(synth); | ||
168 | synth->alive = !failed; | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | module_param_named(ser, synth_audptr.ser, int, S_IRUGO); | ||
173 | module_param_named(start, synth_audptr.startup, short, S_IRUGO); | ||
174 | |||
175 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
176 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
177 | |||
178 | static int __init audptr_init(void) | ||
179 | { | ||
180 | return synth_add(&synth_audptr); | ||
181 | } | ||
182 | |||
183 | static void __exit audptr_exit(void) | ||
184 | { | ||
185 | synth_remove(&synth_audptr); | ||
186 | } | ||
187 | |||
188 | module_init(audptr_init); | ||
189 | module_exit(audptr_exit); | ||
190 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
191 | MODULE_AUTHOR("David Borowski"); | ||
192 | MODULE_DESCRIPTION("Speakup support for Audapter synthesizer"); | ||
193 | MODULE_LICENSE("GPL"); | ||
194 | MODULE_VERSION(DRV_VERSION); | ||
195 | |||
diff --git a/drivers/staging/speakup/speakup_bns.c b/drivers/staging/speakup/speakup_bns.c new file mode 100644 index 00000000000..e1b19f7de7c --- /dev/null +++ b/drivers/staging/speakup/speakup_bns.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * this code is specificly written as a driver for the speakup screenreview | ||
23 | * package and is not a general device driver. | ||
24 | */ | ||
25 | #include "spk_priv.h" | ||
26 | #include "speakup.h" | ||
27 | |||
28 | #define DRV_VERSION "2.11" | ||
29 | #define SYNTH_CLEAR 0x18 | ||
30 | #define PROCSPEECH '\r' | ||
31 | |||
32 | static struct var_t vars[] = { | ||
33 | { CAPS_START, .u.s = {"\x05\x31\x32P" }}, | ||
34 | { CAPS_STOP, .u.s = {"\x05\x38P" }}, | ||
35 | { RATE, .u.n = {"\x05%dE", 8, 1, 16, 0, 0, NULL }}, | ||
36 | { PITCH, .u.n = {"\x05%dP", 8, 0, 16, 0, 0, NULL }}, | ||
37 | { VOL, .u.n = {"\x05%dV", 8, 0, 16, 0, 0, NULL }}, | ||
38 | { TONE, .u.n = {"\x05%dT", 8, 0, 16, 0, 0, NULL }}, | ||
39 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
40 | V_LAST_VAR | ||
41 | }; | ||
42 | |||
43 | /* | ||
44 | * These attributes will appear in /sys/accessibility/speakup/bns. | ||
45 | */ | ||
46 | static struct kobj_attribute caps_start_attribute = | ||
47 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
48 | static struct kobj_attribute caps_stop_attribute = | ||
49 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
50 | static struct kobj_attribute pitch_attribute = | ||
51 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
52 | static struct kobj_attribute rate_attribute = | ||
53 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
54 | static struct kobj_attribute tone_attribute = | ||
55 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
56 | static struct kobj_attribute vol_attribute = | ||
57 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
58 | |||
59 | static struct kobj_attribute delay_time_attribute = | ||
60 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
61 | static struct kobj_attribute direct_attribute = | ||
62 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
63 | static struct kobj_attribute full_time_attribute = | ||
64 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
65 | static struct kobj_attribute jiffy_delta_attribute = | ||
66 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
67 | static struct kobj_attribute trigger_time_attribute = | ||
68 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
69 | |||
70 | /* | ||
71 | * Create a group of attributes so that we can create and destroy them all | ||
72 | * at once. | ||
73 | */ | ||
74 | static struct attribute *synth_attrs[] = { | ||
75 | &caps_start_attribute.attr, | ||
76 | &caps_stop_attribute.attr, | ||
77 | &pitch_attribute.attr, | ||
78 | &rate_attribute.attr, | ||
79 | &tone_attribute.attr, | ||
80 | &vol_attribute.attr, | ||
81 | &delay_time_attribute.attr, | ||
82 | &direct_attribute.attr, | ||
83 | &full_time_attribute.attr, | ||
84 | &jiffy_delta_attribute.attr, | ||
85 | &trigger_time_attribute.attr, | ||
86 | NULL, /* need to NULL terminate the list of attributes */ | ||
87 | }; | ||
88 | |||
89 | static struct spk_synth synth_bns = { | ||
90 | .name = "bns", | ||
91 | .version = DRV_VERSION, | ||
92 | .long_name = "Braille 'N Speak", | ||
93 | .init = "\x05Z\x05\x43", | ||
94 | .procspeech = PROCSPEECH, | ||
95 | .clear = SYNTH_CLEAR, | ||
96 | .delay = 500, | ||
97 | .trigger = 50, | ||
98 | .jiffies = 50, | ||
99 | .full = 40000, | ||
100 | .startup = SYNTH_START, | ||
101 | .checkval = SYNTH_CHECK, | ||
102 | .vars = vars, | ||
103 | .probe = serial_synth_probe, | ||
104 | .release = spk_serial_release, | ||
105 | .synth_immediate = spk_synth_immediate, | ||
106 | .catch_up = spk_do_catch_up, | ||
107 | .flush = spk_synth_flush, | ||
108 | .is_alive = spk_synth_is_alive_restart, | ||
109 | .synth_adjust = NULL, | ||
110 | .read_buff_add = NULL, | ||
111 | .get_index = NULL, | ||
112 | .indexing = { | ||
113 | .command = NULL, | ||
114 | .lowindex = 0, | ||
115 | .highindex = 0, | ||
116 | .currindex = 0, | ||
117 | }, | ||
118 | .attributes = { | ||
119 | .attrs = synth_attrs, | ||
120 | .name = "bns", | ||
121 | }, | ||
122 | }; | ||
123 | |||
124 | module_param_named(ser, synth_bns.ser, int, S_IRUGO); | ||
125 | module_param_named(start, synth_bns.startup, short, S_IRUGO); | ||
126 | |||
127 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
128 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
129 | |||
130 | static int __init bns_init(void) | ||
131 | { | ||
132 | return synth_add(&synth_bns); | ||
133 | } | ||
134 | |||
135 | static void __exit bns_exit(void) | ||
136 | { | ||
137 | synth_remove(&synth_bns); | ||
138 | } | ||
139 | |||
140 | module_init(bns_init); | ||
141 | module_exit(bns_exit); | ||
142 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
143 | MODULE_AUTHOR("David Borowski"); | ||
144 | MODULE_DESCRIPTION("Speakup support for Braille 'n Speak synthesizers"); | ||
145 | MODULE_LICENSE("GPL"); | ||
146 | MODULE_VERSION(DRV_VERSION); | ||
147 | |||
diff --git a/drivers/staging/speakup/speakup_decext.c b/drivers/staging/speakup/speakup_decext.c new file mode 100644 index 00000000000..351bd86d42b --- /dev/null +++ b/drivers/staging/speakup/speakup_decext.c | |||
@@ -0,0 +1,242 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * specificly written as a driver for the speakup screenreview | ||
23 | * s not a general device driver. | ||
24 | */ | ||
25 | #include <linux/jiffies.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/kthread.h> | ||
29 | |||
30 | #include "spk_priv.h" | ||
31 | #include "serialio.h" | ||
32 | #include "speakup.h" | ||
33 | |||
34 | #define DRV_VERSION "2.14" | ||
35 | #define SYNTH_CLEAR 0x03 | ||
36 | #define PROCSPEECH 0x0b | ||
37 | static unsigned char last_char; | ||
38 | #define get_last_char() ((inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)? \ | ||
39 | (last_char = inb_p(speakup_info.port_tts + UART_RX)) : last_char) | ||
40 | #define synth_full() (get_last_char() == 0x13) | ||
41 | |||
42 | static void do_catch_up(struct spk_synth *synth); | ||
43 | static void synth_flush(struct spk_synth *synth); | ||
44 | |||
45 | static int in_escape; | ||
46 | |||
47 | static struct var_t vars[] = { | ||
48 | { CAPS_START, .u.s = {"[:dv ap 222]" }}, | ||
49 | { CAPS_STOP, .u.s = {"[:dv ap 100]" }}, | ||
50 | { RATE, .u.n = {"[:ra %d]", 7, 0, 9, 150, 25, NULL }}, | ||
51 | { PITCH, .u.n = {"[:dv ap %d]", 100, 0, 100, 0, 0, NULL }}, | ||
52 | { VOL, .u.n = {"[:dv gv %d]", 13, 0, 16, 0, 5, NULL }}, | ||
53 | { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" }}, | ||
54 | { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" }}, | ||
55 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
56 | V_LAST_VAR | ||
57 | }; | ||
58 | |||
59 | /* | ||
60 | * These attributes will appear in /sys/accessibility/speakup/decext. | ||
61 | */ | ||
62 | static struct kobj_attribute caps_start_attribute = | ||
63 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
64 | static struct kobj_attribute caps_stop_attribute = | ||
65 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
66 | static struct kobj_attribute pitch_attribute = | ||
67 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
68 | static struct kobj_attribute punct_attribute = | ||
69 | __ATTR(punct, USER_RW, spk_var_show, spk_var_store); | ||
70 | static struct kobj_attribute rate_attribute = | ||
71 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
72 | static struct kobj_attribute voice_attribute = | ||
73 | __ATTR(voice, USER_RW, spk_var_show, spk_var_store); | ||
74 | static struct kobj_attribute vol_attribute = | ||
75 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
76 | |||
77 | static struct kobj_attribute delay_time_attribute = | ||
78 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
79 | static struct kobj_attribute direct_attribute = | ||
80 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
81 | static struct kobj_attribute full_time_attribute = | ||
82 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
83 | static struct kobj_attribute jiffy_delta_attribute = | ||
84 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
85 | static struct kobj_attribute trigger_time_attribute = | ||
86 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
87 | |||
88 | /* | ||
89 | * Create a group of attributes so that we can create and destroy them all | ||
90 | * at once. | ||
91 | */ | ||
92 | static struct attribute *synth_attrs[] = { | ||
93 | &caps_start_attribute.attr, | ||
94 | &caps_stop_attribute.attr, | ||
95 | &pitch_attribute.attr, | ||
96 | &punct_attribute.attr, | ||
97 | &rate_attribute.attr, | ||
98 | &voice_attribute.attr, | ||
99 | &vol_attribute.attr, | ||
100 | &delay_time_attribute.attr, | ||
101 | &direct_attribute.attr, | ||
102 | &full_time_attribute.attr, | ||
103 | &jiffy_delta_attribute.attr, | ||
104 | &trigger_time_attribute.attr, | ||
105 | NULL, /* need to NULL terminate the list of attributes */ | ||
106 | }; | ||
107 | |||
108 | static struct spk_synth synth_decext = { | ||
109 | .name = "decext", | ||
110 | .version = DRV_VERSION, | ||
111 | .long_name = "Dectalk External", | ||
112 | .init = "[:pe -380]", | ||
113 | .procspeech = PROCSPEECH, | ||
114 | .clear = SYNTH_CLEAR, | ||
115 | .delay = 500, | ||
116 | .trigger = 50, | ||
117 | .jiffies = 50, | ||
118 | .full = 40000, | ||
119 | .flags = SF_DEC, | ||
120 | .startup = SYNTH_START, | ||
121 | .checkval = SYNTH_CHECK, | ||
122 | .vars = vars, | ||
123 | .probe = serial_synth_probe, | ||
124 | .release = spk_serial_release, | ||
125 | .synth_immediate = spk_synth_immediate, | ||
126 | .catch_up = do_catch_up, | ||
127 | .flush = synth_flush, | ||
128 | .is_alive = spk_synth_is_alive_restart, | ||
129 | .synth_adjust = NULL, | ||
130 | .read_buff_add = NULL, | ||
131 | .get_index = NULL, | ||
132 | .indexing = { | ||
133 | .command = NULL, | ||
134 | .lowindex = 0, | ||
135 | .highindex = 0, | ||
136 | .currindex = 0, | ||
137 | }, | ||
138 | .attributes = { | ||
139 | .attrs = synth_attrs, | ||
140 | .name = "decext", | ||
141 | }, | ||
142 | }; | ||
143 | |||
144 | static void do_catch_up(struct spk_synth *synth) | ||
145 | { | ||
146 | u_char ch; | ||
147 | static u_char last = '\0'; | ||
148 | unsigned long flags; | ||
149 | unsigned long jiff_max; | ||
150 | struct var_t *jiffy_delta; | ||
151 | struct var_t *delay_time; | ||
152 | int jiffy_delta_val = 0; | ||
153 | int delay_time_val = 0; | ||
154 | |||
155 | jiffy_delta = get_var(JIFFY); | ||
156 | delay_time = get_var(DELAY); | ||
157 | |||
158 | spk_lock(flags); | ||
159 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
160 | spk_unlock(flags); | ||
161 | jiff_max = jiffies + jiffy_delta_val; | ||
162 | |||
163 | while (!kthread_should_stop()) { | ||
164 | spk_lock(flags); | ||
165 | if (speakup_info.flushing) { | ||
166 | speakup_info.flushing = 0; | ||
167 | spk_unlock(flags); | ||
168 | synth->flush(synth); | ||
169 | continue; | ||
170 | } | ||
171 | if (synth_buffer_empty()) { | ||
172 | spk_unlock(flags); | ||
173 | break; | ||
174 | } | ||
175 | ch = synth_buffer_peek(); | ||
176 | set_current_state(TASK_INTERRUPTIBLE); | ||
177 | delay_time_val = delay_time->u.n.value; | ||
178 | spk_unlock(flags); | ||
179 | if (ch == '\n') | ||
180 | ch = 0x0D; | ||
181 | if (synth_full() || !spk_serial_out(ch)) { | ||
182 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
183 | continue; | ||
184 | } | ||
185 | set_current_state(TASK_RUNNING); | ||
186 | spk_lock(flags); | ||
187 | synth_buffer_getc(); | ||
188 | spk_unlock(flags); | ||
189 | if (ch == '[') | ||
190 | in_escape = 1; | ||
191 | else if (ch == ']') | ||
192 | in_escape = 0; | ||
193 | else if (ch <= SPACE) { | ||
194 | if (!in_escape && strchr(",.!?;:", last)) | ||
195 | spk_serial_out(PROCSPEECH); | ||
196 | if (jiffies >= jiff_max) { | ||
197 | if ( ! in_escape ) | ||
198 | spk_serial_out(PROCSPEECH); | ||
199 | spk_lock(flags); | ||
200 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
201 | delay_time_val = delay_time->u.n.value; | ||
202 | spk_unlock(flags); | ||
203 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
204 | jiff_max = jiffies + jiffy_delta_val; | ||
205 | } | ||
206 | } | ||
207 | last = ch; | ||
208 | } | ||
209 | if (!in_escape) | ||
210 | spk_serial_out(PROCSPEECH); | ||
211 | } | ||
212 | |||
213 | static void synth_flush(struct spk_synth *synth) | ||
214 | { | ||
215 | in_escape = 0; | ||
216 | spk_synth_immediate(synth, "\033P;10z\033\\"); | ||
217 | } | ||
218 | |||
219 | module_param_named(ser, synth_decext.ser, int, S_IRUGO); | ||
220 | module_param_named(start, synth_decext.startup, short, S_IRUGO); | ||
221 | |||
222 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
223 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
224 | |||
225 | static int __init decext_init(void) | ||
226 | { | ||
227 | return synth_add(&synth_decext); | ||
228 | } | ||
229 | |||
230 | static void __exit decext_exit(void) | ||
231 | { | ||
232 | synth_remove(&synth_decext); | ||
233 | } | ||
234 | |||
235 | module_init(decext_init); | ||
236 | module_exit(decext_exit); | ||
237 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
238 | MODULE_AUTHOR("David Borowski"); | ||
239 | MODULE_DESCRIPTION("Speakup support for DECtalk External synthesizers"); | ||
240 | MODULE_LICENSE("GPL"); | ||
241 | MODULE_VERSION(DRV_VERSION); | ||
242 | |||
diff --git a/drivers/staging/speakup/speakup_decpc.c b/drivers/staging/speakup/speakup_decpc.c new file mode 100644 index 00000000000..490b758fb4e --- /dev/null +++ b/drivers/staging/speakup/speakup_decpc.c | |||
@@ -0,0 +1,503 @@ | |||
1 | /* | ||
2 | * This is the DECtalk PC speakup driver | ||
3 | * | ||
4 | * Some constants from DEC's DOS driver: | ||
5 | * Copyright (c) by Digital Equipment Corp. | ||
6 | * | ||
7 | * 386BSD DECtalk PC driver: | ||
8 | * Copyright (c) 1996 Brian Buhrow <buhrow@lothlorien.nfbcal.org> | ||
9 | * | ||
10 | * Linux DECtalk PC driver: | ||
11 | * Copyright (c) 1997 Nicolas Pitre <nico@cam.org> | ||
12 | * | ||
13 | * speakup DECtalk PC Internal driver: | ||
14 | * Copyright (c) 2003 David Borowski <david575@golden.net> | ||
15 | * | ||
16 | * All rights reserved. | ||
17 | * | ||
18 | * This program is free software; you can redistribute it and/or modify | ||
19 | * it under the terms of the GNU General Public License as published by | ||
20 | * the Free Software Foundation; either version 2 of the License, or | ||
21 | * (at your option) any later version. | ||
22 | * | ||
23 | * This program is distributed in the hope that it will be useful, | ||
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
26 | * GNU General Public License for more details. | ||
27 | * | ||
28 | * You should have received a copy of the GNU General Public License | ||
29 | * along with this program; if not, write to the Free Software | ||
30 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
31 | */ | ||
32 | #include <linux/jiffies.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/timer.h> | ||
35 | #include <linux/kthread.h> | ||
36 | |||
37 | #include "spk_priv.h" | ||
38 | #include "speakup.h" | ||
39 | |||
40 | #define MODULE_init 0x0dec /* module in boot code */ | ||
41 | #define MODULE_self_test 0x8800 /* module in self-test */ | ||
42 | #define MODULE_reset 0xffff /* reinit the whole module */ | ||
43 | |||
44 | #define MODE_mask 0xf000 /* mode bits in high nibble */ | ||
45 | #define MODE_null 0x0000 | ||
46 | #define MODE_test 0x2000 /* in testing mode */ | ||
47 | #define MODE_status 0x8000 | ||
48 | #define STAT_int 0x0001 /* running in interrupt mode */ | ||
49 | #define STAT_tr_char 0x0002 /* character data to transmit */ | ||
50 | #define STAT_rr_char 0x0004 /* ready to receive char data */ | ||
51 | #define STAT_cmd_ready 0x0008 /* ready to accept commands */ | ||
52 | #define STAT_dma_ready 0x0010 /* dma command ready */ | ||
53 | #define STAT_digitized 0x0020 /* spc in digitized mode */ | ||
54 | #define STAT_new_index 0x0040 /* new last index ready */ | ||
55 | #define STAT_new_status 0x0080 /* new status posted */ | ||
56 | #define STAT_dma_state 0x0100 /* dma state toggle */ | ||
57 | #define STAT_index_valid 0x0200 /* indexs are valid */ | ||
58 | #define STAT_flushing 0x0400 /* flush in progress */ | ||
59 | #define STAT_self_test 0x0800 /* module in self test */ | ||
60 | #define MODE_ready 0xc000 /* module ready for next phase */ | ||
61 | #define READY_boot 0x0000 | ||
62 | #define READY_kernel 0x0001 | ||
63 | #define MODE_error 0xf000 | ||
64 | |||
65 | #define CMD_mask 0xf000 /* mask for command nibble */ | ||
66 | #define CMD_null 0x0000 /* post status */ | ||
67 | #define CMD_control 0x1000 /* hard control command */ | ||
68 | #define CTRL_mask 0x0F00 /* mask off control nibble */ | ||
69 | #define CTRL_data 0x00FF /* madk to get data byte */ | ||
70 | #define CTRL_null 0x0000 /* null control */ | ||
71 | #define CTRL_vol_up 0x0100 /* increase volume */ | ||
72 | #define CTRL_vol_down 0x0200 /* decrease volume */ | ||
73 | #define CTRL_vol_set 0x0300 /* set volume */ | ||
74 | #define CTRL_pause 0x0400 /* pause spc */ | ||
75 | #define CTRL_resume 0x0500 /* resume spc clock */ | ||
76 | #define CTRL_resume_spc 0x0001 /* resume spc soft pause */ | ||
77 | #define CTRL_flush 0x0600 /* flush all buffers */ | ||
78 | #define CTRL_int_enable 0x0700 /* enable status change ints */ | ||
79 | #define CTRL_buff_free 0x0800 /* buffer remain count */ | ||
80 | #define CTRL_buff_used 0x0900 /* buffer in use */ | ||
81 | #define CTRL_speech 0x0a00 /* immediate speech change */ | ||
82 | #define CTRL_SP_voice 0x0001 /* voice change */ | ||
83 | #define CTRL_SP_rate 0x0002 /* rate change */ | ||
84 | #define CTRL_SP_comma 0x0003 /* comma pause change */ | ||
85 | #define CTRL_SP_period 0x0004 /* period pause change */ | ||
86 | #define CTRL_SP_rate_delta 0x0005 /* delta rate change */ | ||
87 | #define CTRL_SP_get_param 0x0006 /* return the desired parameter */ | ||
88 | #define CTRL_last_index 0x0b00 /* get last index spoken */ | ||
89 | #define CTRL_io_priority 0x0c00 /* change i/o priority */ | ||
90 | #define CTRL_free_mem 0x0d00 /* get free paragraphs on module */ | ||
91 | #define CTRL_get_lang 0x0e00 /* return bit mask of loaded | ||
92 | * languages */ | ||
93 | #define CMD_test 0x2000 /* self-test request */ | ||
94 | #define TEST_mask 0x0F00 /* isolate test field */ | ||
95 | #define TEST_null 0x0000 /* no test requested */ | ||
96 | #define TEST_isa_int 0x0100 /* assert isa irq */ | ||
97 | #define TEST_echo 0x0200 /* make data in == data out */ | ||
98 | #define TEST_seg 0x0300 /* set peek/poke segment */ | ||
99 | #define TEST_off 0x0400 /* set peek/poke offset */ | ||
100 | #define TEST_peek 0x0500 /* data out == *peek */ | ||
101 | #define TEST_poke 0x0600 /* *peek == data in */ | ||
102 | #define TEST_sub_code 0x00FF /* user defined test sub codes */ | ||
103 | #define CMD_id 0x3000 /* return software id */ | ||
104 | #define ID_null 0x0000 /* null id */ | ||
105 | #define ID_kernel 0x0100 /* kernel code executing */ | ||
106 | #define ID_boot 0x0200 /* boot code executing */ | ||
107 | #define CMD_dma 0x4000 /* force a dma start */ | ||
108 | #define CMD_reset 0x5000 /* reset module status */ | ||
109 | #define CMD_sync 0x6000 /* kernel sync command */ | ||
110 | #define CMD_char_in 0x7000 /* single character send */ | ||
111 | #define CMD_char_out 0x8000 /* single character get */ | ||
112 | #define CHAR_count_1 0x0100 /* one char in cmd_low */ | ||
113 | #define CHAR_count_2 0x0200 /* the second in data_low */ | ||
114 | #define CHAR_count_3 0x0300 /* the third in data_high */ | ||
115 | #define CMD_spc_mode 0x9000 /* change spc mode */ | ||
116 | #define CMD_spc_to_text 0x0100 /* set to text mode */ | ||
117 | #define CMD_spc_to_digit 0x0200 /* set to digital mode */ | ||
118 | #define CMD_spc_rate 0x0400 /* change spc data rate */ | ||
119 | #define CMD_error 0xf000 /* severe error */ | ||
120 | |||
121 | enum { PRIMARY_DIC = 0, USER_DIC, COMMAND_DIC, ABBREV_DIC }; | ||
122 | |||
123 | #define DMA_single_in 0x01 | ||
124 | #define DMA_single_out 0x02 | ||
125 | #define DMA_buff_in 0x03 | ||
126 | #define DMA_buff_out 0x04 | ||
127 | #define DMA_control 0x05 | ||
128 | #define DT_MEM_ALLOC 0x03 | ||
129 | #define DT_SET_DIC 0x04 | ||
130 | #define DT_START_TASK 0x05 | ||
131 | #define DT_LOAD_MEM 0x06 | ||
132 | #define DT_READ_MEM 0x07 | ||
133 | #define DT_DIGITAL_IN 0x08 | ||
134 | #define DMA_sync 0x06 | ||
135 | #define DMA_sync_char 0x07 | ||
136 | |||
137 | #define DRV_VERSION "2.12" | ||
138 | #define PROCSPEECH 0x0b | ||
139 | #define SYNTH_IO_EXTENT 8 | ||
140 | |||
141 | static int synth_probe(struct spk_synth *synth); | ||
142 | static void dtpc_release(void); | ||
143 | static const char *synth_immediate(struct spk_synth *synth, const char *buf); | ||
144 | static void do_catch_up(struct spk_synth *synth); | ||
145 | static void synth_flush(struct spk_synth *synth); | ||
146 | |||
147 | static int synth_portlist[] = { 0x340, 0x350, 0x240, 0x250, 0 }; | ||
148 | static int in_escape, is_flushing; | ||
149 | static int dt_stat, dma_state; | ||
150 | |||
151 | static struct var_t vars[] = { | ||
152 | { CAPS_START, .u.s = {"[:dv ap 200]" }}, | ||
153 | { CAPS_STOP, .u.s = {"[:dv ap 100]" }}, | ||
154 | { RATE, .u.n = {"[:ra %d]", 9, 0, 18, 150, 25, NULL }}, | ||
155 | { PITCH, .u.n = {"[:dv ap %d]", 80, 0, 100, 20, 0, NULL }}, | ||
156 | { VOL, .u.n = {"[:vo se %d]", 5, 0, 9, 5, 10, NULL }}, | ||
157 | { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" }}, | ||
158 | { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" }}, | ||
159 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
160 | V_LAST_VAR | ||
161 | }; | ||
162 | |||
163 | /* | ||
164 | * These attributes will appear in /sys/accessibility/speakup/decpc. | ||
165 | */ | ||
166 | static struct kobj_attribute caps_start_attribute = | ||
167 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
168 | static struct kobj_attribute caps_stop_attribute = | ||
169 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
170 | static struct kobj_attribute pitch_attribute = | ||
171 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
172 | static struct kobj_attribute punct_attribute = | ||
173 | __ATTR(punct, USER_RW, spk_var_show, spk_var_store); | ||
174 | static struct kobj_attribute rate_attribute = | ||
175 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
176 | static struct kobj_attribute voice_attribute = | ||
177 | __ATTR(voice, USER_RW, spk_var_show, spk_var_store); | ||
178 | static struct kobj_attribute vol_attribute = | ||
179 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
180 | |||
181 | static struct kobj_attribute delay_time_attribute = | ||
182 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
183 | static struct kobj_attribute direct_attribute = | ||
184 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
185 | static struct kobj_attribute full_time_attribute = | ||
186 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
187 | static struct kobj_attribute jiffy_delta_attribute = | ||
188 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
189 | static struct kobj_attribute trigger_time_attribute = | ||
190 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
191 | |||
192 | /* | ||
193 | * Create a group of attributes so that we can create and destroy them all | ||
194 | * at once. | ||
195 | */ | ||
196 | static struct attribute *synth_attrs[] = { | ||
197 | &caps_start_attribute.attr, | ||
198 | &caps_stop_attribute.attr, | ||
199 | &pitch_attribute.attr, | ||
200 | &punct_attribute.attr, | ||
201 | &rate_attribute.attr, | ||
202 | &voice_attribute.attr, | ||
203 | &vol_attribute.attr, | ||
204 | &delay_time_attribute.attr, | ||
205 | &direct_attribute.attr, | ||
206 | &full_time_attribute.attr, | ||
207 | &jiffy_delta_attribute.attr, | ||
208 | &trigger_time_attribute.attr, | ||
209 | NULL, /* need to NULL terminate the list of attributes */ | ||
210 | }; | ||
211 | |||
212 | static struct spk_synth synth_dec_pc = { | ||
213 | .name = "decpc", | ||
214 | .version = DRV_VERSION, | ||
215 | .long_name = "Dectalk PC", | ||
216 | .init = "[:pe -380]", | ||
217 | .procspeech = PROCSPEECH, | ||
218 | .delay = 500, | ||
219 | .trigger = 50, | ||
220 | .jiffies = 50, | ||
221 | .full = 1000, | ||
222 | .flags = SF_DEC, | ||
223 | .startup = SYNTH_START, | ||
224 | .checkval = SYNTH_CHECK, | ||
225 | .vars = vars, | ||
226 | .probe = synth_probe, | ||
227 | .release = dtpc_release, | ||
228 | .synth_immediate = synth_immediate, | ||
229 | .catch_up = do_catch_up, | ||
230 | .flush = synth_flush, | ||
231 | .is_alive = spk_synth_is_alive_nop, | ||
232 | .synth_adjust = NULL, | ||
233 | .read_buff_add = NULL, | ||
234 | .get_index = NULL, | ||
235 | .indexing = { | ||
236 | .command = NULL, | ||
237 | .lowindex = 0, | ||
238 | .highindex = 0, | ||
239 | .currindex = 0, | ||
240 | }, | ||
241 | .attributes = { | ||
242 | .attrs = synth_attrs, | ||
243 | .name = "decpc", | ||
244 | }, | ||
245 | }; | ||
246 | |||
247 | static int dt_getstatus(void) | ||
248 | { | ||
249 | dt_stat = inb_p(speakup_info.port_tts) | | ||
250 | (inb_p(speakup_info.port_tts + 1) << 8); | ||
251 | return dt_stat; | ||
252 | } | ||
253 | |||
254 | static void dt_sendcmd(u_int cmd) | ||
255 | { | ||
256 | outb_p(cmd & 0xFF, speakup_info.port_tts); | ||
257 | outb_p((cmd >> 8) & 0xFF, speakup_info.port_tts+1); | ||
258 | } | ||
259 | |||
260 | static int dt_waitbit(int bit) | ||
261 | { | ||
262 | int timeout = 100; | ||
263 | while (--timeout > 0) { | ||
264 | if ((dt_getstatus() & bit) == bit) | ||
265 | return 1; | ||
266 | udelay(50); | ||
267 | } | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int dt_wait_dma(void) | ||
272 | { | ||
273 | int timeout = 100, state = dma_state; | ||
274 | if (!dt_waitbit(STAT_dma_ready)) | ||
275 | return 0; | ||
276 | while (--timeout > 0) { | ||
277 | if ((dt_getstatus()&STAT_dma_state) == state) | ||
278 | return 1; | ||
279 | udelay(50); | ||
280 | } | ||
281 | dma_state = dt_getstatus() & STAT_dma_state; | ||
282 | return 1; | ||
283 | } | ||
284 | |||
285 | static int dt_ctrl(u_int cmd) | ||
286 | { | ||
287 | int timeout = 10; | ||
288 | if (!dt_waitbit(STAT_cmd_ready)) | ||
289 | return -1; | ||
290 | outb_p(0, speakup_info.port_tts+2); | ||
291 | outb_p(0, speakup_info.port_tts+3); | ||
292 | dt_getstatus(); | ||
293 | dt_sendcmd(CMD_control|cmd); | ||
294 | outb_p(0, speakup_info.port_tts+6); | ||
295 | while (dt_getstatus() & STAT_cmd_ready) { | ||
296 | udelay(20); | ||
297 | if (--timeout == 0) | ||
298 | break; | ||
299 | } | ||
300 | dt_sendcmd(CMD_null); | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static void synth_flush(struct spk_synth *synth) | ||
305 | { | ||
306 | int timeout = 10; | ||
307 | if (is_flushing) | ||
308 | return; | ||
309 | is_flushing = 4; | ||
310 | in_escape = 0; | ||
311 | while (dt_ctrl(CTRL_flush)) { | ||
312 | if (--timeout == 0) | ||
313 | break; | ||
314 | udelay(50); | ||
315 | } | ||
316 | for (timeout = 0; timeout < 10; timeout++) { | ||
317 | if (dt_waitbit(STAT_dma_ready)) | ||
318 | break; | ||
319 | udelay(50); | ||
320 | } | ||
321 | outb_p(DMA_sync, speakup_info.port_tts+4); | ||
322 | outb_p(0, speakup_info.port_tts+4); | ||
323 | udelay(100); | ||
324 | for (timeout = 0; timeout < 10; timeout++) { | ||
325 | if (!(dt_getstatus() & STAT_flushing)) | ||
326 | break; | ||
327 | udelay(50); | ||
328 | } | ||
329 | dma_state = dt_getstatus() & STAT_dma_state; | ||
330 | dma_state ^= STAT_dma_state; | ||
331 | is_flushing = 0; | ||
332 | } | ||
333 | |||
334 | static int dt_sendchar(char ch) | ||
335 | { | ||
336 | if (!dt_wait_dma()) | ||
337 | return -1; | ||
338 | if (!(dt_stat & STAT_rr_char)) | ||
339 | return -2; | ||
340 | outb_p(DMA_single_in, speakup_info.port_tts+4); | ||
341 | outb_p(ch, speakup_info.port_tts+4); | ||
342 | dma_state ^= STAT_dma_state; | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static int testkernel(void) | ||
347 | { | ||
348 | int status = 0; | ||
349 | if (dt_getstatus() == 0xffff) { | ||
350 | status = -1; | ||
351 | goto oops; | ||
352 | } | ||
353 | dt_sendcmd(CMD_sync); | ||
354 | if (!dt_waitbit(STAT_cmd_ready)) | ||
355 | status = -2; | ||
356 | else if (dt_stat&0x8000) | ||
357 | return 0; | ||
358 | else if (dt_stat == 0x0dec) | ||
359 | pr_warn("dec_pc at 0x%x, software not loaded\n", | ||
360 | speakup_info.port_tts); | ||
361 | status = -3; | ||
362 | oops: synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT); | ||
363 | speakup_info.port_tts = 0; | ||
364 | return status; | ||
365 | } | ||
366 | |||
367 | static void do_catch_up(struct spk_synth *synth) | ||
368 | { | ||
369 | u_char ch; | ||
370 | static u_char last = '\0'; | ||
371 | unsigned long flags; | ||
372 | unsigned long jiff_max; | ||
373 | struct var_t *jiffy_delta; | ||
374 | struct var_t *delay_time; | ||
375 | int jiffy_delta_val; | ||
376 | int delay_time_val; | ||
377 | |||
378 | jiffy_delta = get_var(JIFFY); | ||
379 | delay_time = get_var(DELAY); | ||
380 | spk_lock(flags); | ||
381 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
382 | spk_unlock(flags); | ||
383 | jiff_max = jiffies + jiffy_delta_val; | ||
384 | |||
385 | while (!kthread_should_stop()) { | ||
386 | spk_lock(flags); | ||
387 | if (speakup_info.flushing) { | ||
388 | speakup_info.flushing = 0; | ||
389 | spk_unlock(flags); | ||
390 | synth->flush(synth); | ||
391 | continue; | ||
392 | } | ||
393 | if (synth_buffer_empty()) { | ||
394 | spk_unlock(flags); | ||
395 | break; | ||
396 | } | ||
397 | ch = synth_buffer_peek(); | ||
398 | set_current_state(TASK_INTERRUPTIBLE); | ||
399 | delay_time_val = delay_time->u.n.value; | ||
400 | spk_unlock(flags); | ||
401 | if (ch == '\n') | ||
402 | ch = 0x0D; | ||
403 | if (dt_sendchar(ch)) { | ||
404 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
405 | continue; | ||
406 | } | ||
407 | set_current_state(TASK_RUNNING); | ||
408 | spk_lock(flags); | ||
409 | synth_buffer_getc(); | ||
410 | spk_unlock(flags); | ||
411 | if (ch == '[') | ||
412 | in_escape = 1; | ||
413 | else if (ch == ']') | ||
414 | in_escape = 0; | ||
415 | else if (ch <= SPACE) { | ||
416 | if (!in_escape && strchr(",.!?;:", last)) | ||
417 | dt_sendchar(PROCSPEECH); | ||
418 | if (jiffies >= jiff_max) { | ||
419 | if (!in_escape) | ||
420 | dt_sendchar(PROCSPEECH); | ||
421 | spk_lock(flags); | ||
422 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
423 | delay_time_val = delay_time->u.n.value; | ||
424 | spk_unlock(flags); | ||
425 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
426 | jiff_max = jiffies + jiffy_delta_val; | ||
427 | } | ||
428 | } | ||
429 | last = ch; | ||
430 | ch = 0; | ||
431 | } | ||
432 | if (!in_escape) | ||
433 | dt_sendchar(PROCSPEECH); | ||
434 | } | ||
435 | |||
436 | static const char *synth_immediate(struct spk_synth *synth, const char *buf) | ||
437 | { | ||
438 | u_char ch; | ||
439 | while ((ch = *buf)) { | ||
440 | if (ch == '\n') | ||
441 | ch = PROCSPEECH; | ||
442 | if (dt_sendchar(ch)) | ||
443 | return buf; | ||
444 | buf++; | ||
445 | } | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int synth_probe(struct spk_synth *synth) | ||
450 | { | ||
451 | int i = 0, failed = 0; | ||
452 | pr_info("Probing for %s.\n", synth->long_name); | ||
453 | for (i = 0; synth_portlist[i]; i++) { | ||
454 | if (synth_request_region(synth_portlist[i], SYNTH_IO_EXTENT)) { | ||
455 | pr_warn("request_region: failed with 0x%x, %d\n", | ||
456 | synth_portlist[i], SYNTH_IO_EXTENT); | ||
457 | continue; | ||
458 | } | ||
459 | speakup_info.port_tts = synth_portlist[i]; | ||
460 | failed = testkernel(); | ||
461 | if (failed == 0) | ||
462 | break; | ||
463 | } | ||
464 | if (failed) { | ||
465 | pr_info("%s: not found\n", synth->long_name); | ||
466 | return -ENODEV; | ||
467 | } | ||
468 | pr_info("%s: %03x-%03x, Driver Version %s,\n", synth->long_name, | ||
469 | speakup_info.port_tts, speakup_info.port_tts + 7, | ||
470 | synth->version); | ||
471 | synth->alive = 1; | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static void dtpc_release(void) | ||
476 | { | ||
477 | if (speakup_info.port_tts) | ||
478 | synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT); | ||
479 | speakup_info.port_tts = 0; | ||
480 | } | ||
481 | |||
482 | module_param_named(start, synth_dec_pc.startup, short, S_IRUGO); | ||
483 | |||
484 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
485 | |||
486 | static int __init decpc_init(void) | ||
487 | { | ||
488 | return synth_add(&synth_dec_pc); | ||
489 | } | ||
490 | |||
491 | static void __exit decpc_exit(void) | ||
492 | { | ||
493 | synth_remove(&synth_dec_pc); | ||
494 | } | ||
495 | |||
496 | module_init(decpc_init); | ||
497 | module_exit(decpc_exit); | ||
498 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
499 | MODULE_AUTHOR("David Borowski"); | ||
500 | MODULE_DESCRIPTION("Speakup support for DECtalk PC synthesizers"); | ||
501 | MODULE_LICENSE("GPL"); | ||
502 | MODULE_VERSION(DRV_VERSION); | ||
503 | |||
diff --git a/drivers/staging/speakup/speakup_dectlk.c b/drivers/staging/speakup/speakup_dectlk.c new file mode 100644 index 00000000000..c17e9bc3422 --- /dev/null +++ b/drivers/staging/speakup/speakup_dectlk.c | |||
@@ -0,0 +1,315 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * specificly written as a driver for the speakup screenreview | ||
23 | * s not a general device driver. | ||
24 | */ | ||
25 | #include <linux/unistd.h> | ||
26 | #include <linux/proc_fs.h> | ||
27 | #include <linux/jiffies.h> | ||
28 | #include <linux/spinlock.h> | ||
29 | #include <linux/sched.h> | ||
30 | #include <linux/timer.h> | ||
31 | #include <linux/kthread.h> | ||
32 | #include "speakup.h" | ||
33 | #include "spk_priv.h" | ||
34 | #include "serialio.h" | ||
35 | |||
36 | #define DRV_VERSION "2.20" | ||
37 | #define SYNTH_CLEAR 0x03 | ||
38 | #define PROCSPEECH 0x0b | ||
39 | static volatile int xoff; | ||
40 | #define synth_full() (xoff) | ||
41 | |||
42 | static void do_catch_up(struct spk_synth *synth); | ||
43 | static void synth_flush(struct spk_synth *synth); | ||
44 | static void read_buff_add(u_char c); | ||
45 | static unsigned char get_index(void); | ||
46 | |||
47 | static int in_escape; | ||
48 | static int is_flushing; | ||
49 | |||
50 | static spinlock_t flush_lock; | ||
51 | static DECLARE_WAIT_QUEUE_HEAD(flush); | ||
52 | |||
53 | static struct var_t vars[] = { | ||
54 | { CAPS_START, .u.s = {"[:dv ap 160] " }}, | ||
55 | { CAPS_STOP, .u.s = {"[:dv ap 100 ] " }}, | ||
56 | { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL }}, | ||
57 | { PITCH, .u.n = {"[:dv ap %d] ", 122, 50, 350, 0, 0, NULL }}, | ||
58 | { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL }}, | ||
59 | { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" }}, | ||
60 | { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" }}, | ||
61 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
62 | V_LAST_VAR | ||
63 | }; | ||
64 | |||
65 | /* | ||
66 | * These attributes will appear in /sys/accessibility/speakup/dectlk. | ||
67 | */ | ||
68 | static struct kobj_attribute caps_start_attribute = | ||
69 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
70 | static struct kobj_attribute caps_stop_attribute = | ||
71 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
72 | static struct kobj_attribute pitch_attribute = | ||
73 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
74 | static struct kobj_attribute punct_attribute = | ||
75 | __ATTR(punct, USER_RW, spk_var_show, spk_var_store); | ||
76 | static struct kobj_attribute rate_attribute = | ||
77 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
78 | static struct kobj_attribute voice_attribute = | ||
79 | __ATTR(voice, USER_RW, spk_var_show, spk_var_store); | ||
80 | static struct kobj_attribute vol_attribute = | ||
81 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
82 | |||
83 | static struct kobj_attribute delay_time_attribute = | ||
84 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
85 | static struct kobj_attribute direct_attribute = | ||
86 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
87 | static struct kobj_attribute full_time_attribute = | ||
88 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
89 | static struct kobj_attribute jiffy_delta_attribute = | ||
90 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
91 | static struct kobj_attribute trigger_time_attribute = | ||
92 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
93 | |||
94 | /* | ||
95 | * Create a group of attributes so that we can create and destroy them all | ||
96 | * at once. | ||
97 | */ | ||
98 | static struct attribute *synth_attrs[] = { | ||
99 | &caps_start_attribute.attr, | ||
100 | &caps_stop_attribute.attr, | ||
101 | &pitch_attribute.attr, | ||
102 | &punct_attribute.attr, | ||
103 | &rate_attribute.attr, | ||
104 | &voice_attribute.attr, | ||
105 | &vol_attribute.attr, | ||
106 | &delay_time_attribute.attr, | ||
107 | &direct_attribute.attr, | ||
108 | &full_time_attribute.attr, | ||
109 | &jiffy_delta_attribute.attr, | ||
110 | &trigger_time_attribute.attr, | ||
111 | NULL, /* need to NULL terminate the list of attributes */ | ||
112 | }; | ||
113 | |||
114 | static int ap_defaults[] = {122, 89, 155, 110, 208, 240, 200, 106, 306}; | ||
115 | static int g5_defaults[] = {86, 81, 86, 84, 81, 80, 83, 83, 73}; | ||
116 | |||
117 | static struct spk_synth synth_dectlk = { | ||
118 | .name = "dectlk", | ||
119 | .version = DRV_VERSION, | ||
120 | .long_name = "Dectalk Express", | ||
121 | .init = "[:error sp :name paul :rate 180 :tsr off] ", | ||
122 | .procspeech = PROCSPEECH, | ||
123 | .clear = SYNTH_CLEAR, | ||
124 | .delay = 500, | ||
125 | .trigger = 50, | ||
126 | .jiffies = 50, | ||
127 | .full = 40000, | ||
128 | .startup = SYNTH_START, | ||
129 | .checkval = SYNTH_CHECK, | ||
130 | .vars = vars, | ||
131 | .default_pitch = ap_defaults, | ||
132 | .default_vol = g5_defaults, | ||
133 | .probe = serial_synth_probe, | ||
134 | .release = spk_serial_release, | ||
135 | .synth_immediate = spk_synth_immediate, | ||
136 | .catch_up = do_catch_up, | ||
137 | .flush = synth_flush, | ||
138 | .is_alive = spk_synth_is_alive_restart, | ||
139 | .synth_adjust = NULL, | ||
140 | .read_buff_add = read_buff_add, | ||
141 | .get_index = get_index, | ||
142 | .indexing = { | ||
143 | .command = "[:in re %d ] ", | ||
144 | .lowindex = 1, | ||
145 | .highindex = 8, | ||
146 | .currindex = 1, | ||
147 | }, | ||
148 | .attributes = { | ||
149 | .attrs = synth_attrs, | ||
150 | .name = "dectlk", | ||
151 | }, | ||
152 | }; | ||
153 | |||
154 | static int is_indnum(u_char *ch) | ||
155 | { | ||
156 | if ((*ch >= '0') && (*ch <= '9')) { | ||
157 | *ch = *ch - '0'; | ||
158 | return 1; | ||
159 | } | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static u_char lastind = 0; | ||
164 | |||
165 | static unsigned char get_index(void) | ||
166 | { | ||
167 | u_char rv; | ||
168 | rv = lastind; | ||
169 | lastind = 0; | ||
170 | return rv; | ||
171 | } | ||
172 | |||
173 | static void read_buff_add(u_char c) | ||
174 | { | ||
175 | static int ind = -1; | ||
176 | |||
177 | if (c == 0x01) { | ||
178 | unsigned long flags; | ||
179 | spin_lock_irqsave(&flush_lock, flags); | ||
180 | is_flushing = 0; | ||
181 | wake_up_interruptible(&flush); | ||
182 | spin_unlock_irqrestore(&flush_lock, flags); | ||
183 | } else if (c == 0x13) { | ||
184 | xoff = 1; | ||
185 | } else if (c == 0x11) { | ||
186 | xoff = 0; | ||
187 | } else if (is_indnum(&c)) { | ||
188 | if (ind == -1) | ||
189 | ind = c; | ||
190 | else | ||
191 | ind = ind * 10 + c; | ||
192 | } else if ((c > 31) && (c < 127)) { | ||
193 | if (ind != -1) | ||
194 | lastind = (u_char)ind; | ||
195 | ind = -1; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static void do_catch_up(struct spk_synth *synth) | ||
200 | { | ||
201 | static u_char ch = 0; | ||
202 | static u_char last = '\0'; | ||
203 | unsigned long flags; | ||
204 | unsigned long jiff_max; | ||
205 | unsigned long timeout = msecs_to_jiffies(4000); | ||
206 | DEFINE_WAIT(wait); | ||
207 | struct var_t *jiffy_delta; | ||
208 | struct var_t *delay_time; | ||
209 | int jiffy_delta_val; | ||
210 | int delay_time_val; | ||
211 | |||
212 | jiffy_delta = get_var(JIFFY); | ||
213 | delay_time = get_var(DELAY); | ||
214 | spk_lock(flags); | ||
215 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
216 | spk_unlock(flags); | ||
217 | jiff_max = jiffies + jiffy_delta_val; | ||
218 | |||
219 | while (!kthread_should_stop()) { | ||
220 | /* if no ctl-a in 4, send data anyway */ | ||
221 | spin_lock_irqsave(&flush_lock, flags); | ||
222 | while (is_flushing && timeout) { | ||
223 | prepare_to_wait(&flush, &wait, TASK_INTERRUPTIBLE); | ||
224 | spin_unlock_irqrestore(&flush_lock, flags); | ||
225 | timeout = schedule_timeout(timeout); | ||
226 | spin_lock_irqsave(&flush_lock, flags); | ||
227 | } | ||
228 | finish_wait(&flush, &wait); | ||
229 | is_flushing = 0; | ||
230 | spin_unlock_irqrestore(&flush_lock, flags); | ||
231 | |||
232 | spk_lock(flags); | ||
233 | if (speakup_info.flushing) { | ||
234 | speakup_info.flushing = 0; | ||
235 | spk_unlock(flags); | ||
236 | synth->flush(synth); | ||
237 | continue; | ||
238 | } | ||
239 | if (synth_buffer_empty()) { | ||
240 | spk_unlock(flags); | ||
241 | break; | ||
242 | } | ||
243 | ch = synth_buffer_peek(); | ||
244 | set_current_state(TASK_INTERRUPTIBLE); | ||
245 | delay_time_val = delay_time->u.n.value; | ||
246 | spk_unlock(flags); | ||
247 | if (ch == '\n') | ||
248 | ch = 0x0D; | ||
249 | if (synth_full() || !spk_serial_out(ch)) { | ||
250 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
251 | continue; | ||
252 | } | ||
253 | set_current_state(TASK_RUNNING); | ||
254 | spk_lock(flags); | ||
255 | synth_buffer_getc(); | ||
256 | spk_unlock(flags); | ||
257 | if (ch == '[') | ||
258 | in_escape = 1; | ||
259 | else if (ch == ']') | ||
260 | in_escape = 0; | ||
261 | else if (ch <= SPACE) { | ||
262 | if (!in_escape && strchr(",.!?;:", last)) | ||
263 | spk_serial_out(PROCSPEECH); | ||
264 | if (jiffies >= jiff_max) { | ||
265 | if ( ! in_escape ) | ||
266 | spk_serial_out(PROCSPEECH); | ||
267 | spk_lock(flags); | ||
268 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
269 | delay_time_val = delay_time->u.n.value; | ||
270 | spk_unlock(flags); | ||
271 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
272 | jiff_max = jiffies + jiffy_delta_val; | ||
273 | } | ||
274 | } | ||
275 | last = ch; | ||
276 | } | ||
277 | if (!in_escape) | ||
278 | spk_serial_out(PROCSPEECH); | ||
279 | } | ||
280 | |||
281 | static void synth_flush(struct spk_synth *synth) | ||
282 | { | ||
283 | if (in_escape) { | ||
284 | /* if in command output ']' so we don't get an error */ | ||
285 | spk_serial_out(']'); | ||
286 | } | ||
287 | in_escape = 0; | ||
288 | is_flushing = 1; | ||
289 | spk_serial_out(SYNTH_CLEAR); | ||
290 | } | ||
291 | |||
292 | module_param_named(ser, synth_dectlk.ser, int, S_IRUGO); | ||
293 | module_param_named(start, synth_dectlk.startup, short, S_IRUGO); | ||
294 | |||
295 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
296 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
297 | |||
298 | static int __init dectlk_init(void) | ||
299 | { | ||
300 | return synth_add(&synth_dectlk); | ||
301 | } | ||
302 | |||
303 | static void __exit dectlk_exit(void) | ||
304 | { | ||
305 | synth_remove(&synth_dectlk); | ||
306 | } | ||
307 | |||
308 | module_init(dectlk_init); | ||
309 | module_exit(dectlk_exit); | ||
310 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
311 | MODULE_AUTHOR("David Borowski"); | ||
312 | MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers"); | ||
313 | MODULE_LICENSE("GPL"); | ||
314 | MODULE_VERSION(DRV_VERSION); | ||
315 | |||
diff --git a/drivers/staging/speakup/speakup_dtlk.c b/drivers/staging/speakup/speakup_dtlk.c new file mode 100644 index 00000000000..b907c4f27e8 --- /dev/null +++ b/drivers/staging/speakup/speakup_dtlk.c | |||
@@ -0,0 +1,386 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * specificly written as a driver for the speakup screenreview | ||
23 | * package it's not a general device driver. | ||
24 | * This driver is for the RC Systems DoubleTalk PC internal synthesizer. | ||
25 | */ | ||
26 | #include <linux/jiffies.h> | ||
27 | #include <linux/sched.h> | ||
28 | #include <linux/timer.h> | ||
29 | #include <linux/kthread.h> | ||
30 | |||
31 | #include "spk_priv.h" | ||
32 | #include "serialio.h" | ||
33 | #include "speakup_dtlk.h" /* local header file for DoubleTalk values */ | ||
34 | #include "speakup.h" | ||
35 | |||
36 | #define DRV_VERSION "2.10" | ||
37 | #define PROCSPEECH 0x00 | ||
38 | #define synth_readable() ((synth_status = inb_p(speakup_info.port_tts + UART_RX)) & TTS_READABLE) | ||
39 | #define synth_writable() ((synth_status = inb_p(speakup_info.port_tts + UART_RX)) & TTS_WRITABLE) | ||
40 | #define synth_full() ((synth_status = inb_p(speakup_info.port_tts + UART_RX)) & TTS_ALMOST_FULL) | ||
41 | |||
42 | static int synth_probe(struct spk_synth *synth); | ||
43 | static void dtlk_release(void); | ||
44 | static const char *synth_immediate(struct spk_synth *synth, const char *buf); | ||
45 | static void do_catch_up(struct spk_synth *synth); | ||
46 | static void synth_flush(struct spk_synth *synth); | ||
47 | |||
48 | static int synth_lpc; | ||
49 | static int port_forced; | ||
50 | static unsigned int synth_portlist[] = | ||
51 | { 0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0 }; | ||
52 | static u_char synth_status; | ||
53 | |||
54 | static struct var_t vars[] = { | ||
55 | { CAPS_START, .u.s = {"\x01+35p" }}, | ||
56 | { CAPS_STOP, .u.s = {"\x01-35p" }}, | ||
57 | { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL }}, | ||
58 | { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL }}, | ||
59 | { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL }}, | ||
60 | { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL }}, | ||
61 | { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL }}, | ||
62 | { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL }}, | ||
63 | { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL }}, | ||
64 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
65 | V_LAST_VAR | ||
66 | }; | ||
67 | |||
68 | /* | ||
69 | * These attributes will appear in /sys/accessibility/speakup/dtlk. | ||
70 | */ | ||
71 | static struct kobj_attribute caps_start_attribute = | ||
72 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
73 | static struct kobj_attribute caps_stop_attribute = | ||
74 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
75 | static struct kobj_attribute freq_attribute = | ||
76 | __ATTR(freq, USER_RW, spk_var_show, spk_var_store); | ||
77 | static struct kobj_attribute pitch_attribute = | ||
78 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
79 | static struct kobj_attribute punct_attribute = | ||
80 | __ATTR(punct, USER_RW, spk_var_show, spk_var_store); | ||
81 | static struct kobj_attribute rate_attribute = | ||
82 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
83 | static struct kobj_attribute tone_attribute = | ||
84 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
85 | static struct kobj_attribute voice_attribute = | ||
86 | __ATTR(voice, USER_RW, spk_var_show, spk_var_store); | ||
87 | static struct kobj_attribute vol_attribute = | ||
88 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
89 | |||
90 | static struct kobj_attribute delay_time_attribute = | ||
91 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
92 | static struct kobj_attribute direct_attribute = | ||
93 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
94 | static struct kobj_attribute full_time_attribute = | ||
95 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
96 | static struct kobj_attribute jiffy_delta_attribute = | ||
97 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
98 | static struct kobj_attribute trigger_time_attribute = | ||
99 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
100 | |||
101 | /* | ||
102 | * Create a group of attributes so that we can create and destroy them all | ||
103 | * at once. | ||
104 | */ | ||
105 | static struct attribute *synth_attrs[] = { | ||
106 | &caps_start_attribute.attr, | ||
107 | &caps_stop_attribute.attr, | ||
108 | &freq_attribute.attr, | ||
109 | &pitch_attribute.attr, | ||
110 | &punct_attribute.attr, | ||
111 | &rate_attribute.attr, | ||
112 | &tone_attribute.attr, | ||
113 | &voice_attribute.attr, | ||
114 | &vol_attribute.attr, | ||
115 | &delay_time_attribute.attr, | ||
116 | &direct_attribute.attr, | ||
117 | &full_time_attribute.attr, | ||
118 | &jiffy_delta_attribute.attr, | ||
119 | &trigger_time_attribute.attr, | ||
120 | NULL, /* need to NULL terminate the list of attributes */ | ||
121 | }; | ||
122 | |||
123 | static struct spk_synth synth_dtlk = { | ||
124 | .name = "dtlk", | ||
125 | .version = DRV_VERSION, | ||
126 | .long_name = "DoubleTalk PC", | ||
127 | .init = "\x01@\x01\x31y", | ||
128 | .procspeech = PROCSPEECH, | ||
129 | .clear = SYNTH_CLEAR, | ||
130 | .delay = 500, | ||
131 | .trigger = 30, | ||
132 | .jiffies = 50, | ||
133 | .full = 1000, | ||
134 | .startup = SYNTH_START, | ||
135 | .checkval = SYNTH_CHECK, | ||
136 | .vars = vars, | ||
137 | .probe = synth_probe, | ||
138 | .release = dtlk_release, | ||
139 | .synth_immediate = synth_immediate, | ||
140 | .catch_up = do_catch_up, | ||
141 | .flush = synth_flush, | ||
142 | .is_alive = spk_synth_is_alive_nop, | ||
143 | .synth_adjust = NULL, | ||
144 | .read_buff_add = NULL, | ||
145 | .get_index = spk_serial_in_nowait, | ||
146 | .indexing = { | ||
147 | .command = "\x01%di", | ||
148 | .lowindex = 1, | ||
149 | .highindex = 5, | ||
150 | .currindex = 1, | ||
151 | }, | ||
152 | .attributes = { | ||
153 | .attrs = synth_attrs, | ||
154 | .name = "dtlk", | ||
155 | }, | ||
156 | }; | ||
157 | |||
158 | static void spk_out(const char ch) | ||
159 | { | ||
160 | int timeout = SPK_XMITR_TIMEOUT; | ||
161 | while (synth_writable() == 0) { | ||
162 | if (!--timeout) | ||
163 | break; | ||
164 | udelay(1); | ||
165 | } | ||
166 | outb_p(ch, speakup_info.port_tts); | ||
167 | timeout = SPK_XMITR_TIMEOUT; | ||
168 | while (synth_writable() != 0) { | ||
169 | if (!--timeout) | ||
170 | break; | ||
171 | udelay(1); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | static void do_catch_up(struct spk_synth *synth) | ||
176 | { | ||
177 | u_char ch; | ||
178 | unsigned long flags; | ||
179 | unsigned long jiff_max; | ||
180 | struct var_t *jiffy_delta; | ||
181 | struct var_t *delay_time; | ||
182 | int jiffy_delta_val; | ||
183 | int delay_time_val; | ||
184 | |||
185 | jiffy_delta = get_var(JIFFY); | ||
186 | delay_time = get_var(DELAY); | ||
187 | spk_lock(flags); | ||
188 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
189 | spk_unlock(flags); | ||
190 | jiff_max = jiffies + jiffy_delta_val; | ||
191 | while (!kthread_should_stop()) { | ||
192 | spk_lock(flags); | ||
193 | if (speakup_info.flushing) { | ||
194 | speakup_info.flushing = 0; | ||
195 | spk_unlock(flags); | ||
196 | synth->flush(synth); | ||
197 | continue; | ||
198 | } | ||
199 | if (synth_buffer_empty()) { | ||
200 | spk_unlock(flags); | ||
201 | break; | ||
202 | } | ||
203 | set_current_state(TASK_INTERRUPTIBLE); | ||
204 | delay_time_val = delay_time->u.n.value; | ||
205 | spk_unlock(flags); | ||
206 | if (synth_full()) { | ||
207 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
208 | continue; | ||
209 | } | ||
210 | set_current_state(TASK_RUNNING); | ||
211 | spk_lock(flags); | ||
212 | ch = synth_buffer_getc(); | ||
213 | spk_unlock(flags); | ||
214 | if (ch == '\n') | ||
215 | ch = PROCSPEECH; | ||
216 | spk_out(ch); | ||
217 | if ((jiffies >= jiff_max) && (ch == SPACE)) { | ||
218 | spk_out(PROCSPEECH); | ||
219 | spk_lock(flags); | ||
220 | delay_time_val = delay_time->u.n.value; | ||
221 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
222 | spk_unlock(flags); | ||
223 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
224 | jiff_max = jiffies + jiffy_delta_val; | ||
225 | } | ||
226 | } | ||
227 | spk_out(PROCSPEECH); | ||
228 | } | ||
229 | |||
230 | static const char *synth_immediate(struct spk_synth *synth, const char *buf) | ||
231 | { | ||
232 | u_char ch; | ||
233 | while ((ch = (u_char)*buf)) { | ||
234 | if (synth_full()) | ||
235 | return buf; | ||
236 | if (ch == '\n') | ||
237 | ch = PROCSPEECH; | ||
238 | spk_out(ch); | ||
239 | buf++; | ||
240 | } | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static void synth_flush(struct spk_synth *synth) | ||
245 | { | ||
246 | outb_p(SYNTH_CLEAR, speakup_info.port_tts); | ||
247 | while (synth_writable() != 0) | ||
248 | cpu_relax(); | ||
249 | } | ||
250 | |||
251 | static char synth_read_tts(void) | ||
252 | { | ||
253 | u_char ch; | ||
254 | while (synth_readable() == 0) | ||
255 | cpu_relax(); | ||
256 | ch = synth_status & 0x7f; | ||
257 | outb_p(ch, speakup_info.port_tts); | ||
258 | while (synth_readable() != 0) | ||
259 | cpu_relax(); | ||
260 | return (char) ch; | ||
261 | } | ||
262 | |||
263 | /* interrogate the DoubleTalk PC and return its settings */ | ||
264 | static struct synth_settings *synth_interrogate(struct spk_synth *synth) | ||
265 | { | ||
266 | u_char *t; | ||
267 | static char buf[sizeof(struct synth_settings) + 1]; | ||
268 | int total, i; | ||
269 | static struct synth_settings status; | ||
270 | synth_immediate(synth, "\x18\x01?"); | ||
271 | for (total = 0, i = 0; i < 50; i++) { | ||
272 | buf[total] = synth_read_tts(); | ||
273 | if (total > 2 && buf[total] == 0x7f) | ||
274 | break; | ||
275 | if (total < sizeof(struct synth_settings)) | ||
276 | total++; | ||
277 | } | ||
278 | t = buf; | ||
279 | /* serial number is little endian */ | ||
280 | status.serial_number = t[0] + t[1]*256; | ||
281 | t += 2; | ||
282 | for (i = 0; *t != '\r'; t++) { | ||
283 | status.rom_version[i] = *t; | ||
284 | if (i < sizeof(status.rom_version)-1) | ||
285 | i++; | ||
286 | } | ||
287 | status.rom_version[i] = 0; | ||
288 | t++; | ||
289 | status.mode = *t++; | ||
290 | status.punc_level = *t++; | ||
291 | status.formant_freq = *t++; | ||
292 | status.pitch = *t++; | ||
293 | status.speed = *t++; | ||
294 | status.volume = *t++; | ||
295 | status.tone = *t++; | ||
296 | status.expression = *t++; | ||
297 | status.ext_dict_loaded = *t++; | ||
298 | status.ext_dict_status = *t++; | ||
299 | status.free_ram = *t++; | ||
300 | status.articulation = *t++; | ||
301 | status.reverb = *t++; | ||
302 | status.eob = *t++; | ||
303 | return &status; | ||
304 | } | ||
305 | |||
306 | static int synth_probe(struct spk_synth *synth) | ||
307 | { | ||
308 | unsigned int port_val = 0; | ||
309 | int i = 0; | ||
310 | struct synth_settings *sp; | ||
311 | pr_info("Probing for DoubleTalk.\n"); | ||
312 | if (port_forced) { | ||
313 | speakup_info.port_tts = port_forced; | ||
314 | pr_info("probe forced to %x by kernel command line\n", | ||
315 | speakup_info.port_tts); | ||
316 | if ((port_forced & 0xf) != 0xf) | ||
317 | pr_info("warning: port base should probably end with f\n"); | ||
318 | if (synth_request_region(speakup_info.port_tts-1, | ||
319 | SYNTH_IO_EXTENT)) { | ||
320 | pr_warn("sorry, port already reserved\n"); | ||
321 | return -EBUSY; | ||
322 | } | ||
323 | port_val = inw(speakup_info.port_tts-1); | ||
324 | synth_lpc = speakup_info.port_tts-1; | ||
325 | } else { | ||
326 | for (i = 0; synth_portlist[i]; i++) { | ||
327 | if (synth_request_region(synth_portlist[i], | ||
328 | SYNTH_IO_EXTENT)) | ||
329 | continue; | ||
330 | port_val = inw(synth_portlist[i]) & 0xfbff; | ||
331 | if (port_val == 0x107f) { | ||
332 | synth_lpc = synth_portlist[i]; | ||
333 | speakup_info.port_tts = synth_lpc+1; | ||
334 | break; | ||
335 | } | ||
336 | synth_release_region(synth_portlist[i], | ||
337 | SYNTH_IO_EXTENT); | ||
338 | } | ||
339 | } | ||
340 | port_val &= 0xfbff; | ||
341 | if (port_val != 0x107f) { | ||
342 | pr_info("DoubleTalk PC: not found\n"); | ||
343 | synth_release_region(synth_lpc, SYNTH_IO_EXTENT); | ||
344 | return -ENODEV; | ||
345 | } | ||
346 | while (inw_p(synth_lpc) != 0x147f) | ||
347 | cpu_relax(); /* wait until it's ready */ | ||
348 | sp = synth_interrogate(synth); | ||
349 | pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n", | ||
350 | synth->long_name, synth_lpc, synth_lpc+SYNTH_IO_EXTENT - 1, | ||
351 | sp->rom_version, sp->serial_number, synth->version); | ||
352 | synth->alive = 1; | ||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static void dtlk_release(void) | ||
357 | { | ||
358 | if (speakup_info.port_tts) | ||
359 | synth_release_region(speakup_info.port_tts-1, SYNTH_IO_EXTENT); | ||
360 | speakup_info.port_tts = 0; | ||
361 | } | ||
362 | |||
363 | module_param_named(port, port_forced, int, S_IRUGO); | ||
364 | module_param_named(start, synth_dtlk.startup, short, S_IRUGO); | ||
365 | |||
366 | MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); | ||
367 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
368 | |||
369 | static int __init dtlk_init(void) | ||
370 | { | ||
371 | return synth_add(&synth_dtlk); | ||
372 | } | ||
373 | |||
374 | static void __exit dtlk_exit(void) | ||
375 | { | ||
376 | synth_remove(&synth_dtlk); | ||
377 | } | ||
378 | |||
379 | module_init(dtlk_init); | ||
380 | module_exit(dtlk_exit); | ||
381 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
382 | MODULE_AUTHOR("David Borowski"); | ||
383 | MODULE_DESCRIPTION("Speakup support for DoubleTalk PC synthesizers"); | ||
384 | MODULE_LICENSE("GPL"); | ||
385 | MODULE_VERSION(DRV_VERSION); | ||
386 | |||
diff --git a/drivers/staging/speakup/speakup_dtlk.h b/drivers/staging/speakup/speakup_dtlk.h new file mode 100644 index 00000000000..d951d18c579 --- /dev/null +++ b/drivers/staging/speakup/speakup_dtlk.h | |||
@@ -0,0 +1,54 @@ | |||
1 | /* speakup_dtlk.h - header file for speakups DoubleTalk driver. */ | ||
2 | |||
3 | #define SYNTH_IO_EXTENT 0x02 | ||
4 | #define SYNTH_CLEAR 0x18 /* stops speech */ | ||
5 | /* TTS Port Status Flags */ | ||
6 | #define TTS_READABLE 0x80 /* mask for bit which is nonzero if a | ||
7 | byte can be read from the TTS port */ | ||
8 | #define TTS_SPEAKING 0x40 /* mask for SYNC bit, which is nonzero | ||
9 | while DoubleTalk is producing | ||
10 | output with TTS, PCM or CVSD | ||
11 | synthesizers or tone generators | ||
12 | (that is, all but LPC) */ | ||
13 | #define TTS_SPEAKING2 0x20 /* mask for SYNC2 bit, | ||
14 | which falls to zero up to 0.4 sec | ||
15 | before speech stops */ | ||
16 | #define TTS_WRITABLE 0x10 /* mask for RDY bit, which when set to | ||
17 | 1, indicates the TTS port is ready | ||
18 | to accept a byte of data. The RDY | ||
19 | bit goes zero 2-3 usec after | ||
20 | writing, and goes 1 again 180-190 | ||
21 | usec later. */ | ||
22 | #define TTS_ALMOST_FULL 0x08 /* mask for AF bit: When set to 1, | ||
23 | indicates that less than 300 bytes | ||
24 | are available in the TTS input | ||
25 | buffer. AF is always 0 in the PCM, | ||
26 | TGN and CVSD modes. */ | ||
27 | #define TTS_ALMOST_EMPTY 0x04 /* mask for AE bit: When set to 1, | ||
28 | indicates that less than 300 bytes | ||
29 | are remaining in DoubleTalk's input | ||
30 | (TTS or PCM) buffer. AE is always 1 | ||
31 | in the TGN and CVSD modes. */ | ||
32 | |||
33 | /* data returned by Interrogate command */ | ||
34 | struct synth_settings { | ||
35 | u_short serial_number; /* 0-7Fh:0-7Fh */ | ||
36 | u_char rom_version[24]; /* null terminated string */ | ||
37 | u_char mode; /* 0=Character; 1=Phoneme; 2=Text */ | ||
38 | u_char punc_level; /* nB; 0-7 */ | ||
39 | u_char formant_freq; /* nF; 0-9 */ | ||
40 | u_char pitch; /* nP; 0-99 */ | ||
41 | u_char speed; /* nS; 0-9 */ | ||
42 | u_char volume; /* nV; 0-9 */ | ||
43 | u_char tone; /* nX; 0-2 */ | ||
44 | u_char expression; /* nE; 0-9 */ | ||
45 | u_char ext_dict_loaded; /* 1=exception dictionary loaded */ | ||
46 | u_char ext_dict_status; /* 1=exception dictionary enabled */ | ||
47 | u_char free_ram; /* # pages (truncated) remaining for | ||
48 | * text buffer */ | ||
49 | u_char articulation; /* nA; 0-9 */ | ||
50 | u_char reverb; /* nR; 0-9 */ | ||
51 | u_char eob; /* 7Fh value indicating end of | ||
52 | * parameter block */ | ||
53 | u_char has_indexing; /* nonzero if indexing is implemented */ | ||
54 | }; | ||
diff --git a/drivers/staging/speakup/speakup_dummy.c b/drivers/staging/speakup/speakup_dummy.c new file mode 100644 index 00000000000..6712a728faa --- /dev/null +++ b/drivers/staging/speakup/speakup_dummy.c | |||
@@ -0,0 +1,148 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * eventually modified by Samuel Thibault <samuel.thibault@ens-lyon.org> | ||
5 | * | ||
6 | * Copyright (C) 1998-99 Kirk Reiser. | ||
7 | * Copyright (C) 2003 David Borowski. | ||
8 | * Copyright (C) 2007 Samuel Thibault. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | * specificly written as a driver for the speakup screenreview | ||
25 | * s not a general device driver. | ||
26 | */ | ||
27 | #include "spk_priv.h" | ||
28 | #include "speakup.h" | ||
29 | |||
30 | #define PROCSPEECH '\n' | ||
31 | #define DRV_VERSION "2.11" | ||
32 | #define SYNTH_CLEAR '!' | ||
33 | |||
34 | static struct var_t vars[] = { | ||
35 | { CAPS_START, .u.s = {"CAPS_START\n" }}, | ||
36 | { CAPS_STOP, .u.s = {"CAPS_STOP\n" }}, | ||
37 | { RATE, .u.n = {"RATE %d\n", 8, 1, 16, 0, 0, NULL }}, | ||
38 | { PITCH, .u.n = {"PITCH %d\n", 8, 0, 16, 0, 0, NULL }}, | ||
39 | { VOL, .u.n = {"VOL %d\n", 8, 0, 16, 0, 0, NULL }}, | ||
40 | { TONE, .u.n = {"TONE %d\n", 8, 0, 16, 0, 0, NULL }}, | ||
41 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
42 | V_LAST_VAR | ||
43 | }; | ||
44 | |||
45 | /* | ||
46 | * These attributes will appear in /sys/accessibility/speakup/dummy. | ||
47 | */ | ||
48 | static struct kobj_attribute caps_start_attribute = | ||
49 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
50 | static struct kobj_attribute caps_stop_attribute = | ||
51 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
52 | static struct kobj_attribute pitch_attribute = | ||
53 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
54 | static struct kobj_attribute rate_attribute = | ||
55 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
56 | static struct kobj_attribute tone_attribute = | ||
57 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
58 | static struct kobj_attribute vol_attribute = | ||
59 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
60 | |||
61 | static struct kobj_attribute delay_time_attribute = | ||
62 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
63 | static struct kobj_attribute direct_attribute = | ||
64 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
65 | static struct kobj_attribute full_time_attribute = | ||
66 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
67 | static struct kobj_attribute jiffy_delta_attribute = | ||
68 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
69 | static struct kobj_attribute trigger_time_attribute = | ||
70 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
71 | |||
72 | /* | ||
73 | * Create a group of attributes so that we can create and destroy them all | ||
74 | * at once. | ||
75 | */ | ||
76 | static struct attribute *synth_attrs[] = { | ||
77 | &caps_start_attribute.attr, | ||
78 | &caps_stop_attribute.attr, | ||
79 | &pitch_attribute.attr, | ||
80 | &rate_attribute.attr, | ||
81 | &tone_attribute.attr, | ||
82 | &vol_attribute.attr, | ||
83 | &delay_time_attribute.attr, | ||
84 | &direct_attribute.attr, | ||
85 | &full_time_attribute.attr, | ||
86 | &jiffy_delta_attribute.attr, | ||
87 | &trigger_time_attribute.attr, | ||
88 | NULL, /* need to NULL terminate the list of attributes */ | ||
89 | }; | ||
90 | |||
91 | static struct spk_synth synth_dummy = { | ||
92 | .name = "dummy", | ||
93 | .version = DRV_VERSION, | ||
94 | .long_name = "Dummy", | ||
95 | .init = "Speakup\n", | ||
96 | .procspeech = PROCSPEECH, | ||
97 | .clear = SYNTH_CLEAR, | ||
98 | .delay = 500, | ||
99 | .trigger = 50, | ||
100 | .jiffies = 50, | ||
101 | .full = 40000, | ||
102 | .startup = SYNTH_START, | ||
103 | .checkval = SYNTH_CHECK, | ||
104 | .vars = vars, | ||
105 | .probe = serial_synth_probe, | ||
106 | .release = spk_serial_release, | ||
107 | .synth_immediate = spk_synth_immediate, | ||
108 | .catch_up = spk_do_catch_up, | ||
109 | .flush = spk_synth_flush, | ||
110 | .is_alive = spk_synth_is_alive_restart, | ||
111 | .synth_adjust = NULL, | ||
112 | .read_buff_add = NULL, | ||
113 | .get_index = NULL, | ||
114 | .indexing = { | ||
115 | .command = NULL, | ||
116 | .lowindex = 0, | ||
117 | .highindex = 0, | ||
118 | .currindex = 0, | ||
119 | }, | ||
120 | .attributes = { | ||
121 | .attrs = synth_attrs, | ||
122 | .name = "dummy", | ||
123 | }, | ||
124 | }; | ||
125 | |||
126 | module_param_named(ser, synth_dummy.ser, int, S_IRUGO); | ||
127 | module_param_named(start, synth_dummy.startup, short, S_IRUGO); | ||
128 | |||
129 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
130 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
131 | |||
132 | static int __init dummy_init(void) | ||
133 | { | ||
134 | return synth_add(&synth_dummy); | ||
135 | } | ||
136 | |||
137 | static void __exit dummy_exit(void) | ||
138 | { | ||
139 | synth_remove(&synth_dummy); | ||
140 | } | ||
141 | |||
142 | module_init(dummy_init); | ||
143 | module_exit(dummy_exit); | ||
144 | MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>"); | ||
145 | MODULE_DESCRIPTION("Speakup support for text console"); | ||
146 | MODULE_LICENSE("GPL"); | ||
147 | MODULE_VERSION(DRV_VERSION); | ||
148 | |||
diff --git a/drivers/staging/speakup/speakup_keypc.c b/drivers/staging/speakup/speakup_keypc.c new file mode 100644 index 00000000000..0f18ec01425 --- /dev/null +++ b/drivers/staging/speakup/speakup_keypc.c | |||
@@ -0,0 +1,327 @@ | |||
1 | /* | ||
2 | * written by David Borowski | ||
3 | * | ||
4 | * Copyright (C) 2003 David Borowski. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | * specificly written as a driver for the speakup screenreview | ||
21 | * package it's not a general device driver. | ||
22 | * This driver is for the Keynote Gold internal synthesizer. | ||
23 | */ | ||
24 | #include <linux/jiffies.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/timer.h> | ||
27 | #include <linux/kthread.h> | ||
28 | #include <linux/serial_reg.h> | ||
29 | |||
30 | #include "spk_priv.h" | ||
31 | #include "speakup.h" | ||
32 | |||
33 | #define DRV_VERSION "2.10" | ||
34 | #define SYNTH_IO_EXTENT 0x04 | ||
35 | #define SWAIT udelay(70) | ||
36 | #define synth_writable() (inb_p(synth_port + UART_RX) & 0x10) | ||
37 | #define synth_readable() (inb_p(synth_port + UART_RX) & 0x10) | ||
38 | #define synth_full() ((inb_p(synth_port + UART_RX) & 0x80) == 0) | ||
39 | #define PROCSPEECH 0x1f | ||
40 | #define SYNTH_CLEAR 0x03 | ||
41 | |||
42 | static int synth_probe(struct spk_synth *synth); | ||
43 | static void keynote_release(void); | ||
44 | static const char *synth_immediate(struct spk_synth *synth, const char *buf); | ||
45 | static void do_catch_up(struct spk_synth *synth); | ||
46 | static void synth_flush(struct spk_synth *synth); | ||
47 | |||
48 | static int synth_port; | ||
49 | static int port_forced; | ||
50 | static unsigned int synth_portlist[] = { 0x2a8, 0 }; | ||
51 | |||
52 | static struct var_t vars[] = { | ||
53 | { CAPS_START, .u.s = {"[f130]" }}, | ||
54 | { CAPS_STOP, .u.s = {"[f90]" }}, | ||
55 | { RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL }}, | ||
56 | { PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL }}, | ||
57 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
58 | V_LAST_VAR | ||
59 | }; | ||
60 | |||
61 | /* | ||
62 | * These attributes will appear in /sys/accessibility/speakup/keypc. | ||
63 | */ | ||
64 | static struct kobj_attribute caps_start_attribute = | ||
65 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
66 | static struct kobj_attribute caps_stop_attribute = | ||
67 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
68 | static struct kobj_attribute pitch_attribute = | ||
69 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
70 | static struct kobj_attribute rate_attribute = | ||
71 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
72 | |||
73 | static struct kobj_attribute delay_time_attribute = | ||
74 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
75 | static struct kobj_attribute direct_attribute = | ||
76 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
77 | static struct kobj_attribute full_time_attribute = | ||
78 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
79 | static struct kobj_attribute jiffy_delta_attribute = | ||
80 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
81 | static struct kobj_attribute trigger_time_attribute = | ||
82 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
83 | |||
84 | /* | ||
85 | * Create a group of attributes so that we can create and destroy them all | ||
86 | * at once. | ||
87 | */ | ||
88 | static struct attribute *synth_attrs[] = { | ||
89 | &caps_start_attribute.attr, | ||
90 | &caps_stop_attribute.attr, | ||
91 | &pitch_attribute.attr, | ||
92 | &rate_attribute.attr, | ||
93 | &delay_time_attribute.attr, | ||
94 | &direct_attribute.attr, | ||
95 | &full_time_attribute.attr, | ||
96 | &jiffy_delta_attribute.attr, | ||
97 | &trigger_time_attribute.attr, | ||
98 | NULL, /* need to NULL terminate the list of attributes */ | ||
99 | }; | ||
100 | |||
101 | static struct spk_synth synth_keypc = { | ||
102 | .name = "keypc", | ||
103 | .version = DRV_VERSION, | ||
104 | .long_name = "Keynote PC", | ||
105 | .init = "[t][n7,1][n8,0]", | ||
106 | .procspeech = PROCSPEECH, | ||
107 | .clear = SYNTH_CLEAR, | ||
108 | .delay = 500, | ||
109 | .trigger = 50, | ||
110 | .jiffies = 50, | ||
111 | .full = 1000, | ||
112 | .startup = SYNTH_START, | ||
113 | .checkval = SYNTH_CHECK, | ||
114 | .vars = vars, | ||
115 | .probe = synth_probe, | ||
116 | .release = keynote_release, | ||
117 | .synth_immediate = synth_immediate, | ||
118 | .catch_up = do_catch_up, | ||
119 | .flush = synth_flush, | ||
120 | .is_alive = spk_synth_is_alive_nop, | ||
121 | .synth_adjust = NULL, | ||
122 | .read_buff_add = NULL, | ||
123 | .get_index = NULL, | ||
124 | .indexing = { | ||
125 | .command = NULL, | ||
126 | .lowindex = 0, | ||
127 | .highindex = 0, | ||
128 | .currindex = 0, | ||
129 | }, | ||
130 | .attributes = { | ||
131 | .attrs = synth_attrs, | ||
132 | .name = "keypc", | ||
133 | }, | ||
134 | }; | ||
135 | |||
136 | static char *oops(void) | ||
137 | { | ||
138 | int s1, s2, s3, s4; | ||
139 | s1 = inb_p(synth_port); | ||
140 | s2 = inb_p(synth_port+1); | ||
141 | s3 = inb_p(synth_port+2); | ||
142 | s4 = inb_p(synth_port+3); | ||
143 | pr_warn("synth timeout %d %d %d %d\n", s1, s2, s3, s4); | ||
144 | return NULL; | ||
145 | } | ||
146 | |||
147 | static const char *synth_immediate(struct spk_synth *synth, const char *buf) | ||
148 | { | ||
149 | u_char ch; | ||
150 | int timeout; | ||
151 | while ((ch = *buf)) { | ||
152 | if (ch == '\n') | ||
153 | ch = PROCSPEECH; | ||
154 | if (synth_full()) | ||
155 | return buf; | ||
156 | timeout = 1000; | ||
157 | while (synth_writable()) | ||
158 | if (--timeout <= 0) | ||
159 | return oops(); | ||
160 | outb_p(ch, synth_port); | ||
161 | udelay(70); | ||
162 | buf++; | ||
163 | } | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static void do_catch_up(struct spk_synth *synth) | ||
168 | { | ||
169 | u_char ch; | ||
170 | int timeout; | ||
171 | unsigned long flags; | ||
172 | unsigned long jiff_max; | ||
173 | struct var_t *jiffy_delta; | ||
174 | struct var_t *delay_time; | ||
175 | struct var_t *full_time; | ||
176 | int delay_time_val; | ||
177 | int full_time_val; | ||
178 | int jiffy_delta_val; | ||
179 | |||
180 | jiffy_delta = get_var(JIFFY); | ||
181 | delay_time = get_var(DELAY); | ||
182 | full_time = get_var(FULL); | ||
183 | spk_lock(flags); | ||
184 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
185 | spk_unlock(flags); | ||
186 | |||
187 | jiff_max = jiffies + jiffy_delta_val; | ||
188 | while (!kthread_should_stop()) { | ||
189 | spk_lock(flags); | ||
190 | if (speakup_info.flushing) { | ||
191 | speakup_info.flushing = 0; | ||
192 | spk_unlock(flags); | ||
193 | synth->flush(synth); | ||
194 | continue; | ||
195 | } | ||
196 | if (synth_buffer_empty()) { | ||
197 | spk_unlock(flags); | ||
198 | break; | ||
199 | } | ||
200 | set_current_state(TASK_INTERRUPTIBLE); | ||
201 | full_time_val = full_time->u.n.value; | ||
202 | spk_unlock(flags); | ||
203 | if (synth_full()) { | ||
204 | schedule_timeout(msecs_to_jiffies(full_time_val)); | ||
205 | continue; | ||
206 | } | ||
207 | set_current_state(TASK_RUNNING); | ||
208 | timeout = 1000; | ||
209 | while (synth_writable()) | ||
210 | if (--timeout <= 0) | ||
211 | break; | ||
212 | if (timeout <= 0) { | ||
213 | oops(); | ||
214 | break; | ||
215 | } | ||
216 | spk_lock(flags); | ||
217 | ch = synth_buffer_getc(); | ||
218 | spk_unlock(flags); | ||
219 | if (ch == '\n') | ||
220 | ch = PROCSPEECH; | ||
221 | outb_p(ch, synth_port); | ||
222 | SWAIT; | ||
223 | if ((jiffies >= jiff_max) && (ch == SPACE)) { | ||
224 | timeout = 1000; | ||
225 | while (synth_writable()) | ||
226 | if (--timeout <= 0) | ||
227 | break; | ||
228 | if (timeout <= 0) { | ||
229 | oops(); | ||
230 | break; | ||
231 | } | ||
232 | outb_p(PROCSPEECH, synth_port); | ||
233 | spk_lock(flags); | ||
234 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
235 | delay_time_val = delay_time->u.n.value; | ||
236 | spk_unlock(flags); | ||
237 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
238 | jiff_max = jiffies+jiffy_delta_val; | ||
239 | } | ||
240 | } | ||
241 | timeout = 1000; | ||
242 | while (synth_writable()) | ||
243 | if (--timeout <= 0) | ||
244 | break; | ||
245 | if (timeout <= 0) | ||
246 | oops(); | ||
247 | else | ||
248 | outb_p(PROCSPEECH, synth_port); | ||
249 | } | ||
250 | |||
251 | static void synth_flush(struct spk_synth *synth) | ||
252 | { | ||
253 | outb_p(SYNTH_CLEAR, synth_port); | ||
254 | } | ||
255 | |||
256 | static int synth_probe(struct spk_synth *synth) | ||
257 | { | ||
258 | unsigned int port_val = 0; | ||
259 | int i = 0; | ||
260 | pr_info("Probing for %s.\n", synth->long_name); | ||
261 | if (port_forced) { | ||
262 | synth_port = port_forced; | ||
263 | pr_info("probe forced to %x by kernel command line\n", | ||
264 | synth_port); | ||
265 | if (synth_request_region(synth_port-1, SYNTH_IO_EXTENT)) { | ||
266 | pr_warn("sorry, port already reserved\n"); | ||
267 | return -EBUSY; | ||
268 | } | ||
269 | port_val = inb(synth_port); | ||
270 | } else { | ||
271 | for (i = 0; synth_portlist[i]; i++) { | ||
272 | if (synth_request_region(synth_portlist[i], | ||
273 | SYNTH_IO_EXTENT)) { | ||
274 | pr_warn("request_region: failed with 0x%x, %d\n", | ||
275 | synth_portlist[i], SYNTH_IO_EXTENT); | ||
276 | continue; | ||
277 | } | ||
278 | port_val = inb(synth_portlist[i]); | ||
279 | if (port_val == 0x80) { | ||
280 | synth_port = synth_portlist[i]; | ||
281 | break; | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | if (port_val != 0x80) { | ||
286 | pr_info("%s: not found\n", synth->long_name); | ||
287 | synth_release_region(synth_port, SYNTH_IO_EXTENT); | ||
288 | synth_port = 0; | ||
289 | return -ENODEV; | ||
290 | } | ||
291 | pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name, | ||
292 | synth_port, synth_port+SYNTH_IO_EXTENT-1, | ||
293 | synth->version); | ||
294 | synth->alive = 1; | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static void keynote_release(void) | ||
299 | { | ||
300 | if (synth_port) | ||
301 | synth_release_region(synth_port, SYNTH_IO_EXTENT); | ||
302 | synth_port = 0; | ||
303 | } | ||
304 | |||
305 | module_param_named(port, port_forced, int, S_IRUGO); | ||
306 | module_param_named(start, synth_keypc.startup, short, S_IRUGO); | ||
307 | |||
308 | MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); | ||
309 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
310 | |||
311 | static int __init keypc_init(void) | ||
312 | { | ||
313 | return synth_add(&synth_keypc); | ||
314 | } | ||
315 | |||
316 | static void __exit keypc_exit(void) | ||
317 | { | ||
318 | synth_remove(&synth_keypc); | ||
319 | } | ||
320 | |||
321 | module_init(keypc_init); | ||
322 | module_exit(keypc_exit); | ||
323 | MODULE_AUTHOR("David Borowski"); | ||
324 | MODULE_DESCRIPTION("Speakup support for Keynote Gold PC synthesizers"); | ||
325 | MODULE_LICENSE("GPL"); | ||
326 | MODULE_VERSION(DRV_VERSION); | ||
327 | |||
diff --git a/drivers/staging/speakup/speakup_ltlk.c b/drivers/staging/speakup/speakup_ltlk.c new file mode 100644 index 00000000000..d8569591c5a --- /dev/null +++ b/drivers/staging/speakup/speakup_ltlk.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * specificly written as a driver for the speakup screenreview | ||
23 | * s not a general device driver. | ||
24 | */ | ||
25 | #include "speakup.h" | ||
26 | #include "spk_priv.h" | ||
27 | #include "serialio.h" | ||
28 | #include "speakup_dtlk.h" /* local header file for LiteTalk values */ | ||
29 | |||
30 | #define DRV_VERSION "2.11" | ||
31 | #define synth_full( ) ( !( inb( synth_port_tts + UART_MSR ) & UART_MSR_CTS ) ) | ||
32 | #define PROCSPEECH 0x0d | ||
33 | |||
34 | static int synth_probe(struct spk_synth *synth); | ||
35 | |||
36 | static struct var_t vars[] = { | ||
37 | { CAPS_START, .u.s = {"\x01+35p" }}, | ||
38 | { CAPS_STOP, .u.s = {"\x01-35p" }}, | ||
39 | { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL }}, | ||
40 | { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL }}, | ||
41 | { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL }}, | ||
42 | { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL }}, | ||
43 | { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL }}, | ||
44 | { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL }}, | ||
45 | { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL }}, | ||
46 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
47 | V_LAST_VAR | ||
48 | }; | ||
49 | |||
50 | /* | ||
51 | * These attributes will appear in /sys/accessibility/speakup/ltlk. | ||
52 | */ | ||
53 | static struct kobj_attribute caps_start_attribute = | ||
54 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
55 | static struct kobj_attribute caps_stop_attribute = | ||
56 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
57 | static struct kobj_attribute freq_attribute = | ||
58 | __ATTR(freq, USER_RW, spk_var_show, spk_var_store); | ||
59 | static struct kobj_attribute pitch_attribute = | ||
60 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
61 | static struct kobj_attribute punct_attribute = | ||
62 | __ATTR(punct, USER_RW, spk_var_show, spk_var_store); | ||
63 | static struct kobj_attribute rate_attribute = | ||
64 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
65 | static struct kobj_attribute tone_attribute = | ||
66 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
67 | static struct kobj_attribute voice_attribute = | ||
68 | __ATTR(voice, USER_RW, spk_var_show, spk_var_store); | ||
69 | static struct kobj_attribute vol_attribute = | ||
70 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
71 | |||
72 | static struct kobj_attribute delay_time_attribute = | ||
73 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
74 | static struct kobj_attribute direct_attribute = | ||
75 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
76 | static struct kobj_attribute full_time_attribute = | ||
77 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
78 | static struct kobj_attribute jiffy_delta_attribute = | ||
79 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
80 | static struct kobj_attribute trigger_time_attribute = | ||
81 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
82 | |||
83 | /* | ||
84 | * Create a group of attributes so that we can create and destroy them all | ||
85 | * at once. | ||
86 | */ | ||
87 | static struct attribute *synth_attrs[] = { | ||
88 | &caps_start_attribute.attr, | ||
89 | &caps_stop_attribute.attr, | ||
90 | &freq_attribute.attr, | ||
91 | &pitch_attribute.attr, | ||
92 | &punct_attribute.attr, | ||
93 | &rate_attribute.attr, | ||
94 | &tone_attribute.attr, | ||
95 | &voice_attribute.attr, | ||
96 | &vol_attribute.attr, | ||
97 | &delay_time_attribute.attr, | ||
98 | &direct_attribute.attr, | ||
99 | &full_time_attribute.attr, | ||
100 | &jiffy_delta_attribute.attr, | ||
101 | &trigger_time_attribute.attr, | ||
102 | NULL, /* need to NULL terminate the list of attributes */ | ||
103 | }; | ||
104 | |||
105 | static struct spk_synth synth_ltlk = { | ||
106 | .name = "ltlk", | ||
107 | .version = DRV_VERSION, | ||
108 | .long_name = "LiteTalk", | ||
109 | .init = "\01@\x01\x31y\n\0", | ||
110 | .procspeech = PROCSPEECH, | ||
111 | .clear = SYNTH_CLEAR, | ||
112 | .delay = 500, | ||
113 | .trigger = 50, | ||
114 | .jiffies = 50, | ||
115 | .full = 40000, | ||
116 | .startup = SYNTH_START, | ||
117 | .checkval = SYNTH_CHECK, | ||
118 | .vars = vars, | ||
119 | .probe = synth_probe, | ||
120 | .release = spk_serial_release, | ||
121 | .synth_immediate = spk_synth_immediate, | ||
122 | .catch_up = spk_do_catch_up, | ||
123 | .flush = spk_synth_flush, | ||
124 | .is_alive = spk_synth_is_alive_restart, | ||
125 | .synth_adjust = NULL, | ||
126 | .read_buff_add = NULL, | ||
127 | .get_index = spk_serial_in_nowait, | ||
128 | .indexing = { | ||
129 | .command = "\x01%di", | ||
130 | .lowindex = 1, | ||
131 | .highindex = 5, | ||
132 | .currindex = 1, | ||
133 | }, | ||
134 | .attributes = { | ||
135 | .attrs = synth_attrs, | ||
136 | .name = "ltlk", | ||
137 | }, | ||
138 | }; | ||
139 | |||
140 | /* interrogate the LiteTalk and print its settings */ | ||
141 | static void synth_interrogate(struct spk_synth *synth) | ||
142 | { | ||
143 | unsigned char *t, i; | ||
144 | unsigned char buf[50], rom_v[20]; | ||
145 | spk_synth_immediate(synth, "\x18\x01?"); | ||
146 | for (i = 0; i < 50; i++) { | ||
147 | buf[i] = spk_serial_in(); | ||
148 | if (i > 2 && buf[i] == 0x7f) | ||
149 | break; | ||
150 | } | ||
151 | t = buf+2; | ||
152 | for (i = 0; *t != '\r'; t++) { | ||
153 | rom_v[i] = *t; | ||
154 | if (++i >= 19) | ||
155 | break; | ||
156 | } | ||
157 | rom_v[i] = 0; | ||
158 | pr_info("%s: ROM version: %s\n", synth->long_name, rom_v); | ||
159 | } | ||
160 | |||
161 | static int synth_probe(struct spk_synth *synth) | ||
162 | { | ||
163 | int failed = 0; | ||
164 | |||
165 | failed = serial_synth_probe(synth); | ||
166 | if (failed == 0) | ||
167 | synth_interrogate(synth); | ||
168 | synth->alive = !failed; | ||
169 | return failed; | ||
170 | } | ||
171 | |||
172 | module_param_named(ser, synth_ltlk.ser, int, S_IRUGO); | ||
173 | module_param_named(start, synth_ltlk.startup, short, S_IRUGO); | ||
174 | |||
175 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
176 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
177 | |||
178 | static int __init ltlk_init(void) | ||
179 | { | ||
180 | return synth_add(&synth_ltlk); | ||
181 | } | ||
182 | |||
183 | static void __exit ltlk_exit(void) | ||
184 | { | ||
185 | synth_remove(&synth_ltlk); | ||
186 | } | ||
187 | |||
188 | module_init(ltlk_init); | ||
189 | module_exit(ltlk_exit); | ||
190 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
191 | MODULE_AUTHOR("David Borowski"); | ||
192 | MODULE_DESCRIPTION("Speakup support for DoubleTalk LT/LiteTalk synthesizers"); | ||
193 | MODULE_LICENSE("GPL"); | ||
194 | MODULE_VERSION(DRV_VERSION); | ||
195 | |||
diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c new file mode 100644 index 00000000000..2c85773fdbf --- /dev/null +++ b/drivers/staging/speakup/speakup_soft.c | |||
@@ -0,0 +1,366 @@ | |||
1 | /* speakup_soft.c - speakup driver to register and make available | ||
2 | * a user space device for software synthesizers. written by: Kirk | ||
3 | * Reiser <kirk@braille.uwo.ca> | ||
4 | * | ||
5 | * Copyright (C) 2003 Kirk Reiser. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * this code is specificly written as a driver for the speakup screenreview | ||
22 | * package and is not a general device driver. */ | ||
23 | |||
24 | #include <linux/unistd.h> | ||
25 | #include <linux/miscdevice.h> /* for misc_register, and SYNTH_MINOR */ | ||
26 | #include <linux/poll.h> /* for poll_wait() */ | ||
27 | #include <linux/sched.h> /* schedule(), signal_pending(), TASK_INTERRUPTIBLE */ | ||
28 | |||
29 | #include "spk_priv.h" | ||
30 | #include "speakup.h" | ||
31 | |||
32 | #define DRV_VERSION "2.6" | ||
33 | #define SOFTSYNTH_MINOR 26 /* might as well give it one more than /dev/synth */ | ||
34 | #define PROCSPEECH 0x0d | ||
35 | #define CLEAR_SYNTH 0x18 | ||
36 | |||
37 | static int softsynth_probe(struct spk_synth *synth); | ||
38 | static void softsynth_release(void); | ||
39 | static int softsynth_is_alive(struct spk_synth *synth); | ||
40 | static unsigned char get_index(void); | ||
41 | |||
42 | static struct miscdevice synth_device; | ||
43 | static int initialized = 0; | ||
44 | static int misc_registered; | ||
45 | |||
46 | static struct var_t vars[] = { | ||
47 | { CAPS_START, .u.s = {"\x01+3p" }}, | ||
48 | { CAPS_STOP, .u.s = {"\x01-3p" }}, | ||
49 | { RATE, .u.n = {"\x01%ds", 5, 0, 9, 0, 0, NULL }}, | ||
50 | { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL }}, | ||
51 | { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL }}, | ||
52 | { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL }}, | ||
53 | { PUNCT, .u.n = {"\x01%db", 0, 0, 2, 0, 0, NULL }}, | ||
54 | { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL }}, | ||
55 | { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL }}, | ||
56 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
57 | V_LAST_VAR | ||
58 | }; | ||
59 | |||
60 | /* | ||
61 | * These attributes will appear in /sys/accessibility/speakup/soft. | ||
62 | */ | ||
63 | static struct kobj_attribute caps_start_attribute = | ||
64 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
65 | static struct kobj_attribute caps_stop_attribute = | ||
66 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
67 | static struct kobj_attribute freq_attribute = | ||
68 | __ATTR(freq, USER_RW, spk_var_show, spk_var_store); | ||
69 | //static struct kobj_attribute lang_attribute = | ||
70 | // __ATTR(lang, USER_RW, spk_var_show, spk_var_store); | ||
71 | static struct kobj_attribute pitch_attribute = | ||
72 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
73 | static struct kobj_attribute punct_attribute = | ||
74 | __ATTR(punct, USER_RW, spk_var_show, spk_var_store); | ||
75 | static struct kobj_attribute rate_attribute = | ||
76 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
77 | static struct kobj_attribute tone_attribute = | ||
78 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
79 | static struct kobj_attribute voice_attribute = | ||
80 | __ATTR(voice, USER_RW, spk_var_show, spk_var_store); | ||
81 | static struct kobj_attribute vol_attribute = | ||
82 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
83 | |||
84 | static struct kobj_attribute delay_time_attribute = | ||
85 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
86 | static struct kobj_attribute direct_attribute = | ||
87 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
88 | static struct kobj_attribute full_time_attribute = | ||
89 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
90 | static struct kobj_attribute jiffy_delta_attribute = | ||
91 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
92 | static struct kobj_attribute trigger_time_attribute = | ||
93 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
94 | |||
95 | /* | ||
96 | * Create a group of attributes so that we can create and destroy them all | ||
97 | * at once. | ||
98 | */ | ||
99 | static struct attribute *synth_attrs[] = { | ||
100 | &caps_start_attribute.attr, | ||
101 | &caps_stop_attribute.attr, | ||
102 | &freq_attribute.attr, | ||
103 | // &lang_attribute.attr, | ||
104 | &pitch_attribute.attr, | ||
105 | &punct_attribute.attr, | ||
106 | &rate_attribute.attr, | ||
107 | &tone_attribute.attr, | ||
108 | &voice_attribute.attr, | ||
109 | &vol_attribute.attr, | ||
110 | &delay_time_attribute.attr, | ||
111 | &direct_attribute.attr, | ||
112 | &full_time_attribute.attr, | ||
113 | &jiffy_delta_attribute.attr, | ||
114 | &trigger_time_attribute.attr, | ||
115 | NULL, /* need to NULL terminate the list of attributes */ | ||
116 | }; | ||
117 | |||
118 | static struct spk_synth synth_soft = { | ||
119 | .name = "soft", | ||
120 | .version = DRV_VERSION, | ||
121 | .long_name = "software synth", | ||
122 | .init = "\01@\x01\x31y\n", | ||
123 | .procspeech = PROCSPEECH, | ||
124 | .delay = 0, | ||
125 | .trigger = 0, | ||
126 | .jiffies = 0, | ||
127 | .full = 0, | ||
128 | .startup = SYNTH_START, | ||
129 | .checkval = SYNTH_CHECK, | ||
130 | .vars = vars, | ||
131 | .probe = softsynth_probe, | ||
132 | .release = softsynth_release, | ||
133 | .synth_immediate = NULL, | ||
134 | .catch_up = NULL, | ||
135 | .flush = NULL, | ||
136 | .is_alive = softsynth_is_alive, | ||
137 | .synth_adjust = NULL, | ||
138 | .read_buff_add = NULL, | ||
139 | .get_index = get_index, | ||
140 | .indexing = { | ||
141 | .command = "\x01%di", | ||
142 | .lowindex = 1, | ||
143 | .highindex = 5, | ||
144 | .currindex = 1, | ||
145 | }, | ||
146 | .attributes = { | ||
147 | .attrs = synth_attrs, | ||
148 | .name = "soft", | ||
149 | }, | ||
150 | }; | ||
151 | |||
152 | static char *get_initstring(void) | ||
153 | { | ||
154 | static char buf[40]; | ||
155 | char *cp; | ||
156 | struct var_t *var; | ||
157 | |||
158 | memset(buf, 0, sizeof(buf)); | ||
159 | cp = buf; | ||
160 | var = synth_soft.vars; | ||
161 | while (var->var_id != MAXVARS) { | ||
162 | if (var->var_id != CAPS_START && var->var_id != CAPS_STOP | ||
163 | && var->var_id != DIRECT) | ||
164 | cp = cp + sprintf(cp, var->u.n.synth_fmt, var->u.n.value); | ||
165 | var++; | ||
166 | } | ||
167 | cp = cp + sprintf(cp, "\n"); | ||
168 | return buf; | ||
169 | } | ||
170 | |||
171 | static int softsynth_open(struct inode *inode, struct file *fp) | ||
172 | { | ||
173 | unsigned long flags; | ||
174 | /*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */ | ||
175 | /* return -EPERM; */ | ||
176 | spk_lock(flags); | ||
177 | if (synth_soft.alive) { | ||
178 | spk_unlock(flags); | ||
179 | return -EBUSY; | ||
180 | } | ||
181 | synth_soft.alive = 1; | ||
182 | spk_unlock(flags); | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int softsynth_close(struct inode *inode, struct file *fp) | ||
187 | { | ||
188 | unsigned long flags; | ||
189 | spk_lock(flags); | ||
190 | synth_soft.alive = 0; | ||
191 | initialized = 0; | ||
192 | spk_unlock(flags); | ||
193 | /* Make sure we let applications go before leaving */ | ||
194 | speakup_start_ttys(); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static ssize_t softsynth_read(struct file *fp, char *buf, size_t count, | ||
199 | loff_t *pos) | ||
200 | { | ||
201 | int chars_sent = 0; | ||
202 | char *cp; | ||
203 | char *init; | ||
204 | char ch; | ||
205 | int empty; | ||
206 | unsigned long flags; | ||
207 | DEFINE_WAIT(wait); | ||
208 | |||
209 | spk_lock(flags); | ||
210 | while (1) { | ||
211 | prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE); | ||
212 | if (!synth_buffer_empty() || speakup_info.flushing) | ||
213 | break; | ||
214 | spk_unlock(flags); | ||
215 | if (fp->f_flags & O_NONBLOCK) { | ||
216 | finish_wait(&speakup_event, &wait); | ||
217 | return -EAGAIN; | ||
218 | } | ||
219 | if (signal_pending(current)) { | ||
220 | finish_wait(&speakup_event, &wait); | ||
221 | return -ERESTARTSYS; | ||
222 | } | ||
223 | schedule(); | ||
224 | spk_lock(flags); | ||
225 | } | ||
226 | finish_wait(&speakup_event, &wait); | ||
227 | |||
228 | cp = buf; | ||
229 | init = get_initstring(); | ||
230 | while (chars_sent < count) { | ||
231 | if (speakup_info.flushing) { | ||
232 | speakup_info.flushing = 0; | ||
233 | ch = '\x18'; | ||
234 | } else if (synth_buffer_empty()) { | ||
235 | break; | ||
236 | } else if (! initialized) { | ||
237 | if (*init) { | ||
238 | ch = *init; | ||
239 | init++; | ||
240 | } else { | ||
241 | initialized = 1; | ||
242 | } | ||
243 | } else { | ||
244 | ch = synth_buffer_getc(); | ||
245 | } | ||
246 | spk_unlock(flags); | ||
247 | if (copy_to_user(cp, &ch, 1)) | ||
248 | return -EFAULT; | ||
249 | spk_lock(flags); | ||
250 | chars_sent++; | ||
251 | cp++; | ||
252 | } | ||
253 | *pos += chars_sent; | ||
254 | empty = synth_buffer_empty(); | ||
255 | spk_unlock(flags); | ||
256 | if (empty) { | ||
257 | speakup_start_ttys(); | ||
258 | *pos = 0; | ||
259 | } | ||
260 | return chars_sent; | ||
261 | } | ||
262 | |||
263 | static int last_index = 0; | ||
264 | |||
265 | static ssize_t softsynth_write(struct file *fp, const char *buf, size_t count, | ||
266 | loff_t *pos) | ||
267 | { | ||
268 | char indbuf[5]; | ||
269 | if (count >= sizeof(indbuf)) | ||
270 | return -EINVAL; | ||
271 | |||
272 | if (copy_from_user(indbuf, buf, count)) | ||
273 | return -EFAULT; | ||
274 | indbuf[4] = 0; | ||
275 | |||
276 | last_index = simple_strtoul(indbuf, NULL, 0); | ||
277 | return count; | ||
278 | } | ||
279 | |||
280 | static unsigned int softsynth_poll(struct file *fp, | ||
281 | struct poll_table_struct *wait) | ||
282 | { | ||
283 | unsigned long flags; | ||
284 | int ret = 0; | ||
285 | poll_wait(fp, &speakup_event, wait); | ||
286 | |||
287 | spk_lock(flags); | ||
288 | if (! synth_buffer_empty() || speakup_info.flushing) | ||
289 | ret = POLLIN | POLLRDNORM; | ||
290 | spk_unlock(flags); | ||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | static unsigned char get_index(void) | ||
295 | { | ||
296 | int rv; | ||
297 | rv = last_index; | ||
298 | last_index = 0; | ||
299 | return rv; | ||
300 | } | ||
301 | |||
302 | static struct file_operations softsynth_fops = { | ||
303 | .owner = THIS_MODULE, | ||
304 | .poll = softsynth_poll, | ||
305 | .read = softsynth_read, | ||
306 | .write = softsynth_write, | ||
307 | .open = softsynth_open, | ||
308 | .release = softsynth_close, | ||
309 | }; | ||
310 | |||
311 | |||
312 | static int softsynth_probe(struct spk_synth *synth) | ||
313 | { | ||
314 | |||
315 | if (misc_registered != 0) | ||
316 | return 0; | ||
317 | memset(&synth_device, 0, sizeof(synth_device)); | ||
318 | synth_device.minor = SOFTSYNTH_MINOR; | ||
319 | synth_device.name = "softsynth"; | ||
320 | synth_device.fops = &softsynth_fops; | ||
321 | if (misc_register(&synth_device)) { | ||
322 | pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n"); | ||
323 | return -ENODEV; | ||
324 | } | ||
325 | |||
326 | misc_registered = 1; | ||
327 | pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR 26)\n"); | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static void softsynth_release(void) | ||
332 | { | ||
333 | misc_deregister(&synth_device); | ||
334 | misc_registered = 0; | ||
335 | pr_info("unregistered /dev/softsynth\n"); | ||
336 | } | ||
337 | |||
338 | static int softsynth_is_alive(struct spk_synth *synth) | ||
339 | { | ||
340 | if (synth_soft.alive) | ||
341 | return 1; | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | module_param_named(start, synth_soft.startup, short, S_IRUGO); | ||
346 | |||
347 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
348 | |||
349 | |||
350 | static int __init soft_init(void) | ||
351 | { | ||
352 | return synth_add(&synth_soft); | ||
353 | } | ||
354 | |||
355 | static void __exit soft_exit(void) | ||
356 | { | ||
357 | synth_remove(&synth_soft); | ||
358 | } | ||
359 | |||
360 | module_init(soft_init); | ||
361 | module_exit(soft_exit); | ||
362 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
363 | MODULE_DESCRIPTION("Speakup userspace software synthesizer support"); | ||
364 | MODULE_LICENSE("GPL"); | ||
365 | MODULE_VERSION(DRV_VERSION); | ||
366 | |||
diff --git a/drivers/staging/speakup/speakup_spkout.c b/drivers/staging/speakup/speakup_spkout.c new file mode 100644 index 00000000000..32fd8d76152 --- /dev/null +++ b/drivers/staging/speakup/speakup_spkout.c | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * specificly written as a driver for the speakup screenreview | ||
23 | * s not a general device driver. | ||
24 | */ | ||
25 | #include "spk_priv.h" | ||
26 | #include "speakup.h" | ||
27 | #include "serialio.h" | ||
28 | |||
29 | #define DRV_VERSION "2.11" | ||
30 | #define SYNTH_CLEAR 0x18 | ||
31 | #define PROCSPEECH '\r' | ||
32 | |||
33 | static void synth_flush(struct spk_synth *synth); | ||
34 | |||
35 | static struct var_t vars[] = { | ||
36 | { CAPS_START, .u.s = {"\x05P+" }}, | ||
37 | { CAPS_STOP, .u.s = {"\x05P-" }}, | ||
38 | { RATE, .u.n = {"\x05R%d", 7, 0, 9, 0, 0, NULL }}, | ||
39 | { PITCH, .u.n = {"\x05P%d", 3, 0, 9, 0, 0, NULL }}, | ||
40 | { VOL, .u.n = {"\x05V%d", 9, 0, 9, 0, 0, NULL }}, | ||
41 | { TONE, .u.n = {"\x05T%c", 8, 0, 25, 65, 0, NULL }}, | ||
42 | { PUNCT, .u.n = {"\x05M%c", 0, 0, 3, 0, 0, "nsma" }}, | ||
43 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
44 | V_LAST_VAR | ||
45 | }; | ||
46 | |||
47 | /* | ||
48 | * These attributes will appear in /sys/accessibility/speakup/spkout. | ||
49 | */ | ||
50 | static struct kobj_attribute caps_start_attribute = | ||
51 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
52 | static struct kobj_attribute caps_stop_attribute = | ||
53 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
54 | static struct kobj_attribute pitch_attribute = | ||
55 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
56 | static struct kobj_attribute punct_attribute = | ||
57 | __ATTR(punct, USER_RW, spk_var_show, spk_var_store); | ||
58 | static struct kobj_attribute rate_attribute = | ||
59 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
60 | static struct kobj_attribute tone_attribute = | ||
61 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
62 | static struct kobj_attribute vol_attribute = | ||
63 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
64 | |||
65 | static struct kobj_attribute delay_time_attribute = | ||
66 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
67 | static struct kobj_attribute direct_attribute = | ||
68 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
69 | static struct kobj_attribute full_time_attribute = | ||
70 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
71 | static struct kobj_attribute jiffy_delta_attribute = | ||
72 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
73 | static struct kobj_attribute trigger_time_attribute = | ||
74 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
75 | |||
76 | /* | ||
77 | * Create a group of attributes so that we can create and destroy them all | ||
78 | * at once. | ||
79 | */ | ||
80 | static struct attribute *synth_attrs[] = { | ||
81 | &caps_start_attribute.attr, | ||
82 | &caps_stop_attribute.attr, | ||
83 | &pitch_attribute.attr, | ||
84 | &punct_attribute.attr, | ||
85 | &rate_attribute.attr, | ||
86 | &tone_attribute.attr, | ||
87 | &vol_attribute.attr, | ||
88 | &delay_time_attribute.attr, | ||
89 | &direct_attribute.attr, | ||
90 | &full_time_attribute.attr, | ||
91 | &jiffy_delta_attribute.attr, | ||
92 | &trigger_time_attribute.attr, | ||
93 | NULL, /* need to NULL terminate the list of attributes */ | ||
94 | }; | ||
95 | |||
96 | static struct spk_synth synth_spkout = { | ||
97 | .name = "spkout", | ||
98 | .version = DRV_VERSION, | ||
99 | .long_name = "Speakout", | ||
100 | .init = "\005W1\005I2\005C3", | ||
101 | .procspeech = PROCSPEECH, | ||
102 | .clear = SYNTH_CLEAR, | ||
103 | .delay = 500, | ||
104 | .trigger = 50, | ||
105 | .jiffies = 50, | ||
106 | .full = 40000, | ||
107 | .startup = SYNTH_START, | ||
108 | .checkval = SYNTH_CHECK, | ||
109 | .vars = vars, | ||
110 | .probe = serial_synth_probe, | ||
111 | .release = spk_serial_release, | ||
112 | .synth_immediate = spk_synth_immediate, | ||
113 | .catch_up = spk_do_catch_up, | ||
114 | .flush = synth_flush, | ||
115 | .is_alive = spk_synth_is_alive_restart, | ||
116 | .synth_adjust = NULL, | ||
117 | .read_buff_add = NULL, | ||
118 | .get_index = spk_serial_in_nowait, | ||
119 | .indexing = { | ||
120 | .command = "\x05[%c", | ||
121 | .lowindex = 1, | ||
122 | .highindex = 5, | ||
123 | .currindex = 1, | ||
124 | }, | ||
125 | .attributes = { | ||
126 | .attrs = synth_attrs, | ||
127 | .name = "spkout", | ||
128 | }, | ||
129 | }; | ||
130 | |||
131 | static void synth_flush(struct spk_synth *synth) | ||
132 | { | ||
133 | int timeout = SPK_XMITR_TIMEOUT; | ||
134 | while (spk_serial_tx_busy()) { | ||
135 | if (!--timeout) | ||
136 | break; | ||
137 | udelay(1); | ||
138 | } | ||
139 | outb(SYNTH_CLEAR, speakup_info.port_tts); | ||
140 | } | ||
141 | |||
142 | module_param_named(ser, synth_spkout.ser, int, S_IRUGO); | ||
143 | module_param_named(start, synth_spkout.startup, short, S_IRUGO); | ||
144 | |||
145 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
146 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
147 | |||
148 | static int __init spkout_init(void) | ||
149 | { | ||
150 | return synth_add(&synth_spkout); | ||
151 | } | ||
152 | |||
153 | static void __exit spkout_exit(void) | ||
154 | { | ||
155 | synth_remove(&synth_spkout); | ||
156 | } | ||
157 | |||
158 | module_init(spkout_init); | ||
159 | module_exit(spkout_exit); | ||
160 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
161 | MODULE_AUTHOR("David Borowski"); | ||
162 | MODULE_DESCRIPTION("Speakup support for Speak Out synthesizers"); | ||
163 | MODULE_LICENSE("GPL"); | ||
164 | MODULE_VERSION(DRV_VERSION); | ||
165 | |||
diff --git a/drivers/staging/speakup/speakup_txprt.c b/drivers/staging/speakup/speakup_txprt.c new file mode 100644 index 00000000000..9ffeeac0dbc --- /dev/null +++ b/drivers/staging/speakup/speakup_txprt.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> | ||
3 | * this version considerably modified by David Borowski, david575@rogers.com | ||
4 | * | ||
5 | * Copyright (C) 1998-99 Kirk Reiser. | ||
6 | * Copyright (C) 2003 David Borowski. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * specificly written as a driver for the speakup screenreview | ||
23 | * s not a general device driver. | ||
24 | */ | ||
25 | #include "spk_priv.h" | ||
26 | #include "speakup.h" | ||
27 | |||
28 | #define DRV_VERSION "2.11" | ||
29 | #define SYNTH_CLEAR 0x18 | ||
30 | #define PROCSPEECH '\r' /* process speech char */ | ||
31 | |||
32 | static struct var_t vars[] = { | ||
33 | { CAPS_START, .u.s = {"\x05P8" }}, | ||
34 | { CAPS_STOP, .u.s = {"\x05P5" }}, | ||
35 | { RATE, .u.n = {"\x05R%d", 5, 0, 9, 0, 0, NULL }}, | ||
36 | { PITCH, .u.n = {"\x05P%d", 5, 0, 9, 0, 0, NULL }}, | ||
37 | { VOL, .u.n = {"\x05V%d", 5, 0, 9, 0, 0, NULL }}, | ||
38 | { TONE, .u.n = {"\x05T%c", 12, 0, 25, 61, 0, NULL }}, | ||
39 | { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL }}, | ||
40 | V_LAST_VAR | ||
41 | }; | ||
42 | |||
43 | /* | ||
44 | * These attributes will appear in /sys/accessibility/speakup/txprt. | ||
45 | */ | ||
46 | static struct kobj_attribute caps_start_attribute = | ||
47 | __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store); | ||
48 | static struct kobj_attribute caps_stop_attribute = | ||
49 | __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store); | ||
50 | static struct kobj_attribute pitch_attribute = | ||
51 | __ATTR(pitch, USER_RW, spk_var_show, spk_var_store); | ||
52 | static struct kobj_attribute rate_attribute = | ||
53 | __ATTR(rate, USER_RW, spk_var_show, spk_var_store); | ||
54 | static struct kobj_attribute tone_attribute = | ||
55 | __ATTR(tone, USER_RW, spk_var_show, spk_var_store); | ||
56 | static struct kobj_attribute vol_attribute = | ||
57 | __ATTR(vol, USER_RW, spk_var_show, spk_var_store); | ||
58 | |||
59 | static struct kobj_attribute delay_time_attribute = | ||
60 | __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store); | ||
61 | static struct kobj_attribute direct_attribute = | ||
62 | __ATTR(direct, USER_RW, spk_var_show, spk_var_store); | ||
63 | static struct kobj_attribute full_time_attribute = | ||
64 | __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store); | ||
65 | static struct kobj_attribute jiffy_delta_attribute = | ||
66 | __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store); | ||
67 | static struct kobj_attribute trigger_time_attribute = | ||
68 | __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store); | ||
69 | |||
70 | /* | ||
71 | * Create a group of attributes so that we can create and destroy them all | ||
72 | * at once. | ||
73 | */ | ||
74 | static struct attribute *synth_attrs[] = { | ||
75 | &caps_start_attribute.attr, | ||
76 | &caps_stop_attribute.attr, | ||
77 | &pitch_attribute.attr, | ||
78 | &rate_attribute.attr, | ||
79 | &tone_attribute.attr, | ||
80 | &vol_attribute.attr, | ||
81 | &delay_time_attribute.attr, | ||
82 | &direct_attribute.attr, | ||
83 | &full_time_attribute.attr, | ||
84 | &jiffy_delta_attribute.attr, | ||
85 | &trigger_time_attribute.attr, | ||
86 | NULL, /* need to NULL terminate the list of attributes */ | ||
87 | }; | ||
88 | |||
89 | static struct spk_synth synth_txprt = { | ||
90 | .name = "txprt", | ||
91 | .version = DRV_VERSION, | ||
92 | .long_name = "Transport", | ||
93 | .init = "\x05N1", | ||
94 | .procspeech = PROCSPEECH, | ||
95 | .clear = SYNTH_CLEAR, | ||
96 | .delay = 500, | ||
97 | .trigger = 50, | ||
98 | .jiffies = 50, | ||
99 | .full = 40000, | ||
100 | .startup = SYNTH_START, | ||
101 | .checkval = SYNTH_CHECK, | ||
102 | .vars = vars, | ||
103 | .probe = serial_synth_probe, | ||
104 | .release = spk_serial_release, | ||
105 | .synth_immediate = spk_synth_immediate, | ||
106 | .catch_up = spk_do_catch_up, | ||
107 | .flush = spk_synth_flush, | ||
108 | .is_alive = spk_synth_is_alive_restart, | ||
109 | .synth_adjust = NULL, | ||
110 | .read_buff_add = NULL, | ||
111 | .get_index = NULL, | ||
112 | .indexing = { | ||
113 | .command = NULL, | ||
114 | .lowindex = 0, | ||
115 | .highindex = 0, | ||
116 | .currindex = 0, | ||
117 | }, | ||
118 | .attributes = { | ||
119 | .attrs = synth_attrs, | ||
120 | .name = "txprt", | ||
121 | }, | ||
122 | }; | ||
123 | |||
124 | module_param_named(ser, synth_txprt.ser, int, S_IRUGO); | ||
125 | module_param_named(start, synth_txprt.startup, short, S_IRUGO); | ||
126 | |||
127 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); | ||
128 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); | ||
129 | |||
130 | static int __init txprt_init(void) | ||
131 | { | ||
132 | return synth_add(&synth_txprt); | ||
133 | } | ||
134 | |||
135 | static void __exit txprt_exit(void) | ||
136 | { | ||
137 | synth_remove(&synth_txprt); | ||
138 | } | ||
139 | |||
140 | module_init(txprt_init); | ||
141 | module_exit(txprt_exit); | ||
142 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); | ||
143 | MODULE_AUTHOR("David Borowski"); | ||
144 | MODULE_DESCRIPTION("Speakup support for Transport synthesizers"); | ||
145 | MODULE_LICENSE("GPL"); | ||
146 | MODULE_VERSION(DRV_VERSION); | ||
147 | |||
diff --git a/drivers/staging/speakup/speakupmap.h b/drivers/staging/speakup/speakupmap.h new file mode 100644 index 00000000000..f1c0dd3b2c3 --- /dev/null +++ b/drivers/staging/speakup/speakupmap.h | |||
@@ -0,0 +1,65 @@ | |||
1 | 119, 62, 6, | ||
2 | 0, 16, 20, 17, 32, 48, 0, | ||
3 | 2, 0, 78, 0, 0, 0, 0, | ||
4 | 3, 0, 79, 0, 0, 0, 0, | ||
5 | 4, 0, 76, 0, 0, 0, 0, | ||
6 | 5, 0, 77, 0, 0, 0, 0, | ||
7 | 6, 0, 74, 0, 0, 0, 0, | ||
8 | 7, 0, 75, 0, 0, 0, 0, | ||
9 | 9, 0, 5, 46, 0, 0, 0, | ||
10 | 10, 0, 4, 0, 0, 0, 0, | ||
11 | 11, 0, 0, 1, 0, 0, 0, | ||
12 | 12, 0, 27, 0, 33, 0, 0, | ||
13 | 19, 0, 47, 0, 0, 0, 0, | ||
14 | 21, 0, 29, 17, 0, 0, 0, | ||
15 | 22, 0, 15, 0, 0, 0, 0, | ||
16 | 23, 0, 14, 0, 0, 0, 28, | ||
17 | 24, 0, 16, 0, 0, 0, 0, | ||
18 | 25, 0, 30, 18, 0, 0, 0, | ||
19 | 28, 0, 3, 26, 0, 0, 0, | ||
20 | 35, 0, 31, 0, 0, 0, 0, | ||
21 | 36, 0, 12, 0, 0, 0, 0, | ||
22 | 37, 0, 11, 0, 0, 0, 22, | ||
23 | 38, 0, 13, 0, 0, 0, 0, | ||
24 | 39, 0, 32, 7, 0, 0, 0, | ||
25 | 40, 0, 23, 0, 0, 0, 0, | ||
26 | 44, 0, 44, 0, 0, 0, 0, | ||
27 | 49, 0, 24, 0, 0, 0, 0, | ||
28 | 50, 0, 9, 19, 6, 0, 0, | ||
29 | 51, 0, 8, 0, 0, 0, 36, | ||
30 | 52, 0, 10, 20, 0, 0, 0, | ||
31 | 53, 0, 25, 0, 0, 0, 0, | ||
32 | 55, 46, 1, 0, 0, 0, 0, | ||
33 | 58, 128, 128, 0, 0, 0, 0, | ||
34 | 59, 0, 45, 0, 0, 0, 0, | ||
35 | 60, 0, 40, 0, 0, 0, 0, | ||
36 | 61, 0, 41, 0, 0, 0, 0, | ||
37 | 62, 0, 42, 0, 0, 0, 0, | ||
38 | 63, 0, 34, 0, 0, 0, 0, | ||
39 | 64, 0, 35, 0, 0, 0, 0, | ||
40 | 65, 0, 37, 0, 0, 0, 0, | ||
41 | 66, 0, 38, 0, 0, 0, 0, | ||
42 | 67, 0, 66, 0, 39, 0, 0, | ||
43 | 68, 0, 67, 0, 0, 0, 0, | ||
44 | 71, 15, 19, 0, 0, 0, 0, | ||
45 | 72, 14, 29, 0, 0, 28, 0, | ||
46 | 73, 16, 17, 0, 0, 0, 0, | ||
47 | 74, 27, 33, 0, 0, 0, 0, | ||
48 | 75, 12, 31, 0, 0, 0, 0, | ||
49 | 76, 11, 21, 0, 0, 22, 0, | ||
50 | 77, 13, 32, 0, 0, 0, 0, | ||
51 | 78, 23, 43, 0, 0, 0, 0, | ||
52 | 79, 9, 20, 0, 0, 0, 0, | ||
53 | 80, 8, 30, 0, 0, 36, 0, | ||
54 | 81, 10, 18, 0, 0, 0, 0, | ||
55 | 82, 128, 128, 0, 0, 0, 0, | ||
56 | 83, 24, 25, 0, 0, 0, 0, | ||
57 | 87, 0, 68, 0, 0, 0, 0, | ||
58 | 88, 0, 69, 0, 0, 0, 0, | ||
59 | 96, 3, 26, 0, 0, 0, 0, | ||
60 | 98, 4, 5, 0, 0, 0, 0, | ||
61 | 99, 2, 0, 0, 0, 0, 0, | ||
62 | 104, 0, 6, 0, 0, 0, 0, | ||
63 | 109, 0, 7, 0, 0, 0, 0, | ||
64 | 125, 128, 128, 0, 0, 0, 0, | ||
65 | 0, 119 | ||
diff --git a/drivers/staging/speakup/speakupmap.map b/drivers/staging/speakup/speakupmap.map new file mode 100644 index 00000000000..f10d44cf5d7 --- /dev/null +++ b/drivers/staging/speakup/speakupmap.map | |||
@@ -0,0 +1,93 @@ | |||
1 | spk key_f9 = punc_level_dec | ||
2 | spk key_f10 = punc_level_inc | ||
3 | spk key_f11 = reading_punc_dec | ||
4 | spk key_f12 = reading_punc_inc | ||
5 | spk key_1 = vol_dec | ||
6 | spk key_2 = vol_inc | ||
7 | spk key_3 = pitch_dec | ||
8 | spk key_4 = pitch_inc | ||
9 | spk key_5 = rate_dec | ||
10 | spk key_6 = rate_inc | ||
11 | key_kpasterisk = toggle_cursoring | ||
12 | ctrl spk key_8 = toggle_cursoring | ||
13 | spk key_kpasterisk = speakup_goto | ||
14 | spk key_f1 = speakup_help | ||
15 | spk key_f2 = set_win | ||
16 | spk key_f3 = clear_win | ||
17 | spk key_f4 = enable_win | ||
18 | spk key_f5 = edit_some | ||
19 | spk key_f6 = edit_most | ||
20 | spk key_f7 = edit_delim | ||
21 | spk key_f8 = edit_repeat | ||
22 | shift spk key_f9 = edit_exnum | ||
23 | key_kp7 = say_prev_line | ||
24 | spk key_kp7 = left_edge | ||
25 | key_kp8 = say_line | ||
26 | double key_kp8 = say_line_indent | ||
27 | spk key_kp8 = say_from_top | ||
28 | key_kp9 = say_next_line | ||
29 | spk key_kp9 = top_edge | ||
30 | key_kpminus = speakup_parked | ||
31 | spk key_kpminus = say_char_num | ||
32 | key_kp4 = say_prev_word | ||
33 | spk key_kp4 = say_from_left | ||
34 | key_kp5 = say_word | ||
35 | double key_kp5 = spell_word | ||
36 | spk key_kp5 = spell_phonetic | ||
37 | key_kp6 = say_next_word | ||
38 | spk key_kp6 = say_to_right | ||
39 | key_kpplus = say_screen | ||
40 | spk key_kpplus = say_win | ||
41 | key_kp1 = say_prev_char | ||
42 | spk key_kp1 = right_edge | ||
43 | key_kp2 = say_char | ||
44 | spk key_kp2 = say_to_bottom | ||
45 | double key_kp2 = say_phonetic_char | ||
46 | key_kp3 = say_next_char | ||
47 | spk key_kp3 = bottom_edge | ||
48 | key_kp0 = spk_key | ||
49 | key_kpdot = say_position | ||
50 | spk key_kpdot = say_attributes | ||
51 | key_kpenter = speakup_quiet | ||
52 | spk key_kpenter = speakup_off | ||
53 | key_sysrq = speech_kill | ||
54 | key_kpslash = speakup_cut | ||
55 | spk key_kpslash = speakup_paste | ||
56 | spk key_pageup = say_first_char | ||
57 | spk key_pagedown = say_last_char | ||
58 | key_capslock = spk_key | ||
59 | spk key_z = spk_lock | ||
60 | key_leftmeta = spk_key | ||
61 | ctrl spk key_0 = speakup_goto | ||
62 | spk key_u = say_prev_line | ||
63 | spk key_i = say_line | ||
64 | double spk key_i = say_line_indent | ||
65 | spk key_o = say_next_line | ||
66 | spk key_minus = speakup_parked | ||
67 | shift spk key_minus = say_char_num | ||
68 | spk key_j = say_prev_word | ||
69 | spk key_k = say_word | ||
70 | double spk key_k = spell_word | ||
71 | spk key_l = say_next_word | ||
72 | spk key_m = say_prev_char | ||
73 | spk key_comma = say_char | ||
74 | double spk key_comma = say_phonetic_char | ||
75 | spk key_dot = say_next_char | ||
76 | spk key_n = say_position | ||
77 | ctrl spk key_m = left_edge | ||
78 | ctrl spk key_y = top_edge | ||
79 | ctrl spk key_dot = right_edge | ||
80 | ctrl spk key_p = bottom_edge | ||
81 | spk key_apostrophe = say_screen | ||
82 | spk key_h = say_from_left | ||
83 | spk key_y = say_from_top | ||
84 | spk key_semicolon = say_to_right | ||
85 | spk key_p = say_to_bottom | ||
86 | spk key_slash = say_attributes | ||
87 | spk key_enter = speakup_quiet | ||
88 | ctrl spk key_enter = speakup_off | ||
89 | spk key_9 = speakup_cut | ||
90 | spk key_8 = speakup_paste | ||
91 | shift spk key_m = say_first_char | ||
92 | ctrl spk key_semicolon = say_last_char | ||
93 | spk key_r = read_all_doc | ||
diff --git a/drivers/staging/speakup/spk_priv.h b/drivers/staging/speakup/spk_priv.h new file mode 100644 index 00000000000..16ace4af68a --- /dev/null +++ b/drivers/staging/speakup/spk_priv.h | |||
@@ -0,0 +1,93 @@ | |||
1 | /* spk_priv.h | ||
2 | review functions for the speakup screen review package. | ||
3 | originally written by: Kirk Reiser and Andy Berdan. | ||
4 | |||
5 | extensively modified by David Borowski. | ||
6 | |||
7 | Copyright (C) 1998 Kirk Reiser. | ||
8 | Copyright (C) 2003 David Borowski. | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | GNU General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program; if not, write to the Free Software | ||
22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | #ifndef _SPEAKUP_PRIVATE_H | ||
25 | #define _SPEAKUP_PRIVATE_H | ||
26 | |||
27 | #include "spk_types.h" | ||
28 | #include "spk_priv_keyinfo.h" | ||
29 | |||
30 | #ifndef pr_warn | ||
31 | #define pr_warn(fmt, arg...) printk(KERN_WARNING fmt, ##arg) | ||
32 | #endif | ||
33 | |||
34 | #define V_LAST_VAR { MAXVARS } | ||
35 | #define SPACE 0x20 | ||
36 | #define SYNTH_CHECK 20030716 /* today's date ought to do for check value */ | ||
37 | /* synth flags, for odd synths */ | ||
38 | #define SF_DEC 1 /* to fiddle puncs in alpha strings so it doesn't spell */ | ||
39 | #ifdef MODULE | ||
40 | #define SYNTH_START 1 | ||
41 | #else | ||
42 | #define SYNTH_START 0 | ||
43 | #endif | ||
44 | |||
45 | #define KT_SPKUP 15 | ||
46 | |||
47 | extern struct serial_state *spk_serial_init(int index); | ||
48 | extern void stop_serial_interrupt(void); | ||
49 | extern int wait_for_xmitr(void); | ||
50 | extern unsigned char spk_serial_in(void); | ||
51 | extern unsigned char spk_serial_in_nowait(void); | ||
52 | extern int spk_serial_out(const char ch); | ||
53 | extern void spk_serial_release(void); | ||
54 | |||
55 | extern char synth_buffer_getc(void); | ||
56 | extern char synth_buffer_peek(void); | ||
57 | extern int synth_buffer_empty(void); | ||
58 | extern struct var_t *get_var(enum var_id_t var_id); | ||
59 | extern ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
60 | char *buf); | ||
61 | extern ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
62 | const char *buf, size_t count); | ||
63 | |||
64 | extern int serial_synth_probe(struct spk_synth *synth); | ||
65 | extern const char *spk_synth_immediate(struct spk_synth *synth, const char *buff); | ||
66 | extern void spk_do_catch_up(struct spk_synth *synth); | ||
67 | extern void spk_synth_flush(struct spk_synth *synth); | ||
68 | extern int spk_synth_is_alive_nop(struct spk_synth *synth); | ||
69 | extern int spk_synth_is_alive_restart(struct spk_synth *synth); | ||
70 | extern void synth_printf(const char *buf, ...); | ||
71 | extern int synth_request_region(u_long, u_long); | ||
72 | extern int synth_release_region(u_long, u_long); | ||
73 | extern int synth_add(struct spk_synth *in_synth); | ||
74 | extern void synth_remove(struct spk_synth *in_synth); | ||
75 | |||
76 | extern struct speakup_info_t speakup_info; | ||
77 | |||
78 | extern struct var_t synth_time_vars[]; | ||
79 | |||
80 | /* Protect the whole speakup machinery, must be taken at each kernel->speakup | ||
81 | * transition and released at all corresponding speakup->kernel transitions | ||
82 | * (flags must be the same variable between lock/trylock and unlock). | ||
83 | * | ||
84 | * The progression thread only interferes with the speakup machinery through | ||
85 | * the synth buffer, and so only needs to take the lock while tinkering with | ||
86 | * it. | ||
87 | */ | ||
88 | /* Speakup needs to disable the keyboard IRQ, hence _irqsave/restore */ | ||
89 | #define spk_lock(flags) spin_lock_irqsave(&speakup_info.spinlock, flags) | ||
90 | #define spk_trylock(flags) spin_trylock_irqsave(&speakup_info.spinlock, flags) | ||
91 | #define spk_unlock(flags) spin_unlock_irqrestore(&speakup_info.spinlock, flags) | ||
92 | |||
93 | #endif | ||
diff --git a/drivers/staging/speakup/spk_priv_keyinfo.h b/drivers/staging/speakup/spk_priv_keyinfo.h new file mode 100644 index 00000000000..3fd4b82f84a --- /dev/null +++ b/drivers/staging/speakup/spk_priv_keyinfo.h | |||
@@ -0,0 +1,110 @@ | |||
1 | /* spk_priv.h | ||
2 | review functions for the speakup screen review package. | ||
3 | originally written by: Kirk Reiser and Andy Berdan. | ||
4 | |||
5 | extensively modified by David Borowski. | ||
6 | |||
7 | Copyright (C) 1998 Kirk Reiser. | ||
8 | Copyright (C) 2003 David Borowski. | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | GNU General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program; if not, write to the Free Software | ||
22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #ifndef _SPEAKUP_KEYINFO_H | ||
26 | #define _SPEAKUP_KEYINFO_H | ||
27 | |||
28 | #define FIRST_SYNTH_VAR RATE | ||
29 | /* 0 is reserved for no remap */ | ||
30 | #define SPEAKUP_GOTO 0x01 | ||
31 | #define SPEECH_KILL 0x02 | ||
32 | #define SPEAKUP_QUIET 0x03 | ||
33 | #define SPEAKUP_CUT 0x04 | ||
34 | #define SPEAKUP_PASTE 0x05 | ||
35 | #define SAY_FIRST_CHAR 0x06 | ||
36 | #define SAY_LAST_CHAR 0x07 | ||
37 | #define SAY_CHAR 0x08 | ||
38 | #define SAY_PREV_CHAR 0x09 | ||
39 | #define SAY_NEXT_CHAR 0x0a | ||
40 | #define SAY_WORD 0x0b | ||
41 | #define SAY_PREV_WORD 0x0c | ||
42 | #define SAY_NEXT_WORD 0x0d | ||
43 | #define SAY_LINE 0x0e | ||
44 | #define SAY_PREV_LINE 0x0f | ||
45 | #define SAY_NEXT_LINE 0x10 | ||
46 | #define TOP_EDGE 0x11 | ||
47 | #define BOTTOM_EDGE 0x12 | ||
48 | #define LEFT_EDGE 0x13 | ||
49 | #define RIGHT_EDGE 0x14 | ||
50 | #define SPELL_PHONETIC 0x15 | ||
51 | #define SPELL_WORD 0x16 | ||
52 | #define SAY_SCREEN 0x17 | ||
53 | #define SAY_POSITION 0x18 | ||
54 | #define SAY_ATTRIBUTES 0x19 | ||
55 | #define SPEAKUP_OFF 0x1a | ||
56 | #define SPEAKUP_PARKED 0x1b | ||
57 | #define SAY_LINE_INDENT 0x1c | ||
58 | #define SAY_FROM_TOP 0x1d | ||
59 | #define SAY_TO_BOTTOM 0x1e | ||
60 | #define SAY_FROM_LEFT 0x1f | ||
61 | #define SAY_TO_RIGHT 0x20 | ||
62 | #define SAY_CHAR_NUM 0x21 | ||
63 | #define EDIT_SOME 0x22 | ||
64 | #define EDIT_MOST 0x23 | ||
65 | #define SAY_PHONETIC_CHAR 0x24 | ||
66 | #define EDIT_DELIM 0x25 | ||
67 | #define EDIT_REPEAT 0x26 | ||
68 | #define EDIT_EXNUM 0x27 | ||
69 | #define SET_WIN 0x28 | ||
70 | #define CLEAR_WIN 0x29 | ||
71 | #define ENABLE_WIN 0x2a | ||
72 | #define SAY_WIN 0x2b | ||
73 | #define SPK_LOCK 0x2c | ||
74 | #define SPEAKUP_HELP 0x2d | ||
75 | #define TOGGLE_CURSORING 0x2e | ||
76 | #define READ_ALL_DOC 0x2f | ||
77 | #define SPKUP_MAX_FUNC 0x30 /* one greater than the last func handler */ | ||
78 | |||
79 | #define SPK_KEY 0x80 | ||
80 | #define FIRST_EDIT_BITS 0x22 | ||
81 | |||
82 | #define FIRST_SET_VAR SPELL_DELAY | ||
83 | #define VAR_START 0x40 /* increase if adding more than 0x3f functions */ | ||
84 | |||
85 | /* keys for setting variables, must be ordered same as the enum for var_ids */ | ||
86 | /* with dec being even and inc being 1 greater */ | ||
87 | #define SPELL_DELAY_DEC VAR_START+0 | ||
88 | #define SPELL_DELAY_INC SPELL_DELAY_DEC+1 | ||
89 | #define PUNC_LEVEL_DEC SPELL_DELAY_DEC+2 | ||
90 | #define PUNC_LEVEL_INC PUNC_LEVEL_DEC+1 | ||
91 | #define READING_PUNC_DEC PUNC_LEVEL_DEC+2 | ||
92 | #define READING_PUNC_INC READING_PUNC_DEC+1 | ||
93 | #define ATTRIB_BLEEP_DEC READING_PUNC_DEC+2 | ||
94 | #define ATTRIB_BLEEP_INC ATTRIB_BLEEP_DEC+1 | ||
95 | #define BLEEPS_DEC ATTRIB_BLEEP_DEC+2 | ||
96 | #define BLEEPS_INC BLEEPS_DEC+1 | ||
97 | #define RATE_DEC BLEEPS_DEC+2 | ||
98 | #define RATE_INC RATE_DEC+1 | ||
99 | #define PITCH_DEC RATE_DEC+2 | ||
100 | #define PITCH_INC PITCH_DEC+1 | ||
101 | #define VOL_DEC PITCH_DEC+2 | ||
102 | #define VOL_INC VOL_DEC+1 | ||
103 | #define TONE_DEC VOL_DEC+2 | ||
104 | #define TONE_INC TONE_DEC+1 | ||
105 | #define PUNCT_DEC TONE_DEC+2 | ||
106 | #define PUNCT_INC PUNCT_DEC+1 | ||
107 | #define VOICE_DEC PUNCT_DEC+2 | ||
108 | #define VOICE_INC VOICE_DEC+1 | ||
109 | |||
110 | #endif | ||
diff --git a/drivers/staging/speakup/spk_types.h b/drivers/staging/speakup/spk_types.h new file mode 100644 index 00000000000..840bddb6410 --- /dev/null +++ b/drivers/staging/speakup/spk_types.h | |||
@@ -0,0 +1,193 @@ | |||
1 | #ifndef SPEAKUP_TYPES_H | ||
2 | #define SPEAKUP_TYPES_H | ||
3 | |||
4 | /* | ||
5 | * This file includes all of the typedefs and structs used in speakup. | ||
6 | */ | ||
7 | |||
8 | #include <linux/types.h> | ||
9 | #include <linux/fs.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/wait.h> /* for wait_queue */ | ||
13 | #include <linux/init.h> /* for __init */ | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/vt_kern.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | #include <linux/mutex.h> | ||
18 | #include <linux/io.h> /* for inb_p, outb_p, inb, outb, etc... */ | ||
19 | |||
20 | enum var_type_t { | ||
21 | VAR_NUM = 0, | ||
22 | VAR_TIME, | ||
23 | VAR_STRING, | ||
24 | VAR_PROC | ||
25 | }; | ||
26 | |||
27 | enum { | ||
28 | E_DEFAULT = 0, | ||
29 | E_SET, | ||
30 | E_INC, | ||
31 | E_DEC, | ||
32 | E_NEW_DEFAULT, | ||
33 | }; | ||
34 | |||
35 | enum var_id_t { | ||
36 | VERSION = 0, SYNTH, SILENT, SYNTH_DIRECT, | ||
37 | KEYMAP, CHARS, | ||
38 | PUNC_SOME, PUNC_MOST, PUNC_ALL, | ||
39 | DELIM, REPEATS, EXNUMBER, | ||
40 | DELAY, TRIGGER, JIFFY, FULL, /* all timers must be together */ | ||
41 | BLEEP_TIME, CURSOR_TIME, BELL_POS, | ||
42 | SAY_CONTROL, SAY_WORD_CTL, NO_INTERRUPT, KEY_ECHO, | ||
43 | SPELL_DELAY, PUNC_LEVEL, READING_PUNC, | ||
44 | ATTRIB_BLEEP, BLEEPS, | ||
45 | RATE, PITCH, VOL, TONE, PUNCT, VOICE, FREQUENCY, LANG, DIRECT, | ||
46 | CAPS_START, CAPS_STOP, CHARTAB, | ||
47 | MAXVARS | ||
48 | }; | ||
49 | |||
50 | typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch, | ||
51 | u_short key); | ||
52 | |||
53 | #define COLOR_BUFFER_SIZE 160 | ||
54 | |||
55 | struct spk_highlight_color_track{ | ||
56 | /* Count of each background color */ | ||
57 | unsigned int bgcount[8]; | ||
58 | /* Buffer for characters drawn with each background color */ | ||
59 | char highbuf[8][COLOR_BUFFER_SIZE]; | ||
60 | /* Current index into highbuf */ | ||
61 | unsigned int highsize[8]; | ||
62 | /* Reading Position for each color */ | ||
63 | u_long rpos[8], rx[8], ry[8]; | ||
64 | /* Real Cursor Y Position */ | ||
65 | ulong cy; | ||
66 | }; | ||
67 | |||
68 | struct st_spk_t { | ||
69 | u_long reading_x, cursor_x; | ||
70 | u_long reading_y, cursor_y; | ||
71 | u_long reading_pos, cursor_pos; | ||
72 | u_long go_x, go_pos; | ||
73 | u_long w_top, w_bottom, w_left, w_right; | ||
74 | u_char w_start, w_enabled; | ||
75 | u_char reading_attr, old_attr; | ||
76 | char parked, shut_up; | ||
77 | struct spk_highlight_color_track ht; | ||
78 | int tty_stopped; | ||
79 | }; | ||
80 | |||
81 | /* now some defines to make these easier to use. */ | ||
82 | #define spk_shut_up speakup_console[vc->vc_num]->shut_up | ||
83 | #define spk_killed (speakup_console[vc->vc_num]->shut_up & 0x40) | ||
84 | #define spk_x speakup_console[vc->vc_num]->reading_x | ||
85 | #define spk_cx speakup_console[vc->vc_num]->cursor_x | ||
86 | #define spk_y speakup_console[vc->vc_num]->reading_y | ||
87 | #define spk_cy speakup_console[vc->vc_num]->cursor_y | ||
88 | #define spk_pos (speakup_console[vc->vc_num]->reading_pos) | ||
89 | #define spk_cp speakup_console[vc->vc_num]->cursor_pos | ||
90 | #define goto_pos (speakup_console[vc->vc_num]->go_pos) | ||
91 | #define goto_x (speakup_console[vc->vc_num]->go_x) | ||
92 | #define win_top (speakup_console[vc->vc_num]->w_top) | ||
93 | #define win_bottom (speakup_console[vc->vc_num]->w_bottom) | ||
94 | #define win_left (speakup_console[vc->vc_num]->w_left) | ||
95 | #define win_right (speakup_console[vc->vc_num]->w_right) | ||
96 | #define win_start (speakup_console[vc->vc_num]->w_start) | ||
97 | #define win_enabled (speakup_console[vc->vc_num]->w_enabled) | ||
98 | #define spk_attr speakup_console[vc->vc_num]->reading_attr | ||
99 | #define spk_old_attr speakup_console[vc->vc_num]->old_attr | ||
100 | #define spk_parked speakup_console[vc->vc_num]->parked | ||
101 | |||
102 | struct st_var_header { | ||
103 | char *name; | ||
104 | enum var_id_t var_id; | ||
105 | enum var_type_t var_type; | ||
106 | void *p_val; /* ptr to programs variable to store value */ | ||
107 | void *data; /* ptr to the vars data */ | ||
108 | }; | ||
109 | |||
110 | struct num_var_t { | ||
111 | char *synth_fmt; | ||
112 | int default_val; | ||
113 | int low; | ||
114 | int high; | ||
115 | short offset, multiplier; /* for fiddling rates etc. */ | ||
116 | char *out_str; /* if synth needs char representation of number */ | ||
117 | int value; /* current value */ | ||
118 | }; | ||
119 | |||
120 | struct punc_var_t { | ||
121 | enum var_id_t var_id; | ||
122 | short value; | ||
123 | }; | ||
124 | |||
125 | struct string_var_t { | ||
126 | char *default_val; | ||
127 | }; | ||
128 | |||
129 | struct var_t { | ||
130 | enum var_id_t var_id; | ||
131 | union { | ||
132 | struct num_var_t n; | ||
133 | struct string_var_t s; | ||
134 | } u; | ||
135 | }; | ||
136 | |||
137 | struct st_bits_data { /* punc, repeats, word delim bits */ | ||
138 | char *name; | ||
139 | char *value; | ||
140 | short mask; | ||
141 | }; | ||
142 | |||
143 | struct synth_indexing { | ||
144 | char *command; | ||
145 | unsigned char lowindex; | ||
146 | unsigned char highindex; | ||
147 | unsigned char currindex; | ||
148 | }; | ||
149 | |||
150 | struct spk_synth { | ||
151 | const char *name; | ||
152 | const char *version; | ||
153 | const char *long_name; | ||
154 | const char *init; | ||
155 | char procspeech; | ||
156 | char clear; | ||
157 | int delay; | ||
158 | int trigger; | ||
159 | int jiffies; | ||
160 | int full; | ||
161 | int ser; | ||
162 | short flags; | ||
163 | short startup; | ||
164 | const int checkval; /* for validating a proper synth module */ | ||
165 | struct var_t *vars; | ||
166 | int *default_pitch; | ||
167 | int *default_vol; | ||
168 | int (*probe)(struct spk_synth *synth); | ||
169 | void (*release)(void); | ||
170 | const char *(*synth_immediate)(struct spk_synth *synth, const char *buff); | ||
171 | void (*catch_up)(struct spk_synth *synth); | ||
172 | void (*flush)(struct spk_synth *synth); | ||
173 | int (*is_alive)(struct spk_synth *synth); | ||
174 | int (*synth_adjust)(struct st_var_header *var); | ||
175 | void (*read_buff_add)(u_char); | ||
176 | unsigned char (*get_index)(void); | ||
177 | struct synth_indexing indexing; | ||
178 | int alive; | ||
179 | struct attribute_group attributes; | ||
180 | }; | ||
181 | |||
182 | struct speakup_info_t { | ||
183 | spinlock_t spinlock; | ||
184 | int port_tts; | ||
185 | int flushing; | ||
186 | }; | ||
187 | |||
188 | struct bleep { | ||
189 | short freq; | ||
190 | unsigned long jiffies; | ||
191 | int active; | ||
192 | }; | ||
193 | #endif | ||
diff --git a/drivers/staging/speakup/spkguide.txt b/drivers/staging/speakup/spkguide.txt new file mode 100644 index 00000000000..24362eb7b8f --- /dev/null +++ b/drivers/staging/speakup/spkguide.txt | |||
@@ -0,0 +1,1575 @@ | |||
1 | |||
2 | The Speakup User's Guide | ||
3 | For Speakup 3.1.2 and Later | ||
4 | By Gene Collins | ||
5 | Updated by others | ||
6 | Last modified on Mon Sep 27 14:26:31 2010 | ||
7 | Document version 1.3 | ||
8 | |||
9 | Copyright (c) 2005 Gene Collins | ||
10 | Copyright (c) 2008 Samuel Thibault | ||
11 | Copyright (c) 2009, 2010 the Speakup Team | ||
12 | |||
13 | Permission is granted to copy, distribute and/or modify this document | ||
14 | under the terms of the GNU Free Documentation License, Version 1.2 or | ||
15 | any later version published by the Free Software Foundation; with no | ||
16 | Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A | ||
17 | copy of the license is included in the section entitled "GNU Free | ||
18 | Documentation License". | ||
19 | |||
20 | Preface | ||
21 | |||
22 | The purpose of this document is to familiarize users with the user | ||
23 | interface to Speakup, a Linux Screen Reader. If you need instructions | ||
24 | for installing or obtaining Speakup, visit the web site at | ||
25 | http://linux-speakup.org/. Speakup is a set of patches to the standard | ||
26 | Linux kernel source tree. It can be built as a series of modules, or as | ||
27 | a part of a monolithic kernel. These details are beyond the scope of | ||
28 | this manual, but the user may need to be aware of the module | ||
29 | capabilities, depending on how your system administrator has installed | ||
30 | Speakup. If Speakup is built as a part of a monolithic kernel, and the | ||
31 | user is using a hardware synthesizer, then Speakup will be able to | ||
32 | provide speech access from the time the kernel is loaded, until the time | ||
33 | the system is shutdown. This means that if you have obtained Linux | ||
34 | installation media for a distribution which includes Speakup as a part | ||
35 | of its kernel, you will be able, as a blind person, to install Linux | ||
36 | with speech access unaided by a sighted person. Again, these details | ||
37 | are beyond the scope of this manual, but the user should be aware of | ||
38 | them. See the web site mentioned above for further details. | ||
39 | |||
40 | 1. Starting Speakup | ||
41 | |||
42 | If your system administrator has installed Speakup to work with your | ||
43 | specific synthesizer by default, then all you need to do to use Speakup | ||
44 | is to boot your system, and Speakup should come up talking. This | ||
45 | assumes of course that your synthesizer is a supported hardware | ||
46 | synthesizer, and that it is either installed in or connected to your | ||
47 | system, and is if necessary powered on. | ||
48 | |||
49 | It is possible, however, that Speakup may have been compiled into the | ||
50 | kernel with no default synthesizer. It is even possible that your | ||
51 | kernel has been compiled with support for some of the supported | ||
52 | synthesizers and not others. If you find that this is the case, and | ||
53 | your synthesizer is supported but not available, complain to the person | ||
54 | who compiled and installed your kernel. Or better yet, go to the web | ||
55 | site, and learn how to patch Speakup into your own kernel source, and | ||
56 | build and install your own kernel. | ||
57 | |||
58 | If your kernel has been compiled with Speakup, and has no default | ||
59 | synthesizer set, or you would like to use a different synthesizer than | ||
60 | the default one, then you may issue the following command at the boot | ||
61 | prompt of your boot loader. | ||
62 | |||
63 | linux speakup.synth=ltlk | ||
64 | |||
65 | This command would tell Speakup to look for and use a LiteTalk or | ||
66 | DoubleTalk LT at boot up. You may replace the ltlk synthesizer keyword | ||
67 | with the keyword for whatever synthesizer you wish to use. The | ||
68 | speakup.synth parameter will accept the following keywords, provided | ||
69 | that support for the related synthesizers has been built into the | ||
70 | kernel. | ||
71 | |||
72 | acntsa -- Accent SA | ||
73 | acntpc -- Accent PC | ||
74 | apollo -- Apollo | ||
75 | audptr -- Audapter | ||
76 | bns -- Braille 'n Speak | ||
77 | dectlk -- DecTalk Express (old and new, db9 serial only) | ||
78 | decext -- DecTalk (old) External | ||
79 | dtlk -- DoubleTalk PC | ||
80 | keypc -- Keynote Gold PC | ||
81 | ltlk -- DoubleTalk LT, LiteTalk, or external Tripletalk (db9 serial only) | ||
82 | spkout -- Speak Out | ||
83 | txprt -- Transport | ||
84 | dummy -- Plain text terminal | ||
85 | |||
86 | Note: Speakup does * NOT * support usb connections! Speakup also does * | ||
87 | NOT * support the internal Tripletalk! | ||
88 | |||
89 | Speakup does support two other synthesizers, but because they work in | ||
90 | conjunction with other software, they must be loaded as modules after | ||
91 | their related software is loaded, and so are not available at boot up. | ||
92 | These are as follows: | ||
93 | |||
94 | decpc -- DecTalk PC (not available at boot up) | ||
95 | soft -- One of several software synthesizers (not available at boot up) | ||
96 | |||
97 | See the sections on loading modules and software synthesizers later in | ||
98 | this manual for further details. It should be noted here that the | ||
99 | speakup.synth boot parameter will have no effect if Speakup has been | ||
100 | compiled as modules. In order for Speakup modules to be loaded during | ||
101 | the boot process, such action must be configured by your system | ||
102 | administrator. This will mean that you will hear some, but not all, of | ||
103 | the bootup messages. | ||
104 | |||
105 | 2. Basic operation | ||
106 | |||
107 | Once you have booted the system, and if necessary, have supplied the | ||
108 | proper bootup parameter for your synthesizer, Speakup will begin | ||
109 | talking as soon as the kernel is loaded. In fact, it will talk a lot! | ||
110 | It will speak all the boot up messages that the kernel prints on the | ||
111 | screen during the boot process. This is because Speakup is not a | ||
112 | separate screen reader, but is actually built into the operating | ||
113 | system. Since almost all console applications must print text on the | ||
114 | screen using the kernel, and must get their keyboard input through the | ||
115 | kernel, they are automatically handled properly by Speakup. There are a | ||
116 | few exceptions, but we'll come to those later. | ||
117 | |||
118 | Note: In this guide I will refer to the numeric keypad as the keypad. | ||
119 | This is done because the speakupmap.map file referred to later in this | ||
120 | manual uses the term keypad instead of numeric keypad. Also I'm lazy | ||
121 | and would rather only type one word. So keypad it is. Got it? Good. | ||
122 | |||
123 | Most of the Speakup review keys are located on the keypad at the far | ||
124 | right of the keyboard. The numlock key should be off, in order for these | ||
125 | to work. If you toggle the numlock on, the keypad will produce numbers, | ||
126 | which is exactly what you want for spreadsheets and such. For the | ||
127 | purposes of this guide, you should have the numlock turned off, which is | ||
128 | its default state at bootup. | ||
129 | |||
130 | You probably won't want to listen to all the bootup messages every time | ||
131 | you start your system, though it's a good idea to listen to them at | ||
132 | least once, just so you'll know what kind of information is available to | ||
133 | you during the boot process. You can always review these messages after | ||
134 | bootup with the command: | ||
135 | |||
136 | dmesg | more | ||
137 | |||
138 | In order to speed the boot process, and to silence the speaking of the | ||
139 | bootup messages, just press the keypad enter key. This key is located | ||
140 | in the bottom right corner of the keypad. Speakup will shut up and stay | ||
141 | that way, until you press another key. | ||
142 | |||
143 | You can check to see if the boot process has completed by pressing the 8 | ||
144 | key on the keypad, which reads the current line. This also has the | ||
145 | effect of starting Speakup talking again, so you can press keypad enter | ||
146 | to silence it again if the boot process has not completed. | ||
147 | |||
148 | When the boot process is complete, you will arrive at a "login" prompt. | ||
149 | At this point, you'll need to type in your user id and password, as | ||
150 | provided by your system administrator. You will hear Speakup speak the | ||
151 | letters of your user id as you type it, but not the password. This is | ||
152 | because the password is not displayed on the screen for security | ||
153 | reasons. This has nothing to do with Speakup, it's a Linux security | ||
154 | feature. | ||
155 | |||
156 | Once you've logged in, you can run any Linux command or program which is | ||
157 | allowed by your user id. Normal users will not be able to run programs | ||
158 | which require root privileges. | ||
159 | |||
160 | When you are running a program or command, Speakup will automatically | ||
161 | speak new text as it arrives on the screen. You can at any time silence | ||
162 | the speech with keypad enter, or use any of the Speakup review keys. | ||
163 | |||
164 | Here are some basic Speakup review keys, and a short description of what | ||
165 | they do. | ||
166 | |||
167 | keypad 1 -- read previous character | ||
168 | keypad 2 -- read current character (pressing keypad 2 twice rapidly will speak | ||
169 | the current character phonetically) | ||
170 | keypad 3 -- read next character | ||
171 | keypad 4 -- read previous word | ||
172 | keypad 5 -- read current word (press twice rapidly to spell the current word) | ||
173 | keypad 6 -- read next word | ||
174 | keypad 7 -- read previous line | ||
175 | keypad 8 -- read current line (press twice rapidly to hear how much the | ||
176 | text on the current line is indented) | ||
177 | keypad 9 -- read next line | ||
178 | keypad period -- speak current cursor position and announce current | ||
179 | virtual console | ||
180 | |||
181 | It's also worth noting that the insert key on the keypad is mapped | ||
182 | as the speakup key. Instead of pressing and releasing this key, as you | ||
183 | do under DOS or Windows, you hold it like a shift key, and press other | ||
184 | keys in combination with it. For example, repeatedly holding keypad | ||
185 | insert, from now on called speakup, and keypad enter will toggle the | ||
186 | speaking of new text on the screen on and off. This is not the same as | ||
187 | just pressing keypad enter by itself, which just silences the speech | ||
188 | until you hit another key. When you hit speakup plus keypad enter, | ||
189 | Speakup will say, "You turned me off.", or "Hey, that's better." When | ||
190 | Speakup is turned off, no new text on the screen will be spoken. You | ||
191 | can still use the reading controls to review the screen however. | ||
192 | |||
193 | 3. Using the Speakup Help System | ||
194 | |||
195 | In order to enter the Speakup help system, press and hold the speakup | ||
196 | key (remember that this is the keypad insert key), and press the f1 key. | ||
197 | You will hear the message: | ||
198 | |||
199 | "Press space to leave help, cursor up or down to scroll, or a letter to | ||
200 | go to commands in list." | ||
201 | |||
202 | When you press the spacebar to leave the help system, you will hear: | ||
203 | |||
204 | "Leaving help." | ||
205 | |||
206 | While you are in the Speakup help system, you can scroll up or down | ||
207 | through the list of available commands using the cursor keys. The list | ||
208 | of commands is arranged in alphabetical order. If you wish to jump to | ||
209 | commands in a specific part of the alphabet, you may press the letter of | ||
210 | the alphabet you wish to jump to. | ||
211 | |||
212 | You can also just explore by typing keyboard keys. Pressing keys will | ||
213 | cause Speakup to speak the command associated with that key. For | ||
214 | example, if you press the keypad 8 key, you will hear: | ||
215 | |||
216 | "Keypad 8 is line, say current." | ||
217 | |||
218 | You'll notice that some commands do not have keys assigned to them. | ||
219 | This is because they are very infrequently used commands, and are also | ||
220 | accessible through the sys system. We'll discuss the sys system later | ||
221 | in this manual. | ||
222 | |||
223 | You'll also notice that some commands have two keys assigned to them. | ||
224 | This is because Speakup has a built in set of alternative key bindings | ||
225 | for laptop users. The alternate speakup key is the caps lock key. You | ||
226 | can press and hold the caps lock key, while pressing an alternate | ||
227 | speakup command key to activate the command. On most laptops, the | ||
228 | numeric keypad is defined as the keys in the j k l area of the keyboard. | ||
229 | |||
230 | There is usually a function key which turns this keypad function on and | ||
231 | off, and some other key which controls the numlock state. Toggling the | ||
232 | keypad functionality on and off can become a royal pain. So, Speakup | ||
233 | gives you a simple way to get at an alternative set of key mappings for | ||
234 | your laptop. These are also available by default on desktop systems, | ||
235 | because Speakup does not know whether it is running on a desktop or | ||
236 | laptop. So you may choose which set of Speakup keys to use. Some | ||
237 | system administrators may have chosen to compile Speakup for a desktop | ||
238 | system without this set of alternate key bindings, but these details are | ||
239 | beyond the scope of this manual. To use the caps lock for its normal | ||
240 | purpose, hold the shift key while toggling the caps lock on and off. We | ||
241 | should note here, that holding the caps lock key and pressing the z key | ||
242 | will toggle the alternate j k l keypad on and off. | ||
243 | |||
244 | 4. Keys and Their Assigned Commands | ||
245 | |||
246 | In this section, we'll go through a list of all the speakup keys and | ||
247 | commands. You can also get a list of commands and assigned keys from | ||
248 | the help system. | ||
249 | |||
250 | The following list was taken from the speakupmap.map file. Key | ||
251 | assignments are on the left of the equal sign, and the associated | ||
252 | Speakup commands are on the right. The designation "spk" means to press | ||
253 | and hold the speakup key, a.k.a. keypad insert, a.k.a. caps lock, while | ||
254 | pressing the other specified key. | ||
255 | |||
256 | spk key_f9 = punc_level_dec | ||
257 | spk key_f10 = punc_level_inc | ||
258 | spk key_f11 = reading_punc_dec | ||
259 | spk key_f12 = reading_punc_inc | ||
260 | spk key_1 = vol_dec | ||
261 | spk key_2 = vol_inc | ||
262 | spk key_3 = pitch_dec | ||
263 | spk key_4 = pitch_inc | ||
264 | spk key_5 = rate_dec | ||
265 | spk key_6 = rate_inc | ||
266 | key_kpasterisk = toggle_cursoring | ||
267 | spk key_kpasterisk = speakup_goto | ||
268 | spk key_f1 = speakup_help | ||
269 | spk key_f2 = set_win | ||
270 | spk key_f3 = clear_win | ||
271 | spk key_f4 = enable_win | ||
272 | spk key_f5 = edit_some | ||
273 | spk key_f6 = edit_most | ||
274 | spk key_f7 = edit_delim | ||
275 | spk key_f8 = edit_repeat | ||
276 | shift spk key_f9 = edit_exnum | ||
277 | key_kp7 = say_prev_line | ||
278 | spk key_kp7 = left_edge | ||
279 | key_kp8 = say_line | ||
280 | double key_kp8 = say_line_indent | ||
281 | spk key_kp8 = say_from_top | ||
282 | key_kp9 = say_next_line | ||
283 | spk key_kp9 = top_edge | ||
284 | key_kpminus = speakup_parked | ||
285 | spk key_kpminus = say_char_num | ||
286 | key_kp4 = say_prev_word | ||
287 | spk key_kp4 = say_from_left | ||
288 | key_kp5 = say_word | ||
289 | double key_kp5 = spell_word | ||
290 | spk key_kp5 = spell_phonetic | ||
291 | key_kp6 = say_next_word | ||
292 | spk key_kp6 = say_to_right | ||
293 | key_kpplus = say_screen | ||
294 | spk key_kpplus = say_win | ||
295 | key_kp1 = say_prev_char | ||
296 | spk key_kp1 = right_edge | ||
297 | key_kp2 = say_char | ||
298 | spk key_kp2 = say_to_bottom | ||
299 | double key_kp2 = say_phonetic_char | ||
300 | key_kp3 = say_next_char | ||
301 | spk key_kp3 = bottom_edge | ||
302 | key_kp0 = spk_key | ||
303 | key_kpdot = say_position | ||
304 | spk key_kpdot = say_attributes | ||
305 | key_kpenter = speakup_quiet | ||
306 | spk key_kpenter = speakup_off | ||
307 | key_sysrq = speech_kill | ||
308 | key_kpslash = speakup_cut | ||
309 | spk key_kpslash = speakup_paste | ||
310 | spk key_pageup = say_first_char | ||
311 | spk key_pagedown = say_last_char | ||
312 | key_capslock = spk_key | ||
313 | spk key_z = spk_lock | ||
314 | key_leftmeta = spk_key | ||
315 | ctrl spk key_0 = speakup_goto | ||
316 | spk key_u = say_prev_line | ||
317 | spk key_i = say_line | ||
318 | double spk key_i = say_line_indent | ||
319 | spk key_o = say_next_line | ||
320 | spk key_minus = speakup_parked | ||
321 | shift spk key_minus = say_char_num | ||
322 | spk key_j = say_prev_word | ||
323 | spk key_k = say_word | ||
324 | double spk key_k = spell_word | ||
325 | spk key_l = say_next_word | ||
326 | spk key_m = say_prev_char | ||
327 | spk key_comma = say_char | ||
328 | double spk key_comma = say_phonetic_char | ||
329 | spk key_dot = say_next_char | ||
330 | spk key_n = say_position | ||
331 | ctrl spk key_m = left_edge | ||
332 | ctrl spk key_y = top_edge | ||
333 | ctrl spk key_dot = right_edge | ||
334 | ctrl spk key_p = bottom_edge | ||
335 | spk key_apostrophe = say_screen | ||
336 | spk key_h = say_from_left | ||
337 | spk key_y = say_from_top | ||
338 | spk key_semicolon = say_to_right | ||
339 | spk key_p = say_to_bottom | ||
340 | spk key_slash = say_attributes | ||
341 | spk key_enter = speakup_quiet | ||
342 | ctrl spk key_enter = speakup_off | ||
343 | spk key_9 = speakup_cut | ||
344 | spk key_8 = speakup_paste | ||
345 | shift spk key_m = say_first_char | ||
346 | ctrl spk key_semicolon = say_last_char | ||
347 | |||
348 | 5. The Speakup Sys System | ||
349 | |||
350 | The Speakup screen reader also creates a speakup subdirectory as a part | ||
351 | of the sys system. | ||
352 | |||
353 | As a convenience, run as root | ||
354 | |||
355 | ln -s /sys/accessibility/speakup /speakup | ||
356 | |||
357 | to directly access speakup parameters from /speakup. | ||
358 | You can see these entries by typing the command: | ||
359 | |||
360 | ls -1 /speakup/* | ||
361 | |||
362 | If you issue the above ls command, you will get back something like | ||
363 | this: | ||
364 | |||
365 | /speakup/attrib_bleep | ||
366 | /speakup/bell_pos | ||
367 | /speakup/bleep_time | ||
368 | /speakup/bleeps | ||
369 | /speakup/cursor_time | ||
370 | /speakup/delimiters | ||
371 | /speakup/ex_num | ||
372 | /speakup/key_echo | ||
373 | /speakup/keymap | ||
374 | /speakup/no_interrupt | ||
375 | /speakup/punc_all | ||
376 | /speakup/punc_level | ||
377 | /speakup/punc_most | ||
378 | /speakup/punc_some | ||
379 | /speakup/reading_punc | ||
380 | /speakup/repeats | ||
381 | /speakup/say_control | ||
382 | /speakup/say_word_ctl | ||
383 | /speakup/silent | ||
384 | /speakup/spell_delay | ||
385 | /speakup/synth | ||
386 | /speakup/synth_direct | ||
387 | /speakup/version | ||
388 | |||
389 | /speakup/i18n: | ||
390 | announcements | ||
391 | characters | ||
392 | chartab | ||
393 | colors | ||
394 | ctl_keys | ||
395 | formatted | ||
396 | function_names | ||
397 | key_names | ||
398 | states | ||
399 | |||
400 | /speakup/soft: | ||
401 | caps_start | ||
402 | caps_stop | ||
403 | delay_time | ||
404 | direct | ||
405 | freq | ||
406 | full_time | ||
407 | jiffy_delta | ||
408 | pitch | ||
409 | punct | ||
410 | rate | ||
411 | tone | ||
412 | trigger_time | ||
413 | voice | ||
414 | vol | ||
415 | |||
416 | Notice the two subdirectories of /speakup: /speakup/i18n and | ||
417 | /speakup/soft. | ||
418 | The i18n subdirectory is described in a later section. | ||
419 | The files under /speakup/soft represent settings that are specific to the | ||
420 | driver for the software synthesizer. If you use the LiteTalk, your | ||
421 | synthesizer-specific settings would be found in /speakup/ltlk. In other words, | ||
422 | a subdirectory named /speakup/KWD is created to hold parameters specific | ||
423 | to the device whose keyword is KWD. | ||
424 | These parameters include volume, rate, pitch, and others. | ||
425 | |||
426 | In addition to using the Speakup hot keys to change such things as | ||
427 | volume, pitch, and rate, you can also echo values to the appropriate | ||
428 | entry in the /speakup directory. This is very useful, since it | ||
429 | lets you control Speakup parameters from within a script. How you | ||
430 | would write such scripts is somewhat beyond the scope of this manual, | ||
431 | but I will include a couple of simple examples here to give you a | ||
432 | general idea of what such scripts can do. | ||
433 | |||
434 | Suppose for example, that you wanted to control both the punctuation | ||
435 | level and the reading punctuation level at the same time. For | ||
436 | simplicity, we'll call them punc0, punc1, punc2, and punc3. The scripts | ||
437 | might look something like this: | ||
438 | |||
439 | #!/bin/bash | ||
440 | # punc0 | ||
441 | # set punc and reading punc levels to 0 | ||
442 | echo 0 >/speakup/punc_level | ||
443 | echo 0 >/speakup/reading_punc | ||
444 | echo Punctuation level set to 0. | ||
445 | |||
446 | #!/bin/bash | ||
447 | # punc1 | ||
448 | # set punc and reading punc levels to 1 | ||
449 | echo 1 >/speakup/punc_level | ||
450 | echo 1 >/speakup/reading_punc | ||
451 | echo Punctuation level set to 1. | ||
452 | |||
453 | #!/bin/bash | ||
454 | # punc2 | ||
455 | # set punc and reading punc levels to 2 | ||
456 | echo 2 >/speakup/punc_level | ||
457 | echo 2 >/speakup/reading_punc | ||
458 | echo Punctuation level set to 2. | ||
459 | |||
460 | #!/bin/bash | ||
461 | # punc3 | ||
462 | # set punc and reading punc levels to 3 | ||
463 | echo 3 >/speakup/punc_level | ||
464 | echo 3 >/speakup/reading_punc | ||
465 | echo Punctuation level set to 3. | ||
466 | |||
467 | If you were to store these four small scripts in a directory in your | ||
468 | path, perhaps /usr/local/bin, and set the permissions to 755 with the | ||
469 | chmod command, then you could change the default reading punc and | ||
470 | punctuation levels at the same time by issuing just one command. For | ||
471 | example, if you were to execute the punc3 command at your shell prompt, | ||
472 | then the reading punc and punc level would both get set to 3. | ||
473 | |||
474 | I should note that the above scripts were written to work with bash, but | ||
475 | regardless of which shell you use, you should be able to do something | ||
476 | similar. | ||
477 | |||
478 | The Speakup sys system also has another interesting use. You can echo | ||
479 | Speakup parameters into the sys system in a script during system | ||
480 | startup, and speakup will return to your preferred parameters every time | ||
481 | the system is rebooted. | ||
482 | |||
483 | Most of the Speakup sys parameters can be manipulated by a regular user | ||
484 | on the system. However, there are a few parameters that are dangerous | ||
485 | enough that they should only be manipulated by the root user on your | ||
486 | system. There are even some parameters that are read only, and cannot | ||
487 | be written to at all. For example, the version entry in the Speakup | ||
488 | sys system is read only. This is because there is no reason for a user | ||
489 | to tamper with the version number which is reported by Speakup. Doing | ||
490 | an ls -l on /speakup/version will return this: | ||
491 | |||
492 | -r--r--r-- 1 root root 0 Mar 21 13:46 /speakup/version | ||
493 | |||
494 | As you can see, the version entry in the Speakup sys system is read | ||
495 | only, is owned by root, and belongs to the root group. Doing a cat of | ||
496 | /speakup/version will display the Speakup version number, like | ||
497 | this: | ||
498 | |||
499 | cat /speakup/version | ||
500 | Speakup v-2.00 CVS: Thu Oct 21 10:38:21 EDT 2004 | ||
501 | synth dtlk version 1.1 | ||
502 | |||
503 | The display shows the Speakup version number, along with the version | ||
504 | number of the driver for the current synthesizer. | ||
505 | |||
506 | Looking at entries in the Speakup sys system can be useful in many | ||
507 | ways. For example, you might wish to know what level your volume is set | ||
508 | at. You could type: | ||
509 | |||
510 | cat /speakup/KWD/vol | ||
511 | # Replace KWD with the keyword for your synthesizer, E.G., ltlk for LiteTalk. | ||
512 | 5 | ||
513 | |||
514 | The number five which comes back is the level at which the synthesizer | ||
515 | volume is set at. | ||
516 | |||
517 | All the entries in the Speakup sys system are readable, some are | ||
518 | writable by root only, and some are writable by everyone. Unless you | ||
519 | know what you are doing, you should probably leave the ones that are | ||
520 | writable by root only alone. Most of the names are self explanatory. | ||
521 | Vol for controlling volume, pitch for pitch, rate for controlling speaking | ||
522 | rate, etc. If you find one you aren't sure about, you can post a query | ||
523 | on the Speakup list. | ||
524 | |||
525 | 6. Changing Synthesizers | ||
526 | |||
527 | It is possible to change to a different synthesizer while speakup is | ||
528 | running. In other words, it is not necessary to reboot the system | ||
529 | in order to use a different synthesizer. You can simply echo the | ||
530 | synthesizer keyword to the /speakup/synth sys entry. | ||
531 | Depending on your situation, you may wish to echo none to the synth | ||
532 | sys entry, to disable speech while one synthesizer is disconnected and | ||
533 | a second one is connected in its place. Then echo the keyword for the | ||
534 | new synthesizer into the synth sys entry in order to start speech | ||
535 | with the newly connected synthesizer. See the list of synthesizer | ||
536 | keywords in section 1 to find the keyword which matches your synth. | ||
537 | |||
538 | 7. Loading modules | ||
539 | |||
540 | As mentioned earlier, Speakup can either be completely compiled into the | ||
541 | kernel, with the exception of the help module, or it can be compiled as | ||
542 | a series of modules. When compiled as modules, Speakup will only be | ||
543 | able to speak some of the bootup messages if your system administrator | ||
544 | has configured the system to load the modules at boo time. The modules | ||
545 | can be loaded after the file systems have been checked and mounted, or | ||
546 | from an initrd. There is a third possibility. Speakup can be compiled | ||
547 | with some components built into the kernel, and others as modules. As | ||
548 | we'll see in the next section, this is particularly useful when you are | ||
549 | working with software synthesizers. | ||
550 | |||
551 | If Speakup is completely compiled as modules, then you must use the | ||
552 | modprobe command to load Speakup. You do this by loading the module for | ||
553 | the synthesizer driver you wish to use. The driver modules are all | ||
554 | named speakup_<keyword>, where <keyword> is the keyword for the | ||
555 | synthesizer you want. So, in order to load the driver for the DecTalk | ||
556 | Express, you would type the following command: | ||
557 | |||
558 | modprobe speakup_dectlk | ||
559 | |||
560 | Issuing this command would load the DecTalk Express driver and all other | ||
561 | related Speakup modules necessary to get Speakup up and running. | ||
562 | |||
563 | To completely unload Speakup, again presuming that it is entirely built | ||
564 | as modules, you would give the command: | ||
565 | |||
566 | modprobe -r speakup_dectlk | ||
567 | |||
568 | The above command assumes you were running a DecTalk Express. If you | ||
569 | were using a different synth, then you would substitute its keyword in | ||
570 | place of dectlk. | ||
571 | |||
572 | If you have multiple drivers loaded, you need to unload all of them, in | ||
573 | order to completely unload Speakup. | ||
574 | For example, if you have loaded both the dectlk and ltlk drivers, use the | ||
575 | command: | ||
576 | modprobe -r speakup_dectlk speakup_ltlk | ||
577 | |||
578 | You cannot unload the driver for software synthesizers when a user-space | ||
579 | daemon is using /dev/softsynth. First, kill the daemon. Next, remove | ||
580 | the driver with the command: | ||
581 | modprobe -r speakup_soft | ||
582 | |||
583 | Now, suppose we have a situation where the main Speakup component | ||
584 | is built into the kernel, and some or all of the drivers are built as | ||
585 | modules. Since the main part of Speakup is compiled into the kernel, a | ||
586 | partial Speakup sys system has been created which we can take advantage | ||
587 | of by simply echoing the synthesizer keyword into the | ||
588 | /speakup/synth sys entry. This will cause the kernel to | ||
589 | automatically load the appropriate driver module, and start Speakup | ||
590 | talking. To switch to another synth, just echo a new keyword to the | ||
591 | synth sys entry. For example, to load the DoubleTalk LT driver, | ||
592 | you would type: | ||
593 | |||
594 | echo ltlk >/speakup/synth | ||
595 | |||
596 | You can use the modprobe -r command to unload driver modules, regardless | ||
597 | of whether the main part of Speakup has been built into the kernel or | ||
598 | not. | ||
599 | |||
600 | 8. Using Software Synthesizers | ||
601 | |||
602 | Using a software synthesizer requires that some other software be | ||
603 | installed and running on your system. For this reason, software | ||
604 | synthesizers are not available for use at bootup, or during a system | ||
605 | installation process. | ||
606 | There are two freely-available solutions for software speech: Espeakup and | ||
607 | Speech Dispatcher. | ||
608 | These are described in subsections 8.1 and 8.2, respectively. | ||
609 | |||
610 | During the rest of this section, we assume that speakup_soft is either | ||
611 | built in to your kernel, or loaded as a module. | ||
612 | |||
613 | If your system does not have udev installed , before you can use a | ||
614 | software synthesizer, you must have created the /dev/softsynth device. | ||
615 | If you have not already done so, issue the following commands as root: | ||
616 | |||
617 | cd /dev | ||
618 | mknod softsynth c 10 26 | ||
619 | |||
620 | While we are at it, we might just as well create the /dev/synth device, | ||
621 | which can be used to let user space programs send information to your | ||
622 | synthesizer. To create /dev/synth, change to the /dev directory, and | ||
623 | issue the following command as root: | ||
624 | |||
625 | mknod synth c 10 25 | ||
626 | |||
627 | of both. | ||
628 | |||
629 | 8.1. Espeakup | ||
630 | |||
631 | Espeakup is a connector between Speakup and the eSpeak software synthesizer. | ||
632 | Espeakup may already be available as a package for your distribution | ||
633 | of Linux. If it is not packaged, you need to install it manually. | ||
634 | You can find it in the contrib/ subdirectory of the Speakup sources. | ||
635 | The filename is espeakup-$VERSION.tar.bz2, where $VERSION | ||
636 | depends on the current release of Espeakup. The Speakup 3.1.2 source | ||
637 | ships with version 0.71 of Espeakup. | ||
638 | The README file included with the Espeakup sources describes the process | ||
639 | of manual installation. | ||
640 | |||
641 | Assuming that Espeakup is installed, either by the user or by the distributor, | ||
642 | follow these steps to use it. | ||
643 | |||
644 | Tell Speakup to use the "soft driver: | ||
645 | echo soft > /speakup/synth | ||
646 | |||
647 | Finally, start the espeakup program. There are two ways to do it. | ||
648 | Both require root privileges. | ||
649 | |||
650 | If Espeakup was installed as a package for your Linux distribution, | ||
651 | you probably have a distribution-specific script that controls the operation | ||
652 | of the daemon. Look for a file named espeakup under /etc/init.d or | ||
653 | /etc/rc.d. Execute the following command with root privileges: | ||
654 | /etc/init.d/espeakup start | ||
655 | Replace init.d with rc.d, if your distribution uses scripts located under | ||
656 | /etc/rc.d. | ||
657 | Your distribution will also have a procedure for starting daemons at | ||
658 | boot-time, so it is possible to have software speech as soon as user-space | ||
659 | daemons are started by the bootup scripts. | ||
660 | These procedures are not described in this document. | ||
661 | |||
662 | If you built Espeakup manually, the "make install" step placed the binary | ||
663 | under /usr/bin. | ||
664 | Run the following command as root: | ||
665 | /usr/bin/espeakup | ||
666 | Espeakup should start speaking. | ||
667 | |||
668 | 8.2. Speech Dispatcher | ||
669 | |||
670 | For this option, you must have a package called | ||
671 | Speech Dispatcher running on your system, and it must be configured to | ||
672 | work with one of its supported software synthesizers. | ||
673 | |||
674 | Two open source synthesizers you might use are Flite and Festival. You | ||
675 | might also choose to purchase the Software DecTalk from Fonix Sales Inc. | ||
676 | If you run a google search for Fonix, you'll find their web site. | ||
677 | |||
678 | You can obtain a copy of Speech Dispatcher from free(b)soft at | ||
679 | http://www.freebsoft.org/. Follow the installation instructions that | ||
680 | come with Speech Dispatcher in order to install and configure Speech | ||
681 | Dispatcher. You can check out the web site for your Linux distribution | ||
682 | in order to get a copy of either Flite or Festival. Your Linux | ||
683 | distribution may also have a precompiled Speech Dispatcher package. | ||
684 | |||
685 | Once you've installed, configured, and tested Speech Dispatcher with your | ||
686 | chosen software synthesizer, you still need one more piece of software | ||
687 | in order to make things work. You need a package called speechd-up. | ||
688 | You get it from the free(b)soft web site mentioned above. After you've | ||
689 | compiled and installed speechd-up, you are almost ready to begin using | ||
690 | your software synthesizer. | ||
691 | |||
692 | Now you can begin using your software synthesizer. In order to do so, | ||
693 | echo the soft keyword to the synth sys entry like this: | ||
694 | |||
695 | echo soft >/speakup/synth | ||
696 | |||
697 | Next run the speechd_up command like this: | ||
698 | |||
699 | speechd_up & | ||
700 | |||
701 | Your synth should now start talking, and you should be able to adjust | ||
702 | the pitch, rate, etc. | ||
703 | |||
704 | 9. Using The DecTalk PC Card | ||
705 | |||
706 | The DecTalk PC card is an ISA card that is inserted into one of the ISA | ||
707 | slots in your computer. It requires that the DecTalk PC software be | ||
708 | installed on your computer, and that the software be loaded onto the | ||
709 | Dectalk PC card before it can be used. | ||
710 | |||
711 | You can get the dec_pc.tgz file from the linux-speakup.org site. The | ||
712 | dec_pc.tgz file is in the ~ftp/pub/linux/speakup directory. | ||
713 | |||
714 | After you have downloaded the dec_pc.tgz file, untar it in your home | ||
715 | directory, and read the Readme file in the newly created dec_pc | ||
716 | directory. | ||
717 | |||
718 | The easiest way to get the software working is to copy the entire dec_pc | ||
719 | directory into /user/local/lib. To do this, su to root in your home | ||
720 | directory, and issue the command: | ||
721 | |||
722 | cp dec_pc /usr/local/lib | ||
723 | |||
724 | You will need to copy the dtload command from the dec_pc directory to a | ||
725 | directory in your path. Either /usr/bin or /usr/local/bin is a good | ||
726 | choice. | ||
727 | |||
728 | You can now run the dtload command in order to load the DecTalk PC | ||
729 | software onto the card. After you have done this, echo the decpc | ||
730 | keyword to the synth entry in the sys system like this: | ||
731 | |||
732 | echo decpc >/speakup/synth | ||
733 | |||
734 | Your DecTalk PC should start talking, and then you can adjust the pitch, | ||
735 | rate, volume, voice, etc. The voice entry in the Speakup sys system | ||
736 | will accept a number from 0 through 7 for the DecTalk PC synthesizer, | ||
737 | which will give you access to some of the DecTalk voices. | ||
738 | |||
739 | 10. Using Cursor Tracking | ||
740 | |||
741 | In Speakup version 2.0 and later, cursor tracking is turned on by | ||
742 | default. This means that when you are using an editor, Speakup will | ||
743 | automatically speak characters as you move left and right with the | ||
744 | cursor keys, and lines as you move up and down with the cursor keys. | ||
745 | This is the traditional sort of cursor tracking. | ||
746 | Recent versions of Speakup provide two additional ways to control the | ||
747 | text that is spoken when the cursor is moved: | ||
748 | "highlight tracking" and "read window." | ||
749 | They are described later in this section. | ||
750 | Sometimes, these modes get in your way, so you can disable cursor tracking | ||
751 | altogether. | ||
752 | |||
753 | You may select among the various forms of cursor tracking using the keypad | ||
754 | asterisk key. | ||
755 | Each time you press this key, a new mode is selected, and Speakup speaks | ||
756 | the name of the new mode. The names for the four possible states of cursor | ||
757 | tracking are: "cursoring on", "highlight tracking", "read window", | ||
758 | and "cursoring off." The keypad asterisk key moves through the list of | ||
759 | modes in a circular fashion. | ||
760 | |||
761 | If highlight tracking is enabled, Speakup tracks highlighted text, | ||
762 | rather than the cursor itself. When you move the cursor with the arrow keys, | ||
763 | Speakup speaks the currently highlighted information. | ||
764 | This is useful when moving through various menus and dialog boxes. | ||
765 | If cursor tracking isn't helping you while navigating a menu, | ||
766 | try highlight tracking. | ||
767 | |||
768 | With the "read window" variety of cursor tracking, you can limit the text | ||
769 | that Speakup speaks by specifying a window of interest on the screen. | ||
770 | See section 15 for a description of the process of defining windows. | ||
771 | When you move the cursor via the arrow keys, Speakup only speaks | ||
772 | the contents of the window. This is especially helpful when you are hearing | ||
773 | superfluous speech. Consider the following example. | ||
774 | |||
775 | Suppose that you are at a shell prompt. You use bash, and you want to | ||
776 | explore your command history using the up and down arrow keys. If you | ||
777 | have enabled cursor tracking, you will hear two pieces of information. | ||
778 | Speakup speaks both your shell prompt and the current entry from the | ||
779 | command history. You may not want to hear the prompt repeated | ||
780 | each time you move, so you can silence it by specifying a window. Find | ||
781 | the last line of text on the screen. Clear the current window by pressing | ||
782 | the key combination speakup f3. Use the review cursor to find the first | ||
783 | character that follows your shell prompt. Press speakup + f2 twice, to | ||
784 | define a one-line window. The boundaries of the window are the | ||
785 | character following the shell prompt and the end of the line. Now, cycle | ||
786 | through the cursor tracking modes using keypad asterisk, until Speakup | ||
787 | says "read window." Move through your history using your arrow keys. | ||
788 | You will notice that Speakup no longer speaks the redundant prompt. | ||
789 | |||
790 | Some folks like to turn cursor tracking off while they are using the | ||
791 | lynx web browser. You definitely want to turn cursor tracking off when | ||
792 | you are using the alsamixer application. Otherwise, you won't be able | ||
793 | to hear your mixer settings while you are using the arrow keys. | ||
794 | |||
795 | 11. Cut and Paste | ||
796 | |||
797 | One of Speakup's more useful features is the ability to cut and paste | ||
798 | text on the screen. This means that you can capture information from a | ||
799 | program, and paste that captured text into a different place in the | ||
800 | program, or into an entirely different program, which may even be | ||
801 | running on a different console. | ||
802 | |||
803 | For example, in this manual, we have made references to several web | ||
804 | sites. It would be nice if you could cut and paste these urls into your | ||
805 | web browser. Speakup does this quite nicely. Suppose you wanted to | ||
806 | past the following url into your browser: | ||
807 | |||
808 | http://linux-speakup.org/ | ||
809 | |||
810 | Use the speakup review keys to position the reading cursor on the first | ||
811 | character of the above url. When the reading cursor is in position, | ||
812 | press the keypad slash key once. Speakup will say, "mark". Next, | ||
813 | position the reading cursor on the rightmost character of the above | ||
814 | url. Press the keypad slash key once again to actually cut the text | ||
815 | from the screen. Speakup will say, "cut". Although we call this | ||
816 | cutting, Speakup does not actually delete the cut text from the screen. | ||
817 | It makes a copy of the text in a special buffer for later pasting. | ||
818 | |||
819 | Now that you have the url cut from the screen, you can paste it into | ||
820 | your browser, or even paste the url on a command line as an argument to | ||
821 | your browser. | ||
822 | |||
823 | Suppose you want to start lynx and go to the Speakup site. | ||
824 | |||
825 | You can switch to a different console with the alt left and right | ||
826 | arrows, or you can switch to a specific console by typing alt and a | ||
827 | function key. These are not Speakup commands, just standard Linux | ||
828 | console capabilities. | ||
829 | |||
830 | Once you've changed to an appropriate console, and are at a shell prompt, | ||
831 | type the word lynx, followed by a space. Now press and hold the speakup | ||
832 | key, while you type the keypad slash character. The url will be pasted | ||
833 | onto the command line, just as though you had typed it in. Press the | ||
834 | enter key to execute the command. | ||
835 | |||
836 | The paste buffer will continue to hold the cut information, until a new | ||
837 | mark and cut operation is carried out. This means you can paste the cut | ||
838 | information as many times as you like before doing another cut | ||
839 | operation. | ||
840 | |||
841 | You are not limited to cutting and pasting only one line on the screen. | ||
842 | You can also cut and paste rectangular regions of the screen. Just | ||
843 | position the reading cursor at the top left corner of the text to be | ||
844 | cut, mark it with the keypad slash key, then position the reading cursor | ||
845 | at the bottom right corner of the region to be cut, and cut it with the | ||
846 | keypad slash key. | ||
847 | |||
848 | 12. Changing the Pronunciation of Characters | ||
849 | |||
850 | Through the /speakup/i18n/characters sys entry, Speakup gives you the | ||
851 | ability to change how Speakup pronounces a given character. You could, | ||
852 | for example, change how some punctuation characters are spoken. You can | ||
853 | even change how Speakup will pronounce certain letters. | ||
854 | |||
855 | You may, for example, wish to change how Speakup pronounces the z | ||
856 | character. The author of Speakup, Kirk Reiser, is Canadian, and thus | ||
857 | believes that the z should be pronounced zed. If you are an American, | ||
858 | you might wish to use the zee pronunciation instead of zed. You can | ||
859 | change the pronunciation of both the upper and lower case z with the | ||
860 | following two commands: | ||
861 | |||
862 | echo 90 zee >/speakup/characters | ||
863 | echo 122 zee >/speakup/characters | ||
864 | |||
865 | Let's examine the parts of the two previous commands. They are issued | ||
866 | at the shell prompt, and could be placed in a startup script. | ||
867 | |||
868 | The word echo tells the shell that you want to have it display the | ||
869 | string of characters that follow the word echo. If you were to just | ||
870 | type: | ||
871 | |||
872 | echo hello. | ||
873 | |||
874 | You would get the word hello printed on your screen as soon as you | ||
875 | pressed the enter key. In this case, we are echoing strings that we | ||
876 | want to be redirected into the sys system. | ||
877 | |||
878 | The numbers 90 and 122 in the above echo commands are the ascii numeric | ||
879 | values for the upper and lower case z, the characters we wish to change. | ||
880 | |||
881 | The string zee is the pronunciation that we want Speakup to use for the | ||
882 | upper and lower case z. | ||
883 | |||
884 | The > symbol redirects the output of the echo command to a file, just | ||
885 | like in DOS, or at the Windows command prompt. | ||
886 | |||
887 | And finally, /speakup/i18n/characters is the file entry in the sys system | ||
888 | where we want the output to be directed. Speakup looks at the numeric | ||
889 | value of the character we want to change, and inserts the pronunciation | ||
890 | string into an internal table. | ||
891 | |||
892 | You can look at the whole table with the following command: | ||
893 | |||
894 | cat /speakup/i18n/characters | ||
895 | |||
896 | Speakup will then print out the entire character pronunciation table. I | ||
897 | won't display it here, but leave you to look at it at your convenience. | ||
898 | |||
899 | 13. Mapping Keys | ||
900 | |||
901 | Speakup has the capability of allowing you to assign or "map" keys to | ||
902 | internal Speakup commands. This section necessarily assumes you have a | ||
903 | Linux kernel source tree installed, and that it has been patched and | ||
904 | configured with Speakup. How you do this is beyond the scope of this | ||
905 | manual. For this information, visit the Speakup web site at | ||
906 | http://linux-speakup.org/. The reason you'll need the kernel source | ||
907 | tree patched with Speakup is that the genmap utility you'll need for | ||
908 | processing keymaps is in the | ||
909 | /usr/src/linux-<version_number>/drivers/char/speakup directory. The | ||
910 | <version_number> in the above directory path is the version number of | ||
911 | the Linux source tree you are working with. | ||
912 | |||
913 | So ok, you've gone off and gotten your kernel source tree, and patched | ||
914 | and configured it. Now you can start manipulating keymaps. | ||
915 | |||
916 | You can either use the | ||
917 | /usr/src/linux-<version_number>/drivers/char/speakup/speakupmap.map file | ||
918 | included with the Speakup source, or you can cut and paste the copy in | ||
919 | section 4 into a separate file. If you use the one in the Speakup | ||
920 | source tree, make sure you make a backup of it before you start making | ||
921 | changes. You have been warned! | ||
922 | |||
923 | Suppose that you want to swap the key assignments for the Speakup | ||
924 | say_last_char and the Speakup say_first_char commands. The | ||
925 | speakupmap.map lists the key mappings for these two commands as follows: | ||
926 | |||
927 | spk key_pageup = say_first_char | ||
928 | spk key_pagedown = say_last_char | ||
929 | |||
930 | You can edit your copy of the speakupmap.map file and swap the command | ||
931 | names on the right side of the = (equals) sign. You did make a backup, | ||
932 | right? The new keymap lines would look like this: | ||
933 | |||
934 | spk key_pageup = say_last_char | ||
935 | spk key_pagedown = say_first_char | ||
936 | |||
937 | After you edit your copy of the speakupmap.map file, save it under a new | ||
938 | file name, perhaps newmap.map. Then exit your editor and return to the | ||
939 | shell prompt. | ||
940 | |||
941 | You are now ready to load your keymap with your swapped key assignments. | ||
942 | Assuming that you saved your new keymap as the file newmap.map, you | ||
943 | would load your keymap into the sys system like this: | ||
944 | |||
945 | /usr/src/linux-<version_number>/drivers/char/speakup/genmap newmap.map | ||
946 | >/speakup/keymap | ||
947 | |||
948 | Remember to substitute your kernel version number for the | ||
949 | <version_number> in the above command. Also note that although the | ||
950 | above command wrapped onto two lines in this document, you should type | ||
951 | it all on one line. | ||
952 | |||
953 | Your say first and say last characters should now be swapped. Pressing | ||
954 | speakup pagedown should read you the first non-whitespace character on | ||
955 | the line your reading cursor is in, and pressing speakup pageup should | ||
956 | read you the last character on the line your reading cursor is in. | ||
957 | |||
958 | You should note that these new mappings will only stay in effect until | ||
959 | you reboot, or until you load another keymap. | ||
960 | |||
961 | One final warning. If you try to load a partial map, you will quickly | ||
962 | find that all the mappings you didn't include in your file got deleted | ||
963 | from the working map. Be extremely careful, and always make a backup! | ||
964 | You have been warned! | ||
965 | |||
966 | 14. Internationalizing Speakup | ||
967 | |||
968 | Speakup indicates various conditions to the user by speaking messages. | ||
969 | For instance, when you move to the left edge of the screen with the | ||
970 | review keys, Speakup says, "left." | ||
971 | Prior to version 3.1.0 of Speakup, all of these messages were in English, | ||
972 | and they could not be changed. If you used a non-English synthesizer, | ||
973 | you still heard English messages, such as "left" and "cursoring on." | ||
974 | In version 3.1.0 or higher, one may load translations for the various | ||
975 | messages via the /sys filesystem. | ||
976 | |||
977 | The directory /speakup/i18n contains several collections of messages. | ||
978 | Each group of messages is stored in its own file. | ||
979 | The following section lists all of these files, along with a brief description | ||
980 | of each. | ||
981 | |||
982 | 14.1. Files Under the i18n Subdirectory | ||
983 | |||
984 | * announcements: | ||
985 | This file contains various general announcements, most of which cannot | ||
986 | be categorized. You will find messages such as "You killed Speakup", | ||
987 | "I'm alive", "leaving help", "parked", "unparked", and others. | ||
988 | You will also find the names of the screen edges and cursor tracking modes | ||
989 | here. | ||
990 | |||
991 | * characters: | ||
992 | See section 12 for a description of this file. | ||
993 | |||
994 | * chartab: | ||
995 | See section 12. Unlike the rest of the files in the i18n subdirectory, | ||
996 | this one does not contain messages to be spoken. | ||
997 | |||
998 | * colors: | ||
999 | When you use the "say attributes" function, Speakup says the name of the | ||
1000 | foreground and background colors. These names come from the i18n/colors | ||
1001 | file. | ||
1002 | |||
1003 | * ctl_keys: | ||
1004 | Here, you will find names of control keys. These are used with Speakup's | ||
1005 | say_control feature. | ||
1006 | |||
1007 | * formatted: | ||
1008 | This group of messages contains embedded formatting codes, to specify | ||
1009 | the type and width of displayed data. If you change these, you must | ||
1010 | preserve all of the formatting codes, and they must appear in the order | ||
1011 | used by the default messages. | ||
1012 | |||
1013 | * function_names: | ||
1014 | Here, you will find a list of names for Speakup functions. These are used | ||
1015 | by the help system. For example, suppose that you have activated help mode, | ||
1016 | and you pressed keypad 3. Speakup says: | ||
1017 | "keypad 3 is character, say next." | ||
1018 | The message "character, say next" names a Speakup function, and it | ||
1019 | comes from this function_names file. | ||
1020 | |||
1021 | * key_names: | ||
1022 | Again, key_names is used by Speakup's help system. In the previous | ||
1023 | example, Speakup said that you pressed "keypad 3." | ||
1024 | This name came from the key_names file. | ||
1025 | |||
1026 | * states: | ||
1027 | This file contains names for key states. | ||
1028 | Again, these are part of the help system. For instance, if you had pressed | ||
1029 | speakup + keypad 3, you would hear: | ||
1030 | "speakup keypad 3 is go to bottom edge." | ||
1031 | The speakup key is depressed, so the name of the key state is speakup. | ||
1032 | This part of the message comes from the states collection. | ||
1033 | |||
1034 | 14.2. Loading Your Own Messages | ||
1035 | |||
1036 | The files under the i18n subdirectory all follow the same format. | ||
1037 | They consist of lines, with one message per line. | ||
1038 | Each message is represented by a number, followed by the text of the message. | ||
1039 | The number is the position of the message in the given collection. | ||
1040 | For example, if you view the file /speakup/i18n/colors, you will see the | ||
1041 | following list: | ||
1042 | |||
1043 | 0 black | ||
1044 | 1 blue | ||
1045 | 2 green | ||
1046 | 3 cyan | ||
1047 | 4 red | ||
1048 | 5 magenta | ||
1049 | 6 yellow | ||
1050 | 7 white | ||
1051 | 8 grey | ||
1052 | |||
1053 | You can change one message, or you can change a whole group. | ||
1054 | To load a whole collection of messages from a new source, simply use | ||
1055 | the cp command: | ||
1056 | cp ~/my_colors /speakup/i18n/colors | ||
1057 | You can change an individual message with the echo command, | ||
1058 | as shown in the following example. | ||
1059 | |||
1060 | The Spanish name for the color blue is azul. | ||
1061 | Looking at the colors file, we see that the name "blue" is at position 1 | ||
1062 | within the colors group. Let's change blue to azul: | ||
1063 | echo '1 azul' > /speakup/i18n/colors | ||
1064 | The next time that Speakup says message 1 from the colors group, it will | ||
1065 | say "azul", rather than "blue." | ||
1066 | |||
1067 | In the future, translations into various languages will be made available, | ||
1068 | and most users will just load the files necessary for their language. | ||
1069 | |||
1070 | 14.3. No Support for Non-Western-European Languages | ||
1071 | |||
1072 | As of the current release, Speakup only supports Western European languages. | ||
1073 | Support for the extended characters used by languages outside of the Western | ||
1074 | European family of languages is a work in progress. | ||
1075 | |||
1076 | 15. Using Speakup's Windowing Capability | ||
1077 | |||
1078 | Speakup has the capability of defining and manipulating windows on the | ||
1079 | screen. Speakup uses the term "Window", to mean a user defined area of | ||
1080 | the screen. The key strokes for defining and manipulating Speakup | ||
1081 | windows are as follows: | ||
1082 | |||
1083 | speakup + f2 -- Set the bounds of the window. | ||
1084 | Speakup + f3 -- clear the current window definition. | ||
1085 | speakup + f4 -- Toggle window silence on and off. | ||
1086 | speakup + keypad plus -- Say the currently defined window. | ||
1087 | |||
1088 | These capabilities are useful for tracking a certain part of the screen | ||
1089 | without rereading the whole screen, or for silencing a part of the | ||
1090 | screen that is constantly changing, such as a clock or status line. | ||
1091 | |||
1092 | There is no way to save these window settings, and you can only have one | ||
1093 | window defined for each virtual console. There is also no way to have | ||
1094 | windows automaticly defined for specific applications. | ||
1095 | |||
1096 | In order to define a window, use the review keys to move your reading | ||
1097 | cursor to the beginning of the area you want to define. Then press | ||
1098 | speakup + f2. Speakup will tell you that the window starts at the | ||
1099 | indicated row and column position. Then move the reading cursor to the | ||
1100 | end of the area to be defined as a window, and press speakup + f2 again. | ||
1101 | If there is more than one line in the window, Speakup will tell you | ||
1102 | that the window ends at the indicated row and column position. If there | ||
1103 | is only one line in the window, then Speakup will tell you that the | ||
1104 | window is the specified line on the screen. If you are only defining a | ||
1105 | one line window, you can just press speakup + f2 twice after placing the | ||
1106 | reading cursor on the line you want to define as a window. It is not | ||
1107 | necessary to position the reading cursor at the end of the line in order | ||
1108 | to define the whole line as a window. | ||
1109 | |||
1110 | 16. Tools for Controlling Speakup | ||
1111 | |||
1112 | The speakup distribution includes extra tools (in the tools directory) | ||
1113 | which were written to make speakup easier to use. This section will | ||
1114 | briefly describe the use of these tools. | ||
1115 | |||
1116 | 16.1. Speakupconf | ||
1117 | |||
1118 | speakupconf began life as a contribution from Steve Holmes, a member of | ||
1119 | the speakup community. We would like to thank him for his work on the | ||
1120 | early versions of this project. | ||
1121 | |||
1122 | This script may be installed as part of your linux distribution, but if | ||
1123 | it isn't, the recommended places to put it are /usr/local/bin or | ||
1124 | /usr/bin. This script can be run by any user, so it does not require | ||
1125 | root privileges. | ||
1126 | |||
1127 | Speakupconf allows you to save and load your Speakup settings. It works | ||
1128 | by reading and writing the /sys files described above. | ||
1129 | |||
1130 | The directory that speakupconf uses to store your settings depends on | ||
1131 | whether it is run from the root account. If you execute speakupconf as | ||
1132 | root, it uses the directory /etc/speakup. Otherwise, it uses the directory | ||
1133 | ~/.speakup, where ~ is your home directory. | ||
1134 | Anyone who needs to use Speakup from your console can load his own custom | ||
1135 | settings with this script. | ||
1136 | |||
1137 | speakupconf takes one required argument: load or save. | ||
1138 | Use the command | ||
1139 | speakupconf save | ||
1140 | to save your Speakup settings, and | ||
1141 | speakupconf load | ||
1142 | to load them into Speakup. | ||
1143 | A second argument may be specified to use an alternate directory to | ||
1144 | load or save the speakup parameters. | ||
1145 | |||
1146 | 16.2. Talkwith | ||
1147 | |||
1148 | Charles Hallenbeck, another member of the speakup community, wrote the | ||
1149 | initial versions of this script, and we would also like to thank him for | ||
1150 | his work on it. | ||
1151 | |||
1152 | This script needs root privileges to run, so if it is not installed as | ||
1153 | part of your linux distribution, the recommended places to install it | ||
1154 | are /usr/local/sbin or /usr/sbin. | ||
1155 | |||
1156 | Talkwith allows you to switch synthesizers on the fly. It takes a synthesizer | ||
1157 | name as an argument. For instance, | ||
1158 | talkwith dectlk | ||
1159 | causes Speakup to use the DecTalk Express. If you wish to switch to a | ||
1160 | software synthesizer, you must also indicate which daemon you wish to | ||
1161 | use. There are two possible choices: | ||
1162 | spd and espeakup. spd is an abbreviation for speechd-up. | ||
1163 | If you wish to use espeakup for software synthesis, give the command | ||
1164 | talkwith soft espeakup | ||
1165 | To use speechd-up, type: | ||
1166 | talkwith soft spd | ||
1167 | Any arguments that follow the name of the daemon are passed to the daemon | ||
1168 | when it is invoked. For instance: | ||
1169 | talkwith espeakup --default-voice=fr | ||
1170 | causes espeakup to use the French voice. | ||
1171 | Note that talkwith must always be executed with root privileges. | ||
1172 | |||
1173 | Talkwith does not attempt to load your settings after the new | ||
1174 | synthesizer is activated. You can use speakupconf to load your settings | ||
1175 | if desired. | ||
1176 | |||
1177 | GNU Free Documentation License | ||
1178 | Version 1.2, November 2002 | ||
1179 | |||
1180 | |||
1181 | Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. | ||
1182 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
1183 | Everyone is permitted to copy and distribute verbatim copies | ||
1184 | of this license document, but changing it is not allowed. | ||
1185 | |||
1186 | |||
1187 | 0. PREAMBLE | ||
1188 | |||
1189 | The purpose of this License is to make a manual, textbook, or other | ||
1190 | functional and useful document "free" in the sense of freedom: to | ||
1191 | assure everyone the effective freedom to copy and redistribute it, | ||
1192 | with or without modifying it, either commercially or noncommercially. | ||
1193 | Secondarily, this License preserves for the author and publisher a way | ||
1194 | to get credit for their work, while not being considered responsible | ||
1195 | for modifications made by others. | ||
1196 | |||
1197 | This License is a kind of "copyleft", which means that derivative | ||
1198 | works of the document must themselves be free in the same sense. It | ||
1199 | complements the GNU General Public License, which is a copyleft | ||
1200 | license designed for free software. | ||
1201 | |||
1202 | We have designed this License in order to use it for manuals for free | ||
1203 | software, because free software needs free documentation: a free | ||
1204 | program should come with manuals providing the same freedoms that the | ||
1205 | software does. But this License is not limited to software manuals; | ||
1206 | it can be used for any textual work, regardless of subject matter or | ||
1207 | whether it is published as a printed book. We recommend this License | ||
1208 | principally for works whose purpose is instruction or reference. | ||
1209 | |||
1210 | |||
1211 | 1. APPLICABILITY AND DEFINITIONS | ||
1212 | |||
1213 | This License applies to any manual or other work, in any medium, that | ||
1214 | contains a notice placed by the copyright holder saying it can be | ||
1215 | distributed under the terms of this License. Such a notice grants a | ||
1216 | world-wide, royalty-free license, unlimited in duration, to use that | ||
1217 | work under the conditions stated herein. The "Document", below, | ||
1218 | refers to any such manual or work. Any member of the public is a | ||
1219 | licensee, and is addressed as "you". You accept the license if you | ||
1220 | copy, modify or distribute the work in a way requiring permission | ||
1221 | under copyright law. | ||
1222 | |||
1223 | A "Modified Version" of the Document means any work containing the | ||
1224 | Document or a portion of it, either copied verbatim, or with | ||
1225 | modifications and/or translated into another language. | ||
1226 | |||
1227 | A "Secondary Section" is a named appendix or a front-matter section of | ||
1228 | the Document that deals exclusively with the relationship of the | ||
1229 | publishers or authors of the Document to the Document's overall subject | ||
1230 | (or to related matters) and contains nothing that could fall directly | ||
1231 | within that overall subject. (Thus, if the Document is in part a | ||
1232 | textbook of mathematics, a Secondary Section may not explain any | ||
1233 | mathematics.) The relationship could be a matter of historical | ||
1234 | connection with the subject or with related matters, or of legal, | ||
1235 | commercial, philosophical, ethical or political position regarding | ||
1236 | them. | ||
1237 | |||
1238 | The "Invariant Sections" are certain Secondary Sections whose titles | ||
1239 | are designated, as being those of Invariant Sections, in the notice | ||
1240 | that says that the Document is released under this License. If a | ||
1241 | section does not fit the above definition of Secondary then it is not | ||
1242 | allowed to be designated as Invariant. The Document may contain zero | ||
1243 | Invariant Sections. If the Document does not identify any Invariant | ||
1244 | Sections then there are none. | ||
1245 | |||
1246 | The "Cover Texts" are certain short passages of text that are listed, | ||
1247 | as Front-Cover Texts or Back-Cover Texts, in the notice that says that | ||
1248 | the Document is released under this License. A Front-Cover Text may | ||
1249 | be at most 5 words, and a Back-Cover Text may be at most 25 words. | ||
1250 | |||
1251 | A "Transparent" copy of the Document means a machine-readable copy, | ||
1252 | represented in a format whose specification is available to the | ||
1253 | general public, that is suitable for revising the document | ||
1254 | straightforwardly with generic text editors or (for images composed of | ||
1255 | pixels) generic paint programs or (for drawings) some widely available | ||
1256 | drawing editor, and that is suitable for input to text formatters or | ||
1257 | for automatic translation to a variety of formats suitable for input | ||
1258 | to text formatters. A copy made in an otherwise Transparent file | ||
1259 | format whose markup, or absence of markup, has been arranged to thwart | ||
1260 | or discourage subsequent modification by readers is not Transparent. | ||
1261 | An image format is not Transparent if used for any substantial amount | ||
1262 | of text. A copy that is not "Transparent" is called "Opaque". | ||
1263 | |||
1264 | Examples of suitable formats for Transparent copies include plain | ||
1265 | ASCII without markup, Texinfo input format, LaTeX input format, SGML | ||
1266 | or XML using a publicly available DTD, and standard-conforming simple | ||
1267 | HTML, PostScript or PDF designed for human modification. Examples of | ||
1268 | transparent image formats include PNG, XCF and JPG. Opaque formats | ||
1269 | include proprietary formats that can be read and edited only by | ||
1270 | proprietary word processors, SGML or XML for which the DTD and/or | ||
1271 | processing tools are not generally available, and the | ||
1272 | machine-generated HTML, PostScript or PDF produced by some word | ||
1273 | processors for output purposes only. | ||
1274 | |||
1275 | The "Title Page" means, for a printed book, the title page itself, | ||
1276 | plus such following pages as are needed to hold, legibly, the material | ||
1277 | this License requires to appear in the title page. For works in | ||
1278 | formats which do not have any title page as such, "Title Page" means | ||
1279 | the text near the most prominent appearance of the work's title, | ||
1280 | preceding the beginning of the body of the text. | ||
1281 | |||
1282 | A section "Entitled XYZ" means a named subunit of the Document whose | ||
1283 | title either is precisely XYZ or contains XYZ in parentheses following | ||
1284 | text that translates XYZ in another language. (Here XYZ stands for a | ||
1285 | specific section name mentioned below, such as "Acknowledgements", | ||
1286 | "Dedications", "Endorsements", or "History".) To "Preserve the Title" | ||
1287 | of such a section when you modify the Document means that it remains a | ||
1288 | section "Entitled XYZ" according to this definition. | ||
1289 | |||
1290 | The Document may include Warranty Disclaimers next to the notice which | ||
1291 | states that this License applies to the Document. These Warranty | ||
1292 | Disclaimers are considered to be included by reference in this | ||
1293 | License, but only as regards disclaiming warranties: any other | ||
1294 | implication that these Warranty Disclaimers may have is void and has | ||
1295 | no effect on the meaning of this License. | ||
1296 | |||
1297 | |||
1298 | 2. VERBATIM COPYING | ||
1299 | |||
1300 | You may copy and distribute the Document in any medium, either | ||
1301 | commercially or noncommercially, provided that this License, the | ||
1302 | copyright notices, and the license notice saying this License applies | ||
1303 | to the Document are reproduced in all copies, and that you add no other | ||
1304 | conditions whatsoever to those of this License. You may not use | ||
1305 | technical measures to obstruct or control the reading or further | ||
1306 | copying of the copies you make or distribute. However, you may accept | ||
1307 | compensation in exchange for copies. If you distribute a large enough | ||
1308 | number of copies you must also follow the conditions in section 3. | ||
1309 | |||
1310 | You may also lend copies, under the same conditions stated above, and | ||
1311 | you may publicly display copies. | ||
1312 | |||
1313 | |||
1314 | 3. COPYING IN QUANTITY | ||
1315 | |||
1316 | If you publish printed copies (or copies in media that commonly have | ||
1317 | printed covers) of the Document, numbering more than 100, and the | ||
1318 | Document's license notice requires Cover Texts, you must enclose the | ||
1319 | copies in covers that carry, clearly and legibly, all these Cover | ||
1320 | Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on | ||
1321 | the back cover. Both covers must also clearly and legibly identify | ||
1322 | you as the publisher of these copies. The front cover must present | ||
1323 | the full title with all words of the title equally prominent and | ||
1324 | visible. You may add other material on the covers in addition. | ||
1325 | Copying with changes limited to the covers, as long as they preserve | ||
1326 | the title of the Document and satisfy these conditions, can be treated | ||
1327 | as verbatim copying in other respects. | ||
1328 | |||
1329 | If the required texts for either cover are too voluminous to fit | ||
1330 | legibly, you should put the first ones listed (as many as fit | ||
1331 | reasonably) on the actual cover, and continue the rest onto adjacent | ||
1332 | pages. | ||
1333 | |||
1334 | If you publish or distribute Opaque copies of the Document numbering | ||
1335 | more than 100, you must either include a machine-readable Transparent | ||
1336 | copy along with each Opaque copy, or state in or with each Opaque copy | ||
1337 | a computer-network location from which the general network-using | ||
1338 | public has access to download using public-standard network protocols | ||
1339 | a complete Transparent copy of the Document, free of added material. | ||
1340 | If you use the latter option, you must take reasonably prudent steps, | ||
1341 | when you begin distribution of Opaque copies in quantity, to ensure | ||
1342 | that this Transparent copy will remain thus accessible at the stated | ||
1343 | location until at least one year after the last time you distribute an | ||
1344 | Opaque copy (directly or through your agents or retailers) of that | ||
1345 | edition to the public. | ||
1346 | |||
1347 | It is requested, but not required, that you contact the authors of the | ||
1348 | Document well before redistributing any large number of copies, to give | ||
1349 | them a chance to provide you with an updated version of the Document. | ||
1350 | |||
1351 | |||
1352 | 4. MODIFICATIONS | ||
1353 | |||
1354 | You may copy and distribute a Modified Version of the Document under | ||
1355 | the conditions of sections 2 and 3 above, provided that you release | ||
1356 | the Modified Version under precisely this License, with the Modified | ||
1357 | Version filling the role of the Document, thus licensing distribution | ||
1358 | and modification of the Modified Version to whoever possesses a copy | ||
1359 | of it. In addition, you must do these things in the Modified Version: | ||
1360 | |||
1361 | A. Use in the Title Page (and on the covers, if any) a title distinct | ||
1362 | from that of the Document, and from those of previous versions | ||
1363 | (which should, if there were any, be listed in the History section | ||
1364 | of the Document). You may use the same title as a previous version | ||
1365 | if the original publisher of that version gives permission. | ||
1366 | B. List on the Title Page, as authors, one or more persons or entities | ||
1367 | responsible for authorship of the modifications in the Modified | ||
1368 | Version, together with at least five of the principal authors of the | ||
1369 | Document (all of its principal authors, if it has fewer than five), | ||
1370 | unless they release you from this requirement. | ||
1371 | C. State on the Title page the name of the publisher of the | ||
1372 | Modified Version, as the publisher. | ||
1373 | D. Preserve all the copyright notices of the Document. | ||
1374 | E. Add an appropriate copyright notice for your modifications | ||
1375 | adjacent to the other copyright notices. | ||
1376 | F. Include, immediately after the copyright notices, a license notice | ||
1377 | giving the public permission to use the Modified Version under the | ||
1378 | terms of this License, in the form shown in the Addendum below. | ||
1379 | G. Preserve in that license notice the full lists of Invariant Sections | ||
1380 | and required Cover Texts given in the Document's license notice. | ||
1381 | H. Include an unaltered copy of this License. | ||
1382 | I. Preserve the section Entitled "History", Preserve its Title, and add | ||
1383 | to it an item stating at least the title, year, new authors, and | ||
1384 | publisher of the Modified Version as given on the Title Page. If | ||
1385 | there is no section Entitled "History" in the Document, create one | ||
1386 | stating the title, year, authors, and publisher of the Document as | ||
1387 | given on its Title Page, then add an item describing the Modified | ||
1388 | Version as stated in the previous sentence. | ||
1389 | J. Preserve the network location, if any, given in the Document for | ||
1390 | public access to a Transparent copy of the Document, and likewise | ||
1391 | the network locations given in the Document for previous versions | ||
1392 | it was based on. These may be placed in the "History" section. | ||
1393 | You may omit a network location for a work that was published at | ||
1394 | least four years before the Document itself, or if the original | ||
1395 | publisher of the version it refers to gives permission. | ||
1396 | K. For any section Entitled "Acknowledgements" or "Dedications", | ||
1397 | Preserve the Title of the section, and preserve in the section all | ||
1398 | the substance and tone of each of the contributor acknowledgements | ||
1399 | and/or dedications given therein. | ||
1400 | L. Preserve all the Invariant Sections of the Document, | ||
1401 | unaltered in their text and in their titles. Section numbers | ||
1402 | or the equivalent are not considered part of the section titles. | ||
1403 | M. Delete any section Entitled "Endorsements". Such a section | ||
1404 | may not be included in the Modified Version. | ||
1405 | N. Do not retitle any existing section to be Entitled "Endorsements" | ||
1406 | or to conflict in title with any Invariant Section. | ||
1407 | O. Preserve any Warranty Disclaimers. | ||
1408 | |||
1409 | If the Modified Version includes new front-matter sections or | ||
1410 | appendices that qualify as Secondary Sections and contain no material | ||
1411 | copied from the Document, you may at your option designate some or all | ||
1412 | of these sections as invariant. To do this, add their titles to the | ||
1413 | list of Invariant Sections in the Modified Version's license notice. | ||
1414 | These titles must be distinct from any other section titles. | ||
1415 | |||
1416 | You may add a section Entitled "Endorsements", provided it contains | ||
1417 | nothing but endorsements of your Modified Version by various | ||
1418 | parties--for example, statements of peer review or that the text has | ||
1419 | been approved by an organization as the authoritative definition of a | ||
1420 | standard. | ||
1421 | |||
1422 | You may add a passage of up to five words as a Front-Cover Text, and a | ||
1423 | passage of up to 25 words as a Back-Cover Text, to the end of the list | ||
1424 | of Cover Texts in the Modified Version. Only one passage of | ||
1425 | Front-Cover Text and one of Back-Cover Text may be added by (or | ||
1426 | through arrangements made by) any one entity. If the Document already | ||
1427 | includes a cover text for the same cover, previously added by you or | ||
1428 | by arrangement made by the same entity you are acting on behalf of, | ||
1429 | you may not add another; but you may replace the old one, on explicit | ||
1430 | permission from the previous publisher that added the old one. | ||
1431 | |||
1432 | The author(s) and publisher(s) of the Document do not by this License | ||
1433 | give permission to use their names for publicity for or to assert or | ||
1434 | imply endorsement of any Modified Version. | ||
1435 | |||
1436 | |||
1437 | 5. COMBINING DOCUMENTS | ||
1438 | |||
1439 | You may combine the Document with other documents released under this | ||
1440 | License, under the terms defined in section 4 above for modified | ||
1441 | versions, provided that you include in the combination all of the | ||
1442 | Invariant Sections of all of the original documents, unmodified, and | ||
1443 | list them all as Invariant Sections of your combined work in its | ||
1444 | license notice, and that you preserve all their Warranty Disclaimers. | ||
1445 | |||
1446 | The combined work need only contain one copy of this License, and | ||
1447 | multiple identical Invariant Sections may be replaced with a single | ||
1448 | copy. If there are multiple Invariant Sections with the same name but | ||
1449 | different contents, make the title of each such section unique by | ||
1450 | adding at the end of it, in parentheses, the name of the original | ||
1451 | author or publisher of that section if known, or else a unique number. | ||
1452 | Make the same adjustment to the section titles in the list of | ||
1453 | Invariant Sections in the license notice of the combined work. | ||
1454 | |||
1455 | In the combination, you must combine any sections Entitled "History" | ||
1456 | in the various original documents, forming one section Entitled | ||
1457 | "History"; likewise combine any sections Entitled "Acknowledgements", | ||
1458 | and any sections Entitled "Dedications". You must delete all sections | ||
1459 | Entitled "Endorsements". | ||
1460 | |||
1461 | |||
1462 | 6. COLLECTIONS OF DOCUMENTS | ||
1463 | |||
1464 | You may make a collection consisting of the Document and other documents | ||
1465 | released under this License, and replace the individual copies of this | ||
1466 | License in the various documents with a single copy that is included in | ||
1467 | the collection, provided that you follow the rules of this License for | ||
1468 | verbatim copying of each of the documents in all other respects. | ||
1469 | |||
1470 | You may extract a single document from such a collection, and distribute | ||
1471 | it individually under this License, provided you insert a copy of this | ||
1472 | License into the extracted document, and follow this License in all | ||
1473 | other respects regarding verbatim copying of that document. | ||
1474 | |||
1475 | |||
1476 | 7. AGGREGATION WITH INDEPENDENT WORKS | ||
1477 | |||
1478 | A compilation of the Document or its derivatives with other separate | ||
1479 | and independent documents or works, in or on a volume of a storage or | ||
1480 | distribution medium, is called an "aggregate" if the copyright | ||
1481 | resulting from the compilation is not used to limit the legal rights | ||
1482 | of the compilation's users beyond what the individual works permit. | ||
1483 | When the Document is included in an aggregate, this License does not | ||
1484 | apply to the other works in the aggregate which are not themselves | ||
1485 | derivative works of the Document. | ||
1486 | |||
1487 | If the Cover Text requirement of section 3 is applicable to these | ||
1488 | copies of the Document, then if the Document is less than one half of | ||
1489 | the entire aggregate, the Document's Cover Texts may be placed on | ||
1490 | covers that bracket the Document within the aggregate, or the | ||
1491 | electronic equivalent of covers if the Document is in electronic form. | ||
1492 | Otherwise they must appear on printed covers that bracket the whole | ||
1493 | aggregate. | ||
1494 | |||
1495 | |||
1496 | 8. TRANSLATION | ||
1497 | |||
1498 | Translation is considered a kind of modification, so you may | ||
1499 | distribute translations of the Document under the terms of section 4. | ||
1500 | Replacing Invariant Sections with translations requires special | ||
1501 | permission from their copyright holders, but you may include | ||
1502 | translations of some or all Invariant Sections in addition to the | ||
1503 | original versions of these Invariant Sections. You may include a | ||
1504 | translation of this License, and all the license notices in the | ||
1505 | Document, and any Warranty Disclaimers, provided that you also include | ||
1506 | the original English version of this License and the original versions | ||
1507 | of those notices and disclaimers. In case of a disagreement between | ||
1508 | the translation and the original version of this License or a notice | ||
1509 | or disclaimer, the original version will prevail. | ||
1510 | |||
1511 | If a section in the Document is Entitled "Acknowledgements", | ||
1512 | "Dedications", or "History", the requirement (section 4) to Preserve | ||
1513 | its Title (section 1) will typically require changing the actual | ||
1514 | title. | ||
1515 | |||
1516 | |||
1517 | 9. TERMINATION | ||
1518 | |||
1519 | You may not copy, modify, sublicense, or distribute the Document except | ||
1520 | as expressly provided for under this License. Any other attempt to | ||
1521 | copy, modify, sublicense or distribute the Document is void, and will | ||
1522 | automatically terminate your rights under this License. However, | ||
1523 | parties who have received copies, or rights, from you under this | ||
1524 | License will not have their licenses terminated so long as such | ||
1525 | parties remain in full compliance. | ||
1526 | |||
1527 | |||
1528 | 10. FUTURE REVISIONS OF THIS LICENSE | ||
1529 | |||
1530 | The Free Software Foundation may publish new, revised versions | ||
1531 | of the GNU Free Documentation License from time to time. Such new | ||
1532 | versions will be similar in spirit to the present version, but may | ||
1533 | differ in detail to address new problems or concerns. See | ||
1534 | http://www.gnu.org/copyleft/. | ||
1535 | |||
1536 | Each version of the License is given a distinguishing version number. | ||
1537 | If the Document specifies that a particular numbered version of this | ||
1538 | License "or any later version" applies to it, you have the option of | ||
1539 | following the terms and conditions either of that specified version or | ||
1540 | of any later version that has been published (not as a draft) by the | ||
1541 | Free Software Foundation. If the Document does not specify a version | ||
1542 | number of this License, you may choose any version ever published (not | ||
1543 | as a draft) by the Free Software Foundation. | ||
1544 | |||
1545 | |||
1546 | ADDENDUM: How to use this License for your documents | ||
1547 | |||
1548 | To use this License in a document you have written, include a copy of | ||
1549 | the License in the document and put the following copyright and | ||
1550 | license notices just after the title page: | ||
1551 | |||
1552 | Copyright (c) YEAR YOUR NAME. | ||
1553 | Permission is granted to copy, distribute and/or modify this document | ||
1554 | under the terms of the GNU Free Documentation License, Version 1.2 | ||
1555 | or any later version published by the Free Software Foundation; | ||
1556 | with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. | ||
1557 | A copy of the license is included in the section entitled "GNU | ||
1558 | Free Documentation License". | ||
1559 | |||
1560 | If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, | ||
1561 | replace the "with...Texts." line with this: | ||
1562 | |||
1563 | with the Invariant Sections being LIST THEIR TITLES, with the | ||
1564 | Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. | ||
1565 | |||
1566 | If you have Invariant Sections without Cover Texts, or some other | ||
1567 | combination of the three, merge those two alternatives to suit the | ||
1568 | situation. | ||
1569 | |||
1570 | If your document contains nontrivial examples of program code, we | ||
1571 | recommend releasing these examples in parallel under your choice of | ||
1572 | free software license, such as the GNU General Public License, | ||
1573 | to permit their use in free software. | ||
1574 | |||
1575 | The End. | ||
diff --git a/drivers/staging/speakup/synth.c b/drivers/staging/speakup/synth.c new file mode 100644 index 00000000000..7f74f80be69 --- /dev/null +++ b/drivers/staging/speakup/synth.c | |||
@@ -0,0 +1,458 @@ | |||
1 | #include <linux/types.h> | ||
2 | #include <linux/ctype.h> /* for isdigit() and friends */ | ||
3 | #include <linux/fs.h> | ||
4 | #include <linux/mm.h> /* for verify_area */ | ||
5 | #include <linux/errno.h> /* for -EBUSY */ | ||
6 | #include <linux/ioport.h> /* for check_region, request_region */ | ||
7 | #include <linux/interrupt.h> | ||
8 | #include <linux/delay.h> /* for loops_per_sec */ | ||
9 | #include <linux/kmod.h> | ||
10 | #include <linux/jiffies.h> | ||
11 | #include <linux/uaccess.h> /* for copy_from_user */ | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/timer.h> | ||
14 | #include <linux/kthread.h> | ||
15 | |||
16 | #include "spk_priv.h" | ||
17 | #include "speakup.h" | ||
18 | #include "serialio.h" | ||
19 | |||
20 | #define MAXSYNTHS 16 /* Max number of synths in array. */ | ||
21 | static struct spk_synth *synths[MAXSYNTHS]; | ||
22 | struct spk_synth *synth = NULL; | ||
23 | char pitch_buff[32] = ""; | ||
24 | static int module_status; | ||
25 | int quiet_boot; | ||
26 | |||
27 | struct speakup_info_t speakup_info = { | ||
28 | .spinlock = SPIN_LOCK_UNLOCKED, | ||
29 | .flushing = 0, | ||
30 | }; | ||
31 | EXPORT_SYMBOL_GPL(speakup_info); | ||
32 | |||
33 | static int do_synth_init(struct spk_synth *in_synth); | ||
34 | |||
35 | int serial_synth_probe(struct spk_synth *synth) | ||
36 | { | ||
37 | struct serial_state *ser; | ||
38 | int failed = 0; | ||
39 | |||
40 | if ((synth->ser >= SPK_LO_TTY) && (synth->ser <= SPK_HI_TTY)) { | ||
41 | ser = spk_serial_init(synth->ser); | ||
42 | if (ser == NULL) { | ||
43 | failed = -1; | ||
44 | } else { | ||
45 | outb_p(0, ser->port); | ||
46 | mdelay(1); | ||
47 | outb_p('\r', ser->port); | ||
48 | } | ||
49 | } else { | ||
50 | failed = -1; | ||
51 | pr_warn("ttyS%i is an invalid port\n", synth->ser); | ||
52 | } | ||
53 | if (failed) { | ||
54 | pr_info("%s: not found\n", synth->long_name); | ||
55 | return -ENODEV; | ||
56 | } | ||
57 | pr_info("%s: ttyS%i, Driver Version %s\n", | ||
58 | synth->long_name, synth->ser, synth->version); | ||
59 | synth->alive = 1; | ||
60 | return 0; | ||
61 | } | ||
62 | EXPORT_SYMBOL_GPL(serial_synth_probe); | ||
63 | |||
64 | /* Main loop of the progression thread: keep eating from the buffer | ||
65 | * and push to the serial port, waiting as needed | ||
66 | * | ||
67 | * For devices that have a "full" notification mecanism, the driver can | ||
68 | * adapt the loop the way they prefer. | ||
69 | */ | ||
70 | void spk_do_catch_up(struct spk_synth *synth) | ||
71 | { | ||
72 | u_char ch; | ||
73 | unsigned long flags; | ||
74 | unsigned long jiff_max; | ||
75 | struct var_t *delay_time; | ||
76 | struct var_t *full_time; | ||
77 | struct var_t *jiffy_delta; | ||
78 | int jiffy_delta_val; | ||
79 | int delay_time_val; | ||
80 | int full_time_val; | ||
81 | |||
82 | jiffy_delta = get_var(JIFFY); | ||
83 | full_time = get_var(FULL); | ||
84 | delay_time = get_var(DELAY); | ||
85 | |||
86 | spk_lock(flags); | ||
87 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
88 | spk_unlock(flags); | ||
89 | |||
90 | jiff_max = jiffies + jiffy_delta_val; | ||
91 | while (!kthread_should_stop()) { | ||
92 | spk_lock(flags); | ||
93 | if (speakup_info.flushing) { | ||
94 | speakup_info.flushing = 0; | ||
95 | spk_unlock(flags); | ||
96 | synth->flush(synth); | ||
97 | continue; | ||
98 | } | ||
99 | if (synth_buffer_empty()) { | ||
100 | spk_unlock(flags); | ||
101 | break; | ||
102 | } | ||
103 | ch = synth_buffer_peek(); | ||
104 | set_current_state(TASK_INTERRUPTIBLE); | ||
105 | full_time_val = full_time->u.n.value; | ||
106 | spk_unlock(flags); | ||
107 | if (ch == '\n') | ||
108 | ch = synth->procspeech; | ||
109 | if (!spk_serial_out(ch)) { | ||
110 | schedule_timeout(msecs_to_jiffies(full_time_val)); | ||
111 | continue; | ||
112 | } | ||
113 | if ((jiffies >= jiff_max) && (ch == SPACE)) { | ||
114 | spk_lock(flags); | ||
115 | jiffy_delta_val = jiffy_delta->u.n.value; | ||
116 | delay_time_val = delay_time->u.n.value; | ||
117 | full_time_val = full_time->u.n.value; | ||
118 | spk_unlock(flags); | ||
119 | if (spk_serial_out(synth->procspeech)) | ||
120 | schedule_timeout(msecs_to_jiffies(delay_time_val)); | ||
121 | else | ||
122 | schedule_timeout(msecs_to_jiffies(full_time_val)); | ||
123 | jiff_max = jiffies + jiffy_delta_val; | ||
124 | } | ||
125 | set_current_state(TASK_RUNNING); | ||
126 | spk_lock(flags); | ||
127 | synth_buffer_getc(); | ||
128 | spk_unlock(flags); | ||
129 | } | ||
130 | spk_serial_out(synth->procspeech); | ||
131 | } | ||
132 | EXPORT_SYMBOL_GPL(spk_do_catch_up); | ||
133 | |||
134 | const char *spk_synth_immediate(struct spk_synth *synth, const char *buff) | ||
135 | { | ||
136 | u_char ch; | ||
137 | while ((ch = *buff)) { | ||
138 | if (ch == '\n') | ||
139 | ch = synth->procspeech; | ||
140 | if (wait_for_xmitr()) | ||
141 | outb(ch, speakup_info.port_tts); | ||
142 | else | ||
143 | return buff; | ||
144 | buff++; | ||
145 | } | ||
146 | return 0; | ||
147 | } | ||
148 | EXPORT_SYMBOL_GPL(spk_synth_immediate); | ||
149 | |||
150 | void spk_synth_flush(struct spk_synth *synth) | ||
151 | { | ||
152 | spk_serial_out(synth->clear); | ||
153 | } | ||
154 | EXPORT_SYMBOL_GPL(spk_synth_flush); | ||
155 | |||
156 | int spk_synth_is_alive_nop(struct spk_synth *synth) | ||
157 | { | ||
158 | synth->alive = 1; | ||
159 | return 1; | ||
160 | } | ||
161 | EXPORT_SYMBOL_GPL(spk_synth_is_alive_nop); | ||
162 | |||
163 | int spk_synth_is_alive_restart(struct spk_synth *synth) | ||
164 | { | ||
165 | if (synth->alive) | ||
166 | return 1; | ||
167 | if (!synth->alive && wait_for_xmitr() > 0) { | ||
168 | /* restart */ | ||
169 | synth->alive = 1; | ||
170 | synth_printf("%s", synth->init); | ||
171 | return 2; /* reenabled */ | ||
172 | } | ||
173 | pr_warn("%s: can't restart synth\n", synth->long_name); | ||
174 | return 0; | ||
175 | } | ||
176 | EXPORT_SYMBOL_GPL(spk_synth_is_alive_restart); | ||
177 | |||
178 | static void thread_wake_up(u_long data) | ||
179 | { | ||
180 | wake_up_interruptible_all(&speakup_event); | ||
181 | } | ||
182 | |||
183 | static DEFINE_TIMER(thread_timer, thread_wake_up, 0, 0); | ||
184 | |||
185 | void synth_start(void) | ||
186 | { | ||
187 | struct var_t *trigger_time; | ||
188 | |||
189 | if (!synth->alive) { | ||
190 | synth_buffer_clear(); | ||
191 | return; | ||
192 | } | ||
193 | trigger_time = get_var(TRIGGER); | ||
194 | if (!timer_pending(&thread_timer)) | ||
195 | mod_timer(&thread_timer, jiffies + msecs_to_jiffies(trigger_time->u.n.value)); | ||
196 | } | ||
197 | |||
198 | void do_flush(void) | ||
199 | { | ||
200 | speakup_info.flushing = 1; | ||
201 | synth_buffer_clear(); | ||
202 | if (synth->alive) { | ||
203 | if (pitch_shift) { | ||
204 | synth_printf("%s", pitch_buff); | ||
205 | pitch_shift = 0; | ||
206 | } | ||
207 | } | ||
208 | wake_up_interruptible_all(&speakup_event); | ||
209 | wake_up_process(speakup_task); | ||
210 | } | ||
211 | |||
212 | void synth_write(const char *buf, size_t count) | ||
213 | { | ||
214 | while (count--) | ||
215 | synth_buffer_add(*buf++); | ||
216 | synth_start(); | ||
217 | } | ||
218 | |||
219 | void synth_printf(const char *fmt, ...) | ||
220 | { | ||
221 | va_list args; | ||
222 | unsigned char buf[160], *p; | ||
223 | int r; | ||
224 | |||
225 | va_start(args, fmt); | ||
226 | r = vsnprintf(buf, sizeof(buf), fmt, args); | ||
227 | va_end(args); | ||
228 | if (r > sizeof(buf) - 1) | ||
229 | r = sizeof(buf) - 1; | ||
230 | |||
231 | p = buf; | ||
232 | while (r--) | ||
233 | synth_buffer_add(*p++); | ||
234 | synth_start(); | ||
235 | } | ||
236 | EXPORT_SYMBOL_GPL(synth_printf); | ||
237 | |||
238 | static int index_count = 0; | ||
239 | static int sentence_count = 0; | ||
240 | |||
241 | void reset_index_count(int sc) | ||
242 | { | ||
243 | static int first = 1; | ||
244 | if (first) | ||
245 | first = 0; | ||
246 | else | ||
247 | synth->get_index(); | ||
248 | index_count = 0; | ||
249 | sentence_count = sc; | ||
250 | } | ||
251 | |||
252 | int synth_supports_indexing(void) | ||
253 | { | ||
254 | if (synth->get_index != NULL) | ||
255 | return 1; | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | void synth_insert_next_index(int sent_num) | ||
260 | { | ||
261 | int out; | ||
262 | if (synth->alive) { | ||
263 | if (sent_num == 0) { | ||
264 | synth->indexing.currindex++; | ||
265 | index_count++; | ||
266 | if (synth->indexing.currindex > | ||
267 | synth->indexing.highindex) | ||
268 | synth->indexing.currindex = | ||
269 | synth->indexing.lowindex; | ||
270 | } | ||
271 | |||
272 | out = synth->indexing.currindex * 10 + sent_num; | ||
273 | synth_printf(synth->indexing.command, out, out); | ||
274 | } | ||
275 | } | ||
276 | |||
277 | void get_index_count(int *linecount, int *sentcount) | ||
278 | { | ||
279 | int ind = synth->get_index(); | ||
280 | if (ind) { | ||
281 | sentence_count = ind % 10; | ||
282 | |||
283 | if ((ind / 10) <= synth->indexing.currindex) | ||
284 | index_count = synth->indexing.currindex-(ind/10); | ||
285 | else | ||
286 | index_count = synth->indexing.currindex-synth->indexing.lowindex | ||
287 | + synth->indexing.highindex-(ind/10)+1; | ||
288 | |||
289 | } | ||
290 | *sentcount = sentence_count; | ||
291 | *linecount = index_count; | ||
292 | } | ||
293 | |||
294 | static struct resource synth_res; | ||
295 | |||
296 | int synth_request_region(unsigned long start, unsigned long n) | ||
297 | { | ||
298 | struct resource *parent = &ioport_resource; | ||
299 | memset(&synth_res, 0, sizeof(synth_res)); | ||
300 | synth_res.name = synth->name; | ||
301 | synth_res.start = start; | ||
302 | synth_res.end = start + n - 1; | ||
303 | synth_res.flags = IORESOURCE_BUSY; | ||
304 | return request_resource(parent, &synth_res); | ||
305 | } | ||
306 | EXPORT_SYMBOL_GPL(synth_request_region); | ||
307 | |||
308 | int synth_release_region(unsigned long start, unsigned long n) | ||
309 | { | ||
310 | return release_resource(&synth_res); | ||
311 | } | ||
312 | EXPORT_SYMBOL_GPL(synth_release_region); | ||
313 | |||
314 | struct var_t synth_time_vars[] = { | ||
315 | { DELAY, .u.n = {NULL, 100, 100, 2000, 0, 0, NULL }}, | ||
316 | { TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL }}, | ||
317 | { JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL }}, | ||
318 | { FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL }}, | ||
319 | V_LAST_VAR | ||
320 | }; | ||
321 | |||
322 | /* called by: speakup_init() */ | ||
323 | int synth_init(char *synth_name) | ||
324 | { | ||
325 | int i; | ||
326 | int ret = 0; | ||
327 | struct spk_synth *synth = NULL; | ||
328 | |||
329 | if (synth_name == NULL) | ||
330 | return 0; | ||
331 | |||
332 | if (strcmp(synth_name, "none") == 0) { | ||
333 | mutex_lock(&spk_mutex); | ||
334 | synth_release(); | ||
335 | mutex_unlock(&spk_mutex); | ||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | mutex_lock(&spk_mutex); | ||
340 | /* First, check if we already have it loaded. */ | ||
341 | for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++) | ||
342 | if (strcmp(synths[i]->name, synth_name) == 0) | ||
343 | synth = synths[i]; | ||
344 | |||
345 | /* If we got one, initialize it now. */ | ||
346 | if (synth) | ||
347 | ret = do_synth_init(synth); | ||
348 | else | ||
349 | ret = -ENODEV; | ||
350 | mutex_unlock(&spk_mutex); | ||
351 | |||
352 | return ret; | ||
353 | } | ||
354 | |||
355 | /* called by: synth_add() */ | ||
356 | static int do_synth_init(struct spk_synth *in_synth) | ||
357 | { | ||
358 | struct var_t *var; | ||
359 | |||
360 | synth_release(); | ||
361 | if (in_synth->checkval != SYNTH_CHECK) | ||
362 | return -EINVAL; | ||
363 | synth = in_synth; | ||
364 | synth->alive = 0; | ||
365 | pr_warn("synth probe\n"); | ||
366 | if (synth->probe(synth) < 0) { | ||
367 | pr_warn("%s: device probe failed\n", in_synth->name); | ||
368 | synth = NULL; | ||
369 | return -ENODEV; | ||
370 | } | ||
371 | synth_time_vars[0].u.n.value = | ||
372 | synth_time_vars[0].u.n.default_val = synth->delay; | ||
373 | synth_time_vars[1].u.n.value = | ||
374 | synth_time_vars[1].u.n.default_val = synth->trigger; | ||
375 | synth_time_vars[2].u.n.value = | ||
376 | synth_time_vars[2].u.n.default_val = synth->jiffies; | ||
377 | synth_time_vars[3].u.n.value = | ||
378 | synth_time_vars[3].u.n.default_val = synth->full; | ||
379 | synth_printf("%s", synth->init); | ||
380 | for (var = synth->vars; (var->var_id >= 0) && (var->var_id < MAXVARS); var++) | ||
381 | speakup_register_var(var); | ||
382 | if (!quiet_boot) | ||
383 | synth_printf("%s found\n", synth->long_name); | ||
384 | if (synth->attributes.name | ||
385 | && sysfs_create_group(speakup_kobj, &(synth->attributes)) < 0) | ||
386 | return -ENOMEM; | ||
387 | synth_flags = synth->flags; | ||
388 | wake_up_interruptible_all(&speakup_event); | ||
389 | if (speakup_task) | ||
390 | wake_up_process(speakup_task); | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | void synth_release(void) | ||
395 | { | ||
396 | struct var_t *var; | ||
397 | unsigned long flags; | ||
398 | |||
399 | if (synth == NULL) | ||
400 | return; | ||
401 | spk_lock(flags); | ||
402 | pr_info("releasing synth %s\n", synth->name); | ||
403 | synth->alive = 0; | ||
404 | del_timer(&thread_timer); | ||
405 | spk_unlock(flags); | ||
406 | if (synth->attributes.name) | ||
407 | sysfs_remove_group(speakup_kobj, &(synth->attributes)); | ||
408 | for (var = synth->vars; var->var_id != MAXVARS; var++) | ||
409 | speakup_unregister_var(var->var_id); | ||
410 | stop_serial_interrupt(); | ||
411 | synth->release(); | ||
412 | synth = NULL; | ||
413 | } | ||
414 | |||
415 | /* called by: all_driver_init() */ | ||
416 | int synth_add(struct spk_synth *in_synth) | ||
417 | { | ||
418 | int i; | ||
419 | int status = 0; | ||
420 | mutex_lock(&spk_mutex); | ||
421 | for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++) | ||
422 | /* synth_remove() is responsible for rotating the array down */ | ||
423 | if (in_synth == synths[i]) { | ||
424 | mutex_unlock(&spk_mutex); | ||
425 | return 0; | ||
426 | } | ||
427 | if (i == MAXSYNTHS) { | ||
428 | pr_warn("Error: attempting to add a synth past end of array\n"); | ||
429 | mutex_unlock(&spk_mutex); | ||
430 | return -1; | ||
431 | } | ||
432 | synths[i++] = in_synth; | ||
433 | synths[i] = NULL; | ||
434 | if (in_synth->startup) | ||
435 | status = do_synth_init(in_synth); | ||
436 | mutex_unlock(&spk_mutex); | ||
437 | return status; | ||
438 | } | ||
439 | EXPORT_SYMBOL_GPL(synth_add); | ||
440 | |||
441 | void synth_remove(struct spk_synth *in_synth) | ||
442 | { | ||
443 | int i; | ||
444 | mutex_lock(&spk_mutex); | ||
445 | if (synth == in_synth) | ||
446 | synth_release(); | ||
447 | for (i = 0; synths[i] != NULL; i++) { | ||
448 | if (in_synth == synths[i]) | ||
449 | break; | ||
450 | } | ||
451 | for ( ; synths[i] != NULL; i++) /* compress table */ | ||
452 | synths[i] = synths[i+1]; | ||
453 | module_status = 0; | ||
454 | mutex_unlock(&spk_mutex); | ||
455 | } | ||
456 | EXPORT_SYMBOL_GPL(synth_remove); | ||
457 | |||
458 | short punc_masks[] = { 0, SOME, MOST, PUNC, PUNC|B_SYM }; | ||
diff --git a/drivers/staging/speakup/thread.c b/drivers/staging/speakup/thread.c new file mode 100644 index 00000000000..84531e70daf --- /dev/null +++ b/drivers/staging/speakup/thread.c | |||
@@ -0,0 +1,58 @@ | |||
1 | #include <linux/kthread.h> | ||
2 | #include <linux/wait.h> | ||
3 | |||
4 | #include "spk_types.h" | ||
5 | #include "speakup.h" | ||
6 | #include "spk_priv.h" | ||
7 | |||
8 | DECLARE_WAIT_QUEUE_HEAD(speakup_event); | ||
9 | EXPORT_SYMBOL_GPL(speakup_event); | ||
10 | |||
11 | int speakup_thread(void *data) | ||
12 | { | ||
13 | unsigned long flags; | ||
14 | int should_break; | ||
15 | struct bleep our_sound; | ||
16 | |||
17 | our_sound.active = 0; | ||
18 | our_sound.freq = 0; | ||
19 | our_sound.jiffies = 0; | ||
20 | |||
21 | mutex_lock(&spk_mutex); | ||
22 | while (1) { | ||
23 | DEFINE_WAIT(wait); | ||
24 | while(1) { | ||
25 | spk_lock(flags); | ||
26 | our_sound = unprocessed_sound; | ||
27 | unprocessed_sound.active = 0; | ||
28 | prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE); | ||
29 | should_break = kthread_should_stop() || | ||
30 | our_sound.active || | ||
31 | (synth && synth->catch_up && synth->alive && | ||
32 | (speakup_info.flushing || | ||
33 | !synth_buffer_empty())); | ||
34 | spk_unlock(flags); | ||
35 | if (should_break) | ||
36 | break; | ||
37 | mutex_unlock(&spk_mutex); | ||
38 | schedule(); | ||
39 | mutex_lock(&spk_mutex); | ||
40 | } | ||
41 | finish_wait(&speakup_event, &wait); | ||
42 | if (kthread_should_stop()) | ||
43 | break; | ||
44 | |||
45 | if (our_sound.active) { | ||
46 | kd_mksound(our_sound.freq, our_sound.jiffies); | ||
47 | } | ||
48 | if (synth && synth->catch_up && synth->alive) { | ||
49 | /* It is up to the callee to take the lock, so that it | ||
50 | * can sleep whenever it likes */ | ||
51 | synth->catch_up(synth); | ||
52 | } | ||
53 | |||
54 | speakup_start_ttys(); | ||
55 | } | ||
56 | mutex_unlock(&spk_mutex); | ||
57 | return 0; | ||
58 | } | ||
diff --git a/drivers/staging/speakup/varhandlers.c b/drivers/staging/speakup/varhandlers.c new file mode 100644 index 00000000000..2d137bd84df --- /dev/null +++ b/drivers/staging/speakup/varhandlers.c | |||
@@ -0,0 +1,403 @@ | |||
1 | #include <linux/ctype.h> | ||
2 | #include "spk_types.h" | ||
3 | #include "spk_priv.h" | ||
4 | #include "speakup.h" | ||
5 | |||
6 | static struct st_var_header var_headers[] = { | ||
7 | { "version", VERSION, VAR_PROC, NULL, NULL }, | ||
8 | { "synth_name", SYNTH, VAR_PROC, NULL, NULL }, | ||
9 | { "keymap", KEYMAP, VAR_PROC, NULL, NULL }, | ||
10 | { "silent", SILENT, VAR_PROC, NULL, NULL }, | ||
11 | { "punc_some", PUNC_SOME, VAR_PROC, NULL, NULL }, | ||
12 | { "punc_most", PUNC_MOST, VAR_PROC, NULL, NULL }, | ||
13 | { "punc_all", PUNC_ALL, VAR_PROC, NULL, NULL }, | ||
14 | { "delimiters", DELIM, VAR_PROC, NULL, NULL }, | ||
15 | { "repeats", REPEATS, VAR_PROC, NULL, NULL }, | ||
16 | { "ex_num", EXNUMBER, VAR_PROC, NULL, NULL }, | ||
17 | { "characters", CHARS, VAR_PROC, NULL, NULL }, | ||
18 | { "synth_direct", SYNTH_DIRECT, VAR_PROC, NULL, NULL }, | ||
19 | { "caps_start", CAPS_START, VAR_STRING, str_caps_start, NULL }, | ||
20 | { "caps_stop", CAPS_STOP, VAR_STRING, str_caps_stop, NULL }, | ||
21 | { "delay_time", DELAY, VAR_TIME, NULL, NULL }, | ||
22 | { "trigger_time", TRIGGER, VAR_TIME, NULL, NULL }, | ||
23 | { "jiffy_delta", JIFFY, VAR_TIME, NULL, NULL }, | ||
24 | { "full_time", FULL, VAR_TIME, NULL, NULL }, | ||
25 | { "spell_delay", SPELL_DELAY, VAR_NUM, &spell_delay, NULL }, | ||
26 | { "bleeps", BLEEPS, VAR_NUM, &bleeps, NULL }, | ||
27 | { "attrib_bleep", ATTRIB_BLEEP, VAR_NUM, &attrib_bleep, NULL }, | ||
28 | { "bleep_time", BLEEP_TIME, VAR_TIME, &bleep_time, NULL }, | ||
29 | { "cursor_time", CURSOR_TIME, VAR_TIME, NULL, NULL }, | ||
30 | { "punc_level", PUNC_LEVEL, VAR_NUM, &punc_level, NULL }, | ||
31 | { "reading_punc", READING_PUNC, VAR_NUM, &reading_punc, NULL }, | ||
32 | { "say_control", SAY_CONTROL, VAR_NUM, &say_ctrl, NULL }, | ||
33 | { "say_word_ctl", SAY_WORD_CTL, VAR_NUM, &say_word_ctl, NULL }, | ||
34 | { "no_interrupt", NO_INTERRUPT, VAR_NUM, &no_intr, NULL }, | ||
35 | { "key_echo", KEY_ECHO, VAR_NUM, &key_echo, NULL }, | ||
36 | { "bell_pos", BELL_POS, VAR_NUM, &bell_pos, NULL }, | ||
37 | { "rate", RATE, VAR_NUM, NULL, NULL }, | ||
38 | { "pitch", PITCH, VAR_NUM, NULL, NULL }, | ||
39 | { "vol", VOL, VAR_NUM, NULL, NULL }, | ||
40 | { "tone", TONE, VAR_NUM, NULL, NULL }, | ||
41 | { "punct", PUNCT, VAR_NUM, NULL, NULL }, | ||
42 | { "voice", VOICE, VAR_NUM, NULL, NULL }, | ||
43 | { "freq", FREQUENCY, VAR_NUM, NULL, NULL }, | ||
44 | { "lang", LANG, VAR_NUM, NULL, NULL }, | ||
45 | { "chartab", CHARTAB, VAR_PROC, NULL, NULL }, | ||
46 | { "direct", DIRECT, VAR_NUM, NULL, NULL }, | ||
47 | }; | ||
48 | |||
49 | static struct st_var_header *var_ptrs[MAXVARS] = { 0, 0, 0 }; | ||
50 | |||
51 | static struct punc_var_t punc_vars[] = { | ||
52 | { PUNC_SOME, 1 }, | ||
53 | { PUNC_MOST, 2 }, | ||
54 | { PUNC_ALL, 3 }, | ||
55 | { DELIM, 4 }, | ||
56 | { REPEATS, 5 }, | ||
57 | { EXNUMBER, 6 }, | ||
58 | { -1, -1 }, | ||
59 | }; | ||
60 | |||
61 | int chartab_get_value(char *keyword) | ||
62 | { | ||
63 | int value = 0; | ||
64 | |||
65 | if (!strcmp(keyword, "ALPHA")) | ||
66 | value = ALPHA; | ||
67 | else if (!strcmp(keyword, "B_CTL")) | ||
68 | value = B_CTL; | ||
69 | else if (!strcmp(keyword, "WDLM")) | ||
70 | value = WDLM; | ||
71 | else if (!strcmp(keyword, "A_PUNC")) | ||
72 | value = A_PUNC; | ||
73 | else if (!strcmp(keyword, "PUNC")) | ||
74 | value = PUNC; | ||
75 | else if (!strcmp(keyword, "NUM")) | ||
76 | value = NUM; | ||
77 | else if (!strcmp(keyword, "A_CAP")) | ||
78 | value = A_CAP; | ||
79 | else if (!strcmp(keyword, "B_CAPSYM")) | ||
80 | value = B_CAPSYM; | ||
81 | else if (!strcmp(keyword, "B_SYM")) | ||
82 | value = B_SYM; | ||
83 | return value; | ||
84 | } | ||
85 | |||
86 | void speakup_register_var(struct var_t *var) | ||
87 | { | ||
88 | static char nothing[2] = "\0"; | ||
89 | int i; | ||
90 | struct st_var_header *p_header; | ||
91 | |||
92 | BUG_ON(!var || var->var_id < 0 || var->var_id >= MAXVARS); | ||
93 | if (var_ptrs[0] == NULL) { | ||
94 | for (i = 0; i < MAXVARS; i++) { | ||
95 | p_header = &var_headers[i]; | ||
96 | var_ptrs[p_header->var_id] = p_header; | ||
97 | p_header->data = NULL; | ||
98 | } | ||
99 | } | ||
100 | p_header = var_ptrs[var->var_id]; | ||
101 | if (p_header->data != NULL) | ||
102 | return; | ||
103 | p_header->data = var; | ||
104 | switch (p_header->var_type) { | ||
105 | case VAR_STRING: | ||
106 | set_string_var(nothing, p_header, 0); | ||
107 | break; | ||
108 | case VAR_NUM: | ||
109 | case VAR_TIME: | ||
110 | set_num_var(0, p_header, E_DEFAULT); | ||
111 | break; | ||
112 | default: | ||
113 | break; | ||
114 | } | ||
115 | return; | ||
116 | } | ||
117 | |||
118 | void speakup_unregister_var(enum var_id_t var_id) | ||
119 | { | ||
120 | struct st_var_header *p_header; | ||
121 | BUG_ON(var_id < 0 || var_id >= MAXVARS); | ||
122 | p_header = var_ptrs[var_id]; | ||
123 | p_header->data = NULL; | ||
124 | } | ||
125 | |||
126 | struct st_var_header *get_var_header(enum var_id_t var_id) | ||
127 | { | ||
128 | struct st_var_header *p_header; | ||
129 | if (var_id < 0 || var_id >= MAXVARS) | ||
130 | return NULL; | ||
131 | p_header = var_ptrs[var_id]; | ||
132 | if (p_header->data == NULL) | ||
133 | return NULL; | ||
134 | return p_header; | ||
135 | } | ||
136 | |||
137 | struct st_var_header *var_header_by_name(const char *name) | ||
138 | { | ||
139 | int i; | ||
140 | struct st_var_header *where = NULL; | ||
141 | |||
142 | if (name != NULL) { | ||
143 | i = 0; | ||
144 | while ((i < MAXVARS) && (where == NULL)) { | ||
145 | if (strcmp(name, var_ptrs[i]->name) == 0) | ||
146 | where = var_ptrs[i]; | ||
147 | else | ||
148 | i++; | ||
149 | } | ||
150 | } | ||
151 | return where; | ||
152 | } | ||
153 | |||
154 | struct var_t *get_var(enum var_id_t var_id) | ||
155 | { | ||
156 | BUG_ON(var_id < 0 || var_id >= MAXVARS); | ||
157 | BUG_ON(! var_ptrs[var_id]); | ||
158 | return (var_ptrs[var_id]->data); | ||
159 | } | ||
160 | EXPORT_SYMBOL_GPL(get_var); | ||
161 | |||
162 | struct punc_var_t *get_punc_var(enum var_id_t var_id) | ||
163 | { | ||
164 | struct punc_var_t *rv = NULL; | ||
165 | struct punc_var_t *where; | ||
166 | |||
167 | where = punc_vars; | ||
168 | while ((where->var_id != -1) && (rv == NULL)) { | ||
169 | if (where->var_id == var_id) | ||
170 | rv = where; | ||
171 | else | ||
172 | where++; | ||
173 | } | ||
174 | return rv; | ||
175 | } | ||
176 | |||
177 | /* handlers for setting vars */ | ||
178 | int set_num_var(int input, struct st_var_header *var, int how) | ||
179 | { | ||
180 | int val; | ||
181 | short ret = 0; | ||
182 | int *p_val = var->p_val; | ||
183 | int l; | ||
184 | char buf[32]; | ||
185 | char *cp; | ||
186 | struct var_t *var_data = var->data; | ||
187 | if (var_data == NULL) | ||
188 | return E_UNDEF; | ||
189 | |||
190 | if (how == E_NEW_DEFAULT) { | ||
191 | if (input < var_data->u.n.low || input > var_data->u.n.high) | ||
192 | ret = E_RANGE; | ||
193 | else | ||
194 | var_data->u.n.default_val = input; | ||
195 | return ret; | ||
196 | } | ||
197 | if (how == E_DEFAULT) { | ||
198 | val = var_data->u.n.default_val; | ||
199 | ret = SET_DEFAULT; | ||
200 | } else { | ||
201 | if (how == E_SET) | ||
202 | val = input; | ||
203 | else | ||
204 | val = var_data->u.n.value; | ||
205 | if (how == E_INC) | ||
206 | val += input; | ||
207 | else if (how == E_DEC) | ||
208 | val -= input; | ||
209 | if (val < var_data->u.n.low || val > var_data->u.n.high) | ||
210 | return E_RANGE; | ||
211 | } | ||
212 | var_data->u.n.value = val; | ||
213 | if (var->var_type == VAR_TIME && p_val != NULL) { | ||
214 | *p_val = msecs_to_jiffies(val); | ||
215 | return ret; | ||
216 | } | ||
217 | if (p_val != NULL) | ||
218 | *p_val = val; | ||
219 | if (var->var_id == PUNC_LEVEL) { | ||
220 | punc_mask = punc_masks[val]; | ||
221 | return ret; | ||
222 | } | ||
223 | if (var_data->u.n.multiplier != 0) | ||
224 | val *= var_data->u.n.multiplier; | ||
225 | val += var_data->u.n.offset; | ||
226 | if (var->var_id < FIRST_SYNTH_VAR || synth == NULL) | ||
227 | return ret; | ||
228 | if (synth->synth_adjust != NULL) { | ||
229 | int status = synth->synth_adjust(var); | ||
230 | return (status != 0) ? status : ret; | ||
231 | } | ||
232 | if (!var_data->u.n.synth_fmt) | ||
233 | return ret; | ||
234 | if (var->var_id == PITCH) | ||
235 | cp = pitch_buff; | ||
236 | else | ||
237 | cp = buf; | ||
238 | if (!var_data->u.n.out_str) | ||
239 | l = sprintf(cp, var_data->u.n.synth_fmt, (int)val); | ||
240 | else | ||
241 | l = sprintf(cp, var_data->u.n.synth_fmt, var_data->u.n.out_str[val]); | ||
242 | synth_printf("%s", cp); | ||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | int set_string_var(const char *page, struct st_var_header *var, int len) | ||
247 | { | ||
248 | int ret = 0; | ||
249 | struct var_t *var_data = var->data; | ||
250 | if (var_data == NULL) | ||
251 | return E_UNDEF; | ||
252 | if (len > MAXVARLEN) | ||
253 | return -E_TOOLONG; | ||
254 | if (!len) { | ||
255 | if (!var_data->u.s.default_val) | ||
256 | return 0; | ||
257 | ret = SET_DEFAULT; | ||
258 | if (!var->p_val) | ||
259 | var->p_val = var_data->u.s.default_val; | ||
260 | if (var->p_val != var_data->u.s.default_val) | ||
261 | strcpy((char *)var->p_val, var_data->u.s.default_val); | ||
262 | } else if (var->p_val) | ||
263 | strcpy((char *)var->p_val, page); | ||
264 | else | ||
265 | return -E_TOOLONG; | ||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | /* set_mask_bits sets or clears the punc/delim/repeat bits, | ||
270 | * if input is null uses the defaults. | ||
271 | * values for how: 0 clears bits of chars supplied, | ||
272 | * 1 clears allk, 2 sets bits for chars */ | ||
273 | int set_mask_bits(const char *input, const int which, const int how) | ||
274 | { | ||
275 | u_char *cp; | ||
276 | short mask = punc_info[which].mask; | ||
277 | if (how&1) { | ||
278 | for (cp = (u_char *)punc_info[3].value; *cp; cp++) | ||
279 | spk_chartab[*cp] &= ~mask; | ||
280 | } | ||
281 | cp = (u_char *)input; | ||
282 | if (cp == 0) | ||
283 | cp = punc_info[which].value; | ||
284 | else { | ||
285 | for ( ; *cp; cp++) { | ||
286 | if (*cp < SPACE) | ||
287 | break; | ||
288 | if (mask < PUNC) { | ||
289 | if (!(spk_chartab[*cp]&PUNC)) | ||
290 | break; | ||
291 | } else if (spk_chartab[*cp]&B_NUM) | ||
292 | break; | ||
293 | } | ||
294 | if (*cp) | ||
295 | return -EINVAL; | ||
296 | cp = (u_char *)input; | ||
297 | } | ||
298 | if (how&2) { | ||
299 | for ( ; *cp; cp++) | ||
300 | if (*cp > SPACE) | ||
301 | spk_chartab[*cp] |= mask; | ||
302 | } else { | ||
303 | for ( ; *cp; cp++) | ||
304 | if (*cp > SPACE) | ||
305 | spk_chartab[*cp] &= ~mask; | ||
306 | } | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | char *strlwr(char *s) | ||
311 | { | ||
312 | char *p; | ||
313 | if (s == NULL) | ||
314 | return NULL; | ||
315 | |||
316 | for (p = s; *p; p++) | ||
317 | *p = tolower(*p); | ||
318 | return s; | ||
319 | } | ||
320 | |||
321 | char *speakup_s2i(char *start, int *dest) | ||
322 | { | ||
323 | int val; | ||
324 | char ch = *start; | ||
325 | if (ch == '-' || ch == '+') | ||
326 | start++; | ||
327 | if (*start < '0' || *start > '9') | ||
328 | return start; | ||
329 | val = (*start) - '0'; | ||
330 | start++; | ||
331 | while (*start >= '0' && *start <= '9') { | ||
332 | val *= 10; | ||
333 | val += (*start) - '0'; | ||
334 | start++; | ||
335 | } | ||
336 | if (ch == '-') | ||
337 | *dest = -val; | ||
338 | else | ||
339 | *dest = val; | ||
340 | return start; | ||
341 | } | ||
342 | |||
343 | char *s2uchar(char *start, char *dest) | ||
344 | { | ||
345 | int val = 0; | ||
346 | while (*start && *start <= SPACE) | ||
347 | start++; | ||
348 | while (*start >= '0' && *start <= '9') { | ||
349 | val *= 10; | ||
350 | val += (*start) - '0'; | ||
351 | start++; | ||
352 | } | ||
353 | if (*start == ',') | ||
354 | start++; | ||
355 | *dest = (u_char)val; | ||
356 | return start; | ||
357 | } | ||
358 | |||
359 | char *xlate(char *s) | ||
360 | { | ||
361 | static const char finds[] = "nrtvafe"; | ||
362 | static const char subs[] = "\n\r\t\013\001\014\033"; | ||
363 | static const char hx[] = "0123456789abcdefABCDEF"; | ||
364 | char *p = s, *p1, *p2, c; | ||
365 | int num; | ||
366 | while ((p = strchr(p, '\\'))) { | ||
367 | p1 = p+1; | ||
368 | p2 = strchr(finds, *p1); | ||
369 | if (p2) { | ||
370 | *p++ = subs[p2-finds]; | ||
371 | p1++; | ||
372 | } else if (*p1 >= '0' && *p1 <= '7') { | ||
373 | num = (*p1++)&7; | ||
374 | while (num < 256 && *p1 >= '0' && *p1 <= '7') { | ||
375 | num <<= 3; | ||
376 | num = (*p1++)&7; | ||
377 | } | ||
378 | *p++ = num; | ||
379 | } else if (*p1 == 'x' && | ||
380 | strchr(hx, p1[1]) && strchr(hx, p1[2])) { | ||
381 | p1++; | ||
382 | c = *p1++; | ||
383 | if (c > '9') | ||
384 | c = (c - '7') & 0x0f; | ||
385 | else | ||
386 | c -= '0'; | ||
387 | num = c << 4; | ||
388 | c = *p1++; | ||
389 | if (c > '9') | ||
390 | c = (c-'7')&0x0f; | ||
391 | else | ||
392 | c -= '0'; | ||
393 | num += c; | ||
394 | *p++ = num; | ||
395 | } else | ||
396 | *p++ = *p1++; | ||
397 | p2 = p; | ||
398 | while (*p1) | ||
399 | *p2++ = *p1++; | ||
400 | *p2 = '\0'; | ||
401 | } | ||
402 | return s; | ||
403 | } | ||