#include <machine/rtems-bsd-kernel-space.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define RTEMS_FAST_MUTEX

#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>

#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/error.h>
#include <rtems/thread.h>
#include <rtems/rtems_bsdnet.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/domain.h>
#include <sys/mbuf.h>
#include <sys/socketvar.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/callout.h>
#include <sys/proc.h>
#include <sys/sockio.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <vm/vm.h>

#include <net/netisr.h>
#include <net/route.h>

#include "loop.h"

/*
 * Memory allocation
 */
static uint32_t nmbuf       = (64L * 1024L) / _SYS_MBUF_LEGACY_MSIZE;
       uint32_t nmbclusters = (128L * 1024L) / MCLBYTES;

/*
 * Network task synchronization
 */
static rtems_recursive_mutex networkMutex =
    RTEMS_RECURSIVE_MUTEX_INITIALIZER("_Network");
static rtems_id networkDaemonTid;
static uint32_t   networkDaemonPriority;
#ifdef RTEMS_SMP
static const cpu_set_t *networkDaemonCpuset = 0;
static size_t          networkDaemonCpusetSize = 0;
#endif
static void networkDaemon (void *task_argument);

/*
 * Network timing
 */
int			rtems_bsdnet_ticks_per_second;
int			rtems_bsdnet_microseconds_per_tick;

/*
 * Callout processing
 */
static rtems_interval	ticksWhenCalloutsLastChecked;
struct callout *callfree = NULL;
struct callout calltodo;

/*
 * FreeBSD variables
 */
int nfs_diskless_valid;

/*
 * BOOTP values
 */
struct in_addr rtems_bsdnet_log_host_address = {0};
struct in_addr rtems_bsdnet_bootp_server_address = {0};
char *rtems_bsdnet_bootp_boot_file_name = 0;
char *rtems_bsdnet_bootp_server_name = 0;
char *rtems_bsdnet_domain_name = 0;
char *rtems_bsdnet_bootp_cmdline = 0;
static struct in_addr _rtems_bsdnet_nameserver[sizeof rtems_bsdnet_config.name_server /
			sizeof rtems_bsdnet_config.name_server[0]];
struct in_addr *rtems_bsdnet_nameserver = _rtems_bsdnet_nameserver;
int rtems_bsdnet_nameserver_count = 0;
static struct in_addr _rtems_bsdnet_ntpserver[sizeof rtems_bsdnet_config.ntp_server /
			sizeof rtems_bsdnet_config.ntp_server[0]];
struct in_addr *rtems_bsdnet_ntpserver = _rtems_bsdnet_ntpserver;
int rtems_bsdnet_ntpserver_count = 0;
int32_t rtems_bsdnet_timeoffset = 0;

static const struct sockaddr_in address_template = {
	sizeof(address_template),
	AF_INET,
	0,
	{ INADDR_ANY },
	{ 0, 0, 0, 0, 0, 0, 0, 0 }
};

static void
rtems_bsdnet_initialize_sockaddr_in(struct sockaddr_in *addr)
{
	memcpy(addr, &address_template, sizeof(*addr));
}

uint32_t
rtems_bsdnet_semaphore_release_recursive(void)
{
	uint32_t nest_count;

	nest_count = networkMutex._nest_level;
	networkMutex._nest_level = 0;
	rtems_recursive_mutex_unlock(&networkMutex);
	return nest_count;
}

void
rtems_bsdnet_semaphore_obtain_recursive(uint32_t nest_count)
{
	rtems_recursive_mutex_lock(&networkMutex);
	networkMutex._nest_level = nest_count;
}

/*
 * Perform FreeBSD memory allocation.
 * FIXME: This should be modified to keep memory allocation statistics.
 */
#undef malloc
#undef free
extern void *malloc (size_t);
extern void free (void *);
void *
rtems_bsdnet_malloc (size_t size, int type, int flags)
{
	void *p;
	int try = 0;

	for (;;) {
		uint32_t nest_count;

		p = malloc (size);
		if (p || (flags & M_NOWAIT))
			return p;
		nest_count = rtems_bsdnet_semaphore_release_recursive ();
		if (++try >= 30) {
			rtems_bsdnet_malloc_starvation();
			try = 0;
		}
		rtems_task_wake_after (rtems_bsdnet_ticks_per_second);
		rtems_bsdnet_semaphore_obtain_recursive (nest_count);
	}
}

/*
 * Free FreeBSD memory
 * FIXME: This should be modified to keep memory allocation statistics.
 */
void
rtems_bsdnet_free (void *addr, int type)
{
	free (addr);
}

