/*****************************************************************************

 @(#) $Id: test-sdt.c,v 0.7.2.4 2001/02/03 05:49:00 brian Test $

 -----------------------------------------------------------------------------

 Copyright (C) 1997-2001  Brian Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 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., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 Last Modified $Date: 2001/02/03 05:49:00 $ by $Author: brian $

 *****************************************************************************/

static char const ident[] = "$Id: test-sdt.c,v 0.7.2.4 2001/02/03 05:49:00 brian Test $";

/*
 *  This file is for testing the ss7-sdludp driver.
 */

#include <sys/stropts.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/poll.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <ss7/lmi.h>
#include <ss7/lmi_ioctl.h>
#include <ss7/devi.h>
#include <ss7/devi_ioctl.h>
#include <ss7/sdli.h>
#include <ss7/sdli_ioctl.h>
#include <ss7/sdti.h>
#include <ss7/sdti_ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFSIZE 272

typedef struct ppa {
    struct sockaddr_in loc;
    struct sockaddr_in rem;
} ppa_t;

void print_ppa(ppa_t *ppa) {
    printf("PPA loc Address family = %d\n", ppa->loc.sin_family);
    printf("PPA loc Port = %d\n", ppa->loc.sin_port);
    printf("PPA loc Address = %s\n", inet_ntoa(ppa->loc.sin_addr));
    printf("PPA rem Address family = %d\n", ppa->rem.sin_family);
    printf("PPA rem Port = %d\n", ppa->rem.sin_port);
    printf("PPA rem Address = %s\n", inet_ntoa(ppa->rem.sin_addr));
}

const char *lmi_strreason(unsigned int reason)
{
    const char *r;
    switch ( reason ) {
        default:
        case LMI_UNSPEC:         r = "Unknown or unspecified";                  break;
        case LMI_BADADDRESS:     r = "Address was invalid";                     break;
        case LMI_BADADDRTYPE:    r = "Invalid address type";                    break;
        case LMI_BADDIAL:        r = "(not used)";                              break;
        case LMI_BADDIALTYPE:    r = "(not used)";                              break;
        case LMI_BADDISPOSAL:    r = "Invalid disposal parameter";              break;
        case LMI_BADFRAME:       r = "Defective SDU received";                  break;
        case LMI_BADPPA:         r = "Invalid PPA identifier";                  break;
        case LMI_BADPRIM:        r = "Unregognized primitive";                  break;
        case LMI_DISC:           r = "Disconnected";                            break;
        case LMI_EVENT:          r = "Protocol-specific event ocurred";         break;
        case LMI_FATALERR:       r = "Device has become unusable";              break;
        case LMI_INITFAILED:     r = "Link initialization failed";              break;
        case LMI_NOTSUPP:        r = "Primitive not supported by this device";  break;
        case LMI_OUTSTATE:       r = "Primitive was issued from invalid state"; break;
        case LMI_PROTOSHORT:     r = "M_PROTO block too short";                 break;
        case LMI_SYSERR:         r = "UNIX system error";                       break;
        case LMI_WRITEFAIL:      r = "Unitdata request failed";                 break;
        case LMI_CRCERR:         r = "CRC or FCS error";                        break;
        case LMI_DLE_EOT:        r = "DLE EOT detected";                        break;
        case LMI_FORMAT:         r = "Format error detected";                   break;
        case LMI_HDLC_ABORT:     r = "Aborted frame detected";                  break;
        case LMI_OVERRUN:        r = "Input overrun";                           break;
        case LMI_TOOSHORT:       r = "Frame too short";                         break;
        case LMI_INCOMPLETE:     r = "Partial frame received";                  break;
        case LMI_BUSY:           r = "Telephone was busy";                      break;
        case LMI_NOANSWER:       r = "Connection went unanswered";              break;
        case LMI_CALLREJECT:     r = "Connection rejected";                     break;
        case LMI_HDLC_IDLE:      r = "HDLC line went idle";                     break;
        case LMI_HDLC_NOTIDLE:   r = "HDLC link no longer idle";                break;
        case LMI_QUIESCENT:      r = "Line being reassigned";                   break;
        case LMI_RESUMED:        r = "Line has been reassigned";                break;
        case LMI_DSRTIMEOUT:     r = "Did not see DSR in time";                 break;
        case LMI_LAN_COLLISIONS: r = "LAN excessive collisions";                break;
        case LMI_LAN_REFUSED:    r = "LAN message refused";                     break;
        case LMI_LAN_NOSTATION:  r = "LAN no such station";                     break;
        case LMI_LOSTCTS:        r = "Lost Clear to Send signal";               break;
        case LMI_DEVERR:         r = "Start of device-specific error codes";    break;
    }
    return r;
}

