Table of Contents

Table of Chapters

3. PORTING ENGINEER PROVIDED FUNCTIONS

The functions described in this section must be provided by the porting engineer as part of the NicheStack port. The DOS or Win32 reference packages can be referenced for examples. Further functions will be required as described in the Network Interfaces section to implement a network hardware driver.

In the demo packages these functions are either mapped directly to system calls via MACROS in ipport.h_h or they are implemented in the port files.

3.1 General Functions

Name

port_prep

Syntax

extern int (* port_prep)(int already_found);

Parameters

int already_found /* index for nets[ ] structure to use */

Description

This is a pointer to a function. It should be initialized in pre_task_setup() to the actual function (implemented by the porting engineer). This function should prepare the nets[ ] structures for the network interfaces that the target system will want to use. The integer passed is the nets[ ] index of the interface to set up.

The nets[ ] members which need to be filled in are described in Section 2.5.2, entitled "The net Structure, the nets[ ] Array"

Returns

Returns the nets[ ] index of the next interface to be set up. If no interfaces were set up, the returned value is the same as the passed value.

Name

npalloc

npfree

Syntax

void *npalloc(int size);

void *npfree(void *);

Description

All the IP stack's dynamic memory is allocated by calls to npalloc() and released by calls to npfree(). The syntax for these is exactly the same as the standard C library calls malloc() and free(), with the exception that buffers returns from npalloc() are assumed to be pre-initialized to all zeros. In this respect npalloc() is like calloc().

If the target system already supports standard calloc() and free() calls, all that is necessary is to add the following lines to ipport.h_h:

#define npalloc(size)  calloc(1, size)
#define npfree(ptr)    free(ptr)

In the event your target system does not support calloc() and free(), you will need to implement them. An exhaustive description of how these functions work and sample code is available in "The C Programming Language" by Kernighan and Ritchie.

The great majority of the calls to npalloc() are made at initialization time. Only the UDP and TCP layers require these calls during runtime. If your system has severe memory shortages, then these layers can be modified to use pre-allocated blocks of static memory rather than implement a fully functional npalloc() and npfree(), but this is invariably more work and puts limits on the number of simultaneous connections which can be supported.

One issue that must be dealt with on some target processors is memory alignment. Some processors will generate faults if instructions to read or write more than one byte of memory at a time from odd numbered addresses are executed. Even if the target processor supports 2 or 4 byte reads and writes to odd numbered addresses, instructions of this sort usually execute more slowly than accesses to addresses that are an integer multiple of the number of bytes accessed by the instruction. System performance can suffer. If the target system supports the standard calloc() and free() calls then the C library vendors have probably already made sure that the buffers returned by calloc() are properly aligned for the target processor. However if you need to implement npalloc() and npfree() without the standard C library memory allocation functions then you should implement these functions such that the memory blocks are aligned on addresses that are a multiple of the data bus width of the target processor. If you don't know the data bus width of the target processor, 4 is usually a safe guess.

Returns

npalloc() returns a pointer to the block allocated or NULL if memory is unavailable.

Name

tcp_sleep()

tcp_wakeup()

Syntax

void tcp_sleep(void *address);

void tcp_wakeup(void *address);

Description

These functions provide a mechanism by which the InterNiche TCP code can yield control of the target processor while waiting for one or more events to occur. The functions' address parameters provide a mechanism by which the source of the events can be synchronized.

See the detailed description of this in the TCP porting section of this document.

Name

SignalPktDemux()

Syntax

void SignalPktDemux(void);

Description

SignalPktDemux() is called by network interface code to indicate to the InterNiche IP layer that received packets have been enqueued to rcvdq. A call to SignalPktDemux() should result in the target system calling the portable pktdemux() function to dequeue rcvdq.

The implementation of SignalPktDemux() is dependent upon whether the target system has a multitasking OS or is implemented as a superloop. If the target system is implemented as a superloop, as is the case in the DOS demo, SignalPktDemux() can be a no-op so long as the superloop calls pktdemux() on a regular basis. No explicit notification is necessary in this case.

