1/* Hardware ports.
2   Copyright (C) 1998-2023 Free Software Foundation, Inc.
3   Contributed by Andrew Cagney and Cygnus Solutions.
4
5This file is part of GDB, the GNU debugger.
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20/* This must come before any other includes.  */
21#include "defs.h"
22
23#include <ctype.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "hw-main.h"
28#include "hw-base.h"
29
30struct hw_port_edge
31{
32  int my_port;
33  struct hw *dest;
34  int dest_port;
35  struct hw_port_edge *next;
36  object_disposition disposition;
37};
38
39struct hw_port_data
40{
41  hw_port_event_method *to_port_event;
42  const struct hw_port_descriptor *ports;
43  struct hw_port_edge *edges;
44};
45
46const struct hw_port_descriptor empty_hw_ports[] =
47{
48  { NULL, 0, 0, 0 },
49};
50
51static void
52panic_hw_port_event (struct hw *me,
53		     int my_port,
54		     struct hw *source,
55		     int source_port,
56		     int level)
57{
58  hw_abort (me, "no port method");
59}
60
61void
62create_hw_port_data (struct hw *me)
63{
64  me->ports_of_hw = HW_ZALLOC (me, struct hw_port_data);
65  set_hw_port_event (me, panic_hw_port_event);
66  set_hw_ports (me, empty_hw_ports);
67}
68
69void
70delete_hw_port_data (struct hw *me)
71{
72  hw_free (me, me->ports_of_hw);
73  me->ports_of_hw = NULL;
74}
75
76void
77set_hw_ports (struct hw *me,
78	      const struct hw_port_descriptor ports[])
79{
80  me->ports_of_hw->ports = ports;
81}
82
83void
84set_hw_port_event (struct hw *me,
85		   hw_port_event_method *port_event)
86{
87  me->ports_of_hw->to_port_event = port_event;
88}
89
90
91static void
92attach_hw_port_edge (struct hw *me,
93		     struct hw_port_edge **list,
94		     int my_port,
95		     struct hw *dest,
96		     int dest_port,
97		     object_disposition disposition)
98{
99  struct hw_port_edge *new_edge = HW_ZALLOC (me, struct hw_port_edge);
100  new_edge->my_port = my_port;
101  new_edge->dest = dest;
102  new_edge->dest_port = dest_port;
103  new_edge->next = *list;
104  new_edge->disposition = disposition;
105  *list = new_edge;
106}
107
108
109static void
110detach_hw_port_edge (struct hw *me,
111		     struct hw_port_edge **list,
112		     int my_port,
113		     struct hw *dest,
114		     int dest_port)
115{
116  while (*list != NULL)
117    {
118      struct hw_port_edge *old_edge = *list;
119      if (old_edge->dest == dest
120	  && old_edge->dest_port == dest_port
121	  && old_edge->my_port == my_port)
122	{
123	  if (old_edge->disposition == permenant_object)
124	    hw_abort (me, "attempt to delete permenant port edge");
125	  *list = old_edge->next;
126	  hw_free (me, old_edge);
127	  return;
128	}
129    }
130  hw_abort (me, "attempt to delete unattached port");
131}
132
133
134#if 0
135static void
136clean_hw_port_edges (struct hw_port_edge **list)
137{
138  while (*list != NULL)
139    {
140      struct hw_port_edge *old_edge = *list;
141      switch (old_edge->disposition)
142	{
143	case permenant_object:
144	  list = &old_edge->next;
145	  break;
146	case temporary_object:
147	  *list = old_edge->next;
148	  hw_free (me, old_edge);
149	  break;
150	}
151    }
152}
153#endif
154
155
156/* Ports: */
157
158void
159hw_port_event (struct hw *me,
160	       int my_port,
161	       int level)
162{
163  int found_an_edge = 0;
164  struct hw_port_edge *edge;
165  /* device's lines directly connected */
166  for (edge = me->ports_of_hw->edges;
167       edge != NULL;
168       edge = edge->next)
169    {
170      if (edge->my_port == my_port)
171	{
172	  edge->dest->ports_of_hw->to_port_event (edge->dest,
173						  edge->dest_port,
174						  me,
175						  my_port,
176						  level);
177	  found_an_edge = 1;
178	}
179    }
180  if (!found_an_edge)
181    hw_abort (me, "No edge for port %d", my_port);
182}
183
184
185void
186hw_port_attach (struct hw *me,
187		int my_port,
188		struct hw *dest,
189		int dest_port,
190		object_disposition disposition)
191{
192  attach_hw_port_edge (me,
193		       &me->ports_of_hw->edges,
194		       my_port,
195		       dest,
196		       dest_port,
197		       disposition);
198}
199
200
201void
202hw_port_detach (struct hw *me,
203		int my_port,
204		struct hw *dest,
205		int dest_port)
206{
207  detach_hw_port_edge (me,
208		       &me->ports_of_hw->edges,
209		       my_port,
210		       dest,
211		       dest_port);
212}
213
214
215void
216hw_port_traverse (struct hw *me,
217		  hw_port_traverse_function *handler,
218		  void *data)
219{
220  struct hw_port_edge *port_edge;
221  for (port_edge = me->ports_of_hw->edges;
222       port_edge != NULL;
223       port_edge = port_edge->next)
224    {
225      handler (me, port_edge->my_port,
226	       port_edge->dest, port_edge->dest_port,
227	       data);
228    }
229}
230
231
232int
233hw_port_decode (struct hw *me,
234		const char *port_name,
235		port_direction direction)
236{
237  if (port_name == NULL || port_name[0] == '\0')
238    return 0;
239  if (isdigit (port_name[0]))
240    {
241      return strtoul (port_name, NULL, 0);
242    }
243  else
244    {
245      const struct hw_port_descriptor *ports =
246	me->ports_of_hw->ports;
247      if (ports != NULL)
248	{
249	  while (ports->name != NULL)
250	    {
251	      if (ports->direction == bidirect_port
252		  || ports->direction == direction)
253		{
254		  if (ports->nr_ports > 0)
255		    {
256		      int len = strlen (ports->name);
257		      if (strncmp (port_name, ports->name, len) == 0)
258			{
259			  if (port_name[len] == '\0')
260			    return ports->number;
261			  else if (isdigit (port_name[len]))
262			    {
263			      int port = (ports->number
264					  + strtoul (&port_name[len], NULL, 0));
265			      if (port >= ports->number + ports->nr_ports)
266				hw_abort (me,
267					  "Port %s out of range",
268					  port_name);
269			      return port;
270			    }
271			}
272		    }
273		  else if (strcmp (port_name, ports->name) == 0)
274		    return ports->number;
275		}
276	      ports++;
277	    }
278	}
279    }
280  hw_abort (me, "Unrecognized port %s", port_name);
281  return 0;
282}
283
284
285int
286hw_port_encode (struct hw *me,
287		int port_number,
288		char *buf,
289		int sizeof_buf,
290		port_direction direction)
291{
292  const struct hw_port_descriptor *ports = NULL;
293  ports = me->ports_of_hw->ports;
294  if (ports != NULL) {
295    while (ports->name != NULL)
296      {
297	if (ports->direction == bidirect_port
298	    || ports->direction == direction)
299	  {
300	    if (ports->nr_ports > 0)
301	      {
302		if (port_number >= ports->number
303		    && port_number < ports->number + ports->nr_ports)
304		  {
305		    strcpy (buf, ports->name);
306		    sprintf (buf + strlen (buf), "%d", port_number - ports->number);
307		    if (strlen (buf) >= sizeof_buf)
308		      hw_abort (me, "hw_port_encode: buffer overflow");
309		    return strlen (buf);
310		  }
311	      }
312	    else
313	      {
314		if (ports->number == port_number)
315		  {
316		    if (strlen (ports->name) >= sizeof_buf)
317		      hw_abort (me, "hw_port_encode: buffer overflow");
318		    strcpy (buf, ports->name);
319		    return strlen (buf);
320		  }
321	      }
322	  }
323	ports++;
324      }
325  }
326  sprintf (buf, "%d", port_number);
327  if (strlen (buf) >= sizeof_buf)
328    hw_abort (me, "hw_port_encode: buffer overflow");
329  return strlen (buf);
330}
331