Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 4345 → Rev 4346

/branches/dynload/kernel/arch/sparc64/include/drivers/sgcn.h
36,6 → 36,7
#define KERN_sparc64_SGCN_H_
 
#include <arch/types.h>
#include <console/chardev.h>
 
/* number of bytes in the TOC magic, including the terminating '\0' */
#define TOC_MAGIC_BYTES 8
117,8 → 118,8
 
void sgcn_grab(void);
void sgcn_release(void);
void sgcn_poll(void);
void sgcn_init(void);
indev_t *sgcnin_init(void);
void sgcnout_init(void);
 
#endif
 
/branches/dynload/kernel/arch/sparc64/Makefile.inc
42,11 → 42,11
 
DEFS += -D__64_BITS__
 
ifeq ($(MACHINE),us)
ifeq ($(PROCESSOR),us)
DEFS += -DUS
endif
 
ifeq ($(MACHINE),us3)
ifeq ($(PROCESSOR),us3)
DEFS += -DUS3
endif
 
76,7 → 76,8
arch/$(KARCH)/src/drivers/tick.c \
arch/$(KARCH)/src/drivers/kbd.c \
arch/$(KARCH)/src/drivers/sgcn.c \
arch/$(KARCH)/src/drivers/pci.c
arch/$(KARCH)/src/drivers/pci.c \
arch/$(KARCH)/src/drivers/fhc.c
 
ifeq ($(CONFIG_FB),y)
ARCH_SOURCES += \
93,8 → 94,3
ARCH_SOURCES += \
arch/$(KARCH)/src/mm/tsb.c
endif
 
ifdef CONFIG_Z8530
ARCH_SOURCES += \
arch/$(KARCH)/src/drivers/fhc.c
endif
/branches/dynload/kernel/arch/sparc64/src/console.c
37,16 → 37,8
 
#include <arch/drivers/scr.h>
#include <arch/drivers/kbd.h>
 
#include <arch/drivers/sgcn.h>
 
#ifdef CONFIG_Z8530
#include <genarch/kbd/z8530.h>
#endif
#ifdef CONFIG_NS16550
#include <genarch/kbd/ns16550.h>
#endif
 
#include <genarch/srln/srln.h>
#include <console/chardev.h>
#include <console/console.h>
#include <arch/asm.h>
70,36 → 62,29
static void standard_console_init(ofw_tree_node_t *aliases)
{
#ifdef CONFIG_FB
stdin = NULL;
 
ofw_tree_property_t *prop;
ofw_tree_node_t *screen;
ofw_tree_node_t *keyboard;
prop = ofw_tree_getprop(aliases, "screen");
if (!prop)
ofw_tree_property_t *prop_scr = ofw_tree_getprop(aliases, "screen");
if (!prop_scr)
panic("Cannot find property 'screen'.");
if (!prop->value)
if (!prop_scr->value)
panic("Cannot find screen alias.");
screen = ofw_tree_lookup(prop->value);
ofw_tree_node_t *screen = ofw_tree_lookup(prop_scr->value);
if (!screen)
panic("Cannot find %s.", prop->value);
 
panic("Cannot find %s.", prop_scr->value);
scr_init(screen);
#endif
 
prop = ofw_tree_getprop(aliases, "keyboard");
if (!prop)
#ifdef CONFIG_SUN_KBD
ofw_tree_property_t *prop_kbd = ofw_tree_getprop(aliases, "keyboard");
if (!prop_kbd)
panic("Cannot find property 'keyboard'.");
if (!prop->value)
if (!prop_kbd->value)
panic("Cannot find keyboard alias.");
keyboard = ofw_tree_lookup(prop->value);
ofw_tree_node_t *keyboard = ofw_tree_lookup(prop_kbd->value);
if (!keyboard)
panic("Cannot find %s.", prop->value);
 
panic("Cannot find %s.", prop_kbd->value);
kbd_init(keyboard);
#else
panic("Standard console requires FB, "
"but the kernel is not compiled with FB support.");
#endif
}
 
106,7 → 91,15
/** Initilize I/O on the Serengeti machine. */
static void serengeti_init(void)
{
sgcn_init();
#ifdef CONFIG_SGCN_KBD
indev_t *kbrdin;
kbrdin = sgcnin_init();
if (kbrdin)
srlnin_init(kbrdin);
#endif
#ifdef CONFIG_SGCN_PRN
sgcnout_init();
#endif
}
 
/**
133,26 → 126,6
}
 
 
/** Kernel thread for polling keyboard.
*
* @param arg Ignored.
*/
void kkbdpoll(void *arg)
{
thread_detach(THREAD);
 
if (kbd_type != KBD_SGCN)
return;
 
while (1) {
#ifdef CONFIG_SGCN
if (kbd_type == KBD_SGCN)
sgcn_poll();
#endif
thread_usleep(KEYBOARD_POLL_PAUSE);
}
}
 
