📄 device.c
字号:
/* 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 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#ifndef _DEVICE_C_#define _DEVICE_C_#include <stdio.h>#include "device_table.h"#include "cap.h"#include "events.h"#include "psim.h"#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_STRING_H#include <string.h>#else#ifdef HAVE_STRINGS_H#include <strings.h>#endif#endif#include <ctype.h>STATIC_INLINE_DEVICE (void) clean_device_properties(device *);/* property entries */typedef struct _device_property_entry device_property_entry;struct _device_property_entry { device_property_entry *next; device_property *value; const void *init_array; unsigned sizeof_init_array;};/* Interrupt edges */typedef struct _device_interrupt_edge device_interrupt_edge;struct _device_interrupt_edge { int my_port; device *dest; int dest_port; device_interrupt_edge *next; object_disposition disposition;};STATIC_INLINE_DEVICE\(void)attach_device_interrupt_edge(device_interrupt_edge **list, int my_port, device *dest, int dest_port, object_disposition disposition){ device_interrupt_edge *new_edge = ZALLOC(device_interrupt_edge); new_edge->my_port = my_port; new_edge->dest = dest; new_edge->dest_port = dest_port; new_edge->next = *list; new_edge->disposition = disposition; *list = new_edge;}STATIC_INLINE_DEVICE\(void)detach_device_interrupt_edge(device *me, device_interrupt_edge **list, int my_port, device *dest, int dest_port){ while (*list != NULL) { device_interrupt_edge *old_edge = *list; if (old_edge->dest == dest && old_edge->dest_port == dest_port && old_edge->my_port == my_port) { if (old_edge->disposition == permenant_object) device_error(me, "attempt to delete permenant interrupt"); *list = old_edge->next; zfree(old_edge); return; } } device_error(me, "attempt to delete unattached interrupt");}STATIC_INLINE_DEVICE\(void)clean_device_interrupt_edges(device_interrupt_edge **list){ while (*list != NULL) { device_interrupt_edge *old_edge = *list; switch (old_edge->disposition) { case permenant_object: list = &old_edge->next; break; case tempoary_object: *list = old_edge->next; zfree(old_edge); break; } }}/* A device */struct _device { /* my name is ... */ const char *name; device_unit unit_address; const char *path; int nr_address_cells; int nr_size_cells; /* device tree */ device *parent; device *children; device *sibling; /* its template methods */ void *data; /* device specific data */ const device_callbacks *callback; /* device properties */ device_property_entry *properties; /* interrupts */ device_interrupt_edge *interrupt_destinations; /* any open instances of this device */ device_instance *instances; /* the internal/external mappings and other global requirements */ cap *ihandles; cap *phandles; psim *system; /* debugging */ int trace;};/* an instance of a device */struct _device_instance { void *data; char *args; char *path; const device_instance_callbacks *callback; /* the root instance */ device *owner; device_instance *next; /* interposed instance */ device_instance *parent; device_instance *child;};/* creation */STATIC_INLINE_DEVICE\(const char *)device_full_name(device *leaf, char *buf, unsigned sizeof_buf){ /* get a buffer */ char full_name[1024]; if (buf == (char*)0) { buf = full_name; sizeof_buf = sizeof(full_name); } /* construct a name */ if (leaf->parent == NULL) { if (sizeof_buf < 1) error("device_full_name: buffer overflow"); *buf = '\0'; } else { char unit[1024]; device_full_name(leaf->parent, buf, sizeof_buf); if (leaf->parent != NULL && device_encode_unit(leaf->parent, &leaf->unit_address, unit+1, sizeof(unit)-1) > 0) unit[0] = '@'; else unit[0] = '\0'; if (strlen(buf) + strlen("/") + strlen(leaf->name) + strlen(unit) >= sizeof_buf) error("device_full_name: buffer overflow"); strcat(buf, "/"); strcat(buf, leaf->name); strcat (buf, unit); } /* return it usefully */ if (buf == full_name) buf = (char *) strdup(full_name); return buf;}STATIC_INLINE_DEVICE\(device *)device_create_from(const char *name, const device_unit *unit_address, void *data, const device_callbacks *callbacks, device *parent){ device *new_device = ZALLOC(device); /* insert it into the device tree */ new_device->parent = parent; new_device->children = NULL; if (parent != NULL) { device **sibling = &parent->children; while ((*sibling) != NULL) sibling = &(*sibling)->sibling; *sibling = new_device; } /* give it a name */ new_device->name = (char *) strdup(name); new_device->unit_address = *unit_address; new_device->path = device_full_name(new_device, NULL, 0); /* its template */ new_device->data = data; new_device->callback = callbacks; /* its properties - already null */ /* interrupts - already null */ /* mappings - if needed */ if (parent == NULL) { new_device->ihandles = cap_create(name); new_device->phandles = cap_create(name); } else { new_device->ihandles = device_root(parent)->ihandles; new_device->phandles = device_root(parent)->phandles; } cap_add(new_device->phandles, new_device); return new_device;}INLINE_DEVICE\(device *)device_create(device *parent, const char *base, const char *name, const char *unit_address, const char *args){ const device_descriptor *const *table; for (table = device_table; *table != NULL; table++) { const device_descriptor *descr; for (descr = *table; descr->name != NULL; descr++) { if (strcmp(base, descr->name) == 0) { device_unit address = { 0 }; void *data = NULL; if (parent != NULL) if (device_decode_unit(parent, unit_address, &address) < 0) device_error(parent, "invalid address %s for device %s", unit_address, name); if (descr->creator != NULL) data = descr->creator(name, &address, args); return device_create_from(name, &address, data, descr->callbacks, parent); } } } device_error(parent, "attempt to attach unknown device %s", name); return NULL;}INLINE_DEVICE\(void)device_usage(int verbose){ const device_descriptor *const *table; if (verbose == 1) { int pos = 0; for (table = device_table; *table != NULL; table++) { const device_descriptor *descr; for (descr = *table; descr->name != NULL; descr++) { pos += strlen(descr->name) + 2; if (pos > 75) { pos = strlen(descr->name) + 2; printf_filtered("\n"); } printf_filtered(" %s", descr->name); } printf_filtered("\n"); } } if (verbose > 1) { for (table = device_table; *table != NULL; table++) { const device_descriptor *descr; for (descr = *table; descr->name != NULL; descr++) { printf_filtered(" %s:\n", descr->name); /* interrupt ports */ if (descr->callbacks->interrupt.ports != NULL) { const device_interrupt_port_descriptor *ports = descr->callbacks->interrupt.ports; printf_filtered(" interrupt ports:"); while (ports->name != NULL) { printf_filtered(" %s", ports->name); ports++; } printf_filtered("\n"); } /* general info */ if (descr->callbacks->usage != NULL) descr->callbacks->usage(verbose); } } }} /* Device node: */INLINE_DEVICE\(device *)device_parent(device *me){ return me->parent;}INLINE_DEVICE\(device *)device_root(device *me){ ASSERT(me != NULL); while (me->parent != NULL) me = me->parent; return me;}INLINE_DEVICE\(device *)device_sibling(device *me){ return me->sibling;}INLINE_DEVICE\(device *)device_child(device *me){ return me->children;}INLINE_DEVICE\(const char *)device_name(device *me){ return me->name;}INLINE_DEVICE\(const char *)device_path(device *me){ return me->path;}INLINE_DEVICE\(void *)device_data(device *me){ return me->data;}INLINE_DEVICE\(psim *)device_system(device *me){ return me->system;}INLINE_DEVICE\(const device_unit *)device_unit_address(device *me){ return &me->unit_address;}INLINE_DEVICE\(int)device_address_to_attach_address(device *me, const device_unit *address, int *attach_space, unsigned_word *attach_address, device *client){ if (me->callback->convert.address_to_attach_address == NULL) device_error(me, "no convert.address_to_attach_address method"); return me->callback->convert.address_to_attach_address(me, address, attach_space, attach_address, client);}INLINE_DEVICE\(int)device_size_to_attach_size(device *me, const device_unit *size, unsigned *nr_bytes, device *client){ if (me->callback->convert.size_to_attach_size == NULL) device_error(me, "no convert.size_to_attach_size method"); return me->callback->convert.size_to_attach_size(me, size, nr_bytes, client);}INLINE_DEVICE\(int)device_decode_unit(device *bus, const char *unit, device_unit *address){ if (bus->callback->convert.decode_unit == NULL) device_error(bus, "no convert.decode_unit method"); return bus->callback->convert.decode_unit(bus, unit, address);}INLINE_DEVICE\(int)device_encode_unit(device *bus, const device_unit *unit_address, char *buf, int sizeof_buf){ if (bus->callback->convert.encode_unit == NULL) device_error(bus, "no convert.encode_unit method"); return bus->callback->convert.encode_unit(bus, unit_address, buf, sizeof_buf);}INLINE_DEVICE\(unsigned)device_nr_address_cells(device *me){ if (me->nr_address_cells == 0) { if (device_find_property(me, "#address-cells") != NULL) me->nr_address_cells = device_find_integer_property(me, "#address-cells"); else me->nr_address_cells = 2; } return me->nr_address_cells;}INLINE_DEVICE\(unsigned)device_nr_size_cells(device *me){ if (me->nr_size_cells == 0) { if (device_find_property(me, "#size-cells") != NULL) me->nr_size_cells = device_find_integer_property(me, "#size-cells"); else me->nr_size_cells = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -