1999-04-16 03:35:26 +02:00
|
|
|
/* Hardware ports.
|
2020-01-01 07:20:01 +01:00
|
|
|
Copyright (C) 1998-2020 Free Software Foundation, Inc.
|
1999-04-16 03:35:26 +02:00
|
|
|
Contributed by Andrew Cagney and Cygnus Solutions.
|
|
|
|
|
|
|
|
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
|
2007-08-24 16:30:15 +02:00
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
1999-04-16 03:35:26 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2007-08-24 16:30:15 +02:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
1999-04-16 03:35:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
#include "hw-main.h"
|
|
|
|
#include "hw-base.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
|
|
#include <stdlib.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
#include <string.h>
|
|
|
|
#else
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
#include <strings.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
|
2011-04-02 07:30:16 +02:00
|
|
|
struct hw_port_edge
|
|
|
|
{
|
1999-04-16 03:35:26 +02:00
|
|
|
int my_port;
|
|
|
|
struct hw *dest;
|
|
|
|
int dest_port;
|
|
|
|
struct hw_port_edge *next;
|
|
|
|
object_disposition disposition;
|
|
|
|
};
|
|
|
|
|
2011-04-02 07:30:16 +02:00
|
|
|
struct hw_port_data
|
|
|
|
{
|
1999-04-16 03:35:26 +02:00
|
|
|
hw_port_event_method *to_port_event;
|
|
|
|
const struct hw_port_descriptor *ports;
|
|
|
|
struct hw_port_edge *edges;
|
|
|
|
};
|
|
|
|
|
2011-04-02 07:30:16 +02:00
|
|
|
const struct hw_port_descriptor empty_hw_ports[] =
|
|
|
|
{
|
2010-04-12 18:53:33 +02:00
|
|
|
{ NULL, 0, 0, 0 },
|
1999-04-16 03:35:26 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
panic_hw_port_event (struct hw *me,
|
|
|
|
int my_port,
|
|
|
|
struct hw *source,
|
|
|
|
int source_port,
|
|
|
|
int level)
|
|
|
|
{
|
|
|
|
hw_abort (me, "no port method");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
create_hw_port_data (struct hw *me)
|
|
|
|
{
|
|
|
|
me->ports_of_hw = HW_ZALLOC (me, struct hw_port_data);
|
|
|
|
set_hw_port_event (me, panic_hw_port_event);
|
|
|
|
set_hw_ports (me, empty_hw_ports);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
delete_hw_port_data (struct hw *me)
|
|
|
|
{
|
|
|
|
hw_free (me, me->ports_of_hw);
|
|
|
|
me->ports_of_hw = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
set_hw_ports (struct hw *me,
|
|
|
|
const struct hw_port_descriptor ports[])
|
|
|
|
{
|
|
|
|
me->ports_of_hw->ports = ports;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
set_hw_port_event (struct hw *me,
|
|
|
|
hw_port_event_method *port_event)
|
|
|
|
{
|
|
|
|
me->ports_of_hw->to_port_event = port_event;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
attach_hw_port_edge (struct hw *me,
|
|
|
|
struct hw_port_edge **list,
|
|
|
|
int my_port,
|
|
|
|
struct hw *dest,
|
|
|
|
int dest_port,
|
|
|
|
object_disposition disposition)
|
|
|
|
{
|
|
|
|
struct hw_port_edge *new_edge = HW_ZALLOC (me, struct hw_port_edge);
|
|
|
|
new_edge->my_port = my_port;
|
|
|
|
new_edge->dest = dest;
|
|
|
|
new_edge->dest_port = dest_port;
|
|
|
|
new_edge->next = *list;
|
|
|
|
new_edge->disposition = disposition;
|
|
|
|
*list = new_edge;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
detach_hw_port_edge (struct hw *me,
|
|
|
|
struct hw_port_edge **list,
|
|
|
|
int my_port,
|
|
|
|
struct hw *dest,
|
|
|
|
int dest_port)
|
|
|
|
{
|
|
|
|
while (*list != NULL)
|
|
|
|
{
|
|
|
|
struct hw_port_edge *old_edge = *list;
|
|
|
|
if (old_edge->dest == dest
|
|
|
|
&& old_edge->dest_port == dest_port
|
|
|
|
&& old_edge->my_port == my_port)
|
|
|
|
{
|
|
|
|
if (old_edge->disposition == permenant_object)
|
|
|
|
hw_abort (me, "attempt to delete permenant port edge");
|
|
|
|
*list = old_edge->next;
|
|
|
|
hw_free (me, old_edge);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hw_abort (me, "attempt to delete unattached port");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
static void
|
|
|
|
clean_hw_port_edges (struct hw_port_edge **list)
|
|
|
|
{
|
|
|
|
while (*list != NULL)
|
|
|
|
{
|
|
|
|
struct hw_port_edge *old_edge = *list;
|
|
|
|
switch (old_edge->disposition)
|
|
|
|
{
|
|
|
|
case permenant_object:
|
|
|
|
list = &old_edge->next;
|
|
|
|
break;
|
|
|
|
case temporary_object:
|
|
|
|
*list = old_edge->next;
|
|
|
|
hw_free (me, old_edge);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* Ports: */
|
|
|
|
|
|
|
|
void
|
|
|
|
hw_port_event (struct hw *me,
|
|
|
|
int my_port,
|
|
|
|
int level)
|
|
|
|
{
|
|
|
|
int found_an_edge = 0;
|
|
|
|
struct hw_port_edge *edge;
|
|
|
|
/* device's lines directly connected */
|
|
|
|
for (edge = me->ports_of_hw->edges;
|
|
|
|
edge != NULL;
|
|
|
|
edge = edge->next)
|
|
|
|
{
|
|
|
|
if (edge->my_port == my_port)
|
|
|
|
{
|
|
|
|
edge->dest->ports_of_hw->to_port_event (edge->dest,
|
|
|
|
edge->dest_port,
|
|
|
|
me,
|
|
|
|
my_port,
|
|
|
|
level);
|
|
|
|
found_an_edge = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found_an_edge)
|
|
|
|
hw_abort (me, "No edge for port %d", my_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
hw_port_attach (struct hw *me,
|
|
|
|
int my_port,
|
|
|
|
struct hw *dest,
|
|
|
|
int dest_port,
|
|
|
|
object_disposition disposition)
|
|
|
|
{
|
|
|
|
attach_hw_port_edge (me,
|
|
|
|
&me->ports_of_hw->edges,
|
|
|
|
my_port,
|
|
|
|
dest,
|
|
|
|
dest_port,
|
|
|
|
disposition);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
hw_port_detach (struct hw *me,
|
|
|
|
int my_port,
|
|
|
|
struct hw *dest,
|
|
|
|
int dest_port)
|
|
|
|
{
|
|
|
|
detach_hw_port_edge (me,
|
|
|
|
&me->ports_of_hw->edges,
|
|
|
|
my_port,
|
|
|
|
dest,
|
|
|
|
dest_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
hw_port_traverse (struct hw *me,
|
|
|
|
hw_port_traverse_function *handler,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct hw_port_edge *port_edge;
|
|
|
|
for (port_edge = me->ports_of_hw->edges;
|
|
|
|
port_edge != NULL;
|
|
|
|
port_edge = port_edge->next)
|
|
|
|
{
|
|
|
|
handler (me, port_edge->my_port,
|
|
|
|
port_edge->dest, port_edge->dest_port,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
hw_port_decode (struct hw *me,
|
|
|
|
const char *port_name,
|
|
|
|
port_direction direction)
|
|
|
|
{
|
|
|
|
if (port_name == NULL || port_name[0] == '\0')
|
|
|
|
return 0;
|
2011-05-11 22:02:42 +02:00
|
|
|
if (isdigit (port_name[0]))
|
1999-04-16 03:35:26 +02:00
|
|
|
{
|
|
|
|
return strtoul (port_name, NULL, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const struct hw_port_descriptor *ports =
|
|
|
|
me->ports_of_hw->ports;
|
2011-03-15 04:16:17 +01:00
|
|
|
if (ports != NULL)
|
1999-04-16 03:35:26 +02:00
|
|
|
{
|
|
|
|
while (ports->name != NULL)
|
|
|
|
{
|
|
|
|
if (ports->direction == bidirect_port
|
|
|
|
|| ports->direction == direction)
|
|
|
|
{
|
|
|
|
if (ports->nr_ports > 0)
|
|
|
|
{
|
|
|
|
int len = strlen (ports->name);
|
|
|
|
if (strncmp (port_name, ports->name, len) == 0)
|
|
|
|
{
|
|
|
|
if (port_name[len] == '\0')
|
|
|
|
return ports->number;
|
2011-05-11 22:02:42 +02:00
|
|
|
else if (isdigit (port_name[len]))
|
1999-04-16 03:35:26 +02:00
|
|
|
{
|
|
|
|
int port = (ports->number
|
|
|
|
+ strtoul (&port_name[len], NULL, 0));
|
|
|
|
if (port >= ports->number + ports->nr_ports)
|
|
|
|
hw_abort (me,
|
|
|
|
"Port %s out of range",
|
|
|
|
port_name);
|
|
|
|
return port;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (port_name, ports->name) == 0)
|
|
|
|
return ports->number;
|
|
|
|
}
|
|
|
|
ports++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-02 07:21:59 +02:00
|
|
|
hw_abort (me, "Unrecognized port %s", port_name);
|
1999-04-16 03:35:26 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
hw_port_encode (struct hw *me,
|
|
|
|
int port_number,
|
|
|
|
char *buf,
|
|
|
|
int sizeof_buf,
|
|
|
|
port_direction direction)
|
|
|
|
{
|
|
|
|
const struct hw_port_descriptor *ports = NULL;
|
|
|
|
ports = me->ports_of_hw->ports;
|
|
|
|
if (ports != NULL) {
|
|
|
|
while (ports->name != NULL)
|
|
|
|
{
|
|
|
|
if (ports->direction == bidirect_port
|
|
|
|
|| ports->direction == direction)
|
|
|
|
{
|
|
|
|
if (ports->nr_ports > 0)
|
|
|
|
{
|
|
|
|
if (port_number >= ports->number
|
|
|
|
&& port_number < ports->number + ports->nr_ports)
|
|
|
|
{
|
|
|
|
strcpy (buf, ports->name);
|
2011-05-11 22:02:42 +02:00
|
|
|
sprintf (buf + strlen (buf), "%d", port_number - ports->number);
|
1999-04-16 03:35:26 +02:00
|
|
|
if (strlen (buf) >= sizeof_buf)
|
|
|
|
hw_abort (me, "hw_port_encode: buffer overflow");
|
|
|
|
return strlen (buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (ports->number == port_number)
|
|
|
|
{
|
2011-05-11 22:02:42 +02:00
|
|
|
if (strlen (ports->name) >= sizeof_buf)
|
1999-04-16 03:35:26 +02:00
|
|
|
hw_abort (me, "hw_port_encode: buffer overflow");
|
2011-05-11 22:02:42 +02:00
|
|
|
strcpy (buf, ports->name);
|
|
|
|
return strlen (buf);
|
1999-04-16 03:35:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ports++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sprintf (buf, "%d", port_number);
|
2011-05-11 22:02:42 +02:00
|
|
|
if (strlen (buf) >= sizeof_buf)
|
1999-04-16 03:35:26 +02:00
|
|
|
hw_abort (me, "hw_port_encode: buffer overflow");
|
2011-05-11 22:02:42 +02:00
|
|
|
return strlen (buf);
|
1999-04-16 03:35:26 +02:00
|
|
|
}
|