/*
 * Marko Kiiskila carnil@cs.tut.fi 
 * 
 * Copyright (c) 1996
 * Tampere University of Technology - Telecommunications Laboratory
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this
 * software and its documentation is hereby granted,
 * provided that both the copyright notice and this
 * permission notice appear in all copies of the software,
 * derivative works or modified versions, and any portions
 * thereof, that both notices appear in supporting
 * documentation, and that the use of this software is
 * acknowledged in any publications resulting from using
 * the software.
 * 
 * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
 * SOFTWARE.
 * 
 */
/*
 *
 * Connection forming/teardown & packet send/receive
 *
 * $Id: conn.c,v 1.26 1996/08/06 14:14:11 carnil Exp carnil $
 *
 */

/* Global includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <netinet/in.h>
#include <fcntl.h>

/* Atm includes */

#include <linux/atm.h>
#include <linux/atmdev.h>
#include <atm.h>

/* Digital includes */
#include "codes.h"
#include "g_types.h"
#include "cm_sap.h"
#include "lec.h"
#include "atm.h"
#include "atmsap.h"
#include "cm.h"
#include "g_event.h"
#include "conn.h"
#include "utl_os.h"
#include "af_lane.h"
#include "le_disp.h"
#include "kernel_itf.h"
#include "lec_ctrl.h"
#include "addr_reg.h"
#include "lec_arp.h"

#define EMOD MOD_CM
#define EINST "conn.c"
#include "emask.h"

#define BUFSIZE 4096      /* Buffer size. LANE Control packets aren't
			     supposed to be big... */
#define QLEN 5            /* Max length of listen queue */


/* status */
#define CONNECTED 1245    /* Connection made, not notified */
#define NOTIFIED 1246     /* 
			     Connection made, notified
			     Packet transmitted, notified
			   */
#define TRANSMITTED 1247  /* Packet transmitted, not notified */
#define RELEASED 1248     /* Connection released */
#define CONNECTING 1249   /* Non blocking socket, connecting */

/* type */
#define WEMADE 7
#define THEYMADE 8
#define LISTENING 9
#define KERNEL_SOCK 10

extern int errno;

typedef struct _conn_t_ {
  int fd;                  /* Socket connected to this connection */
  unsigned long age_limit; /* In seconds. If this SVC is idle (no traffic) 
			      for longer than the age limit, it will be 
			      torn down.  If the age limit is specified 
			      as zero, the SVC will never be aged out. */
  HANDLE sap_handle;       /* Connection is linked to some sap */
  HANDLE conn_context;     /* Connection context for this VC */
  int status;              /* Connection status */
  int type;                /* We made, they made, listen socket */
  void *p_packet;          /* Save packets address, needed in callback */
  CONN_INFO *conn_info;    /* Listen socket conn_info */
  unsigned char atm_address[ATM_ESA_LEN]; 
                           /* Destination address, not in listen or kernel
                             sockets */
  struct _conn_t_ *next;
  struct _conn_t_ *previous;
} Conn_t;

typedef struct _Sap_client_t_ {
  HANDLE sap_context;
  SAP_RCV_CALLBACK rcv_callback;
  SAP_VC_NOTIFY_CALLBACK vc_notify_callback;
  SAP_CONNECT_CALLBACK svc_connect_callback;
  SAP_XMT_DONE_CALLBACK xmt_done_callback;
  char *p_sap_text;
  unsigned long xmt_packet_count;
  unsigned long rcv_packet_count;
  unsigned long accept_packet_count;
  unsigned long reject_packet_count;
  int itf_num;
  struct _Sap_client_t_ *next;
} Sap_client_t;

Sap_client_t *clientlist = NULL;

Conn_t *connlist = NULL;

/* Local protos */
static void list_remove_conn(HANDLE conn_handle);
static int list_add_conn(HANDLE sap_handle, HANDLE conn_context, 
			 long age_limit, CONN_INFO *info, Conn_t **p_conn);
static Conn_t* conn_already_exists(unsigned char *atm_addr,Conn_t *current);

STATUS 
cm_sap_register(HANDLE sap_context, 
		SAP_RCV_CALLBACK rcv_callback, 
		SAP_VC_NOTIFY_CALLBACK vc_notify_callback,
		SAP_CONNECT_CALLBACK svc_connect_callback,
		SAP_XMT_DONE_CALLBACK xmt_done_callback,
		int itf_num,
		const char *p_sap_text, HANDLE *p_sap_handle)
{
  Sap_client_t *client;

  EVENT(EM_DEBUG,("Cm_sap_register\n"));

  client = (Sap_client_t *)mem_alloc(EINST,sizeof(Sap_client_t));
  if (!client)
    return STATUS_K_RESOURCES;

  /* Callback func copying */
  client->sap_context = sap_context;
  client->rcv_callback = rcv_callback;
  client->vc_notify_callback = vc_notify_callback;
  client->svc_connect_callback = svc_connect_callback;
  client->xmt_done_callback = xmt_done_callback;

  /* Allocate space etc for text string */
  if (p_sap_text) {
    client->p_sap_text = (char *)mem_alloc(EINST,strlen(p_sap_text)+1);
    if (!client->p_sap_text) {
      mem_free(EINST,client);
      return STATUS_K_RESOURCES;
    }
    memcpy(client->p_sap_text, p_sap_text, strlen(p_sap_text));
    client->p_sap_text[strlen(p_sap_text)] = '\0';
  } else {
    client->p_sap_text = (char *)mem_alloc(EINST,1);
    if (!client->p_sap_text) {
      mem_free(EINST,client);
      return STATUS_K_RESOURCES;
    }
    client->p_sap_text[0] = '\0';
  }

  /* Statistic variables */
  client->xmt_packet_count = client->rcv_packet_count = 
    client->accept_packet_count = client->reject_packet_count = 0;

  client->itf_num = itf_num;
  
  /* Link to place */
  client->next = clientlist;
  clientlist = client;

  /* Returning handle */
  *p_sap_handle = client;
  return STATUS_K_SUCCESS;
}

