binutils-gdb/sim/ppc/device.c
1996-07-23 15:42:42 +00:00

1970 lines
49 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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"
#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 *);
STATIC_INLINE_DEVICE (void) init_device_properties(device *, void*);
/* 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\n");
*list = old_edge->next;
zfree(old_edge);
return;
}
}
device_error(me, "attempt to delete unattached interrupt\n");
}
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;
/* 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;
};
/* 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;
};
/* Device node: */
INLINE_DEVICE\
(device *)
device_parent(device *me)
{
return me->parent;
}
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;
}
/* device template: */
/* determine the full name of the device. If buf is specified it is
stored in there. Failing that, a safe area of memory is allocated */
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\n");
*buf = '\0';
}
else {
char unit[1024];
device_full_name(leaf->parent, buf, sizeof_buf);
if (leaf->parent != NULL
&& leaf->parent->callback->convert.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\n");
strcat(buf, "/");
strcat(buf, leaf->name);
strcat (buf, unit);
}
/* return it usefully */
if (buf == full_name)
buf = strdup(full_name);
return buf;
}
/* manipulate/lookup device names */
typedef struct _name_specifier {
/* components in the full length name */
char *path;
char *property;
char *value;
/* current device */
char *name;
char *unit;
char *args;
/* previous device */
char *last_name;
char *last_unit;
char *last_args;
/* work area */
char buf[1024];
} name_specifier;
/* Given a device specifier, break it up into its main components:
path (and if present) property name and property value. */
STATIC_INLINE_DEVICE\
(int)
split_device_specifier(const char *device_specifier,
name_specifier *spec)
{
char *chp;
if (strlen(device_specifier) >= sizeof(spec->buf))
error("split_device_specifier: buffer overflow\n");
/* expand aliases (later) */
strcpy(spec->buf, device_specifier);
/* strip the leading spaces and check that remainder isn't a comment */
chp = spec->buf;
while (*chp != '\0' && isspace(*chp))
chp++;
if (*chp == '\0' || *chp == '#')
return 0;
/* find the path and terminate it with null */
spec->path = chp;
while (*chp != '\0' && !isspace(*chp))
chp++;
if (*chp != '\0') {
*chp = '\0';
chp++;
}
/* and any value */
while (*chp != '\0' && isspace(*chp))
chp++;
spec->value = chp;
/* now go back and chop the property off of the path */
if (spec->value[0] == '\0') {
spec->property = NULL; /*not a property*/
spec->value = NULL;
}
else if (spec->value[0] == '>'
|| spec->value[0] == '<') {
/* an interrupt spec */
spec->property = NULL;
}
else {
chp = strrchr(spec->path, '/');
if (chp == NULL) {
spec->property = spec->path;
spec->path = strchr(spec->property, '\0');
}
else {
*chp = '\0';
spec->property = chp+1;
}
}
/* and mark the rest as invalid */
spec->name = NULL;
spec->unit = NULL;
spec->args = NULL;
spec->last_name = NULL;
spec->last_unit = NULL;
spec->last_args = NULL;
return 1;
}
/* given a device specifier break it up into its main components -
path and property name - assuming that the last `device' is a
property name. */
STATIC_INLINE_DEVICE\
(int)
split_property_specifier(const char *property_specifier,
name_specifier *spec)
{
if (split_device_specifier(property_specifier, spec)) {
if (spec->property == NULL) {
/* force the last name to be a property name */
char *chp = strrchr(spec->path, '/');
if (chp == NULL) {
spec->property = spec->path;
spec->path = strrchr(spec->property, '\0');;
}
else {
*chp = '\0';
spec->property = chp+1;
}
}
return 1;
}
else
return 0;
}
/* parse the next device name and split it up, return 0 when no more
names to parse */
STATIC_INLINE_DEVICE\
(int)
split_device_name(name_specifier *spec)
{
char *chp;
/* remember what came before */
spec->last_name = spec->name;
spec->last_unit = spec->unit;
spec->last_args = spec->args;
/* finished? */
if (spec->path[0] == '\0') {
spec->name = NULL;
spec->unit = NULL;
spec->args = NULL;
return 0;
}
/* break the device name from the path */
spec->name = spec->path;
chp = strchr(spec->name, '/');
if (chp == NULL)
spec->path = strchr(spec->name, '\0');
else {
spec->path = chp+1;
*chp = '\0';
}
/* now break out the unit */
chp = strchr(spec->name, '@');
if (chp == NULL) {
spec->unit = NULL;
chp = spec->name;
}
else {
*chp = '\0';
chp += 1;
spec->unit = chp;
}
/* finally any args */
chp = strchr(chp, ':');
if (chp == NULL)
spec->args = NULL;
else {
*chp = '\0';
spec->args = chp+1;
}
return 1;
}
/* parse the value, returning the next non-space token */
STATIC_INLINE_DEVICE\
(char *)
split_value(name_specifier *spec)
{
char *token;
if (spec->value == NULL)
return NULL;
/* skip leading white space */
while (isspace(spec->value[0]))
spec->value++;
if (spec->value[0] == '\0') {
spec->value = NULL;
return NULL;
}
token = spec->value;
/* find trailing space */
while (spec->value[0] != '\0' && !isspace(spec->value[0]))
spec->value++;
/* chop this value out */
if (spec->value[0] != '\0') {
spec->value[0] = '\0';
spec->value++;
}
return token;
}
/* traverse the path specified by spec starting at current */
STATIC_INLINE_DEVICE\
(device *)
split_find_device(device *current,
name_specifier *spec)
{
/* strip off (and process) any leading ., .., ./ and / */
while (1) {
if (strncmp(spec->path, "/", strlen("/")) == 0) {
/* cd /... */
while (current != NULL && current->parent != NULL)
current = current->parent;
spec->path += strlen("/");
}
else if (strncmp(spec->path, "./", strlen("./")) == 0) {
/* cd ./... */
current = current;
spec->path += strlen("./");
}
else if (strncmp(spec->path, "../", strlen("../")) == 0) {
/* cd ../... */
if (current != NULL && current->parent != NULL)
current = current->parent;
spec->path += strlen("../");
}
else if (strcmp(spec->path, ".") == 0) {
/* cd . */
current = current;
spec->path += strlen(".");
}
else if (strcmp(spec->path, "..") == 0) {
/* cd . */
if (current != NULL && current->parent != NULL)
current = current->parent;
spec->path += strlen("..");
}
else
break;
}
/* now go through the path proper */
if (current == NULL) {
split_device_name(spec);
return current;
}
while (split_device_name(spec)) {
device_unit phys;
device *child;
current->callback->convert.decode_unit(current, spec->unit, &phys);
for (child = current->children; child != NULL; child = child->sibling) {
if (strcmp(spec->name, child->name) == 0) {
if (phys.nr_cells == 0
|| memcmp(&phys, &child->unit_address, sizeof(device_unit)) == 0)
break;
}
}
if (child == NULL)
return current; /* search failed */
current = child;
}
return current;
}
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 = 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);
}
return new_device;
}
STATIC_INLINE_DEVICE\
(device *)
device_template_create_device(device *parent,
const char *name,
const char *unit_address,
const char *args)
{
const device_descriptor *const *table;
int name_len;
char *chp;
chp = strchr(name, '@');
name_len = (chp == NULL ? strlen(name) : chp - name);
for (table = device_table; *table != NULL; table++) {
const device_descriptor *descr;
for (descr = *table; descr->name != NULL; descr++) {
if (strncmp(name, descr->name, name_len) == 0
&& (descr->name[name_len] == '\0'
|| descr->name[name_len] == '@')) {
device_unit address = { 0 };
void *data = NULL;
if (parent != NULL && parent->callback->convert.decode_unit != NULL)
parent->callback->convert.decode_unit(parent,
unit_address,
&address);
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\n", name);
return NULL;
}
/* 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\n");
instance->owner = me;
instance->parent = parent;
instance->data = data;
instance->args = (args == NULL ? NULL : strdup(args));
instance->path = (path == NULL ? NULL : strdup(path));
instance->callback = callbacks;
/*instance->unit*/
if (me != NULL) {
instance->next = me->instances;
me->instances = instance;
}
if (parent != NULL) {
device_instance **previous;
parent->child = instance;
instance->owner = parent->owner;
instance->next = parent->next;
/* replace parent with this new node */
previous = &instance->owner->instances;
while (*previous != parent)
previous = &(*previous)->next;
*previous = instance;
}
return instance;
}
INLINE_DEVICE\
(device_instance *)
device_create_instance(device *me,
const char *device_specifier)
{
/* find the device node */
name_specifier spec;
if (!split_device_specifier(device_specifier, &spec))
return NULL;
me = split_find_device(me, &spec);
if (spec.name != NULL)
return NULL;
/* create the instance */
if (me->callback->instance_create == NULL)
device_error(me, "no instance_create method\n");
return me->callback->instance_create(me,
device_specifier, spec.last_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;
device_instance **curr;
if (instance->callback->delete == NULL)
device_error(me, "no delete method\n");
instance->callback->delete(instance);
if (instance->args != NULL)
zfree(instance->args);
if (instance->path != NULL)
zfree(instance->path);
curr = &me->instances;
while (*curr != NULL && *curr != instance)
curr = &(*curr)->next;
ASSERT(*curr != NULL);
*curr = instance->next;
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\n");
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\n");
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\n");
return instance->callback->seek(instance, pos_hi, pos_lo);
}
INLINE_DEVICE\
(unsigned_word)
device_instance_claim(device_instance *instance,
unsigned_word address,
unsigned_word length,
unsigned_word alignment)
{
device *me = instance->owner;
if (instance->callback->claim == NULL)
device_error(me, "no claim method\n");
return instance->callback->claim(instance, address, length, alignment);
}
INLINE_DEVICE\
(void)
device_instance_release(device_instance *instance,
unsigned_word address,
unsigned_word length)
{
device *me = instance->owner;
if (instance->callback->release == NULL)
device_error(me, "no release method\n");
instance->callback->release(instance, address, length);
}
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 initialization: */
STATIC_INLINE_DEVICE\
(void)
clean_device(device *root,
void *data)
{
psim *system;
system = (psim*)data;
clean_device_interrupt_edges(&root->interrupt_destinations);
clean_device_instances(root);
clean_device_properties(root);
}
STATIC_INLINE_DEVICE\
(void)
init_device_address(device *me,
void *data)
{
psim *system = (psim*)data;
TRACE(trace_device_init, ("init_device_address() initializing %s\n", me->path));
me->system = system; /* misc things not known until now */
if (me->callback->init.address != NULL)
me->callback->init.address(me);
}
STATIC_INLINE_DEVICE\
(void)
init_device_data(device *me,
void *data)
{
TRACE(trace_device_init, ("device_init_data() initializing %s\n", me->path));
if (me->callback->init.data != NULL)
me->callback->init.data(me);
}
INLINE_DEVICE\
(void)
device_tree_init(device *root,
psim *system)
{
TRACE(trace_device_tree, ("device_tree_init(root=0x%lx, system=0x%lx)\n",
(long)root,
(long)system));
/* remove the old, rebuild the new */
device_tree_traverse(root, clean_device, NULL, system);
TRACE(trace_tbd, ("Need to dump the device tree here\n"));
device_tree_traverse(root, init_device_address, NULL, system);
device_tree_traverse(root, init_device_properties, NULL, system);
device_tree_traverse(root, init_device_data, NULL, system);
TRACE(trace_device_tree, ("device_tree_init() = void\n"));
}
/* Device Properties: */
/* local - not available externally */
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;
void *new_array = NULL;
void *new_init_array = 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 = strdup(property);
new_value->type = type;
new_value->sizeof_array = sizeof_array;
new_array = (sizeof_array > 0 ? zalloc(sizeof_array) : NULL);
new_value->array = new_array;
new_value->owner = me;
new_value->original = original;
new_value->disposition = disposition;
if (sizeof_array > 0)
memcpy(new_array, array, sizeof_array);
/* insert the value into the list */
new_entry = ZALLOC(device_property_entry);
*insertion_point = new_entry;
new_entry->sizeof_init_array = sizeof_init_array;
new_init_array = (sizeof_init_array > 0 ? zalloc(sizeof_init_array) : NULL);
new_entry->init_array = new_init_array;
new_entry->value = new_value;
if (sizeof_init_array > 0)
memcpy(new_init_array, init_array, sizeof_init_array);
}
/* 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,
const device_property *original)
{
/* find the property */
device_property_entry *entry = me->properties;
while (entry != NULL) {
if (strcmp(entry->value->name, property) == 0) {
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\n", 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;
}
entry = entry->next;
}
device_add_property(me, property, type,
NULL, 0, array, sizeof_array,
original, 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;
device_property *property = current->value;
switch (current->value->disposition) {
case permenant_object:
{
/* delete the property, and replace it with the original */
ASSERT(((property->array == NULL) == (current->init_array == NULL))
|| property->type == ihandle_property);
if (current->init_array != NULL) {
zfree((void*)current->value->array);
current->value->array = NULL;
if (property->type != ihandle_property) {
device_set_property(me, property->name,
property->type,
current->init_array, current->sizeof_init_array,
NULL);
}
}
delete_point = &(*delete_point)->next;
}
break;
case tempoary_object:
{
/* zap the actual property, was created during simulation run */
*delete_point = current->next;
if (current->value->array != NULL)
zfree((void*)current->value->array);
zfree(current->value);
zfree(current);
}
break;
}
}
}
STATIC_INLINE_DEVICE\
(void)
init_device_properties(device *me,
void *data)
{
device_property_entry *property = me->properties;
while (property != NULL) {
/* now do the phandles */
if (property->value->type == ihandle_property) {
if (property->value->original != NULL) {
const device_property *original = property->value->original;
if (original->array == NULL) {
init_device_properties(original->owner, data);
}
ASSERT(original->array != NULL);
device_set_property(me, property->value->name,
ihandle_property,
original->array, original->sizeof_array, NULL);
}
else {
device_instance *instance =
device_create_instance(me, (char*)property->init_array);
unsigned32 ihandle = H2BE_4(device_instance_to_external(instance));
device_set_property(me, property->value->name,
ihandle_property,
&ihandle, sizeof(ihandle), NULL);
}
}
property = property->next;
}
}
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)
{
name_specifier spec;
if (me == NULL) {
return NULL;
}
else if (property == NULL || strcmp(property, "") == 0) {
if (me->properties == NULL)
return NULL;
else
return me->properties->value;
}
else if (split_property_specifier(property, &spec)) {
me = split_find_device(me, &spec);
if (spec.name == NULL) { /*got to root*/
device_property_entry *entry = me->properties;
while (entry != NULL) {
if (strcmp(entry->value->name, spec.property) == 0)
return entry->value;
entry = entry->next;
}
}
}
return NULL;
}
STATIC_INLINE_DEVICE\
(void)
device_add_array_property(device *me,
const char *property,
const void *array,
int sizeof_array)
{
TRACE(trace_devices,
("device_add_array_property(me=0x%lx, property=%s, ...)\n",
(long)me, property));
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)
{
TRACE(trace_devices,
("device_set_array_property(me=0x%lx, property=%s, ...)\n",
(long)me, property));
device_set_property(me, property, array_property, array, sizeof_array, NULL);
}
INLINE_DEVICE\
(const device_property *)
device_find_array_property(device *me,
const char *property)
{
const device_property *node;
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 != array_property)
device_error(me, "property %s not found or of wrong type\n", property);
return node;
}
STATIC_INLINE_DEVICE\
(void)
device_add_boolean_property(device *me,
const char *property,
int boolean)
{
signed32 new_boolean = (boolean ? -1 : 0);
TRACE(trace_devices,
("device_add_boolean(me=0x%lx, property=%s, boolean=%d)\n",
(long)me, property, boolean));
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;
unsigned32 boolean;
TRACE(trace_devices,
("device_find_boolean(me=0x%lx, property=%s)\n",
(long)me, property));
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\n", property);
ASSERT(sizeof(boolean) == node->sizeof_array);
memcpy(&boolean, node->array, sizeof(boolean));
return boolean;
}
STATIC_INLINE_DEVICE\
(void)
device_add_ihandle_property(device *me,
const char *property,
const char *path)
{
TRACE(trace_devices,
("device_add_ihandle_property(me=0x%lx, property=%s, path=%s)\n",
(long)me, property, path));
device_add_property(me, property, ihandle_property,
path, strlen(path) + 1,
NULL, 0,
NULL, permenant_object);
}
INLINE_DEVICE\
(device_instance *)
device_find_ihandle_property(device *me,
const char *property)
{
const device_property *node;
unsigned32 ihandle;
device_instance *instance;
TRACE(trace_devices,
("device_find_ihandle_property(me=0x%lx, property=%s)\n",
(long)me, property));
node = device_find_property(me, property);
if (node == NULL || node->type != ihandle_property)
device_error(me, "property %s not found or of wrong type\n", property);
if (node->array == NULL)
device_error(me, "property %s not yet initialized\n", property);
ASSERT(sizeof(ihandle) == node->sizeof_array);
memcpy(&ihandle, node->array, sizeof(ihandle));
BE2H(ihandle);
instance = external_to_device_instance(me, ihandle);
ASSERT(instance != NULL);
return instance;
}
STATIC_INLINE_DEVICE\
(void)
device_add_integer_property(device *me,
const char *property,
signed32 integer)
{
TRACE(trace_devices,
("device_add_integer_property(me=0x%lx, property=%s, integer=%ld)\n",
(long)me, property, (long)integer));
H2BE(integer);
device_add_property(me, property, integer_property,
&integer, sizeof(integer),
&integer, sizeof(integer),
NULL, permenant_object);
}
INLINE_DEVICE\
(signed_word)
device_find_integer_property(device *me,
const char *property)
{
const device_property *node;
signed32 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\n", property);
ASSERT(sizeof(integer) == node->sizeof_array);
memcpy(&integer, node->array, sizeof(integer));
BE2H(integer);
return integer;
}
STATIC_INLINE_DEVICE\
(void)
device_add_string_property(device *me,
const char *property,
const char *string)
{
TRACE(trace_devices,
("device_add_property(me=0x%lx, property=%s, string=%s)\n",
(long)me, property, 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;
TRACE(trace_devices,
("device_find_string(me=0x%lx, property=%s)\n",
(long)me, property));
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\n", property);
string = node->array;
ASSERT(strlen(string) + 1 == node->sizeof_array);
return string;
}
STATIC_INLINE_DEVICE\
(void)
device_add_duplicate_property(device *me,
const char *property,
const device_property *original)
{
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\n");
device_add_property(me, property,
original->type,
original->array, original->sizeof_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\n");
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\n");
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\n");
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\n");
return me->callback->dma.write_buffer(me, source, space,
addr, nr_bytes,
violate_read_only_section);
}
INLINE_DEVICE\
(void)
device_attach_address(device *me,
const char *name,
attach_type attach,
int space,
unsigned_word addr,
unsigned nr_bytes,
access_type access,
device *who) /*callback/default*/
{
if (me->callback->address.attach == NULL)
device_error(me, "no address_attach method\n");
me->callback->address.attach(me, name, attach, space,
addr, nr_bytes, access, who);
}
INLINE_DEVICE\
(void)
device_detach_address(device *me,
const char *name,
attach_type attach,
int space,
unsigned_word addr,
unsigned nr_bytes,
access_type access,
device *who) /*callback/default*/
{
if (me->callback->address.detach == NULL)
device_error(me, "no address_detach method\n");
me->callback->address.detach(me, name, attach, space,
addr, nr_bytes, access, who);
}
/* 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\n");
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\n", 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\
(int)
device_interrupt_decode(device *me,
const char *port_name)
{
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->bound > ports->number) {
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->bound)
device_error(me, "Interrupt port %s out of range\n",
port_name);
return port;
}
}
}
else if (strcmp(port_name, ports->name) == 0)
return ports->number;
ports++;
}
}
}
device_error(me, "Unreconized interrupt port %s\n", port_name);
return 0;
}
INLINE_DEVICE\
(int)
device_interrupt_encode(device *me,
int port_number,
char *buf,
int sizeof_buf)
{
const device_interrupt_port_descriptor *ports = NULL;
ports = me->callback->interrupt.ports;
if (ports != NULL) {
while (ports->name != NULL) {
if (ports->bound > ports->number) {
if (port_number >= ports->number
&& port_number < ports->bound) {
strcpy(buf, ports->name);
sprintf(buf + strlen(buf), "%d", port_number - ports->number);
if (strlen(buf) >= sizeof_buf)
error("device_interrupt_encode:buffer overflow\n");
return strlen(buf);
}
}
else {
if (ports->number == port_number) {
if (strlen(ports->name) >= sizeof_buf)
error("device_interrupt_encode: buffer overflow\n");
strcpy(buf, ports->name);
return strlen(buf);
}
}
ports++;
}
}
sprintf(buf, "%d", port_number);
if (strlen(buf) >= sizeof_buf)
error("device_interrupt_encode: buffer overflow\n");
return strlen(buf);
}
/* IOCTL: */
EXTERN_DEVICE\
(int)
device_ioctl(device *me,
cpu *processor,
unsigned_word cia,
...)
{
int status;
va_list ap;
va_start(ap, cia);
if (me->callback->ioctl == NULL)
device_error(me, "no ioctl method\n");
status = me->callback->ioctl(me, processor, cia, 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\n");
if (me == NULL)
error("device: %s\n", message);
else
error("%s: %s\n", me->path, message);
while(1);
}
/* Tree utilities: */
EXTERN_DEVICE\
(device *)
device_tree_add_parsed(device *current,
const char *fmt,
...)
{
char device_specifier[1024];
name_specifier spec;
/* format the path */
{
va_list ap;
va_start(ap, fmt);
vsprintf(device_specifier, fmt, ap);
va_end(ap);
if (strlen(device_specifier) >= sizeof(device_specifier))
error("device_tree_add_parsed: buffer overflow\n");
}
/* break it up */
if (!split_device_specifier(device_specifier, &spec))
device_error(current, "error parsing %s\n", device_specifier);
/* fill our tree with its contents */
current = split_find_device(current, &spec);
/* add any additional devices as needed */
if (spec.name != NULL) {
do {
current =
device_template_create_device(current, spec.name, spec.unit, spec.args);
} while (split_device_name(&spec));
}
/* is there an interrupt spec */
if (spec.property == NULL
&& spec.value != NULL) {
char *op = split_value(&spec);
switch (op[0]) {
case '>':
{
char *my_port_name = split_value(&spec);
char *dest_port_name = split_value(&spec);
device *dest = device_tree_find_device(current, split_value(&spec));
int my_port = device_interrupt_decode(current, my_port_name);
int dest_port = device_interrupt_decode(dest, dest_port_name);
device_interrupt_attach(current,
my_port,
dest,
dest_port,
permenant_object);
}
break;
default:
device_error(current, "unreconised interrupt spec %s\n", spec.value);
break;
}
}
/* is there a property */
if (spec.property != NULL) {
if (strcmp(spec.value, "true") == 0)
device_add_boolean_property(current, spec.property, 1);
else if (strcmp(spec.value, "false") == 0)
device_add_boolean_property(current, spec.property, 0);
else {
const device_property *property;
switch (spec.value[0]) {
case '*':
{
spec.value++;
device_add_ihandle_property(current, spec.property, spec.value);
}
break;
case '-': case '+':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
unsigned long ul = strtoul(spec.value, &spec.value, 0);
device_add_integer_property(current, spec.property, ul);
}
break;
case '[':
{
unsigned8 words[1024];
char *curr = spec.value + 1;
int nr_words = 0;
while (1) {
char *next;
words[nr_words] = H2BE_1(strtoul(curr, &next, 0));
if (curr == next)
break;
curr = next;
nr_words += 1;
}
device_add_array_property(current, spec.property,
words, sizeof(words[0]) * nr_words);
}
break;
case '{':
{
unsigned32 words[1024];
char *curr = spec.value + 1;
int nr_words = 0;
while (1) {
char *next;
words[nr_words] = H2BE_4(strtoul(curr, &next, 0));
if (curr == next)
break;
curr = next;
nr_words += 1;
}
device_add_array_property(current, spec.property,
words, sizeof(words[0]) * nr_words);
}
break;
case '"':
spec.value++;
default:
device_add_string_property(current, spec.property, spec.value);
break;
case '!':
spec.value++;
property = device_find_property(current, spec.value);
if (property == NULL)
device_error(current, "property %s not found\n", spec.value);
device_add_duplicate_property(current,
spec.property,
property);
break;
}
}
}
return current;
}
INLINE_DEVICE\
(void)
device_tree_traverse(device *root,
device_tree_traverse_function *prefix,
device_tree_traverse_function *postfix,
void *data)
{
device *child;
if (prefix != NULL)
prefix(root, data);
for (child = root->children; child != NULL; child = child->sibling) {
device_tree_traverse(child, prefix, postfix, data);
}
if (postfix != NULL)
postfix(root, data);
}
INLINE_DEVICE\
(void)
device_tree_print_device(device *me,
void *ignore_or_null)
{
const device_property *property;
device_interrupt_edge *interrupt_edge;
/* output my name */
printf_filtered("%s\n", me->path);
/* properties */
for (property = device_find_property(me, NULL);
property != NULL;
property = device_next_property(property)) {
printf_filtered("%s/%s", me->path, property->name);
if (property->original != NULL) {
printf_filtered(" !");
printf_filtered("%s/%s\n", property->original->owner->path,
property->original->name);
}
else {
switch (property->type) {
case array_property:
{
if ((property->sizeof_array % sizeof(unsigned32)) == 0) {
unsigned32 *w = (unsigned32*)property->array;
printf_filtered(" {");
while ((char*)w - (char*)property->array < property->sizeof_array) {
printf_filtered(" 0x%lx", BE2H_4(*w));
w++;
}
}
else {
unsigned8 *w = (unsigned8*)property->array;
printf_filtered(" [");
while ((char*)w - (char*)property->array < property->sizeof_array) {
printf_filtered(" 0x%2x", BE2H_1(*w));
w++;
}
}
printf_filtered("\n");
}
break;
case boolean_property:
{
int b = device_find_boolean_property(me, property->name);
printf_filtered(" %s\n", b ? "true" : "false");
}
break;
case ihandle_property:
{
if (property->array != NULL) {
device_instance *i = device_find_ihandle_property(me, property->name);
printf_filtered(" *%s\n", i->path);
}
else {
/* drats, the instance hasn't yet been created. Grub
around and find the path that will be used to create
the ihandle */
device_property_entry *entry = me->properties;
while (entry->value != property) {
entry = entry->next;
ASSERT(entry != NULL);
}
ASSERT(entry->init_array != NULL);
printf_filtered(" *%s\n", (char*)entry->init_array);
}
}
break;
case integer_property:
{
unsigned_word w = device_find_integer_property(me, property->name);
printf_filtered(" 0x%lx\n", (unsigned long)w);
}
break;
case string_property:
{
const char *s = device_find_string_property(me, property->name);
printf_filtered(" \"%s\n", s);
}
break;
}
}
}
/* interrupts */
for (interrupt_edge = me->interrupt_destinations;
interrupt_edge != NULL;
interrupt_edge = interrupt_edge->next) {
char src[32];
char dst[32];
device_interrupt_encode(me, interrupt_edge->my_port, src, sizeof(src));
device_interrupt_encode(interrupt_edge->dest,
interrupt_edge->dest_port, dst, sizeof(dst));
printf_filtered("%s > %s %s %s\n",
me->path,
src, dst,
interrupt_edge->dest->path);
}
}
INLINE_DEVICE\
(device *)
device_tree_find_device(device *root,
const char *path)
{
device *node;
name_specifier spec;
TRACE(trace_device_tree,
("device_tree_find_device_tree(root=0x%lx, path=%s)\n",
(long)root, path));
/* parse the path */
split_device_specifier(path, &spec);
if (spec.value != NULL)
return NULL; /* something wierd */
/* now find it */
node = split_find_device(root, &spec);
if (spec.name != NULL)
return NULL; /* not a leaf */
return node;
}
INLINE_DEVICE\
(void)
device_usage(int verbose)
{
if (verbose == 1) {
const device_descriptor *const *table;
int pos;
printf_filtered("\n");
printf_filtered("A device/property specifier has the form:\n");
printf_filtered("\n");
printf_filtered(" /path/to/a/device [ property-value ]\n");
printf_filtered("\n");
printf_filtered("and a possible device is\n");
printf_filtered("\n");
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) {
const device_descriptor *const *table;
printf_filtered("\n");
printf_filtered("A device/property specifier (<spec>) has the format:\n");
printf_filtered("\n");
printf_filtered(" <spec> ::= <path> [ <value> ] ;\n");
printf_filtered(" <path> ::= { <prefix> } { <node> \"/\" } <node> ;\n");
printf_filtered(" <prefix> ::= ( | \"/\" | \"../\" | \"./\" ) ;\n");
printf_filtered(" <node> ::= <name> [ \"@\" <unit> ] [ \":\" <args> ] ;\n");
printf_filtered(" <unit> ::= <number> { \",\" <number> } ;\n");
printf_filtered("\n");
printf_filtered("Where:\n");
printf_filtered("\n");
printf_filtered(" <name> is the name of a device (list below)\n");
printf_filtered(" <unit> is the unit-address relative to the parent bus\n");
printf_filtered(" <args> additional arguments used when creating the device\n");
printf_filtered(" <value> ::= ( <number> # integer property\n");
printf_filtered(" | \"[\" { <number> } # array property (byte)\n");
printf_filtered(" | \"{\" { <number> } # array property (cell)\n");
printf_filtered(" | [ \"true\" | \"false\" ] # boolean property\n");
printf_filtered(" | \"*\" <path> # ihandle property\n");
printf_filtered(" | \"!\" <path> # copy property\n");
printf_filtered(" | \">\" [ <number> ] <path> # attach interrupt\n");
printf_filtered(" | \"<\" <path> # attach child interrupt\n");
printf_filtered(" | \"\\\"\" <text> # string property\n");
printf_filtered(" | <text> # string property\n");
printf_filtered(" ) ;\n");
printf_filtered("\n");
printf_filtered("And the following are valid device names:\n");
printf_filtered("\n");
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);
}
}
}
}
/* External representation */
INLINE_DEVICE\
(device *)
external_to_device(device *tree_member,
unsigned32 phandle)
{
device *root = device_tree_find_device(tree_member, "/");
device *me = cap_internal(root->phandles, phandle);
return me;
}
INLINE_DEVICE\
(unsigned32)
device_to_external(device *me)
{
device *root = device_tree_find_device(me, "/");
unsigned32 phandle = cap_external(root->phandles, me);
return phandle;
}
INLINE_DEVICE\
(device_instance *)
external_to_device_instance(device *tree_member,
unsigned32 ihandle)
{
device *root = device_tree_find_device(tree_member, "/");
device_instance *instance = cap_internal(root->ihandles, ihandle);
return instance;
}
INLINE_DEVICE\
(unsigned32)
device_instance_to_external(device_instance *instance)
{
device *root = device_tree_find_device(instance->owner, "/");
unsigned32 ihandle = cap_external(root->ihandles, instance);
return ihandle;
}
#endif /* _DEVICE_C_ */