This application demonstrates how to use SMICng and MIBMAN to build 
a MIB and register it with the SNMP agent.

This example should work on all development boards.

Three Multi build files are provided.  The build file project.bld
is used to control how application specific files are built.  All
updates to the build should be made to this file.  Project.bld is 
a subproject to the other build files image.bld and rom.bld.  
Image.bld is used to generate a debuggable version of the 
application that can also be loaded with the bootloader.  Rom.bld 
is used to generate a ROM image of the application.  

The following files are provided in this template.

appconf.h         sets application configuration settings
.\32b\image.bld   used to build the big endian image of this application.
                  Two output files are created:  image which can be
                  loaded with the MULTI debugger, and image.bin which
                  can be written to flash and loaded by the bootloader.
example.c         C file generated by MIBMAN that contains definitions for
                  objects defined in the example MIB.
mibs\example.config
                  MIBMAN configuration file for the example MIB.
example.h         H file generated by MIBMAN that declares constants and
                  data structures used in the MIB.
.\32b\Makefile    makefile for GNU tool set which builds both the image
                  and rom.bin versions of the application.  The file
                  image.elf can be debugged with gdb, the file image.bin
                  can 
mibs\example.inc  SMICng configuration file for the example MIB.
mibs\example.mib  contains the example MIB.
mibs\example.out  contains the intermediate file generated by SMICng.
exampleAction.c   a modified version of the C file generated by MIBMAN that
                  contains templates of the action routines.
gen.bat           example batch file that uses SMICng and MIBMAN to compile 
                  the example MIB into C code.
list              contains the list of intermediate files that MIBMAN is to
                  convert into C code.
loader.c          provides application configurations to expansion code, used 
                  in romzip.bld
Makefile          Make file to build the application with the GNU tools.
mibman.c          C file generated by MIBMAN that contains code to register
                  the example MIB with the management API and the SNMP
                  agent.
mibman.h          H file generated by MIBMAN that contains declarations for
                  constants and functions.
project.bld       contains application specific build settings
mibs\rfc1902.inc  SMICng configuration file for the SNMPv2-SMI MIB.
mibs\rfc1902.out  intermediate file generated by SMICng for the SNMPv2-SMI
                  MIB.
mibs\rfc1902.sm2  contains the SNMPv2-SMI MIB.
mibs\rfc1903.inc  SMICng configuration file for the SNMPv2-TCI MIB.
mibs\rfc1903.out  intermediate file generated by SMICng for the SNMPv2-TC
                  MIB.
mibs\rfc1903.sm2  contains the SNMPv2-TCI MIB.
mibs\rfc1904.inc  SMICng configuration file for the SNMPv2-CONF MIB.
mibs\rfc1904.sm2  contains the SNMPv2-CONF MIB.
readme            this file
table.c           contains the index function used by the table defined in
                  the example MIB.
table.h           declares the index function used by the example table.
.\32b\rom.bld     used to build the big endian ROM image of application
root.c            contains applicationStart() function

In addition, the following files in the BSP directory are built
as part of this application.

bsproot.c       contains the code that starts up the TCP/IP stack
appconf_api.c   contains code to read values in appconf.h

The application build file links in the following libraries.

bsp.a           contains the BSP code
tcpip.a         contains the Net+Works TCP/IP stack
tx.a            contains the ThreadX kernel
na1.lib,na2.lib contains the NET+Works API libraries.
snmp.lib        contains the SNMP agent
snmpd.lib       contains the API for the SNMP agent



This example demonstrates how to use SMICng and MIBMAN.  This MIB defines
scalar objects that are used to control the state of the LEDs on the
development board, and a table.  The example code is in the
\netos\src\examples\namib directory. 



Step 1:  Generate the Template Code

The batch file gen.bat generates template code from the mibs in the
subdirectory netos\src\examples\namib\mibs.

The subdirectory \netos\src\examples\namib\mibs contains the MIBs that are
used.  The example MIB is named example.mib.  The other MIBs contain SMI type
definitions used by the example.  The first step is to compile the MIBs with
SMICng.  Compiler options are set in .inc files for each MIB.  NET+OS ships
with .inc files for all of the standard MIBs, but we have to create one for
the example MIB.  The simplest way to do this is to copy the .inc file for a
standard MIB and modify it for the example.  The .inc file example.inc was
created in this way.  The .inc file contains commands to include the MIBs
referenced by the example MIB, commands to set standard options copied from
the standard .inc file, and the command to include example.mib for processing. 
See the SMICng documentation for more information.