If the target system has a multitasking OS, SignalPktDemux() could still be a no-op so long as a task was created that periodically called pktdemux() to empty rcvdq. This approach could be made to work but is less than optimal in systems with a multitasking OS. The preferred approach in this case would be for a task to be created that was constructed as a loop in which, on each pass through the loop, the task blocked on some OS dependent event. Upon return from the event block, the task would call pktdemux(). In this case, the function of SignalPktDemux() would be to post the event on which the task was blocked so as to cause the task to call pktdemux(). This approach is illustrated in the code below:

Example

void receiveTask()
{
   for ( ; ; )
   {
      block on event X;   /* this call will be dependent on the OS */
      pktdemux();         /* portable function to empty rcvdq */
   }
}

void SignalPktDemux()
{
   post event X;          /* this call will be dependent on the OS */
}

Name

dtrap()

Syntax

void dtrap(void);

Description

This primitive is intended to hook a debugger whenever it is called.

See the detailed description in the Debugging Aids, section 2.2.4.

Returns

Usually nothing, depends on porting engineer modifications.

Name

dprintf()

initmsg()

Syntax

void dprintf(char *, ...);

void initmsg(char *, ...);

Description

These two routines are functionally the same as the standard C library printf() function and are called by the stack code to inform the porting engineer or end user of system status. initmsg() prints status messages at initialization time. dprintf() prints error warnings during runtime.

InterNiche provides an version of printf() in misclib/ttyio.c. This implementation does not support floating point formats, but is otherwise consistent with standard specifications for the function. The compile-time macro NATIVE_PRINTF (ipport.h) determines whether this code, or a user/library implementation is to be used.

See the detailed description in the Debugging Aids, section 2.2.4.

Name

dputchar()

Syntax

void dputchar(int chr);

Description

The InterNiche CUI routines call dputchar() in order to display a character on the target system display or monitor port. If such output is not desired, dputchar() can be implemented as a no-op. Its parameter is an ASCII character that should be displayed on the target system display or monitor device.

dputchar() should perform newline expansion. If the value of chr is an ASCII newline character (0xa) then a newline followed by a carriage return should be output to the display device.

Returns

Nothing.

Name

kbhit()

Syntax

int kbhit(void);

Description

kbhit() should return a non-zero value if a keystroke has been entered by a user at the CUI of the target system. It should not dequeue the character itself from the input device, rather the return value from kbhit() should simply poll the device to determine if a character is present. The entered character is retrieved using the getch() function.

Returns

0 if no character had been entered at the input monitor device, non-zero if at least one character is available.

Name

getch()

Syntax

int getch(void);

Description

kbhit() and getch() are used together to effect CUI input. The stack code calls kbhit() to determine if a character is available and then if a character is available, calls getch() to return the value of the character. getch() should never block for user input.

Returns

If a character is available at the CUI or system monitor device, getch() returns the ASCII value of that character. Its return value is undefined if no character is available.

Name

ENTER_CRIT_SECTION()

EXIT_CRIT_SECTION()

Syntax

#define ENTER_CRIT_SECTION(p);

#define EXIT_CRIT_SECTION(p);

Parameters

None that are currently used.

Description

These two entry points should be designed to be paired around sections of code that must not be interrupted or pre-empted. Usually, ENTER_CRIT_SECTION() should save the processor interrupt state and disable interrupts, whereas EXIT_CRIT_SECTION() should restore the processor interrupt state to as it was before the most recent call to ENTER_CRIT_SECTION(). On UNIX-like systems they can be mapped to the spl() primitive. Examples for embedded Intel x86 processors are provided in the demo. Only the definitions are given here; for examples see the source code.

The stack source code always pairs these two in the same routines. The Intel x86 example takes advantage of this to push the existing flags register onto the processor stack, saving the interrupt flag state and retrieves the value for the flags register later, restoring the interrupt flag as it was before the ENTER_CRIT_SECTION() call.

The parameter p is currently unused.

See the detailed discussion of these macros see Critical Section Method.

Returns

These return no meaningful value.

Name

LOCK_NET_RESOURCE()

UNLOCK_NET_RESOURCE()

Syntax

void LOCK_NET_RESOURCE(int resID);

void UNLOCK_NET_RESOURCE(int resID);

Parameters

The integer defined constants, NET_RESID, RXQ_RESID or FREEQ_RESID.

Description

See description of Net Resource Method.

Returns

Nothing.

Name

cksum()

Syntax

unsigned short cksum (char *buffer, unsigned word_count);

Parameters

