From 39e6e65a2c5a3312f365d59f23c469641e049c82 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Wed, 19 Aug 1998 21:32:28 +0000 Subject: Base files --- c/src/libnetworking/rtems/rtems_glue.c | 898 +++++++++++++++++++++++++++++++++ 1 file changed, 898 insertions(+) create mode 100644 c/src/libnetworking/rtems/rtems_glue.c (limited to 'c/src/libnetworking/rtems/rtems_glue.c') diff --git a/c/src/libnetworking/rtems/rtems_glue.c b/c/src/libnetworking/rtems/rtems_glue.c new file mode 100644 index 0000000000..339d99052b --- /dev/null +++ b/c/src/libnetworking/rtems/rtems_glue.c @@ -0,0 +1,898 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Events used by networking routines. + * Everything will break if the application + * tries to use these events or if the `sleep' + * events are equal to any of the NETISR * events. + */ +#define SBWAIT_EVENT RTEMS_EVENT_24 +#define SOSLEEP_EVENT RTEMS_EVENT_25 +#define NETISR_IP_EVENT (1 << NETISR_IP) +#define NETISR_ARP_EVENT (1 << NETISR_ARP) +#define NETISR_EVENTS (NETISR_IP_EVENT|NETISR_ARP_EVENT) + + +/* + * Memory allocation + */ +static int nmbuf = (64 * 1024) / MSIZE; + int nmbclusters = (128 * 1024) / MCLBYTES; + +/* + * Socket buffering parameters + */ +unsigned long sb_efficiency = 8; + +/* + * Network task synchronization + */ +static rtems_id networkSemaphore; +static rtems_id networkDaemonTid; +static rtems_unsigned32 networkDaemonPriority; +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; +static struct callout *callfree, calltodo; + +/* + * FreeBSD variables + */ +int nfs_diskless_valid; + +/* + * BOOTP values + */ +struct in_addr rtems_bsdnet_log_host_address; +struct in_addr rtems_bsdnet_bootp_server_address; +char *rtems_bsdnet_bootp_boot_file_name; +char *rtems_bsdnet_domain_name; +struct in_addr rtems_bsdnet_nameserver[sizeof rtems_bsdnet_config.name_server / + sizeof rtems_bsdnet_config.name_server[0]]; +int rtems_bsdnet_nameserver_count; + +extern rtems_libio_handler_t rtems_bsdnet_io_handler; + +/* + * 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 (unsigned long size, int type, int flags) +{ + void *p; + + for (;;) { + p = malloc (size); + if (p) + return p; + if (flags & M_NOWAIT) + return p; + /* + * FIXME: This should be redone as: + * static volatile int rtems_bsdnet_need_memory; + * + * rtems_bsdnet_need_memory = 1; + * message_queue_receive + * + * Then in rtems_bsdnet_freee: + * free (....); + * if (rtems_bsdnet_need_memory) + * rtems_bsdnet_need_memory = 0; + * message_queue_broadcast + */ + rtems_task_wake_after (rtems_bsdnet_ticks_per_second); + } +} + +/* + * Free FreeBSD memory + * FIXME: This should be modified to keep memory allocation statistics. + */ +void +rtems_bsdnet_free (void *addr, int type) +{ + free (addr); +} + +/* + * Do the initializations required by the BSD code + * FIXME: Maybe we should use a different memory allocation scheme that + * would let us share space between mbufs and mbuf clusters. + * For now, we'll just take the easy way out! + */ +static void +bsd_init () +{ + /* + * Set up mbuf data strutures + * Cluster allocation *must* come first -- see comment on kmem_malloc(). + */ + m_clalloc (nmbclusters, M_DONTWAIT); + mclrefcnt = malloc (nmbclusters); + if (mclrefcnt == NULL) + rtems_panic ("No memory for mbuf cluster reference counts."); + memset (mclrefcnt, '\0', nmbclusters); + m_mballoc (nmbuf, M_DONTWAIT); + mbstat.m_mtypes[MT_FREE] = nmbuf; + + + /* + * Set up domains + */ + { + extern struct domain routedomain; + extern struct domain inetdomain; + + routedomain.dom_next = domains; + domains = &routedomain; + inetdomain.dom_next = domains; + domains = &inetdomain; + domaininit (NULL); + } + + /* + * Set up interfaces + */ + ifinit (NULL); +} + +/* + * Initialize and start network operations + */ +static void +rtems_bsdnet_initialize (void) +{ + rtems_status_code sc; + + /* + * Set the priority of all network tasks + */ + if (rtems_bsdnet_config.network_task_priority == 0) + networkDaemonPriority = 100; + else + networkDaemonPriority = rtems_bsdnet_config.network_task_priority; + + /* + * Set the memory allocation limits + */ + if (rtems_bsdnet_config.mbuf_bytecount) + nmbuf = rtems_bsdnet_config.mbuf_bytecount / MSIZE; + if (rtems_bsdnet_config.mbuf_cluster_bytecount) + nmbclusters = rtems_bsdnet_config.mbuf_cluster_bytecount / MCLBYTES; + + /* + * Create the task-synchronization semaphore + */ + sc = rtems_semaphore_create (rtems_build_name('B', 'S', 'D', 'n'), + 0, + RTEMS_FIFO | + RTEMS_BINARY_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &networkSemaphore); + if (sc != RTEMS_SUCCESSFUL) + rtems_panic ("Can't create network seamphore: `%s'\n", rtems_status_text (sc)); + + /* + * Compute clock tick conversion factors + */ + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &rtems_bsdnet_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; + + /* + * Ensure that `seconds' is greater than 0 + */ + rtems_task_wake_after (rtems_bsdnet_ticks_per_second); + + /* + * Set up BSD-style sockets + */ + bsd_init (); + + /* + * Start network daemon + */ + networkDaemonTid = rtems_bsdnet_newproc ("ntwk", 4096, networkDaemon, NULL); + + /* + * Register as an external I/O handler + */ + rtems_register_libio_handler (RTEMS_FILE_DESCRIPTOR_TYPE_SOCKET, + &rtems_bsdnet_io_handler); + + /* + * Let other network tasks begin + */ + rtems_bsdnet_semaphore_release (); +} + +rtems_id TaskWithSemaphore; +/* + * Obtain network mutex + */ +void +rtems_bsdnet_semaphore_obtain (void) +{ + rtems_status_code sc; + + sc = rtems_semaphore_obtain (networkSemaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT); +rtems_task_ident (RTEMS_SELF, 0, &TaskWithSemaphore); + if (sc != RTEMS_SUCCESSFUL) + rtems_panic ("Can't obtain network semaphore: `%s'\n", rtems_status_text (sc)); +} + +/* + * Release network mutex + */ +void +rtems_bsdnet_semaphore_release (void) +{ + rtems_status_code sc; + +TaskWithSemaphore = 0; + sc = rtems_semaphore_release (networkSemaphore); + if (sc != RTEMS_SUCCESSFUL) + rtems_panic ("Can't release network semaphore: `%s'\n", rtems_status_text (sc)); + } + +/* + * Wait for something to happen to a socket buffer + */ +int +sbwait(sb) + struct sockbuf *sb; +{ + rtems_event_set events; + rtems_id tid; + rtems_status_code sc; + + /* + * Soak up any pending events. + * The sleep/wakeup synchronization in the FreeBSD + * kernel has no memory. + */ + rtems_event_receive (SBWAIT_EVENT, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT, &events); + + /* + * Set this task as the target of the wakeup operation. + */ + if (sb->sb_sel.si_pid) + rtems_panic ("Another task is already sleeping on that socket buffer"); + rtems_task_ident (RTEMS_SELF, 0, &tid); + sb->sb_sel.si_pid = tid; + + /* + * Mark the socket buffer as waiting. + */ + sb->sb_flags |= SB_WAIT; + + /* + * Release the network semaphore. + */ + rtems_bsdnet_semaphore_release (); + + /* + * Wait for the wakeup event. + */ + sc = rtems_event_receive (SBWAIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, sb->sb_timeo, &events); + + /* + * Reobtain the network semaphore. + */ + rtems_bsdnet_semaphore_obtain (); + + /* + * Relinquish ownership of the socket buffer + */ + sb->sb_flags &= ~SB_WAIT; + sb->sb_sel.si_pid = 0; + + /* + * Return the status of the wait. + */ + switch (sc) { + case RTEMS_SUCCESSFUL: return 0; + case RTEMS_TIMEOUT: return EWOULDBLOCK; + default: return ENXIO; + } +} + + +/* + * Wake up the task waiting on a socket buffer. + */ +void +sowakeup(so, sb) + register struct socket *so; + register struct sockbuf *sb; +{ + if (sb->sb_flags & SB_WAIT) { + sb->sb_flags &= ~SB_WAIT; + rtems_event_send (sb->sb_sel.si_pid, SBWAIT_EVENT); + } +} + +/* + * For now, a socket can be used by only one task at a time. + */ +int +sb_lock(sb) + register 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. + */ +void +soconnsleep (struct socket *so) +{ + rtems_event_set events; + rtems_id tid; + + /* + * Soak up any pending events. + * The sleep/wakeup synchronization in the FreeBSD + * kernel has no memory. + */ + rtems_event_receive (SOSLEEP_EVENT, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT, &events); + + /* + * Set this task as the target of the wakeup operation. + */ + if (so->so_pgid) + rtems_panic ("Another task is already sleeping on that socket"); + rtems_task_ident (RTEMS_SELF, 0, &tid); + so->so_pgid = tid; + + /* + * Wait for the wakeup event. + */ + rtems_bsdnet_event_receive (SOSLEEP_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); + + /* + * Relinquish ownership of the socket. + */ + so->so_pgid = 0; +} + +/* + * Wake up a task waiting for a connection/disconnection to complete. + */ +void +soconnwakeup (struct socket *so) +{ + if (so->so_pgid) + rtems_event_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_send (networkDaemonTid, 1 << n); +} + +/* + * The network daemon + * This provides a context to run BSD software interrupts + */ +static void +networkDaemon (void *task_argument) +{ + rtems_event_set events; + rtems_interval now; + int ticksPassed; + unsigned32 timeout; + struct callout *c; + + for (;;) { + c = calltodo.c_next; + if (c) + timeout = c->c_time; + else + timeout = RTEMS_NO_TIMEOUT; + rtems_bsdnet_event_receive (NETISR_EVENTS, + RTEMS_EVENT_ANY | RTEMS_WAIT, + timeout, + &events); + if (events & NETISR_IP_EVENT) + ipintr (); + if (events & NETISR_ARP_EVENT) + arpintr (); + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); + 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 + */ +rtems_id +rtems_bsdnet_newproc (char *name, int stacksize, void(*entry)(void *), void *arg) +{ + 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), + 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)); + + /* + * 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_receive (event_in, option_set, ticks, event_out); + rtems_bsdnet_semaphore_obtain (); + return sc; +} + +/* + * Return time since startup + */ +void +microtime (struct timeval *t) +{ + rtems_interval now; + + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); + t->tv_sec = now / rtems_bsdnet_ticks_per_second; + t->tv_usec = (now % rtems_bsdnet_ticks_per_second) * rtems_bsdnet_microseconds_per_tick; +} + +unsigned long +rtems_bsdnet_seconds_since_boot (void) +{ + rtems_interval now; + + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); + return now / rtems_bsdnet_ticks_per_second; +} + +/* + * Fake random number generator + */ +unsigned long +rtems_bsdnet_random (void) +{ + rtems_interval now; + + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); + return (now * 99991); +} + +/* + * Callout list processing + */ +void +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 + * FIXME: 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); + } +} + +/* + * Hack alert: kmem_malloc `knows' that its + * first invocation is to get mbuf clusters! + */ +int mb_map_full; +vm_map_t mb_map; +vm_offset_t +kmem_malloc (vm_map_t *map, vm_size_t size, boolean_t waitflag) +{ + void *p; + + /* + * Can't get memory if we're already running. + */ + if (networkDaemonTid) { + if (waitflag == M_WAITOK) + rtems_panic ("Someone is asking for more memory"); + return 0; + } + +#define ROUNDSIZE 2048 + p = malloc (size+ROUNDSIZE); + p = (void *)((unsigned long)p & ~(ROUNDSIZE-1)); + if ((p == NULL) && (waitflag == M_WAITOK)) + rtems_panic ("Can't get initial network memory!"); + if (mbutl == NULL) + mbutl = p; + return (vm_offset_t)p; +} + +/* + * IP header checksum routine for processors which don't have an inline version + */ +u_int +in_cksum_hdr (const void *ip) +{ + rtems_unsigned32 sum; + const rtems_unsigned16 *sp; + int i; + + sum = 0; + sp = (rtems_unsigned16 *)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 void +rtems_bsdnet_setup (void) +{ + struct rtems_bsdnet_ifconfig *ifp; + int s; + struct ifreq ifreq; + struct sockaddr_in address; + struct sockaddr_in netmask; + struct sockaddr_in broadcast; + struct sockaddr_in gateway; + int i; + + /* + * 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]); + } + + /* + * Configure interfaces + */ + s = socket (AF_INET, SOCK_DGRAM, 0); + if (s < 0) + rtems_panic ("Can't create initial socket: %s", strerror (errno)); + for (ifp = rtems_bsdnet_config.ifconfig ; ifp ; ifp = ifp->next) { + if (ifp->ip_address == NULL) + continue; + + /* + * Get the interface flags + */ + strcpy (ifreq.ifr_name, ifp->name); + if (ioctl (s, SIOCGIFFLAGS, &ifreq) < 0) + rtems_panic ("Can't get %s flags: %s", ifp->name, strerror (errno)); + + /* + * Bring interface up + */ + ifreq.ifr_flags |= IFF_UP; + if (ioctl (s, SIOCSIFFLAGS, &ifreq) < 0) + rtems_panic ("Can't bring %s up: %s", ifp->name, strerror (errno)); + + /* + * Set interface netmask + */ + memset (&netmask, '\0', sizeof netmask); + netmask.sin_len = sizeof netmask; + netmask.sin_family = AF_INET; + netmask.sin_addr.s_addr = inet_addr (ifp->ip_netmask); + memcpy (&ifreq.ifr_addr, &netmask, sizeof netmask); + if (ioctl (s, SIOCSIFNETMASK, &ifreq) < 0) + rtems_panic ("Can't set %s netmask: %s", ifp->name, strerror (errno)); + + /* + * Set interface address + */ + memset (&address, '\0', sizeof address); + address.sin_len = sizeof address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr (ifp->ip_address); + memcpy (&ifreq.ifr_addr, &address, sizeof address); + if (ioctl (s, SIOCSIFADDR, &ifreq) < 0) + rtems_panic ("Can't set %s address: %s", ifp->name, strerror (errno)); + + /* + * Set interface broadcast address + */ + memset (&broadcast, '\0', sizeof broadcast); + broadcast.sin_len = sizeof broadcast; + broadcast.sin_family = AF_INET; + broadcast.sin_addr.s_addr = address.sin_addr.s_addr | ~netmask.sin_addr.s_addr; + memcpy (&ifreq.ifr_broadaddr, &broadcast, sizeof broadcast); + if (ioctl (s, SIOCSIFBRDADDR, &ifreq) < 0) + rtems_panic ("Can't set %s broadcast address: %s", ifp->name, strerror (errno)); + } + + /* + * We're done with the dummy socket + */ + close (s); + + /* + * Set default route + */ + if (rtems_bsdnet_config.gateway) { + address.sin_addr.s_addr = INADDR_ANY; + netmask.sin_addr.s_addr = INADDR_ANY; + memset (&gateway, '\0', sizeof gateway); + gateway.sin_len = sizeof gateway; + gateway.sin_family = AF_INET; + 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) + rtems_panic ("Can't set default route: %s", strerror (errno)); + } +} + +/* + * Initialize the network + */ +int +rtems_bsdnet_initialize_network (void) +{ + struct rtems_bsdnet_ifconfig *ifp; + + /* + * Start network tasks. + * Initialize BSD network data structures. + */ + rtems_bsdnet_initialize (); + + /* + * Attach interfaces + */ + for (ifp = rtems_bsdnet_config.ifconfig ; ifp ; ifp = ifp->next) { + rtems_bsdnet_semaphore_obtain (); + (ifp->attach)(ifp); + rtems_bsdnet_semaphore_release (); + } + + /* + * Bring up the network + */ + rtems_bsdnet_setup (); + if (rtems_bsdnet_config.bootp) + (*rtems_bsdnet_config.bootp)(); + return 0; +} -- cgit v1.2.3