Clean up create/delete of hw-ports
This commit is contained in:
parent
f675744718
commit
2f06c437e2
@ -1,3 +1,16 @@
|
||||
Mon May 25 16:55:16 1998 Andrew Cagney <cagney@b1.cygnus.com>
|
||||
|
||||
* hw-base.c (panic_hw_port_event, empty_hw_ports): Move from here.
|
||||
* hw-ports.c: To here.
|
||||
|
||||
* hw-base.h, hw-ports.c (create_hw_port_data,
|
||||
delete_hw_port_data): New functions.
|
||||
* hw-base.c (hw_delete, hw_create): Call same.
|
||||
|
||||
* hw-base.h (set_hw_ports, set_hw_port_event): Move set functions
|
||||
from here.
|
||||
* hw-ports.h: To here.
|
||||
|
||||
Mon May 25 16:42:48 1998 Andrew Cagney <cagney@b1.cygnus.com>
|
||||
|
||||
* hw-device.c (hw_ioctl), hw-device.h (hw_ioctl_callback): Drop
|
||||
|
@ -23,15 +23,6 @@
|
||||
#include "hw-base.h"
|
||||
|
||||
|
||||
/* LATER: #include "hwconfig.h" */
|
||||
extern const struct hw_device_descriptor dv_core_descriptor[];
|
||||
extern const struct hw_device_descriptor dv_pal_descriptor[];
|
||||
const struct hw_device_descriptor *hw_descriptors[] = {
|
||||
dv_core_descriptor,
|
||||
dv_pal_descriptor,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#else
|
||||
@ -46,6 +37,8 @@ const struct hw_device_descriptor *hw_descriptors[] = {
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "hw-config.h"
|
||||
|
||||
struct hw_base_data {
|
||||
int finished_p;
|
||||
const struct hw_device_descriptor *descriptor;
|
||||
@ -222,9 +215,7 @@ panic_hw_io_read_buffer (struct hw *me,
|
||||
void *dest,
|
||||
int space,
|
||||
unsigned_word addr,
|
||||
unsigned nr_bytes,
|
||||
sim_cpu *processor,
|
||||
sim_cia cia)
|
||||
unsigned nr_bytes)
|
||||
{
|
||||
hw_abort (me, "no io-read method");
|
||||
return 0;
|
||||
@ -235,9 +226,7 @@ panic_hw_io_write_buffer (struct hw *me,
|
||||
const void *source,
|
||||
int space,
|
||||
unsigned_word addr,
|
||||
unsigned nr_bytes,
|
||||
sim_cpu *processor,
|
||||
sim_cia cia)
|
||||
unsigned nr_bytes)
|
||||
{
|
||||
hw_abort (me, "no io-write method");
|
||||
return 0;
|
||||
@ -272,22 +261,6 @@ passthrough_hw_dma_write_buffer (struct hw *me,
|
||||
violate_read_only_section);
|
||||
}
|
||||
|
||||
const struct hw_port_descriptor empty_hw_ports[] = {
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
static void
|
||||
panic_hw_port_event (struct hw *me,
|
||||
int my_port,
|
||||
struct hw *source,
|
||||
int source_port,
|
||||
int level,
|
||||
sim_cpu *processor,
|
||||
sim_cia cia)
|
||||
{
|
||||
hw_abort (me, "no port method");
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_hw_delete (struct hw *me)
|
||||
{
|
||||
@ -342,12 +315,12 @@ full_name_of_hw (struct hw *leaf,
|
||||
|
||||
/* return it usefully */
|
||||
if (buf == full_name)
|
||||
buf = (char *) strdup (full_name);
|
||||
buf = hw_strdup (leaf, full_name);
|
||||
return buf;
|
||||
}
|
||||
|
||||
struct hw *
|
||||
hw_create (SIM_DESC sd,
|
||||
hw_create (struct sim_state *sd,
|
||||
struct hw *parent,
|
||||
const char *family,
|
||||
const char *name,
|
||||
@ -358,9 +331,9 @@ hw_create (SIM_DESC sd,
|
||||
struct hw *hw = ZALLOC (struct hw);
|
||||
|
||||
/* our identity */
|
||||
hw->family_of_hw = family;
|
||||
hw->name_of_hw = name;
|
||||
hw->args_of_hw = args;
|
||||
hw->family_of_hw = hw_strdup (hw, family);
|
||||
hw->name_of_hw = hw_strdup (hw, name);
|
||||
hw->args_of_hw = hw_strdup (hw, args);
|
||||
|
||||
/* a hook into the system */
|
||||
if (sd != NULL)
|
||||
@ -433,6 +406,7 @@ hw_create (SIM_DESC sd,
|
||||
if (strcmp (family, entry->family) == 0)
|
||||
{
|
||||
hw->base_of_hw->descriptor = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -443,8 +417,7 @@ hw_create (SIM_DESC sd,
|
||||
}
|
||||
|
||||
/* Attach dummy ports */
|
||||
set_hw_ports (hw, empty_hw_ports);
|
||||
set_hw_port_event (hw, panic_hw_port_event);
|
||||
create_hw_port_data (hw);
|
||||
|
||||
return hw;
|
||||
}
|
||||
@ -477,6 +450,12 @@ hw_finish (struct hw *me)
|
||||
/* Fill in the (hopefully) defined trace variable */
|
||||
if (hw_find_property (me, "trace?") != NULL)
|
||||
me->trace_of_hw_p = hw_find_boolean_property (me, "trace?");
|
||||
/* allow global variable to define default tracing */
|
||||
else if (! hw_trace_p (me)
|
||||
&& hw_find_property (hw_root (me), "global-trace?") != NULL
|
||||
&& hw_find_boolean_property (hw_root (me), "global-trace?"))
|
||||
me->trace_of_hw_p = 1;
|
||||
|
||||
|
||||
/* Allow the real device to override any methods */
|
||||
me->base_of_hw->descriptor->to_finish (me);
|
||||
@ -490,6 +469,8 @@ hw_delete (struct hw *me)
|
||||
/* give the object a chance to tidy up */
|
||||
me->base_of_hw->to_delete (me);
|
||||
|
||||
delete_hw_port_data (me);
|
||||
|
||||
/* now unlink us from the tree */
|
||||
if (hw_parent (me))
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ struct hw_device_descriptor {
|
||||
/* Create a primative device */
|
||||
|
||||
struct hw *hw_create
|
||||
(SIM_DESC sd,
|
||||
(struct sim_state *sd,
|
||||
struct hw *parent,
|
||||
const char *family,
|
||||
const char *name,
|
||||
@ -106,23 +106,38 @@ typedef void (hw_delete_callback)
|
||||
((hw)->base_of_hw->to_delete = (method))
|
||||
|
||||
|
||||
struct hw_port_descriptor {
|
||||
const char *name;
|
||||
int number;
|
||||
int nr_ports;
|
||||
port_direction direction;
|
||||
/* Helper functions to make the implementation of a device easier */
|
||||
|
||||
/* Go through the devices reg properties and look for those specifying
|
||||
an address to attach various registers to */
|
||||
|
||||
void do_hw_attach_regs (struct hw *me);
|
||||
|
||||
/* Perform a polling read on FD returning either the number of bytes
|
||||
or a hw_io status code that indicates the reason for the read
|
||||
failure */
|
||||
|
||||
enum {
|
||||
HW_IO_EOF = -1, HW_IO_NOT_READY = -2, /* See: IEEE 1275 */
|
||||
};
|
||||
|
||||
typedef void (hw_port_event_callback)
|
||||
(struct hw *me,
|
||||
int my_port,
|
||||
struct hw *source,
|
||||
int source_port,
|
||||
int level,
|
||||
sim_cpu *processor,
|
||||
sim_cia cia);
|
||||
typedef int (do_hw_poll_read_method)
|
||||
(SIM_DESC sd, int, char *, int);
|
||||
|
||||
int do_hw_poll_read
|
||||
(struct hw *me,
|
||||
do_hw_poll_read_method *read,
|
||||
int sim_io_fd,
|
||||
void *buf,
|
||||
unsigned size_of_buf);
|
||||
|
||||
|
||||
/* PORTS */
|
||||
|
||||
extern void create_hw_port_data
|
||||
(struct hw *hw);
|
||||
extern void delete_hw_port_data
|
||||
(struct hw *hw);
|
||||
|
||||
extern void set_hw_ports (struct hw *hw, const struct hw_port_descriptor ports[]);
|
||||
extern void set_hw_port_event (struct hw *hw, hw_port_event_callback *to_port_event);
|
||||
|
||||
#endif
|
||||
|
339
sim/common/hw-ports.c
Normal file
339
sim/common/hw-ports.c
Normal file
@ -0,0 +1,339 @@
|
||||
/* Common hardware.
|
||||
Copyright (C) 1998 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 2, 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, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
|
||||
#include "sim-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>
|
||||
|
||||
#define TRACE(x,y)
|
||||
|
||||
|
||||
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_callback *to_port_event;
|
||||
const struct hw_port_descriptor *ports;
|
||||
struct hw_port_edge *edges;
|
||||
};
|
||||
|
||||
const struct hw_port_descriptor empty_hw_ports[] = {
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
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_callback *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, "Unreconized 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);
|
||||
}
|
129
sim/common/hw-ports.h
Normal file
129
sim/common/hw-ports.h
Normal file
@ -0,0 +1,129 @@
|
||||
/* Common hardware.
|
||||
Copyright (C) 1998 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 2, 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, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
|
||||
#ifndef HW_PORTS_H
|
||||
#define HW_PORTS_H
|
||||
|
||||
/* Initialize a port */
|
||||
|
||||
struct hw_port_descriptor {
|
||||
const char *name;
|
||||
int number;
|
||||
int nr_ports;
|
||||
port_direction direction;
|
||||
};
|
||||
|
||||
void set_hw_ports (struct hw *hw, const struct hw_port_descriptor ports[]);
|
||||
|
||||
typedef void (hw_port_event_callback)
|
||||
(struct hw *me,
|
||||
int my_port,
|
||||
struct hw *source,
|
||||
int source_port,
|
||||
int level);
|
||||
|
||||
void set_hw_port_event (struct hw *hw, hw_port_event_callback *to_port_event);
|
||||
|
||||
|
||||
/* Port source
|
||||
|
||||
A device drives its output ports using the call
|
||||
|
||||
*/
|
||||
|
||||
void hw_port_event
|
||||
(struct hw *me,
|
||||
int my_port,
|
||||
int value);
|
||||
|
||||
/* This port event will then be propogated to any attached
|
||||
destination ports.
|
||||
|
||||
Any interpretation of PORT and VALUE is model dependant. As a
|
||||
guideline the following are recommended: PCI interrupts A-D should
|
||||
correspond to ports 0-3; level sensative interrupts be requested
|
||||
with a value of one and withdrawn with a value of 0; edge sensative
|
||||
interrupts always have a value of 1, the event its self is treated
|
||||
as the interrupt.
|
||||
|
||||
|
||||
Port destinations
|
||||
|
||||
Attached to each port of a device can be zero or more
|
||||
desitinations. These destinations consist of a device/port pair.
|
||||
A destination is attached/detached to a device line using the
|
||||
attach and detach calls. */
|
||||
|
||||
void hw_port_attach
|
||||
(struct hw *me,
|
||||
int my_port,
|
||||
struct hw *dest,
|
||||
int dest_port,
|
||||
object_disposition disposition);
|
||||
|
||||
void hw_port_detach
|
||||
(struct hw *me,
|
||||
int my_port,
|
||||
struct hw *dest,
|
||||
int dest_port);
|
||||
|
||||
|
||||
/* Iterate over the list of ports attached to a device */
|
||||
|
||||
typedef void (hw_port_traverse_function)
|
||||
(struct hw *me,
|
||||
int my_port,
|
||||
struct hw *dest,
|
||||
int dest_port,
|
||||
void *data);
|
||||
|
||||
void hw_port_traverse
|
||||
(struct hw *me,
|
||||
hw_port_traverse_function *handler,
|
||||
void *data);
|
||||
|
||||
|
||||
/* DESTINATION is attached (detached) to LINE of the device ME
|
||||
|
||||
|
||||
Port conversion
|
||||
|
||||
Users refer to port numbers symbolically. For instance a device
|
||||
may refer to its `INT' signal which is internally represented by
|
||||
port 3.
|
||||
|
||||
To convert to/from the symbolic and internal representation of a
|
||||
port name/number. The following functions are available. */
|
||||
|
||||
int hw_port_decode
|
||||
(struct hw *me,
|
||||
const char *symbolic_name,
|
||||
port_direction direction);
|
||||
|
||||
int hw_port_encode
|
||||
(struct hw *me,
|
||||
int port_number,
|
||||
char *buf,
|
||||
int sizeof_buf,
|
||||
port_direction direction);
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user