void
cm_sap_unregister(HANDLE sap_handle)
{
  Sap_client_t *sap, *curr;
  Sap_client_t *prev = NULL;
  Conn_t *conn, *next;

  sap = (Sap_client_t *)sap_handle;

  curr = clientlist;
  while(curr && curr != sap) {
    prev = curr;
    curr = curr->next;
  }

  if (!curr) {
    EVENT(EM_NERR,("Aaargh! Unregistering client. Not found\n"));
    return;
  }

  /* Found */
  if (!prev) { /* Removing first */
    clientlist = curr->next;
  } else { /* Removing from middle */
    prev->next = curr->next;    
  }
  /* Remove vcc's */
  for(conn= connlist;conn;conn=next) {
    EVENT(EM_DEBUG,("Destroying:%p fd:%d type:%d\n",conn,conn->fd,conn->type));
    next=conn->next;
    if (conn->type == KERNEL_SOCK) {
      EVENT(EM_DEBUG,("Closing kernel socket\n"));
      close(conn->fd);
      list_remove_conn(conn);
      mem_free(EINST, conn);
    }
  }
  for(conn=connlist;conn;conn=next) {
    next=conn->next;
    close(conn->fd);
    if (conn->conn_info)
      mem_free(EINST, conn->conn_info);
    close(conn->fd);
    list_remove_conn(conn);
    mem_free(EINST, conn);
  }
  mem_free(EINST, sap->p_sap_text);
  mem_free(EINST, sap);
}

/* Don't check parameter current for atm address match */
static Conn_t*
conn_already_exists(unsigned char *atm_addr, Conn_t *current)
{
  Conn_t *conn;
  int len;

  conn = connlist;
  len = sizeof(struct sockaddr_atmsvc);

  while (conn) {
    if (conn != current && conn->type != LISTENING && 
	conn->type != KERNEL_SOCK && conn->status != RELEASED) {
      if (memcmp(conn->atm_address, atm_addr, ATM_ESA_LEN) == 0)
       return conn;
    }
    conn= conn->next;
  }
  return NULL;
}

static int
is_data_direct(CONN_INFO *p_conn_info)
{
  return (p_conn_info->blli.l3.tr9577.snap[4] == 0x02 ||
	  p_conn_info->blli.l3.tr9577.snap[4] == 0x03);
}

