Fix doc.
[bertos.git] / bertos / net / afsk.c
index e516a3f32b854bf23d813ebcc70600afd1eca34d..2b7fe0bedaa5df37b4e18e8f6283cae1f13e2073 100644 (file)
 
 #include <cfg/module.h>
 
+#define LOG_LEVEL   AFSK_LOG_LEVEL
+#define LOG_FORMAT  AFSK_LOG_FORMAT
+#include <cfg/log.h>
+
 #include <cpu/power.h>
+#include <cpu/pgm.h>
 #include <struct/fifobuf.h>
 
 #include <string.h> /* memset */
 
-// Demodulator constants
-#define SAMPLERATE 9600
-#define BITRATE    1200
-
-#define SAMPLEPERBIT (SAMPLERATE / BITRATE)
 #define PHASE_BIT    8
 #define PHASE_INC    1
 
@@ -74,30 +74,13 @@ STATIC_ASSERT(!(CONFIG_AFSK_DAC_SAMPLERATE % BITRATE));
 
 #define DAC_SAMPLEPERBIT (CONFIG_AFSK_DAC_SAMPLERATE / BITRATE)
 
-
-/** Current sample of bit for output data. */
-static uint8_t sample_count;
-
-/** Current character to be modulated */
-static uint8_t curr_out;
-
-/** Mask of current modulated bit */
-static uint8_t tx_bit;
-
-/** True if bit stuff is allowed, false otherwise */
-static bool bit_stuff;
-
-/** Counter for bit stuffing */
-static uint8_t stuff_cnt;
-
 /**
  * Sine table for the first quarter of wave.
  * The rest of the wave is computed from this first quarter.
  * This table is used to generate the modulated data.
  */
-static const uint8_t sin_table[] =
+static const uint8_t PROGMEM sin_table[] =
 {
-       //TODO put in flash!
        128, 129, 131, 132, 134, 135, 137, 138, 140, 142, 143, 145, 146, 148, 149, 151,
        152, 154, 155, 157, 158, 160, 162, 163, 165, 166, 167, 169, 170, 172, 173, 175,
        176, 178, 179, 181, 182, 183, 185, 186, 188, 189, 190, 192, 193, 194, 196, 197,
@@ -112,14 +95,6 @@ static const uint8_t sin_table[] =
 
 STATIC_ASSERT(sizeof(sin_table) == SIN_LEN / 4);
 
-/**
- * DDS phase accumulator for generating modulated data.
- */
-static uint16_t phase_acc;
-
-/** Current phase increment for current modulated bit */
-static uint16_t phase_inc = MARK_INC;
-
 
 /**
  * Given the index, this function computes the correct sine sample
@@ -130,110 +105,116 @@ INLINE uint8_t sin_sample(uint16_t idx)
        ASSERT(idx < SIN_LEN);
        uint16_t new_idx = idx % (SIN_LEN / 2);
        new_idx = (new_idx >= (SIN_LEN / 4)) ? (SIN_LEN / 2 - new_idx - 1) : new_idx;
-       return (idx >= (SIN_LEN / 2)) ? (255 - sin_table[new_idx]) : sin_table[new_idx];
-}
-
-
-static FIFOBuffer delay_fifo;
 
-/**
- * Buffer for delay FIFO.
- * The 1 is added because the FIFO macros need
- * 1 byte more to handle a buffer (SAMPLEPERBIT / 2) bytes long.
- */
-static int8_t delay_buf[SAMPLEPERBIT / 2 + 1];
-
-static FIFOBuffer rx_fifo;
-static uint8_t rx_buf[CONFIG_AFSK_RX_BUFLEN];
-
-static FIFOBuffer tx_fifo;
-static uint8_t tx_buf[CONFIG_AFSK_TX_BUFLEN];
-
-static int16_t iir_x[2];
-static int16_t iir_y[2];
+       #if CPU_HARVARD
+               uint8_t data = pgm_read_char(&sin_table[new_idx]);
+       #else
+               uint8_t data = sin_table[new_idx];
+       #endif
 
-static uint8_t sampled_bits;
-static uint8_t found_bits;
-static uint8_t demod_bits;
-static int8_t curr_phase;
+       return (idx >= (SIN_LEN / 2)) ? (255 - data) : data;
+}
 
