Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 4628 → Rev 4629

/branches/arm/kernel/arch/arm32/src/drivers/pl050.c
0,0 → 1,225
/*
* Copyright (c) 2009 Vineeth Pillai
* 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 arm32
* @{
*/
/**
* @file
* @brief pl050 keyboard/mouse driver.
*
* It takes care of low-level keyboard functions.
*/
 
#include <genarch/kbd/key.h>
#include <cpu.h>
#include <arch/asm.h>
#include <arch.h>
#include <arch/drivers/pl050.h>
#include <console/console.h>
#include <interrupt.h>
#include <sysinfo/sysinfo.h>
#include <ipc/irq.h>
 
#include <arch/debug/print.h>
#include <genarch/fb/fb.h>
#include <genarch/fb/visuals.h>
#include <print.h>
 
#define PL050_KEY_RELEASE 0xF0
#define PL050_ESC_KEY 0xE0
 
static void pl050_suspend(chardev_t *);
static void pl050_resume(chardev_t *);
 
static chardev_operations_t ops = {
.suspend = pl050_suspend,
.resume = pl050_resume,
.read = pl050_key_read
};
 
/** Structure for pl050's IRQ. */
static irq_t pl050_kbd_irq;
/* static irq_t pl050_mouse_irq; */
static uintptr_t pl050_kbd_pbase;
static uintptr_t pl050_kbd_vbase;
 
uint8_t pl050_dataread(void)
{
return *(uint8_t *)(pl050_kbd_vbase + PL050_DATA);
}
 
uint8_t pl050_statusread(void)
{
return *(uint8_t *)(pl050_kbd_vbase + PL050_STAT);
}
 
void pl050_crwrite(uint8_t val)
{
*(uint8_t *)pl050_kbd_vbase = val;
}
void pl050_grab(void)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&pl050_kbd_irq.lock);
pl050_kbd_irq.notif_cfg.notify = false;
spinlock_unlock(&pl050_kbd_irq.lock);
interrupts_restore(ipl);
}
 
void pl050_release(void)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&pl050_kbd_irq.lock);
if (pl050_kbd_irq.notif_cfg.answerbox)
pl050_kbd_irq.notif_cfg.notify = true;
spinlock_unlock(&pl050_kbd_irq.lock);
interrupts_restore(ipl);
}
 
static irq_ownership_t pl050_claim(void)
{
return IRQ_ACCEPT;
}
 
static void pl050_irq_handler(irq_t *irq, void *arg, ...)
{
static int key_released_flag = 0;
static uint8_t prev_char = 0xFF;
 
if (irq->notif_cfg.notify && irq->notif_cfg.answerbox)
ipc_irq_send_notif(irq);
else {
uint8_t data;
uint8_t status;
if (((status = pl050_statusread()) & PL050_STAT_RXFULL)) {
data = pl050_dataread();
 
if (data == PL050_ESC_KEY)
return;
if (data == PL050_KEY_RELEASE) {
key_released_flag = 1;
} else {
if (key_released_flag && prev_char == data) {
prev_char = 0xFF;
key_released(data);
} else {
key_pressed(data);
prev_char = data;
}
key_released_flag = 0;
}
}
}
}
 
/** Initialize pl050. */
void pl050_init(devno_t kbd_devno, inr_t kbd_inr, uintptr_t pbase, uintptr_t vbase)
{
uint8_t val = 0;
chardev_initialize("pl050_kbd", &kbrd, &ops);
stdin = &kbrd;
irq_initialize(&pl050_kbd_irq);
pl050_kbd_irq.devno = kbd_devno;
pl050_kbd_irq.inr = kbd_inr;
pl050_kbd_irq.claim = pl050_claim;
pl050_kbd_irq.handler = pl050_irq_handler;
pl050_kbd_pbase = pbase;
pl050_kbd_vbase = vbase;
irq_register(&pl050_kbd_irq);
val = PL050_CR_RXINTR | PL050_CR_INTR | PL050_CR_FKMIC;
 
pl050_crwrite(val);
 
/* reset the data buffer */
pl050_dataread();
sysinfo_set_item_val("kbd", NULL, true);
sysinfo_set_item_val("kbd.devno", NULL, kbd_devno);
sysinfo_set_item_val("kbd.inr", NULL, kbd_inr);
sysinfo_set_item_val("kbd.pbase", NULL, pl050_kbd_pbase);
sysinfo_set_item_val("kbd.vbase", NULL, vbase);
pl050_grab();
}
 
/* Called from getc(). */
void pl050_resume(chardev_t *d)
{
}
 
/* Called from getc(). */
void pl050_suspend(chardev_t *d)
{
}
 
char pl050_key_read(chardev_t *d)
{
char ch;
 
while(!(ch = active_read_buff_read())) {
uint8_t x;
while (!(pl050_statusread() & PL050_STAT_RXFULL))
;
x = pl050_dataread();
if (x & KEY_RELEASE)
key_released(x ^ KEY_RELEASE);
else
active_read_key_pressed(x);
}
return ch;
}
 
/** Poll for key press and release events.
*
* This function can be used to implement keyboard polling.
*/
void pl050_poll(void)
{
uint8_t x;
 
while (((x = pl050_statusread() & PL050_STAT_RXFULL))) {
x = pl050_dataread();
if (x & KEY_RELEASE)
key_released(x ^ KEY_RELEASE);
else
key_pressed(x);
}
}
 
/** @}
*/