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 /* our IRQ signal */
00065 sigset_t irq_signal;
00066 
00067 /* our emulated interrupt enabled/disabled flag */
00068 uint16_t int_disabled;
00069 
00070 /* number of interrupts that can be outstanding before one is lost */
00071 #define MAX_IRQ_SLOTS 3
00072 
00073 /* type of raised interrupt (timer, usart, ...) */
00074 int interrupts_pending[MAX_IRQ_SLOTS];
00075 
00076 /* index to first, next free, and first unsignalled interrupt type */
00077 int irq_current = 0;
00078 int irq_slot = 0;
00079 int irq_sent = 0;
00080 
00081 /* interrupt thread, signalling Nut threads */
00082 static pthread_t interrupt_thread;
00083 
00084 /* mutex and condition variable used to signal that a new interrupt is pending */
00085 pthread_mutex_t pending_mutex;
00086 pthread_cond_t pending_cv;
00087 
00088 /* mutex and condition variable used to signal that interrupts have been re-enabled */
00089 pthread_mutex_t irq_mutex;
00090 pthread_cond_t irq_cv;
00091 
00092 /* interrupt handler routines */
00093 IRQ_HANDLER irq_handlers[IRQ_MAX];
00094 
00095 /* event queues to signal from non-Nut thread */
00096 HANDLE *irq_eventqueues[IRQ_MAX];
00097 
00098 
00111 int NutRegisterIrqHandler(uint8_t irq, void (*handler) (void *), void *arg)
00112 {
00113     if (irq >= IRQ_MAX)
00114         return -1;
00115 
00116     NutEnterCritical();
00117 
00118     irq_handlers[irq].ir_arg = arg;
00119     irq_handlers[irq].ir_handler = handler;
00120 
00121     NutExitCritical();
00122 
00123     return 0;
00124 }
00125 
00126 
00139 void NutUnixIrqEventPostAsync(uint8_t irq, HANDLE * queue)
00140 {
00141     if (irq < IRQ_MAX)
00142         irq_eventqueues[irq] = queue;
00143 }
00144 
00153 void NutUnixThreadYieldHook(void);
00154 void NutUnixThreadYieldHook()
00155 {
00156     uint8_t irq;
00157     for (irq = 0; irq < IRQ_MAX; irq++) {
00158         if (irq_eventqueues[irq] != 0) {
00159             // printf("NutUnixThreadYield posting event nr %d\n\r", irq);
00160             NutEventPostFromIrq(irq_eventqueues[irq]);
00161             irq_eventqueues[irq] = 0;
00162         }
00163     }
00164 }
00165 
00166 
00167 /*
00168  * Handles SIGINT
00169  *
00170  */
00171 static void NutUnixControlC(int);
00172 static void NutUnixControlC(int signal)
00173 {
00174     printf("CTRL-C! Abort application.\n\r");
00175     tcsetattr(fileno(stdout), TCSANOW, &emulation_options.saved_termios);
00176     exit(0);
00177 }
00178 
00179 /*
00180  * Signal handler for SIGUSR1
00181  * emulates interrupt hardware
00182  * serializes all interrupts and calls their corresponding handlers
00183  * 
00184  * all IRQs are multiplexed through the same signal handler (using only SIGUSR1)
00185  * a global array is used to keep track of the interrupts that have been raised
00186  * further signals are block until interrupt handling has finished
00187  * thus, it may happen that an interrupt signal is silently ignored and never arrives here
00188  * the corresponding irq would never happen.
00189  * to avoid this, all interrupts marked in the interrupts_pending table are handled upon before
00190  * control is relinquished.
00191  * thus, an interrupt may be handled upon before it corresponding signal is received
00192  * and its "real" signal is processed - too early, so to say.
00193  * it doesn't matter, even if the signal is still received as the interrupts_pending table
00194  * will be empty.
00195  * 
00196  */
00197 
00198 static void NutUnixInterruptScheduler(int);
00199 static void NutUnixInterruptScheduler(int signal)
00200 {
00201     int irq;
00202 
00203     // disable interrupts for interrupt processing
00204     pthread_sigmask(SIG_BLOCK, &irq_signal, 0);
00205 
00206     // call interrupt handler
00207     if (irq_current != irq_slot) {
00208         irq = interrupts_pending[irq_current];
00209         if (++irq_current >= MAX_IRQ_SLOTS)
00210             irq_current = 0;
00211         if (irq < IRQ_MAX) {
00212             if (irq_handlers[irq].ir_handler) {
00213                 irq_handlers[irq].ir_handler(irq_handlers[irq].ir_arg);
00214             }
00215         }
00216     }
00217     // re-enable interrupts
00218     pthread_sigmask(SIG_UNBLOCK, &irq_signal, 0);
00219 }
00220 
00221 
00236 extern uint32_t nut_ticks;
00237 void NutUnixRaiseInterrupt(int);
00238 void NutUnixRaiseInterrupt(int irq)
00239 {
00240     int r;
00241 
00242 
00243     // is there a slot available in our list of pending interrupts?
00244     // if so, let signal handler know the type of interrupt
00245     if ((irq_current == 0 && irq_slot != MAX_IRQ_SLOTS - 1) || (irq_current != 0 && irq_slot != irq_current - 1)) {
00246         // make sure we're the only one manipulating the IRQ table
00247         pthread_mutex_lock(&pending_mutex);
00248 
00249         interrupts_pending[irq_slot] = irq;
00250         if (++irq_slot >= MAX_IRQ_SLOTS) {
00251             irq_slot = 0;
00252         }
00253 #if 0
00254         if( (nut_ticks % 1000) == 0 )
00255             printf( "%u\n", nut_ticks );
00256 #endif
00257         pthread_mutex_unlock(&pending_mutex);
00258 
00259         // signal interrupt thread to interrupt Nut threads
00260         r = pthread_cond_signal(&pending_cv);
00261     }
00262 }
00263 
00264 
00287 void *NutInterruptEmulation(void *) __attribute__ ((noreturn));
00288 void *NutInterruptEmulation(void *unused_arg)
00289 {
00290     // non-nut thread => not interested in SIGUSR1 (IRQ signals)
00291     pthread_sigmask(SIG_BLOCK, &irq_signal, 0);
00292 
00293     for (;;) {
00294         pthread_mutex_lock(&pending_mutex);
00295         while (irq_slot == irq_sent) {
00296             // instead of busy waiting, let interrupting thread wake us
00297             pthread_cond_wait(&pending_cv, &pending_mutex);
00298         }
00299         pthread_mutex_unlock(&pending_mutex);
00300 
00301         // interrupt pending ?
00302         if (irq_slot != irq_sent) {
00303             pthread_mutex_lock(&irq_mutex);
00304             // instead of busy waiting, let Nut thread wake us once interrupts have been re-enabled
00305             while (int_disabled == 1) {
00306                 pthread_cond_wait(&irq_cv, &irq_mutex);
00307             }
00308             // signal NUT thread, same effect as hardware interrupt
00309             kill(-getpgrp(), SIGUSR1);
00310             irq_sent = irq_slot;
00311             pthread_mutex_unlock(&irq_mutex);
00312         }
00313     }
00314 }
00315 
00316 /*
00317  * Init IRQ handling
00318  *
00319  */
00320 
00321 static void NutIRQInit(void);
00322 static void NutIRQInit()
00323 {
00324     int irq;
00325 
00326     /* enable interrupts */
00327     int_disabled = 0;
00328 
00329     // initialize interrupt type table
00330     irq_current = 0;
00331     irq_slot = 0;
00332     irq_sent = 0;
00333 
00334     // clear async event postings
00335     for (irq = 0; irq < IRQ_MAX; irq++)
00336         irq_eventqueues[irq] = 0;
00337 
00338     // define our IRQ signal
00339     sigemptyset(&irq_signal);
00340     sigaddset(&irq_signal, SIGUSR1);
00341 
00342     // the signal/IRQ handler
00343     signal(SIGUSR1, NutUnixInterruptScheduler);
00344     signal(SIGINT, NutUnixControlC);    // catch SIGINT (abort) to restore terminal
00345 
00346     // synchronization tools
00347     pthread_mutex_init(&irq_mutex, NULL);       // enable/disable interrupts
00348     irq = pthread_cond_init(&irq_cv, NULL);
00349 
00350     irq = pthread_mutex_init(&pending_mutex, NULL);     // maintenance of internal pending interrupts table
00351     irq = pthread_cond_init(&pending_cv, NULL);
00352 
00353     // start interrupt emulation thread
00354     pthread_create(&interrupt_thread, NULL, NutInterruptEmulation, (void *) (void *) NULL);
00355 }
00356 
00361 
00368 THREAD(NutIdle, arg)
00369 {
00370     /* Initialize system timers. */
00371     NutTimerInit();
00372 
00373     /* Create the main application thread. */
00374     NutThreadCreate("main", NutAppMain, 0, NUT_THREAD_MAINSTACK);
00375 
00376     // printf("main task created, idling now..\n");
00377     /*
00378      * Run in an idle loop at the lowest priority. We can still
00379      * do something useful here, like killing terminated threads
00380      * or putting the CPU into sleep mode.
00381      */
00382     NutThreadSetPriority(254);
00383     for (;;) {
00384         NutThreadYield();
00385         NutThreadDestroy();
00386 
00387         // sleep(); ... sleeping would be fine.
00388     }
00389 }
00390 
00398 #undef main
00399 
00400 #define PSEUDO_RAM_SIZE 999999
00401 uint8_t PSEUDO_RAM[PSEUDO_RAM_SIZE];
00402 
00403 extern void NutThreadInit(void);
00404 
00405 extern NUTFILE *NUT_freopen(CONST char *name, CONST char *mode, NUTFILE * stream);
00406 extern NUTFILE *__iob[];
00407 
00408 int main(int argc, char *argv[])
00409 {
00410     tcgetattr(fileno(stdout), &emulation_options.saved_termios);
00411 
00412     /* get command line options */
00413     emulation_options_parse(argc, argv);
00414 
00415     /*
00416      * Register our Pseudo RAM
00417      */
00418     NutHeapAdd(PSEUDO_RAM, PSEUDO_RAM_SIZE);
00419 
00420     /* Read OS configuration from non-volatile memory. */
00421     NutLoadConfig();
00422 
00423     /*
00424      * set stdio
00425      */
00426 
00427     /*
00428        NutRegisterDevice(&devUart0, 0, 0);
00429        NUT_freopen("uart0", "w", __iob[1]);
00430        printf("OS Debug Mode, stdout opened in unix_nutinit.c\n");
00431        // NutTraceOs( stdout, 1);
00432      */
00433 
00434     /*
00435      * Init interrupt handling
00436      */
00437     NutIRQInit();
00438 
00439     /*
00440      * Init threading
00441      */
00442     NutThreadInit();
00443 
00444     /*
00445      * Create idle thread
00446      */
00447     NutThreadCreate("idle", NutIdle, 0, NUT_THREAD_IDLESTACK);
00448 
00449     return 0;
00450 }
00451 

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