mbed Phone Platform

Dependencies:   ulaw mbed ConfigFile

IpLine.cpp

Committer:
okini3939
Date:
2011-01-21
Revision:
6:bd62b12de751
Parent:
5:30e2847d241b

File content as of revision 6:bd62b12de751:

/** @file IpLine.cpp
 * @brief IP Line Controller (mbed Phone Platform)
 */

#include "IpLine.h"

#define NET_RETRY (FREQ)
#define NET_TIMEOUT (FREQ * 3)
#define PACKET_IDENT 0x6465626d // "mbed"

IpLine::IpLine (EthernetNetIf *p_eth,  AnalogOut p_dac, AnalogIn p_adc) : dac(p_dac), adc(p_adc), dial(DIAL_SIZE), dabuf(DATA_SIZE * 4), adbuf(DATA_SIZE * 2)
, led_y(p25), led_g(p26), eth_link(P1_25), eth_speed(P1_26) {

    mode = ModeOff;
    status = StatusNone;
    timeout = 0;
    packet_num = 1;
    dataskip = 0;
    timerled = 0;

    eth = p_eth;
    led_g = eth_link ? 0 : 1;

    udpsock = new UDPSocket;
    udpsock->setOnEvent(this, &IpLine::onLisnerEvent);
    udpsock->bind(Host(eth->getIp(), UDPPORT));
}

/// 8KHz interrupt
void IpLine::intr () {
    char c;

    if (mode == ModeTalk && hook == HookOn) {
        // dac

        if (dabuf.available() < DATA_SIZE) {
            // down sample
            dabuf.get(c);
            dac.write_u16(ulaw2pcm(c));
            dabuf.get(c);
        } else
        if (dabuf.use() < DATA_SIZE) {
            // over sample
            if (dataskip) {
                dataskip = 0;
            } else {
                if (! dabuf.get(c)) {
                    dac.write_u16(ulaw2pcm(c));
                } else {
                    //dac.write_u16(gaussian());
                }
                dataskip = 1;
            }
        } else {
            // normal
            dabuf.get(c);
            dac.write_u16(ulaw2pcm(c));
        }

        // adc
        if (wait) {
            wait --;
        } else {
            adbuf.put(pcm2ulaw(adc.read_u16()));
//            adbuf.put(adc.read_u16() >> 8);
        }
    }

    if (timeout > 1) timeout --;
    if (timerled > 0) timerled --;
}

/// network
void IpLine::poll () {

    Net::poll();

    led_g = eth_link ? 0 : 1;
    if (! timerled) led_y = 0;

    if (mode == ModeTalk && adbuf.use() >= DATA_SIZE) {
        // send
        struct ipline_packet packet;

        packet.len = adbuf.get(packet.data, DATA_SIZE);
        if (packet.len > 0) {
            packet.header.num = 0;
            packet.header.target = remotetarget;
            packet.header.mode = ModeData;
            packet.header.status = StatusNone;
            send(&packet);
        }
    }

    if (timeout == 1) {
        if (last.target != PhoneNone) {
            // re-try
            send(&last);
            timeout = NET_TIMEOUT;
            last.target = PhoneNone;
        } else {
            // timeout
            status = StatusNg;
        }
        timeout = 0;
    }
}

