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

 @(#) test-sdludp.c,v SS7AlphaRelease(0.7.8.2) 2001/12/11 15:28:46

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

 Copyright (c) 2001 OpenSS7 Corporation <http://www.openss7.com/>
 Copyright (c) 1997-2000 Brian F. G. Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 Unauthorized distribution or duplication is prohibited.

 This software and related documentation is protected by copyright and
 distributed under licenses restricting its use, copying, distribution and
 decompilation.  No part of this software or related documentation may be
 reproduced in any form by any means without the prior written
 authorization of the copyright holder, and licensors, if any.

 The recipient of this document, by its retention and use, warrants that
 the recipient will protect this information and keep it confidential, and
 will not disclose the information contained in this document without the
 written permission of its owner.

 The author reserves the right to revise this software and documentation
 for any reason, including but not limited to, conformity with standards
 promulgated by various agencies, utilization of advances in the state of
 the technical arts, or the reflection of changes in the design of any
 techniques, or procedures embodied, described, or referred to herein.
 The author is under no obligation to provide any feature listed herein.

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

 U.S. GOVERNMENT RESTRICTED RIGHTS.  If you are licensing this Software on
 behalf of the U.S. Government ("Government"), the following provisions apply
 to you.  If the Software is supplied by the Department of Defense ("DoD"),
 it is classified as "Commercial Computer Software" under paragraph
 252.227-7014 of the DoD Supplement to the Federal Acquisition Regulations
 ("DFARS") (or any successor regulations) and the Government is acquiring
 only the license rights granted herein (the license rights customarily
 provided to non-Government users).  If the Software is supplied to any unit
 or agency of the Government other than DoD, it is classified as "Restricted
 Computer Software" and the Government's rights in the Software are defined
 in paragraph 52.227-19 of the Federal Acquisition Regulations ("FAR") (or
 any success regulations) or, in the cases of NASA, in paragraph 18.52.227-86
 of the NASA Supplement to the FAR (or any successor regulations).

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

 Last Modified 2001/12/11 15:28:46 by <bidulock@openss7.org>

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

#ident "@(#) test-sdludp.c,v SS7AlphaRelease(0.7.8.2) 2001/12/11 15:28:46"

static char const ident[] = "test-sdludp.c,v SS7AlphaRelease(0.7.8.2) 2001/12/11 15:28:46";

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

#include <stropts.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/sdli.h>
#include <ss7/sdli_ioctl.h>
#include <ss7/devi.h>
#include <ss7/devi_ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFSIZE 300

union {
    lmi_long              prim;
    union LMI_primitives  lmi;
    union SDL_primitives  sdl;
    char                  cbuf[BUFSIZE];
} cmd;

char dbuf[BUFSIZE];
struct strbuf ctrl = { BUFSIZE, 0, cmd.cbuf };
struct strbuf data = { BUFSIZE, 0, dbuf };

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 do_lmi_get_msg(int fd) {
    int ret, flags;
do_get_again:
    if ( (ret = getmsg(fd, &ctrl, &data, &flags)) < 0 ) {
        printf("ERROR: getmsg: [%d] %s\n", errno, strerror(errno));
        return;
    }
    if ( ret == 0 ) {
        if ( ctrl.len > 0 ) {
            switch ( cmd.lmi.lmi_primitive ) {
                case LMI_INFO_ACK:
                    {
                        int ppalen = ctrl.len - sizeof(cmd.lmi.info_ack);
                        printf("LMI_INFO_ACK:\n");
                        printf("Version = 0x%08lx\n", cmd.lmi.info_ack.lmi_version);
                        printf("State = %lu\n", cmd.lmi.info_ack.lmi_state);
                        printf("Max sdu = %lu\n", cmd.lmi.info_ack.lmi_max_sdu);
                        printf("Min sdu = %lu\n", cmd.lmi.info_ack.lmi_min_sdu);
                        printf("Header len = %lu\n", cmd.lmi.info_ack.lmi_header_len);
                        printf("PPA style = %lu\n", cmd.lmi.info_ack.lmi_ppa_style);
                        printf("PPA length = %d\n", ppalen);
                        print_ppa((ppa_t *)cmd.lmi.info_ack.lmi_ppa_addr);
                    }
                    return;
                case LMI_OK_ACK:
                    {
                        printf("LMI_OK_ACK:\n");
                        printf("Correct primitive = %ld\n", cmd.lmi.ok_ack.lmi_correct_primitive);
                        printf("State = %ld\n", cmd.lmi.ok_ack.lmi_state);
                    }
                    return;
                case LMI_ERROR_ACK:
                    {
                        printf("LMI_ERROR_ACK:\n");
                        printf("Error number = %lu\n", cmd.lmi.error_ack.lmi_errno);
                        printf("Error string = %s\n", strerror(cmd.lmi.error_ack.lmi_errno));
                        printf("Reason number = %lu\n", cmd.lmi.error_ack.lmi_reason);
                        printf("Reason string = %s\n", lmi_strreason(cmd.lmi.error_ack.lmi_reason));
                        printf("Error primitive = %lu\n", cmd.lmi.error_ack.lmi_error_primitive);
                        printf("State = %lu\n", cmd.lmi.error_ack.lmi_state);
                    }
                    return;
                case LMI_ERROR_IND:
                    {
                        printf("LMI_ERROR_IND:\n");
                        printf("Error number = %lu\n", cmd.lmi.error_ind.lmi_errno);
                        printf("Error string = %s\n", strerror(cmd.lmi.error_ind.lmi_errno));
                        printf("Reason number = %lu\n", cmd.lmi.error_ind.lmi_reason);
                        printf("Reason string = %s\n", lmi_strreason(cmd.lmi.error_ind.lmi_reason));
                        printf("State = %lu\n", cmd.lmi.error_ind.lmi_state);
                    }
                    goto do_get_again;
                case LMI_ENABLE_CON:
                    {
                        printf("LMI_ENABLE_CON:\n");
                        printf("State = %lu\n", cmd.lmi.enable_con.lmi_state);
                    }
                    return;
                case LMI_DISABLE_CON:
                    {
                        printf("LMI_DISABLE_CON:\n");
                        printf("State = %lu\n", cmd.lmi.enable_con.lmi_state);
                    }
                    return;
                default:
                    printf("Unrecognized response primitive %lu!\n", cmd.lmi.lmi_primitive);
                    goto do_get_again;
            }
        }
    }
}

void put_and_get(int fd, int flags) {
    if ( putmsg(fd, &ctrl, NULL, flags) < 0 ) {
        printf("ERROR: putmsg: [%d] %s\n", errno, strerror(errno));
        return;
    }
    do_lmi_get_msg(fd);
}


void info_req(int fd) {
    ctrl.len = sizeof(cmd.lmi.info_req);
    cmd.prim = LMI_INFO_REQ;
    printf("\n%d-LMI_INFO_REQ\n",fd);
    put_and_get(fd, RS_HIPRI);
}

void attach_req(int fd, ppa_t *ppap) {
    ctrl.len = sizeof(cmd.lmi.attach_req)+sizeof(ppa_t);
    cmd.prim = LMI_ATTACH_REQ;
    bcopy(ppap, cmd.lmi.attach_req.lmi_ppa, sizeof(ppa_t));
    printf("\n%d-LMI_ATTACH_REQ\n",fd);
    print_ppa((ppa_t *)cmd.lmi.attach_req.lmi_ppa);
    put_and_get(fd, RS_HIPRI);
}

void detach_req(int fd) {
    ctrl.len = sizeof(cmd.lmi.detach_req);
    cmd.prim = LMI_DETACH_REQ;
    printf("\n%d-LMI_DETACH_REQ\n",fd);
    put_and_get(fd, RS_HIPRI);
}

void enable_req(int fd) {
    ctrl.len = sizeof(cmd.lmi.enable_req);
    cmd.prim = LMI_ENABLE_REQ;
    printf("\n%d-LMI_ENABLE_REQ\n",fd);
    put_and_get(fd, RS_HIPRI);
}

void disable_req(int fd) {
    ctrl.len = sizeof(cmd.lmi.disable_req);
    cmd.prim = LMI_DISABLE_REQ;
    printf("\n%d-LMI_DISABLE_REQ\n",fd);
    put_and_get(fd, RS_HIPRI);
}

int sdl_open(void) {
    int fd;
    printf("\nOPEN: ss7-sdl-udp0\n");
    if ( (fd = open("/dev/ss7-sdl-udp0",O_NONBLOCK|O_RDWR)) < 0 ) {
        printf("ERROR: open: [%d] %s\n", errno, strerror(errno));
        exit(2);
    }
    return(fd);
}

void sdl_close(int fd) {
    printf("\n%d-CLOSE\n",fd);
    if ( close(fd) < 0 ) {
        printf("ERROR: close: [%d] %s\n", errno, strerror(errno));
        return;
    }
}

void sdl_write(int fd) {
    const char msg[] = "Hello World!";
    dbuf[0] = 0xff;
    dbuf[1] = 0xff;
    dbuf[2] = sizeof(msg)-1;
    memcpy(&dbuf[3],msg,sizeof(msg)-1);
    printf("\n%d-WRITE %s (%d bytes)\n",fd, msg, sizeof(msg)+2);
    if ( write(fd, dbuf, sizeof(msg)+2) < 0 ) {
        printf("ERROR: write: [%d] %s\n", errno, strerror(errno));
        return;
    }
//  ctrl.len = sizeof(cmd.sdl.daedt_transmission_req);
//  cmd.prim = SDL_DAEDT_TRANSMISSION_REQ;
//  data.len = sizeof(msg)+2;
//  if ( putmsg(fd, &ctrl, &data, 0) < 0 ) {
//      printf("ERROR: putmsg: [%d] %s\n", errno, strerror(errno));
//      return;
//  }
}

void sdl_read(int fd) {
    int n;
//  int flags = 0;
    char *c = &dbuf[3];
    printf("\n%d-READ\n",fd);
    if ( (n = read(fd, dbuf, BUFSIZE)) < 0 ) {
        printf("ERROR: read: [%d] %s\n", errno, strerror(errno));
        return;
    }
//  if ( getmsg(fd, NULL, &data, &flags) < 0 ) {
//      printf("ERROR: getmsg: [%d] %s\n", errno, strerror(errno));
//      return;
//  }
//  n = data.len;
    printf("%d-READ Message[%d]: ",fd, n);
    for (n-=3;n>0;n--) printf("%c", *(c++));
    printf("\n");
}

void sdl_daedt_start(int fd) {
    ctrl.len = sizeof(cmd.sdl.daedt_start_req);
    cmd.prim = SDL_DAEDT_START_REQ;
    printf("\n%d-SDL_DAEDT_START_REQ\n",fd);
    put_and_get(fd, RS_HIPRI);
}

void sdl_daedr_start(int fd) {
    ctrl.len = sizeof(cmd.sdl.daedr_start_req);
    cmd.prim = SDL_DAEDR_START_REQ;
    printf("\n%d-SDL_DAEDR_START_REQ\n",fd);
    put_and_get(fd, RS_HIPRI);
}

int sdl_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("ERROR: ioctl: (rtn = %d) [%d] %s\n", ret, errno, strerror(errno));
        return ret;
    }
    return ctl.ic_len;
}