STATUS 
cm_sap_svc_setup (HANDLE sap_handle, HANDLE conn_context,
		  CONN_INFO *p_conn_info, UINT32 age_limit,
		  HANDLE *p_conn_handle)
{
  Conn_t *conn;
  Sap_client_t *sap = (Sap_client_t *)sap_handle;
  int s;
  int ret;
  struct sockaddr_atmsvc us;
  struct atm_blli blli;

  EVENT(EM_EVENT,("Outgoing call setup\n"));
  
  /* We don't create connection to entity where we already have
     a connection. */
  if (conn_already_exists(p_conn_info->addr.sas_addr.prv, NULL) &&
      is_data_direct(p_conn_info))
    return STATUS_K_ALREADY_OPEN;

  switch(p_conn_info->blli.l3.tr9577.snap[4]) { /* Kludge.  Eh? */
  case 0x01:
    EVENT(EM_DEBUG,("LE Control SVC setup\n"));
    break;
  case 0x02:
    EVENT(EM_DEBUG,("Data direct 802.3\n"));
    break;
  case 0x03:
    EVENT(EM_DEBUG,("Data direct 802.5\n"));
    break;
  case 0x04:
    EVENT(EM_DEBUG,("Multicast 802.3\n"));
    break;
  case 0x05:
    EVENT(EM_DEBUG,("Multicast 802.5\n"));
    break;
  default:
    EVENT(EM_ASSERT,("Unknown codepoint in svc setup\n"));
  }
  
  /* Create socket */
  s = socket(PF_ATMSVC, SOCK_DGRAM, ATM_AAL5);
  if (s<0) {
    EVENT(EM_NERR,("socket creation failure:%s\n",strerror(errno)));
    if (is_data_direct(p_conn_info)) {
      /* Try to remove possible entry in kernel */
      lc_addr_delete(conn_context, p_conn_info->addr.sas_addr.prv);
    }
    *p_conn_handle = NULL;
    return STATUS_K_RESOURCES;
  }
  /* Initialize address structs */
  memset(&us,0,sizeof(us));
  if (addr_getouratmaddr(&us) <0) {
    if (is_data_direct(p_conn_info)) {
      /* Try to remove possible entry in kernel */
      lc_addr_delete(conn_context, p_conn_info->addr.sas_addr.prv);
    }

    close(s);
    *p_conn_handle = NULL;
    return STATUS_K_ATM_RESOURCES;
  }
  
  /* Create local binding point */
  us.sas_addr.blli = &blli;
  /* Copy blli information */
  memcpy(&blli, &(p_conn_info->blli), sizeof(struct atm_blli));
  /* Set traffic parameters */
  if (setsockopt(s,SOL_ATM,SO_ATMQOS, &(p_conn_info->conqos), 
		 sizeof(p_conn_info->conqos)) < 0) {
    EVENT(EM_NERR,("setsockopt SO_ATMQOS: %s\n",strerror(errno)));
    if (is_data_direct(p_conn_info)) {
      /* Try to remove possible entry in kernel */
      lc_addr_delete(conn_context, p_conn_info->addr.sas_addr.prv);
    }
    close(s);
    *p_conn_handle = NULL;
    return STATUS_K_ATM_RESOURCES;
  }
  
  us.sas_family = p_conn_info->addr.sas_family;

  if (EMASK & EM_DEBUG)
    disp_sockaddr(&us);

  ret = bind(s, (struct sockaddr *)&us, sizeof(us));
  if (ret<0) {
    EVENT(EM_NERR,("bind error:%s\n",strerror(errno)));
    if (is_data_direct(p_conn_info)) {
      /* Try to remove possible entry in kernel */
    }

    close(s);
    *p_conn_handle = NULL;
    return STATUS_K_ATM_RESOURCES;
  }

  if (EMASK & EM_DEBUG)
    disp_sockaddr(&(p_conn_info->addr));

  EVENT(EM_EVENT,("Call to %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
		  p_conn_info->addr.sas_addr.prv[0]&0xff,
		  p_conn_info->addr.sas_addr.prv[1]&0xff,
		  p_conn_info->addr.sas_addr.prv[2]&0xff,
		  p_conn_info->addr.sas_addr.prv[3]&0xff,
		  p_conn_info->addr.sas_addr.prv[4]&0xff,
		  p_conn_info->addr.sas_addr.prv[5]&0xff,
		  p_conn_info->addr.sas_addr.prv[6]&0xff,
		  p_conn_info->addr.sas_addr.prv[7]&0xff,
		  p_conn_info->addr.sas_addr.prv[8]&0xff,
		  p_conn_info->addr.sas_addr.prv[9]&0xff,
		  p_conn_info->addr.sas_addr.prv[10]&0xff,
		  p_conn_info->addr.sas_addr.prv[11]&0xff,
		  p_conn_info->addr.sas_addr.prv[12]&0xff,
		  p_conn_info->addr.sas_addr.prv[13]&0xff,
		  p_conn_info->addr.sas_addr.prv[14]&0xff,
		  p_conn_info->addr.sas_addr.prv[15]&0xff,
		  p_conn_info->addr.sas_addr.prv[16]&0xff,
		  p_conn_info->addr.sas_addr.prv[17]&0xff,
		  p_conn_info->addr.sas_addr.prv[18]&0xff,
		  p_conn_info->addr.sas_addr.prv[19]&0xff));

  /* Try to connect */
  if (is_data_direct(p_conn_info)) {
    ret = fcntl(s, F_GETFL);
    if (ret <0) {
      EVENT(EM_SERR, ("fcntl(s, F_GETFL\n"));
      lc_addr_delete(conn_context, p_conn_info->addr.sas_addr.prv);
      close(s);
      *p_conn_handle = NULL;
    }
    if (fcntl(s, F_SETFL, ret|O_NONBLOCK)<0) {
      EVENT(EM_SERR, ("fcntl(s, F_SETFL, x&O_NONBLOCK)\n"));
      lc_addr_delete(conn_context, p_conn_info->addr.sas_addr.prv);
      close(s);
      *p_conn_handle=NULL;
      return STATUS_K_ATM_RESOURCES;
    }
  }
  ret = connect(s, (struct sockaddr *)&(p_conn_info->addr), 
		sizeof(struct sockaddr_atmsvc));
  if (ret<0 && errno != EINPROGRESS) {
    EVENT(EM_NERR,("connect error:%d,%s\n",errno,strerror(errno)));
    if (is_data_direct(p_conn_info)) {
      /* Try to remove possible entry in kernel */
      lc_addr_delete(conn_context, p_conn_info->addr.sas_addr.prv);
    }
    if (errno == ENETRESET)
      sig_reset(0);    
    close(s);
    *p_conn_handle = NULL;
    return STATUS_K_ATM_RESOURCES;
  }

  /* Notify lower layer. */
  switch (p_conn_info->blli.l3.tr9577.snap[4]) { /* AAAAAARRRGGHHH! */
  case 0x02:
  case 0x03:
    break;
  case 0x04:
  case 0x05:
    if (ioctl(s, ATMLEC_MCAST, sap->itf_num)<0) {
      EVENT(EM_SERR,("Can't change socket into LE mcast socket:%s\n",
		     strerror(errno)));
      if (is_data_direct(p_conn_info)) {
	/* Try to remove possible entry in kernel */
	lc_addr_delete(conn_context, p_conn_info->addr.sas_addr.prv);
      }
      close(s);
      *p_conn_handle = NULL;
      return STATUS_K_ATM_RESOURCES;
    }
    break;
  default:
    break;
  }

  /* Ok. Connect made. Add to list */
  ret = list_add_conn(sap_handle, conn_context,
		      age_limit, p_conn_info, &conn);
  EVENT(EM_DEBUG,("Conn:%lx\tcontext:%lx\n",(long)conn, (long)conn_context));
  if (ret <0) {
    if (is_data_direct(p_conn_info)) {
      /* Try to remove possible entry in kernel */
      lc_addr_delete(conn_context, p_conn_info->addr.sas_addr.prv);
    }
    close(s);
    *p_conn_handle = NULL;
    return STATUS_K_RESOURCES;
  }
  conn->fd = s;
  conn->type = WEMADE;

  /* Connection was made. Must notify upper level later. */
  if (is_data_direct(p_conn_info))
    conn->status = CONNECTING;
  else
    conn->status = CONNECTED;

  EVENT(EM_DEBUG,("Conn_handle %lx,fd:%d\n",(long)conn, s));
  *p_conn_handle = conn;
  return STATUS_K_SUCCESS;
}

