nutinit.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. /*
  2. * Copyright (C) 2000-2004 by ETH Zurich
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY ETH ZURICH AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ETH ZURICH
  21. * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. *
  32. */
  33. /*
  34. * unix_nutinit.c - init for unix emulation
  35. *
  36. * 2004.04.01 Matthias Ringwald <matthias.ringwald@inf.ethz.ch>
  37. *
  38. */
  39. #ifdef __CYGWIN__
  40. #include <sys/features.h>
  41. #endif
  42. #include <pthread.h>
  43. #include <stdio.h>
  44. #include <stdlib.h>
  45. #include <unistd.h>
  46. #include <signal.h>
  47. #include <getopt.h>
  48. #include <termios.h>
  49. #include <cfg/os.h>
  50. #include <sys/types.h>
  51. #include <sys/event.h>
  52. #include <sys/device.h>
  53. #include <sys/osdebug.h>
  54. #include <sys/atom.h>
  55. #include <dev/irqreg.h>
  56. #include <dev/unix_devs.h>
  57. extern void NutAppMain(void *arg) NUT_NORETURN_FUNC;
  58. #ifndef NUT_THREAD_MAINSTACK
  59. #define NUT_THREAD_MAINSTACK 1024
  60. #endif
  61. #ifndef NUT_THREAD_IDLESTACK
  62. #define NUT_THREAD_IDLESTACK 1024
  63. #endif
  64. /* number of interrupts that can be outstanding before one is lost */
  65. #define MAX_IRQ_SLOTS 3
  66. /* type of raised interrupt (timer, usart, ...) */
  67. int interrupts_pending[MAX_IRQ_SLOTS];
  68. /* our IRQ signal */
  69. sigset_t irq_signal;
  70. /* our emulated interrupt enabled/disabled flag */
  71. uint16_t int_disabled;
  72. /* index to first, next free, and first unsignalled interrupt type */
  73. int irq_current = 0;
  74. int irq_slot = 0;
  75. int irq_sent = 0;
  76. /* interrupt thread, signalling Nut threads */
  77. static pthread_t interrupt_thread;
  78. /* mutex and condition variable used to signal that a new interrupt is pending */
  79. pthread_mutex_t pending_mutex;
  80. pthread_cond_t pending_cv;
  81. /* mutex and condition variable used to signal that interrupts have been re-enabled */
  82. pthread_mutex_t irq_mutex;
  83. pthread_cond_t irq_cv;
  84. /* interrupt handler routines */
  85. IRQ_HANDLER irq_handlers[IRQ_MAX];
  86. /* event queues to signal from non-Nut thread */
  87. HANDLE *irq_eventqueues[IRQ_MAX];
  88. /*!
  89. * \brief Register an interrupt handler.
  90. *
  91. * This function is typically called by device drivers.
  92. *
  93. * \param irq Interrupt number to be associated with this handler.
  94. * \param handler This routine will be called by Nut/OS, when the
  95. * specified interrupt occurs.
  96. * \param arg Argument to be passed to the interrupt handler.
  97. *
  98. * \return 0 on success, -1 otherwise.
  99. */
  100. int NutRegisterIrqHandler(uint8_t irq, void (*handler) (void *), void *arg)
  101. {
  102. if (irq >= IRQ_MAX)
  103. return -1;
  104. NutEnterCritical();
  105. irq_handlers[irq].ir_arg = arg;
  106. irq_handlers[irq].ir_handler = handler;
  107. NutExitCritical();
  108. return 0;
  109. }
  110. /*!
  111. * \brief Register NutEventPostAsync for next NutThreadYield
  112. *
  113. * Store responsible IRQ and queue to signal in list
  114. *
  115. * \param irq responsible IRQ
  116. * \param queue to signal
  117. *
  118. * this is added to allow an non-nut thread to post events without
  119. * introducing a race-condition
  120. *
  121. */
  122. void NutUnixIrqEventPostAsync(uint8_t irq, HANDLE * queue)
  123. {
  124. if (irq < IRQ_MAX)
  125. irq_eventqueues[irq] = queue;
  126. }
  127. /*!
  128. * \brief Handle interrupt triggered NutEventPostAsync
  129. * Check list of events to post and call NutEventPostAsync on them
  130. *
  131. * this is added to allow an non-nut thread to post events without
  132. * introducing a race-condition
  133. *
  134. */
  135. void NutUnixThreadYieldHook(void);
  136. void NutUnixThreadYieldHook()
  137. {
  138. uint8_t irq;
  139. for (irq = 0; irq < IRQ_MAX; irq++) {
  140. if (irq_eventqueues[irq] != 0) {
  141. // printf("NutUnixThreadYield posting event nr %d\n\r", irq);
  142. NutEventPostFromIrq(irq_eventqueues[irq]);
  143. irq_eventqueues[irq] = 0;
  144. }
  145. }
  146. }
  147. /*
  148. * Handles SIGINT
  149. *
  150. */
  151. static void NutUnixControlC(int);
  152. static void NutUnixControlC(int signal)
  153. {
  154. printf("CTRL-C! Abort application.\n\r");
  155. tcsetattr(fileno(stdout), TCSANOW, &emulation_options.saved_termios);
  156. exit(0);
  157. }
  158. /*
  159. * Signal handler for SIGUSR1
  160. * emulates interrupt hardware
  161. * serializes all interrupts and calls their corresponding handlers
  162. *
  163. * all IRQs are multiplexed through the same signal handler (using only SIGUSR1)
  164. * a global array is used to keep track of the interrupts that have been raised
  165. * further signals are block until interrupt handling has finished
  166. * thus, it may happen that an interrupt signal is silently ignored and never arrives here
  167. * the corresponding irq would never happen.
  168. * to avoid this, all interrupts marked in the interrupts_pending table are handled upon before
  169. * control is relinquished.
  170. * thus, an interrupt may be handled upon before it corresponding signal is received
  171. * and its "real" signal is processed - too early, so to say.
  172. * it doesn't matter, even if the signal is still received as the interrupts_pending table
  173. * will be empty.
  174. *
  175. */
  176. static void NutUnixInterruptScheduler(int);
  177. static void NutUnixInterruptScheduler(int signal)
  178. {
  179. int irq;
  180. // disable interrupts for interrupt processing
  181. pthread_sigmask(SIG_BLOCK, &irq_signal, 0);
  182. // call interrupt handler
  183. if (irq_current != irq_slot) {
  184. irq = interrupts_pending[irq_current];
  185. if (++irq_current >= MAX_IRQ_SLOTS)
  186. irq_current = 0;
  187. if (irq < IRQ_MAX) {
  188. if (irq_handlers[irq].ir_handler) {
  189. irq_handlers[irq].ir_handler(irq_handlers[irq].ir_arg);
  190. }
  191. }
  192. }
  193. // re-enable interrupts
  194. pthread_sigmask(SIG_UNBLOCK, &irq_signal, 0);
  195. }
  196. /*!
  197. * \brief Emulate a Nut hardware interrupt on Unix
  198. *
  199. * Add new interrupt to list of pending interrupts.
  200. *
  201. * \param irq IRQ raised
  202. *
  203. * This is a support function which just maintains the table
  204. * listing raised interrupts. It is called by the non-Nut thread
  205. * emulating the hardware (e.g. the timer).
  206. *
  207. * Sending the interrupt to the Nut threads is done by
  208. * NutInterruptEmulation().
  209. */
  210. extern uint32_t nut_ticks;
  211. void NutUnixRaiseInterrupt(int);
  212. void NutUnixRaiseInterrupt(int irq)
  213. {
  214. int r;
  215. // is there a slot available in our list of pending interrupts?
  216. // if so, let signal handler know the type of interrupt
  217. if ((irq_current == 0 && irq_slot != MAX_IRQ_SLOTS - 1) || (irq_current != 0 && irq_slot != irq_current - 1)) {
  218. // make sure we're the only one manipulating the IRQ table
  219. pthread_mutex_lock(&pending_mutex);
  220. interrupts_pending[irq_slot] = irq;
  221. if (++irq_slot >= MAX_IRQ_SLOTS) {
  222. irq_slot = 0;
  223. }
  224. #if 0
  225. if( (nut_ticks % 1000) == 0 )
  226. printf( "%u\n", nut_ticks );
  227. #endif
  228. pthread_mutex_unlock(&pending_mutex);
  229. // signal interrupt thread to interrupt Nut threads
  230. r = pthread_cond_signal(&pending_cv);
  231. }
  232. }
  233. /*!
  234. * \brief Send emulated interrupt signal to Nut threads
  235. *
  236. * Hardware devices, such as timer, usart, etc., raise interrupts
  237. * to get serviced by NutOS. This function does the same for
  238. * devices on Unix emulation.
  239. *
  240. * \param irq IRQ raised
  241. *
  242. * The Nut thread is interrupted using the SIGUSR1 signal.
  243. * The corresponding signal handler dispatches to the respective
  244. * interrupt handler. All IRQs are multiplexed through the same signal handler.
  245. *
  246. * Signalling the interrupt is done in a separate thread so that
  247. * the "calling/interrupting" thread can go back to emulate the hardware.
  248. * Otherwise, in case of disabled interrupts, it would be hanging and waiting
  249. * for interrupts to be re-enabled.
  250. *
  251. * This thread here does nothing but signal interrupts and can safely hang
  252. * it they are disabled (by NutEnterCritical, for example).
  253. *
  254. */
  255. void *NutInterruptEmulation(void *unused_arg)
  256. {
  257. // non-nut thread => not interested in SIGUSR1 (IRQ signals)
  258. pthread_sigmask(SIG_BLOCK, &irq_signal, 0);
  259. for (;;) {
  260. pthread_mutex_lock(&pending_mutex);
  261. while (irq_slot == irq_sent) {
  262. // instead of busy waiting, let interrupting thread wake us
  263. pthread_cond_wait(&pending_cv, &pending_mutex);
  264. }
  265. pthread_mutex_unlock(&pending_mutex);
  266. // interrupt pending ?
  267. if (irq_slot != irq_sent) {
  268. pthread_mutex_lock(&irq_mutex);
  269. // instead of busy waiting, let Nut thread wake us once interrupts have been re-enabled
  270. while (int_disabled == 1) {
  271. pthread_cond_wait(&irq_cv, &irq_mutex);
  272. }
  273. // signal NUT thread, same effect as hardware interrupt
  274. kill(-getpgrp(), SIGUSR1);
  275. irq_sent = irq_slot;
  276. pthread_mutex_unlock(&irq_mutex);
  277. }
  278. }
  279. return NULL;
  280. }
  281. /*
  282. * Init IRQ handling
  283. *
  284. */
  285. static void NutIRQInit(void);
  286. static void NutIRQInit()
  287. {
  288. int irq;
  289. /* enable interrupts */
  290. int_disabled = 0;
  291. // initialize interrupt type table
  292. irq_current = 0;
  293. irq_slot = 0;
  294. irq_sent = 0;
  295. // clear async event postings
  296. for (irq = 0; irq < IRQ_MAX; irq++)
  297. irq_eventqueues[irq] = 0;
  298. // define our IRQ signal
  299. sigemptyset(&irq_signal);
  300. sigaddset(&irq_signal, SIGUSR1);
  301. // the signal/IRQ handler
  302. signal(SIGUSR1, NutUnixInterruptScheduler);
  303. signal(SIGINT, NutUnixControlC); // catch SIGINT (abort) to restore terminal
  304. // synchronization tools
  305. pthread_mutex_init(&irq_mutex, NULL); // enable/disable interrupts
  306. irq = pthread_cond_init(&irq_cv, NULL);
  307. irq = pthread_mutex_init(&pending_mutex, NULL); // maintenance of internal pending interrupts table
  308. irq = pthread_cond_init(&pending_cv, NULL);
  309. // start interrupt emulation thread
  310. pthread_create(&interrupt_thread, NULL, NutInterruptEmulation, (void *) (void *) NULL);
  311. }
  312. /*!
  313. * \addtogroup xgNutArchUnixInit
  314. */
  315. /*@{*/
  316. /*!
  317. * \brief Emulated idle thread.
  318. *
  319. * After initializing the timers, the idle thread switches to priority 254
  320. * and enters an endless loop.
  321. */
  322. THREAD(NutIdle, arg)
  323. {
  324. /* Initialize system timers. */
  325. NutTimerInit();
  326. /* Create the main application thread. */
  327. NutThreadCreate("main", NutAppMain, 0, NUT_THREAD_MAINSTACK);
  328. // printf("main task created, idling now..\n");
  329. /*
  330. * Run in an idle loop at the lowest priority. We can still
  331. * do something useful here, like killing terminated threads
  332. * or putting the CPU into sleep mode.
  333. */
  334. NutThreadSetPriority(254);
  335. for (;;) {
  336. NutThreadYield();
  337. NutThreadDestroy();
  338. // sleep(); ... sleeping would be fine.
  339. }
  340. }
  341. /*!
  342. * \brief Nut/OS Initialization.
  343. *
  344. * Initializes the memory management and the thread system and starts
  345. * an idle thread, which in turn initializes the timer management.
  346. * Finally the application's main() function is called.
  347. */
  348. #undef main
  349. #define PSEUDO_RAM_SIZE 999999
  350. uint8_t PSEUDO_RAM[PSEUDO_RAM_SIZE];
  351. extern void NutThreadInit(void);
  352. extern NUTFILE *NUT_freopen(const char *name, const char *mode, NUTFILE * stream);
  353. extern NUTFILE *__iob[];
  354. int main(int argc, char *argv[])
  355. {
  356. tcgetattr(fileno(stdout), &emulation_options.saved_termios);
  357. /* get command line options */
  358. emulation_options_parse(argc, argv);
  359. /*
  360. * Register our Pseudo RAM
  361. */
  362. NutHeapAdd(PSEUDO_RAM, PSEUDO_RAM_SIZE);
  363. /* Read OS configuration from non-volatile memory. */
  364. NutLoadConfig();
  365. /*
  366. * set stdio
  367. */
  368. /*
  369. NutRegisterDevice(&devUart0, 0, 0);
  370. NUT_freopen("uart0", "w", __iob[1]);
  371. printf("OS Debug Mode, stdout opened in unix_nutinit.c\n");
  372. // NutTraceOs( stdout, 1);
  373. */
  374. /*
  375. * Init interrupt handling
  376. */
  377. NutIRQInit();
  378. /*
  379. * Init threading
  380. */
  381. NutThreadInit();
  382. /*
  383. * Create idle thread
  384. */
  385. NutThreadCreate("idle", NutIdle, 0, NUT_THREAD_IDLESTACK);
  386. return 0;
  387. }
  388. /*@}*/