/*
 * 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.
 * 
 */
/*
 * Kernel interface
 *
 * $Id: kernel_itf.c,v 1.9 1996/07/07 11:51:47 carnil Exp carnil $
 *
 */
/* System includes */
#include <errno.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/if_ether.h>
#include <string.h>

/* Atm includes */
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/atmlec.h>

/* Local includes */
#include "kernel_itf.h"
#include "g_event.h"
#include "conn.h"

#define EMOD MOD_UP_DRV
#define EINST "kernel_itf.c"
#include "emask.h"

/* Local vars */
extern int errno;
static int lec_socket;
/* Callback func array */
static kernel_callback callback_funcs[ATMLEC_MSG_TYPE_MAX];

int 
kernel_init(unsigned char *mac_addr, int itf_num)
{
  int rvalue;
  struct atmlec_msg msg;

  lec_socket = socket(PF_ATMSVC, SOCK_DGRAM, 0);
  if (lec_socket <0) {
    EVENT(EM_SERR,("Socket creation failure:%s\n",strerror(errno)));
    return -1;
  }
  itf_num = ioctl(lec_socket, ATMLEC_CTRL, itf_num);
  if (itf_num <0) {
    EVENT(EM_SERR,("Socket ioctl to lecd_ctrl failed:%s\n", strerror(errno)));
    close(lec_socket);
    return -1;
  }
  memcpy(&msg.content.normal.mac_addr, mac_addr, ETH_ALEN);

  msg.type = l_set_mac_addr;
  
  rvalue = write(lec_socket, &msg, sizeof(msg));
  if (rvalue<0) {
    EVENT(EM_SERR,("Can't write mac address to LEC:%s\n",strerror(errno)));
    close(lec_socket);
    return -1;
  }
  EVENT(EM_DEBUG,("Kernel interface initialized\n"));
  memset(callback_funcs, 0, sizeof(kernel_callback)*ATMLEC_MSG_TYPE_MAX);
  
  if (conn_set_kernel_socket(lec_socket)<0) {
    close(lec_socket);
    return -1;
  }
  /* Add fd to select */
  return itf_num;
}

int 
kernel_sendmsg(atmlec_msg_type type, unsigned char *mac_addr, 
	       unsigned char *atm_addr, struct atmlec_config_msg *config,
	       unsigned long flag)
{
  struct atmlec_msg mesg;

  memset(&mesg, 0, sizeof(mesg));
  mesg.type = type;
  if (type != l_config) {
    if (mac_addr)
      memcpy(mesg.content.normal.mac_addr, mac_addr, ETH_ALEN);
    if (atm_addr)
      memcpy(mesg.content.normal.atm_addr, atm_addr, ATM_ESA_LEN);
    mesg.content.normal.flag = flag;
  } else
    memcpy(&mesg.content.config, config, sizeof(struct atmlec_config_msg));

  if (write(lec_socket, &mesg, sizeof(mesg))<0) {
    EVENT(EM_SERR,("Write to kernel failed!\n"));
    return -1;
  }
  return 0;  
}

int 
kernel_register_callback(atmlec_msg_type type, kernel_callback callback)
{
  if (callback_funcs[type])
    return -1;
  callback_funcs[type] = callback;

  return 0;
}

static const char*
get_mesg_type_str(atmlec_msg_type type)
{
  switch(type) {
  case l_set_mac_addr:
    return "SET_MAC_ADDR";
  case l_del_mac_addr:
    return "DEL_MAC_ADDR";
  case l_svc_setup:
    return "SVC_SETUP";
  case l_arp_xmt:
    return "ARP_XMT";
  case l_addr_delete:
    return "ADDR_DELETE";
  case l_topology_change:
    return "TOPOLOGY_CHANGE";
  case l_flush_complete:
    return "FLUSH_COMPLETE";
  case l_arp_update:
    return "ARP_UPDATE";
  case l_config:
    return "CONFIG";
  default:
    return "UNKNOWN MSG TYPE";
  }
}

void
kernel_dispatch_handlers(void)
{
  struct atmlec_msg mesg;

  EVENT(EM_DEBUG,("Received message from kernel\n"));
  if (read(lec_socket, &mesg, sizeof(mesg))<0) {
    EVENT(EM_SERR,("Read from kernel socket failed:%s\n",strerror(errno)));
    return;
  }
  if (callback_funcs[mesg.type]) {
    callback_funcs[mesg.type](&mesg);
  } else
    EVENT(EM_NERR,("No handler for message type:%s\n",
		   get_mesg_type_str(mesg.type)));  
}

/*
 *
 * $Log: kernel_itf.c,v $
 * Revision 1.9  1996/07/07 11:51:47  carnil
 * Global msg_mask
 *
 * Revision 1.8  1996/06/10 04:27:28  carnil
 * compiler warning fix
 *
 * Revision 1.7  1996/05/29 08:22:39  carnil
 * *** empty log message ***
 *
 * Revision 1.6  1996/05/23 11:50:54  carnil
 * *** empty log message ***
 *
 * Revision 1.5  1996/04/25 19:42:13  carnil
 * Copyright notice
 *
 * Revision 1.4  1996/04/19 06:37:49  carnil
 * l_set_lecid
 *
 * Revision 1.3  1996/03/18 16:43:50  carnil
 * Bug fixes
 *
 * Revision 1.2  1996/03/17 21:23:41  carnil
 * kernel_sendmsg change
 *
 * Revision 1.1  1996/03/15 07:08:09  carnil
 * Initial revision
 *
 *
 */