The next step is to use MIBMAN to create C files that represent the MIBs.  We
need to setup a list of MIBs to process, and configuration files for them. 
The list of MIBs to process is stored in the file list and contains the
following.

mibs\rfc1902.out
mibs\rfc1903.out
mibs\example.out

The configuration file is used to setup special features.  The example MIB
defines scalar objects that control the LEDs on the development board, and a
table.  The scalar objects that represent the LEDs will be implemented as real
variables.  MIBMAN would not normally generate action routines for these
objects since the agent can read and write them itself.  However, we want to
turn the LEDs on and off when the console writes to these variables.  So, we
need to make MIBMAN generate action routines for them.  We also need to
specify a function that will be used to index the table. 

The configuration file for the example MIB is the file example.mib in the mibs
subdirectory.  It contains the following.

GenerateWriteActionRoutine 1.3.6.1.4.1.901.999.1.1.1
GenerateWriteActionRoutine 1.3.6.1.4.1.901.999.1.1.2
SetIndexFunction 1.3.6.1.4.1.901.999.1.2.1 = tableIndexFn
Include table.h

The first two options direct MIBMAN to create write action routines for the
two objects that represent the LEDs.  The third option specifies that
tableIndexFn will be used to index the MIB table.  The fourth option adds the
include file table.h into the declarations file generated by MIBMAN.  Table.h
will contain the declaration for tableIndexFn.

When you execute the gen.bat file the smicng and MIBMAN programs are used to
convert the example MIB to C code.  You need Java 1.2 or later to use MIBMAN.

The following files are produced:

example.c	contains the C declarations for the example MIB
example.h	contains definitions for variables and functions in example.c and
exampleAction.c
            exampleAction.c	contains templates for the action routines.  
            This file is normally generated by MIBMAN and then edited by
            you to finalize the template code.  An edited version is 
            supplied with this example.  MIBMAN will not overwrite existing
            template files so this C file will not be overwritten when
            you run gen.bat.  
MibMan.c	contains code to register the MIB with the SNMP agent, and the
            management variables with the management API.
MibMan.h	contains global declarations used in the other C files



Step 2:  Write the Table Indexing Function

The next step is to write tableIndexFn.  The parameters and return values for
this function are defined in the management API header file man_api.h by the
prototype manIndexFunctionType.  The management API will pass the function
pointers to an index and a row in the table.  The function must return a value
that indicates whether the row comes before, at, or after the index.  Since
our table uses a simple integer index, all the function needs to do is
subtract the index value from the index field in the row.  The following code
does this.

int tableIndexFn (void *index, void *row, void *indexInfo)
{
    simpleTableType *tableRow = (simpleTableType *) row;
    int *indexValue = (int *) index;

    return (tableRow->index - *indexValue);
}

This code is already implemented in the file table.c.



Step 3:  Write Action Routines for the Scalar Objects.

Next, we need to update the action routines for the LED objects to actually
turn the LEDs on and off.  MIBMAN creates templates for these routines that
look like this.

int greenLedOnWrite (int actionCode, struct varBind *info)
{
    int result = SNMP_ERR_NOERROR;

    if (actionCode == SNMP_SET_ACTION)
    {
        result = snmpWriteObject(info->vp, &info->setTo, info->setToLen);
    }
    else if (actionCode == SNMP_SET_UNDO)
    {
        result = snmpWriteObject(info->vp, &info->val, info->valLen);
    }

    return result;
}

We need to modify the template code to turn the LED on or off whenever the
variable is changed.  The following code does this.

int greenLedOnWrite (int actionCode, struct varBind *info)
{
    int result = SNMP_ERR_NOERROR;
    int ledOn;

    if (actionCode == SNMP_SET_ACTION)
    {
        result = snmpWriteObject(info->vp, &info->setTo, info->setToLen);
        ledOn = info->setTo.intVal;
    }
    else if (actionCode == SNMP_SET_UNDO)
    {
        result = snmpWriteObject(info->vp, &info->val, info->valLen);
        ledOn = info->val.intVal;
    }

    if (   (result == SNMP_ERR_NOERROR)
        && ((actionCode == SNMP_SET_ACTION) || (actionCode == SNMP_SET_UNDO)))
    {
        if (ledOn == 1)             /* if turning LED on*/
        {
            NALedGreenOn();
        }
        else
        {
            NALedGreenOff();
        }
    }

    return result;
}