STATUS 
cm_sap_svc_teardown(HANDLE conn_handle)
{
  Conn_t *conn;

  EVENT(EM_DEBUG,("Cm_sap_svc_teardown %lx\n",(long)conn_handle));
  conn = (Conn_t *)conn_handle;

  close(conn->fd);
  /* Remove conn from list */
  list_remove_conn(conn);
  if (conn->conn_info)
    mem_free(EINST, conn->conn_info);
  mem_free(EINST,conn);

  return STATUS_K_SUCCESS;
}

STATUS
cm_sap_xmt_vc (HANDLE conn_handle, void *p_data,
	       UINT32 length, UINT32 user_data,
	       AAL_DATA *p_aal_data)
{
  Conn_t *conn;
  Sap_client_t *sap;
  int ret;

  /* Write data out */
  conn = (Conn_t *)conn_handle;
  sap = (Sap_client_t *)conn->sap_handle;
  EVENT(EM_DEBUG,("Cm_sap_xmt_vc fd:%d len:%ld\n",conn->fd, length));
  ret=write(conn->fd, p_data, length);

  /* Tried to transmit packet */
  sap->xmt_packet_count++;
  conn->p_packet = p_data;

  /* Failure */
  if (ret<0) {
    EVENT(EM_NERR,("Write failed: %s\n",strerror(errno)));
    conn->status = RELEASED;
    return STATUS_K_CONGESTED; /* Asdf */
  } 
  if (EMASK & EM_XCTRL)
    le_frame_display(p_data, length, "Xmitted");
  return STATUS_K_SUCCESS;
}

void
cm_sap_link_status_get(HANDLE sap_handle, ATM_LINK_STATE *p_link_state)
{
  int s;

  EVENT(EM_DEBUG,("Cm_sap_link_status_get\n"));
  assert(sap_handle);

  if ((s=socket(AF_ATMSVC, SOCK_DGRAM, ATM_AAL5))<0) {
    /* Socket creation failure -> not capable of running LE */
    EVENT(EM_SERR,("Not capable of creating ATM socket."));
    *p_link_state = LINK_DOWN;
    exit(-1);
  }
  *p_link_state = LINK_SIG_UP;
}

void
svcinit_conn_info(CONN_INFO *p_svc_conn_info)
{
  memset(p_svc_conn_info,0,sizeof(CONN_INFO));
  p_svc_conn_info->addr.sas_addr.blli = &(p_svc_conn_info->blli);
}

/* Callbacks for connections */
void
conn_call_callbacks(void)
{
  Conn_t *conn;
  Sap_client_t *sap;
  
  conn = connlist;
  EVENT(EM_DEBUG,("Conn_call_callbacks %p\n",connlist));
  while(conn) {
    if (conn->status == CONNECTED) {
      /* Connection made, not notified yet */
      conn->status = NOTIFIED;
      sap = (Sap_client_t *)conn->sap_handle;
      sap->vc_notify_callback(conn->conn_context,
			      conn,
			      CONN_VC_READY,
			      0,
			      0,
			      TRUE);
      conn = conn->next;
    } else if (conn->status == TRANSMITTED) {
      conn->status = NOTIFIED;
      conn->p_packet = NULL;
      sap = (Sap_client_t *)conn->sap_handle;
      sap->xmt_done_callback(conn->conn_context,
			     conn->p_packet,
			     0);
      conn = conn->next;
    } else if (conn->status == RELEASED) {
      EVENT(EM_DEBUG,("Conn_call_callbacks, released conn %p\n",conn));
      conn->status = NOTIFIED;
      sap = (Sap_client_t *)conn->sap_handle;
      sap->vc_notify_callback(conn->conn_context,
			      conn,
			      CONN_RELEASE,
			      CAUSE_NORMAL,
			      0,
			      (conn->type==WEMADE)?TRUE:FALSE);
      conn=connlist;
    } else {
      conn = conn->next;
    }
  }
}

