ftp.nice.ch/pub/next/developer/resources/libraries/eni.a.tar.gz#/eni/eni.c

This is eni.c in view mode; [Download] [Up]

#include <stdio.h>
#include <kernserv/kern_server_types.h>
#include <net/netif.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <strings.h>
#include "eni.h"
#include "libeni.h"
#include "messages.h"


struct packet_buffer
{
    int in_use ;
    struct m_packet rpck ;
} ;


extern int curipl(void) ;
extern int input(netif_t mnetif, netif_t rnetif, netbuf_t packet, void *extra) ;
extern int netbuf2packet(netbuf_t nb, unsigned char *packet) ;
extern int output_packet(msg_header_t *msg) ;
extern kern_return_t thread_resume(thread_t t) ;
extern kern_return_t thread_suspend(thread_t t) ;
extern port_t thread_self(void) ;
extern void queue_packet(netbuf_t p) ;
extern void send_pmsg(void *ignore) ;


kern_server_t instance ;
static netif_t dev_netif ;	    /* netif_t of the device driver */
static port_t in_port ;		    /* send input packets here */
static struct eni_stats global_stats ;
static struct m_reply_ret_code msg_rcode ;
#if 0
static struct m_reply_stats msg_stats ;
#endif
static struct packet_buffer pbuf ;
static u_short eni_frame_type ;


/*
    Tell the device driver we want its packets.
*/

static void attach(void *private, netif_t rnetif)
{
    netif_t mnetif ;


    if(strcmp(if_type(rnetif), IFTYPE_ETHERNET) != 0)
    {
	return ;
    }

    mnetif = if_attach(0, input, 0, 0, 0, ENI_NAME, ENI_UNIT, ENI_TYPE_NAME,
		       ENI_MTU, IFF_UP, NETIFCLASS_VIRTUAL, (void *)0) ;
    if(mnetif == 0)
    {
	printf("%s: attach: error from if_attach()\n", ENI_NAME) ;
	return ;
    }

    dev_netif = rnetif ;
}


/*
    This gets called when we are loaded by the kernel server loader.
*/

void config(int n)
{
    eni_frame_type = htons(ENI_FRAME_TYPE) ;
    if_registervirtual(attach, 0) ;
}


/*
    This routine handles all the messages we receive.  It is called
    automatically when a message arrives.

    If 'r' is 0 return TRUE, otherwise return FALSE.
*/

boolean_t handle_msg(msg_header_t *msg, void *extra)
{
    int r = 0 ;
    int send_reply = 0 ;
    msg_return_t m ;
    void *reply = (void *)0 ; /* kludge */


    switch(msg->msg_id)
    {
	/*
	    We've received a packet to be output.

	    Send it to the device driver, then send a reply back
	    containing the return code.
	*/
	
	case MSG_SEND_PACKET:
	    r = output_packet(msg) ;
	    if(msg->msg_remote_port != PORT_NULL)
	    {
		send_reply = 1 ;
		reply = (void *)&msg_rcode ;
		
		msg_rcode.head.msg_simple = TRUE ;
		msg_rcode.head.msg_size = sizeof(struct m_reply_ret_code) ;
		msg_rcode.head.msg_type = MSG_TYPE_NORMAL ;
		msg_rcode.head.msg_local_port = PORT_NULL ;
		msg_rcode.head.msg_remote_port = msg->msg_remote_port ;
		msg_rcode.head.msg_id = MSG_RET_CODE ;

		msg_rcode.type.msg_type_name = MSG_TYPE_INTEGER_32 ;
		msg_rcode.type.msg_type_size = sizeof msg_rcode.ret_code * 8 ;
		msg_rcode.type.msg_type_number = 1 ;
		msg_rcode.type.msg_type_inline = TRUE ;
		msg_rcode.type.msg_type_longform = FALSE ;
		msg_rcode.type.msg_type_deallocate = FALSE ;

		msg_rcode.ret_code = r ;
	    }
	    break ;

	/*
	    We've received a request for statistics.

	    Unfinished.
	*/
	
#if 0
	case MSG_STATS:
	    if(msg->msg_remote_port != PORT_NULL)
	    {
		send_reply = 1 ;
		reply = (void *)&msg_stats ;

		msg_stats.head.msg_simple = TRUE ;
		msg_stats.head.msg_size = sizeof(struct m_reply_stats) ;
		msg_stats.head.msg_type = MSG_TYPE_NORMAL ;
		msg_stats.head.msg_local_port = PORT_NULL ;
		msg_stats.head.msg_remote_port = msg->msg_remote_port ;
		msg_stats.head.msg_id = MSG_STATS ;

		bcopy((void *)&global_stats, (void *)&msg_stats.stats,
		      sizeof(struct m_reply_stats)) ;
	    }
	    break ;
#endif

	/*
	    The client is giving us access to the port on which it will
	    receive packets.
	*/
	
	case MSG_INIT:
#ifdef DEBUG
	    printf("%s: handle_msg: MSG_INIT.\n", ENI_NAME) ;
#endif
	    send_reply = 0 ;
	    in_port = msg->msg_remote_port ;
	    break ;

	default:
	    r = 1 ;
	    printf("%s: handle_msg: unknown message type: %d\n", ENI_NAME,
		   msg->msg_id) ;
	    break ;
    }


    /*
        Send a reply, if one is expected.
    */
    
    if(send_reply)
    {
	m = msg_send((msg_header_t *)reply, MSG_OPTION_NONE, 0) ;
	if(m != SEND_SUCCESS)
	{
	    printf("%s: handle_msg: msg_send() returns %d\n", ENI_NAME, m) ;
	}
	else
	{
	    global_stats.replies++ ;
	}
    }

    return r == 0 ? TRUE : FALSE ;
}


