/* Hardware event manager.
Copyright (C) 1998-2019 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
#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/<cpu> directory*/
#if defined (MAIN)
#include "sim-main.h"
#include <string.h>
#include <stdio.h>
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