1 // SPDX-License-Identifier: GPL-2.0+
2 /* fakekey.c
3  * Functions for simulating key presses.
4  *
5  * Copyright (C) 2010 the Speakup Team
6  */
7 #include <linux/types.h>
8 #include <linux/slab.h>
9 #include <linux/preempt.h>
10 #include <linux/percpu.h>
11 #include <linux/input.h>
12 
13 #include "speakup.h"
14 
15 #define PRESSED 1
16 #define RELEASED 0
17 
18 static DEFINE_PER_CPU(int, reporting_keystroke);
19 
20 static struct input_dev *virt_keyboard;
21 
speakup_add_virtual_keyboard(void)22 int speakup_add_virtual_keyboard(void)
23 {
24 	int err;
25 
26 	virt_keyboard = input_allocate_device();
27 
28 	if (!virt_keyboard)
29 		return -ENOMEM;
30 
31 	virt_keyboard->name = "Speakup";
32 	virt_keyboard->id.bustype = BUS_VIRTUAL;
33 	virt_keyboard->phys = "speakup/input0";
34 	virt_keyboard->dev.parent = NULL;
35 
36 	__set_bit(EV_KEY, virt_keyboard->evbit);
37 	__set_bit(KEY_DOWN, virt_keyboard->keybit);
38 
39 	err = input_register_device(virt_keyboard);
40 	if (err) {
41 		input_free_device(virt_keyboard);
42 		virt_keyboard = NULL;
43 	}
44 
45 	return err;
46 }
47 
speakup_remove_virtual_keyboard(void)48 void speakup_remove_virtual_keyboard(void)
49 {
50 	if (virt_keyboard) {
51 		input_unregister_device(virt_keyboard);
52 		virt_keyboard = NULL;
53 	}
54 }
55 
56 /*
57  * Send a simulated down-arrow to the application.
58  */
speakup_fake_down_arrow(void)59 void speakup_fake_down_arrow(void)
60 {
61 	unsigned long flags;
62 
63 	/* disable keyboard interrupts */
64 	local_irq_save(flags);
65 	/* don't change CPU */
66 	preempt_disable();
67 
68 	__this_cpu_write(reporting_keystroke, true);
69 	input_report_key(virt_keyboard, KEY_DOWN, PRESSED);
70 	input_report_key(virt_keyboard, KEY_DOWN, RELEASED);
71 	input_sync(virt_keyboard);
72 	__this_cpu_write(reporting_keystroke, false);
73 
74 	/* reenable preemption */
75 	preempt_enable();
76 	/* reenable keyboard interrupts */
77 	local_irq_restore(flags);
78 }
79 
80 /*
81  * Are we handling a simulated key press on the current CPU?
82  * Returns a boolean.
83  */
speakup_fake_key_pressed(void)84 bool speakup_fake_key_pressed(void)
85 {
86 	return this_cpu_read(reporting_keystroke);
87 }
88