-static bool hdlc_rxstart;
-static uint8_t hdlc_currchar;
-static uint8_t hdlc_bit_idx;
 
 #define BIT_DIFFER(bitline1, bitline2) (((bitline1) ^ (bitline2)) & 0x01)
 #define EDGE_FOUND(bitline)            BIT_DIFFER((bitline), (bitline) >> 1)
 
-static uint16_t preamble_len;
-static uint16_t trailer_len;
-
-static void hdlc_parse(bool bit)
+/**
+ * High-Level Data Link Control parsing function.
+ * Parse bitstream in order to find characters.
+ *
+ * \param hdlc HDLC context.
+ * \param bit  current bit to be parsed.
+ * \param fifo FIFO buffer used to push characters.
+ *
+ * \return true if all is ok, false if the fifo is full.
+ */
+static bool hdlc_parse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo)
 {
-       demod_bits <<= 1;
-       demod_bits |= bit ? 1 : 0;
+       bool ret = true;
+
+       hdlc->demod_bits <<= 1;
+       hdlc->demod_bits |= bit ? 1 : 0;
 
        /* HDLC Flag */
-       if (demod_bits == HDLC_FLAG)
+       if (hdlc->demod_bits == HDLC_FLAG)
        {
-               if (!fifo_isfull(&rx_fifo))
+               if (!fifo_isfull(fifo))
                {
-                       fifo_push(&rx_fifo, HDLC_FLAG);
-                       hdlc_rxstart = true;
+                       fifo_push(fifo, HDLC_FLAG);
+                       hdlc->rxstart = true;
                }
                else
-                       hdlc_rxstart = false;
+               {
+                       ret = false;
+                       hdlc->rxstart = false;
+               }
 
-               hdlc_currchar = 0;
-               hdlc_bit_idx = 0;
-               return;
+               hdlc->currchar = 0;
+               hdlc->bit_idx = 0;
+               return ret;
        }
 
        /* Reset */
-       if ((demod_bits & HDLC_RESET) == HDLC_RESET)
+       if ((hdlc->demod_bits & HDLC_RESET) == HDLC_RESET)
        {
-               hdlc_rxstart = false;
-               return;
+               hdlc->rxstart = false;
+               return ret;
        }
 
-       if (!hdlc_rxstart)
-               return;
+       if (!hdlc->rxstart)
+               return ret;
 
        /* Stuffed bit */
-       if ((demod_bits & 0x3f) == 0x3e)
-               return;
+       if ((hdlc->demod_bits & 0x3f) == 0x3e)
+               return ret;
 
-       if (demod_bits & 0x01)
-               hdlc_currchar |= 0x80;
+       if (hdlc->demod_bits & 0x01)
+               hdlc->currchar |= 0x80;
 
-       if (++hdlc_bit_idx >= 8)
+       if (++hdlc->bit_idx >= 8)
        {
-               if ((hdlc_currchar == HDLC_FLAG
-                       || hdlc_currchar == HDLC_RESET
-                       || hdlc_currchar == AX25_ESC))
+               if ((hdlc->currchar == HDLC_FLAG
+                       || hdlc->currchar == HDLC_RESET
+                       || hdlc->currchar == AX25_ESC))
                {
-                       if (!fifo_isfull(&rx_fifo))
-                               fifo_push(&rx_fifo, AX25_ESC);
+                       if (!fifo_isfull(fifo))
+                               fifo_push(fifo, AX25_ESC);
                        else
-                               hdlc_rxstart = false;
+                       {
+                               hdlc->rxstart = false;
+                               ret = false;
+                       }
                }
 
-               if (!fifo_isfull(&rx_fifo))
-                       fifo_push(&rx_fifo, hdlc_currchar);
+               if (!fifo_isfull(fifo))
+                       fifo_push(fifo, hdlc->currchar);
                else
-                       hdlc_rxstart = false;
+               {
+                       hdlc->rxstart = false;
+                       ret = false;
+               }
 
-               hdlc_currchar = 0;
-               hdlc_bit_idx = 0;
-               return;
+               hdlc->currchar = 0;
+               hdlc->bit_idx = 0;
        }
+       else
+               hdlc->currchar >>= 1;
 
-       hdlc_currchar >>= 1;
+       return ret;
 }
 
