GetEnetAddrDirect.c

/*
    File:       GetEnetAddrDirect.c
 
    Contains:   This is a sample program to obtain the ethernet address from a Power Mac using
                1. direct access to the ROM for the 61/71/8100 Power Macs, or by accessing the
                Name Registry for PCI based systems. This means that the the address can be 
                accessed without Open Transport being available, or without having to open the
                Ethernet device driver.
 
                2. use OpenTransport to display the ethernet address for all ethernet devices
                that are detected.  For this method to work, the program opens a connection over
                the found ethernet port. If the openConnection call works, then we can use the 
                OT APIs to obtain the ethernet address.
 
                For the earlier Power Macs, 6100/ 7100/8100, the Burned In Address is found in 
                ROM at address 0x50f08000.  One must read every 16 bytes for each of the 6
                bytes that make up the address. That is you'll need to 
                read address 50f08000, 50f08010, 50f08020, 50f08030, 50f08040, 
                and  50f08050.  The found address is byte swapped. which means
 
                10 00 E0 2B 06 AF, which if you bit invert each byte becomes
 
                08 00 07 D4 60 F5.
 
                For PCI Power Macs, the burned in address is generally in 
                the Name Registry, however, the name of the entry is vendor 
                specified.  For the 72/73/75/76/85/86/95/9600 Power Macs, look at the
                "local-mac-address" entry. For the PCI Power Macs which use the Comm Slot2
                card, look at the "ASNT,ethernet-address" entry for a pointer to where the
                ethernet address is stored.
 
                The one set of Power Macs, that this program does not demonstrate how to access
                using the direct means are the 52/53/62/6300 systems which use the original
                CommSlot Ethernet card and which can also use the PDS slot LC style ethernet
                card
 
    Written by: Rich Kubota 
 
    Copyright:  Copyright © 1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/22/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
#include <MacTypes.h>
#include <Gestalt.h>
#include <Errors.h>
#include <NameRegistry.h>
#include <stdio.h>
#include <Strings.h>
#include <OpenTransport.h>
#include <OpenTptLinks.h>
#include <OpenTptConfig.h>
 
 
enum {
    kUnsupported        = 0,
    kPDMMachine         = 1,
    kPCIMachine         = 2,
    kCommSlotMachine    = 3,
    kPCIComm2Machine    = 4
};
 
enum {
    kPDMEnetROMBase = 0x50f08000
};
 
enum {
    gestaltPowerBookG3SeriesFSTN    = 314,
    gestaltiMac                     = 406
};
 
struct Address8022
{
    OTAddressType   fAddrFamily;
    UInt8           fHWAddr[k48BitAddrLength];
    UInt16          fSAP;
    UInt8           fSNAP[k8022SNAPLength];
};
typedef struct Address8022 Address8022;
 
UInt32      DoesCPUHaveBuiltInEthernet(void);
UInt8       ByteSwapValue(UInt8 val);
OSStatus    GetPDMBuiltInEnetAddr(UInt8 *enetaddr);
OSStatus    GetPCIBuiltInEnetAddr(UInt8 *enetaddr);
OSStatus    GetPCIComm2EnetAddr(UInt8 *enetaddr);
void        DisplayEnetAddr(UInt8 *enetaddr);
void        DisplayBurnedInAddress(void);
void        DisplayOTEthernetAddresses(void);
 
 
UInt32 DoesCPUHaveBuiltInEthernet(void)
{
    long        response;
    OSStatus    err;
    UInt32      result = kUnsupported;
    
    err = Gestalt(gestaltMachineType, &response);
    switch (response)
    {
        case gestaltPowerMac8100_120:
        case gestaltAWS9150_80:
        case gestaltPowerMac8100_110:
        case gestaltPowerMac7100_80:
        case gestaltPowerMac8100_100:
        case gestaltAWS9150_120:
        case gestaltPowerMac8100_80:
        case gestaltPowerMac6100_60:
        case gestaltPowerMac6100_66:
        case gestaltPowerMac7100_66:
            result = kPDMMachine;
            break;
        
        case gestaltPowerMac9500:
        case gestaltPowerMac7500:
        case gestaltPowerMac8500:
        case gestaltPowerBook3400:
        case gestaltPowerBookG3:
        case gestaltPowerMac7200:
        case gestaltPowerMac7300:
        case gestaltPowerBookG3Series:
        case gestaltPowerBookG3SeriesFSTN:
        case gestaltPowerMacG3:
        case gestaltiMac:
            result = kPCIMachine;
            break;
            
        case gestaltPowerMac5200:
        case gestaltPowerMac6200:
            result = kCommSlotMachine;
            break;
            
        case gestaltPowerMac6400:
        case gestaltPowerMac5400:
        case gestaltPowerMac5500:
        case gestaltPowerMac6500:
        case gestaltPowerMac4400_160:
        case gestaltPowerMac4400:
            result = kPCIComm2Machine;
        
    }
    
    if (response == kUnsupported)
    {
        err = Gestalt(gestaltNameRegistryVersion, (long*) &response);
        if (err == noErr)
            result = kPCIMachine;
    }
    return result;
}
 
UInt8       ByteSwapValue(UInt8 val)
{
    UInt8   result = 0;
    
    if (val & 0x01)
        result |= 0x80;
        
    if (val & 0x02)
        result |= 0x40;
        
    if (val & 0x04)
        result |= 0x20;
        
    if (val & 0x08)
        result |= 0x10;
        
    if (val & 0x10)
        result |= 0x08;
        
    if (val & 0x20)
        result |= 0x04;
        
    if (val & 0x40)
        result |= 0x02;
        
    if (val & 0x80)
        result |= 0x01;
    return result;
}
 
OSStatus    GetPDMBuiltInEnetAddr(UInt8 *enetaddr)
{
    UInt32  i;
    UInt8   *val;
 
    for (i = 0; i < 6; i++)
    {
        val = (UInt8 *)(kPDMEnetROMBase + i * 0x10);
        enetaddr[i] = ByteSwapValue(*val);
    }
    return noErr;
}
 
 
OSStatus    GetPCIBuiltInEnetAddr(UInt8 *enetaddr)
{
    OSStatus                err = noErr;
    RegEntryIter            cookie;
    RegEntryID              theFoundEntry;
    unsigned char           enetAddrStr[32] = "\plocal-mac-address";
    RegCStrEntryNamePtr     enetAddrCStr = p2cstr( enetAddrStr );
    RegEntryIterationOp     iterOp;
    UInt8                   enetAddr[6];
    Boolean                 done = false;
    RegPropertyValueSize    theSize;
    
    err = RegistryEntryIDInit( &theFoundEntry );
    if (err != noErr)
    {
        fprintf(stdout, "RegistryEntryIDInit failed\n");
        return err;
    }
 
    err = RegistryEntryIterateCreate( &cookie );
    if (err != noErr)
    {
        fprintf(stdout, "RegistryEntryIterateCreate failed\n");
        return err;
    }
 
    iterOp = kRegIterDescendants;
 
    err = RegistryEntrySearch( &cookie, iterOp, &theFoundEntry, &done,
                                                       enetAddrCStr, nil, 0);
    
    if (err == noErr)
    {
        theSize = sizeof(enetAddr);;
        err = RegistryPropertyGet(&theFoundEntry, enetAddrCStr, &enetAddr, &theSize );
        if (err == noErr)
            BlockMove(enetAddr, enetaddr, sizeof(enetAddr));
    }
 
    RegistryEntryIterateDispose( &cookie );
 
    return noErr;
}
 
OSStatus    GetPCIComm2EnetAddr(UInt8 *enetaddr)
{
    OSStatus                err = noErr;
    RegEntryIter            cookie;
    RegEntryID              theFoundEntry;
    unsigned char           enetAddrStr[32] = "\pASNT,ethernet-address";
    RegCStrEntryNamePtr     enetAddrCStr = p2cstr( enetAddrStr );
    RegEntryIterationOp     iterOp;
    UInt8                   *enetAddr;
    Boolean                 done = false;
    RegPropertyValueSize    theSize;
    
    err = RegistryEntryIDInit( &theFoundEntry );
    if (err != noErr)
    {
        fprintf(stdout, "RegistryEntryIDInit failed\n");
        return err;
    }
 
    err = RegistryEntryIterateCreate( &cookie );
    if (err != noErr)
    {
        fprintf(stdout, "RegistryEntryIterateCreate failed\n");
        return err;
    }
 
    iterOp = kRegIterDescendants;
 
    err = RegistryEntrySearch( &cookie, iterOp, &theFoundEntry, &done,
                                                       enetAddrCStr, nil, 0);
    
    if (err == noErr)
    {
        theSize = sizeof(enetAddr);;
        err = RegistryPropertyGet(&theFoundEntry, enetAddrCStr, &enetAddr, &theSize );
        if (err == noErr)
            BlockMove(enetAddr, enetaddr, 6);
    }
 
    RegistryEntryIterateDispose( &cookie );
 
    return noErr;
}
 
void        DisplayEnetAddr(UInt8 *enetaddr)
{   
    fprintf(stdout, "%02X.",(int )enetaddr[0]);
    fprintf(stdout, "%02X.",(int )enetaddr[1]);
    fprintf(stdout, "%02X.",(int )enetaddr[2]);
    fprintf(stdout, "%02X.",(int )enetaddr[3]);
    fprintf(stdout, "%02X.",(int )enetaddr[4]);
    fprintf(stdout, "%02X",(int )enetaddr[5]);
}
 
void DisplayBurnedInAddress(void)
{
    OSStatus    err;
    UInt32      cputype;
    UInt8       enetaddr[6];
 
    fprintf(stdout, "First, get the burned-in ethernet address directly from ROM or NameRegistry\n");
    cputype = DoesCPUHaveBuiltInEthernet();
    switch (cputype)
    {
        case kPDMMachine:
            err = GetPDMBuiltInEnetAddr((UInt8*)enetaddr);
            if (err == noErr)
            {
                fprintf(stdout, "\nBurned-In address for Ethernet Built-In => ");
                DisplayEnetAddr((UInt8*)&enetaddr);
            }
            break;
        
        case kPCIMachine:
            err = GetPCIBuiltInEnetAddr((UInt8*)enetaddr);
            if (err == noErr)
            {
                fprintf(stdout, "\nBurned-In address for Ethernet Built-In => ");
                DisplayEnetAddr((UInt8*)&enetaddr);
            }
            break;
        
        case kCommSlotMachine:
            fprintf(stdout, "\n\nThis is a NuBus based Power Mac which may have a CommSlot Ethernet card");
            fprintf(stdout, "\nmust use Open Transport to obtain the ethernet address");
            break;
        
        case kPCIComm2Machine:
            fprintf(stdout, "\n\nThis is a PCI system which may have a CommSlot2 Ethernet card");
            fprintf(stdout, "\nmust use Open Transport to obtain the ethernet address");
            break;
        
        default:
            fprintf(stdout, "\n It appears that this CPU does not have built-in ethernet\n");
            break;
    }
}
 
void DisplayOTEthernetAddresses(void)
{
    OSStatus        status;
    EndpointRef     ep;
    OTPortRecord    devicePortRecord;
    UInt32          index;
    TBind           returnInfo;
    TBind           requestInfo;
    Address8022     theReturnAddr =     {AF_8022, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x0000,
                                        {0x00,0x00,0x00,0x00,0x00}};
    Address8022     theAddr =   {AF_8022, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x8888,
                                    {0x00,0x00,0x00,0x00,0x00}};
    Boolean         foundAPort;
    Str255          userFriendlyName;
    
    
    if (status = InitOpenTransport())
    {
        fprintf(stdout, "\n\nOpen Transport is not installed or is inactive\n");
        return;
    }
    else
    {
        fprintf(stdout, "\n\nNow checking for Ethernet addresses using Open Transport\n");
        fprintf(stdout, "There will be a slight delay as I try to open a connection\n");
        fprintf(stdout, "over each card.\n");
    }
 
    index = 0;
        // iterate thru each OT port record for ethernet ports. 
    while (foundAPort = OTGetIndexedPort(&devicePortRecord,index))
    {
        if ((devicePortRecord.fCapabilities & kOTPortIsDLPI) &&
            (devicePortRecord.fCapabilities & kOTPortIsTPI) &&
            (kOTEthernetDevice == OTGetDeviceTypeFromPortRef(devicePortRecord.fRef)))
        {
    
            ep = OTOpenEndpoint(OTCreateConfiguration(devicePortRecord.fPortName), (OTOpenFlags)NULL, NULL,&status);
    
            if (status == kOTNoError)
            {
                    // we have to bind the endpoint before we can get it's address info
                requestInfo.addr.buf = (UInt8 *)&theAddr;
                requestInfo.addr.len = 10;  // family type + Ethernet + type field
                                // don't use sizeof(theAddr) since we are binding to type 1 Ethernet
                                // address, not to an 802.2 address.
                requestInfo.addr.maxlen = 0;            
                requestInfo.qlen = 0;
    
                status = OTBind(ep, &requestInfo, NULL);
 
                if (status == kOTNoError)
                {
                    returnInfo.addr.buf = (UInt8 *)&theReturnAddr;
                    returnInfo.addr.maxlen = 10;            // family type + 6 bytes for Ethernet + type
                    returnInfo.qlen = 0;
                    
                    
                    status = OTGetProtAddress(ep,&returnInfo,NULL);
                    
                    if (status == kOTNoError)
                    {
                        OTGetUserPortNameFromPortRef(devicePortRecord.fRef, userFriendlyName);
                        fprintf(stdout, "\n   The Ethernet address for %#s is => ", userFriendlyName);
                        DisplayEnetAddr((UInt8*)&theReturnAddr.fHWAddr);
                    }
                    OTUnbind(ep);
                }
                
                OTCloseProvider(ep);
            }   
                
                
        }
        index++;
    }   
    
    
    
        // closing down
    CloseOpenTransport();
    
    
}
 
main (void)
{
    
    fprintf(stdout, "Sample program to obtain the available ethernet addresses");
    DisplayBurnedInAddress();
    
    DisplayOTEthernetAddresses();
    return 0;
}