/** Acquire console back for kernel
*
*/
/branches/dynload/kernel/arch/sparc64/src/sparc64.c
37,7 → 37,6
#include <config.h>
#include <arch/trap/trap.h>
#include <arch/console.h>
#include <proc/thread.h>
#include <console/console.h>
#include <arch/boot/boot.h>
#include <arch/arch.h>
89,8 → 88,6
* But we only create 128 buckets.
*/
irq_init(1 << 11, 128);
 
standalone_sparc64_console_init();
}
}
 
104,17 → 101,7
 
void arch_post_smp_init(void)
{
static thread_t *t = NULL;
 
if (!t) {
/*
* Create thread that polls keyboard.
*/
t = thread_create(kkbdpoll, NULL, TASK, 0, "kkbdpoll", true);
if (!t)
panic("Cannot create kkbdpoll.");
thread_ready(t);
}
standalone_sparc64_console_init();
}
 
/** Calibrate delay loop.
/branches/dynload/kernel/arch/sparc64/src/trap/exception.c
40,14 → 40,19
#include <arch/asm.h>
#include <arch/register.h>
#include <debug.h>
#include <print.h>
#include <symtab.h>
#include <print.h>
 
void dump_istate(istate_t *istate)
{
char *tpcs, *tnpcs;
 
tpcs = symtab_fmt_name_lookup(istate->tpc);
tnpcs = symtab_fmt_name_lookup(istate->tnpc);
 
printf("TSTATE=%#" PRIx64 "\n", istate->tstate);
printf("TPC=%#" PRIx64 " (%s)\n", istate->tpc, get_symtab_entry(istate->tpc));
printf("TNPC=%#" PRIx64 " (%s)\n", istate->tnpc, get_symtab_entry(istate->tnpc));
printf("TPC=%#" PRIx64 " (%s)\n", istate->tpc, tpcs);
printf("TNPC=%#" PRIx64 " (%s)\n", istate->tnpc, tnpcs);
}
 
/** Handle instruction_access_exception. (0x8) */
/branches/dynload/kernel/arch/sparc64/src/mm/page.c
1,5 → 1,5
/*
* Copyright (c) 2005 Jakub Jermar
* Copyright (c) 2009 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
42,126 → 42,27
#include <align.h>
#include <config.h>
 
#ifdef CONFIG_SMP
/** Entries locked in DTLB of BSP.
*
* Application processors need to have the same locked entries in their DTLBs as
* the bootstrap processor.
*/
static struct {
uintptr_t virt_page;
uintptr_t phys_page;
int pagesize_code;
} bsp_locked_dtlb_entry[DTLB_MAX_LOCKED_ENTRIES];
 
/** Number of entries in bsp_locked_dtlb_entry array. */
static count_t bsp_locked_dtlb_entries = 0;
#endif /* CONFIG_SMP */
 
/** Perform sparc64 specific initialization of paging. */
void page_arch_init(void)
{
if (config.cpu_active == 1) {
if (config.cpu_active == 1)
page_mapping_operations = &ht_mapping_operations;
} else {
 
#ifdef CONFIG_SMP
unsigned int i;
 
/*
* Copy locked DTLB entries from the BSP.
*/
for (i = 0; i < bsp_locked_dtlb_entries; i++) {
dtlb_insert_mapping(bsp_locked_dtlb_entry[i].virt_page,
bsp_locked_dtlb_entry[i].phys_page,
bsp_locked_dtlb_entry[i].pagesize_code, true,
false);
}
#endif
 
}
}
 