This code has already been implemented in the copy of exampleAction.c 
provided with this application.



Step 4:  Update the Table Read Action Routine

The template for the table's read action routine contains two comment blocks
with "To Do" markers in them.  They both have to do with setting up SNMP
indexes.  In the first "To Do" section, we need to encode the index value into
a manTableIndexType structure.  The template code will already have decoded it
into an snmpIndexType structure.

The template code for the first "To Do" section looks like this.

if (snmpIndex->isNullIndex)
{
    manIndex.numericIndex = 0;
    manIndex.snmpIndex = NULL;
}
else
{
    /*
     * The raw SNMP indices are stored in snmpIndex.  The
     * algorithm for using these indices should be described
     * somewhere in the MIB's RFC.
     *
     * To Do:  Write code to initialize manIndex.  For a GET
     *         manIndex must be the exact index of the row to
     *         read.  It must be one past it for a GET-NEXT.
     */
}

The index for our example MIB is very simple.  The index passed by the console
will be an integer that corresponds to the columnar object simpleTable.index. 
All we need to do is to set manIndex.snmpIndex to the address of an integer
that has the index value.  The template code already handles the case where a
GET-NEXT operation is using a null index.  It needs to be updated to handle
two other cases:  A GET with a real index, and a GET-NEXT with a real index. 
The index value supplied by the console has been stored in
snmpIndex->index->value.intValue.  For a GET, we need to set
manIndex.snmpIndex to point to it.  For a GET-NEXT, we also need to increment
it.  The following code does this.

if (snmpIndex->isNullIndex)
{
    manIndex.numericIndex = 0;
    manIndex.snmpIndex = NULL;
}
else
{
    if (!isGet)
    {
        snmpIndex->index->value.intValue++;
    }
    manIndex.snmpIndex = (void *) &snmpIndex->index->value.intValue;
}

In the second "To Do" section, we need to update the index value in snmpIndex
to reflect the index of the table row we actually read.  The index will be
different than the one the console specified whenever a GET-NEXT requested is
processed.  MIBMAN generates the following template code.


/*
 * To Do:  Update the index values in snmpIndex to reflect
 *         the actual index the row is at.  This will be
 *         encoded into the name parameter by the call to
 *         snmpEncodeIndices.
 */

memcpy(name, vp->name, vp->namelen * sizeof(WORD32));
*length = snmpEncodeIndices(vp, name, snmpIndex);

The index of the row actually read will be in row->index.  The code to update
snmpIndex is as follows.

snmpIndex->index->value.intValue = row->index;
memcpy(name, vp->name, vp->namelen * sizeof(WORD32));
*length = snmpEncodeIndices(vp, name, snmpIndex);

This code has already been implemented in the exampleAction.c
file provided with this application.



Step 5:  Update the Table Write Action Routine

The next step is to update the table write action routine.  Like the read
action routine, the write action routine has to be modified to handle the
table index.  The first "To Do" section in the routine is as follows.

/*
 * The raw SNMP indices are stored in snmpIndex.  The
 * algorithm for using these indices should be described
 * somewhere in the MIB's RFC.
 *
 * To Do:  Write code to initialize oldIndex.
 */
oldIndex.wantExact = 1;        /* always use exact index for writes */

We need to set oldIndex.snmpIndex to point to an integer that contains the
index supplied by the console.  This value is stored in
snmpIndex->index->value.intValue.  To code to setup oldIndex is below.

oldIndex.wantExact = 1;        /* always use exact index for writes */
oldIndex.snmpIndex = (void *) &snmpIndex->index->value.intValue;

The next "To Do" section occurs in the SNMP_SET_ACTION case.

