/* * $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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_bootp_server_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; /* * 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); /* * 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. */ rtems_task_ident (RTEMS_SELF, 0, &tid); sb->sb_sel.si_pid = tid; /* * Show that socket is 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 (); /* * 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. */ int soconnsleep (struct socket *so) { 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 (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. */ sc = rtems_bsdnet_event_receive (SOSLEEP_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, so->so_rcv.sb_timeo, &events); /* * Relinquish ownership of the socket. */ so->so_pgid = 0; switch (sc) { case RTEMS_SUCCESSFUL: return 0; case RTEMS_TIMEOUT: return EWOULDBLOCK; default: return ENXIO; } } /* * 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 ( "Network mbuf space can not be enlarged after rtems_bsdnet_initialize() has\n" "returned. Enlarge the initial mbuf/cluster size in rtems_bsdnet_config."); 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; } /* * Parse a network driver name into a name and a unit number */ 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"); return -1; } while ((c = *cp++) != '\0') { if ((c >= '0') && (c <= '9')) { int len = cp - config->name - 1; if ((len < 2) || (len > 50)) break; for (;;) { unitNumber = (unitNumber * 10) + (c - '0'); c = *cp++; if (c == '\0') { char *unitName = malloc (len); if (unitName == NULL) { printf ("No memory"); 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'", config->name); return -1; }