char *buffer /* pointer to buffer to checksum */

unsigned word_count /* number of 16 bit words in buffer */

Description

Returns 16 bit Internet checksum of buffer. Algorithms for this are described in RFC1071.

NOTE: A portable C language version of this routine is provided with the demo packages, however TCP implementations can spend a significant portion of their CPU cycles in the checksum routine. This routine is described here to encourage porting engineers to optimize their ports by implementing their checksum routines in assembly language. An Intel x86 assembly language checksum routine is also included which can be used on Intel processors as is. Versions for other CPUs are widely available - contact us if you need help finding one.

Returns

The 16 bit checksum.

Name

panic()

Syntax

void panic(char *msg);

Parameters

char *msg /* short test message describing the fault */

Description

panic() is called if the InterNiche stack software detects a fatal system error. msg is a string describing problem. What this should do varies with the implementation. In a testing or development environment it should print messages, hook debuggers, etc. In an embedded controller, it should try to restart (i.e. warm boot) the system.

Sample for a DOS application is shown below.

Returns

Generally there is no return from this routine, however it is sometimes useful to allow a return under control of a debugger.

Example

void
panic (msg)
   char *msg;
{
   dprintf("panic: %s\n", msg);
   dtrap();      /* try to hook debugger */
   netexit(1);   /* try to clean up */
}

Name

sysuptime()

Syntax

unsigned long sysuptime(void);

Parameters

None

Description

Returns the number of 1/100ths of a second that have elapsed since the target system was last booted.

Returns

See above.

Example

unsigned long sysuptime()
{
   return ((cticks/TPS)*100);   /* 100ths of a sec since boot time */
}

3.2 Network Interfaces

The stack allocates a net structure for each network interface. This structure is described in the section titled "The net Structure, the nets[ ] Array". Each net structure includes pointers to function that implement the network interface. The porting engineer will need to write the routines listed below for each hardware/device driver to be used in the target system or use one of the standard devices provided with the stack.

Network hardware interface routines:

/* net initialization routine */
   int (*n_init)(int);

/* send data on media */
   int (*raw_send)(struct net *, char *, unsigned);
/* OR */
   int (*pkt_send)(PACKET pkt);

/* net close routine */
   int (*n_close)(int);

/* register a MAC type, i.e. 0x0800 for IP */
   int (*n_reg_type)(unshort, struct net*);

/* per net statistics, in addition to n_mib */
   void (*n_stats)(int);

#ifdef DYNAMIC_IFACES
/* set link state up/down */
int (*n_setstate)(struct net *, int);
#endif /* DYNAMIC_IFACES */

Additionally, if the driver is to support Dynamic Network Interfaces, the porting engineer must write a create_device() function for the driver, for use with the stack's ni_create() API function.

The porting engineer must also provide a packet receive mechanism which takes received packets and places them in the queue rcvdq. This receive routine should obtain its data buffers from PACKET structures via calls to pk_alloc().

The remainder of this section describes these routines in detail.

Name

n_init()

Syntax

int n_init(int if_number);

Parameters

int if_number /* interface number, for indexing nets[ ] */

Description

This routine is responsible for preparing the device to send and receive packets. It is called during system startup time after prep_ifaces() has been called, but before any of the other network interface's routines are invoked. When this routine returns, the device should be set up as follows:

This will usually include hardware operation such as initializing the device and enabling interrupts. It does not include setting protocol types. This is handled later (see n_reg_type()). Upon returning from this routine it is safe for your hardware's interrupt or receive routines to start enqueuing received packets in the rcvdq. Packets which are not IP or ARP will be discarded by the stack.

The nets[ ] structure array element that is indexed by if_number should be completely filled in when this function returns. Note that the work of filling this structure is shared between prep_ifaces() and this function, so if all nets[ ] structure setup was done in prep_ifaces() (see section 2.3.) there may be nothing to do here.

Below is an example of code that can be used for setting up the MIB structure for a 10 Mbps Ethernet interface. The n_mib field of the nets[ ] structure points to a structure that is used to contain the MIB information which has already been statically allocated by the calling code. See RFC1213 for detailed descriptions of the MIB fields. Most of the MIB fields are used only for debugging and statistical information, and are not critical unless your device is managed by SNMP. The ifPhysAddress field is an exception. It is used by ARP to obtain the hardware's MAC address and MUST be set up correctly for the IP stack to work over Ethernet. Note that although ifPhysAddress is a pointer, it does not point to valid memory when the MIB structure is created. The porting engineer should make sure it points to a static buffer containing the MAC address before this function returns. The size of this address is determined by the media (6 bytes for Ethernet) and should be set in the nets[ ] structure member n_hal (hardware address length).

