Index: src/Makefile.main
===================================================================
RCS file: /cvsroot/etherboot/etherboot/etherboot-5.3/src/Makefile.main,v
retrieving revision 1.28
diff -u -p -r1.28 Makefile.main
--- src/Makefile.main	26 Nov 2004 11:34:57 -0000	1.28
+++ src/Makefile.main	11 Dec 2004 17:03:12 -0000
@@ -103,7 +103,7 @@ MAKEROM=	$(PERL) ./util/makerom.pl
 VERSION_MAJOR=	5
 VERSION_MINOR=	3
 VERSION_PATCH=	10
-EXTRAVERSION=	
+EXTRAVERSION=	-RTnet
 VERSION=	$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)$(EXTRAVERSION)
 MM_VERSION=	$(VERSION_MAJOR).$(VERSION_MINOR)
 CFLAGS+=	-DVERSION_MAJOR=$(VERSION_MAJOR) \
@@ -179,6 +179,7 @@ SRCS+=	core/proto_slam.c core/proto_tftm
 SRCS+=	core/isapnp.c
 SRCS+=	core/pcmcia.c core/i82365.c
 SRCS+=	core/pxe_export.c core/dns_resolver.c
+SRCS+=	core/rtnet.c
 
 SRCS+=	$(FILO)/drivers/ide_x.c  
 SRCS+=	$(FILO)/fs/blockdev.c $(FILO)/fs/eltorito.c $(FILO)/fs/fsys_ext2fs.c $(FILO)/fs/fsys_fat.c $(FILO)/fs/fsys_iso9660.c
@@ -201,6 +202,7 @@ BOBJS+=		$(BIN)/pci.o $(BIN)/isa_probe.o
 BOBJS+=		$(BIN)/vsprintf.o $(BIN)/string.o
 BOBJS+=		$(BIN)/pcmcia.o $(BIN)/i82365.o
 BOBJS+=		$(BIN)/pxe_export.o $(BIN)/dns_resolver.o
+BOBJS+=		$(BIN)/rtnet.o
 
 BOBJS+=		$(BIN)/ide_x.o $(BIN)/pci_x.o
 BOBJS+=		$(BIN)/blockdev.o $(BIN)/eltorito.o $(BIN)/fsys_ext2fs.o $(BIN)/fsys_fat.o $(BIN)/fsys_iso9660.o $(BIN)/fsys_reiserfs.o $(BIN)/vfs.o
Index: src/arch/i386/Config
===================================================================
RCS file: /cvsroot/etherboot/etherboot/etherboot-5.3/src/arch/i386/Config,v
retrieving revision 1.8
diff -u -p -r1.8 Config
--- src/arch/i386/Config	27 Nov 2004 00:40:06 -0000	1.8
+++ src/arch/i386/Config	11 Dec 2004 17:03:14 -0000
@@ -80,7 +80,7 @@
 # @/OptionDescription@
 
 # BIOS select don't change unless you know what you are doing
-CFLAGS+=	-DPCBIOS
+CFLAGS+=	-DPCBIOS -DCONFIG_TSC_CURRTICKS
 
 # Compile in k8/hammer support
 #CFLAGS+= -DCONFIG_X86_64
Index: src/arch/i386/core/i386_timer.c
===================================================================
RCS file: /cvsroot/etherboot/etherboot/etherboot-5.3/src/arch/i386/core/i386_timer.c,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 i386_timer.c
--- src/arch/i386/core/i386_timer.c	13 Aug 2003 22:43:15 -0000	1.1.1.1
+++ src/arch/i386/core/i386_timer.c	11 Dec 2004 17:03:15 -0000
@@ -74,19 +74,12 @@ void udelay(unsigned int usecs)
      __asm__ __volatile__ ("rdtsc" : "=A" (val))
 
 
