/*
* Copyright (c) 2011 Lubos Slovak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup drvusbhid
* @{
*/
/**
* @file
* USB HID keyboard autorepeat facilities
*/
#include <fibril_synch.h>
#include <io/kbd_event.h>
#include <io/keycode.h>
#include <errno.h>
#include <usb/debug.h>
#include "kbdrepeat.h"
#include "kbddev.h"
/**
* Main loop handling the auto-repeat of keys.
*
* This functions periodically checks if there is some key to be auto-repeated.
*
* If a new key is to be repeated, it uses the delay before first repeat stored
* in the keyboard structure to wait until the key has to start repeating.
*
* If the same key is still pressed, it uses the delay between repeats stored
* in the keyboard structure to wait until the key should be repeated.
*
* If the currently repeated key is not pressed any more (
* usb_kbd_repeat_stop() was called), it stops repeating it and starts
* checking again.
*
* @note For accessing the keyboard device auto-repeat information a fibril
* mutex (repeat_mtx) from the @a kbd structure is used.
*
* @param kbd Keyboard device structure.
*/
static void usb_kbd_repeat_loop(usb_kbd_t *kbd)
{
unsigned int delay = 0;
usb_log_debug("Starting autorepeat loop.");
while (true) {
/* Check if the kbd structure is usable. */
if (!usb_kbd_is_initialized(kbd)) {
usb_log_warning("kbd not ready, exiting autorepeat.");
return;
}
fibril_mutex_lock(&kbd->repeat_mtx);
if (kbd->repeat.key_new > 0) {
if (kbd->repeat.key_new == kbd->repeat.key_repeated) {
usb_log_debug2("Repeating key: %u.",
kbd->repeat.key_repeated);
usb_kbd_push_ev(kbd, KEY_PRESS,
kbd->repeat.key_repeated);
delay = kbd->repeat.delay_between;
} else {
usb_log_debug2("New key to repeat: %u.",
kbd->repeat.key_new);
kbd->repeat.key_repeated = kbd->repeat.key_new;
delay = kbd->repeat.delay_before;
}
} else {
if (kbd->repeat.key_repeated > 0) {
usb_log_debug2("Stopping to repeat key: %u.",
kbd->repeat.key_repeated);
kbd->repeat.key_repeated = 0;
}
delay = CHECK_DELAY;
}
fibril_mutex_unlock(&kbd->repeat_mtx);
fibril_usleep(delay);
}
}
/**
* Main routine to be executed by a fibril for handling auto-repeat.
*
* Starts the loop for checking changes in auto-repeat.
*
* @param arg User-specified argument. Expects pointer to the keyboard device
* structure representing the keyboard.
*
* @retval EOK if the routine has finished.
* @retval EINVAL if no argument is supplied.
*/
errno_t usb_kbd_repeat_fibril(void *arg)
{
usb_log_debug("Autorepeat fibril spawned.");
if (arg == NULL) {
usb_log_error("No device!");
return EINVAL;
}
usb_kbd_t *kbd = arg;
usb_kbd_repeat_loop(kbd);
return EOK;
}
/**
* Start repeating particular key.
*
* @note Only one key is repeated at any time, so calling this function
* effectively cancels auto-repeat of the current repeated key (if any)
* and 'schedules' another key for auto-repeat.
*
* @param kbd Keyboard device structure.
* @param key Key to start repeating.
*/
void usb_kbd_repeat_start(usb_kbd_t *kbd, unsigned int key)
{
fibril_mutex_lock(&kbd->repeat_mtx);
kbd->repeat.key_new = key;
fibril_mutex_unlock(&kbd->repeat_mtx);
}
/**
* Stop repeating particular key.
*
* @note Only one key is repeated at any time, but this function may be called
* even with key that is not currently repeated (in that case nothing
* happens).
*
* @param kbd Keyboard device structure.
* @param key Key to stop repeating.
*/
void usb_kbd_repeat_stop(usb_kbd_t *kbd, unsigned int key)
{
fibril_mutex_lock(&kbd->repeat_mtx);
if (key == kbd->repeat.key_new) {
kbd->repeat.key_new = 0;
}
fibril_mutex_unlock(&kbd->repeat_mtx);
}
/**
* @}
*/