u_char macaddress[6];   /* should contain interface's MAC address */

nets[if_number]->n_mib->ifDescr = "Ethernet Packet Driver";
nets[if_number]->n_mib->ifType = ETHERNET; /* SNMP Ethernet type */
nets[if_number]->n_mib->ifMtu = ET_MAXLEN;
nets[if_number]->n_mib->ifSpeed = 10000000; /* 10 megabits per second */
nets[if_number]->n_mib->ifAdminStatus = 1;
nets[if_number]->n_mib->ifOperStatus = 1;
nets[if_number]->n_mib->ifPhysAddress = ...macaddress[0]; /* example */

See the example in the DOS Demo package's packet driver interface in file \doslib\pktd.c.

Returns

Returns 0 if OK, else one of the ENP_ codes.

Name

pkt_send()

Syntax

int pkt_send(PACKET pkt);

Parameters

typedef struct netbuf *PACKET

PACKET pkt /* pointer to netbuf structure containing frame to send */

Description

This routine is responsible for sending the data described by the passed pkt parameter and queuing the pkt parameter for later release by the device driver. If the MAC hardware is idle the actual transmission of the packet should be started by this routine, else it should be scheduled to be sent later (usually by an "end of transmit" interrupt (EOT) from the hardware).

The PACKET type is described in the section titled "The netbuf Structure and the Packet Queues". All the information needed to send the packet is filled into the structure addressed by this type before this call is made. Some of the important fields are:

pkt->nb_prot;   /* pointer to data to send. */
pkt->nb_plen;   /* length of data to send */
pkt->net;      /* nets[ ]structure for posting statistics */

The data addressed by pkt->nb_prot may or may not have already been prefixed with a MAC layer header depending on how the nets[ ] structure associated with the interface (pkt->net) has been configured. The rule for determining whether the MAC layer header is present or not can be expressed with the following pseudocode fragment.

if ((pkt->net->n_mibifType == SLIP)
      || (pkt->net->n_mib->ifType == PPP)
      || (pkt->net->n_lnh== 0))
   the packet at pkt->nb_prot is not encapsulated with a MAC header;
else
   the packet at pkt->nb_prot is encapsulated with a MAC header;

If the if statement in the above pseudocode evaluates to TRUE then the packet at nb_prot is not encapsulated with a MAC header and it is up to the network interface code to transmit the MAC header that is appropriate for the network medium (if any). On the other hand, if the "if" statement evaluates to FALSE then appropriate MAC headers for media such as Ethernet or Token Ring will have been placed at the head of the buffer passed by the calling routine and are not the responsibility of this routine; however some drivers may have to access, strip or modify the MAC header if they are layered on top of complex lower layers. The ODI pkt_send() routine is an example of this (see doslib/odi.c).

Regardless of whether it is the responsibility of the network interface layer to transmit the MAC header, it is necessary for the network interface to transmit the nb_plen bytes starting at nb_prot plus "any" MAC header bias that was used to align the start of the IP header. For Ethernet devices, the macro ETHHDR_BIAS is sometimes defined to 2 bytes, to align the IP header at a 4 byte boundary. Likewise, the number of bytes to transmit in this case would be (nb_plen - ETHHDR_BIAS), if ETHHDR_BIAS was defined to non-zero. When all the bytes are sent, the structure addressed by the PACKET type should be returned to the free queue by a call to pk_free(), which may be called at interrupt time. Do not free the packet before it has been entirely sent by the hardware, since it may be reused (and its buffer altered) by the IP stack.

The simplest way to implement this routine is to block (busy-wait) until the data is sent. This allows for fast prototyping of new drivers, but will generally hurt performance. The usual design followed by InterNiche in the example drivers is to put the packet in an awaiting_send queue, check to see if the hardware is idle, and then call a send_next_from_q routine to dequeue the packet at the head of the send queue and begin sending it. The "end of transmit" ISR (EOT) frees the just sent packet and again calls the send_next_from_q routine. By moving all the PACKETs through the awaiting_send queue we ensure that they are sent in FIFO order, which significantly improves TCP and application performance.