static const char*
conn_get_type_string(int type) {
  switch(type) {
  case WEMADE:
    return "WEMADE";
  case THEYMADE:
    return "THEYMADE";
  case LISTENING:
    return "LISTENING";
  case KERNEL_SOCK:
    return "KERNEL_SOCK";
  default:
    return "UNKNOWN";
  }
}

/* 
 * Get connections fds for main's select(),
 * Quite ugly way BTW.
 */
void
conn_get_fds(fd_set *fds)
{
  Conn_t *conn;
  char *buff, *ch;

  conn = connlist;

  if (EMASK & EM_DEBUG) {
    buff = (char*)mem_alloc(EINST,1024);
    ch = buff;
  }
  while (conn) {
    if (conn->status != CONNECTING) {
      if (EMASK & EM_DEBUG) {
	ch=ch+sprintf(ch,"-%3d %s",conn->fd, conn_get_type_string(conn->type));
      }
      FD_SET(conn->fd, fds);
    }
    conn = conn->next;
  }
  EVENT(EM_DEBUG,("conn_add_fds:%s\n",buff));
  if (EMASK & EM_DEBUG) 
    mem_free(EINST, buff);
  fflush(stdout);
}

void
conn_get_connecting_fds(fd_set *fds)
{
  Conn_t *conn;
  char *buff, *ch;

  conn = connlist;

  if (EMASK & EM_DEBUG) {
    buff = (char*)mem_alloc(EINST, 1024);
    ch = buff;
  }
  while (conn) {
    if (conn->status == CONNECTING) {
      if (EMASK & EM_DEBUG) {
	ch=ch+sprintf(ch,"-%3d %s",conn->fd, conn_get_type_string(conn->type));
      }
      FD_SET(conn->fd, fds);
    }
    conn = conn->next;
  }
  EVENT(EM_DEBUG,("conn_add_connecting_fds:%s\n",buff));
  if (EMASK & EM_DEBUG) 
    mem_free(EINST, buff);
  fflush(stdout);
}
  