/*
    Receive a packet from the device driver.  If the frame type is ours then
    have it sent to the client.

    Return 0 on success.
*/

int input(netif_t mnetif, netif_t rnetif, netbuf_t pck_nb, void *extra)
{
    int r ;
    u_short etype ;


#ifdef DEBUG
    printf("%s: input: start.\n", ENI_NAME) ;
    printf("%s: input: full packet size = %d.\n", ENI_NAME,
	   (int)nb_size(pck_nb)) ;
#endif

    r = nb_read(pck_nb, (unsigned)TYPE_OFFSET, (unsigned)TYPE_SIZE,
		(void *)&etype) ;

    if(r)
    {
	return r ;
    }
    else
    {
	if(etype != ENI_FRAME_TYPE)
	{
	    return EAFNOSUPPORT ;
	}
	else
	{
	    global_stats.in_packets++ ;
	    queue_packet(pck_nb) ;
	    return 0 ;
	}
    }
}


/*
    Return 0 on success, positive integer on failure.
    *** Look through errno.h for more appropriate return codes. ***

    Cause a packet of the form:

	daddr:saddr:type:utype:...data...

    to be sent.
*/

int output_packet(msg_header_t *msg)
{
    int dlen ;			/* packet data length */
    int r ;
    netbuf_t onb ;		/* output netbuf_t (wrapper) */
    u_short user_frame_type ;	/* the type of the frame given to us */
    unsigned char *pck ;	/* pointer to packet */
    unsigned char *daddr ;	/* destination Ethernet address */


#ifdef DEBUG
    printf("%s: output_packet: start.\n", ENI_NAME) ;
#endif

    onb = if_getbuf(dev_netif) ;
    if(onb == (netbuf_t)0)
    {
	printf("%s: output_packet: can't get netbuf.\n", ENI_NAME) ;
	return ENOBUFS ;
    }

    pck = ((struct m_packet *)msg)->data ;
    dlen = ((struct m_packet *)msg)->eh.data_length ;
    daddr = ((struct m_packet *)msg)->eh.daddr ;
    user_frame_type = ((struct m_packet *)msg)->eh.frame_type ;

    /*
	Put our type into the frame type field of the packet, the user
	specified type into the first two bytes of the data section,
	followed by the user's data.
	
	Check return codes.
    */
    
    if(nb_write(onb, (unsigned)TYPE_OFFSET, (unsigned)TYPE_SIZE, (void *)&eni_frame_type)
       || nb_write(onb, (unsigned)DATA_OFFSET, (unsigned)TYPE_SIZE, (void *)&user_frame_type)
       || nb_write(onb, (unsigned)(DATA_OFFSET + TYPE_SIZE), (unsigned)dlen, (void *)pck))
    {
	printf("%s: output_packet: nb_write() failure.\n", ENI_NAME) ;
	return ENOBUFS ;
    }

    if(nb_shrink_bot(onb, (unsigned)(nb_size(onb) - ETHERHDRSIZE - dlen - TYPE_SIZE)))
    {
	printf("%s: output_packet: nb_shrink_bot() failed.\n", ENI_NAME) ;
	return ENOBUFS ;
    }

    /*
        Fire it off...
    */
    
#ifdef DEBUG
    printf("%s: output_packet: full packet size = %d.\n", ENI_NAME,
	   (int)nb_size(onb)) ;
#endif

    r = if_output(dev_netif, onb, (void *)daddr) ;
    if(r != 0)
    {
	if(r == ENXIO) /* so what else, exactly, can it return?! */
	{
	    printf("%s: output_packet: if_output(): output not implemented.\n",
		   ENI_NAME) ;
	    return ENODEV ;
	}
	else
	{
	    printf("%s: output_packet: if_output() returned %d.\n",
		   ENI_NAME, r) ;
	    return EIO ;
	}
    }

    return 0 ;
}


