/* Hardware ports.
Copyright (C) 1998-2024 Free Software Foundation, Inc.
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
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 . */
/* This must come before any other includes. */
#include "defs.h"
#include
#include
#include
#include "hw-main.h"
#include "hw-base.h"
struct hw_port_edge
{
int my_port;
struct hw *dest;
int dest_port;
struct hw_port_edge *next;
object_disposition disposition;
};
struct hw_port_data
{
hw_port_event_method *to_port_event;
const struct hw_port_descriptor *ports;
struct hw_port_edge *edges;
};
const struct hw_port_descriptor empty_hw_ports[] =
{
{ NULL, 0, 0, 0 },
};
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 == permanent_object)
hw_abort (me, "attempt to delete permanent 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 permanent_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;
if (isdigit (port_name[0]))
{
return strtoul (port_name, NULL, 0);
}
else
{
const struct hw_port_descriptor *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)
{
int len = strlen (ports->name);
if (strncmp (port_name, ports->name, len) == 0)
{
if (port_name[len] == '\0')
return ports->number;
else if (isdigit (port_name[len]))
{
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++;
}
}
}
hw_abort (me, "Unrecognized port %s", port_name);
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);
sprintf (buf + strlen (buf), "%d", port_number - ports->number);
if (strlen (buf) >= sizeof_buf)
hw_abort (me, "hw_port_encode: buffer overflow");
return strlen (buf);
}
}
else
{
if (ports->number == port_number)
{
if (strlen (ports->name) >= sizeof_buf)
hw_abort (me, "hw_port_encode: buffer overflow");
strcpy (buf, ports->name);
return strlen (buf);
}
}
}
ports++;
}
}
sprintf (buf, "%d", port_number);
if (strlen (buf) >= sizeof_buf)
hw_abort (me, "hw_port_encode: buffer overflow");
return strlen (buf);
}