case SNMP_SET_ACTION:
    if (lastActionCode == SNMP_SET_COMMIT)
    {
        /*
         * To Do:  The array fieldsCommitted will indicate which fields
         *         the console has provided values for.  Set default values
         *         for any fields that are missing, and verify that the row
         *         contains valid data and can be written into the table.
         */
        if (isInsert)
        {
            memcpy (&newIndex, &oldIndex, sizeof(newIndex));
            ccode = manInsertSnmpRow(manInfo->id, &newIndex, row,
                                        MAN_TIMEOUT_FOREVER);
        }
        else
        {
            /*
             * To Do:  Initialize newIndex to indicate the row's new
             *         position in the table.
             */
            newIndex.wantExact = 1;

            ccode = manSetSnmpRow(manInfo->id, &oldIndex, &newIndex, row,
                                    MAN_TIMEOUT_FOREVER);
        }
        if (ccode == MAN_SUCCESS)
        {
            didWrite = 1;
        }
        result = snmpErrorLookup[ccode];
    }
    break;

At this point, the routine has written all of the objects specified by the
console into the row buffer.  We need to make sure that all of the required
objects have been supplied and set values for any that have not been.  The
only columnar objects in our table are the table index and a name field.  The
index is read-only, so the console should not set it.  Instead, we should set
it using the console's index value.  We also need to make sure the console
specified a value for name.  The code to do this is below.

if (lastActionCode == SNMP_SET_COMMIT)
{
    /*
     * Make sure user supplied a value for name and set the index
     * element.
     */
    row->index = snmpIndex->index->value.intValue;
    if ((fieldsCommitted[0] & 0x2) != 0)
    {
        if (isInsert)
        {
            memcpy (&newIndex, &oldIndex, sizeof(newIndex));
            ccode = manInsertSnmpRow(manInfo->id, &newIndex, row,
                                        MAN_TIMEOUT_FOREVER);
        }
        else
        {
            /*
             * To Do:  Initialize newIndex to indicate the row's new
             *         position in the table.
             */
            newIndex.wantExact = 1;

            ccode = manSetSnmpRow(manInfo->id, &oldIndex, &newIndex, row,
                                    MAN_TIMEOUT_FOREVER);
        }
        if (ccode == MAN_SUCCESS)
        {
            didWrite = 1;
        }
        result = snmpErrorLookup[ccode];
    }
    else
    {
        result = SNMP_ERR_BADVALUE;
    }
}
break;

The last "To Do" section has to do with the index again. 

/*
 * To Do:  Initialize newIndex to indicate the row's new
 *         position in the table.
 */
newIndex.wantExact = 1;

ccode = manSetSnmpRow(manInfo->id, &oldIndex, &newIndex, row,
                        MAN_TIMEOUT_FOREVER);

If an existing row is being changed, then it is possible that its index will
also be changed.  We need to setup newIndex to indicate the row's new position
in the table.  The index for our example table is read-only, so it cannot be
changed.  So all we have to do is to copy oldIndex into newIndex.

memcpy (&newIndex, &oldIndex, sizeof(newIndex));
ccode = manSetSnmpRow(manInfo->id, &oldIndex, &newIndex, row,
                                MAN_TIMEOUT_FOREVER);

This code has already been implemented in the exampleAction.c
file provided with this application.



Step 6:  Loading the MIB

After the MIB has been coded, it still has to be registered with the
management API and with the SNMP agent.  The file MibMan.c contains a routine
named snmpRegisterManagementVariables() that does this.  We need to make sure
the SNMP agent is started and call snmpRegisterManagementVariables().  The
following calls do this.


NAsetSysAccess (NASYSACC_ADD, "public", "public", NASYSACC_LEVEL_SNMP_R,
                    NULL);
NAsetSysAccess (NASYSACC_ADD, "private", "private", NASYSACC_LEVEL_SNMP_RW,
                    NULL);
snmpd_load();
snmpRegisterManagementVariables();


The first two calls define two user accounts used to access SNMP information. 
These accounts could have been defined with any name and any password. 
However, by default most MIB browsers will use the account name "public" with
password "public" to read MIB objects, and the account "private" with password
"private" to write MIB objects.  You probably want to use a different account
name and password for write access in shipping products.

The third call starts the SNMP agent.  The agent automatically registers
MIB-II when it is started.  The fourth call registers our example MIB with the
agent.

The applicationStart() routine in the root.c file for our example also loads
the table with some values that can be read from a MIB browser.


This code has already been implemented in the root.c
file provided with this application.
