18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/* fakekey.c
38c2ecf20Sopenharmony_ci * Functions for simulating keypresses.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010 the Speakup Team
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/preempt.h>
108c2ecf20Sopenharmony_ci#include <linux/percpu.h>
118c2ecf20Sopenharmony_ci#include <linux/input.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "speakup.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define PRESSED 1
168c2ecf20Sopenharmony_ci#define RELEASED 0
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(int, reporting_keystroke);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic struct input_dev *virt_keyboard;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciint speakup_add_virtual_keyboard(void)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	int err;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	virt_keyboard = input_allocate_device();
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (!virt_keyboard)
298c2ecf20Sopenharmony_ci		return -ENOMEM;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	virt_keyboard->name = "Speakup";
328c2ecf20Sopenharmony_ci	virt_keyboard->id.bustype = BUS_VIRTUAL;
338c2ecf20Sopenharmony_ci	virt_keyboard->phys = "speakup/input0";
348c2ecf20Sopenharmony_ci	virt_keyboard->dev.parent = NULL;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	__set_bit(EV_KEY, virt_keyboard->evbit);
378c2ecf20Sopenharmony_ci	__set_bit(KEY_DOWN, virt_keyboard->keybit);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	err = input_register_device(virt_keyboard);
408c2ecf20Sopenharmony_ci	if (err) {
418c2ecf20Sopenharmony_ci		input_free_device(virt_keyboard);
428c2ecf20Sopenharmony_ci		virt_keyboard = NULL;
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return err;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_civoid speakup_remove_virtual_keyboard(void)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	if (virt_keyboard) {
518c2ecf20Sopenharmony_ci		input_unregister_device(virt_keyboard);
528c2ecf20Sopenharmony_ci		virt_keyboard = NULL;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Send a simulated down-arrow to the application.
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_civoid speakup_fake_down_arrow(void)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	unsigned long flags;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/* disable keyboard interrupts */
648c2ecf20Sopenharmony_ci	local_irq_save(flags);
658c2ecf20Sopenharmony_ci	/* don't change CPU */
668c2ecf20Sopenharmony_ci	preempt_disable();
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	__this_cpu_write(reporting_keystroke, true);
698c2ecf20Sopenharmony_ci	input_report_key(virt_keyboard, KEY_DOWN, PRESSED);
708c2ecf20Sopenharmony_ci	input_report_key(virt_keyboard, KEY_DOWN, RELEASED);
718c2ecf20Sopenharmony_ci	input_sync(virt_keyboard);
728c2ecf20Sopenharmony_ci	__this_cpu_write(reporting_keystroke, false);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* reenable preemption */
758c2ecf20Sopenharmony_ci	preempt_enable();
768c2ecf20Sopenharmony_ci	/* reenable keyboard interrupts */
778c2ecf20Sopenharmony_ci	local_irq_restore(flags);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Are we handling a simulated keypress on the current CPU?
828c2ecf20Sopenharmony_ci * Returns a boolean.
838c2ecf20Sopenharmony_ci */
848c2ecf20Sopenharmony_cibool speakup_fake_key_pressed(void)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	return this_cpu_read(reporting_keystroke);
878c2ecf20Sopenharmony_ci}
88