/*
 * Externs for BSD data we have to access during initialization
 */
extern struct domain routedomain;
extern struct domain inetdomain;

/*
 * Do the initializations required by the BSD code
 */
static int
bsd_init (void)
{
	int i;
	char *p;

	/*
	 * Set up mbuf cluster data strutures
	 */
	p = rtems_bsdnet_malloc_mbuf ((nmbclusters*MCLBYTES)+MCLBYTES-1, MBUF_MALLOC_NMBCLUSTERS);
	if (p == NULL) {
		printf ("Can't get network cluster memory.\n");
		return -1;
	}
	p = (char *)(((intptr_t)p + (MCLBYTES-1)) & ~(MCLBYTES-1));
	mbutl = (struct mbuf *)p;
	for (i = 0; i < nmbclusters; i++) {
		((union mcluster *)p)->mcl_next = mclfree;
		mclfree = (union mcluster *)p;
		p += MCLBYTES;
		mbstat.m_clfree++;
	}
	mbstat.m_clusters = nmbclusters;
	mclrefcnt = rtems_bsdnet_malloc_mbuf (nmbclusters, MBUF_MALLOC_MCLREFCNT);
	if (mclrefcnt == NULL) {
		printf ("Can't get mbuf cluster reference counts memory.\n");
		return -1;
	}
	memset (mclrefcnt, '\0', nmbclusters);

	/*
	 * Set up mbuf data structures
	 */

	p = rtems_bsdnet_malloc_mbuf(nmbuf * _SYS_MBUF_LEGACY_MSIZE + _SYS_MBUF_LEGACY_MSIZE - 1,MBUF_MALLOC_MBUF);
	p = (char *)(((uintptr_t)p + _SYS_MBUF_LEGACY_MSIZE - 1) & ~(_SYS_MBUF_LEGACY_MSIZE - 1));
	if (p == NULL) {
		printf ("Can't get network memory.\n");
		return -1;
	}
	for (i = 0; i < nmbuf; i++) {
		((struct mbuf *)p)->m_next = mmbfree;
		mmbfree = (struct mbuf *)p;
		p += _SYS_MBUF_LEGACY_MSIZE;
	}
	mbstat.m_mbufs = nmbuf;
	mbstat.m_mtypes[MT_FREE] = nmbuf;

	/*
	 * Set up domains
	 */
	{

	routedomain.dom_next = domains;
	domains = &routedomain;
	inetdomain.dom_next = domains;
	domains = &inetdomain;
	domaininit (NULL);
	}

  /*
   * Setup the sysctl, normally done by a SYSINIT call.
   */
  sysctl_register_all(0);

	/*
	 * Set up interfaces
	 */
	ifinit (NULL);
	return 0;
}

/*
 * Initialize and start network operations
 */
static int
rtems_bsdnet_initialize (void)
{
	rtems_bsdnet_semaphore_obtain ();

	/*
	 * Set the priority of all network tasks
	 */
	if (rtems_bsdnet_config.network_task_priority == 0)
		networkDaemonPriority = 100;
#ifdef RTEMS_MULTIPROCESSING
	/*
	 * Allow network tasks to run with priority 0 (PRIORITY_PSEUDO_ISR) using
	 * UINT32_MAX for the network task priority in the network configuration.
	 * This enables MPCI via a TCP/IP network.
	 */
	else if (rtems_bsdnet_config.network_task_priority != UINT32_MAX)
#else
	else
#endif
		networkDaemonPriority = rtems_bsdnet_config.network_task_priority;

	/*
	 * Default network task CPU affinity
	 */
#ifdef RTEMS_SMP
	networkDaemonCpuset = rtems_bsdnet_config.network_task_cpuset;
	networkDaemonCpusetSize = rtems_bsdnet_config.network_task_cpuset_size;
#endif

	/*
	 * Set the memory allocation limits
	 */
	if (rtems_bsdnet_config.mbuf_bytecount)
		nmbuf = rtems_bsdnet_config.mbuf_bytecount / _SYS_MBUF_LEGACY_MSIZE;
	if (rtems_bsdnet_config.mbuf_cluster_bytecount)
		nmbclusters = rtems_bsdnet_config.mbuf_cluster_bytecount / MCLBYTES;

        rtems_set_udp_buffer_sizes(
          rtems_bsdnet_config.udp_tx_buf_size,
          rtems_bsdnet_config.udp_rx_buf_size
        );

        rtems_set_tcp_buffer_sizes(
          rtems_bsdnet_config.tcp_tx_buf_size,
          rtems_bsdnet_config.tcp_rx_buf_size
        );

        rtems_set_sb_efficiency( rtems_bsdnet_config.sb_efficiency );

	/*
	 * Compute clock tick conversion factors
	 */
	rtems_bsdnet_ticks_per_second = rtems_clock_get_ticks_per_second();
	if (rtems_bsdnet_ticks_per_second <= 0)
		rtems_bsdnet_ticks_per_second = 1;
	rtems_bsdnet_microseconds_per_tick =
		1000000 / rtems_bsdnet_ticks_per_second;

	/*
	 * Set up BSD-style sockets
	 */
	if (bsd_init () < 0) {
		rtems_bsdnet_semaphore_release ();
		return -1;
	}

	/*
	 * Start network daemon
	 */
	networkDaemonTid = rtems_bsdnet_newproc ("ntwk", 4096, networkDaemon, NULL);

	/*
	 * Let other network tasks begin
	 */
	rtems_bsdnet_semaphore_release ();

	rtems_bsdnet_initialize_loop();

	return 0;
}