If your hardware (or lower layer driver) does not have an end of transmit (EOT) interrupt or any analogous mechanism, you may need to use the raw_send() alternative to this function.

Slow devices (such as serial links), and hardware which DMAs data directly out of predefined memory areas, may copy the passed buffer into driver managed memory buffers, free the PACKET and return immediately; however they should be prepared to be called with more packets before transmission is complete.

Interface transmit routines should also maintain system statistics about packet transmissions. These are kept in the IfMib structure that is addressed by the n_mib field in each nets[ ] entry. Exact definitions of all these counters are available in RFC1213. At a minimum you should maintain packet byte and error counts since these can aid greatly with debugging your product during development and isolating configuration problems in field. Statistics keeping is best done at EOT time, but can be approximated in this call. The following fragment of code is a generic example:

/* compile statistics about completed transmit */
eth = (struct ethhdr *)pkt->nb_prot;   /* get ether header */
ifc = pkt->net;
if(send_status == SUCCESSFUL)   /* send_status set by hardware EOT */
{
    if(eth->e_dst[0] ... 0x01)      /* see if multicast bit is on */
        ifc->n_mib->ifOutNUcastPkts++;
     else
        ifc->n_mib->ifOutUcastPkts++;

    ifc->n_mib->ifOutOctets +=pkt->nb_plen;
}
else   /* error sending packet */
{
    ifc->n_mib->ifOutErrors++;
}

Returns

Returns 0 if OK, else one of the ENP_ codes. Since this routine may not be waiting for the packet transmission to complete, it is permissible to return a 0 if the packet has been successfully queued for send or the send is in progress. Error (non-zero) codes should only be returned if a distinct hardware (or lower layer) failure is detected. There is no mechanism to report errors detected in previous packets or during the EOT. Upper layers like TCP will retry the packet when it is not acknowledged.

See Also

raw_send()

Name

raw_send()

Syntax

int raw_send(NET net, char * data, unsigned data_bytes);

Parameters

typedef struct net *NET

NET net /* pointer to net structure to send it on */

char *data /* pointer to data buffer to send */

unsigned data_bytes /* number of bytes to send (length of data) */

Description

This routine should transmit the data as indicated on the device corresponding to the net parameter passed. A MAC header may or may not have been prefixed to the IP data depending on how the nets[ ] structure addressed by the net parameter has been configured. See the description of MAC headers in the description of the pkt_send(). This routine should not return until it is through with the data in the passed buffer, as the buffer may be reused (thus corrupting the data) immediately upon return.

The pkt_send() routine should be used instead of this one if there is an end of transmit interrupt (EOT) available on the hardware. This routine was designed for old DOS "packet driver" specification drivers which did not support EOT and should generally not be used on modern designs.

Slow devices (such as serial links), and hardware which DMAs data directly out of predefined memory areas may copy the passed buffer into driver managed memory and return immediately; however they should be prepared to be called with more buffers before transmission is complete.

Interface transmit routines should also maintain system statistics about packet transmissions. These are kept in the n_mib structure attached to each nets[ ] entry. Exact definitions of all these counters are available in RFC1213. At a minimum you should maintain packet byte and error counts since these can aid greatly with debugging your product during development and isolating configuration problems in the field. Statistics keeping is best done at EOT time, but can be approximated in this call. The following fragment of code is an example that works for Ethernet devices:

/* compile statistics about completed transmit */
eth = (struct ethhdr *)pkt->nb_prot;   /* get ether header */
if(send_status == SUCCESSFUL)   /* send_status set by hardware EOT */
{
   if(eth->e_dst[0] ... 0x01)         /* see if multicast bit is on */
      ifc->n_mib->ifOutNUcastPkts++;
   else
      ifc->n_mib->ifOutUcastPkts++;

   ifc->n_mib->ifOutOctets +=pkt->nb_plen;
}
else         /* error sending packet */
{
   ifc->n_mib->ifOutErrors++;
}

Returns

Returns 0 if OK, else one of the ENP_ codes.

See Also

pkt_send()

Name

n_close()

Syntax

int n_close(int if_number);

Parameters

int if_number /* index into nets[ ] for NET to close */

