Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 4376 → Rev 4377

/branches/tracing/kernel/arch/sparc64/src/drivers/sgcn.c
31,25 → 31,25
*/
/**
* @file
* @brief SGCN driver.
* @brief SGCN driver.
*/
 
#include <arch.h>
#include <arch/drivers/sgcn.h>
#include <arch/drivers/kbd.h>
#include <genarch/ofw/ofw_tree.h>
#include <debug.h>
#include <func.h>
#include <string.h>
#include <print.h>
#include <mm/page.h>
#include <ipc/irq.h>
#include <ddi/ddi.h>
#include <ddi/device.h>
#include <proc/thread.h>
#include <console/chardev.h>
#include <console/console.h>
#include <ddi/device.h>
#include <sysinfo/sysinfo.h>
#include <synch/spinlock.h>
 
#define POLL_INTERVAL 10000
 
/*
* Physical address at which the SBBC starts. This value has been obtained
* by inspecting (using Simics) memory accesses made by OBP. It is valid
73,7 → 73,7
* that the OBP console buffer is not the only console buffer
* which can be used. It is, however, used because when the kernel
* is running, the OBP buffer is not used by OBP any more but OBP
* has already made neccessary arangements so that the output will
* has already made necessary arrangements so that the output will
* be read from the OBP buffer and input will go to the OBP buffer.
* Therefore HelenOS needs to make no such arrangements any more.
*/
82,16 → 82,6
/* magic string contained at the beginning of the console buffer */
#define SGCN_BUFFER_MAGIC "CON"
 
/**
* The driver is polling based, but in order to notify the userspace
* of a key being pressed, we need to supply the interface with some
* interrupt number. The interrupt number can be arbitrary as it it
* will never be used for identifying HW interrupts, but only in
* notifying the userspace.
*/
#define FICTIONAL_INR 1
 
 
/*
* Returns a pointer to the object of a given type which is placed at the given
* offset from the SRAM beginning.
106,14 → 96,11
* offset from the console buffer beginning.
*/
#define SGCN_BUFFER(type, offset) \
((type *) (sgcn_buffer_begin + (offset)))
((type *) (sgcn_buffer_begin + (offset)))
 
/** Returns a pointer to the console buffer header. */
#define SGCN_BUFFER_HEADER (SGCN_BUFFER(sgcn_buffer_header_t, 0))
 
/** defined in drivers/kbd.c */
extern kbd_type_t kbd_type;
 
/** starting address of SRAM, will be set by the init_sram_begin function */
static uintptr_t sram_begin;
 
123,14 → 110,9
*/
static uintptr_t sgcn_buffer_begin;
 
/**
* SGCN IRQ structure. So far used only for notifying the userspace of the
* key being pressed, not for kernel being informed about keyboard interrupts.
*/
static irq_t sgcn_irq;
/* true iff the kernel driver should ignore pressed keys */
static bool kbd_disabled;
 
// TODO think of a way how to synchronize accesses to SGCN buffer between the kernel and the userspace
 
/*
* Ensures that writing to the buffer and consequent update of the write pointer
* are together one atomic operation.
145,38 → 127,23
 
 
/* functions referenced from definitions of I/O operations structures */
static void sgcn_noop(chardev_t *);
static void sgcn_putchar(chardev_t *, const char);
static char sgcn_key_read(chardev_t *);
static void sgcn_putchar(outdev_t *, const wchar_t, bool);
 
/** character device operations */
static chardev_operations_t sgcn_ops = {
.suspend = sgcn_noop,
.resume = sgcn_noop,
.read = sgcn_key_read,
/** SGCN output device operations */
static outdev_operations_t sgcnout_ops = {
.write = sgcn_putchar
};
 
/** SGCN character device */
chardev_t sgcn_io;
static outdev_t sgcnout; /**< SGCN output device. */
 
/**
* Registers the physical area of the SRAM so that the userspace SGCN
* driver can map it. Moreover, it sets some sysinfo values (SRAM address
* and SRAM size).
* Set some sysinfo values (SRAM address and SRAM size).
*/
static void register_sram_parea(uintptr_t sram_begin_physical)
static void register_sram(uintptr_t sram_begin_physical)
{
static parea_t sram_parea;
sram_parea.pbase = sram_begin_physical;
sram_parea.vbase = (uintptr_t) sram_begin;
sram_parea.frames = MAPPED_AREA_SIZE / FRAME_SIZE;
sram_parea.cacheable = false;
ddi_parea_register(&sram_parea);
sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE);
sysinfo_set_item_val("sram.address.physical", NULL,
sram_begin_physical);
sram_begin_physical);
}
 
