/* This file is part of the program psim. Copyright (C) 1994-1996, Andrew Cagney 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 #include "device_table.h" #include "cap.h" #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #else #ifdef HAVE_STRINGS_H #include #endif #endif #include 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 () has the format:\n"); printf_filtered("\n"); printf_filtered(" ::= [ ] ;\n"); printf_filtered(" ::= { } { \"/\" } ;\n"); printf_filtered(" ::= ( | \"/\" | \"../\" | \"./\" ) ;\n"); printf_filtered(" ::= [ \"@\" ] [ \":\" ] ;\n"); printf_filtered(" ::= { \",\" } ;\n"); printf_filtered("\n"); printf_filtered("Where:\n"); printf_filtered("\n"); printf_filtered(" is the name of a device (list below)\n"); printf_filtered(" is the unit-address relative to the parent bus\n"); printf_filtered(" additional arguments used when creating the device\n"); printf_filtered(" ::= ( # integer property\n"); printf_filtered(" | \"[\" { } # array property (byte)\n"); printf_filtered(" | \"{\" { } # array property (cell)\n"); printf_filtered(" | [ \"true\" | \"false\" ] # boolean property\n"); printf_filtered(" | \"*\" # ihandle property\n"); printf_filtered(" | \"!\" # copy property\n"); printf_filtered(" | \">\" [ ] # attach interrupt\n"); printf_filtered(" | \"<\" # attach child interrupt\n"); printf_filtered(" | \"\\\"\" # string property\n"); printf_filtered(" | # 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_ */