+/* lock emulated IRQ and return the previous state */
+uint8_t hostsim_irq_save(void)
+{
+ uint8_t old = irq_flags;
+ hostsim_cli();
+ return old;
+}
+
+/* returns a monotonic clock in nanoseconds */
+static inline uint64_t get_clk(void)
+{
+ struct timespec ts;
+ uint64_t t;
+
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) < 0) {
+ printf("clock_getres() failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ t = ts.tv_sec;
+ t *= 1000000000;
+ t += ts.tv_nsec;
+ return t;
+}
+
+static void handle_interrupts(void)
+{
+ struct hostsim_ittimer *tim;
+ uint64_t clk;
+ uint64_t cur, next;
+ int c;
+ unsigned head;
+
+ clk = get_clk();
+
+ /* do the uart events if data is available */
+ if (irq_flags == IRQ_ALLOWED) {
+ while ((int)(out_tail - out_head) > 0) {
+ irq_flags = IRQ_LOCKED;
+ head = out_head;
+ c = out_buf[out_head & RG_MASK];
+ if (__sync_val_compare_and_swap(&out_head,
+ head, head + 1) != head)
+ continue;
+ uart_host_tx_event(c);
+ irq_flags = IRQ_ALLOWED;
+ }
+ while ((int)(in_tail - in_head) > 0) {
+ irq_flags = IRQ_LOCKED;
+ head = in_head;
+ c = in_buf[in_head & RG_MASK];
+ uart_host_rx_event(c);
+ if (__sync_val_compare_and_swap(&in_head,
+ head, head + 1) != head)
+ continue;
+ irq_flags = IRQ_ALLOWED;
+ }
+ }
+
+ /* browse all timers */
+ LIST_FOREACH(tim, &tim_list, next) {
+
+ /* Call the handler if it's time to. We use an atomic operation
+ * because we can be interrupted by our own signal any moment */
+ do {
+ cur = tim->prev_tick;
+ next = tim->prev_tick + tim->period_ns;
+
+ if (next > clk)
+ break;
+
+ if (__sync_val_compare_and_swap(&tim->prev_tick,
+ cur, next) != cur)
+ continue;
+
+ /* if irq are disabled, just mark the timer as pending,
+ * it will be executed from hostsim_irq_restore(). We
+ * may loose interrupts if they stay locked too long,
+ * like on the real hw */
+ if (irq_flags == IRQ_LOCKED)
+ tim->pending = 1;
+ else {
+ irq_flags = IRQ_LOCKED;
+ tim->pending = 0;
+ tim->handler();
+ irq_flags = IRQ_ALLOWED;
+ }
+ } while (1);
+
+ /* also execute the irq if it was pending */
+ if (irq_flags == IRQ_ALLOWED && tim->pending == 1) {
+ irq_flags = IRQ_LOCKED;
+ tim->pending = 0;
+ tim->handler();
+ irq_flags = IRQ_ALLOWED;
+ }
+ }
+}
+
+/* restore the state given as parameter */
+void hostsim_irq_restore(uint8_t flags)
+{
+ /* on transition "locked" -> "unlocked", call any pending interrupts
+ * before releasing IRQ lock. For other transitions, just set the
+ * irq_flags variable */
+
+ if (irq_flags == IRQ_ALLOWED || flags == IRQ_LOCKED) {
+ irq_flags = flags;
+ return;
+ }
+
+ irq_flags = IRQ_ALLOWED;
+ handle_interrupts();
+}
+
+/* return 1 if emulated IRQ are locked */
+uint8_t hostsim_irq_locked(void)
+{
+ return irq_flags == IRQ_LOCKED;