int
conn_check_incoming(fd_set *fds)
{
  Conn_t *conn, *tmpconn;
  Sap_client_t *sap;
  unsigned char buffer[BUFSIZE];
  unsigned char *tmp;
  long nbytes;
  int fd, len;
  unsigned long reject_reason;
  struct sockaddr_atmsvc addr;
  Conn_t *to_add;
  struct atmlec_ioc ioc_data;
  int reset = 0;

  conn = connlist;

  while (conn) {
    if (FD_ISSET(conn->fd, fds) && conn->type == LISTENING){/* Incoming call */
      EVENT(EM_EVENT,("Incoming call\n"));
      if (list_add_conn(conn->sap_handle,
			NULL,
			0,
			NULL,
			&to_add)<0) {
	EVENT(EM_NERR,("list_conn_add failed\n"));
	continue;
      }	       
      sap = (Sap_client_t *)conn->sap_handle;
      if (sap->svc_connect_callback(conn->conn_context,
				    conn->conn_info, 
				    to_add,
				    &to_add->conn_context,
				    &to_add->age_limit,
				    &reject_reason) == FALSE) {
	/* What to do, if ... */
	len = sizeof(addr);
	fd = accept(conn->fd, (struct sockaddr *)&addr, &len);
	if (fd <0 && (errno == EUNATCH || errno == ENETRESET))
	  sig_reset(42);
	if (fd >=0)
	  close(fd);
	list_remove_conn(to_add);
      } else {
	len = sizeof(addr);
	fd = accept(conn->fd, (struct sockaddr *)&addr, &len);
	if (fd<0) {
	  EVENT(EM_NERR,("Accept failed:%s\n",strerror(errno)));
	  if (errno == ENETRESET || errno == EUNATCH)
	    sig_reset(42);
	  list_remove_conn(to_add);
	  sap->vc_notify_callback(to_add->conn_context,
				  to_add,
				  CONN_RELEASE,
				  CAUSE_NORMAL,
				  0,
				  FALSE);
	} else {
	  to_add->fd = fd;
	  to_add->status = NOTIFIED;
	  to_add->type = THEYMADE;
	  if (conn->conn_info->blli.l3.tr9577.snap[4] !=1) { 
	    /*Others are data*/
	    len = sizeof(struct sockaddr_atmsvc);
	    memset(&addr, 0, len);
	    if (getpeername(fd, (struct sockaddr *)&addr, &len)<0) {
	      EVENT(EM_SERR,("Can't get socket address:%s\n",strerror(errno)));
	      list_remove_conn(to_add);
	      sap->vc_notify_callback(to_add->conn_context,
				      to_add,
				      CONN_RELEASE,
				      CAUSE_NORMAL,
				      0,
				      FALSE);
	    } else {
	      EVENT(EM_EVENT,("Call from %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
			      addr.sas_addr.prv[0]&0xff,
			      addr.sas_addr.prv[1]&0xff,
			      addr.sas_addr.prv[2]&0xff,
			      addr.sas_addr.prv[3]&0xff,
			      addr.sas_addr.prv[4]&0xff,
			      addr.sas_addr.prv[5]&0xff,
			      addr.sas_addr.prv[6]&0xff,
			      addr.sas_addr.prv[7]&0xff,
			      addr.sas_addr.prv[8]&0xff,
			      addr.sas_addr.prv[9]&0xff,
			      addr.sas_addr.prv[10]&0xff,
			      addr.sas_addr.prv[11]&0xff,
			      addr.sas_addr.prv[12]&0xff,
			      addr.sas_addr.prv[13]&0xff,
			      addr.sas_addr.prv[14]&0xff,
			      addr.sas_addr.prv[15]&0xff,
			      addr.sas_addr.prv[16]&0xff,
			      addr.sas_addr.prv[17]&0xff,
			      addr.sas_addr.prv[18]&0xff,
			      addr.sas_addr.prv[19]&0xff));
	      /* LE spec 8.1.11. Accept all calls. If there already
		 exists a connection to this calling entity, and our
		 atm address is numerically lower than theirs, then
		 don't make it default interface to use in kernel. */
	      memcpy(ioc_data.atm_addr, addr.sas_addr.prv, ATM_ESA_LEN);
	      if (conn->conn_info->blli.l3.tr9577.snap[4] == 2 ||
		  conn->conn_info->blli.l3.tr9577.snap[4] == 3) {
		tmpconn = conn_already_exists(addr.sas_addr.prv, to_add);
		if (tmpconn) {
		  addr_getouratmaddr(&addr);
		  if (memcmp(addr.sas_addr.prv, ioc_data.atm_addr, ATM_ESA_LEN)
		      <0)
		    ioc_data.receive = 1;
		  else
		    ioc_data.receive = 0;
		} else
		  ioc_data.receive = 0; /* Data direct */
	      } else
		ioc_data.receive = 2; /* Multicast distribute */
	      ioc_data.dev_num = sap->itf_num;
	      EVENT(EM_DEBUG,("About to ioctl(fd, ATMLEC_DATA, &ioc_data)\n"));
	      if (ioctl(fd, ATMLEC_DATA, &ioc_data)<0) {
		EVENT(EM_SERR,("Can't change socket into LE data socket:%s\n",
			       strerror(errno)));
	      }
	      EVENT(EM_DEBUG,("ioctl done\n"));
	    }
	  }
	}
      }
    }
    conn = conn->next;
  }
  conn=connlist;
  while (conn) {
    tmpconn = conn->next;
    if (FD_ISSET(conn->fd, fds)) {
      EVENT(EM_DEBUG,("Event in fd:%d\n",conn->fd));
      if (conn->type == KERNEL_SOCK) { /* Message from kernel */
	kernel_dispatch_handlers();
      } else if (conn->type == LISTENING) { /* Incoming call */
      } else if (conn->status == CONNECTING) { /* Call is ready */
	memset(&addr,0,sizeof(addr));
	/*
	if (!connect(conn->fd, (struct sockaddr*)&addr,sizeof(addr)) &&
	  errno != EINPROGRESS) {
	  */
	if (connect(conn->fd, (struct sockaddr*)&addr, sizeof(addr))<0) {
	  EVENT(EM_SERR,("Nonblocking connect() :%s\n",strerror(errno)));
	  lc_addr_delete(conn->conn_context, conn->atm_address);
	  conn->status = RELEASED;
	} else {
	  EVENT(EM_DEBUG,("Call connected\n"));
	  conn->status = CONNECTED;
	  memcpy(ioc_data.atm_addr, conn->atm_address, ATM_ESA_LEN);
	  sap = conn->sap_handle;
	  ioc_data.dev_num = sap->itf_num;
	  ioc_data.receive = 0;
	  EVENT(EM_DEBUG,("About to ioctl(s, ATMLEC_DATA, &ioc_data)\n"));
	  if (ioctl(conn->fd, ATMLEC_DATA, &ioc_data)<0) {
	    EVENT(EM_SERR,("Can't change socket into LE data socket:%s\n",
			   strerror(errno)));
	    conn->status = RELEASED;
	    lc_addr_delete(conn->conn_context, conn->atm_address);
	  } else {
	    EVENT(EM_DEBUG,("ioctl done\n"));
	    kernel_flush_xmt(conn->atm_address);
	  }
	}
      } else { /* Data coming in */
	nbytes = read(conn->fd, buffer, BUFSIZE);
	if (nbytes <0) {
	  EVENT(EM_SERR,("Read failed on fd %d: %s\n",
			 conn->fd,strerror(errno)));
	  if (errno == EUNATCH || errno == ENOENT || errno == ENETRESET) 
	    reset = -1;
	  else {
	    conn->status = RELEASED;
	    EVENT(EM_EVENT, ("Closing connection\n"));
	  }
	} else if (nbytes==0) {
         /* EOF, connection closed */    
	  conn->status = RELEASED;
	  EVENT(EM_EVENT,("Connection closed\n"));
	} else {
	  sap = (Sap_client_t *)conn->sap_handle;
	  sap->rcv_packet_count++;
	  if ((EMASK & EM_RCTRL) && (((LE_CTRL_HDR*)buffer)->op_code != ntohs(LE_TOPOLOGY_REQ)))
	    le_frame_display(buffer, nbytes, "Received");
	  tmp=buffer;
	  sap->rcv_callback(conn->conn_context,
			    conn,
			    nbytes,
			    0,
			    NULL,
			    (void*)&tmp);
	  fflush(stdout);
	}
      }
    }
    conn = tmpconn;
  }
  return reset;
}