void test_lmi_getmsg(int fd) {
    int ret, flags;
    struct strbuf ctrl;
    struct strbuf data;
    char cbuf[BUFSIZE];
    char dbuf[BUFSIZE];
    union LMI_primitives *p = (union LMI_primitives *)cbuf;

    ctrl.maxlen = BUFSIZE;
    ctrl.len    = 0;
    ctrl.buf    = cbuf;

    data.maxlen = BUFSIZE;
    data.len    = 0;
    data.buf    = dbuf;

do_get_again:
    printf("\nAttempting getmsg\n");

    if ( (ret = getmsg(fd, &ctrl, &data, &flags)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        return;
    } else
        printf("Getmsg succeeded!\n");

    if ( ret == 0 ) {
        if ( ctrl.len > 0 ) {
            switch ( p->lmi_primitive ) {
                case LMI_INFO_ACK:
                    {
                        int ppalen = ctrl.len - sizeof(p->info_ack);
                        printf("LMI_INFO_ACK:\n");
                        printf("Version = 0x%08lx\n", p->info_ack.lmi_version);
                        printf("State = %lu\n", p->info_ack.lmi_state);
                        printf("Max sdu = %lu\n", p->info_ack.lmi_max_sdu);
                        printf("Min sdu = %lu\n", p->info_ack.lmi_min_sdu);
                        printf("Header len = %lu\n", p->info_ack.lmi_header_len);
                        printf("PPA style = %lu\n", p->info_ack.lmi_ppa_style);
                        printf("PPA length = %u\n", ppalen);
                        print_ppa((ppa_t *)p->info_ack.lmi_ppa_addr);
                    }
                    return;
                case LMI_OK_ACK:
                    {
                        printf("LMI_OK_ACK:\n");
                        printf("Correct primitive = %lu\n", p->ok_ack.lmi_correct_primitive);
                        printf("State = %lu\n", p->ok_ack.lmi_state);
                    }
                    return;
                case LMI_ERROR_ACK:
                    {
                        printf("LMI_ERROR_ACK:\n");
                        printf("Error number = %lu\n", p->error_ack.lmi_errno);
                        printf("Error string = %s\n", strerror(p->error_ack.lmi_errno));
                        printf("Reason number = %lu\n", p->error_ack.lmi_reason);
                        printf("Reason string = %s\n", lmi_strreason(p->error_ack.lmi_reason));
                        printf("Error primitive = %lu\n", p->error_ack.lmi_error_primitive);
                        printf("State = %lu\n", p->error_ack.lmi_state);
                    }
                    return;
                case LMI_ERROR_IND:
                    {
                        printf("LMI_ERROR_IND:\n");
                        printf("Error number = %lu\n", p->error_ind.lmi_errno);
                        printf("Error string = %s\n", strerror(p->error_ind.lmi_errno));
                        printf("Reason number = %lu\n", p->error_ind.lmi_reason);
                        printf("Reason string = %s\n", lmi_strreason(p->error_ind.lmi_reason));
                        printf("State = %lu\n", p->error_ind.lmi_state);
                    }
                    goto do_get_again;
                case LMI_ENABLE_CON:
                    {
                        printf("LMI_ENABLE_CON:\n");
                        printf("State = %lu\n", p->enable_con.lmi_state);
                    }
                    return;
                case LMI_DISABLE_CON:
                    {
                        printf("LMI_DISABLE_CON:\n");
                        printf("State = %lu\n", p->enable_con.lmi_state);
                    }
                    return;
                default:
                    printf("Unrecognized response primitive %lu!\n", p->lmi_primitive);
                    goto do_get_again;
            }
        }
    }
}

void test_info_req(int fd) {
    int ret;
    struct strbuf ctrl;
    char cbuf[BUFSIZE];
    union LMI_primitives *p = (union LMI_primitives *)cbuf;
    ctrl.maxlen = BUFSIZE;
    ctrl.len    = sizeof(p->info_req);
    ctrl.buf    = cbuf;
    p->lmi_primitive = LMI_INFO_REQ;
    printf("\nAttempting info request\n");
    if ( (ret = putmsg(fd, &ctrl, NULL, RS_HIPRI)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Putmsg succeeded!\n");
    test_lmi_getmsg(fd);
}

void test_attach_req(int fd, ppa_t *ppap) {
    int ret;
    struct strbuf ctrl;
    char cbuf[BUFSIZE];
    union LMI_primitives *p = (union LMI_primitives *)cbuf;

    ctrl.maxlen = BUFSIZE;
    ctrl.len    = sizeof(p->attach_req)+sizeof(ppa_t);
    ctrl.buf    = cbuf;
    p->attach_req.lmi_primitive = LMI_ATTACH_REQ;
    bcopy(ppap, p->attach_req.lmi_ppa, sizeof(ppa_t));
    printf("\nAttempting attach request\n");
    print_ppa((ppa_t *)p->attach_req.lmi_ppa);
    if ( (ret = putmsg(fd, &ctrl, NULL, RS_HIPRI)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Putmsg succeeded!\n");
    test_lmi_getmsg(fd);
}

void test_detach_req(int fd) {
    int ret;
    struct strbuf ctrl;
    char cbuf[BUFSIZE];
    union LMI_primitives *p = (union LMI_primitives *)cbuf;
    ctrl.maxlen = BUFSIZE;
    ctrl.len    = sizeof(p->detach_req);
    ctrl.buf    = cbuf;
    p->lmi_primitive = LMI_DETACH_REQ;
    printf("\nAttempting detach request\n");
    if ( (ret = putmsg(fd, &ctrl, NULL, RS_HIPRI)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Putmsg succeeded!\n");
    test_lmi_getmsg(fd);
}

void test_enable_req(int fd) {
    int ret;
    struct strbuf ctrl;
    char cbuf[BUFSIZE];
    union LMI_primitives *p = (union LMI_primitives *)cbuf;
    ctrl.maxlen = BUFSIZE;
    ctrl.len    = sizeof(p->enable_req);
    ctrl.buf    = cbuf;
    p->lmi_primitive = LMI_ENABLE_REQ;
    printf("\nAttempting enable request\n");
    if ( (ret = putmsg(fd, &ctrl, NULL, RS_HIPRI)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Putmsg succeeded!\n");
    test_lmi_getmsg(fd);
}

void test_disable_req(int fd) {
    int ret;
    struct strbuf ctrl;
    char cbuf[BUFSIZE];
    union LMI_primitives *p = (union LMI_primitives *)cbuf;
    ctrl.maxlen = BUFSIZE;
    ctrl.len    = sizeof(p->disable_req);
    ctrl.buf    = cbuf;
    p->lmi_primitive = LMI_DISABLE_REQ;
    printf("\nAttempting disable request\n");
    if ( (ret = putmsg(fd, &ctrl, NULL, RS_HIPRI)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Putmsg succeeded!\n");
    test_lmi_getmsg(fd);
}

int test_open(void) {
    int fd;
    printf("\nAttempting to open ss7-sdludp0\n");
    if ( (fd = open("/dev/ss7-sdludp0",O_NONBLOCK|O_RDWR)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Open succeeded!\n");
    return(fd);
}

void test_push(int fd) {
    int ret;
    printf("\nAttempting to push streams-sdt\n");
    if ( (ret = ioctl(fd, I_PUSH, "sdt")) < 0 ) {
        printf("return = %d\n", ret);
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Push succeeded!\n");
}

void test_close(int fd) {
    int ret;
    printf("\nAttempting to close ss7-sdludp0\n");
    if ( (ret = close(fd)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Close succeeded!\n");
}

void test_write(int fd) {
    const char buf[] = "Hello World!";
    int i;
    int ret;
    for ( i=0; i<10; i++ ) {
        printf("\nAttempting write\n");
        ret = write(fd, buf, 13);
        if ( ret < 0 ) {
            printf("error = %d\n", errno);
            perror(__FUNCTION__);
            exit(2);
        } else
            printf("Write succeeded, wrote %d bytes!\n", ret);
    }
}

void test_daedt_start(int fd) {
    int ret;
    struct strbuf ctrl;
    char cbuf[BUFSIZE];
    union SDT_primitives *p = (union SDT_primitives *)cbuf;
    ctrl.maxlen = BUFSIZE;
    ctrl.len    = sizeof(p->daedt_start_req);
    ctrl.buf    = cbuf;
    p->sdt_primitive = SDT_DAEDT_START_REQ;
    printf("\nAttempting DAEDT start request\n");
    if ( (ret = putmsg(fd, &ctrl, NULL, RS_HIPRI)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Putmsg succeeded!\n");
    test_lmi_getmsg(fd);
}

void test_daedr_start(int fd) {
    int ret;
    struct strbuf ctrl;
    char cbuf[BUFSIZE];
    union SDT_primitives *p = (union SDT_primitives *)cbuf;
    ctrl.maxlen = BUFSIZE;
    ctrl.len    = sizeof(p->daedr_start_req);
    ctrl.buf    = cbuf;
    p->sdt_primitive = SDT_DAEDR_START_REQ;
    printf("\nAttempting DAEDR start request\n");
    if ( (ret = putmsg(fd, &ctrl, NULL, RS_HIPRI)) < 0 ) {
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    } else
        printf("Putmsg succeeded!\n");
    test_lmi_getmsg(fd);
}

void test_read(int fd) {
    char buf[256];
    int i, ret;
    i = 0;
    printf("\nAttempting read\n");
    for (;;) {
        ret = read(fd, buf, 256);
        if ( ret < 0 ) {
            if ( errno == EAGAIN ) {
                if ( i++ < 100 )
                    continue;
            }
            printf("error = %d\n", errno);
            perror(__FUNCTION__);
            break;
        } else
            printf("Read succeeded, read %d bytes!\n", ret);
        if ( ret > 0 ) {
            int i;
            char *c = (char *)buf;
            printf("Message[%d]: ", ret);
            for ( i=0; i<ret; i++, c++ )
                printf("%c", *c);
            printf("\n");
        }
    }
}

int test_ioctl(int fd, int cmd, void *arg, int len) {
    struct strioctl ctl = { cmd, 0, len, arg };
    int ret;
    ret = ioctl(fd, I_STR, &ctl);
    if ( ret < 0 ) {
        printf("return = %d\n", ret);
        printf("error = %d\n", errno);
        perror(__FUNCTION__);
        exit(2);
    }
    return ctl.ic_len;
}

void test_ioctls(int fd) {
    unsigned char buf[256];
    sdl_config_t *c = (sdl_config_t *)buf;
    sdl_statem_t *s = (sdl_statem_t *)buf;
    printf("\nAttempting ioctls\n");
    test_ioctl(fd, SDL_IOCGCONFIG, buf, sizeof(sdl_config_t));
    printf("Config:\n");
    printf("  N = %lu\n", c->N);
    printf("  m = %lu\n", c->m);
    test_ioctl(fd, SDL_IOCGSTATEM, buf, sizeof(sdl_statem_t));
    printf("State:\n");
    printf("  daedt_state = %lu\n", s->daedt_state);
    printf("  daedr_state = %lu\n", s->daedr_state);
    printf("  octet_counting_mode = %lu\n", s->octet_counting_mode);
    printf("Iface:\n");
    test_ioctl(fd, DEV_IOCGIFFLAGS, buf, sizeof(unsigned long));
    printf("  ifflags = %lu\n", *((unsigned long *)buf));
    test_ioctl(fd, DEV_IOCGIFTYPE, buf, sizeof(unsigned long));
    printf("  iftype = %lu\n", *((unsigned long *)buf));
    test_ioctl(fd, DEV_IOCGGRPTYPE, buf, sizeof(unsigned long));
    printf("  ifgtype = %lu\n", *((unsigned long *)buf));
    test_ioctl(fd, DEV_IOCGIFMODE, buf, sizeof(unsigned long));
    printf("  ifmode = %lu\n", *((unsigned long *)buf));
    test_ioctl(fd, DEV_IOCGIFRATE, buf, sizeof(unsigned long));
    printf("  ifrate = %lu\n", *((unsigned long *)buf));
    test_ioctl(fd, DEV_IOCGIFCLOCK, buf, sizeof(unsigned long));
    printf("  ifclock = %lu\n", *((unsigned long *)buf));
    test_ioctl(fd, DEV_IOCGIFCODING, buf, sizeof(unsigned long));
    printf("  ifcoding = %lu\n", *((unsigned long *)buf));
    test_ioctl(fd, DEV_IOCGIFLEADS, buf, sizeof(unsigned long));
    printf("  ifleads = %lu\n", *((unsigned long *)buf));
}

int main () {
    int fd1, fd2;
    ppa_t ppa1, ppa2;
    printf("Simple test program for ss7-sdludp driver.\n");

    ppa1.loc.sin_family = AF_INET;
    ppa1.loc.sin_port = 10000;
    ppa1.loc.sin_addr.s_addr = INADDR_ANY;
    ppa1.rem.sin_family = AF_INET;
    ppa1.rem.sin_port = 10001;
    inet_aton("192.168.0.5",&ppa1.rem.sin_addr);

    ppa2.loc.sin_family = AF_INET;
    ppa2.loc.sin_port = 10001;
    ppa2.loc.sin_addr.s_addr = INADDR_ANY;
    ppa2.rem.sin_family = AF_INET;
    ppa2.rem.sin_port = 10000;
    inet_aton("192.168.0.5",&ppa2.rem.sin_addr);

    fd1 = test_open();
    fd2 = test_open();

    ioctl(fd1, I_SRDOPT, RMSGD);
    ioctl(fd2, I_SRDOPT, RMSGD);

    test_push(fd1);
    test_push(fd2);

    test_info_req(fd1);
    test_attach_req(fd1, &ppa1);
    test_info_req(fd1);
    test_enable_req(fd1);
    test_info_req(fd1);

    test_info_req(fd2);
    test_attach_req(fd2, &ppa2);
    test_info_req(fd2);
    test_enable_req(fd2);
    test_info_req(fd2);

    test_ioctls(fd1);

    test_daedt_start(fd1);
    test_daedr_start(fd1);
    test_daedt_start(fd2);
    test_daedr_start(fd2);

    test_ioctls(fd2);

    test_write(fd1);

//  test_lmi_getmsg(fd1);
//  test_lmi_getmsg(fd2);
    
    test_read(fd2);

    test_disable_req(fd1);
    test_info_req(fd1);
    test_detach_req(fd1);
    test_info_req(fd1);

    test_disable_req(fd2);
    test_info_req(fd2);
    test_detach_req(fd2);
    test_info_req(fd2);

    test_close(fd1);
    test_close(fd2);

    printf("Done.\n");

    return(0);
}