/*
 * Obtain network mutex
 */
void
rtems_bsdnet_semaphore_obtain (void)
{
	rtems_recursive_mutex_lock(&networkMutex);
}

/*
 * Release network mutex
 */
void
rtems_bsdnet_semaphore_release (void)
{
	rtems_recursive_mutex_unlock(&networkMutex);
}

static int
rtems_bsdnet_sleep(rtems_event_set in, rtems_interval ticks)
{
	rtems_status_code sc;
	rtems_event_set out;
	rtems_event_set out2;

	in |= RTEMS_EVENT_SYSTEM_NETWORK_CLOSE;

	/*
	 * Soak up any pending events.  The sleep/wakeup synchronization in the
	 * FreeBSD kernel has no memory.
	 */
	rtems_event_system_receive(in, RTEMS_EVENT_ANY | RTEMS_NO_WAIT,
	    RTEMS_NO_TIMEOUT, &out);

	/*
	 * Wait for the wakeup event.
	 */
	sc = rtems_bsdnet_event_receive(in, RTEMS_EVENT_ANY | RTEMS_WAIT,
	    ticks, &out);

	/*
	 * Get additional events that may have been received between the
	 * rtems_event_system_receive() and the rtems_bsdnet_semaphore_obtain().
	 */
	rtems_event_system_receive(in, RTEMS_EVENT_ANY | RTEMS_NO_WAIT,
	    RTEMS_NO_TIMEOUT, &out2);
	out |= out2;

	if (out & RTEMS_EVENT_SYSTEM_NETWORK_CLOSE)
		return (ENXIO);

	if (sc == RTEMS_SUCCESSFUL)
		return (0);

	return (EWOULDBLOCK);
}

/*
 * Wait for something to happen to a socket buffer
 */
int
sbwait(struct sockbuf *sb)
{
	int error;

	/*
	 * Set this task as the target of the wakeup operation.
	 */
	sb->sb_sel.si_pid = rtems_task_self();

	/*
	 * Show that socket is waiting
	 */
	sb->sb_flags |= SB_WAIT;

	error = rtems_bsdnet_sleep(SBWAIT_EVENT, sb->sb_timeo);
	if (error != ENXIO)
		sb->sb_flags &= ~SB_WAIT;

	return (error);
}


/*
 * Wake up the task waiting on a socket buffer.
 */
void
sowakeup(
	struct socket *so,
	struct sockbuf *sb)
{
	if (sb->sb_flags & SB_WAIT) {
		rtems_event_system_send (sb->sb_sel.si_pid, SBWAIT_EVENT);
	}
	if (sb->sb_wakeup) {
		(*sb->sb_wakeup) (so, sb->sb_wakeuparg);
	}
}

/*
 * For now, a socket can be used by only one task at a time.
 */
int
sb_lock(struct sockbuf *sb)
{
	rtems_panic ("Socket buffer is already in use.");
	return 0;
}
void
wakeup (void *p)
{
	rtems_panic ("Wakeup called");
}

/*
 * Wait for a connection/disconnection event.
 */
int
soconnsleep (struct socket *so)
{
	int error;

	/*
	 * Set this task as the target of the wakeup operation.
	 */
	if (so->so_pgid)
		rtems_panic ("Another task is already sleeping on that socket");
	so->so_pgid = rtems_task_self();

	error = rtems_bsdnet_sleep(SOSLEEP_EVENT, so->so_rcv.sb_timeo);
	if (error != ENXIO)
		so->so_pgid = 0;

	return (error);
}

/*
 * Wake up a task waiting for a connection/disconnection to complete.
 */
