nutinit.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2000-2004 by ETH Zurich
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY ETH ZURICH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ETH ZURICH
00021  *  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /* 
00035  * unix_nutinit.c - init for unix emulation
00036  *
00037  * 2004.04.01 Matthias Ringwald <matthias.ringwald@inf.ethz.ch>
00038  *
00039  */
00040 
00041 #ifdef __CYGWIN__
00042 #include <sys/features.h>
00043 #endif
00044 
00045 #include <pthread.h>
00046 #include <stdio.h>
00047 #include <stdlib.h>
00048 #include <unistd.h>
00049 #include <signal.h>
00050 #include <getopt.h>
00051 #include <termios.h>
00052 #include <cfg/os.h>
00053 
00054 #include <sys/types.h>
00055 #include <sys/event.h>
00056 #include <sys/device.h>
00057 #include <sys/osdebug.h>
00058 #include <sys/atom.h>
00059 #include <dev/irqreg.h>
00060 #include <dev/unix_devs.h>
00061 
00062 extern void NutAppMain(void *arg) __attribute__ ((noreturn));
00063 
00064 #ifndef NUT_THREAD_MAINSTACK
00065 #define NUT_THREAD_MAINSTACK    1024
00066 #endif
00067 
00068 #ifndef NUT_THREAD_IDLESTACK
00069 #define NUT_THREAD_IDLESTACK    1024
00070 #endif
00071 
00072 /* number of interrupts that can be outstanding before one is lost */
00073 #define MAX_IRQ_SLOTS 3
00074 
00075 /* type of raised interrupt (timer, usart, ...) */
00076 int interrupts_pending[MAX_IRQ_SLOTS];
00077 
00078 /* our IRQ signal */
00079 sigset_t irq_signal;
00080 
00081 /* our emulated interrupt enabled/disabled flag */
00082 uint16_t int_disabled;
00083 
00084 /* index to first, next free, and first unsignalled interrupt type */
00085 int irq_current = 0;
00086 int irq_slot = 0;
00087 int irq_sent = 0;
00088 
00089 /* interrupt thread, signalling Nut threads */
00090 static pthread_t interrupt_thread;
00091 
00092 /* mutex and condition variable used to signal that a new interrupt is pending */
00093 pthread_mutex_t pending_mutex;
00094 pthread_cond_t pending_cv;
00095 
00096 /* mutex and condition variable used to signal that interrupts have been re-enabled */
00097 pthread_mutex_t irq_mutex;
00098 pthread_cond_t irq_cv;
00099 
00100 /* interrupt handler routines */
00101 IRQ_HANDLER irq_handlers[IRQ_MAX];
00102 
00103 /* event queues to signal from non-Nut thread */
00104 HANDLE *irq_eventqueues[IRQ_MAX];
00105 
00106 
00119 int NutRegisterIrqHandler(uint8_t irq, void (*handler) (void *), void *arg)
00120 {
00121     if (irq >= IRQ_MAX)
00122         return -1;
00123 
00124     NutEnterCritical();
00125 
00126     irq_handlers[irq].ir_arg = arg;
00127     irq_handlers[irq].ir_handler = handler;
00128 
00129     NutExitCritical();
00130 
00131     return 0;
00132 }
00133 
00134 
00147 void NutUnixIrqEventPostAsync(uint8_t irq, HANDLE * queue)
00148 {
00149     if (irq < IRQ_MAX)
00150         irq_eventqueues[irq] = queue;
00151 }
00152 
00161 void NutUnixThreadYieldHook(void);
00162 void NutUnixThreadYieldHook()
00163 {
00164     uint8_t irq;
00165     for (irq = 0; irq < IRQ_MAX; irq++) {
00166         if (irq_eventqueues[irq] != 0) {
00167             // printf("NutUnixThreadYield posting event nr %d\n\r", irq);
00168             NutEventPostFromIrq(irq_eventqueues[irq]);
00169             irq_eventqueues[irq] = 0;
00170         }
00171     }
00172 }
00173 
00174 
00175 /*
00176  * Handles SIGINT
00177  *
00178  */
00179 static void NutUnixControlC(int);
00180 static void NutUnixControlC(int signal)
00181 {
00182     printf("CTRL-C! Abort application.\n\r");
00183     tcsetattr(fileno(stdout), TCSANOW, &emulation_options.saved_termios);
00184     exit(0);
00185 }
00186 
00187 /*
00188  * Signal handler for SIGUSR1
00189  * emulates interrupt hardware
00190  * serializes all interrupts and calls their corresponding handlers
00191  * 
00192  * all IRQs are multiplexed through the same signal handler (using only SIGUSR1)
00193  * a global array is used to keep track of the interrupts that have been raised
00194  * further signals are block until interrupt handling has finished
00195  * thus, it may happen that an interrupt signal is silently ignored and never arrives here
00196  * the corresponding irq would never happen.
00197  * to avoid this, all interrupts marked in the interrupts_pending table are handled upon before
00198  * control is relinquished.
00199  * thus, an interrupt may be handled upon before it corresponding signal is received
00200  * and its "real" signal is processed - too early, so to say.
00201  * it doesn't matter, even if the signal is still received as the interrupts_pending table
00202  * will be empty.
00203  * 
00204  */
00205 
00206 static void NutUnixInterruptScheduler(int);
00207 static void NutUnixInterruptScheduler(int signal)
00208 {
00209     int irq;
00210 
00211     // disable interrupts for interrupt processing
00212     pthread_sigmask(SIG_BLOCK, &irq_signal, 0);
00213 
00214     // call interrupt handler
00215     if (irq_current != irq_slot) {
00216         irq = interrupts_pending[irq_current];
00217         if (++irq_current >= MAX_IRQ_SLOTS)
00218             irq_current = 0;
00219         if (irq < IRQ_MAX) {
00220             if (irq_handlers[irq].ir_handler) {
00221                 irq_handlers[irq].ir_handler(irq_handlers[irq].ir_arg);
00222             }
00223         }
00224     }
00225     // re-enable interrupts
00226     pthread_sigmask(SIG_UNBLOCK, &irq_signal, 0);
00227 }
00228 
00229 
00244 extern uint32_t nut_ticks;
00245 void NutUnixRaiseInterrupt(int);
00246 void NutUnixRaiseInterrupt(int irq)
00247 {
00248     int r;
00249 
00250 
00251     // is there a slot available in our list of pending interrupts?
00252     // if so, let signal handler know the type of interrupt
00253     if ((irq_current == 0 && irq_slot != MAX_IRQ_SLOTS - 1) || (irq_current != 0 && irq_slot != irq_current - 1)) {
00254         // make sure we're the only one manipulating the IRQ table
00255         pthread_mutex_lock(&pending_mutex);
00256 
00257         interrupts_pending[irq_slot] = irq;
00258         if (++irq_slot >= MAX_IRQ_SLOTS) {
00259             irq_slot = 0;
00260         }
00261 #if 0
00262         if( (nut_ticks % 1000) == 0 )
00263             printf( "%u\n", nut_ticks );
00264 #endif
00265         pthread_mutex_unlock(&pending_mutex);
00266 
00267         // signal interrupt thread to interrupt Nut threads
00268         r = pthread_cond_signal(&pending_cv);
00269     }
00270 }
00271 
00272 
00295 void *NutInterruptEmulation(void *unused_arg)
00296 {
00297     // non-nut thread => not interested in SIGUSR1 (IRQ signals)
00298     pthread_sigmask(SIG_BLOCK, &irq_signal, 0);
00299 
00300     for (;;) {
00301         pthread_mutex_lock(&pending_mutex);
00302         while (irq_slot == irq_sent) {
00303             // instead of busy waiting, let interrupting thread wake us
00304             pthread_cond_wait(&pending_cv, &pending_mutex);
00305         }
00306         pthread_mutex_unlock(&pending_mutex);
00307 
00308         // interrupt pending ?
00309         if (irq_slot != irq_sent) {
00310             pthread_mutex_lock(&irq_mutex);
00311             // instead of busy waiting, let Nut thread wake us once interrupts have been re-enabled
00312             while (int_disabled == 1) {
00313                 pthread_cond_wait(&irq_cv, &irq_mutex);
00314             }
00315             // signal NUT thread, same effect as hardware interrupt
00316             kill(-getpgrp(), SIGUSR1);
00317             irq_sent = irq_slot;
00318             pthread_mutex_unlock(&irq_mutex);
00319         }
00320     }
00321     return NULL;
00322 }
00323 
00324 /*
00325  * Init IRQ handling
00326  *
00327  */
00328 
00329 static void NutIRQInit(void);
00330 static void NutIRQInit()
00331 {
00332     int irq;
00333 
00334     /* enable interrupts */
00335     int_disabled = 0;
00336 
00337     // initialize interrupt type table
00338     irq_current = 0;
00339     irq_slot = 0;
00340     irq_sent = 0;
00341 
00342     // clear async event postings
00343     for (irq = 0; irq < IRQ_MAX; irq++)
00344         irq_eventqueues[irq] = 0;
00345 
00346     // define our IRQ signal
00347     sigemptyset(&irq_signal);
00348     sigaddset(&irq_signal, SIGUSR1);
00349 
00350     // the signal/IRQ handler
00351     signal(SIGUSR1, NutUnixInterruptScheduler);
00352     signal(SIGINT, NutUnixControlC);    // catch SIGINT (abort) to restore terminal
00353 
00354     // synchronization tools
00355     pthread_mutex_init(&irq_mutex, NULL);       // enable/disable interrupts
00356     irq = pthread_cond_init(&irq_cv, NULL);
00357 
00358     irq = pthread_mutex_init(&pending_mutex, NULL);     // maintenance of internal pending interrupts table
00359     irq = pthread_cond_init(&pending_cv, NULL);
00360 
00361     // start interrupt emulation thread
00362     pthread_create(&interrupt_thread, NULL, NutInterruptEmulation, (void *) (void *) NULL);
00363 }
00364 
00369 
00376 THREAD(NutIdle, arg)
00377 {
00378     /* Initialize system timers. */
00379     NutTimerInit();
00380 
00381     /* Create the main application thread. */
00382     NutThreadCreate("main", NutAppMain, 0, NUT_THREAD_MAINSTACK);
00383 
00384     // printf("main task created, idling now..\n");
00385     /*
00386      * Run in an idle loop at the lowest priority. We can still
00387      * do something useful here, like killing terminated threads
00388      * or putting the CPU into sleep mode.
00389      */
00390     NutThreadSetPriority(254);
00391     for (;;) {
00392         NutThreadYield();
00393         NutThreadDestroy();
00394 
00395         // sleep(); ... sleeping would be fine.
00396     }
00397 }
00398 
00406 #undef main
00407 
00408 #define PSEUDO_RAM_SIZE 999999
00409 uint8_t PSEUDO_RAM[PSEUDO_RAM_SIZE];
00410 
00411 extern void NutThreadInit(void);
00412 
00413 extern NUTFILE *NUT_freopen(CONST char *name, CONST char *mode, NUTFILE * stream);
00414 extern NUTFILE *__iob[];
00415 
00416 int main(int argc, char *argv[])
00417 {
00418     tcgetattr(fileno(stdout), &emulation_options.saved_termios);
00419 
00420     /* get command line options */
00421     emulation_options_parse(argc, argv);
00422 
00423     /*
00424      * Register our Pseudo RAM
00425      */
00426     NutHeapAdd(PSEUDO_RAM, PSEUDO_RAM_SIZE);
00427 
00428     /* Read OS configuration from non-volatile memory. */
00429     NutLoadConfig();
00430 
00431     /*
00432      * set stdio
00433      */
00434 
00435     /*
00436        NutRegisterDevice(&devUart0, 0, 0);
00437        NUT_freopen("uart0", "w", __iob[1]);
00438        printf("OS Debug Mode, stdout opened in unix_nutinit.c\n");
00439        // NutTraceOs( stdout, 1);
00440      */
00441 
00442     /*
00443      * Init interrupt handling
00444      */
00445     NutIRQInit();
00446 
00447     /*
00448      * Init threading
00449      */
00450     NutThreadInit();
00451 
00452     /*
00453      * Create idle thread
00454      */
00455     NutThreadCreate("idle", NutIdle, 0, NUT_THREAD_IDLESTACK);
00456 
00457     return 0;
00458 }
00459 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/