-DEFINE_AFSK_ADC_ISR()
+
+/**
+ * ADC ISR callback.
+ * This function has to be called by the ADC ISR when a sample of the configured
+ * channel is available.
+ * \param af Afsk context to operate on.
+ * \param curr_sample current sample from the ADC.
+ */
+void afsk_adc_isr(Afsk *af, int8_t curr_sample)
 {
        AFSK_STROBE_ON();
-       int8_t curr_sample = AFSK_READ_ADC();
 
        /*
         * Frequency discriminator and LP IIR filter.
@@ -251,124 +232,134 @@ DEFINE_AFSK_ADC_ISR()
         * through the CONFIG_AFSK_FILTER config variable.
         */
 
-       iir_x[0] = iir_x[1];
+       af->iir_x[0] = af->iir_x[1];
 
        #if (CONFIG_AFSK_FILTER == AFSK_BUTTERWORTH)
-               iir_x[1] = ((int8_t)fifo_pop(&delay_fifo) * curr_sample) >> 2;
-               //iir_x[1] = ((int8_t)fifo_pop(&delay_fifo) * curr_sample) / 6.027339492;
+               af->iir_x[1] = ((int8_t)fifo_pop(&af->delay_fifo) * curr_sample) >> 2;
+               //af->iir_x[1] = ((int8_t)fifo_pop(&af->delay_fifo) * curr_sample) / 6.027339492;
        #elif (CONFIG_AFSK_FILTER == AFSK_CHEBYSHEV)
-               iir_x[1] = ((int8_t)fifo_pop(&delay_fifo) * curr_sample) >> 2;
-               //iir_x[1] = ((int8_t)fifo_pop(&delay_fifo) * curr_sample) / 3.558147322;
+               af->iir_x[1] = ((int8_t)fifo_pop(&af->delay_fifo) * curr_sample) >> 2;
+               //af->iir_x[1] = ((int8_t)fifo_pop(&af->delay_fifo) * curr_sample) / 3.558147322;
        #else
                #error Filter type not found!
        #endif
 
-       iir_y[0] = iir_y[1];
+       af->iir_y[0] = af->iir_y[1];
 
        #if CONFIG_AFSK_FILTER == AFSK_BUTTERWORTH
                /*
-                * This strange sum + shift is an optimization for iir_y[0] * 0.668.
+                * This strange sum + shift is an optimization for af->iir_y[0] * 0.668.
                 * iir * 0.668 ~= (iir * 21) / 32 =
                 * = (iir * 16) / 32 + (iir * 4) / 32 + iir / 32 =
                 * = iir / 2 + iir / 8 + iir / 32 =
                 * = iir >> 1 + iir >> 3 + iir >> 5
                 */
-               iir_y[1] = iir_x[0] + iir_x[1] + (iir_y[0] >> 1) + (iir_y[0] >> 3) + (iir_y[0] >> 5);
-               //iir_y[1] = iir_x[0] + iir_x[1] + iir_y[0] * 0.6681786379;
+               af->iir_y[1] = af->iir_x[0] + af->iir_x[1] + (af->iir_y[0] >> 1) + (af->iir_y[0] >> 3) + (af->iir_y[0] >> 5);
+               //af->iir_y[1] = af->iir_x[0] + af->iir_x[1] + af->iir_y[0] * 0.6681786379;
        #elif CONFIG_AFSK_FILTER == AFSK_CHEBYSHEV
                /*
-                * This should be (iir_y[0] * 0.438) but
-                * (iir_y[0] >> 1) is a faster approximation :-)
+                * This should be (af->iir_y[0] * 0.438) but
+                * (af->iir_y[0] >> 1) is a faster approximation :-)
                 */
-               iir_y[1] = iir_x[0] + iir_x[1] + (iir_y[0] >> 1);
-               //iir_y[1] = iir_x[0] + iir_x[1] + iir_y[0] * 0.4379097269;
+               af->iir_y[1] = af->iir_x[0] + af->iir_x[1] + (af->iir_y[0] >> 1);
+               //af->iir_y[1] = af->iir_x[0] + af->iir_x[1] + af->iir_y[0] * 0.4379097269;
        #endif
 
        /* Save this sampled bit in a delay line */
-       sampled_bits <<= 1;
-       sampled_bits |= (iir_y[1] > 0) ? 1 : 0;
+       af->sampled_bits <<= 1;
+       af->sampled_bits |= (af->iir_y[1] > 0) ? 1 : 0;
 
-       /* Store current ADC sample in the delay_fifo */
-       fifo_push(&delay_fifo, curr_sample);
+       /* Store current ADC sample in the af->delay_fifo */
+       fifo_push(&af->delay_fifo, curr_sample);
 
        /* If there is an edge, adjust phase sampling */
-       if (EDGE_FOUND(sampled_bits))
+       if (EDGE_FOUND(af->sampled_bits))
        {
-               if (curr_phase < PHASE_THRES)
-                       curr_phase += PHASE_INC;
+               if (af->curr_phase < PHASE_THRES)
+                       af->curr_phase += PHASE_INC;
                else
-                       curr_phase -= PHASE_INC;
+                       af->curr_phase -= PHASE_INC;
        }
-       curr_phase += PHASE_BIT;
+       af->curr_phase += PHASE_BIT;
 
        /* sample the bit */
-       if (curr_phase >= PHASE_MAX)
+       if (af->curr_phase >= PHASE_MAX)
        {
-               curr_phase %= PHASE_MAX;
+               af->curr_phase %= PHASE_MAX;
 
                /* Shift 1 position in the shift register of the found bits */
-               found_bits <<= 1;
+               af->found_bits <<= 1;
 
                /*
                 * Determine bit value by reading the last 3 sampled bits.
                 * If the number of ones is two or greater, the bit value is a 1,
                 * otherwise is a 0.
+                * This algorithm presumes that there are 8 samples per bit.
                 */
-               uint8_t bits = sampled_bits & 0x07;
+               STATIC_ASSERT(SAMPLEPERBIT == 8);
+               uint8_t bits = af->sampled_bits & 0x07;
                if (bits == 0x07 // 111, 3 bits set to 1
                 || bits == 0x06 // 110, 2 bits
                 || bits == 0x05 // 101, 2 bits
                 || bits == 0x03 // 011, 2 bits
                )
-                       found_bits |= 1;
+                       af->found_bits |= 1;
 
                /*
                 * NRZI coding: if 2 consecutive bits have the same value
                 * a 1 is received, otherwise it's a 0.
                 */
-               hdlc_parse(!EDGE_FOUND(found_bits));
+               if (!hdlc_parse(&af->hdlc, !EDGE_FOUND(af->found_bits), &af->rx_fifo))
+                       af->status |= AFSK_RXFIFO_OVERRUN;
        }
 
 
        AFSK_STROBE_OFF();
-       AFSK_ADC_IRQ_END();
 }
 
