34,6 → 34,7 |
* @brief SGCN driver. |
*/ |
|
#include <arch.h> |
#include <arch/drivers/sgcn.h> |
#include <arch/drivers/kbd.h> |
#include <genarch/ofw/ofw_tree.h> |
41,15 → 42,14 |
#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,7 → 96,7 |
* 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)) |
123,14 → 113,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,21 → 130,21 |
|
|
/* functions referenced from definitions of I/O operations structures */ |
static void sgcn_noop(chardev_t *); |
static void sgcn_putchar(chardev_t *, const char, bool); |
static char sgcn_key_read(chardev_t *); |
static void sgcn_putchar(outdev_t *, const char, 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; |
/** SGCN input device operations */ |
static indev_operations_t sgcnin_ops = { |
.poll = NULL |
}; |
|
static indev_t sgcnin; /**< SGCN input device. */ |
static outdev_t sgcnout; /**< SGCN output device. */ |
|
/** |
* Set some sysinfo values (SRAM address and SRAM size). |
*/ |
167,7 → 152,7 |
{ |
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); |
} |
|
/** |
178,9 → 163,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,7 → 181,7 |
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(sram_begin_physical); |
218,6 → 200,11 |
*/ |
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); |
233,17 → 220,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. |
256,7 → 238,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); |
|
286,7 → 268,7 |
* feed character is written ('\n'), the carriage return character ('\r') is |
* written straight away. |
*/ |
static void sgcn_putchar(struct chardev * cd, const char c, bool silent) |
static void sgcn_putchar(outdev_t *od, const char c, bool silent) |
{ |
if (!silent) { |
spinlock_lock(&sgcn_output_lock); |
300,49 → 282,11 |
} |
|
/** |
* 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(irq_t *irq) |
{ |
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) |
{ |
panic("Not yet implemented, SGCN works in polled mode."); |
} |
|
/** |
* 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 = true; |
} |
|
/** |
350,12 → 294,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; |
} |
|
/** |
363,74 → 302,82 |
* 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() |
{ |
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) { |
/* XXX: send notification to userspace */ |
} |
|
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(&sgcnin, 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 kkbdpoll(void *arg) { |
while (1) { |
if (!silent) { |
sgcn_poll(); |
} |
thread_usleep(POLL_INTERVAL); |
} |
} |
|
/** |
* A public function which initializes input from the Serengeti console. |
*/ |
indev_t *sgcnin_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); |
|
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); |
|
thread_t *t = thread_create(kkbdpoll, NULL, TASK, 0, "kkbdpoll", true); |
if (!t) |
panic("Cannot create kkbdpoll."); |
thread_ready(t); |
|
chardev_initialize("sgcn_io", &sgcn_io, &sgcn_ops); |
stdin = &sgcn_io; |
stdout = &sgcn_io; |
indev_initialize("sgcnin", &sgcnin, &sgcnin_ops); |
|
return &sgcnin; |
} |
|
/** |
* 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); |
|
outdev_initialize("sgcnout", &sgcnout, &sgcnout_ops); |
stdout = &sgcnout; |
} |
|
/** @} |
*/ |