Description

Does whatever is necessary to restore the device and its associated driver software prior to exiting the application. This function may not be required to do anything on embedded systems which start their devices at power up and don't have any reason to shut them down. If packet types (i.e.: 0x0800 for IP and 0x0806 for ARP) have been accessed in a lower layer driver, they should be released here.

Returns

Returns 0 if OK, else one of the ENP_ codes.

Name

n_reg_type()

Syntax

int n_reg_type(unshort type, NET net);

Parameters

unshort type NET net

Description

Register with any lower level drivers to receive a MAC type, i.e. 0x0800 for IP and 0x0806 for ARP. On most embedded systems with Ethernet, where the InterNiche stack does not share the hardware with other network stacks, no action is required. Since the InterNiche stack gets all the packets anyway, n_reg_type() can simple return an OK code without doing anything. The porting engineer should be sure, however, that all received packets will be passed to the stack. On some driver subsystems, such as DOS Packet Drivers and ODI drivers, a type must be registered with the driver. On DOS NDIS drivers an intermediate layer must be notified that we are interested in the packets.

On SLIP links, all packets are IP, so nothing has to be done in n_reg_type().

On PPP links, PPP will sort out the packets, so again, nothing has to done in n_reg_type().

Returns

Returns 0 if OK, else one of the ENP_ codes.

Name

n_setstate()

Syntax

int n_setstate(struct net * ifp, int opcode);

Parameters

struct net * ifp /* pointer to net structure whose state is to be set */

int opcode /* operation code */

Description

n_setstate() allows the driver to receive requests to mark the interface up or down when the application calls the stack's ni_set_state() API function. This function is optional: it is not used if the stack is built without the DYNAMIC_IFACES option defined and so does not support dynamic network interfaces, and if it is not supplied, the n_setstate member of struct net may be set to NULL to inform the stack that it is not to be used.

When n_setstate() is called, the ifp argument is set to the struct net for the network interface whose state is to be set, and the opcode argument will be set to NI_UP if the network interface is to be marked as "up", or NI_DOWN if it is to be marked as "down".

Returns

Returns 0 if OK, else one of the ENP_ codes.

Name

n_stats()

Syntax

void n_stats(void * pio, int if_number);

Parameters

void * pio /* pointer to a Generic I/O Structure */

int if_number /* interface number to dump statistics for */

Description

OPTIONAL: n_stats() enables the driver to provide hardware specific information which not included in the generic MIB-II interface group. This information might include hardware specific error counters, such as the number of collisions on an Ethernet link; or internal resource information, such as the status and number of current buffers available on a ring-buffer device. The routine usually makes several printf()-like calls to send formatted informational text to a standard output device such as a console or Telnet session.

Returns

void.

Name

create_device()

Syntax

int create_device(NET ifp, void * bindinfo);

Parameters

NET ifp /* interface descriptor (pointer to struct net) */

void * bindinfo /* driver-specific binding information for the device */

Description

A create_device() function must be supplied for drivers that are written to support dynamic network interfaces. This function must be passed to the stack's ni_create() function when creating a network interface; ni_create() will call this function so that the driver can bind or attach to a device and complete the initialization of the struct net for the device.

When the stack calls the driver's create_device() function, the ifp argument will be a pointer to the newly-created network interface's struct net, and the bindinfo argument will be the bindinfo argument that was passed to ni_create(). The create_device() function may use the bindinfo argument to locate any addressing or binding information that it needs to initialize the network interface device, and should perform initialization of the supplied struct net, as would need to be done by prep_ifaces() and n_init() for static network interfaces that are initialized at startup time.

Returns

Returns 0 if OK, else one of the ENP_ codes.

Name

rcvdq

Syntax

queue rcvdq;

Description

rcvdq is a global structure to which packets received at the MAC layer should be enqueued. For each target system network interface, the porting engineer will need to write code to enqueue packets received on that interface to rcvdq. The steps involved in enqueuing received packets to rcvdq are summarized below:

  1. Allocate a netbuf structure to contain the received data by calling pk_alloc().
  2. Initialize the fields of the netbuf structure to properly describe the received data. See the section titled "The netbuf Structure and the Packet Queues" for a description of the netbuf structure fields.
  3. Copy the received data to the buffer associated with the netbuf structure.
  4. Call the putq() function to enqueue the netbuf structure to rcvdq.
  5. Call the SignalPktDemux() function to signal the IP layer to process the enqueued packet.

