/* io.c - I/O operations */
 
/* Written 1996-1997 by Werner Almesberger, EPFL-LRC */
 

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/atm.h>
#include <linux/arequipa.h>

#include "atm.h"
#include "atmd.h"
#include "atmsap.h"

#include "io.h"


#define COMPONENT "IO"


extern int pretty;

static int kernel,incoming;


void open_all(void)
{
    struct atm_sap sap;
    struct atm_qos qos;

    if ((kernel = socket(PF_ATMSVC,SOCK_DGRAM,0)) < 0)
	diag(COMPONENT,DIAG_FATAL,"socket: %s",strerror(errno));
    if (ioctl(kernel,AREQUIPA_CTRL,0) < 0)
	diag(COMPONENT,DIAG_FATAL,"ioctl AREQUIPA_CTRL: %s",strerror(errno));
    if ((incoming = socket(PF_ATMSVC,SOCK_DGRAM,0)) < 0)
	diag(COMPONENT,DIAG_FATAL,"socket: %s",strerror(errno));
    memset(&qos,0,sizeof(qos));
    qos.txtp.traffic_class = qos.rxtp.traffic_class = ATM_ANYCLASS;
	/* compatible with everything else */
    qos.aal = ATM_AAL5;
    if (setsockopt(incoming,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0)
	diag(COMPONENT,DIAG_FATAL,"setsockopt SO_ATMQOS: %s",strerror(errno));
    memset(&sap,0,sizeof(sap));
    sap.bhli.hl_type = ATM_HL_VENDOR;
    sap.bhli.hl_length = 7;
    memcpy(sap.bhli.hl_info,AREQUIPA_HLT_VS_ID,7);
    sap.blli[0].l2_proto = ATM_L2_ISO8802;
    sap.blli[0].l3_proto = ATM_L3_NONE;
    if (setsockopt(incoming,SOL_ATM,SO_ATMSAP,&sap,sizeof(sap)) < 0)
	diag(COMPONENT,DIAG_FATAL,"setsockopt SO_ATMSAP: %s",strerror(errno));
    if (listen(incoming,5) < 0)
	diag(COMPONENT,DIAG_FATAL,"listen: %s",strerror(errno));
}


void close_all(void)
{
    (void) close(kernel);
    (void) close(incoming);
}


static void recv_kernel(void)
{
    if (ioctl(kernel,AREQUIPA_WORK,0) < 0) {
	diag(COMPONENT,DIAG_DEBUG,"Working");
	diag(COMPONENT,DIAG_ERROR,"ioctl AREQUIPA_WORK: %s",strerror(errno));
    }
}


static void accept_new(void)
{
    char buffer[MAX_ATM_ADDR_LEN+1];
    struct sockaddr_atmsvc addr;
    int fd,len;

    len = sizeof(addr);
    if ((fd = accept(incoming,(struct sockaddr *) &addr,&len)) < 0) {
	diag(COMPONENT,errno == EUNATCH ? DIAG_FATAL : DIAG_ERROR,
	  "accept: %s",strerror(errno));
	return;
    }
    if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) &addr,pretty) <
      0) strcpy(buffer,"<atm2text error>");
    diag(COMPONENT,DIAG_DEBUG,"Incoming call from %s",buffer);

    /*
     * Connection acceptance policy functions go here.
     */

    if (ioctl(fd,AREQUIPA_INCOMING,0) < 0)
        diag(COMPONENT,DIAG_ERROR,"ioctl AREQUIPA_INCOMING: %s",
	  strerror(errno));
    (void) close(fd); /* socket is now owned by the kernel */
}


void poll_loop(void)
{
    fd_set fds;
    int ret;

    while (1) {
	FD_ZERO(&fds);
	FD_SET(kernel,&fds);
	FD_SET(incoming,&fds);
	ret = select((kernel > incoming ? kernel : incoming)+1,&fds,NULL,NULL,
	  NULL);
	if (ret < 0) {
	    if (errno != EINTR) perror("select");
	    continue;
	}
	if (FD_ISSET(kernel,&fds)) recv_kernel();
	if (FD_ISSET(incoming,&fds)) accept_new();
	
    }
}