-/* True while modem sends data */
-static volatile bool sending;
-
-static void afsk_txStart(void)
+static void afsk_txStart(Afsk *af)
 {
-       if (!sending)
+       if (!af->sending)
        {
-               phase_inc = MARK_INC;
-               phase_acc = 0;
-               stuff_cnt = 0;
-               sending = true;
-               preamble_len = DIV_ROUND(CONFIG_AFSK_PREAMBLE_LEN * BITRATE, 8000);
-               AFSK_DAC_IRQ_START();
+               af->phase_inc = MARK_INC;
+               af->phase_acc = 0;
+               af->stuff_cnt = 0;
+               af->sending = true;
+               af->preamble_len = DIV_ROUND(CONFIG_AFSK_PREAMBLE_LEN * BITRATE, 8000);
+               AFSK_DAC_IRQ_START(af->dac_ch);
        }
-       ATOMIC(trailer_len  = DIV_ROUND(CONFIG_AFSK_TRAILER_LEN  * BITRATE, 8000));
+       ATOMIC(af->trailer_len  = DIV_ROUND(CONFIG_AFSK_TRAILER_LEN  * BITRATE, 8000));
 }
 
 #define BIT_STUFF_LEN 5
 
 #define SWITCH_TONE(inc)  (((inc) == MARK_INC) ? SPACE_INC : MARK_INC)
 