The details of each of these steps are described below.

3.2.1.1 netbuf Allocation

pk_alloc() returns a pointer to a netbuf structure that contains a pointer to a packet buffer and other fields that describe the packet to be enqueued. (Note that the source code shows pk_alloc() returning a value of type PACKET. PACKET is typed to be a pointer to netbuf structures.) The parameter to pk_alloc() specifies the length in bytes of the data to be stored in the packet buffer. The length that should be specified in the call to pk_alloc() should be the length of the received packet, less the length of the MAC layer header, plus the value containing the global integer MaxLnh. In the example shown below, suppose that the local variable frameLen contained the length of a received Ethernet frame. Given this supposition, the example illustrates the correct call to allocate the netbuf structure to be used to enqueue the frame.

int frameLen;
PACKET pkt;

frameLen = length of received Ethernet frame including Ethernet MAC header;
pkt = pk_alloc(frameLen - 14 + MaxLnh);    /* Ethernet MAC header is 14 bytes */

When pk_alloc() succeeds in allocating a netbuf structure, it returns a pointer to that structure with an associated packet buffer. If pk_alloc() fails due to lack of buffers, it returns NULL. When this happens, the packet should be discarded.

3.2.1.2 netbuf Initialization

Once a netbuf structure has been allocated for the received packet, it needs to be initialized to describe the packet. The code fragments shown below illustrate how the various fields of the netbuf structure need to be initialized.

pkt->nb_prot = pkt->nb_buff + MaxLnh;  /* point to start of IP header */

nb_buff points to the beginning of the allocated packet data buffer. nb_prot points to the received data less any MAC layer header. For received IP packets, nb_prot ends up pointing to the beginning of the IP header. If any MAC header bias was used to align the start of the IP header, then nb_prot must point past the ETHHDR_BIAS offset. If used in the port the ETHHDR_BIAS is usually set to 2 for Ethernet Devices.

Drivers developed for the 1.8 and later releases, and which have set the NF_NBPROT flag in the device's struct net n_flags field, must set nb_prot to point to the start of the IP header. This can be wherever is appropriate for the device and target so long as nb_prot points into the buffer starting at nb_buff. (Note: Some targets may have alignment requirements that force the IP header to start on a word-aligned boundary. If so, the driver should make sure that nb_prot is set so that it is aligned according to ALIGN_TYPE.

NOTE:Drivers developed for releases of NicheStack earlier than 1.8 must set nb_prot so that it is offset from the beginning of the packet data buffer by the number stored in MaxLnh.

pkt->nb_tstamp = cticks;

nb_tstamp is time stamped with the current clock tick.

pkt->type = IPTP or ARPTP;

type is used to indicate to the IP layer whether the received packet is an IP packet or an ARP packet. The network interface code must make this determination which will be dependent on the nature of the interface. IPTP and ARPTP are defined constants in the file ip.h.

pkt->net = appropriate element of nets[ ] array;

net should contain a pointer to the nets[ ] array element associated with the interface.

int length = frameLength - length of MAC layer header;
pkt->net->n_mib->ifInOctets += frameLength;   /* maintain MIB counter */
pkt->nb_plen = length;

nb_plen should be set to indicate the length of the received data less any MAC layer header. This value should be added to the MIB counter.

3.2.1.3 Copy Received Data to netbuf Packet Buffer

The next step is to copy the received packet, less any MAC layer header, to the packet buffer offset stored in nb_prot, as illustrated below:

   MEMCPY(pkt->nb_prot, data, length);

3.2.1.4 Enqueuing netbuf structure to rcvdq

The next step is to enqueue the netbuf structure to rcvdq. This is performed via a single function call, as illustrated below:

   putq(...rcvdq, (q_elt) pkt);

3.2.1.5 Signal Packet Demultiplexor

The last step is to signal the packet demultiplexor to allow the portable pktdemux() function to be called in order to allow the IP layer to demultiplex the received packets that have been enqueued to rcvdq. The network interface software provided by the InterNiche stack calls the port dependent function SignalPktDemux() in order to do this. Network interface code written by the porting engineer for a given target system should do the same. See the discussion of SignalPktDemux().