/*
    Just stuff the packet in a message and let the front end library routines
    worry about swapping in the user frame type.
*/

void queue_packet(netbuf_t nb)
{
    int psize ;
    kern_return_t r ;


#ifdef DEBUG
    printf("%s: queue_packet: start.\n", ENI_NAME) ;
#endif

    /*
        If the packet is in the process of being sent to the user, drop the
        current one.  We don't want to trash the packet being sent.
    */
    
    if(pbuf.in_use)
    {
#ifdef DEBUG
	printf("%s: queue_packet: packet buffer in use -- dropped.\n",
	       ENI_NAME) ;
#endif
	return ;
    }

    /*
        If the buffer is not currently in use then we are assured that it will
        not be accessed until this routine returns, since it is called from
        the device driver.
    */
    
    pbuf.in_use = 1 ;

    /*
        Move the data out of the netbuf_t and into the data area of the
        message.
    */

    psize = nb_size(nb) ;
    if(nb_read(nb, (unsigned)0, (unsigned)psize, (void *)pbuf.rpck.data))
    {
	printf("%s: queue_packet: error moving netbuf_t to message.\n",
	       ENI_NAME) ;
	global_stats.lost_out_packets++ ;
	return ;
    }

    pbuf.rpck.head.msg_simple = TRUE ;
    pbuf.rpck.head.msg_size = sizeof(struct m_packet) ;
    pbuf.rpck.head.msg_type = MSG_TYPE_NORMAL ;
    pbuf.rpck.head.msg_local_port = PORT_NULL ;
    pbuf.rpck.head.msg_remote_port = in_port ;
    pbuf.rpck.head.msg_id = MSG_HAVE_PACKET ;

    pbuf.rpck.type_1.msg_type_name = MSG_TYPE_BYTE ; /* ??? */
    pbuf.rpck.type_1.msg_type_size = sizeof pbuf.rpck.eh * 8 ;
    pbuf.rpck.type_1.msg_type_number = 1 ;
    pbuf.rpck.type_1.msg_type_inline = TRUE ;
    pbuf.rpck.type_1.msg_type_longform = FALSE ;
    pbuf.rpck.type_1.msg_type_deallocate = FALSE ;

    pbuf.rpck.eh.data_length = psize - ETHERHDRSIZE - TYPE_SIZE ;

    pbuf.rpck.type_2.msg_type_name = MSG_TYPE_BYTE ;
    pbuf.rpck.type_2.msg_type_size = 8 ;
    pbuf.rpck.type_2.msg_type_number = sizeof pbuf.rpck.data ;
    pbuf.rpck.type_2.msg_type_inline = TRUE ;
    pbuf.rpck.type_2.msg_type_longform = FALSE ;
    pbuf.rpck.type_2.msg_type_deallocate = FALSE ;

    /*
        Schedule 'send_pmsg' to be run, which will send the packet to the user
        via a message.  'kern_serv_callout' does not sleep/block.
    */
    
    if((r = kern_serv_callout(&instance, send_pmsg, (void *)0)) != KERN_SUCCESS)
    {
	printf("%s: queue_packet: kern_serv_callout() failed with %d.\n",
	       ENI_NAME, (int)r) ;
    }
}


/*
    Send the message containing the packet to the packet reception port of the
    front-end library.
*/

void send_pmsg(void *ignore)
{
    msg_return_t m ;


#ifdef DEBUG
    printf("%s: send_pmsg: start.\n", ENI_NAME) ;
#endif

    if( ! pbuf.in_use)
    {
	printf("%s: send_pmsg: BUG: packet buffer not in use.\n", ENI_NAME) ;
    }
    else
    {
#ifdef DEBUG
	printf("%s: send_pmsg: data length = %d.\n", ENI_NAME,
	       pbuf.rpck.eh.data_length) ;
#endif
	m = msg_send((msg_header_t *)&pbuf.rpck, MSG_OPTION_NONE, 0) ;
	if(m != SEND_SUCCESS)
	{
	    global_stats.lost_out_packets++ ;
	}

	/*
	    Free it so we can receive more packets.
	*/
	
	pbuf.in_use = 0 ;

#ifdef DEBUG
	printf("%s: send_msg: msg_send() returned %d.\n", ENI_NAME, m) ;
#endif
    }
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.