void
soconnwakeup (struct socket *so)
{
	if (so->so_pgid)
		rtems_event_system_send (so->so_pgid, SOSLEEP_EVENT);
}

/*
 * Send an event to the network daemon.
 * This corresponds to sending a software interrupt in the BSD kernel.
 */
void
rtems_bsdnet_schednetisr (int n)
{
	rtems_event_system_send (networkDaemonTid, 1 << n);
}

/*
 * The network daemon
 * This provides a context to run BSD software interrupts
 */
static void
networkDaemon (void *task_argument)
{
	rtems_status_code sc;
	rtems_event_set events;
	rtems_interval now;
	int ticksPassed;
	uint32_t   timeout;
	struct callout *c;

	for (;;) {
		c = calltodo.c_next;
		if (c)
			timeout = c->c_time;
		else
			timeout = RTEMS_NO_TIMEOUT;

		sc = rtems_bsdnet_event_receive (NETISR_EVENTS,
						RTEMS_EVENT_ANY | RTEMS_WAIT,
						timeout,
						&events);
		if ( sc == RTEMS_SUCCESSFUL ) {
			if (events & NETISR_IP_EVENT)
				ipintr ();
			if (events & NETISR_ARP_EVENT)
				arpintr ();
		}

  		now = rtems_clock_get_ticks_since_boot();
		ticksPassed = now - ticksWhenCalloutsLastChecked;
		if (ticksPassed != 0) {
			ticksWhenCalloutsLastChecked = now;

			c = calltodo.c_next;
			if (c) {
				c->c_time -= ticksPassed;
				while ((c = calltodo.c_next) != NULL && c->c_time <= 0) {
					void *arg;
					void (*func) (void *);

					func = c->c_func;
					arg = c->c_arg;
					calltodo.c_next = c->c_next;
					c->c_next = callfree;
					callfree = c;
					(*func)(arg);
				}
			}
		}
	}
}

/*
 * Structure passed to task-start stub
 */
struct newtask {
	void (*entry)(void *);
	void *arg;
};

/*
 * Task-start stub
 */
static void
taskEntry (rtems_task_argument arg)
{
	struct newtask t;

	/*
	 * Pick up task information and free
	 * the memory allocated to pass the
	 * information to this task.
	 */
	t = *(struct newtask *)arg;
	free ((struct newtask *)arg);

	/*
	 * Enter the competition for the network semaphore
	 */
	rtems_bsdnet_semaphore_obtain ();

	/*
	 * Enter the task
	 */
	(*t.entry)(t.arg);
	rtems_panic ("Network task returned!\n");
}


/*
 * Start a network task
 */
#ifdef RTEMS_SMP
rtems_id
rtems_bsdnet_newproc (char *name, int stacksize, void(*entry)(void *), void *arg)
{
	return rtems_bsdnet_newproc_affinity( name, stacksize, entry, arg,
		networkDaemonCpuset, networkDaemonCpusetSize );
}

rtems_id
rtems_bsdnet_newproc_affinity (char *name, int stacksize, void(*entry)(void *),
    void *arg, const cpu_set_t *set, const size_t setsize)
#else
rtems_id
rtems_bsdnet_newproc (char *name, int stacksize, void(*entry)(void *), void *arg)
#endif
{
	struct newtask *t;
	char nm[4];
	rtems_id tid;
	rtems_status_code sc;

	strncpy (nm, name, 4);
	sc = rtems_task_create (rtems_build_name(nm[0], nm[1], nm[2], nm[3]),
		networkDaemonPriority,
		stacksize,
		RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|RTEMS_INTERRUPT_LEVEL(0),
#ifdef RTEMS_MULTIPROCESSING
		RTEMS_SYSTEM_TASK |
#endif
		RTEMS_NO_FLOATING_POINT|RTEMS_LOCAL,
		&tid);
	if (sc != RTEMS_SUCCESSFUL)
		rtems_panic ("Can't create network daemon `%s': `%s'\n", name, rtems_status_text (sc));

#ifdef RTEMS_SMP
	/*
	 * Use the default affinity or use the user-provided CPU set
	 */
	if ( set != 0 )
		rtems_task_set_affinity( tid, setsize, set );
#endif

	/*
	 * Set up task arguments
	 */
	t = malloc (sizeof *t);
	t->entry = entry;
	t->arg = arg;

	/*
	 * Start the task
	 */
	sc = rtems_task_start (tid, taskEntry, (rtems_task_argument)t);
	if (sc != RTEMS_SUCCESSFUL)
		rtems_panic ("Can't start network daemon `%s': `%s'\n", name, rtems_status_text (sc));

	/*
	 * Let our caller know the i.d. of the new task
	 */
	return tid;
}

