summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-11-18 14:09:31 +0100
committerHarald Welte <laforge@gnumonks.org>2010-11-18 14:11:03 +0100
commit3c29506f17ff4a30a3d988361c67d2ec1af13eeb (patch)
treec1a32996605fac63a1b990edada86bbe9cae45cc /firmware
parent815283e30574fb25cfdd105970387bbd9ae10394 (diff)
simtrace: add TC based waiting time counter, signal ATR / expiry via USB
Diffstat (limited to 'firmware')
-rw-r--r--firmware/include/simtrace_usb.h24
-rw-r--r--firmware/src/simtrace.h2
-rw-r--r--firmware/src/simtrace/iso7816_uart.c226
-rw-r--r--firmware/src/simtrace/iso7816_uart.h6
-rw-r--r--firmware/src/simtrace/main_simtrace.c10
-rw-r--r--firmware/src/simtrace/tc_etu.c124
-rw-r--r--firmware/src/simtrace/tc_etu.h4
7 files changed, 316 insertions, 80 deletions
diff --git a/firmware/include/simtrace_usb.h b/firmware/include/simtrace_usb.h
new file mode 100644
index 0000000..c020093
--- /dev/null
+++ b/firmware/include/simtrace_usb.h
@@ -0,0 +1,24 @@
+#ifndef SIMTRACE_USB_H
+#define SIMTRACE_USB_H
+
+//#include <stdint.h>
+
+/* this is kept compatible with OpenPCD protocol */
+struct simtrace_hdr {
+ u_int8_t cmd;
+ u_int8_t flags;
+ u_int8_t res[2];
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+enum simtrace_usb_msgt {
+ SIMTRACE_MSGT_NULL,
+ SIMTRACE_MSGT_DATA,
+ SIMTRACE_MSGT_RESET, /* reset was asserted, no more data */
+};
+
+/* flags for MSGT_DATA */
+#define SIMTRACE_FLAG_ATR 0x01 /* ATR immediately after reset */
+#define SIMTRACE_FLAG_WTIME_EXP 0x04 /* work waiting time expired */
+
+#endif /* SIMTRACE_USB_H */
diff --git a/firmware/src/simtrace.h b/firmware/src/simtrace.h
index 5a27c31..e787dff 100644
--- a/firmware/src/simtrace.h
+++ b/firmware/src/simtrace.h
@@ -14,5 +14,3 @@
#define SIMTRACE_PIO_CLK AT91C_PA2_SCK0
#define SIMTRACE_PIO_IO AT91C_PA6_TXD0
#define SIMTRACE_PIO_nRST AT91C_PIO_PA7
-
-
diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c
index 6ed797c..18335f6 100644
--- a/firmware/src/simtrace/iso7816_uart.c
+++ b/firmware/src/simtrace/iso7816_uart.c
@@ -1,5 +1,20 @@
-/* Driver for AT91SAM7 USART0 in ISO7816-3 mode
+/* Driver for AT91SAM7 USART0 in ISO7816-3 mode for passive sniffing
* (C) 2010 by Harald Welte <hwelte@hmw-consulting.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
+ * (at your option) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
*/
#include <errno.h>
@@ -9,6 +24,8 @@
#include <lib_AT91SAM7.h>
#include <openpcd.h>
+#include <simtrace_usb.h>
+
#include <os/usb_handler.h>
#include <os/dbgu.h>
#include <os/pio_irq.h>
@@ -23,11 +40,12 @@ static const AT91PS_USART usart = AT91C_BASE_US0;
enum iso7816_3_state {
ISO7816_S_RESET, /* in Reset */
ISO7816_S_WAIT_ATR, /* waiting for ATR to start */
- ISO7816_S_IN_ATR,
- ISO7816_S_WAIT_READER, /* waiting for data from reader */
- ISO7816_S_WAIT_CARD, /* waiting for data from card */
+ ISO7816_S_IN_ATR, /* while we are receiving the ATR */
+ ISO7816_S_WAIT_APDU, /* waiting for start of new APDU */
+ ISO7816_S_IN_APDU, /* inside a single APDU */
};
+/* detailed sub-states of ISO7816_S_IN_ATR */
enum atr_state {
ATR_S_WAIT_TS,
ATR_S_WAIT_T0,
@@ -45,6 +63,8 @@ struct iso7816_3_handle {
u_int8_t fi;
u_int8_t di;
+ u_int8_t wi;
+ u_int32_t waiting_time;
u_int8_t atr_idx;
u_int8_t atr_hist_len;
@@ -52,8 +72,9 @@ struct iso7816_3_handle {
enum atr_state atr_state;
u_int8_t atr[64];
- u_int16_t apdu_len;
- u_int16_t apdu_idx;
+ struct simtrace_hdr sh;
+
+ struct req_ctx *rctx;
};
struct iso7816_3_handle isoh;
@@ -71,6 +92,7 @@ static const u_int8_t di_table[] = {
0, 0, 2, 4, 8, 16, 32, 64,
};
+/* compute the F/D ratio based on Fi and Di values */
static int compute_fidi_ratio(u_int8_t fi, u_int8_t di)
{
u_int16_t f, d;
@@ -96,6 +118,43 @@ static int compute_fidi_ratio(u_int8_t fi, u_int8_t di)
return ret;
}
+static void refill_rctx(struct iso7816_3_handle *ih)
+{
+ struct req_ctx *rctx;
+
+ rctx = req_ctx_find_get(0, RCTX_STATE_FREE,
+ RCTX_STATE_LIBRFID_BUSY);
+ if (!rctx) {
+ ih->rctx = NULL;
+ return;
+ }
+
+ ih->sh.cmd = SIMTRACE_MSGT_DATA;
+
+ /* reserve spece at start of rctx */
+ rctx->tot_len = sizeof(struct simtrace_hdr);
+
+ ih->rctx = rctx;
+}
+
+static void send_rctx(struct iso7816_3_handle *ih)
+{
+ struct req_ctx *rctx = ih->rctx;
+
+ if (!rctx)
+ return;
+
+ /* copy the simtrace header */
+ memcpy(rctx->data, &ih->sh, sizeof(ih->sh));
+
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
+
+ memset(&ih->sh, 0, sizeof(ih->sh));
+ ih->rctx = NULL;
+}
+
+
+/* Update the ATR sub-state */
static void set_atr_state(struct iso7816_3_handle *ih, enum atr_state new_atrs)
{
if (new_atrs == ATR_S_WAIT_TS) {
@@ -110,30 +169,48 @@ static void set_atr_state(struct iso7816_3_handle *ih, enum atr_state new_atrs)
ih->atr_state = new_atrs;
}
+#define ISO7816_3_INIT_WTIME 9600
+#define ISO7816_3_DEFAULT_WI 10
+
+static void update_fidi(struct iso7816_3_handle *ih)
+{
+ int rc;
+
+ rc = compute_fidi_ratio(ih->fi, ih->di);
+ if (rc > 0 && rc < 0x400) {
+ DEBUGPCR("computed Fi(%u) Di(%u) ratio: %d", ih->fi, ih->di, rc);
+ /* make sure UART uses new F/D ratio */
+ usart->US_CR |= AT91C_US_RXDIS | AT91C_US_RSTRX;
+ usart->US_FIDI = rc & 0x3ff;
+ usart->US_CR |= AT91C_US_RXEN | AT91C_US_STTTO;
+ /* notify ETU timer about this */
+ tc_etu_set_etu(rc);
+ } else
+ DEBUGPCRF("computed FiDi ratio %d unsupported", rc);
+}
+
+/* Update the ISO 7816-3 APDU receiver state */
static void set_state(struct iso7816_3_handle *ih, enum iso7816_3_state new_state)
{
if (new_state == ISO7816_S_RESET) {
usart->US_CR |= AT91C_US_RXDIS | AT91C_US_RSTRX;
} else if (new_state == ISO7816_S_WAIT_ATR) {
- int rc;
- /* Initial Fi / Di ratio */
+ /* Reset to initial Fi / Di ratio */
ih->fi = 1;
ih->di = 1;
- rc = compute_fidi_ratio(ih->fi, ih->di);
- DEBUGPCRF("computed Fi(%u) Di(%u) ratio: %d", ih->fi, ih->di, rc);
- usart->US_CR |= AT91C_US_RXDIS | AT91C_US_RSTRX;
- usart->US_FIDI = rc & 0x3ff;
- usart->US_CR |= AT91C_US_RXEN;
+ update_fidi(ih);
+ /* initialize todefault WI, this will be overwritten if we
+ * receive TC2, and it will be programmed into hardware after
+ * ATR is finished */
+ ih->wi = ISO7816_3_DEFAULT_WI;
+ /* update waiting time to initial waiting time */
+ ih->waiting_time = ISO7816_3_INIT_WTIME;
+ tc_etu_set_wtime(ih->waiting_time);
+ /* Set ATR sub-state to initial state */
set_atr_state(ih, ATR_S_WAIT_TS);
- } else if (new_state == ISO7816_S_WAIT_READER) {
- /* CLA INS P1 P2 LEN */
- ih->apdu_len = 5;
- ih->apdu_idx = 0;
- } else if (new_state == ISO7816_S_WAIT_CARD) {
- /* 8.2.2 procedure bytes sent by the card */
- /* FIXME: NULL byte and similar oddities */
- ih->apdu_len += 2;
- }
+ /* Notice that we are just coming out of reset */
+ ih->sh.flags |= SIMTRACE_FLAG_ATR;
+ }
if (ih->state == new_state)
return;
@@ -151,6 +228,10 @@ static enum atr_state next_intb_state(struct iso7816_3_handle *ih, u_int8_t ch)
ih->atr_last_td = ch;
goto from_td;
case ATR_S_WAIT_TC:
+ if ((ih->atr_last_td & 0x0f) == 0x02) {
+ /* TC2 contains WI */
+ ih->wi = ch;
+ }
goto from_tc;
case ATR_S_WAIT_TB:
goto from_tb;
@@ -204,7 +285,9 @@ process_byte_atr(struct iso7816_3_handle *ih, u_int8_t byte)
set_atr_state(ih, ATR_S_WAIT_T0);
break;
case ATR_S_WAIT_T0:
+ /* obtain the number of historical bytes */
ih->atr_hist_len = byte & 0xf;
+ /* Mask out the hist-byte-length to indiicate T=0 */
set_atr_state(ih, next_intb_state(ih, byte & 0xf0));
break;
case ATR_S_WAIT_TA:
@@ -215,62 +298,31 @@ process_byte_atr(struct iso7816_3_handle *ih, u_int8_t byte)
break;
case ATR_S_WAIT_HIST:
ih->atr_hist_len--;
+ /* after all historical bytes are recieved, go to TCK */
if (ih->atr_hist_len == 0)
set_atr_state(ih, ATR_S_WAIT_TCK);
break;
case ATR_S_WAIT_TCK:
- /* FIXME: process TCK */
+ /* FIXME: process and verify the TCK */
set_atr_state(ih, ATR_S_DONE);
- /* FIXME: update Fi/Di */
- rc = compute_fidi_ratio(ih->fi, ih->di);
- if (rc > 0 && rc < 0x400) {
- DEBUGPCR("computed FiDi ratio %d", rc);
- /* update baud rate generator in UART */
- usart->US_CR |= AT91C_US_RXDIS| AT91C_US_RSTRX;
- usart->US_FIDI = rc & 0x3ff;
- usart->US_CR |= AT91C_US_RXEN;
- } else
- DEBUGPCRF("computed FiDi ratio %d unsupported", rc);
- return ISO7816_S_WAIT_READER;
+ /* update baud rate generator with Fi/Di */
+ update_fidi(ih);
+ /* update the waiting time */
+ ih->waiting_time = 960 * di_table[ih->di] * ih->wi;
+ tc_etu_set_wtime(ih->waiting_time);
+ return ISO7816_S_WAIT_APDU;
}
return ISO7816_S_IN_ATR;
}
-/* process an incomng byte from the reader */
-static enum iso7816_3_state
-process_byte_reader(struct iso7816_3_handle *ih, u_int8_t byte)
-{
- /* add response length to total number of expected bytes */
- if (ih->apdu_idx == 4)
- ih->apdu_len += byte;
-
- ih->apdu_idx++;
-
- /* once we have received all bytes, transition to card response */
- if (ih->apdu_idx == ih->apdu_len)
- return ISO7816_S_WAIT_CARD;
-
- return ISO7816_S_WAIT_READER;
-}
-
-/* process an incomng byte from the card */
-static enum iso7816_3_state
-process_byte_card(struct iso7816_3_handle *ih, u_int8_t byte)
-{
- ih->apdu_idx++;
-
- /* once we have received all bytes, apdu is finished */
- if (ih->apdu_idx == ih->apdu_len)
- return ISO7816_S_WAIT_READER;
-
- return ISO7816_S_WAIT_CARD;
-}
-
-
-void process_byte(struct iso7816_3_handle *ih, u_int8_t byte)
+static void process_byte(struct iso7816_3_handle *ih, u_int8_t byte)
{
int new_state = -1;
+ struct req_ctx *rctx;
+
+ if (!ih->rctx)
+ refill_rctx(ih);
switch (ih->state) {
case ISO7816_S_RESET:
@@ -279,18 +331,40 @@ void process_byte(struct iso7816_3_handle *ih, u_int8_t byte)
case ISO7816_S_IN_ATR:
new_state = process_byte_atr(ih, byte);
break;
- case ISO7816_S_WAIT_READER:
- new_state = process_byte_reader(ih, byte);
- break;
- case ISO7816_S_WAIT_CARD:
- new_state = process_byte_card(ih, byte);
+ case ISO7816_S_WAIT_APDU:
+ case ISO7816_S_IN_APDU:
+ new_state = ISO7816_S_IN_APDU;
break;
}
+ rctx = ih->rctx;
+ if (!rctx) {
+ DEBUGPCR("==> Lost byte, missing rctx");
+ return;
+ }
+
+ /* store the byte in the USB request context */
+ rctx->data[rctx->tot_len] = byte;
+ rctx->tot_len++;
+
+ if (rctx->tot_len >= rctx->size)
+ send_rctx(ih);
+
if (new_state != -1)
set_state(ih, new_state);
}
+/* timeout of work waiting time during receive */
+void iso7816_wtime_expired(void)
+{
+ /* Always flush the URB at Rx timeout as this indicates end of APDU */
+ if (isoh.rctx) {
+ isoh.sh.flags |= SIMTRACE_FLAG_WTIME_EXP;
+ send_rctx(&isoh);
+ }
+ set_state(&isoh, ISO7816_S_WAIT_APDU);
+}
+
static __ramfunc void usart_irq(void)
{
u_int32_t csr = usart->US_CSR;
@@ -301,7 +375,7 @@ static __ramfunc void usart_irq(void)
if (csr & AT91C_US_RXRDY) {
/* at least one character received */
octet = usart->US_RHR & 0xff;
- DEBUGP("%02x ", octet);
+ //DEBUGP("%02x ", octet);
process_byte(&isoh, octet);
}
@@ -310,7 +384,7 @@ static __ramfunc void usart_irq(void)
}
if (csr & (AT91C_US_PARE|AT91C_US_FRAME|AT91C_US_OVRE)) {
- /* some error has occurrerd */
+ /* FIXME: some error has occurrerd */
}
}
@@ -358,7 +432,6 @@ void iso_uart_rx_mode(void)
usart->US_IER = AT91C_US_RXRDY | AT91C_US_OVRE | AT91C_US_FRAME |
AT91C_US_PARE | AT91C_US_NACK | AT91C_US_ITERATION;
-
/* call interrupt handler once to set initial state RESET / ATR */
reset_pin_irq(SIMTRACE_PIO_nRST);
}
@@ -383,6 +456,8 @@ void iso_uart_init(void)
{
DEBUGPCR("USART Initializing");
+ refill_rctx(&isoh);
+
/* make sure we get clock from the power management controller */
AT91F_US0_CfgPMC();
@@ -395,7 +470,8 @@ void iso_uart_init(void)
AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &usart_irq);
AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_US0);
- usart->US_CR = AT91C_US_RXDIS | AT91C_US_TXDIS | (AT91C_US_RSTRX | AT91C_US_RSTTX);
+ usart->US_CR = AT91C_US_RXDIS | AT91C_US_TXDIS |
+ (AT91C_US_RSTRX | AT91C_US_RSTTX);
/* FIXME: wait for some time */
usart->US_CR = AT91C_US_RXDIS | AT91C_US_TXDIS;
diff --git a/firmware/src/simtrace/iso7816_uart.h b/firmware/src/simtrace/iso7816_uart.h
new file mode 100644
index 0000000..ed1c898
--- /dev/null
+++ b/firmware/src/simtrace/iso7816_uart.h
@@ -0,0 +1,6 @@
+
+void iso_uart_dump(void);
+void iso_uart_rst(unsigned int state);
+void iso_uart_rx_mode(void);
+void iso_uart_clk_master(unsigned int master);
+void iso_uart_init(void);
diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c
index d5a22dd..5e4302e 100644
--- a/firmware/src/simtrace/main_simtrace.c
+++ b/firmware/src/simtrace/main_simtrace.c
@@ -27,22 +27,26 @@
#include <os/usb_handler.h>
#include "../openpcd.h"
#include <os/main.h>
-#include <os/pwm.h>
-#include <os/tc_cdiv.h>
#include <os/pio_irq.h>
-//#include <simtrace/iso7816_uart.h>
+#include <simtrace/tc_etu.h>
+#include <simtrace/iso7816_uart.h>
void _init_func(void)
{
/* low-level hardware initialization */
pio_irq_init();
iso_uart_init();
+ tc_etu_init();
+
+ usbtest_init();
/* high-level protocol */
//opicc_usbapi_init();
led_switch(1, 0);
led_switch(2, 1);
+
+ iso_uart_rx_mode();
}
static void help(void)
diff --git a/firmware/src/simtrace/tc_etu.c b/firmware/src/simtrace/tc_etu.c
new file mode 100644
index 0000000..93d2da0
--- /dev/null
+++ b/firmware/src/simtrace/tc_etu.c
@@ -0,0 +1,124 @@
+/* SimTrace TC (Timer / Clock) support code
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.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
+ * (at your option) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lib_AT91SAM7.h>
+#include <AT91SAM7.h>
+#include <os/dbgu.h>
+
+#include "../openpcd.h"
+
+static AT91PS_TCB tcb;
+static AT91PS_TC tcetu = AT91C_BASE_TC0;
+
+static u_int16_t waiting_time = 9600;
+static u_int16_t clocks_per_etu = 372;
+static u_int16_t wait_events;
+
+static __ramfunc void tc_etu_irq(void)
+{
+ u_int32_t sr = tcetu->TC_SR;
+ static u_int16_t nr_events;
+
+ if (sr & AT91C_TC_ETRGS) {
+ /* external trigger, i.e. we have seen a bit on I/O */
+ //DEBUGPCR("tE");
+ nr_events = 0;
+ /* Make sure we don't accept any additional external trigger */
+ tcetu->TC_CMR &= ~AT91C_TC_ENETRG;
+ }
+
+ if (sr & AT91C_TC_CPCS) {
+ /* Compare C event has occurred, i.e. 1 etu expired */
+ //DEBUGPCR("tC");
+ nr_events++;
+ if (nr_events >= wait_events) {
+ /* enable external triggers again to catch start bit */
+ tcetu->TC_CMR |= AT91C_TC_ENETRG;
+
+ /* disable and re-enable clock to make it stop */
+ tcetu->TC_CCR = AT91C_TC_CLKDIS;
+ tcetu->TC_CCR = AT91C_TC_CLKEN;
+
+ //DEBUGPCR("%u", nr_events);
+
+ /* Indicate that the waiting time has expired */
+ iso7816_wtime_expired();
+ }
+ }
+}
+
+static void recalc_nr_events(void)
+{
+ wait_events = waiting_time/12;
+ /* clocks_per_etu * 12 equals 'sbit + 8 data bits + parity + 2 stop bits */
+ tcetu->TC_RC = clocks_per_etu * 12;
+}
+
+void tc_etu_set_wtime(u_int16_t wtime)
+{
+ waiting_time = wtime;
+ recalc_nr_events();
+}
+
+void tc_etu_set_etu(u_int16_t etu)
+{
+ clocks_per_etu = etu;
+ recalc_nr_events();
+}
+
+void tc_etu_init(void)
+{
+ /* Cfg PA4(TCLK0), PA0(TIOA0), PA1(TIOB0) */
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, 0,
+ AT91C_PA4_TCLK0 | AT91C_PA0_TIOA0 | AT91C_PA1_TIOB0);
+
+ AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC,
+ ((unsigned int) 1 << AT91C_ID_TC0));
+
+ /* Connect TCLK0 to XC0 */
+ tcb->TCB_BMR &= ~(AT91C_TCB_TC0XC0S);
+ tcb->TCB_BMR |= AT91C_TCB_TC0XC0S_TCLK0;
+
+ /* Register Interrupt handler */
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_TC0,
+ OPENPCD_IRQ_PRIO_TC_FDT,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &tc_etu_irq);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_TC0);
+
+ /* enable interrupts for Compare-C and External Trigger */
+ tcetu->TC_IER = AT91C_TC_CPCS | AT91C_TC_ETRGS;
+
+ tcetu->TC_CMR = AT91C_TC_CLKS_XC0 | /* XC0 (TCLK0) clock */
+ AT91C_TC_WAVE | /* Wave Mode */
+ AT91C_TC_ETRGEDG_FALLING |/* Ext trig on falling edge */
+ AT91C_TC_EEVT_TIOB | /* Ext trigger is TIOB0 */
+ AT91C_TC_ENETRG | /* Enable ext. trigger */
+ AT91C_TC_WAVESEL_UP_AUTO |/* Wave mode UP */
+ AT91C_TC_ACPA_SET | /* Set TIOA0 on A compare */
+ AT91C_TC_ACPC_CLEAR | /* Clear TIOA0 on C compare */
+ AT91C_TC_ASWTRG_CLEAR; /* Clear TIOa0 on software trigger */
+
+ tc_etu_set_etu(372);
+
+ /* Enable master clock for TC0 */
+ tcetu->TC_CCR = AT91C_TC_CLKEN;
+
+ /* Reset to start timers */
+ tcb->TCB_BCR = 1;
+}
diff --git a/firmware/src/simtrace/tc_etu.h b/firmware/src/simtrace/tc_etu.h
new file mode 100644
index 0000000..59d9031
--- /dev/null
+++ b/firmware/src/simtrace/tc_etu.h
@@ -0,0 +1,4 @@
+
+void tc_etu_set_wtime(u_int16_t wtime);
+void tc_etu_set_etu(u_int16_t etu);
+void tc_etu_init(void);
personal git repositories of Harald Welte. Your mileage may vary