162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* fakekey.c
362306a36Sopenharmony_ci * Functions for simulating key presses.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 the Speakup Team
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/types.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/preempt.h>
1062306a36Sopenharmony_ci#include <linux/percpu.h>
1162306a36Sopenharmony_ci#include <linux/input.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "speakup.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define PRESSED 1
1662306a36Sopenharmony_ci#define RELEASED 0
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic DEFINE_PER_CPU(int, reporting_keystroke);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic struct input_dev *virt_keyboard;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciint speakup_add_virtual_keyboard(void)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	int err;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	virt_keyboard = input_allocate_device();
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (!virt_keyboard)
2962306a36Sopenharmony_ci		return -ENOMEM;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	virt_keyboard->name = "Speakup";
3262306a36Sopenharmony_ci	virt_keyboard->id.bustype = BUS_VIRTUAL;
3362306a36Sopenharmony_ci	virt_keyboard->phys = "speakup/input0";
3462306a36Sopenharmony_ci	virt_keyboard->dev.parent = NULL;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	__set_bit(EV_KEY, virt_keyboard->evbit);
3762306a36Sopenharmony_ci	__set_bit(KEY_DOWN, virt_keyboard->keybit);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	err = input_register_device(virt_keyboard);
4062306a36Sopenharmony_ci	if (err) {
4162306a36Sopenharmony_ci		input_free_device(virt_keyboard);
4262306a36Sopenharmony_ci		virt_keyboard = NULL;
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return err;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_civoid speakup_remove_virtual_keyboard(void)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	if (virt_keyboard) {
5162306a36Sopenharmony_ci		input_unregister_device(virt_keyboard);
5262306a36Sopenharmony_ci		virt_keyboard = NULL;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci * Send a simulated down-arrow to the application.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_civoid speakup_fake_down_arrow(void)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	unsigned long flags;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* disable keyboard interrupts */
6462306a36Sopenharmony_ci	local_irq_save(flags);
6562306a36Sopenharmony_ci	/* don't change CPU */
6662306a36Sopenharmony_ci	preempt_disable();
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	__this_cpu_write(reporting_keystroke, true);
6962306a36Sopenharmony_ci	input_report_key(virt_keyboard, KEY_DOWN, PRESSED);
7062306a36Sopenharmony_ci	input_report_key(virt_keyboard, KEY_DOWN, RELEASED);
7162306a36Sopenharmony_ci	input_sync(virt_keyboard);
7262306a36Sopenharmony_ci	__this_cpu_write(reporting_keystroke, false);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* reenable preemption */
7562306a36Sopenharmony_ci	preempt_enable();
7662306a36Sopenharmony_ci	/* reenable keyboard interrupts */
7762306a36Sopenharmony_ci	local_irq_restore(flags);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * Are we handling a simulated key press on the current CPU?
8262306a36Sopenharmony_ci * Returns a boolean.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_cibool speakup_fake_key_pressed(void)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	return this_cpu_read(reporting_keystroke);
8762306a36Sopenharmony_ci}
88