/** Map memory-mapped device into virtual memory.
*
* So far, only DTLB is used to map devices into memory. Chances are that there
* will be only a limited amount of devices that the kernel itself needs to
* lock in DTLB.
* We are currently using identity mapping for mapping device registers.
*
* @param physaddr Physical address of the page where the device is located.
* Must be at least page-aligned.
* @param size Size of the device's registers. Must not exceed 4M and must
* include extra space caused by the alignment.
* @param physaddr Physical address of the page where the device is
* located.
* @param size Size of the device's registers. This argument is
* ignored.
*
* @return Virtual address of the page where the device is mapped.
* @return Virtual address of the page where the device is mapped.
*/
uintptr_t hw_map(uintptr_t physaddr, size_t size)
{
unsigned int order;
unsigned int i;
 
ASSERT(config.cpu_active == 1);
 
struct {
int pagesize_code;
size_t increment;
count_t count;
} sizemap[] = {
{ PAGESIZE_8K, 0, 1 }, /* 8K */
{ PAGESIZE_8K, MMU_PAGE_SIZE, 2 }, /* 16K */
{ PAGESIZE_8K, MMU_PAGE_SIZE, 4 }, /* 32K */
{ PAGESIZE_64K, 0, 1}, /* 64K */
{ PAGESIZE_64K, 8 * MMU_PAGE_SIZE, 2 }, /* 128K */
{ PAGESIZE_64K, 8 * MMU_PAGE_SIZE, 4 }, /* 256K */
{ PAGESIZE_512K, 0, 1 }, /* 512K */
{ PAGESIZE_512K, 64 * MMU_PAGE_SIZE, 2 }, /* 1M */
{ PAGESIZE_512K, 64 * MMU_PAGE_SIZE, 4 }, /* 2M */
{ PAGESIZE_4M, 0, 1 }, /* 4M */
{ PAGESIZE_4M, 512 * MMU_PAGE_SIZE, 2 } /* 8M */
};
ASSERT(ALIGN_UP(physaddr, MMU_PAGE_SIZE) == physaddr);
ASSERT(size <= 8 * 1024 * 1024);
if (size <= MMU_FRAME_SIZE)
order = 0;
else
order = (fnzb64(size - 1) + 1) - MMU_FRAME_WIDTH;
 
/*
* Use virtual addresses that are beyond the limit of physical memory.
* Thus, the physical address space will not be wasted by holes created
* by frame_alloc().
*/
ASSERT(PA2KA(last_frame));
uintptr_t virtaddr = ALIGN_UP(PA2KA(last_frame),
1 << (order + FRAME_WIDTH));
last_frame = ALIGN_UP(KA2PA(virtaddr) + size,
1 << (order + FRAME_WIDTH));
for (i = 0; i < sizemap[order].count; i++) {
/*
* First, insert the mapping into DTLB.
*/
dtlb_insert_mapping(virtaddr + i * sizemap[order].increment,
physaddr + i * sizemap[order].increment,
sizemap[order].pagesize_code, true, false);
#ifdef CONFIG_SMP
/*
* Second, save the information about the mapping for APs.
*/
bsp_locked_dtlb_entry[bsp_locked_dtlb_entries].virt_page =
virtaddr + i * sizemap[order].increment;
bsp_locked_dtlb_entry[bsp_locked_dtlb_entries].phys_page =
physaddr + i * sizemap[order].increment;
bsp_locked_dtlb_entry[bsp_locked_dtlb_entries].pagesize_code =
sizemap[order].pagesize_code;
bsp_locked_dtlb_entries++;
#endif
}
return virtaddr;
return PA2KA(physaddr);
}
 
/** @}
/branches/dynload/kernel/arch/sparc64/src/drivers/kbd.c
26,7 → 26,7
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup sparc64
/** @addtogroup sparc64
* @{
*/
/** @file
34,12 → 34,18
 
#include <arch/drivers/kbd.h>
#include <genarch/ofw/ofw_tree.h>
 
#ifdef CONFIG_SUN_KBD
#include <genarch/kbrd/kbrd.h>
#endif
#ifdef CONFIG_Z8530
#include <genarch/kbd/z8530.h>
#include <genarch/drivers/z8530/z8530.h>
#endif
#ifdef CONFIG_NS16550
#include <genarch/kbd/ns16550.h>
#include <genarch/drivers/ns16550/ns16550.h>
#endif
 
#include <console/console.h>
#include <ddi/device.h>
#include <ddi/irq.h>
#include <arch/mm/page.h>
51,6 → 57,8
 
kbd_type_t kbd_type = KBD_UNKNOWN;
 
#ifdef CONFIG_SUN_KBD
 
/** Initialize keyboard.
*
* Traverse OpenFirmware device tree in order to find necessary
159,8 → 167,11
devno = device_assign_devno();
z8530 = (z8530_t *) hw_map(aligned_addr, offset + size) +
offset;
(void) z8530_init(z8530, devno, inr, cir, cir_arg);
indev_t *kbrdin_z8530 = z8530_init(z8530, devno, inr, cir, cir_arg);
if (kbrdin_z8530)
kbrd_init(kbrdin_z8530);
/*
* This is the necessary evil until the userspace drivers are
* entirely self-sufficient.
179,8 → 190,11
devno = device_assign_devno();
ns16550 = (ns16550_t *) hw_map(aligned_addr, offset + size) +
offset;
(void) ns16550_init(ns16550, devno, inr, cir, cir_arg);
indev_t *kbrdin_ns16550 = ns16550_init(ns16550, devno, inr, cir, cir_arg);
if (kbrdin_ns16550)
kbrd_init(kbrdin_ns16550);
/*
* This is the necessary evil until the userspace driver is
* entirely self-sufficient.
200,5 → 214,7
}
}
 
#endif
 
/** @}
*/
/branches/dynload/kernel/arch/sparc64/src/drivers/sgcn.c
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;
}
 
/** @}
*/