Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 3548 → Rev 3549

/branches/sparc/kernel/arch/sparc64/src/drivers/sgcn.c
35,13 → 35,19
*/
 
#include <arch/drivers/sgcn.h>
#include <arch/drivers/kbd.h>
#include <genarch/ofw/ofw_tree.h>
#include <debug.h>
#include <func.h>
#include <print.h>
#include <mm/page.h>
#include <ipc/irq.h>
#include <ddi/ddi.h>
#include <ddi/device.h>
#include <console/chardev.h>
#include <console/console.h>
#include <ddi/device.h>
#include <sysinfo/sysinfo.h>
#include <synch/spinlock.h>
 
/*
55,6 → 61,9
/* offset of SRAM within the SBBC memory */
#define SBBC_SRAM_OFFSET 0x900000
 
/* size (in bytes) of the physical memory area which will be mapped */
#define MAPPED_AREA_SIZE (128 * 1024)
 
/* magic string contained at the beginning of SRAM */
#define SRAM_TOC_MAGIC "TOCSRAM"
 
73,7 → 82,16
/* 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.
90,20 → 108,29
#define SGCN_BUFFER(type, offset) \
((type *) (sgcn_buffer_begin + (offset)))
 
/* Returns a pointer to the console buffer header. */
/** 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 */
/** starting address of SRAM, will be set by the init_sram_begin function */
static uintptr_t sram_begin;
 
/*
/**
* starting address of the SGCN buffer, will be set by the
* init_sgcn_buffer_begin function
*/
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;
 
// 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.
111,8 → 138,8
SPINLOCK_INITIALIZE(sgcn_output_lock);
 
/*
* Ensures that reading from the buffer and consequent update of the read
* pointer are together one atomic operation.
* Prevents the input buffer read/write pointers from getting to inconsistent
* state.
*/
SPINLOCK_INITIALIZE(sgcn_input_lock);
 
122,29 → 149,35
static void sgcn_putchar(chardev_t *, const char);
static char sgcn_key_read(chardev_t *);
 
/** character device output operations */
static chardev_operations_t sgcn_output_ops = {
/** character device operations */
static chardev_operations_t sgcn_ops = {
.suspend = sgcn_noop,
.resume = sgcn_noop,
.write = sgcn_putchar,
.read = NULL
.read = sgcn_key_read,
.write = sgcn_putchar
};
 
/** character device input operations */
static chardev_operations_t sgcn_input_ops = {
.suspend = sgcn_noop,
.resume = sgcn_noop,
.read = sgcn_key_read
};
/** SGCN character device */
chardev_t sgcn_io;
 
/**
* 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).
*/
static void register_sram_parea(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);
}
 
/** SGCN character output device */
chardev_t sgcn_stdout;
 
/** SGCN character input device */
chardev_t sgcn_input;
 
 
/**
* Initializes the starting address of SRAM.
*
152,12 → 185,16
* physical memory, where C is the value read from the "iosram-toc"
* property of the "/chosen" OBP node. The sram_begin variable will
* be set to the virtual address which maps to the SRAM physical
* address.
* 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)
{
ofw_tree_node_t *chosen;
ofw_tree_property_t *iosram_toc;
uintptr_t sram_begin_physical;
 
chosen = ofw_tree_lookup("/chosen");
if (!chosen)
169,10 → 206,11
if (!iosram_toc->value)
panic("Can't find SRAM TOC.\n");
 
sram_begin = hw_map(
SBBC_START + SBBC_SRAM_OFFSET
+ *((uint32_t *) iosram_toc->value),
128 * 1024);
sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
+ *((uint32_t *) iosram_toc->value);
sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
register_sram_parea(sram_begin_physical);
}
 
/**
182,6 → 220,9
* SRAM table of contents. The table of contents contains
* information about several buffers, among which there is an OBP
* console buffer - this one will be used as the SGCN buffer.
*
* This function also writes the offset of the SGCN buffer within SRAM
* under the sram.buffer.offset sysinfo key.
*/
static void sgcn_buffer_begin_init(void)
{
198,6 → 239,9
ASSERT(i < MAX_TOC_ENTRIES);
sgcn_buffer_begin = sram_begin + SRAM_TOC->keys[i].offset;
sysinfo_set_item_val("sram.buffer.offset", NULL,
SRAM_TOC->keys[i].offset);
}
 
/**
271,6 → 315,57
}
 
/**
* 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);
}
 
/**
* Releases the input so that userspace can use it.
*/
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);
}
 
/**
* Function regularly called by the keyboard polling thread. Finds out whether
* there are some unread characters in the input queue. If so, it picks them up
* and sends them to the upper layers of HelenOS.
277,13 → 372,15
*/
void sgcn_poll(void)
{
spinlock_lock(&sgcn_input_lock);
char c;
uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
uint32_t end = SGCN_BUFFER_HEADER->in_end;
uint32_t size = end - begin;
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);
290,15 → 387,31
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) {
c = *buf_ptr;
*in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
buf_ptr = (volatile char *)
SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
chardev_push_character(&sgcn_input, c);
if (c == '\r')
chardev_push_character(&sgcn_input, '\n');
}
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);
}
spinlock_unlock(&sgcn_input_lock);
}
310,13 → 423,27
void sgcn_init(void)
{
sgcn_buffer_begin_init();
 
kbd_type = KBD_SGCN;
 
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);
chardev_initialize("sgcn_output", &sgcn_stdout, &sgcn_output_ops);
stdout = &sgcn_stdout;
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);
sysinfo_set_item_val("fb.kind", NULL, 4);
chardev_initialize("sgcn_input", &sgcn_input, &sgcn_input_ops);
stdin = &sgcn_input;
chardev_initialize("sgcn_io", &sgcn_io, &sgcn_ops);
stdin = &sgcn_io;
stdout = &sgcn_io;
}
 
/** @}
*/
*/