-/* Number of clock ticks to time with the rtc */
-#define LATCH 0xFF
+#define HZ              100
+#define LATCH           ((CLOCK_TICK_RATE + HZ/2) / HZ)
+#define CALIBRATE_LATCH (5 * LATCH)
+#define CALIBRATE_TIME  (5 * 1000020/HZ)
 
-#define LATCHES_PER_SEC ((CLOCK_TICK_RATE + (LATCH/2))/LATCH)
-#define TICKS_PER_LATCH ((LATCHES_PER_SEC + (TICKS_PER_SEC/2))/TICKS_PER_SEC)
-
-static void sleep_latch(void)
-{
-	__load_timer2(LATCH);
-	while(__timer2_running());
-}
-
-/* ------ Calibrate the TSC ------- 
+/* ------ Calibrate the TSC -------
  * Time how long it takes to excute a loop that runs in known time.
  * And find the convertion needed to get to CLOCK_TICK_RATE
  */
@@ -96,9 +89,12 @@ static unsigned long long calibrate_tsc(
 {
 	unsigned long startlow, starthigh;
 	unsigned long endlow, endhigh;
-	
+
+
+	__load_timer2(CALIBRATE_LATCH);
+
 	rdtsc(startlow,starthigh);
-	sleep_latch();
+	while(__timer2_running());
 	rdtsc(endlow,endhigh);
 
 	/* 64-bit subtract - gcc just messes up with long longs */
@@ -107,12 +103,19 @@ static unsigned long long calibrate_tsc(
 		:"=a" (endlow), "=d" (endhigh)
 		:"g" (startlow), "g" (starthigh),
 		"0" (endlow), "1" (endhigh));
-	
+
 	/* Error: ECPUTOOFAST */
 	if (endhigh)
 		goto bad_ctc;
-	
-	endlow *= TICKS_PER_LATCH;
+
+	/* Error: ECPUTOOSLOW */
+	if (endlow <= CALIBRATE_TIME)
+		goto bad_ctc;
+
+	__asm__("divl %2"
+		:"=a" (endlow), "=d" (endhigh)
+		:"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
+
 	return endlow;
 
 	/*
@@ -126,12 +129,40 @@ bad_ctc:
 }
 
 static unsigned long clocks_per_tick;
+static unsigned long cyc2ns_scale;
+
+#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
+
+static inline void set_cyc2ns_scale(unsigned long cpu_mhz)
+{
+	cyc2ns_scale = (1000 << CYC2NS_SCALE_FACTOR)/cpu_mhz;
+}
+
+static inline unsigned long long cycles_2_ns(unsigned long long cyc)
+{
+	return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
+}
+
 void setup_timers(void)
 {
 	if (!clocks_per_tick) {
-		clocks_per_tick = calibrate_tsc();
-		/* Display the CPU Mhz to easily test if the calibration was bad */
-		printf("CPU %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000);
+		unsigned long cpu_khz;
+		unsigned long tsc_quotient = calibrate_tsc();
+
+		/* report CPU clock rate in Hz.
+		 * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
+		 * clock/second. Our precision is about 100 ppm.
+		 */
+		unsigned long eax=0, edx=1000;
+			__asm__("divl %2"
+			:"=a" (cpu_khz), "=d" (edx)
+			:"r" (tsc_quotient),
+			"0" (eax), "1" (edx));
+		printf("Detected %d.%03d MHz processor.\n", cpu_khz / 1000,
+		       cpu_khz % 1000);
+
+		clocks_per_tick = cpu_khz * ((65536 * 1000) / CLOCK_TICK_RATE);
+		set_cyc2ns_scale(cpu_khz/1000);
 	}
 }
 
@@ -188,4 +219,11 @@ int timer2_running(void)
 	return __timer_running();
 }
 
+unsigned long long get_time(void)
+{
+	unsigned long long now;
+	rdtscll(now);
+	return cycles_2_ns(now);
+}
+
 #endif /* RTC_CURRTICKS */
Index: src/core/main.c
===================================================================
RCS file: /cvsroot/etherboot/etherboot/etherboot-5.3/src/core/main.c,v
retrieving revision 1.17
diff -u -p -r1.17 main.c
--- src/core/main.c	17 Nov 2004 02:24:56 -0000	1.17
+++ src/core/main.c	11 Dec 2004 17:03:18 -0000
@@ -23,6 +23,7 @@ Literature dealing with the network prot
 #include "http.h"
 #include "timer.h"
 #include "cpu.h"
+#include "rtnet.h"
 #include <stdarg.h>
 
 #ifdef CONSOLE_BTEXT
@@ -193,7 +194,7 @@ static struct class_operations {
 	int (*load)(struct dev *dev);
 }
 operations[] = {
-	{ &nic.dev,  eth_probe,  eth_load_configuration,  eth_load  },
+	{ &nic.dev,  rteth_probe,  eth_load_configuration,  eth_load  },
 	{ &disk.dev, disk_probe, disk_load_configuration, disk_load },
 	{ &disk.dev, disk_probe, disk_load_configuration, disk_load },
 };
Index: src/include/timer.h
===================================================================
RCS file: /cvsroot/etherboot/etherboot/etherboot-5.3/src/include/timer.h,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 timer.h
--- src/include/timer.h	10 Aug 2003 09:56:11 -0000	1.1.1.1
+++ src/include/timer.h	11 Dec 2004 17:03:23 -0000
@@ -58,5 +58,10 @@ extern void ndelay(unsigned int nsecs);
 extern void udelay(unsigned int usecs);
 extern void mdelay(unsigned int msecs);
 
+#if defined(CONFIG_TSC_CURRTICKS)
+extern unsigned long long get_time(void);
+#define delay_until(timeout) \
+	while (get_time() < timeout)
+#endif /* CONFIG_TSC_CURRTICKS */
 
 #endif	/* TIMER_H */
--- /dev/null	2004-05-20 22:04:03.000000000 +0200
+++ src/core/rtnet.c	2004-12-11 17:46:08.921827504 +0100
@@ -0,0 +1,374 @@
+/**************************************************************************
+Etherboot -  Network Bootstrap Program
+
+Elementary RTnet support (see http://rtnet.sf.net for details)
+	o RTmac framing (revision 2.0)
+	o TDMA slave (revision 2.1, no calibration)
+	o RTcfg client (revision 1.x, stage 1 read-only)
+	o RTcfg stage 1 data encoding of rtnet start script (0.8.0)
+
+Copyright (C) 2004 Jan Kiszka <jan.kiszka@web.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+**************************************************************************/
+#include "etherboot.h"
+#include "rtnet.h"
+#include "nic.h"
+#include "timer.h"
+
+
+#define ETH_RTMAC               0x9021
+#define RTMAC_HLEN              4
+#define RTMAC_VERSION           2
+#define RTMAC_FLAG_TUNNEL       0x01
+
+#define RTMAC_TYPE_TDMA         0x0001
+#define TDMA_FRM_VERSION        0x0201
+#define TDMA_FRM_SYNC           0x0000
+#define TDMA_SYNC_LEN           24
+
+#define ETH_RTCFG               0x9022
+#define RTCFG_ID_STAGE_1_CFG    0
+#define RTCFG_ADDR_MAC          0x00
+#define RTCFG_ADDR_IP           0x01
+#define RTCFG_ADDRSIZE_IP       4
+
+static unsigned long            slot_offset;
+static unsigned long long       next_slot = 0;
+static unsigned int             slot_phasing = 1;
+static unsigned int             slot_period  = 1;
+static int (*nic_poll)P((struct nic *, int));
+static void (*nic_transmit)P((struct nic *, const char *d,
+                              unsigned int t, unsigned int s, const char *p));
+static char shadow_buf[ETH_FRAME_LEN + ETH_DATA_ALIGN] __aligned;
+
+
+static int get_uint(char **p, char *limit)
+{
+	int result = -1;
+	int new_result;
+
+
+	while ((*p < limit) && (**p >= '0') && (**p <= '9')) {
+		if (result == -1)
+			result = 0;
+		new_result = result * 10 + (**p - '0');
+		(*p)++;
+		if (new_result < result)
+			return -1;
+		result = new_result;
+	}
+	return result;
+}
+
+static int get_slot_offset(struct nic *nic)
+{
+	const char   broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	const char   TDMACFG[]   = "$TDMACFG rteth0 slot ";
+	const char   p_param[]   = " -p ";
+	char         *stage1_data;
+	char         *limit;
+	unsigned int len;
+	int          slot;
+	int          phasing;
+	int          period;
+	int          offset;
+	int          slot0_offs    = -1;
+	int          slot0_phasing = 1;
+	int          slot0_period  = 1;
+	int          slot1_offs    = -1;
+	int          slot1_phasing = 1;
+	int          slot1_period  = 1;
+
+
+	printf("Waiting for TDMA configuration...");
+	while (1) {
+		while (!(*nic_poll)(nic, 1))
+			poll_interruptions();
+
+		if ((nic->packetlen < ETH_HLEN+5) ||
+		    (memcmp(nic->packet, broadcast, sizeof(broadcast)) == 0) ||
+		    (*((short *)&nic->packet[12]) != htons(ETH_RTCFG)) ||
+		    (nic->packet[ETH_HLEN] != RTCFG_ID_STAGE_1_CFG))
+			continue;
+
+		stage1_data = &nic->packet[ETH_HLEN+5];
+		len         = ETH_HLEN+5;
+		if (nic->packet[ETH_HLEN+1] == RTCFG_ADDR_IP) {
+			stage1_data += 2*RTCFG_ADDRSIZE_IP;
+			len         += 2*RTCFG_ADDRSIZE_IP;
+		}
+		len += ntohs(*((short *)&nic->packet[len-2]));
+		if (nic->packetlen < len)
+			continue;
+
+		limit = nic->packet + nic->packetlen;
+		while (stage1_data < limit) {
+			if ((stage1_data+sizeof(TDMACFG)-1 > limit) ||
+			    (memcmp(stage1_data, TDMACFG,
+			            sizeof(TDMACFG)-1) != 0)) {
+				stage1_data++;
+				continue;
+			}
+			stage1_data += sizeof(TDMACFG)-1;
+
+			slot = get_uint(&stage1_data, limit);
+			if ((slot < 0) || (stage1_data == limit) ||
+			    (*stage1_data != ' '))
+				continue;
+			stage1_data++;
+
+			offset = get_uint(&stage1_data, limit);
+			if (offset < 0)
+				continue;
+
+			if ((stage1_data+sizeof(p_param)-1 <= limit) &&
+			    (memcmp(stage1_data, p_param,
+			            sizeof(p_param)-1) == 0)) {
+				stage1_data += sizeof(p_param)-1;
+
+				phasing = get_uint(&stage1_data, limit);
+				if ((phasing <= 0) || (stage1_data == limit) ||
+				    (*stage1_data != '/'))
+					continue;
+				stage1_data++;
+
+				period = get_uint(&stage1_data, limit);
+				if (period <= 0)
+					continue;
+			} else {
+				phasing = 1;
+				period  = 1;
+			}
+
+			if (slot == 0) {
+				slot0_offs    = offset;
+				slot0_phasing = phasing;
+				slot0_period  = period;
+			} else if (slot == 1) {
+				slot1_offs = offset;
+				slot1_phasing = phasing;
+				slot1_period  = period;
+			}
+		}
+
+		if (slot1_offs >= 0) {
+			slot_offset  = slot1_offs;
+			slot_phasing = slot1_phasing;
+			slot_period  = slot1_period;
+		} else if (slot0_offs >= 0) {
+			slot_offset  = slot0_offs;
+			slot_phasing = slot0_phasing;
+			slot_period  = slot0_period;
+		} else
+			continue;
+
+		if ((slot_offset < 100) || (slot_offset > 4000000)) {
+			printf("\nUnsupported slot offset (%d us), "
+			       "fix server settings to 100-4000000 us!\n",
+			       slot_offset);
+			return PROBE_FAILED;
+		}
+
+		printf("\nSlot offset: %d us, Phasing/period: %d/%d\n\n",
+		       slot_offset, slot_phasing, slot_period);
+
+		/* convert offset to ns */
+		slot_offset = slot_offset * 1000;
+		/* encode phasing as remainder of cycle / period */
+		slot_phasing--;
+
+		return PROBE_WORKED;
+	}
+}
+
+static int check_tdma_sync(struct nic *nic, unsigned long long recv_stamp)
+{
+	unsigned int       cycle;
+	unsigned long long actual_xmit;
+	unsigned long long sched_xmit;
+
+
+	/* check packet length */
+	if (nic->packetlen < ETH_HLEN+RTMAC_HLEN+TDMA_SYNC_LEN)
+		return 0;
+
+	/* TDMA frame? Non-tunnelled frame? */
+	if (*((short *)&nic->packet[ETH_HLEN]) != htons(RTMAC_TYPE_TDMA))
+		return 0;
+
+	/* Correct TDMA version? Synchronisation frame? */
+	if ((*((short *)&nic->packet[ETH_HLEN+RTMAC_HLEN]) !=
+		htons(TDMA_FRM_VERSION)) ||
+		(*((short *)&nic->packet[ETH_HLEN+RTMAC_HLEN+2]) !=
+		htons(TDMA_FRM_SYNC)))
+		return 0;
+
+	/* Only transmit in assigned phase */
+	cycle = ntohl(*((unsigned int*)
+		&nic->packet[ETH_HLEN+RTMAC_HLEN+4]));
+	if ((cycle % slot_period) != slot_phasing)
+		return 0;
+
+	actual_xmit = (((unsigned long long)
+		ntohl(*((unsigned int *)
+		&nic->packet[ETH_HLEN+RTMAC_HLEN+8]))) << 32) |
+		ntohl(*((unsigned int *)
+		&nic->packet[ETH_HLEN+RTMAC_HLEN+12]));
+	sched_xmit = (((unsigned long long)
+		ntohl(*((unsigned int *)
+		&nic->packet[ETH_HLEN+RTMAC_HLEN+16]))) << 32) |
+		ntohl(*((unsigned int *)
+		&nic->packet[ETH_HLEN+RTMAC_HLEN+20]));
+
+	next_slot = recv_stamp + slot_offset - (actual_xmit - sched_xmit);
+	return 1;
+}
+
+static void wait_on_slot(struct nic *nic)
+{
+	unsigned long long time_stamp;
+	char               *orig_buf;
+
+
+	time_stamp = get_time();
+	if ((next_slot != 0) && (next_slot > time_stamp) &&
+	    ((next_slot - time_stamp) < 4000000000LL)) {
+		delay_until(next_slot);
+		return;
+	}
+
+	/* use different buffer to avoid overwriting */
+	orig_buf    = nic->packet;
+	nic->packet = shadow_buf + ETH_DATA_ALIGN;
+
+	/* clear buffer to get clean time stamps */
+	(*nic_poll)(nic, 1);
+
+	while (1) {
+		do {
+			poll_interruptions();
+			time_stamp = get_time();
+		} while (!(*nic_poll)(nic, 1));
+
+		/* check packet length */
+		if (nic->packetlen < ETH_HLEN+RTMAC_HLEN+TDMA_SYNC_LEN)
+			continue;
+
+		/* RTmac Ethernet frame? */
+		if (*((short *)&nic->packet[12]) != htons(ETH_RTMAC))
+			continue;
+
+		/* Correct RTmac version? Non-tunnelled frame? */
+		if ((nic->packet[ETH_HLEN+2] != RTMAC_VERSION) ||
+		    ((nic->packet[ETH_HLEN+3] & RTMAC_FLAG_TUNNEL) != 0))
+			continue;
+
+		if (!check_tdma_sync(nic, time_stamp))
+			continue;
+
+		/* restore buffer */
+		nic->packet = orig_buf;
+
+		delay_until(next_slot);
+		return;
+	}
+}
+
+static int rtnet_poll(struct nic *nic, int retrieve)
+{
+	unsigned long long recv_stamp = 0;
+
+
+	if (!retrieve)
+		return (*nic_poll)(nic, 0);
+
+	if (!(*nic_poll)(nic, 1)) {
+		do {
+			poll_interruptions();
+			recv_stamp = get_time();
+		} while (!(*nic_poll)(nic, 1));
+	}
+
+	/* check packet header length */
+	if (nic->packetlen < ETH_HLEN + RTMAC_HLEN)
+		return 0;
+
+	/* RTmac Ethernet frame? */
+	if (*((short *)&nic->packet[12]) != htons(ETH_RTMAC))
+		return 0;
+
+	/* Correct RTmac version? */
+	if (nic->packet[ETH_HLEN+2] != RTMAC_VERSION)
+		return 0;
+
+	/* Tunnelled frame? */
+	if ((nic->packet[ETH_HLEN+3] & RTMAC_FLAG_TUNNEL) == 0) {
+		/* no, but check for Sync frame */
+		if (recv_stamp != 0) {
+			check_tdma_sync(nic, recv_stamp);
+		} return 0;
+	}
+
+	/* patch the packet type */
+	*((short *)&nic->packet[12]) =
+	    *((unsigned short *)&nic->packet[ETH_HLEN]);
+
+	/* remove the RTmac header */
+	nic->packetlen -= RTMAC_HLEN;
+	memmove(&nic->packet[ETH_HLEN], &nic->packet[ETH_HLEN+RTMAC_HLEN],
+	        nic->packetlen-ETH_HLEN);
+
+	return 1;
+}
+
+static void rtnet_transmit(struct nic *nic, const char *d, unsigned int t,
+                           unsigned int s, const char *p)
+{
+	char buf[ETH_MAX_MTU];
+
+
+	if (s > ETH_MAX_MTU-RTMAC_HLEN) {
+		printf("Outgoing packet too large - dropping!\n");
+		return;
+	}
+
+	memcpy(&buf[RTMAC_HLEN], p, s);
+	*((unsigned short *)buf) = htons(t);
+	buf[2] = RTMAC_VERSION;
+	buf[3] = RTMAC_FLAG_TUNNEL;
+
+	wait_on_slot(nic);
+	(*nic_transmit)(nic, d, ETH_RTMAC, s+RTMAC_HLEN, buf);
+}
+
+int rteth_probe(struct dev *dev)
+{
+	int        result;
+	struct nic *nic = (struct nic *)dev;
+
+
+	result = eth_probe(dev);
+	if (result == PROBE_FAILED)
+		return result;
+
+	nic_poll      = nic->poll;
+	nic->poll     = rtnet_poll;
+	nic_transmit  = nic->transmit;
+	nic->transmit = rtnet_transmit;
+
+	return get_slot_offset(nic);
+}
--- /dev/null	2004-05-20 22:04:03.000000000 +0200
+++ src/include/rtnet.h	2004-12-11 17:42:01.181489760 +0100
@@ -0,0 +1,14 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#ifndef	RTNET_H
+#define RTNET_H
+
+#include "dev.h"
+
+extern int rteth_probe(struct dev *dev);
+#endif	/* RTNET_H */