int
conn_create_listensocket(HANDLE sap_handle, HANDLE conn_context,
			 unsigned short blli_codepoint, const char *qos_spec,
			 HANDLE *p_conn_handle)
{
  int fd, ret, len;
  Conn_t *conn;
  CONN_INFO *conn_info;

  conn_info = (CONN_INFO *)mem_alloc(EINST, sizeof(CONN_INFO));

  EVENT(EM_DEBUG,("conn_create_listensocket\n"));
  fd = socket(PF_ATMSVC, SOCK_DGRAM, ATM_AAL5);
  if (fd < 0) {
    EVENT(EM_NERR,("Socket failed: %s\n",strerror(errno)));
    return -1;
  }
 
  memset(conn_info , 0, sizeof(CONN_INFO));

  conn_info->conqos.txtp.traffic_class = ATM_UBR;
  conn_info->conqos.rxtp.traffic_class = ATM_UBR;

  switch (blli_codepoint) {
  case BLLI_CONTROL:
  case BLLI_DIRECT_802_3:
  case BLLI_BUS_802_3:
    conn_info->conqos.rxtp.max_sdu = 1516;
    conn_info->conqos.txtp.max_sdu = 1516;
    break;
  case BLLI_DIRECT_802_5:
  case BLLI_BUS_802_5:
    conn_info->conqos.rxtp.max_sdu = 4544;
    conn_info->conqos.txtp.max_sdu = 4544;
    break;
  default:
    EVENT(EM_ASSERT,("Unknown BLLI codepoint:%x\n",blli_codepoint));
  }

  if (qos_spec)
    if (text2qos(qos_spec,&conn_info->conqos,T2Q_DEFAULTS) < 0)
      EVENT(EM_ASSERT,("text2qos failed in conn_create_listensocket\n"));

  /* Set traffic parameters */
  if (setsockopt(fd,SOL_ATM,SO_ATMQOS, &(conn_info->conqos), 
		 sizeof(conn_info->conqos)) < 0) {
    EVENT(EM_NERR,("setsockopt SO_ATMQOS: %s\n",strerror(errno)));
    close(fd);
    return -1;
  }
  
  if (addr_getouratmaddr(&conn_info->addr)<0) {
    close(fd);
    return -1;
  }
  conn_info->addr.sas_addr.blli = &conn_info->blli;
  conn_info->blli.l3_proto = ATM_L3_TR9577;
  conn_info->blli.l3.tr9577.ipi = NLPID_IEEE802_1_SNAP;
  conn_info->blli.l3.tr9577.snap[0] = 0x00;
  conn_info->blli.l3.tr9577.snap[1] = 0xa0;
  conn_info->blli.l3.tr9577.snap[2] = 0x3e;
  conn_info->blli.l3.tr9577.snap[3] = 
    (unsigned char)(0xff&(blli_codepoint>>8));
  conn_info->blli.l3.tr9577.snap[4] = (unsigned char)(0xff&blli_codepoint);
    
  len = sizeof(struct sockaddr_atmsvc);  

  if (EMASK & EM_DEBUG)
    disp_sockaddr(&conn_info->addr);
  
  ret = bind(fd, (struct sockaddr *)&conn_info->addr, len);
  if (ret != 0) {
    EVENT(EM_NERR,("Bind failed: %s\n",strerror(errno)));
    close(fd);
    return -1;
  }

  ret = listen(fd, QLEN);
  if (ret != 0) {
    EVENT(EM_NERR,("Listen failed: %s\n",strerror(errno)));
    close(fd);
    return -1;
  }

  if (list_add_conn(sap_handle, conn_context, 0, NULL, &conn)<0) {
    EVENT(EM_NERR,("List_add_conn failed\n"));
    close(fd);    
    return -1;
  }
  conn->type = LISTENING;
  conn->status = NOTIFIED;
  conn->conn_info = conn_info;
  conn->fd = fd;
  EVENT(EM_EVENT,("Listen socket created blli:%2.2x %2.2x\n",
		  conn_info->blli.l3.tr9577.snap[3],
		  conn_info->blli.l3.tr9577.snap[4]));
  if (p_conn_handle)
    *p_conn_handle = conn;
  return fd;
}

