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); |
|
/** SGCN character output device */ |
chardev_t sgcn_stdout; |
sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE); |
sysinfo_set_item_val("sram.address.physical", NULL, sram_begin_physical); |
} |
|
/** SGCN character input device */ |
chardev_t sgcn_input; |
|
|
/** |
* Initializes the starting address of SRAM. |
* |
153,11 → 186,15 |
* 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) |
{ |
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); |
} |
311,12 → 424,26 |
{ |
sgcn_buffer_begin_init(); |
|
chardev_initialize("sgcn_output", &sgcn_stdout, &sgcn_output_ops); |
stdout = &sgcn_stdout; |
kbd_type = KBD_SGCN; |
|
chardev_initialize("sgcn_input", &sgcn_input, &sgcn_input_ops); |
stdin = &sgcn_input; |
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); |
|
chardev_initialize("sgcn_io", &sgcn_io, &sgcn_ops); |
stdin = &sgcn_io; |
stdout = &sgcn_io; |
} |
|
/** @} |
*/ |
*/ |