| /* This file is part of the program psim. |
| |
| Copyright (C) 1994-1997, 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 3 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, see <http://www.gnu.org/licenses/>. |
| |
| */ |
| |
| |
| #ifndef _DEVICE_C_ |
| #define _DEVICE_C_ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include <stdio.h> |
| |
| #include "device_table.h" |
| #include "cap.h" |
| |
| #include "events.h" |
| #include "psim.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| STATIC_INLINE_DEVICE (void) clean_device_properties(device *); |
| |
| /* property entries */ |
| |
| typedef struct _device_property_entry device_property_entry; |
| struct _device_property_entry { |
| device_property_entry *next; |
| device_property *value; |
| const void *init_array; |
| unsigned sizeof_init_array; |
| }; |
| |
| |
| /* Interrupt edges */ |
| |
| typedef struct _device_interrupt_edge device_interrupt_edge; |
| struct _device_interrupt_edge { |
| int my_port; |
| device *dest; |
| int dest_port; |
| device_interrupt_edge *next; |
| object_disposition disposition; |
| }; |
| |
| STATIC_INLINE_DEVICE\ |
| (void) |
| attach_device_interrupt_edge(device_interrupt_edge **list, |
| int my_port, |
| device *dest, |
| int dest_port, |
| object_disposition disposition) |
| { |
| device_interrupt_edge *new_edge = ZALLOC(device_interrupt_edge); |
| new_edge->my_port = my_port; |
| new_edge->dest = dest; |
| new_edge->dest_port = dest_port; |
| new_edge->next = *list; |
| new_edge->disposition = disposition; |
| *list = new_edge; |
| } |
| |
| STATIC_INLINE_DEVICE\ |
| (void) |
| detach_device_interrupt_edge(device *me, |
| device_interrupt_edge **list, |
| int my_port, |
| device *dest, |
| int dest_port) |
| { |
| while (*list != NULL) { |
| device_interrupt_edge *old_edge = *list; |
| if (old_edge->dest == dest |
| && old_edge->dest_port == dest_port |
| && old_edge->my_port == my_port) { |
| if (old_edge->disposition == permenant_object) |
| device_error(me, "attempt to delete permenant interrupt"); |
| *list = old_edge->next; |
| free(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; |
| free(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 */ |
| if (buf == NULL) { |
| sizeof_buf = 1024; |
| buf = malloc(sizeof_buf); |
| } |
| |
| /* 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 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) |
| free(instance->args); |
| if (instance->path != NULL) |
| free(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); |
| free(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) |
| free((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) { |
| free((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) |
| free((void*)current->value->array); |
| free(current->value); |
| free(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); |
| |
| free(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); |
| |
| free(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) |
| 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) |
| { |
| 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_ */ |