rtems_status_code rtems_bsdnet_event_receive (
  rtems_event_set  event_in,
  rtems_option     option_set,
  rtems_interval   ticks,
  rtems_event_set *event_out)
{
	rtems_status_code sc;

	rtems_bsdnet_semaphore_release ();
	sc = rtems_event_system_receive (event_in, option_set, ticks, event_out);
	rtems_bsdnet_semaphore_obtain ();
	return sc;
}

/*
 * Fake random number generator
 */
unsigned long
rtems_bsdnet_random (void)
{
	rtems_interval now;

	now = rtems_clock_get_ticks_since_boot();
	return (now * 99991);
}

/*
 * Callout list processing
 */
void
rtems_bsdnet_timeout(void (*ftn)(void *), void *arg, int ticks)
{
	register struct callout *new, *p, *t;

	if (ticks <= 0)
		ticks = 1;

	/* Fill in the next free callout structure. */
	if (callfree == NULL) {
		callfree = malloc (sizeof *callfree);
		if (callfree == NULL)
			rtems_panic ("No memory for timeout table entry");
		callfree->c_next = NULL;
	}

	new = callfree;
	callfree = new->c_next;
	new->c_arg = arg;
	new->c_func = ftn;

	/*
	 * The time for each event is stored as a difference from the time
	 * of the previous event on the queue.  Walk the queue, correcting
	 * the ticks argument for queue entries passed.  Correct the ticks
	 * value for the queue entry immediately after the insertion point
	 * as well.  Watch out for negative c_time values; these represent
	 * overdue events.
	 */
	for (p = &calltodo;
	    (t = p->c_next) != NULL && ticks > t->c_time; p = t)
		if (t->c_time > 0)
			ticks -= t->c_time;
	new->c_time = ticks;
	if (t != NULL)
		t->c_time -= ticks;

	/* Insert the new entry into the queue. */
	p->c_next = new;
	new->c_next = t;
}

/*
 * Ticks till specified time
 * XXX: This version worries only about seconds, but that's good
 * enough for the way the network code uses this routine.
 */
int
hzto(struct timeval *tv)
{
	long diff = tv->tv_sec - rtems_bsdnet_seconds_since_boot();

	if (diff <= 0)
		return 1;
	return diff * rtems_bsdnet_ticks_per_second;
}

/*
 * Kernel debugging
 */
int rtems_bsdnet_log_priority;
void
rtems_bsdnet_log (int priority, const char *fmt, ...)
{
	va_list args;

	if (priority & rtems_bsdnet_log_priority) {
		va_start (args, fmt);
		vprintf (fmt, args);
		va_end (args);
	}
}

/*
 * IP header checksum routine for processors which don't have an inline version
 */

struct ip;

u_int in_cksum_hdr(const struct ip *);

u_int
in_cksum_hdr (const struct ip *ip)
{
	uint32_t   sum;
	const uint16_t   *sp;
	int i;

	sum = 0;
	sp = (uint16_t   *)ip;
	for (i = 0 ; i < 10 ; i++)
		sum += *sp++;
	while (sum > 0xFFFF)
		sum = (sum & 0xffff) + (sum >> 16);
	return ~sum & 0xFFFF;
}

/*
 * Manipulate routing tables
 */
int rtems_bsdnet_rtrequest (
    int req,
    struct sockaddr *dst,
    struct sockaddr *gateway,
    struct sockaddr *netmask,
    int flags,
    struct rtentry **net_nrt)
{
	int error;

	rtems_bsdnet_semaphore_obtain ();
	error = rtrequest (req, dst, gateway, netmask, flags, net_nrt);
	rtems_bsdnet_semaphore_release ();
	if (error) {
		errno = error;
		return -1;
	}
	return 0;
}

