summaryrefslogtreecommitdiffstats
path: root/target/linux/package/nvram
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/package/nvram')
-rw-r--r--target/linux/package/nvram/Makefile33
-rw-r--r--target/linux/package/nvram/ipkg/nvram.control7
-rw-r--r--target/linux/package/nvram/src/Makefile22
-rw-r--r--target/linux/package/nvram/src/bcmtimer.h42
-rw-r--r--target/linux/package/nvram/src/defaults.c179
-rw-r--r--target/linux/package/nvram/src/linux_timer.c738
-rw-r--r--target/linux/package/nvram/src/main.c78
-rw-r--r--target/linux/package/nvram/src/nvram_convert.c77
-rw-r--r--target/linux/package/nvram/src/nvram_convert.h7
-rw-r--r--target/linux/package/nvram/src/nvram_linux.c320
-rw-r--r--target/linux/package/nvram/src/shutils.c329
-rw-r--r--target/linux/package/nvram/src/wl.c86
-rw-r--r--target/linux/package/nvram/src/wl_linux.c77
13 files changed, 1995 insertions, 0 deletions
diff --git a/target/linux/package/nvram/Makefile b/target/linux/package/nvram/Makefile
new file mode 100644
index 000000000..da67ffa7f
--- /dev/null
+++ b/target/linux/package/nvram/Makefile
@@ -0,0 +1,33 @@
+# $Id$
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=nvram
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
+
+include $(TOPDIR)/package/rules.mk
+
+BR2_PACKAGE_NVRAM:=y # does not depend on menuconfig
+$(eval $(call PKG_template,NVRAM,nvram,$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
+
+$(PKG_BUILD_DIR)/.prepared:
+ mkdir -p $(PKG_BUILD_DIR)
+ cp -a ./src/* $(PKG_BUILD_DIR)
+ touch $@
+
+$(PKG_BUILD_DIR)/.built:
+ $(MAKE) -C $(PKG_BUILD_DIR) \
+ $(TARGET_CONFIGURE_OPTS) \
+ CFLAGS="$(TARGET_CFLAGS) -I $(STAGING_DIR)/usr/include"
+ touch $@
+
+$(IPKG_NVRAM):
+ mkdir -p $(IDIR_NVRAM)/usr/lib
+ cp $(PKG_BUILD_DIR)/*.so $(IDIR_NVRAM)/usr/lib
+ mkdir -p $(IDIR_NVRAM)/usr/sbin
+ cp $(PKG_BUILD_DIR)/nvram $(IDIR_NVRAM)/usr/sbin
+ $(RSTRIP) $(IDIR_NVRAM)
+ $(IPKG_BUILD) $(IDIR_NVRAM) $(PACKAGE_DIR)
+
diff --git a/target/linux/package/nvram/ipkg/nvram.control b/target/linux/package/nvram/ipkg/nvram.control
new file mode 100644
index 000000000..bb2581303
--- /dev/null
+++ b/target/linux/package/nvram/ipkg/nvram.control
@@ -0,0 +1,7 @@
+Package: nvram
+Priority: optional
+Section: sys
+Maintainer: Felix Fietkau <nbd@vd-s.ath.cx>
+Source: buildroot internal
+Description: NVRAM utility and libraries for Broadcom hardware
+
diff --git a/target/linux/package/nvram/src/Makefile b/target/linux/package/nvram/src/Makefile
new file mode 100644
index 000000000..376c2b7de
--- /dev/null
+++ b/target/linux/package/nvram/src/Makefile
@@ -0,0 +1,22 @@
+# $Id$
+
+EXTRA_CFLAGS := -c -I. -I../include
+LIBSHARED_OBJS := shutils.o wl.o wl_linux.o defaults.o linux_timer.o
+LIBNVRAM_OBJS := nvram_linux.o nvram_convert.o
+
+all: libshared.so libnvram.so nvram
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ $^
+
+libshared.so: $(LIBSHARED_OBJS)
+ $(CC) -shared -o $@ $^
+
+libnvram.so: $(LIBNVRAM_OBJS)
+ $(CC) -shared -o $@ $^
+
+nvram: main.o
+ $(CC) -o $@ $^ -L. -lnvram
+
+clean:
+ rm -f *.o *.so nvram
diff --git a/target/linux/package/nvram/src/bcmtimer.h b/target/linux/package/nvram/src/bcmtimer.h
new file mode 100644
index 000000000..3db9e624f
--- /dev/null
+++ b/target/linux/package/nvram/src/bcmtimer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * Low resolution timer interface. Timer handlers may be called
+ * in a deferred manner in a different task context after the
+ * timer expires or in the task context from which the timer
+ * was created, depending on the implementation.
+ *
+ * $Id$
+ */
+#ifndef __bcmtimer_h__
+#define __bcmtimer_h__
+
+/* ANSI headers */
+#include <time.h>
+
+/* timer ID */
+typedef unsigned int bcm_timer_module_id;
+typedef unsigned int bcm_timer_id;
+
+/* timer callback */
+typedef void (*bcm_timer_cb)(bcm_timer_id id, int data);
+
+/* OS-independant interfaces, applications should call these functions only */
+int bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id);
+int bcm_timer_module_cleanup(bcm_timer_module_id module_id);
+int bcm_timer_module_enable(bcm_timer_module_id module_id, int enable);
+int bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id);
+int bcm_timer_delete(bcm_timer_id timer_id);
+int bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *value);
+int bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *value);
+int bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data);
+int bcm_timer_cancel(bcm_timer_id timer_id);
+int bcm_timer_change_expirytime(bcm_timer_id timer_id, const struct itimerspec *timer_spec);
+
+#endif /* #ifndef __bcmtimer_h__ */
diff --git a/target/linux/package/nvram/src/defaults.c b/target/linux/package/nvram/src/defaults.c
new file mode 100644
index 000000000..119eaac72
--- /dev/null
+++ b/target/linux/package/nvram/src/defaults.c
@@ -0,0 +1,179 @@
+/*
+ * Router default NVRAM values
+ *
+ * Copyright 2004, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#include <epivers.h>
+#include <string.h>
+#include <bcmnvram.h>
+#include <typedefs.h>
+#include <wlioctl.h>
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+struct nvram_tuple router_defaults[] = {
+ /* OS parameters */
+ { "os_name", "", 0 }, /* OS name string */
+ { "os_version", EPI_VERSION_STR, 0 }, /* OS revision */
+ { "os_date", __DATE__, 0 }, /* OS date */
+
+ /* Miscellaneous parameters */
+ { "timer_interval", "3600", 0 }, /* Timer interval in seconds */
+ { "ntp_server", "192.5.41.40 192.5.41.41 133.100.9.2", 0 }, /* NTP server */
+ { "time_zone", "PST8PDT", 0 }, /* Time zone (GNU TZ format) */
+ { "log_level", "0", 0 }, /* Bitmask 0:off 1:denied 2:accepted */
+ { "upnp_enable", "0", 0 }, /* Start UPnP */
+ { "ezc_enable", "1", 0 }, /* Enable EZConfig updates */
+ { "ezc_version", "1", 0 }, /* EZConfig version */
+ { "is_default", "1", 0 }, /* is it default setting: 1:yes 0:no*/
+ { "os_server", "", 0 }, /* URL for getting upgrades */
+ { "stats_server", "", 0 }, /* URL for posting stats */
+ { "console_loglevel", "1", 0 }, /* Kernel panics only */
+
+ /* Big switches */
+ { "router_disable", "0", 0 }, /* lan_proto=static lan_stp=0 wan_proto=disabled */
+ { "fw_disable", "0", 0 }, /* Disable firewall (allow new connections from the WAN) */
+
+ { "log_ipaddr", "", 0 }, /* syslog recipient */
+
+ /* LAN H/W parameters */
+ { "lan_ifname", "", 0 }, /* LAN interface name */
+ { "lan_ifnames", "", 0 }, /* Enslaved LAN interfaces */
+ { "lan_hwnames", "", 0 }, /* LAN driver names (e.g. et0) */
+ { "lan_hwaddr", "", 0 }, /* LAN interface MAC address */
+
+ /* LAN TCP/IP parameters */
+ { "lan_proto", "dhcp", 0 }, /* [static|dhcp] */
+ { "lan_ipaddr", "192.168.1.1", 0 }, /* LAN IP address */
+ { "lan_netmask", "255.255.255.0", 0 }, /* LAN netmask */
+ { "lan_stp", "0", 0 }, /* LAN spanning tree protocol */
+ { "lan_wins", "", 0 }, /* x.x.x.x x.x.x.x ... */
+ { "lan_domain", "", 0 }, /* LAN domain name */
+ { "lan_lease", "86400", 0 }, /* LAN lease time in seconds */
+
+ /* WAN H/W parameters */
+ { "wan_ifname", "", 0 }, /* WAN interface name */
+ { "wan_ifnames", "", 0 }, /* WAN interface names */
+ { "wan_hwname", "", 0 }, /* WAN driver name (e.g. et1) */
+ { "wan_hwaddr", "", 0 }, /* WAN interface MAC address */
+
+ /* WAN TCP/IP parameters */
+ { "wan_proto", "dhcp", 0 }, /* [static|dhcp|pppoe|disabled] */
+ { "wan_ipaddr", "0.0.0.0", 0 }, /* WAN IP address */
+ { "wan_netmask", "0.0.0.0", 0 }, /* WAN netmask */
+ { "wan_gateway", "0.0.0.0", 0 }, /* WAN gateway */
+ { "wan_dns", "", 0 }, /* x.x.x.x x.x.x.x ... */
+ { "wan_wins", "", 0 }, /* x.x.x.x x.x.x.x ... */
+ { "wan_hostname", "", 0 }, /* WAN hostname */
+ { "wan_domain", "", 0 }, /* WAN domain name */
+ { "wan_lease", "86400", 0 }, /* WAN lease time in seconds */
+
+ /* PPPoE parameters */
+ { "wan_pppoe_ifname", "", 0 }, /* PPPoE enslaved interface */
+ { "wan_pppoe_username", "", 0 }, /* PPP username */
+ { "wan_pppoe_passwd", "", 0 }, /* PPP password */
+ { "wan_pppoe_idletime", "60", 0 }, /* Dial on demand max idle time (seconds) */
+ { "wan_pppoe_keepalive", "0", 0 }, /* Restore link automatically */
+ { "wan_pppoe_demand", "0", 0 }, /* Dial on demand */
+ { "wan_pppoe_mru", "1492", 0 }, /* Negotiate MRU to this value */
+ { "wan_pppoe_mtu", "1492", 0 }, /* Negotiate MTU to the smaller of this value or the peer MRU */
+ { "wan_pppoe_service", "", 0 }, /* PPPoE service name */
+ { "wan_pppoe_ac", "", 0 }, /* PPPoE access concentrator name */
+
+ /* Misc WAN parameters */
+ { "wan_desc", "", 0 }, /* WAN connection description */
+ { "wan_route", "", 0 }, /* Static routes (ipaddr:netmask:gateway:metric:ifname ...) */
+ { "wan_primary", "0", 0 }, /* Primary wan connection */
+
+ { "wan_unit", "0", 0 }, /* Last configured connection */
+
+ /* Filters */
+ { "filter_maclist", "", 0 }, /* xx:xx:xx:xx:xx:xx ... */
+ { "filter_macmode", "deny", 0 }, /* "allow" only, "deny" only, or "disabled" (allow all) */
+ { "filter_client0", "", 0 }, /* [lan_ipaddr0-lan_ipaddr1|*]:lan_port0-lan_port1,proto,enable,day_start-day_end,sec_start-sec_end,desc */
+
+ /* Port forwards */
+ { "dmz_ipaddr", "", 0 }, /* x.x.x.x (equivalent to 0-60999>dmz_ipaddr:0-60999) */
+ { "forward_port0", "", 0 }, /* wan_port0-wan_port1>lan_ipaddr:lan_port0-lan_port1[:,]proto[:,]enable[:,]desc */
+ { "autofw_port0", "", 0 }, /* out_proto:out_port,in_proto:in_port0-in_port1>to_port0-to_port1,enable,desc */
+
+ /* DHCP server parameters */
+ { "dhcp_start", "192.168.1.100", 0 }, /* First assignable DHCP address */
+ { "dhcp_end", "192.168.1.150", 0 }, /* Last assignable DHCP address */
+ { "dhcp_domain", "wan", 0 }, /* Use WAN domain name first if available (wan|lan) */
+ { "dhcp_wins", "wan", 0 }, /* Use WAN WINS first if available (wan|lan) */
+
+ /* Web server parameters */
+ { "http_username", "", 0 }, /* Username */
+ { "http_passwd", "admin", 0 }, /* Password */
+ { "http_wanport", "", 0 }, /* WAN port to listen on */
+ { "http_lanport", "80", 0 }, /* LAN port to listen on */
+
+ /* Wireless parameters */
+ { "wl_ifname", "", 0 }, /* Interface name */
+ { "wl_hwaddr", "", 0 }, /* MAC address */
+ { "wl_phytype", "g", 0 }, /* Current wireless band ("a" (5 GHz), "b" (2.4 GHz), or "g" (2.4 GHz)) */
+ { "wl_corerev", "", 0 }, /* Current core revision */
+ { "wl_phytypes", "", 0 }, /* List of supported wireless bands (e.g. "ga") */
+ { "wl_radioids", "", 0 }, /* List of radio IDs */
+ { "wl_ssid", "OpenWrt", 0 }, /* Service set ID (network name) */
+ { "wl_country", "", 0 }, /* Country (default obtained from driver) */
+ { "wl_radio", "1", 0 }, /* Enable (1) or disable (0) radio */
+ { "wl_closed", "0", 0 }, /* Closed (hidden) network */
+ { "wl_ap_isolate", "0", 0 }, /* AP isolate mode */
+ { "wl_mode", "ap", 0 }, /* AP mode (ap|sta|wds) */
+ { "wl_lazywds", "0", 0 }, /* Enable "lazy" WDS mode (0|1) */
+ { "wl_wds", "", 0 }, /* xx:xx:xx:xx:xx:xx ... */
+ { "wl_wep", "disabled", 0 }, /* WEP data encryption (enabled|disabled) */
+ { "wl_auth", "0", 0 }, /* Shared key authentication optional (0) or required (1) */
+ { "wl_key", "1", 0 }, /* Current WEP key */
+ { "wl_key1", "", 0 }, /* 5/13 char ASCII or 10/26 char hex */
+ { "wl_key2", "", 0 }, /* 5/13 char ASCII or 10/26 char hex */
+ { "wl_key3", "", 0 }, /* 5/13 char ASCII or 10/26 char hex */
+ { "wl_key4", "", 0 }, /* 5/13 char ASCII or 10/26 char hex */
+ { "wl_maclist", "", 0 }, /* xx:xx:xx:xx:xx:xx ... */
+ { "wl_macmode", "disabled", 0 }, /* "allow" only, "deny" only, or "disabled" (allow all) */
+ { "wl_channel", "11", 0 }, /* Channel number */
+ { "wl_rate", "0", 0 }, /* Rate (bps, 0 for auto) */
+ { "wl_rateset", "default", 0 }, /* "default" or "all" or "12" */
+ { "wl_frag", "2346", 0 }, /* Fragmentation threshold */
+ { "wl_rts", "2347", 0 }, /* RTS threshold */
+ { "wl_dtim", "1", 0 }, /* DTIM period */
+ { "wl_bcn", "100", 0 }, /* Beacon interval */
+ { "wl_plcphdr", "long", 0 }, /* 802.11b PLCP preamble type */
+ { "wl_net_mode", "mixed", 0 }, /* 54g mode */
+ { "wl_gmode", "6", 0 }, /* 54g mode */
+ { "wl_gmode_protection", "auto", 0 }, /* 802.11g RTS/CTS protection (off|auto) */
+ { "wl_afterburner", "auto", 0 }, /* AfterBurner */
+ { "wl_frameburst", "off", 0 }, /* BRCM Frambursting mode (off|on) */
+ { "wl_antdiv", "-1", 0 }, /* Antenna Diversity (-1|0|1|3) */
+ { "wl_infra", "1", 0 }, /* Network Type (BSS/IBSS) */
+
+ /* WPA parameters */
+ { "security_mode", "open", 0 },
+ { "wl_auth_mode", "open", 0 }, /* Network authentication mode (open|shared|radius|wpa|psk) */
+ { "wl_wpa_psk", "", 0 }, /* WPA pre-shared key */
+ { "wl_wpa_gtk_rekey", "3600", 0 }, /* GTK rotation interval */
+ { "wl_radius_ipaddr", "", 0 }, /* RADIUS server IP address */
+ { "wl_radius_key", "", 0 }, /* RADIUS shared secret */
+ { "wl_radius_port", "1812", 0 }, /* RADIUS server UDP port */
+ { "wl_crypto", "tkip", 0 }, /* WPA data encryption */
+
+
+ { "wl_unit", "0", 0 }, /* Last configured interface */
+
+ /* Restore defaults */
+ { "restore_defaults", "0", 0 }, /* Set to 0 to not restore defaults on boot */
+
+ { 0, 0, 0 }
+};
diff --git a/target/linux/package/nvram/src/linux_timer.c b/target/linux/package/nvram/src/linux_timer.c
new file mode 100644
index 000000000..0402e4437
--- /dev/null
+++ b/target/linux/package/nvram/src/linux_timer.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright 2004, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * Low resolution timer interface linux specific implementation.
+ *
+ * $Id$
+ */
+
+/*
+* debug facilities
+*/
+#define TIMER_DEBUG 0
+#if TIMER_DEBUG
+#define TIMERDBG(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
+#else
+#define TIMERDBG(fmt, args...)
+#endif
+
+
+/*
+ * POSIX timer support for Linux. Taken from linux_timer.c in upnp
+ */
+
+#define __USE_GNU
+
+
+#include <stdlib.h> // for malloc, free, etc.
+#include <string.h> // for memset, strncasecmp, etc.
+#include <assert.h> // for assert, of course.
+#include <signal.h> // for sigemptyset, etc.
+#include <stdio.h> // for printf, etc.
+#include <sys/time.h>
+#include <time.h>
+
+/* define TIMER_PROFILE to enable code which guages how accurate the timer functions are.
+ For each expiring timer the code will print the expected time interval and the actual time interval.
+#define TIMER_PROFILE
+*/
+#undef TIMER_PROFILE
+
+/*
+timer_cancel( ) - cancel a timer
+timer_connect( ) - connect a user routine to the timer signal
+timer_create( ) - allocate a timer using the specified clock for a timing base (POSIX)
+timer_delete( ) - remove a previously created timer (POSIX)
+timer_gettime( ) - get the remaining time before expiration and the reload value (POSIX)
+timer_getoverrun( ) - return the timer expiration overrun (POSIX)
+timer_settime( ) - set the time until the next expiration and arm timer (POSIX)
+nanosleep( ) - suspend the current task until the time interval elapses (POSIX)
+*/
+
+#define MS_PER_SEC 1000
+#define US_PER_SEC 1000000
+#define US_PER_MS 1000
+#define UCLOCKS_PER_SEC 1000000
+
+typedef void (*event_callback_t)(timer_t, int);
+
+#ifndef TIMESPEC_TO_TIMEVAL
+# define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+ (tv)->tv_sec = (ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+}
+#endif
+
+#ifndef TIMEVAL_TO_TIMESPEC
+# define TIMEVAL_TO_TIMESPEC(tv, ts) { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+}
+#endif
+
+#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
+
+#define timerroundup(t,g) \
+ do { \
+ if (!timerisset(t)) (t)->tv_usec=1; \
+ if ((t)->tv_sec == 0) (t)->tv_usec=ROUNDUP((t)->tv_usec, g); \
+ } while (0)
+
+typedef long uclock_t;
+
+#define TFLAG_NONE 0
+#define TFLAG_CANCELLED (1<<0)
+#define TFLAG_DELETED (1<<1)
+
+struct event {
+ struct timeval it_interval;
+ struct timeval it_value;
+ event_callback_t func;
+ int arg;
+ unsigned short flags;
+ struct event *next;
+#ifdef TIMER_PROFILE
+ uint expected_ms;
+ uclock_t start;
+#endif
+};
+
+void timer_cancel(timer_t timerid);
+
+static void alarm_handler(int i);
+static void check_event_queue();
+static void print_event_queue();
+static void check_timer();
+#if THIS_FINDS_USE
+static int count_queue(struct event *);
+#endif
+static int timer_change_settime(timer_t timer_id, const struct itimerspec *timer_spec);
+void block_timer();
+void unblock_timer();
+
+static struct event *event_queue = NULL;
+static struct event *event_freelist;
+static uint g_granularity;
+static int g_maxevents = 0;
+
+uclock_t uclock()
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return ((tv.tv_sec * US_PER_SEC) + tv.tv_usec);
+}
+
+
+void init_event_queue(int n)
+{
+ int i;
+ struct itimerval tv;
+
+ g_maxevents = n;
+ event_freelist = (struct event *) malloc(n * sizeof(struct event));
+ memset(event_freelist, 0, n * sizeof(struct event));
+
+ for (i = 0; i < (n-1); i++)
+ event_freelist[i].next = &event_freelist[i+1];
+
+ event_freelist[i].next = NULL;
+
+ tv.it_interval.tv_sec = 0;
+ tv.it_interval.tv_usec = 1;
+ tv.it_value.tv_sec = 0;
+ tv.it_value.tv_usec = 0;
+ setitimer (ITIMER_REAL, &tv, 0);
+ setitimer (ITIMER_REAL, 0, &tv);
+ g_granularity = tv.it_interval.tv_usec;
+
+ signal(SIGALRM, alarm_handler);
+}
+
+
+int clock_gettime(
+ clockid_t clock_id, /* clock ID (always CLOCK_REALTIME) */
+ struct timespec * tp /* where to store current time */
+)
+{
+ struct timeval tv;
+ int n;
+
+
+ n = gettimeofday(&tv, NULL);
+ TIMEVAL_TO_TIMESPEC(&tv, tp);
+
+ return n;
+}
+
+
+int timer_create(
+ clockid_t clock_id, /* clock ID (always CLOCK_REALTIME) */
+ struct sigevent * evp, /* user event handler */
+ timer_t * pTimer /* ptr to return value */
+)
+{
+ struct event *event;
+
+ if (clock_id != CLOCK_REALTIME) {
+ TIMERDBG("timer_create can only support clock id CLOCK_REALTIME");
+ exit(1);
+ }
+
+ if (evp != NULL) {
+ if (evp->sigev_notify != SIGEV_SIGNAL || evp->sigev_signo != SIGALRM) {
+ TIMERDBG("timer_create can only support signalled alarms using SIGALRM");
+ exit(1);
+ }
+ }
+
+ event = event_freelist;
+ if (event == NULL) {
+ print_event_queue();
+ }
+ assert(event != NULL);
+
+ event->flags = TFLAG_NONE;
+
+ event_freelist = event->next;
+ event->next = NULL;
+
+ check_event_queue();
+
+ *pTimer = (timer_t) event;
+
+ return 0;
+}
+
+int timer_delete(
+ timer_t timerid /* timer ID */
+)
+{
+ struct event *event = (struct event *) timerid;
+
+ if (event->flags & TFLAG_DELETED) {
+ TIMERDBG("Cannot delete a deleted event");
+ return 1;
+ }
+
+ timer_cancel(timerid);
+
+ event->flags |= TFLAG_DELETED;
+
+ event->next = event_freelist;
+ event_freelist = event;
+
+ return 0;
+}
+
+int timer_connect
+(
+ timer_t timerid, /* timer ID */
+ void (*routine)(timer_t, int), /* user routine */
+ int arg /* user argument */
+)
+{
+ struct event *event = (struct event *) timerid;
+
+ assert(routine != NULL);
+ event->func = routine;
+ event->arg = arg;
+
+ return 0;
+}
+
+/*
+ * Please Call this function only from the call back functions of the alarm_handler.
+ * This is just a hack
+*/
+int timer_change_settime
+(
+ timer_t timerid, /* timer ID */
+ const struct itimerspec * value /* time to be set */
+)
+{
+ struct event *event = (struct event *) timerid;
+
+ TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval);
+ TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value);
+
+ return 1;
+}
+
+int timer_settime
+(
+ timer_t timerid, /* timer ID */
+ int flags, /* absolute or relative */
+ const struct itimerspec * value, /* time to be set */
+ struct itimerspec * ovalue /* previous time set (NULL=no result) */
+)
+{
+ struct itimerval itimer;
+ struct event *event = (struct event *) timerid;
+ struct event **ppevent;
+
+ TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval);
+ TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value);
+
+ /* if .it_value is zero, the timer is disarmed */
+ if (!timerisset(&event->it_value)) {
+ timer_cancel(timerid);
+ return 0;
+ }
+
+ block_timer();
+
+#ifdef TIMER_PROFILE
+ event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + (event->it_value.tv_usec / US_PER_MS);
+ event->start = uclock();
+#endif
+ if (event->next) {
+ TIMERDBG("calling timer_settime with a timer that is already on the queue.");
+ }
+
+
+ /* We always want to make sure that the event at the head of the
+ queue has a timeout greater than the itimer granularity.
+ Otherwise we end up with the situation that the time remaining
+ on an itimer is greater than the time at the head of the queue
+ in the first place. */
+ timerroundup(&event->it_value, g_granularity);
+
+ timerclear(&itimer.it_value);
+ getitimer(ITIMER_REAL, &itimer);
+ if (timerisset(&itimer.it_value)) {
+ // reset the top timer to have an interval equal to the remaining interval
+ // when the timer was cancelled.
+ if (event_queue) {
+ if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) {
+ // it is an error if the amount of time remaining is more than the amount of time
+ // requested by the top event.
+ //
+ TIMERDBG("timer_settime: TIMER ERROR!");
+
+ } else {
+ // some portion of the top event has already expired.
+ // Reset the interval of the top event to remaining
+ // time left in that interval.
+ //
+ event_queue->it_value = itimer.it_value;
+
+ // if we were the earliest timer before now, we are still the earliest timer now.
+ // we do not need to reorder the list.
+ }
+ }
+ }
+
+ // Now, march down the list, decrementing the new timer by the
+ // current it_value of each event on the queue.
+ ppevent = &event_queue;
+ while (*ppevent) {
+ if ( timercmp(&(event->it_value), &((*ppevent)->it_value), <) ) {
+ // if the proposed event will trigger sooner than the next event
+ // in the queue, we will insert the new event just before the next one.
+ //
+ // we also need to adjust the delta value to the next event.
+ timersub(&((*ppevent)->it_value), &(event->it_value), &((*ppevent)->it_value));
+ break;
+ }
+ // subtract the interval of the next event from the proposed interval.
+ timersub(&(event->it_value), &((*ppevent)->it_value), &(event->it_value));
+
+ ppevent = &((*ppevent)->next);
+ }
+
+ // we have found our proper place in the queue,
+ // link our new event into the pending event queue.
+ event->next = *ppevent;
+ *ppevent = event;
+
+ check_event_queue();
+
+ // if our new event ended up at the front of the queue, reissue the timer.
+ if (event == event_queue) {
+ timerroundup(&event_queue->it_value, g_granularity);
+ timerclear(&itimer.it_interval);
+ itimer.it_value = event_queue->it_value;
+
+ // we want to be sure to never turn off the timer completely,
+ // so if the next interval is zero, set it to some small value.
+ if (!timerisset(&(itimer.it_value)))
+ itimer.it_value = (struct timeval) { 0, 1 };
+
+ assert(!timerisset(&itimer.it_interval));
+ assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= g_granularity);
+ assert(event_queue->it_value.tv_sec > 0 || event_queue->it_value.tv_usec >= g_granularity);
+ setitimer(ITIMER_REAL, &itimer, NULL);
+ check_timer();
+ }
+
+ event->flags &= ~TFLAG_CANCELLED;
+
+ unblock_timer();
+
+ return 0;
+}
+
+static void check_timer()
+{
+ struct itimerval itimer;
+
+ getitimer(ITIMER_REAL, &itimer);
+ if (timerisset(&itimer.it_interval)) {
+ TIMERDBG("ERROR timer interval is set.");
+ }
+ if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) {
+ TIMERDBG("ERROR timer expires later than top event.");
+ }
+}
+
+
+static void check_event_queue()
+{
+ struct timeval sum;
+ struct event *event;
+ int i = 0;
+
+#ifdef notdef
+ int nfree = 0;
+ struct event *p;
+ for (p = event_freelist; p; p = p->next)
+ nfree++;
+ printf("%d free events\n", nfree);
+#endif
+
+ timerclear(&sum);
+ for (event = event_queue; event; event = event->next) {
+ if (i > g_maxevents) {
+ TIMERDBG("timer queue looks like it loops back on itself!");
+ print_event_queue();
+ exit(1);
+ }
+ i++;
+ }
+}
+
+#if THIS_FINDS_USE
+/* The original upnp version has this unused function, so I left it in
+ to maintain the resemblance. */
+static int count_queue(struct event *event_queue)
+{
+ struct event *event;
+ int i = 0;
+ for (event = event_queue; event; event = event->next)
+ i++;
+ return i;
+}
+#endif
+
+static void print_event_queue()
+{
+ struct event *event;
+ int i = 0;
+
+ for (event = event_queue; event; event = event->next) {
+ printf("#%d (0x%x)->0x%x: \t%d sec %d usec\t%p\n",
+ i++, (unsigned int) event, (unsigned int) event->next, (int) event->it_value.tv_sec, (int) event->it_value.tv_usec, event->func);
+ if (i > g_maxevents) {
+ printf("...(giving up)\n");
+ break;
+ }
+ }
+}
+
+// The top element of the event queue must have expired.
+// Remove that element, run its function, and reset the timer.
+// if there is no interval, recycle the event structure.
+static void alarm_handler(int i)
+{
+ struct event *event, **ppevent;
+ struct itimerval itimer;
+ struct timeval small_interval = { 0, g_granularity/2 };
+#ifdef TIMER_PROFILE
+ uint junk;
+ uclock_t end;
+ uint actual;
+#endif
+
+ block_timer();
+
+ // Loop through the event queue and remove the first event plus any
+ // subsequent events that will expire very soon thereafter (within 'small_interval'}.
+ //
+ do {
+ // remove the top event.
+ event = event_queue;
+ event_queue = event_queue->next;
+ event->next = NULL;
+
+#ifdef TIMER_PROFILE
+ end = uclock();
+ actual = ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000));
+ if (actual < 0)
+ junk = end;
+ TIMERDBG("expected %d ms actual %d ms", event->expected_ms, ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000)));
+#endif
+
+ // call the event callback function
+ (*(event->func))((timer_t) event, (int)event->arg);
+
+ /* If the event has been cancelled, do NOT put it back on the queue. */
+ if ( !(event->flags & TFLAG_CANCELLED) ) {
+
+ // if the event is a recurring event, reset the timer and
+ // find its correct place in the sorted list of events.
+ //
+ if (timerisset(&event->it_interval)) {
+ // event is recurring...
+ //
+ event->it_value = event->it_interval;
+#ifdef TIMER_PROFILE
+ event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + (event->it_value.tv_usec / US_PER_MS);
+ event->start = uclock();
+#endif
+ timerroundup(&event->it_value, g_granularity);
+
+ // Now, march down the list, decrementing the new timer by the
+ // current delta of each event on the queue.
+ ppevent = &event_queue;
+ while (*ppevent) {
+ if ( timercmp(&(event->it_value), &((*ppevent)->it_value), <) ) {
+ // if the proposed event will trigger sooner than the next event
+ // in the queue, we will insert the new event just before the next one.
+ //
+ // we also need to adjust the delta value to the next event.
+ timersub(&((*ppevent)->it_value), &(event->it_value), &((*ppevent)->it_value));
+ break;
+ }
+ timersub(&(event->it_value), &((*ppevent)->it_value), &(event->it_value));
+ ppevent = &((*ppevent)->next);
+ }
+
+ // we have found our proper place in the queue,
+ // link our new event into the pending event queue.
+ event->next = *ppevent;
+ *ppevent = event;
+ } else {
+ // there is no interval, so recycle the event structure.
+ //timer_delete((timer_t) event);
+ }
+ }
+
+ check_event_queue();
+
+ } while (event_queue && timercmp(&event_queue->it_value, &small_interval, <));
+
+ // re-issue the timer...
+ if (event_queue) {
+ timerroundup(&event_queue->it_value, g_granularity);
+
+ timerclear(&itimer.it_interval);
+ itimer.it_value = event_queue->it_value;
+ // we want to be sure to never turn off the timer completely,
+ // so if the next interval is zero, set it to some small value.
+ if (!timerisset(&(itimer.it_value)))
+ itimer.it_value = (struct timeval) { 0, 1 };
+
+ setitimer(ITIMER_REAL, &itimer, NULL);
+ check_timer();
+ } else {
+ TIMERDBG("There are no events in the queue - timer not reset.");
+ }
+
+ unblock_timer();
+}
+
+static int block_count = 0;
+
+void block_timer()
+{
+ sigset_t set;
+
+ if (block_count++ == 0) {
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+ }
+}
+
+void unblock_timer()
+{
+ sigset_t set;
+
+ if (--block_count == 0) {
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ }
+}
+
+void timer_cancel_all()
+{
+ struct itimerval timeroff = { { 0, 0 }, { 0, 0} };
+ struct event *event;
+ struct event **ppevent;
+
+ setitimer(ITIMER_REAL, &timeroff, NULL);
+
+ ppevent = &event_queue;
+ while (*ppevent) {
+ event = *ppevent;
+ *ppevent = event->next;
+ event->next = NULL;
+ }
+}
+
+
+
+void timer_cancel(timer_t timerid)
+{
+ struct itimerval itimer;
+ struct itimerval timeroff = { { 0, 0 }, { 0, 0} };
+ struct event *event = (struct event *) timerid;
+ struct event **ppevent;
+
+ if (event->flags & TFLAG_CANCELLED) {
+ TIMERDBG("Cannot cancel a cancelled event");
+ return;
+ }
+
+ block_timer();
+
+ ppevent = &event_queue;
+ while (*ppevent) {
+ if ( *ppevent == event ) {
+
+ /* RACE CONDITION - if the alarm goes off while we are in
+ this loop, and if the timer we want to cancel is the
+ next to expire, the alarm will end up firing
+ after this routine is complete, causing it to go off early. */
+
+ /* If the cancelled timer is the next to expire,
+ we need to do something special to clean up correctly. */
+ if (event == event_queue && event->next != NULL) {
+ timerclear(&itimer.it_value);
+ getitimer(ITIMER_REAL, &itimer);
+
+ /* subtract the time that has already passed while waiting for this timer... */
+ timersub(&(event->it_value), &(itimer.it_value), &(event->it_value));
+
+ /* and add any remainder to the next timer in the list */
+ timeradd(&(event->next->it_value), &(event->it_value), &(event->next->it_value));
+ }
+
+ *ppevent = event->next;
+ event->next = NULL;
+
+ if (event_queue) {
+ timerroundup(&event_queue->it_value, g_granularity);
+ timerclear(&itimer.it_interval);
+ itimer.it_value = event_queue->it_value;
+
+ /* We want to be sure to never turn off the timer
+ completely if there are more events on the queue,
+ so if the next interval is zero, set it to some
+ small value. */
+
+ if (!timerisset(&(itimer.it_value)))
+ itimer.it_value = (struct timeval) { 0, 1 };
+
+ assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= g_granularity);
+ assert(event_queue->it_value.tv_sec > 0 || event_queue->it_value.tv_usec >= g_granularity);
+ setitimer(ITIMER_REAL, &itimer, NULL);
+ check_timer();
+ } else {
+ setitimer(ITIMER_REAL, &timeroff, NULL);
+ }
+ break;
+ }
+ ppevent = &((*ppevent)->next);
+ }
+
+ event->flags |= TFLAG_CANCELLED;
+
+ unblock_timer();
+}
+
+/*
+* timer related headers
+*/
+#include "bcmtimer.h"
+
+/*
+* locally used global variables and constants
+*/
+
+/*
+* Initialize internal resources used in the timer module. It must be called
+* before any other timer function calls. The param 'timer_entries' is used
+* to pre-allocate fixed number of timer entries.
+*/
+int bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id)
+{
+ init_event_queue(timer_entries);
+ *module_id = (bcm_timer_module_id)event_freelist;
+ return 0;
+}
+
+/*
+* Cleanup internal resources used by this timer module. It deletes all
+* pending timer entries from the backend timer system as well.
+*/
+int bcm_timer_module_cleanup(bcm_timer_module_id module_id)
+{
+ module_id = 0;
+ return 0;
+}
+
+/* Enable/Disable timer module */
+int bcm_timer_module_enable(bcm_timer_module_id module_id, int enable)
+{
+ if (enable)
+ unblock_timer();
+ else
+ block_timer();
+ return 0;
+}
+
+int bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id)
+{
+ module_id = 0;
+ return timer_create(CLOCK_REALTIME, NULL, (timer_t *)timer_id);
+}
+
+int bcm_timer_delete(bcm_timer_id timer_id)
+{
+ return timer_delete((timer_t)timer_id);
+}
+
+int bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *timer_spec)
+{
+ return -1;
+}
+
+int bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
+{
+ return timer_settime((timer_t)timer_id, 0, timer_spec, NULL);
+}
+
+int bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data)
+{
+ return timer_connect((timer_t)timer_id, (void *)func, data);
+}
+
+int bcm_timer_cancel(bcm_timer_id timer_id)
+{
+ timer_cancel((timer_t)timer_id);
+ return 0;
+}
+int bcm_timer_change_expirytime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
+{
+ timer_change_settime((timer_t)timer_id, timer_spec);
+ return 1;
+}
diff --git a/target/linux/package/nvram/src/main.c b/target/linux/package/nvram/src/main.c
new file mode 100644
index 000000000..a64430f7b
--- /dev/null
+++ b/target/linux/package/nvram/src/main.c
@@ -0,0 +1,78 @@
+/*
+ * Frontend command-line utility for Linux NVRAM layer
+ *
+ * Copyright 2004, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <typedefs.h>
+#include <bcmnvram.h>
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: nvram [get name] [set name=value] [unset name] [show]\n");
+ exit(0);
+}
+
+/* NVRAM utility */
+int
+main(int argc, char **argv)
+{
+ char *name, *value, buf[NVRAM_SPACE];
+ int size;
+
+ /* Skip program name */
+ --argc;
+ ++argv;
+
+ if (!*argv)
+ usage();
+
+ /* Process the remaining arguments. */
+ for (; *argv; argv++) {
+ if (!strncmp(*argv, "get", 3)) {
+ if (*++argv) {
+ if ((value = nvram_get(*argv)))
+ puts(value);
+ }
+ }
+ else if (!strncmp(*argv, "set", 3)) {
+ if (*++argv) {
+ strncpy(value = buf, *argv, sizeof(buf));
+ name = strsep(&value, "=");
+ nvram_set(name, value);
+ }
+ }
+ else if (!strncmp(*argv, "unset", 5)) {
+ if (*++argv)
+ nvram_unset(*argv);
+ }
+ else if (!strncmp(*argv, "commit", 5)) {
+ nvram_commit();
+ }
+ else if (!strncmp(*argv, "show", 4) ||
+ !strncmp(*argv, "getall", 6)) {
+ nvram_getall(buf, sizeof(buf));
+ for (name = buf; *name; name += strlen(name) + 1)
+ puts(name);
+ size = sizeof(struct nvram_header) + (int) name - (int) buf;
+ fprintf(stderr, "size: %d bytes (%d left)\n", size, NVRAM_SPACE - size);
+ }
+ if (!*argv)
+ break;
+ }
+
+ return 0;
+}
diff --git a/target/linux/package/nvram/src/nvram_convert.c b/target/linux/package/nvram/src/nvram_convert.c
new file mode 100644
index 000000000..485909026
--- /dev/null
+++ b/target/linux/package/nvram/src/nvram_convert.c
@@ -0,0 +1,77 @@
+
+#define WL(a) "wl_"a
+#define WL0(a) "wl0_"a
+#define D11G(a) "d11g_"a
+
+#define PPP(a) "ppp_"a
+#define PPPOE(a) "pppoe_"a
+
+struct nvram_convert {
+ char *name; // for WEB
+ char *wl0_name; // for driver
+ char *d11g_name; // for old nv name
+};
+
+struct nvram_convert nvram_converts[] = {
+ // Bellow change from 3.11.48.7
+ { WL("ssid"), WL0("ssid"), ""},
+ { WL("radio"), WL0("mode"), ""},
+ { WL("mode"), WL0("mode"), ""},
+ { WL("wds"), WL0("wds"), ""},
+ { WL("auth"), WL0("auth"), ""},
+ { WL("key"), WL0("key"), ""},
+ { WL("key1"), WL0("key1"), ""},
+ { WL("key2"), WL0("key2"), ""},
+ { WL("key3"), WL0("key3"), ""},
+ { WL("key4"), WL0("key4"), ""},
+ { WL("maclist"), WL0("maclist"), ""},
+ { WL("channel"), WL0("channel"), D11G("channel")},
+ { WL("rateset"), WL0("rateset"), D11G("rateset")},
+ { WL("rts"), WL0("rts"), D11G("rts")},
+ { WL("bcn"), WL0("bcn"), D11G("bcn")},
+ { WL("gmode"), WL0("gmode"), "d11g_mode"},
+ { WL("unit"), WL0("unit"), ""},
+ { WL("ifname"), WL0("ifname"), ""},
+ { WL("phytype"), WL0("phytype"), ""},
+ { WL("country"), WL0("country"), ""},
+ { WL("closed"), WL0("closed"), ""},
+ { WL("lazywds"), WL0("lazywds"), ""},
+ { WL("wep"), WL0("wep"), ""},
+ { WL("macmode"), WL0("macmode"), ""},
+ { WL("rate"), WL0("rate"), D11G("rate")},
+ { WL("frag"), WL0("frag"), D11G("frag")},
+ { WL("dtim"), WL0("dtim"), D11G("dtim")},
+ { WL("plcphdr"), WL0("plcphdr"), ""},
+ { WL("gmode_protection"), WL0("gmode_protection"), ""},
+ { WL("radio"), WL0("radio"), ""},
+ // Bellow change from 3.21.9.0
+ { WL("auth_mode"), WL0("auth_mode"), ""},
+ { WL("radius_ipaddr"), WL0("radius_ipaddr"), ""},
+ { WL("radius_port"), WL0("radius_port"), ""},
+ { WL("radius_key"), WL0("radius_key"), ""},
+ { WL("wpa_psk"), WL0("wpa_psk"), ""},
+ { WL("wpa_gtk_rekey"), WL0("wpa_gtk_rekey"), ""},
+ { WL("frameburst"), WL0("frameburst"), ""},
+ { WL("crypto"), WL0("crypto"), ""},
+ { WL("ap_isolate"), WL0("ap_isolate"), ""},
+ { WL("afterburner"), WL0("afterburner"), ""},
+ // for PPPoE
+ { PPP("username"), PPPOE("username"), ""},
+ { PPP("passwd"), PPPOE("passwd"), ""},
+ { PPP("idletime"), PPPOE("idletime"), ""},
+ { PPP("keepalive"), PPPOE("keepalive"), ""},
+ { PPP("demand"), PPPOE("demand"), ""},
+ { PPP("service"), PPPOE("service"), ""},
+ { PPP("ac"), PPPOE("ac"), ""},
+ { PPP("static"), PPPOE("static"), ""},
+ { PPP("static_ip"), PPPOE("static_ip"), ""},
+ { PPP("username_1"), PPPOE("username_1"), ""},
+ { PPP("passwd_1"), PPPOE("passwd_1"), ""},
+ { PPP("idletime_1"), PPPOE("idletime_1"), ""},
+ { PPP("keepalive_1"), PPPOE("keepalive_1"), ""},
+ { PPP("demand_1"), PPPOE("demand_1"), ""},
+ { PPP("service_1"), PPPOE("service_1"), ""},
+ { PPP("ac_1"), PPPOE("ac_1"), ""},
+
+ { 0, 0, 0},
+};
diff --git a/target/linux/package/nvram/src/nvram_convert.h b/target/linux/package/nvram/src/nvram_convert.h
new file mode 100644
index 000000000..70e12327d
--- /dev/null
+++ b/target/linux/package/nvram/src/nvram_convert.h
@@ -0,0 +1,7 @@
+
+struct nvram_convert {
+ char *name;
+ char *wl0_name;
+ char *d11g_name;
+};
+
diff --git a/target/linux/package/nvram/src/nvram_linux.c b/target/linux/package/nvram/src/nvram_linux.c
new file mode 100644
index 000000000..c41e32118
--- /dev/null
+++ b/target/linux/package/nvram/src/nvram_linux.c
@@ -0,0 +1,320 @@
+/*
+ * NVRAM variable manipulation (Linux user mode half)
+ *
+ * Copyright 2004, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <error.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <typedefs.h>
+#include <bcmnvram.h>
+#include <nvram_convert.h>
+#include <shutils.h>
+#include <utils.h>
+
+#define PATH_DEV_NVRAM "/dev/nvram"
+
+/* Globals */
+static int nvram_fd = -1;
+static char *nvram_buf = NULL;
+int check_action(void);
+int file_to_buf(char *path, char *buf, int len);
+
+int
+nvram_init(void *unused)
+{
+ if ((nvram_fd = open(PATH_DEV_NVRAM, O_RDWR)) < 0)
+ goto err;
+
+ /* Map kernel string buffer into user space */
+ if ((nvram_buf = mmap(NULL, NVRAM_SPACE, PROT_READ, MAP_SHARED, nvram_fd, 0)) == MAP_FAILED) {
+ close(nvram_fd);
+ nvram_fd = -1;
+ goto err;
+ }
+
+ return 0;
+
+ err:
+ perror(PATH_DEV_NVRAM);
+ return errno;
+}
+
+char *
+nvram_get(const char *name)
+{
+ size_t count = strlen(name) + 1;
+ char tmp[100], *value;
+ unsigned long *off = (unsigned long *) tmp;
+
+ if (nvram_fd < 0)
+ if (nvram_init(NULL))
+ return NULL;
+
+ if (count > sizeof(tmp)) {
+ if (!(off = malloc(count)))
+ return NULL;
+ }
+
+ /* Get offset into mmap() space */
+ strcpy((char *) off, name);
+
+ count = read(nvram_fd, off, count);
+
+ if (count == sizeof(unsigned long))
+ value = &nvram_buf[*off];
+ else
+ value = NULL;
+
+ if (count < 0)
+ perror(PATH_DEV_NVRAM);
+
+ if (off != (unsigned long *) tmp)
+ free(off);
+
+ return value;
+}
+
+int
+nvram_getall(char *buf, int count)
+{
+ int ret;
+
+ if (nvram_fd < 0)
+ if ((ret = nvram_init(NULL)))
+ return ret;
+
+ if (count == 0)
+ return 0;
+
+ /* Get all variables */
+ *buf = '\0';
+
+ ret = read(nvram_fd, buf, count);
+
+ if (ret < 0)
+ perror(PATH_DEV_NVRAM);
+
+ return (ret == count) ? 0 : ret;
+}
+
+static int
+_nvram_set(const char *name, const char *value)
+{
+ size_t count = strlen(name) + 1;
+ char tmp[100], *buf = tmp;
+ int ret;
+
+ if (nvram_fd < 0)
+ if ((ret = nvram_init(NULL)))
+ return ret;
+
+ /* Unset if value is NULL */
+ if (value)
+ count += strlen(value) + 1;
+
+ if (count > sizeof(tmp)) {
+ if (!(buf = malloc(count)))
+ return -ENOMEM;
+ }
+
+ if (value)
+ sprintf(buf, "%s=%s", name, value);
+ else
+ strcpy(buf, name);
+
+ ret = write(nvram_fd, buf, count);
+
+ if (ret < 0)
+ perror(PATH_DEV_NVRAM);
+
+ if (buf != tmp)
+ free(buf);
+
+ return (ret == count) ? 0 : ret;
+}
+
+int
+nvram_set(const char *name, const char *value)
+{
+ extern struct nvram_convert nvram_converts[];
+ struct nvram_convert *v;
+ int ret;
+
+ ret = _nvram_set(name, value);
+
+ for(v = nvram_converts ; v->name ; v++) {
+ if(!strcmp(v->name, name)){
+ if(strcmp(v->wl0_name,"")) _nvram_set(v->wl0_name, value);
+ if(strcmp(v->d11g_name,"")) _nvram_set(v->d11g_name, value);
+ }
+ }
+
+ return ret;
+}
+
+int
+nvram_unset(const char *name)
+{
+ return _nvram_set(name, NULL);
+}
+
+int
+nvram_commit(void)
+{
+ int ret;
+
+ cprintf("nvram_commit(): start\n");
+
+ if((check_action() == ACT_IDLE) ||
+ (check_action() == ACT_SW_RESTORE) ||
+ (check_action() == ACT_HW_RESTORE)){
+ if (nvram_fd < 0)
+ if ((ret = nvram_init(NULL)))
+ return ret;
+
+ ret = ioctl(nvram_fd, NVRAM_MAGIC, NULL);
+
+ if (ret < 0)
+ perror(PATH_DEV_NVRAM);
+
+ cprintf("nvram_commit(): end\n");
+ }
+ else
+ cprintf("nvram_commit(): nothing to do...\n");
+
+ return ret;
+}
+
+int file2nvram(char *filename, char *varname) {
+ FILE *fp;
+ int c,count;
+ int i=0,j=0;
+ char mem[10000],buf[30000];
+
+ if ( !(fp=fopen(filename,"rb") ))
+ return 0;
+
+ count=fread(mem,1,sizeof(mem),fp);
+ fclose(fp);
+ for (j=0;j<count;j++) {
+ if (i > sizeof(buf)-3 )
+ break;
+ c=mem[j];
+ if (c >= 32 && c <= 126 && c != '\\' && c != '~') {
+ buf[i++]=(unsigned char) c;
+ } else if (c==0) {
+ buf[i++]='~';
+ } else {
+ buf[i++]='\\';
+ sprintf(buf+i,"%02X",c);
+ i+=2;
+ }
+ }
+ if (i==0) return 0;
+ buf[i]=0;
+ //fprintf(stderr,"================ > file2nvram %s = [%s] \n",varname,buf);
+ nvram_set(varname,buf);
+ //nvram_commit(); //Barry adds for test
+}
+
+int nvram2file(char *varname, char *filename) {
+ FILE *fp;
+ int c,tmp;
+ int i=0,j=0;
+ char *buf;
+ char mem[10000];
+
+ if ( !(fp=fopen(filename,"wb") ))
+ return 0;
+
+ buf=strdup(nvram_safe_get(varname));
+ //fprintf(stderr,"=================> nvram2file %s = [%s] \n",varname,buf);
+ while ( buf[i] && j < sizeof(mem)-3 ) {
+ if (buf[i] == '\\') {
+ i++;
+ tmp=buf[i+2];
+ buf[i+2]=0;
+ sscanf(buf+i,"%02X",&c);
+ buf[i+2]=tmp;
+ i+=2;
+ mem[j]=c;j++;
+ } else if (buf[i] == '~') {
+ mem[j]=0;j++;
+ i++;
+ } else {
+ mem[j]=buf[i];j++;
+ i++;
+ }
+ }
+ if (j<=0) return j;
+ j=fwrite(mem,1,j,fp);
+ fclose(fp);
+ free(buf);
+ return j;
+}
+
+int
+check_action(void)
+{
+ char buf[80] = "";
+
+ if(file_to_buf(ACTION_FILE, buf, sizeof(buf))){
+ if(!strcmp(buf, "ACT_TFTP_UPGRADE")){
+ cprintf("Upgrading from tftp now, quiet exit....\n");
+ return ACT_TFTP_UPGRADE;
+ }
+ else if(!strcmp(buf, "ACT_WEBS_UPGRADE")){
+ cprintf("Upgrading from web (https) now, quiet exit....\n");
+ return ACT_WEBS_UPGRADE;
+ }
+ else if(!strcmp(buf, "ACT_WEB_UPGRADE")){
+ cprintf("Upgrading from web (http) now, quiet exit....\n");
+ return ACT_WEB_UPGRADE;
+ }
+ else if(!strcmp(buf, "ACT_SW_RESTORE")){
+ cprintf("Receive restore command from web, quiet exit....\n");
+ return ACT_SW_RESTORE;
+ }
+ else if(!strcmp(buf, "ACT_HW_RESTORE")){
+ cprintf("Receive restore commond from resetbutton, quiet exit....\n");
+ return ACT_HW_RESTORE;
+ }
+ }
+ //fprintf(stderr, "Waiting for upgrading....\n");
+ return ACT_IDLE;
+}
+
+int
+file_to_buf(char *path, char *buf, int len)
+{
+ FILE *fp;
+
+ memset(buf, 0 , len);
+
+ if ((fp = fopen(path, "r"))) {
+ fgets(buf, len, fp);
+ fclose(fp);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/target/linux/package/nvram/src/shutils.c b/target/linux/package/nvram/src/shutils.c
new file mode 100644
index 000000000..49ad41af8
--- /dev/null
+++ b/target/linux/package/nvram/src/shutils.c
@@ -0,0 +1,329 @@
+/*
+ * Shell-like utility functions
+ *
+ * Copyright 2004, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/ethernet.h>
+
+#include <shutils.h>
+
+/*
+ * Reads file and returns contents
+ * @param fd file descriptor
+ * @return contents of file or NULL if an error occurred
+ */
+char *
+fd2str(int fd)
+{
+ char *buf = NULL;
+ size_t count = 0, n;
+
+ do {
+ buf = realloc(buf, count + 512);
+ n = read(fd, buf + count, 512);
+ if (n < 0) {
+ free(buf);
+ buf = NULL;
+ }
+ count += n;
+ } while (n == 512);
+
+ close(fd);
+ if (buf)
+ buf[count] = '\0';
+ return buf;
+}
+
+/*
+ * Reads file and returns contents
+ * @param path path to file
+ * @return contents of file or NULL if an error occurred
+ */
+char *
+file2str(const char *path)
+{
+ int fd;
+
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ perror(path);
+ return NULL;
+ }
+
+ return fd2str(fd);
+}
+
+/*
+ * Waits for a file descriptor to change status or unblocked signal
+ * @param fd file descriptor
+ * @param timeout seconds to wait before timing out or 0 for no timeout
+ * @return 1 if descriptor changed status or 0 if timed out or -1 on error
+ */
+int
+waitfor(int fd, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv = { timeout, 0 };
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ return select(fd + 1, &rfds, NULL, NULL, (timeout > 0) ? &tv : NULL);
+}
+
+/*
+ * Concatenates NULL-terminated list of arguments into a single
+ * commmand and executes it
+ * @param argv argument list
+ * @param path NULL, ">output", or ">>output"
+ * @param timeout seconds to wait before timing out or 0 for no timeout
+ * @param ppid NULL to wait for child termination or pointer to pid
+ * @return return value of executed command or errno
+ */
+int
+_eval(char *const argv[], char *path, int timeout, int *ppid)
+{
+ pid_t pid;
+ int status;
+ int fd;
+ int flags;
+ int sig;
+ char buf[254]="";
+ int i;
+
+ switch (pid = fork()) {
+ case -1: /* error */
+ perror("fork");
+ return errno;
+ case 0: /* child */
+ /* Reset signal handlers set for parent process */
+ for (sig = 0; sig < (_NSIG-1); sig++)
+ signal(sig, SIG_DFL);
+
+ /* Clean up */
+ ioctl(0, TIOCNOTTY, 0);
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ setsid();
+
+ /* We want to check the board if exist UART? , add by honor 2003-12-04 */
+ if ((fd = open("/dev/console", O_RDWR)) < 0) {
+ (void) open("/dev/null", O_RDONLY);
+ (void) open("/dev/null", O_WRONLY);
+ (void) open("/dev/null", O_WRONLY);
+ }
+ else{
+ close(fd);
+ (void) open("/dev/console", O_RDONLY);
+ (void) open("/dev/console", O_WRONLY);
+ (void) open("/dev/console", O_WRONLY);
+ }
+
+ /* Redirect stdout to <path> */
+ if (path) {
+ flags = O_WRONLY | O_CREAT;
+ if (!strncmp(path, ">>", 2)) {
+ /* append to <path> */
+ flags |= O_APPEND;
+ path += 2;
+ } else if (!strncmp(path, ">", 1)) {
+ /* overwrite <path> */
+ flags |= O_TRUNC;
+ path += 1;
+ }
+ if ((fd = open(path, flags, 0644)) < 0)
+ perror(path);
+ else {
+ dup2(fd, STDOUT_FILENO);
+ close(fd);
+ }
+ }
+
+ /* execute command */
+ for(i=0 ; argv[i] ; i++)
+ snprintf(buf+strlen(buf), sizeof(buf), "%s ", argv[i]);
+ dprintf("cmd=[%s]\n", buf);
+ setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
+ alarm(timeout);
+ execvp(argv[0], argv);
+ perror(argv[0]);
+ exit(errno);
+ default: /* parent */
+ if (ppid) {
+ *ppid = pid;
+ return 0;
+ } else {
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ else
+ return status;
+ }
+ }
+}
+
+/*
+ * Concatenates NULL-terminated list of arguments into a single
+ * commmand and executes it
+ * @param argv argument list
+ * @return stdout of executed command or NULL if an error occurred
+ */
+char *
+_backtick(char *const argv[])
+{
+ int filedes[2];
+ pid_t pid;
+ int status;
+ char *buf = NULL;
+
+ /* create pipe */
+ if (pipe(filedes) == -1) {
+ perror(argv[0]);
+ return NULL;
+ }
+
+ switch (pid = fork()) {
+ case -1: /* error */
+ return NULL;
+ case 0: /* child */
+ close(filedes[0]); /* close read end of pipe */
+ dup2(filedes[1], 1); /* redirect stdout to write end of pipe */
+ close(filedes[1]); /* close write end of pipe */
+ execvp(argv[0], argv);
+ exit(errno);
+ break;
+ default: /* parent */
+ close(filedes[1]); /* close write end of pipe */
+ buf = fd2str(filedes[0]);
+ waitpid(pid, &status, 0);
+ break;
+ }
+
+ return buf;
+}
+
+/*
+ * Kills process whose PID is stored in plaintext in pidfile
+ * @param pidfile PID file
+ * @return 0 on success and errno on failure
+ */
+int
+kill_pidfile(char *pidfile)
+{
+ FILE *fp = fopen(pidfile, "r");
+ char buf[256];
+
+ if (fp && fgets(buf, sizeof(buf), fp)) {
+ pid_t pid = strtoul(buf, NULL, 0);
+ fclose(fp);
+ return kill(pid, SIGTERM);
+ } else
+ return errno;
+}
+
+/*
+ * fread() with automatic retry on syscall interrupt
+ * @param ptr location to store to
+ * @param size size of each element of data
+ * @param nmemb number of elements
+ * @param stream file stream
+ * @return number of items successfully read
+ */
+int
+safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ size_t ret = 0;
+
+ do {
+ clearerr(stream);
+ ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
+ } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+ return ret;
+}
+
+/*
+ * fwrite() with automatic retry on syscall interrupt
+ * @param ptr location to read from
+ * @param size size of each element of data
+ * @param nmemb number of elements
+ * @param stream file stream
+ * @return number of items successfully written
+ */
+int
+safe_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ size_t ret = 0;
+
+ do {
+ clearerr(stream);
+ ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream);
+ } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+ return ret;
+}
+
+/*
+ * Convert Ethernet address string representation to binary data
+ * @param a string in xx:xx:xx:xx:xx:xx notation
+ * @param e binary data
+ * @return TRUE if conversion was successful and FALSE otherwise
+ */
+int
+ether_atoe(const char *a, unsigned char *e)
+{
+ char *c = (char *) a;
+ int i = 0;
+
+ memset(e, 0, ETHER_ADDR_LEN);
+ for (;;) {
+ e[i++] = (unsigned char) strtoul(c, &c, 16);
+ if (!*c++ || i == ETHER_ADDR_LEN)
+ break;
+ }
+ return (i == ETHER_ADDR_LEN);
+}
+
+/*
+ * Convert Ethernet address binary data to string representation
+ * @param e binary data
+ * @param a string in xx:xx:xx:xx:xx:xx notation
+ * @return a
+ */
+char *
+ether_etoa(const unsigned char *e, char *a)
+{
+ char *c = a;
+ int i;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ if (i)
+ *c++ = ':';
+ c += sprintf(c, "%02X", e[i] & 0xff);
+ }
+ return a;
+}
diff --git a/target/linux/package/nvram/src/wl.c b/target/linux/package/nvram/src/wl.c
new file mode 100644
index 000000000..f09317ad0
--- /dev/null
+++ b/target/linux/package/nvram/src/wl.c
@@ -0,0 +1,86 @@
+/*
+ * Wireless network adapter utilities
+ *
+ * Copyright 2004, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * $Id$
+ */
+#include <string.h>
+
+#include <typedefs.h>
+#include <wlutils.h>
+
+int
+wl_probe(char *name)
+{
+ int ret, val;
+
+ /* Check interface */
+ if ((ret = wl_ioctl(name, WLC_GET_MAGIC, &val, sizeof(val))))
+ return ret;
+ if (val != WLC_IOCTL_MAGIC)
+ return -1;
+ if ((ret = wl_ioctl(name, WLC_GET_VERSION, &val, sizeof(val))))
+ return ret;
+ if (val > WLC_IOCTL_VERSION)
+ return -1;
+
+ return ret;
+}
+
+int
+wl_set_val(char *name, char *var, void *val, int len)
+{
+ char buf[128];
+ int buf_len;
+
+ /* check for overflow */
+ if ((buf_len = strlen(var)) + 1 + len > sizeof(buf))
+ return -1;
+
+ strcpy(buf, var);
+ buf_len += 1;
+
+ /* append int value onto the end of the name string */
+ memcpy(&buf[buf_len], val, len);
+ buf_len += len;
+
+ return wl_ioctl(name, WLC_SET_VAR, buf, buf_len);
+}
+
+int
+wl_get_val(char *name, char *var, void *val, int len)
+{
+ char buf[128];
+ int ret;
+
+ /* check for overflow */
+ if (strlen(var) + 1 > sizeof(buf) || len > sizeof(buf))
+ return -1;
+
+ strcpy(buf, var);
+ if ((ret = wl_ioctl(name, WLC_GET_VAR, buf, sizeof(buf))))
+ return ret;
+
+ memcpy(val, buf, len);
+ return 0;
+}
+
+int
+wl_set_int(char *name, char *var, int val)
+{
+ return wl_set_val(name, var, &val, sizeof(val));
+}
+
+int
+wl_get_int(char *name, char *var, int *val)
+{
+ return wl_get_val(name, var, val, sizeof(*val));
+}
+
diff --git a/target/linux/package/nvram/src/wl_linux.c b/target/linux/package/nvram/src/wl_linux.c
new file mode 100644
index 000000000..126a40b4b
--- /dev/null
+++ b/target/linux/package/nvram/src/wl_linux.c
@@ -0,0 +1,77 @@
+/*
+ * Wireless network adapter utilities (linux-specific)
+ *
+ * Copyright 2004, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include <typedefs.h>
+#include <wlioctl.h>
+#include <wlutils.h>
+
+int
+wl_ioctl(char *name, int cmd, void *buf, int len)
+{
+ struct ifreq ifr;
+ wl_ioctl_t ioc;
+ int ret = 0;
+ int s;
+
+ /* open socket to kernel */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return errno;
+ }
+
+ /* do it */
+ ioc.cmd = cmd;
+ ioc.buf = buf;
+ ioc.len = len;
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ ifr.ifr_data = (caddr_t) &ioc;
+ if ((ret = ioctl(s, SIOCDEVPRIVATE, &ifr)) < 0)
+ if (cmd != WLC_GET_MAGIC)
+ perror(ifr.ifr_name);
+
+ /* cleanup */
+ close(s);
+ return ret;
+}
+
+int
+wl_hwaddr(char *name, unsigned char *hwaddr)
+{
+ struct ifreq ifr;
+ int ret = 0;
+ int s;
+
+ /* open socket to kernel */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return errno;
+ }
+
+ /* do it */
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ if ((ret = ioctl(s, SIOCGIFHWADDR, &ifr)) == 0)
+ memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+
+ /* cleanup */
+ close(s);
+ return ret;
+}
+