/**
187,9 → 154,6
* property of the "/chosen" OBP node. The sram_begin variable will
* be set to the virtual address which maps to the SRAM physical
* address.
*
* It also registers the physical area of SRAM and sets some sysinfo
* values (SRAM address and SRAM size).
*/
static void init_sram_begin(void)
{
199,19 → 163,19
 
chosen = ofw_tree_lookup("/chosen");
if (!chosen)
panic("Can't find /chosen.\n");
panic("Cannot find '/chosen'.");
 
iosram_toc = ofw_tree_getprop(chosen, "iosram-toc");
if (!iosram_toc)
panic("Can't find property \"iosram-toc\".\n");
panic("Cannot find property 'iosram-toc'.");
if (!iosram_toc->value)
panic("Can't find SRAM TOC.\n");
panic("Cannot find SRAM TOC.");
 
sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
+ *((uint32_t *) iosram_toc->value);
+ *((uint32_t *) iosram_toc->value);
sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
register_sram_parea(sram_begin_physical);
register_sram(sram_begin_physical);
}
 
/**
227,14 → 191,19
*/
static void sgcn_buffer_begin_init(void)
{
static bool initialized;
if (initialized)
return;
 
init_sram_begin();
ASSERT(strcmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
ASSERT(str_cmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
/* lookup TOC entry with the correct key */
uint32_t i;
for (i = 0; i < MAX_TOC_ENTRIES; i++) {
if (strcmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
if (str_cmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
break;
}
ASSERT(i < MAX_TOC_ENTRIES);
242,17 → 211,12
sgcn_buffer_begin = sram_begin + SRAM_TOC->keys[i].offset;
sysinfo_set_item_val("sram.buffer.offset", NULL,
SRAM_TOC->keys[i].offset);
SRAM_TOC->keys[i].offset);
initialized = true;
}
 
/**
* Default suspend/resume operation for the input device.
*/
static void sgcn_noop(chardev_t *d)
{
}
 
/**
* Writes a single character to the SGCN (circular) output buffer
* and updates the output write pointer so that SGCN gets to know
* that the character has been written.
265,7 → 229,7
/* we need pointers to volatile variables */
volatile char *buf_ptr = (volatile char *)
SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
volatile uint32_t *out_wrptr_ptr = &(SGCN_BUFFER_HEADER->out_wrptr);
volatile uint32_t *out_rdptr_ptr = &(SGCN_BUFFER_HEADER->out_rdptr);
 
291,66 → 255,31
}
 
/**
* SGCN output operation. Prints a single character to the SGCN. If the line
* feed character is written ('\n'), the carriage return character ('\r') is
* written straight away.
* SGCN output operation. Prints a single character to the SGCN. Newline
* character is converted to CRLF.
*/
static void sgcn_putchar(struct chardev * cd, const char c)
static void sgcn_putchar(outdev_t *od, const wchar_t ch, bool silent)
{
spinlock_lock(&sgcn_output_lock);
sgcn_do_putchar(c);
if (c == '\n') {
sgcn_do_putchar('\r');
if (!silent) {
spinlock_lock(&sgcn_output_lock);
if (ascii_check(ch)) {
if (ch == '\n')
sgcn_do_putchar('\r');
sgcn_do_putchar(ch);
} else
sgcn_do_putchar(U_SPECIAL);
spinlock_unlock(&sgcn_output_lock);
}
spinlock_unlock(&sgcn_output_lock);
}
 
/**
* Called when actively reading the character. Not implemented yet.
*/
static char sgcn_key_read(chardev_t *d)
{
return (char) 0;
}
 
/**
* The driver works in polled mode, so no interrupt should be handled by it.
*/
static irq_ownership_t sgcn_claim(void)
{
return IRQ_DECLINE;
}
 
/**
* The driver works in polled mode, so no interrupt should be handled by it.
*/
static void sgcn_irq_handler(irq_t *irq, void *arg, ...)
{
panic("Not yet implemented, SGCN works in polled mode.\n");
}
 
/**
* Grabs the input for kernel.
*/
void sgcn_grab(void)
{
ipl_t ipl = interrupts_disable();
volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
/* skip all the user typed before the grab and hasn't been processed */
spinlock_lock(&sgcn_input_lock);
*in_rdptr_ptr = *in_wrptr_ptr;
spinlock_unlock(&sgcn_input_lock);
 
spinlock_lock(&sgcn_irq.lock);
sgcn_irq.notif_cfg.notify = false;
spinlock_unlock(&sgcn_irq.lock);
interrupts_restore(ipl);
kbd_disabled = false;
}
 
/**
358,12 → 287,7
*/
void sgcn_release(void)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&sgcn_irq.lock);
if (sgcn_irq.notif_cfg.answerbox)
sgcn_irq.notif_cfg.notify = true;
spinlock_unlock(&sgcn_irq.lock);
interrupts_restore(ipl);
kbd_disabled = true;
}
 
/**
371,79 → 295,92
* there are some unread characters in the input queue. If so, it picks them up
* and sends them to the upper layers of HelenOS.
*/
void sgcn_poll(void)
static void sgcn_poll(sgcn_instance_t *instance)
{
uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
uint32_t end = SGCN_BUFFER_HEADER->in_end;
uint32_t size = end - begin;
 
if (kbd_disabled)
return;
 
spinlock_lock(&sgcn_input_lock);
ipl_t ipl = interrupts_disable();
spinlock_lock(&sgcn_irq.lock);
/* we need pointers to volatile variables */
volatile char *buf_ptr = (volatile char *)
SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
if (*in_rdptr_ptr != *in_wrptr_ptr) {
if (sgcn_irq.notif_cfg.notify && sgcn_irq.notif_cfg.answerbox) {
ipc_irq_send_notif(&sgcn_irq);
spinlock_unlock(&sgcn_irq.lock);
interrupts_restore(ipl);
spinlock_unlock(&sgcn_input_lock);
return;
}
}
spinlock_unlock(&sgcn_irq.lock);
interrupts_restore(ipl);
 
while (*in_rdptr_ptr != *in_wrptr_ptr) {
buf_ptr = (volatile char *)
SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
char c = *buf_ptr;
*in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
if (c == '\r') {
c = '\n';
}
chardev_push_character(&sgcn_io, c);
indev_push_character(instance->srlnin, c);
}
 
spinlock_unlock(&sgcn_input_lock);
}
 
/**
* A public function which initializes I/O from/to Serengeti console
* and sets it as a default input/output.
* Polling thread function.
*/
void sgcn_init(void)
static void ksgcnpoll(void *instance) {
while (1) {
if (!silent)
sgcn_poll(instance);
thread_usleep(POLL_INTERVAL);
}
}
 
/**
* A public function which initializes input from the Serengeti console.
*/
sgcn_instance_t *sgcnin_init(void)
{
sgcn_buffer_begin_init();
sgcn_instance_t *instance =
malloc(sizeof(sgcn_instance_t), FRAME_ATOMIC);
if (instance) {
instance->srlnin = NULL;
instance->thread = thread_create(ksgcnpoll, instance, TASK, 0,
"ksgcnpoll", true);
if (!instance->thread) {
free(instance);
return NULL;
}
}
return instance;
}
 
kbd_type = KBD_SGCN;
void sgcnin_wire(sgcn_instance_t *instance, indev_t *srlnin)
{
ASSERT(instance);
ASSERT(srlnin);
 
devno_t devno = device_assign_devno();
irq_initialize(&sgcn_irq);
sgcn_irq.devno = devno;
sgcn_irq.inr = FICTIONAL_INR;
sgcn_irq.claim = sgcn_claim;
sgcn_irq.handler = sgcn_irq_handler;
irq_register(&sgcn_irq);
instance->srlnin = srlnin;
thread_ready(instance->thread);
 
sysinfo_set_item_val("kbd", NULL, true);
sysinfo_set_item_val("kbd.type", NULL, KBD_SGCN);
sysinfo_set_item_val("kbd.devno", NULL, devno);
sysinfo_set_item_val("kbd.inr", NULL, FICTIONAL_INR);
}
 
/**
* A public function which initializes output to the Serengeti console.
*/
void sgcnout_init(void)
{
sgcn_buffer_begin_init();
 
sysinfo_set_item_val("fb.kind", NULL, 4);
chardev_initialize("sgcn_io", &sgcn_io, &sgcn_ops);
stdin = &sgcn_io;
stdout = &sgcn_io;
 
outdev_initialize("sgcnout", &sgcnout, &sgcnout_ops);
stdout = &sgcnout;
}
 
/** @}