static bool
rtems_bsdnet_setup_interface(
	const char *name,
	const char *ip_address,
	const char *ip_netmask
)
{
	struct sockaddr_in address;
	struct sockaddr_in netmask;
	short flags;

	/*
	 * Bring interface up
	 */
	flags = IFF_UP;
	if (rtems_bsdnet_ifconfig (name, SIOCSIFFLAGS, &flags) < 0) {
		printf ("Can't bring %s up: %s\n", name, strerror (errno));
		return false;
	}

	/*
	 * Set interface netmask
	 */
	rtems_bsdnet_initialize_sockaddr_in(&netmask);
	netmask.sin_addr.s_addr = inet_addr (ip_netmask);
	if (rtems_bsdnet_ifconfig (name, SIOCSIFNETMASK, &netmask) < 0) {
		printf ("Can't set %s netmask: %s\n", name, strerror (errno));
		return false;
	}

	/*
	 * Set interface address
	 */
	rtems_bsdnet_initialize_sockaddr_in(&address);
	address.sin_addr.s_addr = inet_addr (ip_address);
	if (rtems_bsdnet_ifconfig (name, SIOCSIFADDR, &address) < 0) {
		printf ("Can't set %s address: %s\n", name, strerror (errno));
		return false;
	}

	/*
	 * Set interface broadcast address if the interface has the
	 * broadcast flag set.
	 */
	if (rtems_bsdnet_ifconfig (name, SIOCGIFFLAGS, &flags) < 0) {
		printf ("Can't read %s flags: %s\n", name, strerror (errno));
		return false;
	}

	if (flags & IFF_BROADCAST) {
		struct sockaddr_in broadcast;

		rtems_bsdnet_initialize_sockaddr_in(&broadcast);
		broadcast.sin_addr.s_addr =
				address.sin_addr.s_addr | ~netmask.sin_addr.s_addr;
		if (rtems_bsdnet_ifconfig (name, SIOCSIFBRDADDR, &broadcast) < 0) {
			struct in_addr	in_addr;
			char			buf[20];
			in_addr.s_addr = broadcast.sin_addr.s_addr;
			if (!inet_ntop(AF_INET, &in_addr, buf, sizeof(buf)))
					strcpy(buf,"?.?.?.?");
			printf ("Can't set %s broadcast address %s: %s\n",
				name, buf, strerror (errno));
		}
	}

	return true;
}