-DEFINE_AFSK_DAC_ISR()
+/**
+ * DAC ISR callback.
+ * This function has to be called by the DAC ISR when a sample of the configured
+ * channel has been converted out.
+ *
+ * \param af Afsk context to operate on.
+ *
+ * \return The next DAC output sample.
+ */
+uint8_t afsk_dac_isr(Afsk *af)
 {
+       AFSK_STROBE_ON();
+
        /* Check if we are at a start of a sample cycle */
-       if (sample_count == 0)
+       if (af->sample_count == 0)
        {
-               if (tx_bit == 0)
+               if (af->tx_bit == 0)
                {
                        /* We have just finished transimitting a char, get a new one. */
-                       if (fifo_isempty(&tx_fifo) && trailer_len == 0)
+                       if (fifo_isempty(&af->tx_fifo) && af->trailer_len == 0)
                        {
-                               AFSK_DAC_IRQ_STOP();
-                               sending = false;
-                               AFSK_DAC_IRQ_END();
-                               return;
+                               AFSK_DAC_IRQ_STOP(af->dac_ch);
+                               af->sending = false;
+                               AFSK_STROBE_OFF();
+                               return 0;
                        }
                        else
                        {
@@ -376,58 +367,58 @@ DEFINE_AFSK_DAC_ISR()
                                 * If we have just finished sending an unstuffed byte,
                                 * reset bitstuff counter.
                                 */
-                               if (!bit_stuff)
-                                       stuff_cnt = 0;
+                               if (!af->bit_stuff)
+                                       af->stuff_cnt = 0;
 
-                               bit_stuff = true;
+                               af->bit_stuff = true;
 
                                /*
                                 * Handle preamble and trailer
                                 */
-                               if (preamble_len == 0)
+                               if (af->preamble_len == 0)
                                {
-                                       if (fifo_isempty(&tx_fifo))
+                                       if (fifo_isempty(&af->tx_fifo))
                                        {
-                                               trailer_len--;
-                                               curr_out = HDLC_FLAG;
+                                               af->trailer_len--;
+                                               af->curr_out = HDLC_FLAG;
                                        }
                                        else
-                                               curr_out = fifo_pop(&tx_fifo);
+                                               af->curr_out = fifo_pop(&af->tx_fifo);
                                }
                                else
                                {
-                                       preamble_len--;
-                                       curr_out = HDLC_FLAG;
+                                       af->preamble_len--;
+                                       af->curr_out = HDLC_FLAG;
                                }
 
                                /* Handle char escape */
-                               if (curr_out == AX25_ESC)
+                               if (af->curr_out == AX25_ESC)
                                {
-                                       if (fifo_isempty(&tx_fifo))
+                                       if (fifo_isempty(&af->tx_fifo))
                                        {
-                                               AFSK_DAC_IRQ_STOP();
-                                               sending = false;
-                                               AFSK_DAC_IRQ_END();
-                                               return;
+                                               AFSK_DAC_IRQ_STOP(af->dac_ch);
+                                               af->sending = false;
+                                               AFSK_STROBE_OFF();
+                                               return 0;
                                        }
                                        else
-                                               curr_out = fifo_pop(&tx_fifo);
+                                               af->curr_out = fifo_pop(&af->tx_fifo);
                                }
-                               else if (curr_out == HDLC_FLAG || curr_out == HDLC_RESET)
+                               else if (af->curr_out == HDLC_FLAG || af->curr_out == HDLC_RESET)
                                        /* If these chars are not escaped disable bit stuffing */
-                                       bit_stuff = false;
+                                       af->bit_stuff = false;
                        }
                        /* Start with LSB mask */
-                       tx_bit = 0x01;
+                       af->tx_bit = 0x01;
                }
 
                /* check for bit stuffing */
-               if (bit_stuff && stuff_cnt >= BIT_STUFF_LEN)
+               if (af->bit_stuff && af->stuff_cnt >= BIT_STUFF_LEN)
                {
                        /* If there are more than 5 ones in a row insert a 0 */
-                       stuff_cnt = 0;
+                       af->stuff_cnt = 0;
                        /* switch tone */
-                       phase_inc = SWITCH_TONE(phase_inc);
+                       af->phase_inc = SWITCH_TONE(af->phase_inc);
                }
                else
                {
@@ -435,48 +426,49 @@ DEFINE_AFSK_DAC_ISR()
                         * NRZI: if we want to transmit a 1 the modulated frequency will stay
                         * unchanged; with a 0, there will be a change in the tone.
                         */
-                       if (curr_out & tx_bit)
+                       if (af->curr_out & af->tx_bit)
                        {
                                /*
                                 * Transmit a 1:
                                 * - Stay on the previous tone
-                                * - Increace bit stuff count
+                                * - Increase bit stuff counter
                                 */
-                               stuff_cnt++;
+                               af->stuff_cnt++;
                        }
                        else
                        {
                                /*
                                 * Transmit a 0:
-                                * - Reset bit stuff count
+                                * - Reset bit stuff counter
                                 * - Switch tone
                                 */
-                               stuff_cnt = 0;
-                               phase_inc = SWITCH_TONE(phase_inc);
+                               af->stuff_cnt = 0;
+                               af->phase_inc = SWITCH_TONE(af->phase_inc);
                        }
 
                        /* Go to the next bit */
-                       tx_bit <<= 1;
+                       af->tx_bit <<= 1;
                }
-               sample_count = DAC_SAMPLEPERBIT;
+               af->sample_count = DAC_SAMPLEPERBIT;
        }
 
        /* Get new sample and put it out on the DAC */
-       phase_acc += phase_inc;
-       phase_acc %= SIN_LEN;
+       af->phase_acc += af->phase_inc;
+       af->phase_acc %= SIN_LEN;
 
-       AFSK_SET_DAC(sin_sample(phase_acc));
-       sample_count--;
-       AFSK_DAC_IRQ_END();
+       af->sample_count--;
+       AFSK_STROBE_OFF();
+       return sin_sample(af->phase_acc);
 }
 
 
