From 2067fd92d75b6d9085a43caf050bca5d88c491b8 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Wed, 29 Jul 2020 02:35:31 +0200 Subject: staging/speakup: Move out of staging The nasty TODO items are done. Signed-off-by: Samuel Thibault Link: https://lore.kernel.org/r/20200729003531.907370-1-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/stable/sysfs-driver-speakup | 375 +++ Documentation/admin-guide/spkguide.txt | 1575 +++++++++++++ MAINTAINERS | 20 +- drivers/accessibility/Kconfig | 2 + drivers/accessibility/Makefile | 1 + .../accessibility/speakup/DefaultKeyAssignments | 46 + drivers/accessibility/speakup/Kconfig | 200 ++ drivers/accessibility/speakup/Makefile | 32 + drivers/accessibility/speakup/TODO | 22 + drivers/accessibility/speakup/buffers.c | 124 + drivers/accessibility/speakup/devsynth.c | 92 + drivers/accessibility/speakup/fakekey.c | 87 + drivers/accessibility/speakup/i18n.c | 625 +++++ drivers/accessibility/speakup/i18n.h | 235 ++ drivers/accessibility/speakup/keyhelp.c | 209 ++ drivers/accessibility/speakup/kobjects.c | 1056 +++++++++ drivers/accessibility/speakup/main.c | 2460 ++++++++++++++++++++ drivers/accessibility/speakup/selection.c | 144 ++ drivers/accessibility/speakup/serialio.c | 316 +++ drivers/accessibility/speakup/serialio.h | 41 + drivers/accessibility/speakup/speakup.h | 121 + drivers/accessibility/speakup/speakup_acnt.h | 19 + drivers/accessibility/speakup/speakup_acntpc.c | 319 +++ drivers/accessibility/speakup/speakup_acntsa.c | 144 ++ drivers/accessibility/speakup/speakup_apollo.c | 208 ++ drivers/accessibility/speakup/speakup_audptr.c | 171 ++ drivers/accessibility/speakup/speakup_bns.c | 128 + drivers/accessibility/speakup/speakup_decext.c | 240 ++ drivers/accessibility/speakup/speakup_decpc.c | 495 ++++ drivers/accessibility/speakup/speakup_dectlk.c | 311 +++ drivers/accessibility/speakup/speakup_dtlk.c | 390 ++++ drivers/accessibility/speakup/speakup_dtlk.h | 63 + drivers/accessibility/speakup/speakup_dummy.c | 134 ++ drivers/accessibility/speakup/speakup_keypc.c | 318 +++ drivers/accessibility/speakup/speakup_ltlk.c | 175 ++ drivers/accessibility/speakup/speakup_soft.c | 430 ++++ drivers/accessibility/speakup/speakup_spkout.c | 139 ++ drivers/accessibility/speakup/speakup_txprt.c | 127 + drivers/accessibility/speakup/speakupmap.h | 66 + drivers/accessibility/speakup/speakupmap.map | 93 + drivers/accessibility/speakup/spk_priv.h | 84 + drivers/accessibility/speakup/spk_priv_keyinfo.h | 100 + drivers/accessibility/speakup/spk_ttyio.c | 384 +++ drivers/accessibility/speakup/spk_types.h | 221 ++ drivers/accessibility/speakup/synth.c | 490 ++++ drivers/accessibility/speakup/thread.c | 62 + drivers/accessibility/speakup/varhandlers.c | 339 +++ drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/speakup/DefaultKeyAssignments | 46 - drivers/staging/speakup/Kconfig | 200 -- drivers/staging/speakup/Makefile | 32 - drivers/staging/speakup/TODO | 22 - drivers/staging/speakup/buffers.c | 124 - drivers/staging/speakup/devsynth.c | 92 - drivers/staging/speakup/fakekey.c | 87 - drivers/staging/speakup/i18n.c | 625 ----- drivers/staging/speakup/i18n.h | 235 -- drivers/staging/speakup/keyhelp.c | 209 -- drivers/staging/speakup/kobjects.c | 1056 --------- drivers/staging/speakup/main.c | 2460 -------------------- drivers/staging/speakup/selection.c | 144 -- drivers/staging/speakup/serialio.c | 316 --- drivers/staging/speakup/serialio.h | 41 - drivers/staging/speakup/speakup.h | 121 - drivers/staging/speakup/speakup_acnt.h | 19 - drivers/staging/speakup/speakup_acntpc.c | 319 --- drivers/staging/speakup/speakup_acntsa.c | 144 -- drivers/staging/speakup/speakup_apollo.c | 208 -- drivers/staging/speakup/speakup_audptr.c | 171 -- drivers/staging/speakup/speakup_bns.c | 128 - drivers/staging/speakup/speakup_decext.c | 240 -- drivers/staging/speakup/speakup_decpc.c | 495 ---- drivers/staging/speakup/speakup_dectlk.c | 311 --- drivers/staging/speakup/speakup_dtlk.c | 390 ---- drivers/staging/speakup/speakup_dtlk.h | 63 - drivers/staging/speakup/speakup_dummy.c | 134 -- drivers/staging/speakup/speakup_keypc.c | 318 --- drivers/staging/speakup/speakup_ltlk.c | 175 -- drivers/staging/speakup/speakup_soft.c | 430 ---- drivers/staging/speakup/speakup_spkout.c | 139 -- drivers/staging/speakup/speakup_txprt.c | 127 - drivers/staging/speakup/speakupmap.h | 66 - drivers/staging/speakup/speakupmap.map | 93 - drivers/staging/speakup/spk_priv.h | 84 - drivers/staging/speakup/spk_priv_keyinfo.h | 100 - drivers/staging/speakup/spk_ttyio.c | 384 --- drivers/staging/speakup/spk_types.h | 221 -- drivers/staging/speakup/spkguide.txt | 1575 ------------- drivers/staging/speakup/synth.c | 490 ---- drivers/staging/speakup/sysfs-driver-speakup | 375 --- drivers/staging/speakup/thread.c | 62 - drivers/staging/speakup/varhandlers.c | 339 --- 93 files changed, 13423 insertions(+), 13423 deletions(-) create mode 100644 Documentation/ABI/stable/sysfs-driver-speakup create mode 100644 Documentation/admin-guide/spkguide.txt create mode 100644 drivers/accessibility/speakup/DefaultKeyAssignments create mode 100644 drivers/accessibility/speakup/Kconfig create mode 100644 drivers/accessibility/speakup/Makefile create mode 100644 drivers/accessibility/speakup/TODO create mode 100644 drivers/accessibility/speakup/buffers.c create mode 100644 drivers/accessibility/speakup/devsynth.c create mode 100644 drivers/accessibility/speakup/fakekey.c create mode 100644 drivers/accessibility/speakup/i18n.c create mode 100644 drivers/accessibility/speakup/i18n.h create mode 100644 drivers/accessibility/speakup/keyhelp.c create mode 100644 drivers/accessibility/speakup/kobjects.c create mode 100644 drivers/accessibility/speakup/main.c create mode 100644 drivers/accessibility/speakup/selection.c create mode 100644 drivers/accessibility/speakup/serialio.c create mode 100644 drivers/accessibility/speakup/serialio.h create mode 100644 drivers/accessibility/speakup/speakup.h create mode 100644 drivers/accessibility/speakup/speakup_acnt.h create mode 100644 drivers/accessibility/speakup/speakup_acntpc.c create mode 100644 drivers/accessibility/speakup/speakup_acntsa.c create mode 100644 drivers/accessibility/speakup/speakup_apollo.c create mode 100644 drivers/accessibility/speakup/speakup_audptr.c create mode 100644 drivers/accessibility/speakup/speakup_bns.c create mode 100644 drivers/accessibility/speakup/speakup_decext.c create mode 100644 drivers/accessibility/speakup/speakup_decpc.c create mode 100644 drivers/accessibility/speakup/speakup_dectlk.c create mode 100644 drivers/accessibility/speakup/speakup_dtlk.c create mode 100644 drivers/accessibility/speakup/speakup_dtlk.h create mode 100644 drivers/accessibility/speakup/speakup_dummy.c create mode 100644 drivers/accessibility/speakup/speakup_keypc.c create mode 100644 drivers/accessibility/speakup/speakup_ltlk.c create mode 100644 drivers/accessibility/speakup/speakup_soft.c create mode 100644 drivers/accessibility/speakup/speakup_spkout.c create mode 100644 drivers/accessibility/speakup/speakup_txprt.c create mode 100644 drivers/accessibility/speakup/speakupmap.h create mode 100644 drivers/accessibility/speakup/speakupmap.map create mode 100644 drivers/accessibility/speakup/spk_priv.h create mode 100644 drivers/accessibility/speakup/spk_priv_keyinfo.h create mode 100644 drivers/accessibility/speakup/spk_ttyio.c create mode 100644 drivers/accessibility/speakup/spk_types.h create mode 100644 drivers/accessibility/speakup/synth.c create mode 100644 drivers/accessibility/speakup/thread.c create mode 100644 drivers/accessibility/speakup/varhandlers.c delete mode 100644 drivers/staging/speakup/DefaultKeyAssignments delete mode 100644 drivers/staging/speakup/Kconfig delete mode 100644 drivers/staging/speakup/Makefile delete mode 100644 drivers/staging/speakup/TODO delete mode 100644 drivers/staging/speakup/buffers.c delete mode 100644 drivers/staging/speakup/devsynth.c delete mode 100644 drivers/staging/speakup/fakekey.c delete mode 100644 drivers/staging/speakup/i18n.c delete mode 100644 drivers/staging/speakup/i18n.h delete mode 100644 drivers/staging/speakup/keyhelp.c delete mode 100644 drivers/staging/speakup/kobjects.c delete mode 100644 drivers/staging/speakup/main.c delete mode 100644 drivers/staging/speakup/selection.c delete mode 100644 drivers/staging/speakup/serialio.c delete mode 100644 drivers/staging/speakup/serialio.h delete mode 100644 drivers/staging/speakup/speakup.h delete mode 100644 drivers/staging/speakup/speakup_acnt.h delete mode 100644 drivers/staging/speakup/speakup_acntpc.c delete mode 100644 drivers/staging/speakup/speakup_acntsa.c delete mode 100644 drivers/staging/speakup/speakup_apollo.c delete mode 100644 drivers/staging/speakup/speakup_audptr.c delete mode 100644 drivers/staging/speakup/speakup_bns.c delete mode 100644 drivers/staging/speakup/speakup_decext.c delete mode 100644 drivers/staging/speakup/speakup_decpc.c delete mode 100644 drivers/staging/speakup/speakup_dectlk.c delete mode 100644 drivers/staging/speakup/speakup_dtlk.c delete mode 100644 drivers/staging/speakup/speakup_dtlk.h delete mode 100644 drivers/staging/speakup/speakup_dummy.c delete mode 100644 drivers/staging/speakup/speakup_keypc.c delete mode 100644 drivers/staging/speakup/speakup_ltlk.c delete mode 100644 drivers/staging/speakup/speakup_soft.c delete mode 100644 drivers/staging/speakup/speakup_spkout.c delete mode 100644 drivers/staging/speakup/speakup_txprt.c delete mode 100644 drivers/staging/speakup/speakupmap.h delete mode 100644 drivers/staging/speakup/speakupmap.map delete mode 100644 drivers/staging/speakup/spk_priv.h delete mode 100644 drivers/staging/speakup/spk_priv_keyinfo.h delete mode 100644 drivers/staging/speakup/spk_ttyio.c delete mode 100644 drivers/staging/speakup/spk_types.h delete mode 100644 drivers/staging/speakup/spkguide.txt delete mode 100644 drivers/staging/speakup/synth.c delete mode 100644 drivers/staging/speakup/sysfs-driver-speakup delete mode 100644 drivers/staging/speakup/thread.c delete mode 100644 drivers/staging/speakup/varhandlers.c diff --git a/Documentation/ABI/stable/sysfs-driver-speakup b/Documentation/ABI/stable/sysfs-driver-speakup new file mode 100644 index 000000000000..c6a32c434ce9 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-driver-speakup @@ -0,0 +1,375 @@ +What: /sys/accessibility/speakup/attrib_bleep +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Beeps the PC speaker when there is an attribute change such as + foreground or background color when using speakup review + commands. One = on, zero = off. + +What: /sys/accessibility/speakup/bell_pos +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This works much like a typewriter bell. If for example 72 is + echoed to bell_pos, it will beep the PC speaker when typing on + a line past character 72. + +What: /sys/accessibility/speakup/bleeps +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls whether one hears beeps through the PC speaker + when using speakup's review commands. + TODO: what values does it accept? + +What: /sys/accessibility/speakup/bleep_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls the duration of the PC speaker beeps speakup + produces. + TODO: What are the units? Jiffies? + +What: /sys/accessibility/speakup/cursor_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls cursor delay when using arrow keys. When a + connection is very slow, with the default setting, when moving + with the arrows, or backspacing etc. speakup says the incorrect + characters. Set this to a higher value to adjust for the delay + and better synchronisation between cursor position and speech. + +What: /sys/accessibility/speakup/delimiters +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Delimit a word from speakup. + TODO: add more info + +What: /sys/accessibility/speakup/ex_num +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/key_echo +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Controls if speakup speaks keys when they are typed. One = on, + zero = off or don't echo keys. + +What: /sys/accessibility/speakup/keymap +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Speakup keymap remaps keys to Speakup functions. + It uses a binary + format. A special program called genmap is needed to compile a + textual keymap into the binary format which is then loaded into + /sys/accessibility/speakup/keymap. + +What: /sys/accessibility/speakup/no_interrupt +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Controls if typing interrupts output from speakup. With + no_interrupt set to zero, typing on the keyboard will interrupt + speakup if for example + the say screen command is used before the + entire screen is read. + With no_interrupt set to one, if the say + screen command is used, and one then types on the keyboard, + speakup will continue to say the whole screen regardless until + it finishes. + +What: /sys/accessibility/speakup/punc_all +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is a list of all the punctuation speakup should speak when + punc_level is set to four. + +What: /sys/accessibility/speakup/punc_level +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Controls the level of punctuation spoken as the screen is + displayed, not reviewed. Levels range from zero no punctuation, + to four, all punctuation. One corresponds to punc_some, two + corresponds to punc_most, and three as well as four both + correspond to punc_all. Some hardware synthesizers may have + different levels each corresponding to three and four for + punc_level. Also note that if punc_level is set to zero, and + key_echo is set to one, typed punctuation is still spoken as it + is typed. + +What: /sys/accessibility/speakup/punc_most +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is a list of all the punctuation speakup should speak when + punc_level is set to two. + +What: /sys/accessibility/speakup/punc_some +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is a list of all the punctuation speakup should speak when + punc_level is set to one. + +What: /sys/accessibility/speakup/reading_punc +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Almost the same as punc_level, the differences being that + reading_punc controls the level of punctuation when reviewing + the screen with speakup's screen review commands. The other + difference is that reading_punc set to three speaks punc_all, + and reading_punc set to four speaks all punctuation, including + spaces. + +What: /sys/accessibility/speakup/repeats +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: A list of characters speakup repeats. Normally, when there are + more than three characters in a row, speakup + just reads three of + those characters. For example, "......" would be read as dot, + dot, dot. If a . is added to the list of characters in repeats, + "......" would be read as dot, dot, dot, times six. + +What: /sys/accessibility/speakup/say_control +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: If set to one, speakup speaks shift, alt and control when those + keys are pressed. If say_control is set to zero, shift, ctrl, + and alt are not spoken when they are pressed. + +What: /sys/accessibility/speakup/say_word_ctl +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/silent +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/spell_delay +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls how fast a word is spelled + when speakup's say word + review command is pressed twice quickly to speak the current + word being reviewed. Zero just speaks the letters one after + another, while values one through four + seem to introduce more of + a pause between the spelling of each letter by speakup. + +What: /sys/accessibility/speakup/synth +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the synthesizer driver currently in use. Reading + synth returns the synthesizer driver currently in use. Writing + synth switches to the given synthesizer driver, provided it is + either built into the kernel, or already loaded as a module. + +What: /sys/accessibility/speakup/synth_direct +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Sends whatever is written to synth_direct + directly to the speech synthesizer in use, bypassing speakup. + This could be used to make the synthesizer speak + a string, or to + send control sequences to the synthesizer to change how the + synthesizer behaves. + +What: /sys/accessibility/speakup/version +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Reading version returns the version of speakup, and the version + of the synthesizer driver currently in use. + +What: /sys/accessibility/speakup/i18n/announcements +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This file contains various general announcements, most of which + cannot be categorized. You will find messages such as "You + killed Speakup", "I'm alive", "leaving help", "parked", + "unparked", and others. You will also find the names of the + screen edges and cursor tracking modes here. + +What: /sys/accessibility/speakup/i18n/chartab +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO + +What: /sys/accessibility/speakup/i18n/ctl_keys +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Here, you will find names of control keys. These are used with + Speakup's say_control feature. + +What: /sys/accessibility/speakup/i18n/function_names +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Here, you will find a list of names for Speakup functions. + These are used by the help system. For example, suppose that + you have activated help mode, and you pressed + keypad 3. Speakup + says: "keypad 3 is character, say next." + The message "character, say next" names a Speakup function, and + it comes from this function_names file. + +What: /sys/accessibility/speakup/i18n/states +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This file contains names for key states. + Again, these are part of the help system. For instance, if you + had pressed speakup + keypad 3, you would hear: + "speakup keypad 3 is go to bottom edge." + The speakup key is depressed, so the name of the key state is + speakup. + This part of the message comes from the states collection. + +What: /sys/accessibility/speakup/i18n/characters +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Through this sys entry, Speakup gives you the ability to change + how Speakup pronounces a given character. You could, for + example, change how some punctuation characters are spoken. You + can even change how Speakup will pronounce certain letters. For + further details see '12. Changing the Pronunciation of + Characters' in Speakup User's Guide (file spkguide.txt in + source). + +What: /sys/accessibility/speakup/i18n/colors +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: When you use the "say attributes" function, Speakup says the + name of the foreground and background colors. These names come + from the i18n/colors file. + +What: /sys/accessibility/speakup/i18n/formatted +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This group of messages contains embedded formatting codes, to + specify the type and width of displayed data. If you change + these, you must preserve all of the formatting codes, and they + must appear in the order used by the default messages. + +What: /sys/accessibility/speakup/i18n/key_names +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Again, key_names is used by Speakup's help system. In the + previous example, Speakup said that you pressed "keypad 3." + This name came from the key_names file. + +What: /sys/accessibility/speakup// +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: In `/sys/accessibility/speakup` is a directory corresponding to + the synthesizer driver currently in use (E.G) `soft` for the + soft driver. This directory contains files which control the + speech synthesizer itself, + as opposed to controlling the speakup + screen reader. The parameters in this directory have the same + names and functions across all + supported synthesizers. The range + of values for freq, pitch, rate, and vol is the same for all + supported synthesizers, with the given range being internally + mapped by the driver to more or less fit the range of values + supported for a given parameter by the individual synthesizer. + Below is a description of values and parameters for soft + synthesizer, which is currently the most commonly used. + +What: /sys/accessibility/speakup/soft/caps_start +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is the string that is sent to the synthesizer to cause it + to start speaking uppercase letters. For the soft synthesizer + and most others, this causes the pitch of the voice to rise + above the currently set pitch. + +What: /sys/accessibility/speakup/soft/caps_stop +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is the string sent to the synthesizer to cause it to stop + speaking uppercase letters. In the case of the soft synthesizer + and most others, this returns the pitch of the voice + down to the + currently set pitch. + +What: /sys/accessibility/speakup/soft/delay_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/soft/direct +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Controls if punctuation is spoken by speakup, or by the + synthesizer. + For example, speakup speaks ">" as "greater", while + the espeak synthesizer used by the soft driver speaks "greater + than". Zero lets speakup speak the punctuation. One lets the + synthesizer itself speak punctuation. + +What: /sys/accessibility/speakup/soft/freq +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the frequency of the speech synthesizer. Range is + 0-9. + +What: /sys/accessibility/speakup/soft/full_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/soft/jiffy_delta +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls how many jiffys the kernel gives to the + synthesizer. Setting this too high can make a system unstable, + or even crash it. + +What: /sys/accessibility/speakup/soft/pitch +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the pitch of the synthesizer. The range is 0-9. + +What: /sys/accessibility/speakup/soft/inflection +KernelVersion: 5.8 +Contact: speakup@linux-speakup.org +Description: Gets or sets the inflection of the synthesizer, i.e. the pitch + range. The range is 0-9. + +What: /sys/accessibility/speakup/soft/punct +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the amount of punctuation spoken by the + synthesizer. The range for the soft driver seems to be 0-2. + TODO: How is this related to speakup's punc_level, or + reading_punc. + +What: /sys/accessibility/speakup/soft/rate +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the rate of the synthesizer. Range is from zero + slowest, to nine fastest. + +What: /sys/accessibility/speakup/soft/tone +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the tone of the speech synthesizer. The range for + the soft driver seems to be 0-2. This seems to make no + difference if using espeak and the espeakup connector. + TODO: does espeakup support different tonalities? + +What: /sys/accessibility/speakup/soft/trigger_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/soft/voice +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the voice used by the synthesizer if the + synthesizer can speak in more than one voice. The range for the + soft driver is 0-7. Note that while espeak supports multiple + voices, this parameter will not set the voice when the espeakup + connector is used between speakup and espeak. + +What: /sys/accessibility/speakup/soft/vol +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the volume of the speech synthesizer. Range is 0-9, + with zero being the softest, and nine being the loudest. + diff --git a/Documentation/admin-guide/spkguide.txt b/Documentation/admin-guide/spkguide.txt new file mode 100644 index 000000000000..3782f6a09e97 --- /dev/null +++ b/Documentation/admin-guide/spkguide.txt @@ -0,0 +1,1575 @@ + +The Speakup User's Guide +For Speakup 3.1.2 and Later +By Gene Collins +Updated by others +Last modified on Mon Sep 27 14:26:31 2010 +Document version 1.3 + +Copyright (c) 2005 Gene Collins +Copyright (c) 2008 Samuel Thibault +Copyright (c) 2009, 2010 the Speakup Team + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.2 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A +copy of the license is included in the section entitled "GNU Free +Documentation License". + +Preface + +The purpose of this document is to familiarize users with the user +interface to Speakup, a Linux Screen Reader. If you need instructions +for installing or obtaining Speakup, visit the web site at +http://linux-speakup.org/. Speakup is a set of patches to the standard +Linux kernel source tree. It can be built as a series of modules, or as +a part of a monolithic kernel. These details are beyond the scope of +this manual, but the user may need to be aware of the module +capabilities, depending on how your system administrator has installed +Speakup. If Speakup is built as a part of a monolithic kernel, and the +user is using a hardware synthesizer, then Speakup will be able to +provide speech access from the time the kernel is loaded, until the time +the system is shutdown. This means that if you have obtained Linux +installation media for a distribution which includes Speakup as a part +of its kernel, you will be able, as a blind person, to install Linux +with speech access unaided by a sighted person. Again, these details +are beyond the scope of this manual, but the user should be aware of +them. See the web site mentioned above for further details. + +1. Starting Speakup + +If your system administrator has installed Speakup to work with your +specific synthesizer by default, then all you need to do to use Speakup +is to boot your system, and Speakup should come up talking. This +assumes of course that your synthesizer is a supported hardware +synthesizer, and that it is either installed in or connected to your +system, and is if necessary powered on. + +It is possible, however, that Speakup may have been compiled into the +kernel with no default synthesizer. It is even possible that your +kernel has been compiled with support for some of the supported +synthesizers and not others. If you find that this is the case, and +your synthesizer is supported but not available, complain to the person +who compiled and installed your kernel. Or better yet, go to the web +site, and learn how to patch Speakup into your own kernel source, and +build and install your own kernel. + +If your kernel has been compiled with Speakup, and has no default +synthesizer set, or you would like to use a different synthesizer than +the default one, then you may issue the following command at the boot +prompt of your boot loader. + +linux speakup.synth=ltlk + +This command would tell Speakup to look for and use a LiteTalk or +DoubleTalk LT at boot up. You may replace the ltlk synthesizer keyword +with the keyword for whatever synthesizer you wish to use. The +speakup.synth parameter will accept the following keywords, provided +that support for the related synthesizers has been built into the +kernel. + +acntsa -- Accent SA +acntpc -- Accent PC +apollo -- Apollo +audptr -- Audapter +bns -- Braille 'n Speak +dectlk -- DecTalk Express (old and new, db9 serial only) +decext -- DecTalk (old) External +dtlk -- DoubleTalk PC +keypc -- Keynote Gold PC +ltlk -- DoubleTalk LT, LiteTalk, or external Tripletalk (db9 serial only) +spkout -- Speak Out +txprt -- Transport +dummy -- Plain text terminal + +Note: Speakup does * NOT * support usb connections! Speakup also does * +NOT * support the internal Tripletalk! + +Speakup does support two other synthesizers, but because they work in +conjunction with other software, they must be loaded as modules after +their related software is loaded, and so are not available at boot up. +These are as follows: + +decpc -- DecTalk PC (not available at boot up) +soft -- One of several software synthesizers (not available at boot up) + +See the sections on loading modules and software synthesizers later in +this manual for further details. It should be noted here that the +speakup.synth boot parameter will have no effect if Speakup has been +compiled as modules. In order for Speakup modules to be loaded during +the boot process, such action must be configured by your system +administrator. This will mean that you will hear some, but not all, of +the bootup messages. + +2. Basic operation + +Once you have booted the system, and if necessary, have supplied the +proper bootup parameter for your synthesizer, Speakup will begin +talking as soon as the kernel is loaded. In fact, it will talk a lot! +It will speak all the boot up messages that the kernel prints on the +screen during the boot process. This is because Speakup is not a +separate screen reader, but is actually built into the operating +system. Since almost all console applications must print text on the +screen using the kernel, and must get their keyboard input through the +kernel, they are automatically handled properly by Speakup. There are a +few exceptions, but we'll come to those later. + +Note: In this guide I will refer to the numeric keypad as the keypad. +This is done because the speakupmap.map file referred to later in this +manual uses the term keypad instead of numeric keypad. Also I'm lazy +and would rather only type one word. So keypad it is. Got it? Good. + +Most of the Speakup review keys are located on the keypad at the far +right of the keyboard. The numlock key should be off, in order for these +to work. If you toggle the numlock on, the keypad will produce numbers, +which is exactly what you want for spreadsheets and such. For the +purposes of this guide, you should have the numlock turned off, which is +its default state at bootup. + +You probably won't want to listen to all the bootup messages every time +you start your system, though it's a good idea to listen to them at +least once, just so you'll know what kind of information is available to +you during the boot process. You can always review these messages after +bootup with the command: + +dmesg | more + +In order to speed the boot process, and to silence the speaking of the +bootup messages, just press the keypad enter key. This key is located +in the bottom right corner of the keypad. Speakup will shut up and stay +that way, until you press another key. + +You can check to see if the boot process has completed by pressing the 8 +key on the keypad, which reads the current line. This also has the +effect of starting Speakup talking again, so you can press keypad enter +to silence it again if the boot process has not completed. + +When the boot process is complete, you will arrive at a "login" prompt. +At this point, you'll need to type in your user id and password, as +provided by your system administrator. You will hear Speakup speak the +letters of your user id as you type it, but not the password. This is +because the password is not displayed on the screen for security +reasons. This has nothing to do with Speakup, it's a Linux security +feature. + +Once you've logged in, you can run any Linux command or program which is +allowed by your user id. Normal users will not be able to run programs +which require root privileges. + +When you are running a program or command, Speakup will automatically +speak new text as it arrives on the screen. You can at any time silence +the speech with keypad enter, or use any of the Speakup review keys. + +Here are some basic Speakup review keys, and a short description of what +they do. + +keypad 1 -- read previous character +keypad 2 -- read current character (pressing keypad 2 twice rapidly will speak + the current character phonetically) +keypad 3 -- read next character +keypad 4 -- read previous word +keypad 5 -- read current word (press twice rapidly to spell the current word) +keypad 6 -- read next word +keypad 7 -- read previous line +keypad 8 -- read current line (press twice rapidly to hear how much the + text on the current line is indented) +keypad 9 -- read next line +keypad period -- speak current cursor position and announce current + virtual console + +It's also worth noting that the insert key on the keypad is mapped +as the speakup key. Instead of pressing and releasing this key, as you +do under DOS or Windows, you hold it like a shift key, and press other +keys in combination with it. For example, repeatedly holding keypad +insert, from now on called speakup, and keypad enter will toggle the +speaking of new text on the screen on and off. This is not the same as +just pressing keypad enter by itself, which just silences the speech +until you hit another key. When you hit speakup plus keypad enter, +Speakup will say, "You turned me off.", or "Hey, that's better." When +Speakup is turned off, no new text on the screen will be spoken. You +can still use the reading controls to review the screen however. + +3. Using the Speakup Help System + +In order to enter the Speakup help system, press and hold the speakup +key (remember that this is the keypad insert key), and press the f1 key. +You will hear the message: + +"Press space to leave help, cursor up or down to scroll, or a letter to +go to commands in list." + +When you press the spacebar to leave the help system, you will hear: + +"Leaving help." + +While you are in the Speakup help system, you can scroll up or down +through the list of available commands using the cursor keys. The list +of commands is arranged in alphabetical order. If you wish to jump to +commands in a specific part of the alphabet, you may press the letter of +the alphabet you wish to jump to. + +You can also just explore by typing keyboard keys. Pressing keys will +cause Speakup to speak the command associated with that key. For +example, if you press the keypad 8 key, you will hear: + +"Keypad 8 is line, say current." + +You'll notice that some commands do not have keys assigned to them. +This is because they are very infrequently used commands, and are also +accessible through the sys system. We'll discuss the sys system later +in this manual. + +You'll also notice that some commands have two keys assigned to them. +This is because Speakup has a built in set of alternative key bindings +for laptop users. The alternate speakup key is the caps lock key. You +can press and hold the caps lock key, while pressing an alternate +speakup command key to activate the command. On most laptops, the +numeric keypad is defined as the keys in the j k l area of the keyboard. + +There is usually a function key which turns this keypad function on and +off, and some other key which controls the numlock state. Toggling the +keypad functionality on and off can become a royal pain. So, Speakup +gives you a simple way to get at an alternative set of key mappings for +your laptop. These are also available by default on desktop systems, +because Speakup does not know whether it is running on a desktop or +laptop. So you may choose which set of Speakup keys to use. Some +system administrators may have chosen to compile Speakup for a desktop +system without this set of alternate key bindings, but these details are +beyond the scope of this manual. To use the caps lock for its normal +purpose, hold the shift key while toggling the caps lock on and off. We +should note here, that holding the caps lock key and pressing the z key +will toggle the alternate j k l keypad on and off. + +4. Keys and Their Assigned Commands + +In this section, we'll go through a list of all the speakup keys and +commands. You can also get a list of commands and assigned keys from +the help system. + +The following list was taken from the speakupmap.map file. Key +assignments are on the left of the equal sign, and the associated +Speakup commands are on the right. The designation "spk" means to press +and hold the speakup key, a.k.a. keypad insert, a.k.a. caps lock, while +pressing the other specified key. + +spk key_f9 = punc_level_dec +spk key_f10 = punc_level_inc +spk key_f11 = reading_punc_dec +spk key_f12 = reading_punc_inc +spk key_1 = vol_dec +spk key_2 = vol_inc +spk key_3 = pitch_dec +spk key_4 = pitch_inc +spk key_5 = rate_dec +spk key_6 = rate_inc +key_kpasterisk = toggle_cursoring +spk key_kpasterisk = speakup_goto +spk key_f1 = speakup_help +spk key_f2 = set_win +spk key_f3 = clear_win +spk key_f4 = enable_win +spk key_f5 = edit_some +spk key_f6 = edit_most +spk key_f7 = edit_delim +spk key_f8 = edit_repeat +shift spk key_f9 = edit_exnum + key_kp7 = say_prev_line +spk key_kp7 = left_edge + key_kp8 = say_line +double key_kp8 = say_line_indent +spk key_kp8 = say_from_top + key_kp9 = say_next_line +spk key_kp9 = top_edge + key_kpminus = speakup_parked +spk key_kpminus = say_char_num + key_kp4 = say_prev_word +spk key_kp4 = say_from_left + key_kp5 = say_word +double key_kp5 = spell_word +spk key_kp5 = spell_phonetic + key_kp6 = say_next_word +spk key_kp6 = say_to_right + key_kpplus = say_screen +spk key_kpplus = say_win + key_kp1 = say_prev_char +spk key_kp1 = right_edge + key_kp2 = say_char +spk key_kp2 = say_to_bottom +double key_kp2 = say_phonetic_char + key_kp3 = say_next_char +spk key_kp3 = bottom_edge + key_kp0 = spk_key + key_kpdot = say_position +spk key_kpdot = say_attributes +key_kpenter = speakup_quiet +spk key_kpenter = speakup_off +key_sysrq = speech_kill + key_kpslash = speakup_cut +spk key_kpslash = speakup_paste +spk key_pageup = say_first_char +spk key_pagedown = say_last_char +key_capslock = spk_key + spk key_z = spk_lock +key_leftmeta = spk_key +ctrl spk key_0 = speakup_goto +spk key_u = say_prev_line +spk key_i = say_line +double spk key_i = say_line_indent +spk key_o = say_next_line +spk key_minus = speakup_parked +shift spk key_minus = say_char_num +spk key_j = say_prev_word +spk key_k = say_word +double spk key_k = spell_word +spk key_l = say_next_word +spk key_m = say_prev_char +spk key_comma = say_char +double spk key_comma = say_phonetic_char +spk key_dot = say_next_char +spk key_n = say_position + ctrl spk key_m = left_edge + ctrl spk key_y = top_edge + ctrl spk key_dot = right_edge +ctrl spk key_p = bottom_edge +spk key_apostrophe = say_screen +spk key_h = say_from_left +spk key_y = say_from_top +spk key_semicolon = say_to_right +spk key_p = say_to_bottom +spk key_slash = say_attributes + spk key_enter = speakup_quiet + ctrl spk key_enter = speakup_off + spk key_9 = speakup_cut +spk key_8 = speakup_paste +shift spk key_m = say_first_char + ctrl spk key_semicolon = say_last_char + +5. The Speakup Sys System + +The Speakup screen reader also creates a speakup subdirectory as a part +of the sys system. + +As a convenience, run as root + +ln -s /sys/accessibility/speakup /speakup + +to directly access speakup parameters from /speakup. +You can see these entries by typing the command: + +ls -1 /speakup/* + +If you issue the above ls command, you will get back something like +this: + +/speakup/attrib_bleep +/speakup/bell_pos +/speakup/bleep_time +/speakup/bleeps +/speakup/cursor_time +/speakup/delimiters +/speakup/ex_num +/speakup/key_echo +/speakup/keymap +/speakup/no_interrupt +/speakup/punc_all +/speakup/punc_level +/speakup/punc_most +/speakup/punc_some +/speakup/reading_punc +/speakup/repeats +/speakup/say_control +/speakup/say_word_ctl +/speakup/silent +/speakup/spell_delay +/speakup/synth +/speakup/synth_direct +/speakup/version + +/speakup/i18n: +announcements +characters +chartab +colors +ctl_keys +formatted +function_names +key_names +states + +/speakup/soft: +caps_start +caps_stop +delay_time +direct +freq +full_time +jiffy_delta +pitch +inflection +punct +rate +tone +trigger_time +voice +vol + +Notice the two subdirectories of /speakup: /speakup/i18n and +/speakup/soft. +The i18n subdirectory is described in a later section. +The files under /speakup/soft represent settings that are specific to the +driver for the software synthesizer. If you use the LiteTalk, your +synthesizer-specific settings would be found in /speakup/ltlk. In other words, +a subdirectory named /speakup/KWD is created to hold parameters specific +to the device whose keyword is KWD. +These parameters include volume, rate, pitch, and others. + +In addition to using the Speakup hot keys to change such things as +volume, pitch, and rate, you can also echo values to the appropriate +entry in the /speakup directory. This is very useful, since it +lets you control Speakup parameters from within a script. How you +would write such scripts is somewhat beyond the scope of this manual, +but I will include a couple of simple examples here to give you a +general idea of what such scripts can do. + +Suppose for example, that you wanted to control both the punctuation +level and the reading punctuation level at the same time. For +simplicity, we'll call them punc0, punc1, punc2, and punc3. The scripts +might look something like this: + +#!/bin/bash +# punc0 +# set punc and reading punc levels to 0 +echo 0 >/speakup/punc_level +echo 0 >/speakup/reading_punc +echo Punctuation level set to 0. + +#!/bin/bash +# punc1 +# set punc and reading punc levels to 1 +echo 1 >/speakup/punc_level +echo 1 >/speakup/reading_punc +echo Punctuation level set to 1. + +#!/bin/bash +# punc2 +# set punc and reading punc levels to 2 +echo 2 >/speakup/punc_level +echo 2 >/speakup/reading_punc +echo Punctuation level set to 2. + +#!/bin/bash +# punc3 +# set punc and reading punc levels to 3 +echo 3 >/speakup/punc_level +echo 3 >/speakup/reading_punc +echo Punctuation level set to 3. + +If you were to store these four small scripts in a directory in your +path, perhaps /usr/local/bin, and set the permissions to 755 with the +chmod command, then you could change the default reading punc and +punctuation levels at the same time by issuing just one command. For +example, if you were to execute the punc3 command at your shell prompt, +then the reading punc and punc level would both get set to 3. + +I should note that the above scripts were written to work with bash, but +regardless of which shell you use, you should be able to do something +similar. + +The Speakup sys system also has another interesting use. You can echo +Speakup parameters into the sys system in a script during system +startup, and speakup will return to your preferred parameters every time +the system is rebooted. + +Most of the Speakup sys parameters can be manipulated by a regular user +on the system. However, there are a few parameters that are dangerous +enough that they should only be manipulated by the root user on your +system. There are even some parameters that are read only, and cannot +be written to at all. For example, the version entry in the Speakup +sys system is read only. This is because there is no reason for a user +to tamper with the version number which is reported by Speakup. Doing +an ls -l on /speakup/version will return this: + +-r--r--r-- 1 root root 0 Mar 21 13:46 /speakup/version + +As you can see, the version entry in the Speakup sys system is read +only, is owned by root, and belongs to the root group. Doing a cat of +/speakup/version will display the Speakup version number, like +this: + +cat /speakup/version +Speakup v-2.00 CVS: Thu Oct 21 10:38:21 EDT 2004 +synth dtlk version 1.1 + +The display shows the Speakup version number, along with the version +number of the driver for the current synthesizer. + +Looking at entries in the Speakup sys system can be useful in many +ways. For example, you might wish to know what level your volume is set +at. You could type: + +cat /speakup/KWD/vol +# Replace KWD with the keyword for your synthesizer, E.G., ltlk for LiteTalk. +5 + +The number five which comes back is the level at which the synthesizer +volume is set at. + +All the entries in the Speakup sys system are readable, some are +writable by root only, and some are writable by everyone. Unless you +know what you are doing, you should probably leave the ones that are +writable by root only alone. Most of the names are self explanatory. +Vol for controlling volume, pitch for pitch, inflection for pitch range, rate +for controlling speaking rate, etc. If you find one you aren't sure about, you +can post a query on the Speakup list. + +6. Changing Synthesizers + +It is possible to change to a different synthesizer while speakup is +running. In other words, it is not necessary to reboot the system +in order to use a different synthesizer. You can simply echo the +synthesizer keyword to the /speakup/synth sys entry. +Depending on your situation, you may wish to echo none to the synth +sys entry, to disable speech while one synthesizer is disconnected and +a second one is connected in its place. Then echo the keyword for the +new synthesizer into the synth sys entry in order to start speech +with the newly connected synthesizer. See the list of synthesizer +keywords in section 1 to find the keyword which matches your synth. + +7. Loading modules + +As mentioned earlier, Speakup can either be completely compiled into the +kernel, with the exception of the help module, or it can be compiled as +a series of modules. When compiled as modules, Speakup will only be +able to speak some of the bootup messages if your system administrator +has configured the system to load the modules at boo time. The modules +can be loaded after the file systems have been checked and mounted, or +from an initrd. There is a third possibility. Speakup can be compiled +with some components built into the kernel, and others as modules. As +we'll see in the next section, this is particularly useful when you are +working with software synthesizers. + +If Speakup is completely compiled as modules, then you must use the +modprobe command to load Speakup. You do this by loading the module for +the synthesizer driver you wish to use. The driver modules are all +named speakup_, where is the keyword for the +synthesizer you want. So, in order to load the driver for the DecTalk +Express, you would type the following command: + +modprobe speakup_dectlk + +Issuing this command would load the DecTalk Express driver and all other +related Speakup modules necessary to get Speakup up and running. + +To completely unload Speakup, again presuming that it is entirely built +as modules, you would give the command: + +modprobe -r speakup_dectlk + +The above command assumes you were running a DecTalk Express. If you +were using a different synth, then you would substitute its keyword in +place of dectlk. + +If you have multiple drivers loaded, you need to unload all of them, in +order to completely unload Speakup. +For example, if you have loaded both the dectlk and ltlk drivers, use the +command: +modprobe -r speakup_dectlk speakup_ltlk + +You cannot unload the driver for software synthesizers when a user-space +daemon is using /dev/softsynth. First, kill the daemon. Next, remove +the driver with the command: +modprobe -r speakup_soft + +Now, suppose we have a situation where the main Speakup component +is built into the kernel, and some or all of the drivers are built as +modules. Since the main part of Speakup is compiled into the kernel, a +partial Speakup sys system has been created which we can take advantage +of by simply echoing the synthesizer keyword into the +/speakup/synth sys entry. This will cause the kernel to +automatically load the appropriate driver module, and start Speakup +talking. To switch to another synth, just echo a new keyword to the +synth sys entry. For example, to load the DoubleTalk LT driver, +you would type: + +echo ltlk >/speakup/synth + +You can use the modprobe -r command to unload driver modules, regardless +of whether the main part of Speakup has been built into the kernel or +not. + +8. Using Software Synthesizers + +Using a software synthesizer requires that some other software be +installed and running on your system. For this reason, software +synthesizers are not available for use at bootup, or during a system +installation process. +There are two freely-available solutions for software speech: Espeakup and +Speech Dispatcher. +These are described in subsections 8.1 and 8.2, respectively. + +During the rest of this section, we assume that speakup_soft is either +built in to your kernel, or loaded as a module. + +If your system does not have udev installed , before you can use a +software synthesizer, you must have created the /dev/softsynth device. +If you have not already done so, issue the following commands as root: + +cd /dev +mknod softsynth c 10 26 + +While we are at it, we might just as well create the /dev/synth device, +which can be used to let user space programs send information to your +synthesizer. To create /dev/synth, change to the /dev directory, and +issue the following command as root: + +mknod synth c 10 25 + +of both. + +8.1. Espeakup + +Espeakup is a connector between Speakup and the eSpeak software synthesizer. +Espeakup may already be available as a package for your distribution +of Linux. If it is not packaged, you need to install it manually. +You can find it in the contrib/ subdirectory of the Speakup sources. +The filename is espeakup-$VERSION.tar.bz2, where $VERSION +depends on the current release of Espeakup. The Speakup 3.1.2 source +ships with version 0.71 of Espeakup. +The README file included with the Espeakup sources describes the process +of manual installation. + +Assuming that Espeakup is installed, either by the user or by the distributor, +follow these steps to use it. + +Tell Speakup to use the "soft driver: +echo soft > /speakup/synth + +Finally, start the espeakup program. There are two ways to do it. +Both require root privileges. + +If Espeakup was installed as a package for your Linux distribution, +you probably have a distribution-specific script that controls the operation +of the daemon. Look for a file named espeakup under /etc/init.d or +/etc/rc.d. Execute the following command with root privileges: +/etc/init.d/espeakup start +Replace init.d with rc.d, if your distribution uses scripts located under +/etc/rc.d. +Your distribution will also have a procedure for starting daemons at +boot-time, so it is possible to have software speech as soon as user-space +daemons are started by the bootup scripts. +These procedures are not described in this document. + +If you built Espeakup manually, the "make install" step placed the binary +under /usr/bin. +Run the following command as root: +/usr/bin/espeakup +Espeakup should start speaking. + +8.2. Speech Dispatcher + +For this option, you must have a package called +Speech Dispatcher running on your system, and it must be configured to +work with one of its supported software synthesizers. + +Two open source synthesizers you might use are Flite and Festival. You +might also choose to purchase the Software DecTalk from Fonix Sales Inc. +If you run a google search for Fonix, you'll find their web site. + +You can obtain a copy of Speech Dispatcher from free(b)soft at +http://www.freebsoft.org/. Follow the installation instructions that +come with Speech Dispatcher in order to install and configure Speech +Dispatcher. You can check out the web site for your Linux distribution +in order to get a copy of either Flite or Festival. Your Linux +distribution may also have a precompiled Speech Dispatcher package. + +Once you've installed, configured, and tested Speech Dispatcher with your +chosen software synthesizer, you still need one more piece of software +in order to make things work. You need a package called speechd-up. +You get it from the free(b)soft web site mentioned above. After you've +compiled and installed speechd-up, you are almost ready to begin using +your software synthesizer. + +Now you can begin using your software synthesizer. In order to do so, +echo the soft keyword to the synth sys entry like this: + +echo soft >/speakup/synth + +Next run the speechd_up command like this: + +speechd_up & + +Your synth should now start talking, and you should be able to adjust +the pitch, rate, etc. + +9. Using The DecTalk PC Card + +The DecTalk PC card is an ISA card that is inserted into one of the ISA +slots in your computer. It requires that the DecTalk PC software be +installed on your computer, and that the software be loaded onto the +Dectalk PC card before it can be used. + +You can get the dec_pc.tgz file from the linux-speakup.org site. The +dec_pc.tgz file is in the ~ftp/pub/linux/speakup directory. + +After you have downloaded the dec_pc.tgz file, untar it in your home +directory, and read the Readme file in the newly created dec_pc +directory. + +The easiest way to get the software working is to copy the entire dec_pc +directory into /user/local/lib. To do this, su to root in your home +directory, and issue the command: + +cp dec_pc /usr/local/lib + +You will need to copy the dtload command from the dec_pc directory to a +directory in your path. Either /usr/bin or /usr/local/bin is a good +choice. + +You can now run the dtload command in order to load the DecTalk PC +software onto the card. After you have done this, echo the decpc +keyword to the synth entry in the sys system like this: + +echo decpc >/speakup/synth + +Your DecTalk PC should start talking, and then you can adjust the pitch, +rate, volume, voice, etc. The voice entry in the Speakup sys system +will accept a number from 0 through 7 for the DecTalk PC synthesizer, +which will give you access to some of the DecTalk voices. + +10. Using Cursor Tracking + +In Speakup version 2.0 and later, cursor tracking is turned on by +default. This means that when you are using an editor, Speakup will +automatically speak characters as you move left and right with the +cursor keys, and lines as you move up and down with the cursor keys. +This is the traditional sort of cursor tracking. +Recent versions of Speakup provide two additional ways to control the +text that is spoken when the cursor is moved: +"highlight tracking" and "read window." +They are described later in this section. +Sometimes, these modes get in your way, so you can disable cursor tracking +altogether. + +You may select among the various forms of cursor tracking using the keypad +asterisk key. +Each time you press this key, a new mode is selected, and Speakup speaks +the name of the new mode. The names for the four possible states of cursor +tracking are: "cursoring on", "highlight tracking", "read window", +and "cursoring off." The keypad asterisk key moves through the list of +modes in a circular fashion. + +If highlight tracking is enabled, Speakup tracks highlighted text, +rather than the cursor itself. When you move the cursor with the arrow keys, +Speakup speaks the currently highlighted information. +This is useful when moving through various menus and dialog boxes. +If cursor tracking isn't helping you while navigating a menu, +try highlight tracking. + +With the "read window" variety of cursor tracking, you can limit the text +that Speakup speaks by specifying a window of interest on the screen. +See section 15 for a description of the process of defining windows. +When you move the cursor via the arrow keys, Speakup only speaks +the contents of the window. This is especially helpful when you are hearing +superfluous speech. Consider the following example. + +Suppose that you are at a shell prompt. You use bash, and you want to +explore your command history using the up and down arrow keys. If you +have enabled cursor tracking, you will hear two pieces of information. +Speakup speaks both your shell prompt and the current entry from the +command history. You may not want to hear the prompt repeated +each time you move, so you can silence it by specifying a window. Find +the last line of text on the screen. Clear the current window by pressing +the key combination speakup f3. Use the review cursor to find the first +character that follows your shell prompt. Press speakup + f2 twice, to +define a one-line window. The boundaries of the window are the +character following the shell prompt and the end of the line. Now, cycle +through the cursor tracking modes using keypad asterisk, until Speakup +says "read window." Move through your history using your arrow keys. +You will notice that Speakup no longer speaks the redundant prompt. + +Some folks like to turn cursor tracking off while they are using the +lynx web browser. You definitely want to turn cursor tracking off when +you are using the alsamixer application. Otherwise, you won't be able +to hear your mixer settings while you are using the arrow keys. + +11. Cut and Paste + +One of Speakup's more useful features is the ability to cut and paste +text on the screen. This means that you can capture information from a +program, and paste that captured text into a different place in the +program, or into an entirely different program, which may even be +running on a different console. + +For example, in this manual, we have made references to several web +sites. It would be nice if you could cut and paste these urls into your +web browser. Speakup does this quite nicely. Suppose you wanted to +past the following url into your browser: + +http://linux-speakup.org/ + +Use the speakup review keys to position the reading cursor on the first +character of the above url. When the reading cursor is in position, +press the keypad slash key once. Speakup will say, "mark". Next, +position the reading cursor on the rightmost character of the above +url. Press the keypad slash key once again to actually cut the text +from the screen. Speakup will say, "cut". Although we call this +cutting, Speakup does not actually delete the cut text from the screen. +It makes a copy of the text in a special buffer for later pasting. + +Now that you have the url cut from the screen, you can paste it into +your browser, or even paste the url on a command line as an argument to +your browser. + +Suppose you want to start lynx and go to the Speakup site. + +You can switch to a different console with the alt left and right +arrows, or you can switch to a specific console by typing alt and a +function key. These are not Speakup commands, just standard Linux +console capabilities. + +Once you've changed to an appropriate console, and are at a shell prompt, +type the word lynx, followed by a space. Now press and hold the speakup +key, while you type the keypad slash character. The url will be pasted +onto the command line, just as though you had typed it in. Press the +enter key to execute the command. + +The paste buffer will continue to hold the cut information, until a new +mark and cut operation is carried out. This means you can paste the cut +information as many times as you like before doing another cut +operation. + +You are not limited to cutting and pasting only one line on the screen. +You can also cut and paste rectangular regions of the screen. Just +position the reading cursor at the top left corner of the text to be +cut, mark it with the keypad slash key, then position the reading cursor +at the bottom right corner of the region to be cut, and cut it with the +keypad slash key. + +12. Changing the Pronunciation of Characters + +Through the /speakup/i18n/characters sys entry, Speakup gives you the +ability to change how Speakup pronounces a given character. You could, +for example, change how some punctuation characters are spoken. You can +even change how Speakup will pronounce certain letters. + +You may, for example, wish to change how Speakup pronounces the z +character. The author of Speakup, Kirk Reiser, is Canadian, and thus +believes that the z should be pronounced zed. If you are an American, +you might wish to use the zee pronunciation instead of zed. You can +change the pronunciation of both the upper and lower case z with the +following two commands: + +echo 90 zee >/speakup/characters +echo 122 zee >/speakup/characters + +Let's examine the parts of the two previous commands. They are issued +at the shell prompt, and could be placed in a startup script. + +The word echo tells the shell that you want to have it display the +string of characters that follow the word echo. If you were to just +type: + +echo hello. + +You would get the word hello printed on your screen as soon as you +pressed the enter key. In this case, we are echoing strings that we +want to be redirected into the sys system. + +The numbers 90 and 122 in the above echo commands are the ascii numeric +values for the upper and lower case z, the characters we wish to change. + +The string zee is the pronunciation that we want Speakup to use for the +upper and lower case z. + +The > symbol redirects the output of the echo command to a file, just +like in DOS, or at the Windows command prompt. + +And finally, /speakup/i18n/characters is the file entry in the sys system +where we want the output to be directed. Speakup looks at the numeric +value of the character we want to change, and inserts the pronunciation +string into an internal table. + +You can look at the whole table with the following command: + +cat /speakup/i18n/characters + +Speakup will then print out the entire character pronunciation table. I +won't display it here, but leave you to look at it at your convenience. + +13. Mapping Keys + +Speakup has the capability of allowing you to assign or "map" keys to +internal Speakup commands. This section necessarily assumes you have a +Linux kernel source tree installed, and that it has been patched and +configured with Speakup. How you do this is beyond the scope of this +manual. For this information, visit the Speakup web site at +http://linux-speakup.org/. The reason you'll need the kernel source +tree patched with Speakup is that the genmap utility you'll need for +processing keymaps is in the +/usr/src/linux-/drivers/char/speakup directory. The + in the above directory path is the version number of +the Linux source tree you are working with. + +So ok, you've gone off and gotten your kernel source tree, and patched +and configured it. Now you can start manipulating keymaps. + +You can either use the +/usr/src/linux-/drivers/char/speakup/speakupmap.map file +included with the Speakup source, or you can cut and paste the copy in +section 4 into a separate file. If you use the one in the Speakup +source tree, make sure you make a backup of it before you start making +changes. You have been warned! + +Suppose that you want to swap the key assignments for the Speakup +say_last_char and the Speakup say_first_char commands. The +speakupmap.map lists the key mappings for these two commands as follows: + +spk key_pageup = say_first_char +spk key_pagedown = say_last_char + +You can edit your copy of the speakupmap.map file and swap the command +names on the right side of the = (equals) sign. You did make a backup, +right? The new keymap lines would look like this: + +spk key_pageup = say_last_char +spk key_pagedown = say_first_char + +After you edit your copy of the speakupmap.map file, save it under a new +file name, perhaps newmap.map. Then exit your editor and return to the +shell prompt. + +You are now ready to load your keymap with your swapped key assignments. + Assuming that you saved your new keymap as the file newmap.map, you +would load your keymap into the sys system like this: + +/usr/src/linux-/drivers/char/speakup/genmap newmap.map +>/speakup/keymap + +Remember to substitute your kernel version number for the + in the above command. Also note that although the +above command wrapped onto two lines in this document, you should type +it all on one line. + +Your say first and say last characters should now be swapped. Pressing +speakup pagedown should read you the first non-whitespace character on +the line your reading cursor is in, and pressing speakup pageup should +read you the last character on the line your reading cursor is in. + +You should note that these new mappings will only stay in effect until +you reboot, or until you load another keymap. + +One final warning. If you try to load a partial map, you will quickly +find that all the mappings you didn't include in your file got deleted +from the working map. Be extremely careful, and always make a backup! +You have been warned! + +14. Internationalizing Speakup + +Speakup indicates various conditions to the user by speaking messages. +For instance, when you move to the left edge of the screen with the +review keys, Speakup says, "left." +Prior to version 3.1.0 of Speakup, all of these messages were in English, +and they could not be changed. If you used a non-English synthesizer, +you still heard English messages, such as "left" and "cursoring on." +In version 3.1.0 or higher, one may load translations for the various +messages via the /sys filesystem. + +The directory /speakup/i18n contains several collections of messages. +Each group of messages is stored in its own file. +The following section lists all of these files, along with a brief description +of each. + +14.1. Files Under the i18n Subdirectory + +* announcements: +This file contains various general announcements, most of which cannot +be categorized. You will find messages such as "You killed Speakup", +"I'm alive", "leaving help", "parked", "unparked", and others. +You will also find the names of the screen edges and cursor tracking modes +here. + +* characters: +See section 12 for a description of this file. + +* chartab: +See section 12. Unlike the rest of the files in the i18n subdirectory, +this one does not contain messages to be spoken. + +* colors: +When you use the "say attributes" function, Speakup says the name of the +foreground and background colors. These names come from the i18n/colors +file. + +* ctl_keys: +Here, you will find names of control keys. These are used with Speakup's +say_control feature. + +* formatted: +This group of messages contains embedded formatting codes, to specify +the type and width of displayed data. If you change these, you must +preserve all of the formatting codes, and they must appear in the order +used by the default messages. + +* function_names: +Here, you will find a list of names for Speakup functions. These are used +by the help system. For example, suppose that you have activated help mode, +and you pressed keypad 3. Speakup says: +"keypad 3 is character, say next." +The message "character, say next" names a Speakup function, and it +comes from this function_names file. + +* key_names: +Again, key_names is used by Speakup's help system. In the previous +example, Speakup said that you pressed "keypad 3." +This name came from the key_names file. + +* states: +This file contains names for key states. +Again, these are part of the help system. For instance, if you had pressed +speakup + keypad 3, you would hear: +"speakup keypad 3 is go to bottom edge." +The speakup key is depressed, so the name of the key state is speakup. +This part of the message comes from the states collection. + +14.2. Loading Your Own Messages + +The files under the i18n subdirectory all follow the same format. +They consist of lines, with one message per line. +Each message is represented by a number, followed by the text of the message. +The number is the position of the message in the given collection. +For example, if you view the file /speakup/i18n/colors, you will see the +following list: + +0 black +1 blue +2 green +3 cyan +4 red +5 magenta +6 yellow +7 white +8 grey + +You can change one message, or you can change a whole group. +To load a whole collection of messages from a new source, simply use +the cp command: +cp ~/my_colors /speakup/i18n/colors +You can change an individual message with the echo command, +as shown in the following example. + +The Spanish name for the color blue is azul. +Looking at the colors file, we see that the name "blue" is at position 1 +within the colors group. Let's change blue to azul: +echo '1 azul' > /speakup/i18n/colors +The next time that Speakup says message 1 from the colors group, it will +say "azul", rather than "blue." + +In the future, translations into various languages will be made available, +and most users will just load the files necessary for their language. + +14.3. No Support for Non-Western-European Languages + +As of the current release, Speakup only supports Western European languages. +Support for the extended characters used by languages outside of the Western +European family of languages is a work in progress. + +15. Using Speakup's Windowing Capability + +Speakup has the capability of defining and manipulating windows on the +screen. Speakup uses the term "Window", to mean a user defined area of +the screen. The key strokes for defining and manipulating Speakup +windows are as follows: + +speakup + f2 -- Set the bounds of the window. +Speakup + f3 -- clear the current window definition. +speakup + f4 -- Toggle window silence on and off. +speakup + keypad plus -- Say the currently defined window. + +These capabilities are useful for tracking a certain part of the screen +without rereading the whole screen, or for silencing a part of the +screen that is constantly changing, such as a clock or status line. + +There is no way to save these window settings, and you can only have one +window defined for each virtual console. There is also no way to have +windows automatically defined for specific applications. + +In order to define a window, use the review keys to move your reading +cursor to the beginning of the area you want to define. Then press +speakup + f2. Speakup will tell you that the window starts at the +indicated row and column position. Then move the reading cursor to the +end of the area to be defined as a window, and press speakup + f2 again. + If there is more than one line in the window, Speakup will tell you +that the window ends at the indicated row and column position. If there +is only one line in the window, then Speakup will tell you that the +window is the specified line on the screen. If you are only defining a +one line window, you can just press speakup + f2 twice after placing the +reading cursor on the line you want to define as a window. It is not +necessary to position the reading cursor at the end of the line in order +to define the whole line as a window. + +16. Tools for Controlling Speakup + +The speakup distribution includes extra tools (in the tools directory) +which were written to make speakup easier to use. This section will +briefly describe the use of these tools. + +16.1. Speakupconf + +speakupconf began life as a contribution from Steve Holmes, a member of +the speakup community. We would like to thank him for his work on the +early versions of this project. + +This script may be installed as part of your linux distribution, but if +it isn't, the recommended places to put it are /usr/local/bin or +/usr/bin. This script can be run by any user, so it does not require +root privileges. + +Speakupconf allows you to save and load your Speakup settings. It works +by reading and writing the /sys files described above. + +The directory that speakupconf uses to store your settings depends on +whether it is run from the root account. If you execute speakupconf as +root, it uses the directory /etc/speakup. Otherwise, it uses the directory +~/.speakup, where ~ is your home directory. +Anyone who needs to use Speakup from your console can load his own custom +settings with this script. + +speakupconf takes one required argument: load or save. +Use the command +speakupconf save +to save your Speakup settings, and +speakupconf load +to load them into Speakup. +A second argument may be specified to use an alternate directory to +load or save the speakup parameters. + +16.2. Talkwith + +Charles Hallenbeck, another member of the speakup community, wrote the +initial versions of this script, and we would also like to thank him for +his work on it. + +This script needs root privileges to run, so if it is not installed as +part of your linux distribution, the recommended places to install it +are /usr/local/sbin or /usr/sbin. + +Talkwith allows you to switch synthesizers on the fly. It takes a synthesizer +name as an argument. For instance, +talkwith dectlk +causes Speakup to use the DecTalk Express. If you wish to switch to a +software synthesizer, you must also indicate which daemon you wish to +use. There are two possible choices: +spd and espeakup. spd is an abbreviation for speechd-up. +If you wish to use espeakup for software synthesis, give the command +talkwith soft espeakup +To use speechd-up, type: +talkwith soft spd +Any arguments that follow the name of the daemon are passed to the daemon +when it is invoked. For instance: +talkwith espeakup --default-voice=fr +causes espeakup to use the French voice. +Note that talkwith must always be executed with root privileges. + +Talkwith does not attempt to load your settings after the new +synthesizer is activated. You can use speakupconf to load your settings +if desired. + + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +https://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +The End. diff --git a/MAINTAINERS b/MAINTAINERS index 8d5294bfade6..49a29c557592 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16100,6 +16100,16 @@ Q: https://patchwork.kernel.org/project/linux-sparse/list/ B: https://bugzilla.kernel.org/enter_bug.cgi?component=Sparse&product=Tools F: include/linux/compiler.h +SPEAKUP CONSOLE SPEECH DRIVER +M: William Hubbs +M: Chris Brannon +M: Kirk Reiser +M: Samuel Thibault +L: speakup@linux-speakup.org +S: Odd Fixes +W: http://www.linux-speakup.org/ +F: drivers/accessibility/speakup/ + SPEAR CLOCK FRAMEWORK SUPPORT M: Viresh Kumar L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -16287,16 +16297,6 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/staging/sm750fb/ -STAGING - SPEAKUP CONSOLE SPEECH DRIVER -M: William Hubbs -M: Chris Brannon -M: Kirk Reiser -M: Samuel Thibault -L: speakup@linux-speakup.org -S: Odd Fixes -W: http://www.linux-speakup.org/ -F: drivers/staging/speakup/ - STAGING - VIA VT665X DRIVERS M: Forest Bond S: Odd Fixes diff --git a/drivers/accessibility/Kconfig b/drivers/accessibility/Kconfig index f10c17dc1dee..6b2f79d1f1b8 100644 --- a/drivers/accessibility/Kconfig +++ b/drivers/accessibility/Kconfig @@ -31,4 +31,6 @@ config A11Y_BRAILLE_CONSOLE If unsure, say N. +source "drivers/accessibility/speakup/Kconfig" + endif # ACCESSIBILITY diff --git a/drivers/accessibility/Makefile b/drivers/accessibility/Makefile index e8c182f82c44..833603086530 100644 --- a/drivers/accessibility/Makefile +++ b/drivers/accessibility/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += braille/ +obj-$(CONFIG_SPEAKUP) += speakup/ diff --git a/drivers/accessibility/speakup/DefaultKeyAssignments b/drivers/accessibility/speakup/DefaultKeyAssignments new file mode 100644 index 000000000000..101c803b21fd --- /dev/null +++ b/drivers/accessibility/speakup/DefaultKeyAssignments @@ -0,0 +1,46 @@ +This file is intended to give you an overview of the default keys used +by speakup for it's review functions. You may change them to be +anything you want but that will take some familiarity with key +mapping. + +We have remapped the insert or zero key on the keypad to act as a +shift key. Well, actually as an altgr key. So in the following list +InsKeyPad-period means hold down the insert key like a shift key and +hit the keypad period. + +KeyPad-8 Say current Line +InsKeyPad-8 say from top of screen to reading cursor. +KeyPad-7 Say Previous Line (UP one line) +KeyPad-9 Say Next Line (down one line) +KeyPad-5 Say Current Word +InsKeyPad-5 Spell Current Word +KeyPad-4 Say Previous Word (left one word) +InsKeyPad-4 say from left edge of line to reading cursor. +KeyPad-6 Say Next Word (right one word) +InsKeyPad-6 Say from reading cursor to right edge of line. +KeyPad-2 Say Current Letter +InsKeyPad-2 say current letter phonetically +KeyPad-1 Say Previous Character (left one letter) +KeyPad-3 Say Next Character (right one letter) +KeyPad-plus Say Entire Screen +InsKeyPad-plus Say from reading cursor line to bottom of screen. +KeyPad-Minus Park reading cursor (toggle) +InsKeyPad-minus Say character hex and decimal value. +KeyPad-period Say Position (current line, position and console) +InsKeyPad-period say colour attributes of current position. +InsKeyPad-9 Move reading cursor to top of screen (insert pgup) +InsKeyPad-3 Move reading cursor to bottom of screen (insert pgdn) +InsKeyPad-7 Move reading cursor to left edge of screen (insert home) +InsKeyPad-1 Move reading cursor to right edge of screen (insert end) +ControlKeyPad-1 Move reading cursor to last character on current line. +KeyPad-Enter Shut Up (until another key is hit) and sync reading cursor +InsKeyPad-Enter Shut Up (until toggled back on). +InsKeyPad-star n go to line (y) or column (x). Where 'n' is any + allowed value for the row or column for your current screen. +KeyPad-/ Mark and Cut screen region. +InsKeyPad-/ Paste screen region into any console. + +Hitting any key while speakup is outputting speech will quiet the +synth until it has caught up with what is being printed on the +console. + diff --git a/drivers/accessibility/speakup/Kconfig b/drivers/accessibility/speakup/Kconfig new file mode 100644 index 000000000000..0803c2013cf4 --- /dev/null +++ b/drivers/accessibility/speakup/Kconfig @@ -0,0 +1,200 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "Speakup console speech" + +config SPEAKUP + depends on VT + tristate "Speakup core" + help + This is the Speakup screen reader. Think of it as a + video console for blind people. If built in to the + kernel, it can speak everything on the text console from + boot up to shutdown. For more information on Speakup, + point your browser at . + There is also a mailing list at the above url that you + can subscribe to. + + Supported synthesizers are accent sa, accent pc, + appollo II., Auddapter, Braille 'n Speak, Dectalk + external (old), Dectalk PC (full length isa board), + Dectalk express, Doubletalk, Doubletalk LT or + Litetalk, Keynote Gold internal PC, software + synthesizers, Speakout, transport, and a dummy module + that can be used with a plain text terminal. + + Speakup can either be built in or compiled as a module + by answering y or m. If you answer y here, then you + must answer either y or m to at least one of the + synthesizer drivers below. If you answer m here, then + the synthesizer drivers below can only be built as + modules. + + These drivers are not standalone drivers, but must be + used in conjunction with Speakup. Think of them as + video cards for blind people. + + + The Dectalk pc driver can only be built as a module, and + requires software to be pre-loaded on to the card before + the module can be loaded. See the decpc choice below + for more details. + + If you are not a blind person, or don't have access to + one of the listed synthesizers, you should say n. + +if SPEAKUP +config SPEAKUP_SYNTH_ACNTSA + tristate "Accent SA synthesizer support" + help + This is the Speakup driver for the Accent SA + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_SYNTH_ACNTPC + tristate "Accent PC synthesizer support" + depends on ISA || COMPILE_TEST + help + This is the Speakup driver for the accent pc + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_SYNTH_APOLLO + tristate "Apollo II synthesizer support" + help + This is the Speakup driver for the Apollo II + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_SYNTH_AUDPTR + tristate "Audapter synthesizer support" + help + This is the Speakup driver for the Audapter synthesizer. + You can say y to build it into the kernel, or m to + build it as a module. See the configuration help on the + Speakup choice above for more info. + +config SPEAKUP_SYNTH_BNS + tristate "Braille 'n' Speak synthesizer support" + help + This is the Speakup driver for the Braille 'n' Speak + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_SYNTH_DECTLK + tristate "DECtalk Express synthesizer support" + help + + This is the Speakup driver for the DecTalk Express + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_SYNTH_DECEXT + tristate "DECtalk External (old) synthesizer support" + help + + This is the Speakup driver for the DecTalk External + (old) synthesizer. You can say y to build it into the + kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more + info. + +config SPEAKUP_SYNTH_DECPC + depends on m + depends on ISA || COMPILE_TEST + tristate "DECtalk PC (big ISA card) synthesizer support" + help + + This is the Speakup driver for the DecTalk PC (full + length ISA) synthesizer. You can say m to build it as + a module. See the configuration help on the Speakup + choice above for more info. + + In order to use the DecTalk PC driver, you must download + the dec_pc.tgz file from linux-speakup.org. It is in + the pub/linux/goodies directory. The dec_pc.tgz file + contains the software which must be pre-loaded on to the + DecTalk PC board in order to use it with this driver. + This driver must be built as a module, and can not be + loaded until the file system is mounted and the DecTalk + PC software has been pre-loaded on to the board. + + See the README file in the dec_pc.tgz file for more + details. + +config SPEAKUP_SYNTH_DTLK + tristate "DoubleTalk PC synthesizer support" + depends on ISA || COMPILE_TEST + help + + This is the Speakup driver for the internal DoubleTalk + PC synthesizer. You can say y to build it into the + kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more + info. + +config SPEAKUP_SYNTH_KEYPC + tristate "Keynote Gold PC synthesizer support" + depends on ISA || COMPILE_TEST + help + + This is the Speakup driver for the Keynote Gold + PC synthesizer. You can say y to build it into the + kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more + info. + +config SPEAKUP_SYNTH_LTLK + tristate "DoubleTalk LT/LiteTalk synthesizer support" +help + + This is the Speakup driver for the LiteTalk/DoubleTalk + LT synthesizer. You can say y to build it into the + kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more + info. + +config SPEAKUP_SYNTH_SOFT + tristate "Userspace software synthesizer support" + help + + This is the software synthesizer device node. It will + register a device /dev/softsynth which midware programs + and speech daemons may open and read to provide kernel + output to software synths such as espeak, festival, + flite and so forth. You can select 'y' or 'm' to have + it built-in to the kernel or loaded as a module. + +config SPEAKUP_SYNTH_SPKOUT + tristate "Speak Out synthesizer support" + help + + This is the Speakup driver for the Speakout synthesizer. + You can say y to build it into the kernel, or m to + build it as a module. See the configuration help on the + Speakup choice above for more info. + +config SPEAKUP_SYNTH_TXPRT + tristate "Transport synthesizer support" + help + + This is the Speakup driver for the Transport + synthesizer. You can say y to build it into the kernel, + or m to build it as a module. See the configuration + help on the Speakup choice above for more info. + +config SPEAKUP_SYNTH_DUMMY + tristate "Dummy synthesizer driver (for testing)" + help + + This is a dummy Speakup driver for plugging a mere serial + terminal. This is handy if you want to test speakup but + don't have the hardware. You can say y to build it into + the kernel, or m to build it as a module. See the + configuration help on the Speakup choice above for more info. + +endif # SPEAKUP +endmenu diff --git a/drivers/accessibility/speakup/Makefile b/drivers/accessibility/speakup/Makefile new file mode 100644 index 000000000000..5befb4933b85 --- /dev/null +++ b/drivers/accessibility/speakup/Makefile @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SPEAKUP_SYNTH_ACNTSA) += speakup_acntsa.o +obj-$(CONFIG_SPEAKUP_SYNTH_ACNTPC) += speakup_acntpc.o +obj-$(CONFIG_SPEAKUP_SYNTH_APOLLO) += speakup_apollo.o +obj-$(CONFIG_SPEAKUP_SYNTH_AUDPTR) += speakup_audptr.o +obj-$(CONFIG_SPEAKUP_SYNTH_BNS) += speakup_bns.o +obj-$(CONFIG_SPEAKUP_SYNTH_DECTLK) += speakup_dectlk.o +obj-$(CONFIG_SPEAKUP_SYNTH_DECEXT) += speakup_decext.o +obj-$(CONFIG_SPEAKUP_SYNTH_DECPC) += speakup_decpc.o +obj-$(CONFIG_SPEAKUP_SYNTH_DTLK) += speakup_dtlk.o +obj-$(CONFIG_SPEAKUP_SYNTH_KEYPC) += speakup_keypc.o +obj-$(CONFIG_SPEAKUP_SYNTH_LTLK) += speakup_ltlk.o +obj-$(CONFIG_SPEAKUP_SYNTH_SOFT) += speakup_soft.o +obj-$(CONFIG_SPEAKUP_SYNTH_SPKOUT) += speakup_spkout.o +obj-$(CONFIG_SPEAKUP_SYNTH_TXPRT) += speakup_txprt.o +obj-$(CONFIG_SPEAKUP_SYNTH_DUMMY) += speakup_dummy.o + +obj-$(CONFIG_SPEAKUP) += speakup.o +speakup-y := \ + buffers.o \ + devsynth.o \ + i18n.o \ + fakekey.o \ + main.o \ + keyhelp.o \ + kobjects.o \ + selection.o \ + serialio.o \ + spk_ttyio.o \ + synth.o \ + thread.o \ + varhandlers.o diff --git a/drivers/accessibility/speakup/TODO b/drivers/accessibility/speakup/TODO new file mode 100644 index 000000000000..d4ca093bf0bd --- /dev/null +++ b/drivers/accessibility/speakup/TODO @@ -0,0 +1,22 @@ +Speakup project home: http://www.linux-speakup.org + +Mailing List: speakup@linux-speakup.org + +Speakup is a kernel based screen review package for the linux operating +system. It allows blind users to interact with applications on the +linux console by means of synthetic speech. + +Currently, speakup has one issue we know of. + +It seems to only happen on SMP systems. It seems that text in the output buffer +gets garbled because a lock is not set. This bug happens regularly, but no one +has been able to find a situation which produces it consistently. + +Patches, suggestions, corrections, etc, are definitely welcome. + +We prefer that you contact us on the mailing list; however, if you do +not want to subscribe to a mailing list, send your email to all of the +following: + +okash.khawaja@gmail.com, w.d.hubbs@gmail.com, chris@the-brannons.com, +kirk@reisers.ca and samuel.thibault@ens-lyon.org. diff --git a/drivers/accessibility/speakup/buffers.c b/drivers/accessibility/speakup/buffers.c new file mode 100644 index 000000000000..1371ced2f5ca --- /dev/null +++ b/drivers/accessibility/speakup/buffers.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#include "speakup.h" +#include "spk_priv.h" + +#define SYNTH_BUF_SIZE 8192 /* currently 8K bytes */ + +static u16 synth_buffer[SYNTH_BUF_SIZE]; /* guess what this is for! */ +static u16 *buff_in = synth_buffer; +static u16 *buff_out = synth_buffer; +static u16 *buffer_end = synth_buffer + SYNTH_BUF_SIZE - 1; + +/* These try to throttle applications by stopping the TTYs + * Note: we need to make sure that we will restart them eventually, which is + * usually not possible to do from the notifiers. TODO: it should be possible + * starting from linux 2.6.26. + * + * So we only stop when we know alive == 1 (else we discard the data anyway), + * and the alive synth will eventually call start_ttys from the thread context. + */ +void speakup_start_ttys(void) +{ + int i; + + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (speakup_console[i] && speakup_console[i]->tty_stopped) + continue; + if (vc_cons[i].d && vc_cons[i].d->port.tty) + start_tty(vc_cons[i].d->port.tty); + } +} +EXPORT_SYMBOL_GPL(speakup_start_ttys); + +static void speakup_stop_ttys(void) +{ + int i; + + for (i = 0; i < MAX_NR_CONSOLES; i++) + if (vc_cons[i].d && vc_cons[i].d->port.tty) + stop_tty(vc_cons[i].d->port.tty); +} + +static int synth_buffer_free(void) +{ + int chars_free; + + if (buff_in >= buff_out) + chars_free = SYNTH_BUF_SIZE - (buff_in - buff_out); + else + chars_free = buff_out - buff_in; + return chars_free; +} + +int synth_buffer_empty(void) +{ + return (buff_in == buff_out); +} +EXPORT_SYMBOL_GPL(synth_buffer_empty); + +void synth_buffer_add(u16 ch) +{ + if (!synth->alive) { + /* This makes sure that we won't stop TTYs if there is no synth + * to restart them + */ + return; + } + if (synth_buffer_free() <= 100) { + synth_start(); + speakup_stop_ttys(); + } + if (synth_buffer_free() <= 1) + return; + *buff_in++ = ch; + if (buff_in > buffer_end) + buff_in = synth_buffer; + /* We have written something to the speech synthesis, so we are not + * paused any more. + */ + spk_paused = false; +} + +u16 synth_buffer_getc(void) +{ + u16 ch; + + if (buff_out == buff_in) + return 0; + ch = *buff_out++; + if (buff_out > buffer_end) + buff_out = synth_buffer; + return ch; +} +EXPORT_SYMBOL_GPL(synth_buffer_getc); + +u16 synth_buffer_peek(void) +{ + if (buff_out == buff_in) + return 0; + return *buff_out; +} +EXPORT_SYMBOL_GPL(synth_buffer_peek); + +void synth_buffer_skip_nonlatin1(void) +{ + while (buff_out != buff_in) { + if (*buff_out < 0x100) + return; + buff_out++; + if (buff_out > buffer_end) + buff_out = synth_buffer; + } +} +EXPORT_SYMBOL_GPL(synth_buffer_skip_nonlatin1); + +void synth_buffer_clear(void) +{ + buff_in = synth_buffer; + buff_out = synth_buffer; +} +EXPORT_SYMBOL_GPL(synth_buffer_clear); diff --git a/drivers/accessibility/speakup/devsynth.c b/drivers/accessibility/speakup/devsynth.c new file mode 100644 index 000000000000..d30571663585 --- /dev/null +++ b/drivers/accessibility/speakup/devsynth.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include /* for misc_register, and MISC_DYNAMIC_MINOR */ +#include +#include + +#include "speakup.h" +#include "spk_priv.h" + +static int misc_registered; +static int dev_opened; + +static ssize_t speakup_file_write(struct file *fp, const char __user *buffer, + size_t nbytes, loff_t *ppos) +{ + size_t count = nbytes; + const char __user *ptr = buffer; + size_t bytes; + unsigned long flags; + u_char buf[256]; + + if (!synth) + return -ENODEV; + while (count > 0) { + bytes = min(count, sizeof(buf)); + if (copy_from_user(buf, ptr, bytes)) + return -EFAULT; + count -= bytes; + ptr += bytes; + spin_lock_irqsave(&speakup_info.spinlock, flags); + synth_write(buf, bytes); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + } + return (ssize_t)nbytes; +} + +static ssize_t speakup_file_read(struct file *fp, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + return 0; +} + +static int speakup_file_open(struct inode *ip, struct file *fp) +{ + if (!synth) + return -ENODEV; + if (xchg(&dev_opened, 1)) + return -EBUSY; + return 0; +} + +static int speakup_file_release(struct inode *ip, struct file *fp) +{ + dev_opened = 0; + return 0; +} + +static const struct file_operations synth_fops = { + .read = speakup_file_read, + .write = speakup_file_write, + .open = speakup_file_open, + .release = speakup_file_release, +}; + +static struct miscdevice synth_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "synth", + .fops = &synth_fops, +}; + +void speakup_register_devsynth(void) +{ + if (misc_registered != 0) + return; +/* zero it so if register fails, deregister will not ref invalid ptrs */ + if (misc_register(&synth_device)) { + pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); + } else { + pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", + MISC_MAJOR, synth_device.minor); + misc_registered = 1; + } +} + +void speakup_unregister_devsynth(void) +{ + if (!misc_registered) + return; + pr_info("speakup: unregistering synth device /dev/synth\n"); + misc_deregister(&synth_device); + misc_registered = 0; +} diff --git a/drivers/accessibility/speakup/fakekey.c b/drivers/accessibility/speakup/fakekey.c new file mode 100644 index 000000000000..cd029968462f --- /dev/null +++ b/drivers/accessibility/speakup/fakekey.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* fakekey.c + * Functions for simulating keypresses. + * + * Copyright (C) 2010 the Speakup Team + */ +#include +#include +#include +#include +#include + +#include "speakup.h" + +#define PRESSED 1 +#define RELEASED 0 + +static DEFINE_PER_CPU(int, reporting_keystroke); + +static struct input_dev *virt_keyboard; + +int speakup_add_virtual_keyboard(void) +{ + int err; + + virt_keyboard = input_allocate_device(); + + if (!virt_keyboard) + return -ENOMEM; + + virt_keyboard->name = "Speakup"; + virt_keyboard->id.bustype = BUS_VIRTUAL; + virt_keyboard->phys = "speakup/input0"; + virt_keyboard->dev.parent = NULL; + + __set_bit(EV_KEY, virt_keyboard->evbit); + __set_bit(KEY_DOWN, virt_keyboard->keybit); + + err = input_register_device(virt_keyboard); + if (err) { + input_free_device(virt_keyboard); + virt_keyboard = NULL; + } + + return err; +} + +void speakup_remove_virtual_keyboard(void) +{ + if (virt_keyboard) { + input_unregister_device(virt_keyboard); + virt_keyboard = NULL; + } +} + +/* + * Send a simulated down-arrow to the application. + */ +void speakup_fake_down_arrow(void) +{ + unsigned long flags; + + /* disable keyboard interrupts */ + local_irq_save(flags); + /* don't change CPU */ + preempt_disable(); + + __this_cpu_write(reporting_keystroke, true); + input_report_key(virt_keyboard, KEY_DOWN, PRESSED); + input_report_key(virt_keyboard, KEY_DOWN, RELEASED); + input_sync(virt_keyboard); + __this_cpu_write(reporting_keystroke, false); + + /* reenable preemption */ + preempt_enable(); + /* reenable keyboard interrupts */ + local_irq_restore(flags); +} + +/* + * Are we handling a simulated keypress on the current CPU? + * Returns a boolean. + */ +bool speakup_fake_key_pressed(void) +{ + return this_cpu_read(reporting_keystroke); +} diff --git a/drivers/accessibility/speakup/i18n.c b/drivers/accessibility/speakup/i18n.c new file mode 100644 index 000000000000..ee240d36f947 --- /dev/null +++ b/drivers/accessibility/speakup/i18n.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Internationalization implementation. Includes definitions of English + * string arrays, and the i18n pointer. + */ + +#include /* For kmalloc. */ +#include +#include +#include +#include "speakup.h" +#include "spk_priv.h" + +static char *speakup_msgs[MSG_LAST_INDEX]; +static char *speakup_default_msgs[MSG_LAST_INDEX] = { + [MSG_BLANK] = "blank", + [MSG_IAM_ALIVE] = "I'm aLive!", + [MSG_YOU_KILLED_SPEAKUP] = "You killed speakup!", + [MSG_HEY_THATS_BETTER] = "hey. That's better!", + [MSG_YOU_TURNED_ME_OFF] = "You turned me off!", + [MSG_PARKED] = "parked!", + [MSG_UNPARKED] = "unparked!", + [MSG_MARK] = "mark", + [MSG_CUT] = "cut", + [MSG_MARK_CLEARED] = "mark, cleared", + [MSG_PASTE] = "paste", + [MSG_BRIGHT] = "bright", + [MSG_ON_BLINKING] = "on blinking", + [MSG_OFF] = "off", + [MSG_ON] = "on", + [MSG_NO_WINDOW] = "no window", + [MSG_CURSORING_OFF] = "cursoring off", + [MSG_CURSORING_ON] = "cursoring on", + [MSG_HIGHLIGHT_TRACKING] = "highlight tracking", + [MSG_READ_WINDOW] = "read windo", + [MSG_READ_ALL] = "read all", + [MSG_EDIT_DONE] = "edit done", + [MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset", + [MSG_END_BEFORE_START] = "error end before start", + [MSG_WINDOW_CLEARED] = "window cleared", + [MSG_WINDOW_SILENCED] = "window silenced", + [MSG_WINDOW_SILENCE_DISABLED] = "window silence disabled", + [MSG_ERROR] = "error", + [MSG_GOTO_CANCELED] = "goto canceled", + [MSG_GOTO] = "go to?", + [MSG_LEAVING_HELP] = "leaving help", + [MSG_IS_UNASSIGNED] = "is unassigned", + [MSG_HELP_INFO] = + "press space to exit, up or down to scroll, or a letter to go to a command", + [MSG_EDGE_TOP] = "top,", + [MSG_EDGE_BOTTOM] = "bottom,", + [MSG_EDGE_LEFT] = "left,", + [MSG_EDGE_RIGHT] = "right,", + [MSG_NUMBER] = "number", + [MSG_SPACE] = "space", + [MSG_START] = "start", + [MSG_END] = "end", + [MSG_CTRL] = "control-", + [MSG_DISJUNCTION] = "or", + +/* Messages with embedded format specifiers. */ + [MSG_POS_INFO] = "line %ld, col %ld, t t y %d", + [MSG_CHAR_INFO] = "hex %02x, decimal %d", + [MSG_REPEAT_DESC] = "times %d .", + [MSG_REPEAT_DESC2] = "repeated %d .", + [MSG_WINDOW_LINE] = "window is line %d", + [MSG_WINDOW_BOUNDARY] = "%s at line %d, column %d", + [MSG_EDIT_PROMPT] = "edit %s, press space when done", + [MSG_NO_COMMAND] = "no commands for %c", + [MSG_KEYDESC] = "is %s", + + /* Control keys. */ + /* Most of these duplicate the entries in state names. */ + [MSG_CTL_SHIFT] = "shift", + [MSG_CTL_ALTGR] = "altgr", + [MSG_CTL_CONTROL] = "control", + [MSG_CTL_ALT] = "alt", + [MSG_CTL_LSHIFT] = "l shift", + [MSG_CTL_SPEAKUP] = "speakup", + [MSG_CTL_LCONTROL] = "l control", + [MSG_CTL_RCONTROL] = "r control", + [MSG_CTL_CAPSSHIFT] = "caps shift", + + /* Color names. */ + [MSG_COLOR_BLACK] = "black", + [MSG_COLOR_BLUE] = "blue", + [MSG_COLOR_GREEN] = "green", + [MSG_COLOR_CYAN] = "cyan", + [MSG_COLOR_RED] = "red", + [MSG_COLOR_MAGENTA] = "magenta", + [MSG_COLOR_YELLOW] = "yellow", + [MSG_COLOR_WHITE] = "white", + [MSG_COLOR_GREY] = "grey", + + /* Names of key states. */ + [MSG_STATE_DOUBLE] = "double", + [MSG_STATE_SPEAKUP] = "speakup", + [MSG_STATE_ALT] = "alt", + [MSG_STATE_CONTROL] = "ctrl", + [MSG_STATE_ALTGR] = "altgr", + [MSG_STATE_SHIFT] = "shift", + + /* Key names. */ + [MSG_KEYNAME_ESC] = "escape", + [MSG_KEYNAME_1] = "1", + [MSG_KEYNAME_2] = "2", + [MSG_KEYNAME_3] = "3", + [MSG_KEYNAME_4] = "4", + [MSG_KEYNAME_5] = "5", + [MSG_KEYNAME_6] = "6", + [MSG_KEYNAME_7] = "7", + [MSG_KEYNAME_8] = "8", + [MSG_KEYNAME_9] = "9", + [MSG_KEYNAME_0] = "0", + [MSG_KEYNAME_DASH] = "minus", + [MSG_KEYNAME_EQUAL] = "equal", + [MSG_KEYNAME_BS] = "back space", + [MSG_KEYNAME_TAB] = "tab", + [MSG_KEYNAME_Q] = "q", + [MSG_KEYNAME_W] = "w", + [MSG_KEYNAME_E] = "e", + [MSG_KEYNAME_R] = "r", + [MSG_KEYNAME_T] = "t", + [MSG_KEYNAME_Y] = "y", + [MSG_KEYNAME_U] = "u", + [MSG_KEYNAME_I] = "i", + [MSG_KEYNAME_O] = "o", + [MSG_KEYNAME_P] = "p", + [MSG_KEYNAME_LEFTBRACE] = "left brace", + [MSG_KEYNAME_RIGHTBRACE] = "right brace", + [MSG_KEYNAME_ENTER] = "enter", + [MSG_KEYNAME_LEFTCTRL] = "left control", + [MSG_KEYNAME_A] = "a", + [MSG_KEYNAME_S] = "s", + [MSG_KEYNAME_D] = "d", + [MSG_KEYNAME_F] = "f", + [MSG_KEYNAME_G] = "g", + [MSG_KEYNAME_H] = "h", + [MSG_KEYNAME_J] = "j", + [MSG_KEYNAME_K] = "k", + [MSG_KEYNAME_L] = "l", + [MSG_KEYNAME_SEMICOLON] = "semicolon", + [MSG_KEYNAME_SINGLEQUOTE] = "apostrophe", + [MSG_KEYNAME_GRAVE] = "accent", + [MSG_KEYNAME_LEFTSHFT] = "left shift", + [MSG_KEYNAME_BACKSLASH] = "back slash", + [MSG_KEYNAME_Z] = "z", + [MSG_KEYNAME_X] = "x", + [MSG_KEYNAME_C] = "c", + [MSG_KEYNAME_V] = "v", + [MSG_KEYNAME_B] = "b", + [MSG_KEYNAME_N] = "n", + [MSG_KEYNAME_M] = "m", + [MSG_KEYNAME_COMMA] = "comma", + [MSG_KEYNAME_DOT] = "dot", + [MSG_KEYNAME_SLASH] = "slash", + [MSG_KEYNAME_RIGHTSHFT] = "right shift", + [MSG_KEYNAME_KPSTAR] = "keypad asterisk", + [MSG_KEYNAME_LEFTALT] = "left alt", + [MSG_KEYNAME_SPACE] = "space", + [MSG_KEYNAME_CAPSLOCK] = "caps lock", + [MSG_KEYNAME_F1] = "f1", + [MSG_KEYNAME_F2] = "f2", + [MSG_KEYNAME_F3] = "f3", + [MSG_KEYNAME_F4] = "f4", + [MSG_KEYNAME_F5] = "f5", + [MSG_KEYNAME_F6] = "f6", + [MSG_KEYNAME_F7] = "f7", + [MSG_KEYNAME_F8] = "f8", + [MSG_KEYNAME_F9] = "f9", + [MSG_KEYNAME_F10] = "f10", + [MSG_KEYNAME_NUMLOCK] = "num lock", + [MSG_KEYNAME_SCROLLLOCK] = "scroll lock", + [MSG_KEYNAME_KP7] = "keypad 7", + [MSG_KEYNAME_KP8] = "keypad 8", + [MSG_KEYNAME_KP9] = "keypad 9", + [MSG_KEYNAME_KPMINUS] = "keypad minus", + [MSG_KEYNAME_KP4] = "keypad 4", + [MSG_KEYNAME_KP5] = "keypad 5", + [MSG_KEYNAME_KP6] = "keypad 6", + [MSG_KEYNAME_KPPLUS] = "keypad plus", + [MSG_KEYNAME_KP1] = "keypad 1", + [MSG_KEYNAME_KP2] = "keypad 2", + [MSG_KEYNAME_KP3] = "keypad 3", + [MSG_KEYNAME_KP0] = "keypad 0", + [MSG_KEYNAME_KPDOT] = "keypad dot", + [MSG_KEYNAME_103RD] = "103rd", + [MSG_KEYNAME_F13] = "f13", + [MSG_KEYNAME_102ND] = "102nd", + [MSG_KEYNAME_F11] = "f11", + [MSG_KEYNAME_F12] = "f12", + [MSG_KEYNAME_F14] = "f14", + [MSG_KEYNAME_F15] = "f15", + [MSG_KEYNAME_F16] = "f16", + [MSG_KEYNAME_F17] = "f17", + [MSG_KEYNAME_F18] = "f18", + [MSG_KEYNAME_F19] = "f19", + [MSG_KEYNAME_F20] = "f20", + [MSG_KEYNAME_KPENTER] = "keypad enter", + [MSG_KEYNAME_RIGHTCTRL] = "right control", + [MSG_KEYNAME_KPSLASH] = "keypad slash", + [MSG_KEYNAME_SYSRQ] = "sysrq", + [MSG_KEYNAME_RIGHTALT] = "right alt", + [MSG_KEYNAME_LF] = "line feed", + [MSG_KEYNAME_HOME] = "home", + [MSG_KEYNAME_UP] = "up", + [MSG_KEYNAME_PGUP] = "page up", + [MSG_KEYNAME_LEFT] = "left", + [MSG_KEYNAME_RIGHT] = "right", + [MSG_KEYNAME_END] = "end", + [MSG_KEYNAME_DOWN] = "down", + [MSG_KEYNAME_PGDN] = "page down", + [MSG_KEYNAME_INS] = "insert", + [MSG_KEYNAME_DEL] = "delete", + [MSG_KEYNAME_MACRO] = "macro", + [MSG_KEYNAME_MUTE] = "mute", + [MSG_KEYNAME_VOLDOWN] = "volume down", + [MSG_KEYNAME_VOLUP] = "volume up", + [MSG_KEYNAME_POWER] = "power", + [MSG_KEYNAME_KPEQUAL] = "keypad equal", + [MSG_KEYNAME_KPPLUSDASH] = "keypad plusminus", + [MSG_KEYNAME_PAUSE] = "pause", + [MSG_KEYNAME_F21] = "f21", + [MSG_KEYNAME_F22] = "f22", + [MSG_KEYNAME_F23] = "f23", + [MSG_KEYNAME_F24] = "f24", + [MSG_KEYNAME_KPCOMMA] = "keypad comma", + [MSG_KEYNAME_LEFTMETA] = "left meta", + [MSG_KEYNAME_RIGHTMETA] = "right meta", + [MSG_KEYNAME_COMPOSE] = "compose", + [MSG_KEYNAME_STOP] = "stop", + [MSG_KEYNAME_AGAIN] = "again", + [MSG_KEYNAME_PROPS] = "props", + [MSG_KEYNAME_UNDO] = "undo", + [MSG_KEYNAME_FRONT] = "front", + [MSG_KEYNAME_COPY] = "copy", + [MSG_KEYNAME_OPEN] = "open", + [MSG_KEYNAME_PASTE] = "paste", + [MSG_KEYNAME_FIND] = "find", + [MSG_KEYNAME_CUT] = "cut", + [MSG_KEYNAME_HELP] = "help", + [MSG_KEYNAME_MENU] = "menu", + [MSG_KEYNAME_CALC] = "calc", + [MSG_KEYNAME_SETUP] = "setup", + [MSG_KEYNAME_SLEEP] = "sleep", + [MSG_KEYNAME_WAKEUP] = "wakeup", + [MSG_KEYNAME_FILE] = "file", + [MSG_KEYNAME_SENDFILE] = "send file", + [MSG_KEYNAME_DELFILE] = "delete file", + [MSG_KEYNAME_XFER] = "transfer", + [MSG_KEYNAME_PROG1] = "prog1", + [MSG_KEYNAME_PROG2] = "prog2", + [MSG_KEYNAME_WWW] = "www", + [MSG_KEYNAME_MSDOS] = "msdos", + [MSG_KEYNAME_COFFEE] = "coffee", + [MSG_KEYNAME_DIRECTION] = "direction", + [MSG_KEYNAME_CYCLEWINDOWS] = "cycle windows", + [MSG_KEYNAME_MAIL] = "mail", + [MSG_KEYNAME_BOOKMARKS] = "bookmarks", + [MSG_KEYNAME_COMPUTER] = "computer", + [MSG_KEYNAME_BACK] = "back", + [MSG_KEYNAME_FORWARD] = "forward", + [MSG_KEYNAME_CLOSECD] = "close cd", + [MSG_KEYNAME_EJECTCD] = "eject cd", + [MSG_KEYNAME_EJECTCLOSE] = "eject close cd", + [MSG_KEYNAME_NEXTSONG] = "next song", + [MSG_KEYNAME_PLAYPAUSE] = "play pause", + [MSG_KEYNAME_PREVSONG] = "previous song", + [MSG_KEYNAME_STOPCD] = "stop cd", + [MSG_KEYNAME_RECORD] = "record", + [MSG_KEYNAME_REWIND] = "rewind", + [MSG_KEYNAME_PHONE] = "phone", + [MSG_KEYNAME_ISO] = "iso", + [MSG_KEYNAME_CONFIG] = "config", + [MSG_KEYNAME_HOMEPG] = "home page", + [MSG_KEYNAME_REFRESH] = "refresh", + [MSG_KEYNAME_EXIT] = "exit", + [MSG_KEYNAME_MOVE] = "move", + [MSG_KEYNAME_EDIT] = "edit", + [MSG_KEYNAME_SCROLLUP] = "scroll up", + [MSG_KEYNAME_SCROLLDN] = "scroll down", + [MSG_KEYNAME_KPLEFTPAR] = "keypad left paren", + [MSG_KEYNAME_KPRIGHTPAR] = "keypad right paren", + + /* Function names. */ + [MSG_FUNCNAME_ATTRIB_BLEEP_DEC] = "attribute bleep decrement", + [MSG_FUNCNAME_ATTRIB_BLEEP_INC] = "attribute bleep increment", + [MSG_FUNCNAME_BLEEPS_DEC] = "bleeps decrement", + [MSG_FUNCNAME_BLEEPS_INC] = "bleeps increment", + [MSG_FUNCNAME_CHAR_FIRST] = "character, first", + [MSG_FUNCNAME_CHAR_LAST] = "character, last", + [MSG_FUNCNAME_CHAR_CURRENT] = "character, say current", + [MSG_FUNCNAME_CHAR_HEX_AND_DEC] = "character, say hex and decimal", + [MSG_FUNCNAME_CHAR_NEXT] = "character, say next", + [MSG_FUNCNAME_CHAR_PHONETIC] = "character, say phonetic", + [MSG_FUNCNAME_CHAR_PREVIOUS] = "character, say previous", + [MSG_FUNCNAME_CURSOR_PARK] = "cursor park", + [MSG_FUNCNAME_CUT] = "cut", + [MSG_FUNCNAME_EDIT_DELIM] = "edit delimiters", + [MSG_FUNCNAME_EDIT_EXNUM] = "edit exnum", + [MSG_FUNCNAME_EDIT_MOST] = "edit most", + [MSG_FUNCNAME_EDIT_REPEATS] = "edit repeats", + [MSG_FUNCNAME_EDIT_SOME] = "edit some", + [MSG_FUNCNAME_GOTO] = "go to", + [MSG_FUNCNAME_GOTO_BOTTOM] = "go to bottom edge", + [MSG_FUNCNAME_GOTO_LEFT] = "go to left edge", + [MSG_FUNCNAME_GOTO_RIGHT] = "go to right edge", + [MSG_FUNCNAME_GOTO_TOP] = "go to top edge", + [MSG_FUNCNAME_HELP] = "help", + [MSG_FUNCNAME_LINE_SAY_CURRENT] = "line, say current", + [MSG_FUNCNAME_LINE_SAY_NEXT] = "line, say next", + [MSG_FUNCNAME_LINE_SAY_PREVIOUS] = "line, say previous", + [MSG_FUNCNAME_LINE_SAY_WITH_INDENT] = "line, say with indent", + [MSG_FUNCNAME_PASTE] = "paste", + [MSG_FUNCNAME_PITCH_DEC] = "pitch decrement", + [MSG_FUNCNAME_PITCH_INC] = "pitch increment", + [MSG_FUNCNAME_PUNC_DEC] = "punctuation decrement", + [MSG_FUNCNAME_PUNC_INC] = "punctuation increment", + [MSG_FUNCNAME_PUNC_LEVEL_DEC] = "punc level decrement", + [MSG_FUNCNAME_PUNC_LEVEL_INC] = "punc level increment", + [MSG_FUNCNAME_QUIET] = "quiet", + [MSG_FUNCNAME_RATE_DEC] = "rate decrement", + [MSG_FUNCNAME_RATE_INC] = "rate increment", + [MSG_FUNCNAME_READING_PUNC_DEC] = "reading punctuation decrement", + [MSG_FUNCNAME_READING_PUNC_INC] = "reading punctuation increment", + [MSG_FUNCNAME_SAY_ATTRIBUTES] = "say attributes", + [MSG_FUNCNAME_SAY_FROM_LEFT] = "say from left", + [MSG_FUNCNAME_SAY_FROM_TOP] = "say from top", + [MSG_FUNCNAME_SAY_POSITION] = "say position", + [MSG_FUNCNAME_SAY_SCREEN] = "say screen", + [MSG_FUNCNAME_SAY_TO_BOTTOM] = "say to bottom", + [MSG_FUNCNAME_SAY_TO_RIGHT] = "say to right", + [MSG_FUNCNAME_SPEAKUP] = "speakup", + [MSG_FUNCNAME_SPEAKUP_LOCK] = "speakup lock", + [MSG_FUNCNAME_SPEAKUP_OFF] = "speakup off", + [MSG_FUNCNAME_SPEECH_KILL] = "speech kill", + [MSG_FUNCNAME_SPELL_DELAY_DEC] = "spell delay decrement", + [MSG_FUNCNAME_SPELL_DELAY_INC] = "spell delay increment", + [MSG_FUNCNAME_SPELL_WORD] = "spell word", + [MSG_FUNCNAME_SPELL_WORD_PHONETICALLY] = "spell word phonetically", + [MSG_FUNCNAME_TONE_DEC] = "tone decrement", + [MSG_FUNCNAME_TONE_INC] = "tone increment", + [MSG_FUNCNAME_VOICE_DEC] = "voice decrement", + [MSG_FUNCNAME_VOICE_INC] = "voice increment", + [MSG_FUNCNAME_VOLUME_DEC] = "volume decrement", + [MSG_FUNCNAME_VOLUME_INC] = "volume increment", + [MSG_FUNCNAME_WINDOW_CLEAR] = "window, clear", + [MSG_FUNCNAME_WINDOW_SAY] = "window, say", + [MSG_FUNCNAME_WINDOW_SET] = "window, set", + [MSG_FUNCNAME_WINDOW_SILENCE] = "window, silence", + [MSG_FUNCNAME_WORD_SAY_CURRENT] = "word, say current", + [MSG_FUNCNAME_WORD_SAY_NEXT] = "word, say next", + [MSG_FUNCNAME_WORD_SAY_PREVIOUS] = "word, say previous", +}; + +static struct msg_group_t all_groups[] = { + { + .name = "ctl_keys", + .start = MSG_CTL_START, + .end = MSG_CTL_END, + }, + { + .name = "colors", + .start = MSG_COLORS_START, + .end = MSG_COLORS_END, + }, + { + .name = "formatted", + .start = MSG_FORMATTED_START, + .end = MSG_FORMATTED_END, + }, + { + .name = "function_names", + .start = MSG_FUNCNAMES_START, + .end = MSG_FUNCNAMES_END, + }, + { + .name = "key_names", + .start = MSG_KEYNAMES_START, + .end = MSG_KEYNAMES_END, + }, + { + .name = "announcements", + .start = MSG_ANNOUNCEMENTS_START, + .end = MSG_ANNOUNCEMENTS_END, + }, + { + .name = "states", + .start = MSG_STATES_START, + .end = MSG_STATES_END, + }, +}; + +static const int num_groups = ARRAY_SIZE(all_groups); + +char *spk_msg_get(enum msg_index_t index) +{ + return speakup_msgs[index]; +} + +/* + * Function: next_specifier + * Finds the start of the next format specifier in the argument string. + * Return value: pointer to start of format + * specifier, or NULL if no specifier exists. + */ +static char *next_specifier(char *input) +{ + int found = 0; + char *next_percent = input; + + while (next_percent && !found) { + next_percent = strchr(next_percent, '%'); + if (next_percent) { + /* skip over doubled percent signs */ + while (next_percent[0] == '%' && + next_percent[1] == '%') + next_percent += 2; + if (*next_percent == '%') + found = 1; + else if (*next_percent == '\0') + next_percent = NULL; + } + } + + return next_percent; +} + +/* Skip over 0 or more flags. */ +static char *skip_flags(char *input) +{ + while ((*input != '\0') && strchr(" 0+-#", *input)) + input++; + return input; +} + +/* Skip over width.precision, if it exists. */ +static char *skip_width(char *input) +{ + while (isdigit(*input)) + input++; + if (*input == '.') { + input++; + while (isdigit(*input)) + input++; + } + return input; +} + +/* + * Skip past the end of the conversion part. + * Note that this code only accepts a handful of conversion specifiers: + * c d s x and ld. Not accidental; these are exactly the ones used in + * the default group of formatted messages. + */ +static char *skip_conversion(char *input) +{ + if ((input[0] == 'l') && (input[1] == 'd')) + input += 2; + else if ((*input != '\0') && strchr("cdsx", *input)) + input++; + return input; +} + +/* + * Function: find_specifier_end + * Return a pointer to the end of the format specifier. + */ +static char *find_specifier_end(char *input) +{ + input++; /* Advance over %. */ + input = skip_flags(input); + input = skip_width(input); + input = skip_conversion(input); + return input; +} + +/* + * Function: compare_specifiers + * Compare the format specifiers pointed to by *input1 and *input2. + * Return true if they are the same, false otherwise. + * Advance *input1 and *input2 so that they point to the character following + * the end of the specifier. + */ +static bool compare_specifiers(char **input1, char **input2) +{ + bool same = false; + char *end1 = find_specifier_end(*input1); + char *end2 = find_specifier_end(*input2); + size_t length1 = end1 - *input1; + size_t length2 = end2 - *input2; + + if ((length1 == length2) && !memcmp(*input1, *input2, length1)) + same = true; + + *input1 = end1; + *input2 = end2; + return same; +} + +/* + * Function: fmt_validate + * Check that two format strings contain the same number of format specifiers, + * and that the order of specifiers is the same in both strings. + * Return true if the condition holds, false if it doesn't. + */ +static bool fmt_validate(char *template, char *user) +{ + bool valid = true; + bool still_comparing = true; + char *template_ptr = template; + char *user_ptr = user; + + while (still_comparing && valid) { + template_ptr = next_specifier(template_ptr); + user_ptr = next_specifier(user_ptr); + if (template_ptr && user_ptr) { + /* Both have at least one more specifier. */ + valid = compare_specifiers(&template_ptr, &user_ptr); + } else { + /* No more format specifiers in one or both strings. */ + still_comparing = false; + /* See if one has more specifiers than the other. */ + if (template_ptr || user_ptr) + valid = false; + } + } + return valid; +} + +/* + * Function: msg_set + * Description: Add a user-supplied message to the user_messages array. + * The message text is copied to a memory area allocated with kmalloc. + * If the function fails, then user_messages is untouched. + * Arguments: + * - index: a message number, as found in i18n.h. + * - text: text of message. Not NUL-terminated. + * - length: number of bytes in text. + * Failure conditions: + * -EINVAL - Invalid format specifiers in formatted message or illegal index. + * -ENOMEM - Unable to allocate memory. + */ +ssize_t spk_msg_set(enum msg_index_t index, char *text, size_t length) +{ + char *newstr = NULL; + unsigned long flags; + + if ((index < MSG_FIRST_INDEX) || (index >= MSG_LAST_INDEX)) + return -EINVAL; + + newstr = kmalloc(length + 1, GFP_KERNEL); + if (!newstr) + return -ENOMEM; + + memcpy(newstr, text, length); + newstr[length] = '\0'; + if (index >= MSG_FORMATTED_START && + index <= MSG_FORMATTED_END && + !fmt_validate(speakup_default_msgs[index], newstr)) { + kfree(newstr); + return -EINVAL; + } + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (speakup_msgs[index] != speakup_default_msgs[index]) + kfree(speakup_msgs[index]); + speakup_msgs[index] = newstr; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return 0; +} + +/* + * Find a message group, given its name. Return a pointer to the structure + * if found, or NULL otherwise. + */ +struct msg_group_t *spk_find_msg_group(const char *group_name) +{ + struct msg_group_t *group = NULL; + int i; + + for (i = 0; i < num_groups; i++) { + if (!strcmp(all_groups[i].name, group_name)) { + group = &all_groups[i]; + break; + } + } + return group; +} + +void spk_reset_msg_group(struct msg_group_t *group) +{ + unsigned long flags; + enum msg_index_t i; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + + for (i = group->start; i <= group->end; i++) { + if (speakup_msgs[i] != speakup_default_msgs[i]) + kfree(speakup_msgs[i]); + speakup_msgs[i] = speakup_default_msgs[i]; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +/* Called at initialization time, to establish default messages. */ +void spk_initialize_msgs(void) +{ + memcpy(speakup_msgs, speakup_default_msgs, + sizeof(speakup_default_msgs)); +} + +/* Free user-supplied strings when module is unloaded: */ +void spk_free_user_msgs(void) +{ + enum msg_index_t index; + unsigned long flags; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + for (index = MSG_FIRST_INDEX; index < MSG_LAST_INDEX; index++) { + if (speakup_msgs[index] != speakup_default_msgs[index]) { + kfree(speakup_msgs[index]); + speakup_msgs[index] = speakup_default_msgs[index]; + } + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} diff --git a/drivers/accessibility/speakup/i18n.h b/drivers/accessibility/speakup/i18n.h new file mode 100644 index 000000000000..2ec6e659d02b --- /dev/null +++ b/drivers/accessibility/speakup/i18n.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef I18N_H +#define I18N_H +/* Internationalization declarations */ + +enum msg_index_t { + MSG_FIRST_INDEX, + MSG_ANNOUNCEMENTS_START = MSG_FIRST_INDEX, + MSG_BLANK = MSG_ANNOUNCEMENTS_START, + MSG_IAM_ALIVE, + MSG_YOU_KILLED_SPEAKUP, + MSG_HEY_THATS_BETTER, + MSG_YOU_TURNED_ME_OFF, + MSG_PARKED, + MSG_UNPARKED, + MSG_MARK, + MSG_CUT, + MSG_MARK_CLEARED, + MSG_PASTE, + MSG_BRIGHT, + MSG_ON_BLINKING, + MSG_STATUS_START, + MSG_OFF = MSG_STATUS_START, + MSG_ON, + MSG_NO_WINDOW, + MSG_CURSOR_MSGS_START, + MSG_CURSORING_OFF = MSG_CURSOR_MSGS_START, + MSG_CURSORING_ON, + MSG_HIGHLIGHT_TRACKING, + MSG_READ_WINDOW, + MSG_READ_ALL, + MSG_EDIT_DONE, + MSG_WINDOW_ALREADY_SET, + MSG_END_BEFORE_START, + MSG_WINDOW_CLEARED, + MSG_WINDOW_SILENCED, + MSG_WINDOW_SILENCE_DISABLED, + MSG_ERROR, + MSG_GOTO_CANCELED, + MSG_GOTO, + MSG_LEAVING_HELP, + MSG_IS_UNASSIGNED, + MSG_HELP_INFO, + MSG_EDGE_MSGS_START, + MSG_EDGE_TOP = MSG_EDGE_MSGS_START, + MSG_EDGE_BOTTOM, + MSG_EDGE_LEFT, + MSG_EDGE_RIGHT, + MSG_NUMBER, + MSG_SPACE, + MSG_START, /* A little confusing, given our convention. */ + MSG_END, /* A little confusing, given our convention. */ + MSG_CTRL, + +/* A message containing the single word "or". */ + MSG_DISJUNCTION, + MSG_ANNOUNCEMENTS_END = MSG_DISJUNCTION, + +/* Messages with format specifiers. */ + MSG_FORMATTED_START, + MSG_POS_INFO = MSG_FORMATTED_START, + MSG_CHAR_INFO, + MSG_REPEAT_DESC, + MSG_REPEAT_DESC2, + MSG_WINDOW_LINE, + MSG_WINDOW_BOUNDARY, + MSG_EDIT_PROMPT, + MSG_NO_COMMAND, + MSG_KEYDESC, + MSG_FORMATTED_END = MSG_KEYDESC, + + /* Control keys. */ + MSG_CTL_START, + MSG_CTL_SHIFT = MSG_CTL_START, + MSG_CTL_ALTGR, + MSG_CTL_CONTROL, + MSG_CTL_ALT, + MSG_CTL_LSHIFT, + MSG_CTL_SPEAKUP, + MSG_CTL_LCONTROL, + MSG_CTL_RCONTROL, + MSG_CTL_CAPSSHIFT, + MSG_CTL_END = MSG_CTL_CAPSSHIFT, + + /* Colors. */ + MSG_COLORS_START, + MSG_COLOR_BLACK = MSG_COLORS_START, + MSG_COLOR_BLUE, + MSG_COLOR_GREEN, + MSG_COLOR_CYAN, + MSG_COLOR_RED, + MSG_COLOR_MAGENTA, + MSG_COLOR_YELLOW, + MSG_COLOR_WHITE, + MSG_COLOR_GREY, + MSG_COLORS_END = MSG_COLOR_GREY, + + MSG_STATES_START, + MSG_STATE_DOUBLE = MSG_STATES_START, + MSG_STATE_SPEAKUP, + MSG_STATE_ALT, + MSG_STATE_CONTROL, + MSG_STATE_ALTGR, + MSG_STATE_SHIFT, + MSG_STATES_END = MSG_STATE_SHIFT, + + MSG_KEYNAMES_START, + MSG_KEYNAME_ESC = MSG_KEYNAMES_START, + MSG_KEYNAME_1, MSG_KEYNAME_2, MSG_KEYNAME_3, MSG_KEYNAME_4, + MSG_KEYNAME_5, MSG_KEYNAME_6, MSG_KEYNAME_7, MSG_KEYNAME_8, + MSG_KEYNAME_9, + MSG_KEYNAME_0, MSG_KEYNAME_DASH, MSG_KEYNAME_EQUAL, MSG_KEYNAME_BS, + MSG_KEYNAME_TAB, + MSG_KEYNAME_Q, MSG_KEYNAME_W, MSG_KEYNAME_E, MSG_KEYNAME_R, + MSG_KEYNAME_T, MSG_KEYNAME_Y, MSG_KEYNAME_U, MSG_KEYNAME_I, + MSG_KEYNAME_O, MSG_KEYNAME_P, + MSG_KEYNAME_LEFTBRACE, MSG_KEYNAME_RIGHTBRACE, MSG_KEYNAME_ENTER, + MSG_KEYNAME_LEFTCTRL, MSG_KEYNAME_A, + MSG_KEYNAME_S, MSG_KEYNAME_D, MSG_KEYNAME_F, MSG_KEYNAME_G, + MSG_KEYNAME_H, MSG_KEYNAME_J, MSG_KEYNAME_K, MSG_KEYNAME_L, + MSG_KEYNAME_SEMICOLON, + MSG_KEYNAME_SINGLEQUOTE, MSG_KEYNAME_GRAVE, + MSG_KEYNAME_LEFTSHFT, MSG_KEYNAME_BACKSLASH, MSG_KEYNAME_Z, + MSG_KEYNAME_X, MSG_KEYNAME_C, MSG_KEYNAME_V, MSG_KEYNAME_B, + MSG_KEYNAME_N, MSG_KEYNAME_M, MSG_KEYNAME_COMMA, MSG_KEYNAME_DOT, + MSG_KEYNAME_SLASH, MSG_KEYNAME_RIGHTSHFT, + MSG_KEYNAME_KPSTAR, + MSG_KEYNAME_LEFTALT, MSG_KEYNAME_SPACE, MSG_KEYNAME_CAPSLOCK, + MSG_KEYNAME_F1, MSG_KEYNAME_F2, + MSG_KEYNAME_F3, MSG_KEYNAME_F4, MSG_KEYNAME_F5, MSG_KEYNAME_F6, + MSG_KEYNAME_F7, + MSG_KEYNAME_F8, MSG_KEYNAME_F9, MSG_KEYNAME_F10, MSG_KEYNAME_NUMLOCK, + MSG_KEYNAME_SCROLLLOCK, + MSG_KEYNAME_KP7, MSG_KEYNAME_KP8, MSG_KEYNAME_KP9, MSG_KEYNAME_KPMINUS, + MSG_KEYNAME_KP4, + MSG_KEYNAME_KP5, MSG_KEYNAME_KP6, MSG_KEYNAME_KPPLUS, MSG_KEYNAME_KP1, + MSG_KEYNAME_KP2, + MSG_KEYNAME_KP3, MSG_KEYNAME_KP0, MSG_KEYNAME_KPDOT, MSG_KEYNAME_103RD, + MSG_KEYNAME_F13, + MSG_KEYNAME_102ND, MSG_KEYNAME_F11, MSG_KEYNAME_F12, MSG_KEYNAME_F14, + MSG_KEYNAME_F15, + MSG_KEYNAME_F16, MSG_KEYNAME_F17, MSG_KEYNAME_F18, MSG_KEYNAME_F19, + MSG_KEYNAME_F20, + MSG_KEYNAME_KPENTER, MSG_KEYNAME_RIGHTCTRL, MSG_KEYNAME_KPSLASH, + MSG_KEYNAME_SYSRQ, MSG_KEYNAME_RIGHTALT, + MSG_KEYNAME_LF, MSG_KEYNAME_HOME, MSG_KEYNAME_UP, MSG_KEYNAME_PGUP, + MSG_KEYNAME_LEFT, + MSG_KEYNAME_RIGHT, MSG_KEYNAME_END, MSG_KEYNAME_DOWN, MSG_KEYNAME_PGDN, + MSG_KEYNAME_INS, + MSG_KEYNAME_DEL, MSG_KEYNAME_MACRO, MSG_KEYNAME_MUTE, + MSG_KEYNAME_VOLDOWN, MSG_KEYNAME_VOLUP, + MSG_KEYNAME_POWER, MSG_KEYNAME_KPEQUAL, MSG_KEYNAME_KPPLUSDASH, + MSG_KEYNAME_PAUSE, MSG_KEYNAME_F21, MSG_KEYNAME_F22, MSG_KEYNAME_F23, + MSG_KEYNAME_F24, MSG_KEYNAME_KPCOMMA, MSG_KEYNAME_LEFTMETA, + MSG_KEYNAME_RIGHTMETA, MSG_KEYNAME_COMPOSE, MSG_KEYNAME_STOP, + MSG_KEYNAME_AGAIN, MSG_KEYNAME_PROPS, + MSG_KEYNAME_UNDO, MSG_KEYNAME_FRONT, MSG_KEYNAME_COPY, MSG_KEYNAME_OPEN, + MSG_KEYNAME_PASTE, + MSG_KEYNAME_FIND, MSG_KEYNAME_CUT, MSG_KEYNAME_HELP, MSG_KEYNAME_MENU, + MSG_KEYNAME_CALC, + MSG_KEYNAME_SETUP, MSG_KEYNAME_SLEEP, MSG_KEYNAME_WAKEUP, + MSG_KEYNAME_FILE, MSG_KEYNAME_SENDFILE, + MSG_KEYNAME_DELFILE, MSG_KEYNAME_XFER, MSG_KEYNAME_PROG1, + MSG_KEYNAME_PROG2, MSG_KEYNAME_WWW, + MSG_KEYNAME_MSDOS, MSG_KEYNAME_COFFEE, MSG_KEYNAME_DIRECTION, + MSG_KEYNAME_CYCLEWINDOWS, MSG_KEYNAME_MAIL, + MSG_KEYNAME_BOOKMARKS, MSG_KEYNAME_COMPUTER, MSG_KEYNAME_BACK, + MSG_KEYNAME_FORWARD, MSG_KEYNAME_CLOSECD, + MSG_KEYNAME_EJECTCD, MSG_KEYNAME_EJECTCLOSE, MSG_KEYNAME_NEXTSONG, + MSG_KEYNAME_PLAYPAUSE, MSG_KEYNAME_PREVSONG, + MSG_KEYNAME_STOPCD, MSG_KEYNAME_RECORD, MSG_KEYNAME_REWIND, + MSG_KEYNAME_PHONE, MSG_KEYNAME_ISO, + MSG_KEYNAME_CONFIG, MSG_KEYNAME_HOMEPG, MSG_KEYNAME_REFRESH, + MSG_KEYNAME_EXIT, MSG_KEYNAME_MOVE, + MSG_KEYNAME_EDIT, MSG_KEYNAME_SCROLLUP, MSG_KEYNAME_SCROLLDN, + MSG_KEYNAME_KPLEFTPAR, MSG_KEYNAME_KPRIGHTPAR, + MSG_KEYNAMES_END = MSG_KEYNAME_KPRIGHTPAR, + + MSG_FUNCNAMES_START, + MSG_FUNCNAME_ATTRIB_BLEEP_DEC = MSG_FUNCNAMES_START, + MSG_FUNCNAME_ATTRIB_BLEEP_INC, + MSG_FUNCNAME_BLEEPS_DEC, MSG_FUNCNAME_BLEEPS_INC, + MSG_FUNCNAME_CHAR_FIRST, MSG_FUNCNAME_CHAR_LAST, + MSG_FUNCNAME_CHAR_CURRENT, MSG_FUNCNAME_CHAR_HEX_AND_DEC, + MSG_FUNCNAME_CHAR_NEXT, + MSG_FUNCNAME_CHAR_PHONETIC, MSG_FUNCNAME_CHAR_PREVIOUS, + MSG_FUNCNAME_CURSOR_PARK, MSG_FUNCNAME_CUT, + MSG_FUNCNAME_EDIT_DELIM, MSG_FUNCNAME_EDIT_EXNUM, + MSG_FUNCNAME_EDIT_MOST, MSG_FUNCNAME_EDIT_REPEATS, + MSG_FUNCNAME_EDIT_SOME, + MSG_FUNCNAME_GOTO, MSG_FUNCNAME_GOTO_BOTTOM, MSG_FUNCNAME_GOTO_LEFT, + MSG_FUNCNAME_GOTO_RIGHT, MSG_FUNCNAME_GOTO_TOP, MSG_FUNCNAME_HELP, + MSG_FUNCNAME_LINE_SAY_CURRENT, MSG_FUNCNAME_LINE_SAY_NEXT, + MSG_FUNCNAME_LINE_SAY_PREVIOUS, MSG_FUNCNAME_LINE_SAY_WITH_INDENT, + MSG_FUNCNAME_PASTE, MSG_FUNCNAME_PITCH_DEC, MSG_FUNCNAME_PITCH_INC, + MSG_FUNCNAME_PUNC_DEC, MSG_FUNCNAME_PUNC_INC, + MSG_FUNCNAME_PUNC_LEVEL_DEC, MSG_FUNCNAME_PUNC_LEVEL_INC, + MSG_FUNCNAME_QUIET, + MSG_FUNCNAME_RATE_DEC, MSG_FUNCNAME_RATE_INC, + MSG_FUNCNAME_READING_PUNC_DEC, MSG_FUNCNAME_READING_PUNC_INC, + MSG_FUNCNAME_SAY_ATTRIBUTES, + MSG_FUNCNAME_SAY_FROM_LEFT, MSG_FUNCNAME_SAY_FROM_TOP, + MSG_FUNCNAME_SAY_POSITION, MSG_FUNCNAME_SAY_SCREEN, + MSG_FUNCNAME_SAY_TO_BOTTOM, MSG_FUNCNAME_SAY_TO_RIGHT, + MSG_FUNCNAME_SPEAKUP, MSG_FUNCNAME_SPEAKUP_LOCK, + MSG_FUNCNAME_SPEAKUP_OFF, MSG_FUNCNAME_SPEECH_KILL, + MSG_FUNCNAME_SPELL_DELAY_DEC, MSG_FUNCNAME_SPELL_DELAY_INC, + MSG_FUNCNAME_SPELL_WORD, MSG_FUNCNAME_SPELL_WORD_PHONETICALLY, + MSG_FUNCNAME_TONE_DEC, MSG_FUNCNAME_TONE_INC, + MSG_FUNCNAME_VOICE_DEC, MSG_FUNCNAME_VOICE_INC, + MSG_FUNCNAME_VOLUME_DEC, MSG_FUNCNAME_VOLUME_INC, + MSG_FUNCNAME_WINDOW_CLEAR, MSG_FUNCNAME_WINDOW_SAY, + MSG_FUNCNAME_WINDOW_SET, MSG_FUNCNAME_WINDOW_SILENCE, + MSG_FUNCNAME_WORD_SAY_CURRENT, MSG_FUNCNAME_WORD_SAY_NEXT, + MSG_FUNCNAME_WORD_SAY_PREVIOUS, + MSG_FUNCNAMES_END = MSG_FUNCNAME_WORD_SAY_PREVIOUS, + + /* all valid indices must be above this */ + MSG_LAST_INDEX +}; + +struct msg_group_t { + char *name; + enum msg_index_t start; + enum msg_index_t end; +}; + +char *spk_msg_get(enum msg_index_t index); +ssize_t spk_msg_set(enum msg_index_t index, char *text, size_t length); +struct msg_group_t *spk_find_msg_group(const char *group_name); +void spk_reset_msg_group(struct msg_group_t *group); +void spk_initialize_msgs(void); +void spk_free_user_msgs(void); + +#endif diff --git a/drivers/accessibility/speakup/keyhelp.c b/drivers/accessibility/speakup/keyhelp.c new file mode 100644 index 000000000000..822ceac83068 --- /dev/null +++ b/drivers/accessibility/speakup/keyhelp.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* speakup_keyhelp.c + * help module for speakup + * + *written by David Borowski. + * + * Copyright (C) 2003 David Borowski. + */ + +#include +#include "spk_priv.h" +#include "speakup.h" + +#define MAXFUNCS 130 +#define MAXKEYS 256 +static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1; +static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS]; +static u_short masks[] = { 32, 16, 8, 4, 2, 1 }; + +static short letter_offsets[26] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1 }; + +static u_char funcvals[] = { + ATTRIB_BLEEP_DEC, ATTRIB_BLEEP_INC, BLEEPS_DEC, BLEEPS_INC, + SAY_FIRST_CHAR, SAY_LAST_CHAR, SAY_CHAR, SAY_CHAR_NUM, + SAY_NEXT_CHAR, SAY_PHONETIC_CHAR, SAY_PREV_CHAR, SPEAKUP_PARKED, + SPEAKUP_CUT, EDIT_DELIM, EDIT_EXNUM, EDIT_MOST, + EDIT_REPEAT, EDIT_SOME, SPEAKUP_GOTO, BOTTOM_EDGE, + LEFT_EDGE, RIGHT_EDGE, TOP_EDGE, SPEAKUP_HELP, + SAY_LINE, SAY_NEXT_LINE, SAY_PREV_LINE, SAY_LINE_INDENT, + SPEAKUP_PASTE, PITCH_DEC, PITCH_INC, PUNCT_DEC, + PUNCT_INC, PUNC_LEVEL_DEC, PUNC_LEVEL_INC, SPEAKUP_QUIET, + RATE_DEC, RATE_INC, READING_PUNC_DEC, READING_PUNC_INC, + SAY_ATTRIBUTES, SAY_FROM_LEFT, SAY_FROM_TOP, SAY_POSITION, + SAY_SCREEN, SAY_TO_BOTTOM, SAY_TO_RIGHT, SPK_KEY, + SPK_LOCK, SPEAKUP_OFF, SPEECH_KILL, SPELL_DELAY_DEC, + SPELL_DELAY_INC, SPELL_WORD, SPELL_PHONETIC, TONE_DEC, + TONE_INC, VOICE_DEC, VOICE_INC, VOL_DEC, + VOL_INC, CLEAR_WIN, SAY_WIN, SET_WIN, + ENABLE_WIN, SAY_WORD, SAY_NEXT_WORD, SAY_PREV_WORD, 0 +}; + +static u_char *state_tbl; +static int cur_item, nstates; + +static void build_key_data(void) +{ + u_char *kp, counters[MAXFUNCS], ch, ch1; + u_short *p_key, key; + int i, offset = 1; + + nstates = (int)(state_tbl[-1]); + memset(counters, 0, sizeof(counters)); + memset(key_offsets, 0, sizeof(key_offsets)); + kp = state_tbl + nstates + 1; + while (*kp++) { + /* count occurrences of each function */ + for (i = 0; i < nstates; i++, kp++) { + if (!*kp) + continue; + if ((state_tbl[i] & 16) != 0 && *kp == SPK_KEY) + continue; + counters[*kp]++; + } + } + for (i = 0; i < MAXFUNCS; i++) { + if (counters[i] == 0) + continue; + key_offsets[i] = offset; + offset += (counters[i] + 1); + if (offset >= MAXKEYS) + break; + } +/* leave counters set so high keycodes come first. + * this is done so num pad and other extended keys maps are spoken before + * the alpha with speakup type mapping. + */ + kp = state_tbl + nstates + 1; + while ((ch = *kp++)) { + for (i = 0; i < nstates; i++) { + ch1 = *kp++; + if (!ch1) + continue; + if ((state_tbl[i] & 16) != 0 && ch1 == SPK_KEY) + continue; + key = (state_tbl[i] << 8) + ch; + counters[ch1]--; + offset = key_offsets[ch1]; + if (!offset) + continue; + p_key = key_data + offset + counters[ch1]; + *p_key = key; + } + } +} + +static void say_key(int key) +{ + int i, state = key >> 8; + + key &= 0xff; + for (i = 0; i < 6; i++) { + if (state & masks[i]) + synth_printf(" %s", spk_msg_get(MSG_STATES_START + i)); + } + if ((key > 0) && (key <= num_key_names)) + synth_printf(" %s\n", + spk_msg_get(MSG_KEYNAMES_START + (key - 1))); +} + +static int help_init(void) +{ + char start = SPACE; + int i; + int num_funcs = MSG_FUNCNAMES_END - MSG_FUNCNAMES_START + 1; + + state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2; + for (i = 0; i < num_funcs; i++) { + char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i); + + if (start == *cur_funcname) + continue; + start = *cur_funcname; + letter_offsets[(start & 31) - 1] = i; + } + return 0; +} + +int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key) +{ + int i, n; + char *name; + u_char func, *kp; + u_short *p_keys, val; + + if (letter_offsets[0] == -1) + help_init(); + if (type == KT_LATIN) { + if (ch == SPACE) { + spk_special_handler = NULL; + synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP)); + return 1; + } + ch |= 32; /* lower case */ + if (ch < 'a' || ch > 'z') + return -1; + if (letter_offsets[ch - 'a'] == -1) { + synth_printf(spk_msg_get(MSG_NO_COMMAND), ch); + synth_printf("\n"); + return 1; + } + cur_item = letter_offsets[ch - 'a']; + } else if (type == KT_CUR) { + if (ch == 0 && + (MSG_FUNCNAMES_START + cur_item + 1) <= MSG_FUNCNAMES_END) + cur_item++; + else if (ch == 3 && cur_item > 0) + cur_item--; + else + return -1; + } else if (type == KT_SPKUP && ch == SPEAKUP_HELP && + !spk_special_handler) { + spk_special_handler = spk_handle_help; + synth_printf("%s\n", spk_msg_get(MSG_HELP_INFO)); + build_key_data(); /* rebuild each time in case new mapping */ + return 1; + } else { + name = NULL; + if ((type != KT_SPKUP) && (key > 0) && (key <= num_key_names)) { + synth_printf("%s\n", + spk_msg_get(MSG_KEYNAMES_START + key - 1)); + return 1; + } + for (i = 0; funcvals[i] != 0 && !name; i++) { + if (ch == funcvals[i]) + name = spk_msg_get(MSG_FUNCNAMES_START + i); + } + if (!name) + return -1; + kp = spk_our_keys[key] + 1; + for (i = 0; i < nstates; i++) { + if (ch == kp[i]) + break; + } + key += (state_tbl[i] << 8); + say_key(key); + synth_printf(spk_msg_get(MSG_KEYDESC), name); + synth_printf("\n"); + return 1; + } + name = spk_msg_get(MSG_FUNCNAMES_START + cur_item); + func = funcvals[cur_item]; + synth_printf("%s", name); + if (key_offsets[func] == 0) { + synth_printf(" %s\n", spk_msg_get(MSG_IS_UNASSIGNED)); + return 1; + } + p_keys = key_data + key_offsets[func]; + for (n = 0; p_keys[n]; n++) { + val = p_keys[n]; + if (n > 0) + synth_printf("%s ", spk_msg_get(MSG_DISJUNCTION)); + say_key(val); + } + return 1; +} diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c new file mode 100644 index 000000000000..41ae24ab5d08 --- /dev/null +++ b/drivers/accessibility/speakup/kobjects.c @@ -0,0 +1,1056 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Speakup kobject implementation + * + * Copyright (C) 2009 William Hubbs + * + * This code is based on kobject-example.c, which came with linux 2.6.x. + * + * Copyright (C) 2004-2007 Greg Kroah-Hartman + * Copyright (C) 2007 Novell Inc. + * + * Released under the GPL version 2 only. + * + */ +#include /* For kmalloc. */ +#include +#include +#include +#include +#include +#include + +#include "speakup.h" +#include "spk_priv.h" + +/* + * This is called when a user reads the characters or chartab sys file. + */ +static ssize_t chars_chartab_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int len = 0; + char *cp; + char *buf_pointer = buf; + size_t bufsize = PAGE_SIZE; + unsigned long flags; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + *buf_pointer = '\0'; + for (i = 0; i < 256; i++) { + if (bufsize <= 1) + break; + if (strcmp("characters", attr->attr.name) == 0) { + len = scnprintf(buf_pointer, bufsize, "%d\t%s\n", + i, spk_characters[i]); + } else { /* show chartab entry */ + if (IS_TYPE(i, B_CTL)) + cp = "B_CTL"; + else if (IS_TYPE(i, WDLM)) + cp = "WDLM"; + else if (IS_TYPE(i, A_PUNC)) + cp = "A_PUNC"; + else if (IS_TYPE(i, PUNC)) + cp = "PUNC"; + else if (IS_TYPE(i, NUM)) + cp = "NUM"; + else if (IS_TYPE(i, A_CAP)) + cp = "A_CAP"; + else if (IS_TYPE(i, ALPHA)) + cp = "ALPHA"; + else if (IS_TYPE(i, B_CAPSYM)) + cp = "B_CAPSYM"; + else if (IS_TYPE(i, B_SYM)) + cp = "B_SYM"; + else + cp = "0"; + len = + scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp); + } + bufsize -= len; + buf_pointer += len; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return buf_pointer - buf; +} + +/* + * Print informational messages or warnings after updating + * character descriptions or chartab entries. + */ +static void report_char_chartab_status(int reset, int received, int used, + int rejected, int do_characters) +{ + static char const *object_type[] = { + "character class entries", + "character descriptions", + }; + int len; + char buf[80]; + + if (reset) { + pr_info("%s reset to defaults\n", object_type[do_characters]); + } else if (received) { + len = snprintf(buf, sizeof(buf), + " updated %d of %d %s\n", + used, received, object_type[do_characters]); + if (rejected) + snprintf(buf + (len - 1), sizeof(buf) - (len - 1), + " with %d reject%s\n", + rejected, rejected > 1 ? "s" : ""); + pr_info("%s", buf); + } +} + +/* + * This is called when a user changes the characters or chartab parameters. + */ +static ssize_t chars_chartab_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + char *cp = (char *)buf; + char *end = cp + count; /* the null at the end of the buffer */ + char *linefeed = NULL; + char keyword[MAX_DESC_LEN + 1]; + char *outptr = NULL; /* Will hold keyword or desc. */ + char *temp = NULL; + char *desc = NULL; + ssize_t retval = count; + unsigned long flags; + unsigned long index = 0; + int charclass = 0; + int received = 0; + int used = 0; + int rejected = 0; + int reset = 0; + int do_characters = !strcmp(attr->attr.name, "characters"); + size_t desc_length = 0; + int i; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + while (cp < end) { + while ((cp < end) && (*cp == ' ' || *cp == '\t')) + cp++; + + if (cp == end) + break; + if ((*cp == '\n') || strchr("dDrR", *cp)) { + reset = 1; + break; + } + received++; + + linefeed = strchr(cp, '\n'); + if (!linefeed) { + rejected++; + break; + } + + if (!isdigit(*cp)) { + rejected++; + cp = linefeed + 1; + continue; + } + + /* + * Do not replace with kstrtoul: + * here we need temp to be updated + */ + index = simple_strtoul(cp, &temp, 10); + if (index > 255) { + rejected++; + cp = linefeed + 1; + continue; + } + + while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) + temp++; + + desc_length = linefeed - temp; + if (desc_length > MAX_DESC_LEN) { + rejected++; + cp = linefeed + 1; + continue; + } + if (do_characters) { + desc = kmalloc(desc_length + 1, GFP_ATOMIC); + if (!desc) { + retval = -ENOMEM; + reset = 1; /* just reset on error. */ + break; + } + outptr = desc; + } else { + outptr = keyword; + } + + for (i = 0; i < desc_length; i++) + outptr[i] = temp[i]; + outptr[desc_length] = '\0'; + + if (do_characters) { + if (spk_characters[index] != spk_default_chars[index]) + kfree(spk_characters[index]); + spk_characters[index] = desc; + used++; + } else { + charclass = spk_chartab_get_value(keyword); + if (charclass == 0) { + rejected++; + cp = linefeed + 1; + continue; + } + if (charclass != spk_chartab[index]) { + spk_chartab[index] = charclass; + used++; + } + } + cp = linefeed + 1; + } + + if (reset) { + if (do_characters) + spk_reset_default_chars(); + else + spk_reset_default_chartab(); + } + + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + report_char_chartab_status(reset, received, used, rejected, + do_characters); + return retval; +} + +/* + * This is called when a user reads the keymap parameter. + */ +static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *cp = buf; + int i; + int n; + int num_keys; + int nstates; + u_char *cp1; + u_char ch; + unsigned long flags; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + cp1 = spk_key_buf + SHIFT_TBL_SIZE; + num_keys = (int)(*cp1); + nstates = (int)cp1[1]; + cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates); + cp1 += 2; /* now pointing at shift states */ + /* dump num_keys+1 as first row is shift states + flags, + * each subsequent row is key + states + */ + for (n = 0; n <= num_keys; n++) { + for (i = 0; i <= nstates; i++) { + ch = *cp1++; + cp += sprintf(cp, "%d,", (int)ch); + *cp++ = (i < nstates) ? SPACE : '\n'; + } + } + cp += sprintf(cp, "0, %d\n", KEY_MAP_VER); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return (int)(cp - buf); +} + +/* + * This is called when a user changes the keymap parameter. + */ +static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int i; + ssize_t ret = count; + char *in_buff = NULL; + char *cp; + u_char *cp1; + unsigned long flags; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + in_buff = kmemdup(buf, count + 1, GFP_ATOMIC); + if (!in_buff) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return -ENOMEM; + } + if (strchr("dDrR", *in_buff)) { + spk_set_key_info(spk_key_defaults, spk_key_buf); + pr_info("keymap set to default values\n"); + kfree(in_buff); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return count; + } + if (in_buff[count - 1] == '\n') + in_buff[count - 1] = '\0'; + cp = in_buff; + cp1 = (u_char *)in_buff; + for (i = 0; i < 3; i++) { + cp = spk_s2uchar(cp, cp1); + cp1++; + } + i = (int)cp1[-2] + 1; + i *= (int)cp1[-1] + 1; + i += 2; /* 0 and last map ver */ + if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 || + i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) { + pr_warn("i %d %d %d %d\n", i, + (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); + kfree(in_buff); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return -EINVAL; + } + while (--i >= 0) { + cp = spk_s2uchar(cp, cp1); + cp1++; + if (!(*cp)) + break; + } + if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) { + ret = -EINVAL; + pr_warn("end %d %d %d %d\n", i, + (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); + } else { + if (spk_set_key_info(in_buff, spk_key_buf)) { + spk_set_key_info(spk_key_defaults, spk_key_buf); + ret = -EINVAL; + pr_warn("set key failed\n"); + } + } + kfree(in_buff); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return ret; +} + +/* + * This is called when a user changes the value of the silent parameter. + */ +static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int len; + struct vc_data *vc = vc_cons[fg_console].d; + char ch = 0; + char shut; + unsigned long flags; + + len = strlen(buf); + if (len > 0 && len < 3) { + ch = buf[0]; + if (ch == '\n') + ch = '0'; + } + if (ch < '0' || ch > '7') { + pr_warn("silent value '%c' not in range (0,7)\n", ch); + return -EINVAL; + } + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (ch & 2) { + shut = 1; + spk_do_flush(); + } else { + shut = 0; + } + if (ch & 4) + shut |= 0x40; + if (ch & 1) + spk_shut_up |= shut; + else + spk_shut_up &= ~shut; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return count; +} + +/* + * This is called when a user reads the synth setting. + */ +static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int rv; + + if (!synth) + rv = sprintf(buf, "%s\n", "none"); + else + rv = sprintf(buf, "%s\n", synth->name); + return rv; +} + +/* + * This is called when a user requests to change synthesizers. + */ +static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int len; + char new_synth_name[10]; + + len = strlen(buf); + if (len < 2 || len > 9) + return -EINVAL; + memcpy(new_synth_name, buf, len); + if (new_synth_name[len - 1] == '\n') + len--; + new_synth_name[len] = '\0'; + spk_strlwr(new_synth_name); + if (synth && !strcmp(new_synth_name, synth->name)) { + pr_warn("%s already in use\n", new_synth_name); + } else if (synth_init(new_synth_name) != 0) { + pr_warn("failed to init synth %s\n", new_synth_name); + return -ENODEV; + } + return count; +} + +/* + * This is called when text is sent to the synth via the synth_direct file. + */ +static ssize_t synth_direct_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + u_char tmp[256]; + int len; + int bytes; + const char *ptr = buf; + unsigned long flags; + + if (!synth) + return -EPERM; + + len = strlen(buf); + spin_lock_irqsave(&speakup_info.spinlock, flags); + while (len > 0) { + bytes = min_t(size_t, len, 250); + strncpy(tmp, ptr, bytes); + tmp[bytes] = '\0'; + string_unescape_any_inplace(tmp); + synth_printf("%s", tmp); + ptr += bytes; + len -= bytes; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return count; +} + +/* + * This function is called when a user reads the version. + */ +static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *cp; + + cp = buf; + cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION); + if (synth) + cp += sprintf(cp, "%s synthesizer driver version %s\n", + synth->name, synth->version); + return cp - buf; +} + +/* + * This is called when a user reads the punctuation settings. + */ +static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int i; + char *cp = buf; + struct st_var_header *p_header; + struct punc_var_t *var; + struct st_bits_data *pb; + short mask; + unsigned long flags; + + p_header = spk_var_header_by_name(attr->attr.name); + if (!p_header) { + pr_warn("p_header is null, attr->attr.name is %s\n", + attr->attr.name); + return -EINVAL; + } + + var = spk_get_punc_var(p_header->var_id); + if (!var) { + pr_warn("var is null, p_header->var_id is %i\n", + p_header->var_id); + return -EINVAL; + } + + spin_lock_irqsave(&speakup_info.spinlock, flags); + pb = (struct st_bits_data *)&spk_punc_info[var->value]; + mask = pb->mask; + for (i = 33; i < 128; i++) { + if (!(spk_chartab[i] & mask)) + continue; + *cp++ = (char)i; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return cp - buf; +} + +/* + * This is called when a user changes the punctuation settings. + */ +static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int x; + struct st_var_header *p_header; + struct punc_var_t *var; + char punc_buf[100]; + unsigned long flags; + + x = strlen(buf); + if (x < 1 || x > 99) + return -EINVAL; + + p_header = spk_var_header_by_name(attr->attr.name); + if (!p_header) { + pr_warn("p_header is null, attr->attr.name is %s\n", + attr->attr.name); + return -EINVAL; + } + + var = spk_get_punc_var(p_header->var_id); + if (!var) { + pr_warn("var is null, p_header->var_id is %i\n", + p_header->var_id); + return -EINVAL; + } + + memcpy(punc_buf, buf, x); + + while (x && punc_buf[x - 1] == '\n') + x--; + punc_buf[x] = '\0'; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + + if (*punc_buf == 'd' || *punc_buf == 'r') + x = spk_set_mask_bits(NULL, var->value, 3); + else + x = spk_set_mask_bits(punc_buf, var->value, 3); + + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return count; +} + +/* + * This function is called when a user reads one of the variable parameters. + */ +ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int rv = 0; + struct st_var_header *param; + struct var_t *var; + char *cp1; + char *cp; + char ch; + unsigned long flags; + + param = spk_var_header_by_name(attr->attr.name); + if (!param) + return -EINVAL; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + var = (struct var_t *)param->data; + switch (param->var_type) { + case VAR_NUM: + case VAR_TIME: + if (var) + rv = sprintf(buf, "%i\n", var->u.n.value); + else + rv = sprintf(buf, "0\n"); + break; + case VAR_STRING: + if (var) { + cp1 = buf; + *cp1++ = '"'; + for (cp = (char *)param->p_val; (ch = *cp); cp++) { + if (ch >= ' ' && ch < '~') + *cp1++ = ch; + else + cp1 += sprintf(cp1, "\\x%02x", ch); + } + *cp1++ = '"'; + *cp1++ = '\n'; + *cp1 = '\0'; + rv = cp1 - buf; + } else { + rv = sprintf(buf, "\"\"\n"); + } + break; + default: + rv = sprintf(buf, "Bad parameter %s, type %i\n", + param->name, param->var_type); + break; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return rv; +} +EXPORT_SYMBOL_GPL(spk_var_show); + +/* + * Used to reset either default_pitch or default_vol. + */ +static inline void spk_reset_default_value(char *header_name, + int *synth_default_value, int idx) +{ + struct st_var_header *param; + + if (synth && synth_default_value) { + param = spk_var_header_by_name(header_name); + if (param) { + spk_set_num_var(synth_default_value[idx], + param, E_NEW_DEFAULT); + spk_set_num_var(0, param, E_DEFAULT); + pr_info("%s reset to default value\n", param->name); + } + } +} + +/* + * This function is called when a user echos a value to one of the + * variable parameters. + */ +ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct st_var_header *param; + int ret; + int len; + char *cp; + struct var_t *var_data; + long value; + unsigned long flags; + + param = spk_var_header_by_name(attr->attr.name); + if (!param) + return -EINVAL; + if (!param->data) + return 0; + ret = 0; + cp = (char *)buf; + string_unescape_any_inplace(cp); + + spin_lock_irqsave(&speakup_info.spinlock, flags); + switch (param->var_type) { + case VAR_NUM: + case VAR_TIME: + if (*cp == 'd' || *cp == 'r' || *cp == '\0') + len = E_DEFAULT; + else if (*cp == '+' || *cp == '-') + len = E_INC; + else + len = E_SET; + if (kstrtol(cp, 10, &value) == 0) + ret = spk_set_num_var(value, param, len); + else + pr_warn("overflow or parsing error has occurred"); + if (ret == -ERANGE) { + var_data = param->data; + pr_warn("value for %s out of range, expect %d to %d\n", + param->name, + var_data->u.n.low, var_data->u.n.high); + } + + /* + * If voice was just changed, we might need to reset our default + * pitch and volume. + */ + if (param->var_id == VOICE && synth && + (ret == 0 || ret == -ERESTART)) { + var_data = param->data; + value = var_data->u.n.value; + spk_reset_default_value("pitch", synth->default_pitch, + value); + spk_reset_default_value("vol", synth->default_vol, + value); + } + break; + case VAR_STRING: + len = strlen(cp); + if ((len >= 1) && (cp[len - 1] == '\n')) + --len; + if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) { + ++cp; + len -= 2; + } + cp[len] = '\0'; + ret = spk_set_string_var(cp, param, len); + if (ret == -E2BIG) + pr_warn("value too long for %s\n", + param->name); + break; + default: + pr_warn("%s unknown type %d\n", + param->name, (int)param->var_type); + break; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + + if (ret == -ERESTART) + pr_info("%s reset to default value\n", param->name); + return count; +} +EXPORT_SYMBOL_GPL(spk_var_store); + +/* + * Functions for reading and writing lists of i18n messages. Incomplete. + */ + +static ssize_t message_show_helper(char *buf, enum msg_index_t first, + enum msg_index_t last) +{ + size_t bufsize = PAGE_SIZE; + char *buf_pointer = buf; + int printed; + enum msg_index_t cursor; + int index = 0; + *buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */ + + for (cursor = first; cursor <= last; cursor++, index++) { + if (bufsize <= 1) + break; + printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n", + index, spk_msg_get(cursor)); + buf_pointer += printed; + bufsize -= printed; + } + + return buf_pointer - buf; +} + +static void report_msg_status(int reset, int received, int used, + int rejected, char *groupname) +{ + int len; + char buf[160]; + + if (reset) { + pr_info("i18n messages from group %s reset to defaults\n", + groupname); + } else if (received) { + len = snprintf(buf, sizeof(buf), + " updated %d of %d i18n messages from group %s\n", + used, received, groupname); + if (rejected) + snprintf(buf + (len - 1), sizeof(buf) - (len - 1), + " with %d reject%s\n", + rejected, rejected > 1 ? "s" : ""); + pr_info("%s", buf); + } +} + +static ssize_t message_store_helper(const char *buf, size_t count, + struct msg_group_t *group) +{ + char *cp = (char *)buf; + char *end = cp + count; + char *linefeed = NULL; + char *temp = NULL; + ssize_t msg_stored = 0; + ssize_t retval = count; + size_t desc_length = 0; + unsigned long index = 0; + int received = 0; + int used = 0; + int rejected = 0; + int reset = 0; + enum msg_index_t firstmessage = group->start; + enum msg_index_t lastmessage = group->end; + enum msg_index_t curmessage; + + while (cp < end) { + while ((cp < end) && (*cp == ' ' || *cp == '\t')) + cp++; + + if (cp == end) + break; + if (strchr("dDrR", *cp)) { + reset = 1; + break; + } + received++; + + linefeed = strchr(cp, '\n'); + if (!linefeed) { + rejected++; + break; + } + + if (!isdigit(*cp)) { + rejected++; + cp = linefeed + 1; + continue; + } + + /* + * Do not replace with kstrtoul: + * here we need temp to be updated + */ + index = simple_strtoul(cp, &temp, 10); + + while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) + temp++; + + desc_length = linefeed - temp; + curmessage = firstmessage + index; + + /* + * Note the check (curmessage < firstmessage). It is not + * redundant. Suppose that the user gave us an index + * equal to ULONG_MAX - 1. If firstmessage > 1, then + * firstmessage + index < firstmessage! + */ + + if ((curmessage < firstmessage) || (curmessage > lastmessage)) { + rejected++; + cp = linefeed + 1; + continue; + } + + msg_stored = spk_msg_set(curmessage, temp, desc_length); + if (msg_stored < 0) { + retval = msg_stored; + if (msg_stored == -ENOMEM) + reset = 1; + break; + } + + used++; + + cp = linefeed + 1; + } + + if (reset) + spk_reset_msg_group(group); + + report_msg_status(reset, received, used, rejected, group->name); + return retval; +} + +static ssize_t message_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + ssize_t retval = 0; + struct msg_group_t *group = spk_find_msg_group(attr->attr.name); + unsigned long flags; + + if (WARN_ON(!group)) + return -EINVAL; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + retval = message_show_helper(buf, group->start, group->end); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return retval; +} + +static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct msg_group_t *group = spk_find_msg_group(attr->attr.name); + + if (WARN_ON(!group)) + return -EINVAL; + + return message_store_helper(buf, count, group); +} + +/* + * Declare the attributes. + */ +static struct kobj_attribute keymap_attribute = + __ATTR_RW(keymap); +static struct kobj_attribute silent_attribute = + __ATTR_WO(silent); +static struct kobj_attribute synth_attribute = + __ATTR_RW(synth); +static struct kobj_attribute synth_direct_attribute = + __ATTR_WO(synth_direct); +static struct kobj_attribute version_attribute = + __ATTR_RO(version); + +static struct kobj_attribute delimiters_attribute = + __ATTR(delimiters, 0644, punc_show, punc_store); +static struct kobj_attribute ex_num_attribute = + __ATTR(ex_num, 0644, punc_show, punc_store); +static struct kobj_attribute punc_all_attribute = + __ATTR(punc_all, 0644, punc_show, punc_store); +static struct kobj_attribute punc_most_attribute = + __ATTR(punc_most, 0644, punc_show, punc_store); +static struct kobj_attribute punc_some_attribute = + __ATTR(punc_some, 0644, punc_show, punc_store); +static struct kobj_attribute repeats_attribute = + __ATTR(repeats, 0644, punc_show, punc_store); + +static struct kobj_attribute attrib_bleep_attribute = + __ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute bell_pos_attribute = + __ATTR(bell_pos, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute bleep_time_attribute = + __ATTR(bleep_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute bleeps_attribute = + __ATTR(bleeps, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute cursor_time_attribute = + __ATTR(cursor_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute key_echo_attribute = + __ATTR(key_echo, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute no_interrupt_attribute = + __ATTR(no_interrupt, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punc_level_attribute = + __ATTR(punc_level, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute reading_punc_attribute = + __ATTR(reading_punc, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute say_control_attribute = + __ATTR(say_control, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute say_word_ctl_attribute = + __ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute spell_delay_attribute = + __ATTR(spell_delay, 0644, spk_var_show, spk_var_store); + +/* + * These attributes are i18n related. + */ +static struct kobj_attribute announcements_attribute = + __ATTR(announcements, 0644, message_show, message_store); +static struct kobj_attribute characters_attribute = + __ATTR(characters, 0644, chars_chartab_show, + chars_chartab_store); +static struct kobj_attribute chartab_attribute = + __ATTR(chartab, 0644, chars_chartab_show, + chars_chartab_store); +static struct kobj_attribute ctl_keys_attribute = + __ATTR(ctl_keys, 0644, message_show, message_store); +static struct kobj_attribute colors_attribute = + __ATTR(colors, 0644, message_show, message_store); +static struct kobj_attribute formatted_attribute = + __ATTR(formatted, 0644, message_show, message_store); +static struct kobj_attribute function_names_attribute = + __ATTR(function_names, 0644, message_show, message_store); +static struct kobj_attribute key_names_attribute = + __ATTR(key_names, 0644, message_show, message_store); +static struct kobj_attribute states_attribute = + __ATTR(states, 0644, message_show, message_store); + +/* + * Create groups of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *main_attrs[] = { + &keymap_attribute.attr, + &silent_attribute.attr, + &synth_attribute.attr, + &synth_direct_attribute.attr, + &version_attribute.attr, + &delimiters_attribute.attr, + &ex_num_attribute.attr, + &punc_all_attribute.attr, + &punc_most_attribute.attr, + &punc_some_attribute.attr, + &repeats_attribute.attr, + &attrib_bleep_attribute.attr, + &bell_pos_attribute.attr, + &bleep_time_attribute.attr, + &bleeps_attribute.attr, + &cursor_time_attribute.attr, + &key_echo_attribute.attr, + &no_interrupt_attribute.attr, + &punc_level_attribute.attr, + &reading_punc_attribute.attr, + &say_control_attribute.attr, + &say_word_ctl_attribute.attr, + &spell_delay_attribute.attr, + NULL, +}; + +static struct attribute *i18n_attrs[] = { + &announcements_attribute.attr, + &characters_attribute.attr, + &chartab_attribute.attr, + &ctl_keys_attribute.attr, + &colors_attribute.attr, + &formatted_attribute.attr, + &function_names_attribute.attr, + &key_names_attribute.attr, + &states_attribute.attr, + NULL, +}; + +/* + * An unnamed attribute group will put all of the attributes directly in + * the kobject directory. If we specify a name, a subdirectory will be + * created for the attributes with the directory being the name of the + * attribute group. + */ +static const struct attribute_group main_attr_group = { + .attrs = main_attrs, +}; + +static const struct attribute_group i18n_attr_group = { + .attrs = i18n_attrs, + .name = "i18n", +}; + +static struct kobject *accessibility_kobj; +struct kobject *speakup_kobj; + +int speakup_kobj_init(void) +{ + int retval; + + /* + * Create a simple kobject with the name of "accessibility", + * located under /sys/ + * + * As this is a simple directory, no uevent will be sent to + * userspace. That is why this function should not be used for + * any type of dynamic kobjects, where the name and number are + * not known ahead of time. + */ + accessibility_kobj = kobject_create_and_add("accessibility", NULL); + if (!accessibility_kobj) { + retval = -ENOMEM; + goto out; + } + + speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj); + if (!speakup_kobj) { + retval = -ENOMEM; + goto err_acc; + } + + /* Create the files associated with this kobject */ + retval = sysfs_create_group(speakup_kobj, &main_attr_group); + if (retval) + goto err_speakup; + + retval = sysfs_create_group(speakup_kobj, &i18n_attr_group); + if (retval) + goto err_group; + + goto out; + +err_group: + sysfs_remove_group(speakup_kobj, &main_attr_group); +err_speakup: + kobject_put(speakup_kobj); +err_acc: + kobject_put(accessibility_kobj); +out: + return retval; +} + +void speakup_kobj_exit(void) +{ + sysfs_remove_group(speakup_kobj, &i18n_attr_group); + sysfs_remove_group(speakup_kobj, &main_attr_group); + kobject_put(speakup_kobj); + kobject_put(accessibility_kobj); +} diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c new file mode 100644 index 000000000000..02471d932d71 --- /dev/null +++ b/drivers/accessibility/speakup/main.c @@ -0,0 +1,2460 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* speakup.c + * review functions for the speakup screen review package. + * originally written by: Kirk Reiser and Andy Berdan. + * + * extensively modified by David Borowski. + * + ** Copyright (C) 1998 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + */ + +#include +#include +#include +#include /* __get_free_page() and friends */ +#include +#include +#include +#include +#include +#include +#include /* for KT_SHIFT */ +#include /* for vc_kbd_* and friends */ +#include +#include + +/* speakup_*_selection */ +#include +#include +#include +#include +#include + +#include +#include + +#include /* copy_from|to|user() and others */ + +#include "spk_priv.h" +#include "speakup.h" + +#define MAX_DELAY msecs_to_jiffies(500) +#define MINECHOCHAR SPACE + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("Daniel Drake "); +MODULE_DESCRIPTION("Speakup console speech"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SPEAKUP_VERSION); + +char *synth_name; +module_param_named(synth, synth_name, charp, 0444); +module_param_named(quiet, spk_quiet_boot, bool, 0444); + +MODULE_PARM_DESC(synth, "Synth to start if speakup is built in."); +MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found."); + +special_func spk_special_handler; + +short spk_pitch_shift, synth_flags; +static u16 buf[256]; +int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10; +int spk_no_intr, spk_spell_delay; +int spk_key_echo, spk_say_word_ctl; +int spk_say_ctrl, spk_bell_pos; +short spk_punc_mask; +int spk_punc_level, spk_reading_punc; +char spk_str_caps_start[MAXVARLEN + 1] = "\0"; +char spk_str_caps_stop[MAXVARLEN + 1] = "\0"; +char spk_str_pause[MAXVARLEN + 1] = "\0"; +bool spk_paused; +const struct st_bits_data spk_punc_info[] = { + {"none", "", 0}, + {"some", "/$%&@", SOME}, + {"most", "$%&#()=+*/@^<>|\\", MOST}, + {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC}, + {"delimiters", "", B_WDLM}, + {"repeats", "()", CH_RPT}, + {"extended numeric", "", B_EXNUM}, + {"symbols", "", B_SYM}, + {NULL, NULL} +}; + +static char mark_cut_flag; +#define MAX_KEY 160 +static u_char *spk_shift_table; +u_char *spk_our_keys[MAX_KEY]; +u_char spk_key_buf[600]; +const u_char spk_key_defaults[] = { +#include "speakupmap.h" +}; + +/* Speakup Cursor Track Variables */ +static int cursor_track = 1, prev_cursor_track = 1; + +/* cursor track modes, must be ordered same as cursor_msgs */ +enum { + CT_Off = 0, + CT_On, + CT_Highlight, + CT_Window, + CT_Max +}; + +#define read_all_mode CT_Max + +static struct tty_struct *tty; + +static void spkup_write(const u16 *in_buf, int count); + +static char *phonetic[] = { + "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", + "india", "juliett", "keelo", "leema", "mike", "november", "oscar", + "papa", + "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey", + "x ray", "yankee", "zulu" +}; + +/* array of 256 char pointers (one for each character description) + * initialized to default_chars and user selectable via + * /proc/speakup/characters + */ +char *spk_characters[256]; + +char *spk_default_chars[256] = { +/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g", +/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o", +/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w", +/*024*/ "^x", "^y", "^z", "control", "control", "control", "control", + "control", +/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and", + "tick", +/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash", + "dot", + "slash", +/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", +/*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at", +/*065*/ "EIGH", "B", "C", "D", "E", "F", "G", +/*072*/ "H", "I", "J", "K", "L", "M", "N", "O", +/*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X", +/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket", + "caret", + "line", +/*096*/ "accent", "a", "b", "c", "d", "e", "f", "g", +/*104*/ "h", "i", "j", "k", "l", "m", "n", "o", +/*112*/ "p", "q", "r", "s", "t", "u", "v", "w", +/*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh", +/*127*/ "del", "control", "control", "control", "control", "control", + "control", "control", "control", "control", "control", +/*138*/ "control", "control", "control", "control", "control", + "control", "control", "control", "control", "control", + "control", "control", +/*150*/ "control", "control", "control", "control", "control", + "control", "control", "control", "control", "control", +/*160*/ "nbsp", "inverted bang", +/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section", +/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle", +/*172*/ "not", "soft hyphen", "registered", "macron", +/*176*/ "degrees", "plus or minus", "super two", "super three", +/*180*/ "acute accent", "micro", "pilcrow", "middle dot", +/*184*/ "cedilla", "super one", "male ordinal", "double right angle", +/*188*/ "one quarter", "one half", "three quarters", + "inverted question", +/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT", + "A RING", +/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX", + "E OOMLAUT", +/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH", + "N TILDE", +/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT", +/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE", + "U CIRCUMFLEX", +/*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave", +/*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring", +/*230*/ "ae", "c cidella", "e grave", "e acute", +/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute", + "i circumflex", +/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute", + "o circumflex", +/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave", + "u acute", +/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut" +}; + +/* array of 256 u_short (one for each character) + * initialized to default_chartab and user selectable via + * /sys/module/speakup/parameters/chartab + */ +u_short spk_chartab[256]; + +static u_short default_chartab[256] = { + B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */ + B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */ + B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */ + B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */ + WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */ + PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */ + NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */ + NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */ + PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */ + A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */ + A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */ + A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */ + PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */ + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */ + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */ + ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */ + B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */ + B_SYM, /* 135 */ + B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */ + B_CAPSYM, /* 143 */ + B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */ + B_SYM, /* 151 */ + B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */ + B_SYM, /* 159 */ + WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */ + B_SYM, /* 167 */ + B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */ + B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */ + B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */ + A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */ + A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */ + A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */ + A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */ + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */ + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */ + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */ + ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */ +}; + +struct task_struct *speakup_task; +struct bleep spk_unprocessed_sound; +static int spk_keydown; +static u16 spk_lastkey; +static u_char spk_close_press, keymap_flags; +static u_char last_keycode, this_speakup_key; +static u_long last_spk_jiffy; + +struct st_spk_t *speakup_console[MAX_NR_CONSOLES]; + +DEFINE_MUTEX(spk_mutex); + +static int keyboard_notifier_call(struct notifier_block *, + unsigned long code, void *param); + +static struct notifier_block keyboard_notifier_block = { + .notifier_call = keyboard_notifier_call, +}; + +static int vt_notifier_call(struct notifier_block *, + unsigned long code, void *param); + +static struct notifier_block vt_notifier_block = { + .notifier_call = vt_notifier_call, +}; + +static unsigned char get_attributes(struct vc_data *vc, u16 *pos) +{ + pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, 1); + return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8; +} + +static void speakup_date(struct vc_data *vc) +{ + spk_x = spk_cx = vc->vc_x; + spk_y = spk_cy = vc->vc_y; + spk_pos = spk_cp = vc->vc_pos; + spk_old_attr = spk_attr; + spk_attr = get_attributes(vc, (u_short *)spk_pos); +} + +static void bleep(u_short val) +{ + static const short vals[] = { + 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659 + }; + short freq; + int time = spk_bleep_time; + + freq = vals[val % 12]; + if (val > 11) + freq *= (1 << (val / 12)); + spk_unprocessed_sound.freq = freq; + spk_unprocessed_sound.jiffies = msecs_to_jiffies(time); + spk_unprocessed_sound.active = 1; + /* We can only have 1 active sound at a time. */ +} + +static void speakup_shut_up(struct vc_data *vc) +{ + if (spk_killed) + return; + spk_shut_up |= 0x01; + spk_parked &= 0xfe; + speakup_date(vc); + if (synth) + spk_do_flush(); +} + +static void speech_kill(struct vc_data *vc) +{ + char val = synth->is_alive(synth); + + if (val == 0) + return; + + /* re-enables synth, if disabled */ + if (val == 2 || spk_killed) { + /* dead */ + spk_shut_up &= ~0x40; + synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE)); + } else { + synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP)); + spk_shut_up |= 0x40; + } +} + +static void speakup_off(struct vc_data *vc) +{ + if (spk_shut_up & 0x80) { + spk_shut_up &= 0x7f; + synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER)); + } else { + spk_shut_up |= 0x80; + synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF)); + } + speakup_date(vc); +} + +static void speakup_parked(struct vc_data *vc) +{ + if (spk_parked & 0x80) { + spk_parked = 0; + synth_printf("%s\n", spk_msg_get(MSG_UNPARKED)); + } else { + spk_parked |= 0x80; + synth_printf("%s\n", spk_msg_get(MSG_PARKED)); + } +} + +static void speakup_cut(struct vc_data *vc) +{ + static const char err_buf[] = "set selection failed"; + int ret; + + if (!mark_cut_flag) { + mark_cut_flag = 1; + spk_xs = (u_short)spk_x; + spk_ys = (u_short)spk_y; + spk_sel_cons = vc; + synth_printf("%s\n", spk_msg_get(MSG_MARK)); + return; + } + spk_xe = (u_short)spk_x; + spk_ye = (u_short)spk_y; + mark_cut_flag = 0; + synth_printf("%s\n", spk_msg_get(MSG_CUT)); + + speakup_clear_selection(); + ret = speakup_set_selection(tty); + + switch (ret) { + case 0: + break; /* no error */ + case -EFAULT: + pr_warn("%sEFAULT\n", err_buf); + break; + case -EINVAL: + pr_warn("%sEINVAL\n", err_buf); + break; + case -ENOMEM: + pr_warn("%sENOMEM\n", err_buf); + break; + } +} + +static void speakup_paste(struct vc_data *vc) +{ + if (mark_cut_flag) { + mark_cut_flag = 0; + synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED)); + } else { + synth_printf("%s\n", spk_msg_get(MSG_PASTE)); + speakup_paste_selection(tty); + } +} + +static void say_attributes(struct vc_data *vc) +{ + int fg = spk_attr & 0x0f; + int bg = spk_attr >> 4; + + if (fg > 8) { + synth_printf("%s ", spk_msg_get(MSG_BRIGHT)); + fg -= 8; + } + synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg)); + if (bg > 7) { + synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING)); + bg -= 8; + } else { + synth_printf(" %s ", spk_msg_get(MSG_ON)); + } + synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg)); +} + +enum { + edge_top = 1, + edge_bottom, + edge_left, + edge_right, + edge_quiet +}; + +static void announce_edge(struct vc_data *vc, int msg_id) +{ + if (spk_bleeps & 1) + bleep(spk_y); + if ((spk_bleeps & 2) && (msg_id < edge_quiet)) + synth_printf("%s\n", + spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1)); +} + +static void speak_char(u16 ch) +{ + char *cp; + struct var_t *direct = spk_get_var(DIRECT); + + if (ch >= 0x100 || (direct && direct->u.n.value)) { + if (ch < 0x100 && IS_CHAR(ch, B_CAP)) { + spk_pitch_shift++; + synth_printf("%s", spk_str_caps_start); + } + synth_putwc_s(ch); + if (ch < 0x100 && IS_CHAR(ch, B_CAP)) + synth_printf("%s", spk_str_caps_stop); + return; + } + + cp = spk_characters[ch]; + if (!cp) { + pr_info("%s: cp == NULL!\n", __func__); + return; + } + if (IS_CHAR(ch, B_CAP)) { + spk_pitch_shift++; + synth_printf("%s %s %s", + spk_str_caps_start, cp, spk_str_caps_stop); + } else { + if (*cp == '^') { + cp++; + synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp); + } else { + synth_printf(" %s ", cp); + } + } +} + +static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs) +{ + u16 ch = ' '; + + if (vc && pos) { + u16 w; + u16 c; + + pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, 1); + w = scr_readw(pos); + c = w & 0xff; + + if (w & vc->vc_hi_font_mask) { + w &= ~vc->vc_hi_font_mask; + c |= 0x100; + } + + ch = inverse_translate(vc, c, 1); + *attribs = (w & 0xff00) >> 8; + } + return ch; +} + +static void say_char(struct vc_data *vc) +{ + u16 ch; + + spk_old_attr = spk_attr; + ch = get_char(vc, (u_short *)spk_pos, &spk_attr); + if (spk_attr != spk_old_attr) { + if (spk_attrib_bleep & 1) + bleep(spk_y); + if (spk_attrib_bleep & 2) + say_attributes(vc); + } + speak_char(ch); +} + +static void say_phonetic_char(struct vc_data *vc) +{ + u16 ch; + + spk_old_attr = spk_attr; + ch = get_char(vc, (u_short *)spk_pos, &spk_attr); + if (ch <= 0x7f && isalpha(ch)) { + ch &= 0x1f; + synth_printf("%s\n", phonetic[--ch]); + } else { + if (ch < 0x100 && IS_CHAR(ch, B_NUM)) + synth_printf("%s ", spk_msg_get(MSG_NUMBER)); + speak_char(ch); + } +} + +static void say_prev_char(struct vc_data *vc) +{ + spk_parked |= 0x01; + if (spk_x == 0) { + announce_edge(vc, edge_left); + return; + } + spk_x--; + spk_pos -= 2; + say_char(vc); +} + +static void say_next_char(struct vc_data *vc) +{ + spk_parked |= 0x01; + if (spk_x == vc->vc_cols - 1) { + announce_edge(vc, edge_right); + return; + } + spk_x++; + spk_pos += 2; + say_char(vc); +} + +/* get_word - will first check to see if the character under the + * reading cursor is a space and if spk_say_word_ctl is true it will + * return the word space. If spk_say_word_ctl is not set it will check to + * see if there is a word starting on the next position to the right + * and return that word if it exists. If it does not exist it will + * move left to the beginning of any previous word on the line or the + * beginning off the line whichever comes first.. + */ + +static u_long get_word(struct vc_data *vc) +{ + u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos; + u16 ch; + u16 attr_ch; + u_char temp; + + spk_old_attr = spk_attr; + ch = get_char(vc, (u_short *)tmp_pos, &temp); + +/* decided to take out the sayword if on a space (mis-information */ + if (spk_say_word_ctl && ch == SPACE) { + *buf = '\0'; + synth_printf("%s\n", spk_msg_get(MSG_SPACE)); + return 0; + } else if (tmpx < vc->vc_cols - 2 && + (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && + get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) { + tmp_pos += 2; + tmpx++; + } else { + while (tmpx > 0) { + ch = get_char(vc, (u_short *)tmp_pos - 1, &temp); + if ((ch == SPACE || ch == 0 || + (ch < 0x100 && IS_WDLM(ch))) && + get_char(vc, (u_short *)tmp_pos, &temp) > SPACE) + break; + tmp_pos -= 2; + tmpx--; + } + } + attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr); + buf[cnt++] = attr_ch; + while (tmpx < vc->vc_cols - 1) { + tmp_pos += 2; + tmpx++; + ch = get_char(vc, (u_short *)tmp_pos, &temp); + if (ch == SPACE || ch == 0 || + (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) && + ch > SPACE)) + break; + buf[cnt++] = ch; + } + buf[cnt] = '\0'; + return cnt; +} + +static void say_word(struct vc_data *vc) +{ + u_long cnt = get_word(vc); + u_short saved_punc_mask = spk_punc_mask; + + if (cnt == 0) + return; + spk_punc_mask = PUNC; + buf[cnt++] = SPACE; + spkup_write(buf, cnt); + spk_punc_mask = saved_punc_mask; +} + +static void say_prev_word(struct vc_data *vc) +{ + u_char temp; + u16 ch; + u_short edge_said = 0, last_state = 0, state = 0; + + spk_parked |= 0x01; + + if (spk_x == 0) { + if (spk_y == 0) { + announce_edge(vc, edge_top); + return; + } + spk_y--; + spk_x = vc->vc_cols; + edge_said = edge_quiet; + } + while (1) { + if (spk_x == 0) { + if (spk_y == 0) { + edge_said = edge_top; + break; + } + if (edge_said != edge_quiet) + edge_said = edge_left; + if (state > 0) + break; + spk_y--; + spk_x = vc->vc_cols - 1; + } else { + spk_x--; + } + spk_pos -= 2; + ch = get_char(vc, (u_short *)spk_pos, &temp); + if (ch == SPACE || ch == 0) + state = 0; + else if (ch < 0x100 && IS_WDLM(ch)) + state = 1; + else + state = 2; + if (state < last_state) { + spk_pos += 2; + spk_x++; + break; + } + last_state = state; + } + if (spk_x == 0 && edge_said == edge_quiet) + edge_said = edge_left; + if (edge_said > 0 && edge_said < edge_quiet) + announce_edge(vc, edge_said); + say_word(vc); +} + +static void say_next_word(struct vc_data *vc) +{ + u_char temp; + u16 ch; + u_short edge_said = 0, last_state = 2, state = 0; + + spk_parked |= 0x01; + if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) { + announce_edge(vc, edge_bottom); + return; + } + while (1) { + ch = get_char(vc, (u_short *)spk_pos, &temp); + if (ch == SPACE || ch == 0) + state = 0; + else if (ch < 0x100 && IS_WDLM(ch)) + state = 1; + else + state = 2; + if (state > last_state) + break; + if (spk_x >= vc->vc_cols - 1) { + if (spk_y == vc->vc_rows - 1) { + edge_said = edge_bottom; + break; + } + state = 0; + spk_y++; + spk_x = 0; + edge_said = edge_right; + } else { + spk_x++; + } + spk_pos += 2; + last_state = state; + } + if (edge_said > 0) + announce_edge(vc, edge_said); + say_word(vc); +} + +static void spell_word(struct vc_data *vc) +{ + static char const *delay_str[] = { "", ",", ".", ". .", ". . ." }; + u16 *cp = buf; + char *cp1; + char *str_cap = spk_str_caps_stop; + char *last_cap = spk_str_caps_stop; + struct var_t *direct = spk_get_var(DIRECT); + u16 ch; + + if (!get_word(vc)) + return; + while ((ch = *cp)) { + if (cp != buf) + synth_printf(" %s ", delay_str[spk_spell_delay]); + /* FIXME: Non-latin1 considered as lower case */ + if (ch < 0x100 && IS_CHAR(ch, B_CAP)) { + str_cap = spk_str_caps_start; + if (*spk_str_caps_stop) + spk_pitch_shift++; + else /* synth has no pitch */ + last_cap = spk_str_caps_stop; + } else { + str_cap = spk_str_caps_stop; + } + if (str_cap != last_cap) { + synth_printf("%s", str_cap); + last_cap = str_cap; + } + if (ch >= 0x100 || (direct && direct->u.n.value)) { + synth_putwc_s(ch); + } else if (this_speakup_key == SPELL_PHONETIC && + ch <= 0x7f && isalpha(ch)) { + ch &= 0x1f; + cp1 = phonetic[--ch]; + synth_printf("%s", cp1); + } else { + cp1 = spk_characters[ch]; + if (*cp1 == '^') { + synth_printf("%s", spk_msg_get(MSG_CTRL)); + cp1++; + } + synth_printf("%s", cp1); + } + cp++; + } + if (str_cap != spk_str_caps_stop) + synth_printf("%s", spk_str_caps_stop); +} + +static int get_line(struct vc_data *vc) +{ + u_long tmp = spk_pos - (spk_x * 2); + int i = 0; + u_char tmp2; + + spk_old_attr = spk_attr; + spk_attr = get_attributes(vc, (u_short *)spk_pos); + for (i = 0; i < vc->vc_cols; i++) { + buf[i] = get_char(vc, (u_short *)tmp, &tmp2); + tmp += 2; + } + for (--i; i >= 0; i--) + if (buf[i] != SPACE) + break; + return ++i; +} + +static void say_line(struct vc_data *vc) +{ + int i = get_line(vc); + u16 *cp; + u_short saved_punc_mask = spk_punc_mask; + + if (i == 0) { + synth_printf("%s\n", spk_msg_get(MSG_BLANK)); + return; + } + buf[i++] = '\n'; + if (this_speakup_key == SAY_LINE_INDENT) { + cp = buf; + while (*cp == SPACE) + cp++; + synth_printf("%zd, ", (cp - buf) + 1); + } + spk_punc_mask = spk_punc_masks[spk_reading_punc]; + spkup_write(buf, i); + spk_punc_mask = saved_punc_mask; +} + +static void say_prev_line(struct vc_data *vc) +{ + spk_parked |= 0x01; + if (spk_y == 0) { + announce_edge(vc, edge_top); + return; + } + spk_y--; + spk_pos -= vc->vc_size_row; + say_line(vc); +} + +static void say_next_line(struct vc_data *vc) +{ + spk_parked |= 0x01; + if (spk_y == vc->vc_rows - 1) { + announce_edge(vc, edge_bottom); + return; + } + spk_y++; + spk_pos += vc->vc_size_row; + say_line(vc); +} + +static int say_from_to(struct vc_data *vc, u_long from, u_long to, + int read_punc) +{ + int i = 0; + u_char tmp; + u_short saved_punc_mask = spk_punc_mask; + + spk_old_attr = spk_attr; + spk_attr = get_attributes(vc, (u_short *)from); + while (from < to) { + buf[i++] = get_char(vc, (u_short *)from, &tmp); + from += 2; + if (i >= vc->vc_size_row) + break; + } + for (--i; i >= 0; i--) + if (buf[i] != SPACE) + break; + buf[++i] = SPACE; + buf[++i] = '\0'; + if (i < 1) + return i; + if (read_punc) + spk_punc_mask = spk_punc_info[spk_reading_punc].mask; + spkup_write(buf, i); + if (read_punc) + spk_punc_mask = saved_punc_mask; + return i - 1; +} + +static void say_line_from_to(struct vc_data *vc, u_long from, u_long to, + int read_punc) +{ + u_long start = vc->vc_origin + (spk_y * vc->vc_size_row); + u_long end = start + (to * 2); + + start += from * 2; + if (say_from_to(vc, start, end, read_punc) <= 0) + if (cursor_track != read_all_mode) + synth_printf("%s\n", spk_msg_get(MSG_BLANK)); +} + +/* Sentence Reading Commands */ + +static int currsentence; +static int numsentences[2]; +static u16 *sentbufend[2]; +static u16 *sentmarks[2][10]; +static int currbuf; +static int bn; +static u16 sentbuf[2][256]; + +static int say_sentence_num(int num, int prev) +{ + bn = currbuf; + currsentence = num + 1; + if (prev && --bn == -1) + bn = 1; + + if (num > numsentences[bn]) + return 0; + + spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]); + return 1; +} + +static int get_sentence_buf(struct vc_data *vc, int read_punc) +{ + u_long start, end; + int i, bn; + u_char tmp; + + currbuf++; + if (currbuf == 2) + currbuf = 0; + bn = currbuf; + start = vc->vc_origin + ((spk_y) * vc->vc_size_row); + end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2; + + numsentences[bn] = 0; + sentmarks[bn][0] = &sentbuf[bn][0]; + i = 0; + spk_old_attr = spk_attr; + spk_attr = get_attributes(vc, (u_short *)start); + + while (start < end) { + sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp); + if (i > 0) { + if (sentbuf[bn][i] == SPACE && + sentbuf[bn][i - 1] == '.' && + numsentences[bn] < 9) { + /* Sentence Marker */ + numsentences[bn]++; + sentmarks[bn][numsentences[bn]] = + &sentbuf[bn][i]; + } + } + i++; + start += 2; + if (i >= vc->vc_size_row) + break; + } + + for (--i; i >= 0; i--) + if (sentbuf[bn][i] != SPACE) + break; + + if (i < 1) + return -1; + + sentbuf[bn][++i] = SPACE; + sentbuf[bn][++i] = '\0'; + + sentbufend[bn] = &sentbuf[bn][i]; + return numsentences[bn]; +} + +static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to) +{ + u_long start = vc->vc_origin, end; + + if (from > 0) + start += from * vc->vc_size_row; + if (to > vc->vc_rows) + to = vc->vc_rows; + end = vc->vc_origin + (to * vc->vc_size_row); + for (from = start; from < end; from = to) { + to = from + vc->vc_size_row; + say_from_to(vc, from, to, 1); + } +} + +static void say_screen(struct vc_data *vc) +{ + say_screen_from_to(vc, 0, vc->vc_rows); +} + +static void speakup_win_say(struct vc_data *vc) +{ + u_long start, end, from, to; + + if (win_start < 2) { + synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW)); + return; + } + start = vc->vc_origin + (win_top * vc->vc_size_row); + end = vc->vc_origin + (win_bottom * vc->vc_size_row); + while (start <= end) { + from = start + (win_left * 2); + to = start + (win_right * 2); + say_from_to(vc, from, to, 1); + start += vc->vc_size_row; + } +} + +static void top_edge(struct vc_data *vc) +{ + spk_parked |= 0x01; + spk_pos = vc->vc_origin + 2 * spk_x; + spk_y = 0; + say_line(vc); +} + +static void bottom_edge(struct vc_data *vc) +{ + spk_parked |= 0x01; + spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row; + spk_y = vc->vc_rows - 1; + say_line(vc); +} + +static void left_edge(struct vc_data *vc) +{ + spk_parked |= 0x01; + spk_pos -= spk_x * 2; + spk_x = 0; + say_char(vc); +} + +static void right_edge(struct vc_data *vc) +{ + spk_parked |= 0x01; + spk_pos += (vc->vc_cols - spk_x - 1) * 2; + spk_x = vc->vc_cols - 1; + say_char(vc); +} + +static void say_first_char(struct vc_data *vc) +{ + int i, len = get_line(vc); + u16 ch; + + spk_parked |= 0x01; + if (len == 0) { + synth_printf("%s\n", spk_msg_get(MSG_BLANK)); + return; + } + for (i = 0; i < len; i++) + if (buf[i] != SPACE) + break; + ch = buf[i]; + spk_pos -= (spk_x - i) * 2; + spk_x = i; + synth_printf("%d, ", ++i); + speak_char(ch); +} + +static void say_last_char(struct vc_data *vc) +{ + int len = get_line(vc); + u16 ch; + + spk_parked |= 0x01; + if (len == 0) { + synth_printf("%s\n", spk_msg_get(MSG_BLANK)); + return; + } + ch = buf[--len]; + spk_pos -= (spk_x - len) * 2; + spk_x = len; + synth_printf("%d, ", ++len); + speak_char(ch); +} + +static void say_position(struct vc_data *vc) +{ + synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1, + vc->vc_num + 1); + synth_printf("\n"); +} + +/* Added by brianb */ +static void say_char_num(struct vc_data *vc) +{ + u_char tmp; + u16 ch = get_char(vc, (u_short *)spk_pos, &tmp); + + synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch); +} + +/* these are stub functions to keep keyboard.c happy. */ + +static void say_from_top(struct vc_data *vc) +{ + say_screen_from_to(vc, 0, spk_y); +} + +static void say_to_bottom(struct vc_data *vc) +{ + say_screen_from_to(vc, spk_y, vc->vc_rows); +} + +static void say_from_left(struct vc_data *vc) +{ + say_line_from_to(vc, 0, spk_x, 1); +} + +static void say_to_right(struct vc_data *vc) +{ + say_line_from_to(vc, spk_x, vc->vc_cols, 1); +} + +/* end of stub functions. */ + +static void spkup_write(const u16 *in_buf, int count) +{ + static int rep_count; + static u16 ch = '\0', old_ch = '\0'; + static u_short char_type, last_type; + int in_count = count; + + spk_keydown = 0; + while (count--) { + if (cursor_track == read_all_mode) { + /* Insert Sentence Index */ + if ((in_buf == sentmarks[bn][currsentence]) && + (currsentence <= numsentences[bn])) + synth_insert_next_index(currsentence++); + } + ch = *in_buf++; + if (ch < 0x100) + char_type = spk_chartab[ch]; + else + char_type = ALPHA; + if (ch == old_ch && !(char_type & B_NUM)) { + if (++rep_count > 2) + continue; + } else { + if ((last_type & CH_RPT) && rep_count > 2) { + synth_printf(" "); + synth_printf(spk_msg_get(MSG_REPEAT_DESC), + ++rep_count); + synth_printf(" "); + } + rep_count = 0; + } + if (ch == spk_lastkey) { + rep_count = 0; + if (spk_key_echo == 1 && ch >= MINECHOCHAR) + speak_char(ch); + } else if (char_type & B_ALPHA) { + if ((synth_flags & SF_DEC) && (last_type & PUNC)) + synth_buffer_add(SPACE); + synth_putwc_s(ch); + } else if (char_type & B_NUM) { + rep_count = 0; + synth_putwc_s(ch); + } else if (char_type & spk_punc_mask) { + speak_char(ch); + char_type &= ~PUNC; /* for dec nospell processing */ + } else if (char_type & SYNTH_OK) { + /* these are usually puncts like . and , which synth + * needs for expression. + * suppress multiple to get rid of long pauses and + * clear repeat count + * so if someone has + * repeats on you don't get nothing repeated count + */ + if (ch != old_ch) + synth_putwc_s(ch); + else + rep_count = 0; + } else { +/* send space and record position, if next is num overwrite space */ + if (old_ch != ch) + synth_buffer_add(SPACE); + else + rep_count = 0; + } + old_ch = ch; + last_type = char_type; + } + spk_lastkey = 0; + if (in_count > 2 && rep_count > 2) { + if (last_type & CH_RPT) { + synth_printf(" "); + synth_printf(spk_msg_get(MSG_REPEAT_DESC2), + ++rep_count); + synth_printf(" "); + } + rep_count = 0; + } +} + +static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1); + +static void read_all_doc(struct vc_data *vc); +static void cursor_done(struct timer_list *unused); +static DEFINE_TIMER(cursor_timer, cursor_done); + +static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag) +{ + unsigned long flags; + + if (!synth || up_flag || spk_killed) + return; + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (cursor_track == read_all_mode) { + switch (value) { + case KVAL(K_SHIFT): + del_timer(&cursor_timer); + spk_shut_up &= 0xfe; + spk_do_flush(); + read_all_doc(vc); + break; + case KVAL(K_CTRL): + del_timer(&cursor_timer); + cursor_track = prev_cursor_track; + spk_shut_up &= 0xfe; + spk_do_flush(); + break; + } + } else { + spk_shut_up &= 0xfe; + spk_do_flush(); + } + if (spk_say_ctrl && value < NUM_CTL_LABELS) + synth_printf("%s", spk_msg_get(MSG_CTL_START + value)); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag) +{ + unsigned long flags; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (up_flag) { + spk_lastkey = 0; + spk_keydown = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return; + } + if (!synth || spk_killed) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return; + } + spk_shut_up &= 0xfe; + spk_lastkey = value; + spk_keydown++; + spk_parked &= 0xfe; + if (spk_key_echo == 2 && value >= MINECHOCHAR) + speak_char(value); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +int spk_set_key_info(const u_char *key_info, u_char *k_buffer) +{ + int i = 0, states, key_data_len; + const u_char *cp = key_info; + u_char *cp1 = k_buffer; + u_char ch, version, num_keys; + + version = *cp++; + if (version != KEY_MAP_VER) { + pr_debug("version found %d should be %d\n", + version, KEY_MAP_VER); + return -EINVAL; + } + num_keys = *cp; + states = (int)cp[1]; + key_data_len = (states + 1) * (num_keys + 1); + if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) { + pr_debug("too many key_infos (%d over %u)\n", + key_data_len + SHIFT_TBL_SIZE + 4, + (unsigned int)(sizeof(spk_key_buf))); + return -EINVAL; + } + memset(k_buffer, 0, SHIFT_TBL_SIZE); + memset(spk_our_keys, 0, sizeof(spk_our_keys)); + spk_shift_table = k_buffer; + spk_our_keys[0] = spk_shift_table; + cp1 += SHIFT_TBL_SIZE; + memcpy(cp1, cp, key_data_len + 3); + /* get num_keys, states and data */ + cp1 += 2; /* now pointing at shift states */ + for (i = 1; i <= states; i++) { + ch = *cp1++; + if (ch >= SHIFT_TBL_SIZE) { + pr_debug("(%d) not valid shift state (max_allowed = %d)\n", + ch, SHIFT_TBL_SIZE); + return -EINVAL; + } + spk_shift_table[ch] = i; + } + keymap_flags = *cp1++; + while ((ch = *cp1)) { + if (ch >= MAX_KEY) { + pr_debug("(%d), not valid key, (max_allowed = %d)\n", + ch, MAX_KEY); + return -EINVAL; + } + spk_our_keys[ch] = cp1; + cp1 += states + 1; + } + return 0; +} + +static struct var_t spk_vars[] = { + /* bell must be first to set high limit */ + {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} }, + {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} }, + {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} }, + {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} }, + {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} }, + {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} }, + {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} }, + {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} }, + {SAY_CONTROL, TOGGLE_0}, + {SAY_WORD_CTL, TOGGLE_0}, + {NO_INTERRUPT, TOGGLE_0}, + {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} }, + V_LAST_VAR +}; + +static void toggle_cursoring(struct vc_data *vc) +{ + if (cursor_track == read_all_mode) + cursor_track = prev_cursor_track; + if (++cursor_track >= CT_Max) + cursor_track = 0; + synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track)); +} + +void spk_reset_default_chars(void) +{ + int i; + + /* First, free any non-default */ + for (i = 0; i < 256; i++) { + if (spk_characters[i] && + (spk_characters[i] != spk_default_chars[i])) + kfree(spk_characters[i]); + } + + memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars)); +} + +void spk_reset_default_chartab(void) +{ + memcpy(spk_chartab, default_chartab, sizeof(default_chartab)); +} + +static const struct st_bits_data *pb_edit; + +static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key) +{ + short mask = pb_edit->mask, ch_type = spk_chartab[ch]; + + if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE) + return -1; + if (ch == SPACE) { + synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE)); + spk_special_handler = NULL; + return 1; + } + if (mask < PUNC && !(ch_type & PUNC)) + return -1; + spk_chartab[ch] ^= mask; + speak_char(ch); + synth_printf(" %s\n", + (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) : + spk_msg_get(MSG_OFF)); + return 1; +} + +/* Allocation concurrency is protected by the console semaphore */ +static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags) +{ + int vc_num; + + vc_num = vc->vc_num; + if (!speakup_console[vc_num]) { + speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]), + gfp_flags); + if (!speakup_console[vc_num]) + return -ENOMEM; + speakup_date(vc); + } else if (!spk_parked) { + speakup_date(vc); + } + + return 0; +} + +static void speakup_deallocate(struct vc_data *vc) +{ + int vc_num; + + vc_num = vc->vc_num; + kfree(speakup_console[vc_num]); + speakup_console[vc_num] = NULL; +} + +static u_char is_cursor; +static u_long old_cursor_pos, old_cursor_x, old_cursor_y; +static int cursor_con; + +static void reset_highlight_buffers(struct vc_data *); + +static int read_all_key; + +static int in_keyboard_notifier; + +static void start_read_all_timer(struct vc_data *vc, int command); + +enum { + RA_NOTHING, + RA_NEXT_SENT, + RA_PREV_LINE, + RA_NEXT_LINE, + RA_PREV_SENT, + RA_DOWN_ARROW, + RA_TIMER, + RA_FIND_NEXT_SENT, + RA_FIND_PREV_SENT, +}; + +static void kbd_fakekey2(struct vc_data *vc, int command) +{ + del_timer(&cursor_timer); + speakup_fake_down_arrow(); + start_read_all_timer(vc, command); +} + +static void read_all_doc(struct vc_data *vc) +{ + if ((vc->vc_num != fg_console) || !synth || spk_shut_up) + return; + if (!synth_supports_indexing()) + return; + if (cursor_track != read_all_mode) + prev_cursor_track = cursor_track; + cursor_track = read_all_mode; + spk_reset_index_count(0); + if (get_sentence_buf(vc, 0) == -1) { + del_timer(&cursor_timer); + if (!in_keyboard_notifier) + speakup_fake_down_arrow(); + start_read_all_timer(vc, RA_DOWN_ARROW); + } else { + say_sentence_num(0, 0); + synth_insert_next_index(0); + start_read_all_timer(vc, RA_TIMER); + } +} + +static void stop_read_all(struct vc_data *vc) +{ + del_timer(&cursor_timer); + cursor_track = prev_cursor_track; + spk_shut_up &= 0xfe; + spk_do_flush(); +} + +static void start_read_all_timer(struct vc_data *vc, int command) +{ + struct var_t *cursor_timeout; + + cursor_con = vc->vc_num; + read_all_key = command; + cursor_timeout = spk_get_var(CURSOR_TIME); + mod_timer(&cursor_timer, + jiffies + msecs_to_jiffies(cursor_timeout->u.n.value)); +} + +static void handle_cursor_read_all(struct vc_data *vc, int command) +{ + int indcount, sentcount, rv, sn; + + switch (command) { + case RA_NEXT_SENT: + /* Get Current Sentence */ + spk_get_index_count(&indcount, &sentcount); + /*printk("%d %d ", indcount, sentcount); */ + spk_reset_index_count(sentcount + 1); + if (indcount == 1) { + if (!say_sentence_num(sentcount + 1, 0)) { + kbd_fakekey2(vc, RA_FIND_NEXT_SENT); + return; + } + synth_insert_next_index(0); + } else { + sn = 0; + if (!say_sentence_num(sentcount + 1, 1)) { + sn = 1; + spk_reset_index_count(sn); + } else { + synth_insert_next_index(0); + } + if (!say_sentence_num(sn, 0)) { + kbd_fakekey2(vc, RA_FIND_NEXT_SENT); + return; + } + synth_insert_next_index(0); + } + start_read_all_timer(vc, RA_TIMER); + break; + case RA_PREV_SENT: + break; + case RA_NEXT_LINE: + read_all_doc(vc); + break; + case RA_PREV_LINE: + break; + case RA_DOWN_ARROW: + if (get_sentence_buf(vc, 0) == -1) { + kbd_fakekey2(vc, RA_DOWN_ARROW); + } else { + say_sentence_num(0, 0); + synth_insert_next_index(0); + start_read_all_timer(vc, RA_TIMER); + } + break; + case RA_FIND_NEXT_SENT: + rv = get_sentence_buf(vc, 0); + if (rv == -1) + read_all_doc(vc); + if (rv == 0) { + kbd_fakekey2(vc, RA_FIND_NEXT_SENT); + } else { + say_sentence_num(1, 0); + synth_insert_next_index(0); + start_read_all_timer(vc, RA_TIMER); + } + break; + case RA_FIND_PREV_SENT: + break; + case RA_TIMER: + spk_get_index_count(&indcount, &sentcount); + if (indcount < 2) + kbd_fakekey2(vc, RA_DOWN_ARROW); + else + start_read_all_timer(vc, RA_TIMER); + break; + } +} + +static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag) +{ + unsigned long flags; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (cursor_track == read_all_mode) { + spk_parked &= 0xfe; + if (!synth || up_flag || spk_shut_up) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return NOTIFY_STOP; + } + del_timer(&cursor_timer); + spk_shut_up &= 0xfe; + spk_do_flush(); + start_read_all_timer(vc, value + 1); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return NOTIFY_STOP; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return NOTIFY_OK; +} + +static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag) +{ + unsigned long flags; + struct var_t *cursor_timeout; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + spk_parked &= 0xfe; + if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return; + } + spk_shut_up &= 0xfe; + if (spk_no_intr) + spk_do_flush(); +/* the key press flushes if !no_inter but we want to flush on cursor + * moves regardless of no_inter state + */ + is_cursor = value + 1; + old_cursor_pos = vc->vc_pos; + old_cursor_x = vc->vc_x; + old_cursor_y = vc->vc_y; + speakup_console[vc->vc_num]->ht.cy = vc->vc_y; + cursor_con = vc->vc_num; + if (cursor_track == CT_Highlight) + reset_highlight_buffers(vc); + cursor_timeout = spk_get_var(CURSOR_TIME); + mod_timer(&cursor_timer, + jiffies + msecs_to_jiffies(cursor_timeout->u.n.value)); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len) +{ + int i, bi, hi; + int vc_num = vc->vc_num; + + bi = (vc->vc_attr & 0x70) >> 4; + hi = speakup_console[vc_num]->ht.highsize[bi]; + + i = 0; + if (speakup_console[vc_num]->ht.highsize[bi] == 0) { + speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos; + speakup_console[vc_num]->ht.rx[bi] = vc->vc_x; + speakup_console[vc_num]->ht.ry[bi] = vc->vc_y; + } + while ((hi < COLOR_BUFFER_SIZE) && (i < len)) { + if (ic[i] > 32) { + speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i]; + hi++; + } else if ((ic[i] == 32) && (hi != 0)) { + if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] != + 32) { + speakup_console[vc_num]->ht.highbuf[bi][hi] = + ic[i]; + hi++; + } + } + i++; + } + speakup_console[vc_num]->ht.highsize[bi] = hi; +} + +static void reset_highlight_buffers(struct vc_data *vc) +{ + int i; + int vc_num = vc->vc_num; + + for (i = 0; i < 8; i++) + speakup_console[vc_num]->ht.highsize[i] = 0; +} + +static int count_highlight_color(struct vc_data *vc) +{ + int i, bg; + int cc; + int vc_num = vc->vc_num; + u16 ch; + u16 *start = (u16 *)vc->vc_origin; + + for (i = 0; i < 8; i++) + speakup_console[vc_num]->ht.bgcount[i] = 0; + + for (i = 0; i < vc->vc_rows; i++) { + u16 *end = start + vc->vc_cols * 2; + u16 *ptr; + + for (ptr = start; ptr < end; ptr++) { + ch = get_attributes(vc, ptr); + bg = (ch & 0x70) >> 4; + speakup_console[vc_num]->ht.bgcount[bg]++; + } + start += vc->vc_size_row; + } + + cc = 0; + for (i = 0; i < 8; i++) + if (speakup_console[vc_num]->ht.bgcount[i] > 0) + cc++; + return cc; +} + +static int get_highlight_color(struct vc_data *vc) +{ + int i, j; + unsigned int cptr[8]; + int vc_num = vc->vc_num; + + for (i = 0; i < 8; i++) + cptr[i] = i; + + for (i = 0; i < 7; i++) + for (j = i + 1; j < 8; j++) + if (speakup_console[vc_num]->ht.bgcount[cptr[i]] > + speakup_console[vc_num]->ht.bgcount[cptr[j]]) + swap(cptr[i], cptr[j]); + + for (i = 0; i < 8; i++) + if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0) + if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0) + return cptr[i]; + return -1; +} + +static int speak_highlight(struct vc_data *vc) +{ + int hc, d; + int vc_num = vc->vc_num; + + if (count_highlight_color(vc) == 1) + return 0; + hc = get_highlight_color(vc); + if (hc != -1) { + d = vc->vc_y - speakup_console[vc_num]->ht.cy; + if ((d == 1) || (d == -1)) + if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y) + return 0; + spk_parked |= 0x01; + spk_do_flush(); + spkup_write(speakup_console[vc_num]->ht.highbuf[hc], + speakup_console[vc_num]->ht.highsize[hc]); + spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc]; + spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc]; + spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc]; + return 1; + } + return 0; +} + +static void cursor_done(struct timer_list *unused) +{ + struct vc_data *vc = vc_cons[cursor_con].d; + unsigned long flags; + + del_timer(&cursor_timer); + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (cursor_con != fg_console) { + is_cursor = 0; + goto out; + } + speakup_date(vc); + if (win_enabled) { + if (vc->vc_x >= win_left && vc->vc_x <= win_right && + vc->vc_y >= win_top && vc->vc_y <= win_bottom) { + spk_keydown = 0; + is_cursor = 0; + goto out; + } + } + if (cursor_track == read_all_mode) { + handle_cursor_read_all(vc, read_all_key); + goto out; + } + if (cursor_track == CT_Highlight) { + if (speak_highlight(vc)) { + spk_keydown = 0; + is_cursor = 0; + goto out; + } + } + if (cursor_track == CT_Window) + speakup_win_say(vc); + else if (is_cursor == 1 || is_cursor == 4) + say_line_from_to(vc, 0, vc->vc_cols, 0); + else + say_char(vc); + spk_keydown = 0; + is_cursor = 0; +out: + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +/* called by: vt_notifier_call() */ +static void speakup_bs(struct vc_data *vc) +{ + unsigned long flags; + + if (!speakup_console[vc->vc_num]) + return; + if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) + /* Speakup output, discard */ + return; + if (!spk_parked) + speakup_date(vc); + if (spk_shut_up || !synth) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return; + } + if (vc->vc_num == fg_console && spk_keydown) { + spk_keydown = 0; + if (!is_cursor) + say_char(vc); + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +/* called by: vt_notifier_call() */ +static void speakup_con_write(struct vc_data *vc, u16 *str, int len) +{ + unsigned long flags; + + if ((vc->vc_num != fg_console) || spk_shut_up || !synth) + return; + if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) + /* Speakup output, discard */ + return; + if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1)) + bleep(3); + if ((is_cursor) || (cursor_track == read_all_mode)) { + if (cursor_track == CT_Highlight) + update_color_buffer(vc, str, len); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return; + } + if (win_enabled) { + if (vc->vc_x >= win_left && vc->vc_x <= win_right && + vc->vc_y >= win_top && vc->vc_y <= win_bottom) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return; + } + } + + spkup_write(str, len); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +static void speakup_con_update(struct vc_data *vc) +{ + unsigned long flags; + + if (!speakup_console[vc->vc_num] || spk_parked) + return; + if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) + /* Speakup output, discard */ + return; + speakup_date(vc); + if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) { + synth_printf("%s", spk_str_pause); + spk_paused = true; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag) +{ + unsigned long flags; + int on_off = 2; + char *label; + + if (!synth || up_flag || spk_killed) + return; + spin_lock_irqsave(&speakup_info.spinlock, flags); + spk_shut_up &= 0xfe; + if (spk_no_intr) + spk_do_flush(); + switch (value) { + case KVAL(K_CAPS): + label = spk_msg_get(MSG_KEYNAME_CAPSLOCK); + on_off = vt_get_leds(fg_console, VC_CAPSLOCK); + break; + case KVAL(K_NUM): + label = spk_msg_get(MSG_KEYNAME_NUMLOCK); + on_off = vt_get_leds(fg_console, VC_NUMLOCK); + break; + case KVAL(K_HOLD): + label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK); + on_off = vt_get_leds(fg_console, VC_SCROLLOCK); + if (speakup_console[vc->vc_num]) + speakup_console[vc->vc_num]->tty_stopped = on_off; + break; + default: + spk_parked &= 0xfe; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return; + } + if (on_off < 2) + synth_printf("%s %s\n", + label, spk_msg_get(MSG_STATUS_START + on_off)); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); +} + +static int inc_dec_var(u_char value) +{ + struct st_var_header *p_header; + struct var_t *var_data; + char num_buf[32]; + char *cp = num_buf; + char *pn; + int var_id = (int)value - VAR_START; + int how = (var_id & 1) ? E_INC : E_DEC; + + var_id = var_id / 2 + FIRST_SET_VAR; + p_header = spk_get_var_header(var_id); + if (!p_header) + return -1; + if (p_header->var_type != VAR_NUM) + return -1; + var_data = p_header->data; + if (spk_set_num_var(1, p_header, how) != 0) + return -1; + if (!spk_close_press) { + for (pn = p_header->name; *pn; pn++) { + if (*pn == '_') + *cp = SPACE; + else + *cp++ = *pn; + } + } + snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ", + var_data->u.n.value); + synth_printf("%s", num_buf); + return 0; +} + +static void speakup_win_set(struct vc_data *vc) +{ + char info[40]; + + if (win_start > 1) { + synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET)); + return; + } + if (spk_x < win_left || spk_y < win_top) { + synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START)); + return; + } + if (win_start && spk_x == win_left && spk_y == win_top) { + win_left = 0; + win_right = vc->vc_cols - 1; + win_bottom = spk_y; + snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE), + (int)win_top + 1); + } else { + if (!win_start) { + win_top = spk_y; + win_left = spk_x; + } else { + win_bottom = spk_y; + win_right = spk_x; + } + snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY), + (win_start) ? + spk_msg_get(MSG_END) : spk_msg_get(MSG_START), + (int)spk_y + 1, (int)spk_x + 1); + } + synth_printf("%s\n", info); + win_start++; +} + +static void speakup_win_clear(struct vc_data *vc) +{ + win_top = 0; + win_bottom = 0; + win_left = 0; + win_right = 0; + win_start = 0; + synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED)); +} + +static void speakup_win_enable(struct vc_data *vc) +{ + if (win_start < 2) { + synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW)); + return; + } + win_enabled ^= 1; + if (win_enabled) + synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED)); + else + synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED)); +} + +static void speakup_bits(struct vc_data *vc) +{ + int val = this_speakup_key - (FIRST_EDIT_BITS - 1); + + if (spk_special_handler || val < 1 || val > 6) { + synth_printf("%s\n", spk_msg_get(MSG_ERROR)); + return; + } + pb_edit = &spk_punc_info[val]; + synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name); + spk_special_handler = edit_bits; +} + +static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key) +{ + static u_char goto_buf[8]; + static int num; + int maxlen; + char *cp; + u16 wch; + + if (type == KT_SPKUP && ch == SPEAKUP_GOTO) + goto do_goto; + if (type == KT_LATIN && ch == '\n') + goto do_goto; + if (type != 0) + goto oops; + if (ch == 8) { + u16 wch; + + if (num == 0) + return -1; + wch = goto_buf[--num]; + goto_buf[num] = '\0'; + spkup_write(&wch, 1); + return 1; + } + if (ch < '+' || ch > 'y') + goto oops; + wch = ch; + goto_buf[num++] = ch; + goto_buf[num] = '\0'; + spkup_write(&wch, 1); + maxlen = (*goto_buf >= '0') ? 3 : 4; + if ((ch == '+' || ch == '-') && num == 1) + return 1; + if (ch >= '0' && ch <= '9' && num < maxlen) + return 1; + if (num < maxlen - 1 || num > maxlen) + goto oops; + if (ch < 'x' || ch > 'y') { +oops: + if (!spk_killed) + synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED)); + goto_buf[num = 0] = '\0'; + spk_special_handler = NULL; + return 1; + } + + /* Do not replace with kstrtoul: here we need cp to be updated */ + goto_pos = simple_strtoul(goto_buf, &cp, 10); + + if (*cp == 'x') { + if (*goto_buf < '0') + goto_pos += spk_x; + else if (goto_pos > 0) + goto_pos--; + + if (goto_pos >= vc->vc_cols) + goto_pos = vc->vc_cols - 1; + goto_x = 1; + } else { + if (*goto_buf < '0') + goto_pos += spk_y; + else if (goto_pos > 0) + goto_pos--; + + if (goto_pos >= vc->vc_rows) + goto_pos = vc->vc_rows - 1; + goto_x = 0; + } + goto_buf[num = 0] = '\0'; +do_goto: + spk_special_handler = NULL; + spk_parked |= 0x01; + if (goto_x) { + spk_pos -= spk_x * 2; + spk_x = goto_pos; + spk_pos += goto_pos * 2; + say_word(vc); + } else { + spk_y = goto_pos; + spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row); + say_line(vc); + } + return 1; +} + +static void speakup_goto(struct vc_data *vc) +{ + if (spk_special_handler) { + synth_printf("%s\n", spk_msg_get(MSG_ERROR)); + return; + } + synth_printf("%s\n", spk_msg_get(MSG_GOTO)); + spk_special_handler = handle_goto; +} + +static void speakup_help(struct vc_data *vc) +{ + spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0); +} + +static void do_nothing(struct vc_data *vc) +{ + return; /* flush done in do_spkup */ +} + +static u_char key_speakup, spk_key_locked; + +static void speakup_lock(struct vc_data *vc) +{ + if (!spk_key_locked) { + spk_key_locked = 16; + key_speakup = 16; + } else { + spk_key_locked = 0; + key_speakup = 0; + } +} + +typedef void (*spkup_hand) (struct vc_data *); +static spkup_hand spkup_handler[] = { + /* must be ordered same as defines in speakup.h */ + do_nothing, speakup_goto, speech_kill, speakup_shut_up, + speakup_cut, speakup_paste, say_first_char, say_last_char, + say_char, say_prev_char, say_next_char, + say_word, say_prev_word, say_next_word, + say_line, say_prev_line, say_next_line, + top_edge, bottom_edge, left_edge, right_edge, + spell_word, spell_word, say_screen, + say_position, say_attributes, + speakup_off, speakup_parked, say_line, /* this is for indent */ + say_from_top, say_to_bottom, + say_from_left, say_to_right, + say_char_num, speakup_bits, speakup_bits, say_phonetic_char, + speakup_bits, speakup_bits, speakup_bits, + speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say, + speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL +}; + +static void do_spkup(struct vc_data *vc, u_char value) +{ + if (spk_killed && value != SPEECH_KILL) + return; + spk_keydown = 0; + spk_lastkey = 0; + spk_shut_up &= 0xfe; + this_speakup_key = value; + if (value < SPKUP_MAX_FUNC && spkup_handler[value]) { + spk_do_flush(); + (*spkup_handler[value]) (vc); + } else { + if (inc_dec_var(value) < 0) + bleep(9); + } +} + +static const char *pad_chars = "0123456789+-*/\015,.?()"; + +static int +speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym, + int up_flag) +{ + unsigned long flags; + int kh; + u_char *key_info; + u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0; + u_char shift_info, offset; + int ret = 0; + + if (!synth) + return 0; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + tty = vc->port.tty; + if (type >= 0xf0) + type -= 0xf0; + if (type == KT_PAD && + (vt_get_leds(fg_console, VC_NUMLOCK))) { + if (up_flag) { + spk_keydown = 0; + goto out; + } + value = pad_chars[value]; + spk_lastkey = value; + spk_keydown++; + spk_parked &= 0xfe; + goto no_map; + } + if (keycode >= MAX_KEY) + goto no_map; + key_info = spk_our_keys[keycode]; + if (!key_info) + goto no_map; + /* Check valid read all mode keys */ + if ((cursor_track == read_all_mode) && (!up_flag)) { + switch (value) { + case KVAL(K_DOWN): + case KVAL(K_UP): + case KVAL(K_LEFT): + case KVAL(K_RIGHT): + case KVAL(K_PGUP): + case KVAL(K_PGDN): + break; + default: + stop_read_all(vc); + break; + } + } + shift_info = (shift_state & 0x0f) + key_speakup; + offset = spk_shift_table[shift_info]; + if (offset) { + new_key = key_info[offset]; + if (new_key) { + ret = 1; + if (new_key == SPK_KEY) { + if (!spk_key_locked) + key_speakup = (up_flag) ? 0 : 16; + if (up_flag || spk_killed) + goto out; + spk_shut_up &= 0xfe; + spk_do_flush(); + goto out; + } + if (up_flag) + goto out; + if (last_keycode == keycode && + time_after(last_spk_jiffy + MAX_DELAY, jiffies)) { + spk_close_press = 1; + offset = spk_shift_table[shift_info + 32]; + /* double press? */ + if (offset && key_info[offset]) + new_key = key_info[offset]; + } + last_keycode = keycode; + last_spk_jiffy = jiffies; + type = KT_SPKUP; + value = new_key; + } + } +no_map: + if (type == KT_SPKUP && !spk_special_handler) { + do_spkup(vc, new_key); + spk_close_press = 0; + ret = 1; + goto out; + } + if (up_flag || spk_killed || type == KT_SHIFT) + goto out; + spk_shut_up &= 0xfe; + kh = (value == KVAL(K_DOWN)) || + (value == KVAL(K_UP)) || + (value == KVAL(K_LEFT)) || + (value == KVAL(K_RIGHT)); + if ((cursor_track != read_all_mode) || !kh) + if (!spk_no_intr) + spk_do_flush(); + if (spk_special_handler) { + if (type == KT_SPEC && value == 1) { + value = '\n'; + type = KT_LATIN; + } else if (type == KT_LETTER) { + type = KT_LATIN; + } else if (value == 0x7f) { + value = 8; /* make del = backspace */ + } + ret = (*spk_special_handler) (vc, type, value, keycode); + spk_close_press = 0; + if (ret < 0) + bleep(9); + goto out; + } + last_keycode = 0; +out: + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return ret; +} + +static int keyboard_notifier_call(struct notifier_block *nb, + unsigned long code, void *_param) +{ + struct keyboard_notifier_param *param = _param; + struct vc_data *vc = param->vc; + int up = !param->down; + int ret = NOTIFY_OK; + static int keycode; /* to hold the current keycode */ + + in_keyboard_notifier = 1; + + if (vc->vc_mode == KD_GRAPHICS) + goto out; + + /* + * First, determine whether we are handling a fake keypress on + * the current processor. If we are, then return NOTIFY_OK, + * to pass the keystroke up the chain. This prevents us from + * trying to take the Speakup lock while it is held by the + * processor on which the simulated keystroke was generated. + * Also, the simulated keystrokes should be ignored by Speakup. + */ + + if (speakup_fake_key_pressed()) + goto out; + + switch (code) { + case KBD_KEYCODE: + /* speakup requires keycode and keysym currently */ + keycode = param->value; + break; + case KBD_UNBOUND_KEYCODE: + /* not used yet */ + break; + case KBD_UNICODE: + /* not used yet */ + break; + case KBD_KEYSYM: + if (speakup_key(vc, param->shift, keycode, param->value, up)) + ret = NOTIFY_STOP; + else if (KTYP(param->value) == KT_CUR) + ret = pre_handle_cursor(vc, KVAL(param->value), up); + break; + case KBD_POST_KEYSYM:{ + unsigned char type = KTYP(param->value) - 0xf0; + unsigned char val = KVAL(param->value); + + switch (type) { + case KT_SHIFT: + do_handle_shift(vc, val, up); + break; + case KT_LATIN: + case KT_LETTER: + do_handle_latin(vc, val, up); + break; + case KT_CUR: + do_handle_cursor(vc, val, up); + break; + case KT_SPEC: + do_handle_spec(vc, val, up); + break; + } + break; + } + } +out: + in_keyboard_notifier = 0; + return ret; +} + +static int vt_notifier_call(struct notifier_block *nb, + unsigned long code, void *_param) +{ + struct vt_notifier_param *param = _param; + struct vc_data *vc = param->vc; + + switch (code) { + case VT_ALLOCATE: + if (vc->vc_mode == KD_TEXT) + speakup_allocate(vc, GFP_ATOMIC); + break; + case VT_DEALLOCATE: + speakup_deallocate(vc); + break; + case VT_WRITE: + if (param->c == '\b') { + speakup_bs(vc); + } else { + u16 d = param->c; + + speakup_con_write(vc, &d, 1); + } + break; + case VT_UPDATE: + speakup_con_update(vc); + break; + } + return NOTIFY_OK; +} + +/* called by: module_exit() */ +static void __exit speakup_exit(void) +{ + int i; + + unregister_keyboard_notifier(&keyboard_notifier_block); + unregister_vt_notifier(&vt_notifier_block); + speakup_unregister_devsynth(); + speakup_cancel_selection(); + speakup_cancel_paste(); + del_timer_sync(&cursor_timer); + kthread_stop(speakup_task); + speakup_task = NULL; + mutex_lock(&spk_mutex); + synth_release(); + mutex_unlock(&spk_mutex); + spk_ttyio_unregister_ldisc(); + + speakup_kobj_exit(); + + for (i = 0; i < MAX_NR_CONSOLES; i++) + kfree(speakup_console[i]); + + speakup_remove_virtual_keyboard(); + + for (i = 0; i < MAXVARS; i++) + speakup_unregister_var(i); + + for (i = 0; i < 256; i++) { + if (spk_characters[i] != spk_default_chars[i]) + kfree(spk_characters[i]); + } + + spk_free_user_msgs(); +} + +/* call by: module_init() */ +static int __init speakup_init(void) +{ + int i; + long err = 0; + struct vc_data *vc = vc_cons[fg_console].d; + struct var_t *var; + + /* These first few initializations cannot fail. */ + spk_initialize_msgs(); /* Initialize arrays for i18n. */ + spk_reset_default_chars(); + spk_reset_default_chartab(); + spk_strlwr(synth_name); + spk_vars[0].u.n.high = vc->vc_cols; + for (var = spk_vars; var->var_id != MAXVARS; var++) + speakup_register_var(var); + for (var = synth_time_vars; + (var->var_id >= 0) && (var->var_id < MAXVARS); var++) + speakup_register_var(var); + for (i = 1; spk_punc_info[i].mask != 0; i++) + spk_set_mask_bits(NULL, i, 2); + + spk_set_key_info(spk_key_defaults, spk_key_buf); + + /* From here on out, initializations can fail. */ + err = speakup_add_virtual_keyboard(); + if (err) + goto error_virtkeyboard; + + for (i = 0; i < MAX_NR_CONSOLES; i++) + if (vc_cons[i].d) { + err = speakup_allocate(vc_cons[i].d, GFP_KERNEL); + if (err) + goto error_kobjects; + } + + if (spk_quiet_boot) + spk_shut_up |= 0x01; + + err = speakup_kobj_init(); + if (err) + goto error_kobjects; + + spk_ttyio_register_ldisc(); + synth_init(synth_name); + speakup_register_devsynth(); + /* + * register_devsynth might fail, but this error is not fatal. + * /dev/synth is an extra feature; the rest of Speakup + * will work fine without it. + */ + + err = register_keyboard_notifier(&keyboard_notifier_block); + if (err) + goto error_kbdnotifier; + err = register_vt_notifier(&vt_notifier_block); + if (err) + goto error_vtnotifier; + + speakup_task = kthread_create(speakup_thread, NULL, "speakup"); + + if (IS_ERR(speakup_task)) { + err = PTR_ERR(speakup_task); + goto error_task; + } + + set_user_nice(speakup_task, 10); + wake_up_process(speakup_task); + + pr_info("speakup %s: initialized\n", SPEAKUP_VERSION); + pr_info("synth name on entry is: %s\n", synth_name); + goto out; + +error_task: + unregister_vt_notifier(&vt_notifier_block); + +error_vtnotifier: + unregister_keyboard_notifier(&keyboard_notifier_block); + del_timer(&cursor_timer); + +error_kbdnotifier: + speakup_unregister_devsynth(); + mutex_lock(&spk_mutex); + synth_release(); + mutex_unlock(&spk_mutex); + speakup_kobj_exit(); + +error_kobjects: + for (i = 0; i < MAX_NR_CONSOLES; i++) + kfree(speakup_console[i]); + + speakup_remove_virtual_keyboard(); + +error_virtkeyboard: + for (i = 0; i < MAXVARS; i++) + speakup_unregister_var(i); + + for (i = 0; i < 256; i++) { + if (spk_characters[i] != spk_default_chars[i]) + kfree(spk_characters[i]); + } + + spk_free_user_msgs(); + +out: + return err; +} + +module_init(speakup_init); +module_exit(speakup_exit); diff --git a/drivers/accessibility/speakup/selection.c b/drivers/accessibility/speakup/selection.c new file mode 100644 index 000000000000..032f3264fba1 --- /dev/null +++ b/drivers/accessibility/speakup/selection.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 +#include /* for kmalloc */ +#include +#include +#include +#include /* for dev_warn */ +#include +#include +#include +#include +#include +#include + +#include "speakup.h" + +unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ +struct vc_data *spk_sel_cons; + +struct speakup_selection_work { + struct work_struct work; + struct tiocl_selection sel; + struct tty_struct *tty; +}; + +void speakup_clear_selection(void) +{ + console_lock(); + clear_selection(); + console_unlock(); +} + +static void __speakup_set_selection(struct work_struct *work) +{ + struct speakup_selection_work *ssw = + container_of(work, struct speakup_selection_work, work); + + struct tty_struct *tty; + struct tiocl_selection sel; + + sel = ssw->sel; + + /* this ensures we copy sel before releasing the lock below */ + rmb(); + + /* release the lock by setting tty of the struct to NULL */ + tty = xchg(&ssw->tty, NULL); + + if (spk_sel_cons != vc_cons[fg_console].d) { + spk_sel_cons = vc_cons[fg_console].d; + pr_warn("Selection: mark console not the same as cut\n"); + goto unref; + } + + set_selection_kernel(&sel, tty); + +unref: + tty_kref_put(tty); +} + +static struct speakup_selection_work speakup_sel_work = { + .work = __WORK_INITIALIZER(speakup_sel_work.work, + __speakup_set_selection) +}; + +int speakup_set_selection(struct tty_struct *tty) +{ + /* we get kref here first in order to avoid a subtle race when + * cancelling selection work. getting kref first establishes the + * invariant that if speakup_sel_work.tty is not NULL when + * speakup_cancel_selection() is called, it must be the case that a put + * kref is pending. + */ + tty_kref_get(tty); + if (cmpxchg(&speakup_sel_work.tty, NULL, tty)) { + tty_kref_put(tty); + return -EBUSY; + } + /* now we have the 'lock' by setting tty member of + * speakup_selection_work. wmb() ensures that writes to + * speakup_sel_work don't happen before cmpxchg() above. + */ + wmb(); + + speakup_sel_work.sel.xs = spk_xs + 1; + speakup_sel_work.sel.ys = spk_ys + 1; + speakup_sel_work.sel.xe = spk_xe + 1; + speakup_sel_work.sel.ye = spk_ye + 1; + speakup_sel_work.sel.sel_mode = TIOCL_SELCHAR; + + schedule_work_on(WORK_CPU_UNBOUND, &speakup_sel_work.work); + + return 0; +} + +void speakup_cancel_selection(void) +{ + struct tty_struct *tty; + + cancel_work_sync(&speakup_sel_work.work); + /* setting to null so that if work fails to run and we cancel it, + * we can run it again without getting EBUSY forever from there on. + * we need to use xchg here to avoid race with speakup_set_selection() + */ + tty = xchg(&speakup_sel_work.tty, NULL); + if (tty) + tty_kref_put(tty); +} + +static void __speakup_paste_selection(struct work_struct *work) +{ + struct speakup_selection_work *ssw = + container_of(work, struct speakup_selection_work, work); + struct tty_struct *tty = xchg(&ssw->tty, NULL); + + paste_selection(tty); + tty_kref_put(tty); +} + +static struct speakup_selection_work speakup_paste_work = { + .work = __WORK_INITIALIZER(speakup_paste_work.work, + __speakup_paste_selection) +}; + +int speakup_paste_selection(struct tty_struct *tty) +{ + tty_kref_get(tty); + if (cmpxchg(&speakup_paste_work.tty, NULL, tty)) { + tty_kref_put(tty); + return -EBUSY; + } + + schedule_work_on(WORK_CPU_UNBOUND, &speakup_paste_work.work); + return 0; +} + +void speakup_cancel_paste(void) +{ + struct tty_struct *tty; + + cancel_work_sync(&speakup_paste_work.work); + tty = xchg(&speakup_paste_work.tty, NULL); + if (tty) + tty_kref_put(tty); +} diff --git a/drivers/accessibility/speakup/serialio.c b/drivers/accessibility/speakup/serialio.c new file mode 100644 index 000000000000..177a2988641c --- /dev/null +++ b/drivers/accessibility/speakup/serialio.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#include "spk_types.h" +#include "speakup.h" +#include "spk_priv.h" +#include "serialio.h" + +#include +/* WARNING: Do not change this to without testing that + * SERIAL_PORT_DFNS does get defined to the appropriate value. + */ +#include + +#ifndef SERIAL_PORT_DFNS +#define SERIAL_PORT_DFNS +#endif + +static void start_serial_interrupt(int irq); + +static const struct old_serial_port rs_table[] = { + SERIAL_PORT_DFNS +}; + +static const struct old_serial_port *serstate; +static int timeouts; + +static int spk_serial_out(struct spk_synth *in_synth, const char ch); +static void spk_serial_send_xchar(char ch); +static void spk_serial_tiocmset(unsigned int set, unsigned int clear); +static unsigned char spk_serial_in(void); +static unsigned char spk_serial_in_nowait(void); +static void spk_serial_flush_buffer(void); + +struct spk_io_ops spk_serial_io_ops = { + .synth_out = spk_serial_out, + .send_xchar = spk_serial_send_xchar, + .tiocmset = spk_serial_tiocmset, + .synth_in = spk_serial_in, + .synth_in_nowait = spk_serial_in_nowait, + .flush_buffer = spk_serial_flush_buffer, +}; +EXPORT_SYMBOL_GPL(spk_serial_io_ops); + +const struct old_serial_port *spk_serial_init(int index) +{ + int baud = 9600, quot = 0; + unsigned int cval = 0; + int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8; + const struct old_serial_port *ser; + int err; + + if (index >= ARRAY_SIZE(rs_table)) { + pr_info("no port info for ttyS%d\n", index); + return NULL; + } + ser = rs_table + index; + + /* Divisor, bytesize and parity */ + quot = ser->baud_base / baud; + cval = cflag & (CSIZE | CSTOPB); +#if defined(__powerpc__) || defined(__alpha__) + cval >>= 8; +#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + if (synth_request_region(ser->port, 8)) { + /* try to take it back. */ + pr_info("Ports not available, trying to steal them\n"); + __release_region(&ioport_resource, ser->port, 8); + err = synth_request_region(ser->port, 8); + if (err) { + pr_warn("Unable to allocate port at %x, errno %i", + ser->port, err); + return NULL; + } + } + + /* Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ + outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ + outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ + outb(cval, ser->port + UART_LCR); /* reset DLAB */ + + /* Turn off Interrupts */ + outb(0, ser->port + UART_IER); + outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); + + /* If we read 0xff from the LSR, there is no UART here. */ + if (inb(ser->port + UART_LSR) == 0xff) { + synth_release_region(ser->port, 8); + serstate = NULL; + return NULL; + } + + mdelay(1); + speakup_info.port_tts = ser->port; + serstate = ser; + + start_serial_interrupt(ser->irq); + + return ser; +} + +static irqreturn_t synth_readbuf_handler(int irq, void *dev_id) +{ + unsigned long flags; + int c; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) { + c = inb_p(speakup_info.port_tts + UART_RX); + synth->read_buff_add((u_char)c); + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return IRQ_HANDLED; +} + +static void start_serial_interrupt(int irq) +{ + int rv; + + if (!synth->read_buff_add) + return; + + rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED, + "serial", (void *)synth_readbuf_handler); + + if (rv) + pr_err("Unable to request Speakup serial I R Q\n"); + /* Set MCR */ + outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, + speakup_info.port_tts + UART_MCR); + /* Turn on Interrupts */ + outb(UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI, + speakup_info.port_tts + UART_IER); + inb(speakup_info.port_tts + UART_LSR); + inb(speakup_info.port_tts + UART_RX); + inb(speakup_info.port_tts + UART_IIR); + inb(speakup_info.port_tts + UART_MSR); + outb(1, speakup_info.port_tts + UART_FCR); /* Turn FIFO On */ +} + +static void spk_serial_send_xchar(char ch) +{ + int timeout = SPK_XMITR_TIMEOUT; + + while (spk_serial_tx_busy()) { + if (!--timeout) + break; + udelay(1); + } + outb(ch, speakup_info.port_tts); +} + +static void spk_serial_tiocmset(unsigned int set, unsigned int clear) +{ + int old = inb(speakup_info.port_tts + UART_MCR); + + outb((old & ~clear) | set, speakup_info.port_tts + UART_MCR); +} + +int spk_serial_synth_probe(struct spk_synth *synth) +{ + const struct old_serial_port *ser; + int failed = 0; + + if ((synth->ser >= SPK_LO_TTY) && (synth->ser <= SPK_HI_TTY)) { + ser = spk_serial_init(synth->ser); + if (!ser) { + failed = -1; + } else { + outb_p(0, ser->port); + mdelay(1); + outb_p('\r', ser->port); + } + } else { + failed = -1; + pr_warn("ttyS%i is an invalid port\n", synth->ser); + } + if (failed) { + pr_info("%s: not found\n", synth->long_name); + return -ENODEV; + } + pr_info("%s: ttyS%i, Driver Version %s\n", + synth->long_name, synth->ser, synth->version); + synth->alive = 1; + return 0; +} +EXPORT_SYMBOL_GPL(spk_serial_synth_probe); + +void spk_stop_serial_interrupt(void) +{ + if (speakup_info.port_tts == 0) + return; + + if (!synth->read_buff_add) + return; + + /* Turn off interrupts */ + outb(0, speakup_info.port_tts + UART_IER); + /* Free IRQ */ + free_irq(serstate->irq, (void *)synth_readbuf_handler); +} +EXPORT_SYMBOL_GPL(spk_stop_serial_interrupt); + +int spk_wait_for_xmitr(struct spk_synth *in_synth) +{ + int tmout = SPK_XMITR_TIMEOUT; + + if ((in_synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) { + pr_warn("%s: too many timeouts, deactivating speakup\n", + in_synth->long_name); + in_synth->alive = 0; + /* No synth any more, so nobody will restart TTYs, and we thus + * need to do it ourselves. Now that there is no synth we can + * let application flood anyway + */ + speakup_start_ttys(); + timeouts = 0; + return 0; + } + while (spk_serial_tx_busy()) { + if (--tmout == 0) { + pr_warn("%s: timed out (tx busy)\n", + in_synth->long_name); + timeouts++; + return 0; + } + udelay(1); + } + tmout = SPK_CTS_TIMEOUT; + while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) { + /* CTS */ + if (--tmout == 0) { + timeouts++; + return 0; + } + udelay(1); + } + timeouts = 0; + return 1; +} + +static unsigned char spk_serial_in(void) +{ + int tmout = SPK_SERIAL_TIMEOUT; + + while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) { + if (--tmout == 0) { + pr_warn("time out while waiting for input.\n"); + return 0xff; + } + udelay(1); + } + return inb_p(speakup_info.port_tts + UART_RX); +} + +static unsigned char spk_serial_in_nowait(void) +{ + unsigned char lsr; + + lsr = inb_p(speakup_info.port_tts + UART_LSR); + if (!(lsr & UART_LSR_DR)) + return 0; + return inb_p(speakup_info.port_tts + UART_RX); +} + +static void spk_serial_flush_buffer(void) +{ + /* TODO: flush the UART 16550 buffer */ +} + +static int spk_serial_out(struct spk_synth *in_synth, const char ch) +{ + if (in_synth->alive && spk_wait_for_xmitr(in_synth)) { + outb_p(ch, speakup_info.port_tts); + return 1; + } + return 0; +} + +const char *spk_serial_synth_immediate(struct spk_synth *synth, + const char *buff) +{ + u_char ch; + + while ((ch = *buff)) { + if (ch == '\n') + ch = synth->procspeech; + if (spk_wait_for_xmitr(synth)) + outb(ch, speakup_info.port_tts); + else + return buff; + buff++; + } + return NULL; +} +EXPORT_SYMBOL_GPL(spk_serial_synth_immediate); + +void spk_serial_release(void) +{ + spk_stop_serial_interrupt(); + if (speakup_info.port_tts == 0) + return; + synth_release_region(speakup_info.port_tts, 8); + speakup_info.port_tts = 0; +} +EXPORT_SYMBOL_GPL(spk_serial_release); diff --git a/drivers/accessibility/speakup/serialio.h b/drivers/accessibility/speakup/serialio.h new file mode 100644 index 000000000000..6f8f86f161bb --- /dev/null +++ b/drivers/accessibility/speakup/serialio.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _SPEAKUP_SERIAL_H +#define _SPEAKUP_SERIAL_H + +#include /* for rs_table, serial constants */ +#include /* for more serial constants */ +#include + +#include "spk_priv.h" + +/* + * this is cut&paste from 8250.h. Get rid of the structure, the definitions + * and this whole broken driver. + */ +struct old_serial_port { + unsigned int uart; /* unused */ + unsigned int baud_base; + unsigned int port; + unsigned int irq; + upf_t flags; /* unused */ +}; + +/* countdown values for serial timeouts in us */ +#define SPK_SERIAL_TIMEOUT SPK_SYNTH_TIMEOUT +/* countdown values transmitter/dsr timeouts in us */ +#define SPK_XMITR_TIMEOUT 100000 +/* countdown values cts timeouts in us */ +#define SPK_CTS_TIMEOUT 100000 +/* check ttyS0 ... ttyS3 */ +#define SPK_LO_TTY 0 +#define SPK_HI_TTY 3 +/* # of timeouts permitted before disable */ +#define NUM_DISABLE_TIMEOUTS 3 +/* buffer timeout in ms */ +#define SPK_TIMEOUT 100 +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +#define spk_serial_tx_busy() \ + ((inb(speakup_info.port_tts + UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY) + +#endif diff --git a/drivers/accessibility/speakup/speakup.h b/drivers/accessibility/speakup/speakup.h new file mode 100644 index 000000000000..74fe49c2c511 --- /dev/null +++ b/drivers/accessibility/speakup/speakup.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _SPEAKUP_H +#define _SPEAKUP_H + +#include "spk_types.h" +#include "i18n.h" + +#define SPEAKUP_VERSION "3.1.6" +#define KEY_MAP_VER 119 +#define SHIFT_TBL_SIZE 64 +#define MAX_DESC_LEN 72 + +#define TOGGLE_0 .u.n = {NULL, 0, 0, 1, 0, 0, NULL } +#define TOGGLE_1 .u.n = {NULL, 1, 0, 1, 0, 0, NULL } +#define MAXVARLEN 15 + +#define SYNTH_OK 0x0001 +#define B_ALPHA 0x0002 +#define ALPHA 0x0003 +#define B_CAP 0x0004 +#define A_CAP 0x0007 +#define B_NUM 0x0008 +#define NUM 0x0009 +#define ALPHANUM (B_ALPHA | B_NUM) +#define SOME 0x0010 +#define MOST 0x0020 +#define PUNC 0x0040 +#define A_PUNC 0x0041 +#define B_WDLM 0x0080 +#define WDLM 0x0081 +#define B_EXNUM 0x0100 +#define CH_RPT 0x0200 +#define B_CTL 0x0400 +#define A_CTL (B_CTL + SYNTH_OK) +#define B_SYM 0x0800 +#define B_CAPSYM (B_CAP | B_SYM) + +/* FIXME: u16 */ +#define IS_WDLM(x) (spk_chartab[((u_char)x)] & B_WDLM) +#define IS_CHAR(x, type) (spk_chartab[((u_char)x)] & type) +#define IS_TYPE(x, type) ((spk_chartab[((u_char)x)] & type) == type) + +int speakup_thread(void *data); +void spk_reset_default_chars(void); +void spk_reset_default_chartab(void); +void synth_start(void); +void synth_insert_next_index(int sent_num); +void spk_reset_index_count(int sc); +void spk_get_index_count(int *linecount, int *sentcount); +int spk_set_key_info(const u_char *key_info, u_char *k_buffer); +char *spk_strlwr(char *s); +char *spk_s2uchar(char *start, char *dest); +int speakup_kobj_init(void); +void speakup_kobj_exit(void); +int spk_chartab_get_value(char *keyword); +void speakup_register_var(struct var_t *var); +void speakup_unregister_var(enum var_id_t var_id); +struct st_var_header *spk_get_var_header(enum var_id_t var_id); +struct st_var_header *spk_var_header_by_name(const char *name); +struct punc_var_t *spk_get_punc_var(enum var_id_t var_id); +int spk_set_num_var(int val, struct st_var_header *var, int how); +int spk_set_string_var(const char *page, struct st_var_header *var, int len); +int spk_set_mask_bits(const char *input, const int which, const int how); +extern special_func spk_special_handler; +int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key); +int synth_init(char *name); +void synth_release(void); + +void spk_do_flush(void); +void speakup_start_ttys(void); +void synth_buffer_add(u16 ch); +void synth_buffer_clear(void); +void speakup_clear_selection(void); +int speakup_set_selection(struct tty_struct *tty); +void speakup_cancel_selection(void); +int speakup_paste_selection(struct tty_struct *tty); +void speakup_cancel_paste(void); +void speakup_register_devsynth(void); +void speakup_unregister_devsynth(void); +void synth_write(const char *buf, size_t count); +int synth_supports_indexing(void); + +extern struct vc_data *spk_sel_cons; +extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ + +extern wait_queue_head_t speakup_event; +extern struct kobject *speakup_kobj; +extern struct task_struct *speakup_task; +extern const u_char spk_key_defaults[]; + +/* Protect speakup synthesizer list */ +extern struct mutex spk_mutex; +extern struct st_spk_t *speakup_console[]; +extern struct spk_synth *synth; +extern char spk_pitch_buff[]; +extern u_char *spk_our_keys[]; +extern short spk_punc_masks[]; +extern char spk_str_caps_start[], spk_str_caps_stop[], spk_str_pause[]; +extern bool spk_paused; +extern const struct st_bits_data spk_punc_info[]; +extern u_char spk_key_buf[600]; +extern char *spk_characters[]; +extern char *spk_default_chars[]; +extern u_short spk_chartab[]; +extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level; +extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps; +extern int spk_bleep_time, spk_bell_pos; +extern int spk_spell_delay, spk_key_echo; +extern short spk_punc_mask; +extern short spk_pitch_shift, synth_flags; +extern bool spk_quiet_boot; +extern char *synth_name; +extern struct bleep spk_unprocessed_sound; + +/* Prototypes from fakekey.c. */ +int speakup_add_virtual_keyboard(void); +void speakup_remove_virtual_keyboard(void); +void speakup_fake_down_arrow(void); +bool speakup_fake_key_pressed(void); + +#endif diff --git a/drivers/accessibility/speakup/speakup_acnt.h b/drivers/accessibility/speakup/speakup_acnt.h new file mode 100644 index 000000000000..cffa938ae580 --- /dev/null +++ b/drivers/accessibility/speakup/speakup_acnt.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* speakup_acntpc.h - header file for speakups Accent-PC driver. */ + +#define SYNTH_IO_EXTENT 0x02 + +#define SYNTH_CLEAR 0x18 /* stops speech */ + + /* Port Status Flags */ +#define SYNTH_READABLE 0x01 /* mask for bit which is nonzero if a + * byte can be read from the data port + */ +#define SYNTH_WRITABLE 0x02 /* mask for RDY bit, which when set to + * 1, indicates the data port is ready + * to accept a byte of data. + */ +#define SYNTH_QUIET 'S' /* synth is not speaking */ +#define SYNTH_FULL 'F' /* synth is full. */ +#define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */ +#define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */ diff --git a/drivers/accessibility/speakup/speakup_acntpc.c b/drivers/accessibility/speakup/speakup_acntpc.c new file mode 100644 index 000000000000..c94328a5bd4a --- /dev/null +++ b/drivers/accessibility/speakup/speakup_acntpc.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + * This driver is for the Aicom Acent PC internal synthesizer. + */ + +#include +#include +#include +#include + +#include "spk_priv.h" +#include "serialio.h" +#include "speakup.h" +#include "speakup_acnt.h" /* local header file for Accent values */ + +#define DRV_VERSION "2.10" +#define PROCSPEECH '\r' + +static int synth_probe(struct spk_synth *synth); +static void accent_release(void); +static const char *synth_immediate(struct spk_synth *synth, const char *buf); +static void do_catch_up(struct spk_synth *synth); +static void synth_flush(struct spk_synth *synth); + +static int synth_port_control; +static int port_forced; +static unsigned int synth_portlist[] = { 0x2a8, 0 }; + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\033P8" } }, + { CAPS_STOP, .u.s = {"\033P5" } }, + { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } }, + { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } }, + { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } }, + { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/acntpc. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_acntpc = { + .name = "acntpc", + .version = DRV_VERSION, + .long_name = "Accent PC", + .init = "\033=X \033Oi\033T2\033=M\033N1\n", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 1000, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_serial_io_ops, + .probe = synth_probe, + .release = accent_release, + .synth_immediate = synth_immediate, + .catch_up = do_catch_up, + .flush = synth_flush, + .is_alive = spk_synth_is_alive_nop, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "acntpc", + }, +}; + +static inline bool synth_writable(void) +{ + return inb_p(synth_port_control) & SYNTH_WRITABLE; +} + +static inline bool synth_full(void) +{ + return inb_p(speakup_info.port_tts + UART_RX) == 'F'; +} + +static const char *synth_immediate(struct spk_synth *synth, const char *buf) +{ + u_char ch; + + while ((ch = *buf)) { + int timeout = SPK_XMITR_TIMEOUT; + + if (ch == '\n') + ch = PROCSPEECH; + if (synth_full()) + return buf; + while (synth_writable()) { + if (!--timeout) + return buf; + udelay(1); + } + outb_p(ch, speakup_info.port_tts); + buf++; + } + return NULL; +} + +static void do_catch_up(struct spk_synth *synth) +{ + u_char ch; + unsigned long flags; + unsigned long jiff_max; + int timeout; + int delay_time_val; + int jiffy_delta_val; + int full_time_val; + struct var_t *delay_time; + struct var_t *full_time; + struct var_t *jiffy_delta; + + jiffy_delta = spk_get_var(JIFFY); + delay_time = spk_get_var(DELAY); + full_time = spk_get_var(FULL); + + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + + jiff_max = jiffies + jiffy_delta_val; + while (!kthread_should_stop()) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (speakup_info.flushing) { + speakup_info.flushing = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + synth->flush(synth); + continue; + } + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + break; + } + set_current_state(TASK_INTERRUPTIBLE); + full_time_val = full_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (synth_full()) { + schedule_timeout(msecs_to_jiffies(full_time_val)); + continue; + } + set_current_state(TASK_RUNNING); + timeout = SPK_XMITR_TIMEOUT; + while (synth_writable()) { + if (!--timeout) + break; + udelay(1); + } + spin_lock_irqsave(&speakup_info.spinlock, flags); + ch = synth_buffer_getc(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '\n') + ch = PROCSPEECH; + outb_p(ch, speakup_info.port_tts); + if (time_after_eq(jiffies, jiff_max) && ch == SPACE) { + timeout = SPK_XMITR_TIMEOUT; + while (synth_writable()) { + if (!--timeout) + break; + udelay(1); + } + outb_p(PROCSPEECH, speakup_info.port_tts); + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + schedule_timeout(msecs_to_jiffies(delay_time_val)); + jiff_max = jiffies + jiffy_delta_val; + } + } + timeout = SPK_XMITR_TIMEOUT; + while (synth_writable()) { + if (!--timeout) + break; + udelay(1); + } + outb_p(PROCSPEECH, speakup_info.port_tts); +} + +static void synth_flush(struct spk_synth *synth) +{ + outb_p(SYNTH_CLEAR, speakup_info.port_tts); +} + +static int synth_probe(struct spk_synth *synth) +{ + unsigned int port_val = 0; + int i = 0; + + pr_info("Probing for %s.\n", synth->long_name); + if (port_forced) { + speakup_info.port_tts = port_forced; + pr_info("probe forced to %x by kernel command line\n", + speakup_info.port_tts); + if (synth_request_region(speakup_info.port_tts - 1, + SYNTH_IO_EXTENT)) { + pr_warn("sorry, port already reserved\n"); + return -EBUSY; + } + port_val = inw(speakup_info.port_tts - 1); + synth_port_control = speakup_info.port_tts - 1; + } else { + for (i = 0; synth_portlist[i]; i++) { + if (synth_request_region(synth_portlist[i], + SYNTH_IO_EXTENT)) { + pr_warn + ("request_region: failed with 0x%x, %d\n", + synth_portlist[i], SYNTH_IO_EXTENT); + continue; + } + port_val = inw(synth_portlist[i]) & 0xfffc; + if (port_val == 0x53fc) { + /* 'S' and out&input bits */ + synth_port_control = synth_portlist[i]; + speakup_info.port_tts = synth_port_control + 1; + break; + } + } + } + port_val &= 0xfffc; + if (port_val != 0x53fc) { + /* 'S' and out&input bits */ + pr_info("%s: not found\n", synth->long_name); + synth_release_region(synth_port_control, SYNTH_IO_EXTENT); + synth_port_control = 0; + return -ENODEV; + } + pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name, + synth_port_control, synth_port_control + SYNTH_IO_EXTENT - 1, + synth->version); + synth->alive = 1; + return 0; +} + +static void accent_release(void) +{ + spk_stop_serial_interrupt(); + if (speakup_info.port_tts) + synth_release_region(speakup_info.port_tts - 1, + SYNTH_IO_EXTENT); + speakup_info.port_tts = 0; +} + +module_param_hw_named(port, port_forced, int, ioport, 0444); +module_param_named(start, synth_acntpc.startup, short, 0444); + +MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_acntpc); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for Accent PC synthesizer"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_acntsa.c b/drivers/accessibility/speakup/speakup_acntsa.c new file mode 100644 index 000000000000..3a863dc61286 --- /dev/null +++ b/drivers/accessibility/speakup/speakup_acntsa.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ + +#include "spk_priv.h" +#include "speakup.h" +#include "speakup_acnt.h" /* local header file for Accent values */ + +#define DRV_VERSION "2.11" +#define PROCSPEECH '\r' + +static int synth_probe(struct spk_synth *synth); + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\033P8" } }, + { CAPS_STOP, .u.s = {"\033P5" } }, + { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } }, + { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } }, + { VOL, .u.n = {"\033A%d", 9, 0, 9, 0, 0, NULL } }, + { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/acntsa. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_acntsa = { + .name = "acntsa", + .version = DRV_VERSION, + .long_name = "Accent-SA", + .init = "\033T2\033=M\033Oi\033N1\n", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 400, + .trigger = 50, + .jiffies = 30, + .full = 40000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = spk_do_catch_up, + .flush = spk_synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "acntsa", + }, +}; + +static int synth_probe(struct spk_synth *synth) +{ + int failed; + + failed = spk_ttyio_synth_probe(synth); + if (failed == 0) { + synth->synth_immediate(synth, "\033=R\r"); + mdelay(100); + } + synth->alive = !failed; + return failed; +} + +module_param_named(ser, synth_acntsa.ser, int, 0444); +module_param_named(dev, synth_acntsa.dev_name, charp, 0444); +module_param_named(start, synth_acntsa.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_acntsa); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for Accent SA synthesizer"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_apollo.c b/drivers/accessibility/speakup/speakup_apollo.c new file mode 100644 index 000000000000..0877b4044c28 --- /dev/null +++ b/drivers/accessibility/speakup/speakup_apollo.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ +#include +#include +#include +#include +#include /* for UART_MCR* constants */ + +#include "spk_priv.h" +#include "speakup.h" + +#define DRV_VERSION "2.21" +#define SYNTH_CLEAR 0x18 +#define PROCSPEECH '\r' + +static void do_catch_up(struct spk_synth *synth); + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"cap, " } }, + { CAPS_STOP, .u.s = {"" } }, + { RATE, .u.n = {"@W%d", 6, 1, 9, 0, 0, NULL } }, + { PITCH, .u.n = {"@F%x", 10, 0, 15, 0, 0, NULL } }, + { VOL, .u.n = {"@A%x", 10, 0, 15, 0, 0, NULL } }, + { VOICE, .u.n = {"@V%d", 1, 1, 6, 0, 0, NULL } }, + { LANG, .u.n = {"@=%d,", 1, 1, 4, 0, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/apollo. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute lang_attribute = + __ATTR(lang, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute voice_attribute = + __ATTR(voice, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &lang_attribute.attr, + &pitch_attribute.attr, + &rate_attribute.attr, + &voice_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_apollo = { + .name = "apollo", + .version = DRV_VERSION, + .long_name = "Apollo", + .init = "@R3@D0@K1\r", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 40000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = spk_ttyio_synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = do_catch_up, + .flush = spk_synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "apollo", + }, +}; + +static void do_catch_up(struct spk_synth *synth) +{ + u_char ch; + unsigned long flags; + unsigned long jiff_max; + struct var_t *jiffy_delta; + struct var_t *delay_time; + struct var_t *full_time; + int full_time_val = 0; + int delay_time_val = 0; + int jiffy_delta_val = 0; + + jiffy_delta = spk_get_var(JIFFY); + delay_time = spk_get_var(DELAY); + full_time = spk_get_var(FULL); + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + jiff_max = jiffies + jiffy_delta_val; + + while (!kthread_should_stop()) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + full_time_val = full_time->u.n.value; + delay_time_val = delay_time->u.n.value; + if (speakup_info.flushing) { + speakup_info.flushing = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + synth->flush(synth); + continue; + } + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + break; + } + ch = synth_buffer_peek(); + set_current_state(TASK_INTERRUPTIBLE); + full_time_val = full_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (!synth->io_ops->synth_out(synth, ch)) { + synth->io_ops->tiocmset(0, UART_MCR_RTS); + synth->io_ops->tiocmset(UART_MCR_RTS, 0); + schedule_timeout(msecs_to_jiffies(full_time_val)); + continue; + } + if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + full_time_val = full_time->u.n.value; + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (synth->io_ops->synth_out(synth, synth->procspeech)) + schedule_timeout(msecs_to_jiffies + (delay_time_val)); + else + schedule_timeout(msecs_to_jiffies + (full_time_val)); + jiff_max = jiffies + jiffy_delta_val; + } + set_current_state(TASK_RUNNING); + spin_lock_irqsave(&speakup_info.spinlock, flags); + synth_buffer_getc(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + } + synth->io_ops->synth_out(synth, PROCSPEECH); +} + +module_param_named(ser, synth_apollo.ser, int, 0444); +module_param_named(dev, synth_apollo.dev_name, charp, 0444); +module_param_named(start, synth_apollo.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_apollo); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for Apollo II synthesizer"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_audptr.c b/drivers/accessibility/speakup/speakup_audptr.c new file mode 100644 index 000000000000..e6a6a9665d8f --- /dev/null +++ b/drivers/accessibility/speakup/speakup_audptr.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * specificly written as a driver for the speakup screenreview + * s not a general device driver. + */ +#include "spk_priv.h" +#include "speakup.h" + +#define DRV_VERSION "2.11" +#define SYNTH_CLEAR 0x18 /* flush synth buffer */ +#define PROCSPEECH '\r' /* start synth processing speech char */ + +static int synth_probe(struct spk_synth *synth); +static void synth_flush(struct spk_synth *synth); + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\x05[f99]" } }, + { CAPS_STOP, .u.s = {"\x05[f80]" } }, + { RATE, .u.n = {"\x05[r%d]", 10, 0, 20, 100, -10, NULL } }, + { PITCH, .u.n = {"\x05[f%d]", 80, 39, 4500, 0, 0, NULL } }, + { VOL, .u.n = {"\x05[g%d]", 21, 0, 40, 0, 0, NULL } }, + { TONE, .u.n = {"\x05[s%d]", 9, 0, 63, 0, 0, NULL } }, + { PUNCT, .u.n = {"\x05[A%c]", 0, 0, 3, 0, 0, "nmsa" } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/audptr. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &punct_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_audptr = { + .name = "audptr", + .version = DRV_VERSION, + .long_name = "Audapter", + .init = "\x05[D1]\x05[Ol]", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 400, + .trigger = 50, + .jiffies = 30, + .full = 18000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = spk_do_catch_up, + .flush = synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "audptr", + }, +}; + +static void synth_flush(struct spk_synth *synth) +{ + synth->io_ops->flush_buffer(); + synth->io_ops->send_xchar(SYNTH_CLEAR); + synth->io_ops->synth_out(synth, PROCSPEECH); +} + +static void synth_version(struct spk_synth *synth) +{ + unsigned char test = 0; + char synth_id[40] = ""; + + synth->synth_immediate(synth, "\x05[Q]"); + synth_id[test] = synth->io_ops->synth_in(); + if (synth_id[test] == 'A') { + do { + /* read version string from synth */ + synth_id[++test] = synth->io_ops->synth_in(); + } while (synth_id[test] != '\n' && test < 32); + synth_id[++test] = 0x00; + } + if (synth_id[0] == 'A') + pr_info("%s version: %s", synth->long_name, synth_id); +} + +static int synth_probe(struct spk_synth *synth) +{ + int failed; + + failed = spk_ttyio_synth_probe(synth); + if (failed == 0) + synth_version(synth); + synth->alive = !failed; + return 0; +} + +module_param_named(ser, synth_audptr.ser, int, 0444); +module_param_named(dev, synth_audptr.dev_name, charp, 0444); +module_param_named(start, synth_audptr.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_audptr); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for Audapter synthesizer"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_bns.c b/drivers/accessibility/speakup/speakup_bns.c new file mode 100644 index 000000000000..76dfa3f7c058 --- /dev/null +++ b/drivers/accessibility/speakup/speakup_bns.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ +#include "spk_priv.h" +#include "speakup.h" + +#define DRV_VERSION "2.11" +#define SYNTH_CLEAR 0x18 +#define PROCSPEECH '\r' + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\x05\x31\x32P" } }, + { CAPS_STOP, .u.s = {"\x05\x38P" } }, + { RATE, .u.n = {"\x05%dE", 8, 1, 16, 0, 0, NULL } }, + { PITCH, .u.n = {"\x05%dP", 8, 0, 16, 0, 0, NULL } }, + { VOL, .u.n = {"\x05%dV", 8, 0, 16, 0, 0, NULL } }, + { TONE, .u.n = {"\x05%dT", 8, 0, 16, 0, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/bns. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_bns = { + .name = "bns", + .version = DRV_VERSION, + .long_name = "Braille 'N Speak", + .init = "\x05Z\x05\x43", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 40000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = spk_ttyio_synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = spk_do_catch_up, + .flush = spk_synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "bns", + }, +}; + +module_param_named(ser, synth_bns.ser, int, 0444); +module_param_named(dev, synth_bns.dev_name, charp, 0444); +module_param_named(start, synth_bns.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_bns); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for Braille 'n Speak synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_decext.c b/drivers/accessibility/speakup/speakup_decext.c new file mode 100644 index 000000000000..7408eb29cf38 --- /dev/null +++ b/drivers/accessibility/speakup/speakup_decext.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * specificly written as a driver for the speakup screenreview + * s not a general device driver. + */ +#include +#include +#include +#include + +#include "spk_priv.h" +#include "speakup.h" + +#define DRV_VERSION "2.14" +#define SYNTH_CLEAR 0x03 +#define PROCSPEECH 0x0b + +static volatile unsigned char last_char; + +static void read_buff_add(u_char ch) +{ + last_char = ch; +} + +static inline bool synth_full(void) +{ + return last_char == 0x13; +} + +static void do_catch_up(struct spk_synth *synth); +static void synth_flush(struct spk_synth *synth); + +static int in_escape; + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"[:dv ap 222]" } }, + { CAPS_STOP, .u.s = {"[:dv ap 100]" } }, + { RATE, .u.n = {"[:ra %d]", 7, 0, 9, 150, 25, NULL } }, + { PITCH, .u.n = {"[:dv ap %d]", 100, 0, 100, 0, 0, NULL } }, + { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } }, + { VOL, .u.n = {"[:dv gv %d]", 13, 0, 16, 0, 5, NULL } }, + { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } }, + { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/decext. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute inflection_attribute = + __ATTR(inflection, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute voice_attribute = + __ATTR(voice, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &inflection_attribute.attr, + &punct_attribute.attr, + &rate_attribute.attr, + &voice_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_decext = { + .name = "decext", + .version = DRV_VERSION, + .long_name = "Dectalk External", + .init = "[:pe -380]", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 40000, + .flags = SF_DEC, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = spk_ttyio_synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = do_catch_up, + .flush = synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = read_buff_add, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "decext", + }, +}; + +static void do_catch_up(struct spk_synth *synth) +{ + u_char ch; + static u_char last = '\0'; + unsigned long flags; + unsigned long jiff_max; + struct var_t *jiffy_delta; + struct var_t *delay_time; + int jiffy_delta_val = 0; + int delay_time_val = 0; + + jiffy_delta = spk_get_var(JIFFY); + delay_time = spk_get_var(DELAY); + + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + jiff_max = jiffies + jiffy_delta_val; + + while (!kthread_should_stop()) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (speakup_info.flushing) { + speakup_info.flushing = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + synth->flush(synth); + continue; + } + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + break; + } + ch = synth_buffer_peek(); + set_current_state(TASK_INTERRUPTIBLE); + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '\n') + ch = 0x0D; + if (synth_full() || !synth->io_ops->synth_out(synth, ch)) { + schedule_timeout(msecs_to_jiffies(delay_time_val)); + continue; + } + set_current_state(TASK_RUNNING); + spin_lock_irqsave(&speakup_info.spinlock, flags); + synth_buffer_getc(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '[') { + in_escape = 1; + } else if (ch == ']') { + in_escape = 0; + } else if (ch <= SPACE) { + if (!in_escape && strchr(",.!?;:", last)) + synth->io_ops->synth_out(synth, PROCSPEECH); + if (time_after_eq(jiffies, jiff_max)) { + if (!in_escape) + synth->io_ops->synth_out(synth, + PROCSPEECH); + spin_lock_irqsave(&speakup_info.spinlock, + flags); + jiffy_delta_val = jiffy_delta->u.n.value; + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, + flags); + schedule_timeout(msecs_to_jiffies + (delay_time_val)); + jiff_max = jiffies + jiffy_delta_val; + } + } + last = ch; + } + if (!in_escape) + synth->io_ops->synth_out(synth, PROCSPEECH); +} + +static void synth_flush(struct spk_synth *synth) +{ + in_escape = 0; + synth->io_ops->flush_buffer(); + synth->synth_immediate(synth, "\033P;10z\033\\"); +} + +module_param_named(ser, synth_decext.ser, int, 0444); +module_param_named(dev, synth_decext.dev_name, charp, 0444); +module_param_named(start, synth_decext.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_decext); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for DECtalk External synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_decpc.c b/drivers/accessibility/speakup/speakup_decpc.c new file mode 100644 index 000000000000..96f24c848cc5 --- /dev/null +++ b/drivers/accessibility/speakup/speakup_decpc.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * This is the DECtalk PC speakup driver + * + * Some constants from DEC's DOS driver: + * Copyright (c) by Digital Equipment Corp. + * + * 386BSD DECtalk PC driver: + * Copyright (c) 1996 Brian Buhrow + * + * Linux DECtalk PC driver: + * Copyright (c) 1997 Nicolas Pitre + * + * speakup DECtalk PC Internal driver: + * Copyright (c) 2003 David Borowski + * + * All rights reserved. + */ +#include +#include +#include +#include + +#include "spk_priv.h" +#include "speakup.h" + +#define MODULE_init 0x0dec /* module in boot code */ +#define MODULE_self_test 0x8800 /* module in self-test */ +#define MODULE_reset 0xffff /* reinit the whole module */ + +#define MODE_mask 0xf000 /* mode bits in high nibble */ +#define MODE_null 0x0000 +#define MODE_test 0x2000 /* in testing mode */ +#define MODE_status 0x8000 +#define STAT_int 0x0001 /* running in interrupt mode */ +#define STAT_tr_char 0x0002 /* character data to transmit */ +#define STAT_rr_char 0x0004 /* ready to receive char data */ +#define STAT_cmd_ready 0x0008 /* ready to accept commands */ +#define STAT_dma_ready 0x0010 /* dma command ready */ +#define STAT_digitized 0x0020 /* spc in digitized mode */ +#define STAT_new_index 0x0040 /* new last index ready */ +#define STAT_new_status 0x0080 /* new status posted */ +#define STAT_dma_state 0x0100 /* dma state toggle */ +#define STAT_index_valid 0x0200 /* indexs are valid */ +#define STAT_flushing 0x0400 /* flush in progress */ +#define STAT_self_test 0x0800 /* module in self test */ +#define MODE_ready 0xc000 /* module ready for next phase */ +#define READY_boot 0x0000 +#define READY_kernel 0x0001 +#define MODE_error 0xf000 + +#define CMD_mask 0xf000 /* mask for command nibble */ +#define CMD_null 0x0000 /* post status */ +#define CMD_control 0x1000 /* hard control command */ +#define CTRL_mask 0x0F00 /* mask off control nibble */ +#define CTRL_data 0x00FF /* mask to get data byte */ +#define CTRL_null 0x0000 /* null control */ +#define CTRL_vol_up 0x0100 /* increase volume */ +#define CTRL_vol_down 0x0200 /* decrease volume */ +#define CTRL_vol_set 0x0300 /* set volume */ +#define CTRL_pause 0x0400 /* pause spc */ +#define CTRL_resume 0x0500 /* resume spc clock */ +#define CTRL_resume_spc 0x0001 /* resume spc soft pause */ +#define CTRL_flush 0x0600 /* flush all buffers */ +#define CTRL_int_enable 0x0700 /* enable status change ints */ +#define CTRL_buff_free 0x0800 /* buffer remain count */ +#define CTRL_buff_used 0x0900 /* buffer in use */ +#define CTRL_speech 0x0a00 /* immediate speech change */ +#define CTRL_SP_voice 0x0001 /* voice change */ +#define CTRL_SP_rate 0x0002 /* rate change */ +#define CTRL_SP_comma 0x0003 /* comma pause change */ +#define CTRL_SP_period 0x0004 /* period pause change */ +#define CTRL_SP_rate_delta 0x0005 /* delta rate change */ +#define CTRL_SP_get_param 0x0006 /* return the desired parameter */ +#define CTRL_last_index 0x0b00 /* get last index spoken */ +#define CTRL_io_priority 0x0c00 /* change i/o priority */ +#define CTRL_free_mem 0x0d00 /* get free paragraphs on module */ +#define CTRL_get_lang 0x0e00 /* return bitmask of loaded languages */ +#define CMD_test 0x2000 /* self-test request */ +#define TEST_mask 0x0F00 /* isolate test field */ +#define TEST_null 0x0000 /* no test requested */ +#define TEST_isa_int 0x0100 /* assert isa irq */ +#define TEST_echo 0x0200 /* make data in == data out */ +#define TEST_seg 0x0300 /* set peek/poke segment */ +#define TEST_off 0x0400 /* set peek/poke offset */ +#define TEST_peek 0x0500 /* data out == *peek */ +#define TEST_poke 0x0600 /* *peek == data in */ +#define TEST_sub_code 0x00FF /* user defined test sub codes */ +#define CMD_id 0x3000 /* return software id */ +#define ID_null 0x0000 /* null id */ +#define ID_kernel 0x0100 /* kernel code executing */ +#define ID_boot 0x0200 /* boot code executing */ +#define CMD_dma 0x4000 /* force a dma start */ +#define CMD_reset 0x5000 /* reset module status */ +#define CMD_sync 0x6000 /* kernel sync command */ +#define CMD_char_in 0x7000 /* single character send */ +#define CMD_char_out 0x8000 /* single character get */ +#define CHAR_count_1 0x0100 /* one char in cmd_low */ +#define CHAR_count_2 0x0200 /* the second in data_low */ +#define CHAR_count_3 0x0300 /* the third in data_high */ +#define CMD_spc_mode 0x9000 /* change spc mode */ +#define CMD_spc_to_text 0x0100 /* set to text mode */ +#define CMD_spc_to_digit 0x0200 /* set to digital mode */ +#define CMD_spc_rate 0x0400 /* change spc data rate */ +#define CMD_error 0xf000 /* severe error */ + +enum { PRIMARY_DIC = 0, USER_DIC, COMMAND_DIC, ABBREV_DIC }; + +#define DMA_single_in 0x01 +#define DMA_single_out 0x02 +#define DMA_buff_in 0x03 +#define DMA_buff_out 0x04 +#define DMA_control 0x05 +#define DT_MEM_ALLOC 0x03 +#define DT_SET_DIC 0x04 +#define DT_START_TASK 0x05 +#define DT_LOAD_MEM 0x06 +#define DT_READ_MEM 0x07 +#define DT_DIGITAL_IN 0x08 +#define DMA_sync 0x06 +#define DMA_sync_char 0x07 + +#define DRV_VERSION "2.12" +#define PROCSPEECH 0x0b +#define SYNTH_IO_EXTENT 8 + +static int synth_probe(struct spk_synth *synth); +static void dtpc_release(void); +static const char *synth_immediate(struct spk_synth *synth, const char *buf); +static void do_catch_up(struct spk_synth *synth); +static void synth_flush(struct spk_synth *synth); + +static int synth_portlist[] = { 0x340, 0x350, 0x240, 0x250, 0 }; +static int in_escape, is_flushing; +static int dt_stat, dma_state; + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"[:dv ap 200]" } }, + { CAPS_STOP, .u.s = {"[:dv ap 100]" } }, + { RATE, .u.n = {"[:ra %d]", 9, 0, 18, 150, 25, NULL } }, + { PITCH, .u.n = {"[:dv ap %d]", 80, 0, 100, 20, 0, NULL } }, + { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } }, + { VOL, .u.n = {"[:vo se %d]", 5, 0, 9, 5, 10, NULL } }, + { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } }, + { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/decpc. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute inflection_attribute = + __ATTR(inflection, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute voice_attribute = + __ATTR(voice, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &inflection_attribute.attr, + &punct_attribute.attr, + &rate_attribute.attr, + &voice_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_dec_pc = { + .name = "decpc", + .version = DRV_VERSION, + .long_name = "Dectalk PC", + .init = "[:pe -380]", + .procspeech = PROCSPEECH, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 1000, + .flags = SF_DEC, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_serial_io_ops, + .probe = synth_probe, + .release = dtpc_release, + .synth_immediate = synth_immediate, + .catch_up = do_catch_up, + .flush = synth_flush, + .is_alive = spk_synth_is_alive_nop, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "decpc", + }, +}; + +static int dt_getstatus(void) +{ + dt_stat = inb_p(speakup_info.port_tts) | + (inb_p(speakup_info.port_tts + 1) << 8); + return dt_stat; +} + +static void dt_sendcmd(u_int cmd) +{ + outb_p(cmd & 0xFF, speakup_info.port_tts); + outb_p((cmd >> 8) & 0xFF, speakup_info.port_tts + 1); +} + +static int dt_waitbit(int bit) +{ + int timeout = 100; + + while (--timeout > 0) { + if ((dt_getstatus() & bit) == bit) + return 1; + udelay(50); + } + return 0; +} + +static int dt_wait_dma(void) +{ + int timeout = 100, state = dma_state; + + if (!dt_waitbit(STAT_dma_ready)) + return 0; + while (--timeout > 0) { + if ((dt_getstatus() & STAT_dma_state) == state) + return 1; + udelay(50); + } + dma_state = dt_getstatus() & STAT_dma_state; + return 1; +} + +static int dt_ctrl(u_int cmd) +{ + int timeout = 10; + + if (!dt_waitbit(STAT_cmd_ready)) + return -1; + outb_p(0, speakup_info.port_tts + 2); + outb_p(0, speakup_info.port_tts + 3); + dt_getstatus(); + dt_sendcmd(CMD_control | cmd); + outb_p(0, speakup_info.port_tts + 6); + while (dt_getstatus() & STAT_cmd_ready) { + udelay(20); + if (--timeout == 0) + break; + } + dt_sendcmd(CMD_null); + return 0; +} + +static void synth_flush(struct spk_synth *synth) +{ + int timeout = 10; + + if (is_flushing) + return; + is_flushing = 4; + in_escape = 0; + while (dt_ctrl(CTRL_flush)) { + if (--timeout == 0) + break; + udelay(50); + } + for (timeout = 0; timeout < 10; timeout++) { + if (dt_waitbit(STAT_dma_ready)) + break; + udelay(50); + } + outb_p(DMA_sync, speakup_info.port_tts + 4); + outb_p(0, speakup_info.port_tts + 4); + udelay(100); + for (timeout = 0; timeout < 10; timeout++) { + if (!(dt_getstatus() & STAT_flushing)) + break; + udelay(50); + } + dma_state = dt_getstatus() & STAT_dma_state; + dma_state ^= STAT_dma_state; + is_flushing = 0; +} + +static int dt_sendchar(char ch) +{ + if (!dt_wait_dma()) + return -1; + if (!(dt_stat & STAT_rr_char)) + return -2; + outb_p(DMA_single_in, speakup_info.port_tts + 4); + outb_p(ch, speakup_info.port_tts + 4); + dma_state ^= STAT_dma_state; + return 0; +} + +static int testkernel(void) +{ + int status = 0; + + if (dt_getstatus() == 0xffff) { + status = -1; + goto oops; + } + dt_sendcmd(CMD_sync); + if (!dt_waitbit(STAT_cmd_ready)) + status = -2; + else if (dt_stat & 0x8000) + return 0; + else if (dt_stat == 0x0dec) + pr_warn("dec_pc at 0x%x, software not loaded\n", + speakup_info.port_tts); + status = -3; +oops: synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT); + speakup_info.port_tts = 0; + return status; +} + +static void do_catch_up(struct spk_synth *synth) +{ + u_char ch; + static u_char last; + unsigned long flags; + unsigned long jiff_max; + struct var_t *jiffy_delta; + struct var_t *delay_time; + int jiffy_delta_val; + int delay_time_val; + + jiffy_delta = spk_get_var(JIFFY); + delay_time = spk_get_var(DELAY); + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + jiff_max = jiffies + jiffy_delta_val; + + while (!kthread_should_stop()) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (speakup_info.flushing) { + speakup_info.flushing = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + synth->flush(synth); + continue; + } + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + break; + } + ch = synth_buffer_peek(); + set_current_state(TASK_INTERRUPTIBLE); + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '\n') + ch = 0x0D; + if (dt_sendchar(ch)) { + schedule_timeout(msecs_to_jiffies(delay_time_val)); + continue; + } + set_current_state(TASK_RUNNING); + spin_lock_irqsave(&speakup_info.spinlock, flags); + synth_buffer_getc(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '[') { + in_escape = 1; + } else if (ch == ']') { + in_escape = 0; + } else if (ch <= SPACE) { + if (!in_escape && strchr(",.!?;:", last)) + dt_sendchar(PROCSPEECH); + if (time_after_eq(jiffies, jiff_max)) { + if (!in_escape) + dt_sendchar(PROCSPEECH); + spin_lock_irqsave(&speakup_info.spinlock, + flags); + jiffy_delta_val = jiffy_delta->u.n.value; + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, + flags); + schedule_timeout(msecs_to_jiffies + (delay_time_val)); + jiff_max = jiffies + jiffy_delta_val; + } + } + last = ch; + ch = 0; + } + if (!in_escape) + dt_sendchar(PROCSPEECH); +} + +static const char *synth_immediate(struct spk_synth *synth, const char *buf) +{ + u_char ch; + + while ((ch = *buf)) { + if (ch == '\n') + ch = PROCSPEECH; + if (dt_sendchar(ch)) + return buf; + buf++; + } + return NULL; +} + +static int synth_probe(struct spk_synth *synth) +{ + int i = 0, failed = 0; + + pr_info("Probing for %s.\n", synth->long_name); + for (i = 0; synth_portlist[i]; i++) { + if (synth_request_region(synth_portlist[i], SYNTH_IO_EXTENT)) { + pr_warn("request_region: failed with 0x%x, %d\n", + synth_portlist[i], SYNTH_IO_EXTENT); + continue; + } + speakup_info.port_tts = synth_portlist[i]; + failed = testkernel(); + if (failed == 0) + break; + } + if (failed) { + pr_info("%s: not found\n", synth->long_name); + return -ENODEV; + } + pr_info("%s: %03x-%03x, Driver Version %s,\n", synth->long_name, + speakup_info.port_tts, speakup_info.port_tts + 7, + synth->version); + synth->alive = 1; + return 0; +} + +static void dtpc_release(void) +{ + spk_stop_serial_interrupt(); + if (speakup_info.port_tts) + synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT); + speakup_info.port_tts = 0; +} + +module_param_named(start, synth_dec_pc.startup, short, 0444); + +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_dec_pc); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for DECtalk PC synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/accessibility/speakup/speakup_dectlk.c b/drivers/accessibility/speakup/speakup_dectlk.c new file mode 100644 index 000000000000..780214b5ca16 --- /dev/null +++ b/drivers/accessibility/speakup/speakup_dectlk.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * specificly written as a driver for the speakup screenreview + * s not a general device driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include "speakup.h" +#include "spk_priv.h" + +#define DRV_VERSION "2.20" +#define SYNTH_CLEAR 0x03 +#define PROCSPEECH 0x0b +static int xoff; + +static inline int synth_full(void) +{ + return xoff; +} + +static void do_catch_up(struct spk_synth *synth); +static void synth_flush(struct spk_synth *synth); +static void read_buff_add(u_char c); +static unsigned char get_index(struct spk_synth *synth); + +static int in_escape; +static int is_flushing; + +static spinlock_t flush_lock; +static DECLARE_WAIT_QUEUE_HEAD(flush); + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"[:dv ap 160] " } }, + { CAPS_STOP, .u.s = {"[:dv ap 100 ] " } }, + { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL } }, + { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } }, + { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL } }, + { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" } }, + { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/dectlk. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute inflection_attribute = + __ATTR(inflection, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute voice_attribute = + __ATTR(voice, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &inflection_attribute.attr, + &punct_attribute.attr, + &rate_attribute.attr, + &voice_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static int ap_defaults[] = {122, 89, 155, 110, 208, 240, 200, 106, 306}; +static int g5_defaults[] = {86, 81, 86, 84, 81, 80, 83, 83, 73}; + +static struct spk_synth synth_dectlk = { + .name = "dectlk", + .version = DRV_VERSION, + .long_name = "Dectalk Express", + .init = "[:error sp :name paul :rate 180 :tsr off] ", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 40000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .default_pitch = ap_defaults, + .default_vol = g5_defaults, + .io_ops = &spk_ttyio_ops, + .probe = spk_ttyio_synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = do_catch_up, + .flush = synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = read_buff_add, + .get_index = get_index, + .indexing = { + .command = "[:in re %d ] ", + .lowindex = 1, + .highindex = 8, + .currindex = 1, + }, + .attributes = { + .attrs = synth_attrs, + .name = "dectlk", + }, +}; + +static int is_indnum(u_char *ch) +{ + if ((*ch >= '0') && (*ch <= '9')) { + *ch = *ch - '0'; + return 1; + } + return 0; +} + +static u_char lastind; + +static unsigned char get_index(struct spk_synth *synth) +{ + u_char rv; + + rv = lastind; + lastind = 0; + return rv; +} + +static void read_buff_add(u_char c) +{ + static int ind = -1; + + if (c == 0x01) { + unsigned long flags; + + spin_lock_irqsave(&flush_lock, flags); + is_flushing = 0; + wake_up_interruptible(&flush); + spin_unlock_irqrestore(&flush_lock, flags); + } else if (c == 0x13) { + xoff = 1; + } else if (c == 0x11) { + xoff = 0; + } else if (is_indnum(&c)) { + if (ind == -1) + ind = c; + else + ind = ind * 10 + c; + } else if ((c > 31) && (c < 127)) { + if (ind != -1) + lastind = (u_char)ind; + ind = -1; + } +} + +static void do_catch_up(struct spk_synth *synth) +{ + int synth_full_val = 0; + static u_char ch; + static u_char last = '\0'; + unsigned long flags; + unsigned long jiff_max; + unsigned long timeout = msecs_to_jiffies(4000); + DEFINE_WAIT(wait); + struct var_t *jiffy_delta; + struct var_t *delay_time; + int jiffy_delta_val; + int delay_time_val; + + jiffy_delta = spk_get_var(JIFFY); + delay_time = spk_get_var(DELAY); + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + jiff_max = jiffies + jiffy_delta_val; + + while (!kthread_should_stop()) { + /* if no ctl-a in 4, send data anyway */ + spin_lock_irqsave(&flush_lock, flags); + while (is_flushing && timeout) { + prepare_to_wait(&flush, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&flush_lock, flags); + timeout = schedule_timeout(timeout); + spin_lock_irqsave(&flush_lock, flags); + } + finish_wait(&flush, &wait); + is_flushing = 0; + spin_unlock_irqrestore(&flush_lock, flags); + + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (speakup_info.flushing) { + speakup_info.flushing = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + synth->flush(synth); + continue; + } + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + break; + } + ch = synth_buffer_peek(); + set_current_state(TASK_INTERRUPTIBLE); + delay_time_val = delay_time->u.n.value; + synth_full_val = synth_full(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '\n') + ch = 0x0D; + if (synth_full_val || !synth->io_ops->synth_out(synth, ch)) { + schedule_timeout(msecs_to_jiffies(delay_time_val)); + continue; + } + set_current_state(TASK_RUNNING); + spin_lock_irqsave(&speakup_info.spinlock, flags); + synth_buffer_getc(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '[') { + in_escape = 1; + } else if (ch == ']') { + in_escape = 0; + } else if (ch <= SPACE) { + if (!in_escape && strchr(",.!?;:", last)) + synth->io_ops->synth_out(synth, PROCSPEECH); + if (time_after_eq(jiffies, jiff_max)) { + if (!in_escape) + synth->io_ops->synth_out(synth, + PROCSPEECH); + spin_lock_irqsave(&speakup_info.spinlock, + flags); + jiffy_delta_val = jiffy_delta->u.n.value; + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, + flags); + schedule_timeout(msecs_to_jiffies + (delay_time_val)); + jiff_max = jiffies + jiffy_delta_val; + } + } + last = ch; + } + if (!in_escape) + synth->io_ops->synth_out(synth, PROCSPEECH); +} + +static void synth_flush(struct spk_synth *synth) +{ + if (in_escape) + /* if in command output ']' so we don't get an error */ + synth->io_ops->synth_out(synth, ']'); + in_escape = 0; + is_flushing = 1; + synth->io_ops->flush_buffer(); + synth->io_ops->synth_out(synth, SYNTH_CLEAR); +} + +module_param_named(ser, synth_dectlk.ser, int, 0444); +module_param_named(dev, synth_dectlk.dev_name, charp, 0444); +module_param_named(start, synth_dectlk.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_dectlk); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_dtlk.c b/drivers/accessibility/speakup/speakup_dtlk.c new file mode 100644 index 000000000000..dbebed0eeeec --- /dev/null +++ b/drivers/accessibility/speakup/speakup_dtlk.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * specificly written as a driver for the speakup screenreview + * package it's not a general device driver. + * This driver is for the RC Systems DoubleTalk PC internal synthesizer. + */ +#include +#include +#include +#include + +#include "spk_priv.h" +#include "serialio.h" +#include "speakup_dtlk.h" /* local header file for DoubleTalk values */ +#include "speakup.h" + +#define DRV_VERSION "2.10" +#define PROCSPEECH 0x00 + +static int synth_probe(struct spk_synth *synth); +static void dtlk_release(void); +static const char *synth_immediate(struct spk_synth *synth, const char *buf); +static void do_catch_up(struct spk_synth *synth); +static void synth_flush(struct spk_synth *synth); + +static int synth_lpc; +static int port_forced; +static unsigned int synth_portlist[] = { + 0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0 +}; + +static u_char synth_status; + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\x01+35p" } }, + { CAPS_STOP, .u.s = {"\x01-35p" } }, + { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } }, + { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } }, + { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, + { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, + { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } }, + { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, + { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/dtlk. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute freq_attribute = + __ATTR(freq, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute voice_attribute = + __ATTR(voice, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &freq_attribute.attr, + &pitch_attribute.attr, + &punct_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &voice_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_dtlk = { + .name = "dtlk", + .version = DRV_VERSION, + .long_name = "DoubleTalk PC", + .init = "\x01@\x01\x31y", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 30, + .jiffies = 50, + .full = 1000, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_serial_io_ops, + .probe = synth_probe, + .release = dtlk_release, + .synth_immediate = synth_immediate, + .catch_up = do_catch_up, + .flush = synth_flush, + .is_alive = spk_synth_is_alive_nop, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = spk_synth_get_index, + .indexing = { + .command = "\x01%di", + .lowindex = 1, + .highindex = 5, + .currindex = 1, + }, + .attributes = { + .attrs = synth_attrs, + .name = "dtlk", + }, +}; + +static inline bool synth_readable(void) +{ + synth_status = inb_p(speakup_info.port_tts + UART_RX); + return (synth_status & TTS_READABLE) != 0; +} + +static inline bool synth_writable(void) +{ + synth_status = inb_p(speakup_info.port_tts + UART_RX); + return (synth_status & TTS_WRITABLE) != 0; +} + +static inline bool synth_full(void) +{ + synth_status = inb_p(speakup_info.port_tts + UART_RX); + return (synth_status & TTS_ALMOST_FULL) != 0; +} + +static void spk_out(const char ch) +{ + int timeout = SPK_XMITR_TIMEOUT; + + while (!synth_writable()) { + if (!--timeout) + break; + udelay(1); + } + outb_p(ch, speakup_info.port_tts); + timeout = SPK_XMITR_TIMEOUT; + while (synth_writable()) { + if (!--timeout) + break; + udelay(1); + } +} + +static void do_catch_up(struct spk_synth *synth) +{ + u_char ch; + unsigned long flags; + unsigned long jiff_max; + struct var_t *jiffy_delta; + struct var_t *delay_time; + int jiffy_delta_val; + int delay_time_val; + + jiffy_delta = spk_get_var(JIFFY); + delay_time = spk_get_var(DELAY); + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + jiff_max = jiffies + jiffy_delta_val; + while (!kthread_should_stop()) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (speakup_info.flushing) { + speakup_info.flushing = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + synth->flush(synth); + continue; + } + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + break; + } + set_current_state(TASK_INTERRUPTIBLE); + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (synth_full()) { + schedule_timeout(msecs_to_jiffies(delay_time_val)); + continue; + } + set_current_state(TASK_RUNNING); + spin_lock_irqsave(&speakup_info.spinlock, flags); + ch = synth_buffer_getc(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '\n') + ch = PROCSPEECH; + spk_out(ch); + if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { + spk_out(PROCSPEECH); + spin_lock_irqsave(&speakup_info.spinlock, flags); + delay_time_val = delay_time->u.n.value; + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + schedule_timeout(msecs_to_jiffies(delay_time_val)); + jiff_max = jiffies + jiffy_delta_val; + } + } + spk_out(PROCSPEECH); +} + +static const char *synth_immediate(struct spk_synth *synth, const char *buf) +{ + u_char ch; + + while ((ch = (u_char)*buf)) { + if (synth_full()) + return buf; + if (ch == '\n') + ch = PROCSPEECH; + spk_out(ch); + buf++; + } + return NULL; +} + +static void synth_flush(struct spk_synth *synth) +{ + outb_p(SYNTH_CLEAR, speakup_info.port_tts); + while (synth_writable()) + cpu_relax(); +} + +static char synth_read_tts(void) +{ + u_char ch; + + while (!synth_readable()) + cpu_relax(); + ch = synth_status & 0x7f; + outb_p(ch, speakup_info.port_tts); + while (synth_readable()) + cpu_relax(); + return (char)ch; +} + +/* interrogate the DoubleTalk PC and return its settings */ +static struct synth_settings *synth_interrogate(struct spk_synth *synth) +{ + u_char *t; + static char buf[sizeof(struct synth_settings) + 1]; + int total, i; + static struct synth_settings status; + + synth_immediate(synth, "\x18\x01?"); + for (total = 0, i = 0; i < 50; i++) { + buf[total] = synth_read_tts(); + if (total > 2 && buf[total] == 0x7f) + break; + if (total < sizeof(struct synth_settings)) + total++; + } + t = buf; + /* serial number is little endian */ + status.serial_number = t[0] + t[1] * 256; + t += 2; + for (i = 0; *t != '\r'; t++) { + status.rom_version[i] = *t; + if (i < sizeof(status.rom_version) - 1) + i++; + } + status.rom_version[i] = 0; + t++; + status.mode = *t++; + status.punc_level = *t++; + status.formant_freq = *t++; + status.pitch = *t++; + status.speed = *t++; + status.volume = *t++; + status.tone = *t++; + status.expression = *t++; + status.ext_dict_loaded = *t++; + status.ext_dict_status = *t++; + status.free_ram = *t++; + status.articulation = *t++; + status.reverb = *t++; + status.eob = *t++; + return &status; +} + +static int synth_probe(struct spk_synth *synth) +{ + unsigned int port_val = 0; + int i = 0; + struct synth_settings *sp; + + pr_info("Probing for DoubleTalk.\n"); + if (port_forced) { + speakup_info.port_tts = port_forced; + pr_info("probe forced to %x by kernel command line\n", + speakup_info.port_tts); + if ((port_forced & 0xf) != 0xf) + pr_info("warning: port base should probably end with f\n"); + if (synth_request_region(speakup_info.port_tts - 1, + SYNTH_IO_EXTENT)) { + pr_warn("sorry, port already reserved\n"); + return -EBUSY; + } + port_val = inw(speakup_info.port_tts - 1); + synth_lpc = speakup_info.port_tts - 1; + } else { + for (i = 0; synth_portlist[i]; i++) { + if (synth_request_region(synth_portlist[i], + SYNTH_IO_EXTENT)) + continue; + port_val = inw(synth_portlist[i]) & 0xfbff; + if (port_val == 0x107f) { + synth_lpc = synth_portlist[i]; + speakup_info.port_tts = synth_lpc + 1; + break; + } + synth_release_region(synth_portlist[i], + SYNTH_IO_EXTENT); + } + } + port_val &= 0xfbff; + if (port_val != 0x107f) { + pr_info("DoubleTalk PC: not found\n"); + if (synth_lpc) + synth_release_region(synth_lpc, SYNTH_IO_EXTENT); + return -ENODEV; + } + while (inw_p(synth_lpc) != 0x147f) + cpu_relax(); /* wait until it's ready */ + sp = synth_interrogate(synth); + pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n", + synth->long_name, synth_lpc, synth_lpc + SYNTH_IO_EXTENT - 1, + sp->rom_version, sp->serial_number, synth->version); + synth->alive = 1; + return 0; +} + +static void dtlk_release(void) +{ + spk_stop_serial_interrupt(); + if (speakup_info.port_tts) + synth_release_region(speakup_info.port_tts - 1, + SYNTH_IO_EXTENT); + speakup_info.port_tts = 0; +} + +module_param_hw_named(port, port_forced, int, ioport, 0444); +module_param_named(start, synth_dtlk.startup, short, 0444); + +MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_dtlk); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for DoubleTalk PC synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_dtlk.h b/drivers/accessibility/speakup/speakup_dtlk.h new file mode 100644 index 000000000000..9c378b58066e --- /dev/null +++ b/drivers/accessibility/speakup/speakup_dtlk.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* speakup_dtlk.h - header file for speakups DoubleTalk driver. */ + +#define SYNTH_IO_EXTENT 0x02 +#define SYNTH_CLEAR 0x18 /* stops speech */ + /* TTS Port Status Flags */ +#define TTS_READABLE 0x80 /* mask for bit which is nonzero if a + * byte can be read from the TTS port + */ +#define TTS_SPEAKING 0x40 /* mask for SYNC bit, which is nonzero + * while DoubleTalk is producing + * output with TTS, PCM or CVSD + * synthesizers or tone generators + * (that is, all but LPC) + */ +#define TTS_SPEAKING2 0x20 /* mask for SYNC2 bit, + * which falls to zero up to 0.4 sec + * before speech stops + */ +#define TTS_WRITABLE 0x10 /* mask for RDY bit, which when set to + * 1, indicates the TTS port is ready + * to accept a byte of data. The RDY + * bit goes zero 2-3 usec after + * writing, and goes 1 again 180-190 + * usec later. + */ +#define TTS_ALMOST_FULL 0x08 /* mask for AF bit: When set to 1, + * indicates that less than 300 bytes + * are available in the TTS input + * buffer. AF is always 0 in the PCM, + * TGN and CVSD modes. + */ +#define TTS_ALMOST_EMPTY 0x04 /* mask for AE bit: When set to 1, + * indicates that less than 300 bytes + * are remaining in DoubleTalk's input + * (TTS or PCM) buffer. AE is always 1 + * in the TGN and CVSD modes. + */ + + /* data returned by Interrogate command */ +struct synth_settings { + u_short serial_number; /* 0-7Fh:0-7Fh */ + u_char rom_version[24]; /* null terminated string */ + u_char mode; /* 0=Character; 1=Phoneme; 2=Text */ + u_char punc_level; /* nB; 0-7 */ + u_char formant_freq; /* nF; 0-9 */ + u_char pitch; /* nP; 0-99 */ + u_char speed; /* nS; 0-9 */ + u_char volume; /* nV; 0-9 */ + u_char tone; /* nX; 0-2 */ + u_char expression; /* nE; 0-9 */ + u_char ext_dict_loaded; /* 1=exception dictionary loaded */ + u_char ext_dict_status; /* 1=exception dictionary enabled */ + u_char free_ram; /* # pages (truncated) remaining for + * text buffer + */ + u_char articulation; /* nA; 0-9 */ + u_char reverb; /* nR; 0-9 */ + u_char eob; /* 7Fh value indicating end of + * parameter block + */ + u_char has_indexing; /* nonzero if indexing is implemented */ +}; diff --git a/drivers/accessibility/speakup/speakup_dummy.c b/drivers/accessibility/speakup/speakup_dummy.c new file mode 100644 index 000000000000..e393438af81b --- /dev/null +++ b/drivers/accessibility/speakup/speakup_dummy.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * eventually modified by Samuel Thibault + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * Copyright (C) 2007 Samuel Thibault. + * + * specificly written as a driver for the speakup screenreview + * s not a general device driver. + */ +#include "spk_priv.h" +#include "speakup.h" + +#define PROCSPEECH '\n' +#define DRV_VERSION "2.11" +#define SYNTH_CLEAR '!' + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"CAPS_START\n" } }, + { CAPS_STOP, .u.s = {"CAPS_STOP\n" } }, + { PAUSE, .u.s = {"PAUSE\n"} }, + { RATE, .u.n = {"RATE %d\n", 8, 1, 16, 0, 0, NULL } }, + { PITCH, .u.n = {"PITCH %d\n", 8, 0, 16, 0, 0, NULL } }, + { INFLECTION, .u.n = {"INFLECTION %d\n", 8, 0, 16, 0, 0, NULL } }, + { VOL, .u.n = {"VOL %d\n", 8, 0, 16, 0, 0, NULL } }, + { TONE, .u.n = {"TONE %d\n", 8, 0, 16, 0, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/dummy. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute inflection_attribute = + __ATTR(inflection, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &inflection_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_dummy = { + .name = "dummy", + .version = DRV_VERSION, + .long_name = "Dummy", + .init = "Speakup\n", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 40000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = spk_ttyio_synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = spk_do_catch_up_unicode, + .flush = spk_synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "dummy", + }, +}; + +module_param_named(ser, synth_dummy.ser, int, 0444); +module_param_named(dev, synth_dummy.dev_name, charp, 0444); +module_param_named(start, synth_dummy.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_dummy); + +MODULE_AUTHOR("Samuel Thibault "); +MODULE_DESCRIPTION("Speakup support for text console"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_keypc.c b/drivers/accessibility/speakup/speakup_keypc.c new file mode 100644 index 000000000000..414827e888fc --- /dev/null +++ b/drivers/accessibility/speakup/speakup_keypc.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * written by David Borowski + * + * Copyright (C) 2003 David Borowski. + * + * specificly written as a driver for the speakup screenreview + * package it's not a general device driver. + * This driver is for the Keynote Gold internal synthesizer. + */ +#include +#include +#include +#include +#include + +#include "spk_priv.h" +#include "speakup.h" + +#define DRV_VERSION "2.10" +#define SYNTH_IO_EXTENT 0x04 +#define SWAIT udelay(70) +#define PROCSPEECH 0x1f +#define SYNTH_CLEAR 0x03 + +static int synth_probe(struct spk_synth *synth); +static void keynote_release(void); +static const char *synth_immediate(struct spk_synth *synth, const char *buf); +static void do_catch_up(struct spk_synth *synth); +static void synth_flush(struct spk_synth *synth); + +static int synth_port; +static int port_forced; +static unsigned int synth_portlist[] = { 0x2a8, 0 }; + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"[f130]" } }, + { CAPS_STOP, .u.s = {"[f90]" } }, + { RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL } }, + { PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/keypc. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &rate_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_keypc = { + .name = "keypc", + .version = DRV_VERSION, + .long_name = "Keynote PC", + .init = "[t][n7,1][n8,0]", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 1000, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_serial_io_ops, + .probe = synth_probe, + .release = keynote_release, + .synth_immediate = synth_immediate, + .catch_up = do_catch_up, + .flush = synth_flush, + .is_alive = spk_synth_is_alive_nop, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "keypc", + }, +}; + +static inline bool synth_writable(void) +{ + return (inb_p(synth_port + UART_RX) & 0x10) != 0; +} + +static inline bool synth_full(void) +{ + return (inb_p(synth_port + UART_RX) & 0x80) == 0; +} + +static char *oops(void) +{ + int s1, s2, s3, s4; + + s1 = inb_p(synth_port); + s2 = inb_p(synth_port + 1); + s3 = inb_p(synth_port + 2); + s4 = inb_p(synth_port + 3); + pr_warn("synth timeout %d %d %d %d\n", s1, s2, s3, s4); + return NULL; +} + +static const char *synth_immediate(struct spk_synth *synth, const char *buf) +{ + u_char ch; + int timeout; + + while ((ch = *buf)) { + if (ch == '\n') + ch = PROCSPEECH; + if (synth_full()) + return buf; + timeout = 1000; + while (synth_writable()) + if (--timeout <= 0) + return oops(); + outb_p(ch, synth_port); + udelay(70); + buf++; + } + return NULL; +} + +static void do_catch_up(struct spk_synth *synth) +{ + u_char ch; + int timeout; + unsigned long flags; + unsigned long jiff_max; + struct var_t *jiffy_delta; + struct var_t *delay_time; + struct var_t *full_time; + int delay_time_val; + int full_time_val; + int jiffy_delta_val; + + jiffy_delta = spk_get_var(JIFFY); + delay_time = spk_get_var(DELAY); + full_time = spk_get_var(FULL); + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + + jiff_max = jiffies + jiffy_delta_val; + while (!kthread_should_stop()) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (speakup_info.flushing) { + speakup_info.flushing = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + synth->flush(synth); + continue; + } + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + break; + } + set_current_state(TASK_INTERRUPTIBLE); + full_time_val = full_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (synth_full()) { + schedule_timeout(msecs_to_jiffies(full_time_val)); + continue; + } + set_current_state(TASK_RUNNING); + timeout = 1000; + while (synth_writable()) + if (--timeout <= 0) + break; + if (timeout <= 0) { + oops(); + break; + } + spin_lock_irqsave(&speakup_info.spinlock, flags); + ch = synth_buffer_getc(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '\n') + ch = PROCSPEECH; + outb_p(ch, synth_port); + SWAIT; + if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { + timeout = 1000; + while (synth_writable()) + if (--timeout <= 0) + break; + if (timeout <= 0) { + oops(); + break; + } + outb_p(PROCSPEECH, synth_port); + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + delay_time_val = delay_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + schedule_timeout(msecs_to_jiffies(delay_time_val)); + jiff_max = jiffies + jiffy_delta_val; + } + } + timeout = 1000; + while (synth_writable()) + if (--timeout <= 0) + break; + if (timeout <= 0) + oops(); + else + outb_p(PROCSPEECH, synth_port); +} + +static void synth_flush(struct spk_synth *synth) +{ + outb_p(SYNTH_CLEAR, synth_port); +} + +static int synth_probe(struct spk_synth *synth) +{ + unsigned int port_val = 0; + int i = 0; + + pr_info("Probing for %s.\n", synth->long_name); + if (port_forced) { + synth_port = port_forced; + pr_info("probe forced to %x by kernel command line\n", + synth_port); + if (synth_request_region(synth_port - 1, SYNTH_IO_EXTENT)) { + pr_warn("sorry, port already reserved\n"); + return -EBUSY; + } + port_val = inb(synth_port); + } else { + for (i = 0; synth_portlist[i]; i++) { + if (synth_request_region(synth_portlist[i], + SYNTH_IO_EXTENT)) { + pr_warn + ("request_region: failed with 0x%x, %d\n", + synth_portlist[i], SYNTH_IO_EXTENT); + continue; + } + port_val = inb(synth_portlist[i]); + if (port_val == 0x80) { + synth_port = synth_portlist[i]; + break; + } + } + } + if (port_val != 0x80) { + pr_info("%s: not found\n", synth->long_name); + synth_release_region(synth_port, SYNTH_IO_EXTENT); + synth_port = 0; + return -ENODEV; + } + pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name, + synth_port, synth_port + SYNTH_IO_EXTENT - 1, + synth->version); + synth->alive = 1; + return 0; +} + +static void keynote_release(void) +{ + spk_stop_serial_interrupt(); + if (synth_port) + synth_release_region(synth_port, SYNTH_IO_EXTENT); + synth_port = 0; +} + +module_param_hw_named(port, port_forced, int, ioport, 0444); +module_param_named(start, synth_keypc.startup, short, 0444); + +MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_keypc); + +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for Keynote Gold PC synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_ltlk.c b/drivers/accessibility/speakup/speakup_ltlk.c new file mode 100644 index 000000000000..3c59519a871f --- /dev/null +++ b/drivers/accessibility/speakup/speakup_ltlk.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * specificly written as a driver for the speakup screenreview + * s not a general device driver. + */ +#include "speakup.h" +#include "spk_priv.h" +#include "speakup_dtlk.h" /* local header file for LiteTalk values */ + +#define DRV_VERSION "2.11" +#define PROCSPEECH 0x0d + +static int synth_probe(struct spk_synth *synth); + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\x01+35p" } }, + { CAPS_STOP, .u.s = {"\x01-35p" } }, + { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } }, + { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } }, + { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, + { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, + { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } }, + { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, + { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* + * These attributes will appear in /sys/accessibility/speakup/ltlk. + */ +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute freq_attribute = + __ATTR(freq, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute voice_attribute = + __ATTR(voice, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &freq_attribute.attr, + &pitch_attribute.attr, + &punct_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &voice_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_ltlk = { + .name = "ltlk", + .version = DRV_VERSION, + .long_name = "LiteTalk", + .init = "\01@\x01\x31y\n\0", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 40000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = spk_do_catch_up, + .flush = spk_synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = spk_synth_get_index, + .indexing = { + .command = "\x01%di", + .lowindex = 1, + .highindex = 5, + .currindex = 1, + }, + .attributes = { + .attrs = synth_attrs, + .name = "ltlk", + }, +}; + +/* interrogate the LiteTalk and print its settings */ +static void synth_interrogate(struct spk_synth *synth) +{ + unsigned char *t, i; + unsigned char buf[50], rom_v[20]; + + synth->synth_immediate(synth, "\x18\x01?"); + for (i = 0; i < 50; i++) { + buf[i] = synth->io_ops->synth_in(); + if (i > 2 && buf[i] == 0x7f) + break; + } + t = buf + 2; + for (i = 0; *t != '\r'; t++) { + rom_v[i] = *t; + if (++i >= 19) + break; + } + rom_v[i] = 0; + pr_info("%s: ROM version: %s\n", synth->long_name, rom_v); +} + +static int synth_probe(struct spk_synth *synth) +{ + int failed = 0; + + failed = spk_ttyio_synth_probe(synth); + if (failed == 0) + synth_interrogate(synth); + synth->alive = !failed; + return failed; +} + +module_param_named(ser, synth_ltlk.ser, int, 0444); +module_param_named(dev, synth_ltlk.dev_name, charp, 0444); +module_param_named(start, synth_ltlk.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_ltlk); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for DoubleTalk LT/LiteTalk synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c new file mode 100644 index 000000000000..9a7029539f35 --- /dev/null +++ b/drivers/accessibility/speakup/speakup_soft.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* speakup_soft.c - speakup driver to register and make available + * a user space device for software synthesizers. written by: Kirk + * Reiser + * + * Copyright (C) 2003 Kirk Reiser. + * + * this code is specificly written as a driver for the speakup screenreview + * package and is not a general device driver. + */ + +#include +#include /* for misc_register, and MISC_DYNAMIC_MINOR */ +#include /* for poll_wait() */ + +/* schedule(), signal_pending(), TASK_INTERRUPTIBLE */ +#include + +#include "spk_priv.h" +#include "speakup.h" + +#define DRV_VERSION "2.6" +#define PROCSPEECH 0x0d +#define CLEAR_SYNTH 0x18 + +static int softsynth_probe(struct spk_synth *synth); +static void softsynth_release(void); +static int softsynth_is_alive(struct spk_synth *synth); +static unsigned char get_index(struct spk_synth *synth); + +static struct miscdevice synth_device, synthu_device; +static int init_pos; +static int misc_registered; + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\x01+3p" } }, + { CAPS_STOP, .u.s = {"\x01-3p" } }, + { PAUSE, .u.n = {"\x01P" } }, + { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } }, + { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } }, + { INFLECTION, .u.n = {"\x01%dr", 5, 0, 9, 0, 0, NULL } }, + { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, + { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, + { PUNCT, .u.n = {"\x01%db", 0, 0, 2, 0, 0, NULL } }, + { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, + { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* These attributes will appear in /sys/accessibility/speakup/soft. */ + +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute freq_attribute = + __ATTR(freq, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute inflection_attribute = + __ATTR(inflection, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute voice_attribute = + __ATTR(voice, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +/* + * We should uncomment the following definition, when we agree on a + * method of passing a language designation to the software synthesizer. + * static struct kobj_attribute lang_attribute = + * __ATTR(lang, 0644, spk_var_show, spk_var_store); + */ + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &freq_attribute.attr, +/* &lang_attribute.attr, */ + &pitch_attribute.attr, + &inflection_attribute.attr, + &punct_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &voice_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_soft = { + .name = "soft", + .version = DRV_VERSION, + .long_name = "software synth", + .init = "\01@\x01\x31y\n", + .procspeech = PROCSPEECH, + .delay = 0, + .trigger = 0, + .jiffies = 0, + .full = 0, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = NULL, + .probe = softsynth_probe, + .release = softsynth_release, + .synth_immediate = NULL, + .catch_up = NULL, + .flush = NULL, + .is_alive = softsynth_is_alive, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = get_index, + .indexing = { + .command = "\x01%di", + .lowindex = 1, + .highindex = 5, + .currindex = 1, + }, + .attributes = { + .attrs = synth_attrs, + .name = "soft", + }, +}; + +static char *get_initstring(void) +{ + static char buf[40]; + char *cp; + struct var_t *var; + + memset(buf, 0, sizeof(buf)); + cp = buf; + var = synth_soft.vars; + while (var->var_id != MAXVARS) { + if (var->var_id != CAPS_START && var->var_id != CAPS_STOP && + var->var_id != PAUSE && var->var_id != DIRECT) + cp = cp + sprintf(cp, var->u.n.synth_fmt, + var->u.n.value); + var++; + } + cp = cp + sprintf(cp, "\n"); + return buf; +} + +static int softsynth_open(struct inode *inode, struct file *fp) +{ + unsigned long flags; + /*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */ + /* return -EPERM; */ + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (synth_soft.alive) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return -EBUSY; + } + synth_soft.alive = 1; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return 0; +} + +static int softsynth_close(struct inode *inode, struct file *fp) +{ + unsigned long flags; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + synth_soft.alive = 0; + init_pos = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + /* Make sure we let applications go before leaving */ + speakup_start_ttys(); + return 0; +} + +static ssize_t softsynthx_read(struct file *fp, char __user *buf, size_t count, + loff_t *pos, int unicode) +{ + int chars_sent = 0; + char __user *cp; + char *init; + size_t bytes_per_ch = unicode ? 3 : 1; + u16 ch; + int empty; + unsigned long flags; + DEFINE_WAIT(wait); + + if (count < bytes_per_ch) + return -EINVAL; + + spin_lock_irqsave(&speakup_info.spinlock, flags); + synth_soft.alive = 1; + while (1) { + prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE); + if (synth_current() == &synth_soft) { + if (!unicode) + synth_buffer_skip_nonlatin1(); + if (!synth_buffer_empty() || speakup_info.flushing) + break; + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (fp->f_flags & O_NONBLOCK) { + finish_wait(&speakup_event, &wait); + return -EAGAIN; + } + if (signal_pending(current)) { + finish_wait(&speakup_event, &wait); + return -ERESTARTSYS; + } + schedule(); + spin_lock_irqsave(&speakup_info.spinlock, flags); + } + finish_wait(&speakup_event, &wait); + + cp = buf; + init = get_initstring(); + + /* Keep 3 bytes available for a 16bit UTF-8-encoded character */ + while (chars_sent <= count - bytes_per_ch) { + if (synth_current() != &synth_soft) + break; + if (speakup_info.flushing) { + speakup_info.flushing = 0; + ch = '\x18'; + } else if (init[init_pos]) { + ch = init[init_pos++]; + } else { + if (!unicode) + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) + break; + ch = synth_buffer_getc(); + } + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + + if ((!unicode && ch < 0x100) || (unicode && ch < 0x80)) { + u_char c = ch; + + if (copy_to_user(cp, &c, 1)) + return -EFAULT; + + chars_sent++; + cp++; + } else if (unicode && ch < 0x800) { + u_char s[2] = { + 0xc0 | (ch >> 6), + 0x80 | (ch & 0x3f) + }; + + if (copy_to_user(cp, s, sizeof(s))) + return -EFAULT; + + chars_sent += sizeof(s); + cp += sizeof(s); + } else if (unicode) { + u_char s[3] = { + 0xe0 | (ch >> 12), + 0x80 | ((ch >> 6) & 0x3f), + 0x80 | (ch & 0x3f) + }; + + if (copy_to_user(cp, s, sizeof(s))) + return -EFAULT; + + chars_sent += sizeof(s); + cp += sizeof(s); + } + + spin_lock_irqsave(&speakup_info.spinlock, flags); + } + *pos += chars_sent; + empty = synth_buffer_empty(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (empty) { + speakup_start_ttys(); + *pos = 0; + } + return chars_sent; +} + +static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, + loff_t *pos) +{ + return softsynthx_read(fp, buf, count, pos, 0); +} + +static ssize_t softsynthu_read(struct file *fp, char __user *buf, size_t count, + loff_t *pos) +{ + return softsynthx_read(fp, buf, count, pos, 1); +} + +static int last_index; + +static ssize_t softsynth_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + unsigned long supplied_index = 0; + int converted; + + converted = kstrtoul_from_user(buf, count, 0, &supplied_index); + + if (converted < 0) + return converted; + + last_index = supplied_index; + return count; +} + +static __poll_t softsynth_poll(struct file *fp, struct poll_table_struct *wait) +{ + unsigned long flags; + __poll_t ret = 0; + + poll_wait(fp, &speakup_event, wait); + + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (synth_current() == &synth_soft && + (!synth_buffer_empty() || speakup_info.flushing)) + ret = EPOLLIN | EPOLLRDNORM; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + return ret; +} + +static unsigned char get_index(struct spk_synth *synth) +{ + int rv; + + rv = last_index; + last_index = 0; + return rv; +} + +static const struct file_operations softsynth_fops = { + .owner = THIS_MODULE, + .poll = softsynth_poll, + .read = softsynth_read, + .write = softsynth_write, + .open = softsynth_open, + .release = softsynth_close, +}; + +static const struct file_operations softsynthu_fops = { + .owner = THIS_MODULE, + .poll = softsynth_poll, + .read = softsynthu_read, + .write = softsynth_write, + .open = softsynth_open, + .release = softsynth_close, +}; + +static int softsynth_probe(struct spk_synth *synth) +{ + if (misc_registered != 0) + return 0; + memset(&synth_device, 0, sizeof(synth_device)); + synth_device.minor = MISC_DYNAMIC_MINOR; + synth_device.name = "softsynth"; + synth_device.fops = &softsynth_fops; + if (misc_register(&synth_device)) { + pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n"); + return -ENODEV; + } + + memset(&synthu_device, 0, sizeof(synthu_device)); + synthu_device.minor = MISC_DYNAMIC_MINOR; + synthu_device.name = "softsynthu"; + synthu_device.fops = &softsynthu_fops; + if (misc_register(&synthu_device)) { + pr_warn("Couldn't initialize miscdevice /dev/softsynthu.\n"); + return -ENODEV; + } + + misc_registered = 1; + pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR %d)\n", + synth_device.minor); + pr_info("initialized device: /dev/softsynthu, node (MAJOR 10, MINOR %d)\n", + synthu_device.minor); + return 0; +} + +static void softsynth_release(void) +{ + misc_deregister(&synth_device); + misc_deregister(&synthu_device); + misc_registered = 0; + pr_info("unregistered /dev/softsynth\n"); + pr_info("unregistered /dev/softsynthu\n"); +} + +static int softsynth_is_alive(struct spk_synth *synth) +{ + if (synth_soft.alive) + return 1; + return 0; +} + +module_param_named(start, synth_soft.startup, short, 0444); + +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_soft); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_DESCRIPTION("Speakup userspace software synthesizer support"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/accessibility/speakup/speakup_spkout.c b/drivers/accessibility/speakup/speakup_spkout.c new file mode 100644 index 000000000000..6e933bf1de2e --- /dev/null +++ b/drivers/accessibility/speakup/speakup_spkout.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * specificly written as a driver for the speakup screenreview + * s not a general device driver. + */ +#include "spk_priv.h" +#include "speakup.h" + +#define DRV_VERSION "2.11" +#define SYNTH_CLEAR 0x18 +#define PROCSPEECH '\r' + +static void synth_flush(struct spk_synth *synth); + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\x05P+" } }, + { CAPS_STOP, .u.s = {"\x05P-" } }, + { RATE, .u.n = {"\x05R%d", 7, 0, 9, 0, 0, NULL } }, + { PITCH, .u.n = {"\x05P%d", 3, 0, 9, 0, 0, NULL } }, + { VOL, .u.n = {"\x05V%d", 9, 0, 9, 0, 0, NULL } }, + { TONE, .u.n = {"\x05T%c", 8, 0, 25, 65, 0, NULL } }, + { PUNCT, .u.n = {"\x05M%c", 0, 0, 3, 0, 0, "nsma" } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* These attributes will appear in /sys/accessibility/speakup/spkout. */ + +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &punct_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_spkout = { + .name = "spkout", + .version = DRV_VERSION, + .long_name = "Speakout", + .init = "\005W1\005I2\005C3", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 40000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = spk_ttyio_synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = spk_do_catch_up, + .flush = synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = spk_synth_get_index, + .indexing = { + .command = "\x05[%c", + .lowindex = 1, + .highindex = 5, + .currindex = 1, + }, + .attributes = { + .attrs = synth_attrs, + .name = "spkout", + }, +}; + +static void synth_flush(struct spk_synth *synth) +{ + synth->io_ops->flush_buffer(); + synth->io_ops->send_xchar(SYNTH_CLEAR); +} + +module_param_named(ser, synth_spkout.ser, int, 0444); +module_param_named(dev, synth_spkout.dev_name, charp, 0444); +module_param_named(start, synth_spkout.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_spkout); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for Speak Out synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakup_txprt.c b/drivers/accessibility/speakup/speakup_txprt.c new file mode 100644 index 000000000000..a7326f226a5e --- /dev/null +++ b/drivers/accessibility/speakup/speakup_txprt.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * originally written by: Kirk Reiser + * this version considerably modified by David Borowski, david575@rogers.com + * + * Copyright (C) 1998-99 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + * + * specificly written as a driver for the speakup screenreview + * s not a general device driver. + */ +#include "spk_priv.h" +#include "speakup.h" + +#define DRV_VERSION "2.11" +#define SYNTH_CLEAR 0x18 +#define PROCSPEECH '\r' /* process speech char */ + +static struct var_t vars[] = { + { CAPS_START, .u.s = {"\x05P8" } }, + { CAPS_STOP, .u.s = {"\x05P5" } }, + { RATE, .u.n = {"\x05R%d", 5, 0, 9, 0, 0, NULL } }, + { PITCH, .u.n = {"\x05P%d", 5, 0, 9, 0, 0, NULL } }, + { VOL, .u.n = {"\x05V%d", 5, 0, 9, 0, 0, NULL } }, + { TONE, .u.n = {"\x05T%c", 12, 0, 25, 61, 0, NULL } }, + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + V_LAST_VAR + }; + +/* These attributes will appear in /sys/accessibility/speakup/txprt. */ + +static struct kobj_attribute caps_start_attribute = + __ATTR(caps_start, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute caps_stop_attribute = + __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute pitch_attribute = + __ATTR(pitch, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute rate_attribute = + __ATTR(rate, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute tone_attribute = + __ATTR(tone, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute vol_attribute = + __ATTR(vol, 0644, spk_var_show, spk_var_store); + +static struct kobj_attribute delay_time_attribute = + __ATTR(delay_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute direct_attribute = + __ATTR(direct, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute full_time_attribute = + __ATTR(full_time, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute jiffy_delta_attribute = + __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute trigger_time_attribute = + __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); + +/* + * Create a group of attributes so that we can create and destroy them all + * at once. + */ +static struct attribute *synth_attrs[] = { + &caps_start_attribute.attr, + &caps_stop_attribute.attr, + &pitch_attribute.attr, + &rate_attribute.attr, + &tone_attribute.attr, + &vol_attribute.attr, + &delay_time_attribute.attr, + &direct_attribute.attr, + &full_time_attribute.attr, + &jiffy_delta_attribute.attr, + &trigger_time_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct spk_synth synth_txprt = { + .name = "txprt", + .version = DRV_VERSION, + .long_name = "Transport", + .init = "\x05N1", + .procspeech = PROCSPEECH, + .clear = SYNTH_CLEAR, + .delay = 500, + .trigger = 50, + .jiffies = 50, + .full = 40000, + .dev_name = SYNTH_DEFAULT_DEV, + .startup = SYNTH_START, + .checkval = SYNTH_CHECK, + .vars = vars, + .io_ops = &spk_ttyio_ops, + .probe = spk_ttyio_synth_probe, + .release = spk_ttyio_release, + .synth_immediate = spk_ttyio_synth_immediate, + .catch_up = spk_do_catch_up, + .flush = spk_synth_flush, + .is_alive = spk_synth_is_alive_restart, + .synth_adjust = NULL, + .read_buff_add = NULL, + .get_index = NULL, + .indexing = { + .command = NULL, + .lowindex = 0, + .highindex = 0, + .currindex = 0, + }, + .attributes = { + .attrs = synth_attrs, + .name = "txprt", + }, +}; + +module_param_named(ser, synth_txprt.ser, int, 0444); +module_param_named(dev, synth_txprt.dev_name, charp, 0444); +module_param_named(start, synth_txprt.startup, short, 0444); + +MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); +MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); +MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); + +module_spk_synth(synth_txprt); + +MODULE_AUTHOR("Kirk Reiser "); +MODULE_AUTHOR("David Borowski"); +MODULE_DESCRIPTION("Speakup support for Transport synthesizers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/accessibility/speakup/speakupmap.h b/drivers/accessibility/speakup/speakupmap.h new file mode 100644 index 000000000000..c60d7339b89a --- /dev/null +++ b/drivers/accessibility/speakup/speakupmap.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + 119, 62, 6, + 0, 16, 20, 17, 32, 48, 0, + 2, 0, 78, 0, 0, 0, 0, + 3, 0, 79, 0, 0, 0, 0, + 4, 0, 76, 0, 0, 0, 0, + 5, 0, 77, 0, 0, 0, 0, + 6, 0, 74, 0, 0, 0, 0, + 7, 0, 75, 0, 0, 0, 0, + 9, 0, 5, 46, 0, 0, 0, + 10, 0, 4, 0, 0, 0, 0, + 11, 0, 0, 1, 0, 0, 0, + 12, 0, 27, 0, 33, 0, 0, + 19, 0, 47, 0, 0, 0, 0, + 21, 0, 29, 17, 0, 0, 0, + 22, 0, 15, 0, 0, 0, 0, + 23, 0, 14, 0, 0, 0, 28, + 24, 0, 16, 0, 0, 0, 0, + 25, 0, 30, 18, 0, 0, 0, + 28, 0, 3, 26, 0, 0, 0, + 35, 0, 31, 0, 0, 0, 0, + 36, 0, 12, 0, 0, 0, 0, + 37, 0, 11, 0, 0, 0, 22, + 38, 0, 13, 0, 0, 0, 0, + 39, 0, 32, 7, 0, 0, 0, + 40, 0, 23, 0, 0, 0, 0, + 44, 0, 44, 0, 0, 0, 0, + 49, 0, 24, 0, 0, 0, 0, + 50, 0, 9, 19, 6, 0, 0, + 51, 0, 8, 0, 0, 0, 36, + 52, 0, 10, 20, 0, 0, 0, + 53, 0, 25, 0, 0, 0, 0, + 55, 46, 1, 0, 0, 0, 0, + 58, 128, 128, 0, 0, 0, 0, + 59, 0, 45, 0, 0, 0, 0, + 60, 0, 40, 0, 0, 0, 0, + 61, 0, 41, 0, 0, 0, 0, + 62, 0, 42, 0, 0, 0, 0, + 63, 0, 34, 0, 0, 0, 0, + 64, 0, 35, 0, 0, 0, 0, + 65, 0, 37, 0, 0, 0, 0, + 66, 0, 38, 0, 0, 0, 0, + 67, 0, 66, 0, 39, 0, 0, + 68, 0, 67, 0, 0, 0, 0, + 71, 15, 19, 0, 0, 0, 0, + 72, 14, 29, 0, 0, 28, 0, + 73, 16, 17, 0, 0, 0, 0, + 74, 27, 33, 0, 0, 0, 0, + 75, 12, 31, 0, 0, 0, 0, + 76, 11, 21, 0, 0, 22, 0, + 77, 13, 32, 0, 0, 0, 0, + 78, 23, 43, 0, 0, 0, 0, + 79, 9, 20, 0, 0, 0, 0, + 80, 8, 30, 0, 0, 36, 0, + 81, 10, 18, 0, 0, 0, 0, + 82, 128, 128, 0, 0, 0, 0, + 83, 24, 25, 0, 0, 0, 0, + 87, 0, 68, 0, 0, 0, 0, + 88, 0, 69, 0, 0, 0, 0, + 96, 3, 26, 0, 0, 0, 0, + 98, 4, 5, 0, 0, 0, 0, + 99, 2, 0, 0, 0, 0, 0, + 104, 0, 6, 0, 0, 0, 0, + 109, 0, 7, 0, 0, 0, 0, + 125, 128, 128, 0, 0, 0, 0, + 0, 119 diff --git a/drivers/accessibility/speakup/speakupmap.map b/drivers/accessibility/speakup/speakupmap.map new file mode 100644 index 000000000000..f10d44cf5d7a --- /dev/null +++ b/drivers/accessibility/speakup/speakupmap.map @@ -0,0 +1,93 @@ +spk key_f9 = punc_level_dec +spk key_f10 = punc_level_inc +spk key_f11 = reading_punc_dec +spk key_f12 = reading_punc_inc +spk key_1 = vol_dec +spk key_2 = vol_inc +spk key_3 = pitch_dec +spk key_4 = pitch_inc +spk key_5 = rate_dec +spk key_6 = rate_inc +key_kpasterisk = toggle_cursoring +ctrl spk key_8 = toggle_cursoring +spk key_kpasterisk = speakup_goto +spk key_f1 = speakup_help +spk key_f2 = set_win +spk key_f3 = clear_win +spk key_f4 = enable_win +spk key_f5 = edit_some +spk key_f6 = edit_most +spk key_f7 = edit_delim +spk key_f8 = edit_repeat +shift spk key_f9 = edit_exnum + key_kp7 = say_prev_line +spk key_kp7 = left_edge + key_kp8 = say_line +double key_kp8 = say_line_indent +spk key_kp8 = say_from_top + key_kp9 = say_next_line +spk key_kp9 = top_edge + key_kpminus = speakup_parked +spk key_kpminus = say_char_num + key_kp4 = say_prev_word +spk key_kp4 = say_from_left + key_kp5 = say_word +double key_kp5 = spell_word +spk key_kp5 = spell_phonetic + key_kp6 = say_next_word +spk key_kp6 = say_to_right + key_kpplus = say_screen +spk key_kpplus = say_win + key_kp1 = say_prev_char +spk key_kp1 = right_edge + key_kp2 = say_char +spk key_kp2 = say_to_bottom +double key_kp2 = say_phonetic_char + key_kp3 = say_next_char +spk key_kp3 = bottom_edge + key_kp0 = spk_key + key_kpdot = say_position +spk key_kpdot = say_attributes +key_kpenter = speakup_quiet +spk key_kpenter = speakup_off +key_sysrq = speech_kill + key_kpslash = speakup_cut +spk key_kpslash = speakup_paste +spk key_pageup = say_first_char +spk key_pagedown = say_last_char +key_capslock = spk_key + spk key_z = spk_lock +key_leftmeta = spk_key +ctrl spk key_0 = speakup_goto +spk key_u = say_prev_line +spk key_i = say_line +double spk key_i = say_line_indent +spk key_o = say_next_line +spk key_minus = speakup_parked +shift spk key_minus = say_char_num +spk key_j = say_prev_word +spk key_k = say_word +double spk key_k = spell_word +spk key_l = say_next_word +spk key_m = say_prev_char +spk key_comma = say_char +double spk key_comma = say_phonetic_char +spk key_dot = say_next_char +spk key_n = say_position + ctrl spk key_m = left_edge + ctrl spk key_y = top_edge + ctrl spk key_dot = right_edge +ctrl spk key_p = bottom_edge +spk key_apostrophe = say_screen +spk key_h = say_from_left +spk key_y = say_from_top +spk key_semicolon = say_to_right +spk key_p = say_to_bottom +spk key_slash = say_attributes + spk key_enter = speakup_quiet + ctrl spk key_enter = speakup_off + spk key_9 = speakup_cut +spk key_8 = speakup_paste +shift spk key_m = say_first_char + ctrl spk key_semicolon = say_last_char +spk key_r = read_all_doc diff --git a/drivers/accessibility/speakup/spk_priv.h b/drivers/accessibility/speakup/spk_priv.h new file mode 100644 index 000000000000..c75b40838794 --- /dev/null +++ b/drivers/accessibility/speakup/spk_priv.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* spk_priv.h + * review functions for the speakup screen review package. + * originally written by: Kirk Reiser and Andy Berdan. + * + * extensively modified by David Borowski. + * + * Copyright (C) 1998 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + */ +#ifndef _SPEAKUP_PRIVATE_H +#define _SPEAKUP_PRIVATE_H + +#include + +#include "spk_types.h" +#include "spk_priv_keyinfo.h" + +#define V_LAST_VAR { MAXVARS } +#define SPACE 0x20 +#define SYNTH_CHECK 20030716 /* today's date ought to do for check value */ +/* synth flags, for odd synths */ +#define SF_DEC 1 /* to fiddle puncs in alpha strings so it doesn't spell */ +#ifdef MODULE +#define SYNTH_START 1 +#else +#define SYNTH_START 0 +#endif + +#define KT_SPKUP 15 +#define SPK_SYNTH_TIMEOUT 100000 /* in micro-seconds */ +#define SYNTH_DEFAULT_DEV "ttyS0" +#define SYNTH_DEFAULT_SER 0 + +const struct old_serial_port *spk_serial_init(int index); +void spk_stop_serial_interrupt(void); +int spk_wait_for_xmitr(struct spk_synth *in_synth); +void spk_serial_release(void); +void spk_ttyio_release(void); +void spk_ttyio_register_ldisc(void); +void spk_ttyio_unregister_ldisc(void); + +void synth_buffer_skip_nonlatin1(void); +u16 synth_buffer_getc(void); +u16 synth_buffer_peek(void); +int synth_buffer_empty(void); +struct var_t *spk_get_var(enum var_id_t var_id); +ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf); +ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count); + +int spk_serial_synth_probe(struct spk_synth *synth); +int spk_ttyio_synth_probe(struct spk_synth *synth); +const char *spk_serial_synth_immediate(struct spk_synth *synth, + const char *buff); +const char *spk_ttyio_synth_immediate(struct spk_synth *synth, + const char *buff); +void spk_do_catch_up(struct spk_synth *synth); +void spk_do_catch_up_unicode(struct spk_synth *synth); +void spk_synth_flush(struct spk_synth *synth); +unsigned char spk_synth_get_index(struct spk_synth *synth); +int spk_synth_is_alive_nop(struct spk_synth *synth); +int spk_synth_is_alive_restart(struct spk_synth *synth); +__printf(1, 2) +void synth_printf(const char *buf, ...); +void synth_putwc(u16 wc); +void synth_putwc_s(u16 wc); +void synth_putws(const u16 *buf); +void synth_putws_s(const u16 *buf); +int synth_request_region(unsigned long start, unsigned long n); +int synth_release_region(unsigned long start, unsigned long n); +int synth_add(struct spk_synth *in_synth); +void synth_remove(struct spk_synth *in_synth); +struct spk_synth *synth_current(void); + +extern struct speakup_info_t speakup_info; + +extern struct var_t synth_time_vars[]; + +extern struct spk_io_ops spk_serial_io_ops; +extern struct spk_io_ops spk_ttyio_ops; + +#endif diff --git a/drivers/accessibility/speakup/spk_priv_keyinfo.h b/drivers/accessibility/speakup/spk_priv_keyinfo.h new file mode 100644 index 000000000000..1f789bd1c678 --- /dev/null +++ b/drivers/accessibility/speakup/spk_priv_keyinfo.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* spk_priv.h + * review functions for the speakup screen review package. + * originally written by: Kirk Reiser and Andy Berdan. + * + * extensively modified by David Borowski. + * + * Copyright (C) 1998 Kirk Reiser. + * Copyright (C) 2003 David Borowski. + */ + +#ifndef _SPEAKUP_KEYINFO_H +#define _SPEAKUP_KEYINFO_H + +#define FIRST_SYNTH_VAR RATE +/* 0 is reserved for no remap */ +#define SPEAKUP_GOTO 0x01 +#define SPEECH_KILL 0x02 +#define SPEAKUP_QUIET 0x03 +#define SPEAKUP_CUT 0x04 +#define SPEAKUP_PASTE 0x05 +#define SAY_FIRST_CHAR 0x06 +#define SAY_LAST_CHAR 0x07 +#define SAY_CHAR 0x08 +#define SAY_PREV_CHAR 0x09 +#define SAY_NEXT_CHAR 0x0a +#define SAY_WORD 0x0b +#define SAY_PREV_WORD 0x0c +#define SAY_NEXT_WORD 0x0d +#define SAY_LINE 0x0e +#define SAY_PREV_LINE 0x0f +#define SAY_NEXT_LINE 0x10 +#define TOP_EDGE 0x11 +#define BOTTOM_EDGE 0x12 +#define LEFT_EDGE 0x13 +#define RIGHT_EDGE 0x14 +#define SPELL_PHONETIC 0x15 +#define SPELL_WORD 0x16 +#define SAY_SCREEN 0x17 +#define SAY_POSITION 0x18 +#define SAY_ATTRIBUTES 0x19 +#define SPEAKUP_OFF 0x1a +#define SPEAKUP_PARKED 0x1b +#define SAY_LINE_INDENT 0x1c +#define SAY_FROM_TOP 0x1d +#define SAY_TO_BOTTOM 0x1e +#define SAY_FROM_LEFT 0x1f +#define SAY_TO_RIGHT 0x20 +#define SAY_CHAR_NUM 0x21 +#define EDIT_SOME 0x22 +#define EDIT_MOST 0x23 +#define SAY_PHONETIC_CHAR 0x24 +#define EDIT_DELIM 0x25 +#define EDIT_REPEAT 0x26 +#define EDIT_EXNUM 0x27 +#define SET_WIN 0x28 +#define CLEAR_WIN 0x29 +#define ENABLE_WIN 0x2a +#define SAY_WIN 0x2b +#define SPK_LOCK 0x2c +#define SPEAKUP_HELP 0x2d +#define TOGGLE_CURSORING 0x2e +#define READ_ALL_DOC 0x2f + +/* one greater than the last func handler */ +#define SPKUP_MAX_FUNC 0x30 + +#define SPK_KEY 0x80 +#define FIRST_EDIT_BITS 0x22 +#define FIRST_SET_VAR SPELL_DELAY + +/* increase if adding more than 0x3f functions */ +#define VAR_START 0x40 + +/* keys for setting variables, must be ordered same as the enum for var_ids */ +/* with dec being even and inc being 1 greater */ +#define SPELL_DELAY_DEC (VAR_START + 0) +#define SPELL_DELAY_INC (SPELL_DELAY_DEC + 1) +#define PUNC_LEVEL_DEC (SPELL_DELAY_DEC + 2) +#define PUNC_LEVEL_INC (PUNC_LEVEL_DEC + 1) +#define READING_PUNC_DEC (PUNC_LEVEL_DEC + 2) +#define READING_PUNC_INC (READING_PUNC_DEC + 1) +#define ATTRIB_BLEEP_DEC (READING_PUNC_DEC + 2) +#define ATTRIB_BLEEP_INC (ATTRIB_BLEEP_DEC + 1) +#define BLEEPS_DEC (ATTRIB_BLEEP_DEC + 2) +#define BLEEPS_INC (BLEEPS_DEC + 1) +#define RATE_DEC (BLEEPS_DEC + 2) +#define RATE_INC (RATE_DEC + 1) +#define PITCH_DEC (RATE_DEC + 2) +#define PITCH_INC (PITCH_DEC + 1) +#define VOL_DEC (PITCH_DEC + 2) +#define VOL_INC (VOL_DEC + 1) +#define TONE_DEC (VOL_DEC + 2) +#define TONE_INC (TONE_DEC + 1) +#define PUNCT_DEC (TONE_DEC + 2) +#define PUNCT_INC (PUNCT_DEC + 1) +#define VOICE_DEC (PUNCT_DEC + 2) +#define VOICE_INC (VOICE_DEC + 1) + +#endif diff --git a/drivers/accessibility/speakup/spk_ttyio.c b/drivers/accessibility/speakup/spk_ttyio.c new file mode 100644 index 000000000000..9b95f77f9265 --- /dev/null +++ b/drivers/accessibility/speakup/spk_ttyio.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +#include "speakup.h" +#include "spk_types.h" +#include "spk_priv.h" + +struct spk_ldisc_data { + char buf; + struct completion completion; + bool buf_free; +}; + +static struct spk_synth *spk_ttyio_synth; +static struct tty_struct *speakup_tty; +/* mutex to protect against speakup_tty disappearing from underneath us while + * we are using it. this can happen when the device physically unplugged, + * while in use. it also serialises access to speakup_tty. + */ +static DEFINE_MUTEX(speakup_tty_mutex); + +static int ser_to_dev(int ser, dev_t *dev_no) +{ + if (ser < 0 || ser > (255 - 64)) { + pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n"); + return -EINVAL; + } + + *dev_no = MKDEV(4, (64 + ser)); + return 0; +} + +static int get_dev_to_use(struct spk_synth *synth, dev_t *dev_no) +{ + /* use ser only when dev is not specified */ + if (strcmp(synth->dev_name, SYNTH_DEFAULT_DEV) || + synth->ser == SYNTH_DEFAULT_SER) + return tty_dev_name_to_number(synth->dev_name, dev_no); + + return ser_to_dev(synth->ser, dev_no); +} + +static int spk_ttyio_ldisc_open(struct tty_struct *tty) +{ + struct spk_ldisc_data *ldisc_data; + + if (!tty->ops->write) + return -EOPNOTSUPP; + speakup_tty = tty; + + ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL); + if (!ldisc_data) + return -ENOMEM; + + init_completion(&ldisc_data->completion); + ldisc_data->buf_free = true; + speakup_tty->disc_data = ldisc_data; + + return 0; +} + +static void spk_ttyio_ldisc_close(struct tty_struct *tty) +{ + mutex_lock(&speakup_tty_mutex); + kfree(speakup_tty->disc_data); + speakup_tty = NULL; + mutex_unlock(&speakup_tty_mutex); +} + +static int spk_ttyio_receive_buf2(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) +{ + struct spk_ldisc_data *ldisc_data = tty->disc_data; + + if (spk_ttyio_synth->read_buff_add) { + int i; + + for (i = 0; i < count; i++) + spk_ttyio_synth->read_buff_add(cp[i]); + + return count; + } + + if (!ldisc_data->buf_free) + /* ttyio_in will tty_schedule_flip */ + return 0; + + /* Make sure the consumer has read buf before we have seen + * buf_free == true and overwrite buf + */ + mb(); + + ldisc_data->buf = cp[0]; + ldisc_data->buf_free = false; + complete(&ldisc_data->completion); + + return 1; +} + +static struct tty_ldisc_ops spk_ttyio_ldisc_ops = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "speakup_ldisc", + .open = spk_ttyio_ldisc_open, + .close = spk_ttyio_ldisc_close, + .receive_buf2 = spk_ttyio_receive_buf2, +}; + +static int spk_ttyio_out(struct spk_synth *in_synth, const char ch); +static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch); +static void spk_ttyio_send_xchar(char ch); +static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear); +static unsigned char spk_ttyio_in(void); +static unsigned char spk_ttyio_in_nowait(void); +static void spk_ttyio_flush_buffer(void); + +struct spk_io_ops spk_ttyio_ops = { + .synth_out = spk_ttyio_out, + .synth_out_unicode = spk_ttyio_out_unicode, + .send_xchar = spk_ttyio_send_xchar, + .tiocmset = spk_ttyio_tiocmset, + .synth_in = spk_ttyio_in, + .synth_in_nowait = spk_ttyio_in_nowait, + .flush_buffer = spk_ttyio_flush_buffer, +}; +EXPORT_SYMBOL_GPL(spk_ttyio_ops); + +static inline void get_termios(struct tty_struct *tty, + struct ktermios *out_termios) +{ + down_read(&tty->termios_rwsem); + *out_termios = tty->termios; + up_read(&tty->termios_rwsem); +} + +static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) +{ + int ret = 0; + struct tty_struct *tty; + struct ktermios tmp_termios; + dev_t dev; + + ret = get_dev_to_use(synth, &dev); + if (ret) + return ret; + + tty = tty_kopen(dev); + if (IS_ERR(tty)) + return PTR_ERR(tty); + + if (tty->ops->open) + ret = tty->ops->open(tty, NULL); + else + ret = -ENODEV; + + if (ret) { + tty_unlock(tty); + return ret; + } + + clear_bit(TTY_HUPPED, &tty->flags); + /* ensure hardware flow control is enabled */ + get_termios(tty, &tmp_termios); + if (!(tmp_termios.c_cflag & CRTSCTS)) { + tmp_termios.c_cflag |= CRTSCTS; + tty_set_termios(tty, &tmp_termios); + /* + * check c_cflag to see if it's updated as tty_set_termios + * may not return error even when no tty bits are + * changed by the request. + */ + get_termios(tty, &tmp_termios); + if (!(tmp_termios.c_cflag & CRTSCTS)) + pr_warn("speakup: Failed to set hardware flow control\n"); + } + + tty_unlock(tty); + + ret = tty_set_ldisc(tty, N_SPEAKUP); + if (ret) + pr_err("speakup: Failed to set N_SPEAKUP on tty\n"); + + return ret; +} + +void spk_ttyio_register_ldisc(void) +{ + if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops)) + pr_warn("speakup: Error registering line discipline. Most synths won't work.\n"); +} + +void spk_ttyio_unregister_ldisc(void) +{ + if (tty_unregister_ldisc(N_SPEAKUP)) + pr_warn("speakup: Couldn't unregister ldisc\n"); +} + +static int spk_ttyio_out(struct spk_synth *in_synth, const char ch) +{ + mutex_lock(&speakup_tty_mutex); + if (in_synth->alive && speakup_tty && speakup_tty->ops->write) { + int ret = speakup_tty->ops->write(speakup_tty, &ch, 1); + + mutex_unlock(&speakup_tty_mutex); + if (ret == 0) + /* No room */ + return 0; + if (ret < 0) { + pr_warn("%s: I/O error, deactivating speakup\n", + in_synth->long_name); + /* No synth any more, so nobody will restart TTYs, + * and we thus need to do it ourselves. Now that there + * is no synth we can let application flood anyway + */ + in_synth->alive = 0; + speakup_start_ttys(); + return 0; + } + return 1; + } + + mutex_unlock(&speakup_tty_mutex); + return 0; +} + +static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch) +{ + int ret; + + if (ch < 0x80) { + ret = spk_ttyio_out(in_synth, ch); + } else if (ch < 0x800) { + ret = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6)); + ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f)); + } else { + ret = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12)); + ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f)); + ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f)); + } + return ret; +} + +static int check_tty(struct tty_struct *tty) +{ + if (!tty) { + pr_warn("%s: I/O error, deactivating speakup\n", + spk_ttyio_synth->long_name); + /* No synth any more, so nobody will restart TTYs, and we thus + * need to do it ourselves. Now that there is no synth we can + * let application flood anyway + */ + spk_ttyio_synth->alive = 0; + speakup_start_ttys(); + return 1; + } + + return 0; +} + +static void spk_ttyio_send_xchar(char ch) +{ + mutex_lock(&speakup_tty_mutex); + if (check_tty(speakup_tty)) { + mutex_unlock(&speakup_tty_mutex); + return; + } + + if (speakup_tty->ops->send_xchar) + speakup_tty->ops->send_xchar(speakup_tty, ch); + mutex_unlock(&speakup_tty_mutex); +} + +static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear) +{ + mutex_lock(&speakup_tty_mutex); + if (check_tty(speakup_tty)) { + mutex_unlock(&speakup_tty_mutex); + return; + } + + if (speakup_tty->ops->tiocmset) + speakup_tty->ops->tiocmset(speakup_tty, set, clear); + mutex_unlock(&speakup_tty_mutex); +} + +static unsigned char ttyio_in(int timeout) +{ + struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data; + char rv; + + if (wait_for_completion_timeout(&ldisc_data->completion, + usecs_to_jiffies(timeout)) == 0) { + if (timeout) + pr_warn("spk_ttyio: timeout (%d) while waiting for input\n", + timeout); + return 0xff; + } + + rv = ldisc_data->buf; + /* Make sure we have read buf before we set buf_free to let + * the producer overwrite it + */ + mb(); + ldisc_data->buf_free = true; + /* Let TTY push more characters */ + tty_schedule_flip(speakup_tty->port); + + return rv; +} + +static unsigned char spk_ttyio_in(void) +{ + return ttyio_in(SPK_SYNTH_TIMEOUT); +} + +static unsigned char spk_ttyio_in_nowait(void) +{ + u8 rv = ttyio_in(0); + + return (rv == 0xff) ? 0 : rv; +} + +static void spk_ttyio_flush_buffer(void) +{ + mutex_lock(&speakup_tty_mutex); + if (check_tty(speakup_tty)) { + mutex_unlock(&speakup_tty_mutex); + return; + } + + if (speakup_tty->ops->flush_buffer) + speakup_tty->ops->flush_buffer(speakup_tty); + + mutex_unlock(&speakup_tty_mutex); +} + +int spk_ttyio_synth_probe(struct spk_synth *synth) +{ + int rv = spk_ttyio_initialise_ldisc(synth); + + if (rv) + return rv; + + synth->alive = 1; + spk_ttyio_synth = synth; + + return 0; +} +EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe); + +void spk_ttyio_release(void) +{ + if (!speakup_tty) + return; + + tty_lock(speakup_tty); + + if (speakup_tty->ops->close) + speakup_tty->ops->close(speakup_tty, NULL); + + tty_ldisc_flush(speakup_tty); + tty_unlock(speakup_tty); + tty_kclose(speakup_tty); +} +EXPORT_SYMBOL_GPL(spk_ttyio_release); + +const char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff) +{ + u_char ch; + + while ((ch = *buff)) { + if (ch == '\n') + ch = synth->procspeech; + if (tty_write_room(speakup_tty) < 1 || + !synth->io_ops->synth_out(synth, ch)) + return buff; + buff++; + } + return NULL; +} +EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate); diff --git a/drivers/accessibility/speakup/spk_types.h b/drivers/accessibility/speakup/spk_types.h new file mode 100644 index 000000000000..d3272c6d199a --- /dev/null +++ b/drivers/accessibility/speakup/spk_types.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef SPEAKUP_TYPES_H +#define SPEAKUP_TYPES_H + +/* This file includes all of the typedefs and structs used in speakup. */ + +#include +#include +#include +#include +#include /* for wait_queue */ +#include /* for __init */ +#include +#include +#include +#include +#include /* for inb_p, outb_p, inb, outb, etc... */ +#include + +enum var_type_t { + VAR_NUM = 0, + VAR_TIME, + VAR_STRING, + VAR_PROC +}; + +enum { + E_DEFAULT = 0, + E_SET, + E_INC, + E_DEC, + E_NEW_DEFAULT, +}; + +enum var_id_t { + VERSION = 0, SYNTH, SILENT, SYNTH_DIRECT, + KEYMAP, CHARS, + PUNC_SOME, PUNC_MOST, PUNC_ALL, + DELIM, REPEATS, EXNUMBER, + DELAY, TRIGGER, JIFFY, FULL, /* all timers must be together */ + BLEEP_TIME, CURSOR_TIME, BELL_POS, + SAY_CONTROL, SAY_WORD_CTL, NO_INTERRUPT, KEY_ECHO, + SPELL_DELAY, PUNC_LEVEL, READING_PUNC, + ATTRIB_BLEEP, BLEEPS, + RATE, PITCH, INFLECTION, VOL, TONE, PUNCT, VOICE, FREQUENCY, LANG, + DIRECT, PAUSE, + CAPS_START, CAPS_STOP, CHARTAB, + MAXVARS +}; + +typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch, + u_short key); + +#define COLOR_BUFFER_SIZE 160 + +struct spk_highlight_color_track { + /* Count of each background color */ + unsigned int bgcount[8]; + /* Buffer for characters drawn with each background color */ + u16 highbuf[8][COLOR_BUFFER_SIZE]; + /* Current index into highbuf */ + unsigned int highsize[8]; + /* Reading Position for each color */ + u_long rpos[8], rx[8], ry[8]; + /* Real Cursor Y Position */ + ulong cy; +}; + +struct st_spk_t { + u_long reading_x, cursor_x; + u_long reading_y, cursor_y; + u_long reading_pos, cursor_pos; + u_long go_x, go_pos; + u_long w_top, w_bottom, w_left, w_right; + u_char w_start, w_enabled; + u_char reading_attr, old_attr; + char parked, shut_up; + struct spk_highlight_color_track ht; + int tty_stopped; +}; + +/* now some defines to make these easier to use. */ +#define spk_shut_up (speakup_console[vc->vc_num]->shut_up) +#define spk_killed (speakup_console[vc->vc_num]->shut_up & 0x40) +#define spk_x (speakup_console[vc->vc_num]->reading_x) +#define spk_cx (speakup_console[vc->vc_num]->cursor_x) +#define spk_y (speakup_console[vc->vc_num]->reading_y) +#define spk_cy (speakup_console[vc->vc_num]->cursor_y) +#define spk_pos (speakup_console[vc->vc_num]->reading_pos) +#define spk_cp (speakup_console[vc->vc_num]->cursor_pos) +#define goto_pos (speakup_console[vc->vc_num]->go_pos) +#define goto_x (speakup_console[vc->vc_num]->go_x) +#define win_top (speakup_console[vc->vc_num]->w_top) +#define win_bottom (speakup_console[vc->vc_num]->w_bottom) +#define win_left (speakup_console[vc->vc_num]->w_left) +#define win_right (speakup_console[vc->vc_num]->w_right) +#define win_start (speakup_console[vc->vc_num]->w_start) +#define win_enabled (speakup_console[vc->vc_num]->w_enabled) +#define spk_attr (speakup_console[vc->vc_num]->reading_attr) +#define spk_old_attr (speakup_console[vc->vc_num]->old_attr) +#define spk_parked (speakup_console[vc->vc_num]->parked) + +struct st_var_header { + char *name; + enum var_id_t var_id; + enum var_type_t var_type; + void *p_val; /* ptr to programs variable to store value */ + void *data; /* ptr to the vars data */ +}; + +struct num_var_t { + char *synth_fmt; + int default_val; + int low; + int high; + short offset, multiplier; /* for fiddling rates etc. */ + char *out_str; /* if synth needs char representation of number */ + int value; /* current value */ +}; + +struct punc_var_t { + enum var_id_t var_id; + short value; +}; + +struct string_var_t { + char *default_val; +}; + +struct var_t { + enum var_id_t var_id; + union { + struct num_var_t n; + struct string_var_t s; + } u; +}; + +struct st_bits_data { /* punc, repeats, word delim bits */ + char *name; + char *value; + short mask; +}; + +struct synth_indexing { + char *command; + unsigned char lowindex; + unsigned char highindex; + unsigned char currindex; +}; + +struct spk_synth; + +struct spk_io_ops { + int (*synth_out)(struct spk_synth *synth, const char ch); + int (*synth_out_unicode)(struct spk_synth *synth, u16 ch); + void (*send_xchar)(char ch); + void (*tiocmset)(unsigned int set, unsigned int clear); + unsigned char (*synth_in)(void); + unsigned char (*synth_in_nowait)(void); + void (*flush_buffer)(void); +}; + +struct spk_synth { + struct list_head node; + + const char *name; + const char *version; + const char *long_name; + const char *init; + char procspeech; + char clear; + int delay; + int trigger; + int jiffies; + int full; + int ser; + char *dev_name; + short flags; + short startup; + const int checkval; /* for validating a proper synth module */ + struct var_t *vars; + int *default_pitch; + int *default_vol; + struct spk_io_ops *io_ops; + int (*probe)(struct spk_synth *synth); + void (*release)(void); + const char *(*synth_immediate)(struct spk_synth *synth, + const char *buff); + void (*catch_up)(struct spk_synth *synth); + void (*flush)(struct spk_synth *synth); + int (*is_alive)(struct spk_synth *synth); + int (*synth_adjust)(struct st_var_header *var); + void (*read_buff_add)(u_char c); + unsigned char (*get_index)(struct spk_synth *synth); + struct synth_indexing indexing; + int alive; + struct attribute_group attributes; +}; + +/* + * module_spk_synth() - Helper macro for registering a speakup driver + * @__spk_synth: spk_synth struct + * Helper macro for speakup drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_spk_synth(__spk_synth) \ + module_driver(__spk_synth, synth_add, synth_remove) + +struct speakup_info_t { + spinlock_t spinlock; + int port_tts; + int flushing; +}; + +struct bleep { + short freq; + unsigned long jiffies; + int active; +}; +#endif diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c new file mode 100644 index 000000000000..3568bfb89912 --- /dev/null +++ b/drivers/accessibility/speakup/synth.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include /* for isdigit() and friends */ +#include +#include /* for verify_area */ +#include /* for -EBUSY */ +#include /* for check_region, request_region */ +#include +#include /* for loops_per_sec */ +#include +#include +#include /* for copy_from_user */ +#include +#include +#include + +#include "spk_priv.h" +#include "speakup.h" +#include "serialio.h" + +static LIST_HEAD(synths); +struct spk_synth *synth; +char spk_pitch_buff[32] = ""; +static int module_status; +bool spk_quiet_boot; + +struct speakup_info_t speakup_info = { + /* + * This spinlock is used to protect the entire speakup machinery, and + * must be taken at each kernel->speakup transition and released at + * each corresponding speakup->kernel transition. + * + * The progression thread only interferes with the speakup machinery + * through the synth buffer, so only needs to take the lock + * while tinkering with the buffer. + * + * We use spin_lock/trylock_irqsave and spin_unlock_irqrestore with this + * spinlock because speakup needs to disable the keyboard IRQ. + */ + .spinlock = __SPIN_LOCK_UNLOCKED(speakup_info.spinlock), + .flushing = 0, +}; +EXPORT_SYMBOL_GPL(speakup_info); + +static int do_synth_init(struct spk_synth *in_synth); + +/* + * Main loop of the progression thread: keep eating from the buffer + * and push to the serial port, waiting as needed + * + * For devices that have a "full" notification mechanism, the driver can + * adapt the loop the way they prefer. + */ +static void _spk_do_catch_up(struct spk_synth *synth, int unicode) +{ + u16 ch; + unsigned long flags; + unsigned long jiff_max; + struct var_t *delay_time; + struct var_t *full_time; + struct var_t *jiffy_delta; + int jiffy_delta_val; + int delay_time_val; + int full_time_val; + int ret; + + jiffy_delta = spk_get_var(JIFFY); + full_time = spk_get_var(FULL); + delay_time = spk_get_var(DELAY); + + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + + jiff_max = jiffies + jiffy_delta_val; + while (!kthread_should_stop()) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + if (speakup_info.flushing) { + speakup_info.flushing = 0; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + synth->flush(synth); + continue; + } + if (!unicode) + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) { + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + break; + } + ch = synth_buffer_peek(); + set_current_state(TASK_INTERRUPTIBLE); + full_time_val = full_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (ch == '\n') + ch = synth->procspeech; + if (unicode) + ret = synth->io_ops->synth_out_unicode(synth, ch); + else + ret = synth->io_ops->synth_out(synth, ch); + if (!ret) { + schedule_timeout(msecs_to_jiffies(full_time_val)); + continue; + } + if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + jiffy_delta_val = jiffy_delta->u.n.value; + delay_time_val = delay_time->u.n.value; + full_time_val = full_time->u.n.value; + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (synth->io_ops->synth_out(synth, synth->procspeech)) + schedule_timeout( + msecs_to_jiffies(delay_time_val)); + else + schedule_timeout( + msecs_to_jiffies(full_time_val)); + jiff_max = jiffies + jiffy_delta_val; + } + set_current_state(TASK_RUNNING); + spin_lock_irqsave(&speakup_info.spinlock, flags); + synth_buffer_getc(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + } + synth->io_ops->synth_out(synth, synth->procspeech); +} + +void spk_do_catch_up(struct spk_synth *synth) +{ + _spk_do_catch_up(synth, 0); +} +EXPORT_SYMBOL_GPL(spk_do_catch_up); + +void spk_do_catch_up_unicode(struct spk_synth *synth) +{ + _spk_do_catch_up(synth, 1); +} +EXPORT_SYMBOL_GPL(spk_do_catch_up_unicode); + +void spk_synth_flush(struct spk_synth *synth) +{ + synth->io_ops->flush_buffer(); + synth->io_ops->synth_out(synth, synth->clear); +} +EXPORT_SYMBOL_GPL(spk_synth_flush); + +unsigned char spk_synth_get_index(struct spk_synth *synth) +{ + return synth->io_ops->synth_in_nowait(); +} +EXPORT_SYMBOL_GPL(spk_synth_get_index); + +int spk_synth_is_alive_nop(struct spk_synth *synth) +{ + synth->alive = 1; + return 1; +} +EXPORT_SYMBOL_GPL(spk_synth_is_alive_nop); + +int spk_synth_is_alive_restart(struct spk_synth *synth) +{ + if (synth->alive) + return 1; + if (spk_wait_for_xmitr(synth) > 0) { + /* restart */ + synth->alive = 1; + synth_printf("%s", synth->init); + return 2; /* reenabled */ + } + pr_warn("%s: can't restart synth\n", synth->long_name); + return 0; +} +EXPORT_SYMBOL_GPL(spk_synth_is_alive_restart); + +static void thread_wake_up(struct timer_list *unused) +{ + wake_up_interruptible_all(&speakup_event); +} + +static DEFINE_TIMER(thread_timer, thread_wake_up); + +void synth_start(void) +{ + struct var_t *trigger_time; + + if (!synth->alive) { + synth_buffer_clear(); + return; + } + trigger_time = spk_get_var(TRIGGER); + if (!timer_pending(&thread_timer)) + mod_timer(&thread_timer, jiffies + + msecs_to_jiffies(trigger_time->u.n.value)); +} + +void spk_do_flush(void) +{ + if (!synth) + return; + + speakup_info.flushing = 1; + synth_buffer_clear(); + if (synth->alive) { + if (spk_pitch_shift) { + synth_printf("%s", spk_pitch_buff); + spk_pitch_shift = 0; + } + } + wake_up_interruptible_all(&speakup_event); + wake_up_process(speakup_task); +} + +void synth_write(const char *buf, size_t count) +{ + while (count--) + synth_buffer_add(*buf++); + synth_start(); +} + +void synth_printf(const char *fmt, ...) +{ + va_list args; + unsigned char buf[160], *p; + int r; + + va_start(args, fmt); + r = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + if (r > sizeof(buf) - 1) + r = sizeof(buf) - 1; + + p = buf; + while (r--) + synth_buffer_add(*p++); + synth_start(); +} +EXPORT_SYMBOL_GPL(synth_printf); + +void synth_putwc(u16 wc) +{ + synth_buffer_add(wc); +} +EXPORT_SYMBOL_GPL(synth_putwc); + +void synth_putwc_s(u16 wc) +{ + synth_buffer_add(wc); + synth_start(); +} +EXPORT_SYMBOL_GPL(synth_putwc_s); + +void synth_putws(const u16 *buf) +{ + const u16 *p; + + for (p = buf; *p; p++) + synth_buffer_add(*p); +} +EXPORT_SYMBOL_GPL(synth_putws); + +void synth_putws_s(const u16 *buf) +{ + synth_putws(buf); + synth_start(); +} +EXPORT_SYMBOL_GPL(synth_putws_s); + +static int index_count; +static int sentence_count; + +void spk_reset_index_count(int sc) +{ + static int first = 1; + + if (first) + first = 0; + else + synth->get_index(synth); + index_count = 0; + sentence_count = sc; +} + +int synth_supports_indexing(void) +{ + if (synth->get_index) + return 1; + return 0; +} + +void synth_insert_next_index(int sent_num) +{ + int out; + + if (synth->alive) { + if (sent_num == 0) { + synth->indexing.currindex++; + index_count++; + if (synth->indexing.currindex > + synth->indexing.highindex) + synth->indexing.currindex = + synth->indexing.lowindex; + } + + out = synth->indexing.currindex * 10 + sent_num; + synth_printf(synth->indexing.command, out, out); + } +} + +void spk_get_index_count(int *linecount, int *sentcount) +{ + int ind = synth->get_index(synth); + + if (ind) { + sentence_count = ind % 10; + + if ((ind / 10) <= synth->indexing.currindex) + index_count = synth->indexing.currindex - (ind / 10); + else + index_count = synth->indexing.currindex + - synth->indexing.lowindex + + synth->indexing.highindex - (ind / 10) + 1; + } + *sentcount = sentence_count; + *linecount = index_count; +} + +static struct resource synth_res; + +int synth_request_region(unsigned long start, unsigned long n) +{ + struct resource *parent = &ioport_resource; + + memset(&synth_res, 0, sizeof(synth_res)); + synth_res.name = synth->name; + synth_res.start = start; + synth_res.end = start + n - 1; + synth_res.flags = IORESOURCE_BUSY; + return request_resource(parent, &synth_res); +} +EXPORT_SYMBOL_GPL(synth_request_region); + +int synth_release_region(unsigned long start, unsigned long n) +{ + return release_resource(&synth_res); +} +EXPORT_SYMBOL_GPL(synth_release_region); + +struct var_t synth_time_vars[] = { + { DELAY, .u.n = {NULL, 100, 100, 2000, 0, 0, NULL } }, + { TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL } }, + { JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL } }, + { FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL } }, + V_LAST_VAR +}; + +/* called by: speakup_init() */ +int synth_init(char *synth_name) +{ + int ret = 0; + struct spk_synth *tmp, *synth = NULL; + + if (!synth_name) + return 0; + + if (strcmp(synth_name, "none") == 0) { + mutex_lock(&spk_mutex); + synth_release(); + mutex_unlock(&spk_mutex); + return 0; + } + + mutex_lock(&spk_mutex); + /* First, check if we already have it loaded. */ + list_for_each_entry(tmp, &synths, node) { + if (strcmp(tmp->name, synth_name) == 0) + synth = tmp; + } + + /* If we got one, initialize it now. */ + if (synth) + ret = do_synth_init(synth); + else + ret = -ENODEV; + mutex_unlock(&spk_mutex); + + return ret; +} + +/* called by: synth_add() */ +static int do_synth_init(struct spk_synth *in_synth) +{ + struct var_t *var; + + synth_release(); + if (in_synth->checkval != SYNTH_CHECK) + return -EINVAL; + synth = in_synth; + synth->alive = 0; + pr_warn("synth probe\n"); + if (synth->probe(synth) < 0) { + pr_warn("%s: device probe failed\n", in_synth->name); + synth = NULL; + return -ENODEV; + } + synth_time_vars[0].u.n.value = + synth_time_vars[0].u.n.default_val = synth->delay; + synth_time_vars[1].u.n.value = + synth_time_vars[1].u.n.default_val = synth->trigger; + synth_time_vars[2].u.n.value = + synth_time_vars[2].u.n.default_val = synth->jiffies; + synth_time_vars[3].u.n.value = + synth_time_vars[3].u.n.default_val = synth->full; + synth_printf("%s", synth->init); + for (var = synth->vars; + (var->var_id >= 0) && (var->var_id < MAXVARS); var++) + speakup_register_var(var); + if (!spk_quiet_boot) + synth_printf("%s found\n", synth->long_name); + if (synth->attributes.name && + sysfs_create_group(speakup_kobj, &synth->attributes) < 0) + return -ENOMEM; + synth_flags = synth->flags; + wake_up_interruptible_all(&speakup_event); + if (speakup_task) + wake_up_process(speakup_task); + return 0; +} + +void synth_release(void) +{ + struct var_t *var; + unsigned long flags; + + if (!synth) + return; + spin_lock_irqsave(&speakup_info.spinlock, flags); + pr_info("releasing synth %s\n", synth->name); + synth->alive = 0; + del_timer(&thread_timer); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (synth->attributes.name) + sysfs_remove_group(speakup_kobj, &synth->attributes); + for (var = synth->vars; var->var_id != MAXVARS; var++) + speakup_unregister_var(var->var_id); + synth->release(); + synth = NULL; +} + +/* called by: all_driver_init() */ +int synth_add(struct spk_synth *in_synth) +{ + int status = 0; + struct spk_synth *tmp; + + mutex_lock(&spk_mutex); + + list_for_each_entry(tmp, &synths, node) { + if (tmp == in_synth) { + mutex_unlock(&spk_mutex); + return 0; + } + } + + if (in_synth->startup) + status = do_synth_init(in_synth); + + if (!status) + list_add_tail(&in_synth->node, &synths); + + mutex_unlock(&spk_mutex); + return status; +} +EXPORT_SYMBOL_GPL(synth_add); + +void synth_remove(struct spk_synth *in_synth) +{ + mutex_lock(&spk_mutex); + if (synth == in_synth) + synth_release(); + list_del(&in_synth->node); + module_status = 0; + mutex_unlock(&spk_mutex); +} +EXPORT_SYMBOL_GPL(synth_remove); + +struct spk_synth *synth_current(void) +{ + return synth; +} +EXPORT_SYMBOL_GPL(synth_current); + +short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; diff --git a/drivers/accessibility/speakup/thread.c b/drivers/accessibility/speakup/thread.c new file mode 100644 index 000000000000..2fc75e60fbac --- /dev/null +++ b/drivers/accessibility/speakup/thread.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#include "spk_types.h" +#include "speakup.h" +#include "spk_priv.h" + +DECLARE_WAIT_QUEUE_HEAD(speakup_event); +EXPORT_SYMBOL_GPL(speakup_event); + +int speakup_thread(void *data) +{ + unsigned long flags; + int should_break; + struct bleep our_sound; + + our_sound.active = 0; + our_sound.freq = 0; + our_sound.jiffies = 0; + + mutex_lock(&spk_mutex); + while (1) { + DEFINE_WAIT(wait); + + while (1) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + our_sound = spk_unprocessed_sound; + spk_unprocessed_sound.active = 0; + prepare_to_wait(&speakup_event, &wait, + TASK_INTERRUPTIBLE); + should_break = kthread_should_stop() || + our_sound.active || + (synth && synth->catch_up && synth->alive && + (speakup_info.flushing || + !synth_buffer_empty())); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + if (should_break) + break; + mutex_unlock(&spk_mutex); + schedule(); + mutex_lock(&spk_mutex); + } + finish_wait(&speakup_event, &wait); + if (kthread_should_stop()) + break; + + if (our_sound.active) + kd_mksound(our_sound.freq, our_sound.jiffies); + if (synth && synth->catch_up && synth->alive) { + /* + * It is up to the callee to take the lock, so that it + * can sleep whenever it likes + */ + synth->catch_up(synth); + } + + speakup_start_ttys(); + } + mutex_unlock(&spk_mutex); + return 0; +} diff --git a/drivers/accessibility/speakup/varhandlers.c b/drivers/accessibility/speakup/varhandlers.c new file mode 100644 index 000000000000..d7f6bec7ff06 --- /dev/null +++ b/drivers/accessibility/speakup/varhandlers.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include "spk_types.h" +#include "spk_priv.h" +#include "speakup.h" + +static struct st_var_header var_headers[] = { + { "version", VERSION, VAR_PROC, NULL, NULL }, + { "synth_name", SYNTH, VAR_PROC, NULL, NULL }, + { "keymap", KEYMAP, VAR_PROC, NULL, NULL }, + { "silent", SILENT, VAR_PROC, NULL, NULL }, + { "punc_some", PUNC_SOME, VAR_PROC, NULL, NULL }, + { "punc_most", PUNC_MOST, VAR_PROC, NULL, NULL }, + { "punc_all", PUNC_ALL, VAR_PROC, NULL, NULL }, + { "delimiters", DELIM, VAR_PROC, NULL, NULL }, + { "repeats", REPEATS, VAR_PROC, NULL, NULL }, + { "ex_num", EXNUMBER, VAR_PROC, NULL, NULL }, + { "characters", CHARS, VAR_PROC, NULL, NULL }, + { "synth_direct", SYNTH_DIRECT, VAR_PROC, NULL, NULL }, + { "caps_start", CAPS_START, VAR_STRING, spk_str_caps_start, NULL }, + { "caps_stop", CAPS_STOP, VAR_STRING, spk_str_caps_stop, NULL }, + { "delay_time", DELAY, VAR_TIME, NULL, NULL }, + { "trigger_time", TRIGGER, VAR_TIME, NULL, NULL }, + { "jiffy_delta", JIFFY, VAR_TIME, NULL, NULL }, + { "full_time", FULL, VAR_TIME, NULL, NULL }, + { "spell_delay", SPELL_DELAY, VAR_NUM, &spk_spell_delay, NULL }, + { "bleeps", BLEEPS, VAR_NUM, &spk_bleeps, NULL }, + { "attrib_bleep", ATTRIB_BLEEP, VAR_NUM, &spk_attrib_bleep, NULL }, + { "bleep_time", BLEEP_TIME, VAR_TIME, &spk_bleep_time, NULL }, + { "cursor_time", CURSOR_TIME, VAR_TIME, NULL, NULL }, + { "punc_level", PUNC_LEVEL, VAR_NUM, &spk_punc_level, NULL }, + { "reading_punc", READING_PUNC, VAR_NUM, &spk_reading_punc, NULL }, + { "say_control", SAY_CONTROL, VAR_NUM, &spk_say_ctrl, NULL }, + { "say_word_ctl", SAY_WORD_CTL, VAR_NUM, &spk_say_word_ctl, NULL }, + { "no_interrupt", NO_INTERRUPT, VAR_NUM, &spk_no_intr, NULL }, + { "key_echo", KEY_ECHO, VAR_NUM, &spk_key_echo, NULL }, + { "bell_pos", BELL_POS, VAR_NUM, &spk_bell_pos, NULL }, + { "rate", RATE, VAR_NUM, NULL, NULL }, + { "pitch", PITCH, VAR_NUM, NULL, NULL }, + { "inflection", INFLECTION, VAR_NUM, NULL, NULL }, + { "vol", VOL, VAR_NUM, NULL, NULL }, + { "tone", TONE, VAR_NUM, NULL, NULL }, + { "punct", PUNCT, VAR_NUM, NULL, NULL }, + { "voice", VOICE, VAR_NUM, NULL, NULL }, + { "freq", FREQUENCY, VAR_NUM, NULL, NULL }, + { "lang", LANG, VAR_NUM, NULL, NULL }, + { "chartab", CHARTAB, VAR_PROC, NULL, NULL }, + { "direct", DIRECT, VAR_NUM, NULL, NULL }, + { "pause", PAUSE, VAR_STRING, spk_str_pause, NULL }, +}; + +static struct st_var_header *var_ptrs[MAXVARS] = { NULL, NULL, NULL }; + +static struct punc_var_t punc_vars[] = { + { PUNC_SOME, 1 }, + { PUNC_MOST, 2 }, + { PUNC_ALL, 3 }, + { DELIM, 4 }, + { REPEATS, 5 }, + { EXNUMBER, 6 }, + { -1, -1 }, +}; + +int spk_chartab_get_value(char *keyword) +{ + int value = 0; + + if (!strcmp(keyword, "ALPHA")) + value = ALPHA; + else if (!strcmp(keyword, "B_CTL")) + value = B_CTL; + else if (!strcmp(keyword, "WDLM")) + value = WDLM; + else if (!strcmp(keyword, "A_PUNC")) + value = A_PUNC; + else if (!strcmp(keyword, "PUNC")) + value = PUNC; + else if (!strcmp(keyword, "NUM")) + value = NUM; + else if (!strcmp(keyword, "A_CAP")) + value = A_CAP; + else if (!strcmp(keyword, "B_CAPSYM")) + value = B_CAPSYM; + else if (!strcmp(keyword, "B_SYM")) + value = B_SYM; + return value; +} + +void speakup_register_var(struct var_t *var) +{ + static char nothing[2] = "\0"; + int i; + struct st_var_header *p_header; + + BUG_ON(!var || var->var_id < 0 || var->var_id >= MAXVARS); + if (!var_ptrs[0]) { + for (i = 0; i < MAXVARS; i++) { + p_header = &var_headers[i]; + var_ptrs[p_header->var_id] = p_header; + p_header->data = NULL; + } + } + p_header = var_ptrs[var->var_id]; + if (p_header->data) + return; + p_header->data = var; + switch (p_header->var_type) { + case VAR_STRING: + spk_set_string_var(nothing, p_header, 0); + break; + case VAR_NUM: + case VAR_TIME: + spk_set_num_var(0, p_header, E_DEFAULT); + break; + default: + break; + } +} + +void speakup_unregister_var(enum var_id_t var_id) +{ + struct st_var_header *p_header; + + BUG_ON(var_id < 0 || var_id >= MAXVARS); + p_header = var_ptrs[var_id]; + p_header->data = NULL; +} + +struct st_var_header *spk_get_var_header(enum var_id_t var_id) +{ + struct st_var_header *p_header; + + if (var_id < 0 || var_id >= MAXVARS) + return NULL; + p_header = var_ptrs[var_id]; + if (!p_header->data) + return NULL; + return p_header; +} + +struct st_var_header *spk_var_header_by_name(const char *name) +{ + int i; + + if (!name) + return NULL; + + for (i = 0; i < MAXVARS; i++) { + if (strcmp(name, var_ptrs[i]->name) == 0) + return var_ptrs[i]; + } + return NULL; +} + +struct var_t *spk_get_var(enum var_id_t var_id) +{ + BUG_ON(var_id < 0 || var_id >= MAXVARS); + BUG_ON(!var_ptrs[var_id]); + return var_ptrs[var_id]->data; +} +EXPORT_SYMBOL_GPL(spk_get_var); + +struct punc_var_t *spk_get_punc_var(enum var_id_t var_id) +{ + struct punc_var_t *rv = NULL; + struct punc_var_t *where; + + where = punc_vars; + while ((where->var_id != -1) && (!rv)) { + if (where->var_id == var_id) + rv = where; + else + where++; + } + return rv; +} + +/* handlers for setting vars */ +int spk_set_num_var(int input, struct st_var_header *var, int how) +{ + int val; + int *p_val = var->p_val; + char buf[32]; + char *cp; + struct var_t *var_data = var->data; + + if (!var_data) + return -ENODATA; + + val = var_data->u.n.value; + switch (how) { + case E_NEW_DEFAULT: + if (input < var_data->u.n.low || input > var_data->u.n.high) + return -ERANGE; + var_data->u.n.default_val = input; + return 0; + case E_DEFAULT: + val = var_data->u.n.default_val; + break; + case E_SET: + val = input; + break; + case E_INC: + val += input; + break; + case E_DEC: + val -= input; + break; + } + + if (val < var_data->u.n.low || val > var_data->u.n.high) + return -ERANGE; + + var_data->u.n.value = val; + if (var->var_type == VAR_TIME && p_val) { + *p_val = msecs_to_jiffies(val); + return 0; + } + if (p_val) + *p_val = val; + if (var->var_id == PUNC_LEVEL) { + spk_punc_mask = spk_punc_masks[val]; + return 0; + } + if (var_data->u.n.multiplier != 0) + val *= var_data->u.n.multiplier; + val += var_data->u.n.offset; + if (var->var_id < FIRST_SYNTH_VAR || !synth) + return 0; + if (synth->synth_adjust) + return synth->synth_adjust(var); + + if (!var_data->u.n.synth_fmt) + return 0; + if (var->var_id == PITCH) + cp = spk_pitch_buff; + else + cp = buf; + if (!var_data->u.n.out_str) + sprintf(cp, var_data->u.n.synth_fmt, (int)val); + else + sprintf(cp, var_data->u.n.synth_fmt, + var_data->u.n.out_str[val]); + synth_printf("%s", cp); + return 0; +} + +int spk_set_string_var(const char *page, struct st_var_header *var, int len) +{ + struct var_t *var_data = var->data; + + if (!var_data) + return -ENODATA; + if (len > MAXVARLEN) + return -E2BIG; + if (!len) { + if (!var_data->u.s.default_val) + return 0; + if (!var->p_val) + var->p_val = var_data->u.s.default_val; + if (var->p_val != var_data->u.s.default_val) + strcpy((char *)var->p_val, var_data->u.s.default_val); + return -ERESTART; + } else if (var->p_val) { + strcpy((char *)var->p_val, page); + } else { + return -E2BIG; + } + return 0; +} + +/* + * spk_set_mask_bits sets or clears the punc/delim/repeat bits, + * if input is null uses the defaults. + * values for how: 0 clears bits of chars supplied, + * 1 clears allk, 2 sets bits for chars + */ +int spk_set_mask_bits(const char *input, const int which, const int how) +{ + u_char *cp; + short mask = spk_punc_info[which].mask; + + if (how & 1) { + for (cp = (u_char *)spk_punc_info[3].value; *cp; cp++) + spk_chartab[*cp] &= ~mask; + } + cp = (u_char *)input; + if (!cp) { + cp = spk_punc_info[which].value; + } else { + for (; *cp; cp++) { + if (*cp < SPACE) + break; + if (mask < PUNC) { + if (!(spk_chartab[*cp] & PUNC)) + break; + } else if (spk_chartab[*cp] & B_NUM) { + break; + } + } + if (*cp) + return -EINVAL; + cp = (u_char *)input; + } + if (how & 2) { + for (; *cp; cp++) + if (*cp > SPACE) + spk_chartab[*cp] |= mask; + } else { + for (; *cp; cp++) + if (*cp > SPACE) + spk_chartab[*cp] &= ~mask; + } + return 0; +} + +char *spk_strlwr(char *s) +{ + char *p; + + if (!s) + return NULL; + + for (p = s; *p; p++) + *p = tolower(*p); + return s; +} + +char *spk_s2uchar(char *start, char *dest) +{ + int val; + + /* Do not replace with kstrtoul: here we need start to be updated */ + val = simple_strtoul(skip_spaces(start), &start, 10); + if (*start == ',') + start++; + *dest = (u_char)val; + return start; +} diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index b3fb4d41e231..e6c831c6cccc 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -56,8 +56,6 @@ source "drivers/staging/sm750fb/Kconfig" source "drivers/staging/emxx_udc/Kconfig" -source "drivers/staging/speakup/Kconfig" - source "drivers/staging/nvec/Kconfig" source "drivers/staging/media/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 3d8c7ea21a10..a3b1fd0622f9 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_FB_SM750) += sm750fb/ obj-$(CONFIG_USB_EMXX) += emxx_udc/ -obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_MFD_NVEC) += nvec/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_STAGING_BOARD) += board/ diff --git a/drivers/staging/speakup/DefaultKeyAssignments b/drivers/staging/speakup/DefaultKeyAssignments deleted file mode 100644 index 101c803b21fd..000000000000 --- a/drivers/staging/speakup/DefaultKeyAssignments +++ /dev/null @@ -1,46 +0,0 @@ -This file is intended to give you an overview of the default keys used -by speakup for it's review functions. You may change them to be -anything you want but that will take some familiarity with key -mapping. - -We have remapped the insert or zero key on the keypad to act as a -shift key. Well, actually as an altgr key. So in the following list -InsKeyPad-period means hold down the insert key like a shift key and -hit the keypad period. - -KeyPad-8 Say current Line -InsKeyPad-8 say from top of screen to reading cursor. -KeyPad-7 Say Previous Line (UP one line) -KeyPad-9 Say Next Line (down one line) -KeyPad-5 Say Current Word -InsKeyPad-5 Spell Current Word -KeyPad-4 Say Previous Word (left one word) -InsKeyPad-4 say from left edge of line to reading cursor. -KeyPad-6 Say Next Word (right one word) -InsKeyPad-6 Say from reading cursor to right edge of line. -KeyPad-2 Say Current Letter -InsKeyPad-2 say current letter phonetically -KeyPad-1 Say Previous Character (left one letter) -KeyPad-3 Say Next Character (right one letter) -KeyPad-plus Say Entire Screen -InsKeyPad-plus Say from reading cursor line to bottom of screen. -KeyPad-Minus Park reading cursor (toggle) -InsKeyPad-minus Say character hex and decimal value. -KeyPad-period Say Position (current line, position and console) -InsKeyPad-period say colour attributes of current position. -InsKeyPad-9 Move reading cursor to top of screen (insert pgup) -InsKeyPad-3 Move reading cursor to bottom of screen (insert pgdn) -InsKeyPad-7 Move reading cursor to left edge of screen (insert home) -InsKeyPad-1 Move reading cursor to right edge of screen (insert end) -ControlKeyPad-1 Move reading cursor to last character on current line. -KeyPad-Enter Shut Up (until another key is hit) and sync reading cursor -InsKeyPad-Enter Shut Up (until toggled back on). -InsKeyPad-star n go to line (y) or column (x). Where 'n' is any - allowed value for the row or column for your current screen. -KeyPad-/ Mark and Cut screen region. -InsKeyPad-/ Paste screen region into any console. - -Hitting any key while speakup is outputting speech will quiet the -synth until it has caught up with what is being printed on the -console. - diff --git a/drivers/staging/speakup/Kconfig b/drivers/staging/speakup/Kconfig deleted file mode 100644 index 0803c2013cf4..000000000000 --- a/drivers/staging/speakup/Kconfig +++ /dev/null @@ -1,200 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -menu "Speakup console speech" - -config SPEAKUP - depends on VT - tristate "Speakup core" - help - This is the Speakup screen reader. Think of it as a - video console for blind people. If built in to the - kernel, it can speak everything on the text console from - boot up to shutdown. For more information on Speakup, - point your browser at . - There is also a mailing list at the above url that you - can subscribe to. - - Supported synthesizers are accent sa, accent pc, - appollo II., Auddapter, Braille 'n Speak, Dectalk - external (old), Dectalk PC (full length isa board), - Dectalk express, Doubletalk, Doubletalk LT or - Litetalk, Keynote Gold internal PC, software - synthesizers, Speakout, transport, and a dummy module - that can be used with a plain text terminal. - - Speakup can either be built in or compiled as a module - by answering y or m. If you answer y here, then you - must answer either y or m to at least one of the - synthesizer drivers below. If you answer m here, then - the synthesizer drivers below can only be built as - modules. - - These drivers are not standalone drivers, but must be - used in conjunction with Speakup. Think of them as - video cards for blind people. - - - The Dectalk pc driver can only be built as a module, and - requires software to be pre-loaded on to the card before - the module can be loaded. See the decpc choice below - for more details. - - If you are not a blind person, or don't have access to - one of the listed synthesizers, you should say n. - -if SPEAKUP -config SPEAKUP_SYNTH_ACNTSA - tristate "Accent SA synthesizer support" - help - This is the Speakup driver for the Accent SA - synthesizer. You can say y to build it into the kernel, - or m to build it as a module. See the configuration - help on the Speakup choice above for more info. - -config SPEAKUP_SYNTH_ACNTPC - tristate "Accent PC synthesizer support" - depends on ISA || COMPILE_TEST - help - This is the Speakup driver for the accent pc - synthesizer. You can say y to build it into the kernel, - or m to build it as a module. See the configuration - help on the Speakup choice above for more info. - -config SPEAKUP_SYNTH_APOLLO - tristate "Apollo II synthesizer support" - help - This is the Speakup driver for the Apollo II - synthesizer. You can say y to build it into the kernel, - or m to build it as a module. See the configuration - help on the Speakup choice above for more info. - -config SPEAKUP_SYNTH_AUDPTR - tristate "Audapter synthesizer support" - help - This is the Speakup driver for the Audapter synthesizer. - You can say y to build it into the kernel, or m to - build it as a module. See the configuration help on the - Speakup choice above for more info. - -config SPEAKUP_SYNTH_BNS - tristate "Braille 'n' Speak synthesizer support" - help - This is the Speakup driver for the Braille 'n' Speak - synthesizer. You can say y to build it into the kernel, - or m to build it as a module. See the configuration - help on the Speakup choice above for more info. - -config SPEAKUP_SYNTH_DECTLK - tristate "DECtalk Express synthesizer support" - help - - This is the Speakup driver for the DecTalk Express - synthesizer. You can say y to build it into the kernel, - or m to build it as a module. See the configuration - help on the Speakup choice above for more info. - -config SPEAKUP_SYNTH_DECEXT - tristate "DECtalk External (old) synthesizer support" - help - - This is the Speakup driver for the DecTalk External - (old) synthesizer. You can say y to build it into the - kernel, or m to build it as a module. See the - configuration help on the Speakup choice above for more - info. - -config SPEAKUP_SYNTH_DECPC - depends on m - depends on ISA || COMPILE_TEST - tristate "DECtalk PC (big ISA card) synthesizer support" - help - - This is the Speakup driver for the DecTalk PC (full - length ISA) synthesizer. You can say m to build it as - a module. See the configuration help on the Speakup - choice above for more info. - - In order to use the DecTalk PC driver, you must download - the dec_pc.tgz file from linux-speakup.org. It is in - the pub/linux/goodies directory. The dec_pc.tgz file - contains the software which must be pre-loaded on to the - DecTalk PC board in order to use it with this driver. - This driver must be built as a module, and can not be - loaded until the file system is mounted and the DecTalk - PC software has been pre-loaded on to the board. - - See the README file in the dec_pc.tgz file for more - details. - -config SPEAKUP_SYNTH_DTLK - tristate "DoubleTalk PC synthesizer support" - depends on ISA || COMPILE_TEST - help - - This is the Speakup driver for the internal DoubleTalk - PC synthesizer. You can say y to build it into the - kernel, or m to build it as a module. See the - configuration help on the Speakup choice above for more - info. - -config SPEAKUP_SYNTH_KEYPC - tristate "Keynote Gold PC synthesizer support" - depends on ISA || COMPILE_TEST - help - - This is the Speakup driver for the Keynote Gold - PC synthesizer. You can say y to build it into the - kernel, or m to build it as a module. See the - configuration help on the Speakup choice above for more - info. - -config SPEAKUP_SYNTH_LTLK - tristate "DoubleTalk LT/LiteTalk synthesizer support" -help - - This is the Speakup driver for the LiteTalk/DoubleTalk - LT synthesizer. You can say y to build it into the - kernel, or m to build it as a module. See the - configuration help on the Speakup choice above for more - info. - -config SPEAKUP_SYNTH_SOFT - tristate "Userspace software synthesizer support" - help - - This is the software synthesizer device node. It will - register a device /dev/softsynth which midware programs - and speech daemons may open and read to provide kernel - output to software synths such as espeak, festival, - flite and so forth. You can select 'y' or 'm' to have - it built-in to the kernel or loaded as a module. - -config SPEAKUP_SYNTH_SPKOUT - tristate "Speak Out synthesizer support" - help - - This is the Speakup driver for the Speakout synthesizer. - You can say y to build it into the kernel, or m to - build it as a module. See the configuration help on the - Speakup choice above for more info. - -config SPEAKUP_SYNTH_TXPRT - tristate "Transport synthesizer support" - help - - This is the Speakup driver for the Transport - synthesizer. You can say y to build it into the kernel, - or m to build it as a module. See the configuration - help on the Speakup choice above for more info. - -config SPEAKUP_SYNTH_DUMMY - tristate "Dummy synthesizer driver (for testing)" - help - - This is a dummy Speakup driver for plugging a mere serial - terminal. This is handy if you want to test speakup but - don't have the hardware. You can say y to build it into - the kernel, or m to build it as a module. See the - configuration help on the Speakup choice above for more info. - -endif # SPEAKUP -endmenu diff --git a/drivers/staging/speakup/Makefile b/drivers/staging/speakup/Makefile deleted file mode 100644 index 5befb4933b85..000000000000 --- a/drivers/staging/speakup/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SPEAKUP_SYNTH_ACNTSA) += speakup_acntsa.o -obj-$(CONFIG_SPEAKUP_SYNTH_ACNTPC) += speakup_acntpc.o -obj-$(CONFIG_SPEAKUP_SYNTH_APOLLO) += speakup_apollo.o -obj-$(CONFIG_SPEAKUP_SYNTH_AUDPTR) += speakup_audptr.o -obj-$(CONFIG_SPEAKUP_SYNTH_BNS) += speakup_bns.o -obj-$(CONFIG_SPEAKUP_SYNTH_DECTLK) += speakup_dectlk.o -obj-$(CONFIG_SPEAKUP_SYNTH_DECEXT) += speakup_decext.o -obj-$(CONFIG_SPEAKUP_SYNTH_DECPC) += speakup_decpc.o -obj-$(CONFIG_SPEAKUP_SYNTH_DTLK) += speakup_dtlk.o -obj-$(CONFIG_SPEAKUP_SYNTH_KEYPC) += speakup_keypc.o -obj-$(CONFIG_SPEAKUP_SYNTH_LTLK) += speakup_ltlk.o -obj-$(CONFIG_SPEAKUP_SYNTH_SOFT) += speakup_soft.o -obj-$(CONFIG_SPEAKUP_SYNTH_SPKOUT) += speakup_spkout.o -obj-$(CONFIG_SPEAKUP_SYNTH_TXPRT) += speakup_txprt.o -obj-$(CONFIG_SPEAKUP_SYNTH_DUMMY) += speakup_dummy.o - -obj-$(CONFIG_SPEAKUP) += speakup.o -speakup-y := \ - buffers.o \ - devsynth.o \ - i18n.o \ - fakekey.o \ - main.o \ - keyhelp.o \ - kobjects.o \ - selection.o \ - serialio.o \ - spk_ttyio.o \ - synth.o \ - thread.o \ - varhandlers.o diff --git a/drivers/staging/speakup/TODO b/drivers/staging/speakup/TODO deleted file mode 100644 index d4ca093bf0bd..000000000000 --- a/drivers/staging/speakup/TODO +++ /dev/null @@ -1,22 +0,0 @@ -Speakup project home: http://www.linux-speakup.org - -Mailing List: speakup@linux-speakup.org - -Speakup is a kernel based screen review package for the linux operating -system. It allows blind users to interact with applications on the -linux console by means of synthetic speech. - -Currently, speakup has one issue we know of. - -It seems to only happen on SMP systems. It seems that text in the output buffer -gets garbled because a lock is not set. This bug happens regularly, but no one -has been able to find a situation which produces it consistently. - -Patches, suggestions, corrections, etc, are definitely welcome. - -We prefer that you contact us on the mailing list; however, if you do -not want to subscribe to a mailing list, send your email to all of the -following: - -okash.khawaja@gmail.com, w.d.hubbs@gmail.com, chris@the-brannons.com, -kirk@reisers.ca and samuel.thibault@ens-lyon.org. diff --git a/drivers/staging/speakup/buffers.c b/drivers/staging/speakup/buffers.c deleted file mode 100644 index 1371ced2f5ca..000000000000 --- a/drivers/staging/speakup/buffers.c +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include - -#include "speakup.h" -#include "spk_priv.h" - -#define SYNTH_BUF_SIZE 8192 /* currently 8K bytes */ - -static u16 synth_buffer[SYNTH_BUF_SIZE]; /* guess what this is for! */ -static u16 *buff_in = synth_buffer; -static u16 *buff_out = synth_buffer; -static u16 *buffer_end = synth_buffer + SYNTH_BUF_SIZE - 1; - -/* These try to throttle applications by stopping the TTYs - * Note: we need to make sure that we will restart them eventually, which is - * usually not possible to do from the notifiers. TODO: it should be possible - * starting from linux 2.6.26. - * - * So we only stop when we know alive == 1 (else we discard the data anyway), - * and the alive synth will eventually call start_ttys from the thread context. - */ -void speakup_start_ttys(void) -{ - int i; - - for (i = 0; i < MAX_NR_CONSOLES; i++) { - if (speakup_console[i] && speakup_console[i]->tty_stopped) - continue; - if (vc_cons[i].d && vc_cons[i].d->port.tty) - start_tty(vc_cons[i].d->port.tty); - } -} -EXPORT_SYMBOL_GPL(speakup_start_ttys); - -static void speakup_stop_ttys(void) -{ - int i; - - for (i = 0; i < MAX_NR_CONSOLES; i++) - if (vc_cons[i].d && vc_cons[i].d->port.tty) - stop_tty(vc_cons[i].d->port.tty); -} - -static int synth_buffer_free(void) -{ - int chars_free; - - if (buff_in >= buff_out) - chars_free = SYNTH_BUF_SIZE - (buff_in - buff_out); - else - chars_free = buff_out - buff_in; - return chars_free; -} - -int synth_buffer_empty(void) -{ - return (buff_in == buff_out); -} -EXPORT_SYMBOL_GPL(synth_buffer_empty); - -void synth_buffer_add(u16 ch) -{ - if (!synth->alive) { - /* This makes sure that we won't stop TTYs if there is no synth - * to restart them - */ - return; - } - if (synth_buffer_free() <= 100) { - synth_start(); - speakup_stop_ttys(); - } - if (synth_buffer_free() <= 1) - return; - *buff_in++ = ch; - if (buff_in > buffer_end) - buff_in = synth_buffer; - /* We have written something to the speech synthesis, so we are not - * paused any more. - */ - spk_paused = false; -} - -u16 synth_buffer_getc(void) -{ - u16 ch; - - if (buff_out == buff_in) - return 0; - ch = *buff_out++; - if (buff_out > buffer_end) - buff_out = synth_buffer; - return ch; -} -EXPORT_SYMBOL_GPL(synth_buffer_getc); - -u16 synth_buffer_peek(void) -{ - if (buff_out == buff_in) - return 0; - return *buff_out; -} -EXPORT_SYMBOL_GPL(synth_buffer_peek); - -void synth_buffer_skip_nonlatin1(void) -{ - while (buff_out != buff_in) { - if (*buff_out < 0x100) - return; - buff_out++; - if (buff_out > buffer_end) - buff_out = synth_buffer; - } -} -EXPORT_SYMBOL_GPL(synth_buffer_skip_nonlatin1); - -void synth_buffer_clear(void) -{ - buff_in = synth_buffer; - buff_out = synth_buffer; -} -EXPORT_SYMBOL_GPL(synth_buffer_clear); diff --git a/drivers/staging/speakup/devsynth.c b/drivers/staging/speakup/devsynth.c deleted file mode 100644 index d30571663585..000000000000 --- a/drivers/staging/speakup/devsynth.c +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include /* for misc_register, and MISC_DYNAMIC_MINOR */ -#include -#include - -#include "speakup.h" -#include "spk_priv.h" - -static int misc_registered; -static int dev_opened; - -static ssize_t speakup_file_write(struct file *fp, const char __user *buffer, - size_t nbytes, loff_t *ppos) -{ - size_t count = nbytes; - const char __user *ptr = buffer; - size_t bytes; - unsigned long flags; - u_char buf[256]; - - if (!synth) - return -ENODEV; - while (count > 0) { - bytes = min(count, sizeof(buf)); - if (copy_from_user(buf, ptr, bytes)) - return -EFAULT; - count -= bytes; - ptr += bytes; - spin_lock_irqsave(&speakup_info.spinlock, flags); - synth_write(buf, bytes); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - } - return (ssize_t)nbytes; -} - -static ssize_t speakup_file_read(struct file *fp, char __user *buf, - size_t nbytes, loff_t *ppos) -{ - return 0; -} - -static int speakup_file_open(struct inode *ip, struct file *fp) -{ - if (!synth) - return -ENODEV; - if (xchg(&dev_opened, 1)) - return -EBUSY; - return 0; -} - -static int speakup_file_release(struct inode *ip, struct file *fp) -{ - dev_opened = 0; - return 0; -} - -static const struct file_operations synth_fops = { - .read = speakup_file_read, - .write = speakup_file_write, - .open = speakup_file_open, - .release = speakup_file_release, -}; - -static struct miscdevice synth_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "synth", - .fops = &synth_fops, -}; - -void speakup_register_devsynth(void) -{ - if (misc_registered != 0) - return; -/* zero it so if register fails, deregister will not ref invalid ptrs */ - if (misc_register(&synth_device)) { - pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); - } else { - pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", - MISC_MAJOR, synth_device.minor); - misc_registered = 1; - } -} - -void speakup_unregister_devsynth(void) -{ - if (!misc_registered) - return; - pr_info("speakup: unregistering synth device /dev/synth\n"); - misc_deregister(&synth_device); - misc_registered = 0; -} diff --git a/drivers/staging/speakup/fakekey.c b/drivers/staging/speakup/fakekey.c deleted file mode 100644 index cd029968462f..000000000000 --- a/drivers/staging/speakup/fakekey.c +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* fakekey.c - * Functions for simulating keypresses. - * - * Copyright (C) 2010 the Speakup Team - */ -#include -#include -#include -#include -#include - -#include "speakup.h" - -#define PRESSED 1 -#define RELEASED 0 - -static DEFINE_PER_CPU(int, reporting_keystroke); - -static struct input_dev *virt_keyboard; - -int speakup_add_virtual_keyboard(void) -{ - int err; - - virt_keyboard = input_allocate_device(); - - if (!virt_keyboard) - return -ENOMEM; - - virt_keyboard->name = "Speakup"; - virt_keyboard->id.bustype = BUS_VIRTUAL; - virt_keyboard->phys = "speakup/input0"; - virt_keyboard->dev.parent = NULL; - - __set_bit(EV_KEY, virt_keyboard->evbit); - __set_bit(KEY_DOWN, virt_keyboard->keybit); - - err = input_register_device(virt_keyboard); - if (err) { - input_free_device(virt_keyboard); - virt_keyboard = NULL; - } - - return err; -} - -void speakup_remove_virtual_keyboard(void) -{ - if (virt_keyboard) { - input_unregister_device(virt_keyboard); - virt_keyboard = NULL; - } -} - -/* - * Send a simulated down-arrow to the application. - */ -void speakup_fake_down_arrow(void) -{ - unsigned long flags; - - /* disable keyboard interrupts */ - local_irq_save(flags); - /* don't change CPU */ - preempt_disable(); - - __this_cpu_write(reporting_keystroke, true); - input_report_key(virt_keyboard, KEY_DOWN, PRESSED); - input_report_key(virt_keyboard, KEY_DOWN, RELEASED); - input_sync(virt_keyboard); - __this_cpu_write(reporting_keystroke, false); - - /* reenable preemption */ - preempt_enable(); - /* reenable keyboard interrupts */ - local_irq_restore(flags); -} - -/* - * Are we handling a simulated keypress on the current CPU? - * Returns a boolean. - */ -bool speakup_fake_key_pressed(void) -{ - return this_cpu_read(reporting_keystroke); -} diff --git a/drivers/staging/speakup/i18n.c b/drivers/staging/speakup/i18n.c deleted file mode 100644 index ee240d36f947..000000000000 --- a/drivers/staging/speakup/i18n.c +++ /dev/null @@ -1,625 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Internationalization implementation. Includes definitions of English - * string arrays, and the i18n pointer. - */ - -#include /* For kmalloc. */ -#include -#include -#include -#include "speakup.h" -#include "spk_priv.h" - -static char *speakup_msgs[MSG_LAST_INDEX]; -static char *speakup_default_msgs[MSG_LAST_INDEX] = { - [MSG_BLANK] = "blank", - [MSG_IAM_ALIVE] = "I'm aLive!", - [MSG_YOU_KILLED_SPEAKUP] = "You killed speakup!", - [MSG_HEY_THATS_BETTER] = "hey. That's better!", - [MSG_YOU_TURNED_ME_OFF] = "You turned me off!", - [MSG_PARKED] = "parked!", - [MSG_UNPARKED] = "unparked!", - [MSG_MARK] = "mark", - [MSG_CUT] = "cut", - [MSG_MARK_CLEARED] = "mark, cleared", - [MSG_PASTE] = "paste", - [MSG_BRIGHT] = "bright", - [MSG_ON_BLINKING] = "on blinking", - [MSG_OFF] = "off", - [MSG_ON] = "on", - [MSG_NO_WINDOW] = "no window", - [MSG_CURSORING_OFF] = "cursoring off", - [MSG_CURSORING_ON] = "cursoring on", - [MSG_HIGHLIGHT_TRACKING] = "highlight tracking", - [MSG_READ_WINDOW] = "read windo", - [MSG_READ_ALL] = "read all", - [MSG_EDIT_DONE] = "edit done", - [MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset", - [MSG_END_BEFORE_START] = "error end before start", - [MSG_WINDOW_CLEARED] = "window cleared", - [MSG_WINDOW_SILENCED] = "window silenced", - [MSG_WINDOW_SILENCE_DISABLED] = "window silence disabled", - [MSG_ERROR] = "error", - [MSG_GOTO_CANCELED] = "goto canceled", - [MSG_GOTO] = "go to?", - [MSG_LEAVING_HELP] = "leaving help", - [MSG_IS_UNASSIGNED] = "is unassigned", - [MSG_HELP_INFO] = - "press space to exit, up or down to scroll, or a letter to go to a command", - [MSG_EDGE_TOP] = "top,", - [MSG_EDGE_BOTTOM] = "bottom,", - [MSG_EDGE_LEFT] = "left,", - [MSG_EDGE_RIGHT] = "right,", - [MSG_NUMBER] = "number", - [MSG_SPACE] = "space", - [MSG_START] = "start", - [MSG_END] = "end", - [MSG_CTRL] = "control-", - [MSG_DISJUNCTION] = "or", - -/* Messages with embedded format specifiers. */ - [MSG_POS_INFO] = "line %ld, col %ld, t t y %d", - [MSG_CHAR_INFO] = "hex %02x, decimal %d", - [MSG_REPEAT_DESC] = "times %d .", - [MSG_REPEAT_DESC2] = "repeated %d .", - [MSG_WINDOW_LINE] = "window is line %d", - [MSG_WINDOW_BOUNDARY] = "%s at line %d, column %d", - [MSG_EDIT_PROMPT] = "edit %s, press space when done", - [MSG_NO_COMMAND] = "no commands for %c", - [MSG_KEYDESC] = "is %s", - - /* Control keys. */ - /* Most of these duplicate the entries in state names. */ - [MSG_CTL_SHIFT] = "shift", - [MSG_CTL_ALTGR] = "altgr", - [MSG_CTL_CONTROL] = "control", - [MSG_CTL_ALT] = "alt", - [MSG_CTL_LSHIFT] = "l shift", - [MSG_CTL_SPEAKUP] = "speakup", - [MSG_CTL_LCONTROL] = "l control", - [MSG_CTL_RCONTROL] = "r control", - [MSG_CTL_CAPSSHIFT] = "caps shift", - - /* Color names. */ - [MSG_COLOR_BLACK] = "black", - [MSG_COLOR_BLUE] = "blue", - [MSG_COLOR_GREEN] = "green", - [MSG_COLOR_CYAN] = "cyan", - [MSG_COLOR_RED] = "red", - [MSG_COLOR_MAGENTA] = "magenta", - [MSG_COLOR_YELLOW] = "yellow", - [MSG_COLOR_WHITE] = "white", - [MSG_COLOR_GREY] = "grey", - - /* Names of key states. */ - [MSG_STATE_DOUBLE] = "double", - [MSG_STATE_SPEAKUP] = "speakup", - [MSG_STATE_ALT] = "alt", - [MSG_STATE_CONTROL] = "ctrl", - [MSG_STATE_ALTGR] = "altgr", - [MSG_STATE_SHIFT] = "shift", - - /* Key names. */ - [MSG_KEYNAME_ESC] = "escape", - [MSG_KEYNAME_1] = "1", - [MSG_KEYNAME_2] = "2", - [MSG_KEYNAME_3] = "3", - [MSG_KEYNAME_4] = "4", - [MSG_KEYNAME_5] = "5", - [MSG_KEYNAME_6] = "6", - [MSG_KEYNAME_7] = "7", - [MSG_KEYNAME_8] = "8", - [MSG_KEYNAME_9] = "9", - [MSG_KEYNAME_0] = "0", - [MSG_KEYNAME_DASH] = "minus", - [MSG_KEYNAME_EQUAL] = "equal", - [MSG_KEYNAME_BS] = "back space", - [MSG_KEYNAME_TAB] = "tab", - [MSG_KEYNAME_Q] = "q", - [MSG_KEYNAME_W] = "w", - [MSG_KEYNAME_E] = "e", - [MSG_KEYNAME_R] = "r", - [MSG_KEYNAME_T] = "t", - [MSG_KEYNAME_Y] = "y", - [MSG_KEYNAME_U] = "u", - [MSG_KEYNAME_I] = "i", - [MSG_KEYNAME_O] = "o", - [MSG_KEYNAME_P] = "p", - [MSG_KEYNAME_LEFTBRACE] = "left brace", - [MSG_KEYNAME_RIGHTBRACE] = "right brace", - [MSG_KEYNAME_ENTER] = "enter", - [MSG_KEYNAME_LEFTCTRL] = "left control", - [MSG_KEYNAME_A] = "a", - [MSG_KEYNAME_S] = "s", - [MSG_KEYNAME_D] = "d", - [MSG_KEYNAME_F] = "f", - [MSG_KEYNAME_G] = "g", - [MSG_KEYNAME_H] = "h", - [MSG_KEYNAME_J] = "j", - [MSG_KEYNAME_K] = "k", - [MSG_KEYNAME_L] = "l", - [MSG_KEYNAME_SEMICOLON] = "semicolon", - [MSG_KEYNAME_SINGLEQUOTE] = "apostrophe", - [MSG_KEYNAME_GRAVE] = "accent", - [MSG_KEYNAME_LEFTSHFT] = "left shift", - [MSG_KEYNAME_BACKSLASH] = "back slash", - [MSG_KEYNAME_Z] = "z", - [MSG_KEYNAME_X] = "x", - [MSG_KEYNAME_C] = "c", - [MSG_KEYNAME_V] = "v", - [MSG_KEYNAME_B] = "b", - [MSG_KEYNAME_N] = "n", - [MSG_KEYNAME_M] = "m", - [MSG_KEYNAME_COMMA] = "comma", - [MSG_KEYNAME_DOT] = "dot", - [MSG_KEYNAME_SLASH] = "slash", - [MSG_KEYNAME_RIGHTSHFT] = "right shift", - [MSG_KEYNAME_KPSTAR] = "keypad asterisk", - [MSG_KEYNAME_LEFTALT] = "left alt", - [MSG_KEYNAME_SPACE] = "space", - [MSG_KEYNAME_CAPSLOCK] = "caps lock", - [MSG_KEYNAME_F1] = "f1", - [MSG_KEYNAME_F2] = "f2", - [MSG_KEYNAME_F3] = "f3", - [MSG_KEYNAME_F4] = "f4", - [MSG_KEYNAME_F5] = "f5", - [MSG_KEYNAME_F6] = "f6", - [MSG_KEYNAME_F7] = "f7", - [MSG_KEYNAME_F8] = "f8", - [MSG_KEYNAME_F9] = "f9", - [MSG_KEYNAME_F10] = "f10", - [MSG_KEYNAME_NUMLOCK] = "num lock", - [MSG_KEYNAME_SCROLLLOCK] = "scroll lock", - [MSG_KEYNAME_KP7] = "keypad 7", - [MSG_KEYNAME_KP8] = "keypad 8", - [MSG_KEYNAME_KP9] = "keypad 9", - [MSG_KEYNAME_KPMINUS] = "keypad minus", - [MSG_KEYNAME_KP4] = "keypad 4", - [MSG_KEYNAME_KP5] = "keypad 5", - [MSG_KEYNAME_KP6] = "keypad 6", - [MSG_KEYNAME_KPPLUS] = "keypad plus", - [MSG_KEYNAME_KP1] = "keypad 1", - [MSG_KEYNAME_KP2] = "keypad 2", - [MSG_KEYNAME_KP3] = "keypad 3", - [MSG_KEYNAME_KP0] = "keypad 0", - [MSG_KEYNAME_KPDOT] = "keypad dot", - [MSG_KEYNAME_103RD] = "103rd", - [MSG_KEYNAME_F13] = "f13", - [MSG_KEYNAME_102ND] = "102nd", - [MSG_KEYNAME_F11] = "f11", - [MSG_KEYNAME_F12] = "f12", - [MSG_KEYNAME_F14] = "f14", - [MSG_KEYNAME_F15] = "f15", - [MSG_KEYNAME_F16] = "f16", - [MSG_KEYNAME_F17] = "f17", - [MSG_KEYNAME_F18] = "f18", - [MSG_KEYNAME_F19] = "f19", - [MSG_KEYNAME_F20] = "f20", - [MSG_KEYNAME_KPENTER] = "keypad enter", - [MSG_KEYNAME_RIGHTCTRL] = "right control", - [MSG_KEYNAME_KPSLASH] = "keypad slash", - [MSG_KEYNAME_SYSRQ] = "sysrq", - [MSG_KEYNAME_RIGHTALT] = "right alt", - [MSG_KEYNAME_LF] = "line feed", - [MSG_KEYNAME_HOME] = "home", - [MSG_KEYNAME_UP] = "up", - [MSG_KEYNAME_PGUP] = "page up", - [MSG_KEYNAME_LEFT] = "left", - [MSG_KEYNAME_RIGHT] = "right", - [MSG_KEYNAME_END] = "end", - [MSG_KEYNAME_DOWN] = "down", - [MSG_KEYNAME_PGDN] = "page down", - [MSG_KEYNAME_INS] = "insert", - [MSG_KEYNAME_DEL] = "delete", - [MSG_KEYNAME_MACRO] = "macro", - [MSG_KEYNAME_MUTE] = "mute", - [MSG_KEYNAME_VOLDOWN] = "volume down", - [MSG_KEYNAME_VOLUP] = "volume up", - [MSG_KEYNAME_POWER] = "power", - [MSG_KEYNAME_KPEQUAL] = "keypad equal", - [MSG_KEYNAME_KPPLUSDASH] = "keypad plusminus", - [MSG_KEYNAME_PAUSE] = "pause", - [MSG_KEYNAME_F21] = "f21", - [MSG_KEYNAME_F22] = "f22", - [MSG_KEYNAME_F23] = "f23", - [MSG_KEYNAME_F24] = "f24", - [MSG_KEYNAME_KPCOMMA] = "keypad comma", - [MSG_KEYNAME_LEFTMETA] = "left meta", - [MSG_KEYNAME_RIGHTMETA] = "right meta", - [MSG_KEYNAME_COMPOSE] = "compose", - [MSG_KEYNAME_STOP] = "stop", - [MSG_KEYNAME_AGAIN] = "again", - [MSG_KEYNAME_PROPS] = "props", - [MSG_KEYNAME_UNDO] = "undo", - [MSG_KEYNAME_FRONT] = "front", - [MSG_KEYNAME_COPY] = "copy", - [MSG_KEYNAME_OPEN] = "open", - [MSG_KEYNAME_PASTE] = "paste", - [MSG_KEYNAME_FIND] = "find", - [MSG_KEYNAME_CUT] = "cut", - [MSG_KEYNAME_HELP] = "help", - [MSG_KEYNAME_MENU] = "menu", - [MSG_KEYNAME_CALC] = "calc", - [MSG_KEYNAME_SETUP] = "setup", - [MSG_KEYNAME_SLEEP] = "sleep", - [MSG_KEYNAME_WAKEUP] = "wakeup", - [MSG_KEYNAME_FILE] = "file", - [MSG_KEYNAME_SENDFILE] = "send file", - [MSG_KEYNAME_DELFILE] = "delete file", - [MSG_KEYNAME_XFER] = "transfer", - [MSG_KEYNAME_PROG1] = "prog1", - [MSG_KEYNAME_PROG2] = "prog2", - [MSG_KEYNAME_WWW] = "www", - [MSG_KEYNAME_MSDOS] = "msdos", - [MSG_KEYNAME_COFFEE] = "coffee", - [MSG_KEYNAME_DIRECTION] = "direction", - [MSG_KEYNAME_CYCLEWINDOWS] = "cycle windows", - [MSG_KEYNAME_MAIL] = "mail", - [MSG_KEYNAME_BOOKMARKS] = "bookmarks", - [MSG_KEYNAME_COMPUTER] = "computer", - [MSG_KEYNAME_BACK] = "back", - [MSG_KEYNAME_FORWARD] = "forward", - [MSG_KEYNAME_CLOSECD] = "close cd", - [MSG_KEYNAME_EJECTCD] = "eject cd", - [MSG_KEYNAME_EJECTCLOSE] = "eject close cd", - [MSG_KEYNAME_NEXTSONG] = "next song", - [MSG_KEYNAME_PLAYPAUSE] = "play pause", - [MSG_KEYNAME_PREVSONG] = "previous song", - [MSG_KEYNAME_STOPCD] = "stop cd", - [MSG_KEYNAME_RECORD] = "record", - [MSG_KEYNAME_REWIND] = "rewind", - [MSG_KEYNAME_PHONE] = "phone", - [MSG_KEYNAME_ISO] = "iso", - [MSG_KEYNAME_CONFIG] = "config", - [MSG_KEYNAME_HOMEPG] = "home page", - [MSG_KEYNAME_REFRESH] = "refresh", - [MSG_KEYNAME_EXIT] = "exit", - [MSG_KEYNAME_MOVE] = "move", - [MSG_KEYNAME_EDIT] = "edit", - [MSG_KEYNAME_SCROLLUP] = "scroll up", - [MSG_KEYNAME_SCROLLDN] = "scroll down", - [MSG_KEYNAME_KPLEFTPAR] = "keypad left paren", - [MSG_KEYNAME_KPRIGHTPAR] = "keypad right paren", - - /* Function names. */ - [MSG_FUNCNAME_ATTRIB_BLEEP_DEC] = "attribute bleep decrement", - [MSG_FUNCNAME_ATTRIB_BLEEP_INC] = "attribute bleep increment", - [MSG_FUNCNAME_BLEEPS_DEC] = "bleeps decrement", - [MSG_FUNCNAME_BLEEPS_INC] = "bleeps increment", - [MSG_FUNCNAME_CHAR_FIRST] = "character, first", - [MSG_FUNCNAME_CHAR_LAST] = "character, last", - [MSG_FUNCNAME_CHAR_CURRENT] = "character, say current", - [MSG_FUNCNAME_CHAR_HEX_AND_DEC] = "character, say hex and decimal", - [MSG_FUNCNAME_CHAR_NEXT] = "character, say next", - [MSG_FUNCNAME_CHAR_PHONETIC] = "character, say phonetic", - [MSG_FUNCNAME_CHAR_PREVIOUS] = "character, say previous", - [MSG_FUNCNAME_CURSOR_PARK] = "cursor park", - [MSG_FUNCNAME_CUT] = "cut", - [MSG_FUNCNAME_EDIT_DELIM] = "edit delimiters", - [MSG_FUNCNAME_EDIT_EXNUM] = "edit exnum", - [MSG_FUNCNAME_EDIT_MOST] = "edit most", - [MSG_FUNCNAME_EDIT_REPEATS] = "edit repeats", - [MSG_FUNCNAME_EDIT_SOME] = "edit some", - [MSG_FUNCNAME_GOTO] = "go to", - [MSG_FUNCNAME_GOTO_BOTTOM] = "go to bottom edge", - [MSG_FUNCNAME_GOTO_LEFT] = "go to left edge", - [MSG_FUNCNAME_GOTO_RIGHT] = "go to right edge", - [MSG_FUNCNAME_GOTO_TOP] = "go to top edge", - [MSG_FUNCNAME_HELP] = "help", - [MSG_FUNCNAME_LINE_SAY_CURRENT] = "line, say current", - [MSG_FUNCNAME_LINE_SAY_NEXT] = "line, say next", - [MSG_FUNCNAME_LINE_SAY_PREVIOUS] = "line, say previous", - [MSG_FUNCNAME_LINE_SAY_WITH_INDENT] = "line, say with indent", - [MSG_FUNCNAME_PASTE] = "paste", - [MSG_FUNCNAME_PITCH_DEC] = "pitch decrement", - [MSG_FUNCNAME_PITCH_INC] = "pitch increment", - [MSG_FUNCNAME_PUNC_DEC] = "punctuation decrement", - [MSG_FUNCNAME_PUNC_INC] = "punctuation increment", - [MSG_FUNCNAME_PUNC_LEVEL_DEC] = "punc level decrement", - [MSG_FUNCNAME_PUNC_LEVEL_INC] = "punc level increment", - [MSG_FUNCNAME_QUIET] = "quiet", - [MSG_FUNCNAME_RATE_DEC] = "rate decrement", - [MSG_FUNCNAME_RATE_INC] = "rate increment", - [MSG_FUNCNAME_READING_PUNC_DEC] = "reading punctuation decrement", - [MSG_FUNCNAME_READING_PUNC_INC] = "reading punctuation increment", - [MSG_FUNCNAME_SAY_ATTRIBUTES] = "say attributes", - [MSG_FUNCNAME_SAY_FROM_LEFT] = "say from left", - [MSG_FUNCNAME_SAY_FROM_TOP] = "say from top", - [MSG_FUNCNAME_SAY_POSITION] = "say position", - [MSG_FUNCNAME_SAY_SCREEN] = "say screen", - [MSG_FUNCNAME_SAY_TO_BOTTOM] = "say to bottom", - [MSG_FUNCNAME_SAY_TO_RIGHT] = "say to right", - [MSG_FUNCNAME_SPEAKUP] = "speakup", - [MSG_FUNCNAME_SPEAKUP_LOCK] = "speakup lock", - [MSG_FUNCNAME_SPEAKUP_OFF] = "speakup off", - [MSG_FUNCNAME_SPEECH_KILL] = "speech kill", - [MSG_FUNCNAME_SPELL_DELAY_DEC] = "spell delay decrement", - [MSG_FUNCNAME_SPELL_DELAY_INC] = "spell delay increment", - [MSG_FUNCNAME_SPELL_WORD] = "spell word", - [MSG_FUNCNAME_SPELL_WORD_PHONETICALLY] = "spell word phonetically", - [MSG_FUNCNAME_TONE_DEC] = "tone decrement", - [MSG_FUNCNAME_TONE_INC] = "tone increment", - [MSG_FUNCNAME_VOICE_DEC] = "voice decrement", - [MSG_FUNCNAME_VOICE_INC] = "voice increment", - [MSG_FUNCNAME_VOLUME_DEC] = "volume decrement", - [MSG_FUNCNAME_VOLUME_INC] = "volume increment", - [MSG_FUNCNAME_WINDOW_CLEAR] = "window, clear", - [MSG_FUNCNAME_WINDOW_SAY] = "window, say", - [MSG_FUNCNAME_WINDOW_SET] = "window, set", - [MSG_FUNCNAME_WINDOW_SILENCE] = "window, silence", - [MSG_FUNCNAME_WORD_SAY_CURRENT] = "word, say current", - [MSG_FUNCNAME_WORD_SAY_NEXT] = "word, say next", - [MSG_FUNCNAME_WORD_SAY_PREVIOUS] = "word, say previous", -}; - -static struct msg_group_t all_groups[] = { - { - .name = "ctl_keys", - .start = MSG_CTL_START, - .end = MSG_CTL_END, - }, - { - .name = "colors", - .start = MSG_COLORS_START, - .end = MSG_COLORS_END, - }, - { - .name = "formatted", - .start = MSG_FORMATTED_START, - .end = MSG_FORMATTED_END, - }, - { - .name = "function_names", - .start = MSG_FUNCNAMES_START, - .end = MSG_FUNCNAMES_END, - }, - { - .name = "key_names", - .start = MSG_KEYNAMES_START, - .end = MSG_KEYNAMES_END, - }, - { - .name = "announcements", - .start = MSG_ANNOUNCEMENTS_START, - .end = MSG_ANNOUNCEMENTS_END, - }, - { - .name = "states", - .start = MSG_STATES_START, - .end = MSG_STATES_END, - }, -}; - -static const int num_groups = ARRAY_SIZE(all_groups); - -char *spk_msg_get(enum msg_index_t index) -{ - return speakup_msgs[index]; -} - -/* - * Function: next_specifier - * Finds the start of the next format specifier in the argument string. - * Return value: pointer to start of format - * specifier, or NULL if no specifier exists. - */ -static char *next_specifier(char *input) -{ - int found = 0; - char *next_percent = input; - - while (next_percent && !found) { - next_percent = strchr(next_percent, '%'); - if (next_percent) { - /* skip over doubled percent signs */ - while (next_percent[0] == '%' && - next_percent[1] == '%') - next_percent += 2; - if (*next_percent == '%') - found = 1; - else if (*next_percent == '\0') - next_percent = NULL; - } - } - - return next_percent; -} - -/* Skip over 0 or more flags. */ -static char *skip_flags(char *input) -{ - while ((*input != '\0') && strchr(" 0+-#", *input)) - input++; - return input; -} - -/* Skip over width.precision, if it exists. */ -static char *skip_width(char *input) -{ - while (isdigit(*input)) - input++; - if (*input == '.') { - input++; - while (isdigit(*input)) - input++; - } - return input; -} - -/* - * Skip past the end of the conversion part. - * Note that this code only accepts a handful of conversion specifiers: - * c d s x and ld. Not accidental; these are exactly the ones used in - * the default group of formatted messages. - */ -static char *skip_conversion(char *input) -{ - if ((input[0] == 'l') && (input[1] == 'd')) - input += 2; - else if ((*input != '\0') && strchr("cdsx", *input)) - input++; - return input; -} - -/* - * Function: find_specifier_end - * Return a pointer to the end of the format specifier. - */ -static char *find_specifier_end(char *input) -{ - input++; /* Advance over %. */ - input = skip_flags(input); - input = skip_width(input); - input = skip_conversion(input); - return input; -} - -/* - * Function: compare_specifiers - * Compare the format specifiers pointed to by *input1 and *input2. - * Return true if they are the same, false otherwise. - * Advance *input1 and *input2 so that they point to the character following - * the end of the specifier. - */ -static bool compare_specifiers(char **input1, char **input2) -{ - bool same = false; - char *end1 = find_specifier_end(*input1); - char *end2 = find_specifier_end(*input2); - size_t length1 = end1 - *input1; - size_t length2 = end2 - *input2; - - if ((length1 == length2) && !memcmp(*input1, *input2, length1)) - same = true; - - *input1 = end1; - *input2 = end2; - return same; -} - -/* - * Function: fmt_validate - * Check that two format strings contain the same number of format specifiers, - * and that the order of specifiers is the same in both strings. - * Return true if the condition holds, false if it doesn't. - */ -static bool fmt_validate(char *template, char *user) -{ - bool valid = true; - bool still_comparing = true; - char *template_ptr = template; - char *user_ptr = user; - - while (still_comparing && valid) { - template_ptr = next_specifier(template_ptr); - user_ptr = next_specifier(user_ptr); - if (template_ptr && user_ptr) { - /* Both have at least one more specifier. */ - valid = compare_specifiers(&template_ptr, &user_ptr); - } else { - /* No more format specifiers in one or both strings. */ - still_comparing = false; - /* See if one has more specifiers than the other. */ - if (template_ptr || user_ptr) - valid = false; - } - } - return valid; -} - -/* - * Function: msg_set - * Description: Add a user-supplied message to the user_messages array. - * The message text is copied to a memory area allocated with kmalloc. - * If the function fails, then user_messages is untouched. - * Arguments: - * - index: a message number, as found in i18n.h. - * - text: text of message. Not NUL-terminated. - * - length: number of bytes in text. - * Failure conditions: - * -EINVAL - Invalid format specifiers in formatted message or illegal index. - * -ENOMEM - Unable to allocate memory. - */ -ssize_t spk_msg_set(enum msg_index_t index, char *text, size_t length) -{ - char *newstr = NULL; - unsigned long flags; - - if ((index < MSG_FIRST_INDEX) || (index >= MSG_LAST_INDEX)) - return -EINVAL; - - newstr = kmalloc(length + 1, GFP_KERNEL); - if (!newstr) - return -ENOMEM; - - memcpy(newstr, text, length); - newstr[length] = '\0'; - if (index >= MSG_FORMATTED_START && - index <= MSG_FORMATTED_END && - !fmt_validate(speakup_default_msgs[index], newstr)) { - kfree(newstr); - return -EINVAL; - } - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (speakup_msgs[index] != speakup_default_msgs[index]) - kfree(speakup_msgs[index]); - speakup_msgs[index] = newstr; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return 0; -} - -/* - * Find a message group, given its name. Return a pointer to the structure - * if found, or NULL otherwise. - */ -struct msg_group_t *spk_find_msg_group(const char *group_name) -{ - struct msg_group_t *group = NULL; - int i; - - for (i = 0; i < num_groups; i++) { - if (!strcmp(all_groups[i].name, group_name)) { - group = &all_groups[i]; - break; - } - } - return group; -} - -void spk_reset_msg_group(struct msg_group_t *group) -{ - unsigned long flags; - enum msg_index_t i; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - - for (i = group->start; i <= group->end; i++) { - if (speakup_msgs[i] != speakup_default_msgs[i]) - kfree(speakup_msgs[i]); - speakup_msgs[i] = speakup_default_msgs[i]; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -/* Called at initialization time, to establish default messages. */ -void spk_initialize_msgs(void) -{ - memcpy(speakup_msgs, speakup_default_msgs, - sizeof(speakup_default_msgs)); -} - -/* Free user-supplied strings when module is unloaded: */ -void spk_free_user_msgs(void) -{ - enum msg_index_t index; - unsigned long flags; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - for (index = MSG_FIRST_INDEX; index < MSG_LAST_INDEX; index++) { - if (speakup_msgs[index] != speakup_default_msgs[index]) { - kfree(speakup_msgs[index]); - speakup_msgs[index] = speakup_default_msgs[index]; - } - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} diff --git a/drivers/staging/speakup/i18n.h b/drivers/staging/speakup/i18n.h deleted file mode 100644 index 2ec6e659d02b..000000000000 --- a/drivers/staging/speakup/i18n.h +++ /dev/null @@ -1,235 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef I18N_H -#define I18N_H -/* Internationalization declarations */ - -enum msg_index_t { - MSG_FIRST_INDEX, - MSG_ANNOUNCEMENTS_START = MSG_FIRST_INDEX, - MSG_BLANK = MSG_ANNOUNCEMENTS_START, - MSG_IAM_ALIVE, - MSG_YOU_KILLED_SPEAKUP, - MSG_HEY_THATS_BETTER, - MSG_YOU_TURNED_ME_OFF, - MSG_PARKED, - MSG_UNPARKED, - MSG_MARK, - MSG_CUT, - MSG_MARK_CLEARED, - MSG_PASTE, - MSG_BRIGHT, - MSG_ON_BLINKING, - MSG_STATUS_START, - MSG_OFF = MSG_STATUS_START, - MSG_ON, - MSG_NO_WINDOW, - MSG_CURSOR_MSGS_START, - MSG_CURSORING_OFF = MSG_CURSOR_MSGS_START, - MSG_CURSORING_ON, - MSG_HIGHLIGHT_TRACKING, - MSG_READ_WINDOW, - MSG_READ_ALL, - MSG_EDIT_DONE, - MSG_WINDOW_ALREADY_SET, - MSG_END_BEFORE_START, - MSG_WINDOW_CLEARED, - MSG_WINDOW_SILENCED, - MSG_WINDOW_SILENCE_DISABLED, - MSG_ERROR, - MSG_GOTO_CANCELED, - MSG_GOTO, - MSG_LEAVING_HELP, - MSG_IS_UNASSIGNED, - MSG_HELP_INFO, - MSG_EDGE_MSGS_START, - MSG_EDGE_TOP = MSG_EDGE_MSGS_START, - MSG_EDGE_BOTTOM, - MSG_EDGE_LEFT, - MSG_EDGE_RIGHT, - MSG_NUMBER, - MSG_SPACE, - MSG_START, /* A little confusing, given our convention. */ - MSG_END, /* A little confusing, given our convention. */ - MSG_CTRL, - -/* A message containing the single word "or". */ - MSG_DISJUNCTION, - MSG_ANNOUNCEMENTS_END = MSG_DISJUNCTION, - -/* Messages with format specifiers. */ - MSG_FORMATTED_START, - MSG_POS_INFO = MSG_FORMATTED_START, - MSG_CHAR_INFO, - MSG_REPEAT_DESC, - MSG_REPEAT_DESC2, - MSG_WINDOW_LINE, - MSG_WINDOW_BOUNDARY, - MSG_EDIT_PROMPT, - MSG_NO_COMMAND, - MSG_KEYDESC, - MSG_FORMATTED_END = MSG_KEYDESC, - - /* Control keys. */ - MSG_CTL_START, - MSG_CTL_SHIFT = MSG_CTL_START, - MSG_CTL_ALTGR, - MSG_CTL_CONTROL, - MSG_CTL_ALT, - MSG_CTL_LSHIFT, - MSG_CTL_SPEAKUP, - MSG_CTL_LCONTROL, - MSG_CTL_RCONTROL, - MSG_CTL_CAPSSHIFT, - MSG_CTL_END = MSG_CTL_CAPSSHIFT, - - /* Colors. */ - MSG_COLORS_START, - MSG_COLOR_BLACK = MSG_COLORS_START, - MSG_COLOR_BLUE, - MSG_COLOR_GREEN, - MSG_COLOR_CYAN, - MSG_COLOR_RED, - MSG_COLOR_MAGENTA, - MSG_COLOR_YELLOW, - MSG_COLOR_WHITE, - MSG_COLOR_GREY, - MSG_COLORS_END = MSG_COLOR_GREY, - - MSG_STATES_START, - MSG_STATE_DOUBLE = MSG_STATES_START, - MSG_STATE_SPEAKUP, - MSG_STATE_ALT, - MSG_STATE_CONTROL, - MSG_STATE_ALTGR, - MSG_STATE_SHIFT, - MSG_STATES_END = MSG_STATE_SHIFT, - - MSG_KEYNAMES_START, - MSG_KEYNAME_ESC = MSG_KEYNAMES_START, - MSG_KEYNAME_1, MSG_KEYNAME_2, MSG_KEYNAME_3, MSG_KEYNAME_4, - MSG_KEYNAME_5, MSG_KEYNAME_6, MSG_KEYNAME_7, MSG_KEYNAME_8, - MSG_KEYNAME_9, - MSG_KEYNAME_0, MSG_KEYNAME_DASH, MSG_KEYNAME_EQUAL, MSG_KEYNAME_BS, - MSG_KEYNAME_TAB, - MSG_KEYNAME_Q, MSG_KEYNAME_W, MSG_KEYNAME_E, MSG_KEYNAME_R, - MSG_KEYNAME_T, MSG_KEYNAME_Y, MSG_KEYNAME_U, MSG_KEYNAME_I, - MSG_KEYNAME_O, MSG_KEYNAME_P, - MSG_KEYNAME_LEFTBRACE, MSG_KEYNAME_RIGHTBRACE, MSG_KEYNAME_ENTER, - MSG_KEYNAME_LEFTCTRL, MSG_KEYNAME_A, - MSG_KEYNAME_S, MSG_KEYNAME_D, MSG_KEYNAME_F, MSG_KEYNAME_G, - MSG_KEYNAME_H, MSG_KEYNAME_J, MSG_KEYNAME_K, MSG_KEYNAME_L, - MSG_KEYNAME_SEMICOLON, - MSG_KEYNAME_SINGLEQUOTE, MSG_KEYNAME_GRAVE, - MSG_KEYNAME_LEFTSHFT, MSG_KEYNAME_BACKSLASH, MSG_KEYNAME_Z, - MSG_KEYNAME_X, MSG_KEYNAME_C, MSG_KEYNAME_V, MSG_KEYNAME_B, - MSG_KEYNAME_N, MSG_KEYNAME_M, MSG_KEYNAME_COMMA, MSG_KEYNAME_DOT, - MSG_KEYNAME_SLASH, MSG_KEYNAME_RIGHTSHFT, - MSG_KEYNAME_KPSTAR, - MSG_KEYNAME_LEFTALT, MSG_KEYNAME_SPACE, MSG_KEYNAME_CAPSLOCK, - MSG_KEYNAME_F1, MSG_KEYNAME_F2, - MSG_KEYNAME_F3, MSG_KEYNAME_F4, MSG_KEYNAME_F5, MSG_KEYNAME_F6, - MSG_KEYNAME_F7, - MSG_KEYNAME_F8, MSG_KEYNAME_F9, MSG_KEYNAME_F10, MSG_KEYNAME_NUMLOCK, - MSG_KEYNAME_SCROLLLOCK, - MSG_KEYNAME_KP7, MSG_KEYNAME_KP8, MSG_KEYNAME_KP9, MSG_KEYNAME_KPMINUS, - MSG_KEYNAME_KP4, - MSG_KEYNAME_KP5, MSG_KEYNAME_KP6, MSG_KEYNAME_KPPLUS, MSG_KEYNAME_KP1, - MSG_KEYNAME_KP2, - MSG_KEYNAME_KP3, MSG_KEYNAME_KP0, MSG_KEYNAME_KPDOT, MSG_KEYNAME_103RD, - MSG_KEYNAME_F13, - MSG_KEYNAME_102ND, MSG_KEYNAME_F11, MSG_KEYNAME_F12, MSG_KEYNAME_F14, - MSG_KEYNAME_F15, - MSG_KEYNAME_F16, MSG_KEYNAME_F17, MSG_KEYNAME_F18, MSG_KEYNAME_F19, - MSG_KEYNAME_F20, - MSG_KEYNAME_KPENTER, MSG_KEYNAME_RIGHTCTRL, MSG_KEYNAME_KPSLASH, - MSG_KEYNAME_SYSRQ, MSG_KEYNAME_RIGHTALT, - MSG_KEYNAME_LF, MSG_KEYNAME_HOME, MSG_KEYNAME_UP, MSG_KEYNAME_PGUP, - MSG_KEYNAME_LEFT, - MSG_KEYNAME_RIGHT, MSG_KEYNAME_END, MSG_KEYNAME_DOWN, MSG_KEYNAME_PGDN, - MSG_KEYNAME_INS, - MSG_KEYNAME_DEL, MSG_KEYNAME_MACRO, MSG_KEYNAME_MUTE, - MSG_KEYNAME_VOLDOWN, MSG_KEYNAME_VOLUP, - MSG_KEYNAME_POWER, MSG_KEYNAME_KPEQUAL, MSG_KEYNAME_KPPLUSDASH, - MSG_KEYNAME_PAUSE, MSG_KEYNAME_F21, MSG_KEYNAME_F22, MSG_KEYNAME_F23, - MSG_KEYNAME_F24, MSG_KEYNAME_KPCOMMA, MSG_KEYNAME_LEFTMETA, - MSG_KEYNAME_RIGHTMETA, MSG_KEYNAME_COMPOSE, MSG_KEYNAME_STOP, - MSG_KEYNAME_AGAIN, MSG_KEYNAME_PROPS, - MSG_KEYNAME_UNDO, MSG_KEYNAME_FRONT, MSG_KEYNAME_COPY, MSG_KEYNAME_OPEN, - MSG_KEYNAME_PASTE, - MSG_KEYNAME_FIND, MSG_KEYNAME_CUT, MSG_KEYNAME_HELP, MSG_KEYNAME_MENU, - MSG_KEYNAME_CALC, - MSG_KEYNAME_SETUP, MSG_KEYNAME_SLEEP, MSG_KEYNAME_WAKEUP, - MSG_KEYNAME_FILE, MSG_KEYNAME_SENDFILE, - MSG_KEYNAME_DELFILE, MSG_KEYNAME_XFER, MSG_KEYNAME_PROG1, - MSG_KEYNAME_PROG2, MSG_KEYNAME_WWW, - MSG_KEYNAME_MSDOS, MSG_KEYNAME_COFFEE, MSG_KEYNAME_DIRECTION, - MSG_KEYNAME_CYCLEWINDOWS, MSG_KEYNAME_MAIL, - MSG_KEYNAME_BOOKMARKS, MSG_KEYNAME_COMPUTER, MSG_KEYNAME_BACK, - MSG_KEYNAME_FORWARD, MSG_KEYNAME_CLOSECD, - MSG_KEYNAME_EJECTCD, MSG_KEYNAME_EJECTCLOSE, MSG_KEYNAME_NEXTSONG, - MSG_KEYNAME_PLAYPAUSE, MSG_KEYNAME_PREVSONG, - MSG_KEYNAME_STOPCD, MSG_KEYNAME_RECORD, MSG_KEYNAME_REWIND, - MSG_KEYNAME_PHONE, MSG_KEYNAME_ISO, - MSG_KEYNAME_CONFIG, MSG_KEYNAME_HOMEPG, MSG_KEYNAME_REFRESH, - MSG_KEYNAME_EXIT, MSG_KEYNAME_MOVE, - MSG_KEYNAME_EDIT, MSG_KEYNAME_SCROLLUP, MSG_KEYNAME_SCROLLDN, - MSG_KEYNAME_KPLEFTPAR, MSG_KEYNAME_KPRIGHTPAR, - MSG_KEYNAMES_END = MSG_KEYNAME_KPRIGHTPAR, - - MSG_FUNCNAMES_START, - MSG_FUNCNAME_ATTRIB_BLEEP_DEC = MSG_FUNCNAMES_START, - MSG_FUNCNAME_ATTRIB_BLEEP_INC, - MSG_FUNCNAME_BLEEPS_DEC, MSG_FUNCNAME_BLEEPS_INC, - MSG_FUNCNAME_CHAR_FIRST, MSG_FUNCNAME_CHAR_LAST, - MSG_FUNCNAME_CHAR_CURRENT, MSG_FUNCNAME_CHAR_HEX_AND_DEC, - MSG_FUNCNAME_CHAR_NEXT, - MSG_FUNCNAME_CHAR_PHONETIC, MSG_FUNCNAME_CHAR_PREVIOUS, - MSG_FUNCNAME_CURSOR_PARK, MSG_FUNCNAME_CUT, - MSG_FUNCNAME_EDIT_DELIM, MSG_FUNCNAME_EDIT_EXNUM, - MSG_FUNCNAME_EDIT_MOST, MSG_FUNCNAME_EDIT_REPEATS, - MSG_FUNCNAME_EDIT_SOME, - MSG_FUNCNAME_GOTO, MSG_FUNCNAME_GOTO_BOTTOM, MSG_FUNCNAME_GOTO_LEFT, - MSG_FUNCNAME_GOTO_RIGHT, MSG_FUNCNAME_GOTO_TOP, MSG_FUNCNAME_HELP, - MSG_FUNCNAME_LINE_SAY_CURRENT, MSG_FUNCNAME_LINE_SAY_NEXT, - MSG_FUNCNAME_LINE_SAY_PREVIOUS, MSG_FUNCNAME_LINE_SAY_WITH_INDENT, - MSG_FUNCNAME_PASTE, MSG_FUNCNAME_PITCH_DEC, MSG_FUNCNAME_PITCH_INC, - MSG_FUNCNAME_PUNC_DEC, MSG_FUNCNAME_PUNC_INC, - MSG_FUNCNAME_PUNC_LEVEL_DEC, MSG_FUNCNAME_PUNC_LEVEL_INC, - MSG_FUNCNAME_QUIET, - MSG_FUNCNAME_RATE_DEC, MSG_FUNCNAME_RATE_INC, - MSG_FUNCNAME_READING_PUNC_DEC, MSG_FUNCNAME_READING_PUNC_INC, - MSG_FUNCNAME_SAY_ATTRIBUTES, - MSG_FUNCNAME_SAY_FROM_LEFT, MSG_FUNCNAME_SAY_FROM_TOP, - MSG_FUNCNAME_SAY_POSITION, MSG_FUNCNAME_SAY_SCREEN, - MSG_FUNCNAME_SAY_TO_BOTTOM, MSG_FUNCNAME_SAY_TO_RIGHT, - MSG_FUNCNAME_SPEAKUP, MSG_FUNCNAME_SPEAKUP_LOCK, - MSG_FUNCNAME_SPEAKUP_OFF, MSG_FUNCNAME_SPEECH_KILL, - MSG_FUNCNAME_SPELL_DELAY_DEC, MSG_FUNCNAME_SPELL_DELAY_INC, - MSG_FUNCNAME_SPELL_WORD, MSG_FUNCNAME_SPELL_WORD_PHONETICALLY, - MSG_FUNCNAME_TONE_DEC, MSG_FUNCNAME_TONE_INC, - MSG_FUNCNAME_VOICE_DEC, MSG_FUNCNAME_VOICE_INC, - MSG_FUNCNAME_VOLUME_DEC, MSG_FUNCNAME_VOLUME_INC, - MSG_FUNCNAME_WINDOW_CLEAR, MSG_FUNCNAME_WINDOW_SAY, - MSG_FUNCNAME_WINDOW_SET, MSG_FUNCNAME_WINDOW_SILENCE, - MSG_FUNCNAME_WORD_SAY_CURRENT, MSG_FUNCNAME_WORD_SAY_NEXT, - MSG_FUNCNAME_WORD_SAY_PREVIOUS, - MSG_FUNCNAMES_END = MSG_FUNCNAME_WORD_SAY_PREVIOUS, - - /* all valid indices must be above this */ - MSG_LAST_INDEX -}; - -struct msg_group_t { - char *name; - enum msg_index_t start; - enum msg_index_t end; -}; - -char *spk_msg_get(enum msg_index_t index); -ssize_t spk_msg_set(enum msg_index_t index, char *text, size_t length); -struct msg_group_t *spk_find_msg_group(const char *group_name); -void spk_reset_msg_group(struct msg_group_t *group); -void spk_initialize_msgs(void); -void spk_free_user_msgs(void); - -#endif diff --git a/drivers/staging/speakup/keyhelp.c b/drivers/staging/speakup/keyhelp.c deleted file mode 100644 index 822ceac83068..000000000000 --- a/drivers/staging/speakup/keyhelp.c +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* speakup_keyhelp.c - * help module for speakup - * - *written by David Borowski. - * - * Copyright (C) 2003 David Borowski. - */ - -#include -#include "spk_priv.h" -#include "speakup.h" - -#define MAXFUNCS 130 -#define MAXKEYS 256 -static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1; -static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS]; -static u_short masks[] = { 32, 16, 8, 4, 2, 1 }; - -static short letter_offsets[26] = { - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1 }; - -static u_char funcvals[] = { - ATTRIB_BLEEP_DEC, ATTRIB_BLEEP_INC, BLEEPS_DEC, BLEEPS_INC, - SAY_FIRST_CHAR, SAY_LAST_CHAR, SAY_CHAR, SAY_CHAR_NUM, - SAY_NEXT_CHAR, SAY_PHONETIC_CHAR, SAY_PREV_CHAR, SPEAKUP_PARKED, - SPEAKUP_CUT, EDIT_DELIM, EDIT_EXNUM, EDIT_MOST, - EDIT_REPEAT, EDIT_SOME, SPEAKUP_GOTO, BOTTOM_EDGE, - LEFT_EDGE, RIGHT_EDGE, TOP_EDGE, SPEAKUP_HELP, - SAY_LINE, SAY_NEXT_LINE, SAY_PREV_LINE, SAY_LINE_INDENT, - SPEAKUP_PASTE, PITCH_DEC, PITCH_INC, PUNCT_DEC, - PUNCT_INC, PUNC_LEVEL_DEC, PUNC_LEVEL_INC, SPEAKUP_QUIET, - RATE_DEC, RATE_INC, READING_PUNC_DEC, READING_PUNC_INC, - SAY_ATTRIBUTES, SAY_FROM_LEFT, SAY_FROM_TOP, SAY_POSITION, - SAY_SCREEN, SAY_TO_BOTTOM, SAY_TO_RIGHT, SPK_KEY, - SPK_LOCK, SPEAKUP_OFF, SPEECH_KILL, SPELL_DELAY_DEC, - SPELL_DELAY_INC, SPELL_WORD, SPELL_PHONETIC, TONE_DEC, - TONE_INC, VOICE_DEC, VOICE_INC, VOL_DEC, - VOL_INC, CLEAR_WIN, SAY_WIN, SET_WIN, - ENABLE_WIN, SAY_WORD, SAY_NEXT_WORD, SAY_PREV_WORD, 0 -}; - -static u_char *state_tbl; -static int cur_item, nstates; - -static void build_key_data(void) -{ - u_char *kp, counters[MAXFUNCS], ch, ch1; - u_short *p_key, key; - int i, offset = 1; - - nstates = (int)(state_tbl[-1]); - memset(counters, 0, sizeof(counters)); - memset(key_offsets, 0, sizeof(key_offsets)); - kp = state_tbl + nstates + 1; - while (*kp++) { - /* count occurrences of each function */ - for (i = 0; i < nstates; i++, kp++) { - if (!*kp) - continue; - if ((state_tbl[i] & 16) != 0 && *kp == SPK_KEY) - continue; - counters[*kp]++; - } - } - for (i = 0; i < MAXFUNCS; i++) { - if (counters[i] == 0) - continue; - key_offsets[i] = offset; - offset += (counters[i] + 1); - if (offset >= MAXKEYS) - break; - } -/* leave counters set so high keycodes come first. - * this is done so num pad and other extended keys maps are spoken before - * the alpha with speakup type mapping. - */ - kp = state_tbl + nstates + 1; - while ((ch = *kp++)) { - for (i = 0; i < nstates; i++) { - ch1 = *kp++; - if (!ch1) - continue; - if ((state_tbl[i] & 16) != 0 && ch1 == SPK_KEY) - continue; - key = (state_tbl[i] << 8) + ch; - counters[ch1]--; - offset = key_offsets[ch1]; - if (!offset) - continue; - p_key = key_data + offset + counters[ch1]; - *p_key = key; - } - } -} - -static void say_key(int key) -{ - int i, state = key >> 8; - - key &= 0xff; - for (i = 0; i < 6; i++) { - if (state & masks[i]) - synth_printf(" %s", spk_msg_get(MSG_STATES_START + i)); - } - if ((key > 0) && (key <= num_key_names)) - synth_printf(" %s\n", - spk_msg_get(MSG_KEYNAMES_START + (key - 1))); -} - -static int help_init(void) -{ - char start = SPACE; - int i; - int num_funcs = MSG_FUNCNAMES_END - MSG_FUNCNAMES_START + 1; - - state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2; - for (i = 0; i < num_funcs; i++) { - char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i); - - if (start == *cur_funcname) - continue; - start = *cur_funcname; - letter_offsets[(start & 31) - 1] = i; - } - return 0; -} - -int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key) -{ - int i, n; - char *name; - u_char func, *kp; - u_short *p_keys, val; - - if (letter_offsets[0] == -1) - help_init(); - if (type == KT_LATIN) { - if (ch == SPACE) { - spk_special_handler = NULL; - synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP)); - return 1; - } - ch |= 32; /* lower case */ - if (ch < 'a' || ch > 'z') - return -1; - if (letter_offsets[ch - 'a'] == -1) { - synth_printf(spk_msg_get(MSG_NO_COMMAND), ch); - synth_printf("\n"); - return 1; - } - cur_item = letter_offsets[ch - 'a']; - } else if (type == KT_CUR) { - if (ch == 0 && - (MSG_FUNCNAMES_START + cur_item + 1) <= MSG_FUNCNAMES_END) - cur_item++; - else if (ch == 3 && cur_item > 0) - cur_item--; - else - return -1; - } else if (type == KT_SPKUP && ch == SPEAKUP_HELP && - !spk_special_handler) { - spk_special_handler = spk_handle_help; - synth_printf("%s\n", spk_msg_get(MSG_HELP_INFO)); - build_key_data(); /* rebuild each time in case new mapping */ - return 1; - } else { - name = NULL; - if ((type != KT_SPKUP) && (key > 0) && (key <= num_key_names)) { - synth_printf("%s\n", - spk_msg_get(MSG_KEYNAMES_START + key - 1)); - return 1; - } - for (i = 0; funcvals[i] != 0 && !name; i++) { - if (ch == funcvals[i]) - name = spk_msg_get(MSG_FUNCNAMES_START + i); - } - if (!name) - return -1; - kp = spk_our_keys[key] + 1; - for (i = 0; i < nstates; i++) { - if (ch == kp[i]) - break; - } - key += (state_tbl[i] << 8); - say_key(key); - synth_printf(spk_msg_get(MSG_KEYDESC), name); - synth_printf("\n"); - return 1; - } - name = spk_msg_get(MSG_FUNCNAMES_START + cur_item); - func = funcvals[cur_item]; - synth_printf("%s", name); - if (key_offsets[func] == 0) { - synth_printf(" %s\n", spk_msg_get(MSG_IS_UNASSIGNED)); - return 1; - } - p_keys = key_data + key_offsets[func]; - for (n = 0; p_keys[n]; n++) { - val = p_keys[n]; - if (n > 0) - synth_printf("%s ", spk_msg_get(MSG_DISJUNCTION)); - say_key(val); - } - return 1; -} diff --git a/drivers/staging/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c deleted file mode 100644 index 41ae24ab5d08..000000000000 --- a/drivers/staging/speakup/kobjects.c +++ /dev/null @@ -1,1056 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Speakup kobject implementation - * - * Copyright (C) 2009 William Hubbs - * - * This code is based on kobject-example.c, which came with linux 2.6.x. - * - * Copyright (C) 2004-2007 Greg Kroah-Hartman - * Copyright (C) 2007 Novell Inc. - * - * Released under the GPL version 2 only. - * - */ -#include /* For kmalloc. */ -#include -#include -#include -#include -#include -#include - -#include "speakup.h" -#include "spk_priv.h" - -/* - * This is called when a user reads the characters or chartab sys file. - */ -static ssize_t chars_chartab_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - int i; - int len = 0; - char *cp; - char *buf_pointer = buf; - size_t bufsize = PAGE_SIZE; - unsigned long flags; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - *buf_pointer = '\0'; - for (i = 0; i < 256; i++) { - if (bufsize <= 1) - break; - if (strcmp("characters", attr->attr.name) == 0) { - len = scnprintf(buf_pointer, bufsize, "%d\t%s\n", - i, spk_characters[i]); - } else { /* show chartab entry */ - if (IS_TYPE(i, B_CTL)) - cp = "B_CTL"; - else if (IS_TYPE(i, WDLM)) - cp = "WDLM"; - else if (IS_TYPE(i, A_PUNC)) - cp = "A_PUNC"; - else if (IS_TYPE(i, PUNC)) - cp = "PUNC"; - else if (IS_TYPE(i, NUM)) - cp = "NUM"; - else if (IS_TYPE(i, A_CAP)) - cp = "A_CAP"; - else if (IS_TYPE(i, ALPHA)) - cp = "ALPHA"; - else if (IS_TYPE(i, B_CAPSYM)) - cp = "B_CAPSYM"; - else if (IS_TYPE(i, B_SYM)) - cp = "B_SYM"; - else - cp = "0"; - len = - scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp); - } - bufsize -= len; - buf_pointer += len; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return buf_pointer - buf; -} - -/* - * Print informational messages or warnings after updating - * character descriptions or chartab entries. - */ -static void report_char_chartab_status(int reset, int received, int used, - int rejected, int do_characters) -{ - static char const *object_type[] = { - "character class entries", - "character descriptions", - }; - int len; - char buf[80]; - - if (reset) { - pr_info("%s reset to defaults\n", object_type[do_characters]); - } else if (received) { - len = snprintf(buf, sizeof(buf), - " updated %d of %d %s\n", - used, received, object_type[do_characters]); - if (rejected) - snprintf(buf + (len - 1), sizeof(buf) - (len - 1), - " with %d reject%s\n", - rejected, rejected > 1 ? "s" : ""); - pr_info("%s", buf); - } -} - -/* - * This is called when a user changes the characters or chartab parameters. - */ -static ssize_t chars_chartab_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t count) -{ - char *cp = (char *)buf; - char *end = cp + count; /* the null at the end of the buffer */ - char *linefeed = NULL; - char keyword[MAX_DESC_LEN + 1]; - char *outptr = NULL; /* Will hold keyword or desc. */ - char *temp = NULL; - char *desc = NULL; - ssize_t retval = count; - unsigned long flags; - unsigned long index = 0; - int charclass = 0; - int received = 0; - int used = 0; - int rejected = 0; - int reset = 0; - int do_characters = !strcmp(attr->attr.name, "characters"); - size_t desc_length = 0; - int i; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - while (cp < end) { - while ((cp < end) && (*cp == ' ' || *cp == '\t')) - cp++; - - if (cp == end) - break; - if ((*cp == '\n') || strchr("dDrR", *cp)) { - reset = 1; - break; - } - received++; - - linefeed = strchr(cp, '\n'); - if (!linefeed) { - rejected++; - break; - } - - if (!isdigit(*cp)) { - rejected++; - cp = linefeed + 1; - continue; - } - - /* - * Do not replace with kstrtoul: - * here we need temp to be updated - */ - index = simple_strtoul(cp, &temp, 10); - if (index > 255) { - rejected++; - cp = linefeed + 1; - continue; - } - - while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) - temp++; - - desc_length = linefeed - temp; - if (desc_length > MAX_DESC_LEN) { - rejected++; - cp = linefeed + 1; - continue; - } - if (do_characters) { - desc = kmalloc(desc_length + 1, GFP_ATOMIC); - if (!desc) { - retval = -ENOMEM; - reset = 1; /* just reset on error. */ - break; - } - outptr = desc; - } else { - outptr = keyword; - } - - for (i = 0; i < desc_length; i++) - outptr[i] = temp[i]; - outptr[desc_length] = '\0'; - - if (do_characters) { - if (spk_characters[index] != spk_default_chars[index]) - kfree(spk_characters[index]); - spk_characters[index] = desc; - used++; - } else { - charclass = spk_chartab_get_value(keyword); - if (charclass == 0) { - rejected++; - cp = linefeed + 1; - continue; - } - if (charclass != spk_chartab[index]) { - spk_chartab[index] = charclass; - used++; - } - } - cp = linefeed + 1; - } - - if (reset) { - if (do_characters) - spk_reset_default_chars(); - else - spk_reset_default_chartab(); - } - - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - report_char_chartab_status(reset, received, used, rejected, - do_characters); - return retval; -} - -/* - * This is called when a user reads the keymap parameter. - */ -static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) -{ - char *cp = buf; - int i; - int n; - int num_keys; - int nstates; - u_char *cp1; - u_char ch; - unsigned long flags; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - cp1 = spk_key_buf + SHIFT_TBL_SIZE; - num_keys = (int)(*cp1); - nstates = (int)cp1[1]; - cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates); - cp1 += 2; /* now pointing at shift states */ - /* dump num_keys+1 as first row is shift states + flags, - * each subsequent row is key + states - */ - for (n = 0; n <= num_keys; n++) { - for (i = 0; i <= nstates; i++) { - ch = *cp1++; - cp += sprintf(cp, "%d,", (int)ch); - *cp++ = (i < nstates) ? SPACE : '\n'; - } - } - cp += sprintf(cp, "0, %d\n", KEY_MAP_VER); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return (int)(cp - buf); -} - -/* - * This is called when a user changes the keymap parameter. - */ -static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr, - const char *buf, size_t count) -{ - int i; - ssize_t ret = count; - char *in_buff = NULL; - char *cp; - u_char *cp1; - unsigned long flags; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - in_buff = kmemdup(buf, count + 1, GFP_ATOMIC); - if (!in_buff) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return -ENOMEM; - } - if (strchr("dDrR", *in_buff)) { - spk_set_key_info(spk_key_defaults, spk_key_buf); - pr_info("keymap set to default values\n"); - kfree(in_buff); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return count; - } - if (in_buff[count - 1] == '\n') - in_buff[count - 1] = '\0'; - cp = in_buff; - cp1 = (u_char *)in_buff; - for (i = 0; i < 3; i++) { - cp = spk_s2uchar(cp, cp1); - cp1++; - } - i = (int)cp1[-2] + 1; - i *= (int)cp1[-1] + 1; - i += 2; /* 0 and last map ver */ - if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 || - i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) { - pr_warn("i %d %d %d %d\n", i, - (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); - kfree(in_buff); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return -EINVAL; - } - while (--i >= 0) { - cp = spk_s2uchar(cp, cp1); - cp1++; - if (!(*cp)) - break; - } - if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) { - ret = -EINVAL; - pr_warn("end %d %d %d %d\n", i, - (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); - } else { - if (spk_set_key_info(in_buff, spk_key_buf)) { - spk_set_key_info(spk_key_defaults, spk_key_buf); - ret = -EINVAL; - pr_warn("set key failed\n"); - } - } - kfree(in_buff); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return ret; -} - -/* - * This is called when a user changes the value of the silent parameter. - */ -static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr, - const char *buf, size_t count) -{ - int len; - struct vc_data *vc = vc_cons[fg_console].d; - char ch = 0; - char shut; - unsigned long flags; - - len = strlen(buf); - if (len > 0 && len < 3) { - ch = buf[0]; - if (ch == '\n') - ch = '0'; - } - if (ch < '0' || ch > '7') { - pr_warn("silent value '%c' not in range (0,7)\n", ch); - return -EINVAL; - } - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (ch & 2) { - shut = 1; - spk_do_flush(); - } else { - shut = 0; - } - if (ch & 4) - shut |= 0x40; - if (ch & 1) - spk_shut_up |= shut; - else - spk_shut_up &= ~shut; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return count; -} - -/* - * This is called when a user reads the synth setting. - */ -static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) -{ - int rv; - - if (!synth) - rv = sprintf(buf, "%s\n", "none"); - else - rv = sprintf(buf, "%s\n", synth->name); - return rv; -} - -/* - * This is called when a user requests to change synthesizers. - */ -static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr, - const char *buf, size_t count) -{ - int len; - char new_synth_name[10]; - - len = strlen(buf); - if (len < 2 || len > 9) - return -EINVAL; - memcpy(new_synth_name, buf, len); - if (new_synth_name[len - 1] == '\n') - len--; - new_synth_name[len] = '\0'; - spk_strlwr(new_synth_name); - if (synth && !strcmp(new_synth_name, synth->name)) { - pr_warn("%s already in use\n", new_synth_name); - } else if (synth_init(new_synth_name) != 0) { - pr_warn("failed to init synth %s\n", new_synth_name); - return -ENODEV; - } - return count; -} - -/* - * This is called when text is sent to the synth via the synth_direct file. - */ -static ssize_t synth_direct_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t count) -{ - u_char tmp[256]; - int len; - int bytes; - const char *ptr = buf; - unsigned long flags; - - if (!synth) - return -EPERM; - - len = strlen(buf); - spin_lock_irqsave(&speakup_info.spinlock, flags); - while (len > 0) { - bytes = min_t(size_t, len, 250); - strncpy(tmp, ptr, bytes); - tmp[bytes] = '\0'; - string_unescape_any_inplace(tmp); - synth_printf("%s", tmp); - ptr += bytes; - len -= bytes; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return count; -} - -/* - * This function is called when a user reads the version. - */ -static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) -{ - char *cp; - - cp = buf; - cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION); - if (synth) - cp += sprintf(cp, "%s synthesizer driver version %s\n", - synth->name, synth->version); - return cp - buf; -} - -/* - * This is called when a user reads the punctuation settings. - */ -static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) -{ - int i; - char *cp = buf; - struct st_var_header *p_header; - struct punc_var_t *var; - struct st_bits_data *pb; - short mask; - unsigned long flags; - - p_header = spk_var_header_by_name(attr->attr.name); - if (!p_header) { - pr_warn("p_header is null, attr->attr.name is %s\n", - attr->attr.name); - return -EINVAL; - } - - var = spk_get_punc_var(p_header->var_id); - if (!var) { - pr_warn("var is null, p_header->var_id is %i\n", - p_header->var_id); - return -EINVAL; - } - - spin_lock_irqsave(&speakup_info.spinlock, flags); - pb = (struct st_bits_data *)&spk_punc_info[var->value]; - mask = pb->mask; - for (i = 33; i < 128; i++) { - if (!(spk_chartab[i] & mask)) - continue; - *cp++ = (char)i; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return cp - buf; -} - -/* - * This is called when a user changes the punctuation settings. - */ -static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr, - const char *buf, size_t count) -{ - int x; - struct st_var_header *p_header; - struct punc_var_t *var; - char punc_buf[100]; - unsigned long flags; - - x = strlen(buf); - if (x < 1 || x > 99) - return -EINVAL; - - p_header = spk_var_header_by_name(attr->attr.name); - if (!p_header) { - pr_warn("p_header is null, attr->attr.name is %s\n", - attr->attr.name); - return -EINVAL; - } - - var = spk_get_punc_var(p_header->var_id); - if (!var) { - pr_warn("var is null, p_header->var_id is %i\n", - p_header->var_id); - return -EINVAL; - } - - memcpy(punc_buf, buf, x); - - while (x && punc_buf[x - 1] == '\n') - x--; - punc_buf[x] = '\0'; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - - if (*punc_buf == 'd' || *punc_buf == 'r') - x = spk_set_mask_bits(NULL, var->value, 3); - else - x = spk_set_mask_bits(punc_buf, var->value, 3); - - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return count; -} - -/* - * This function is called when a user reads one of the variable parameters. - */ -ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) -{ - int rv = 0; - struct st_var_header *param; - struct var_t *var; - char *cp1; - char *cp; - char ch; - unsigned long flags; - - param = spk_var_header_by_name(attr->attr.name); - if (!param) - return -EINVAL; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - var = (struct var_t *)param->data; - switch (param->var_type) { - case VAR_NUM: - case VAR_TIME: - if (var) - rv = sprintf(buf, "%i\n", var->u.n.value); - else - rv = sprintf(buf, "0\n"); - break; - case VAR_STRING: - if (var) { - cp1 = buf; - *cp1++ = '"'; - for (cp = (char *)param->p_val; (ch = *cp); cp++) { - if (ch >= ' ' && ch < '~') - *cp1++ = ch; - else - cp1 += sprintf(cp1, "\\x%02x", ch); - } - *cp1++ = '"'; - *cp1++ = '\n'; - *cp1 = '\0'; - rv = cp1 - buf; - } else { - rv = sprintf(buf, "\"\"\n"); - } - break; - default: - rv = sprintf(buf, "Bad parameter %s, type %i\n", - param->name, param->var_type); - break; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return rv; -} -EXPORT_SYMBOL_GPL(spk_var_show); - -/* - * Used to reset either default_pitch or default_vol. - */ -static inline void spk_reset_default_value(char *header_name, - int *synth_default_value, int idx) -{ - struct st_var_header *param; - - if (synth && synth_default_value) { - param = spk_var_header_by_name(header_name); - if (param) { - spk_set_num_var(synth_default_value[idx], - param, E_NEW_DEFAULT); - spk_set_num_var(0, param, E_DEFAULT); - pr_info("%s reset to default value\n", param->name); - } - } -} - -/* - * This function is called when a user echos a value to one of the - * variable parameters. - */ -ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr, - const char *buf, size_t count) -{ - struct st_var_header *param; - int ret; - int len; - char *cp; - struct var_t *var_data; - long value; - unsigned long flags; - - param = spk_var_header_by_name(attr->attr.name); - if (!param) - return -EINVAL; - if (!param->data) - return 0; - ret = 0; - cp = (char *)buf; - string_unescape_any_inplace(cp); - - spin_lock_irqsave(&speakup_info.spinlock, flags); - switch (param->var_type) { - case VAR_NUM: - case VAR_TIME: - if (*cp == 'd' || *cp == 'r' || *cp == '\0') - len = E_DEFAULT; - else if (*cp == '+' || *cp == '-') - len = E_INC; - else - len = E_SET; - if (kstrtol(cp, 10, &value) == 0) - ret = spk_set_num_var(value, param, len); - else - pr_warn("overflow or parsing error has occurred"); - if (ret == -ERANGE) { - var_data = param->data; - pr_warn("value for %s out of range, expect %d to %d\n", - param->name, - var_data->u.n.low, var_data->u.n.high); - } - - /* - * If voice was just changed, we might need to reset our default - * pitch and volume. - */ - if (param->var_id == VOICE && synth && - (ret == 0 || ret == -ERESTART)) { - var_data = param->data; - value = var_data->u.n.value; - spk_reset_default_value("pitch", synth->default_pitch, - value); - spk_reset_default_value("vol", synth->default_vol, - value); - } - break; - case VAR_STRING: - len = strlen(cp); - if ((len >= 1) && (cp[len - 1] == '\n')) - --len; - if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) { - ++cp; - len -= 2; - } - cp[len] = '\0'; - ret = spk_set_string_var(cp, param, len); - if (ret == -E2BIG) - pr_warn("value too long for %s\n", - param->name); - break; - default: - pr_warn("%s unknown type %d\n", - param->name, (int)param->var_type); - break; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - - if (ret == -ERESTART) - pr_info("%s reset to default value\n", param->name); - return count; -} -EXPORT_SYMBOL_GPL(spk_var_store); - -/* - * Functions for reading and writing lists of i18n messages. Incomplete. - */ - -static ssize_t message_show_helper(char *buf, enum msg_index_t first, - enum msg_index_t last) -{ - size_t bufsize = PAGE_SIZE; - char *buf_pointer = buf; - int printed; - enum msg_index_t cursor; - int index = 0; - *buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */ - - for (cursor = first; cursor <= last; cursor++, index++) { - if (bufsize <= 1) - break; - printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n", - index, spk_msg_get(cursor)); - buf_pointer += printed; - bufsize -= printed; - } - - return buf_pointer - buf; -} - -static void report_msg_status(int reset, int received, int used, - int rejected, char *groupname) -{ - int len; - char buf[160]; - - if (reset) { - pr_info("i18n messages from group %s reset to defaults\n", - groupname); - } else if (received) { - len = snprintf(buf, sizeof(buf), - " updated %d of %d i18n messages from group %s\n", - used, received, groupname); - if (rejected) - snprintf(buf + (len - 1), sizeof(buf) - (len - 1), - " with %d reject%s\n", - rejected, rejected > 1 ? "s" : ""); - pr_info("%s", buf); - } -} - -static ssize_t message_store_helper(const char *buf, size_t count, - struct msg_group_t *group) -{ - char *cp = (char *)buf; - char *end = cp + count; - char *linefeed = NULL; - char *temp = NULL; - ssize_t msg_stored = 0; - ssize_t retval = count; - size_t desc_length = 0; - unsigned long index = 0; - int received = 0; - int used = 0; - int rejected = 0; - int reset = 0; - enum msg_index_t firstmessage = group->start; - enum msg_index_t lastmessage = group->end; - enum msg_index_t curmessage; - - while (cp < end) { - while ((cp < end) && (*cp == ' ' || *cp == '\t')) - cp++; - - if (cp == end) - break; - if (strchr("dDrR", *cp)) { - reset = 1; - break; - } - received++; - - linefeed = strchr(cp, '\n'); - if (!linefeed) { - rejected++; - break; - } - - if (!isdigit(*cp)) { - rejected++; - cp = linefeed + 1; - continue; - } - - /* - * Do not replace with kstrtoul: - * here we need temp to be updated - */ - index = simple_strtoul(cp, &temp, 10); - - while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) - temp++; - - desc_length = linefeed - temp; - curmessage = firstmessage + index; - - /* - * Note the check (curmessage < firstmessage). It is not - * redundant. Suppose that the user gave us an index - * equal to ULONG_MAX - 1. If firstmessage > 1, then - * firstmessage + index < firstmessage! - */ - - if ((curmessage < firstmessage) || (curmessage > lastmessage)) { - rejected++; - cp = linefeed + 1; - continue; - } - - msg_stored = spk_msg_set(curmessage, temp, desc_length); - if (msg_stored < 0) { - retval = msg_stored; - if (msg_stored == -ENOMEM) - reset = 1; - break; - } - - used++; - - cp = linefeed + 1; - } - - if (reset) - spk_reset_msg_group(group); - - report_msg_status(reset, received, used, rejected, group->name); - return retval; -} - -static ssize_t message_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - ssize_t retval = 0; - struct msg_group_t *group = spk_find_msg_group(attr->attr.name); - unsigned long flags; - - if (WARN_ON(!group)) - return -EINVAL; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - retval = message_show_helper(buf, group->start, group->end); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return retval; -} - -static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr, - const char *buf, size_t count) -{ - struct msg_group_t *group = spk_find_msg_group(attr->attr.name); - - if (WARN_ON(!group)) - return -EINVAL; - - return message_store_helper(buf, count, group); -} - -/* - * Declare the attributes. - */ -static struct kobj_attribute keymap_attribute = - __ATTR_RW(keymap); -static struct kobj_attribute silent_attribute = - __ATTR_WO(silent); -static struct kobj_attribute synth_attribute = - __ATTR_RW(synth); -static struct kobj_attribute synth_direct_attribute = - __ATTR_WO(synth_direct); -static struct kobj_attribute version_attribute = - __ATTR_RO(version); - -static struct kobj_attribute delimiters_attribute = - __ATTR(delimiters, 0644, punc_show, punc_store); -static struct kobj_attribute ex_num_attribute = - __ATTR(ex_num, 0644, punc_show, punc_store); -static struct kobj_attribute punc_all_attribute = - __ATTR(punc_all, 0644, punc_show, punc_store); -static struct kobj_attribute punc_most_attribute = - __ATTR(punc_most, 0644, punc_show, punc_store); -static struct kobj_attribute punc_some_attribute = - __ATTR(punc_some, 0644, punc_show, punc_store); -static struct kobj_attribute repeats_attribute = - __ATTR(repeats, 0644, punc_show, punc_store); - -static struct kobj_attribute attrib_bleep_attribute = - __ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute bell_pos_attribute = - __ATTR(bell_pos, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute bleep_time_attribute = - __ATTR(bleep_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute bleeps_attribute = - __ATTR(bleeps, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute cursor_time_attribute = - __ATTR(cursor_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute key_echo_attribute = - __ATTR(key_echo, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute no_interrupt_attribute = - __ATTR(no_interrupt, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punc_level_attribute = - __ATTR(punc_level, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute reading_punc_attribute = - __ATTR(reading_punc, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute say_control_attribute = - __ATTR(say_control, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute say_word_ctl_attribute = - __ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute spell_delay_attribute = - __ATTR(spell_delay, 0644, spk_var_show, spk_var_store); - -/* - * These attributes are i18n related. - */ -static struct kobj_attribute announcements_attribute = - __ATTR(announcements, 0644, message_show, message_store); -static struct kobj_attribute characters_attribute = - __ATTR(characters, 0644, chars_chartab_show, - chars_chartab_store); -static struct kobj_attribute chartab_attribute = - __ATTR(chartab, 0644, chars_chartab_show, - chars_chartab_store); -static struct kobj_attribute ctl_keys_attribute = - __ATTR(ctl_keys, 0644, message_show, message_store); -static struct kobj_attribute colors_attribute = - __ATTR(colors, 0644, message_show, message_store); -static struct kobj_attribute formatted_attribute = - __ATTR(formatted, 0644, message_show, message_store); -static struct kobj_attribute function_names_attribute = - __ATTR(function_names, 0644, message_show, message_store); -static struct kobj_attribute key_names_attribute = - __ATTR(key_names, 0644, message_show, message_store); -static struct kobj_attribute states_attribute = - __ATTR(states, 0644, message_show, message_store); - -/* - * Create groups of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *main_attrs[] = { - &keymap_attribute.attr, - &silent_attribute.attr, - &synth_attribute.attr, - &synth_direct_attribute.attr, - &version_attribute.attr, - &delimiters_attribute.attr, - &ex_num_attribute.attr, - &punc_all_attribute.attr, - &punc_most_attribute.attr, - &punc_some_attribute.attr, - &repeats_attribute.attr, - &attrib_bleep_attribute.attr, - &bell_pos_attribute.attr, - &bleep_time_attribute.attr, - &bleeps_attribute.attr, - &cursor_time_attribute.attr, - &key_echo_attribute.attr, - &no_interrupt_attribute.attr, - &punc_level_attribute.attr, - &reading_punc_attribute.attr, - &say_control_attribute.attr, - &say_word_ctl_attribute.attr, - &spell_delay_attribute.attr, - NULL, -}; - -static struct attribute *i18n_attrs[] = { - &announcements_attribute.attr, - &characters_attribute.attr, - &chartab_attribute.attr, - &ctl_keys_attribute.attr, - &colors_attribute.attr, - &formatted_attribute.attr, - &function_names_attribute.attr, - &key_names_attribute.attr, - &states_attribute.attr, - NULL, -}; - -/* - * An unnamed attribute group will put all of the attributes directly in - * the kobject directory. If we specify a name, a subdirectory will be - * created for the attributes with the directory being the name of the - * attribute group. - */ -static const struct attribute_group main_attr_group = { - .attrs = main_attrs, -}; - -static const struct attribute_group i18n_attr_group = { - .attrs = i18n_attrs, - .name = "i18n", -}; - -static struct kobject *accessibility_kobj; -struct kobject *speakup_kobj; - -int speakup_kobj_init(void) -{ - int retval; - - /* - * Create a simple kobject with the name of "accessibility", - * located under /sys/ - * - * As this is a simple directory, no uevent will be sent to - * userspace. That is why this function should not be used for - * any type of dynamic kobjects, where the name and number are - * not known ahead of time. - */ - accessibility_kobj = kobject_create_and_add("accessibility", NULL); - if (!accessibility_kobj) { - retval = -ENOMEM; - goto out; - } - - speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj); - if (!speakup_kobj) { - retval = -ENOMEM; - goto err_acc; - } - - /* Create the files associated with this kobject */ - retval = sysfs_create_group(speakup_kobj, &main_attr_group); - if (retval) - goto err_speakup; - - retval = sysfs_create_group(speakup_kobj, &i18n_attr_group); - if (retval) - goto err_group; - - goto out; - -err_group: - sysfs_remove_group(speakup_kobj, &main_attr_group); -err_speakup: - kobject_put(speakup_kobj); -err_acc: - kobject_put(accessibility_kobj); -out: - return retval; -} - -void speakup_kobj_exit(void) -{ - sysfs_remove_group(speakup_kobj, &i18n_attr_group); - sysfs_remove_group(speakup_kobj, &main_attr_group); - kobject_put(speakup_kobj); - kobject_put(accessibility_kobj); -} diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c deleted file mode 100644 index 02471d932d71..000000000000 --- a/drivers/staging/speakup/main.c +++ /dev/null @@ -1,2460 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* speakup.c - * review functions for the speakup screen review package. - * originally written by: Kirk Reiser and Andy Berdan. - * - * extensively modified by David Borowski. - * - ** Copyright (C) 1998 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - */ - -#include -#include -#include -#include /* __get_free_page() and friends */ -#include -#include -#include -#include -#include -#include -#include /* for KT_SHIFT */ -#include /* for vc_kbd_* and friends */ -#include -#include - -/* speakup_*_selection */ -#include -#include -#include -#include -#include - -#include -#include - -#include /* copy_from|to|user() and others */ - -#include "spk_priv.h" -#include "speakup.h" - -#define MAX_DELAY msecs_to_jiffies(500) -#define MINECHOCHAR SPACE - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("Daniel Drake "); -MODULE_DESCRIPTION("Speakup console speech"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(SPEAKUP_VERSION); - -char *synth_name; -module_param_named(synth, synth_name, charp, 0444); -module_param_named(quiet, spk_quiet_boot, bool, 0444); - -MODULE_PARM_DESC(synth, "Synth to start if speakup is built in."); -MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found."); - -special_func spk_special_handler; - -short spk_pitch_shift, synth_flags; -static u16 buf[256]; -int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10; -int spk_no_intr, spk_spell_delay; -int spk_key_echo, spk_say_word_ctl; -int spk_say_ctrl, spk_bell_pos; -short spk_punc_mask; -int spk_punc_level, spk_reading_punc; -char spk_str_caps_start[MAXVARLEN + 1] = "\0"; -char spk_str_caps_stop[MAXVARLEN + 1] = "\0"; -char spk_str_pause[MAXVARLEN + 1] = "\0"; -bool spk_paused; -const struct st_bits_data spk_punc_info[] = { - {"none", "", 0}, - {"some", "/$%&@", SOME}, - {"most", "$%&#()=+*/@^<>|\\", MOST}, - {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC}, - {"delimiters", "", B_WDLM}, - {"repeats", "()", CH_RPT}, - {"extended numeric", "", B_EXNUM}, - {"symbols", "", B_SYM}, - {NULL, NULL} -}; - -static char mark_cut_flag; -#define MAX_KEY 160 -static u_char *spk_shift_table; -u_char *spk_our_keys[MAX_KEY]; -u_char spk_key_buf[600]; -const u_char spk_key_defaults[] = { -#include "speakupmap.h" -}; - -/* Speakup Cursor Track Variables */ -static int cursor_track = 1, prev_cursor_track = 1; - -/* cursor track modes, must be ordered same as cursor_msgs */ -enum { - CT_Off = 0, - CT_On, - CT_Highlight, - CT_Window, - CT_Max -}; - -#define read_all_mode CT_Max - -static struct tty_struct *tty; - -static void spkup_write(const u16 *in_buf, int count); - -static char *phonetic[] = { - "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", - "india", "juliett", "keelo", "leema", "mike", "november", "oscar", - "papa", - "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey", - "x ray", "yankee", "zulu" -}; - -/* array of 256 char pointers (one for each character description) - * initialized to default_chars and user selectable via - * /proc/speakup/characters - */ -char *spk_characters[256]; - -char *spk_default_chars[256] = { -/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g", -/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o", -/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w", -/*024*/ "^x", "^y", "^z", "control", "control", "control", "control", - "control", -/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and", - "tick", -/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash", - "dot", - "slash", -/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven", - "eight", "nine", -/*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at", -/*065*/ "EIGH", "B", "C", "D", "E", "F", "G", -/*072*/ "H", "I", "J", "K", "L", "M", "N", "O", -/*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X", -/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket", - "caret", - "line", -/*096*/ "accent", "a", "b", "c", "d", "e", "f", "g", -/*104*/ "h", "i", "j", "k", "l", "m", "n", "o", -/*112*/ "p", "q", "r", "s", "t", "u", "v", "w", -/*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh", -/*127*/ "del", "control", "control", "control", "control", "control", - "control", "control", "control", "control", "control", -/*138*/ "control", "control", "control", "control", "control", - "control", "control", "control", "control", "control", - "control", "control", -/*150*/ "control", "control", "control", "control", "control", - "control", "control", "control", "control", "control", -/*160*/ "nbsp", "inverted bang", -/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section", -/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle", -/*172*/ "not", "soft hyphen", "registered", "macron", -/*176*/ "degrees", "plus or minus", "super two", "super three", -/*180*/ "acute accent", "micro", "pilcrow", "middle dot", -/*184*/ "cedilla", "super one", "male ordinal", "double right angle", -/*188*/ "one quarter", "one half", "three quarters", - "inverted question", -/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT", - "A RING", -/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX", - "E OOMLAUT", -/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH", - "N TILDE", -/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT", -/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE", - "U CIRCUMFLEX", -/*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave", -/*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring", -/*230*/ "ae", "c cidella", "e grave", "e acute", -/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute", - "i circumflex", -/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute", - "o circumflex", -/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave", - "u acute", -/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut" -}; - -/* array of 256 u_short (one for each character) - * initialized to default_chartab and user selectable via - * /sys/module/speakup/parameters/chartab - */ -u_short spk_chartab[256]; - -static u_short default_chartab[256] = { - B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */ - B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */ - B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */ - B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */ - WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */ - PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */ - NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */ - NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */ - PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */ - A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */ - A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */ - A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */ - PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */ - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */ - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */ - ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */ - B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */ - B_SYM, /* 135 */ - B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */ - B_CAPSYM, /* 143 */ - B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */ - B_SYM, /* 151 */ - B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */ - B_SYM, /* 159 */ - WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */ - B_SYM, /* 167 */ - B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */ - B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */ - B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */ - A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */ - A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */ - A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */ - A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */ - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */ - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */ - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */ - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */ -}; - -struct task_struct *speakup_task; -struct bleep spk_unprocessed_sound; -static int spk_keydown; -static u16 spk_lastkey; -static u_char spk_close_press, keymap_flags; -static u_char last_keycode, this_speakup_key; -static u_long last_spk_jiffy; - -struct st_spk_t *speakup_console[MAX_NR_CONSOLES]; - -DEFINE_MUTEX(spk_mutex); - -static int keyboard_notifier_call(struct notifier_block *, - unsigned long code, void *param); - -static struct notifier_block keyboard_notifier_block = { - .notifier_call = keyboard_notifier_call, -}; - -static int vt_notifier_call(struct notifier_block *, - unsigned long code, void *param); - -static struct notifier_block vt_notifier_block = { - .notifier_call = vt_notifier_call, -}; - -static unsigned char get_attributes(struct vc_data *vc, u16 *pos) -{ - pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, 1); - return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8; -} - -static void speakup_date(struct vc_data *vc) -{ - spk_x = spk_cx = vc->vc_x; - spk_y = spk_cy = vc->vc_y; - spk_pos = spk_cp = vc->vc_pos; - spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)spk_pos); -} - -static void bleep(u_short val) -{ - static const short vals[] = { - 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659 - }; - short freq; - int time = spk_bleep_time; - - freq = vals[val % 12]; - if (val > 11) - freq *= (1 << (val / 12)); - spk_unprocessed_sound.freq = freq; - spk_unprocessed_sound.jiffies = msecs_to_jiffies(time); - spk_unprocessed_sound.active = 1; - /* We can only have 1 active sound at a time. */ -} - -static void speakup_shut_up(struct vc_data *vc) -{ - if (spk_killed) - return; - spk_shut_up |= 0x01; - spk_parked &= 0xfe; - speakup_date(vc); - if (synth) - spk_do_flush(); -} - -static void speech_kill(struct vc_data *vc) -{ - char val = synth->is_alive(synth); - - if (val == 0) - return; - - /* re-enables synth, if disabled */ - if (val == 2 || spk_killed) { - /* dead */ - spk_shut_up &= ~0x40; - synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE)); - } else { - synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP)); - spk_shut_up |= 0x40; - } -} - -static void speakup_off(struct vc_data *vc) -{ - if (spk_shut_up & 0x80) { - spk_shut_up &= 0x7f; - synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER)); - } else { - spk_shut_up |= 0x80; - synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF)); - } - speakup_date(vc); -} - -static void speakup_parked(struct vc_data *vc) -{ - if (spk_parked & 0x80) { - spk_parked = 0; - synth_printf("%s\n", spk_msg_get(MSG_UNPARKED)); - } else { - spk_parked |= 0x80; - synth_printf("%s\n", spk_msg_get(MSG_PARKED)); - } -} - -static void speakup_cut(struct vc_data *vc) -{ - static const char err_buf[] = "set selection failed"; - int ret; - - if (!mark_cut_flag) { - mark_cut_flag = 1; - spk_xs = (u_short)spk_x; - spk_ys = (u_short)spk_y; - spk_sel_cons = vc; - synth_printf("%s\n", spk_msg_get(MSG_MARK)); - return; - } - spk_xe = (u_short)spk_x; - spk_ye = (u_short)spk_y; - mark_cut_flag = 0; - synth_printf("%s\n", spk_msg_get(MSG_CUT)); - - speakup_clear_selection(); - ret = speakup_set_selection(tty); - - switch (ret) { - case 0: - break; /* no error */ - case -EFAULT: - pr_warn("%sEFAULT\n", err_buf); - break; - case -EINVAL: - pr_warn("%sEINVAL\n", err_buf); - break; - case -ENOMEM: - pr_warn("%sENOMEM\n", err_buf); - break; - } -} - -static void speakup_paste(struct vc_data *vc) -{ - if (mark_cut_flag) { - mark_cut_flag = 0; - synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED)); - } else { - synth_printf("%s\n", spk_msg_get(MSG_PASTE)); - speakup_paste_selection(tty); - } -} - -static void say_attributes(struct vc_data *vc) -{ - int fg = spk_attr & 0x0f; - int bg = spk_attr >> 4; - - if (fg > 8) { - synth_printf("%s ", spk_msg_get(MSG_BRIGHT)); - fg -= 8; - } - synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg)); - if (bg > 7) { - synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING)); - bg -= 8; - } else { - synth_printf(" %s ", spk_msg_get(MSG_ON)); - } - synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg)); -} - -enum { - edge_top = 1, - edge_bottom, - edge_left, - edge_right, - edge_quiet -}; - -static void announce_edge(struct vc_data *vc, int msg_id) -{ - if (spk_bleeps & 1) - bleep(spk_y); - if ((spk_bleeps & 2) && (msg_id < edge_quiet)) - synth_printf("%s\n", - spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1)); -} - -static void speak_char(u16 ch) -{ - char *cp; - struct var_t *direct = spk_get_var(DIRECT); - - if (ch >= 0x100 || (direct && direct->u.n.value)) { - if (ch < 0x100 && IS_CHAR(ch, B_CAP)) { - spk_pitch_shift++; - synth_printf("%s", spk_str_caps_start); - } - synth_putwc_s(ch); - if (ch < 0x100 && IS_CHAR(ch, B_CAP)) - synth_printf("%s", spk_str_caps_stop); - return; - } - - cp = spk_characters[ch]; - if (!cp) { - pr_info("%s: cp == NULL!\n", __func__); - return; - } - if (IS_CHAR(ch, B_CAP)) { - spk_pitch_shift++; - synth_printf("%s %s %s", - spk_str_caps_start, cp, spk_str_caps_stop); - } else { - if (*cp == '^') { - cp++; - synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp); - } else { - synth_printf(" %s ", cp); - } - } -} - -static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs) -{ - u16 ch = ' '; - - if (vc && pos) { - u16 w; - u16 c; - - pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, 1); - w = scr_readw(pos); - c = w & 0xff; - - if (w & vc->vc_hi_font_mask) { - w &= ~vc->vc_hi_font_mask; - c |= 0x100; - } - - ch = inverse_translate(vc, c, 1); - *attribs = (w & 0xff00) >> 8; - } - return ch; -} - -static void say_char(struct vc_data *vc) -{ - u16 ch; - - spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)spk_pos, &spk_attr); - if (spk_attr != spk_old_attr) { - if (spk_attrib_bleep & 1) - bleep(spk_y); - if (spk_attrib_bleep & 2) - say_attributes(vc); - } - speak_char(ch); -} - -static void say_phonetic_char(struct vc_data *vc) -{ - u16 ch; - - spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)spk_pos, &spk_attr); - if (ch <= 0x7f && isalpha(ch)) { - ch &= 0x1f; - synth_printf("%s\n", phonetic[--ch]); - } else { - if (ch < 0x100 && IS_CHAR(ch, B_NUM)) - synth_printf("%s ", spk_msg_get(MSG_NUMBER)); - speak_char(ch); - } -} - -static void say_prev_char(struct vc_data *vc) -{ - spk_parked |= 0x01; - if (spk_x == 0) { - announce_edge(vc, edge_left); - return; - } - spk_x--; - spk_pos -= 2; - say_char(vc); -} - -static void say_next_char(struct vc_data *vc) -{ - spk_parked |= 0x01; - if (spk_x == vc->vc_cols - 1) { - announce_edge(vc, edge_right); - return; - } - spk_x++; - spk_pos += 2; - say_char(vc); -} - -/* get_word - will first check to see if the character under the - * reading cursor is a space and if spk_say_word_ctl is true it will - * return the word space. If spk_say_word_ctl is not set it will check to - * see if there is a word starting on the next position to the right - * and return that word if it exists. If it does not exist it will - * move left to the beginning of any previous word on the line or the - * beginning off the line whichever comes first.. - */ - -static u_long get_word(struct vc_data *vc) -{ - u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos; - u16 ch; - u16 attr_ch; - u_char temp; - - spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)tmp_pos, &temp); - -/* decided to take out the sayword if on a space (mis-information */ - if (spk_say_word_ctl && ch == SPACE) { - *buf = '\0'; - synth_printf("%s\n", spk_msg_get(MSG_SPACE)); - return 0; - } else if (tmpx < vc->vc_cols - 2 && - (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && - get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) { - tmp_pos += 2; - tmpx++; - } else { - while (tmpx > 0) { - ch = get_char(vc, (u_short *)tmp_pos - 1, &temp); - if ((ch == SPACE || ch == 0 || - (ch < 0x100 && IS_WDLM(ch))) && - get_char(vc, (u_short *)tmp_pos, &temp) > SPACE) - break; - tmp_pos -= 2; - tmpx--; - } - } - attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr); - buf[cnt++] = attr_ch; - while (tmpx < vc->vc_cols - 1) { - tmp_pos += 2; - tmpx++; - ch = get_char(vc, (u_short *)tmp_pos, &temp); - if (ch == SPACE || ch == 0 || - (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) && - ch > SPACE)) - break; - buf[cnt++] = ch; - } - buf[cnt] = '\0'; - return cnt; -} - -static void say_word(struct vc_data *vc) -{ - u_long cnt = get_word(vc); - u_short saved_punc_mask = spk_punc_mask; - - if (cnt == 0) - return; - spk_punc_mask = PUNC; - buf[cnt++] = SPACE; - spkup_write(buf, cnt); - spk_punc_mask = saved_punc_mask; -} - -static void say_prev_word(struct vc_data *vc) -{ - u_char temp; - u16 ch; - u_short edge_said = 0, last_state = 0, state = 0; - - spk_parked |= 0x01; - - if (spk_x == 0) { - if (spk_y == 0) { - announce_edge(vc, edge_top); - return; - } - spk_y--; - spk_x = vc->vc_cols; - edge_said = edge_quiet; - } - while (1) { - if (spk_x == 0) { - if (spk_y == 0) { - edge_said = edge_top; - break; - } - if (edge_said != edge_quiet) - edge_said = edge_left; - if (state > 0) - break; - spk_y--; - spk_x = vc->vc_cols - 1; - } else { - spk_x--; - } - spk_pos -= 2; - ch = get_char(vc, (u_short *)spk_pos, &temp); - if (ch == SPACE || ch == 0) - state = 0; - else if (ch < 0x100 && IS_WDLM(ch)) - state = 1; - else - state = 2; - if (state < last_state) { - spk_pos += 2; - spk_x++; - break; - } - last_state = state; - } - if (spk_x == 0 && edge_said == edge_quiet) - edge_said = edge_left; - if (edge_said > 0 && edge_said < edge_quiet) - announce_edge(vc, edge_said); - say_word(vc); -} - -static void say_next_word(struct vc_data *vc) -{ - u_char temp; - u16 ch; - u_short edge_said = 0, last_state = 2, state = 0; - - spk_parked |= 0x01; - if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) { - announce_edge(vc, edge_bottom); - return; - } - while (1) { - ch = get_char(vc, (u_short *)spk_pos, &temp); - if (ch == SPACE || ch == 0) - state = 0; - else if (ch < 0x100 && IS_WDLM(ch)) - state = 1; - else - state = 2; - if (state > last_state) - break; - if (spk_x >= vc->vc_cols - 1) { - if (spk_y == vc->vc_rows - 1) { - edge_said = edge_bottom; - break; - } - state = 0; - spk_y++; - spk_x = 0; - edge_said = edge_right; - } else { - spk_x++; - } - spk_pos += 2; - last_state = state; - } - if (edge_said > 0) - announce_edge(vc, edge_said); - say_word(vc); -} - -static void spell_word(struct vc_data *vc) -{ - static char const *delay_str[] = { "", ",", ".", ". .", ". . ." }; - u16 *cp = buf; - char *cp1; - char *str_cap = spk_str_caps_stop; - char *last_cap = spk_str_caps_stop; - struct var_t *direct = spk_get_var(DIRECT); - u16 ch; - - if (!get_word(vc)) - return; - while ((ch = *cp)) { - if (cp != buf) - synth_printf(" %s ", delay_str[spk_spell_delay]); - /* FIXME: Non-latin1 considered as lower case */ - if (ch < 0x100 && IS_CHAR(ch, B_CAP)) { - str_cap = spk_str_caps_start; - if (*spk_str_caps_stop) - spk_pitch_shift++; - else /* synth has no pitch */ - last_cap = spk_str_caps_stop; - } else { - str_cap = spk_str_caps_stop; - } - if (str_cap != last_cap) { - synth_printf("%s", str_cap); - last_cap = str_cap; - } - if (ch >= 0x100 || (direct && direct->u.n.value)) { - synth_putwc_s(ch); - } else if (this_speakup_key == SPELL_PHONETIC && - ch <= 0x7f && isalpha(ch)) { - ch &= 0x1f; - cp1 = phonetic[--ch]; - synth_printf("%s", cp1); - } else { - cp1 = spk_characters[ch]; - if (*cp1 == '^') { - synth_printf("%s", spk_msg_get(MSG_CTRL)); - cp1++; - } - synth_printf("%s", cp1); - } - cp++; - } - if (str_cap != spk_str_caps_stop) - synth_printf("%s", spk_str_caps_stop); -} - -static int get_line(struct vc_data *vc) -{ - u_long tmp = spk_pos - (spk_x * 2); - int i = 0; - u_char tmp2; - - spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)spk_pos); - for (i = 0; i < vc->vc_cols; i++) { - buf[i] = get_char(vc, (u_short *)tmp, &tmp2); - tmp += 2; - } - for (--i; i >= 0; i--) - if (buf[i] != SPACE) - break; - return ++i; -} - -static void say_line(struct vc_data *vc) -{ - int i = get_line(vc); - u16 *cp; - u_short saved_punc_mask = spk_punc_mask; - - if (i == 0) { - synth_printf("%s\n", spk_msg_get(MSG_BLANK)); - return; - } - buf[i++] = '\n'; - if (this_speakup_key == SAY_LINE_INDENT) { - cp = buf; - while (*cp == SPACE) - cp++; - synth_printf("%zd, ", (cp - buf) + 1); - } - spk_punc_mask = spk_punc_masks[spk_reading_punc]; - spkup_write(buf, i); - spk_punc_mask = saved_punc_mask; -} - -static void say_prev_line(struct vc_data *vc) -{ - spk_parked |= 0x01; - if (spk_y == 0) { - announce_edge(vc, edge_top); - return; - } - spk_y--; - spk_pos -= vc->vc_size_row; - say_line(vc); -} - -static void say_next_line(struct vc_data *vc) -{ - spk_parked |= 0x01; - if (spk_y == vc->vc_rows - 1) { - announce_edge(vc, edge_bottom); - return; - } - spk_y++; - spk_pos += vc->vc_size_row; - say_line(vc); -} - -static int say_from_to(struct vc_data *vc, u_long from, u_long to, - int read_punc) -{ - int i = 0; - u_char tmp; - u_short saved_punc_mask = spk_punc_mask; - - spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)from); - while (from < to) { - buf[i++] = get_char(vc, (u_short *)from, &tmp); - from += 2; - if (i >= vc->vc_size_row) - break; - } - for (--i; i >= 0; i--) - if (buf[i] != SPACE) - break; - buf[++i] = SPACE; - buf[++i] = '\0'; - if (i < 1) - return i; - if (read_punc) - spk_punc_mask = spk_punc_info[spk_reading_punc].mask; - spkup_write(buf, i); - if (read_punc) - spk_punc_mask = saved_punc_mask; - return i - 1; -} - -static void say_line_from_to(struct vc_data *vc, u_long from, u_long to, - int read_punc) -{ - u_long start = vc->vc_origin + (spk_y * vc->vc_size_row); - u_long end = start + (to * 2); - - start += from * 2; - if (say_from_to(vc, start, end, read_punc) <= 0) - if (cursor_track != read_all_mode) - synth_printf("%s\n", spk_msg_get(MSG_BLANK)); -} - -/* Sentence Reading Commands */ - -static int currsentence; -static int numsentences[2]; -static u16 *sentbufend[2]; -static u16 *sentmarks[2][10]; -static int currbuf; -static int bn; -static u16 sentbuf[2][256]; - -static int say_sentence_num(int num, int prev) -{ - bn = currbuf; - currsentence = num + 1; - if (prev && --bn == -1) - bn = 1; - - if (num > numsentences[bn]) - return 0; - - spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]); - return 1; -} - -static int get_sentence_buf(struct vc_data *vc, int read_punc) -{ - u_long start, end; - int i, bn; - u_char tmp; - - currbuf++; - if (currbuf == 2) - currbuf = 0; - bn = currbuf; - start = vc->vc_origin + ((spk_y) * vc->vc_size_row); - end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2; - - numsentences[bn] = 0; - sentmarks[bn][0] = &sentbuf[bn][0]; - i = 0; - spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)start); - - while (start < end) { - sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp); - if (i > 0) { - if (sentbuf[bn][i] == SPACE && - sentbuf[bn][i - 1] == '.' && - numsentences[bn] < 9) { - /* Sentence Marker */ - numsentences[bn]++; - sentmarks[bn][numsentences[bn]] = - &sentbuf[bn][i]; - } - } - i++; - start += 2; - if (i >= vc->vc_size_row) - break; - } - - for (--i; i >= 0; i--) - if (sentbuf[bn][i] != SPACE) - break; - - if (i < 1) - return -1; - - sentbuf[bn][++i] = SPACE; - sentbuf[bn][++i] = '\0'; - - sentbufend[bn] = &sentbuf[bn][i]; - return numsentences[bn]; -} - -static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to) -{ - u_long start = vc->vc_origin, end; - - if (from > 0) - start += from * vc->vc_size_row; - if (to > vc->vc_rows) - to = vc->vc_rows; - end = vc->vc_origin + (to * vc->vc_size_row); - for (from = start; from < end; from = to) { - to = from + vc->vc_size_row; - say_from_to(vc, from, to, 1); - } -} - -static void say_screen(struct vc_data *vc) -{ - say_screen_from_to(vc, 0, vc->vc_rows); -} - -static void speakup_win_say(struct vc_data *vc) -{ - u_long start, end, from, to; - - if (win_start < 2) { - synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW)); - return; - } - start = vc->vc_origin + (win_top * vc->vc_size_row); - end = vc->vc_origin + (win_bottom * vc->vc_size_row); - while (start <= end) { - from = start + (win_left * 2); - to = start + (win_right * 2); - say_from_to(vc, from, to, 1); - start += vc->vc_size_row; - } -} - -static void top_edge(struct vc_data *vc) -{ - spk_parked |= 0x01; - spk_pos = vc->vc_origin + 2 * spk_x; - spk_y = 0; - say_line(vc); -} - -static void bottom_edge(struct vc_data *vc) -{ - spk_parked |= 0x01; - spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row; - spk_y = vc->vc_rows - 1; - say_line(vc); -} - -static void left_edge(struct vc_data *vc) -{ - spk_parked |= 0x01; - spk_pos -= spk_x * 2; - spk_x = 0; - say_char(vc); -} - -static void right_edge(struct vc_data *vc) -{ - spk_parked |= 0x01; - spk_pos += (vc->vc_cols - spk_x - 1) * 2; - spk_x = vc->vc_cols - 1; - say_char(vc); -} - -static void say_first_char(struct vc_data *vc) -{ - int i, len = get_line(vc); - u16 ch; - - spk_parked |= 0x01; - if (len == 0) { - synth_printf("%s\n", spk_msg_get(MSG_BLANK)); - return; - } - for (i = 0; i < len; i++) - if (buf[i] != SPACE) - break; - ch = buf[i]; - spk_pos -= (spk_x - i) * 2; - spk_x = i; - synth_printf("%d, ", ++i); - speak_char(ch); -} - -static void say_last_char(struct vc_data *vc) -{ - int len = get_line(vc); - u16 ch; - - spk_parked |= 0x01; - if (len == 0) { - synth_printf("%s\n", spk_msg_get(MSG_BLANK)); - return; - } - ch = buf[--len]; - spk_pos -= (spk_x - len) * 2; - spk_x = len; - synth_printf("%d, ", ++len); - speak_char(ch); -} - -static void say_position(struct vc_data *vc) -{ - synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1, - vc->vc_num + 1); - synth_printf("\n"); -} - -/* Added by brianb */ -static void say_char_num(struct vc_data *vc) -{ - u_char tmp; - u16 ch = get_char(vc, (u_short *)spk_pos, &tmp); - - synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch); -} - -/* these are stub functions to keep keyboard.c happy. */ - -static void say_from_top(struct vc_data *vc) -{ - say_screen_from_to(vc, 0, spk_y); -} - -static void say_to_bottom(struct vc_data *vc) -{ - say_screen_from_to(vc, spk_y, vc->vc_rows); -} - -static void say_from_left(struct vc_data *vc) -{ - say_line_from_to(vc, 0, spk_x, 1); -} - -static void say_to_right(struct vc_data *vc) -{ - say_line_from_to(vc, spk_x, vc->vc_cols, 1); -} - -/* end of stub functions. */ - -static void spkup_write(const u16 *in_buf, int count) -{ - static int rep_count; - static u16 ch = '\0', old_ch = '\0'; - static u_short char_type, last_type; - int in_count = count; - - spk_keydown = 0; - while (count--) { - if (cursor_track == read_all_mode) { - /* Insert Sentence Index */ - if ((in_buf == sentmarks[bn][currsentence]) && - (currsentence <= numsentences[bn])) - synth_insert_next_index(currsentence++); - } - ch = *in_buf++; - if (ch < 0x100) - char_type = spk_chartab[ch]; - else - char_type = ALPHA; - if (ch == old_ch && !(char_type & B_NUM)) { - if (++rep_count > 2) - continue; - } else { - if ((last_type & CH_RPT) && rep_count > 2) { - synth_printf(" "); - synth_printf(spk_msg_get(MSG_REPEAT_DESC), - ++rep_count); - synth_printf(" "); - } - rep_count = 0; - } - if (ch == spk_lastkey) { - rep_count = 0; - if (spk_key_echo == 1 && ch >= MINECHOCHAR) - speak_char(ch); - } else if (char_type & B_ALPHA) { - if ((synth_flags & SF_DEC) && (last_type & PUNC)) - synth_buffer_add(SPACE); - synth_putwc_s(ch); - } else if (char_type & B_NUM) { - rep_count = 0; - synth_putwc_s(ch); - } else if (char_type & spk_punc_mask) { - speak_char(ch); - char_type &= ~PUNC; /* for dec nospell processing */ - } else if (char_type & SYNTH_OK) { - /* these are usually puncts like . and , which synth - * needs for expression. - * suppress multiple to get rid of long pauses and - * clear repeat count - * so if someone has - * repeats on you don't get nothing repeated count - */ - if (ch != old_ch) - synth_putwc_s(ch); - else - rep_count = 0; - } else { -/* send space and record position, if next is num overwrite space */ - if (old_ch != ch) - synth_buffer_add(SPACE); - else - rep_count = 0; - } - old_ch = ch; - last_type = char_type; - } - spk_lastkey = 0; - if (in_count > 2 && rep_count > 2) { - if (last_type & CH_RPT) { - synth_printf(" "); - synth_printf(spk_msg_get(MSG_REPEAT_DESC2), - ++rep_count); - synth_printf(" "); - } - rep_count = 0; - } -} - -static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1); - -static void read_all_doc(struct vc_data *vc); -static void cursor_done(struct timer_list *unused); -static DEFINE_TIMER(cursor_timer, cursor_done); - -static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag) -{ - unsigned long flags; - - if (!synth || up_flag || spk_killed) - return; - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (cursor_track == read_all_mode) { - switch (value) { - case KVAL(K_SHIFT): - del_timer(&cursor_timer); - spk_shut_up &= 0xfe; - spk_do_flush(); - read_all_doc(vc); - break; - case KVAL(K_CTRL): - del_timer(&cursor_timer); - cursor_track = prev_cursor_track; - spk_shut_up &= 0xfe; - spk_do_flush(); - break; - } - } else { - spk_shut_up &= 0xfe; - spk_do_flush(); - } - if (spk_say_ctrl && value < NUM_CTL_LABELS) - synth_printf("%s", spk_msg_get(MSG_CTL_START + value)); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag) -{ - unsigned long flags; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (up_flag) { - spk_lastkey = 0; - spk_keydown = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return; - } - if (!synth || spk_killed) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return; - } - spk_shut_up &= 0xfe; - spk_lastkey = value; - spk_keydown++; - spk_parked &= 0xfe; - if (spk_key_echo == 2 && value >= MINECHOCHAR) - speak_char(value); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -int spk_set_key_info(const u_char *key_info, u_char *k_buffer) -{ - int i = 0, states, key_data_len; - const u_char *cp = key_info; - u_char *cp1 = k_buffer; - u_char ch, version, num_keys; - - version = *cp++; - if (version != KEY_MAP_VER) { - pr_debug("version found %d should be %d\n", - version, KEY_MAP_VER); - return -EINVAL; - } - num_keys = *cp; - states = (int)cp[1]; - key_data_len = (states + 1) * (num_keys + 1); - if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) { - pr_debug("too many key_infos (%d over %u)\n", - key_data_len + SHIFT_TBL_SIZE + 4, - (unsigned int)(sizeof(spk_key_buf))); - return -EINVAL; - } - memset(k_buffer, 0, SHIFT_TBL_SIZE); - memset(spk_our_keys, 0, sizeof(spk_our_keys)); - spk_shift_table = k_buffer; - spk_our_keys[0] = spk_shift_table; - cp1 += SHIFT_TBL_SIZE; - memcpy(cp1, cp, key_data_len + 3); - /* get num_keys, states and data */ - cp1 += 2; /* now pointing at shift states */ - for (i = 1; i <= states; i++) { - ch = *cp1++; - if (ch >= SHIFT_TBL_SIZE) { - pr_debug("(%d) not valid shift state (max_allowed = %d)\n", - ch, SHIFT_TBL_SIZE); - return -EINVAL; - } - spk_shift_table[ch] = i; - } - keymap_flags = *cp1++; - while ((ch = *cp1)) { - if (ch >= MAX_KEY) { - pr_debug("(%d), not valid key, (max_allowed = %d)\n", - ch, MAX_KEY); - return -EINVAL; - } - spk_our_keys[ch] = cp1; - cp1 += states + 1; - } - return 0; -} - -static struct var_t spk_vars[] = { - /* bell must be first to set high limit */ - {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} }, - {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} }, - {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} }, - {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} }, - {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} }, - {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} }, - {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} }, - {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} }, - {SAY_CONTROL, TOGGLE_0}, - {SAY_WORD_CTL, TOGGLE_0}, - {NO_INTERRUPT, TOGGLE_0}, - {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} }, - V_LAST_VAR -}; - -static void toggle_cursoring(struct vc_data *vc) -{ - if (cursor_track == read_all_mode) - cursor_track = prev_cursor_track; - if (++cursor_track >= CT_Max) - cursor_track = 0; - synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track)); -} - -void spk_reset_default_chars(void) -{ - int i; - - /* First, free any non-default */ - for (i = 0; i < 256; i++) { - if (spk_characters[i] && - (spk_characters[i] != spk_default_chars[i])) - kfree(spk_characters[i]); - } - - memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars)); -} - -void spk_reset_default_chartab(void) -{ - memcpy(spk_chartab, default_chartab, sizeof(default_chartab)); -} - -static const struct st_bits_data *pb_edit; - -static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key) -{ - short mask = pb_edit->mask, ch_type = spk_chartab[ch]; - - if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE) - return -1; - if (ch == SPACE) { - synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE)); - spk_special_handler = NULL; - return 1; - } - if (mask < PUNC && !(ch_type & PUNC)) - return -1; - spk_chartab[ch] ^= mask; - speak_char(ch); - synth_printf(" %s\n", - (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) : - spk_msg_get(MSG_OFF)); - return 1; -} - -/* Allocation concurrency is protected by the console semaphore */ -static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags) -{ - int vc_num; - - vc_num = vc->vc_num; - if (!speakup_console[vc_num]) { - speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]), - gfp_flags); - if (!speakup_console[vc_num]) - return -ENOMEM; - speakup_date(vc); - } else if (!spk_parked) { - speakup_date(vc); - } - - return 0; -} - -static void speakup_deallocate(struct vc_data *vc) -{ - int vc_num; - - vc_num = vc->vc_num; - kfree(speakup_console[vc_num]); - speakup_console[vc_num] = NULL; -} - -static u_char is_cursor; -static u_long old_cursor_pos, old_cursor_x, old_cursor_y; -static int cursor_con; - -static void reset_highlight_buffers(struct vc_data *); - -static int read_all_key; - -static int in_keyboard_notifier; - -static void start_read_all_timer(struct vc_data *vc, int command); - -enum { - RA_NOTHING, - RA_NEXT_SENT, - RA_PREV_LINE, - RA_NEXT_LINE, - RA_PREV_SENT, - RA_DOWN_ARROW, - RA_TIMER, - RA_FIND_NEXT_SENT, - RA_FIND_PREV_SENT, -}; - -static void kbd_fakekey2(struct vc_data *vc, int command) -{ - del_timer(&cursor_timer); - speakup_fake_down_arrow(); - start_read_all_timer(vc, command); -} - -static void read_all_doc(struct vc_data *vc) -{ - if ((vc->vc_num != fg_console) || !synth || spk_shut_up) - return; - if (!synth_supports_indexing()) - return; - if (cursor_track != read_all_mode) - prev_cursor_track = cursor_track; - cursor_track = read_all_mode; - spk_reset_index_count(0); - if (get_sentence_buf(vc, 0) == -1) { - del_timer(&cursor_timer); - if (!in_keyboard_notifier) - speakup_fake_down_arrow(); - start_read_all_timer(vc, RA_DOWN_ARROW); - } else { - say_sentence_num(0, 0); - synth_insert_next_index(0); - start_read_all_timer(vc, RA_TIMER); - } -} - -static void stop_read_all(struct vc_data *vc) -{ - del_timer(&cursor_timer); - cursor_track = prev_cursor_track; - spk_shut_up &= 0xfe; - spk_do_flush(); -} - -static void start_read_all_timer(struct vc_data *vc, int command) -{ - struct var_t *cursor_timeout; - - cursor_con = vc->vc_num; - read_all_key = command; - cursor_timeout = spk_get_var(CURSOR_TIME); - mod_timer(&cursor_timer, - jiffies + msecs_to_jiffies(cursor_timeout->u.n.value)); -} - -static void handle_cursor_read_all(struct vc_data *vc, int command) -{ - int indcount, sentcount, rv, sn; - - switch (command) { - case RA_NEXT_SENT: - /* Get Current Sentence */ - spk_get_index_count(&indcount, &sentcount); - /*printk("%d %d ", indcount, sentcount); */ - spk_reset_index_count(sentcount + 1); - if (indcount == 1) { - if (!say_sentence_num(sentcount + 1, 0)) { - kbd_fakekey2(vc, RA_FIND_NEXT_SENT); - return; - } - synth_insert_next_index(0); - } else { - sn = 0; - if (!say_sentence_num(sentcount + 1, 1)) { - sn = 1; - spk_reset_index_count(sn); - } else { - synth_insert_next_index(0); - } - if (!say_sentence_num(sn, 0)) { - kbd_fakekey2(vc, RA_FIND_NEXT_SENT); - return; - } - synth_insert_next_index(0); - } - start_read_all_timer(vc, RA_TIMER); - break; - case RA_PREV_SENT: - break; - case RA_NEXT_LINE: - read_all_doc(vc); - break; - case RA_PREV_LINE: - break; - case RA_DOWN_ARROW: - if (get_sentence_buf(vc, 0) == -1) { - kbd_fakekey2(vc, RA_DOWN_ARROW); - } else { - say_sentence_num(0, 0); - synth_insert_next_index(0); - start_read_all_timer(vc, RA_TIMER); - } - break; - case RA_FIND_NEXT_SENT: - rv = get_sentence_buf(vc, 0); - if (rv == -1) - read_all_doc(vc); - if (rv == 0) { - kbd_fakekey2(vc, RA_FIND_NEXT_SENT); - } else { - say_sentence_num(1, 0); - synth_insert_next_index(0); - start_read_all_timer(vc, RA_TIMER); - } - break; - case RA_FIND_PREV_SENT: - break; - case RA_TIMER: - spk_get_index_count(&indcount, &sentcount); - if (indcount < 2) - kbd_fakekey2(vc, RA_DOWN_ARROW); - else - start_read_all_timer(vc, RA_TIMER); - break; - } -} - -static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag) -{ - unsigned long flags; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (cursor_track == read_all_mode) { - spk_parked &= 0xfe; - if (!synth || up_flag || spk_shut_up) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return NOTIFY_STOP; - } - del_timer(&cursor_timer); - spk_shut_up &= 0xfe; - spk_do_flush(); - start_read_all_timer(vc, value + 1); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return NOTIFY_STOP; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return NOTIFY_OK; -} - -static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag) -{ - unsigned long flags; - struct var_t *cursor_timeout; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - spk_parked &= 0xfe; - if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return; - } - spk_shut_up &= 0xfe; - if (spk_no_intr) - spk_do_flush(); -/* the key press flushes if !no_inter but we want to flush on cursor - * moves regardless of no_inter state - */ - is_cursor = value + 1; - old_cursor_pos = vc->vc_pos; - old_cursor_x = vc->vc_x; - old_cursor_y = vc->vc_y; - speakup_console[vc->vc_num]->ht.cy = vc->vc_y; - cursor_con = vc->vc_num; - if (cursor_track == CT_Highlight) - reset_highlight_buffers(vc); - cursor_timeout = spk_get_var(CURSOR_TIME); - mod_timer(&cursor_timer, - jiffies + msecs_to_jiffies(cursor_timeout->u.n.value)); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len) -{ - int i, bi, hi; - int vc_num = vc->vc_num; - - bi = (vc->vc_attr & 0x70) >> 4; - hi = speakup_console[vc_num]->ht.highsize[bi]; - - i = 0; - if (speakup_console[vc_num]->ht.highsize[bi] == 0) { - speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos; - speakup_console[vc_num]->ht.rx[bi] = vc->vc_x; - speakup_console[vc_num]->ht.ry[bi] = vc->vc_y; - } - while ((hi < COLOR_BUFFER_SIZE) && (i < len)) { - if (ic[i] > 32) { - speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i]; - hi++; - } else if ((ic[i] == 32) && (hi != 0)) { - if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] != - 32) { - speakup_console[vc_num]->ht.highbuf[bi][hi] = - ic[i]; - hi++; - } - } - i++; - } - speakup_console[vc_num]->ht.highsize[bi] = hi; -} - -static void reset_highlight_buffers(struct vc_data *vc) -{ - int i; - int vc_num = vc->vc_num; - - for (i = 0; i < 8; i++) - speakup_console[vc_num]->ht.highsize[i] = 0; -} - -static int count_highlight_color(struct vc_data *vc) -{ - int i, bg; - int cc; - int vc_num = vc->vc_num; - u16 ch; - u16 *start = (u16 *)vc->vc_origin; - - for (i = 0; i < 8; i++) - speakup_console[vc_num]->ht.bgcount[i] = 0; - - for (i = 0; i < vc->vc_rows; i++) { - u16 *end = start + vc->vc_cols * 2; - u16 *ptr; - - for (ptr = start; ptr < end; ptr++) { - ch = get_attributes(vc, ptr); - bg = (ch & 0x70) >> 4; - speakup_console[vc_num]->ht.bgcount[bg]++; - } - start += vc->vc_size_row; - } - - cc = 0; - for (i = 0; i < 8; i++) - if (speakup_console[vc_num]->ht.bgcount[i] > 0) - cc++; - return cc; -} - -static int get_highlight_color(struct vc_data *vc) -{ - int i, j; - unsigned int cptr[8]; - int vc_num = vc->vc_num; - - for (i = 0; i < 8; i++) - cptr[i] = i; - - for (i = 0; i < 7; i++) - for (j = i + 1; j < 8; j++) - if (speakup_console[vc_num]->ht.bgcount[cptr[i]] > - speakup_console[vc_num]->ht.bgcount[cptr[j]]) - swap(cptr[i], cptr[j]); - - for (i = 0; i < 8; i++) - if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0) - if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0) - return cptr[i]; - return -1; -} - -static int speak_highlight(struct vc_data *vc) -{ - int hc, d; - int vc_num = vc->vc_num; - - if (count_highlight_color(vc) == 1) - return 0; - hc = get_highlight_color(vc); - if (hc != -1) { - d = vc->vc_y - speakup_console[vc_num]->ht.cy; - if ((d == 1) || (d == -1)) - if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y) - return 0; - spk_parked |= 0x01; - spk_do_flush(); - spkup_write(speakup_console[vc_num]->ht.highbuf[hc], - speakup_console[vc_num]->ht.highsize[hc]); - spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc]; - spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc]; - spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc]; - return 1; - } - return 0; -} - -static void cursor_done(struct timer_list *unused) -{ - struct vc_data *vc = vc_cons[cursor_con].d; - unsigned long flags; - - del_timer(&cursor_timer); - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (cursor_con != fg_console) { - is_cursor = 0; - goto out; - } - speakup_date(vc); - if (win_enabled) { - if (vc->vc_x >= win_left && vc->vc_x <= win_right && - vc->vc_y >= win_top && vc->vc_y <= win_bottom) { - spk_keydown = 0; - is_cursor = 0; - goto out; - } - } - if (cursor_track == read_all_mode) { - handle_cursor_read_all(vc, read_all_key); - goto out; - } - if (cursor_track == CT_Highlight) { - if (speak_highlight(vc)) { - spk_keydown = 0; - is_cursor = 0; - goto out; - } - } - if (cursor_track == CT_Window) - speakup_win_say(vc); - else if (is_cursor == 1 || is_cursor == 4) - say_line_from_to(vc, 0, vc->vc_cols, 0); - else - say_char(vc); - spk_keydown = 0; - is_cursor = 0; -out: - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -/* called by: vt_notifier_call() */ -static void speakup_bs(struct vc_data *vc) -{ - unsigned long flags; - - if (!speakup_console[vc->vc_num]) - return; - if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) - /* Speakup output, discard */ - return; - if (!spk_parked) - speakup_date(vc); - if (spk_shut_up || !synth) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return; - } - if (vc->vc_num == fg_console && spk_keydown) { - spk_keydown = 0; - if (!is_cursor) - say_char(vc); - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -/* called by: vt_notifier_call() */ -static void speakup_con_write(struct vc_data *vc, u16 *str, int len) -{ - unsigned long flags; - - if ((vc->vc_num != fg_console) || spk_shut_up || !synth) - return; - if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) - /* Speakup output, discard */ - return; - if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1)) - bleep(3); - if ((is_cursor) || (cursor_track == read_all_mode)) { - if (cursor_track == CT_Highlight) - update_color_buffer(vc, str, len); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return; - } - if (win_enabled) { - if (vc->vc_x >= win_left && vc->vc_x <= win_right && - vc->vc_y >= win_top && vc->vc_y <= win_bottom) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return; - } - } - - spkup_write(str, len); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -static void speakup_con_update(struct vc_data *vc) -{ - unsigned long flags; - - if (!speakup_console[vc->vc_num] || spk_parked) - return; - if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) - /* Speakup output, discard */ - return; - speakup_date(vc); - if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) { - synth_printf("%s", spk_str_pause); - spk_paused = true; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag) -{ - unsigned long flags; - int on_off = 2; - char *label; - - if (!synth || up_flag || spk_killed) - return; - spin_lock_irqsave(&speakup_info.spinlock, flags); - spk_shut_up &= 0xfe; - if (spk_no_intr) - spk_do_flush(); - switch (value) { - case KVAL(K_CAPS): - label = spk_msg_get(MSG_KEYNAME_CAPSLOCK); - on_off = vt_get_leds(fg_console, VC_CAPSLOCK); - break; - case KVAL(K_NUM): - label = spk_msg_get(MSG_KEYNAME_NUMLOCK); - on_off = vt_get_leds(fg_console, VC_NUMLOCK); - break; - case KVAL(K_HOLD): - label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK); - on_off = vt_get_leds(fg_console, VC_SCROLLOCK); - if (speakup_console[vc->vc_num]) - speakup_console[vc->vc_num]->tty_stopped = on_off; - break; - default: - spk_parked &= 0xfe; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return; - } - if (on_off < 2) - synth_printf("%s %s\n", - label, spk_msg_get(MSG_STATUS_START + on_off)); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); -} - -static int inc_dec_var(u_char value) -{ - struct st_var_header *p_header; - struct var_t *var_data; - char num_buf[32]; - char *cp = num_buf; - char *pn; - int var_id = (int)value - VAR_START; - int how = (var_id & 1) ? E_INC : E_DEC; - - var_id = var_id / 2 + FIRST_SET_VAR; - p_header = spk_get_var_header(var_id); - if (!p_header) - return -1; - if (p_header->var_type != VAR_NUM) - return -1; - var_data = p_header->data; - if (spk_set_num_var(1, p_header, how) != 0) - return -1; - if (!spk_close_press) { - for (pn = p_header->name; *pn; pn++) { - if (*pn == '_') - *cp = SPACE; - else - *cp++ = *pn; - } - } - snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ", - var_data->u.n.value); - synth_printf("%s", num_buf); - return 0; -} - -static void speakup_win_set(struct vc_data *vc) -{ - char info[40]; - - if (win_start > 1) { - synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET)); - return; - } - if (spk_x < win_left || spk_y < win_top) { - synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START)); - return; - } - if (win_start && spk_x == win_left && spk_y == win_top) { - win_left = 0; - win_right = vc->vc_cols - 1; - win_bottom = spk_y; - snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE), - (int)win_top + 1); - } else { - if (!win_start) { - win_top = spk_y; - win_left = spk_x; - } else { - win_bottom = spk_y; - win_right = spk_x; - } - snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY), - (win_start) ? - spk_msg_get(MSG_END) : spk_msg_get(MSG_START), - (int)spk_y + 1, (int)spk_x + 1); - } - synth_printf("%s\n", info); - win_start++; -} - -static void speakup_win_clear(struct vc_data *vc) -{ - win_top = 0; - win_bottom = 0; - win_left = 0; - win_right = 0; - win_start = 0; - synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED)); -} - -static void speakup_win_enable(struct vc_data *vc) -{ - if (win_start < 2) { - synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW)); - return; - } - win_enabled ^= 1; - if (win_enabled) - synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED)); - else - synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED)); -} - -static void speakup_bits(struct vc_data *vc) -{ - int val = this_speakup_key - (FIRST_EDIT_BITS - 1); - - if (spk_special_handler || val < 1 || val > 6) { - synth_printf("%s\n", spk_msg_get(MSG_ERROR)); - return; - } - pb_edit = &spk_punc_info[val]; - synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name); - spk_special_handler = edit_bits; -} - -static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key) -{ - static u_char goto_buf[8]; - static int num; - int maxlen; - char *cp; - u16 wch; - - if (type == KT_SPKUP && ch == SPEAKUP_GOTO) - goto do_goto; - if (type == KT_LATIN && ch == '\n') - goto do_goto; - if (type != 0) - goto oops; - if (ch == 8) { - u16 wch; - - if (num == 0) - return -1; - wch = goto_buf[--num]; - goto_buf[num] = '\0'; - spkup_write(&wch, 1); - return 1; - } - if (ch < '+' || ch > 'y') - goto oops; - wch = ch; - goto_buf[num++] = ch; - goto_buf[num] = '\0'; - spkup_write(&wch, 1); - maxlen = (*goto_buf >= '0') ? 3 : 4; - if ((ch == '+' || ch == '-') && num == 1) - return 1; - if (ch >= '0' && ch <= '9' && num < maxlen) - return 1; - if (num < maxlen - 1 || num > maxlen) - goto oops; - if (ch < 'x' || ch > 'y') { -oops: - if (!spk_killed) - synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED)); - goto_buf[num = 0] = '\0'; - spk_special_handler = NULL; - return 1; - } - - /* Do not replace with kstrtoul: here we need cp to be updated */ - goto_pos = simple_strtoul(goto_buf, &cp, 10); - - if (*cp == 'x') { - if (*goto_buf < '0') - goto_pos += spk_x; - else if (goto_pos > 0) - goto_pos--; - - if (goto_pos >= vc->vc_cols) - goto_pos = vc->vc_cols - 1; - goto_x = 1; - } else { - if (*goto_buf < '0') - goto_pos += spk_y; - else if (goto_pos > 0) - goto_pos--; - - if (goto_pos >= vc->vc_rows) - goto_pos = vc->vc_rows - 1; - goto_x = 0; - } - goto_buf[num = 0] = '\0'; -do_goto: - spk_special_handler = NULL; - spk_parked |= 0x01; - if (goto_x) { - spk_pos -= spk_x * 2; - spk_x = goto_pos; - spk_pos += goto_pos * 2; - say_word(vc); - } else { - spk_y = goto_pos; - spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row); - say_line(vc); - } - return 1; -} - -static void speakup_goto(struct vc_data *vc) -{ - if (spk_special_handler) { - synth_printf("%s\n", spk_msg_get(MSG_ERROR)); - return; - } - synth_printf("%s\n", spk_msg_get(MSG_GOTO)); - spk_special_handler = handle_goto; -} - -static void speakup_help(struct vc_data *vc) -{ - spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0); -} - -static void do_nothing(struct vc_data *vc) -{ - return; /* flush done in do_spkup */ -} - -static u_char key_speakup, spk_key_locked; - -static void speakup_lock(struct vc_data *vc) -{ - if (!spk_key_locked) { - spk_key_locked = 16; - key_speakup = 16; - } else { - spk_key_locked = 0; - key_speakup = 0; - } -} - -typedef void (*spkup_hand) (struct vc_data *); -static spkup_hand spkup_handler[] = { - /* must be ordered same as defines in speakup.h */ - do_nothing, speakup_goto, speech_kill, speakup_shut_up, - speakup_cut, speakup_paste, say_first_char, say_last_char, - say_char, say_prev_char, say_next_char, - say_word, say_prev_word, say_next_word, - say_line, say_prev_line, say_next_line, - top_edge, bottom_edge, left_edge, right_edge, - spell_word, spell_word, say_screen, - say_position, say_attributes, - speakup_off, speakup_parked, say_line, /* this is for indent */ - say_from_top, say_to_bottom, - say_from_left, say_to_right, - say_char_num, speakup_bits, speakup_bits, say_phonetic_char, - speakup_bits, speakup_bits, speakup_bits, - speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say, - speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL -}; - -static void do_spkup(struct vc_data *vc, u_char value) -{ - if (spk_killed && value != SPEECH_KILL) - return; - spk_keydown = 0; - spk_lastkey = 0; - spk_shut_up &= 0xfe; - this_speakup_key = value; - if (value < SPKUP_MAX_FUNC && spkup_handler[value]) { - spk_do_flush(); - (*spkup_handler[value]) (vc); - } else { - if (inc_dec_var(value) < 0) - bleep(9); - } -} - -static const char *pad_chars = "0123456789+-*/\015,.?()"; - -static int -speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym, - int up_flag) -{ - unsigned long flags; - int kh; - u_char *key_info; - u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0; - u_char shift_info, offset; - int ret = 0; - - if (!synth) - return 0; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - tty = vc->port.tty; - if (type >= 0xf0) - type -= 0xf0; - if (type == KT_PAD && - (vt_get_leds(fg_console, VC_NUMLOCK))) { - if (up_flag) { - spk_keydown = 0; - goto out; - } - value = pad_chars[value]; - spk_lastkey = value; - spk_keydown++; - spk_parked &= 0xfe; - goto no_map; - } - if (keycode >= MAX_KEY) - goto no_map; - key_info = spk_our_keys[keycode]; - if (!key_info) - goto no_map; - /* Check valid read all mode keys */ - if ((cursor_track == read_all_mode) && (!up_flag)) { - switch (value) { - case KVAL(K_DOWN): - case KVAL(K_UP): - case KVAL(K_LEFT): - case KVAL(K_RIGHT): - case KVAL(K_PGUP): - case KVAL(K_PGDN): - break; - default: - stop_read_all(vc); - break; - } - } - shift_info = (shift_state & 0x0f) + key_speakup; - offset = spk_shift_table[shift_info]; - if (offset) { - new_key = key_info[offset]; - if (new_key) { - ret = 1; - if (new_key == SPK_KEY) { - if (!spk_key_locked) - key_speakup = (up_flag) ? 0 : 16; - if (up_flag || spk_killed) - goto out; - spk_shut_up &= 0xfe; - spk_do_flush(); - goto out; - } - if (up_flag) - goto out; - if (last_keycode == keycode && - time_after(last_spk_jiffy + MAX_DELAY, jiffies)) { - spk_close_press = 1; - offset = spk_shift_table[shift_info + 32]; - /* double press? */ - if (offset && key_info[offset]) - new_key = key_info[offset]; - } - last_keycode = keycode; - last_spk_jiffy = jiffies; - type = KT_SPKUP; - value = new_key; - } - } -no_map: - if (type == KT_SPKUP && !spk_special_handler) { - do_spkup(vc, new_key); - spk_close_press = 0; - ret = 1; - goto out; - } - if (up_flag || spk_killed || type == KT_SHIFT) - goto out; - spk_shut_up &= 0xfe; - kh = (value == KVAL(K_DOWN)) || - (value == KVAL(K_UP)) || - (value == KVAL(K_LEFT)) || - (value == KVAL(K_RIGHT)); - if ((cursor_track != read_all_mode) || !kh) - if (!spk_no_intr) - spk_do_flush(); - if (spk_special_handler) { - if (type == KT_SPEC && value == 1) { - value = '\n'; - type = KT_LATIN; - } else if (type == KT_LETTER) { - type = KT_LATIN; - } else if (value == 0x7f) { - value = 8; /* make del = backspace */ - } - ret = (*spk_special_handler) (vc, type, value, keycode); - spk_close_press = 0; - if (ret < 0) - bleep(9); - goto out; - } - last_keycode = 0; -out: - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return ret; -} - -static int keyboard_notifier_call(struct notifier_block *nb, - unsigned long code, void *_param) -{ - struct keyboard_notifier_param *param = _param; - struct vc_data *vc = param->vc; - int up = !param->down; - int ret = NOTIFY_OK; - static int keycode; /* to hold the current keycode */ - - in_keyboard_notifier = 1; - - if (vc->vc_mode == KD_GRAPHICS) - goto out; - - /* - * First, determine whether we are handling a fake keypress on - * the current processor. If we are, then return NOTIFY_OK, - * to pass the keystroke up the chain. This prevents us from - * trying to take the Speakup lock while it is held by the - * processor on which the simulated keystroke was generated. - * Also, the simulated keystrokes should be ignored by Speakup. - */ - - if (speakup_fake_key_pressed()) - goto out; - - switch (code) { - case KBD_KEYCODE: - /* speakup requires keycode and keysym currently */ - keycode = param->value; - break; - case KBD_UNBOUND_KEYCODE: - /* not used yet */ - break; - case KBD_UNICODE: - /* not used yet */ - break; - case KBD_KEYSYM: - if (speakup_key(vc, param->shift, keycode, param->value, up)) - ret = NOTIFY_STOP; - else if (KTYP(param->value) == KT_CUR) - ret = pre_handle_cursor(vc, KVAL(param->value), up); - break; - case KBD_POST_KEYSYM:{ - unsigned char type = KTYP(param->value) - 0xf0; - unsigned char val = KVAL(param->value); - - switch (type) { - case KT_SHIFT: - do_handle_shift(vc, val, up); - break; - case KT_LATIN: - case KT_LETTER: - do_handle_latin(vc, val, up); - break; - case KT_CUR: - do_handle_cursor(vc, val, up); - break; - case KT_SPEC: - do_handle_spec(vc, val, up); - break; - } - break; - } - } -out: - in_keyboard_notifier = 0; - return ret; -} - -static int vt_notifier_call(struct notifier_block *nb, - unsigned long code, void *_param) -{ - struct vt_notifier_param *param = _param; - struct vc_data *vc = param->vc; - - switch (code) { - case VT_ALLOCATE: - if (vc->vc_mode == KD_TEXT) - speakup_allocate(vc, GFP_ATOMIC); - break; - case VT_DEALLOCATE: - speakup_deallocate(vc); - break; - case VT_WRITE: - if (param->c == '\b') { - speakup_bs(vc); - } else { - u16 d = param->c; - - speakup_con_write(vc, &d, 1); - } - break; - case VT_UPDATE: - speakup_con_update(vc); - break; - } - return NOTIFY_OK; -} - -/* called by: module_exit() */ -static void __exit speakup_exit(void) -{ - int i; - - unregister_keyboard_notifier(&keyboard_notifier_block); - unregister_vt_notifier(&vt_notifier_block); - speakup_unregister_devsynth(); - speakup_cancel_selection(); - speakup_cancel_paste(); - del_timer_sync(&cursor_timer); - kthread_stop(speakup_task); - speakup_task = NULL; - mutex_lock(&spk_mutex); - synth_release(); - mutex_unlock(&spk_mutex); - spk_ttyio_unregister_ldisc(); - - speakup_kobj_exit(); - - for (i = 0; i < MAX_NR_CONSOLES; i++) - kfree(speakup_console[i]); - - speakup_remove_virtual_keyboard(); - - for (i = 0; i < MAXVARS; i++) - speakup_unregister_var(i); - - for (i = 0; i < 256; i++) { - if (spk_characters[i] != spk_default_chars[i]) - kfree(spk_characters[i]); - } - - spk_free_user_msgs(); -} - -/* call by: module_init() */ -static int __init speakup_init(void) -{ - int i; - long err = 0; - struct vc_data *vc = vc_cons[fg_console].d; - struct var_t *var; - - /* These first few initializations cannot fail. */ - spk_initialize_msgs(); /* Initialize arrays for i18n. */ - spk_reset_default_chars(); - spk_reset_default_chartab(); - spk_strlwr(synth_name); - spk_vars[0].u.n.high = vc->vc_cols; - for (var = spk_vars; var->var_id != MAXVARS; var++) - speakup_register_var(var); - for (var = synth_time_vars; - (var->var_id >= 0) && (var->var_id < MAXVARS); var++) - speakup_register_var(var); - for (i = 1; spk_punc_info[i].mask != 0; i++) - spk_set_mask_bits(NULL, i, 2); - - spk_set_key_info(spk_key_defaults, spk_key_buf); - - /* From here on out, initializations can fail. */ - err = speakup_add_virtual_keyboard(); - if (err) - goto error_virtkeyboard; - - for (i = 0; i < MAX_NR_CONSOLES; i++) - if (vc_cons[i].d) { - err = speakup_allocate(vc_cons[i].d, GFP_KERNEL); - if (err) - goto error_kobjects; - } - - if (spk_quiet_boot) - spk_shut_up |= 0x01; - - err = speakup_kobj_init(); - if (err) - goto error_kobjects; - - spk_ttyio_register_ldisc(); - synth_init(synth_name); - speakup_register_devsynth(); - /* - * register_devsynth might fail, but this error is not fatal. - * /dev/synth is an extra feature; the rest of Speakup - * will work fine without it. - */ - - err = register_keyboard_notifier(&keyboard_notifier_block); - if (err) - goto error_kbdnotifier; - err = register_vt_notifier(&vt_notifier_block); - if (err) - goto error_vtnotifier; - - speakup_task = kthread_create(speakup_thread, NULL, "speakup"); - - if (IS_ERR(speakup_task)) { - err = PTR_ERR(speakup_task); - goto error_task; - } - - set_user_nice(speakup_task, 10); - wake_up_process(speakup_task); - - pr_info("speakup %s: initialized\n", SPEAKUP_VERSION); - pr_info("synth name on entry is: %s\n", synth_name); - goto out; - -error_task: - unregister_vt_notifier(&vt_notifier_block); - -error_vtnotifier: - unregister_keyboard_notifier(&keyboard_notifier_block); - del_timer(&cursor_timer); - -error_kbdnotifier: - speakup_unregister_devsynth(); - mutex_lock(&spk_mutex); - synth_release(); - mutex_unlock(&spk_mutex); - speakup_kobj_exit(); - -error_kobjects: - for (i = 0; i < MAX_NR_CONSOLES; i++) - kfree(speakup_console[i]); - - speakup_remove_virtual_keyboard(); - -error_virtkeyboard: - for (i = 0; i < MAXVARS; i++) - speakup_unregister_var(i); - - for (i = 0; i < 256; i++) { - if (spk_characters[i] != spk_default_chars[i]) - kfree(spk_characters[i]); - } - - spk_free_user_msgs(); - -out: - return err; -} - -module_init(speakup_init); -module_exit(speakup_exit); diff --git a/drivers/staging/speakup/selection.c b/drivers/staging/speakup/selection.c deleted file mode 100644 index 032f3264fba1..000000000000 --- a/drivers/staging/speakup/selection.c +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include /* for kmalloc */ -#include -#include -#include -#include /* for dev_warn */ -#include -#include -#include -#include -#include -#include - -#include "speakup.h" - -unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ -struct vc_data *spk_sel_cons; - -struct speakup_selection_work { - struct work_struct work; - struct tiocl_selection sel; - struct tty_struct *tty; -}; - -void speakup_clear_selection(void) -{ - console_lock(); - clear_selection(); - console_unlock(); -} - -static void __speakup_set_selection(struct work_struct *work) -{ - struct speakup_selection_work *ssw = - container_of(work, struct speakup_selection_work, work); - - struct tty_struct *tty; - struct tiocl_selection sel; - - sel = ssw->sel; - - /* this ensures we copy sel before releasing the lock below */ - rmb(); - - /* release the lock by setting tty of the struct to NULL */ - tty = xchg(&ssw->tty, NULL); - - if (spk_sel_cons != vc_cons[fg_console].d) { - spk_sel_cons = vc_cons[fg_console].d; - pr_warn("Selection: mark console not the same as cut\n"); - goto unref; - } - - set_selection_kernel(&sel, tty); - -unref: - tty_kref_put(tty); -} - -static struct speakup_selection_work speakup_sel_work = { - .work = __WORK_INITIALIZER(speakup_sel_work.work, - __speakup_set_selection) -}; - -int speakup_set_selection(struct tty_struct *tty) -{ - /* we get kref here first in order to avoid a subtle race when - * cancelling selection work. getting kref first establishes the - * invariant that if speakup_sel_work.tty is not NULL when - * speakup_cancel_selection() is called, it must be the case that a put - * kref is pending. - */ - tty_kref_get(tty); - if (cmpxchg(&speakup_sel_work.tty, NULL, tty)) { - tty_kref_put(tty); - return -EBUSY; - } - /* now we have the 'lock' by setting tty member of - * speakup_selection_work. wmb() ensures that writes to - * speakup_sel_work don't happen before cmpxchg() above. - */ - wmb(); - - speakup_sel_work.sel.xs = spk_xs + 1; - speakup_sel_work.sel.ys = spk_ys + 1; - speakup_sel_work.sel.xe = spk_xe + 1; - speakup_sel_work.sel.ye = spk_ye + 1; - speakup_sel_work.sel.sel_mode = TIOCL_SELCHAR; - - schedule_work_on(WORK_CPU_UNBOUND, &speakup_sel_work.work); - - return 0; -} - -void speakup_cancel_selection(void) -{ - struct tty_struct *tty; - - cancel_work_sync(&speakup_sel_work.work); - /* setting to null so that if work fails to run and we cancel it, - * we can run it again without getting EBUSY forever from there on. - * we need to use xchg here to avoid race with speakup_set_selection() - */ - tty = xchg(&speakup_sel_work.tty, NULL); - if (tty) - tty_kref_put(tty); -} - -static void __speakup_paste_selection(struct work_struct *work) -{ - struct speakup_selection_work *ssw = - container_of(work, struct speakup_selection_work, work); - struct tty_struct *tty = xchg(&ssw->tty, NULL); - - paste_selection(tty); - tty_kref_put(tty); -} - -static struct speakup_selection_work speakup_paste_work = { - .work = __WORK_INITIALIZER(speakup_paste_work.work, - __speakup_paste_selection) -}; - -int speakup_paste_selection(struct tty_struct *tty) -{ - tty_kref_get(tty); - if (cmpxchg(&speakup_paste_work.tty, NULL, tty)) { - tty_kref_put(tty); - return -EBUSY; - } - - schedule_work_on(WORK_CPU_UNBOUND, &speakup_paste_work.work); - return 0; -} - -void speakup_cancel_paste(void) -{ - struct tty_struct *tty; - - cancel_work_sync(&speakup_paste_work.work); - tty = xchg(&speakup_paste_work.tty, NULL); - if (tty) - tty_kref_put(tty); -} diff --git a/drivers/staging/speakup/serialio.c b/drivers/staging/speakup/serialio.c deleted file mode 100644 index 177a2988641c..000000000000 --- a/drivers/staging/speakup/serialio.c +++ /dev/null @@ -1,316 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -#include "spk_types.h" -#include "speakup.h" -#include "spk_priv.h" -#include "serialio.h" - -#include -/* WARNING: Do not change this to without testing that - * SERIAL_PORT_DFNS does get defined to the appropriate value. - */ -#include - -#ifndef SERIAL_PORT_DFNS -#define SERIAL_PORT_DFNS -#endif - -static void start_serial_interrupt(int irq); - -static const struct old_serial_port rs_table[] = { - SERIAL_PORT_DFNS -}; - -static const struct old_serial_port *serstate; -static int timeouts; - -static int spk_serial_out(struct spk_synth *in_synth, const char ch); -static void spk_serial_send_xchar(char ch); -static void spk_serial_tiocmset(unsigned int set, unsigned int clear); -static unsigned char spk_serial_in(void); -static unsigned char spk_serial_in_nowait(void); -static void spk_serial_flush_buffer(void); - -struct spk_io_ops spk_serial_io_ops = { - .synth_out = spk_serial_out, - .send_xchar = spk_serial_send_xchar, - .tiocmset = spk_serial_tiocmset, - .synth_in = spk_serial_in, - .synth_in_nowait = spk_serial_in_nowait, - .flush_buffer = spk_serial_flush_buffer, -}; -EXPORT_SYMBOL_GPL(spk_serial_io_ops); - -const struct old_serial_port *spk_serial_init(int index) -{ - int baud = 9600, quot = 0; - unsigned int cval = 0; - int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8; - const struct old_serial_port *ser; - int err; - - if (index >= ARRAY_SIZE(rs_table)) { - pr_info("no port info for ttyS%d\n", index); - return NULL; - } - ser = rs_table + index; - - /* Divisor, bytesize and parity */ - quot = ser->baud_base / baud; - cval = cflag & (CSIZE | CSTOPB); -#if defined(__powerpc__) || defined(__alpha__) - cval >>= 8; -#else /* !__powerpc__ && !__alpha__ */ - cval >>= 4; -#endif /* !__powerpc__ && !__alpha__ */ - if (cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(cflag & PARODD)) - cval |= UART_LCR_EPAR; - if (synth_request_region(ser->port, 8)) { - /* try to take it back. */ - pr_info("Ports not available, trying to steal them\n"); - __release_region(&ioport_resource, ser->port, 8); - err = synth_request_region(ser->port, 8); - if (err) { - pr_warn("Unable to allocate port at %x, errno %i", - ser->port, err); - return NULL; - } - } - - /* Disable UART interrupts, set DTR and RTS high - * and set speed. - */ - outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ - outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ - outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ - outb(cval, ser->port + UART_LCR); /* reset DLAB */ - - /* Turn off Interrupts */ - outb(0, ser->port + UART_IER); - outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); - - /* If we read 0xff from the LSR, there is no UART here. */ - if (inb(ser->port + UART_LSR) == 0xff) { - synth_release_region(ser->port, 8); - serstate = NULL; - return NULL; - } - - mdelay(1); - speakup_info.port_tts = ser->port; - serstate = ser; - - start_serial_interrupt(ser->irq); - - return ser; -} - -static irqreturn_t synth_readbuf_handler(int irq, void *dev_id) -{ - unsigned long flags; - int c; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) { - c = inb_p(speakup_info.port_tts + UART_RX); - synth->read_buff_add((u_char)c); - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return IRQ_HANDLED; -} - -static void start_serial_interrupt(int irq) -{ - int rv; - - if (!synth->read_buff_add) - return; - - rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED, - "serial", (void *)synth_readbuf_handler); - - if (rv) - pr_err("Unable to request Speakup serial I R Q\n"); - /* Set MCR */ - outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, - speakup_info.port_tts + UART_MCR); - /* Turn on Interrupts */ - outb(UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI, - speakup_info.port_tts + UART_IER); - inb(speakup_info.port_tts + UART_LSR); - inb(speakup_info.port_tts + UART_RX); - inb(speakup_info.port_tts + UART_IIR); - inb(speakup_info.port_tts + UART_MSR); - outb(1, speakup_info.port_tts + UART_FCR); /* Turn FIFO On */ -} - -static void spk_serial_send_xchar(char ch) -{ - int timeout = SPK_XMITR_TIMEOUT; - - while (spk_serial_tx_busy()) { - if (!--timeout) - break; - udelay(1); - } - outb(ch, speakup_info.port_tts); -} - -static void spk_serial_tiocmset(unsigned int set, unsigned int clear) -{ - int old = inb(speakup_info.port_tts + UART_MCR); - - outb((old & ~clear) | set, speakup_info.port_tts + UART_MCR); -} - -int spk_serial_synth_probe(struct spk_synth *synth) -{ - const struct old_serial_port *ser; - int failed = 0; - - if ((synth->ser >= SPK_LO_TTY) && (synth->ser <= SPK_HI_TTY)) { - ser = spk_serial_init(synth->ser); - if (!ser) { - failed = -1; - } else { - outb_p(0, ser->port); - mdelay(1); - outb_p('\r', ser->port); - } - } else { - failed = -1; - pr_warn("ttyS%i is an invalid port\n", synth->ser); - } - if (failed) { - pr_info("%s: not found\n", synth->long_name); - return -ENODEV; - } - pr_info("%s: ttyS%i, Driver Version %s\n", - synth->long_name, synth->ser, synth->version); - synth->alive = 1; - return 0; -} -EXPORT_SYMBOL_GPL(spk_serial_synth_probe); - -void spk_stop_serial_interrupt(void) -{ - if (speakup_info.port_tts == 0) - return; - - if (!synth->read_buff_add) - return; - - /* Turn off interrupts */ - outb(0, speakup_info.port_tts + UART_IER); - /* Free IRQ */ - free_irq(serstate->irq, (void *)synth_readbuf_handler); -} -EXPORT_SYMBOL_GPL(spk_stop_serial_interrupt); - -int spk_wait_for_xmitr(struct spk_synth *in_synth) -{ - int tmout = SPK_XMITR_TIMEOUT; - - if ((in_synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) { - pr_warn("%s: too many timeouts, deactivating speakup\n", - in_synth->long_name); - in_synth->alive = 0; - /* No synth any more, so nobody will restart TTYs, and we thus - * need to do it ourselves. Now that there is no synth we can - * let application flood anyway - */ - speakup_start_ttys(); - timeouts = 0; - return 0; - } - while (spk_serial_tx_busy()) { - if (--tmout == 0) { - pr_warn("%s: timed out (tx busy)\n", - in_synth->long_name); - timeouts++; - return 0; - } - udelay(1); - } - tmout = SPK_CTS_TIMEOUT; - while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) { - /* CTS */ - if (--tmout == 0) { - timeouts++; - return 0; - } - udelay(1); - } - timeouts = 0; - return 1; -} - -static unsigned char spk_serial_in(void) -{ - int tmout = SPK_SERIAL_TIMEOUT; - - while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) { - if (--tmout == 0) { - pr_warn("time out while waiting for input.\n"); - return 0xff; - } - udelay(1); - } - return inb_p(speakup_info.port_tts + UART_RX); -} - -static unsigned char spk_serial_in_nowait(void) -{ - unsigned char lsr; - - lsr = inb_p(speakup_info.port_tts + UART_LSR); - if (!(lsr & UART_LSR_DR)) - return 0; - return inb_p(speakup_info.port_tts + UART_RX); -} - -static void spk_serial_flush_buffer(void) -{ - /* TODO: flush the UART 16550 buffer */ -} - -static int spk_serial_out(struct spk_synth *in_synth, const char ch) -{ - if (in_synth->alive && spk_wait_for_xmitr(in_synth)) { - outb_p(ch, speakup_info.port_tts); - return 1; - } - return 0; -} - -const char *spk_serial_synth_immediate(struct spk_synth *synth, - const char *buff) -{ - u_char ch; - - while ((ch = *buff)) { - if (ch == '\n') - ch = synth->procspeech; - if (spk_wait_for_xmitr(synth)) - outb(ch, speakup_info.port_tts); - else - return buff; - buff++; - } - return NULL; -} -EXPORT_SYMBOL_GPL(spk_serial_synth_immediate); - -void spk_serial_release(void) -{ - spk_stop_serial_interrupt(); - if (speakup_info.port_tts == 0) - return; - synth_release_region(speakup_info.port_tts, 8); - speakup_info.port_tts = 0; -} -EXPORT_SYMBOL_GPL(spk_serial_release); diff --git a/drivers/staging/speakup/serialio.h b/drivers/staging/speakup/serialio.h deleted file mode 100644 index 6f8f86f161bb..000000000000 --- a/drivers/staging/speakup/serialio.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _SPEAKUP_SERIAL_H -#define _SPEAKUP_SERIAL_H - -#include /* for rs_table, serial constants */ -#include /* for more serial constants */ -#include - -#include "spk_priv.h" - -/* - * this is cut&paste from 8250.h. Get rid of the structure, the definitions - * and this whole broken driver. - */ -struct old_serial_port { - unsigned int uart; /* unused */ - unsigned int baud_base; - unsigned int port; - unsigned int irq; - upf_t flags; /* unused */ -}; - -/* countdown values for serial timeouts in us */ -#define SPK_SERIAL_TIMEOUT SPK_SYNTH_TIMEOUT -/* countdown values transmitter/dsr timeouts in us */ -#define SPK_XMITR_TIMEOUT 100000 -/* countdown values cts timeouts in us */ -#define SPK_CTS_TIMEOUT 100000 -/* check ttyS0 ... ttyS3 */ -#define SPK_LO_TTY 0 -#define SPK_HI_TTY 3 -/* # of timeouts permitted before disable */ -#define NUM_DISABLE_TIMEOUTS 3 -/* buffer timeout in ms */ -#define SPK_TIMEOUT 100 -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -#define spk_serial_tx_busy() \ - ((inb(speakup_info.port_tts + UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY) - -#endif diff --git a/drivers/staging/speakup/speakup.h b/drivers/staging/speakup/speakup.h deleted file mode 100644 index 74fe49c2c511..000000000000 --- a/drivers/staging/speakup/speakup.h +++ /dev/null @@ -1,121 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _SPEAKUP_H -#define _SPEAKUP_H - -#include "spk_types.h" -#include "i18n.h" - -#define SPEAKUP_VERSION "3.1.6" -#define KEY_MAP_VER 119 -#define SHIFT_TBL_SIZE 64 -#define MAX_DESC_LEN 72 - -#define TOGGLE_0 .u.n = {NULL, 0, 0, 1, 0, 0, NULL } -#define TOGGLE_1 .u.n = {NULL, 1, 0, 1, 0, 0, NULL } -#define MAXVARLEN 15 - -#define SYNTH_OK 0x0001 -#define B_ALPHA 0x0002 -#define ALPHA 0x0003 -#define B_CAP 0x0004 -#define A_CAP 0x0007 -#define B_NUM 0x0008 -#define NUM 0x0009 -#define ALPHANUM (B_ALPHA | B_NUM) -#define SOME 0x0010 -#define MOST 0x0020 -#define PUNC 0x0040 -#define A_PUNC 0x0041 -#define B_WDLM 0x0080 -#define WDLM 0x0081 -#define B_EXNUM 0x0100 -#define CH_RPT 0x0200 -#define B_CTL 0x0400 -#define A_CTL (B_CTL + SYNTH_OK) -#define B_SYM 0x0800 -#define B_CAPSYM (B_CAP | B_SYM) - -/* FIXME: u16 */ -#define IS_WDLM(x) (spk_chartab[((u_char)x)] & B_WDLM) -#define IS_CHAR(x, type) (spk_chartab[((u_char)x)] & type) -#define IS_TYPE(x, type) ((spk_chartab[((u_char)x)] & type) == type) - -int speakup_thread(void *data); -void spk_reset_default_chars(void); -void spk_reset_default_chartab(void); -void synth_start(void); -void synth_insert_next_index(int sent_num); -void spk_reset_index_count(int sc); -void spk_get_index_count(int *linecount, int *sentcount); -int spk_set_key_info(const u_char *key_info, u_char *k_buffer); -char *spk_strlwr(char *s); -char *spk_s2uchar(char *start, char *dest); -int speakup_kobj_init(void); -void speakup_kobj_exit(void); -int spk_chartab_get_value(char *keyword); -void speakup_register_var(struct var_t *var); -void speakup_unregister_var(enum var_id_t var_id); -struct st_var_header *spk_get_var_header(enum var_id_t var_id); -struct st_var_header *spk_var_header_by_name(const char *name); -struct punc_var_t *spk_get_punc_var(enum var_id_t var_id); -int spk_set_num_var(int val, struct st_var_header *var, int how); -int spk_set_string_var(const char *page, struct st_var_header *var, int len); -int spk_set_mask_bits(const char *input, const int which, const int how); -extern special_func spk_special_handler; -int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key); -int synth_init(char *name); -void synth_release(void); - -void spk_do_flush(void); -void speakup_start_ttys(void); -void synth_buffer_add(u16 ch); -void synth_buffer_clear(void); -void speakup_clear_selection(void); -int speakup_set_selection(struct tty_struct *tty); -void speakup_cancel_selection(void); -int speakup_paste_selection(struct tty_struct *tty); -void speakup_cancel_paste(void); -void speakup_register_devsynth(void); -void speakup_unregister_devsynth(void); -void synth_write(const char *buf, size_t count); -int synth_supports_indexing(void); - -extern struct vc_data *spk_sel_cons; -extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ - -extern wait_queue_head_t speakup_event; -extern struct kobject *speakup_kobj; -extern struct task_struct *speakup_task; -extern const u_char spk_key_defaults[]; - -/* Protect speakup synthesizer list */ -extern struct mutex spk_mutex; -extern struct st_spk_t *speakup_console[]; -extern struct spk_synth *synth; -extern char spk_pitch_buff[]; -extern u_char *spk_our_keys[]; -extern short spk_punc_masks[]; -extern char spk_str_caps_start[], spk_str_caps_stop[], spk_str_pause[]; -extern bool spk_paused; -extern const struct st_bits_data spk_punc_info[]; -extern u_char spk_key_buf[600]; -extern char *spk_characters[]; -extern char *spk_default_chars[]; -extern u_short spk_chartab[]; -extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level; -extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps; -extern int spk_bleep_time, spk_bell_pos; -extern int spk_spell_delay, spk_key_echo; -extern short spk_punc_mask; -extern short spk_pitch_shift, synth_flags; -extern bool spk_quiet_boot; -extern char *synth_name; -extern struct bleep spk_unprocessed_sound; - -/* Prototypes from fakekey.c. */ -int speakup_add_virtual_keyboard(void); -void speakup_remove_virtual_keyboard(void); -void speakup_fake_down_arrow(void); -bool speakup_fake_key_pressed(void); - -#endif diff --git a/drivers/staging/speakup/speakup_acnt.h b/drivers/staging/speakup/speakup_acnt.h deleted file mode 100644 index cffa938ae580..000000000000 --- a/drivers/staging/speakup/speakup_acnt.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* speakup_acntpc.h - header file for speakups Accent-PC driver. */ - -#define SYNTH_IO_EXTENT 0x02 - -#define SYNTH_CLEAR 0x18 /* stops speech */ - - /* Port Status Flags */ -#define SYNTH_READABLE 0x01 /* mask for bit which is nonzero if a - * byte can be read from the data port - */ -#define SYNTH_WRITABLE 0x02 /* mask for RDY bit, which when set to - * 1, indicates the data port is ready - * to accept a byte of data. - */ -#define SYNTH_QUIET 'S' /* synth is not speaking */ -#define SYNTH_FULL 'F' /* synth is full. */ -#define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */ -#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 deleted file mode 100644 index c94328a5bd4a..000000000000 --- a/drivers/staging/speakup/speakup_acntpc.c +++ /dev/null @@ -1,319 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * this code is specificly written as a driver for the speakup screenreview - * package and is not a general device driver. - * This driver is for the Aicom Acent PC internal synthesizer. - */ - -#include -#include -#include -#include - -#include "spk_priv.h" -#include "serialio.h" -#include "speakup.h" -#include "speakup_acnt.h" /* local header file for Accent values */ - -#define DRV_VERSION "2.10" -#define PROCSPEECH '\r' - -static int synth_probe(struct spk_synth *synth); -static void accent_release(void); -static const char *synth_immediate(struct spk_synth *synth, const char *buf); -static void do_catch_up(struct spk_synth *synth); -static void synth_flush(struct spk_synth *synth); - -static int synth_port_control; -static int port_forced; -static unsigned int synth_portlist[] = { 0x2a8, 0 }; - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\033P8" } }, - { CAPS_STOP, .u.s = {"\033P5" } }, - { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } }, - { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } }, - { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } }, - { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/acntpc. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_acntpc = { - .name = "acntpc", - .version = DRV_VERSION, - .long_name = "Accent PC", - .init = "\033=X \033Oi\033T2\033=M\033N1\n", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 1000, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_serial_io_ops, - .probe = synth_probe, - .release = accent_release, - .synth_immediate = synth_immediate, - .catch_up = do_catch_up, - .flush = synth_flush, - .is_alive = spk_synth_is_alive_nop, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "acntpc", - }, -}; - -static inline bool synth_writable(void) -{ - return inb_p(synth_port_control) & SYNTH_WRITABLE; -} - -static inline bool synth_full(void) -{ - return inb_p(speakup_info.port_tts + UART_RX) == 'F'; -} - -static const char *synth_immediate(struct spk_synth *synth, const char *buf) -{ - u_char ch; - - while ((ch = *buf)) { - int timeout = SPK_XMITR_TIMEOUT; - - if (ch == '\n') - ch = PROCSPEECH; - if (synth_full()) - return buf; - while (synth_writable()) { - if (!--timeout) - return buf; - udelay(1); - } - outb_p(ch, speakup_info.port_tts); - buf++; - } - return NULL; -} - -static void do_catch_up(struct spk_synth *synth) -{ - u_char ch; - unsigned long flags; - unsigned long jiff_max; - int timeout; - int delay_time_val; - int jiffy_delta_val; - int full_time_val; - struct var_t *delay_time; - struct var_t *full_time; - struct var_t *jiffy_delta; - - jiffy_delta = spk_get_var(JIFFY); - delay_time = spk_get_var(DELAY); - full_time = spk_get_var(FULL); - - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - - jiff_max = jiffies + jiffy_delta_val; - while (!kthread_should_stop()) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (speakup_info.flushing) { - speakup_info.flushing = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - synth->flush(synth); - continue; - } - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - break; - } - set_current_state(TASK_INTERRUPTIBLE); - full_time_val = full_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (synth_full()) { - schedule_timeout(msecs_to_jiffies(full_time_val)); - continue; - } - set_current_state(TASK_RUNNING); - timeout = SPK_XMITR_TIMEOUT; - while (synth_writable()) { - if (!--timeout) - break; - udelay(1); - } - spin_lock_irqsave(&speakup_info.spinlock, flags); - ch = synth_buffer_getc(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '\n') - ch = PROCSPEECH; - outb_p(ch, speakup_info.port_tts); - if (time_after_eq(jiffies, jiff_max) && ch == SPACE) { - timeout = SPK_XMITR_TIMEOUT; - while (synth_writable()) { - if (!--timeout) - break; - udelay(1); - } - outb_p(PROCSPEECH, speakup_info.port_tts); - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - schedule_timeout(msecs_to_jiffies(delay_time_val)); - jiff_max = jiffies + jiffy_delta_val; - } - } - timeout = SPK_XMITR_TIMEOUT; - while (synth_writable()) { - if (!--timeout) - break; - udelay(1); - } - outb_p(PROCSPEECH, speakup_info.port_tts); -} - -static void synth_flush(struct spk_synth *synth) -{ - outb_p(SYNTH_CLEAR, speakup_info.port_tts); -} - -static int synth_probe(struct spk_synth *synth) -{ - unsigned int port_val = 0; - int i = 0; - - pr_info("Probing for %s.\n", synth->long_name); - if (port_forced) { - speakup_info.port_tts = port_forced; - pr_info("probe forced to %x by kernel command line\n", - speakup_info.port_tts); - if (synth_request_region(speakup_info.port_tts - 1, - SYNTH_IO_EXTENT)) { - pr_warn("sorry, port already reserved\n"); - return -EBUSY; - } - port_val = inw(speakup_info.port_tts - 1); - synth_port_control = speakup_info.port_tts - 1; - } else { - for (i = 0; synth_portlist[i]; i++) { - if (synth_request_region(synth_portlist[i], - SYNTH_IO_EXTENT)) { - pr_warn - ("request_region: failed with 0x%x, %d\n", - synth_portlist[i], SYNTH_IO_EXTENT); - continue; - } - port_val = inw(synth_portlist[i]) & 0xfffc; - if (port_val == 0x53fc) { - /* 'S' and out&input bits */ - synth_port_control = synth_portlist[i]; - speakup_info.port_tts = synth_port_control + 1; - break; - } - } - } - port_val &= 0xfffc; - if (port_val != 0x53fc) { - /* 'S' and out&input bits */ - pr_info("%s: not found\n", synth->long_name); - synth_release_region(synth_port_control, SYNTH_IO_EXTENT); - synth_port_control = 0; - return -ENODEV; - } - pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name, - synth_port_control, synth_port_control + SYNTH_IO_EXTENT - 1, - synth->version); - synth->alive = 1; - return 0; -} - -static void accent_release(void) -{ - spk_stop_serial_interrupt(); - if (speakup_info.port_tts) - synth_release_region(speakup_info.port_tts - 1, - SYNTH_IO_EXTENT); - speakup_info.port_tts = 0; -} - -module_param_hw_named(port, port_forced, int, ioport, 0444); -module_param_named(start, synth_acntpc.startup, short, 0444); - -MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_acntpc); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for Accent PC synthesizer"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_acntsa.c b/drivers/staging/speakup/speakup_acntsa.c deleted file mode 100644 index 3a863dc61286..000000000000 --- a/drivers/staging/speakup/speakup_acntsa.c +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * this code is specificly written as a driver for the speakup screenreview - * package and is not a general device driver. - */ - -#include "spk_priv.h" -#include "speakup.h" -#include "speakup_acnt.h" /* local header file for Accent values */ - -#define DRV_VERSION "2.11" -#define PROCSPEECH '\r' - -static int synth_probe(struct spk_synth *synth); - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\033P8" } }, - { CAPS_STOP, .u.s = {"\033P5" } }, - { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } }, - { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } }, - { VOL, .u.n = {"\033A%d", 9, 0, 9, 0, 0, NULL } }, - { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/acntsa. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_acntsa = { - .name = "acntsa", - .version = DRV_VERSION, - .long_name = "Accent-SA", - .init = "\033T2\033=M\033Oi\033N1\n", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 400, - .trigger = 50, - .jiffies = 30, - .full = 40000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = spk_do_catch_up, - .flush = spk_synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "acntsa", - }, -}; - -static int synth_probe(struct spk_synth *synth) -{ - int failed; - - failed = spk_ttyio_synth_probe(synth); - if (failed == 0) { - synth->synth_immediate(synth, "\033=R\r"); - mdelay(100); - } - synth->alive = !failed; - return failed; -} - -module_param_named(ser, synth_acntsa.ser, int, 0444); -module_param_named(dev, synth_acntsa.dev_name, charp, 0444); -module_param_named(start, synth_acntsa.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_acntsa); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for Accent SA synthesizer"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_apollo.c b/drivers/staging/speakup/speakup_apollo.c deleted file mode 100644 index 0877b4044c28..000000000000 --- a/drivers/staging/speakup/speakup_apollo.c +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * this code is specificly written as a driver for the speakup screenreview - * package and is not a general device driver. - */ -#include -#include -#include -#include -#include /* for UART_MCR* constants */ - -#include "spk_priv.h" -#include "speakup.h" - -#define DRV_VERSION "2.21" -#define SYNTH_CLEAR 0x18 -#define PROCSPEECH '\r' - -static void do_catch_up(struct spk_synth *synth); - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"cap, " } }, - { CAPS_STOP, .u.s = {"" } }, - { RATE, .u.n = {"@W%d", 6, 1, 9, 0, 0, NULL } }, - { PITCH, .u.n = {"@F%x", 10, 0, 15, 0, 0, NULL } }, - { VOL, .u.n = {"@A%x", 10, 0, 15, 0, 0, NULL } }, - { VOICE, .u.n = {"@V%d", 1, 1, 6, 0, 0, NULL } }, - { LANG, .u.n = {"@=%d,", 1, 1, 4, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/apollo. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute lang_attribute = - __ATTR(lang, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute voice_attribute = - __ATTR(voice, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &lang_attribute.attr, - &pitch_attribute.attr, - &rate_attribute.attr, - &voice_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_apollo = { - .name = "apollo", - .version = DRV_VERSION, - .long_name = "Apollo", - .init = "@R3@D0@K1\r", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 40000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = spk_ttyio_synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = do_catch_up, - .flush = spk_synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "apollo", - }, -}; - -static void do_catch_up(struct spk_synth *synth) -{ - u_char ch; - unsigned long flags; - unsigned long jiff_max; - struct var_t *jiffy_delta; - struct var_t *delay_time; - struct var_t *full_time; - int full_time_val = 0; - int delay_time_val = 0; - int jiffy_delta_val = 0; - - jiffy_delta = spk_get_var(JIFFY); - delay_time = spk_get_var(DELAY); - full_time = spk_get_var(FULL); - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - jiff_max = jiffies + jiffy_delta_val; - - while (!kthread_should_stop()) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - full_time_val = full_time->u.n.value; - delay_time_val = delay_time->u.n.value; - if (speakup_info.flushing) { - speakup_info.flushing = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - synth->flush(synth); - continue; - } - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - break; - } - ch = synth_buffer_peek(); - set_current_state(TASK_INTERRUPTIBLE); - full_time_val = full_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (!synth->io_ops->synth_out(synth, ch)) { - synth->io_ops->tiocmset(0, UART_MCR_RTS); - synth->io_ops->tiocmset(UART_MCR_RTS, 0); - schedule_timeout(msecs_to_jiffies(full_time_val)); - continue; - } - if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - full_time_val = full_time->u.n.value; - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (synth->io_ops->synth_out(synth, synth->procspeech)) - schedule_timeout(msecs_to_jiffies - (delay_time_val)); - else - schedule_timeout(msecs_to_jiffies - (full_time_val)); - jiff_max = jiffies + jiffy_delta_val; - } - set_current_state(TASK_RUNNING); - spin_lock_irqsave(&speakup_info.spinlock, flags); - synth_buffer_getc(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - } - synth->io_ops->synth_out(synth, PROCSPEECH); -} - -module_param_named(ser, synth_apollo.ser, int, 0444); -module_param_named(dev, synth_apollo.dev_name, charp, 0444); -module_param_named(start, synth_apollo.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_apollo); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for Apollo II synthesizer"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_audptr.c b/drivers/staging/speakup/speakup_audptr.c deleted file mode 100644 index e6a6a9665d8f..000000000000 --- a/drivers/staging/speakup/speakup_audptr.c +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * specificly written as a driver for the speakup screenreview - * s not a general device driver. - */ -#include "spk_priv.h" -#include "speakup.h" - -#define DRV_VERSION "2.11" -#define SYNTH_CLEAR 0x18 /* flush synth buffer */ -#define PROCSPEECH '\r' /* start synth processing speech char */ - -static int synth_probe(struct spk_synth *synth); -static void synth_flush(struct spk_synth *synth); - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\x05[f99]" } }, - { CAPS_STOP, .u.s = {"\x05[f80]" } }, - { RATE, .u.n = {"\x05[r%d]", 10, 0, 20, 100, -10, NULL } }, - { PITCH, .u.n = {"\x05[f%d]", 80, 39, 4500, 0, 0, NULL } }, - { VOL, .u.n = {"\x05[g%d]", 21, 0, 40, 0, 0, NULL } }, - { TONE, .u.n = {"\x05[s%d]", 9, 0, 63, 0, 0, NULL } }, - { PUNCT, .u.n = {"\x05[A%c]", 0, 0, 3, 0, 0, "nmsa" } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/audptr. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punct_attribute = - __ATTR(punct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &punct_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_audptr = { - .name = "audptr", - .version = DRV_VERSION, - .long_name = "Audapter", - .init = "\x05[D1]\x05[Ol]", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 400, - .trigger = 50, - .jiffies = 30, - .full = 18000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = spk_do_catch_up, - .flush = synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "audptr", - }, -}; - -static void synth_flush(struct spk_synth *synth) -{ - synth->io_ops->flush_buffer(); - synth->io_ops->send_xchar(SYNTH_CLEAR); - synth->io_ops->synth_out(synth, PROCSPEECH); -} - -static void synth_version(struct spk_synth *synth) -{ - unsigned char test = 0; - char synth_id[40] = ""; - - synth->synth_immediate(synth, "\x05[Q]"); - synth_id[test] = synth->io_ops->synth_in(); - if (synth_id[test] == 'A') { - do { - /* read version string from synth */ - synth_id[++test] = synth->io_ops->synth_in(); - } while (synth_id[test] != '\n' && test < 32); - synth_id[++test] = 0x00; - } - if (synth_id[0] == 'A') - pr_info("%s version: %s", synth->long_name, synth_id); -} - -static int synth_probe(struct spk_synth *synth) -{ - int failed; - - failed = spk_ttyio_synth_probe(synth); - if (failed == 0) - synth_version(synth); - synth->alive = !failed; - return 0; -} - -module_param_named(ser, synth_audptr.ser, int, 0444); -module_param_named(dev, synth_audptr.dev_name, charp, 0444); -module_param_named(start, synth_audptr.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_audptr); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for Audapter synthesizer"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_bns.c b/drivers/staging/speakup/speakup_bns.c deleted file mode 100644 index 76dfa3f7c058..000000000000 --- a/drivers/staging/speakup/speakup_bns.c +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * this code is specificly written as a driver for the speakup screenreview - * package and is not a general device driver. - */ -#include "spk_priv.h" -#include "speakup.h" - -#define DRV_VERSION "2.11" -#define SYNTH_CLEAR 0x18 -#define PROCSPEECH '\r' - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\x05\x31\x32P" } }, - { CAPS_STOP, .u.s = {"\x05\x38P" } }, - { RATE, .u.n = {"\x05%dE", 8, 1, 16, 0, 0, NULL } }, - { PITCH, .u.n = {"\x05%dP", 8, 0, 16, 0, 0, NULL } }, - { VOL, .u.n = {"\x05%dV", 8, 0, 16, 0, 0, NULL } }, - { TONE, .u.n = {"\x05%dT", 8, 0, 16, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/bns. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_bns = { - .name = "bns", - .version = DRV_VERSION, - .long_name = "Braille 'N Speak", - .init = "\x05Z\x05\x43", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 40000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = spk_ttyio_synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = spk_do_catch_up, - .flush = spk_synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "bns", - }, -}; - -module_param_named(ser, synth_bns.ser, int, 0444); -module_param_named(dev, synth_bns.dev_name, charp, 0444); -module_param_named(start, synth_bns.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_bns); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for Braille 'n Speak synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_decext.c b/drivers/staging/speakup/speakup_decext.c deleted file mode 100644 index 7408eb29cf38..000000000000 --- a/drivers/staging/speakup/speakup_decext.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * specificly written as a driver for the speakup screenreview - * s not a general device driver. - */ -#include -#include -#include -#include - -#include "spk_priv.h" -#include "speakup.h" - -#define DRV_VERSION "2.14" -#define SYNTH_CLEAR 0x03 -#define PROCSPEECH 0x0b - -static volatile unsigned char last_char; - -static void read_buff_add(u_char ch) -{ - last_char = ch; -} - -static inline bool synth_full(void) -{ - return last_char == 0x13; -} - -static void do_catch_up(struct spk_synth *synth); -static void synth_flush(struct spk_synth *synth); - -static int in_escape; - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"[:dv ap 222]" } }, - { CAPS_STOP, .u.s = {"[:dv ap 100]" } }, - { RATE, .u.n = {"[:ra %d]", 7, 0, 9, 150, 25, NULL } }, - { PITCH, .u.n = {"[:dv ap %d]", 100, 0, 100, 0, 0, NULL } }, - { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } }, - { VOL, .u.n = {"[:dv gv %d]", 13, 0, 16, 0, 5, NULL } }, - { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } }, - { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/decext. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute inflection_attribute = - __ATTR(inflection, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punct_attribute = - __ATTR(punct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute voice_attribute = - __ATTR(voice, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &inflection_attribute.attr, - &punct_attribute.attr, - &rate_attribute.attr, - &voice_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_decext = { - .name = "decext", - .version = DRV_VERSION, - .long_name = "Dectalk External", - .init = "[:pe -380]", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 40000, - .flags = SF_DEC, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = spk_ttyio_synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = do_catch_up, - .flush = synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = read_buff_add, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "decext", - }, -}; - -static void do_catch_up(struct spk_synth *synth) -{ - u_char ch; - static u_char last = '\0'; - unsigned long flags; - unsigned long jiff_max; - struct var_t *jiffy_delta; - struct var_t *delay_time; - int jiffy_delta_val = 0; - int delay_time_val = 0; - - jiffy_delta = spk_get_var(JIFFY); - delay_time = spk_get_var(DELAY); - - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - jiff_max = jiffies + jiffy_delta_val; - - while (!kthread_should_stop()) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (speakup_info.flushing) { - speakup_info.flushing = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - synth->flush(synth); - continue; - } - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - break; - } - ch = synth_buffer_peek(); - set_current_state(TASK_INTERRUPTIBLE); - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '\n') - ch = 0x0D; - if (synth_full() || !synth->io_ops->synth_out(synth, ch)) { - schedule_timeout(msecs_to_jiffies(delay_time_val)); - continue; - } - set_current_state(TASK_RUNNING); - spin_lock_irqsave(&speakup_info.spinlock, flags); - synth_buffer_getc(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '[') { - in_escape = 1; - } else if (ch == ']') { - in_escape = 0; - } else if (ch <= SPACE) { - if (!in_escape && strchr(",.!?;:", last)) - synth->io_ops->synth_out(synth, PROCSPEECH); - if (time_after_eq(jiffies, jiff_max)) { - if (!in_escape) - synth->io_ops->synth_out(synth, - PROCSPEECH); - spin_lock_irqsave(&speakup_info.spinlock, - flags); - jiffy_delta_val = jiffy_delta->u.n.value; - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, - flags); - schedule_timeout(msecs_to_jiffies - (delay_time_val)); - jiff_max = jiffies + jiffy_delta_val; - } - } - last = ch; - } - if (!in_escape) - synth->io_ops->synth_out(synth, PROCSPEECH); -} - -static void synth_flush(struct spk_synth *synth) -{ - in_escape = 0; - synth->io_ops->flush_buffer(); - synth->synth_immediate(synth, "\033P;10z\033\\"); -} - -module_param_named(ser, synth_decext.ser, int, 0444); -module_param_named(dev, synth_decext.dev_name, charp, 0444); -module_param_named(start, synth_decext.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_decext); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for DECtalk External synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_decpc.c b/drivers/staging/speakup/speakup_decpc.c deleted file mode 100644 index 96f24c848cc5..000000000000 --- a/drivers/staging/speakup/speakup_decpc.c +++ /dev/null @@ -1,495 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * This is the DECtalk PC speakup driver - * - * Some constants from DEC's DOS driver: - * Copyright (c) by Digital Equipment Corp. - * - * 386BSD DECtalk PC driver: - * Copyright (c) 1996 Brian Buhrow - * - * Linux DECtalk PC driver: - * Copyright (c) 1997 Nicolas Pitre - * - * speakup DECtalk PC Internal driver: - * Copyright (c) 2003 David Borowski - * - * All rights reserved. - */ -#include -#include -#include -#include - -#include "spk_priv.h" -#include "speakup.h" - -#define MODULE_init 0x0dec /* module in boot code */ -#define MODULE_self_test 0x8800 /* module in self-test */ -#define MODULE_reset 0xffff /* reinit the whole module */ - -#define MODE_mask 0xf000 /* mode bits in high nibble */ -#define MODE_null 0x0000 -#define MODE_test 0x2000 /* in testing mode */ -#define MODE_status 0x8000 -#define STAT_int 0x0001 /* running in interrupt mode */ -#define STAT_tr_char 0x0002 /* character data to transmit */ -#define STAT_rr_char 0x0004 /* ready to receive char data */ -#define STAT_cmd_ready 0x0008 /* ready to accept commands */ -#define STAT_dma_ready 0x0010 /* dma command ready */ -#define STAT_digitized 0x0020 /* spc in digitized mode */ -#define STAT_new_index 0x0040 /* new last index ready */ -#define STAT_new_status 0x0080 /* new status posted */ -#define STAT_dma_state 0x0100 /* dma state toggle */ -#define STAT_index_valid 0x0200 /* indexs are valid */ -#define STAT_flushing 0x0400 /* flush in progress */ -#define STAT_self_test 0x0800 /* module in self test */ -#define MODE_ready 0xc000 /* module ready for next phase */ -#define READY_boot 0x0000 -#define READY_kernel 0x0001 -#define MODE_error 0xf000 - -#define CMD_mask 0xf000 /* mask for command nibble */ -#define CMD_null 0x0000 /* post status */ -#define CMD_control 0x1000 /* hard control command */ -#define CTRL_mask 0x0F00 /* mask off control nibble */ -#define CTRL_data 0x00FF /* mask to get data byte */ -#define CTRL_null 0x0000 /* null control */ -#define CTRL_vol_up 0x0100 /* increase volume */ -#define CTRL_vol_down 0x0200 /* decrease volume */ -#define CTRL_vol_set 0x0300 /* set volume */ -#define CTRL_pause 0x0400 /* pause spc */ -#define CTRL_resume 0x0500 /* resume spc clock */ -#define CTRL_resume_spc 0x0001 /* resume spc soft pause */ -#define CTRL_flush 0x0600 /* flush all buffers */ -#define CTRL_int_enable 0x0700 /* enable status change ints */ -#define CTRL_buff_free 0x0800 /* buffer remain count */ -#define CTRL_buff_used 0x0900 /* buffer in use */ -#define CTRL_speech 0x0a00 /* immediate speech change */ -#define CTRL_SP_voice 0x0001 /* voice change */ -#define CTRL_SP_rate 0x0002 /* rate change */ -#define CTRL_SP_comma 0x0003 /* comma pause change */ -#define CTRL_SP_period 0x0004 /* period pause change */ -#define CTRL_SP_rate_delta 0x0005 /* delta rate change */ -#define CTRL_SP_get_param 0x0006 /* return the desired parameter */ -#define CTRL_last_index 0x0b00 /* get last index spoken */ -#define CTRL_io_priority 0x0c00 /* change i/o priority */ -#define CTRL_free_mem 0x0d00 /* get free paragraphs on module */ -#define CTRL_get_lang 0x0e00 /* return bitmask of loaded languages */ -#define CMD_test 0x2000 /* self-test request */ -#define TEST_mask 0x0F00 /* isolate test field */ -#define TEST_null 0x0000 /* no test requested */ -#define TEST_isa_int 0x0100 /* assert isa irq */ -#define TEST_echo 0x0200 /* make data in == data out */ -#define TEST_seg 0x0300 /* set peek/poke segment */ -#define TEST_off 0x0400 /* set peek/poke offset */ -#define TEST_peek 0x0500 /* data out == *peek */ -#define TEST_poke 0x0600 /* *peek == data in */ -#define TEST_sub_code 0x00FF /* user defined test sub codes */ -#define CMD_id 0x3000 /* return software id */ -#define ID_null 0x0000 /* null id */ -#define ID_kernel 0x0100 /* kernel code executing */ -#define ID_boot 0x0200 /* boot code executing */ -#define CMD_dma 0x4000 /* force a dma start */ -#define CMD_reset 0x5000 /* reset module status */ -#define CMD_sync 0x6000 /* kernel sync command */ -#define CMD_char_in 0x7000 /* single character send */ -#define CMD_char_out 0x8000 /* single character get */ -#define CHAR_count_1 0x0100 /* one char in cmd_low */ -#define CHAR_count_2 0x0200 /* the second in data_low */ -#define CHAR_count_3 0x0300 /* the third in data_high */ -#define CMD_spc_mode 0x9000 /* change spc mode */ -#define CMD_spc_to_text 0x0100 /* set to text mode */ -#define CMD_spc_to_digit 0x0200 /* set to digital mode */ -#define CMD_spc_rate 0x0400 /* change spc data rate */ -#define CMD_error 0xf000 /* severe error */ - -enum { PRIMARY_DIC = 0, USER_DIC, COMMAND_DIC, ABBREV_DIC }; - -#define DMA_single_in 0x01 -#define DMA_single_out 0x02 -#define DMA_buff_in 0x03 -#define DMA_buff_out 0x04 -#define DMA_control 0x05 -#define DT_MEM_ALLOC 0x03 -#define DT_SET_DIC 0x04 -#define DT_START_TASK 0x05 -#define DT_LOAD_MEM 0x06 -#define DT_READ_MEM 0x07 -#define DT_DIGITAL_IN 0x08 -#define DMA_sync 0x06 -#define DMA_sync_char 0x07 - -#define DRV_VERSION "2.12" -#define PROCSPEECH 0x0b -#define SYNTH_IO_EXTENT 8 - -static int synth_probe(struct spk_synth *synth); -static void dtpc_release(void); -static const char *synth_immediate(struct spk_synth *synth, const char *buf); -static void do_catch_up(struct spk_synth *synth); -static void synth_flush(struct spk_synth *synth); - -static int synth_portlist[] = { 0x340, 0x350, 0x240, 0x250, 0 }; -static int in_escape, is_flushing; -static int dt_stat, dma_state; - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"[:dv ap 200]" } }, - { CAPS_STOP, .u.s = {"[:dv ap 100]" } }, - { RATE, .u.n = {"[:ra %d]", 9, 0, 18, 150, 25, NULL } }, - { PITCH, .u.n = {"[:dv ap %d]", 80, 0, 100, 20, 0, NULL } }, - { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } }, - { VOL, .u.n = {"[:vo se %d]", 5, 0, 9, 5, 10, NULL } }, - { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } }, - { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/decpc. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute inflection_attribute = - __ATTR(inflection, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punct_attribute = - __ATTR(punct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute voice_attribute = - __ATTR(voice, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &inflection_attribute.attr, - &punct_attribute.attr, - &rate_attribute.attr, - &voice_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_dec_pc = { - .name = "decpc", - .version = DRV_VERSION, - .long_name = "Dectalk PC", - .init = "[:pe -380]", - .procspeech = PROCSPEECH, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 1000, - .flags = SF_DEC, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_serial_io_ops, - .probe = synth_probe, - .release = dtpc_release, - .synth_immediate = synth_immediate, - .catch_up = do_catch_up, - .flush = synth_flush, - .is_alive = spk_synth_is_alive_nop, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "decpc", - }, -}; - -static int dt_getstatus(void) -{ - dt_stat = inb_p(speakup_info.port_tts) | - (inb_p(speakup_info.port_tts + 1) << 8); - return dt_stat; -} - -static void dt_sendcmd(u_int cmd) -{ - outb_p(cmd & 0xFF, speakup_info.port_tts); - outb_p((cmd >> 8) & 0xFF, speakup_info.port_tts + 1); -} - -static int dt_waitbit(int bit) -{ - int timeout = 100; - - while (--timeout > 0) { - if ((dt_getstatus() & bit) == bit) - return 1; - udelay(50); - } - return 0; -} - -static int dt_wait_dma(void) -{ - int timeout = 100, state = dma_state; - - if (!dt_waitbit(STAT_dma_ready)) - return 0; - while (--timeout > 0) { - if ((dt_getstatus() & STAT_dma_state) == state) - return 1; - udelay(50); - } - dma_state = dt_getstatus() & STAT_dma_state; - return 1; -} - -static int dt_ctrl(u_int cmd) -{ - int timeout = 10; - - if (!dt_waitbit(STAT_cmd_ready)) - return -1; - outb_p(0, speakup_info.port_tts + 2); - outb_p(0, speakup_info.port_tts + 3); - dt_getstatus(); - dt_sendcmd(CMD_control | cmd); - outb_p(0, speakup_info.port_tts + 6); - while (dt_getstatus() & STAT_cmd_ready) { - udelay(20); - if (--timeout == 0) - break; - } - dt_sendcmd(CMD_null); - return 0; -} - -static void synth_flush(struct spk_synth *synth) -{ - int timeout = 10; - - if (is_flushing) - return; - is_flushing = 4; - in_escape = 0; - while (dt_ctrl(CTRL_flush)) { - if (--timeout == 0) - break; - udelay(50); - } - for (timeout = 0; timeout < 10; timeout++) { - if (dt_waitbit(STAT_dma_ready)) - break; - udelay(50); - } - outb_p(DMA_sync, speakup_info.port_tts + 4); - outb_p(0, speakup_info.port_tts + 4); - udelay(100); - for (timeout = 0; timeout < 10; timeout++) { - if (!(dt_getstatus() & STAT_flushing)) - break; - udelay(50); - } - dma_state = dt_getstatus() & STAT_dma_state; - dma_state ^= STAT_dma_state; - is_flushing = 0; -} - -static int dt_sendchar(char ch) -{ - if (!dt_wait_dma()) - return -1; - if (!(dt_stat & STAT_rr_char)) - return -2; - outb_p(DMA_single_in, speakup_info.port_tts + 4); - outb_p(ch, speakup_info.port_tts + 4); - dma_state ^= STAT_dma_state; - return 0; -} - -static int testkernel(void) -{ - int status = 0; - - if (dt_getstatus() == 0xffff) { - status = -1; - goto oops; - } - dt_sendcmd(CMD_sync); - if (!dt_waitbit(STAT_cmd_ready)) - status = -2; - else if (dt_stat & 0x8000) - return 0; - else if (dt_stat == 0x0dec) - pr_warn("dec_pc at 0x%x, software not loaded\n", - speakup_info.port_tts); - status = -3; -oops: synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT); - speakup_info.port_tts = 0; - return status; -} - -static void do_catch_up(struct spk_synth *synth) -{ - u_char ch; - static u_char last; - unsigned long flags; - unsigned long jiff_max; - struct var_t *jiffy_delta; - struct var_t *delay_time; - int jiffy_delta_val; - int delay_time_val; - - jiffy_delta = spk_get_var(JIFFY); - delay_time = spk_get_var(DELAY); - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - jiff_max = jiffies + jiffy_delta_val; - - while (!kthread_should_stop()) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (speakup_info.flushing) { - speakup_info.flushing = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - synth->flush(synth); - continue; - } - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - break; - } - ch = synth_buffer_peek(); - set_current_state(TASK_INTERRUPTIBLE); - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '\n') - ch = 0x0D; - if (dt_sendchar(ch)) { - schedule_timeout(msecs_to_jiffies(delay_time_val)); - continue; - } - set_current_state(TASK_RUNNING); - spin_lock_irqsave(&speakup_info.spinlock, flags); - synth_buffer_getc(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '[') { - in_escape = 1; - } else if (ch == ']') { - in_escape = 0; - } else if (ch <= SPACE) { - if (!in_escape && strchr(",.!?;:", last)) - dt_sendchar(PROCSPEECH); - if (time_after_eq(jiffies, jiff_max)) { - if (!in_escape) - dt_sendchar(PROCSPEECH); - spin_lock_irqsave(&speakup_info.spinlock, - flags); - jiffy_delta_val = jiffy_delta->u.n.value; - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, - flags); - schedule_timeout(msecs_to_jiffies - (delay_time_val)); - jiff_max = jiffies + jiffy_delta_val; - } - } - last = ch; - ch = 0; - } - if (!in_escape) - dt_sendchar(PROCSPEECH); -} - -static const char *synth_immediate(struct spk_synth *synth, const char *buf) -{ - u_char ch; - - while ((ch = *buf)) { - if (ch == '\n') - ch = PROCSPEECH; - if (dt_sendchar(ch)) - return buf; - buf++; - } - return NULL; -} - -static int synth_probe(struct spk_synth *synth) -{ - int i = 0, failed = 0; - - pr_info("Probing for %s.\n", synth->long_name); - for (i = 0; synth_portlist[i]; i++) { - if (synth_request_region(synth_portlist[i], SYNTH_IO_EXTENT)) { - pr_warn("request_region: failed with 0x%x, %d\n", - synth_portlist[i], SYNTH_IO_EXTENT); - continue; - } - speakup_info.port_tts = synth_portlist[i]; - failed = testkernel(); - if (failed == 0) - break; - } - if (failed) { - pr_info("%s: not found\n", synth->long_name); - return -ENODEV; - } - pr_info("%s: %03x-%03x, Driver Version %s,\n", synth->long_name, - speakup_info.port_tts, speakup_info.port_tts + 7, - synth->version); - synth->alive = 1; - return 0; -} - -static void dtpc_release(void) -{ - spk_stop_serial_interrupt(); - if (speakup_info.port_tts) - synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT); - speakup_info.port_tts = 0; -} - -module_param_named(start, synth_dec_pc.startup, short, 0444); - -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_dec_pc); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for DECtalk PC synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); diff --git a/drivers/staging/speakup/speakup_dectlk.c b/drivers/staging/speakup/speakup_dectlk.c deleted file mode 100644 index 780214b5ca16..000000000000 --- a/drivers/staging/speakup/speakup_dectlk.c +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * specificly written as a driver for the speakup screenreview - * s not a general device driver. - */ -#include -#include -#include -#include -#include -#include -#include -#include "speakup.h" -#include "spk_priv.h" - -#define DRV_VERSION "2.20" -#define SYNTH_CLEAR 0x03 -#define PROCSPEECH 0x0b -static int xoff; - -static inline int synth_full(void) -{ - return xoff; -} - -static void do_catch_up(struct spk_synth *synth); -static void synth_flush(struct spk_synth *synth); -static void read_buff_add(u_char c); -static unsigned char get_index(struct spk_synth *synth); - -static int in_escape; -static int is_flushing; - -static spinlock_t flush_lock; -static DECLARE_WAIT_QUEUE_HEAD(flush); - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"[:dv ap 160] " } }, - { CAPS_STOP, .u.s = {"[:dv ap 100 ] " } }, - { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL } }, - { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } }, - { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL } }, - { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" } }, - { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/dectlk. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute inflection_attribute = - __ATTR(inflection, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punct_attribute = - __ATTR(punct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute voice_attribute = - __ATTR(voice, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &inflection_attribute.attr, - &punct_attribute.attr, - &rate_attribute.attr, - &voice_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static int ap_defaults[] = {122, 89, 155, 110, 208, 240, 200, 106, 306}; -static int g5_defaults[] = {86, 81, 86, 84, 81, 80, 83, 83, 73}; - -static struct spk_synth synth_dectlk = { - .name = "dectlk", - .version = DRV_VERSION, - .long_name = "Dectalk Express", - .init = "[:error sp :name paul :rate 180 :tsr off] ", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 40000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .default_pitch = ap_defaults, - .default_vol = g5_defaults, - .io_ops = &spk_ttyio_ops, - .probe = spk_ttyio_synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = do_catch_up, - .flush = synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = read_buff_add, - .get_index = get_index, - .indexing = { - .command = "[:in re %d ] ", - .lowindex = 1, - .highindex = 8, - .currindex = 1, - }, - .attributes = { - .attrs = synth_attrs, - .name = "dectlk", - }, -}; - -static int is_indnum(u_char *ch) -{ - if ((*ch >= '0') && (*ch <= '9')) { - *ch = *ch - '0'; - return 1; - } - return 0; -} - -static u_char lastind; - -static unsigned char get_index(struct spk_synth *synth) -{ - u_char rv; - - rv = lastind; - lastind = 0; - return rv; -} - -static void read_buff_add(u_char c) -{ - static int ind = -1; - - if (c == 0x01) { - unsigned long flags; - - spin_lock_irqsave(&flush_lock, flags); - is_flushing = 0; - wake_up_interruptible(&flush); - spin_unlock_irqrestore(&flush_lock, flags); - } else if (c == 0x13) { - xoff = 1; - } else if (c == 0x11) { - xoff = 0; - } else if (is_indnum(&c)) { - if (ind == -1) - ind = c; - else - ind = ind * 10 + c; - } else if ((c > 31) && (c < 127)) { - if (ind != -1) - lastind = (u_char)ind; - ind = -1; - } -} - -static void do_catch_up(struct spk_synth *synth) -{ - int synth_full_val = 0; - static u_char ch; - static u_char last = '\0'; - unsigned long flags; - unsigned long jiff_max; - unsigned long timeout = msecs_to_jiffies(4000); - DEFINE_WAIT(wait); - struct var_t *jiffy_delta; - struct var_t *delay_time; - int jiffy_delta_val; - int delay_time_val; - - jiffy_delta = spk_get_var(JIFFY); - delay_time = spk_get_var(DELAY); - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - jiff_max = jiffies + jiffy_delta_val; - - while (!kthread_should_stop()) { - /* if no ctl-a in 4, send data anyway */ - spin_lock_irqsave(&flush_lock, flags); - while (is_flushing && timeout) { - prepare_to_wait(&flush, &wait, TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&flush_lock, flags); - timeout = schedule_timeout(timeout); - spin_lock_irqsave(&flush_lock, flags); - } - finish_wait(&flush, &wait); - is_flushing = 0; - spin_unlock_irqrestore(&flush_lock, flags); - - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (speakup_info.flushing) { - speakup_info.flushing = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - synth->flush(synth); - continue; - } - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - break; - } - ch = synth_buffer_peek(); - set_current_state(TASK_INTERRUPTIBLE); - delay_time_val = delay_time->u.n.value; - synth_full_val = synth_full(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '\n') - ch = 0x0D; - if (synth_full_val || !synth->io_ops->synth_out(synth, ch)) { - schedule_timeout(msecs_to_jiffies(delay_time_val)); - continue; - } - set_current_state(TASK_RUNNING); - spin_lock_irqsave(&speakup_info.spinlock, flags); - synth_buffer_getc(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '[') { - in_escape = 1; - } else if (ch == ']') { - in_escape = 0; - } else if (ch <= SPACE) { - if (!in_escape && strchr(",.!?;:", last)) - synth->io_ops->synth_out(synth, PROCSPEECH); - if (time_after_eq(jiffies, jiff_max)) { - if (!in_escape) - synth->io_ops->synth_out(synth, - PROCSPEECH); - spin_lock_irqsave(&speakup_info.spinlock, - flags); - jiffy_delta_val = jiffy_delta->u.n.value; - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, - flags); - schedule_timeout(msecs_to_jiffies - (delay_time_val)); - jiff_max = jiffies + jiffy_delta_val; - } - } - last = ch; - } - if (!in_escape) - synth->io_ops->synth_out(synth, PROCSPEECH); -} - -static void synth_flush(struct spk_synth *synth) -{ - if (in_escape) - /* if in command output ']' so we don't get an error */ - synth->io_ops->synth_out(synth, ']'); - in_escape = 0; - is_flushing = 1; - synth->io_ops->flush_buffer(); - synth->io_ops->synth_out(synth, SYNTH_CLEAR); -} - -module_param_named(ser, synth_dectlk.ser, int, 0444); -module_param_named(dev, synth_dectlk.dev_name, charp, 0444); -module_param_named(start, synth_dectlk.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_dectlk); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_dtlk.c b/drivers/staging/speakup/speakup_dtlk.c deleted file mode 100644 index dbebed0eeeec..000000000000 --- a/drivers/staging/speakup/speakup_dtlk.c +++ /dev/null @@ -1,390 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * specificly written as a driver for the speakup screenreview - * package it's not a general device driver. - * This driver is for the RC Systems DoubleTalk PC internal synthesizer. - */ -#include -#include -#include -#include - -#include "spk_priv.h" -#include "serialio.h" -#include "speakup_dtlk.h" /* local header file for DoubleTalk values */ -#include "speakup.h" - -#define DRV_VERSION "2.10" -#define PROCSPEECH 0x00 - -static int synth_probe(struct spk_synth *synth); -static void dtlk_release(void); -static const char *synth_immediate(struct spk_synth *synth, const char *buf); -static void do_catch_up(struct spk_synth *synth); -static void synth_flush(struct spk_synth *synth); - -static int synth_lpc; -static int port_forced; -static unsigned int synth_portlist[] = { - 0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0 -}; - -static u_char synth_status; - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\x01+35p" } }, - { CAPS_STOP, .u.s = {"\x01-35p" } }, - { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } }, - { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } }, - { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, - { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, - { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } }, - { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, - { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/dtlk. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute freq_attribute = - __ATTR(freq, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punct_attribute = - __ATTR(punct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute voice_attribute = - __ATTR(voice, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &freq_attribute.attr, - &pitch_attribute.attr, - &punct_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &voice_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_dtlk = { - .name = "dtlk", - .version = DRV_VERSION, - .long_name = "DoubleTalk PC", - .init = "\x01@\x01\x31y", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 30, - .jiffies = 50, - .full = 1000, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_serial_io_ops, - .probe = synth_probe, - .release = dtlk_release, - .synth_immediate = synth_immediate, - .catch_up = do_catch_up, - .flush = synth_flush, - .is_alive = spk_synth_is_alive_nop, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = spk_synth_get_index, - .indexing = { - .command = "\x01%di", - .lowindex = 1, - .highindex = 5, - .currindex = 1, - }, - .attributes = { - .attrs = synth_attrs, - .name = "dtlk", - }, -}; - -static inline bool synth_readable(void) -{ - synth_status = inb_p(speakup_info.port_tts + UART_RX); - return (synth_status & TTS_READABLE) != 0; -} - -static inline bool synth_writable(void) -{ - synth_status = inb_p(speakup_info.port_tts + UART_RX); - return (synth_status & TTS_WRITABLE) != 0; -} - -static inline bool synth_full(void) -{ - synth_status = inb_p(speakup_info.port_tts + UART_RX); - return (synth_status & TTS_ALMOST_FULL) != 0; -} - -static void spk_out(const char ch) -{ - int timeout = SPK_XMITR_TIMEOUT; - - while (!synth_writable()) { - if (!--timeout) - break; - udelay(1); - } - outb_p(ch, speakup_info.port_tts); - timeout = SPK_XMITR_TIMEOUT; - while (synth_writable()) { - if (!--timeout) - break; - udelay(1); - } -} - -static void do_catch_up(struct spk_synth *synth) -{ - u_char ch; - unsigned long flags; - unsigned long jiff_max; - struct var_t *jiffy_delta; - struct var_t *delay_time; - int jiffy_delta_val; - int delay_time_val; - - jiffy_delta = spk_get_var(JIFFY); - delay_time = spk_get_var(DELAY); - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - jiff_max = jiffies + jiffy_delta_val; - while (!kthread_should_stop()) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (speakup_info.flushing) { - speakup_info.flushing = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - synth->flush(synth); - continue; - } - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - break; - } - set_current_state(TASK_INTERRUPTIBLE); - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (synth_full()) { - schedule_timeout(msecs_to_jiffies(delay_time_val)); - continue; - } - set_current_state(TASK_RUNNING); - spin_lock_irqsave(&speakup_info.spinlock, flags); - ch = synth_buffer_getc(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '\n') - ch = PROCSPEECH; - spk_out(ch); - if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { - spk_out(PROCSPEECH); - spin_lock_irqsave(&speakup_info.spinlock, flags); - delay_time_val = delay_time->u.n.value; - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - schedule_timeout(msecs_to_jiffies(delay_time_val)); - jiff_max = jiffies + jiffy_delta_val; - } - } - spk_out(PROCSPEECH); -} - -static const char *synth_immediate(struct spk_synth *synth, const char *buf) -{ - u_char ch; - - while ((ch = (u_char)*buf)) { - if (synth_full()) - return buf; - if (ch == '\n') - ch = PROCSPEECH; - spk_out(ch); - buf++; - } - return NULL; -} - -static void synth_flush(struct spk_synth *synth) -{ - outb_p(SYNTH_CLEAR, speakup_info.port_tts); - while (synth_writable()) - cpu_relax(); -} - -static char synth_read_tts(void) -{ - u_char ch; - - while (!synth_readable()) - cpu_relax(); - ch = synth_status & 0x7f; - outb_p(ch, speakup_info.port_tts); - while (synth_readable()) - cpu_relax(); - return (char)ch; -} - -/* interrogate the DoubleTalk PC and return its settings */ -static struct synth_settings *synth_interrogate(struct spk_synth *synth) -{ - u_char *t; - static char buf[sizeof(struct synth_settings) + 1]; - int total, i; - static struct synth_settings status; - - synth_immediate(synth, "\x18\x01?"); - for (total = 0, i = 0; i < 50; i++) { - buf[total] = synth_read_tts(); - if (total > 2 && buf[total] == 0x7f) - break; - if (total < sizeof(struct synth_settings)) - total++; - } - t = buf; - /* serial number is little endian */ - status.serial_number = t[0] + t[1] * 256; - t += 2; - for (i = 0; *t != '\r'; t++) { - status.rom_version[i] = *t; - if (i < sizeof(status.rom_version) - 1) - i++; - } - status.rom_version[i] = 0; - t++; - status.mode = *t++; - status.punc_level = *t++; - status.formant_freq = *t++; - status.pitch = *t++; - status.speed = *t++; - status.volume = *t++; - status.tone = *t++; - status.expression = *t++; - status.ext_dict_loaded = *t++; - status.ext_dict_status = *t++; - status.free_ram = *t++; - status.articulation = *t++; - status.reverb = *t++; - status.eob = *t++; - return &status; -} - -static int synth_probe(struct spk_synth *synth) -{ - unsigned int port_val = 0; - int i = 0; - struct synth_settings *sp; - - pr_info("Probing for DoubleTalk.\n"); - if (port_forced) { - speakup_info.port_tts = port_forced; - pr_info("probe forced to %x by kernel command line\n", - speakup_info.port_tts); - if ((port_forced & 0xf) != 0xf) - pr_info("warning: port base should probably end with f\n"); - if (synth_request_region(speakup_info.port_tts - 1, - SYNTH_IO_EXTENT)) { - pr_warn("sorry, port already reserved\n"); - return -EBUSY; - } - port_val = inw(speakup_info.port_tts - 1); - synth_lpc = speakup_info.port_tts - 1; - } else { - for (i = 0; synth_portlist[i]; i++) { - if (synth_request_region(synth_portlist[i], - SYNTH_IO_EXTENT)) - continue; - port_val = inw(synth_portlist[i]) & 0xfbff; - if (port_val == 0x107f) { - synth_lpc = synth_portlist[i]; - speakup_info.port_tts = synth_lpc + 1; - break; - } - synth_release_region(synth_portlist[i], - SYNTH_IO_EXTENT); - } - } - port_val &= 0xfbff; - if (port_val != 0x107f) { - pr_info("DoubleTalk PC: not found\n"); - if (synth_lpc) - synth_release_region(synth_lpc, SYNTH_IO_EXTENT); - return -ENODEV; - } - while (inw_p(synth_lpc) != 0x147f) - cpu_relax(); /* wait until it's ready */ - sp = synth_interrogate(synth); - pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n", - synth->long_name, synth_lpc, synth_lpc + SYNTH_IO_EXTENT - 1, - sp->rom_version, sp->serial_number, synth->version); - synth->alive = 1; - return 0; -} - -static void dtlk_release(void) -{ - spk_stop_serial_interrupt(); - if (speakup_info.port_tts) - synth_release_region(speakup_info.port_tts - 1, - SYNTH_IO_EXTENT); - speakup_info.port_tts = 0; -} - -module_param_hw_named(port, port_forced, int, ioport, 0444); -module_param_named(start, synth_dtlk.startup, short, 0444); - -MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_dtlk); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for DoubleTalk PC synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_dtlk.h b/drivers/staging/speakup/speakup_dtlk.h deleted file mode 100644 index 9c378b58066e..000000000000 --- a/drivers/staging/speakup/speakup_dtlk.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* speakup_dtlk.h - header file for speakups DoubleTalk driver. */ - -#define SYNTH_IO_EXTENT 0x02 -#define SYNTH_CLEAR 0x18 /* stops speech */ - /* TTS Port Status Flags */ -#define TTS_READABLE 0x80 /* mask for bit which is nonzero if a - * byte can be read from the TTS port - */ -#define TTS_SPEAKING 0x40 /* mask for SYNC bit, which is nonzero - * while DoubleTalk is producing - * output with TTS, PCM or CVSD - * synthesizers or tone generators - * (that is, all but LPC) - */ -#define TTS_SPEAKING2 0x20 /* mask for SYNC2 bit, - * which falls to zero up to 0.4 sec - * before speech stops - */ -#define TTS_WRITABLE 0x10 /* mask for RDY bit, which when set to - * 1, indicates the TTS port is ready - * to accept a byte of data. The RDY - * bit goes zero 2-3 usec after - * writing, and goes 1 again 180-190 - * usec later. - */ -#define TTS_ALMOST_FULL 0x08 /* mask for AF bit: When set to 1, - * indicates that less than 300 bytes - * are available in the TTS input - * buffer. AF is always 0 in the PCM, - * TGN and CVSD modes. - */ -#define TTS_ALMOST_EMPTY 0x04 /* mask for AE bit: When set to 1, - * indicates that less than 300 bytes - * are remaining in DoubleTalk's input - * (TTS or PCM) buffer. AE is always 1 - * in the TGN and CVSD modes. - */ - - /* data returned by Interrogate command */ -struct synth_settings { - u_short serial_number; /* 0-7Fh:0-7Fh */ - u_char rom_version[24]; /* null terminated string */ - u_char mode; /* 0=Character; 1=Phoneme; 2=Text */ - u_char punc_level; /* nB; 0-7 */ - u_char formant_freq; /* nF; 0-9 */ - u_char pitch; /* nP; 0-99 */ - u_char speed; /* nS; 0-9 */ - u_char volume; /* nV; 0-9 */ - u_char tone; /* nX; 0-2 */ - u_char expression; /* nE; 0-9 */ - u_char ext_dict_loaded; /* 1=exception dictionary loaded */ - u_char ext_dict_status; /* 1=exception dictionary enabled */ - u_char free_ram; /* # pages (truncated) remaining for - * text buffer - */ - u_char articulation; /* nA; 0-9 */ - u_char reverb; /* nR; 0-9 */ - u_char eob; /* 7Fh value indicating end of - * parameter block - */ - u_char has_indexing; /* nonzero if indexing is implemented */ -}; diff --git a/drivers/staging/speakup/speakup_dummy.c b/drivers/staging/speakup/speakup_dummy.c deleted file mode 100644 index e393438af81b..000000000000 --- a/drivers/staging/speakup/speakup_dummy.c +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * eventually modified by Samuel Thibault - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * Copyright (C) 2007 Samuel Thibault. - * - * specificly written as a driver for the speakup screenreview - * s not a general device driver. - */ -#include "spk_priv.h" -#include "speakup.h" - -#define PROCSPEECH '\n' -#define DRV_VERSION "2.11" -#define SYNTH_CLEAR '!' - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"CAPS_START\n" } }, - { CAPS_STOP, .u.s = {"CAPS_STOP\n" } }, - { PAUSE, .u.s = {"PAUSE\n"} }, - { RATE, .u.n = {"RATE %d\n", 8, 1, 16, 0, 0, NULL } }, - { PITCH, .u.n = {"PITCH %d\n", 8, 0, 16, 0, 0, NULL } }, - { INFLECTION, .u.n = {"INFLECTION %d\n", 8, 0, 16, 0, 0, NULL } }, - { VOL, .u.n = {"VOL %d\n", 8, 0, 16, 0, 0, NULL } }, - { TONE, .u.n = {"TONE %d\n", 8, 0, 16, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/dummy. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute inflection_attribute = - __ATTR(inflection, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &inflection_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_dummy = { - .name = "dummy", - .version = DRV_VERSION, - .long_name = "Dummy", - .init = "Speakup\n", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 40000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = spk_ttyio_synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = spk_do_catch_up_unicode, - .flush = spk_synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "dummy", - }, -}; - -module_param_named(ser, synth_dummy.ser, int, 0444); -module_param_named(dev, synth_dummy.dev_name, charp, 0444); -module_param_named(start, synth_dummy.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_dummy); - -MODULE_AUTHOR("Samuel Thibault "); -MODULE_DESCRIPTION("Speakup support for text console"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_keypc.c b/drivers/staging/speakup/speakup_keypc.c deleted file mode 100644 index 414827e888fc..000000000000 --- a/drivers/staging/speakup/speakup_keypc.c +++ /dev/null @@ -1,318 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * written by David Borowski - * - * Copyright (C) 2003 David Borowski. - * - * specificly written as a driver for the speakup screenreview - * package it's not a general device driver. - * This driver is for the Keynote Gold internal synthesizer. - */ -#include -#include -#include -#include -#include - -#include "spk_priv.h" -#include "speakup.h" - -#define DRV_VERSION "2.10" -#define SYNTH_IO_EXTENT 0x04 -#define SWAIT udelay(70) -#define PROCSPEECH 0x1f -#define SYNTH_CLEAR 0x03 - -static int synth_probe(struct spk_synth *synth); -static void keynote_release(void); -static const char *synth_immediate(struct spk_synth *synth, const char *buf); -static void do_catch_up(struct spk_synth *synth); -static void synth_flush(struct spk_synth *synth); - -static int synth_port; -static int port_forced; -static unsigned int synth_portlist[] = { 0x2a8, 0 }; - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"[f130]" } }, - { CAPS_STOP, .u.s = {"[f90]" } }, - { RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL } }, - { PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/keypc. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &rate_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_keypc = { - .name = "keypc", - .version = DRV_VERSION, - .long_name = "Keynote PC", - .init = "[t][n7,1][n8,0]", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 1000, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_serial_io_ops, - .probe = synth_probe, - .release = keynote_release, - .synth_immediate = synth_immediate, - .catch_up = do_catch_up, - .flush = synth_flush, - .is_alive = spk_synth_is_alive_nop, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "keypc", - }, -}; - -static inline bool synth_writable(void) -{ - return (inb_p(synth_port + UART_RX) & 0x10) != 0; -} - -static inline bool synth_full(void) -{ - return (inb_p(synth_port + UART_RX) & 0x80) == 0; -} - -static char *oops(void) -{ - int s1, s2, s3, s4; - - s1 = inb_p(synth_port); - s2 = inb_p(synth_port + 1); - s3 = inb_p(synth_port + 2); - s4 = inb_p(synth_port + 3); - pr_warn("synth timeout %d %d %d %d\n", s1, s2, s3, s4); - return NULL; -} - -static const char *synth_immediate(struct spk_synth *synth, const char *buf) -{ - u_char ch; - int timeout; - - while ((ch = *buf)) { - if (ch == '\n') - ch = PROCSPEECH; - if (synth_full()) - return buf; - timeout = 1000; - while (synth_writable()) - if (--timeout <= 0) - return oops(); - outb_p(ch, synth_port); - udelay(70); - buf++; - } - return NULL; -} - -static void do_catch_up(struct spk_synth *synth) -{ - u_char ch; - int timeout; - unsigned long flags; - unsigned long jiff_max; - struct var_t *jiffy_delta; - struct var_t *delay_time; - struct var_t *full_time; - int delay_time_val; - int full_time_val; - int jiffy_delta_val; - - jiffy_delta = spk_get_var(JIFFY); - delay_time = spk_get_var(DELAY); - full_time = spk_get_var(FULL); - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - - jiff_max = jiffies + jiffy_delta_val; - while (!kthread_should_stop()) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (speakup_info.flushing) { - speakup_info.flushing = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - synth->flush(synth); - continue; - } - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - break; - } - set_current_state(TASK_INTERRUPTIBLE); - full_time_val = full_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (synth_full()) { - schedule_timeout(msecs_to_jiffies(full_time_val)); - continue; - } - set_current_state(TASK_RUNNING); - timeout = 1000; - while (synth_writable()) - if (--timeout <= 0) - break; - if (timeout <= 0) { - oops(); - break; - } - spin_lock_irqsave(&speakup_info.spinlock, flags); - ch = synth_buffer_getc(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '\n') - ch = PROCSPEECH; - outb_p(ch, synth_port); - SWAIT; - if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { - timeout = 1000; - while (synth_writable()) - if (--timeout <= 0) - break; - if (timeout <= 0) { - oops(); - break; - } - outb_p(PROCSPEECH, synth_port); - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - delay_time_val = delay_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - schedule_timeout(msecs_to_jiffies(delay_time_val)); - jiff_max = jiffies + jiffy_delta_val; - } - } - timeout = 1000; - while (synth_writable()) - if (--timeout <= 0) - break; - if (timeout <= 0) - oops(); - else - outb_p(PROCSPEECH, synth_port); -} - -static void synth_flush(struct spk_synth *synth) -{ - outb_p(SYNTH_CLEAR, synth_port); -} - -static int synth_probe(struct spk_synth *synth) -{ - unsigned int port_val = 0; - int i = 0; - - pr_info("Probing for %s.\n", synth->long_name); - if (port_forced) { - synth_port = port_forced; - pr_info("probe forced to %x by kernel command line\n", - synth_port); - if (synth_request_region(synth_port - 1, SYNTH_IO_EXTENT)) { - pr_warn("sorry, port already reserved\n"); - return -EBUSY; - } - port_val = inb(synth_port); - } else { - for (i = 0; synth_portlist[i]; i++) { - if (synth_request_region(synth_portlist[i], - SYNTH_IO_EXTENT)) { - pr_warn - ("request_region: failed with 0x%x, %d\n", - synth_portlist[i], SYNTH_IO_EXTENT); - continue; - } - port_val = inb(synth_portlist[i]); - if (port_val == 0x80) { - synth_port = synth_portlist[i]; - break; - } - } - } - if (port_val != 0x80) { - pr_info("%s: not found\n", synth->long_name); - synth_release_region(synth_port, SYNTH_IO_EXTENT); - synth_port = 0; - return -ENODEV; - } - pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name, - synth_port, synth_port + SYNTH_IO_EXTENT - 1, - synth->version); - synth->alive = 1; - return 0; -} - -static void keynote_release(void) -{ - spk_stop_serial_interrupt(); - if (synth_port) - synth_release_region(synth_port, SYNTH_IO_EXTENT); - synth_port = 0; -} - -module_param_hw_named(port, port_forced, int, ioport, 0444); -module_param_named(start, synth_keypc.startup, short, 0444); - -MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_keypc); - -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for Keynote Gold PC synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_ltlk.c b/drivers/staging/speakup/speakup_ltlk.c deleted file mode 100644 index 3c59519a871f..000000000000 --- a/drivers/staging/speakup/speakup_ltlk.c +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * specificly written as a driver for the speakup screenreview - * s not a general device driver. - */ -#include "speakup.h" -#include "spk_priv.h" -#include "speakup_dtlk.h" /* local header file for LiteTalk values */ - -#define DRV_VERSION "2.11" -#define PROCSPEECH 0x0d - -static int synth_probe(struct spk_synth *synth); - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\x01+35p" } }, - { CAPS_STOP, .u.s = {"\x01-35p" } }, - { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } }, - { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } }, - { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, - { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, - { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } }, - { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, - { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* - * These attributes will appear in /sys/accessibility/speakup/ltlk. - */ -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute freq_attribute = - __ATTR(freq, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punct_attribute = - __ATTR(punct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute voice_attribute = - __ATTR(voice, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &freq_attribute.attr, - &pitch_attribute.attr, - &punct_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &voice_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_ltlk = { - .name = "ltlk", - .version = DRV_VERSION, - .long_name = "LiteTalk", - .init = "\01@\x01\x31y\n\0", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 40000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = spk_do_catch_up, - .flush = spk_synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = spk_synth_get_index, - .indexing = { - .command = "\x01%di", - .lowindex = 1, - .highindex = 5, - .currindex = 1, - }, - .attributes = { - .attrs = synth_attrs, - .name = "ltlk", - }, -}; - -/* interrogate the LiteTalk and print its settings */ -static void synth_interrogate(struct spk_synth *synth) -{ - unsigned char *t, i; - unsigned char buf[50], rom_v[20]; - - synth->synth_immediate(synth, "\x18\x01?"); - for (i = 0; i < 50; i++) { - buf[i] = synth->io_ops->synth_in(); - if (i > 2 && buf[i] == 0x7f) - break; - } - t = buf + 2; - for (i = 0; *t != '\r'; t++) { - rom_v[i] = *t; - if (++i >= 19) - break; - } - rom_v[i] = 0; - pr_info("%s: ROM version: %s\n", synth->long_name, rom_v); -} - -static int synth_probe(struct spk_synth *synth) -{ - int failed = 0; - - failed = spk_ttyio_synth_probe(synth); - if (failed == 0) - synth_interrogate(synth); - synth->alive = !failed; - return failed; -} - -module_param_named(ser, synth_ltlk.ser, int, 0444); -module_param_named(dev, synth_ltlk.dev_name, charp, 0444); -module_param_named(start, synth_ltlk.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_ltlk); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for DoubleTalk LT/LiteTalk synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c deleted file mode 100644 index 9a7029539f35..000000000000 --- a/drivers/staging/speakup/speakup_soft.c +++ /dev/null @@ -1,430 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* speakup_soft.c - speakup driver to register and make available - * a user space device for software synthesizers. written by: Kirk - * Reiser - * - * Copyright (C) 2003 Kirk Reiser. - * - * this code is specificly written as a driver for the speakup screenreview - * package and is not a general device driver. - */ - -#include -#include /* for misc_register, and MISC_DYNAMIC_MINOR */ -#include /* for poll_wait() */ - -/* schedule(), signal_pending(), TASK_INTERRUPTIBLE */ -#include - -#include "spk_priv.h" -#include "speakup.h" - -#define DRV_VERSION "2.6" -#define PROCSPEECH 0x0d -#define CLEAR_SYNTH 0x18 - -static int softsynth_probe(struct spk_synth *synth); -static void softsynth_release(void); -static int softsynth_is_alive(struct spk_synth *synth); -static unsigned char get_index(struct spk_synth *synth); - -static struct miscdevice synth_device, synthu_device; -static int init_pos; -static int misc_registered; - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\x01+3p" } }, - { CAPS_STOP, .u.s = {"\x01-3p" } }, - { PAUSE, .u.n = {"\x01P" } }, - { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } }, - { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } }, - { INFLECTION, .u.n = {"\x01%dr", 5, 0, 9, 0, 0, NULL } }, - { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, - { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, - { PUNCT, .u.n = {"\x01%db", 0, 0, 2, 0, 0, NULL } }, - { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, - { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* These attributes will appear in /sys/accessibility/speakup/soft. */ - -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute freq_attribute = - __ATTR(freq, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute inflection_attribute = - __ATTR(inflection, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punct_attribute = - __ATTR(punct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute voice_attribute = - __ATTR(voice, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -/* - * We should uncomment the following definition, when we agree on a - * method of passing a language designation to the software synthesizer. - * static struct kobj_attribute lang_attribute = - * __ATTR(lang, 0644, spk_var_show, spk_var_store); - */ - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &freq_attribute.attr, -/* &lang_attribute.attr, */ - &pitch_attribute.attr, - &inflection_attribute.attr, - &punct_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &voice_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_soft = { - .name = "soft", - .version = DRV_VERSION, - .long_name = "software synth", - .init = "\01@\x01\x31y\n", - .procspeech = PROCSPEECH, - .delay = 0, - .trigger = 0, - .jiffies = 0, - .full = 0, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = NULL, - .probe = softsynth_probe, - .release = softsynth_release, - .synth_immediate = NULL, - .catch_up = NULL, - .flush = NULL, - .is_alive = softsynth_is_alive, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = get_index, - .indexing = { - .command = "\x01%di", - .lowindex = 1, - .highindex = 5, - .currindex = 1, - }, - .attributes = { - .attrs = synth_attrs, - .name = "soft", - }, -}; - -static char *get_initstring(void) -{ - static char buf[40]; - char *cp; - struct var_t *var; - - memset(buf, 0, sizeof(buf)); - cp = buf; - var = synth_soft.vars; - while (var->var_id != MAXVARS) { - if (var->var_id != CAPS_START && var->var_id != CAPS_STOP && - var->var_id != PAUSE && var->var_id != DIRECT) - cp = cp + sprintf(cp, var->u.n.synth_fmt, - var->u.n.value); - var++; - } - cp = cp + sprintf(cp, "\n"); - return buf; -} - -static int softsynth_open(struct inode *inode, struct file *fp) -{ - unsigned long flags; - /*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */ - /* return -EPERM; */ - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (synth_soft.alive) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return -EBUSY; - } - synth_soft.alive = 1; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return 0; -} - -static int softsynth_close(struct inode *inode, struct file *fp) -{ - unsigned long flags; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - synth_soft.alive = 0; - init_pos = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - /* Make sure we let applications go before leaving */ - speakup_start_ttys(); - return 0; -} - -static ssize_t softsynthx_read(struct file *fp, char __user *buf, size_t count, - loff_t *pos, int unicode) -{ - int chars_sent = 0; - char __user *cp; - char *init; - size_t bytes_per_ch = unicode ? 3 : 1; - u16 ch; - int empty; - unsigned long flags; - DEFINE_WAIT(wait); - - if (count < bytes_per_ch) - return -EINVAL; - - spin_lock_irqsave(&speakup_info.spinlock, flags); - synth_soft.alive = 1; - while (1) { - prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE); - if (synth_current() == &synth_soft) { - if (!unicode) - synth_buffer_skip_nonlatin1(); - if (!synth_buffer_empty() || speakup_info.flushing) - break; - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (fp->f_flags & O_NONBLOCK) { - finish_wait(&speakup_event, &wait); - return -EAGAIN; - } - if (signal_pending(current)) { - finish_wait(&speakup_event, &wait); - return -ERESTARTSYS; - } - schedule(); - spin_lock_irqsave(&speakup_info.spinlock, flags); - } - finish_wait(&speakup_event, &wait); - - cp = buf; - init = get_initstring(); - - /* Keep 3 bytes available for a 16bit UTF-8-encoded character */ - while (chars_sent <= count - bytes_per_ch) { - if (synth_current() != &synth_soft) - break; - if (speakup_info.flushing) { - speakup_info.flushing = 0; - ch = '\x18'; - } else if (init[init_pos]) { - ch = init[init_pos++]; - } else { - if (!unicode) - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) - break; - ch = synth_buffer_getc(); - } - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - - if ((!unicode && ch < 0x100) || (unicode && ch < 0x80)) { - u_char c = ch; - - if (copy_to_user(cp, &c, 1)) - return -EFAULT; - - chars_sent++; - cp++; - } else if (unicode && ch < 0x800) { - u_char s[2] = { - 0xc0 | (ch >> 6), - 0x80 | (ch & 0x3f) - }; - - if (copy_to_user(cp, s, sizeof(s))) - return -EFAULT; - - chars_sent += sizeof(s); - cp += sizeof(s); - } else if (unicode) { - u_char s[3] = { - 0xe0 | (ch >> 12), - 0x80 | ((ch >> 6) & 0x3f), - 0x80 | (ch & 0x3f) - }; - - if (copy_to_user(cp, s, sizeof(s))) - return -EFAULT; - - chars_sent += sizeof(s); - cp += sizeof(s); - } - - spin_lock_irqsave(&speakup_info.spinlock, flags); - } - *pos += chars_sent; - empty = synth_buffer_empty(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (empty) { - speakup_start_ttys(); - *pos = 0; - } - return chars_sent; -} - -static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, - loff_t *pos) -{ - return softsynthx_read(fp, buf, count, pos, 0); -} - -static ssize_t softsynthu_read(struct file *fp, char __user *buf, size_t count, - loff_t *pos) -{ - return softsynthx_read(fp, buf, count, pos, 1); -} - -static int last_index; - -static ssize_t softsynth_write(struct file *fp, const char __user *buf, - size_t count, loff_t *pos) -{ - unsigned long supplied_index = 0; - int converted; - - converted = kstrtoul_from_user(buf, count, 0, &supplied_index); - - if (converted < 0) - return converted; - - last_index = supplied_index; - return count; -} - -static __poll_t softsynth_poll(struct file *fp, struct poll_table_struct *wait) -{ - unsigned long flags; - __poll_t ret = 0; - - poll_wait(fp, &speakup_event, wait); - - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (synth_current() == &synth_soft && - (!synth_buffer_empty() || speakup_info.flushing)) - ret = EPOLLIN | EPOLLRDNORM; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - return ret; -} - -static unsigned char get_index(struct spk_synth *synth) -{ - int rv; - - rv = last_index; - last_index = 0; - return rv; -} - -static const struct file_operations softsynth_fops = { - .owner = THIS_MODULE, - .poll = softsynth_poll, - .read = softsynth_read, - .write = softsynth_write, - .open = softsynth_open, - .release = softsynth_close, -}; - -static const struct file_operations softsynthu_fops = { - .owner = THIS_MODULE, - .poll = softsynth_poll, - .read = softsynthu_read, - .write = softsynth_write, - .open = softsynth_open, - .release = softsynth_close, -}; - -static int softsynth_probe(struct spk_synth *synth) -{ - if (misc_registered != 0) - return 0; - memset(&synth_device, 0, sizeof(synth_device)); - synth_device.minor = MISC_DYNAMIC_MINOR; - synth_device.name = "softsynth"; - synth_device.fops = &softsynth_fops; - if (misc_register(&synth_device)) { - pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n"); - return -ENODEV; - } - - memset(&synthu_device, 0, sizeof(synthu_device)); - synthu_device.minor = MISC_DYNAMIC_MINOR; - synthu_device.name = "softsynthu"; - synthu_device.fops = &softsynthu_fops; - if (misc_register(&synthu_device)) { - pr_warn("Couldn't initialize miscdevice /dev/softsynthu.\n"); - return -ENODEV; - } - - misc_registered = 1; - pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR %d)\n", - synth_device.minor); - pr_info("initialized device: /dev/softsynthu, node (MAJOR 10, MINOR %d)\n", - synthu_device.minor); - return 0; -} - -static void softsynth_release(void) -{ - misc_deregister(&synth_device); - misc_deregister(&synthu_device); - misc_registered = 0; - pr_info("unregistered /dev/softsynth\n"); - pr_info("unregistered /dev/softsynthu\n"); -} - -static int softsynth_is_alive(struct spk_synth *synth) -{ - if (synth_soft.alive) - return 1; - return 0; -} - -module_param_named(start, synth_soft.startup, short, 0444); - -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_soft); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_DESCRIPTION("Speakup userspace software synthesizer support"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); diff --git a/drivers/staging/speakup/speakup_spkout.c b/drivers/staging/speakup/speakup_spkout.c deleted file mode 100644 index 6e933bf1de2e..000000000000 --- a/drivers/staging/speakup/speakup_spkout.c +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * specificly written as a driver for the speakup screenreview - * s not a general device driver. - */ -#include "spk_priv.h" -#include "speakup.h" - -#define DRV_VERSION "2.11" -#define SYNTH_CLEAR 0x18 -#define PROCSPEECH '\r' - -static void synth_flush(struct spk_synth *synth); - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\x05P+" } }, - { CAPS_STOP, .u.s = {"\x05P-" } }, - { RATE, .u.n = {"\x05R%d", 7, 0, 9, 0, 0, NULL } }, - { PITCH, .u.n = {"\x05P%d", 3, 0, 9, 0, 0, NULL } }, - { VOL, .u.n = {"\x05V%d", 9, 0, 9, 0, 0, NULL } }, - { TONE, .u.n = {"\x05T%c", 8, 0, 25, 65, 0, NULL } }, - { PUNCT, .u.n = {"\x05M%c", 0, 0, 3, 0, 0, "nsma" } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* These attributes will appear in /sys/accessibility/speakup/spkout. */ - -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute punct_attribute = - __ATTR(punct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &punct_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_spkout = { - .name = "spkout", - .version = DRV_VERSION, - .long_name = "Speakout", - .init = "\005W1\005I2\005C3", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 40000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = spk_ttyio_synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = spk_do_catch_up, - .flush = synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = spk_synth_get_index, - .indexing = { - .command = "\x05[%c", - .lowindex = 1, - .highindex = 5, - .currindex = 1, - }, - .attributes = { - .attrs = synth_attrs, - .name = "spkout", - }, -}; - -static void synth_flush(struct spk_synth *synth) -{ - synth->io_ops->flush_buffer(); - synth->io_ops->send_xchar(SYNTH_CLEAR); -} - -module_param_named(ser, synth_spkout.ser, int, 0444); -module_param_named(dev, synth_spkout.dev_name, charp, 0444); -module_param_named(start, synth_spkout.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_spkout); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for Speak Out synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakup_txprt.c b/drivers/staging/speakup/speakup_txprt.c deleted file mode 100644 index a7326f226a5e..000000000000 --- a/drivers/staging/speakup/speakup_txprt.c +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * originally written by: Kirk Reiser - * this version considerably modified by David Borowski, david575@rogers.com - * - * Copyright (C) 1998-99 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - * - * specificly written as a driver for the speakup screenreview - * s not a general device driver. - */ -#include "spk_priv.h" -#include "speakup.h" - -#define DRV_VERSION "2.11" -#define SYNTH_CLEAR 0x18 -#define PROCSPEECH '\r' /* process speech char */ - -static struct var_t vars[] = { - { CAPS_START, .u.s = {"\x05P8" } }, - { CAPS_STOP, .u.s = {"\x05P5" } }, - { RATE, .u.n = {"\x05R%d", 5, 0, 9, 0, 0, NULL } }, - { PITCH, .u.n = {"\x05P%d", 5, 0, 9, 0, 0, NULL } }, - { VOL, .u.n = {"\x05V%d", 5, 0, 9, 0, 0, NULL } }, - { TONE, .u.n = {"\x05T%c", 12, 0, 25, 61, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, - V_LAST_VAR - }; - -/* These attributes will appear in /sys/accessibility/speakup/txprt. */ - -static struct kobj_attribute caps_start_attribute = - __ATTR(caps_start, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute caps_stop_attribute = - __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute pitch_attribute = - __ATTR(pitch, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute rate_attribute = - __ATTR(rate, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute tone_attribute = - __ATTR(tone, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute vol_attribute = - __ATTR(vol, 0644, spk_var_show, spk_var_store); - -static struct kobj_attribute delay_time_attribute = - __ATTR(delay_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute direct_attribute = - __ATTR(direct, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute full_time_attribute = - __ATTR(full_time, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute jiffy_delta_attribute = - __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); -static struct kobj_attribute trigger_time_attribute = - __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); - -/* - * Create a group of attributes so that we can create and destroy them all - * at once. - */ -static struct attribute *synth_attrs[] = { - &caps_start_attribute.attr, - &caps_stop_attribute.attr, - &pitch_attribute.attr, - &rate_attribute.attr, - &tone_attribute.attr, - &vol_attribute.attr, - &delay_time_attribute.attr, - &direct_attribute.attr, - &full_time_attribute.attr, - &jiffy_delta_attribute.attr, - &trigger_time_attribute.attr, - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct spk_synth synth_txprt = { - .name = "txprt", - .version = DRV_VERSION, - .long_name = "Transport", - .init = "\x05N1", - .procspeech = PROCSPEECH, - .clear = SYNTH_CLEAR, - .delay = 500, - .trigger = 50, - .jiffies = 50, - .full = 40000, - .dev_name = SYNTH_DEFAULT_DEV, - .startup = SYNTH_START, - .checkval = SYNTH_CHECK, - .vars = vars, - .io_ops = &spk_ttyio_ops, - .probe = spk_ttyio_synth_probe, - .release = spk_ttyio_release, - .synth_immediate = spk_ttyio_synth_immediate, - .catch_up = spk_do_catch_up, - .flush = spk_synth_flush, - .is_alive = spk_synth_is_alive_restart, - .synth_adjust = NULL, - .read_buff_add = NULL, - .get_index = NULL, - .indexing = { - .command = NULL, - .lowindex = 0, - .highindex = 0, - .currindex = 0, - }, - .attributes = { - .attrs = synth_attrs, - .name = "txprt", - }, -}; - -module_param_named(ser, synth_txprt.ser, int, 0444); -module_param_named(dev, synth_txprt.dev_name, charp, 0444); -module_param_named(start, synth_txprt.startup, short, 0444); - -MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); -MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); -MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); - -module_spk_synth(synth_txprt); - -MODULE_AUTHOR("Kirk Reiser "); -MODULE_AUTHOR("David Borowski"); -MODULE_DESCRIPTION("Speakup support for Transport synthesizers"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/staging/speakup/speakupmap.h b/drivers/staging/speakup/speakupmap.h deleted file mode 100644 index c60d7339b89a..000000000000 --- a/drivers/staging/speakup/speakupmap.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - 119, 62, 6, - 0, 16, 20, 17, 32, 48, 0, - 2, 0, 78, 0, 0, 0, 0, - 3, 0, 79, 0, 0, 0, 0, - 4, 0, 76, 0, 0, 0, 0, - 5, 0, 77, 0, 0, 0, 0, - 6, 0, 74, 0, 0, 0, 0, - 7, 0, 75, 0, 0, 0, 0, - 9, 0, 5, 46, 0, 0, 0, - 10, 0, 4, 0, 0, 0, 0, - 11, 0, 0, 1, 0, 0, 0, - 12, 0, 27, 0, 33, 0, 0, - 19, 0, 47, 0, 0, 0, 0, - 21, 0, 29, 17, 0, 0, 0, - 22, 0, 15, 0, 0, 0, 0, - 23, 0, 14, 0, 0, 0, 28, - 24, 0, 16, 0, 0, 0, 0, - 25, 0, 30, 18, 0, 0, 0, - 28, 0, 3, 26, 0, 0, 0, - 35, 0, 31, 0, 0, 0, 0, - 36, 0, 12, 0, 0, 0, 0, - 37, 0, 11, 0, 0, 0, 22, - 38, 0, 13, 0, 0, 0, 0, - 39, 0, 32, 7, 0, 0, 0, - 40, 0, 23, 0, 0, 0, 0, - 44, 0, 44, 0, 0, 0, 0, - 49, 0, 24, 0, 0, 0, 0, - 50, 0, 9, 19, 6, 0, 0, - 51, 0, 8, 0, 0, 0, 36, - 52, 0, 10, 20, 0, 0, 0, - 53, 0, 25, 0, 0, 0, 0, - 55, 46, 1, 0, 0, 0, 0, - 58, 128, 128, 0, 0, 0, 0, - 59, 0, 45, 0, 0, 0, 0, - 60, 0, 40, 0, 0, 0, 0, - 61, 0, 41, 0, 0, 0, 0, - 62, 0, 42, 0, 0, 0, 0, - 63, 0, 34, 0, 0, 0, 0, - 64, 0, 35, 0, 0, 0, 0, - 65, 0, 37, 0, 0, 0, 0, - 66, 0, 38, 0, 0, 0, 0, - 67, 0, 66, 0, 39, 0, 0, - 68, 0, 67, 0, 0, 0, 0, - 71, 15, 19, 0, 0, 0, 0, - 72, 14, 29, 0, 0, 28, 0, - 73, 16, 17, 0, 0, 0, 0, - 74, 27, 33, 0, 0, 0, 0, - 75, 12, 31, 0, 0, 0, 0, - 76, 11, 21, 0, 0, 22, 0, - 77, 13, 32, 0, 0, 0, 0, - 78, 23, 43, 0, 0, 0, 0, - 79, 9, 20, 0, 0, 0, 0, - 80, 8, 30, 0, 0, 36, 0, - 81, 10, 18, 0, 0, 0, 0, - 82, 128, 128, 0, 0, 0, 0, - 83, 24, 25, 0, 0, 0, 0, - 87, 0, 68, 0, 0, 0, 0, - 88, 0, 69, 0, 0, 0, 0, - 96, 3, 26, 0, 0, 0, 0, - 98, 4, 5, 0, 0, 0, 0, - 99, 2, 0, 0, 0, 0, 0, - 104, 0, 6, 0, 0, 0, 0, - 109, 0, 7, 0, 0, 0, 0, - 125, 128, 128, 0, 0, 0, 0, - 0, 119 diff --git a/drivers/staging/speakup/speakupmap.map b/drivers/staging/speakup/speakupmap.map deleted file mode 100644 index f10d44cf5d7a..000000000000 --- a/drivers/staging/speakup/speakupmap.map +++ /dev/null @@ -1,93 +0,0 @@ -spk key_f9 = punc_level_dec -spk key_f10 = punc_level_inc -spk key_f11 = reading_punc_dec -spk key_f12 = reading_punc_inc -spk key_1 = vol_dec -spk key_2 = vol_inc -spk key_3 = pitch_dec -spk key_4 = pitch_inc -spk key_5 = rate_dec -spk key_6 = rate_inc -key_kpasterisk = toggle_cursoring -ctrl spk key_8 = toggle_cursoring -spk key_kpasterisk = speakup_goto -spk key_f1 = speakup_help -spk key_f2 = set_win -spk key_f3 = clear_win -spk key_f4 = enable_win -spk key_f5 = edit_some -spk key_f6 = edit_most -spk key_f7 = edit_delim -spk key_f8 = edit_repeat -shift spk key_f9 = edit_exnum - key_kp7 = say_prev_line -spk key_kp7 = left_edge - key_kp8 = say_line -double key_kp8 = say_line_indent -spk key_kp8 = say_from_top - key_kp9 = say_next_line -spk key_kp9 = top_edge - key_kpminus = speakup_parked -spk key_kpminus = say_char_num - key_kp4 = say_prev_word -spk key_kp4 = say_from_left - key_kp5 = say_word -double key_kp5 = spell_word -spk key_kp5 = spell_phonetic - key_kp6 = say_next_word -spk key_kp6 = say_to_right - key_kpplus = say_screen -spk key_kpplus = say_win - key_kp1 = say_prev_char -spk key_kp1 = right_edge - key_kp2 = say_char -spk key_kp2 = say_to_bottom -double key_kp2 = say_phonetic_char - key_kp3 = say_next_char -spk key_kp3 = bottom_edge - key_kp0 = spk_key - key_kpdot = say_position -spk key_kpdot = say_attributes -key_kpenter = speakup_quiet -spk key_kpenter = speakup_off -key_sysrq = speech_kill - key_kpslash = speakup_cut -spk key_kpslash = speakup_paste -spk key_pageup = say_first_char -spk key_pagedown = say_last_char -key_capslock = spk_key - spk key_z = spk_lock -key_leftmeta = spk_key -ctrl spk key_0 = speakup_goto -spk key_u = say_prev_line -spk key_i = say_line -double spk key_i = say_line_indent -spk key_o = say_next_line -spk key_minus = speakup_parked -shift spk key_minus = say_char_num -spk key_j = say_prev_word -spk key_k = say_word -double spk key_k = spell_word -spk key_l = say_next_word -spk key_m = say_prev_char -spk key_comma = say_char -double spk key_comma = say_phonetic_char -spk key_dot = say_next_char -spk key_n = say_position - ctrl spk key_m = left_edge - ctrl spk key_y = top_edge - ctrl spk key_dot = right_edge -ctrl spk key_p = bottom_edge -spk key_apostrophe = say_screen -spk key_h = say_from_left -spk key_y = say_from_top -spk key_semicolon = say_to_right -spk key_p = say_to_bottom -spk key_slash = say_attributes - spk key_enter = speakup_quiet - ctrl spk key_enter = speakup_off - spk key_9 = speakup_cut -spk key_8 = speakup_paste -shift spk key_m = say_first_char - ctrl spk key_semicolon = say_last_char -spk key_r = read_all_doc diff --git a/drivers/staging/speakup/spk_priv.h b/drivers/staging/speakup/spk_priv.h deleted file mode 100644 index c75b40838794..000000000000 --- a/drivers/staging/speakup/spk_priv.h +++ /dev/null @@ -1,84 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* spk_priv.h - * review functions for the speakup screen review package. - * originally written by: Kirk Reiser and Andy Berdan. - * - * extensively modified by David Borowski. - * - * Copyright (C) 1998 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - */ -#ifndef _SPEAKUP_PRIVATE_H -#define _SPEAKUP_PRIVATE_H - -#include - -#include "spk_types.h" -#include "spk_priv_keyinfo.h" - -#define V_LAST_VAR { MAXVARS } -#define SPACE 0x20 -#define SYNTH_CHECK 20030716 /* today's date ought to do for check value */ -/* synth flags, for odd synths */ -#define SF_DEC 1 /* to fiddle puncs in alpha strings so it doesn't spell */ -#ifdef MODULE -#define SYNTH_START 1 -#else -#define SYNTH_START 0 -#endif - -#define KT_SPKUP 15 -#define SPK_SYNTH_TIMEOUT 100000 /* in micro-seconds */ -#define SYNTH_DEFAULT_DEV "ttyS0" -#define SYNTH_DEFAULT_SER 0 - -const struct old_serial_port *spk_serial_init(int index); -void spk_stop_serial_interrupt(void); -int spk_wait_for_xmitr(struct spk_synth *in_synth); -void spk_serial_release(void); -void spk_ttyio_release(void); -void spk_ttyio_register_ldisc(void); -void spk_ttyio_unregister_ldisc(void); - -void synth_buffer_skip_nonlatin1(void); -u16 synth_buffer_getc(void); -u16 synth_buffer_peek(void); -int synth_buffer_empty(void); -struct var_t *spk_get_var(enum var_id_t var_id); -ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr, - char *buf); -ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr, - const char *buf, size_t count); - -int spk_serial_synth_probe(struct spk_synth *synth); -int spk_ttyio_synth_probe(struct spk_synth *synth); -const char *spk_serial_synth_immediate(struct spk_synth *synth, - const char *buff); -const char *spk_ttyio_synth_immediate(struct spk_synth *synth, - const char *buff); -void spk_do_catch_up(struct spk_synth *synth); -void spk_do_catch_up_unicode(struct spk_synth *synth); -void spk_synth_flush(struct spk_synth *synth); -unsigned char spk_synth_get_index(struct spk_synth *synth); -int spk_synth_is_alive_nop(struct spk_synth *synth); -int spk_synth_is_alive_restart(struct spk_synth *synth); -__printf(1, 2) -void synth_printf(const char *buf, ...); -void synth_putwc(u16 wc); -void synth_putwc_s(u16 wc); -void synth_putws(const u16 *buf); -void synth_putws_s(const u16 *buf); -int synth_request_region(unsigned long start, unsigned long n); -int synth_release_region(unsigned long start, unsigned long n); -int synth_add(struct spk_synth *in_synth); -void synth_remove(struct spk_synth *in_synth); -struct spk_synth *synth_current(void); - -extern struct speakup_info_t speakup_info; - -extern struct var_t synth_time_vars[]; - -extern struct spk_io_ops spk_serial_io_ops; -extern struct spk_io_ops spk_ttyio_ops; - -#endif diff --git a/drivers/staging/speakup/spk_priv_keyinfo.h b/drivers/staging/speakup/spk_priv_keyinfo.h deleted file mode 100644 index 1f789bd1c678..000000000000 --- a/drivers/staging/speakup/spk_priv_keyinfo.h +++ /dev/null @@ -1,100 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* spk_priv.h - * review functions for the speakup screen review package. - * originally written by: Kirk Reiser and Andy Berdan. - * - * extensively modified by David Borowski. - * - * Copyright (C) 1998 Kirk Reiser. - * Copyright (C) 2003 David Borowski. - */ - -#ifndef _SPEAKUP_KEYINFO_H -#define _SPEAKUP_KEYINFO_H - -#define FIRST_SYNTH_VAR RATE -/* 0 is reserved for no remap */ -#define SPEAKUP_GOTO 0x01 -#define SPEECH_KILL 0x02 -#define SPEAKUP_QUIET 0x03 -#define SPEAKUP_CUT 0x04 -#define SPEAKUP_PASTE 0x05 -#define SAY_FIRST_CHAR 0x06 -#define SAY_LAST_CHAR 0x07 -#define SAY_CHAR 0x08 -#define SAY_PREV_CHAR 0x09 -#define SAY_NEXT_CHAR 0x0a -#define SAY_WORD 0x0b -#define SAY_PREV_WORD 0x0c -#define SAY_NEXT_WORD 0x0d -#define SAY_LINE 0x0e -#define SAY_PREV_LINE 0x0f -#define SAY_NEXT_LINE 0x10 -#define TOP_EDGE 0x11 -#define BOTTOM_EDGE 0x12 -#define LEFT_EDGE 0x13 -#define RIGHT_EDGE 0x14 -#define SPELL_PHONETIC 0x15 -#define SPELL_WORD 0x16 -#define SAY_SCREEN 0x17 -#define SAY_POSITION 0x18 -#define SAY_ATTRIBUTES 0x19 -#define SPEAKUP_OFF 0x1a -#define SPEAKUP_PARKED 0x1b -#define SAY_LINE_INDENT 0x1c -#define SAY_FROM_TOP 0x1d -#define SAY_TO_BOTTOM 0x1e -#define SAY_FROM_LEFT 0x1f -#define SAY_TO_RIGHT 0x20 -#define SAY_CHAR_NUM 0x21 -#define EDIT_SOME 0x22 -#define EDIT_MOST 0x23 -#define SAY_PHONETIC_CHAR 0x24 -#define EDIT_DELIM 0x25 -#define EDIT_REPEAT 0x26 -#define EDIT_EXNUM 0x27 -#define SET_WIN 0x28 -#define CLEAR_WIN 0x29 -#define ENABLE_WIN 0x2a -#define SAY_WIN 0x2b -#define SPK_LOCK 0x2c -#define SPEAKUP_HELP 0x2d -#define TOGGLE_CURSORING 0x2e -#define READ_ALL_DOC 0x2f - -/* one greater than the last func handler */ -#define SPKUP_MAX_FUNC 0x30 - -#define SPK_KEY 0x80 -#define FIRST_EDIT_BITS 0x22 -#define FIRST_SET_VAR SPELL_DELAY - -/* increase if adding more than 0x3f functions */ -#define VAR_START 0x40 - -/* keys for setting variables, must be ordered same as the enum for var_ids */ -/* with dec being even and inc being 1 greater */ -#define SPELL_DELAY_DEC (VAR_START + 0) -#define SPELL_DELAY_INC (SPELL_DELAY_DEC + 1) -#define PUNC_LEVEL_DEC (SPELL_DELAY_DEC + 2) -#define PUNC_LEVEL_INC (PUNC_LEVEL_DEC + 1) -#define READING_PUNC_DEC (PUNC_LEVEL_DEC + 2) -#define READING_PUNC_INC (READING_PUNC_DEC + 1) -#define ATTRIB_BLEEP_DEC (READING_PUNC_DEC + 2) -#define ATTRIB_BLEEP_INC (ATTRIB_BLEEP_DEC + 1) -#define BLEEPS_DEC (ATTRIB_BLEEP_DEC + 2) -#define BLEEPS_INC (BLEEPS_DEC + 1) -#define RATE_DEC (BLEEPS_DEC + 2) -#define RATE_INC (RATE_DEC + 1) -#define PITCH_DEC (RATE_DEC + 2) -#define PITCH_INC (PITCH_DEC + 1) -#define VOL_DEC (PITCH_DEC + 2) -#define VOL_INC (VOL_DEC + 1) -#define TONE_DEC (VOL_DEC + 2) -#define TONE_INC (TONE_DEC + 1) -#define PUNCT_DEC (TONE_DEC + 2) -#define PUNCT_INC (PUNCT_DEC + 1) -#define VOICE_DEC (PUNCT_DEC + 2) -#define VOICE_INC (VOICE_DEC + 1) - -#endif diff --git a/drivers/staging/speakup/spk_ttyio.c b/drivers/staging/speakup/spk_ttyio.c deleted file mode 100644 index 9b95f77f9265..000000000000 --- a/drivers/staging/speakup/spk_ttyio.c +++ /dev/null @@ -1,384 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include - -#include "speakup.h" -#include "spk_types.h" -#include "spk_priv.h" - -struct spk_ldisc_data { - char buf; - struct completion completion; - bool buf_free; -}; - -static struct spk_synth *spk_ttyio_synth; -static struct tty_struct *speakup_tty; -/* mutex to protect against speakup_tty disappearing from underneath us while - * we are using it. this can happen when the device physically unplugged, - * while in use. it also serialises access to speakup_tty. - */ -static DEFINE_MUTEX(speakup_tty_mutex); - -static int ser_to_dev(int ser, dev_t *dev_no) -{ - if (ser < 0 || ser > (255 - 64)) { - pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n"); - return -EINVAL; - } - - *dev_no = MKDEV(4, (64 + ser)); - return 0; -} - -static int get_dev_to_use(struct spk_synth *synth, dev_t *dev_no) -{ - /* use ser only when dev is not specified */ - if (strcmp(synth->dev_name, SYNTH_DEFAULT_DEV) || - synth->ser == SYNTH_DEFAULT_SER) - return tty_dev_name_to_number(synth->dev_name, dev_no); - - return ser_to_dev(synth->ser, dev_no); -} - -static int spk_ttyio_ldisc_open(struct tty_struct *tty) -{ - struct spk_ldisc_data *ldisc_data; - - if (!tty->ops->write) - return -EOPNOTSUPP; - speakup_tty = tty; - - ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL); - if (!ldisc_data) - return -ENOMEM; - - init_completion(&ldisc_data->completion); - ldisc_data->buf_free = true; - speakup_tty->disc_data = ldisc_data; - - return 0; -} - -static void spk_ttyio_ldisc_close(struct tty_struct *tty) -{ - mutex_lock(&speakup_tty_mutex); - kfree(speakup_tty->disc_data); - speakup_tty = NULL; - mutex_unlock(&speakup_tty_mutex); -} - -static int spk_ttyio_receive_buf2(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) -{ - struct spk_ldisc_data *ldisc_data = tty->disc_data; - - if (spk_ttyio_synth->read_buff_add) { - int i; - - for (i = 0; i < count; i++) - spk_ttyio_synth->read_buff_add(cp[i]); - - return count; - } - - if (!ldisc_data->buf_free) - /* ttyio_in will tty_schedule_flip */ - return 0; - - /* Make sure the consumer has read buf before we have seen - * buf_free == true and overwrite buf - */ - mb(); - - ldisc_data->buf = cp[0]; - ldisc_data->buf_free = false; - complete(&ldisc_data->completion); - - return 1; -} - -static struct tty_ldisc_ops spk_ttyio_ldisc_ops = { - .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, - .name = "speakup_ldisc", - .open = spk_ttyio_ldisc_open, - .close = spk_ttyio_ldisc_close, - .receive_buf2 = spk_ttyio_receive_buf2, -}; - -static int spk_ttyio_out(struct spk_synth *in_synth, const char ch); -static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch); -static void spk_ttyio_send_xchar(char ch); -static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear); -static unsigned char spk_ttyio_in(void); -static unsigned char spk_ttyio_in_nowait(void); -static void spk_ttyio_flush_buffer(void); - -struct spk_io_ops spk_ttyio_ops = { - .synth_out = spk_ttyio_out, - .synth_out_unicode = spk_ttyio_out_unicode, - .send_xchar = spk_ttyio_send_xchar, - .tiocmset = spk_ttyio_tiocmset, - .synth_in = spk_ttyio_in, - .synth_in_nowait = spk_ttyio_in_nowait, - .flush_buffer = spk_ttyio_flush_buffer, -}; -EXPORT_SYMBOL_GPL(spk_ttyio_ops); - -static inline void get_termios(struct tty_struct *tty, - struct ktermios *out_termios) -{ - down_read(&tty->termios_rwsem); - *out_termios = tty->termios; - up_read(&tty->termios_rwsem); -} - -static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) -{ - int ret = 0; - struct tty_struct *tty; - struct ktermios tmp_termios; - dev_t dev; - - ret = get_dev_to_use(synth, &dev); - if (ret) - return ret; - - tty = tty_kopen(dev); - if (IS_ERR(tty)) - return PTR_ERR(tty); - - if (tty->ops->open) - ret = tty->ops->open(tty, NULL); - else - ret = -ENODEV; - - if (ret) { - tty_unlock(tty); - return ret; - } - - clear_bit(TTY_HUPPED, &tty->flags); - /* ensure hardware flow control is enabled */ - get_termios(tty, &tmp_termios); - if (!(tmp_termios.c_cflag & CRTSCTS)) { - tmp_termios.c_cflag |= CRTSCTS; - tty_set_termios(tty, &tmp_termios); - /* - * check c_cflag to see if it's updated as tty_set_termios - * may not return error even when no tty bits are - * changed by the request. - */ - get_termios(tty, &tmp_termios); - if (!(tmp_termios.c_cflag & CRTSCTS)) - pr_warn("speakup: Failed to set hardware flow control\n"); - } - - tty_unlock(tty); - - ret = tty_set_ldisc(tty, N_SPEAKUP); - if (ret) - pr_err("speakup: Failed to set N_SPEAKUP on tty\n"); - - return ret; -} - -void spk_ttyio_register_ldisc(void) -{ - if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops)) - pr_warn("speakup: Error registering line discipline. Most synths won't work.\n"); -} - -void spk_ttyio_unregister_ldisc(void) -{ - if (tty_unregister_ldisc(N_SPEAKUP)) - pr_warn("speakup: Couldn't unregister ldisc\n"); -} - -static int spk_ttyio_out(struct spk_synth *in_synth, const char ch) -{ - mutex_lock(&speakup_tty_mutex); - if (in_synth->alive && speakup_tty && speakup_tty->ops->write) { - int ret = speakup_tty->ops->write(speakup_tty, &ch, 1); - - mutex_unlock(&speakup_tty_mutex); - if (ret == 0) - /* No room */ - return 0; - if (ret < 0) { - pr_warn("%s: I/O error, deactivating speakup\n", - in_synth->long_name); - /* No synth any more, so nobody will restart TTYs, - * and we thus need to do it ourselves. Now that there - * is no synth we can let application flood anyway - */ - in_synth->alive = 0; - speakup_start_ttys(); - return 0; - } - return 1; - } - - mutex_unlock(&speakup_tty_mutex); - return 0; -} - -static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch) -{ - int ret; - - if (ch < 0x80) { - ret = spk_ttyio_out(in_synth, ch); - } else if (ch < 0x800) { - ret = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6)); - ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f)); - } else { - ret = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12)); - ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f)); - ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f)); - } - return ret; -} - -static int check_tty(struct tty_struct *tty) -{ - if (!tty) { - pr_warn("%s: I/O error, deactivating speakup\n", - spk_ttyio_synth->long_name); - /* No synth any more, so nobody will restart TTYs, and we thus - * need to do it ourselves. Now that there is no synth we can - * let application flood anyway - */ - spk_ttyio_synth->alive = 0; - speakup_start_ttys(); - return 1; - } - - return 0; -} - -static void spk_ttyio_send_xchar(char ch) -{ - mutex_lock(&speakup_tty_mutex); - if (check_tty(speakup_tty)) { - mutex_unlock(&speakup_tty_mutex); - return; - } - - if (speakup_tty->ops->send_xchar) - speakup_tty->ops->send_xchar(speakup_tty, ch); - mutex_unlock(&speakup_tty_mutex); -} - -static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear) -{ - mutex_lock(&speakup_tty_mutex); - if (check_tty(speakup_tty)) { - mutex_unlock(&speakup_tty_mutex); - return; - } - - if (speakup_tty->ops->tiocmset) - speakup_tty->ops->tiocmset(speakup_tty, set, clear); - mutex_unlock(&speakup_tty_mutex); -} - -static unsigned char ttyio_in(int timeout) -{ - struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data; - char rv; - - if (wait_for_completion_timeout(&ldisc_data->completion, - usecs_to_jiffies(timeout)) == 0) { - if (timeout) - pr_warn("spk_ttyio: timeout (%d) while waiting for input\n", - timeout); - return 0xff; - } - - rv = ldisc_data->buf; - /* Make sure we have read buf before we set buf_free to let - * the producer overwrite it - */ - mb(); - ldisc_data->buf_free = true; - /* Let TTY push more characters */ - tty_schedule_flip(speakup_tty->port); - - return rv; -} - -static unsigned char spk_ttyio_in(void) -{ - return ttyio_in(SPK_SYNTH_TIMEOUT); -} - -static unsigned char spk_ttyio_in_nowait(void) -{ - u8 rv = ttyio_in(0); - - return (rv == 0xff) ? 0 : rv; -} - -static void spk_ttyio_flush_buffer(void) -{ - mutex_lock(&speakup_tty_mutex); - if (check_tty(speakup_tty)) { - mutex_unlock(&speakup_tty_mutex); - return; - } - - if (speakup_tty->ops->flush_buffer) - speakup_tty->ops->flush_buffer(speakup_tty); - - mutex_unlock(&speakup_tty_mutex); -} - -int spk_ttyio_synth_probe(struct spk_synth *synth) -{ - int rv = spk_ttyio_initialise_ldisc(synth); - - if (rv) - return rv; - - synth->alive = 1; - spk_ttyio_synth = synth; - - return 0; -} -EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe); - -void spk_ttyio_release(void) -{ - if (!speakup_tty) - return; - - tty_lock(speakup_tty); - - if (speakup_tty->ops->close) - speakup_tty->ops->close(speakup_tty, NULL); - - tty_ldisc_flush(speakup_tty); - tty_unlock(speakup_tty); - tty_kclose(speakup_tty); -} -EXPORT_SYMBOL_GPL(spk_ttyio_release); - -const char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff) -{ - u_char ch; - - while ((ch = *buff)) { - if (ch == '\n') - ch = synth->procspeech; - if (tty_write_room(speakup_tty) < 1 || - !synth->io_ops->synth_out(synth, ch)) - return buff; - buff++; - } - return NULL; -} -EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate); diff --git a/drivers/staging/speakup/spk_types.h b/drivers/staging/speakup/spk_types.h deleted file mode 100644 index d3272c6d199a..000000000000 --- a/drivers/staging/speakup/spk_types.h +++ /dev/null @@ -1,221 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef SPEAKUP_TYPES_H -#define SPEAKUP_TYPES_H - -/* This file includes all of the typedefs and structs used in speakup. */ - -#include -#include -#include -#include -#include /* for wait_queue */ -#include /* for __init */ -#include -#include -#include -#include -#include /* for inb_p, outb_p, inb, outb, etc... */ -#include - -enum var_type_t { - VAR_NUM = 0, - VAR_TIME, - VAR_STRING, - VAR_PROC -}; - -enum { - E_DEFAULT = 0, - E_SET, - E_INC, - E_DEC, - E_NEW_DEFAULT, -}; - -enum var_id_t { - VERSION = 0, SYNTH, SILENT, SYNTH_DIRECT, - KEYMAP, CHARS, - PUNC_SOME, PUNC_MOST, PUNC_ALL, - DELIM, REPEATS, EXNUMBER, - DELAY, TRIGGER, JIFFY, FULL, /* all timers must be together */ - BLEEP_TIME, CURSOR_TIME, BELL_POS, - SAY_CONTROL, SAY_WORD_CTL, NO_INTERRUPT, KEY_ECHO, - SPELL_DELAY, PUNC_LEVEL, READING_PUNC, - ATTRIB_BLEEP, BLEEPS, - RATE, PITCH, INFLECTION, VOL, TONE, PUNCT, VOICE, FREQUENCY, LANG, - DIRECT, PAUSE, - CAPS_START, CAPS_STOP, CHARTAB, - MAXVARS -}; - -typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch, - u_short key); - -#define COLOR_BUFFER_SIZE 160 - -struct spk_highlight_color_track { - /* Count of each background color */ - unsigned int bgcount[8]; - /* Buffer for characters drawn with each background color */ - u16 highbuf[8][COLOR_BUFFER_SIZE]; - /* Current index into highbuf */ - unsigned int highsize[8]; - /* Reading Position for each color */ - u_long rpos[8], rx[8], ry[8]; - /* Real Cursor Y Position */ - ulong cy; -}; - -struct st_spk_t { - u_long reading_x, cursor_x; - u_long reading_y, cursor_y; - u_long reading_pos, cursor_pos; - u_long go_x, go_pos; - u_long w_top, w_bottom, w_left, w_right; - u_char w_start, w_enabled; - u_char reading_attr, old_attr; - char parked, shut_up; - struct spk_highlight_color_track ht; - int tty_stopped; -}; - -/* now some defines to make these easier to use. */ -#define spk_shut_up (speakup_console[vc->vc_num]->shut_up) -#define spk_killed (speakup_console[vc->vc_num]->shut_up & 0x40) -#define spk_x (speakup_console[vc->vc_num]->reading_x) -#define spk_cx (speakup_console[vc->vc_num]->cursor_x) -#define spk_y (speakup_console[vc->vc_num]->reading_y) -#define spk_cy (speakup_console[vc->vc_num]->cursor_y) -#define spk_pos (speakup_console[vc->vc_num]->reading_pos) -#define spk_cp (speakup_console[vc->vc_num]->cursor_pos) -#define goto_pos (speakup_console[vc->vc_num]->go_pos) -#define goto_x (speakup_console[vc->vc_num]->go_x) -#define win_top (speakup_console[vc->vc_num]->w_top) -#define win_bottom (speakup_console[vc->vc_num]->w_bottom) -#define win_left (speakup_console[vc->vc_num]->w_left) -#define win_right (speakup_console[vc->vc_num]->w_right) -#define win_start (speakup_console[vc->vc_num]->w_start) -#define win_enabled (speakup_console[vc->vc_num]->w_enabled) -#define spk_attr (speakup_console[vc->vc_num]->reading_attr) -#define spk_old_attr (speakup_console[vc->vc_num]->old_attr) -#define spk_parked (speakup_console[vc->vc_num]->parked) - -struct st_var_header { - char *name; - enum var_id_t var_id; - enum var_type_t var_type; - void *p_val; /* ptr to programs variable to store value */ - void *data; /* ptr to the vars data */ -}; - -struct num_var_t { - char *synth_fmt; - int default_val; - int low; - int high; - short offset, multiplier; /* for fiddling rates etc. */ - char *out_str; /* if synth needs char representation of number */ - int value; /* current value */ -}; - -struct punc_var_t { - enum var_id_t var_id; - short value; -}; - -struct string_var_t { - char *default_val; -}; - -struct var_t { - enum var_id_t var_id; - union { - struct num_var_t n; - struct string_var_t s; - } u; -}; - -struct st_bits_data { /* punc, repeats, word delim bits */ - char *name; - char *value; - short mask; -}; - -struct synth_indexing { - char *command; - unsigned char lowindex; - unsigned char highindex; - unsigned char currindex; -}; - -struct spk_synth; - -struct spk_io_ops { - int (*synth_out)(struct spk_synth *synth, const char ch); - int (*synth_out_unicode)(struct spk_synth *synth, u16 ch); - void (*send_xchar)(char ch); - void (*tiocmset)(unsigned int set, unsigned int clear); - unsigned char (*synth_in)(void); - unsigned char (*synth_in_nowait)(void); - void (*flush_buffer)(void); -}; - -struct spk_synth { - struct list_head node; - - const char *name; - const char *version; - const char *long_name; - const char *init; - char procspeech; - char clear; - int delay; - int trigger; - int jiffies; - int full; - int ser; - char *dev_name; - short flags; - short startup; - const int checkval; /* for validating a proper synth module */ - struct var_t *vars; - int *default_pitch; - int *default_vol; - struct spk_io_ops *io_ops; - int (*probe)(struct spk_synth *synth); - void (*release)(void); - const char *(*synth_immediate)(struct spk_synth *synth, - const char *buff); - void (*catch_up)(struct spk_synth *synth); - void (*flush)(struct spk_synth *synth); - int (*is_alive)(struct spk_synth *synth); - int (*synth_adjust)(struct st_var_header *var); - void (*read_buff_add)(u_char c); - unsigned char (*get_index)(struct spk_synth *synth); - struct synth_indexing indexing; - int alive; - struct attribute_group attributes; -}; - -/* - * module_spk_synth() - Helper macro for registering a speakup driver - * @__spk_synth: spk_synth struct - * Helper macro for speakup drivers which do not do anything special in module - * init/exit. This eliminates a lot of boilerplate. Each module may only - * use this macro once, and calling it replaces module_init() and module_exit() - */ -#define module_spk_synth(__spk_synth) \ - module_driver(__spk_synth, synth_add, synth_remove) - -struct speakup_info_t { - spinlock_t spinlock; - int port_tts; - int flushing; -}; - -struct bleep { - short freq; - unsigned long jiffies; - int active; -}; -#endif diff --git a/drivers/staging/speakup/spkguide.txt b/drivers/staging/speakup/spkguide.txt deleted file mode 100644 index 3782f6a09e97..000000000000 --- a/drivers/staging/speakup/spkguide.txt +++ /dev/null @@ -1,1575 +0,0 @@ - -The Speakup User's Guide -For Speakup 3.1.2 and Later -By Gene Collins -Updated by others -Last modified on Mon Sep 27 14:26:31 2010 -Document version 1.3 - -Copyright (c) 2005 Gene Collins -Copyright (c) 2008 Samuel Thibault -Copyright (c) 2009, 2010 the Speakup Team - -Permission is granted to copy, distribute and/or modify this document -under the terms of the GNU Free Documentation License, Version 1.2 or -any later version published by the Free Software Foundation; with no -Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A -copy of the license is included in the section entitled "GNU Free -Documentation License". - -Preface - -The purpose of this document is to familiarize users with the user -interface to Speakup, a Linux Screen Reader. If you need instructions -for installing or obtaining Speakup, visit the web site at -http://linux-speakup.org/. Speakup is a set of patches to the standard -Linux kernel source tree. It can be built as a series of modules, or as -a part of a monolithic kernel. These details are beyond the scope of -this manual, but the user may need to be aware of the module -capabilities, depending on how your system administrator has installed -Speakup. If Speakup is built as a part of a monolithic kernel, and the -user is using a hardware synthesizer, then Speakup will be able to -provide speech access from the time the kernel is loaded, until the time -the system is shutdown. This means that if you have obtained Linux -installation media for a distribution which includes Speakup as a part -of its kernel, you will be able, as a blind person, to install Linux -with speech access unaided by a sighted person. Again, these details -are beyond the scope of this manual, but the user should be aware of -them. See the web site mentioned above for further details. - -1. Starting Speakup - -If your system administrator has installed Speakup to work with your -specific synthesizer by default, then all you need to do to use Speakup -is to boot your system, and Speakup should come up talking. This -assumes of course that your synthesizer is a supported hardware -synthesizer, and that it is either installed in or connected to your -system, and is if necessary powered on. - -It is possible, however, that Speakup may have been compiled into the -kernel with no default synthesizer. It is even possible that your -kernel has been compiled with support for some of the supported -synthesizers and not others. If you find that this is the case, and -your synthesizer is supported but not available, complain to the person -who compiled and installed your kernel. Or better yet, go to the web -site, and learn how to patch Speakup into your own kernel source, and -build and install your own kernel. - -If your kernel has been compiled with Speakup, and has no default -synthesizer set, or you would like to use a different synthesizer than -the default one, then you may issue the following command at the boot -prompt of your boot loader. - -linux speakup.synth=ltlk - -This command would tell Speakup to look for and use a LiteTalk or -DoubleTalk LT at boot up. You may replace the ltlk synthesizer keyword -with the keyword for whatever synthesizer you wish to use. The -speakup.synth parameter will accept the following keywords, provided -that support for the related synthesizers has been built into the -kernel. - -acntsa -- Accent SA -acntpc -- Accent PC -apollo -- Apollo -audptr -- Audapter -bns -- Braille 'n Speak -dectlk -- DecTalk Express (old and new, db9 serial only) -decext -- DecTalk (old) External -dtlk -- DoubleTalk PC -keypc -- Keynote Gold PC -ltlk -- DoubleTalk LT, LiteTalk, or external Tripletalk (db9 serial only) -spkout -- Speak Out -txprt -- Transport -dummy -- Plain text terminal - -Note: Speakup does * NOT * support usb connections! Speakup also does * -NOT * support the internal Tripletalk! - -Speakup does support two other synthesizers, but because they work in -conjunction with other software, they must be loaded as modules after -their related software is loaded, and so are not available at boot up. -These are as follows: - -decpc -- DecTalk PC (not available at boot up) -soft -- One of several software synthesizers (not available at boot up) - -See the sections on loading modules and software synthesizers later in -this manual for further details. It should be noted here that the -speakup.synth boot parameter will have no effect if Speakup has been -compiled as modules. In order for Speakup modules to be loaded during -the boot process, such action must be configured by your system -administrator. This will mean that you will hear some, but not all, of -the bootup messages. - -2. Basic operation - -Once you have booted the system, and if necessary, have supplied the -proper bootup parameter for your synthesizer, Speakup will begin -talking as soon as the kernel is loaded. In fact, it will talk a lot! -It will speak all the boot up messages that the kernel prints on the -screen during the boot process. This is because Speakup is not a -separate screen reader, but is actually built into the operating -system. Since almost all console applications must print text on the -screen using the kernel, and must get their keyboard input through the -kernel, they are automatically handled properly by Speakup. There are a -few exceptions, but we'll come to those later. - -Note: In this guide I will refer to the numeric keypad as the keypad. -This is done because the speakupmap.map file referred to later in this -manual uses the term keypad instead of numeric keypad. Also I'm lazy -and would rather only type one word. So keypad it is. Got it? Good. - -Most of the Speakup review keys are located on the keypad at the far -right of the keyboard. The numlock key should be off, in order for these -to work. If you toggle the numlock on, the keypad will produce numbers, -which is exactly what you want for spreadsheets and such. For the -purposes of this guide, you should have the numlock turned off, which is -its default state at bootup. - -You probably won't want to listen to all the bootup messages every time -you start your system, though it's a good idea to listen to them at -least once, just so you'll know what kind of information is available to -you during the boot process. You can always review these messages after -bootup with the command: - -dmesg | more - -In order to speed the boot process, and to silence the speaking of the -bootup messages, just press the keypad enter key. This key is located -in the bottom right corner of the keypad. Speakup will shut up and stay -that way, until you press another key. - -You can check to see if the boot process has completed by pressing the 8 -key on the keypad, which reads the current line. This also has the -effect of starting Speakup talking again, so you can press keypad enter -to silence it again if the boot process has not completed. - -When the boot process is complete, you will arrive at a "login" prompt. -At this point, you'll need to type in your user id and password, as -provided by your system administrator. You will hear Speakup speak the -letters of your user id as you type it, but not the password. This is -because the password is not displayed on the screen for security -reasons. This has nothing to do with Speakup, it's a Linux security -feature. - -Once you've logged in, you can run any Linux command or program which is -allowed by your user id. Normal users will not be able to run programs -which require root privileges. - -When you are running a program or command, Speakup will automatically -speak new text as it arrives on the screen. You can at any time silence -the speech with keypad enter, or use any of the Speakup review keys. - -Here are some basic Speakup review keys, and a short description of what -they do. - -keypad 1 -- read previous character -keypad 2 -- read current character (pressing keypad 2 twice rapidly will speak - the current character phonetically) -keypad 3 -- read next character -keypad 4 -- read previous word -keypad 5 -- read current word (press twice rapidly to spell the current word) -keypad 6 -- read next word -keypad 7 -- read previous line -keypad 8 -- read current line (press twice rapidly to hear how much the - text on the current line is indented) -keypad 9 -- read next line -keypad period -- speak current cursor position and announce current - virtual console - -It's also worth noting that the insert key on the keypad is mapped -as the speakup key. Instead of pressing and releasing this key, as you -do under DOS or Windows, you hold it like a shift key, and press other -keys in combination with it. For example, repeatedly holding keypad -insert, from now on called speakup, and keypad enter will toggle the -speaking of new text on the screen on and off. This is not the same as -just pressing keypad enter by itself, which just silences the speech -until you hit another key. When you hit speakup plus keypad enter, -Speakup will say, "You turned me off.", or "Hey, that's better." When -Speakup is turned off, no new text on the screen will be spoken. You -can still use the reading controls to review the screen however. - -3. Using the Speakup Help System - -In order to enter the Speakup help system, press and hold the speakup -key (remember that this is the keypad insert key), and press the f1 key. -You will hear the message: - -"Press space to leave help, cursor up or down to scroll, or a letter to -go to commands in list." - -When you press the spacebar to leave the help system, you will hear: - -"Leaving help." - -While you are in the Speakup help system, you can scroll up or down -through the list of available commands using the cursor keys. The list -of commands is arranged in alphabetical order. If you wish to jump to -commands in a specific part of the alphabet, you may press the letter of -the alphabet you wish to jump to. - -You can also just explore by typing keyboard keys. Pressing keys will -cause Speakup to speak the command associated with that key. For -example, if you press the keypad 8 key, you will hear: - -"Keypad 8 is line, say current." - -You'll notice that some commands do not have keys assigned to them. -This is because they are very infrequently used commands, and are also -accessible through the sys system. We'll discuss the sys system later -in this manual. - -You'll also notice that some commands have two keys assigned to them. -This is because Speakup has a built in set of alternative key bindings -for laptop users. The alternate speakup key is the caps lock key. You -can press and hold the caps lock key, while pressing an alternate -speakup command key to activate the command. On most laptops, the -numeric keypad is defined as the keys in the j k l area of the keyboard. - -There is usually a function key which turns this keypad function on and -off, and some other key which controls the numlock state. Toggling the -keypad functionality on and off can become a royal pain. So, Speakup -gives you a simple way to get at an alternative set of key mappings for -your laptop. These are also available by default on desktop systems, -because Speakup does not know whether it is running on a desktop or -laptop. So you may choose which set of Speakup keys to use. Some -system administrators may have chosen to compile Speakup for a desktop -system without this set of alternate key bindings, but these details are -beyond the scope of this manual. To use the caps lock for its normal -purpose, hold the shift key while toggling the caps lock on and off. We -should note here, that holding the caps lock key and pressing the z key -will toggle the alternate j k l keypad on and off. - -4. Keys and Their Assigned Commands - -In this section, we'll go through a list of all the speakup keys and -commands. You can also get a list of commands and assigned keys from -the help system. - -The following list was taken from the speakupmap.map file. Key -assignments are on the left of the equal sign, and the associated -Speakup commands are on the right. The designation "spk" means to press -and hold the speakup key, a.k.a. keypad insert, a.k.a. caps lock, while -pressing the other specified key. - -spk key_f9 = punc_level_dec -spk key_f10 = punc_level_inc -spk key_f11 = reading_punc_dec -spk key_f12 = reading_punc_inc -spk key_1 = vol_dec -spk key_2 = vol_inc -spk key_3 = pitch_dec -spk key_4 = pitch_inc -spk key_5 = rate_dec -spk key_6 = rate_inc -key_kpasterisk = toggle_cursoring -spk key_kpasterisk = speakup_goto -spk key_f1 = speakup_help -spk key_f2 = set_win -spk key_f3 = clear_win -spk key_f4 = enable_win -spk key_f5 = edit_some -spk key_f6 = edit_most -spk key_f7 = edit_delim -spk key_f8 = edit_repeat -shift spk key_f9 = edit_exnum - key_kp7 = say_prev_line -spk key_kp7 = left_edge - key_kp8 = say_line -double key_kp8 = say_line_indent -spk key_kp8 = say_from_top - key_kp9 = say_next_line -spk key_kp9 = top_edge - key_kpminus = speakup_parked -spk key_kpminus = say_char_num - key_kp4 = say_prev_word -spk key_kp4 = say_from_left - key_kp5 = say_word -double key_kp5 = spell_word -spk key_kp5 = spell_phonetic - key_kp6 = say_next_word -spk key_kp6 = say_to_right - key_kpplus = say_screen -spk key_kpplus = say_win - key_kp1 = say_prev_char -spk key_kp1 = right_edge - key_kp2 = say_char -spk key_kp2 = say_to_bottom -double key_kp2 = say_phonetic_char - key_kp3 = say_next_char -spk key_kp3 = bottom_edge - key_kp0 = spk_key - key_kpdot = say_position -spk key_kpdot = say_attributes -key_kpenter = speakup_quiet -spk key_kpenter = speakup_off -key_sysrq = speech_kill - key_kpslash = speakup_cut -spk key_kpslash = speakup_paste -spk key_pageup = say_first_char -spk key_pagedown = say_last_char -key_capslock = spk_key - spk key_z = spk_lock -key_leftmeta = spk_key -ctrl spk key_0 = speakup_goto -spk key_u = say_prev_line -spk key_i = say_line -double spk key_i = say_line_indent -spk key_o = say_next_line -spk key_minus = speakup_parked -shift spk key_minus = say_char_num -spk key_j = say_prev_word -spk key_k = say_word -double spk key_k = spell_word -spk key_l = say_next_word -spk key_m = say_prev_char -spk key_comma = say_char -double spk key_comma = say_phonetic_char -spk key_dot = say_next_char -spk key_n = say_position - ctrl spk key_m = left_edge - ctrl spk key_y = top_edge - ctrl spk key_dot = right_edge -ctrl spk key_p = bottom_edge -spk key_apostrophe = say_screen -spk key_h = say_from_left -spk key_y = say_from_top -spk key_semicolon = say_to_right -spk key_p = say_to_bottom -spk key_slash = say_attributes - spk key_enter = speakup_quiet - ctrl spk key_enter = speakup_off - spk key_9 = speakup_cut -spk key_8 = speakup_paste -shift spk key_m = say_first_char - ctrl spk key_semicolon = say_last_char - -5. The Speakup Sys System - -The Speakup screen reader also creates a speakup subdirectory as a part -of the sys system. - -As a convenience, run as root - -ln -s /sys/accessibility/speakup /speakup - -to directly access speakup parameters from /speakup. -You can see these entries by typing the command: - -ls -1 /speakup/* - -If you issue the above ls command, you will get back something like -this: - -/speakup/attrib_bleep -/speakup/bell_pos -/speakup/bleep_time -/speakup/bleeps -/speakup/cursor_time -/speakup/delimiters -/speakup/ex_num -/speakup/key_echo -/speakup/keymap -/speakup/no_interrupt -/speakup/punc_all -/speakup/punc_level -/speakup/punc_most -/speakup/punc_some -/speakup/reading_punc -/speakup/repeats -/speakup/say_control -/speakup/say_word_ctl -/speakup/silent -/speakup/spell_delay -/speakup/synth -/speakup/synth_direct -/speakup/version - -/speakup/i18n: -announcements -characters -chartab -colors -ctl_keys -formatted -function_names -key_names -states - -/speakup/soft: -caps_start -caps_stop -delay_time -direct -freq -full_time -jiffy_delta -pitch -inflection -punct -rate -tone -trigger_time -voice -vol - -Notice the two subdirectories of /speakup: /speakup/i18n and -/speakup/soft. -The i18n subdirectory is described in a later section. -The files under /speakup/soft represent settings that are specific to the -driver for the software synthesizer. If you use the LiteTalk, your -synthesizer-specific settings would be found in /speakup/ltlk. In other words, -a subdirectory named /speakup/KWD is created to hold parameters specific -to the device whose keyword is KWD. -These parameters include volume, rate, pitch, and others. - -In addition to using the Speakup hot keys to change such things as -volume, pitch, and rate, you can also echo values to the appropriate -entry in the /speakup directory. This is very useful, since it -lets you control Speakup parameters from within a script. How you -would write such scripts is somewhat beyond the scope of this manual, -but I will include a couple of simple examples here to give you a -general idea of what such scripts can do. - -Suppose for example, that you wanted to control both the punctuation -level and the reading punctuation level at the same time. For -simplicity, we'll call them punc0, punc1, punc2, and punc3. The scripts -might look something like this: - -#!/bin/bash -# punc0 -# set punc and reading punc levels to 0 -echo 0 >/speakup/punc_level -echo 0 >/speakup/reading_punc -echo Punctuation level set to 0. - -#!/bin/bash -# punc1 -# set punc and reading punc levels to 1 -echo 1 >/speakup/punc_level -echo 1 >/speakup/reading_punc -echo Punctuation level set to 1. - -#!/bin/bash -# punc2 -# set punc and reading punc levels to 2 -echo 2 >/speakup/punc_level -echo 2 >/speakup/reading_punc -echo Punctuation level set to 2. - -#!/bin/bash -# punc3 -# set punc and reading punc levels to 3 -echo 3 >/speakup/punc_level -echo 3 >/speakup/reading_punc -echo Punctuation level set to 3. - -If you were to store these four small scripts in a directory in your -path, perhaps /usr/local/bin, and set the permissions to 755 with the -chmod command, then you could change the default reading punc and -punctuation levels at the same time by issuing just one command. For -example, if you were to execute the punc3 command at your shell prompt, -then the reading punc and punc level would both get set to 3. - -I should note that the above scripts were written to work with bash, but -regardless of which shell you use, you should be able to do something -similar. - -The Speakup sys system also has another interesting use. You can echo -Speakup parameters into the sys system in a script during system -startup, and speakup will return to your preferred parameters every time -the system is rebooted. - -Most of the Speakup sys parameters can be manipulated by a regular user -on the system. However, there are a few parameters that are dangerous -enough that they should only be manipulated by the root user on your -system. There are even some parameters that are read only, and cannot -be written to at all. For example, the version entry in the Speakup -sys system is read only. This is because there is no reason for a user -to tamper with the version number which is reported by Speakup. Doing -an ls -l on /speakup/version will return this: - --r--r--r-- 1 root root 0 Mar 21 13:46 /speakup/version - -As you can see, the version entry in the Speakup sys system is read -only, is owned by root, and belongs to the root group. Doing a cat of -/speakup/version will display the Speakup version number, like -this: - -cat /speakup/version -Speakup v-2.00 CVS: Thu Oct 21 10:38:21 EDT 2004 -synth dtlk version 1.1 - -The display shows the Speakup version number, along with the version -number of the driver for the current synthesizer. - -Looking at entries in the Speakup sys system can be useful in many -ways. For example, you might wish to know what level your volume is set -at. You could type: - -cat /speakup/KWD/vol -# Replace KWD with the keyword for your synthesizer, E.G., ltlk for LiteTalk. -5 - -The number five which comes back is the level at which the synthesizer -volume is set at. - -All the entries in the Speakup sys system are readable, some are -writable by root only, and some are writable by everyone. Unless you -know what you are doing, you should probably leave the ones that are -writable by root only alone. Most of the names are self explanatory. -Vol for controlling volume, pitch for pitch, inflection for pitch range, rate -for controlling speaking rate, etc. If you find one you aren't sure about, you -can post a query on the Speakup list. - -6. Changing Synthesizers - -It is possible to change to a different synthesizer while speakup is -running. In other words, it is not necessary to reboot the system -in order to use a different synthesizer. You can simply echo the -synthesizer keyword to the /speakup/synth sys entry. -Depending on your situation, you may wish to echo none to the synth -sys entry, to disable speech while one synthesizer is disconnected and -a second one is connected in its place. Then echo the keyword for the -new synthesizer into the synth sys entry in order to start speech -with the newly connected synthesizer. See the list of synthesizer -keywords in section 1 to find the keyword which matches your synth. - -7. Loading modules - -As mentioned earlier, Speakup can either be completely compiled into the -kernel, with the exception of the help module, or it can be compiled as -a series of modules. When compiled as modules, Speakup will only be -able to speak some of the bootup messages if your system administrator -has configured the system to load the modules at boo time. The modules -can be loaded after the file systems have been checked and mounted, or -from an initrd. There is a third possibility. Speakup can be compiled -with some components built into the kernel, and others as modules. As -we'll see in the next section, this is particularly useful when you are -working with software synthesizers. - -If Speakup is completely compiled as modules, then you must use the -modprobe command to load Speakup. You do this by loading the module for -the synthesizer driver you wish to use. The driver modules are all -named speakup_, where is the keyword for the -synthesizer you want. So, in order to load the driver for the DecTalk -Express, you would type the following command: - -modprobe speakup_dectlk - -Issuing this command would load the DecTalk Express driver and all other -related Speakup modules necessary to get Speakup up and running. - -To completely unload Speakup, again presuming that it is entirely built -as modules, you would give the command: - -modprobe -r speakup_dectlk - -The above command assumes you were running a DecTalk Express. If you -were using a different synth, then you would substitute its keyword in -place of dectlk. - -If you have multiple drivers loaded, you need to unload all of them, in -order to completely unload Speakup. -For example, if you have loaded both the dectlk and ltlk drivers, use the -command: -modprobe -r speakup_dectlk speakup_ltlk - -You cannot unload the driver for software synthesizers when a user-space -daemon is using /dev/softsynth. First, kill the daemon. Next, remove -the driver with the command: -modprobe -r speakup_soft - -Now, suppose we have a situation where the main Speakup component -is built into the kernel, and some or all of the drivers are built as -modules. Since the main part of Speakup is compiled into the kernel, a -partial Speakup sys system has been created which we can take advantage -of by simply echoing the synthesizer keyword into the -/speakup/synth sys entry. This will cause the kernel to -automatically load the appropriate driver module, and start Speakup -talking. To switch to another synth, just echo a new keyword to the -synth sys entry. For example, to load the DoubleTalk LT driver, -you would type: - -echo ltlk >/speakup/synth - -You can use the modprobe -r command to unload driver modules, regardless -of whether the main part of Speakup has been built into the kernel or -not. - -8. Using Software Synthesizers - -Using a software synthesizer requires that some other software be -installed and running on your system. For this reason, software -synthesizers are not available for use at bootup, or during a system -installation process. -There are two freely-available solutions for software speech: Espeakup and -Speech Dispatcher. -These are described in subsections 8.1 and 8.2, respectively. - -During the rest of this section, we assume that speakup_soft is either -built in to your kernel, or loaded as a module. - -If your system does not have udev installed , before you can use a -software synthesizer, you must have created the /dev/softsynth device. -If you have not already done so, issue the following commands as root: - -cd /dev -mknod softsynth c 10 26 - -While we are at it, we might just as well create the /dev/synth device, -which can be used to let user space programs send information to your -synthesizer. To create /dev/synth, change to the /dev directory, and -issue the following command as root: - -mknod synth c 10 25 - -of both. - -8.1. Espeakup - -Espeakup is a connector between Speakup and the eSpeak software synthesizer. -Espeakup may already be available as a package for your distribution -of Linux. If it is not packaged, you need to install it manually. -You can find it in the contrib/ subdirectory of the Speakup sources. -The filename is espeakup-$VERSION.tar.bz2, where $VERSION -depends on the current release of Espeakup. The Speakup 3.1.2 source -ships with version 0.71 of Espeakup. -The README file included with the Espeakup sources describes the process -of manual installation. - -Assuming that Espeakup is installed, either by the user or by the distributor, -follow these steps to use it. - -Tell Speakup to use the "soft driver: -echo soft > /speakup/synth - -Finally, start the espeakup program. There are two ways to do it. -Both require root privileges. - -If Espeakup was installed as a package for your Linux distribution, -you probably have a distribution-specific script that controls the operation -of the daemon. Look for a file named espeakup under /etc/init.d or -/etc/rc.d. Execute the following command with root privileges: -/etc/init.d/espeakup start -Replace init.d with rc.d, if your distribution uses scripts located under -/etc/rc.d. -Your distribution will also have a procedure for starting daemons at -boot-time, so it is possible to have software speech as soon as user-space -daemons are started by the bootup scripts. -These procedures are not described in this document. - -If you built Espeakup manually, the "make install" step placed the binary -under /usr/bin. -Run the following command as root: -/usr/bin/espeakup -Espeakup should start speaking. - -8.2. Speech Dispatcher - -For this option, you must have a package called -Speech Dispatcher running on your system, and it must be configured to -work with one of its supported software synthesizers. - -Two open source synthesizers you might use are Flite and Festival. You -might also choose to purchase the Software DecTalk from Fonix Sales Inc. -If you run a google search for Fonix, you'll find their web site. - -You can obtain a copy of Speech Dispatcher from free(b)soft at -http://www.freebsoft.org/. Follow the installation instructions that -come with Speech Dispatcher in order to install and configure Speech -Dispatcher. You can check out the web site for your Linux distribution -in order to get a copy of either Flite or Festival. Your Linux -distribution may also have a precompiled Speech Dispatcher package. - -Once you've installed, configured, and tested Speech Dispatcher with your -chosen software synthesizer, you still need one more piece of software -in order to make things work. You need a package called speechd-up. -You get it from the free(b)soft web site mentioned above. After you've -compiled and installed speechd-up, you are almost ready to begin using -your software synthesizer. - -Now you can begin using your software synthesizer. In order to do so, -echo the soft keyword to the synth sys entry like this: - -echo soft >/speakup/synth - -Next run the speechd_up command like this: - -speechd_up & - -Your synth should now start talking, and you should be able to adjust -the pitch, rate, etc. - -9. Using The DecTalk PC Card - -The DecTalk PC card is an ISA card that is inserted into one of the ISA -slots in your computer. It requires that the DecTalk PC software be -installed on your computer, and that the software be loaded onto the -Dectalk PC card before it can be used. - -You can get the dec_pc.tgz file from the linux-speakup.org site. The -dec_pc.tgz file is in the ~ftp/pub/linux/speakup directory. - -After you have downloaded the dec_pc.tgz file, untar it in your home -directory, and read the Readme file in the newly created dec_pc -directory. - -The easiest way to get the software working is to copy the entire dec_pc -directory into /user/local/lib. To do this, su to root in your home -directory, and issue the command: - -cp dec_pc /usr/local/lib - -You will need to copy the dtload command from the dec_pc directory to a -directory in your path. Either /usr/bin or /usr/local/bin is a good -choice. - -You can now run the dtload command in order to load the DecTalk PC -software onto the card. After you have done this, echo the decpc -keyword to the synth entry in the sys system like this: - -echo decpc >/speakup/synth - -Your DecTalk PC should start talking, and then you can adjust the pitch, -rate, volume, voice, etc. The voice entry in the Speakup sys system -will accept a number from 0 through 7 for the DecTalk PC synthesizer, -which will give you access to some of the DecTalk voices. - -10. Using Cursor Tracking - -In Speakup version 2.0 and later, cursor tracking is turned on by -default. This means that when you are using an editor, Speakup will -automatically speak characters as you move left and right with the -cursor keys, and lines as you move up and down with the cursor keys. -This is the traditional sort of cursor tracking. -Recent versions of Speakup provide two additional ways to control the -text that is spoken when the cursor is moved: -"highlight tracking" and "read window." -They are described later in this section. -Sometimes, these modes get in your way, so you can disable cursor tracking -altogether. - -You may select among the various forms of cursor tracking using the keypad -asterisk key. -Each time you press this key, a new mode is selected, and Speakup speaks -the name of the new mode. The names for the four possible states of cursor -tracking are: "cursoring on", "highlight tracking", "read window", -and "cursoring off." The keypad asterisk key moves through the list of -modes in a circular fashion. - -If highlight tracking is enabled, Speakup tracks highlighted text, -rather than the cursor itself. When you move the cursor with the arrow keys, -Speakup speaks the currently highlighted information. -This is useful when moving through various menus and dialog boxes. -If cursor tracking isn't helping you while navigating a menu, -try highlight tracking. - -With the "read window" variety of cursor tracking, you can limit the text -that Speakup speaks by specifying a window of interest on the screen. -See section 15 for a description of the process of defining windows. -When you move the cursor via the arrow keys, Speakup only speaks -the contents of the window. This is especially helpful when you are hearing -superfluous speech. Consider the following example. - -Suppose that you are at a shell prompt. You use bash, and you want to -explore your command history using the up and down arrow keys. If you -have enabled cursor tracking, you will hear two pieces of information. -Speakup speaks both your shell prompt and the current entry from the -command history. You may not want to hear the prompt repeated -each time you move, so you can silence it by specifying a window. Find -the last line of text on the screen. Clear the current window by pressing -the key combination speakup f3. Use the review cursor to find the first -character that follows your shell prompt. Press speakup + f2 twice, to -define a one-line window. The boundaries of the window are the -character following the shell prompt and the end of the line. Now, cycle -through the cursor tracking modes using keypad asterisk, until Speakup -says "read window." Move through your history using your arrow keys. -You will notice that Speakup no longer speaks the redundant prompt. - -Some folks like to turn cursor tracking off while they are using the -lynx web browser. You definitely want to turn cursor tracking off when -you are using the alsamixer application. Otherwise, you won't be able -to hear your mixer settings while you are using the arrow keys. - -11. Cut and Paste - -One of Speakup's more useful features is the ability to cut and paste -text on the screen. This means that you can capture information from a -program, and paste that captured text into a different place in the -program, or into an entirely different program, which may even be -running on a different console. - -For example, in this manual, we have made references to several web -sites. It would be nice if you could cut and paste these urls into your -web browser. Speakup does this quite nicely. Suppose you wanted to -past the following url into your browser: - -http://linux-speakup.org/ - -Use the speakup review keys to position the reading cursor on the first -character of the above url. When the reading cursor is in position, -press the keypad slash key once. Speakup will say, "mark". Next, -position the reading cursor on the rightmost character of the above -url. Press the keypad slash key once again to actually cut the text -from the screen. Speakup will say, "cut". Although we call this -cutting, Speakup does not actually delete the cut text from the screen. -It makes a copy of the text in a special buffer for later pasting. - -Now that you have the url cut from the screen, you can paste it into -your browser, or even paste the url on a command line as an argument to -your browser. - -Suppose you want to start lynx and go to the Speakup site. - -You can switch to a different console with the alt left and right -arrows, or you can switch to a specific console by typing alt and a -function key. These are not Speakup commands, just standard Linux -console capabilities. - -Once you've changed to an appropriate console, and are at a shell prompt, -type the word lynx, followed by a space. Now press and hold the speakup -key, while you type the keypad slash character. The url will be pasted -onto the command line, just as though you had typed it in. Press the -enter key to execute the command. - -The paste buffer will continue to hold the cut information, until a new -mark and cut operation is carried out. This means you can paste the cut -information as many times as you like before doing another cut -operation. - -You are not limited to cutting and pasting only one line on the screen. -You can also cut and paste rectangular regions of the screen. Just -position the reading cursor at the top left corner of the text to be -cut, mark it with the keypad slash key, then position the reading cursor -at the bottom right corner of the region to be cut, and cut it with the -keypad slash key. - -12. Changing the Pronunciation of Characters - -Through the /speakup/i18n/characters sys entry, Speakup gives you the -ability to change how Speakup pronounces a given character. You could, -for example, change how some punctuation characters are spoken. You can -even change how Speakup will pronounce certain letters. - -You may, for example, wish to change how Speakup pronounces the z -character. The author of Speakup, Kirk Reiser, is Canadian, and thus -believes that the z should be pronounced zed. If you are an American, -you might wish to use the zee pronunciation instead of zed. You can -change the pronunciation of both the upper and lower case z with the -following two commands: - -echo 90 zee >/speakup/characters -echo 122 zee >/speakup/characters - -Let's examine the parts of the two previous commands. They are issued -at the shell prompt, and could be placed in a startup script. - -The word echo tells the shell that you want to have it display the -string of characters that follow the word echo. If you were to just -type: - -echo hello. - -You would get the word hello printed on your screen as soon as you -pressed the enter key. In this case, we are echoing strings that we -want to be redirected into the sys system. - -The numbers 90 and 122 in the above echo commands are the ascii numeric -values for the upper and lower case z, the characters we wish to change. - -The string zee is the pronunciation that we want Speakup to use for the -upper and lower case z. - -The > symbol redirects the output of the echo command to a file, just -like in DOS, or at the Windows command prompt. - -And finally, /speakup/i18n/characters is the file entry in the sys system -where we want the output to be directed. Speakup looks at the numeric -value of the character we want to change, and inserts the pronunciation -string into an internal table. - -You can look at the whole table with the following command: - -cat /speakup/i18n/characters - -Speakup will then print out the entire character pronunciation table. I -won't display it here, but leave you to look at it at your convenience. - -13. Mapping Keys - -Speakup has the capability of allowing you to assign or "map" keys to -internal Speakup commands. This section necessarily assumes you have a -Linux kernel source tree installed, and that it has been patched and -configured with Speakup. How you do this is beyond the scope of this -manual. For this information, visit the Speakup web site at -http://linux-speakup.org/. The reason you'll need the kernel source -tree patched with Speakup is that the genmap utility you'll need for -processing keymaps is in the -/usr/src/linux-/drivers/char/speakup directory. The - in the above directory path is the version number of -the Linux source tree you are working with. - -So ok, you've gone off and gotten your kernel source tree, and patched -and configured it. Now you can start manipulating keymaps. - -You can either use the -/usr/src/linux-/drivers/char/speakup/speakupmap.map file -included with the Speakup source, or you can cut and paste the copy in -section 4 into a separate file. If you use the one in the Speakup -source tree, make sure you make a backup of it before you start making -changes. You have been warned! - -Suppose that you want to swap the key assignments for the Speakup -say_last_char and the Speakup say_first_char commands. The -speakupmap.map lists the key mappings for these two commands as follows: - -spk key_pageup = say_first_char -spk key_pagedown = say_last_char - -You can edit your copy of the speakupmap.map file and swap the command -names on the right side of the = (equals) sign. You did make a backup, -right? The new keymap lines would look like this: - -spk key_pageup = say_last_char -spk key_pagedown = say_first_char - -After you edit your copy of the speakupmap.map file, save it under a new -file name, perhaps newmap.map. Then exit your editor and return to the -shell prompt. - -You are now ready to load your keymap with your swapped key assignments. - Assuming that you saved your new keymap as the file newmap.map, you -would load your keymap into the sys system like this: - -/usr/src/linux-/drivers/char/speakup/genmap newmap.map ->/speakup/keymap - -Remember to substitute your kernel version number for the - in the above command. Also note that although the -above command wrapped onto two lines in this document, you should type -it all on one line. - -Your say first and say last characters should now be swapped. Pressing -speakup pagedown should read you the first non-whitespace character on -the line your reading cursor is in, and pressing speakup pageup should -read you the last character on the line your reading cursor is in. - -You should note that these new mappings will only stay in effect until -you reboot, or until you load another keymap. - -One final warning. If you try to load a partial map, you will quickly -find that all the mappings you didn't include in your file got deleted -from the working map. Be extremely careful, and always make a backup! -You have been warned! - -14. Internationalizing Speakup - -Speakup indicates various conditions to the user by speaking messages. -For instance, when you move to the left edge of the screen with the -review keys, Speakup says, "left." -Prior to version 3.1.0 of Speakup, all of these messages were in English, -and they could not be changed. If you used a non-English synthesizer, -you still heard English messages, such as "left" and "cursoring on." -In version 3.1.0 or higher, one may load translations for the various -messages via the /sys filesystem. - -The directory /speakup/i18n contains several collections of messages. -Each group of messages is stored in its own file. -The following section lists all of these files, along with a brief description -of each. - -14.1. Files Under the i18n Subdirectory - -* announcements: -This file contains various general announcements, most of which cannot -be categorized. You will find messages such as "You killed Speakup", -"I'm alive", "leaving help", "parked", "unparked", and others. -You will also find the names of the screen edges and cursor tracking modes -here. - -* characters: -See section 12 for a description of this file. - -* chartab: -See section 12. Unlike the rest of the files in the i18n subdirectory, -this one does not contain messages to be spoken. - -* colors: -When you use the "say attributes" function, Speakup says the name of the -foreground and background colors. These names come from the i18n/colors -file. - -* ctl_keys: -Here, you will find names of control keys. These are used with Speakup's -say_control feature. - -* formatted: -This group of messages contains embedded formatting codes, to specify -the type and width of displayed data. If you change these, you must -preserve all of the formatting codes, and they must appear in the order -used by the default messages. - -* function_names: -Here, you will find a list of names for Speakup functions. These are used -by the help system. For example, suppose that you have activated help mode, -and you pressed keypad 3. Speakup says: -"keypad 3 is character, say next." -The message "character, say next" names a Speakup function, and it -comes from this function_names file. - -* key_names: -Again, key_names is used by Speakup's help system. In the previous -example, Speakup said that you pressed "keypad 3." -This name came from the key_names file. - -* states: -This file contains names for key states. -Again, these are part of the help system. For instance, if you had pressed -speakup + keypad 3, you would hear: -"speakup keypad 3 is go to bottom edge." -The speakup key is depressed, so the name of the key state is speakup. -This part of the message comes from the states collection. - -14.2. Loading Your Own Messages - -The files under the i18n subdirectory all follow the same format. -They consist of lines, with one message per line. -Each message is represented by a number, followed by the text of the message. -The number is the position of the message in the given collection. -For example, if you view the file /speakup/i18n/colors, you will see the -following list: - -0 black -1 blue -2 green -3 cyan -4 red -5 magenta -6 yellow -7 white -8 grey - -You can change one message, or you can change a whole group. -To load a whole collection of messages from a new source, simply use -the cp command: -cp ~/my_colors /speakup/i18n/colors -You can change an individual message with the echo command, -as shown in the following example. - -The Spanish name for the color blue is azul. -Looking at the colors file, we see that the name "blue" is at position 1 -within the colors group. Let's change blue to azul: -echo '1 azul' > /speakup/i18n/colors -The next time that Speakup says message 1 from the colors group, it will -say "azul", rather than "blue." - -In the future, translations into various languages will be made available, -and most users will just load the files necessary for their language. - -14.3. No Support for Non-Western-European Languages - -As of the current release, Speakup only supports Western European languages. -Support for the extended characters used by languages outside of the Western -European family of languages is a work in progress. - -15. Using Speakup's Windowing Capability - -Speakup has the capability of defining and manipulating windows on the -screen. Speakup uses the term "Window", to mean a user defined area of -the screen. The key strokes for defining and manipulating Speakup -windows are as follows: - -speakup + f2 -- Set the bounds of the window. -Speakup + f3 -- clear the current window definition. -speakup + f4 -- Toggle window silence on and off. -speakup + keypad plus -- Say the currently defined window. - -These capabilities are useful for tracking a certain part of the screen -without rereading the whole screen, or for silencing a part of the -screen that is constantly changing, such as a clock or status line. - -There is no way to save these window settings, and you can only have one -window defined for each virtual console. There is also no way to have -windows automatically defined for specific applications. - -In order to define a window, use the review keys to move your reading -cursor to the beginning of the area you want to define. Then press -speakup + f2. Speakup will tell you that the window starts at the -indicated row and column position. Then move the reading cursor to the -end of the area to be defined as a window, and press speakup + f2 again. - If there is more than one line in the window, Speakup will tell you -that the window ends at the indicated row and column position. If there -is only one line in the window, then Speakup will tell you that the -window is the specified line on the screen. If you are only defining a -one line window, you can just press speakup + f2 twice after placing the -reading cursor on the line you want to define as a window. It is not -necessary to position the reading cursor at the end of the line in order -to define the whole line as a window. - -16. Tools for Controlling Speakup - -The speakup distribution includes extra tools (in the tools directory) -which were written to make speakup easier to use. This section will -briefly describe the use of these tools. - -16.1. Speakupconf - -speakupconf began life as a contribution from Steve Holmes, a member of -the speakup community. We would like to thank him for his work on the -early versions of this project. - -This script may be installed as part of your linux distribution, but if -it isn't, the recommended places to put it are /usr/local/bin or -/usr/bin. This script can be run by any user, so it does not require -root privileges. - -Speakupconf allows you to save and load your Speakup settings. It works -by reading and writing the /sys files described above. - -The directory that speakupconf uses to store your settings depends on -whether it is run from the root account. If you execute speakupconf as -root, it uses the directory /etc/speakup. Otherwise, it uses the directory -~/.speakup, where ~ is your home directory. -Anyone who needs to use Speakup from your console can load his own custom -settings with this script. - -speakupconf takes one required argument: load or save. -Use the command -speakupconf save -to save your Speakup settings, and -speakupconf load -to load them into Speakup. -A second argument may be specified to use an alternate directory to -load or save the speakup parameters. - -16.2. Talkwith - -Charles Hallenbeck, another member of the speakup community, wrote the -initial versions of this script, and we would also like to thank him for -his work on it. - -This script needs root privileges to run, so if it is not installed as -part of your linux distribution, the recommended places to install it -are /usr/local/sbin or /usr/sbin. - -Talkwith allows you to switch synthesizers on the fly. It takes a synthesizer -name as an argument. For instance, -talkwith dectlk -causes Speakup to use the DecTalk Express. If you wish to switch to a -software synthesizer, you must also indicate which daemon you wish to -use. There are two possible choices: -spd and espeakup. spd is an abbreviation for speechd-up. -If you wish to use espeakup for software synthesis, give the command -talkwith soft espeakup -To use speechd-up, type: -talkwith soft spd -Any arguments that follow the name of the daemon are passed to the daemon -when it is invoked. For instance: -talkwith espeakup --default-voice=fr -causes espeakup to use the French voice. -Note that talkwith must always be executed with root privileges. - -Talkwith does not attempt to load your settings after the new -synthesizer is activated. You can use speakupconf to load your settings -if desired. - - GNU Free Documentation License - Version 1.2, November 2002 - - - Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - -0. PREAMBLE - -The purpose of this License is to make a manual, textbook, or other -functional and useful document "free" in the sense of freedom: to -assure everyone the effective freedom to copy and redistribute it, -with or without modifying it, either commercially or noncommercially. -Secondarily, this License preserves for the author and publisher a way -to get credit for their work, while not being considered responsible -for modifications made by others. - -This License is a kind of "copyleft", which means that derivative -works of the document must themselves be free in the same sense. It -complements the GNU General Public License, which is a copyleft -license designed for free software. - -We have designed this License in order to use it for manuals for free -software, because free software needs free documentation: a free -program should come with manuals providing the same freedoms that the -software does. But this License is not limited to software manuals; -it can be used for any textual work, regardless of subject matter or -whether it is published as a printed book. We recommend this License -principally for works whose purpose is instruction or reference. - - -1. APPLICABILITY AND DEFINITIONS - -This License applies to any manual or other work, in any medium, that -contains a notice placed by the copyright holder saying it can be -distributed under the terms of this License. Such a notice grants a -world-wide, royalty-free license, unlimited in duration, to use that -work under the conditions stated herein. The "Document", below, -refers to any such manual or work. Any member of the public is a -licensee, and is addressed as "you". You accept the license if you -copy, modify or distribute the work in a way requiring permission -under copyright law. - -A "Modified Version" of the Document means any work containing the -Document or a portion of it, either copied verbatim, or with -modifications and/or translated into another language. - -A "Secondary Section" is a named appendix or a front-matter section of -the Document that deals exclusively with the relationship of the -publishers or authors of the Document to the Document's overall subject -(or to related matters) and contains nothing that could fall directly -within that overall subject. (Thus, if the Document is in part a -textbook of mathematics, a Secondary Section may not explain any -mathematics.) The relationship could be a matter of historical -connection with the subject or with related matters, or of legal, -commercial, philosophical, ethical or political position regarding -them. - -The "Invariant Sections" are certain Secondary Sections whose titles -are designated, as being those of Invariant Sections, in the notice -that says that the Document is released under this License. If a -section does not fit the above definition of Secondary then it is not -allowed to be designated as Invariant. The Document may contain zero -Invariant Sections. If the Document does not identify any Invariant -Sections then there are none. - -The "Cover Texts" are certain short passages of text that are listed, -as Front-Cover Texts or Back-Cover Texts, in the notice that says that -the Document is released under this License. A Front-Cover Text may -be at most 5 words, and a Back-Cover Text may be at most 25 words. - -A "Transparent" copy of the Document means a machine-readable copy, -represented in a format whose specification is available to the -general public, that is suitable for revising the document -straightforwardly with generic text editors or (for images composed of -pixels) generic paint programs or (for drawings) some widely available -drawing editor, and that is suitable for input to text formatters or -for automatic translation to a variety of formats suitable for input -to text formatters. A copy made in an otherwise Transparent file -format whose markup, or absence of markup, has been arranged to thwart -or discourage subsequent modification by readers is not Transparent. -An image format is not Transparent if used for any substantial amount -of text. A copy that is not "Transparent" is called "Opaque". - -Examples of suitable formats for Transparent copies include plain -ASCII without markup, Texinfo input format, LaTeX input format, SGML -or XML using a publicly available DTD, and standard-conforming simple -HTML, PostScript or PDF designed for human modification. Examples of -transparent image formats include PNG, XCF and JPG. Opaque formats -include proprietary formats that can be read and edited only by -proprietary word processors, SGML or XML for which the DTD and/or -processing tools are not generally available, and the -machine-generated HTML, PostScript or PDF produced by some word -processors for output purposes only. - -The "Title Page" means, for a printed book, the title page itself, -plus such following pages as are needed to hold, legibly, the material -this License requires to appear in the title page. For works in -formats which do not have any title page as such, "Title Page" means -the text near the most prominent appearance of the work's title, -preceding the beginning of the body of the text. - -A section "Entitled XYZ" means a named subunit of the Document whose -title either is precisely XYZ or contains XYZ in parentheses following -text that translates XYZ in another language. (Here XYZ stands for a -specific section name mentioned below, such as "Acknowledgements", -"Dedications", "Endorsements", or "History".) To "Preserve the Title" -of such a section when you modify the Document means that it remains a -section "Entitled XYZ" according to this definition. - -The Document may include Warranty Disclaimers next to the notice which -states that this License applies to the Document. These Warranty -Disclaimers are considered to be included by reference in this -License, but only as regards disclaiming warranties: any other -implication that these Warranty Disclaimers may have is void and has -no effect on the meaning of this License. - - -2. VERBATIM COPYING - -You may copy and distribute the Document in any medium, either -commercially or noncommercially, provided that this License, the -copyright notices, and the license notice saying this License applies -to the Document are reproduced in all copies, and that you add no other -conditions whatsoever to those of this License. You may not use -technical measures to obstruct or control the reading or further -copying of the copies you make or distribute. However, you may accept -compensation in exchange for copies. If you distribute a large enough -number of copies you must also follow the conditions in section 3. - -You may also lend copies, under the same conditions stated above, and -you may publicly display copies. - - -3. COPYING IN QUANTITY - -If you publish printed copies (or copies in media that commonly have -printed covers) of the Document, numbering more than 100, and the -Document's license notice requires Cover Texts, you must enclose the -copies in covers that carry, clearly and legibly, all these Cover -Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on -the back cover. Both covers must also clearly and legibly identify -you as the publisher of these copies. The front cover must present -the full title with all words of the title equally prominent and -visible. You may add other material on the covers in addition. -Copying with changes limited to the covers, as long as they preserve -the title of the Document and satisfy these conditions, can be treated -as verbatim copying in other respects. - -If the required texts for either cover are too voluminous to fit -legibly, you should put the first ones listed (as many as fit -reasonably) on the actual cover, and continue the rest onto adjacent -pages. - -If you publish or distribute Opaque copies of the Document numbering -more than 100, you must either include a machine-readable Transparent -copy along with each Opaque copy, or state in or with each Opaque copy -a computer-network location from which the general network-using -public has access to download using public-standard network protocols -a complete Transparent copy of the Document, free of added material. -If you use the latter option, you must take reasonably prudent steps, -when you begin distribution of Opaque copies in quantity, to ensure -that this Transparent copy will remain thus accessible at the stated -location until at least one year after the last time you distribute an -Opaque copy (directly or through your agents or retailers) of that -edition to the public. - -It is requested, but not required, that you contact the authors of the -Document well before redistributing any large number of copies, to give -them a chance to provide you with an updated version of the Document. - - -4. MODIFICATIONS - -You may copy and distribute a Modified Version of the Document under -the conditions of sections 2 and 3 above, provided that you release -the Modified Version under precisely this License, with the Modified -Version filling the role of the Document, thus licensing distribution -and modification of the Modified Version to whoever possesses a copy -of it. In addition, you must do these things in the Modified Version: - -A. Use in the Title Page (and on the covers, if any) a title distinct - from that of the Document, and from those of previous versions - (which should, if there were any, be listed in the History section - of the Document). You may use the same title as a previous version - if the original publisher of that version gives permission. -B. List on the Title Page, as authors, one or more persons or entities - responsible for authorship of the modifications in the Modified - Version, together with at least five of the principal authors of the - Document (all of its principal authors, if it has fewer than five), - unless they release you from this requirement. -C. State on the Title page the name of the publisher of the - Modified Version, as the publisher. -D. Preserve all the copyright notices of the Document. -E. Add an appropriate copyright notice for your modifications - adjacent to the other copyright notices. -F. Include, immediately after the copyright notices, a license notice - giving the public permission to use the Modified Version under the - terms of this License, in the form shown in the Addendum below. -G. Preserve in that license notice the full lists of Invariant Sections - and required Cover Texts given in the Document's license notice. -H. Include an unaltered copy of this License. -I. Preserve the section Entitled "History", Preserve its Title, and add - to it an item stating at least the title, year, new authors, and - publisher of the Modified Version as given on the Title Page. If - there is no section Entitled "History" in the Document, create one - stating the title, year, authors, and publisher of the Document as - given on its Title Page, then add an item describing the Modified - Version as stated in the previous sentence. -J. Preserve the network location, if any, given in the Document for - public access to a Transparent copy of the Document, and likewise - the network locations given in the Document for previous versions - it was based on. These may be placed in the "History" section. - You may omit a network location for a work that was published at - least four years before the Document itself, or if the original - publisher of the version it refers to gives permission. -K. For any section Entitled "Acknowledgements" or "Dedications", - Preserve the Title of the section, and preserve in the section all - the substance and tone of each of the contributor acknowledgements - and/or dedications given therein. -L. Preserve all the Invariant Sections of the Document, - unaltered in their text and in their titles. Section numbers - or the equivalent are not considered part of the section titles. -M. Delete any section Entitled "Endorsements". Such a section - may not be included in the Modified Version. -N. Do not retitle any existing section to be Entitled "Endorsements" - or to conflict in title with any Invariant Section. -O. Preserve any Warranty Disclaimers. - -If the Modified Version includes new front-matter sections or -appendices that qualify as Secondary Sections and contain no material -copied from the Document, you may at your option designate some or all -of these sections as invariant. To do this, add their titles to the -list of Invariant Sections in the Modified Version's license notice. -These titles must be distinct from any other section titles. - -You may add a section Entitled "Endorsements", provided it contains -nothing but endorsements of your Modified Version by various -parties--for example, statements of peer review or that the text has -been approved by an organization as the authoritative definition of a -standard. - -You may add a passage of up to five words as a Front-Cover Text, and a -passage of up to 25 words as a Back-Cover Text, to the end of the list -of Cover Texts in the Modified Version. Only one passage of -Front-Cover Text and one of Back-Cover Text may be added by (or -through arrangements made by) any one entity. If the Document already -includes a cover text for the same cover, previously added by you or -by arrangement made by the same entity you are acting on behalf of, -you may not add another; but you may replace the old one, on explicit -permission from the previous publisher that added the old one. - -The author(s) and publisher(s) of the Document do not by this License -give permission to use their names for publicity for or to assert or -imply endorsement of any Modified Version. - - -5. COMBINING DOCUMENTS - -You may combine the Document with other documents released under this -License, under the terms defined in section 4 above for modified -versions, provided that you include in the combination all of the -Invariant Sections of all of the original documents, unmodified, and -list them all as Invariant Sections of your combined work in its -license notice, and that you preserve all their Warranty Disclaimers. - -The combined work need only contain one copy of this License, and -multiple identical Invariant Sections may be replaced with a single -copy. If there are multiple Invariant Sections with the same name but -different contents, make the title of each such section unique by -adding at the end of it, in parentheses, the name of the original -author or publisher of that section if known, or else a unique number. -Make the same adjustment to the section titles in the list of -Invariant Sections in the license notice of the combined work. - -In the combination, you must combine any sections Entitled "History" -in the various original documents, forming one section Entitled -"History"; likewise combine any sections Entitled "Acknowledgements", -and any sections Entitled "Dedications". You must delete all sections -Entitled "Endorsements". - - -6. COLLECTIONS OF DOCUMENTS - -You may make a collection consisting of the Document and other documents -released under this License, and replace the individual copies of this -License in the various documents with a single copy that is included in -the collection, provided that you follow the rules of this License for -verbatim copying of each of the documents in all other respects. - -You may extract a single document from such a collection, and distribute -it individually under this License, provided you insert a copy of this -License into the extracted document, and follow this License in all -other respects regarding verbatim copying of that document. - - -7. AGGREGATION WITH INDEPENDENT WORKS - -A compilation of the Document or its derivatives with other separate -and independent documents or works, in or on a volume of a storage or -distribution medium, is called an "aggregate" if the copyright -resulting from the compilation is not used to limit the legal rights -of the compilation's users beyond what the individual works permit. -When the Document is included in an aggregate, this License does not -apply to the other works in the aggregate which are not themselves -derivative works of the Document. - -If the Cover Text requirement of section 3 is applicable to these -copies of the Document, then if the Document is less than one half of -the entire aggregate, the Document's Cover Texts may be placed on -covers that bracket the Document within the aggregate, or the -electronic equivalent of covers if the Document is in electronic form. -Otherwise they must appear on printed covers that bracket the whole -aggregate. - - -8. TRANSLATION - -Translation is considered a kind of modification, so you may -distribute translations of the Document under the terms of section 4. -Replacing Invariant Sections with translations requires special -permission from their copyright holders, but you may include -translations of some or all Invariant Sections in addition to the -original versions of these Invariant Sections. You may include a -translation of this License, and all the license notices in the -Document, and any Warranty Disclaimers, provided that you also include -the original English version of this License and the original versions -of those notices and disclaimers. In case of a disagreement between -the translation and the original version of this License or a notice -or disclaimer, the original version will prevail. - -If a section in the Document is Entitled "Acknowledgements", -"Dedications", or "History", the requirement (section 4) to Preserve -its Title (section 1) will typically require changing the actual -title. - - -9. TERMINATION - -You may not copy, modify, sublicense, or distribute the Document except -as expressly provided for under this License. Any other attempt to -copy, modify, sublicense or distribute the Document is void, and will -automatically terminate your rights under this License. However, -parties who have received copies, or rights, from you under this -License will not have their licenses terminated so long as such -parties remain in full compliance. - - -10. FUTURE REVISIONS OF THIS LICENSE - -The Free Software Foundation may publish new, revised versions -of the GNU Free Documentation License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. See -https://www.gnu.org/copyleft/. - -Each version of the License is given a distinguishing version number. -If the Document specifies that a particular numbered version of this -License "or any later version" applies to it, you have the option of -following the terms and conditions either of that specified version or -of any later version that has been published (not as a draft) by the -Free Software Foundation. If the Document does not specify a version -number of this License, you may choose any version ever published (not -as a draft) by the Free Software Foundation. - - -ADDENDUM: How to use this License for your documents - -To use this License in a document you have written, include a copy of -the License in the document and put the following copyright and -license notices just after the title page: - - Copyright (c) YEAR YOUR NAME. - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.2 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. - A copy of the license is included in the section entitled "GNU - Free Documentation License". - -If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, -replace the "with...Texts." line with this: - - with the Invariant Sections being LIST THEIR TITLES, with the - Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. - -If you have Invariant Sections without Cover Texts, or some other -combination of the three, merge those two alternatives to suit the -situation. - -If your document contains nontrivial examples of program code, we -recommend releasing these examples in parallel under your choice of -free software license, such as the GNU General Public License, -to permit their use in free software. - -The End. diff --git a/drivers/staging/speakup/synth.c b/drivers/staging/speakup/synth.c deleted file mode 100644 index 3568bfb89912..000000000000 --- a/drivers/staging/speakup/synth.c +++ /dev/null @@ -1,490 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include /* for isdigit() and friends */ -#include -#include /* for verify_area */ -#include /* for -EBUSY */ -#include /* for check_region, request_region */ -#include -#include /* for loops_per_sec */ -#include -#include -#include /* for copy_from_user */ -#include -#include -#include - -#include "spk_priv.h" -#include "speakup.h" -#include "serialio.h" - -static LIST_HEAD(synths); -struct spk_synth *synth; -char spk_pitch_buff[32] = ""; -static int module_status; -bool spk_quiet_boot; - -struct speakup_info_t speakup_info = { - /* - * This spinlock is used to protect the entire speakup machinery, and - * must be taken at each kernel->speakup transition and released at - * each corresponding speakup->kernel transition. - * - * The progression thread only interferes with the speakup machinery - * through the synth buffer, so only needs to take the lock - * while tinkering with the buffer. - * - * We use spin_lock/trylock_irqsave and spin_unlock_irqrestore with this - * spinlock because speakup needs to disable the keyboard IRQ. - */ - .spinlock = __SPIN_LOCK_UNLOCKED(speakup_info.spinlock), - .flushing = 0, -}; -EXPORT_SYMBOL_GPL(speakup_info); - -static int do_synth_init(struct spk_synth *in_synth); - -/* - * Main loop of the progression thread: keep eating from the buffer - * and push to the serial port, waiting as needed - * - * For devices that have a "full" notification mechanism, the driver can - * adapt the loop the way they prefer. - */ -static void _spk_do_catch_up(struct spk_synth *synth, int unicode) -{ - u16 ch; - unsigned long flags; - unsigned long jiff_max; - struct var_t *delay_time; - struct var_t *full_time; - struct var_t *jiffy_delta; - int jiffy_delta_val; - int delay_time_val; - int full_time_val; - int ret; - - jiffy_delta = spk_get_var(JIFFY); - full_time = spk_get_var(FULL); - delay_time = spk_get_var(DELAY); - - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - - jiff_max = jiffies + jiffy_delta_val; - while (!kthread_should_stop()) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - if (speakup_info.flushing) { - speakup_info.flushing = 0; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - synth->flush(synth); - continue; - } - if (!unicode) - synth_buffer_skip_nonlatin1(); - if (synth_buffer_empty()) { - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - break; - } - ch = synth_buffer_peek(); - set_current_state(TASK_INTERRUPTIBLE); - full_time_val = full_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (ch == '\n') - ch = synth->procspeech; - if (unicode) - ret = synth->io_ops->synth_out_unicode(synth, ch); - else - ret = synth->io_ops->synth_out(synth, ch); - if (!ret) { - schedule_timeout(msecs_to_jiffies(full_time_val)); - continue; - } - if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - jiffy_delta_val = jiffy_delta->u.n.value; - delay_time_val = delay_time->u.n.value; - full_time_val = full_time->u.n.value; - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (synth->io_ops->synth_out(synth, synth->procspeech)) - schedule_timeout( - msecs_to_jiffies(delay_time_val)); - else - schedule_timeout( - msecs_to_jiffies(full_time_val)); - jiff_max = jiffies + jiffy_delta_val; - } - set_current_state(TASK_RUNNING); - spin_lock_irqsave(&speakup_info.spinlock, flags); - synth_buffer_getc(); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - } - synth->io_ops->synth_out(synth, synth->procspeech); -} - -void spk_do_catch_up(struct spk_synth *synth) -{ - _spk_do_catch_up(synth, 0); -} -EXPORT_SYMBOL_GPL(spk_do_catch_up); - -void spk_do_catch_up_unicode(struct spk_synth *synth) -{ - _spk_do_catch_up(synth, 1); -} -EXPORT_SYMBOL_GPL(spk_do_catch_up_unicode); - -void spk_synth_flush(struct spk_synth *synth) -{ - synth->io_ops->flush_buffer(); - synth->io_ops->synth_out(synth, synth->clear); -} -EXPORT_SYMBOL_GPL(spk_synth_flush); - -unsigned char spk_synth_get_index(struct spk_synth *synth) -{ - return synth->io_ops->synth_in_nowait(); -} -EXPORT_SYMBOL_GPL(spk_synth_get_index); - -int spk_synth_is_alive_nop(struct spk_synth *synth) -{ - synth->alive = 1; - return 1; -} -EXPORT_SYMBOL_GPL(spk_synth_is_alive_nop); - -int spk_synth_is_alive_restart(struct spk_synth *synth) -{ - if (synth->alive) - return 1; - if (spk_wait_for_xmitr(synth) > 0) { - /* restart */ - synth->alive = 1; - synth_printf("%s", synth->init); - return 2; /* reenabled */ - } - pr_warn("%s: can't restart synth\n", synth->long_name); - return 0; -} -EXPORT_SYMBOL_GPL(spk_synth_is_alive_restart); - -static void thread_wake_up(struct timer_list *unused) -{ - wake_up_interruptible_all(&speakup_event); -} - -static DEFINE_TIMER(thread_timer, thread_wake_up); - -void synth_start(void) -{ - struct var_t *trigger_time; - - if (!synth->alive) { - synth_buffer_clear(); - return; - } - trigger_time = spk_get_var(TRIGGER); - if (!timer_pending(&thread_timer)) - mod_timer(&thread_timer, jiffies + - msecs_to_jiffies(trigger_time->u.n.value)); -} - -void spk_do_flush(void) -{ - if (!synth) - return; - - speakup_info.flushing = 1; - synth_buffer_clear(); - if (synth->alive) { - if (spk_pitch_shift) { - synth_printf("%s", spk_pitch_buff); - spk_pitch_shift = 0; - } - } - wake_up_interruptible_all(&speakup_event); - wake_up_process(speakup_task); -} - -void synth_write(const char *buf, size_t count) -{ - while (count--) - synth_buffer_add(*buf++); - synth_start(); -} - -void synth_printf(const char *fmt, ...) -{ - va_list args; - unsigned char buf[160], *p; - int r; - - va_start(args, fmt); - r = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - if (r > sizeof(buf) - 1) - r = sizeof(buf) - 1; - - p = buf; - while (r--) - synth_buffer_add(*p++); - synth_start(); -} -EXPORT_SYMBOL_GPL(synth_printf); - -void synth_putwc(u16 wc) -{ - synth_buffer_add(wc); -} -EXPORT_SYMBOL_GPL(synth_putwc); - -void synth_putwc_s(u16 wc) -{ - synth_buffer_add(wc); - synth_start(); -} -EXPORT_SYMBOL_GPL(synth_putwc_s); - -void synth_putws(const u16 *buf) -{ - const u16 *p; - - for (p = buf; *p; p++) - synth_buffer_add(*p); -} -EXPORT_SYMBOL_GPL(synth_putws); - -void synth_putws_s(const u16 *buf) -{ - synth_putws(buf); - synth_start(); -} -EXPORT_SYMBOL_GPL(synth_putws_s); - -static int index_count; -static int sentence_count; - -void spk_reset_index_count(int sc) -{ - static int first = 1; - - if (first) - first = 0; - else - synth->get_index(synth); - index_count = 0; - sentence_count = sc; -} - -int synth_supports_indexing(void) -{ - if (synth->get_index) - return 1; - return 0; -} - -void synth_insert_next_index(int sent_num) -{ - int out; - - if (synth->alive) { - if (sent_num == 0) { - synth->indexing.currindex++; - index_count++; - if (synth->indexing.currindex > - synth->indexing.highindex) - synth->indexing.currindex = - synth->indexing.lowindex; - } - - out = synth->indexing.currindex * 10 + sent_num; - synth_printf(synth->indexing.command, out, out); - } -} - -void spk_get_index_count(int *linecount, int *sentcount) -{ - int ind = synth->get_index(synth); - - if (ind) { - sentence_count = ind % 10; - - if ((ind / 10) <= synth->indexing.currindex) - index_count = synth->indexing.currindex - (ind / 10); - else - index_count = synth->indexing.currindex - - synth->indexing.lowindex - + synth->indexing.highindex - (ind / 10) + 1; - } - *sentcount = sentence_count; - *linecount = index_count; -} - -static struct resource synth_res; - -int synth_request_region(unsigned long start, unsigned long n) -{ - struct resource *parent = &ioport_resource; - - memset(&synth_res, 0, sizeof(synth_res)); - synth_res.name = synth->name; - synth_res.start = start; - synth_res.end = start + n - 1; - synth_res.flags = IORESOURCE_BUSY; - return request_resource(parent, &synth_res); -} -EXPORT_SYMBOL_GPL(synth_request_region); - -int synth_release_region(unsigned long start, unsigned long n) -{ - return release_resource(&synth_res); -} -EXPORT_SYMBOL_GPL(synth_release_region); - -struct var_t synth_time_vars[] = { - { DELAY, .u.n = {NULL, 100, 100, 2000, 0, 0, NULL } }, - { TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL } }, - { JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL } }, - { FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL } }, - V_LAST_VAR -}; - -/* called by: speakup_init() */ -int synth_init(char *synth_name) -{ - int ret = 0; - struct spk_synth *tmp, *synth = NULL; - - if (!synth_name) - return 0; - - if (strcmp(synth_name, "none") == 0) { - mutex_lock(&spk_mutex); - synth_release(); - mutex_unlock(&spk_mutex); - return 0; - } - - mutex_lock(&spk_mutex); - /* First, check if we already have it loaded. */ - list_for_each_entry(tmp, &synths, node) { - if (strcmp(tmp->name, synth_name) == 0) - synth = tmp; - } - - /* If we got one, initialize it now. */ - if (synth) - ret = do_synth_init(synth); - else - ret = -ENODEV; - mutex_unlock(&spk_mutex); - - return ret; -} - -/* called by: synth_add() */ -static int do_synth_init(struct spk_synth *in_synth) -{ - struct var_t *var; - - synth_release(); - if (in_synth->checkval != SYNTH_CHECK) - return -EINVAL; - synth = in_synth; - synth->alive = 0; - pr_warn("synth probe\n"); - if (synth->probe(synth) < 0) { - pr_warn("%s: device probe failed\n", in_synth->name); - synth = NULL; - return -ENODEV; - } - synth_time_vars[0].u.n.value = - synth_time_vars[0].u.n.default_val = synth->delay; - synth_time_vars[1].u.n.value = - synth_time_vars[1].u.n.default_val = synth->trigger; - synth_time_vars[2].u.n.value = - synth_time_vars[2].u.n.default_val = synth->jiffies; - synth_time_vars[3].u.n.value = - synth_time_vars[3].u.n.default_val = synth->full; - synth_printf("%s", synth->init); - for (var = synth->vars; - (var->var_id >= 0) && (var->var_id < MAXVARS); var++) - speakup_register_var(var); - if (!spk_quiet_boot) - synth_printf("%s found\n", synth->long_name); - if (synth->attributes.name && - sysfs_create_group(speakup_kobj, &synth->attributes) < 0) - return -ENOMEM; - synth_flags = synth->flags; - wake_up_interruptible_all(&speakup_event); - if (speakup_task) - wake_up_process(speakup_task); - return 0; -} - -void synth_release(void) -{ - struct var_t *var; - unsigned long flags; - - if (!synth) - return; - spin_lock_irqsave(&speakup_info.spinlock, flags); - pr_info("releasing synth %s\n", synth->name); - synth->alive = 0; - del_timer(&thread_timer); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (synth->attributes.name) - sysfs_remove_group(speakup_kobj, &synth->attributes); - for (var = synth->vars; var->var_id != MAXVARS; var++) - speakup_unregister_var(var->var_id); - synth->release(); - synth = NULL; -} - -/* called by: all_driver_init() */ -int synth_add(struct spk_synth *in_synth) -{ - int status = 0; - struct spk_synth *tmp; - - mutex_lock(&spk_mutex); - - list_for_each_entry(tmp, &synths, node) { - if (tmp == in_synth) { - mutex_unlock(&spk_mutex); - return 0; - } - } - - if (in_synth->startup) - status = do_synth_init(in_synth); - - if (!status) - list_add_tail(&in_synth->node, &synths); - - mutex_unlock(&spk_mutex); - return status; -} -EXPORT_SYMBOL_GPL(synth_add); - -void synth_remove(struct spk_synth *in_synth) -{ - mutex_lock(&spk_mutex); - if (synth == in_synth) - synth_release(); - list_del(&in_synth->node); - module_status = 0; - mutex_unlock(&spk_mutex); -} -EXPORT_SYMBOL_GPL(synth_remove); - -struct spk_synth *synth_current(void) -{ - return synth; -} -EXPORT_SYMBOL_GPL(synth_current); - -short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; diff --git a/drivers/staging/speakup/sysfs-driver-speakup b/drivers/staging/speakup/sysfs-driver-speakup deleted file mode 100644 index c6a32c434ce9..000000000000 --- a/drivers/staging/speakup/sysfs-driver-speakup +++ /dev/null @@ -1,375 +0,0 @@ -What: /sys/accessibility/speakup/attrib_bleep -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Beeps the PC speaker when there is an attribute change such as - foreground or background color when using speakup review - commands. One = on, zero = off. - -What: /sys/accessibility/speakup/bell_pos -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This works much like a typewriter bell. If for example 72 is - echoed to bell_pos, it will beep the PC speaker when typing on - a line past character 72. - -What: /sys/accessibility/speakup/bleeps -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This controls whether one hears beeps through the PC speaker - when using speakup's review commands. - TODO: what values does it accept? - -What: /sys/accessibility/speakup/bleep_time -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This controls the duration of the PC speaker beeps speakup - produces. - TODO: What are the units? Jiffies? - -What: /sys/accessibility/speakup/cursor_time -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This controls cursor delay when using arrow keys. When a - connection is very slow, with the default setting, when moving - with the arrows, or backspacing etc. speakup says the incorrect - characters. Set this to a higher value to adjust for the delay - and better synchronisation between cursor position and speech. - -What: /sys/accessibility/speakup/delimiters -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Delimit a word from speakup. - TODO: add more info - -What: /sys/accessibility/speakup/ex_num -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: TODO: - -What: /sys/accessibility/speakup/key_echo -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Controls if speakup speaks keys when they are typed. One = on, - zero = off or don't echo keys. - -What: /sys/accessibility/speakup/keymap -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Speakup keymap remaps keys to Speakup functions. - It uses a binary - format. A special program called genmap is needed to compile a - textual keymap into the binary format which is then loaded into - /sys/accessibility/speakup/keymap. - -What: /sys/accessibility/speakup/no_interrupt -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Controls if typing interrupts output from speakup. With - no_interrupt set to zero, typing on the keyboard will interrupt - speakup if for example - the say screen command is used before the - entire screen is read. - With no_interrupt set to one, if the say - screen command is used, and one then types on the keyboard, - speakup will continue to say the whole screen regardless until - it finishes. - -What: /sys/accessibility/speakup/punc_all -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This is a list of all the punctuation speakup should speak when - punc_level is set to four. - -What: /sys/accessibility/speakup/punc_level -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Controls the level of punctuation spoken as the screen is - displayed, not reviewed. Levels range from zero no punctuation, - to four, all punctuation. One corresponds to punc_some, two - corresponds to punc_most, and three as well as four both - correspond to punc_all. Some hardware synthesizers may have - different levels each corresponding to three and four for - punc_level. Also note that if punc_level is set to zero, and - key_echo is set to one, typed punctuation is still spoken as it - is typed. - -What: /sys/accessibility/speakup/punc_most -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This is a list of all the punctuation speakup should speak when - punc_level is set to two. - -What: /sys/accessibility/speakup/punc_some -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This is a list of all the punctuation speakup should speak when - punc_level is set to one. - -What: /sys/accessibility/speakup/reading_punc -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Almost the same as punc_level, the differences being that - reading_punc controls the level of punctuation when reviewing - the screen with speakup's screen review commands. The other - difference is that reading_punc set to three speaks punc_all, - and reading_punc set to four speaks all punctuation, including - spaces. - -What: /sys/accessibility/speakup/repeats -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: A list of characters speakup repeats. Normally, when there are - more than three characters in a row, speakup - just reads three of - those characters. For example, "......" would be read as dot, - dot, dot. If a . is added to the list of characters in repeats, - "......" would be read as dot, dot, dot, times six. - -What: /sys/accessibility/speakup/say_control -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: If set to one, speakup speaks shift, alt and control when those - keys are pressed. If say_control is set to zero, shift, ctrl, - and alt are not spoken when they are pressed. - -What: /sys/accessibility/speakup/say_word_ctl -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: TODO: - -What: /sys/accessibility/speakup/silent -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: TODO: - -What: /sys/accessibility/speakup/spell_delay -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This controls how fast a word is spelled - when speakup's say word - review command is pressed twice quickly to speak the current - word being reviewed. Zero just speaks the letters one after - another, while values one through four - seem to introduce more of - a pause between the spelling of each letter by speakup. - -What: /sys/accessibility/speakup/synth -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Gets or sets the synthesizer driver currently in use. Reading - synth returns the synthesizer driver currently in use. Writing - synth switches to the given synthesizer driver, provided it is - either built into the kernel, or already loaded as a module. - -What: /sys/accessibility/speakup/synth_direct -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Sends whatever is written to synth_direct - directly to the speech synthesizer in use, bypassing speakup. - This could be used to make the synthesizer speak - a string, or to - send control sequences to the synthesizer to change how the - synthesizer behaves. - -What: /sys/accessibility/speakup/version -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Reading version returns the version of speakup, and the version - of the synthesizer driver currently in use. - -What: /sys/accessibility/speakup/i18n/announcements -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This file contains various general announcements, most of which - cannot be categorized. You will find messages such as "You - killed Speakup", "I'm alive", "leaving help", "parked", - "unparked", and others. You will also find the names of the - screen edges and cursor tracking modes here. - -What: /sys/accessibility/speakup/i18n/chartab -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: TODO - -What: /sys/accessibility/speakup/i18n/ctl_keys -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Here, you will find names of control keys. These are used with - Speakup's say_control feature. - -What: /sys/accessibility/speakup/i18n/function_names -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Here, you will find a list of names for Speakup functions. - These are used by the help system. For example, suppose that - you have activated help mode, and you pressed - keypad 3. Speakup - says: "keypad 3 is character, say next." - The message "character, say next" names a Speakup function, and - it comes from this function_names file. - -What: /sys/accessibility/speakup/i18n/states -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This file contains names for key states. - Again, these are part of the help system. For instance, if you - had pressed speakup + keypad 3, you would hear: - "speakup keypad 3 is go to bottom edge." - The speakup key is depressed, so the name of the key state is - speakup. - This part of the message comes from the states collection. - -What: /sys/accessibility/speakup/i18n/characters -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Through this sys entry, Speakup gives you the ability to change - how Speakup pronounces a given character. You could, for - example, change how some punctuation characters are spoken. You - can even change how Speakup will pronounce certain letters. For - further details see '12. Changing the Pronunciation of - Characters' in Speakup User's Guide (file spkguide.txt in - source). - -What: /sys/accessibility/speakup/i18n/colors -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: When you use the "say attributes" function, Speakup says the - name of the foreground and background colors. These names come - from the i18n/colors file. - -What: /sys/accessibility/speakup/i18n/formatted -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This group of messages contains embedded formatting codes, to - specify the type and width of displayed data. If you change - these, you must preserve all of the formatting codes, and they - must appear in the order used by the default messages. - -What: /sys/accessibility/speakup/i18n/key_names -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Again, key_names is used by Speakup's help system. In the - previous example, Speakup said that you pressed "keypad 3." - This name came from the key_names file. - -What: /sys/accessibility/speakup// -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: In `/sys/accessibility/speakup` is a directory corresponding to - the synthesizer driver currently in use (E.G) `soft` for the - soft driver. This directory contains files which control the - speech synthesizer itself, - as opposed to controlling the speakup - screen reader. The parameters in this directory have the same - names and functions across all - supported synthesizers. The range - of values for freq, pitch, rate, and vol is the same for all - supported synthesizers, with the given range being internally - mapped by the driver to more or less fit the range of values - supported for a given parameter by the individual synthesizer. - Below is a description of values and parameters for soft - synthesizer, which is currently the most commonly used. - -What: /sys/accessibility/speakup/soft/caps_start -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This is the string that is sent to the synthesizer to cause it - to start speaking uppercase letters. For the soft synthesizer - and most others, this causes the pitch of the voice to rise - above the currently set pitch. - -What: /sys/accessibility/speakup/soft/caps_stop -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This is the string sent to the synthesizer to cause it to stop - speaking uppercase letters. In the case of the soft synthesizer - and most others, this returns the pitch of the voice - down to the - currently set pitch. - -What: /sys/accessibility/speakup/soft/delay_time -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: TODO: - -What: /sys/accessibility/speakup/soft/direct -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Controls if punctuation is spoken by speakup, or by the - synthesizer. - For example, speakup speaks ">" as "greater", while - the espeak synthesizer used by the soft driver speaks "greater - than". Zero lets speakup speak the punctuation. One lets the - synthesizer itself speak punctuation. - -What: /sys/accessibility/speakup/soft/freq -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Gets or sets the frequency of the speech synthesizer. Range is - 0-9. - -What: /sys/accessibility/speakup/soft/full_time -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: TODO: - -What: /sys/accessibility/speakup/soft/jiffy_delta -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: This controls how many jiffys the kernel gives to the - synthesizer. Setting this too high can make a system unstable, - or even crash it. - -What: /sys/accessibility/speakup/soft/pitch -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Gets or sets the pitch of the synthesizer. The range is 0-9. - -What: /sys/accessibility/speakup/soft/inflection -KernelVersion: 5.8 -Contact: speakup@linux-speakup.org -Description: Gets or sets the inflection of the synthesizer, i.e. the pitch - range. The range is 0-9. - -What: /sys/accessibility/speakup/soft/punct -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Gets or sets the amount of punctuation spoken by the - synthesizer. The range for the soft driver seems to be 0-2. - TODO: How is this related to speakup's punc_level, or - reading_punc. - -What: /sys/accessibility/speakup/soft/rate -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Gets or sets the rate of the synthesizer. Range is from zero - slowest, to nine fastest. - -What: /sys/accessibility/speakup/soft/tone -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Gets or sets the tone of the speech synthesizer. The range for - the soft driver seems to be 0-2. This seems to make no - difference if using espeak and the espeakup connector. - TODO: does espeakup support different tonalities? - -What: /sys/accessibility/speakup/soft/trigger_time -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: TODO: - -What: /sys/accessibility/speakup/soft/voice -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Gets or sets the voice used by the synthesizer if the - synthesizer can speak in more than one voice. The range for the - soft driver is 0-7. Note that while espeak supports multiple - voices, this parameter will not set the voice when the espeakup - connector is used between speakup and espeak. - -What: /sys/accessibility/speakup/soft/vol -KernelVersion: 2.6 -Contact: speakup@linux-speakup.org -Description: Gets or sets the volume of the speech synthesizer. Range is 0-9, - with zero being the softest, and nine being the loudest. - diff --git a/drivers/staging/speakup/thread.c b/drivers/staging/speakup/thread.c deleted file mode 100644 index 2fc75e60fbac..000000000000 --- a/drivers/staging/speakup/thread.c +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -#include "spk_types.h" -#include "speakup.h" -#include "spk_priv.h" - -DECLARE_WAIT_QUEUE_HEAD(speakup_event); -EXPORT_SYMBOL_GPL(speakup_event); - -int speakup_thread(void *data) -{ - unsigned long flags; - int should_break; - struct bleep our_sound; - - our_sound.active = 0; - our_sound.freq = 0; - our_sound.jiffies = 0; - - mutex_lock(&spk_mutex); - while (1) { - DEFINE_WAIT(wait); - - while (1) { - spin_lock_irqsave(&speakup_info.spinlock, flags); - our_sound = spk_unprocessed_sound; - spk_unprocessed_sound.active = 0; - prepare_to_wait(&speakup_event, &wait, - TASK_INTERRUPTIBLE); - should_break = kthread_should_stop() || - our_sound.active || - (synth && synth->catch_up && synth->alive && - (speakup_info.flushing || - !synth_buffer_empty())); - spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (should_break) - break; - mutex_unlock(&spk_mutex); - schedule(); - mutex_lock(&spk_mutex); - } - finish_wait(&speakup_event, &wait); - if (kthread_should_stop()) - break; - - if (our_sound.active) - kd_mksound(our_sound.freq, our_sound.jiffies); - if (synth && synth->catch_up && synth->alive) { - /* - * It is up to the callee to take the lock, so that it - * can sleep whenever it likes - */ - synth->catch_up(synth); - } - - speakup_start_ttys(); - } - mutex_unlock(&spk_mutex); - return 0; -} diff --git a/drivers/staging/speakup/varhandlers.c b/drivers/staging/speakup/varhandlers.c deleted file mode 100644 index d7f6bec7ff06..000000000000 --- a/drivers/staging/speakup/varhandlers.c +++ /dev/null @@ -1,339 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include "spk_types.h" -#include "spk_priv.h" -#include "speakup.h" - -static struct st_var_header var_headers[] = { - { "version", VERSION, VAR_PROC, NULL, NULL }, - { "synth_name", SYNTH, VAR_PROC, NULL, NULL }, - { "keymap", KEYMAP, VAR_PROC, NULL, NULL }, - { "silent", SILENT, VAR_PROC, NULL, NULL }, - { "punc_some", PUNC_SOME, VAR_PROC, NULL, NULL }, - { "punc_most", PUNC_MOST, VAR_PROC, NULL, NULL }, - { "punc_all", PUNC_ALL, VAR_PROC, NULL, NULL }, - { "delimiters", DELIM, VAR_PROC, NULL, NULL }, - { "repeats", REPEATS, VAR_PROC, NULL, NULL }, - { "ex_num", EXNUMBER, VAR_PROC, NULL, NULL }, - { "characters", CHARS, VAR_PROC, NULL, NULL }, - { "synth_direct", SYNTH_DIRECT, VAR_PROC, NULL, NULL }, - { "caps_start", CAPS_START, VAR_STRING, spk_str_caps_start, NULL }, - { "caps_stop", CAPS_STOP, VAR_STRING, spk_str_caps_stop, NULL }, - { "delay_time", DELAY, VAR_TIME, NULL, NULL }, - { "trigger_time", TRIGGER, VAR_TIME, NULL, NULL }, - { "jiffy_delta", JIFFY, VAR_TIME, NULL, NULL }, - { "full_time", FULL, VAR_TIME, NULL, NULL }, - { "spell_delay", SPELL_DELAY, VAR_NUM, &spk_spell_delay, NULL }, - { "bleeps", BLEEPS, VAR_NUM, &spk_bleeps, NULL }, - { "attrib_bleep", ATTRIB_BLEEP, VAR_NUM, &spk_attrib_bleep, NULL }, - { "bleep_time", BLEEP_TIME, VAR_TIME, &spk_bleep_time, NULL }, - { "cursor_time", CURSOR_TIME, VAR_TIME, NULL, NULL }, - { "punc_level", PUNC_LEVEL, VAR_NUM, &spk_punc_level, NULL }, - { "reading_punc", READING_PUNC, VAR_NUM, &spk_reading_punc, NULL }, - { "say_control", SAY_CONTROL, VAR_NUM, &spk_say_ctrl, NULL }, - { "say_word_ctl", SAY_WORD_CTL, VAR_NUM, &spk_say_word_ctl, NULL }, - { "no_interrupt", NO_INTERRUPT, VAR_NUM, &spk_no_intr, NULL }, - { "key_echo", KEY_ECHO, VAR_NUM, &spk_key_echo, NULL }, - { "bell_pos", BELL_POS, VAR_NUM, &spk_bell_pos, NULL }, - { "rate", RATE, VAR_NUM, NULL, NULL }, - { "pitch", PITCH, VAR_NUM, NULL, NULL }, - { "inflection", INFLECTION, VAR_NUM, NULL, NULL }, - { "vol", VOL, VAR_NUM, NULL, NULL }, - { "tone", TONE, VAR_NUM, NULL, NULL }, - { "punct", PUNCT, VAR_NUM, NULL, NULL }, - { "voice", VOICE, VAR_NUM, NULL, NULL }, - { "freq", FREQUENCY, VAR_NUM, NULL, NULL }, - { "lang", LANG, VAR_NUM, NULL, NULL }, - { "chartab", CHARTAB, VAR_PROC, NULL, NULL }, - { "direct", DIRECT, VAR_NUM, NULL, NULL }, - { "pause", PAUSE, VAR_STRING, spk_str_pause, NULL }, -}; - -static struct st_var_header *var_ptrs[MAXVARS] = { NULL, NULL, NULL }; - -static struct punc_var_t punc_vars[] = { - { PUNC_SOME, 1 }, - { PUNC_MOST, 2 }, - { PUNC_ALL, 3 }, - { DELIM, 4 }, - { REPEATS, 5 }, - { EXNUMBER, 6 }, - { -1, -1 }, -}; - -int spk_chartab_get_value(char *keyword) -{ - int value = 0; - - if (!strcmp(keyword, "ALPHA")) - value = ALPHA; - else if (!strcmp(keyword, "B_CTL")) - value = B_CTL; - else if (!strcmp(keyword, "WDLM")) - value = WDLM; - else if (!strcmp(keyword, "A_PUNC")) - value = A_PUNC; - else if (!strcmp(keyword, "PUNC")) - value = PUNC; - else if (!strcmp(keyword, "NUM")) - value = NUM; - else if (!strcmp(keyword, "A_CAP")) - value = A_CAP; - else if (!strcmp(keyword, "B_CAPSYM")) - value = B_CAPSYM; - else if (!strcmp(keyword, "B_SYM")) - value = B_SYM; - return value; -} - -void speakup_register_var(struct var_t *var) -{ - static char nothing[2] = "\0"; - int i; - struct st_var_header *p_header; - - BUG_ON(!var || var->var_id < 0 || var->var_id >= MAXVARS); - if (!var_ptrs[0]) { - for (i = 0; i < MAXVARS; i++) { - p_header = &var_headers[i]; - var_ptrs[p_header->var_id] = p_header; - p_header->data = NULL; - } - } - p_header = var_ptrs[var->var_id]; - if (p_header->data) - return; - p_header->data = var; - switch (p_header->var_type) { - case VAR_STRING: - spk_set_string_var(nothing, p_header, 0); - break; - case VAR_NUM: - case VAR_TIME: - spk_set_num_var(0, p_header, E_DEFAULT); - break; - default: - break; - } -} - -void speakup_unregister_var(enum var_id_t var_id) -{ - struct st_var_header *p_header; - - BUG_ON(var_id < 0 || var_id >= MAXVARS); - p_header = var_ptrs[var_id]; - p_header->data = NULL; -} - -struct st_var_header *spk_get_var_header(enum var_id_t var_id) -{ - struct st_var_header *p_header; - - if (var_id < 0 || var_id >= MAXVARS) - return NULL; - p_header = var_ptrs[var_id]; - if (!p_header->data) - return NULL; - return p_header; -} - -struct st_var_header *spk_var_header_by_name(const char *name) -{ - int i; - - if (!name) - return NULL; - - for (i = 0; i < MAXVARS; i++) { - if (strcmp(name, var_ptrs[i]->name) == 0) - return var_ptrs[i]; - } - return NULL; -} - -struct var_t *spk_get_var(enum var_id_t var_id) -{ - BUG_ON(var_id < 0 || var_id >= MAXVARS); - BUG_ON(!var_ptrs[var_id]); - return var_ptrs[var_id]->data; -} -EXPORT_SYMBOL_GPL(spk_get_var); - -struct punc_var_t *spk_get_punc_var(enum var_id_t var_id) -{ - struct punc_var_t *rv = NULL; - struct punc_var_t *where; - - where = punc_vars; - while ((where->var_id != -1) && (!rv)) { - if (where->var_id == var_id) - rv = where; - else - where++; - } - return rv; -} - -/* handlers for setting vars */ -int spk_set_num_var(int input, struct st_var_header *var, int how) -{ - int val; - int *p_val = var->p_val; - char buf[32]; - char *cp; - struct var_t *var_data = var->data; - - if (!var_data) - return -ENODATA; - - val = var_data->u.n.value; - switch (how) { - case E_NEW_DEFAULT: - if (input < var_data->u.n.low || input > var_data->u.n.high) - return -ERANGE; - var_data->u.n.default_val = input; - return 0; - case E_DEFAULT: - val = var_data->u.n.default_val; - break; - case E_SET: - val = input; - break; - case E_INC: - val += input; - break; - case E_DEC: - val -= input; - break; - } - - if (val < var_data->u.n.low || val > var_data->u.n.high) - return -ERANGE; - - var_data->u.n.value = val; - if (var->var_type == VAR_TIME && p_val) { - *p_val = msecs_to_jiffies(val); - return 0; - } - if (p_val) - *p_val = val; - if (var->var_id == PUNC_LEVEL) { - spk_punc_mask = spk_punc_masks[val]; - return 0; - } - if (var_data->u.n.multiplier != 0) - val *= var_data->u.n.multiplier; - val += var_data->u.n.offset; - if (var->var_id < FIRST_SYNTH_VAR || !synth) - return 0; - if (synth->synth_adjust) - return synth->synth_adjust(var); - - if (!var_data->u.n.synth_fmt) - return 0; - if (var->var_id == PITCH) - cp = spk_pitch_buff; - else - cp = buf; - if (!var_data->u.n.out_str) - sprintf(cp, var_data->u.n.synth_fmt, (int)val); - else - sprintf(cp, var_data->u.n.synth_fmt, - var_data->u.n.out_str[val]); - synth_printf("%s", cp); - return 0; -} - -int spk_set_string_var(const char *page, struct st_var_header *var, int len) -{ - struct var_t *var_data = var->data; - - if (!var_data) - return -ENODATA; - if (len > MAXVARLEN) - return -E2BIG; - if (!len) { - if (!var_data->u.s.default_val) - return 0; - if (!var->p_val) - var->p_val = var_data->u.s.default_val; - if (var->p_val != var_data->u.s.default_val) - strcpy((char *)var->p_val, var_data->u.s.default_val); - return -ERESTART; - } else if (var->p_val) { - strcpy((char *)var->p_val, page); - } else { - return -E2BIG; - } - return 0; -} - -/* - * spk_set_mask_bits sets or clears the punc/delim/repeat bits, - * if input is null uses the defaults. - * values for how: 0 clears bits of chars supplied, - * 1 clears allk, 2 sets bits for chars - */ -int spk_set_mask_bits(const char *input, const int which, const int how) -{ - u_char *cp; - short mask = spk_punc_info[which].mask; - - if (how & 1) { - for (cp = (u_char *)spk_punc_info[3].value; *cp; cp++) - spk_chartab[*cp] &= ~mask; - } - cp = (u_char *)input; - if (!cp) { - cp = spk_punc_info[which].value; - } else { - for (; *cp; cp++) { - if (*cp < SPACE) - break; - if (mask < PUNC) { - if (!(spk_chartab[*cp] & PUNC)) - break; - } else if (spk_chartab[*cp] & B_NUM) { - break; - } - } - if (*cp) - return -EINVAL; - cp = (u_char *)input; - } - if (how & 2) { - for (; *cp; cp++) - if (*cp > SPACE) - spk_chartab[*cp] |= mask; - } else { - for (; *cp; cp++) - if (*cp > SPACE) - spk_chartab[*cp] &= ~mask; - } - return 0; -} - -char *spk_strlwr(char *s) -{ - char *p; - - if (!s) - return NULL; - - for (p = s; *p; p++) - *p = tolower(*p); - return s; -} - -char *spk_s2uchar(char *start, char *dest) -{ - int val; - - /* Do not replace with kstrtoul: here we need start to be updated */ - val = simple_strtoul(skip_spaces(start), &start, 10); - if (*start == ',') - start++; - *dest = (u_char)val; - return start; -} -- cgit v1.2.3