2052 lines
50 KiB
C
2052 lines
50 KiB
C
/* This file is part of the program psim.
|
||
|
||
Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
|
||
|
||
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 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, write to the Free Software
|
||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
||
*/
|
||
|
||
|
||
#ifndef _DEVICE_C_
|
||
#define _DEVICE_C_
|
||
|
||
#include <stdio.h>
|
||
|
||
#include "device_table.h"
|
||
#include "cap.h"
|
||
|
||
#include "events.h"
|
||
#include "psim.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>
|
||
|
||
STATIC_INLINE_DEVICE (void) clean_device_properties(device *);
|
||
|
||
/* property entries */
|
||
|
||
typedef struct _device_property_entry device_property_entry;
|
||
struct _device_property_entry {
|
||
device_property_entry *next;
|
||
device_property *value;
|
||
const void *init_array;
|
||
unsigned sizeof_init_array;
|
||
};
|
||
|
||
|
||
/* Interrupt edges */
|
||
|
||
typedef struct _device_interrupt_edge device_interrupt_edge;
|
||
struct _device_interrupt_edge {
|
||
int my_port;
|
||
device *dest;
|
||
int dest_port;
|
||
device_interrupt_edge *next;
|
||
object_disposition disposition;
|
||
};
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(void)
|
||
attach_device_interrupt_edge(device_interrupt_edge **list,
|
||
int my_port,
|
||
device *dest,
|
||
int dest_port,
|
||
object_disposition disposition)
|
||
{
|
||
device_interrupt_edge *new_edge = ZALLOC(device_interrupt_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_INLINE_DEVICE\
|
||
(void)
|
||
detach_device_interrupt_edge(device *me,
|
||
device_interrupt_edge **list,
|
||
int my_port,
|
||
device *dest,
|
||
int dest_port)
|
||
{
|
||
while (*list != NULL) {
|
||
device_interrupt_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)
|
||
device_error(me, "attempt to delete permenant interrupt");
|
||
*list = old_edge->next;
|
||
zfree(old_edge);
|
||
return;
|
||
}
|
||
}
|
||
device_error(me, "attempt to delete unattached interrupt");
|
||
}
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(void)
|
||
clean_device_interrupt_edges(device_interrupt_edge **list)
|
||
{
|
||
while (*list != NULL) {
|
||
device_interrupt_edge *old_edge = *list;
|
||
switch (old_edge->disposition) {
|
||
case permenant_object:
|
||
list = &old_edge->next;
|
||
break;
|
||
case tempoary_object:
|
||
*list = old_edge->next;
|
||
zfree(old_edge);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* A device */
|
||
|
||
struct _device {
|
||
|
||
/* my name is ... */
|
||
const char *name;
|
||
device_unit unit_address;
|
||
const char *path;
|
||
int nr_address_cells;
|
||
int nr_size_cells;
|
||
|
||
/* device tree */
|
||
device *parent;
|
||
device *children;
|
||
device *sibling;
|
||
|
||
/* its template methods */
|
||
void *data; /* device specific data */
|
||
const device_callbacks *callback;
|
||
|
||
/* device properties */
|
||
device_property_entry *properties;
|
||
|
||
/* interrupts */
|
||
device_interrupt_edge *interrupt_destinations;
|
||
|
||
/* any open instances of this device */
|
||
device_instance *instances;
|
||
|
||
/* the internal/external mappings and other global requirements */
|
||
cap *ihandles;
|
||
cap *phandles;
|
||
psim *system;
|
||
|
||
/* debugging */
|
||
int trace;
|
||
};
|
||
|
||
|
||
/* an instance of a device */
|
||
struct _device_instance {
|
||
void *data;
|
||
char *args;
|
||
char *path;
|
||
const device_instance_callbacks *callback;
|
||
/* the root instance */
|
||
device *owner;
|
||
device_instance *next;
|
||
/* interposed instance */
|
||
device_instance *parent;
|
||
device_instance *child;
|
||
};
|
||
|
||
|
||
|
||
/* creation */
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(const char *)
|
||
device_full_name(device *leaf,
|
||
char *buf,
|
||
unsigned sizeof_buf)
|
||
{
|
||
/* get a buffer */
|
||
char full_name[1024];
|
||
if (buf == (char*)0) {
|
||
buf = full_name;
|
||
sizeof_buf = sizeof(full_name);
|
||
}
|
||
|
||
/* construct a name */
|
||
if (leaf->parent == NULL) {
|
||
if (sizeof_buf < 1)
|
||
error("device_full_name: buffer overflow");
|
||
*buf = '\0';
|
||
}
|
||
else {
|
||
char unit[1024];
|
||
device_full_name(leaf->parent, buf, sizeof_buf);
|
||
if (leaf->parent != NULL
|
||
&& device_encode_unit(leaf->parent,
|
||
&leaf->unit_address,
|
||
unit+1,
|
||
sizeof(unit)-1) > 0)
|
||
unit[0] = '@';
|
||
else
|
||
unit[0] = '\0';
|
||
if (strlen(buf) + strlen("/") + strlen(leaf->name) + strlen(unit)
|
||
>= sizeof_buf)
|
||
error("device_full_name: buffer overflow");
|
||
strcat(buf, "/");
|
||
strcat(buf, leaf->name);
|
||
strcat (buf, unit);
|
||
}
|
||
|
||
/* return it usefully */
|
||
if (buf == full_name)
|
||
buf = (char *) strdup(full_name);
|
||
return buf;
|
||
}
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(device *)
|
||
device_create_from(const char *name,
|
||
const device_unit *unit_address,
|
||
void *data,
|
||
const device_callbacks *callbacks,
|
||
device *parent)
|
||
{
|
||
device *new_device = ZALLOC(device);
|
||
|
||
/* insert it into the device tree */
|
||
new_device->parent = parent;
|
||
new_device->children = NULL;
|
||
if (parent != NULL) {
|
||
device **sibling = &parent->children;
|
||
while ((*sibling) != NULL)
|
||
sibling = &(*sibling)->sibling;
|
||
*sibling = new_device;
|
||
}
|
||
|
||
/* give it a name */
|
||
new_device->name = (char *) strdup(name);
|
||
new_device->unit_address = *unit_address;
|
||
new_device->path = device_full_name(new_device, NULL, 0);
|
||
|
||
/* its template */
|
||
new_device->data = data;
|
||
new_device->callback = callbacks;
|
||
|
||
/* its properties - already null */
|
||
/* interrupts - already null */
|
||
|
||
/* mappings - if needed */
|
||
if (parent == NULL) {
|
||
new_device->ihandles = cap_create(name);
|
||
new_device->phandles = cap_create(name);
|
||
}
|
||
else {
|
||
new_device->ihandles = device_root(parent)->ihandles;
|
||
new_device->phandles = device_root(parent)->phandles;
|
||
}
|
||
|
||
cap_add(new_device->phandles, new_device);
|
||
return new_device;
|
||
}
|
||
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(device *)
|
||
device_create(device *parent,
|
||
const char *base,
|
||
const char *name,
|
||
const char *unit_address,
|
||
const char *args)
|
||
{
|
||
const device_descriptor *const *table;
|
||
for (table = device_table; *table != NULL; table++) {
|
||
const device_descriptor *descr;
|
||
for (descr = *table; descr->name != NULL; descr++) {
|
||
if (strcmp(base, descr->name) == 0) {
|
||
device_unit address = { 0 };
|
||
void *data = NULL;
|
||
if (parent != NULL)
|
||
if (device_decode_unit(parent, unit_address, &address) < 0)
|
||
device_error(parent, "invalid address %s for device %s",
|
||
unit_address, name);
|
||
if (descr->creator != NULL)
|
||
data = descr->creator(name, &address, args);
|
||
return device_create_from(name, &address, data,
|
||
descr->callbacks, parent);
|
||
}
|
||
}
|
||
}
|
||
device_error(parent, "attempt to attach unknown device %s", name);
|
||
return NULL;
|
||
}
|
||
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_usage(int verbose)
|
||
{
|
||
const device_descriptor *const *table;
|
||
if (verbose == 1) {
|
||
int pos = 0;
|
||
for (table = device_table; *table != NULL; table++) {
|
||
const device_descriptor *descr;
|
||
for (descr = *table; descr->name != NULL; descr++) {
|
||
pos += strlen(descr->name) + 2;
|
||
if (pos > 75) {
|
||
pos = strlen(descr->name) + 2;
|
||
printf_filtered("\n");
|
||
}
|
||
printf_filtered(" %s", descr->name);
|
||
}
|
||
printf_filtered("\n");
|
||
}
|
||
}
|
||
if (verbose > 1) {
|
||
for (table = device_table; *table != NULL; table++) {
|
||
const device_descriptor *descr;
|
||
for (descr = *table; descr->name != NULL; descr++) {
|
||
printf_filtered(" %s:\n", descr->name);
|
||
/* interrupt ports */
|
||
if (descr->callbacks->interrupt.ports != NULL) {
|
||
const device_interrupt_port_descriptor *ports =
|
||
descr->callbacks->interrupt.ports;
|
||
printf_filtered(" interrupt ports:");
|
||
while (ports->name != NULL) {
|
||
printf_filtered(" %s", ports->name);
|
||
ports++;
|
||
}
|
||
printf_filtered("\n");
|
||
}
|
||
/* general info */
|
||
if (descr->callbacks->usage != NULL)
|
||
descr->callbacks->usage(verbose);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/* Device node: */
|
||
|
||
INLINE_DEVICE\
|
||
(device *)
|
||
device_parent(device *me)
|
||
{
|
||
return me->parent;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(device *)
|
||
device_root(device *me)
|
||
{
|
||
ASSERT(me != NULL);
|
||
while (me->parent != NULL)
|
||
me = me->parent;
|
||
return me;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(device *)
|
||
device_sibling(device *me)
|
||
{
|
||
return me->sibling;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(device *)
|
||
device_child(device *me)
|
||
{
|
||
return me->children;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(const char *)
|
||
device_name(device *me)
|
||
{
|
||
return me->name;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(const char *)
|
||
device_path(device *me)
|
||
{
|
||
return me->path;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void *)
|
||
device_data(device *me)
|
||
{
|
||
return me->data;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(psim *)
|
||
device_system(device *me)
|
||
{
|
||
return me->system;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(const device_unit *)
|
||
device_unit_address(device *me)
|
||
{
|
||
return &me->unit_address;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_address_to_attach_address(device *me,
|
||
const device_unit *address,
|
||
int *attach_space,
|
||
unsigned_word *attach_address,
|
||
device *client)
|
||
{
|
||
if (me->callback->convert.address_to_attach_address == NULL)
|
||
device_error(me, "no convert.address_to_attach_address method");
|
||
return me->callback->convert.address_to_attach_address(me, address, attach_space, attach_address, client);
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_size_to_attach_size(device *me,
|
||
const device_unit *size,
|
||
unsigned *nr_bytes,
|
||
device *client)
|
||
{
|
||
if (me->callback->convert.size_to_attach_size == NULL)
|
||
device_error(me, "no convert.size_to_attach_size method");
|
||
return me->callback->convert.size_to_attach_size(me, size, nr_bytes, client);
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_decode_unit(device *bus,
|
||
const char *unit,
|
||
device_unit *address)
|
||
{
|
||
if (bus->callback->convert.decode_unit == NULL)
|
||
device_error(bus, "no convert.decode_unit method");
|
||
return bus->callback->convert.decode_unit(bus, unit, address);
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_encode_unit(device *bus,
|
||
const device_unit *unit_address,
|
||
char *buf,
|
||
int sizeof_buf)
|
||
{
|
||
if (bus->callback->convert.encode_unit == NULL)
|
||
device_error(bus, "no convert.encode_unit method");
|
||
return bus->callback->convert.encode_unit(bus, unit_address, buf, sizeof_buf);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(unsigned)
|
||
device_nr_address_cells(device *me)
|
||
{
|
||
if (me->nr_address_cells == 0) {
|
||
if (device_find_property(me, "#address-cells") != NULL)
|
||
me->nr_address_cells = device_find_integer_property(me, "#address-cells");
|
||
else
|
||
me->nr_address_cells = 2;
|
||
}
|
||
return me->nr_address_cells;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(unsigned)
|
||
device_nr_size_cells(device *me)
|
||
{
|
||
if (me->nr_size_cells == 0) {
|
||
if (device_find_property(me, "#size-cells") != NULL)
|
||
me->nr_size_cells = device_find_integer_property(me, "#size-cells");
|
||
else
|
||
me->nr_size_cells = 1;
|
||
}
|
||
return me->nr_size_cells;
|
||
}
|
||
|
||
|
||
|
||
/* device-instance: */
|
||
|
||
INLINE_DEVICE\
|
||
(device_instance *)
|
||
device_create_instance_from(device *me,
|
||
device_instance *parent,
|
||
void *data,
|
||
const char *path,
|
||
const char *args,
|
||
const device_instance_callbacks *callbacks)
|
||
{
|
||
device_instance *instance = ZALLOC(device_instance);
|
||
if ((me == NULL) == (parent == NULL))
|
||
device_error(me, "can't have both parent instance and parent device");
|
||
/*instance->unit*/
|
||
/* link this instance into the devices list */
|
||
if (me != NULL) {
|
||
ASSERT(parent == NULL);
|
||
instance->owner = me;
|
||
instance->parent = NULL;
|
||
/* link this instance into the front of the devices instance list */
|
||
instance->next = me->instances;
|
||
me->instances = instance;
|
||
}
|
||
if (parent != NULL) {
|
||
device_instance **previous;
|
||
ASSERT(parent->child == NULL);
|
||
parent->child = instance;
|
||
ASSERT(me == NULL);
|
||
instance->owner = parent->owner;
|
||
instance->parent = parent;
|
||
/* in the devices instance list replace the parent instance with
|
||
this one */
|
||
instance->next = parent->next;
|
||
/* replace parent with this new node */
|
||
previous = &instance->owner->instances;
|
||
while (*previous != parent) {
|
||
ASSERT(*previous != NULL);
|
||
previous = &(*previous)->next;
|
||
}
|
||
*previous = instance;
|
||
}
|
||
instance->data = data;
|
||
instance->args = (args == NULL ? NULL : (char *) strdup(args));
|
||
instance->path = (path == NULL ? NULL : (char *) strdup(path));
|
||
instance->callback = callbacks;
|
||
cap_add(instance->owner->ihandles, instance);
|
||
return instance;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(device_instance *)
|
||
device_create_instance(device *me,
|
||
const char *path,
|
||
const char *args)
|
||
{
|
||
/* create the instance */
|
||
if (me->callback->instance_create == NULL)
|
||
device_error(me, "no instance_create method");
|
||
return me->callback->instance_create(me, path, args);
|
||
}
|
||
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(void)
|
||
clean_device_instances(device *me)
|
||
{
|
||
device_instance **instance = &me->instances;
|
||
while (*instance != NULL) {
|
||
device_instance *old_instance = *instance;
|
||
device_instance_delete(old_instance);
|
||
instance = &me->instances;
|
||
}
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_instance_delete(device_instance *instance)
|
||
{
|
||
device *me = instance->owner;
|
||
if (instance->callback->delete == NULL)
|
||
device_error(me, "no delete method");
|
||
instance->callback->delete(instance);
|
||
if (instance->args != NULL)
|
||
zfree(instance->args);
|
||
if (instance->path != NULL)
|
||
zfree(instance->path);
|
||
if (instance->child == NULL) {
|
||
/* only remove leaf nodes */
|
||
device_instance **curr = &me->instances;
|
||
while (*curr != instance) {
|
||
ASSERT(*curr != NULL);
|
||
curr = &(*curr)->next;
|
||
}
|
||
*curr = instance->next;
|
||
}
|
||
else {
|
||
/* check it isn't in the instance list */
|
||
device_instance *curr = me->instances;
|
||
while (curr != NULL) {
|
||
ASSERT(curr != instance);
|
||
curr = curr->next;
|
||
}
|
||
/* unlink the child */
|
||
ASSERT(instance->child->parent == instance);
|
||
instance->child->parent = NULL;
|
||
}
|
||
cap_remove(me->ihandles, instance);
|
||
zfree(instance);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_instance_read(device_instance *instance,
|
||
void *addr,
|
||
unsigned_word len)
|
||
{
|
||
device *me = instance->owner;
|
||
if (instance->callback->read == NULL)
|
||
device_error(me, "no read method");
|
||
return instance->callback->read(instance, addr, len);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_instance_write(device_instance *instance,
|
||
const void *addr,
|
||
unsigned_word len)
|
||
{
|
||
device *me = instance->owner;
|
||
if (instance->callback->write == NULL)
|
||
device_error(me, "no write method");
|
||
return instance->callback->write(instance, addr, len);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_instance_seek(device_instance *instance,
|
||
unsigned_word pos_hi,
|
||
unsigned_word pos_lo)
|
||
{
|
||
device *me = instance->owner;
|
||
if (instance->callback->seek == NULL)
|
||
device_error(me, "no seek method");
|
||
return instance->callback->seek(instance, pos_hi, pos_lo);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_instance_call_method(device_instance *instance,
|
||
const char *method_name,
|
||
int n_stack_args,
|
||
unsigned_cell stack_args[/*n_stack_args*/],
|
||
int n_stack_returns,
|
||
unsigned_cell stack_returns[/*n_stack_args*/])
|
||
{
|
||
device *me = instance->owner;
|
||
const device_instance_methods *method = instance->callback->methods;
|
||
if (method == NULL) {
|
||
device_error(me, "no methods (want %s)", method_name);
|
||
}
|
||
while (method->name != NULL) {
|
||
if (strcmp(method->name, method_name) == 0) {
|
||
return method->method(instance,
|
||
n_stack_args, stack_args,
|
||
n_stack_returns, stack_returns);
|
||
}
|
||
method++;
|
||
}
|
||
device_error(me, "no %s method", method_name);
|
||
return 0;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(device *)
|
||
device_instance_device(device_instance *instance)
|
||
{
|
||
return instance->owner;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(const char *)
|
||
device_instance_path(device_instance *instance)
|
||
{
|
||
return instance->path;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void *)
|
||
device_instance_data(device_instance *instance)
|
||
{
|
||
return instance->data;
|
||
}
|
||
|
||
|
||
|
||
/* Device Properties: */
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(device_property_entry *)
|
||
find_property_entry(device *me,
|
||
const char *property)
|
||
{
|
||
device_property_entry *entry;
|
||
ASSERT(property != NULL);
|
||
entry = me->properties;
|
||
while (entry != NULL) {
|
||
if (strcmp(entry->value->name, property) == 0)
|
||
return entry;
|
||
entry = entry->next;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(void)
|
||
device_add_property(device *me,
|
||
const char *property,
|
||
device_property_type type,
|
||
const void *init_array,
|
||
unsigned sizeof_init_array,
|
||
const void *array,
|
||
unsigned sizeof_array,
|
||
const device_property *original,
|
||
object_disposition disposition)
|
||
{
|
||
device_property_entry *new_entry = NULL;
|
||
device_property *new_value = NULL;
|
||
|
||
/* find the list end */
|
||
device_property_entry **insertion_point = &me->properties;
|
||
while (*insertion_point != NULL) {
|
||
if (strcmp((*insertion_point)->value->name, property) == 0)
|
||
return;
|
||
insertion_point = &(*insertion_point)->next;
|
||
}
|
||
|
||
/* create a new value */
|
||
new_value = ZALLOC(device_property);
|
||
new_value->name = (char *) strdup(property);
|
||
new_value->type = type;
|
||
if (sizeof_array > 0) {
|
||
void *new_array = zalloc(sizeof_array);
|
||
memcpy(new_array, array, sizeof_array);
|
||
new_value->array = new_array;
|
||
new_value->sizeof_array = sizeof_array;
|
||
}
|
||
new_value->owner = me;
|
||
new_value->original = original;
|
||
new_value->disposition = disposition;
|
||
|
||
/* insert the value into the list */
|
||
new_entry = ZALLOC(device_property_entry);
|
||
*insertion_point = new_entry;
|
||
if (sizeof_init_array > 0) {
|
||
void *new_init_array = zalloc(sizeof_init_array);
|
||
memcpy(new_init_array, init_array, sizeof_init_array);
|
||
new_entry->init_array = new_init_array;
|
||
new_entry->sizeof_init_array = sizeof_init_array;
|
||
}
|
||
new_entry->value = new_value;
|
||
|
||
}
|
||
|
||
|
||
/* local - not available externally */
|
||
STATIC_INLINE_DEVICE\
|
||
(void)
|
||
device_set_property(device *me,
|
||
const char *property,
|
||
device_property_type type,
|
||
const void *array,
|
||
int sizeof_array)
|
||
{
|
||
/* find the property */
|
||
device_property_entry *entry = find_property_entry(me, property);
|
||
if (entry != NULL) {
|
||
/* existing property - update it */
|
||
void *new_array = 0;
|
||
device_property *value = entry->value;
|
||
/* check the type matches */
|
||
if (value->type != type)
|
||
device_error(me, "conflict between type of new and old value for property %s", property);
|
||
/* replace its value */
|
||
if (value->array != NULL)
|
||
zfree((void*)value->array);
|
||
new_array = (sizeof_array > 0
|
||
? zalloc(sizeof_array)
|
||
: (void*)0);
|
||
value->array = new_array;
|
||
value->sizeof_array = sizeof_array;
|
||
if (sizeof_array > 0)
|
||
memcpy(new_array, array, sizeof_array);
|
||
return;
|
||
}
|
||
else {
|
||
/* new property - create it */
|
||
device_add_property(me, property, type,
|
||
NULL, 0, array, sizeof_array,
|
||
NULL, tempoary_object);
|
||
}
|
||
}
|
||
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(void)
|
||
clean_device_properties(device *me)
|
||
{
|
||
device_property_entry **delete_point = &me->properties;
|
||
while (*delete_point != NULL) {
|
||
device_property_entry *current = *delete_point;
|
||
switch (current->value->disposition) {
|
||
case permenant_object:
|
||
/* zap the current value, will be initialized later */
|
||
ASSERT(current->init_array != NULL);
|
||
if (current->value->array != NULL) {
|
||
zfree((void*)current->value->array);
|
||
current->value->array = NULL;
|
||
}
|
||
delete_point = &(*delete_point)->next;
|
||
break;
|
||
case tempoary_object:
|
||
/* zap the actual property, was created during simulation run */
|
||
ASSERT(current->init_array == NULL);
|
||
*delete_point = current->next;
|
||
if (current->value->array != NULL)
|
||
zfree((void*)current->value->array);
|
||
zfree(current->value);
|
||
zfree(current);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_init_static_properties(device *me,
|
||
void *data)
|
||
{
|
||
device_property_entry *property;
|
||
for (property = me->properties;
|
||
property != NULL;
|
||
property = property->next) {
|
||
ASSERT(property->init_array != NULL);
|
||
ASSERT(property->value->array == NULL);
|
||
ASSERT(property->value->disposition == permenant_object);
|
||
switch (property->value->type) {
|
||
case array_property:
|
||
case boolean_property:
|
||
case range_array_property:
|
||
case reg_array_property:
|
||
case string_property:
|
||
case string_array_property:
|
||
case integer_property:
|
||
/* delete the property, and replace it with the original */
|
||
device_set_property(me, property->value->name,
|
||
property->value->type,
|
||
property->init_array,
|
||
property->sizeof_init_array);
|
||
break;
|
||
case ihandle_property:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_init_runtime_properties(device *me,
|
||
void *data)
|
||
{
|
||
device_property_entry *property;
|
||
for (property = me->properties;
|
||
property != NULL;
|
||
property = property->next) {
|
||
switch (property->value->disposition) {
|
||
case permenant_object:
|
||
switch (property->value->type) {
|
||
case ihandle_property:
|
||
{
|
||
device_instance *ihandle;
|
||
ihandle_runtime_property_spec spec;
|
||
ASSERT(property->init_array != NULL);
|
||
ASSERT(property->value->array == NULL);
|
||
device_find_ihandle_runtime_property(me, property->value->name, &spec);
|
||
ihandle = device_create_instance(spec.phandle,
|
||
spec.full_path,
|
||
spec.args);
|
||
device_set_ihandle_property(me, property->value->name, ihandle);
|
||
break;
|
||
}
|
||
case array_property:
|
||
case boolean_property:
|
||
case range_array_property:
|
||
case integer_property:
|
||
case reg_array_property:
|
||
case string_property:
|
||
case string_array_property:
|
||
ASSERT(property->init_array != NULL);
|
||
ASSERT(property->value->array != NULL);
|
||
break;
|
||
}
|
||
break;
|
||
case tempoary_object:
|
||
ASSERT(property->init_array == NULL);
|
||
ASSERT(property->value->array != NULL);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(const device_property *)
|
||
device_next_property(const device_property *property)
|
||
{
|
||
/* find the property in the list */
|
||
device *owner = property->owner;
|
||
device_property_entry *entry = owner->properties;
|
||
while (entry != NULL && entry->value != property)
|
||
entry = entry->next;
|
||
/* now return the following property */
|
||
ASSERT(entry != NULL); /* must be a member! */
|
||
if (entry->next != NULL)
|
||
return entry->next->value;
|
||
else
|
||
return NULL;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(const device_property *)
|
||
device_find_property(device *me,
|
||
const char *property)
|
||
{
|
||
if (me == NULL) {
|
||
return NULL;
|
||
}
|
||
else if (property == NULL || strcmp(property, "") == 0) {
|
||
if (me->properties == NULL)
|
||
return NULL;
|
||
else
|
||
return me->properties->value;
|
||
}
|
||
else {
|
||
device_property_entry *entry = find_property_entry(me, property);
|
||
if (entry != NULL)
|
||
return entry->value;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_array_property(device *me,
|
||
const char *property,
|
||
const void *array,
|
||
int sizeof_array)
|
||
{
|
||
device_add_property(me, property, array_property,
|
||
array, sizeof_array, array, sizeof_array,
|
||
NULL, permenant_object);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_set_array_property(device *me,
|
||
const char *property,
|
||
const void *array,
|
||
int sizeof_array)
|
||
{
|
||
device_set_property(me, property, array_property, array, sizeof_array);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(const device_property *)
|
||
device_find_array_property(device *me,
|
||
const char *property)
|
||
{
|
||
const device_property *node;
|
||
node = device_find_property(me, property);
|
||
if (node == (device_property*)0
|
||
|| node->type != array_property)
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
return node;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_boolean_property(device *me,
|
||
const char *property,
|
||
int boolean)
|
||
{
|
||
signed32 new_boolean = (boolean ? -1 : 0);
|
||
device_add_property(me, property, boolean_property,
|
||
&new_boolean, sizeof(new_boolean),
|
||
&new_boolean, sizeof(new_boolean),
|
||
NULL, permenant_object);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_find_boolean_property(device *me,
|
||
const char *property)
|
||
{
|
||
const device_property *node;
|
||
unsigned_cell boolean;
|
||
node = device_find_property(me, property);
|
||
if (node == (device_property*)0
|
||
|| node->type != boolean_property)
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
ASSERT(sizeof(boolean) == node->sizeof_array);
|
||
memcpy(&boolean, node->array, sizeof(boolean));
|
||
return boolean;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_ihandle_runtime_property(device *me,
|
||
const char *property,
|
||
const ihandle_runtime_property_spec *ihandle)
|
||
{
|
||
unsigned_cell *cells;
|
||
char *chp;
|
||
unsigned sizeof_cells = (sizeof(unsigned_cell) * 3
|
||
+ (strlen(ihandle->full_path) + 1)
|
||
+ (ihandle->args != NULL
|
||
? (strlen(ihandle->args) + 1)
|
||
: 0));
|
||
|
||
/* the basics */
|
||
cells = zalloc(sizeof_cells);
|
||
cells[0] = H2BE_cell(device_to_external(ihandle->phandle));
|
||
cells[1] = (ihandle->full_path == NULL ? 0 : -1);
|
||
cells[2] = (ihandle->args == NULL ? 0 : -1);
|
||
chp = (char*)&cells[3];
|
||
|
||
/* the full path (if present) */
|
||
if (ihandle->full_path != NULL) {
|
||
strcpy(chp, ihandle->full_path);
|
||
chp += strlen(ihandle->full_path) + 1;
|
||
}
|
||
|
||
/* the args (if present) */
|
||
if (ihandle->args != NULL) {
|
||
strcpy(chp, ihandle->args);
|
||
chp += strlen(ihandle->args) + 1;
|
||
}
|
||
|
||
/* add it */
|
||
ASSERT(sizeof_cells == (chp - (char*)cells));
|
||
device_add_property(me, property, ihandle_property,
|
||
cells, sizeof_cells,
|
||
NULL, 0,
|
||
NULL, permenant_object);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_find_ihandle_runtime_property(device *me,
|
||
const char *property,
|
||
ihandle_runtime_property_spec *ihandle)
|
||
{
|
||
const unsigned_cell *cells;
|
||
const char *chp;
|
||
device_property_entry *entry = find_property_entry(me, property);
|
||
TRACE(trace_devices,
|
||
("device_find_ihandle_runtime_property(me=0x%lx, property=%s)\n",
|
||
(long)me, property));
|
||
if (entry == NULL
|
||
|| entry->value->type != ihandle_property
|
||
|| entry->value->disposition != permenant_object)
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
cells = entry->init_array;
|
||
chp = (char*)&cells[3];
|
||
ASSERT(entry->init_array != NULL);
|
||
/* the device to be opened */
|
||
ihandle->phandle = external_to_device(me, BE2H_cell(cells[0]));
|
||
/* the full path */
|
||
if (cells[1] != 0) {
|
||
ihandle->full_path = chp;
|
||
chp += strlen(ihandle->full_path) + 1;
|
||
}
|
||
else
|
||
ihandle->full_path = NULL;
|
||
/* the args */
|
||
if (cells[2] != 0) {
|
||
ihandle->args = chp;
|
||
chp += strlen(ihandle->args) + 1;
|
||
}
|
||
else
|
||
ihandle->args = NULL;
|
||
/* reached the end? */
|
||
ASSERT(entry->sizeof_init_array
|
||
== (chp - (char*)cells));
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_set_ihandle_property(device *me,
|
||
const char *property,
|
||
device_instance *ihandle)
|
||
{
|
||
unsigned_cell cells;
|
||
cells = H2BE_cell(device_instance_to_external(ihandle));
|
||
device_set_property(me, property, ihandle_property,
|
||
&cells, sizeof(cells));
|
||
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(device_instance *)
|
||
device_find_ihandle_property(device *me,
|
||
const char *property)
|
||
{
|
||
const device_property *node;
|
||
unsigned_cell ihandle;
|
||
device_instance *instance;
|
||
|
||
node = device_find_property(me, property);
|
||
if (node == NULL || node->type != ihandle_property)
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
if (node->array == NULL)
|
||
device_error(me, "runtime property %s not yet initialized", property);
|
||
|
||
ASSERT(sizeof(ihandle) == node->sizeof_array);
|
||
memcpy(&ihandle, node->array, sizeof(ihandle));
|
||
instance = external_to_device_instance(me, BE2H_cell(ihandle));
|
||
ASSERT(instance != NULL);
|
||
return instance;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_integer_property(device *me,
|
||
const char *property,
|
||
signed_cell integer)
|
||
{
|
||
H2BE(integer);
|
||
device_add_property(me, property, integer_property,
|
||
&integer, sizeof(integer),
|
||
&integer, sizeof(integer),
|
||
NULL, permenant_object);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(signed_cell)
|
||
device_find_integer_property(device *me,
|
||
const char *property)
|
||
{
|
||
const device_property *node;
|
||
signed_cell integer;
|
||
TRACE(trace_devices,
|
||
("device_find_integer(me=0x%lx, property=%s)\n",
|
||
(long)me, property));
|
||
node = device_find_property(me, property);
|
||
if (node == (device_property*)0
|
||
|| node->type != integer_property)
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
ASSERT(sizeof(integer) == node->sizeof_array);
|
||
memcpy(&integer, node->array, sizeof(integer));
|
||
return BE2H_cell(integer);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_find_integer_array_property(device *me,
|
||
const char *property,
|
||
unsigned index,
|
||
signed_cell *integer)
|
||
{
|
||
const device_property *node;
|
||
int sizeof_integer = sizeof(*integer);
|
||
signed_cell *cell;
|
||
TRACE(trace_devices,
|
||
("device_find_integer(me=0x%lx, property=%s)\n",
|
||
(long)me, property));
|
||
|
||
/* check things sane */
|
||
node = device_find_property(me, property);
|
||
if (node == (device_property*)0
|
||
|| (node->type != integer_property
|
||
&& node->type != array_property))
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
if ((node->sizeof_array % sizeof_integer) != 0)
|
||
device_error(me, "property %s contains an incomplete number of cells", property);
|
||
if (node->sizeof_array <= sizeof_integer * index)
|
||
return 0;
|
||
|
||
/* Find and convert the value */
|
||
cell = ((signed_cell*)node->array) + index;
|
||
*integer = BE2H_cell(*cell);
|
||
|
||
return node->sizeof_array / sizeof_integer;
|
||
}
|
||
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(unsigned_cell *)
|
||
unit_address_to_cells(const device_unit *unit,
|
||
unsigned_cell *cell,
|
||
int nr_cells)
|
||
{
|
||
int i;
|
||
ASSERT(nr_cells == unit->nr_cells);
|
||
for (i = 0; i < unit->nr_cells; i++) {
|
||
*cell = H2BE_cell(unit->cells[i]);
|
||
cell += 1;
|
||
}
|
||
return cell;
|
||
}
|
||
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(const unsigned_cell *)
|
||
cells_to_unit_address(const unsigned_cell *cell,
|
||
device_unit *unit,
|
||
int nr_cells)
|
||
{
|
||
int i;
|
||
memset(unit, 0, sizeof(*unit));
|
||
unit->nr_cells = nr_cells;
|
||
for (i = 0; i < unit->nr_cells; i++) {
|
||
unit->cells[i] = BE2H_cell(*cell);
|
||
cell += 1;
|
||
}
|
||
return cell;
|
||
}
|
||
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(unsigned)
|
||
nr_range_property_cells(device *me,
|
||
int nr_ranges)
|
||
{
|
||
return ((device_nr_address_cells(me)
|
||
+ device_nr_address_cells(device_parent(me))
|
||
+ device_nr_size_cells(me))
|
||
) * nr_ranges;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_range_array_property(device *me,
|
||
const char *property,
|
||
const range_property_spec *ranges,
|
||
unsigned nr_ranges)
|
||
{
|
||
unsigned sizeof_cells = (nr_range_property_cells(me, nr_ranges)
|
||
* sizeof(unsigned_cell));
|
||
unsigned_cell *cells = zalloc(sizeof_cells);
|
||
unsigned_cell *cell;
|
||
int i;
|
||
|
||
/* copy the property elements over */
|
||
cell = cells;
|
||
for (i = 0; i < nr_ranges; i++) {
|
||
const range_property_spec *range = &ranges[i];
|
||
/* copy the child address */
|
||
cell = unit_address_to_cells(&range->child_address, cell,
|
||
device_nr_address_cells(me));
|
||
/* copy the parent address */
|
||
cell = unit_address_to_cells(&range->parent_address, cell,
|
||
device_nr_address_cells(device_parent(me)));
|
||
/* copy the size */
|
||
cell = unit_address_to_cells(&range->size, cell,
|
||
device_nr_size_cells(me));
|
||
}
|
||
ASSERT(cell == &cells[nr_range_property_cells(me, nr_ranges)]);
|
||
|
||
/* add it */
|
||
device_add_property(me, property, range_array_property,
|
||
cells, sizeof_cells,
|
||
cells, sizeof_cells,
|
||
NULL, permenant_object);
|
||
|
||
zfree(cells);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_find_range_array_property(device *me,
|
||
const char *property,
|
||
unsigned index,
|
||
range_property_spec *range)
|
||
{
|
||
const device_property *node;
|
||
unsigned sizeof_entry = (nr_range_property_cells(me, 1)
|
||
* sizeof(unsigned_cell));
|
||
const unsigned_cell *cells;
|
||
|
||
/* locate the property */
|
||
node = device_find_property(me, property);
|
||
if (node == (device_property*)0
|
||
|| node->type != range_array_property)
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
|
||
/* aligned ? */
|
||
if ((node->sizeof_array % sizeof_entry) != 0)
|
||
device_error(me, "property %s contains an incomplete number of entries",
|
||
property);
|
||
|
||
/* within bounds? */
|
||
if (node->sizeof_array < sizeof_entry * (index + 1))
|
||
return 0;
|
||
|
||
/* find the range of interest */
|
||
cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index);
|
||
|
||
/* copy the child address out - converting as we go */
|
||
cells = cells_to_unit_address(cells, &range->child_address,
|
||
device_nr_address_cells(me));
|
||
|
||
/* copy the parent address out - converting as we go */
|
||
cells = cells_to_unit_address(cells, &range->parent_address,
|
||
device_nr_address_cells(device_parent(me)));
|
||
|
||
/* copy the size - converting as we go */
|
||
cells = cells_to_unit_address(cells, &range->size,
|
||
device_nr_size_cells(me));
|
||
|
||
return node->sizeof_array / sizeof_entry;
|
||
}
|
||
|
||
|
||
STATIC_INLINE_DEVICE\
|
||
(unsigned)
|
||
nr_reg_property_cells(device *me,
|
||
int nr_regs)
|
||
{
|
||
return (device_nr_address_cells(device_parent(me))
|
||
+ device_nr_size_cells(device_parent(me))
|
||
) * nr_regs;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_reg_array_property(device *me,
|
||
const char *property,
|
||
const reg_property_spec *regs,
|
||
unsigned nr_regs)
|
||
{
|
||
unsigned sizeof_cells = (nr_reg_property_cells(me, nr_regs)
|
||
* sizeof(unsigned_cell));
|
||
unsigned_cell *cells = zalloc(sizeof_cells);
|
||
unsigned_cell *cell;
|
||
int i;
|
||
|
||
/* copy the property elements over */
|
||
cell = cells;
|
||
for (i = 0; i < nr_regs; i++) {
|
||
const reg_property_spec *reg = ®s[i];
|
||
/* copy the address */
|
||
cell = unit_address_to_cells(®->address, cell,
|
||
device_nr_address_cells(device_parent(me)));
|
||
/* copy the size */
|
||
cell = unit_address_to_cells(®->size, cell,
|
||
device_nr_size_cells(device_parent(me)));
|
||
}
|
||
ASSERT(cell == &cells[nr_reg_property_cells(me, nr_regs)]);
|
||
|
||
/* add it */
|
||
device_add_property(me, property, reg_array_property,
|
||
cells, sizeof_cells,
|
||
cells, sizeof_cells,
|
||
NULL, permenant_object);
|
||
|
||
zfree(cells);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_find_reg_array_property(device *me,
|
||
const char *property,
|
||
unsigned index,
|
||
reg_property_spec *reg)
|
||
{
|
||
const device_property *node;
|
||
unsigned sizeof_entry = (nr_reg_property_cells(me, 1)
|
||
* sizeof(unsigned_cell));
|
||
const unsigned_cell *cells;
|
||
|
||
/* locate the property */
|
||
node = device_find_property(me, property);
|
||
if (node == (device_property*)0
|
||
|| node->type != reg_array_property)
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
|
||
/* aligned ? */
|
||
if ((node->sizeof_array % sizeof_entry) != 0)
|
||
device_error(me, "property %s contains an incomplete number of entries",
|
||
property);
|
||
|
||
/* within bounds? */
|
||
if (node->sizeof_array < sizeof_entry * (index + 1))
|
||
return 0;
|
||
|
||
/* find the range of interest */
|
||
cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index);
|
||
|
||
/* copy the address out - converting as we go */
|
||
cells = cells_to_unit_address(cells, ®->address,
|
||
device_nr_address_cells(device_parent(me)));
|
||
|
||
/* copy the size out - converting as we go */
|
||
cells = cells_to_unit_address(cells, ®->size,
|
||
device_nr_size_cells(device_parent(me)));
|
||
|
||
return node->sizeof_array / sizeof_entry;
|
||
}
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_string_property(device *me,
|
||
const char *property,
|
||
const char *string)
|
||
{
|
||
device_add_property(me, property, string_property,
|
||
string, strlen(string) + 1,
|
||
string, strlen(string) + 1,
|
||
NULL, permenant_object);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(const char *)
|
||
device_find_string_property(device *me,
|
||
const char *property)
|
||
{
|
||
const device_property *node;
|
||
const char *string;
|
||
node = device_find_property(me, property);
|
||
if (node == (device_property*)0
|
||
|| node->type != string_property)
|
||
device_error(me, "property %s not found or of wrong type", property);
|
||
string = node->array;
|
||
ASSERT(strlen(string) + 1 == node->sizeof_array);
|
||
return string;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_string_array_property(device *me,
|
||
const char *property,
|
||
const string_property_spec *strings,
|
||
unsigned nr_strings)
|
||
{
|
||
int sizeof_array;
|
||
int string_nr;
|
||
char *array;
|
||
char *chp;
|
||
if (nr_strings == 0)
|
||
device_error(me, "property %s must be non-null", property);
|
||
/* total up the size of the needed array */
|
||
for (sizeof_array = 0, string_nr = 0;
|
||
string_nr < nr_strings;
|
||
string_nr ++) {
|
||
sizeof_array += strlen(strings[string_nr]) + 1;
|
||
}
|
||
/* create the array */
|
||
array = (char*)zalloc(sizeof_array);
|
||
chp = array;
|
||
for (string_nr = 0;
|
||
string_nr < nr_strings;
|
||
string_nr++) {
|
||
strcpy(chp, strings[string_nr]);
|
||
chp += strlen(chp) + 1;
|
||
}
|
||
ASSERT(chp == array + sizeof_array);
|
||
/* now enter it */
|
||
device_add_property(me, property, string_array_property,
|
||
array, sizeof_array,
|
||
array, sizeof_array,
|
||
NULL, permenant_object);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_find_string_array_property(device *me,
|
||
const char *property,
|
||
unsigned index,
|
||
string_property_spec *string)
|
||
{
|
||
const device_property *node;
|
||
node = device_find_property(me, property);
|
||
if (node == (device_property*)0)
|
||
device_error(me, "property %s not found", property);
|
||
switch (node->type) {
|
||
default:
|
||
device_error(me, "property %s of wrong type", property);
|
||
break;
|
||
case string_property:
|
||
if (index == 0) {
|
||
*string = node->array;
|
||
ASSERT(strlen(*string) + 1 == node->sizeof_array);
|
||
return 1;
|
||
}
|
||
break;
|
||
case array_property:
|
||
if (node->sizeof_array == 0
|
||
|| ((char*)node->array)[node->sizeof_array - 1] != '\0')
|
||
device_error(me, "property %s invalid for string array", property);
|
||
/* FALL THROUGH */
|
||
case string_array_property:
|
||
ASSERT(node->sizeof_array > 0);
|
||
ASSERT(((char*)node->array)[node->sizeof_array - 1] == '\0');
|
||
{
|
||
const char *chp = node->array;
|
||
int nr_entries = 0;
|
||
/* count the number of strings, keeping an eye out for the one
|
||
we're looking for */
|
||
*string = chp;
|
||
do {
|
||
if (*chp == '\0') {
|
||
/* next string */
|
||
nr_entries++;
|
||
chp++;
|
||
if (nr_entries == index)
|
||
*string = chp;
|
||
}
|
||
else {
|
||
chp++;
|
||
}
|
||
} while (chp < (char*)node->array + node->sizeof_array);
|
||
if (index < nr_entries)
|
||
return nr_entries;
|
||
else {
|
||
*string = NULL;
|
||
return 0;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_add_duplicate_property(device *me,
|
||
const char *property,
|
||
const device_property *original)
|
||
{
|
||
device_property_entry *master;
|
||
TRACE(trace_devices,
|
||
("device_add_duplicate_property(me=0x%lx, property=%s, ...)\n",
|
||
(long)me, property));
|
||
if (original->disposition != permenant_object)
|
||
device_error(me, "Can only duplicate permenant objects");
|
||
/* find the original's master */
|
||
master = original->owner->properties;
|
||
while (master->value != original) {
|
||
master = master->next;
|
||
ASSERT(master != NULL);
|
||
}
|
||
/* now duplicate it */
|
||
device_add_property(me, property,
|
||
original->type,
|
||
master->init_array, master->sizeof_init_array,
|
||
original->array, original->sizeof_array,
|
||
original, permenant_object);
|
||
}
|
||
|
||
|
||
|
||
/* Device Hardware: */
|
||
|
||
INLINE_DEVICE\
|
||
(unsigned)
|
||
device_io_read_buffer(device *me,
|
||
void *dest,
|
||
int space,
|
||
unsigned_word addr,
|
||
unsigned nr_bytes,
|
||
cpu *processor,
|
||
unsigned_word cia)
|
||
{
|
||
if (me->callback->io.read_buffer == NULL)
|
||
device_error(me, "no io.read_buffer method");
|
||
return me->callback->io.read_buffer(me, dest, space,
|
||
addr, nr_bytes,
|
||
processor, cia);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(unsigned)
|
||
device_io_write_buffer(device *me,
|
||
const void *source,
|
||
int space,
|
||
unsigned_word addr,
|
||
unsigned nr_bytes,
|
||
cpu *processor,
|
||
unsigned_word cia)
|
||
{
|
||
if (me->callback->io.write_buffer == NULL)
|
||
device_error(me, "no io.write_buffer method");
|
||
return me->callback->io.write_buffer(me, source, space,
|
||
addr, nr_bytes,
|
||
processor, cia);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(unsigned)
|
||
device_dma_read_buffer(device *me,
|
||
void *dest,
|
||
int space,
|
||
unsigned_word addr,
|
||
unsigned nr_bytes)
|
||
{
|
||
if (me->callback->dma.read_buffer == NULL)
|
||
device_error(me, "no dma.read_buffer method");
|
||
return me->callback->dma.read_buffer(me, dest, space,
|
||
addr, nr_bytes);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(unsigned)
|
||
device_dma_write_buffer(device *me,
|
||
const void *source,
|
||
int space,
|
||
unsigned_word addr,
|
||
unsigned nr_bytes,
|
||
int violate_read_only_section)
|
||
{
|
||
if (me->callback->dma.write_buffer == NULL)
|
||
device_error(me, "no dma.write_buffer method");
|
||
return me->callback->dma.write_buffer(me, source, space,
|
||
addr, nr_bytes,
|
||
violate_read_only_section);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_attach_address(device *me,
|
||
attach_type attach,
|
||
int space,
|
||
unsigned_word addr,
|
||
unsigned nr_bytes,
|
||
access_type access,
|
||
device *client) /*callback/default*/
|
||
{
|
||
if (me->callback->address.attach == NULL)
|
||
device_error(me, "no address.attach method");
|
||
me->callback->address.attach(me, attach, space,
|
||
addr, nr_bytes, access, client);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_detach_address(device *me,
|
||
attach_type attach,
|
||
int space,
|
||
unsigned_word addr,
|
||
unsigned nr_bytes,
|
||
access_type access,
|
||
device *client) /*callback/default*/
|
||
{
|
||
if (me->callback->address.detach == NULL)
|
||
device_error(me, "no address.detach method");
|
||
me->callback->address.detach(me, attach, space,
|
||
addr, nr_bytes, access, client);
|
||
}
|
||
|
||
|
||
|
||
/* Interrupts: */
|
||
|
||
INLINE_DEVICE(void)
|
||
device_interrupt_event(device *me,
|
||
int my_port,
|
||
int level,
|
||
cpu *processor,
|
||
unsigned_word cia)
|
||
{
|
||
int found_an_edge = 0;
|
||
device_interrupt_edge *edge;
|
||
/* device's interrupt lines directly connected */
|
||
for (edge = me->interrupt_destinations;
|
||
edge != NULL;
|
||
edge = edge->next) {
|
||
if (edge->my_port == my_port) {
|
||
if (edge->dest->callback->interrupt.event == NULL)
|
||
device_error(me, "no interrupt method");
|
||
edge->dest->callback->interrupt.event(edge->dest,
|
||
edge->dest_port,
|
||
me,
|
||
my_port,
|
||
level,
|
||
processor, cia);
|
||
found_an_edge = 1;
|
||
}
|
||
}
|
||
if (!found_an_edge) {
|
||
device_error(me, "No interrupt edge for port %d", my_port);
|
||
}
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_interrupt_attach(device *me,
|
||
int my_port,
|
||
device *dest,
|
||
int dest_port,
|
||
object_disposition disposition)
|
||
{
|
||
attach_device_interrupt_edge(&me->interrupt_destinations,
|
||
my_port,
|
||
dest,
|
||
dest_port,
|
||
disposition);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_interrupt_detach(device *me,
|
||
int my_port,
|
||
device *dest,
|
||
int dest_port)
|
||
{
|
||
detach_device_interrupt_edge(me,
|
||
&me->interrupt_destinations,
|
||
my_port,
|
||
dest,
|
||
dest_port);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_interrupt_traverse(device *me,
|
||
device_interrupt_traverse_function *handler,
|
||
void *data)
|
||
{
|
||
device_interrupt_edge *interrupt_edge;
|
||
for (interrupt_edge = me->interrupt_destinations;
|
||
interrupt_edge != NULL;
|
||
interrupt_edge = interrupt_edge->next) {
|
||
handler(me, interrupt_edge->my_port,
|
||
interrupt_edge->dest, interrupt_edge->dest_port,
|
||
data);
|
||
}
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_interrupt_decode(device *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 device_interrupt_port_descriptor *ports =
|
||
me->callback->interrupt.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)
|
||
device_error(me, "Interrupt port %s out of range",
|
||
port_name);
|
||
return port;
|
||
}
|
||
}
|
||
}
|
||
else if (strcmp(port_name, ports->name) == 0)
|
||
return ports->number;
|
||
}
|
||
ports++;
|
||
}
|
||
}
|
||
}
|
||
device_error(me, "Unreconized interrupt port %s", port_name);
|
||
return 0;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_interrupt_encode(device *me,
|
||
int port_number,
|
||
char *buf,
|
||
int sizeof_buf,
|
||
port_direction direction)
|
||
{
|
||
const device_interrupt_port_descriptor *ports = NULL;
|
||
ports = me->callback->interrupt.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)
|
||
error("device_interrupt_encode: buffer overflow");
|
||
return strlen(buf);
|
||
}
|
||
}
|
||
else {
|
||
if (ports->number == port_number) {
|
||
if (strlen(ports->name) >= sizeof_buf)
|
||
error("device_interrupt_encode: buffer overflow");
|
||
strcpy(buf, ports->name);
|
||
return strlen(buf);
|
||
}
|
||
}
|
||
}
|
||
ports++;
|
||
}
|
||
}
|
||
sprintf(buf, "%d", port_number);
|
||
if (strlen(buf) >= sizeof_buf)
|
||
error("device_interrupt_encode: buffer overflow");
|
||
return strlen(buf);
|
||
}
|
||
|
||
|
||
|
||
/* IOCTL: */
|
||
|
||
EXTERN_DEVICE\
|
||
(int)
|
||
device_ioctl(device *me,
|
||
cpu *processor,
|
||
unsigned_word cia,
|
||
device_ioctl_request request,
|
||
...)
|
||
{
|
||
int status;
|
||
va_list ap;
|
||
va_start(ap, request);
|
||
if (me->callback->ioctl == NULL)
|
||
device_error(me, "no ioctl method");
|
||
status = me->callback->ioctl(me, processor, cia, request, ap);
|
||
va_end(ap);
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
/* I/O */
|
||
|
||
EXTERN_DEVICE\
|
||
(void volatile)
|
||
device_error(device *me,
|
||
const char *fmt,
|
||
...)
|
||
{
|
||
char message[1024];
|
||
va_list ap;
|
||
/* format the message */
|
||
va_start(ap, fmt);
|
||
vsprintf(message, fmt, ap);
|
||
va_end(ap);
|
||
/* sanity check */
|
||
if (strlen(message) >= sizeof(message))
|
||
error("device_error: buffer overflow");
|
||
if (me == NULL)
|
||
error("device: %s", message);
|
||
else if (me->path != NULL && me->path[0] != '\0')
|
||
error("%s: %s", me->path, message);
|
||
else if (me->name != NULL && me->name[0] != '\0')
|
||
error("%s: %s", me->name, message);
|
||
else
|
||
error("device: %s", message);
|
||
while(1);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(int)
|
||
device_trace(device *me)
|
||
{
|
||
return me->trace;
|
||
}
|
||
|
||
|
||
/* External representation */
|
||
|
||
INLINE_DEVICE\
|
||
(device *)
|
||
external_to_device(device *tree_member,
|
||
unsigned_cell phandle)
|
||
{
|
||
device *me = cap_internal(tree_member->phandles, phandle);
|
||
return me;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(unsigned_cell)
|
||
device_to_external(device *me)
|
||
{
|
||
unsigned_cell phandle = cap_external(me->phandles, me);
|
||
return phandle;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(device_instance *)
|
||
external_to_device_instance(device *tree_member,
|
||
unsigned_cell ihandle)
|
||
{
|
||
device_instance *instance = cap_internal(tree_member->ihandles, ihandle);
|
||
return instance;
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(unsigned_cell)
|
||
device_instance_to_external(device_instance *instance)
|
||
{
|
||
unsigned_cell ihandle = cap_external(instance->owner->ihandles, instance);
|
||
return ihandle;
|
||
}
|
||
|
||
|
||
/* Map onto the event functions */
|
||
|
||
INLINE_DEVICE\
|
||
(event_entry_tag)
|
||
device_event_queue_schedule(device *me,
|
||
signed64 delta_time,
|
||
device_event_handler *handler,
|
||
void *data)
|
||
{
|
||
return event_queue_schedule(psim_event_queue(me->system),
|
||
delta_time,
|
||
handler,
|
||
data);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_event_queue_deschedule(device *me,
|
||
event_entry_tag event_to_remove)
|
||
{
|
||
event_queue_deschedule(psim_event_queue(me->system),
|
||
event_to_remove);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(signed64)
|
||
device_event_queue_time(device *me)
|
||
{
|
||
return event_queue_time(psim_event_queue(me->system));
|
||
}
|
||
|
||
|
||
/* Initialization: */
|
||
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_clean(device *me,
|
||
void *data)
|
||
{
|
||
psim *system;
|
||
system = (psim*)data;
|
||
TRACE(trace_device_init, ("device_clean - initializing %s", me->path));
|
||
clean_device_interrupt_edges(&me->interrupt_destinations);
|
||
clean_device_instances(me);
|
||
clean_device_properties(me);
|
||
}
|
||
|
||
/* Device initialization: */
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_init_address(device *me,
|
||
void *data)
|
||
{
|
||
psim *system = (psim*)data;
|
||
int nr_address_cells;
|
||
int nr_size_cells;
|
||
TRACE(trace_device_init, ("device_init_address - initializing %s", me->path));
|
||
|
||
/* ensure the cap database is valid */
|
||
if (me->parent == NULL) {
|
||
cap_init(me->ihandles);
|
||
cap_init(me->phandles);
|
||
}
|
||
|
||
/* some basics */
|
||
me->system = system; /* misc things not known until now */
|
||
me->trace = (device_find_property(me, "trace")
|
||
? device_find_integer_property(me, "trace")
|
||
: 0);
|
||
|
||
/* Ensure that the first address found in the reg property matches
|
||
anything that was specified as part of the devices name */
|
||
if (device_find_property(me, "reg") != NULL) {
|
||
reg_property_spec unit;
|
||
device_find_reg_array_property(me, "reg", 0, &unit);
|
||
if (memcmp(device_unit_address(me), &unit.address, sizeof(unit.address))
|
||
!= 0)
|
||
device_error(me, "Unit address as specified by the reg property in conflict with the value previously specified in the devices path");
|
||
}
|
||
|
||
/* ensure that the devices #address/size-cells is consistent */
|
||
nr_address_cells = device_nr_address_cells(me);
|
||
if (device_find_property(me, "#address-cells") != NULL
|
||
&& (nr_address_cells
|
||
!= device_find_integer_property(me, "#address-cells")))
|
||
device_error(me, "#address-cells property used before defined");
|
||
nr_size_cells = device_nr_size_cells(me);
|
||
if (device_find_property(me, "#size-cells") != NULL
|
||
&& (nr_size_cells
|
||
!= device_find_integer_property(me, "#size-cells")))
|
||
device_error(me, "#size-cells property used before defined");
|
||
|
||
/* now init it */
|
||
if (me->callback->init.address != NULL)
|
||
me->callback->init.address(me);
|
||
}
|
||
|
||
INLINE_DEVICE\
|
||
(void)
|
||
device_init_data(device *me,
|
||
void *data)
|
||
{
|
||
TRACE(trace_device_init, ("device_init_data - initializing %s", me->path));
|
||
if (me->callback->init.data != NULL)
|
||
me->callback->init.data(me);
|
||
}
|
||
|
||
#endif /* _DEVICE_C_ */
|