| /* Hardware ports. |
| Copyright (C) 1998-2021 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 <http://www.gnu.org/licenses/>. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include "hw-main.h" |
| #include "hw-base.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.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 == 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; |
| 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); |
| } |