14.3   Upgrading to 4.4 BSD

To upgrade a driver from 4.3 BSD to 4.4 BSD you must change how the driver uses ether_attach( ). This routine is almost always called from the driver's own xxattach( )routine and is responsible for placing the driver's entry points, listed in Table 14-1, into the ifnet structure that the TCP/IP protocol to track drivers. Consider the call to ether_attach( ) shown below:

ether_attach( 
    (IFNET *) & pDrvCtrl->idr, 
    unit, 
    "xx", 
    (FUNCPTR) NULL, 
    (FUNCPTR) xxIoctl, 
    (FUNCPTR) xxOutput, 
    (FUNCPTR) xxReset 
    );

As arguments, this routine expects an Interface Data Record (idr), a unit number, and a quoted string that is the name of the device, in this case, "xx". The next four arguments are the function pointers to relevant driver routines.

The first function pointer references this driver's init( )routine, which this driver does not need or have. The second function pointer references the driver's ioctl( )interface, which allows the upper layer to manipulate the device state. The third function pointer references the routine that outputs packets on the physical medium. The last function pointer references a routine that can reset the device if the TCP/IP stack decides that this needs to be done.

In 4.4 BSD, there is a generic output routine called ether_output( )that all Ethernet device drivers can use. Thus, to convert the above ether_attach( ) call to a 4.4-style call, you would call ether_attach( ) as follows:

ether_attach( 
    (IFNET *) & pDrvCtrl->idr, 
    unit, 
    "xx", 
    (FUNCPTR) NULL, 
    (FUNCPTR) xxIoctl, 
    (FUNCPTR) ether_output, /* generic ether_output */ 
    (FUNCPTR) xxReset 
    ); 
pDrvCtrl->idr.ac_if.if_start = (FUNCPTR)xxTxStartup;

This time, there is an extra line following the call to ether_attach( ). This line of code adds a transmit startup routine to the Interface Data Record. The transmit startup routine is called by the TCP/IP stack after the generic ether_output( ) routine is called. This extra line of code assumes that the driver already has a transmit startup routine. If a driver lacks a separate transmit startup routine, you must write one. See the template in 14.3.4 Creating a Transmit Startup Routine.

14.3.1   Removing the xxOutput Routine

If a 4.3 BSD driver has an xxOutput( ) routine, it probably looks something like the following:

static int xxOutput 
    ( 
    IDR *    pIDR, 
    MBUF *     pMbuf, 
    SOCK * pDestAddr 
    ) 
    { 
    return (ether_output ((IFNET *)pIDR, pMbuf, pDestAddr, 
        (FUNCPTR) xxTxStartup, pIDR)); 
    }

Internally, this routine calls the ether_output( )routine, which expects a pointer to the startup routine as one of its arguments. However, in the 4.4 BSD model, all that work that is now handled in the TCP/IP stack. Thus, in a 4.4 BSD driver, this code is unnecessary and should be removed.

14.3.2   Changing the Transmit Startup Routine

Under 4.3 BSD, the function prototype for a transmit startup routine is as follows:

static void xxTxStartup (int unit);

Under 4.4 BSD, the prototype has changed to the following:

static void xxTxStartup (struct ifnet * pDrvCtrl);

The 4.4 BSD version expects a pointer to a driver control structure. This change eases the burden on the startup routine. Instead of having to find its own driver control structure, it receives a pointer to a driver control structure as input.

If the driver uses netJobAdd( )to schedule the transmit startup routine for task-level execution, edit the netJobAdd( )call to pass in a DRV_CTRL structure pointer instead of a unit number.

14.3.3   Changes in Receiving Packets

Under 4.3 BSD, the driver calls do_protocol_with_type( ). For example:

do_protocol_with_type (etherType, pMbuf, &pDrvCtrl->idr, len);

This call expects an etherType (which the driver had to discover previously), a pointer to an mbuf containing the packet data, the Interface Data Record, and the length of the data.

Under 4.4 BSD, replace the call above with a call to do_protocol( ). For example:

do_protocol (pEh, pMbuf, &pDrvCtrl->idr, len);

The first parameter is a pointer to the very beginning of the packet (including the link level header). All the other parameters remain the same. The driver no longer needs to figure out the etherType for the protocol.

14.3.4   Creating a Transmit Startup Routine

Some 4.3 BSD drivers did not have a transmit startup routine. For such a driver, you must create one. The template is as follows:

void templateStartup 
    ( 
    DRV_CTRL *pDrvCtrl 
    ) 
    { 
    MBUF * pMbuf; 
    int length; 
    TFD * pTfd; 
 
    /* Loop until there are no more packets ready to send or we 
     * have insufficient resources left to send another one. */ 
    while (pDrvCtrl->idr.ac_if.if_snd.ifq_head) 
        { 
        /* Deque a packet from the send queue. */ 
        IF_DEQUEUE (&pDrvCtrl->idr.ac_if.if_snd, pMbuf);  
 
        /* Device specific code to get transmit resources, such as a 
         * transmit descriptor, goes here. */ 
 
        if (Insufficient Resources) 
            { 
            m_freem (pMbuf);    /* Make sure to free the packet. */ 
            return; 
            } 
 
        /* pData below is really the place in your descriptor, 
         * transmit descriptor, or equivalent, where the data is 
         * to be placed. */ 
 
        copy_from_mbufs (pData, pMbuf, length); 
        if ((etherOutputHookRtn != NULL) && 
            (* etherOutputHookRtn) 
            (&pDrvCtrl->idr, (ETH_HDR *)pTfd->enetHdr, length)) 
            continue; 
 
        /* Do hardware manipulation to set appropriate bits 
         * and other stuff to get the packet to actually go. 
         */ 
 
        /* 
         * Update the counter that determines the number of 
         * packets that have been output. 
         */ 
 
        pDrvCtrl->idr.ac_if.if_opackets++; 
 
        } /* End of while loop. */ 
    } /* End of transmit routine. */