void do_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");
    c->N = 16;
    c->m = 272;
    sdl_ioctl(fd, SDL_IOCSCONFIG, buf, sizeof(sdl_config_t));
    sdl_ioctl(fd, SDL_IOCGCONFIG, buf, sizeof(sdl_config_t));
    printf("Config:\n");
    printf("  N = %lu\n", c->N);
    printf("  m = %lu\n", c->m);
    sdl_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");
    sdl_ioctl(fd, DEV_IOCGIFFLAGS, buf, sizeof(unsigned long));
    printf("  ifflags = %lu\n", *((unsigned long *)buf));
    sdl_ioctl(fd, DEV_IOCGIFTYPE, buf, sizeof(unsigned long));
    printf("  iftype = %lu\n", *((unsigned long *)buf));
    sdl_ioctl(fd, DEV_IOCGGRPTYPE, buf, sizeof(unsigned long));
    printf("  ifgtype = %lu\n", *((unsigned long *)buf));
    sdl_ioctl(fd, DEV_IOCGIFMODE, buf, sizeof(unsigned long));
    printf("  ifmode = %lu\n", *((unsigned long *)buf));
    sdl_ioctl(fd, DEV_IOCGIFRATE, buf, sizeof(unsigned long));
    printf("  ifrate = %lu\n", *((unsigned long *)buf));
    sdl_ioctl(fd, DEV_IOCGIFCLOCK, buf, sizeof(unsigned long));
    printf("  ifclock = %lu\n", *((unsigned long *)buf));
    sdl_ioctl(fd, DEV_IOCGIFCODING, buf, sizeof(unsigned long));
    printf("  ifcoding = %lu\n", *((unsigned long *)buf));
    sdl_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-sdl-udp 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("127.0.0.1",&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("127.0.0.1",&ppa2.rem.sin_addr);

    fd1 = sdl_open();
    fd2 = sdl_open();

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

    info_req(fd1);
    attach_req(fd1, &ppa1);
    info_req(fd1);
    enable_req(fd1);
    info_req(fd1);

    info_req(fd2);
    attach_req(fd2, &ppa2);
    info_req(fd2);
    enable_req(fd2);
    info_req(fd2);

    do_ioctls(fd1);

    sdl_daedt_start(fd1);
    sdl_daedr_start(fd1);
    sdl_daedt_start(fd2);
    sdl_daedr_start(fd2);

    do_ioctls(fd2);

    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);
    sdl_write(fd1);

//  do_lmi_get_msg(fd1);
//  do_lmi_get_msg(fd2);
    
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);
    sdl_read(fd2);

    disable_req(fd1);
    info_req(fd1);
    detach_req(fd1);
    info_req(fd1);

    disable_req(fd2);
    info_req(fd2);
    detach_req(fd2);
    info_req(fd2);

    sdl_close(fd1);
    sdl_close(fd2);

    printf("Done.\n");

    return(0);
}