static int
rtems_bsdnet_setup (void)
{
	struct rtems_bsdnet_ifconfig *ifp;
	int i;
	bool any_if_configured = false;

	/*
	 * Set local parameters
	 */
	if (rtems_bsdnet_config.hostname)
		sethostname (rtems_bsdnet_config.hostname,
					strlen (rtems_bsdnet_config.hostname));
	if (rtems_bsdnet_config.domainname)
		rtems_bsdnet_domain_name =
					strdup (rtems_bsdnet_config.domainname);
	if (rtems_bsdnet_config.log_host)
		rtems_bsdnet_log_host_address.s_addr =
				inet_addr (rtems_bsdnet_config.log_host);
	for (i = 0 ; i < sizeof rtems_bsdnet_config.name_server /
			sizeof rtems_bsdnet_config.name_server[0] ; i++) {
		if (!rtems_bsdnet_config.name_server[i])
			break;
		rtems_bsdnet_nameserver[rtems_bsdnet_nameserver_count++].s_addr
			= inet_addr (rtems_bsdnet_config.name_server[i]);
	}
	for (i = 0 ; i < sizeof rtems_bsdnet_config.ntp_server /
			sizeof rtems_bsdnet_config.ntp_server[0] ; i++) {
		if (!rtems_bsdnet_config.ntp_server[i])
			break;
		rtems_bsdnet_ntpserver[rtems_bsdnet_ntpserver_count++].s_addr
			= inet_addr (rtems_bsdnet_config.ntp_server[i]);
	}

	/*
	 * Configure interfaces
	 */
	any_if_configured |= rtems_bsdnet_setup_interface(
		"lo0",
		"127.0.0.1",
		"255.0.0.0"
	);
	for (ifp = rtems_bsdnet_config.ifconfig ; ifp ; ifp = ifp->next) {
		if (ifp->ip_address == NULL)
			continue;

		any_if_configured |= rtems_bsdnet_setup_interface(
			ifp->name,
			ifp->ip_address,
			ifp->ip_netmask
		);
	}

	/*
	 * Set default route
	 */
	if (rtems_bsdnet_config.gateway && any_if_configured) {
		struct sockaddr_in address;
		struct sockaddr_in netmask;
		struct sockaddr_in gateway;

		rtems_bsdnet_initialize_sockaddr_in(&address);
		rtems_bsdnet_initialize_sockaddr_in(&netmask);
		rtems_bsdnet_initialize_sockaddr_in(&gateway);

		gateway.sin_addr.s_addr = inet_addr (rtems_bsdnet_config.gateway);

		if (rtems_bsdnet_rtrequest (
				RTM_ADD,
				(struct sockaddr *)&address,
				(struct sockaddr *)&gateway,
				(struct sockaddr *)&netmask,
				(RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL) < 0) {
			printf ("Can't set default route: %s\n", strerror (errno));
			return -1;
		}
	}
	return 0;
}

/*
 * Initialize the network
 */
int
rtems_bsdnet_initialize_network(void)
{
	struct rtems_bsdnet_ifconfig *ifp;

	/*
	 * Start network tasks.
	 * Initialize BSD network data structures.
	 */
	if (rtems_bsdnet_initialize () < 0)
		return -1;

	/*
	 * Attach interfaces
	 */
	for (ifp = rtems_bsdnet_config.ifconfig ; ifp ; ifp = ifp->next) {
		rtems_bsdnet_attach (ifp);
	}

	/*
	 * Bring up the network
	 */
	if (rtems_bsdnet_setup () < 0)
		return -1;
	if (rtems_bsdnet_config.bootp)
		(*rtems_bsdnet_config.bootp)();
	return 0;
}

/*
 * Attach a network interface.
 */
void rtems_bsdnet_attach(struct rtems_bsdnet_ifconfig *ifp)
{
	if (ifp) {
		rtems_bsdnet_semaphore_obtain ();
		(ifp->attach)(ifp, 1);
		rtems_bsdnet_semaphore_release ();
	}
}

/*
 * Detach a network interface.
 */
void rtems_bsdnet_detach (struct rtems_bsdnet_ifconfig *ifp)
{
	if (ifp) {
		rtems_bsdnet_semaphore_obtain ();
		(ifp->attach)(ifp, 0);
		rtems_bsdnet_semaphore_release ();
	}
}

/*
 * Interface Configuration.
 */
int rtems_bsdnet_ifconfig(const char *ifname, uint32_t cmd, void *param)
{
	int s, r = 0;
	struct ifreq ifreq;

	/*
	 * Configure interfaces
	 */
	s = socket (AF_INET, SOCK_DGRAM, 0);
	if (s < 0)
		return -1;

	strncpy (ifreq.ifr_name, ifname, IFNAMSIZ);

	rtems_bsdnet_semaphore_obtain ();

	switch (cmd) {
		case SIOCSIFADDR:
		case SIOCSIFNETMASK:
			memcpy (&ifreq.ifr_addr, param, sizeof (struct sockaddr));
			r = ioctl (s, cmd, &ifreq);
			break;

		case OSIOCGIFADDR:
		case SIOCGIFADDR:
		case OSIOCGIFNETMASK:
		case SIOCGIFNETMASK:
			if ((r = ioctl (s, cmd, &ifreq)) < 0)
				break;
			memcpy (param, &ifreq.ifr_addr, sizeof (struct sockaddr));
			break;

		case SIOCGIFFLAGS:
		case SIOCSIFFLAGS:
			if ((r = ioctl (s, SIOCGIFFLAGS, &ifreq)) < 0)
				break;
			if (cmd == SIOCGIFFLAGS) {
				*((short*) param) = ifreq.ifr_flags;
				break;
			}
			ifreq.ifr_flags |= *((short*) param);
			if ( (*((short*) param) & IFF_UP ) == 0 ) {
			    /* set the interface down */
			    ifreq.ifr_flags &= ~(IFF_UP);
			}
			r = ioctl (s, SIOCSIFFLAGS, &ifreq);
			break;

		case SIOCSIFDSTADDR:
			memcpy (&ifreq.ifr_dstaddr, param, sizeof (struct sockaddr));
			r = ioctl (s, cmd, &ifreq);
			break;

		case OSIOCGIFDSTADDR:
		case SIOCGIFDSTADDR:
			if ((r = ioctl (s, cmd, &ifreq)) < 0)
				break;
			memcpy (param, &ifreq.ifr_dstaddr, sizeof (struct sockaddr));
			break;

		case SIOCSIFBRDADDR:
			memcpy (&ifreq.ifr_broadaddr, param, sizeof (struct sockaddr));
			r = ioctl (s, cmd, &ifreq);
			break;

		case OSIOCGIFBRDADDR:
		case SIOCGIFBRDADDR:
			if ((r = ioctl (s, cmd, &ifreq)) < 0)
				break;
			memcpy (param, &ifreq.ifr_broadaddr, sizeof (struct sockaddr));
			break;

		case SIOCSIFMETRIC:
			ifreq.ifr_metric = *((int*) param);
			r = ioctl (s, cmd, &ifreq);
			break;

		case SIOCGIFMETRIC:
			if ((r = ioctl (s, cmd, &ifreq)) < 0)
				break;
			*((int*) param) = ifreq.ifr_metric;
			break;

		case SIOCSIFMTU:
			ifreq.ifr_mtu = *((int*) param);
			r = ioctl (s, cmd, &ifreq);
			break;

		case SIOCGIFMTU:
			if ((r = ioctl (s, cmd, &ifreq)) < 0)
				break;
			*((int*) param) = ifreq.ifr_mtu;
			break;

		case SIOCSIFPHYS:
			ifreq.ifr_phys = *((int*) param);
			r = ioctl (s, cmd, &ifreq);
			break;

		case SIOCGIFPHYS:
			if ((r = ioctl (s, cmd, &ifreq)) < 0)
				break;
			*((int*) param) = ifreq.ifr_phys;
			break;

		case SIOCSIFMEDIA:
			ifreq.ifr_media = *((int*) param);
			r = ioctl (s, cmd, &ifreq);
			break;

		case SIOCGIFMEDIA:
			/* 'param' passes the phy index they want to
			 * look at...
			 */
			ifreq.ifr_media = *((int*) param);
			if ((r = ioctl (s, cmd, &ifreq)) < 0)
				break;
			*((int*) param) = ifreq.ifr_media;
			break;

		case SIOCAIFADDR:
		case SIOCDIFADDR:
			r = ioctl(s, cmd, (struct ifreq *) param);
			break;

		default:
			errno = EOPNOTSUPP;
			r = -1;
			break;
	}

	rtems_bsdnet_semaphore_release ();

	close (s);
	return r;
}

/**
 * @brief Splits a network interface name with interface configuration @a
 * config into the unit name and number parts.
 *
 * Memory for the unit name will be allocated from the heap and copied to @a
 * namep.  If @a namep is NULL nothing will be allocated and copied.
 *
 * Returns the unit number or -1 on error.
 */
int
rtems_bsdnet_parse_driver_name (const struct rtems_bsdnet_ifconfig *config, char **namep)
{
	const char *cp = config->name;
	char c;
	int unitNumber = 0;

	if (cp == NULL) {
		printf ("No network driver name.\n");
		return -1;
	}
	while ((c = *cp++) != '\0') {
		if ((c >= '0') && (c <= '9')) {
			int len = cp - config->name;
			if ((len < 2) || (len > 50))
				break;
			for (;;) {
				unitNumber = (unitNumber * 10) + (c - '0');
				c = *cp++;
				if (c == '\0') {
					if (namep != NULL) {
						char *unitName = malloc (len);
						if (unitName == NULL) {
							printf ("No memory.\n");
							return -1;
						}
						strncpy (unitName, config->name, len - 1);
						unitName[len-1] = '\0';
						*namep = unitName;
					}
					return unitNumber;
				}
				if ((c < '0') || (c > '9'))
					break;
			}
			break;
		}
	}
	printf ("Bad network driver name `%s'.\n", config->name);
	return -1;
}

/*
 * Handle requests for more network memory
 * XXX: Another possibility would be to use a semaphore here with
 *      a release in the mbuf free macro.  I have chosen this `polling'
 *      approach because:
 *      1) It is simpler.
 *      2) It adds no complexity to the free macro.
 *      3) Running out of mbufs should be a rare
 *         condition -- predeployment testing of
 *         an application should indicate the
 *         required mbuf pool size.
 * XXX: Should there be a panic if a task is stuck in the loop for
 *      more than a minute or so?
 */
int
m_mballoc(int nmb, int nowait)
{
	if (nowait)
		return 0;
	m_reclaim ();
	if (mmbfree == NULL) {
		int try = 0;
		int print_limit = 30 * rtems_bsdnet_ticks_per_second;

		mbstat.m_wait++;
		for (;;) {
			uint32_t nest_count = rtems_bsdnet_semaphore_release_recursive ();
			rtems_task_wake_after (1);
			rtems_bsdnet_semaphore_obtain_recursive (nest_count);
			if (mmbfree)
				break;
			if (++try >= print_limit) {
				printf ("Still waiting for mbuf.\n");
				try = 0;
			}
		}
	}
	else {
		mbstat.m_drops++;
	}
	return 1;
}

int
m_clalloc(int ncl, int nowait)
{
	if (nowait)
		return 0;
	m_reclaim ();
	if (mclfree == NULL) {
		int try = 0;
		int print_limit = 30 * rtems_bsdnet_ticks_per_second;

		mbstat.m_wait++;
		for (;;) {
			uint32_t nest_count = rtems_bsdnet_semaphore_release_recursive ();
			rtems_task_wake_after (1);
			rtems_bsdnet_semaphore_obtain_recursive (nest_count);
			if (mclfree)
				break;
			if (++try >= print_limit) {
				printf ("Still waiting for mbuf cluster.\n");
				try = 0;
			}
		}
	}
	else {
		mbstat.m_drops++;
	}
	return 1;
}

