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

 @(#) $Id: test-sdludp.c,v 0.7.4.1 2001/02/18 09:44:55 brian Exp $

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

 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/18 09:44:55 $ by $Author: brian $

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

static char const ident[] = "$Id: test-sdludp.c,v 0.7.4.1 2001/02/18 09:44:55 brian Exp $";

/*
 *  This file is for testing the ss7-sdludp 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-sdludp0\n");
    if ( (fd = open("/dev/ss7-sdludp0",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-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("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);
}