-static size_t afsk_read(UNUSED_ARG(KFile *, fd), void *_buf, size_t size)
+static size_t afsk_read(KFile *fd, void *_buf, size_t size)
 {
+       Afsk *af = AFSK_CAST(fd);
        uint8_t *buf = (uint8_t *)_buf;
 
        #if CONFIG_AFSK_RXTIMEOUT == 0
-       while (size-- && !fifo_isempty_locked(&rx_fifo))
+       while (size-- && !fifo_isempty_locked(&af->rx_fifo))
        #else
        while (size--)
        #endif
@@ -485,7 +477,7 @@ static size_t afsk_read(UNUSED_ARG(KFile *, fd), void *_buf, size_t size)
                ticks_t start = timer_clock();
                #endif
 
-               while (fifo_isempty_locked(&rx_fifo));
+               while (fifo_isempty_locked(&af->rx_fifo));
                {
                        cpu_relax();
                        #if CONFIG_AFSK_RXTIMEOUT != -1
@@ -494,58 +486,87 @@ static size_t afsk_read(UNUSED_ARG(KFile *, fd), void *_buf, size_t size)
                        #endif
                }
 
-               *buf++ = fifo_pop_locked(&rx_fifo);
+               *buf++ = fifo_pop_locked(&af->rx_fifo);
        }
 
        return buf - (uint8_t *)_buf;
 }
 