/// recv packet
void IpLine::onLisnerEvent (UDPSocketEvent e) {
    int len;
    struct ipline_packet packet;
    Host recv;

    if (e != UDPSOCKET_READABLE) return;

    // get data
    len = udpsock->recvfrom((char *)&packet, sizeof(struct ipline_packet), &recv);
    led_y = 1;
    timerled = FREQ / 10;

    if (packet.header.ident != PACKET_IDENT) return;

    if (packet.header.status != StatusNone) {
        // recv ack
        if (packet.header.num == last.num) {
            status = packet.header.status;
            timeout = 0;
        }

    } else
    if (packet.header.mode == ModeData && len > sizeof(struct ipline_header)) {
        // data

        if (packet.len <= len - sizeof(struct ipline_header) - 2) {
            dabuf.put(packet.data, packet.len);
        }

    } else
    if (len == sizeof(struct ipline_header)) {
        // enter
        packet.header.status = StatusNg;

        switch (packet.header.mode) {
        case ModeRing:
            // ring --> onhook, dial
            if (mode == ModeReady) {
                dial.put(11);
                dial.put(packet.header.target);
                dial.put(12);
                hook = HookOn;
                remote = recv;
                packet.header.status = StatusOk;
            }
            break;
            
        case ModeBT:
        case ModeDisconnect:
            if (recv.getIp() == remote.getIp()) {
                hook = HookOff;
                packet.header.status = StatusOk;
            }
            break;
            
        case ModeTalk:
            if (recv.getIp() == remote.getIp()) {
                hook = HookOn;
                packet.header.status = StatusOk;
            }
            break;
        }

        // send ack
        send(&packet.header);

      for (int i = 0; i < len; i ++) {
        printf(" %02x", ((char *)&packet)[i]);
      }
      printf("\r\n");

    }
}


/// send packet (header only)
void IpLine::send (struct ipline_header *header) {

    led_y = 1;
    timerled = FREQ / 10;
    header->ident = PACKET_IDENT;
    if (! header->num) {
        header->num = packet_num;
        packet_num ++;
        if (packet_num >= 65536) packet_num = 1;
    }
    udpsock->sendto((char *)header, sizeof(struct ipline_header), &remote);
    memcpy(&last, header, sizeof(struct ipline_header));
}

/// send packet
void IpLine::send (struct ipline_packet *packet) {

    led_y = 1;
    timerled = FREQ / 10;
    packet->header.ident = PACKET_IDENT;
    if (! packet->header.num) {
        packet->header.num = packet_num;
        packet_num ++;
        if (packet_num >= 65536) packet_num = 1;
    }
    udpsock->sendto((char *)packet, sizeof(struct ipline_packet), &remote);
}

/// change mode
int IpLine::enter (enum Mode newmode) {
    struct ipline_header header;

    if (eth_link) return -1; // link down

    mode = newmode;
    status = StatusOk;

    switch (mode) {
    case ModeReady:
        hook = HookOff;
        adbuf.clear();
        dabuf.clear();
        wait = FREQ / 2;
        break;

    case ModeBT:
    case ModeDisconnect:
        hook = HookOff;
    case ModeRing:
    case ModeTalk:
        status = StatusNone;
        header.num = 0;
        header.target = remotetarget;
        header.mode = mode;
        header.status = status;
        send(&header);
        timeout = NET_RETRY;
        break;
    }

    return 0;
}

/// return status
int IpLine::scan (enum Scan type) {

    switch (type) {
    case ScanMode:
        return mode;

    case ScanStatus:
        return status;

    case ScanHook:
        return hook;

    case ScanDial:
        char c;
        if (dial.get(c) == 0)
            return c;
        break;

    }

    return -1;
}

/// set target
void IpLine::settarget (enum PhoneType target, char *host) {
    int ip0, ip1, ip2, ip3;

    remotetarget = target;
    if (host[0] >= '0' && host[0] <= '9') {
        sscanf(host, "%d.%d.%d.%d", &ip0, &ip1, &ip2, &ip3);
        remote.setIp(IpAddr(ip0, ip1, ip2, ip3));
        remote.setName(NULL);
    } else {
        remote.setIp(NULL);
        remote.setName(host);
    }
    remote.setPort(UDPPORT);
}

unsigned long IpLine::xor128 () {
    static unsigned long x = 123456789;
    static unsigned long y = 362436069, z = 521288629, w = 88675123;
    unsigned long t; 
    t = (x ^ (x << 11));
    x = y; y = z; z = w;
    return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)) ); 
}

int IpLine::gaussian () {
    int i, j;

    j = 0;
    for (i = 0; i < 12; i ++) {
       j = j + (xor128() >> 16);
    }
    j = (j - 0x60000) & 0xffff;
    return 0x3fff + (j >> 2);
}