Developing Custom Network Protocols
by Curtis Schwaderer 


Listing One
(a)
/* The A, B, and C are module names that invoke drivers A, B, and C. */
/* S_IREAD | S_IWRITE are permissions for accessing path, in         */
/*         this case read/write access.                              */
/* path is a handle given back by the OS that identifies protocol    */ 
/*         stack instance for the app                                */
Ite_path_open("/A/B/C", S_IREAD | S_IWRITE, &path);

(b) 
Ite_path_open("/A", S_IREAD | S_IWRITE, &path);
Ite_path_push(path, "/B");
Ite_path_push(path, "/C");

(c)
/* Where /stack is name of a module which contains explicit stack /A/B/C. */
/* How an app invokes whatever protocol stack is required for operation   */
/* in a network independent fashion.                                      */
Ite_path_open("/stack", S_IREAD | S_IWRITE, &path);

Listing Two
/* NETWORK SENDER APPLICATION CODE */
void main(int argc, char **argv)
{
    /* main program variables:
     * ite_path     = path to our DEVICE
     * snd_size     = used to remember the size of our data send packets
     * snd_buffer   = data send buffer
     * stack_size   = used to remember the length of stack string
     * stack_buffer = stack buffer
     * err          = used for error checking
     */
    path_id     ite_path;
    u_int32     snd_size;
    char        snd_buffer[32];
    u_int32     stack_size;
    char        stack_buffer[256];
    error_code  err;

    /* Most apps will need a signal handler due to asynchronous nature of 
     * using a network device. Reset any global notification flags to zero!
     */
    if ((err = _os_intercept(sighand, _glob_data)) != SUCCESS) {
     printf("Error %03d:%03d installing signal handler\n", err/256, err%256);
     exit(0);
    }
    /* Shows how to set up protocol stack using driver-based architecture */
    /* Two processes used in this demo: staxsend and staxrecv */
    /* staxrecv should already be running on the target. */
    /* Both processes are using the ITEM Application User Interface */

    /* 1. First, specify a protocol stack */
    printf(" * Type in the desired stack (i.e. /loopcl5): ");
    fflush(stdout);
    stack_size = 255;
    _os_readln(0, stack_buffer, &stack_size);
    stack_buffer[stack_size - 1] = '\0';
    printf(" */\n");

    /* Now invoke the protocol stack. We only need the path to be writeable*/
    if ((err = ite_path_open(stack_buffer, FAM_WRITE, &ite_path, 
                                                        NULL)) != SUCCESS) {
        printf("\n\n    FAILURE\n");
        printf("    Error %03d:%03d on ite_path_open()\n", err/256, err%256);
        printf("    Could not open stack [%s]\n", stack_buffer);
        if (err == 215) {
            printf("    Make sure all descriptors in your stack are 
                                                 valid and in memory\n\n");
        }
        exit(0);
    }
    fflush(stdout);
    snd_size = 31;
    _os_readln(0, snd_buffer, &snd_size);
    snd_buffer[snd_size - 1] = '\0';
    snd_size--;

    /* Write the data down the stack */
    if ((err = ite_data_write(ite_path, snd_buffer, &snd_size)) != SUCCESS) {
       printf("\n\n    FAILURE\n");
       printf("    Error %03d:%03d on ite_data_write()\n", err/256, err%256);
       if (err == 246) {
            printf("    You probably haven't started 
                                          the staxrecv application\n");
            printf("    Type [procs -e] and make sure staxrecv is running 
                                          before executing staxsend\n\n");
       }
       exit(0);
    }
    /* close the path and exit */
    ite_path_close(ite_path);
    exit(0);
}
/*NETWORK RECEIVER APPLICATION */
void main(int argc, char **argv)
{
    /* main program variables:
     * dev_name     = pointer to the name of our DEVICE
     * ite_path     = path to our DEVICE
     * rcv_size     = used to remember the size of our data receive packets
     * snd_size     = used to remember the size of our data send packets
     * rcv_buffer   = data receive buffer
     * snd_buffer   = data send buffer
     * err          = used for error checking
     */
    char        *dev_name = DEVICE;
    path_id     ite_path;
    u_int32     naptime, exit_flag = 0;
    signal_code sig;
    u_int32     rcv_size;
    u_char      rcv_buffer[32];
    error_code  err;

    /* Most apps will need a signal handler due to asynchronous nature of
     * using a network device. Reset any global notification flags to zero!
     */
    if ((err = _os_intercept(sighand, _glob_data)) != SUCCESS)
    {
        printf("Error %03d:%03d installing signal 
                                            handler\n", err/256, err%256);
        exit(0);
    }
    printf("    /* 1. First, a path to the following stack is 
                                                    opened. */\n", DEVICE);
    if ((err = ite_path_open(DEVICE, FAM_READ, &ite_path, NULL)) != SUCCESS) {
        printf("FAILURE\n");
        printf("Error %03d:%03d on ite_path_open()\n", err/256, err%256);
       exit(0);
    }

    /* Loop continuously until user terminates application */
    while (exit_flag == 0)  {
        printf("\n");
        printf("        /* 2. Now, wait for data to arrive on the 
                                               path we've opened. */\n");
        fflush(stdout);
        naptime = 0;
        sig = 0;
        _os_sigmask(1);
        _os_ss_sendsig(ite_path, 1000);
        _os_sleep(&naptime, &sig);

        /* Ask system how many bytes are available to be read */
        ite_data_ready(ite_path, &rcv_size);

        /* Read the number of data bytes */
        if ((err=ite_data_read(ite_path,rcv_buffer,&rcv_size)) != SUCCESS) {
          printf("Error %03d:%03d on ite_data_read()\n", err/256, err%256);
          exit(0);
        }
        rcv_buffer[rcv_size] = '\0';
        printf("%s", rcv_buffer);
    printf("\n");
    /* Close the path */
    ite_path_close(ite_path);
    exit(0);
}


Listing Three
/* Get the default profile values for a particular profile */
error_code ite_path_profileget(path_id path, conn_type *conn, 
                                   u_int32 *pr_size, void *pr_buffer)
/* Set the values for a particular profile for this path */
error_code ite_path_profileset(path_id path, conn_type *conn, 
                                   u_int32 *pr_size, void *pr_buffer)


Listing Four
#define SMCALL(mydeventry,deventry,entrypoint,param)
(   /* update destination fields in the logical unit */
(((Spf_lustat)((deventry)->v_lu_stat))->lu_pathdesc = 
                ((Spf_lustat)((mydeventry)->v_lu_stat))->lu_pathdesc),
/* call destination driver entry point */
(*entrypoint)(deventry,param)
)
/* Passing data up/down between drivers */
#define SMCALL_DNDATA(mydeventry,deventry,mb) \
(SMCALL(mydeventry,deventry,     
              ((Spf_drstat)(deventry->v_dr_stat))->dr_downdata,mb))
#define SMCALL_UPDATA(mydeventry,deventry,mb) \
(SMCALL(mydeventry,deventry,     
              ((Spf_drstat)(deventry->v_dr_stat))->dr_updata,mb))
/* Passing control up/down the stack */
#define SMCALL_SS(mydeventry,deventry,mb) \
(SMCALL(mydeventry,deventry,     
              ((Spf_drstat)(deventry->v_dr_stat))->dr_setstat,mb))
#define SMCALL_GS(mydeventry,deventry,mb) \
(SMCALL(mydeventry,deventry,     
              ((Spf_drstat)(deventry->v_dr_stat))->dr_getstat,mb))


Listing Five
/*  Device Driver Initialization Entry point. Any special considerations or 
** initialization that the protocol needs to make to the driver static or 
** logical unit static areas should be done here. Nothing path-specific: 
** That's done in the setstat entry point at the SS_OPEN setstat. The SPF 
** File manager will call the dr_iniz entry point of the driver only 
** when the logical unit attach count is 1 (i.e. the 1st attach to a 
** particular logical unit).
*/
error_code dr_iniz(Dev_list deventry)
{
    Spf_lustat lustat = (Spf_lustat)(deventry->v_lu_stat);
    /* This piece of code creates a debug data module that you can link to 
     * and look at when in rombug to aid you in */
/*  troubleshooting your driver. If you'd like debugging on, you define the  
 *   DEBUG macro in the spfdrvr.mak */
/* makefile and include debug_mod.l which contains the calls that 
 * you'll see throughout this driver source. */
#if defined(DEBUG)
    if (debug_init(DEBUG,(Dbg_stat*)&lustat->lu_dbg,
                 lustat->lu_dbg_name) != SUCCESS) { lustat->lu_dbg = NULL; }
debug_data(lustat->lu_dbg,"PRIniz     ", (u_int32)deventry);
#endif
    /* I/O is enabled when protocol initialization completes successfully */
    lustat->lu_ioenabled = TRUE;                            /* Enable I/O */
    /* ANY CUSTOM INITIALIZATION CODE FOR THE CUSTOM PROTOCOL GOES HERE */
    return(SUCCESS);
}
/* Device Driver Termination Entry point. This entry point allows the driver 
** to clean up before the operating system gets rid of this particular logical
** unit. This entry point is called by the SPF file manager only on 
** the last detach from a particular logical unit.
*/
error_code dr_term(Dev_list deventry)
{
#ifdef DEBUG
    Spf_drstat drstat = (Spf_drstat)(deventry->v_dr_stat);
#endif
    Spf_lustat lustat = (Spf_lustat)(deventry->v_lu_stat);
    /* I/O is disabled on a protocol when the end-end protocol terminates */
/* Depending on the protocol, this may be on terminate, or closing of 
 * the last path on a particular logical unit */
    lustat->lu_ioenabled = FALSE;                           /* disable I/O */
    DEBUG_DATA(lustat->lu_dbg, "PRTerm   ", deventry);
    DEBUG_4DATA(lustat->lu_dbg, drstat->dr_att_cnt, drstat->dr_use_cnt, 
                                     lustat->lu_att_cnt, lustat->lu_use_cnt);
    /* ANY CUSTOM TERMINATION CODE FOR THE CUSTOM PROTOCOL GOES HERE */
    return(SUCCESS);
}

Listing Six
/* Device Driver Get-Stat Entry point. This entry point should handle any 
** protocol specific getstats. The SPF file manager calls this entry
** point whenever an unknown getstat code is received. Likewise, if 
** you're protocol driver doesn't understand the getstat, pass it down to
** the next lower driver in the stack.
*/
error_code dr_getstat(Dev_list deventry, Spf_ss_pb pb)
{
    Spf_lustat lustat = (Spf_lustat)(deventry->v_lu_stat);
    error_code err;
    switch (pb->code) {
        case SPF_GS_PROTID: {   /* Someone requesting my protocol ID value */
pb->param = (void *)SPF_PR_SPPROTO;
return(SUCCESS);
                          }
           case SPF_GS_UPDATE: {   /* Attempting to get update statistics  */
        Spf_update_pb   upb = (Spf_update_pb)pb;
        Spf_ppstat ppentry;

        /* Provided library call that finds the right per path storage */
        if ((err = pps_find_entry(lustat, lustat->lu_pathdesc, 
                                     &ppentry)) != SUCCESS) { return err; }
        if (ppentry->pp_dndrvr) {
if ((err = SMCALL_GS(deventry, 
                        ppentry->pp_dndrvr, pb != SUCCESS) { return(err); }
            /* Now Update per my protocol status */
            /*  The smallest MTU */
            if (upb->stk_txsize > lustat->lu_txsize) { upb->stk_txsize = 
                                                        lustat->lu_txsize; }
            /* If we need, We keep the header and trailer sizes below */
            ppentry->pp_stk_txoffset  += upb->stk_txoffset;
            ppentry->pp_stk_txtrailer += upb->stk_txtrailer;
            /* If there are bytes that need to be allowed the header, add 
             * them to what came from below */
            upb->stk_txoffset = upb->stk_txoffset + lustat->lu_txoffset;

            /* Send up IO DISABLED if disabled, Otherwise, send up 
             * whatever the lower one put in there */
            if (lustat->lu_ioenabled == DRVR_IODIS)
                upb->stk_ioenabled = DRVR_IODIS;
        } else {    /* We're the lowest driver on the stack */
        /* If no one's below us, copy our stats into parameter 
         * block and return */
                upb->stk_txsize = lustat->lu_txsize;
                upb->stk_txoffset = lustat->lu_txoffset;
                upb->stk_ioenabled = lustat->lu_ioenabled;
        }
 return(SUCCESS);
        } /* End SPF_GS_UPDATE */
       default: {
Spf_ppstat ppentry;
        if ((err = pps_find_entry(lustat, lustat->lu_pathdesc, 
                                   &ppentry)) != SUCCESS) { return err; }
       if (pb->updir == SPB_GOINGDWN) {
        if (ppentry->pp_dndrvr) { return(SMCALL_GS(deventry, 
                                              ppentry->pp_dndrvr, pb)); }
        } else { return(SMCALL_GS(deventry, ppentry->pp_updrvr, pb)); }
        } /* End default */
} /* End switch */
return(EOS_UNKSVC);     /* If we get this far, return unknown service   */
}


Listing Seven
/*
** Device Driver Downgoing-Data Entry point. This is the place where data 
** being transmitted through the protocol is encapsulated with
** any headers used by the protocol, then sent down to the next lower driver 
** in the stack. Note that the lu_txoffset field tells SPF
** how many bytes to leave at the front of the packet for the protocol 
** header. Therefore, you can back up the pointer in the mbuf to add
** on the header information, saving excessive copies. The SPF file manager 
** calls this whenever transmit (write) data is
** sent from the application. (or higher layer drivers).
*/
error_code dr_downdata(Dev_list deventry, Mbuf mb)
{
    Spf_lustat  lustat = (Spf_lustat)(deventry->v_lu_stat);
    Spf_ppstat  ppentry;
    error_code  err;

    if (mb) {   DEBUG_DATA(lustat->lu_dbg, "PRDnMbData", mb);
        DEBUG_MBUF_DOWN(lustat->lu_dbg, mb);
    } else {    DEBUG_DATA(lustat->lu_dbg, "PRDnMbEpty",0); }

    if ((err = pps_find_entry(lustat, lustat->lu_pathdesc, 
                                                  &ppentry)) != SUCCESS) {
        m_free_p(mb);           /* sorry, drop the packet */
        return err;
    }
    DEBUG_DATA(lustat->lu_dbg, "PRPpStat", ppentry);

#if 1   /* This code segment is used in a demonstration environment to 
          ** show how a protocol driver can be written. It changes the case
          ** of any alphabetic character from upper to lower and vice versa.
          */
{
    u_int16 size = mb->m_size;
    u_int32 temp = 0;
    u_char  *buffer = mtod(mb,u_char*);

    while (temp < size) {
        if ((buffer[temp] >= 'a') && (buffer[temp] <= 'z')) {
            buffer[temp] -= 32;
        } else if ((buffer[temp] >= 'A') && (buffer[temp] <= 'Z')) {
            buffer[temp] += 32;
        }
        temp++;
    }
}
#endif
    if (ppentry->pp_dndrvr != NULL) {
       /* just send the data down to lower protocols */
        return(SMCALL_DNDATA(deventry, ppentry->pp_dndrvr, mb));
    } else {
        return(EOS_NODNDRVR);
    }
}










8


