/* Hardware event manager. Copyright (C) 1998, 2007 Free Software Foundation, Inc. Contributed by Cygnus Support. This file is part of GDB, the GNU debugger. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "hw-main.h" #include "hw-base.h" #include "sim-events.h" /* The hw-events object is implemented using sim-events */ struct hw_event { void *data; struct hw *me; hw_event_callback *callback; sim_event *real; struct hw_event_data *entry; }; struct hw_event_data { struct hw_event event; struct hw_event_data *next; }; void create_hw_event_data (struct hw *me) { if (me->events_of_hw != NULL) hw_abort (me, "stray events"); /* NOP */ } void delete_hw_event_data (struct hw *me) { /* Remove the scheduled event. */ while (me->events_of_hw) hw_event_queue_deschedule (me, &me->events_of_hw->event); } /* Pass the H/W event onto the real callback */ static void bounce_hw_event (SIM_DESC sd, void *data) { /* save the data */ struct hw_event_data *entry = (struct hw_event_data *) data; struct hw *me = entry->event.me; void *event_data = entry->event.data; hw_event_callback *callback = entry->event.callback; struct hw_event_data **prev = &me->events_of_hw; while ((*prev) != entry) prev = &(*prev)->next; (*prev) = entry->next; hw_free (me, entry); callback (me, event_data); /* may not return */ } /* Map onto the event functions */ struct hw_event * hw_event_queue_schedule (struct hw *me, signed64 delta_time, hw_event_callback *callback, void *data) { struct hw_event *event; va_list dummy; memset (&dummy, 0, sizeof dummy); event = hw_event_queue_schedule_vtracef (me, delta_time, callback, data, NULL, dummy); return event; } struct hw_event * hw_event_queue_schedule_tracef (struct hw *me, signed64 delta_time, hw_event_callback *callback, void *data, const char *fmt, ...) { struct hw_event *event; va_list ap; va_start (ap, fmt); event = hw_event_queue_schedule_vtracef (me, delta_time, callback, data, fmt, ap); va_end (ap); return event; } struct hw_event * hw_event_queue_schedule_vtracef (struct hw *me, signed64 delta_time, hw_event_callback *callback, void *data, const char *fmt, va_list ap) { struct hw_event_data *entry = HW_ZALLOC (me, struct hw_event_data); entry->next = me->events_of_hw; me->events_of_hw = entry; /* fill it in */ entry->event.entry = entry; entry->event.data = data; entry->event.callback = callback; entry->event.me = me; entry->event.real = sim_events_schedule_vtracef (hw_system (me), delta_time, bounce_hw_event, entry, fmt, ap); return &entry->event; } void hw_event_queue_deschedule (struct hw *me, struct hw_event *event_to_remove) { /* ZAP the event but only if it is still in the event queue. Note that event_to_remove is only de-referenced after its validity has been confirmed. */ struct hw_event_data **prev; for (prev = &me->events_of_hw; (*prev) != NULL; prev = &(*prev)->next) { struct hw_event_data *entry = (*prev); if (&entry->event == event_to_remove) { sim_events_deschedule (hw_system (me), entry->event.real); (*prev) = entry->next; hw_free (me, entry); return; } } } signed64 hw_event_queue_time (struct hw *me) { return sim_events_time (hw_system (me)); } /* Returns the time that remains before the event is raised. */ signed64 hw_event_remain_time (struct hw *me, struct hw_event *event) { signed64 t; t = sim_events_remain_time (hw_system (me), event->real); return t; } /* Only worry about this compling on ANSI systems. Build with `make test-hw-events' in sim/ directory*/ #if defined (MAIN) #include "sim-main.h" #include #include static void test_handler (struct hw *me, void *data) { int *n = data; if (*n != hw_event_queue_time (me)) abort (); *n = -(*n); } int main (int argc, char **argv) { host_callback *cb = ZALLOC (host_callback); struct sim_state *sd = sim_state_alloc (0, cb); struct hw *me = ZALLOC (struct hw); sim_pre_argv_init (sd, "test-hw-events"); sim_post_argv_init (sd); me->system_of_hw = sd; printf ("Create hw-event-data\n"); { create_hw_alloc_data (me); create_hw_event_data (me); delete_hw_event_data (me); delete_hw_alloc_data (me); } printf ("Create hw-events\n"); { struct hw_event *a; struct hw_event *b; struct hw_event *c; struct hw_event *d; create_hw_alloc_data (me); create_hw_event_data (me); a = hw_event_queue_schedule (me, 0, NULL, NULL); b = hw_event_queue_schedule (me, 1, NULL, NULL); c = hw_event_queue_schedule (me, 2, NULL, NULL); d = hw_event_queue_schedule (me, 1, NULL, NULL); hw_event_queue_deschedule (me, c); hw_event_queue_deschedule (me, b); hw_event_queue_deschedule (me, a); hw_event_queue_deschedule (me, d); c = HW_ZALLOC (me, struct hw_event); hw_event_queue_deschedule (me, b); /* OOPS! */ hw_free (me, c); delete_hw_event_data (me); delete_hw_alloc_data (me); } printf ("Schedule hw-events\n"); { struct hw_event **e; int *n; int i; int nr = 4; e = HW_NZALLOC (me, struct hw_event *, nr); n = HW_NZALLOC (me, int, nr); create_hw_alloc_data (me); create_hw_event_data (me); for (i = 0; i < nr; i++) { n[i] = i; e[i] = hw_event_queue_schedule (me, i, test_handler, &n[i]); } sim_events_preprocess (sd, 1, 1); for (i = 0; i < nr; i++) { if (sim_events_tick (sd)) sim_events_process (sd); } for (i = 0; i < nr; i++) { if (n[i] != -i) abort (); hw_event_queue_deschedule (me, e[i]); } hw_free (me, n); hw_free (me, e); delete_hw_event_data (me); delete_hw_alloc_data (me); } return 0; } #endif