static int
list_add_conn(HANDLE sap_handle, HANDLE conn_context, long age_limit, 
	      CONN_INFO *info, Conn_t **p_conn)
{
  Conn_t *conn;

  conn = (Conn_t *)mem_alloc(EINST,sizeof(Conn_t));
  *p_conn = conn;
  if (!conn)
    return -1;
  
  conn->sap_handle = sap_handle;
  conn->conn_context = conn_context;
  conn->age_limit = age_limit;
  if (info)
    memcpy(conn->atm_address, info->addr.sas_addr.prv, ATM_ESA_LEN);
  else
    memset(conn->atm_address, 0, ATM_ESA_LEN);

  conn->next = connlist;
  conn->previous = NULL;
  if (connlist)
    connlist->previous = conn;
  connlist = conn;
  EVENT(EM_DEBUG,("Added conn:%p\n",conn));
  
  return 0;
}

static void
list_remove_conn(HANDLE conn_handle)
{
  Conn_t *conn;

  conn = (Conn_t *)conn_handle;

  if (conn->next == NULL && conn->previous == NULL
      && connlist != conn) return;
  EVENT(EM_DEBUG,("Removing conn:%p fd:%d previous:%p next:%p ", conn, 
		  conn->fd,
		  conn->previous, conn->next));
  if (conn->previous) 
    EVENT(EM_DEBUG,("Previous:%p, fd:%d, next:%p, previous:%p ", conn->previous,
		    conn->previous->fd,
		    conn->previous->next, conn->previous->previous));
  if (conn->next)
    EVENT(EM_DEBUG,("Next:%p, fd:%d next:%p, previous:%p ",conn->next,
		    conn->next->fd,
		    conn->next->next, conn->next->previous));  
  if (conn->previous) {
    conn->previous->next = conn->next;
  } else /* First in line */
    connlist = conn->next;
  if (conn->next)
    conn->next->previous = conn->previous;  
  EVENT(EM_DEBUG,("Connlist:%lx\n",(long)connlist));
  conn->next=conn->previous= NULL;
}

int 
conn_set_kernel_socket(int fd)
{
  Conn_t *conn;
  
  if (list_add_conn(NULL, NULL, 0, NULL, &conn)<0) {
    EVENT(EM_NERR,("List_add_conn failed\n"));
    return -1;
  }
  conn->type = KERNEL_SOCK;
  conn->status = NOTIFIED;
  conn->fd = fd;
  return fd;
}


/*
 *
 * $Log: conn.c,v $
 * Revision 1.26  1996/08/06 14:14:11  carnil
 * Cleaning up
 * Address handling moved to address.c from conn.c
 *
 * Revision 1.25  1996/07/07 11:51:47  carnil
 * Global msg mask
 *
 * Revision 1.24  1996/06/10 04:27:28  carnil
 * traffic parameter via setsockopt
 * compiler warning fix
 * Bug fixes
 *
 * Revision 1.22  1996/05/29 08:22:39  carnil
 * bug fix
 *
 * Revision 1.21  1996/05/23 11:50:54  carnil
 * *** empty log message ***
 *
 * Revision 1.20  1996/05/13 08:53:27  carnil
 * *** empty log message ***
 *
 * Revision 1.19  1996/04/25 19:42:13  carnil
 * Copyright notice
 *
 * Revision 1.18  1996/04/20 16:24:47  carnil
 * *** empty log message ***
 *
 * Revision 1.17  1996/04/19 06:37:49  carnil
 * *** empty log message ***
 *
 * Revision 1.16  1996/04/11 09:16:14  carnil
 * conn direction, data direct
 *
 * Revision 1.15  1996/03/30 15:14:23  carnil
 * DATA ioctl
 *
 * Revision 1.14  1996/03/22 11:26:23  carnil
 * *** empty log message ***
 *
 * Revision 1.13  1996/03/18 18:15:25  carnil
 * ATMLECD_DATA ioctl arg change
 *
 * Revision 1.12  1996/03/18 16:43:50  carnil
 * *** empty log message ***
 *
 * Revision 1.11  1996/03/17 21:23:41  carnil
 * kernel_init()
 *
 * Revision 1.10  1996/03/15 07:08:09  carnil
 * Kernel fd added to list
 *
 * Revision 1.9  1996/03/04 08:55:39  carnil
 * listen socket creation works
 * Communication to servers works
 *
 * Revision 1.8  1996/02/29 11:24:12  carnil
 * Atm connection making works
 *
 * Revision 1.7  1996/02/19 16:32:01  carnil
 * *** empty log message ***
 *
 * Revision 1.6  1996/02/16 06:16:20  carnil
 * listen socket creation, separate funcs for conn list handling
 * tcp code to atmsocket code
 * connection accepting
 *
 * Revision 1.5  1996/02/06  11:23:41  carnil
 * fds for select
 * xmt,rcv, tcp implementation
 *
 * Revision 1.4  1996/01/30  15:20:49  carnil
 * Clients to list, cm_sap_xmt, receiving of packets
 * svc creation using tcp sockets
 *
 * Revision 1.3  1996/01/29  09:30:43  carnil
 * mem_alloc, mem_free
 *
 * Revision 1.2  1996/01/23  10:02:12  carnil
 * Debug info
 *
 * Revision 1.1  1996/01/22  12:59:50  carnil
 * Initial revision
 *
 *
 */
