| /tmp/sos-code-article6/sos/kthread.c (1970-01-01 01:00:00.000000000 +0100
) |
|
| ../sos-code-article6.5/sos/kthread.c (2005-01-04 04:12:16.000000000 +0100
) |
|
|
|
|
| | /* Copyright (C) 2004 David Decotigny |
| | |
| | This program is free software; you can redistribute it and/or |
| | modify it under the terms of the GNU General Public License |
| | as published by the Free Software Foundation; either version 2 |
| | of the License, or (at your option) any later version. |
| | |
| | This program is distributed in the hope that it will be useful, |
| | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | GNU General Public License for more details. |
| | |
| | You should have received a copy of the GNU General Public License |
| | along with this program; if not, write to the Free Software |
| | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| | USA. |
| | */ |
| | |
| | #include <sos/physmem.h> |
| | #include <sos/kmem_slab.h> |
| | #include <sos/kmalloc.h> |
| | #include <sos/klibc.h> |
| | #include <sos/list.h> |
| | #include <sos/assert.h> |
| | |
| | #include <hwcore/irq.h> |
| | |
| | #include "kthread.h" |
| | |
| | |
| | /** |
| | * The size of the stack of a kernel thread |
| | */ |
| | #define SOS_KTHREAD_STACK_SIZE (1*SOS_PAGE_SIZE) |
| | |
| | |
| | /** |
| | * The identifier of the thread currently running on CPU. |
| | * |
| | * We only support a SINGLE processor, ie a SINGLE kernel thread |
| | * running at any time in the system. This greatly simplifies the |
| | * implementation of the system, since we don't have to complicate |
| | * things in order to retrieve the identifier of the threads running |
| | * on the CPU. On multiprocessor systems the current_kthread below is |
| | * an array indexed by the id of the CPU, so that the challenge is to |
| | * retrieve the identifier of the CPU. This is usually done based on |
| | * the stack address (Linux implementation) or on some form of TLS |
| | * ("Thread Local Storage": can be implemented by way of LDTs for the |
| | * processes, accessed through the fs or gs registers). |
| | */ |
| | static volatile struct sos_kthread *current_kthread = NULL; |
| | |
| | |
| | /* |
| | * The list of kernel threads currently in the system. |
| | * |
| | * @note We could have used current_kthread for that... |
| | */ |
| | static struct sos_kthread *kthread_list = NULL; |
| | |
| | |
| | /** |
| | * The Cache of kthread structures |
| | */ |
| | static struct sos_kslab_cache *cache_kthread; |
| | |
| | |
| | struct sos_kthread *sos_kthread_get_current() |
| | { |
| | SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING); |
| | return (struct sos_kthread*)current_kthread; |
| | } |
| | |
| | |
| | inline static sos_ret_t _set_current(struct sos_kthread *thr) |
| | { |
| | SOS_ASSERT_FATAL(thr->state == SOS_KTHR_READY); |
| | current_kthread = thr; |
| | current_kthread->state = SOS_KTHR_RUNNING; |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t sos_kthread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr, |
| | sos_size_t init_thread_stack_size) |
| | { |
| | struct sos_kthread *myself; |
| | |
| | /* Allocate the cache of kthreads */ |
| | cache_kthread = sos_kmem_cache_create("kthread", |
| | sizeof(struct sos_kthread), |
| | 2, |
| | 0, |
| | SOS_KSLAB_CREATE_MAP |
| | | SOS_KSLAB_CREATE_ZERO); |
| | if (! cache_kthread) |
| | return -SOS_ENOMEM; |
| | |
| | /* Allocate a new kthread structure for the current running thread */ |
| | myself = (struct sos_kthread*) sos_kmem_cache_alloc(cache_kthread, |
| | SOS_KSLAB_ALLOC_ATOMIC); |
| | if (! myself) |
| | return -SOS_ENOMEM; |
| | |
| | /* Initialize the thread attributes */ |
| | strzcpy(myself->name, "[kinit]", SOS_KTHR_MAX_NAMELEN); |
| | myself->state = SOS_KTHR_CREATED; |
| | myself->stack_base_addr = init_thread_stack_base_addr; |
| | myself->stack_size = init_thread_stack_size; |
| | |
| | /* Do some stack poisoning on the bottom of the stack, if needed */ |
| | sos_cpu_kstate_prepare_detect_stack_overflow(myself->cpu_kstate, |
| | myself->stack_base_addr, |
| | myself->stack_size); |
| | |
| | /* Add the thread in the global list */ |
| | list_singleton_named(kthread_list, myself, gbl_prev, gbl_next); |
| | |
| | /* Ok, now pretend that the running thread is ourselves */ |
| | myself->state = SOS_KTHR_READY; |
| | _set_current(myself); |
| | |
| | return SOS_OK; |
| | } |
| | |
| | |
| | struct sos_kthread *sos_kthread_create(const char *name, |
| | sos_kthread_start_routine_t start_func, |
| | void *start_arg) |
| | { |
| | __label__ undo_creation; |
| | struct sos_kthread *new_thread; |
| | |
| | if (! start_func) |
| | return NULL; |
| | |
| | /* Allocate a new kthread structure for the current running thread */ |
| | new_thread |
| | = (struct sos_kthread*) sos_kmem_cache_alloc(cache_kthread, |
| | SOS_KSLAB_ALLOC_ATOMIC); |
| | if (! new_thread) |
| | return NULL; |
| | |
| | /* Initialize the thread attributes */ |
| | strzcpy(new_thread->name, ((name)?name:"[NONAME]"), SOS_KTHR_MAX_NAMELEN); |
| | new_thread->state = SOS_KTHR_CREATED; |
| | |
| | /* Allocate the stack for the new thread */ |
| | new_thread->stack_base_addr = sos_kmalloc(SOS_KTHREAD_STACK_SIZE, 0); |
| | new_thread->stack_size = SOS_KTHREAD_STACK_SIZE; |
| | if (! new_thread->stack_base_addr) |
| | goto undo_creation; |
| | |
| | /* Initialize the CPU context of the new thread */ |
| | if (SOS_OK |
| | != sos_cpu_kstate_init(& new_thread->cpu_kstate, |
| | (sos_cpu_kstate_function_arg1_t*) start_func, |
| | (sos_ui32_t) start_arg, |
| | new_thread->stack_base_addr, |
| | new_thread->stack_size, |
| | (sos_cpu_kstate_function_arg1_t*) sos_kthread_exit, |
| | (sos_ui32_t) NULL)) |
| | goto undo_creation; |
| | |
| | /* Add the thread in the global list */ |
| | list_add_tail_named(kthread_list, new_thread, gbl_prev, gbl_next); |
| | |
| | /* Mark the thread ready */ |
| | if (SOS_OK != sos_sched_set_ready(new_thread)) |
| | goto undo_creation; |
| | |
| | /* Normal non-erroneous end of function */ |
| | return new_thread; |
| | |
| | undo_creation: |
| | sos_kmem_cache_free((sos_vaddr_t) new_thread); |
| | return NULL; |
| | } |
| | |
| | |
| | /** Function called after thr has terminated. Called from inside the context |
| | of another thread, interrupts disabled */ |
| | static void delete_thread(struct sos_kthread *thr) |
| | { |
| | list_delete_named(kthread_list, thr, gbl_prev, gbl_next); |
| | |
| | sos_cpu_kstate_detect_stack_overflow(thr->cpu_kstate, |
| | thr->stack_base_addr, |
| | thr->stack_size); |
| | |
| | sos_kfree((sos_vaddr_t) thr->stack_base_addr); |
| | memset(thr, 0x0, sizeof(struct sos_kthread)); |
| | sos_kmem_cache_free((sos_vaddr_t) thr); |
| | } |
| | |
| | |
| | void sos_kthread_exit() |
| | { |
| | sos_ui32_t flags; |
| | struct sos_kthread *myself, *next_thread; |
| | |
| | myself = sos_kthread_get_current(); |
| | |
| | /* Refuse to end the current executing thread if it still holds a |
| | resource ! */ |
| | SOS_ASSERT_FATAL(list_is_empty_named(myself->kwaitq_list, |
| | prev_entry_for_kthread, |
| | next_entry_for_kthread)); |
| | |
| | /* Prepare to run the next thread */ |
| | sos_disable_IRQs(flags); |
| | myself->state = SOS_KTHR_ZOMBIE; |
| | next_thread = sos_reschedule(myself, FALSE); |
| | _set_current(next_thread); |
| | |
| | /* No need for sos_restore_IRQs() here because the IRQ flag will be |
| | restored to that of the next thread upon context switch */ |
| | |
| | /* Immediate switch to next thread */ |
| | sos_cpu_kstate_exit_to(next_thread->cpu_kstate, |
| | (sos_cpu_kstate_function_arg1_t*) delete_thread, |
| | (sos_ui32_t) myself); |
| | } |
| | |
| | |
| | sos_kthread_state_t sos_kthread_get_state(struct sos_kthread *thr) |
| | { |
| | if (! thr) |
| | thr = (struct sos_kthread*)current_kthread; |
| | |
| | return thr->state; |
| | } |
| | |
| | |
| | typedef enum { YIELD_MYSELF, BLOCK_MYSELF } switch_type_t; |
| | /** |
| | * Helper function to initiate a context switch in case the current |
| | * thread becomes blocked, waiting for a timeout, or calls yield. |
| | */ |
| | static sos_ret_t _switch_to_next_thread(switch_type_t operation) |
| | { |
| | struct sos_kthread *myself, *next_thread; |
| | |
| | SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING); |
| | |
| | /* Interrupt handlers are NOT allowed to block ! */ |
| | SOS_ASSERT_FATAL(! sos_servicing_irq()); |
| | |
| | myself = (struct sos_kthread*)current_kthread; |
| | |
| | /* Make sure that if we are to be marked "BLOCKED", we have any |
| | reason of effectively being blocked */ |
| | if (BLOCK_MYSELF == operation) |
| | { |
| | myself->state = SOS_KTHR_BLOCKED; |
| | } |
| | |
| | /* Identify the next thread */ |
| | next_thread = sos_reschedule(myself, YIELD_MYSELF == operation); |
| | |
| | /* Avoid context switch if the context does not change */ |
| | if (myself != next_thread) |
| | { |
| | /* Sanity checks for the next thread */ |
| | sos_cpu_kstate_detect_stack_overflow(next_thread->cpu_kstate, |
| | next_thread->stack_base_addr, |
| | next_thread->stack_size); |
| | |
| | /* Actual context switch */ |
| | _set_current(next_thread); |
| | sos_cpu_kstate_switch(& myself->cpu_kstate, next_thread->cpu_kstate); |
| | |
| | /* Back here ! */ |
| | SOS_ASSERT_FATAL(current_kthread == myself); |
| | SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING); |
| | } |
| | else |
| | { |
| | /* No context switch but still update ID of current thread */ |
| | _set_current(next_thread); |
| | } |
| | |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t sos_kthread_yield() |
| | { |
| | sos_ui32_t flags; |
| | sos_ret_t retval; |
| | |
| | sos_disable_IRQs(flags); |
| | |
| | retval = _switch_to_next_thread(YIELD_MYSELF); |
| | |
| | sos_restore_IRQs(flags); |
| | return retval; |
| | } |
| | |
| | |
| | /** |
| | * Internal sleep timeout management |
| | */ |
| | struct sleep_timeout_params |
| | { |
| | struct sos_kthread *thread_to_wakeup; |
| | sos_bool_t timeout_triggered; |
| | }; |
| | |
| | |
| | /** |
| | * Callback called when a timeout happened |
| | */ |
| | static void sleep_timeout(struct sos_timeout_action *act) |
| | { |
| | struct sleep_timeout_params *sleep_timeout_params |
| | = (struct sleep_timeout_params*) act->routine_data; |
| | |
| | /* Signal that we have been woken up by the timeout */ |
| | sleep_timeout_params->timeout_triggered = TRUE; |
| | |
| | /* Mark the thread ready */ |
| | SOS_ASSERT_FATAL(SOS_OK == |
| | sos_kthread_force_unblock(sleep_timeout_params |
| | ->thread_to_wakeup)); |
| | } |
| | |
| | |
| | sos_ret_t sos_kthread_sleep(struct sos_time *timeout) |
| | { |
| | sos_ui32_t flags; |
| | struct sleep_timeout_params sleep_timeout_params; |
| | struct sos_timeout_action timeout_action; |
| | sos_ret_t retval; |
| | |
| | /* Block forever if no timeout is given */ |
| | if (NULL == timeout) |
| | { |
| | sos_disable_IRQs(flags); |
| | retval = _switch_to_next_thread(BLOCK_MYSELF); |
| | sos_restore_IRQs(flags); |
| | |
| | return retval; |
| | } |
| | |
| | /* Initialize the timeout action */ |
| | sos_time_init_action(& timeout_action); |
| | |
| | /* Prepare parameters used by the sleep timeout callback */ |
| | sleep_timeout_params.thread_to_wakeup |
| | = (struct sos_kthread*)current_kthread; |
| | sleep_timeout_params.timeout_triggered = FALSE; |
| | |
| | sos_disable_IRQs(flags); |
| | |
| | /* Now program the timeout ! */ |
| | SOS_ASSERT_FATAL(SOS_OK == |
| | sos_time_register_action_relative(& timeout_action, |
| | timeout, |
| | sleep_timeout, |
| | & sleep_timeout_params)); |
| | |
| | /* Prepare to block: wait for sleep_timeout() to wakeup us in the |
| | timeout kwaitq, or for someone to wake us up in any other |
| | waitq */ |
| | retval = _switch_to_next_thread(BLOCK_MYSELF); |
| | /* Unblocked by something ! */ |
| | |
| | /* Unblocked by timeout ? */ |
| | if (sleep_timeout_params.timeout_triggered) |
| | { |
| | /* Yes */ |
| | SOS_ASSERT_FATAL(sos_time_is_zero(& timeout_action.timeout)); |
| | retval = SOS_OK; |
| | } |
| | else |
| | { |
| | /* No: We have probably been woken up while in some other |
| | kwaitq */ |
| | SOS_ASSERT_FATAL(SOS_OK == sos_time_unregister_action(& timeout_action)); |
| | retval = -SOS_EINTR; |
| | } |
| | |
| | sos_restore_IRQs(flags); |
| | |
| | /* Update the remaining timeout */ |
| | memcpy(timeout, & timeout_action.timeout, sizeof(struct sos_time)); |
| | |
| | return retval; |
| | } |
| | |
| | |
| | sos_ret_t sos_kthread_force_unblock(struct sos_kthread *kthread) |
| | { |
| | sos_ret_t retval; |
| | sos_ui32_t flags; |
| | |
| | if (! kthread) |
| | return -SOS_EINVAL; |
| | |
| | sos_disable_IRQs(flags); |
| | |
| | /* Thread already woken up ? */ |
| | retval = SOS_OK; |
| | switch(sos_kthread_get_state(kthread)) |
| | { |
| | case SOS_KTHR_RUNNING: |
| | case SOS_KTHR_READY: |
| | /* Do nothing */ |
| | break; |
| | |
| | case SOS_KTHR_ZOMBIE: |
| | retval = -SOS_EFATAL; |
| | break; |
| | |
| | default: |
| | retval = sos_sched_set_ready(kthread); |
| | break; |
| | } |
| | |
| | sos_restore_IRQs(flags); |
| | |
| | return retval; |
| | } |
| | |
| /tmp/sos-code-article6/sos/main.c (2004-12-03 16:27:45.000000000 +0100
) |
|
| ../sos-code-article6.5/sos/main.c (2005-01-04 04:12:15.000000000 +0100
) |
|
|
|
|
| #include <hwcore/paging.h> | #include <hwcore/paging.h> |
| #include <sos/kmem_vmm.h> | #include <sos/kmem_vmm.h> |
| #include <sos/kmalloc.h> | #include <sos/kmalloc.h> |
| | #include <sos/time.h> |
| | #include <sos/kthread.h> |
| #include <sos/klibc.h> | #include <sos/klibc.h> |
| #include <sos/assert.h> | #include <sos/assert.h> |
| #include <drivers/x86_videomem.h> | #include <drivers/x86_videomem.h> |
|
|
|
| SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE, | SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE, |
| clock_count); | clock_count); |
| clock_count++; | clock_count++; |
| | |
| | /* Execute the expired timeout actions (if any) */ |
| | sos_time_do_tick(); |
| } | } |
| | |
| | |
|
|
|
| } | } |
| | |
| | |
| | |
| /* ====================================================================== | |
| * Demonstrate the use of the CPU kernet context management API: | |
| * - A coroutine prints "Hlowrd" and switches to the other after each | |
| * letter | |
| * - A coroutine prints "el ol\n" and switches back to the other after | |
| * each letter. | |
| * The first to reach the '\n' returns back to main. | |
| */ | |
| struct sos_cpu_kstate *ctxt_hello1; | |
| struct sos_cpu_kstate *ctxt_hello2; | |
| struct sos_cpu_kstate *ctxt_main; | |
| sos_vaddr_t hello1_stack, hello2_stack; | |
| | |
| static void reclaim_stack(sos_vaddr_t stack_vaddr) | |
| { | |
| sos_kfree(stack_vaddr); | |
| } | |
| | |
| | |
| static void exit_hello12(sos_vaddr_t stack_vaddr) | |
| { | |
| sos_cpu_kstate_exit_to(ctxt_main, | |
| (sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |
| stack_vaddr); | |
| } | |
| | |
| | |
| static void hello1 (char *str) | |
| { | |
| for ( ; *str != '\n' ; str++) | |
| { | |
| sos_bochs_printf("hello1: %c\n", *str); | |
| sos_cpu_kstate_switch(& ctxt_hello1, ctxt_hello2); | |
| } | |
| | |
| /* You can uncomment this in case you explicitly want to exit | |
| now. But returning from the function will do the same */ | |
| /* sos_cpu_kstate_exit_to(ctxt_main, | |
| (sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |
| hello1_stack); */ | |
| } | |
| | |
| | |
| static void hello2 (char *str) | |
| { | |
| for ( ; *str != '\n' ; str++) | |
| { | |
| sos_bochs_printf("hello2: %c\n", *str); | |
| sos_cpu_kstate_switch(& ctxt_hello2, ctxt_hello1); | |
| } | |
| | |
| /* You can uncomment this in case you explicitly want to exit | |
| now. But returning from the function will do the same */ | |
| /* sos_cpu_kstate_exit_to(ctxt_main, | |
| (sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |
| hello2_stack); */ | |
| } | |
| | |
| | |
| void print_hello_world () | |
| { | |
| #define DEMO_STACK_SIZE 1024 | |
| /* Allocate the stacks */ | |
| hello1_stack = sos_kmalloc(DEMO_STACK_SIZE, 0); | |
| hello2_stack = sos_kmalloc(DEMO_STACK_SIZE, 0); | |
| | |
| /* Initialize the coroutines' contexts */ | |
| sos_cpu_kstate_init(&ctxt_hello1, | |
| (sos_cpu_kstate_function_arg1_t*) hello1, | |
| (sos_ui32_t) "Hlowrd", | |
| (sos_vaddr_t) hello1_stack, DEMO_STACK_SIZE, | |
| (sos_cpu_kstate_function_arg1_t*) exit_hello12, | |
| (sos_ui32_t) hello1_stack); | |
| sos_cpu_kstate_init(&ctxt_hello2, | |
| (sos_cpu_kstate_function_arg1_t*) hello2, | |
| (sos_ui32_t) "el ol\n", | |
| (sos_vaddr_t) hello2_stack, DEMO_STACK_SIZE, | |
| (sos_cpu_kstate_function_arg1_t*) exit_hello12, | |
| (sos_ui32_t) hello2_stack); | |
| | |
| /* Go to first coroutine */ | |
| sos_bochs_printf("Printing Hello World\\n...\n"); | |
| sos_cpu_kstate_switch(& ctxt_main, ctxt_hello1); | |
| | |
| /* The first coroutine to reach the '\n' switched back to us */ | |
| sos_bochs_printf("Back in main !\n"); | |
| } | |
| | |
| | |
| /* ====================================================================== | |
| * Generate page faults on an unmapped but allocated kernel virtual | |
| * region, which results in a series of physical memory mappings for the | |
| * faulted pages. | |
| */ | |
| static void test_demand_paging(int nb_alloc_vpages, int nb_alloc_ppages) | |
| { | |
| int i; | |
| sos_vaddr_t base_vaddr; | |
| | |
| sos_x86_videomem_printf(10, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, | |
| "Demand paging test (alloc %dMB of VMM, test %dkB RAM)", | |
| nb_alloc_vpages >> 8, nb_alloc_ppages << 2); | |
| | |
| /* Allocate virtual memory */ | |
| base_vaddr = sos_kmem_vmm_alloc(nb_alloc_vpages, 0); | |
| | |
| SOS_ASSERT_FATAL(base_vaddr != (sos_vaddr_t)NULL); | |
| sos_x86_videomem_printf(11, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Allocated virtual region [0x%x, 0x%x[", | |
| base_vaddr, | |
| base_vaddr + nb_alloc_vpages*SOS_PAGE_SIZE); | |
| | |
| /* Now use part of it in physical memory */ | |
| for (i = 0 ; (i < nb_alloc_ppages) && (i < nb_alloc_vpages) ; i++) | |
| { | |
| /* Compute an address inside the range */ | |
| sos_ui32_t *value, j; | |
| sos_vaddr_t vaddr = base_vaddr; | |
| vaddr += (nb_alloc_vpages - (i + 1))*SOS_PAGE_SIZE; | |
| vaddr += 2345; | |
| | |
| sos_x86_videomem_printf(12, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Writing %d at virtual address 0x%x...", | |
| i, vaddr); | |
| | |
| /* Write at this address */ | |
| value = (sos_ui32_t*)vaddr; | |
| *value = i; | |
| | |
| /* Yep ! A new page should normally have been allocated for us */ | |
| sos_x86_videomem_printf(13, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Value read at address 0x%x = %d", | |
| vaddr, (unsigned)*value); | |
| } | |
| | |
| SOS_ASSERT_FATAL(SOS_OK == sos_kmem_vmm_free(base_vaddr)); | |
| /* Yep ! A new page should normally have been allocated for us */ | |
| sos_x86_videomem_printf(14, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Done (area un-allocated)"); | |
| } | |
| | |
| | |
| | |
| * Shows how the backtrace stuff works | * Demonstrate the use of SOS kernel threads |
| | * - Kernel Threads are created with various priorities and their |
| | * state is printed on both the console and the bochs' 0xe9 port |
| | * - For tests regarding threads' synchronization, see mouse_sim.c |
| | |
| /* Recursive function. Print the backtrace from the innermost function */ | struct thr_arg |
| static void test_backtrace(int i, int magic, sos_vaddr_t stack_bottom, | |
| sos_size_t stack_size) | |
| if (i <= 0) | char character; |
| { | int color; |
| /* The page fault exception handler will print the backtrace of | |
| this function, because address 0x42 is not mapped */ | |
| *((char*)0x42) = 12; | |
| | |
| /* More direct variant: */ | |
| /* dump_backtrace(NULL, stack_bottom, stack_size, TRUE, TRUE); */ | |
| } | |
| else | |
| test_backtrace(i-1, magic, stack_bottom, stack_size); | |
| } | |
| | |
| /* ====================================================================== | int col; |
| * Parsing of Mathematical expressions | int row; |
| * | }; |
| * This is a recursive lexer/parser/evaluator for arithmetical | |
| * expressions. Supports both binary +/-* and unary +- operators, as | |
| * well as parentheses. | |
| * | |
| * Terminal tokens (Lexer): | |
| * - Number: positive integer number | |
| * - Variable: ascii name (regexp: [a-zA-Z]+) | |
| * - Operator: +*-/ | |
| * - Opening/closing parentheses | |
| * | |
| * Grammar (Parser): | |
| * Expression ::= Term E' | |
| * Expr_lr ::= + Term Expr_lr | - Term Expr_lr | Nothing | |
| * Term ::= Factor Term_lr | |
| * Term_lr ::= * Factor Term_lr | / Factor Term_lr | Nothing | |
| * Factor ::= - Factor | + Factor | Scalar | ( Expression ) | |
| * Scalar ::= Number | Variable | |
| * | |
| * Note. This is the left-recursive equivalent of the following basic grammar: | |
| * Expression ::= Expression + Term | Expression - Term | |
| * Term ::= Term * Factor | Term / Factor | |
| * factor ::= - Factor | + Factor | Scalar | Variable | ( Expression ) | |
| * Scalar ::= Number | Variable | |
| * | |
| * The parsing is composed of a 3 stages pipeline: | |
| * - The reader: reads a string 1 character at a time, transferring | |
| * the control back to lexer after each char. This function shows the | |
| * interest in using coroutines, because its state (str) is | |
| * implicitely stored in the stack between each iteration. | |
| * - The lexer: consumes the characters from the reader and identifies | |
| * the terminal tokens, 1 token at a time, transferring control back | |
| * to the parser after each token. This function shows the interest | |
| * in using coroutines, because its state (c and got_what_before) is | |
| * implicitely stored in the stack between each iteration. | |
| * - The parser: consumes the tokens from the lexer and builds the | |
| * syntax tree of the expression. There is no real algorithmic | |
| * interest in defining a coroutine devoted to do this. HOWEVER, we | |
| * do use one for that because this allows us to switch to a much | |
| * deeper stack. Actually, the parser is highly recursive, so that | |
| * the default 16kB stack of the sos_main() function might not be | |
| * enough. Here, we switch to a 64kB stack, which is safer for | |
| * recursive functions. The Parser uses intermediary functions: these | |
| * are defined and implemented as internal nested functions. This is | |
| * just for the sake of clarity, and is absolutely not mandatory for | |
| * the algorithm: one can transfer these functions out of the parser | |
| * function without restriction. | |
| * | |
| * The evaluator is another recursive function that reuses the | |
| * parser's stack to evaluate the parsed expression with the given | |
| * values for the variables present in the expression. As for the | |
| * parser function, this function defines and uses a nested function, | |
| * which can be extracted from the main evaluation function at will. | |
| * | |
| * All these functions support a kind of "exception" feature: when | |
| * something goes wrong, control is transferred DIRECTLY back to the | |
| * sos_main() context, without unrolling the recursions. This shows | |
| * how exceptions basically work, but one should not consider this as | |
| * a reference exceptions implementation. Real exception mechanisms | |
| * (such as that in the C++ language) call the destructors to the | |
| * objects allocated on the stack during the "stack unwinding" process | |
| * upon exception handling, which complicates a lot the mechanism. We | |
| * don't have real Objects here (in the OOP sense, full-featured with | |
| * destructors), so we don't have to complicate things. | |
| * | |
| * After this little coroutine demo, one should forget all about such | |
| * a low-level manual direct manipulation of stacks. This would | |
| * probably mess up the whole kernel to do what we do here (locked | |
| * resources such as mutex/semaphore won't be correctly unlocked, | |
| * ...). Higher level "kernel thread" primitives will soon be | |
| * presented, which provide a higher-level set of APIs to manage CPU | |
| * contexts. You'll have to use EXCLUSIVELY those APIs. If you still | |
| * need a huge stack to do recursion for example, please don't even | |
| * think of changing manually the stack for something bigger ! Simply | |
| * rethink your algorithm, making it non-recursive. | |
| */ | |
| | |
| | |
| /* The stacks involved */ | |
| static char stack_reader[1024]; | |
| static char stack_lexer[1024]; | |
| static char deep_stack[65536]; /* For the parser and the evaluator */ | |
| | |
| /* The CPU states for the various coroutines */ | |
| static struct sos_cpu_kstate *st_reader, *st_lexer, *st_parser, | |
| *st_eval, *st_free, *st_main; | |
| | |
| /* | static void demo_thread(void *arg) |
| * Default exit/reclaim functions: return control to the "sos_main()" | |
| * context | |
| */ | |
| static void reclaim(int unused) | |
| } | struct thr_arg *thr_arg = (struct thr_arg*)arg; |
| static void func_exit(sos_ui32_t unused) | int progress = 0; |
| { | |
| sos_cpu_kstate_exit_to(st_main, (sos_cpu_kstate_function_arg1_t*)reclaim, 0); | |
| } | |
| | |
| | |
| /* | |
| * The reader coroutine and associated variable. This coroutine could | |
| * have been a normal function, except that the current parsed | |
| * character would have to be stored somewhere. | |
| */ | |
| static char data_reader_to_lexer; | |
| static void func_reader(const char *str) | sos_bochs_printf("start %c", thr_arg->character); |
| { | while (1) |
| for ( ; str && (*str != '\0') ; str++) | |
| data_reader_to_lexer = *str; | progress ++; |
| sos_cpu_kstate_switch(& st_reader, st_lexer); | display_bits(thr_arg->row, thr_arg->col+1, thr_arg->color, progress); |
| } | |
| | |
| data_reader_to_lexer = '\0'; | |
| sos_cpu_kstate_switch(& st_reader, st_lexer); | |
| } | |
| | sos_bochs_putchar(thr_arg->character); |
| | |
| /* | /* Yield the CPU to another thread sometimes... */ |
| * The Lexer coroutine and associated types/variables. This coroutine | if ((random() % 100) == 0) |
| * could have been a normal function, except that the current parsed | |
| * character, token and previous token would have to be stored | |
| * somewhere. | |
| */ | |
| #define STR_VAR_MAXLEN 16 | |
| static struct lex_elem | |
| { | |
| enum { LEX_IS_NUMBER, LEX_IS_OPER, LEX_IS_VAR, | |
| LEX_IS_OPENPAR, LEX_IS_CLOSEPAR, LEX_END } type; | |
| union { | |
| int number; | |
| char operator; | |
| char var[STR_VAR_MAXLEN]; | |
| }; | |
| } data_lexer_to_parser; | |
| | |
| static void func_lexer(sos_ui32_t unused) | |
| { | |
| char c; | |
| enum { GOT_SPACE, GOT_NUM, GOT_OP, GOT_STR, | |
| GOT_OPENPAR, GOT_CLOSEPAR } got_what, got_what_before; | |
| | |
| data_lexer_to_parser.number = 0; | |
| got_what_before = GOT_SPACE; | |
| do | |
| { | |
| /* Consume one character from the reader */ | |
| sos_cpu_kstate_switch(& st_lexer, st_reader); | |
| c = data_reader_to_lexer; | |
| | |
| /* Classify the consumed character */ | |
| if ( (c >= '0') && (c <= '9') ) | |
| got_what = GOT_NUM; | |
| else if ( (c == '+') || (c == '-') || (c == '*') || (c == '/') ) | |
| got_what = GOT_OP; | |
| else if ( ( (c >= 'a') && (c <= 'z') ) | |
| || ( (c >= 'A') && (c <= 'Z') ) ) | |
| got_what = GOT_STR; | |
| else if (c == '(') | |
| got_what = GOT_OPENPAR; | |
| else if (c == ')') | |
| got_what = GOT_CLOSEPAR; | |
| else | |
| got_what = GOT_SPACE; | |
| | |
| /* Determine whether the current token is ended */ | |
| if ( (got_what != got_what_before) | |
| || (got_what_before == GOT_OP) | |
| || (got_what_before == GOT_OPENPAR) | |
| || (got_what_before == GOT_CLOSEPAR) ) | |
| /* return control back to the parser if the previous token | sos_bochs_printf("[37myield(%c)[m\n", thr_arg->character); |
| has been recognized */ | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'Y'); |
| if ( (got_what_before != GOT_SPACE) ) | SOS_ASSERT_FATAL(SOS_OK == sos_kthread_yield()); |
| sos_cpu_kstate_switch(& st_lexer, st_parser); | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); |
| | |
| data_lexer_to_parser.number = 0; | |
| | |
| /* Update the token being currently recognized */ | /* Go to sleep some other times... */ |
| if (got_what == GOT_OP) | else if ((random() % 200) == 0) |
| { | |
| data_lexer_to_parser.type = LEX_IS_OPER; | |
| data_lexer_to_parser.operator = c; | |
| } | |
| else if (got_what == GOT_NUM) | |
| data_lexer_to_parser.type = LEX_IS_NUMBER; | struct sos_time t = (struct sos_time){ .sec=0, .nanosec=50000000 }; |
| data_lexer_to_parser.number *= 10; | sos_bochs_printf("[37msleep1(%c)[m\n", thr_arg->character); |
| data_lexer_to_parser.number += (c - '0'); | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 's'); |
| | SOS_ASSERT_FATAL(SOS_OK == sos_kthread_sleep(& t)); |
| | SOS_ASSERT_FATAL(sos_time_is_zero(& t)); |
| | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); |
| else if (got_what == GOT_STR) | |
| | /* Go to sleep for a longer time some other times... */ |
| | else if ((random() % 300) == 0) |
| char to_cat[] = { c, '\0' }; | struct sos_time t = (struct sos_time){ .sec=0, .nanosec=300000000 }; |
| data_lexer_to_parser.type = LEX_IS_VAR; | sos_bochs_printf("[37msleep2(%c)[m\n", thr_arg->character); |
| strzcat(data_lexer_to_parser.var, to_cat, STR_VAR_MAXLEN); | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'S'); |
| | SOS_ASSERT_FATAL(SOS_OK == sos_kthread_sleep(& t)); |
| | SOS_ASSERT_FATAL(sos_time_is_zero(& t)); |
| | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); |
| else if (got_what == GOT_OPENPAR) | |
| data_lexer_to_parser.type = LEX_IS_OPENPAR; | |
| else if (got_what == GOT_CLOSEPAR) | |
| data_lexer_to_parser.type = LEX_IS_CLOSEPAR; | |
| got_what_before = got_what; | /* Infinite loop otherwise */ |
| while (c != '\0'); | |
| | |
| /* Transfer last recognized token to the parser */ | |
| if ( (got_what_before != GOT_SPACE) ) | |
| sos_cpu_kstate_switch(& st_lexer, st_parser); | |
| | |
| /* Signal that no more token are available */ | |
| data_lexer_to_parser.type = LEX_END; | |
| sos_cpu_kstate_switch(& st_lexer, st_parser); | |
| | |
| /* Exception: parser asks for a token AFTER having received the last | |
| one */ | |
| sos_bochs_printf("Error: end of string already reached !\n"); | |
| sos_cpu_kstate_switch(& st_lexer, st_main); | |
| | |
| | |
| /* | static void test_kthread() |
| * The Parser coroutine and associated types/variables | |
| */ | |
| struct syntax_node | |
| enum { YY_IS_BINOP, YY_IS_UNAROP, YY_IS_NUM, YY_IS_VAR } type; | /* "static" variables because we want them to remain even when the |
| union | function returns */ |
| { | static struct thr_arg arg_b, arg_c, arg_d, arg_e, arg_R, arg_S; |
| int number; | sos_ui32_t flags; |
| char var[STR_VAR_MAXLEN]; | |
| struct | |
| { | |
| char op; | |
| struct syntax_node *parm_left, *parm_right; | |
| } binop; | |
| struct | |
| { | |
| char op; | |
| struct syntax_node *parm; | |
| } unarop; | |
| }; | |
| }; | |
| static void func_parser(struct syntax_node ** syntax_tree) | sos_disable_IRQs(flags); |
| { | |
| static struct syntax_node *alloc_node_num(int val); | |
| static struct syntax_node *alloc_node_var(const char * name); | |
| static struct syntax_node *alloc_node_binop(char op, | |
| struct syntax_node *parm_left, | |
| struct syntax_node *parm_right); | |
| static struct syntax_node *alloc_node_unarop(char op, | |
| struct syntax_node *parm); | |
| static struct syntax_node * get_expr(); | |
| static struct syntax_node * get_expr_lr(struct syntax_node *n); | |
| static struct syntax_node * get_term(); | |
| static struct syntax_node * get_term_lr(struct syntax_node *n); | |
| static struct syntax_node * get_factor(); | |
| static struct syntax_node * get_scalar(); | |
| /* Create a new node to store a number */ | arg_b = (struct thr_arg) { .character='b', .col=0, .row=21, .color=0x14 }; |
| static struct syntax_node *alloc_node_num(int val) | sos_kthread_create("YO[b]", demo_thread, (void*)&arg_b); |
| { | |
| struct syntax_node *n | |
| = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |
| n->type = YY_IS_NUM; | |
| n->number = val; | |
| return n; | |
| } | |
| /* Create a new node to store a variable */ | |
| static struct syntax_node *alloc_node_var(const char * name) | |
| { | |
| struct syntax_node *n | |
| = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |
| n->type = YY_IS_VAR; | |
| strzcpy(n->var, name, STR_VAR_MAXLEN); | |
| return n; | |
| } | |
| /* Create a new node to store a binary operator */ | |
| static struct syntax_node *alloc_node_binop(char op, | |
| struct syntax_node *parm_left, | |
| struct syntax_node *parm_right) | |
| { | |
| struct syntax_node *n | |
| = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |
| n->type = YY_IS_BINOP; | |
| n->binop.op = op; | |
| n->binop.parm_left = parm_left; | |
| n->binop.parm_right = parm_right; | |
| return n; | |
| } | |
| /* Create a new node to store a unary operator */ | |
| static struct syntax_node *alloc_node_unarop(char op, | |
| struct syntax_node *parm) | |
| { | |
| struct syntax_node *n | |
| = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |
| n->type = YY_IS_UNAROP; | |
| n->unarop.op = op; | |
| n->unarop.parm = parm; | |
| return n; | |
| } | |
| /* Raise an exception: transfer control back to main context, | arg_c = (struct thr_arg) { .character='c', .col=46, .row=21, .color=0x14 }; |
| without unrolling the whole recursion */ | sos_kthread_create("YO[c]", demo_thread, (void*)&arg_c); |
| static void parser_exception(const char *str) | |
| { | |
| sos_bochs_printf("Parser exception: %s\n", str); | |
| sos_cpu_kstate_switch(& st_parser, st_main); | |
| } | |
| /* Consume the current terminal "number" token and ask for a new | arg_d = (struct thr_arg) { .character='d', .col=0, .row=20, .color=0x14 }; |
| token */ | sos_kthread_create("YO[d]", demo_thread, (void*)&arg_d); |
| static int get_number() | |
| { | |
| int v; | |
| if (data_lexer_to_parser.type != LEX_IS_NUMBER) | |
| parser_exception("Expected number"); | |
| v = data_lexer_to_parser.number; | |
| sos_cpu_kstate_switch(& st_parser, st_lexer); | |
| return v; | |
| } | |
| /* Consume the current terminal "variable" token and ask for a new | |
| token */ | |
| static void get_str(char name[STR_VAR_MAXLEN]) | |
| { | |
| if (data_lexer_to_parser.type != LEX_IS_VAR) | |
| parser_exception("Expected variable"); | |
| strzcpy(name, data_lexer_to_parser.var, STR_VAR_MAXLEN); | |
| sos_cpu_kstate_switch(& st_parser, st_lexer); | |
| } | |
| /* Consume the current terminal "operator" token and ask for a new | |
| token */ | |
| static char get_op() | |
| { | |
| char op; | |
| if (data_lexer_to_parser.type != LEX_IS_OPER) | |
| parser_exception("Expected operator"); | |
| op = data_lexer_to_parser.operator; | |
| sos_cpu_kstate_switch(& st_parser, st_lexer); | |
| return op; | |
| } | |
| /* Consume the current terminal "parenthese" token and ask for a new | |
| token */ | |
| static void get_par() | |
| { | |
| if ( (data_lexer_to_parser.type != LEX_IS_OPENPAR) | |
| && (data_lexer_to_parser.type != LEX_IS_CLOSEPAR) ) | |
| parser_exception("Expected parenthese"); | |
| sos_cpu_kstate_switch(& st_parser, st_lexer); | |
| } | |
| | |
| /* Parse an Expression */ | |
| static struct syntax_node * get_expr() | |
| { | |
| struct syntax_node *t = get_term(); | |
| return get_expr_lr(t); | |
| } | |
| /* Parse an Expr_lr */ | |
| static struct syntax_node * get_expr_lr(struct syntax_node *n) | |
| { | |
| if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |
| && ( (data_lexer_to_parser.operator == '+') | |
| || (data_lexer_to_parser.operator == '-') ) ) | |
| { | |
| char op = get_op(); | |
| struct syntax_node *term = get_term(); | |
| struct syntax_node *node_op = alloc_node_binop(op, n, term); | |
| return get_expr_lr(node_op); | |
| } | |
| return n; | |
| } | |
| /* Parse a Term */ | |
| static struct syntax_node * get_term() | |
| { | |
| struct syntax_node *f1 = get_factor(); | |
| return get_term_lr(f1); | |
| } | |
| /* Parse a Term_lr */ | |
| static struct syntax_node * get_term_lr(struct syntax_node *n) | |
| { | |
| if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |
| && ( (data_lexer_to_parser.operator == '*') | |
| || (data_lexer_to_parser.operator == '/') ) ) | |
| { | |
| char op = get_op(); | |
| struct syntax_node *factor = get_factor(); | |
| struct syntax_node *node_op = alloc_node_binop(op, n, factor); | |
| return get_term_lr(node_op); | |
| } | |
| return n; | |
| } | |
| /* Parse a Factor */ | |
| static struct syntax_node * get_factor() | |
| { | |
| if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |
| && ( (data_lexer_to_parser.operator == '-') | |
| || (data_lexer_to_parser.operator == '+') ) ) | |
| { char op = data_lexer_to_parser.operator; | |
| get_op(); return alloc_node_unarop(op, get_factor()); } | |
| else if (data_lexer_to_parser.type == LEX_IS_OPENPAR) | |
| { | |
| struct syntax_node *expr; | |
| get_par(); | |
| expr = get_expr(); | |
| if (data_lexer_to_parser.type != LEX_IS_CLOSEPAR) | |
| parser_exception("Mismatched parentheses"); | |
| get_par(); | |
| return expr; | |
| } | |
| | |
| return get_scalar(); | |
| } | |
| /* Parse a Scalar */ | |
| static struct syntax_node * get_scalar() | |
| { | |
| if (data_lexer_to_parser.type != LEX_IS_NUMBER) | |
| { | |
| char var[STR_VAR_MAXLEN]; | |
| get_str(var); | |
| return alloc_node_var(var); | |
| } | |
| return alloc_node_num(get_number()); | |
| } | |
| | arg_e = (struct thr_arg) { .character='e', .col=0, .row=19, .color=0x14 }; |
| | sos_kthread_create("YO[e]", demo_thread, (void*)&arg_e); |
| | |
| /* | arg_R = (struct thr_arg) { .character='R', .col=0, .row=17, .color=0x1c }; |
| * Body of the function | sos_kthread_create("YO[R]", demo_thread, (void*)&arg_R); |
| */ | |
| /* Get the first token */ | arg_S = (struct thr_arg) { .character='S', .col=0, .row=16, .color=0x1c }; |
| sos_cpu_kstate_switch(& st_parser, st_lexer); | sos_kthread_create("YO[S]", demo_thread, (void*)&arg_S); |
| /* Begin the parsing ! */ | sos_restore_IRQs(flags); |
| *syntax_tree = get_expr(); | |
| /* The result is returned in the syntax_tree parameter */ | |
| | |
| | |
| /* | /* ====================================================================== |
| * Setup the parser's pipeline | * An operating system MUST always have a ready thread ! Otherwise: |
| */ | * what would the CPU have to execute ?! |
| static struct syntax_node * parse_expression(const char *expr) | |
| { | |
| struct syntax_node *retval = NULL; | |
| | |
| /* Build the context of the functions in the pipeline */ | |
| sos_cpu_kstate_init(& st_reader, | |
| (sos_cpu_kstate_function_arg1_t*)func_reader, | |
| (sos_ui32_t)expr, | |
| (sos_vaddr_t)stack_reader, sizeof(stack_reader), | |
| (sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
| sos_cpu_kstate_init(& st_lexer, | |
| (sos_cpu_kstate_function_arg1_t*)func_lexer, | |
| 0, | |
| (sos_vaddr_t)stack_lexer, sizeof(stack_lexer), | |
| (sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
| sos_cpu_kstate_init(& st_parser, | |
| (sos_cpu_kstate_function_arg1_t*)func_parser, | |
| (sos_ui32_t) /* syntax tree ! */&retval, | |
| (sos_vaddr_t)deep_stack, sizeof(deep_stack), | |
| (sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
| | |
| /* Parse the expression */ | |
| sos_cpu_kstate_switch(& st_main, st_parser); | |
| return retval; | |
| } | |
| | |
| | |
| /* | |
| * The Evaluator coroutine and associated types/variables | |
| struct func_eval_params | static void idle_kthread() |
| const struct syntax_node *e; | sos_ui32_t idle_twiddle = 0; |
| const char **var_name; | |
| int *var_val; | |
| int nb_vars; | |
| int result; | while (1) |
| }; | |
| | |
| static void func_eval(struct func_eval_params *parms) | |
| { | |
| /* The internal (recursive) nested function to evaluate each node of | |
| the syntax tree */ | |
| static int rec_eval(const struct syntax_node *n, | |
| const char* var_name[], int var_val[], int nb_vars) | |
| switch (n->type) | /* Remove this instruction if you get an "Invalid opcode" CPU |
| { | exception (old 80386 CPU) */ |
| case YY_IS_NUM: | asm("hlt\n"); |
| return n->number; | |
| case YY_IS_VAR: | idle_twiddle ++; |
| { | display_bits(0, 0, SOS_X86_VIDEO_FG_GREEN | SOS_X86_VIDEO_BG_BLUE, |
| int i; | idle_twiddle); |
| for (i = 0 ; i < nb_vars ; i++) | |
| if (0 == strcmp(var_name[i], n->var)) | |
| return var_val[i]; | |
| | |
| /* Exception: no variable with that name ! */ | |
| sos_bochs_printf("ERROR: unknown variable %s\n", n->var); | |
| sos_cpu_kstate_switch(& st_eval, st_main); | |
| } | |
| | |
| case YY_IS_BINOP: | |
| { | |
| int left = rec_eval(n->binop.parm_left, | |
| var_name, var_val, nb_vars); | |
| int right = rec_eval(n->binop.parm_right, | |
| var_name, var_val, nb_vars); | |
| switch (n->binop.op) | |
| { | |
| case '+': return left + right; | |
| case '-': return left - right; | |
| case '*': return left * right; | |
| case '/': return left / right; | |
| default: | |
| /* Exception: no such operator (INTERNAL error) ! */ | |
| sos_bochs_printf("ERROR: unknown binop %c\n", n->binop.op); | |
| sos_cpu_kstate_switch(& st_eval, st_main); | |
| } | |
| } | |
| | |
| case YY_IS_UNAROP: | |
| { | |
| int arg = rec_eval(n->unarop.parm, var_name, var_val, nb_vars); | |
| switch (n->unarop.op) | |
| { | |
| case '-': return -arg; | |
| case '+': return arg; | |
| default: | |
| /* Exception: no such operator (INTERNAL error) ! */ | |
| sos_bochs_printf("ERROR: unknown unarop %c\n", n->unarop.op); | |
| sos_cpu_kstate_switch(& st_eval, st_main); | |
| } | |
| } | |
| } | |
| /* Exception: no such syntax node (INTERNAL error) ! */ | /* Lend the CPU to some other thread */ |
| sos_bochs_printf("ERROR: invalid node type\n"); | sos_kthread_yield(); |
| sos_cpu_kstate_switch(& st_eval, st_main); | |
| return -1; /* let's make gcc happy */ | |
| | |
| | |
| /* | |
| * Function BODY | |
| */ | |
| /* Update p.result returned back to calling function */ | |
| parms->result | |
| = rec_eval(parms->e, parms->var_name, parms->var_val, parms->nb_vars); | |
| } | |
| | |
| /* | |
| * Change the stack for something larger in order to call the | |
| * recursive function above in a safe way | |
| */ | |
| static int eval_expression(const struct syntax_node *e, | |
| const char* var_name[], int var_val[], int nb_vars) | |
| { | |
| struct func_eval_params p | |
| = (struct func_eval_params){ .e=e, | |
| .var_name=var_name, | |
| .var_val=var_val, | |
| .nb_vars=nb_vars, | |
| .result = 0 }; | |
| | |
| sos_cpu_kstate_init(& st_eval, | |
| (sos_cpu_kstate_function_arg1_t*)func_eval, | |
| (sos_ui32_t)/* p.result is modified upon success */&p, | |
| (sos_vaddr_t)deep_stack, sizeof(deep_stack), | |
| (sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
| | |
| /* Go ! */ | |
| sos_cpu_kstate_switch(& st_main, st_eval); | |
| return p.result; | |
| | |
| | |
| /* | /* ====================================================================== |
| * Function to free the syntax tree | * The C entry point of our operating system |
| */ | |
| static void func_free(struct syntax_node *n) | |
| { | |
| switch (n->type) | |
| { | |
| case YY_IS_NUM: | |
| case YY_IS_VAR: | |
| break; | |
| | |
| case YY_IS_BINOP: | |
| func_free(n->binop.parm_left); | |
| func_free(n->binop.parm_right); | |
| break; | |
| | |
| case YY_IS_UNAROP: | |
| func_free(n->unarop.parm); | |
| break; | |
| } | |
| | |
| sos_kfree((sos_vaddr_t)n); | |
| } | |
| | |
| /* | |
| * Change the stack for something larger in order to call the | |
| * recursive function above in a safe way | |
| static void free_syntax_tree(struct syntax_node *tree) | |
| { | |
| sos_cpu_kstate_init(& st_free, | |
| (sos_cpu_kstate_function_arg1_t*)func_free, | |
| (sos_ui32_t)tree, | |
| (sos_vaddr_t)deep_stack, sizeof(deep_stack), | |
| (sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
| | |
| /* Go ! */ | |
| sos_cpu_kstate_switch(& st_main, st_free); | |
| } | |
| | |
| | |
| /* The C entry point of our operating system */ | |
| { | { |
| unsigned i; | unsigned i; |
| sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr; | sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr; |
| struct syntax_node *syntax_tree; | struct sos_time tick_resolution; |
| /* Grub sends us a structure, called multiboot_info_t with a lot of | /* Grub sends us a structure, called multiboot_info_t with a lot of |
| precious informations about the system, see the multiboot | precious informations about the system, see the multiboot |
|
|
|
| sos_bochs_putstring("Message in a bochs\n"); | sos_bochs_putstring("Message in a bochs\n"); |
| | |
| /* Setup CPU segmentation and IRQ subsystem */ | /* Setup CPU segmentation and IRQ subsystem */ |
| sos_gdt_setup(); | sos_gdt_subsystem_setup(); |
| sos_idt_setup(); | sos_idt_subsystem_setup(); |
| /* Setup SOS IRQs and exceptions subsystem */ | /* Setup SOS IRQs and exceptions subsystem */ |
| sos_exceptions_setup(); | sos_exception_subsystem_setup(); |
| sos_irq_setup(); | sos_irq_subsystem_setup(); |
| /* Configure the timer so as to raise the IRQ0 at a 100Hz rate */ | /* Configure the timer so as to raise the IRQ0 at a 100Hz rate */ |
| sos_i8254_set_frequency(100); | sos_i8254_set_frequency(100); |
| | |
| | /* Setup the kernel time subsystem to get prepared to take the timer |
| | ticks into account */ |
| | tick_resolution = (struct sos_time) { .sec=0, .nanosec=10000000UL }; |
| | sos_time_subsysem_setup(& tick_resolution); |
| | |
| /* We need a multiboot-compliant boot loader to get the size of the RAM */ | /* We need a multiboot-compliant boot loader to get the size of the RAM */ |
| if (magic != MULTIBOOT_BOOTLOADER_MAGIC) | if (magic != MULTIBOOT_BOOTLOADER_MAGIC) |
| { | { |
|
|
|
| /* Multiboot says: "The value returned for upper memory is maximally | /* Multiboot says: "The value returned for upper memory is maximally |
| the address of the first upper memory hole minus 1 megabyte.". It | the address of the first upper memory hole minus 1 megabyte.". It |
| also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */ | also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */ |
| sos_physmem_setup((mbi->mem_upper<<10) + (1<<20), | sos_physmem_subsystem_setup((mbi->mem_upper<<10) + (1<<20), |
| & sos_kernel_core_base_paddr, | & sos_kernel_core_base_paddr, |
| & sos_kernel_core_top_paddr); | & sos_kernel_core_top_paddr); |
| /* | /* |
| * Switch to paged-memory mode | * Switch to paged-memory mode |
|
|
|
| | |
| /* Disabling interrupts should seem more correct, but it's not really | /* Disabling interrupts should seem more correct, but it's not really |
| necessary at this stage */ | necessary at this stage */ |
| if (sos_paging_setup(sos_kernel_core_base_paddr, | SOS_ASSERT_FATAL(SOS_OK == |
| sos_kernel_core_top_paddr)) | sos_paging_subsystem_setup(sos_kernel_core_base_paddr, |
| sos_bochs_printf("Could not setup paged memory mode\n"); | sos_kernel_core_top_paddr)); |
| sos_x86_videomem_printf(2, 0, | |
| SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, | |
| "Paged-memory mode is activated"); | |
| | |
| sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT, | sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT, |
| pgflt_ex); | pgflt_ex); |
|
|
|
| * Setup kernel virtual memory allocator | * Setup kernel virtual memory allocator |
| */ | */ |
| | |
| if (sos_kmem_vmm_setup(sos_kernel_core_base_paddr, | if (sos_kmem_vmm_subsystem_setup(sos_kernel_core_base_paddr, |
| sos_kernel_core_top_paddr, | sos_kernel_core_top_paddr, |
| bootstrap_stack_bottom, | bootstrap_stack_bottom, |
| bootstrap_stack_bottom + bootstrap_stack_size)) | bootstrap_stack_bottom |
| | + bootstrap_stack_size)) |
| | |
| if (sos_kmalloc_setup()) | if (sos_kmalloc_subsystem_setup()) |
| | |
| /* | /* |
| * Enabling the HW interrupts here, this will make the timer HW | * Initialize the Kernel thread and scheduler subsystems |
| * interrupt call our clk_it handler | |
| asm volatile ("sti\n"); | |
| | /* Initialize kernel thread subsystem */ |
| /* | sos_kthread_subsystem_setup(bootstrap_stack_bottom, |
| * Print hello world using coroutines | bootstrap_stack_size); |
| */ | |
| print_hello_world(); | |
| | /* Initialize the scheduler */ |
| | sos_sched_subsystem_setup(); |
| | |
| /* | /* Declare the IDLE thread */ |
| * Run coroutine tests | SOS_ASSERT_FATAL(sos_kthread_create("idle", idle_kthread, NULL) != NULL); |
| */ | |
| sos_x86_videomem_printf(4, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, | |
| "Coroutine test"); | |
| sos_x86_videomem_printf(5, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Parsing..."); | |
| syntax_tree = parse_expression(" - ( (69/ toto)+ ( (( - +-- 1))) + --toto*((toto+ - - y - +2*(y-toto))*y) +2*(y-toto) )/- (( y - toto)*2)"); | |
| if (syntax_tree != NULL) | |
| { | |
| sos_x86_videomem_printf(6, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Evaluating..."); | |
| sos_x86_videomem_printf(7, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Result=%d (if 0: check bochs output)", | |
| eval_expression(syntax_tree, | |
| (const char*[]){"toto", "y"}, | |
| (int[]){3, 4}, | |
| 2)); | |
| free_syntax_tree(syntax_tree); | |
| sos_x86_videomem_printf(8, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Done (un-allocated syntax tree)"); | |
| } | |
| else | |
| { | |
| sos_x86_videomem_printf(6, 0, | |
| SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
| "Error in parsing (see bochs output)"); | |
| } | |
| /* | /* Enabling the HW interrupts here, this will make the timer HW |
| * Run some demand-paging tests | interrupt call the scheduler */ |
| */ | asm volatile ("sti\n"); |
| test_demand_paging(234567, 500); | |
| | |
| | |
| /* | |
| * Create an un-resolved page fault, which will make the page fault | |
| * handler print the backtrace. | |
| */ | |
| test_backtrace(6, 0xdeadbeef, bootstrap_stack_bottom, bootstrap_stack_size); | |
| /* | /* |
| * System should be halted BEFORE here ! | * Force the idle thread to run at least once to force a context |
| */ | * switch. This way the "cpu_kstate" of the kernel thread for the |
| | * sos_main thread gets a chance to be filled with the current CPU |
| | * context. Useful only if we call sos_kthread_exit() too early from |
| /* An operatig system never ends */ | * sos_main: a "stack overflow" will be wrongly detected simply |
| for (;;) | * because the "cpu_kstate" of the thread has not be correctly |
| continue; | * initialised. A context switch is a good way to initialise it. |
| | */ |
| return; | sos_kthread_yield(); |
| | |
| | |
| | /* Now run some Kernel threads just for fun ! */ |
| | extern void MouseSim(); |
| | MouseSim(); |
| | test_kthread(); |
| | |
| | /* |
| | * We can safely exit from this function now, for there is already |
| | * an idle Kernel thread ready to make the CPU busy working... |
| | * |
| | * However, we must EXPLICITELY call sos_kthread_exit() because a |
| | * simple "return" will return nowhere ! Actually this first thread |
| | * was initialized by the Grub bootstrap stage, at a time when the |
| | * word "thread" did not exist. This means that the stack was not |
| | * setup in order for a return here to call sos_kthread_exit() |
| | * automagically. Hence we must call it manually. This is the ONLY |
| | * kernel thread where we must do this manually. |
| | */ |
| | sos_bochs_printf("Bye from primary thread !\n"); |
| | sos_kthread_exit(); |
| | SOS_FATAL_ERROR("No trespassing !"); |
| | |
| /tmp/sos-code-article6/sos/mouse_sim.c (1970-01-01 01:00:00.000000000 +0100
) |
|
| ../sos-code-article6.5/sos/mouse_sim.c (2005-01-04 04:12:16.000000000 +0100
) |
|
|
|
|
| | /*************************************************************************** |
| | * Copyright (C) 2004 by cyril dupuit * |
| | * cyrildupuit@hotmail.com * |
| | * http://perso.wanadoo.fr/koalys/ * |
| | * (Adaptation for SOS by d2 -- 2004/12/20) * |
| | * * |
| | * This program is free software; you can redistribute it and/or modify * |
| | * it under the terms of the GNU General Public License as published by * |
| | * the Free Software Foundation; either version 2 of the License, or * |
| | * (at your option) any later version. * |
| | * * |
| | * This program is distributed in the hope that it will be useful, * |
| | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| | * GNU General Public License for more details. * |
| | * * |
| | * You should have received a copy of the GNU General Public License * |
| | * along with this program; if not, write to the * |
| | * Free Software Foundation, Inc., * |
| | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
| | ***************************************************************************/ |
| | |
| | //***************************************************************************** |
| | // Nom du module : MouseSim.c |
| | // Description : Creation et destruction de souris mangeuse de fromages |
| | //***************************************************************************** |
| | |
| | #include <sos/assert.h> |
| | #include <sos/klibc.h> |
| | #include <sos/kthread.h> |
| | #include <sos/ksynch.h> |
| | #include <sos/kmalloc.h> |
| | #include <drivers/x86_videomem.h> |
| | |
| | // Historique : |
| | // 20/12/04 : Suppr DestroyMap et suppr handler kbd dans version LM (d2) |
| | // 26/11/04 : Bug trouve et resolu dans la fonction DestroyMap |
| | // 21/11/04 : Creation du module V1.0 |
| | |
| | //***************************************************************************** |
| | // Definition des equivalences : |
| | //***************************************************************************** |
| | #define MAP_X 76 |
| | #define MAP_Y 12 |
| | #define MAP_SIZE MAP_X * MAP_Y |
| | |
| | #define MOUSE 0x01 |
| | #define CHEESE 0x02 |
| | #define OBSTACLE 0x04 |
| | #define INPUT 0x08 |
| | #define OUTPUT 0x10 |
| | |
| | #define OBSTACLE_COUNT 100 |
| | #define CHEESE_COUNT 650 |
| | |
| | #define MOUSE_FULL 0x01 |
| | #define MOUSE_EMPTY 0x02 |
| | #define CHEESE_FOUND 0x04 |
| | #define MOUSE_EXITED 0x08 |
| | |
| | #define MOUSE_SPEED_MAX 1000 |
| | #define MOUSE_SPEED_MIN 4 |
| | |
| | typedef unsigned int Color_t; |
| | |
| | struct Point{ |
| | int X; |
| | int Y; |
| | }; |
| | |
| | typedef struct Point Point_t; |
| | |
| | #define Set(Reg, Flag) Reg = (Reg | Flag) |
| | #define Reset(Reg, Flag) Reg = (Reg &(~Flag)) |
| | #define IsSet(Reg, Flag) (Reg & Flag) |
| | |
| | |
| | //***************************************************************************** |
| | // Structure de gestion d'un element |
| | //***************************************************************************** |
| | struct Element{ |
| | sos_ui32_t Type;//Type d'element |
| | sos_ui32_t Status; |
| | Color_t Color;//Couleur de l'element |
| | Point_t P;//Coordonnees de l'element |
| | struct sos_kthread * ThreadID;//Thread associe a la souris |
| | int Way;//Direction de la souris |
| | }; |
| | |
| | typedef struct Element Element_t; |
| | |
| | //***************************************************************************** |
| | // Prototypes des fonctions/procedures : |
| | //***************************************************************************** |
| | static void MouseCommander(void); |
| | static void DrawMap(void); |
| | static sos_ret_t CreateMap(void); |
| | static sos_ret_t InitMapInput(Element_t * * pMap); |
| | static sos_ret_t InitMapOutput(Element_t * * pMap); |
| | static sos_ret_t ElementInit(Element_t * * pMap, unsigned int Type); |
| | static void Mouse(unsigned long Param); |
| | static void MouseMove(Point_t * P); |
| | static Point_t ChoosePosition(Element_t * pMouse, int Positions[], int Count); |
| | static int EvaluatePositions(Point_t Org, int Positions[], Point_t * Cheese); |
| | static sos_bool_t IsCollision(Point_t Org, Point_t p, Point_t *Cheese); |
| | static sos_bool_t AffectMovement(Point_t Org, Point_t p); |
| | static void MouseCreator(void); |
| | static sos_ret_t CreateMouse(void); |
| | |
| | //***************************************************************************** |
| | // Variables globales de ce module : |
| | //***************************************************************************** |
| | |
| | static Element_t * * pMap; |
| | static struct sos_ksema SemMap; |
| | static struct sos_ksema SemMouse; |
| | static int MouseCount = 0; |
| | static int CheeseCount = 0; |
| | static int ObstacleCount = 0; |
| | static int MouseSpeed = 100; |
| | |
| | //***************************************************************************** |
| | // Koalys Glue |
| | //***************************************************************************** |
| | void DrawPixel(int x, int y, Color_t color) |
| | { |
| | sos_x86_videomem_putchar(y+3, x+2, color, 219); |
| | } |
| | |
| | |
| | void ThreadDelete(struct sos_kthread *tid) |
| | { |
| | SOS_ASSERT_FATAL(! "TODO !"); |
| | } |
| | |
| | //***************************************************************************** |
| | // Point d'entre de la 'simulation' |
| | //***************************************************************************** |
| | void MouseSim(void) |
| | { |
| | //Creation du semaphore de protection de la carte |
| | SOS_ASSERT_FATAL(SOS_OK == sos_ksema_init(& SemMap, "SemMap", 1)); |
| | |
| | //Creation du semaphore de creation de souris |
| | SOS_ASSERT_FATAL(SOS_OK == sos_ksema_init(& SemMouse, "SemMouse", 2)); |
| | |
| | //Creation de la carte |
| | SOS_ASSERT_FATAL(SOS_OK == CreateMap()); |
| | |
| | //Creation du thread createur de souris |
| | SOS_ASSERT_FATAL(sos_kthread_create("MouseCreator", |
| | (sos_kthread_start_routine_t)MouseCreator, |
| | 0) != NULL); |
| | |
| | } |
| | |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Creer et initialiser la carte |
| | // Entree : Aucune |
| | // Parametre retourne : ERROR si la memoire est insuffisante, TRUE sinon |
| | //***************************************************************************** |
| | static sos_ret_t CreateMap(void) |
| | { |
| | pMap = (Element_t * *)sos_kmalloc(MAP_SIZE * sizeof(Element_t *), 0); |
| | if(pMap == NULL) return -SOS_ENOMEM; |
| | |
| | //Mettre la carte a 0 |
| | memset(pMap, 0, MAP_SIZE * sizeof(Element_t *)); |
| | |
| | //Initialisation de l'entree de la carte |
| | if(SOS_OK != InitMapInput(pMap)) |
| | {//Memoire insuffisante |
| | return -SOS_EFATAL; |
| | } |
| | |
| | //Initialisation de la sortie de la carte |
| | if(InitMapOutput(pMap) != SOS_OK) |
| | {//Memoire insuffisante |
| | return -SOS_EFATAL; |
| | } |
| | |
| | //Initialisation du fromage |
| | if(ElementInit(pMap, CHEESE) != SOS_OK) |
| | {//Memoire insuffisante |
| | return -SOS_EFATAL; |
| | } |
| | |
| | //Initialisation des obstacles |
| | if(ElementInit(pMap, OBSTACLE) != SOS_OK) |
| | {//Memoire insuffisante |
| | return -SOS_EFATAL; |
| | } |
| | |
| | DrawMap();//Afficher la carte creee |
| | |
| | return SOS_OK; |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la procedure : Dessiner la carte a l'ecran |
| | // Entree : Aucune |
| | // Sortie : Aucune |
| | //***************************************************************************** |
| | static void DrawMap(void) |
| | { |
| | unsigned int I; |
| | |
| | for(I = 0; I < MAP_SIZE; I++) |
| | { |
| | if(pMap[I] != NULL) |
| | { |
| | DrawPixel(I % MAP_X, I/MAP_X, pMap[I]->Color); |
| | } |
| | else DrawPixel(I % MAP_X, I/MAP_X, SOS_X86_VIDEO_FG_BLACK); |
| | } |
| | sos_x86_videomem_printf(23, 0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, |
| | "Souris = %d; Fromages = %d; Obstacles = %d ", |
| | MouseCount, CheeseCount, ObstacleCount); |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Initialiser l'entree de la carte |
| | // Entree : |
| | // pMap : Pointeur sur la carte |
| | // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon |
| | //***************************************************************************** |
| | static sos_ret_t InitMapInput(Element_t * * pMap) |
| | { |
| | Element_t * pElement; |
| | |
| | //Definir le point d'entree |
| | pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0); |
| | if(pElement == NULL) return -SOS_ENOMEM; |
| | |
| | //Initialiser l'entree |
| | pElement->Type = INPUT; |
| | pElement->Status = 0; |
| | pElement->Color = SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE; |
| | pElement->P.X = 0; |
| | pElement->P.Y = MAP_Y / 2; |
| | pElement->ThreadID = 0; |
| | |
| | pMap[(pElement->P.Y * MAP_X) + pElement->P.X] = pElement; |
| | |
| | return SOS_OK; |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Initialiser la sortie de la carte |
| | // Entree : |
| | // pMap : Pointeur sur la carte |
| | // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon |
| | //***************************************************************************** |
| | static sos_ret_t InitMapOutput(Element_t * * pMap) |
| | { |
| | Element_t * pElement; |
| | |
| | //Definir le point de sortie |
| | pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0); |
| | if(pElement == NULL) return -SOS_ENOMEM; |
| | |
| | //Initialiser l'entree |
| | pElement->Type = OUTPUT; |
| | pElement->Status = 0; |
| | pElement->Color = SOS_X86_VIDEO_FG_LTBLUE; |
| | pElement->P.X = MAP_X - 1; |
| | pElement->P.Y = MAP_Y / 2; |
| | pElement->ThreadID = 0; |
| | |
| | pMap[(pElement->P.Y * MAP_X) + pElement->P.X] = pElement; |
| | |
| | return SOS_OK; |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Initialiser un type d'objet sur la carte |
| | // Entree : |
| | // pMap : Pointeur sur la carte |
| | // Type : Type d'objet a initialiser |
| | // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon |
| | //***************************************************************************** |
| | static sos_ret_t ElementInit(Element_t * * pMap, unsigned int Type) |
| | { |
| | unsigned int I, J; |
| | unsigned int Max; |
| | Color_t Color; |
| | |
| | if(Type == CHEESE) |
| | {//Type d'element = fromage |
| | Max = CHEESE_COUNT; |
| | Color = SOS_X86_VIDEO_FG_YELLOW; |
| | } |
| | else if(Type == OBSTACLE) |
| | {//Type d'element = Obstacle |
| | Max = OBSTACLE_COUNT; |
| | Color = SOS_X86_VIDEO_FG_GREEN; |
| | } |
| | else |
| | {//Aucune autre type reconnu |
| | return -SOS_EINVAL; |
| | } |
| | |
| | for(I = 0; I < Max; I++) |
| | {//Tirer les fromages |
| | J = random(); |
| | J += random(); |
| | J %= MAP_SIZE; |
| | if(pMap[J] == NULL) |
| | {//Si l'emplacement est libre |
| | pMap[J] = (Element_t *)sos_kmalloc(sizeof(Element_t), |
| | 0); |
| | if(pMap[J] == NULL) return -SOS_ENOMEM; |
| | |
| | pMap[J]->Type = Type; |
| | //Initialiser l'element |
| | if(Type == CHEESE) |
| | {//Type d'element = fromage |
| | CheeseCount++; |
| | } |
| | else if(Type == OBSTACLE) |
| | {//Type d'element = Obstacle |
| | ObstacleCount++; |
| | } |
| | |
| | pMap[J]->Color = Color; |
| | pMap[J]->Status = 0; |
| | pMap[J]->Color = Color; |
| | pMap[J]->P.X = J % MAP_X; |
| | pMap[J]->P.Y = J / MAP_X; |
| | pMap[J]->ThreadID = 0; |
| | } |
| | } |
| | |
| | return SOS_OK; |
| | } |
| | |
| | |
| | //***************************************************************************** |
| | // But du thread : Deplacer la souris sur la carte selon les regles etablies. |
| | // Regles : |
| | // - La souris doit se placer devant l'entree puis commence a recolter du |
| | // fromage. |
| | // - Des que la souris a ramasse un morceau de fromage, elle doit aller en |
| | // entree de la carte afin de deposer sa recolte. |
| | // - Si une souris a prouve sa recolte, une autre souris est creee. |
| | // - Si une souris prend la sortie, elle est eliminee. |
| | //***************************************************************************** |
| | static void Mouse(unsigned long Param) |
| | { |
| | Element_t * pMouse = (Element_t *)Param; |
| | Point_t P; |
| | |
| | SOS_ASSERT_FATAL(pMouse != NULL); |
| | |
| | //Position de depart de la souris |
| | P = pMouse->P; |
| | P = pMouse->P; |
| | |
| | while(1) |
| | { |
| | int delay_ms; |
| | struct sos_time delay; |
| | |
| | //La souris doit se deplacer |
| | sos_ksema_down(& SemMap, NULL); |
| | |
| | MouseMove(&P); |
| | |
| | sos_ksema_up(& SemMap); |
| | |
| | // Est-ce que la souris est sortie ? |
| | if (IsSet(pMouse->Status, MOUSE_EXITED)) |
| | // Oui => on sort |
| | break; |
| | |
| | // Delai entre MOUSE_SPEED_MIN et MouseSpeed - 1 |
| | delay_ms = MOUSE_SPEED_MIN + (random() % MouseSpeed); |
| | delay.sec = delay_ms / 1000; |
| | delay.nanosec = (delay_ms % 1000) * 1000000; |
| | sos_kthread_sleep(& delay); |
| | } |
| | |
| | // Libere la structure associee |
| | sos_kfree((sos_vaddr_t)pMouse); |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la procedure : Deplacer la souris de maniere aleatoire sur la carte |
| | // Entrees : |
| | // P : Position courante de la souris |
| | // Sorties : |
| | // P : Position suivante de la souris |
| | //***************************************************************************** |
| | static void MouseMove(Point_t * P) |
| | { |
| | Point_t Org; |
| | Point_t p; |
| | Point_t Cheese; |
| | int Positions[8]; |
| | int Count = 0; |
| | Element_t * pMouse; |
| | |
| | Org = *P; |
| | |
| | pMouse = pMap[Org.X + (Org.Y * MAP_X)]; |
| | |
| | Count = EvaluatePositions(Org, Positions, &Cheese); |
| | |
| | if(Count == 0) return; |
| | |
| | p = Org; |
| | |
| | if(IsSet(pMouse->Status, CHEESE_FOUND)) |
| | {//Prendre le fromage |
| | Reset(pMouse->Status, CHEESE_FOUND); |
| | p = Cheese; |
| | } |
| | else |
| | {//Choisir une position au hasard |
| | p = ChoosePosition(pMouse, Positions, Count); |
| | } |
| | if(AffectMovement(Org, p) == FALSE) return; |
| | //Deplacer la souris |
| | pMap[Org.X + (Org.Y * MAP_X)] = NULL; |
| | pMap[p.X + (p.Y * MAP_X)] = pMouse; |
| | pMouse->P = p; |
| | //Mettre a jour l'affichage |
| | DrawPixel(Org.X, Org.Y, SOS_X86_VIDEO_FG_BLACK); |
| | DrawPixel(p.X, p.Y, pMouse->Color); |
| | sos_x86_videomem_printf( 23,0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, "Souris = %d; Fromages = %d; Obstacles = %d ", MouseCount, CheeseCount, ObstacleCount); |
| | //Mettre a jour les coordonnees |
| | *P = p; |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Choisir un mouvement |
| | // Entree : |
| | // pMouse : Pointeur sur la souris |
| | // Positions : Tableau de position possible |
| | // Count :Nombre de positions valides |
| | // Sortie : Aucune |
| | // Parametre retourne : Position choisie |
| | //***************************************************************************** |
| | static Point_t ChoosePosition(Element_t * pMouse, int Positions[], int Count) |
| | { |
| | int I, J; |
| | Point_t p; |
| | |
| | for(J = 0; J < Count; J++) |
| | {//Chercher dans le tableau si cette position est disponible |
| | I = Positions[J]; |
| | if(I == pMouse->Way) |
| | {//Poursuivre ce sens d'avance |
| | p = pMouse->P; |
| | switch(I) |
| | { |
| | case 0: |
| | p.Y++; |
| | break; |
| | case 1: |
| | p.X++; |
| | p.Y++; |
| | break; |
| | case 2: |
| | p.X++; |
| | break; |
| | case 3: |
| | p.Y--; |
| | p.X++; |
| | break; |
| | case 4: |
| | p.Y--; |
| | break; |
| | case 5: |
| | p.Y--; |
| | p.X--; |
| | break; |
| | case 6: |
| | p.X--; |
| | break; |
| | case 7: |
| | p.X--; |
| | p.Y++; |
| | break; |
| | } |
| | return p; |
| | } |
| | } |
| | |
| | J = random() % Count; |
| | I = Positions[J]; |
| | if(((I + 4) % 8) == pMouse->Way) |
| | {//Eviter le sens inverse |
| | J = (J + 1) % Count; |
| | I = Positions[J]; |
| | } |
| | |
| | p = pMouse->P; |
| | switch(I) |
| | {//Repere le deplacement |
| | case 0: |
| | p.Y++; |
| | break; |
| | case 1: |
| | p.X++; |
| | p.Y++; |
| | break; |
| | case 2: |
| | p.X++; |
| | break; |
| | case 3: |
| | p.Y--; |
| | p.X++; |
| | break; |
| | case 4: |
| | p.Y--; |
| | break; |
| | case 5: |
| | p.Y--; |
| | p.X--; |
| | break; |
| | case 6: |
| | p.X--; |
| | break; |
| | case 7: |
| | p.X--; |
| | p.Y++; |
| | break; |
| | } |
| | |
| | pMouse->Way = I;//Memoriser la direction selectionnee |
| | |
| | return p; |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Evaluer les positions possibles et les memoriser dans |
| | // un tableau de positions si aucun fromage n'a ete detecte. Si du fromage a |
| | // ete detecte, il sera selectionne en premier. La presence d'un fromage est |
| | // indiquee par le drapeau CHEESE_FOUND |
| | // Entree : |
| | // Org : Position de la souris |
| | // Sorties : |
| | // Positions : Tableau de positions valides |
| | // Cheese : Position du fromage |
| | // Parametre retourne : Nombre de positions valides |
| | //***************************************************************************** |
| | static int EvaluatePositions(Point_t Org, int Positions[], Point_t * Cheese) |
| | { |
| | int I; |
| | int Count = 0; |
| | Point_t p; |
| | Point_t CheesePos; |
| | |
| | for(I = 0; I < 8; I++) |
| | {//Explorer toute les directions |
| | p = Org; |
| | switch(I) |
| | {//Repere le deplacement |
| | case 0: |
| | p.Y++; |
| | break; |
| | case 1: |
| | p.X++; |
| | p.Y++; |
| | break; |
| | case 2: |
| | p.X++; |
| | break; |
| | case 3: |
| | p.Y--; |
| | p.X++; |
| | break; |
| | case 4: |
| | p.Y--; |
| | break; |
| | case 5: |
| | p.Y--; |
| | p.X--; |
| | break; |
| | case 6: |
| | p.X--; |
| | break; |
| | case 7: |
| | p.X--; |
| | p.Y++; |
| | break; |
| | } |
| | //Tester la collision |
| | if(IsCollision(Org, p, &CheesePos) == FALSE) |
| | {//La souris n'a rencontre aucun obstacle |
| | Positions[Count] = I; |
| | Count++; |
| | } |
| | } |
| | |
| | *Cheese = CheesePos; |
| | |
| | return Count; |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Affecter un mouvement a la souris |
| | // Entrees : |
| | // Org : Coordonnees de la souris |
| | // p : Coordonnees voulu par la souris |
| | // Parametre retourne : TRUE si le mouvement a eu lieu, FALSE sinon |
| | //***************************************************************************** |
| | static sos_bool_t AffectMovement(Point_t Org, Point_t p) |
| | { |
| | Element_t * pMouse = pMap[Org.X + (Org.Y * MAP_X)]; |
| | Element_t * pElement; |
| | |
| | pElement = pMap[p.X + (p.Y * MAP_X)]; |
| | |
| | //La place est libre |
| | if(pElement == NULL) return TRUE;//Autoriser le mouvement |
| | |
| | switch(pElement->Type) |
| | { |
| | case CHEESE: |
| | // Liberer l'emplacement memoire du fromage |
| | sos_kfree((sos_vaddr_t)pElement); |
| | pMap[p.X + (p.Y * MAP_X)] = NULL; |
| | |
| | //Donner le fromage a la souris |
| | Set(pMouse->Status, MOUSE_FULL); |
| | Reset(pMouse->Status, MOUSE_EMPTY); |
| | pMouse->Color = SOS_X86_VIDEO_FG_MAGENTA; |
| | CheeseCount--; |
| | return TRUE; |
| | case OUTPUT: |
| | //Supprimer la souris |
| | pMap[Org.X + (Org.Y * MAP_X)] = NULL; |
| | MouseCount--; |
| | DrawPixel(Org.X, Org.Y, SOS_X86_VIDEO_FG_BLACK); |
| | sos_x86_videomem_printf( 23,0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, |
| | "Souris = %d; Fromages = %d; Obstacles = %d ", |
| | MouseCount, CheeseCount, |
| | ObstacleCount); |
| | Set(pMouse->Status, MOUSE_EXITED); |
| | return FALSE; |
| | default : |
| | return FALSE; |
| | } |
| | |
| | return FALSE; |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Tester si une collision a eu lieu avec un obstacle |
| | // Entrees : |
| | // Org : Coordonnees de la souris |
| | // p : coordonnees desirees par la souris |
| | // Sortie : |
| | // Cheese : Coordonnees du fromage |
| | // Parametre retourne : TRUE si une collision a eu lieu, FALSE sinon |
| | //***************************************************************************** |
| | static sos_bool_t IsCollision(Point_t Org, Point_t p, Point_t *Cheese) |
| | { |
| | Element_t * pMouse = pMap[Org.X + (Org.Y * MAP_X)]; |
| | Element_t * pElement; |
| | |
| | //Tester les bordures de la map |
| | if((p.X < 0)||(p.Y < 0)) return TRUE; |
| | |
| | if((p.Y >= MAP_Y)||(p.X >= MAP_X)) return TRUE; |
| | |
| | pElement = pMap[p.X + (p.Y * MAP_X)]; |
| | |
| | //L'element est vide |
| | if(pElement == NULL) return FALSE; |
| | |
| | //Si du fromage a ete trouve, stopper la recherche |
| | if(IsSet(pMouse->Status, CHEESE_FOUND)) return FALSE; |
| | |
| | switch(pElement->Type) |
| | { |
| | case CHEESE: |
| | if(IsSet(pMouse->Status, MOUSE_FULL)) return TRUE; |
| | //Indiquer que du fromage a ete trouve |
| | Set(pMouse->Status, CHEESE_FOUND); |
| | //Retenir la position du fromage |
| | (*Cheese).X = p.X; |
| | (*Cheese).Y = p.Y; |
| | break; |
| | case INPUT: |
| | if(IsSet(pMouse->Status, MOUSE_EMPTY)) return TRUE; |
| | //Remplir les reserves de fromage |
| | Set(pMouse->Status, MOUSE_EMPTY); |
| | Reset(pMouse->Status, MOUSE_FULL); |
| | pMouse->Color = SOS_X86_VIDEO_FG_LTRED; |
| | //Autoriser la creation d'une autre souris |
| | sos_ksema_up(& SemMouse); |
| | return TRUE; |
| | case OUTPUT: |
| | break; |
| | default : |
| | return TRUE; |
| | } |
| | |
| | return FALSE;//Aucune collision |
| | } |
| | |
| | //***************************************************************************** |
| | // But du thread : Creer une souris et la placer autour de l'entree |
| | //***************************************************************************** |
| | static void MouseCreator(void) |
| | { |
| | while(1) |
| | { |
| | sos_ksema_down(& SemMouse, NULL); |
| | sos_ksema_down(& SemMap, NULL); |
| | CreateMouse(); |
| | sos_ksema_up(& SemMap); |
| | } |
| | } |
| | |
| | //***************************************************************************** |
| | // But de la fonction : Creer une souris et l'inserer dans la carte |
| | // Entree : Aucune |
| | // Parametre retourne : ERROR si memoire insuffisante, FALSE si souris non |
| | // cree, TRUE sinon |
| | //***************************************************************************** |
| | static sos_ret_t CreateMouse(void) |
| | { |
| | Element_t * pElement; |
| | unsigned int I; |
| | |
| | Point_t p; |
| | |
| | for(I = 0; I < 8; I++) |
| | {//Explorer tous les emplacements |
| | p.X = 0; |
| | p.Y = MAP_Y / 2; |
| | switch(I) |
| | {//Repere le deplacement |
| | case 0: |
| | p.Y++; |
| | break; |
| | case 1: |
| | p.X++; |
| | p.Y++; |
| | break; |
| | case 2: |
| | p.X++; |
| | break; |
| | case 3: |
| | p.Y--; |
| | p.X++; |
| | break; |
| | case 4: |
| | p.Y--; |
| | break; |
| | case 5: |
| | p.Y--; |
| | p.X--; |
| | break; |
| | case 6: |
| | p.X--; |
| | break; |
| | case 7: |
| | p.X--; |
| | p.Y++; |
| | break; |
| | } |
| | if((p.X >= 0)&&(p.Y >= 0)&&(p.X < MAP_X)&&(p.Y < MAP_Y)) |
| | {//L'emplacement est valide |
| | pElement = pMap[p.X + (p.Y * MAP_X)]; |
| | if(pElement == NULL) |
| | {//Creer la souris |
| | pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0); |
| | if(pElement != NULL) |
| | {//Initialiser l'entree |
| | pElement->Type = MOUSE; |
| | Set(pElement->Status, MOUSE_EMPTY); |
| | pElement->Color = SOS_X86_VIDEO_FG_LTRED; |
| | pElement->P = p; |
| | pElement->Way = 0; |
| | pElement->ThreadID = sos_kthread_create("Mouse", (sos_kthread_start_routine_t)Mouse, pElement); |
| | if(pElement->ThreadID == 0) |
| | { |
| | sos_kfree((sos_vaddr_t)pElement); |
| | pElement = NULL; |
| | } |
| | pMap[p.X + (p.Y * MAP_X)] = pElement; |
| | MouseCount++; |
| | |
| | DrawPixel(p.X, p.Y, pElement->Color); |
| | sos_x86_videomem_printf(23, 0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, "Souris = %d; Fromages = %d; Obstacles = %d ", MouseCount, CheeseCount, ObstacleCount); |
| | |
| | return SOS_OK; |
| | } |
| | } |
| | } |
| | } |
| | return -SOS_EBUSY; |
| | } |
| | |
| | //***************************************************************************** |
| | // C'est fini !!!! |
| | //***************************************************************************** |
| | |
| /tmp/sos-code-article6/sos/time.c (1970-01-01 01:00:00.000000000 +0100
) |
|
| ../sos-code-article6.5/sos/time.c (2005-01-04 04:12:16.000000000 +0100
) |
|
|
|
|
| | /* Copyright (C) 2004 David Decotigny |
| | |
| | This program is free software; you can redistribute it and/or |
| | modify it under the terms of the GNU General Public License |
| | as published by the Free Software Foundation; either version 2 |
| | of the License, or (at your option) any later version. |
| | |
| | This program is distributed in the hope that it will be useful, |
| | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | GNU General Public License for more details. |
| | |
| | You should have received a copy of the GNU General Public License |
| | along with this program; if not, write to the Free Software |
| | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| | USA. |
| | */ |
| | |
| | #include <sos/assert.h> |
| | #include <sos/klibc.h> |
| | #include <hwcore/irq.h> |
| | #include <sos/list.h> |
| | #include <sos/kthread.h> |
| | |
| | #include "time.h" |
| | |
| | |
| | /** |
| | * Number of nanoseconds in 1 second |
| | */ |
| | #define NS_IN_SEC 1000000000UL |
| | |
| | |
| | /** |
| | * The list of timeout actions waiting for a timeout. The timeout |
| | * actions are stored in the list in increasing initial timeout |
| | * order. Actually, the "timeout" field won't reflect this initial |
| | * timeout: for each element in the list, it stores the timeout |
| | * _difference_ between the timeout action and the previous in the |
| | * list. |
| | */ |
| | static struct sos_timeout_action *timeout_action_list; |
| | |
| | |
| | /** |
| | * Current resolution of a time tick |
| | */ |
| | static struct sos_time tick_resolution; |
| | |
| | |
| | /** |
| | * Time elapsed between boot and last timer tick |
| | * |
| | * @note No 'volatile' here because the tick value is NEVER modified |
| | * while in any of the functions below: it is modified only out of |
| | * these functions by the IRQ timer handler because these functions |
| | * are protected against timer IRQ and are "one shot" (no busy waiting |
| | * for a change in the tick's value). |
| | */ |
| | static struct sos_time last_tick_time; |
| | |
| | |
| | sos_ret_t sos_time_inc(struct sos_time *dest, |
| | const struct sos_time *to_add) |
| | { |
| | /* nanosec is always < 1e9 so that their sum is always < 2e9, which |
| | is smaller than 2^32-1 */ |
| | sos_ui32_t sigma_ns = dest->nanosec + to_add->nanosec; |
| | |
| | dest->sec += to_add->sec; |
| | dest->sec += sigma_ns / NS_IN_SEC; |
| | dest->nanosec = sigma_ns % NS_IN_SEC; |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t sos_time_dec(struct sos_time *dest, |
| | const struct sos_time *to_dec) |
| | { |
| | /* nanosec is always < 1e9 so that their difference is always in |
| | (-1e9, 1e9), which is compatible with the (-2^31, 2^31 - 1) |
| | cpacity of a signed dword */ |
| | sos_si32_t diff_ns = ((sos_si32_t)dest->nanosec) |
| | - ((sos_si32_t)to_dec->nanosec); |
| | |
| | /* Make sure substraction is possible */ |
| | SOS_ASSERT_FATAL(dest->sec >= to_dec->sec); |
| | if (dest->sec == to_dec->sec) |
| | SOS_ASSERT_FATAL(dest->nanosec >= to_dec->nanosec); |
| | |
| | dest->sec -= to_dec->sec; |
| | if (diff_ns > 0) |
| | dest->sec += diff_ns / NS_IN_SEC; |
| | else |
| | dest->sec -= ((-diff_ns) / NS_IN_SEC); |
| | dest->nanosec = (NS_IN_SEC + diff_ns) % NS_IN_SEC; |
| | if (diff_ns < 0) |
| | dest->sec --; |
| | return SOS_OK; |
| | } |
| | |
| | |
| | int sos_time_cmp(const struct sos_time *t1, |
| | const struct sos_time *t2) |
| | { |
| | /* Compare seconds */ |
| | if (t1->sec < t2->sec) |
| | return -1; |
| | else if (t1->sec > t2->sec) |
| | return 1; |
| | |
| | /* seconds are equal => compare nanoseconds */ |
| | else if (t1->nanosec < t2->nanosec) |
| | return -1; |
| | else if (t1->nanosec > t2->nanosec) |
| | return 1; |
| | |
| | /* else: sec and nanosecs are equal */ |
| | return 0; |
| | } |
| | |
| | |
| | sos_bool_t sos_time_is_zero(const struct sos_time *tm) |
| | { |
| | return ( (0 == tm->sec) && (0 == tm->nanosec) ); |
| | } |
| | |
| | |
| | sos_ret_t sos_time_subsysem_setup(const struct sos_time *initial_resolution) |
| | { |
| | timeout_action_list = NULL; |
| | last_tick_time = (struct sos_time) { .sec = 0, .nanosec = 0 }; |
| | memcpy(& tick_resolution, initial_resolution, sizeof(struct sos_time)); |
| | |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t sos_time_get_tick_resolution(struct sos_time *resolution) |
| | { |
| | sos_ui32_t flags; |
| | sos_disable_IRQs(flags); |
| | |
| | memcpy(resolution, & tick_resolution, sizeof(struct sos_time)); |
| | |
| | sos_restore_IRQs(flags); |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t sos_time_set_tick_resolution(const struct sos_time *resolution) |
| | { |
| | sos_ui32_t flags; |
| | sos_disable_IRQs(flags); |
| | |
| | memcpy(& tick_resolution, resolution, sizeof(struct sos_time)); |
| | |
| | sos_restore_IRQs(flags); |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t sos_time_get_now(struct sos_time *now) |
| | { |
| | sos_ui32_t flags; |
| | sos_disable_IRQs(flags); |
| | |
| | memcpy(now, & last_tick_time, sizeof(struct sos_time)); |
| | |
| | sos_restore_IRQs(flags); |
| | return SOS_OK; |
| | } |
| | |
| | |
| | /** |
| | * Helper routine to add the action in the list. MUST be called with |
| | * interrupts disabled ! |
| | */ |
| | static sos_ret_t _add_action(struct sos_timeout_action *act, |
| | const struct sos_time *due_date, |
| | sos_bool_t is_relative_due_date, |
| | sos_timeout_routine_t *routine, |
| | void *routine_data) |
| | { |
| | struct sos_timeout_action *other, *insert_before; |
| | int nb_act; |
| | |
| | /* Delay must be specified */ |
| | if (due_date == NULL) |
| | return -SOS_EINVAL; |
| | |
| | /* Action container MUST be specified */ |
| | if (act == NULL) |
| | return -SOS_EINVAL; |
| | |
| | /* Refuse to add an empty action */ |
| | if (NULL == routine) |
| | return -SOS_EINVAL; |
| | |
| | /* Refuse to add the action if it is already added */ |
| | if (NULL != act->tmo_next) |
| | return -SOS_EBUSY; |
| | |
| | /* Compute action absolute due date */ |
| | if (is_relative_due_date) |
| | { |
| | /* The provided due_date is relative to the current time */ |
| | memcpy(& act->timeout, & last_tick_time, sizeof(struct sos_time)); |
| | sos_time_inc(& act->timeout, due_date); |
| | } |
| | else |
| | { |
| | /* The provided due_date is absolute (ie relative to the system |
| | boot instant) */ |
| | |
| | if (sos_time_cmp(due_date, & last_tick_time) < 0) |
| | /* Refuse to add a past action ! */ |
| | return -SOS_EINVAL; |
| | |
| | memcpy(& act->timeout, due_date, sizeof(struct sos_time)); |
| | } |
| | |
| | /* Prepare the action data structure */ |
| | act->routine = routine; |
| | act->routine_data = routine_data; |
| | |
| | /* Find the right place in the list of the timeout action. */ |
| | insert_before = NULL; |
| | list_foreach_forward_named(timeout_action_list, |
| | other, nb_act, |
| | tmo_prev, tmo_next) |
| | { |
| | if (sos_time_cmp(& act->timeout, & other->timeout) < 0) |
| | { |
| | insert_before = other; |
| | break; |
| | } |
| | |
| | /* Loop over to next timeout */ |
| | } |
| | |
| | /* Now insert the action in the list */ |
| | if (insert_before != NULL) |
| | list_insert_before_named(timeout_action_list, insert_before, act, |
| | tmo_prev, tmo_next); |
| | else |
| | list_add_tail_named(timeout_action_list, act, |
| | tmo_prev, tmo_next); |
| | |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t |
| | sos_time_register_action_relative(struct sos_timeout_action *act, |
| | const struct sos_time *delay, |
| | sos_timeout_routine_t *routine, |
| | void *routine_data) |
| | { |
| | sos_ui32_t flags; |
| | sos_ret_t retval; |
| | |
| | sos_disable_IRQs(flags); |
| | retval = _add_action(act, delay, TRUE, routine, routine_data); |
| | sos_restore_IRQs(flags); |
| | |
| | return retval; |
| | } |
| | |
| | |
| | sos_ret_t |
| | sos_time_register_action_absolute(struct sos_timeout_action *act, |
| | const struct sos_time *date, |
| | sos_timeout_routine_t *routine, |
| | void *routine_data) |
| | { |
| | sos_ui32_t flags; |
| | sos_ret_t retval; |
| | |
| | sos_disable_IRQs(flags); |
| | retval = _add_action(act, date, FALSE, routine, routine_data); |
| | sos_restore_IRQs(flags); |
| | |
| | return retval; |
| | } |
| | |
| | |
| | /** |
| | * Helper routine to remove the action from the list. MUST be called |
| | * with interrupts disabled ! |
| | */ |
| | static sos_ret_t _remove_action(struct sos_timeout_action *act) |
| | { |
| | /* Don't do anything if action is not in timeout list */ |
| | if (NULL == act->tmo_next) |
| | return -SOS_EINVAL; |
| | |
| | /* Update the action's remaining timeout */ |
| | if (sos_time_cmp(& act->timeout, & last_tick_time) <= 0) |
| | act->timeout = (struct sos_time){ .sec=0, .nanosec=0 }; |
| | else |
| | sos_time_dec(& act->timeout, & last_tick_time); |
| | |
| | /* Actually remove the action from the list */ |
| | list_delete_named(timeout_action_list, act, |
| | tmo_prev, tmo_next); |
| | act->tmo_prev = act->tmo_next = NULL; |
| | |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t sos_time_unregister_action(struct sos_timeout_action *act) |
| | { |
| | sos_ret_t retval; |
| | sos_ui32_t flags; |
| | |
| | sos_disable_IRQs(flags); |
| | retval = _remove_action(act); |
| | sos_restore_IRQs(flags); |
| | |
| | return SOS_OK; |
| | } |
| | |
| | |
| | sos_ret_t sos_time_do_tick() |
| | { |
| | sos_ui32_t flags; |
| | |
| | sos_disable_IRQs(flags); |
| | |
| | /* Update kernel time */ |
| | sos_time_inc(& last_tick_time, & tick_resolution); |
| | |
| | while (! list_is_empty_named(timeout_action_list, tmo_prev, tmo_next)) |
| | { |
| | struct sos_timeout_action *act; |
| | act = list_get_head_named(timeout_action_list, tmo_prev, tmo_next); |
| | |
| | /* Did we go too far in the actions' list ? */ |
| | if (sos_time_cmp(& last_tick_time, & act->timeout) < 0) |
| | { |
| | /* Yes: No need to look further. */ |
| | break; |
| | } |
| | |
| | /* Remove the action from the list */ |
| | _remove_action(act); |
| | |
| | /* Call the action's routine */ |
| | act->routine(act); |
| | } |
| | |
| | sos_restore_IRQs(flags); |
| | return SOS_OK; |
| | } |
| | |