| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- /*
- * Copyright (C) 2000-2004 by ETH Zurich
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY ETH ZURICH AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ETH ZURICH
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- *
- */
- /*
- * unix_nutinit.c - init for unix emulation
- *
- * 2004.04.01 Matthias Ringwald <matthias.ringwald@inf.ethz.ch>
- *
- */
- #ifdef __CYGWIN__
- #include <sys/features.h>
- #endif
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <signal.h>
- #include <getopt.h>
- #include <termios.h>
- #include <cfg/os.h>
- #include <sys/types.h>
- #include <sys/event.h>
- #include <sys/device.h>
- #include <sys/osdebug.h>
- #include <sys/atom.h>
- #include <dev/irqreg.h>
- #include <dev/unix_devs.h>
- extern void NutAppMain(void *arg) NUT_NORETURN_FUNC;
- #ifndef NUT_THREAD_MAINSTACK
- #define NUT_THREAD_MAINSTACK 1024
- #endif
- #ifndef NUT_THREAD_IDLESTACK
- #define NUT_THREAD_IDLESTACK 1024
- #endif
- /* number of interrupts that can be outstanding before one is lost */
- #define MAX_IRQ_SLOTS 3
- /* type of raised interrupt (timer, usart, ...) */
- int interrupts_pending[MAX_IRQ_SLOTS];
- /* our IRQ signal */
- sigset_t irq_signal;
- /* our emulated interrupt enabled/disabled flag */
- uint16_t int_disabled;
- /* index to first, next free, and first unsignalled interrupt type */
- int irq_current = 0;
- int irq_slot = 0;
- int irq_sent = 0;
- /* interrupt thread, signalling Nut threads */
- static pthread_t interrupt_thread;
- /* mutex and condition variable used to signal that a new interrupt is pending */
- pthread_mutex_t pending_mutex;
- pthread_cond_t pending_cv;
- /* mutex and condition variable used to signal that interrupts have been re-enabled */
- pthread_mutex_t irq_mutex;
- pthread_cond_t irq_cv;
- /* interrupt handler routines */
- IRQ_HANDLER irq_handlers[IRQ_MAX];
- /* event queues to signal from non-Nut thread */
- HANDLE *irq_eventqueues[IRQ_MAX];
- /*!
- * \brief Register an interrupt handler.
- *
- * This function is typically called by device drivers.
- *
- * \param irq Interrupt number to be associated with this handler.
- * \param handler This routine will be called by Nut/OS, when the
- * specified interrupt occurs.
- * \param arg Argument to be passed to the interrupt handler.
- *
- * \return 0 on success, -1 otherwise.
- */
- int NutRegisterIrqHandler(uint8_t irq, void (*handler) (void *), void *arg)
- {
- if (irq >= IRQ_MAX)
- return -1;
- NutEnterCritical();
- irq_handlers[irq].ir_arg = arg;
- irq_handlers[irq].ir_handler = handler;
- NutExitCritical();
- return 0;
- }
- /*!
- * \brief Register NutEventPostAsync for next NutThreadYield
- *
- * Store responsible IRQ and queue to signal in list
- *
- * \param irq responsible IRQ
- * \param queue to signal
- *
- * this is added to allow an non-nut thread to post events without
- * introducing a race-condition
- *
- */
- void NutUnixIrqEventPostAsync(uint8_t irq, HANDLE * queue)
- {
- if (irq < IRQ_MAX)
- irq_eventqueues[irq] = queue;
- }
- /*!
- * \brief Handle interrupt triggered NutEventPostAsync
- * Check list of events to post and call NutEventPostAsync on them
- *
- * this is added to allow an non-nut thread to post events without
- * introducing a race-condition
- *
- */
- void NutUnixThreadYieldHook(void);
- void NutUnixThreadYieldHook()
- {
- uint8_t irq;
- for (irq = 0; irq < IRQ_MAX; irq++) {
- if (irq_eventqueues[irq] != 0) {
- // printf("NutUnixThreadYield posting event nr %d\n\r", irq);
- NutEventPostFromIrq(irq_eventqueues[irq]);
- irq_eventqueues[irq] = 0;
- }
- }
- }
- /*
- * Handles SIGINT
- *
- */
- static void NutUnixControlC(int);
- static void NutUnixControlC(int signal)
- {
- printf("CTRL-C! Abort application.\n\r");
- tcsetattr(fileno(stdout), TCSANOW, &emulation_options.saved_termios);
- exit(0);
- }
- /*
- * Signal handler for SIGUSR1
- * emulates interrupt hardware
- * serializes all interrupts and calls their corresponding handlers
- *
- * all IRQs are multiplexed through the same signal handler (using only SIGUSR1)
- * a global array is used to keep track of the interrupts that have been raised
- * further signals are block until interrupt handling has finished
- * thus, it may happen that an interrupt signal is silently ignored and never arrives here
- * the corresponding irq would never happen.
- * to avoid this, all interrupts marked in the interrupts_pending table are handled upon before
- * control is relinquished.
- * thus, an interrupt may be handled upon before it corresponding signal is received
- * and its "real" signal is processed - too early, so to say.
- * it doesn't matter, even if the signal is still received as the interrupts_pending table
- * will be empty.
- *
- */
- static void NutUnixInterruptScheduler(int);
- static void NutUnixInterruptScheduler(int signal)
- {
- int irq;
- // disable interrupts for interrupt processing
- pthread_sigmask(SIG_BLOCK, &irq_signal, 0);
- // call interrupt handler
- if (irq_current != irq_slot) {
- irq = interrupts_pending[irq_current];
- if (++irq_current >= MAX_IRQ_SLOTS)
- irq_current = 0;
- if (irq < IRQ_MAX) {
- if (irq_handlers[irq].ir_handler) {
- irq_handlers[irq].ir_handler(irq_handlers[irq].ir_arg);
- }
- }
- }
- // re-enable interrupts
- pthread_sigmask(SIG_UNBLOCK, &irq_signal, 0);
- }
- /*!
- * \brief Emulate a Nut hardware interrupt on Unix
- *
- * Add new interrupt to list of pending interrupts.
- *
- * \param irq IRQ raised
- *
- * This is a support function which just maintains the table
- * listing raised interrupts. It is called by the non-Nut thread
- * emulating the hardware (e.g. the timer).
- *
- * Sending the interrupt to the Nut threads is done by
- * NutInterruptEmulation().
- */
- extern uint32_t nut_ticks;
- void NutUnixRaiseInterrupt(int);
- void NutUnixRaiseInterrupt(int irq)
- {
- int r;
- // is there a slot available in our list of pending interrupts?
- // if so, let signal handler know the type of interrupt
- if ((irq_current == 0 && irq_slot != MAX_IRQ_SLOTS - 1) || (irq_current != 0 && irq_slot != irq_current - 1)) {
- // make sure we're the only one manipulating the IRQ table
- pthread_mutex_lock(&pending_mutex);
- interrupts_pending[irq_slot] = irq;
- if (++irq_slot >= MAX_IRQ_SLOTS) {
- irq_slot = 0;
- }
- #if 0
- if( (nut_ticks % 1000) == 0 )
- printf( "%u\n", nut_ticks );
- #endif
- pthread_mutex_unlock(&pending_mutex);
- // signal interrupt thread to interrupt Nut threads
- r = pthread_cond_signal(&pending_cv);
- }
- }
- /*!
- * \brief Send emulated interrupt signal to Nut threads
- *
- * Hardware devices, such as timer, usart, etc., raise interrupts
- * to get serviced by NutOS. This function does the same for
- * devices on Unix emulation.
- *
- * \param irq IRQ raised
- *
- * The Nut thread is interrupted using the SIGUSR1 signal.
- * The corresponding signal handler dispatches to the respective
- * interrupt handler. All IRQs are multiplexed through the same signal handler.
- *
- * Signalling the interrupt is done in a separate thread so that
- * the "calling/interrupting" thread can go back to emulate the hardware.
- * Otherwise, in case of disabled interrupts, it would be hanging and waiting
- * for interrupts to be re-enabled.
- *
- * This thread here does nothing but signal interrupts and can safely hang
- * it they are disabled (by NutEnterCritical, for example).
- *
- */
- void *NutInterruptEmulation(void *unused_arg)
- {
- // non-nut thread => not interested in SIGUSR1 (IRQ signals)
- pthread_sigmask(SIG_BLOCK, &irq_signal, 0);
- for (;;) {
- pthread_mutex_lock(&pending_mutex);
- while (irq_slot == irq_sent) {
- // instead of busy waiting, let interrupting thread wake us
- pthread_cond_wait(&pending_cv, &pending_mutex);
- }
- pthread_mutex_unlock(&pending_mutex);
- // interrupt pending ?
- if (irq_slot != irq_sent) {
- pthread_mutex_lock(&irq_mutex);
- // instead of busy waiting, let Nut thread wake us once interrupts have been re-enabled
- while (int_disabled == 1) {
- pthread_cond_wait(&irq_cv, &irq_mutex);
- }
- // signal NUT thread, same effect as hardware interrupt
- kill(-getpgrp(), SIGUSR1);
- irq_sent = irq_slot;
- pthread_mutex_unlock(&irq_mutex);
- }
- }
- return NULL;
- }
- /*
- * Init IRQ handling
- *
- */
- static void NutIRQInit(void);
- static void NutIRQInit()
- {
- int irq;
- /* enable interrupts */
- int_disabled = 0;
- // initialize interrupt type table
- irq_current = 0;
- irq_slot = 0;
- irq_sent = 0;
- // clear async event postings
- for (irq = 0; irq < IRQ_MAX; irq++)
- irq_eventqueues[irq] = 0;
- // define our IRQ signal
- sigemptyset(&irq_signal);
- sigaddset(&irq_signal, SIGUSR1);
- // the signal/IRQ handler
- signal(SIGUSR1, NutUnixInterruptScheduler);
- signal(SIGINT, NutUnixControlC); // catch SIGINT (abort) to restore terminal
- // synchronization tools
- pthread_mutex_init(&irq_mutex, NULL); // enable/disable interrupts
- irq = pthread_cond_init(&irq_cv, NULL);
- irq = pthread_mutex_init(&pending_mutex, NULL); // maintenance of internal pending interrupts table
- irq = pthread_cond_init(&pending_cv, NULL);
- // start interrupt emulation thread
- pthread_create(&interrupt_thread, NULL, NutInterruptEmulation, (void *) (void *) NULL);
- }
- /*!
- * \addtogroup xgNutArchUnixInit
- */
- /*@{*/
- /*!
- * \brief Emulated idle thread.
- *
- * After initializing the timers, the idle thread switches to priority 254
- * and enters an endless loop.
- */
- THREAD(NutIdle, arg)
- {
- /* Initialize system timers. */
- NutTimerInit();
- /* Create the main application thread. */
- NutThreadCreate("main", NutAppMain, 0, NUT_THREAD_MAINSTACK);
- // printf("main task created, idling now..\n");
- /*
- * Run in an idle loop at the lowest priority. We can still
- * do something useful here, like killing terminated threads
- * or putting the CPU into sleep mode.
- */
- NutThreadSetPriority(254);
- for (;;) {
- NutThreadYield();
- NutThreadDestroy();
- // sleep(); ... sleeping would be fine.
- }
- }
- /*!
- * \brief Nut/OS Initialization.
- *
- * Initializes the memory management and the thread system and starts
- * an idle thread, which in turn initializes the timer management.
- * Finally the application's main() function is called.
- */
- #undef main
- #define PSEUDO_RAM_SIZE 999999
- uint8_t PSEUDO_RAM[PSEUDO_RAM_SIZE];
- extern void NutThreadInit(void);
- extern NUTFILE *NUT_freopen(const char *name, const char *mode, NUTFILE * stream);
- extern NUTFILE *__iob[];
- int main(int argc, char *argv[])
- {
- tcgetattr(fileno(stdout), &emulation_options.saved_termios);
- /* get command line options */
- emulation_options_parse(argc, argv);
- /*
- * Register our Pseudo RAM
- */
- NutHeapAdd(PSEUDO_RAM, PSEUDO_RAM_SIZE);
- /* Read OS configuration from non-volatile memory. */
- NutLoadConfig();
- /*
- * set stdio
- */
- /*
- NutRegisterDevice(&devUart0, 0, 0);
- NUT_freopen("uart0", "w", __iob[1]);
- printf("OS Debug Mode, stdout opened in unix_nutinit.c\n");
- // NutTraceOs( stdout, 1);
- */
- /*
- * Init interrupt handling
- */
- NutIRQInit();
- /*
- * Init threading
- */
- NutThreadInit();
- /*
- * Create idle thread
- */
- NutThreadCreate("idle", NutIdle, 0, NUT_THREAD_IDLESTACK);
- return 0;
- }
- /*@}*/
|