-static size_t afsk_write(UNUSED_ARG(KFile *, fd), const void *_buf, size_t size)
+static size_t afsk_write(KFile *fd, const void *_buf, size_t size)
 {
+       Afsk *af = AFSK_CAST(fd);
        const uint8_t *buf = (const uint8_t *)_buf;
 
        while (size--)
        {
-               while (fifo_isfull_locked(&tx_fifo))
+               while (fifo_isfull_locked(&af->tx_fifo))
                        cpu_relax();
 
-               fifo_push_locked(&tx_fifo, *buf++);
-               afsk_txStart();
+               fifo_push_locked(&af->tx_fifo, *buf++);
+               afsk_txStart(af);
        }
 
        return buf - (const uint8_t *)_buf;
 }
 
-static int afsk_flush(UNUSED_ARG(KFile *, fd))
+static int afsk_flush(KFile *fd)
 {
-       while (sending)
+       Afsk *af = AFSK_CAST(fd);
+       while (af->sending)
                cpu_relax();
        return 0;
 }
 
+static int afsk_error(KFile *fd)
+{
+       Afsk *af = AFSK_CAST(fd);
+       int err;
+
+       ATOMIC(err = af->status);
+       return err;
+}
+
+static void afsk_clearerr(KFile *fd)
+{
+       Afsk *af = AFSK_CAST(fd);
+       ATOMIC(af->status = 0);
+}
 
-void afsk_init(Afsk *af)
+
+/**
+ * Initialize an AFSK1200 modem.
+ * \param af Afsk context to operate on.
+ * \param adc_ch  ADC channel used by the demodulator.
+ * \param dac_ch  DAC channel used by the modulator.
+ */
+void afsk_init(Afsk *af, int adc_ch, int dac_ch)
 {
        #if CONFIG_AFSK_RXTIMEOUT != -1
        MOD_CHECK(timer);
        #endif
+       memset(af, 0, sizeof(*af));
+       af->adc_ch = adc_ch;
+       af->dac_ch = dac_ch;
 
-       fifo_init(&delay_fifo, (uint8_t *)delay_buf, sizeof(delay_buf));
-       fifo_init(&rx_fifo, rx_buf, sizeof(rx_buf));
+       fifo_init(&af->delay_fifo, (uint8_t *)af->delay_buf, sizeof(af->delay_buf));
+       fifo_init(&af->rx_fifo, af->rx_buf, sizeof(af->rx_buf));
 
        /* Fill sample FIFO with 0 */
        for (int i = 0; i < SAMPLEPERBIT / 2; i++)
-               fifo_push(&delay_fifo, 0);
+               fifo_push(&af->delay_fifo, 0);
 
-       fifo_init(&tx_fifo, tx_buf, sizeof(tx_buf));
+       fifo_init(&af->tx_fifo, af->tx_buf, sizeof(af->tx_buf));
 
-       AFSK_ADC_INIT();
+       AFSK_ADC_INIT(adc_ch, af);
+       AFSK_DAC_INIT(dac_ch, af);
        AFSK_STROBE_INIT();
-       kprintf("MARK_INC %d, SPACE_INC %d\n", MARK_INC, SPACE_INC);
+       LOG_INFO("MARK_INC %d, SPACE_INC %d\n", MARK_INC, SPACE_INC);
 
-       memset(af, 0, sizeof(*af));
        DB(af->fd._type = KFT_AFSK);
        af->fd.write = afsk_write;
        af->fd.read = afsk_read;
        af->fd.flush = afsk_flush;
+       af->fd.error = afsk_error;
+       af->fd.clearerr = afsk_clearerr;
+       af->phase_inc = MARK_INC;
 }