mac-rom/Toolbox/ExpansionBusMgr/DeviceTree.c

738 lines
24 KiB
C
Raw Permalink Normal View History

/*
File: DeviceTree.c
Contains: Expansion Mgr Device Tree Manipulation Routines
Written by: Al Kossow
Copyright: <EFBFBD> 1993-1994 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<SM5> 2/7/94 AK fix warning in universal builds
<SM5> 2/07/94 aek get rid of warning on universal builds
<SM4> 1/18/94 AK fix device tree init
*/
/*
* <4> 01/18/94 aek make sure adrs aren't updated on restart
* <3> 01/18/94 aek correct tree initialization code
* <2> 01/17/94 aek wire into Open Boot
* <1> 10/20/93 aek Created
*
*
* Device nodes can have other device nodes as children.
*
* The bulk of the Device Tree is built at system initialization time in
* the system heap from information passed to StartInit from Open Boot.
*
* It is slightly different from the Device Tree in Open Boot, since there
* are client calls to remove as well as add devices and properties to
* the tree.
*
* No assuptions should be made about what a nodeid represents. It may be
* a pointer, a handle, or a small integer, depending upon the underlying
* implemetation of the device tree routines.
*
*
* pascal NodeID DevTreeRoot();
* pascal OSErr DevTreeNodeInfo(NodeID node, NodeInfoPtr p);
* pascal NodeID DevTreeAddNode(NodeID parentNode, ulong attrib, char *name);
* pascal OSErr DevTreeDelNode(NodeID theNode);
* pascal OSErr DevTreeSetProperty(NodeID theNode, unsigned char *buf, long size);
* pascal OSErr DevTreeGetProperty(NodeID theNode, unsigned char *buf, long size);
*
*
* TODO:
* Real error code #'s
* Non-volatile properties
* Properties saved in the system file
*
*/
#include "ExpansionMgrInternal.h"
#include "Memory.h"
#ifndef TEST
#ifndef __ERRORS__
#include <Errors.h>
#endif
#ifndef __EXPANDMEMPRIV__
#include "ExpandMemPriv.h"
#endif
#endif
/**************************************************************************/
/* */
/* */
/* Private Device Tree Interface Routines */
/* */
/* */
/**************************************************************************/
/*
* Internal routine to allocate memory for a tree node
* Returns NIL if allocation fails
*/
Ptr AllocTreeMem(size)
{
#ifdef TEST
return(NewPtrClear(size));
#else
return(NewPtrSysClear(size));
#endif
}
/*
* Internal routine to free memory for a tree node
*/
FreeTreeMem(ptr)
Ptr ptr;
{
DisposPtr(ptr);
}
/*
* Internal routine to free data associated with a property
* Checks to make sure data isn't in ROM or is indirectly referenced
*/
FreePropData(pp)
PropertyNodePtr pp;
{
if(pp->attrib & (kAttribROMProp | kAttribDefProp))
return(-1);
DisposPtr((Ptr)pp);
}
/*
* CheckNodeName(name)
*
* Verify that name is a valid device tree name
* 31 chrs max, letters, digits ,',' ,'_', '+', or '-'
*
* Returns the number of characters in the name, or zero
* if there was an error
*
* TODO: really verify it..
*/
int CheckNodeName(name)
char *name;
{
register char *cp = name;
register int len = 0;
while( *cp && (len < 32)){
len++, cp++;
}
if(len == 32)
return(0);
else
return(len);
}
/*
* CheckPropertyName(name)
*
* Verify that the name is a valid property name
* 31 chrs max, no upper case, '/', '\', ':', '[', ']' or '@'
* Returns the number of characters in the name, or zero
* if there was an error
*
* TODO: really verify it..
*/
int CheckPropertyName(name)
char *name;
{
register char *cp = name;
register int len = 0;
while( *cp && (len < 32)){
len++, cp++;
}
if(len == 32)
return(0);
else
return(len);
}
/*
* Validate the NodeID that was passed to us
* Returns TRUE if NodeID actually exists in
* the tree by looking at all nodes starting
* at the root
*
* TODO: really verify it..
*/
Boolean CheckNodeID(theNode)
NodeID theNode;
{
#pragma unused(theNode);
return(TRUE);
}
/**************************************************************************/
/* */
/* Munge the tree passed to us by open boot */
/* */
/**************************************************************************/
cvtDevNode(dp, depth, DTBase)
DeviceNodePtr dp;
int depth;
unsigned long DTBase;
{
register DeviceNodePtr childptr;
register PropertyNodePtr propptr;
if(dp->parent)
dp->parent = (DeviceNodePtr)((unsigned long)(dp->parent) + DTBase);
dp->peer = (DeviceNodePtr)((unsigned long)(dp->peer) + DTBase);
if(dp->propertyList){
dp->propertyList = (PropertyNodePtr)((unsigned long)dp->propertyList + DTBase);
dp->propertyTail = (PropertyNodePtr)((unsigned long)dp->propertyTail + DTBase);
propptr = dp->propertyList;
do {
propptr->parent = (DeviceNodePtr)((unsigned long)propptr->parent + DTBase);
propptr->peer = (PropertyNodePtr)((unsigned long)propptr->peer + DTBase);
if(propptr->dataPtr)
propptr->dataPtr = (unsigned char *)propptr->dataPtr + DTBase;
propptr = propptr->peer;
} while (propptr != dp->propertyList);
}
if(dp->childList){
dp->childList = (DeviceNodePtr)((unsigned long)(dp->childList) + DTBase);
dp->childTail = (DeviceNodePtr)((unsigned long)(dp->childTail) + DTBase);
childptr = (DeviceNodePtr)dp->childList;
do {
cvtDevNode(childptr, depth+1, DTBase);
childptr = childptr->peer;
} while (childptr != (DeviceNodePtr)dp->childList);
}
}
/**************************************************************************/
/* */
/* */
/* Public Device Tree Interface Routines */
/* */
/* */
/**************************************************************************/
/**************************************************************************/
/* */
/* */
/* DevTreeRoot() */
/* */
/* Return the root node ID in the nodeID field of the parameter block */
/* */
/* */
/**************************************************************************/
pascal NodeID DevTreeRoot()
{
register DeviceNodePtr dp;
register unsigned short i;
/*
* get the saved tree base address
*/
dp = (DeviceNodePtr)GetExpandMemExpansionBusGlobals();
/*
* test if the device tree has been initialized
* return node id of root if it has been
*/
if(dp)
return((NodeID)dp);
/*
* If we're running on a machine with Open Firmware
* we get device tree info at startup
* Wire the info in as the initial device tree
*/
#ifdef forTNTDebug
i = *(unsigned short *)0x5ff00000;
if((i & 0x8000) == 0){
cvtDevNode(0x5ff00004, 1, (unsigned long)0x5ff00000L);
*(unsigned short *)0x5ff00000 = i | 0x8000;
}
dp = (DeviceNodePtr)0x5ff00004;
#endif
/*
* Create a device tree in the system heap if there
* wasn't one passed to us.
*/
#ifndef forTNTDebug
dp = (DeviceNodePtr)AllocTreeMem(sizeof(DeviceNode));
dp->attrib = kDeviceNode | kAttribLocked;
dp->name[0] = 'r'; dp->name[1] = 'o'; dp->name[2] = 'o'; dp->name[3] = 't';
#endif
SetExpandMemExpansionBusGlobals((Ptr)dp); /* remember where the root is */
return((NodeID)dp);
}
/**************************************************************************/
/* */
/* */
/* DevTreeNodeInfo() */
/* */
/* Return info about a node in the device tree */
/* */
/* */
/**************************************************************************/
pascal OSErr DevTreeNodeInfo(node, p)
NodeID node;
NodeInfoPtr p;
{
register DeviceNodePtr dp;
register PropertyNodePtr pp;
register char *src, *dst;
register int cnt;
dp = (DeviceNodePtr)node;
/*
* Fill in values unique to each node type
*/
switch(dp->attrib & kTypeMask) {
case(kDeviceNode):
p->childNode = (NodeID)dp->childList;
p->propertyNode = (NodeID)dp->propertyList;
src = dp->name;
break;
case(kPropertyNode):
pp = (PropertyNodePtr)dp; // convert to prop node ptr
p->childNode = 0; // properties don't have children
p->propertySize = pp->size;
p->propertyNode = 0;
src = pp->name;
break;
default:
return(-1); // all other node types are bogus
}
/*
* Fill in info common to all nodes;
* attributes, parent, peer, and name.
*/
p->attrib = dp->attrib & (kTypeMask | kAttribMask); // return only user-settable bits
p->parentNode = (NodeID)dp->parent;
p->peerNode = (NodeID)dp->peer;
/*
* Copy node name using src pointer formed in node-specific
* code
*/
dst = p->name;
for(cnt = 32; cnt; cnt--)
*dst++ = *src++; // copy name into record
return(noErr);
}
/**************************************************************************/
/* */
/* */
/* DevTreeAddNode() */
/* */
/* Add a node to the Device Tree */
/* Creating nodes with special attributes are done through a non-exported */
/* node creation routine */
/* */
/* The naming conventions for Device and Property nodes are the same as */
/* the conventions for Open Boot: */
/* */
/* Device Nodes: Mixed-case letters, digits, ',' '_' '+' '-' */
/* */
/* ' ' ! " # $ % & ' ( ) * + , - . / */
/* _ _ _ */
/* */
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
/* _ _ _ _ _ _ _ _ _ _ */
/* */
/* @ A B C D E F G H I J K L M N O */
/* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */
/* */
/* P Q R S T U V W X Y Z [ \ ] ^ _ */
/* _ _ _ _ _ _ _ _ _ _ _ */
/* */
/* ` a b c d e f g h i j k l m n o */
/* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */
/* */
/* p q r s t u v w x y z { | } ~ */
/* _ _ _ _ _ _ _ _ _ _ _ */
/* */
/* */
/* Properties: All printable characters except upper case letters, */
/* '/' '\' ':' '[' ']' '@' */
/* */
/* ' ' ! " # $ % & ' ( ) * + , - . / */
/* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */
/* */
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
/* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */
/* */
/* @ A B C D E F G H I J K L M N O */
/* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */
/* */
/* P Q R S T U V W X Y Z [ \ ] ^ _ */
/* _ _ _ _ _ _ _ _ _ _ _ _ _ */
/* */
/* ` a b c d e f g h i j k l m n o */
/* _ */
/* */
/* p q r s t u v w x y z { | } ~ */
/* _ _ _ _ */
/* */
/* Note: Open Boot defines Printable Characters as 0x21 -> 0x7e and */
/* 0xa1 -> 0xfe */
/* */
/**************************************************************************/
pascal NodeID DevTreeAddNode( parentNode ,attrib, name)
NodeID parentNode;
unsigned long attrib;
char *name;
{
register nameLen;
char *src, *dst;
DeviceNodePtr dp;
PropertyNodePtr pp;
NodeID retVal;
if(!CheckNodeID(parentNode))
return(-1); // make sure parent exists
src = name;
dp = (DeviceNodePtr)parentNode;
if((dp->attrib & kTypeMask) != kDeviceNode)
return(-1); // parent must be device node
switch(attrib & kTypeMask) { // check for valid node types
/*
* create a new device node
*/
case(kDeviceNode):
if((nameLen = CheckNodeName(name)) == 0)
return(-1); // something wrong with name
if(attrib & (kAttribNVRAM | kAttribSysSav))
return(-1); // only properties are non-volatile
dp = (DeviceNodePtr)AllocTreeMem(sizeof(DeviceNode));
retVal = (NodeID)dp;
dst = dp->name;
dp->attrib = attrib & (kTypeMask | kAttribMask); // set user-settable bits
dp->parent = (DeviceNodePtr)parentNode;
if(dp->parent->childList == 0){ // first child node
dp->parent->childList = dp; // new node is head of child list
dp->parent->childTail = dp; // and is at the tail of the list
dp->peer = dp; // no other peers
}
else { // add node to others in child list
dp->peer = dp->parent->childList;
dp->parent->childTail->peer = dp;
dp->parent->childTail = dp;
}
break;
/*
* create a new property node
*/
case(kPropertyNode):
if((attrib & kTypeMask) == kDeviceNode)
return(-1); // can't attach dev node to property
if((nameLen = CheckPropertyName(name)) == 0)
return(-1); // something wrong with name
pp = (PropertyNodePtr)AllocTreeMem(sizeof(PropertyNode));
retVal = (NodeID)pp;
dst = pp->name;
pp->attrib = attrib & (kTypeMask | kAttribMask); // set user-settable bits
pp->parent = (DeviceNodePtr)parentNode;
if(pp->parent->propertyList == 0){ // first property node
pp->parent->propertyList = pp;
pp->parent->propertyTail = pp;
pp->peer = pp;
}
else { // add node to others in property list
pp->peer = pp->parent->propertyList;
pp->parent->propertyTail->peer = pp;
pp->parent->propertyTail = pp;
}
break;
default:
return(-1);
}
/*
* copy name into the node
*/
while(nameLen){
*dst++ = *src++;
nameLen--;
}
*dst = '\0';
return(retVal);
}
/**************************************************************************/
/* */
/* */
/* DevTreeDelNode() */
/* */
/* Delete a node, and child nodes from the Device Tree */
/* */
/* */
/**************************************************************************/
pascal OSErr DevTreeDelNode(theNode)
NodeID theNode;
{
register DeviceNodePtr dp, chldp, parentp, peerp;
register PropertyNodePtr pp, prevprop;
register OSErr status;
if(!CheckNodeID(theNode))
return(-1); // make sure node exists
switch(((DeviceNodePtr)theNode)->attrib & kTypeMask) {
/*
* Delete a device tree node, its property nodes
* and its decendents.
* ..checking to make sure it's not locked
*
* NOTE: locks on any child nodes are cheerfully ignored!
*
*/
case(kDeviceNode):
dp = (DeviceNodePtr)theNode;
if(dp->attrib & kAttribLocked)
return(-1);
/*
* start recursive deletion for all child nodes
*/
while(dp->childList){ // while there are child nodes
chldp = dp->childList;
peerp = chldp->peer;
status = DevTreeDelNode((NodeID)chldp); // recurse down one branch
}
/*
* Free all the property nodes (if any attached)
* Break condition is when the node you've just deleted
* is the tail node.
*/
while(dp->propertyList){
status = DevTreeDelNode((NodeID)dp->propertyList);
}
/*
* If we're at the bottom of the recursion,
* free memory used for the device node,
* and return. Note that if you try to delete the
* root node, it deletes all of the root node's
* properties and children, but not the node itself
*
*/
if(dp->parent == 0)
return(noErr);
parentp = dp->parent;
if(parentp->childList == dp){ // delete child at list head
if(parentp->childTail == dp){ // only one node in list
parentp->childList = 0;
parentp->childTail = 0;
}
else{
parentp->childTail->peer = dp->peer;
parentp->childList = dp->peer;
}
}
else{
chldp = parentp->childList;
while(chldp->peer != dp){
chldp = chldp->peer;
}
/*
* chldp points to prev node, so squeeze out
* the node we're deleting, updating childTail too
*/
if(parentp->childTail == dp){
parentp->childTail = chldp;
}
chldp->peer = dp->peer;
}
if(dp->childList == 0){ // if we have no children
FreeTreeMem((Ptr)dp); // delete ourselves..
}
break;
/*
* Delete a property node from a device, making sure
* it's not locked. Backing storage is released for properties
* with NVRAM and SYSSAVED attributes
* (take care of this in FreeTreeMem??)
*/
case(kPropertyNode):
pp = (PropertyNodePtr)theNode;
if(pp->attrib & kAttribLocked){
return(-1);
}
/*
* delete property specified by theNode
*/
dp = pp->parent;
if(dp->propertyList == pp){ // node to del at head
if(dp->propertyTail == pp){
dp->propertyList = 0; // only one property
dp->propertyTail = 0;
if(pp->size)
FreePropData(pp); // free property data
FreeTreeMem((Ptr)pp);
break;
}
else{
dp->propertyTail->peer = pp->peer; // move back-link in tail node
dp->propertyList = pp->peer; // move list head down one
if(pp->size)
FreePropData(pp); // free property data
FreeTreeMem((Ptr)pp);
break;
}
}
else{
/*
* since there are no back pointers, spin around the list to
* find the node just before us
*/
prevprop = pp;
pp = pp->peer;
while(pp != (PropertyNodePtr)theNode){
prevprop = pp;
pp = pp->peer;
}
/*
* prevprop now points to the node before us in the list.
* Squish out the node we want to delete. If the node being
* deleted is the last node in the list, make sure the tail
* pointer is backed up one.
*/
if(dp->propertyTail == pp)
dp->propertyTail = prevprop;
prevprop->peer = pp->peer;
/*
* NOTE: only works for direct property data
*/
if(pp->size)
FreePropData(pp); // free property data
FreeTreeMem((Ptr)pp);
}
break;
default: // all other node types fail
return(-1);
}
return(noErr);
}
/**************************************************************************/
/* */
/* */
/* DevTreeSetProperty() */
/* */
/* Change the value of a property */
/* */
/* */
/**************************************************************************/
pascal OSErr DevTreeSetProperty(theNode, buf, size)
NodeID theNode;
unsigned char *buf;
long size;
{
PropertyNodePtr pp;
switch(((DeviceNodePtr)theNode)->attrib & kTypeMask) {
case(kDeviceNode):
return(-1); // dev nodes have no value attached
case(kPropertyNode):
pp = (PropertyNodePtr)theNode;
if(pp->attrib & kAttribROMProp)
return(-1); // can't change a ROM property
if(pp->attrib & kAttribDefProp)
return(-1); // can't set a Defered property (for now)
if(pp->size){ // already has a prop?
FreeTreeMem((Ptr)pp->dataPtr); // replace with new value
}
pp->dataPtr = (unsigned char *)AllocTreeMem(size);
pp->size = size;
BlockMove(buf, pp->dataPtr, size);
break;
default:
return(-1);
}
return(noErr);
}
/**************************************************************************/
/* */
/* */
/* DevTreeGetProperty() */
/* */
/* Get the value of a property */
/* */
/* */
/**************************************************************************/
pascal OSErr DevTreeGetProperty(theNode, buf, size)
NodeID theNode;
unsigned char *buf;
long size;
{
PropertyNodePtr pp;
switch(((DeviceNodePtr)theNode)->attrib & kTypeMask) {
case(kDeviceNode):
return(-1); // dev nodes have no value attached
case(kPropertyNode):
pp = (PropertyNodePtr)theNode;
if(pp->attrib & kAttribDefProp) // no defered properties yet
return(-1);
if((pp->size == 0)||(pp->dataPtr == 0))
return(-1); // node has no property value
if(pp->size < size)
size = pp->size; // return only as much as is there
BlockMove(pp->dataPtr, buf, size);
break;
default:
return(-1);
}
return(noErr);
}