/* This file is part of the program psim. Copyright (C) 1994-1997, 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" #include "events.h" #include "psim.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 *); /* 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 = tree_instance(me, spec.full_path); 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) { /* enter the full path as the init array */ device_add_property(me, property, ihandle_property, ihandle->full_path, strlen(ihandle->full_path) + 1, NULL, 0, NULL, permenant_object); } INLINE_DEVICE\ (void) device_find_ihandle_runtime_property(device *me, const char *property, ihandle_runtime_property_spec *ihandle) { 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); ASSERT(entry->init_array != NULL); /* the full path */ ihandle->full_path = entry->init_array; } 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_ */