text: /***
   *
   *  rtcfg/rtcfg_event.c
   *
   *  Real-Time Configuration Distribution Protocol
   *
   *  Copyright (C) 2003-2005 Jan Kiszka <jan.kiszka@web.de>
   *
   *  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.
   *
   */
  
  #include <asm/uaccess.h>
  #include <linux/kernel.h>
  #include <linux/list.h>
  #include <linux/vmalloc.h>
  
  #include <rtdev.h>
  #include <ipv4/route.h>
  #include <rtcfg/rtcfg.h>
  #include <rtcfg/rtcfg_client_event.h>
  #include <rtcfg/rtcfg_conn_event.h>
  #include <rtcfg/rtcfg_file.h>
  #include <rtcfg/rtcfg_frame.h>
  #include <rtcfg/rtcfg_timer.h>
  
  
  /*** Common and Server States ***/
  static int rtcfg_main_state_off(
      int ifindex, RTCFG_EVENT event_id, void* event_data);
  static int rtcfg_main_state_server_running(
      int ifindex, RTCFG_EVENT event_id, void* event_data);
  
  
  #ifdef CONFIG_XENO_DRIVERS_NET_RTCFG_DEBUG
  const char *rtcfg_event[] = {
      "RTCFG_CMD_SERVER",
      "RTCFG_CMD_ADD",
      "RTCFG_CMD_DEL",
      "RTCFG_CMD_WAIT",
      "RTCFG_CMD_CLIENT",
      "RTCFG_CMD_ANNOUNCE",
      "RTCFG_CMD_READY",
      "RTCFG_CMD_DETACH",
      "RTCFG_TIMER",
      "RTCFG_FRM_STAGE_1_CFG",
      "RTCFG_FRM_ANNOUNCE_NEW",
      "RTCFG_FRM_ANNOUNCE_REPLY",
      "RTCFG_FRM_STAGE_2_CFG",
      "RTCFG_FRM_STAGE_2_CFG_FRAG",
      "RTCFG_FRM_ACK_CFG",
      "RTCFG_FRM_READY",
      "RTCFG_FRM_HEARTBEAT",
      "RTCFG_FRM_DEAD_STATION"
  };
  
  const char *rtcfg_main_state[] = {
      "RTCFG_MAIN_OFF",
      "RTCFG_MAIN_SERVER_RUNNING",
      "RTCFG_MAIN_CLIENT_0",
      "RTCFG_MAIN_CLIENT_1",
      "RTCFG_MAIN_CLIENT_ANNOUNCED",
      "RTCFG_MAIN_CLIENT_ALL_KNOWN",
      "RTCFG_MAIN_CLIENT_ALL_FRAMES",
      "RTCFG_MAIN_CLIENT_2",
      "RTCFG_MAIN_CLIENT_READY"
  };
  
  int rtcfg_debug = RTCFG_DEFAULT_DEBUG_LEVEL;
  #endif /* CONFIG_XENO_DRIVERS_NET_RTCFG_DEBUG */
  
  
  struct rtcfg_device device[MAX_RT_DEVICES];
  
  static int (*state[])(int ifindex, RTCFG_EVENT event_id, void* event_data) =
  {
      rtcfg_main_state_off,
      rtcfg_main_state_server_running,
      rtcfg_main_state_client_0,
      rtcfg_main_state_client_1,
      rtcfg_main_state_client_announced,
      rtcfg_main_state_client_all_known,
      rtcfg_main_state_client_all_frames,
      rtcfg_main_state_client_2,
      rtcfg_main_state_client_ready
  };
  
  
  static int rtcfg_server_add(struct rtcfg_cmd *cmd_event);
  static int rtcfg_server_del(struct rtcfg_cmd *cmd_event);
  static int rtcfg_server_detach(int ifindex, struct rtcfg_cmd *cmd_event);
  static int rtcfg_server_recv_announce(int ifindex, RTCFG_EVENT event_id,
  				      struct rtskb *rtskb);
  static int rtcfg_server_recv_ack(int ifindex, struct rtskb *rtskb);
  static int rtcfg_server_recv_simple_frame(int ifindex, RTCFG_EVENT event_id,
  					  struct rtskb *rtskb);
  
  
  
  int rtcfg_do_main_event(int ifindex, RTCFG_EVENT event_id, void* event_data)
  {
      int main_state;
  
  
      rtdm_mutex_lock(&device[ifindex].dev_mutex);
  
      main_state = device[ifindex].state;
  
      RTCFG_DEBUG(3, "RTcfg: %s() rtdev=%d, event=%s, state=%s\n", __FUNCTION__,
  		ifindex, rtcfg_event[event_id], rtcfg_main_state[main_state]);
  
      return (*state[main_state])(ifindex, event_id, event_data);
  }
  
  
  
  void rtcfg_next_main_state(int ifindex, RTCFG_MAIN_STATE state)
  {
      RTCFG_DEBUG(4, "RTcfg: next main state=%s \n", rtcfg_main_state[state]);
  
      device[ifindex].state = state;
  }
  
  
  
  static int rtcfg_main_state_off(int ifindex, RTCFG_EVENT event_id,
  				void* event_data)
  {
      struct rtcfg_device     *rtcfg_dev = &device[ifindex];
      struct rt_proc_call     *call      = (struct rt_proc_call *)event_data;
      struct rtcfg_cmd        *cmd_event;
      int                     ret;
  
      cmd_event = rtpc_get_priv(call, struct rtcfg_cmd);
      switch (event_id) {
  	case RTCFG_CMD_SERVER:
  	    INIT_LIST_HEAD(&rtcfg_dev->spec.srv.conn_list);
  
  	    ret = rtdm_timer_init(&rtcfg_dev->timer, rtcfg_timer, "rtcfg-timer");
  	    if (ret == 0) {
  		    ret = rtdm_timer_start(&rtcfg_dev->timer,
  					    XN_INFINITE,
  					    (nanosecs_rel_t)
  					    cmd_event->args.server.period
  					    * 1000000,
  					    RTDM_TIMERMODE_RELATIVE);
  		    if (ret < 0)
  			    rtdm_timer_destroy(&rtcfg_dev->timer);
  	    }
  	    if (ret < 0) {
  		rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  		return ret;
  	    }
  
  	    if (cmd_event->args.server.flags & _RTCFG_FLAG_READY)
  		    set_bit(RTCFG_FLAG_READY, &rtcfg_dev->flags);
  	    set_bit(FLAG_TIMER_STARTED, &rtcfg_dev->flags);
  
  	    rtcfg_dev->burstrate = cmd_event->args.server.burstrate;
  
  	    rtcfg_dev->spec.srv.heartbeat = cmd_event->args.server.heartbeat;
  
  	    rtcfg_dev->spec.srv.heartbeat_timeout =
  		    ((u64)cmd_event->args.server.heartbeat) * 1000000 *
  		    cmd_event->args.server.threshold;
  
  	    rtcfg_next_main_state(ifindex, RTCFG_MAIN_SERVER_RUNNING);
  
  	    rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
  	    break;
  
  	case RTCFG_CMD_CLIENT:
  	    rtcfg_dev->spec.clt.station_addr_list =
  		cmd_event->args.client.station_buf;
  	    cmd_event->args.client.station_buf = NULL;
  
  	    rtcfg_dev->spec.clt.max_stations =
  		cmd_event->args.client.max_stations;
  	    rtcfg_dev->other_stations = -1;
  
  	    rtcfg_queue_blocking_call(ifindex, call);
  
  	    rtcfg_next_main_state(ifindex, RTCFG_MAIN_CLIENT_0);
  
  	    rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
  	    return -CALL_PENDING;
  
  	default:
  	    rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
  	    RTCFG_DEBUG(1, "RTcfg: unknown event %s for rtdev %d in %s()\n",
  			rtcfg_event[event_id], ifindex, __FUNCTION__);
  	    return -EINVAL;
      }
      return 0;
  }
  
  
  
  /*** Server States ***/
  
  static int rtcfg_main_state_server_running(int ifindex, RTCFG_EVENT event_id,
  					   void* event_data)
  {
      struct rt_proc_call *call;
      struct rtcfg_cmd    *cmd_event;
      struct rtcfg_device *rtcfg_dev;
      struct rtskb        *rtskb;
  
  
      switch (event_id) {
  	case RTCFG_CMD_ADD:
  	    call      = (struct rt_proc_call *)event_data;
  	    cmd_event = rtpc_get_priv(call, struct rtcfg_cmd);
  
  	    return rtcfg_server_add(cmd_event);
  
  	case RTCFG_CMD_DEL:
  	    call      = (struct rt_proc_call *)event_data;
  	    cmd_event = rtpc_get_priv(call, struct rtcfg_cmd);
  
  	    return rtcfg_server_del(cmd_event);
  
  	case RTCFG_CMD_WAIT:
  	    call = (struct rt_proc_call *)event_data;
  
  	    rtcfg_dev = &device[ifindex];
  
  	    if (rtcfg_dev->spec.srv.clients_configured ==
  		rtcfg_dev->other_stations)
  		rtpc_complete_call(call, 0);
  	    else
  		rtcfg_queue_blocking_call(ifindex, call);
  
  	    rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
  	    return -CALL_PENDING;
  
  	case RTCFG_CMD_READY:
  	    call = (struct rt_proc_call *)event_data;
  
  	    rtcfg_dev = &device[ifindex];
  
  	    if (rtcfg_dev->stations_ready == rtcfg_dev->other_stations)
  		rtpc_complete_call(call, 0);
  	    else
  		rtcfg_queue_blocking_call(ifindex, call);
  
  	    if (!test_and_set_bit(RTCFG_FLAG_READY, &rtcfg_dev->flags))
  		rtcfg_send_ready(ifindex);
  
  	    rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
  	    return -CALL_PENDING;
  
  	case RTCFG_CMD_DETACH:
  	    call      = (struct rt_proc_call *)event_data;
  	    cmd_event = rtpc_get_priv(call, struct rtcfg_cmd);
  
  	    return rtcfg_server_detach(ifindex, cmd_event);
  
  	case RTCFG_FRM_ANNOUNCE_NEW:
  	case RTCFG_FRM_ANNOUNCE_REPLY:
  	    rtskb = (struct rtskb *)event_data;
  	    return rtcfg_server_recv_announce(ifindex, event_id, rtskb);
  
  	case RTCFG_FRM_ACK_CFG:
  	    rtskb = (struct rtskb *)event_data;
  	    return rtcfg_server_recv_ack(ifindex, rtskb);
  
  	case RTCFG_FRM_READY:
  	case RTCFG_FRM_HEARTBEAT:
  	    rtskb = (struct rtskb *)event_data;
  	    return rtcfg_server_recv_simple_frame(ifindex, event_id, rtskb);
  
  	default:
  	    rtdm_mutex_unlock(&device[ifindex].dev_mutex);
  
  	    RTCFG_DEBUG(1, "RTcfg: unknown event %s for rtdev %d in %s()\n",
  			rtcfg_event[event_id], ifindex, __FUNCTION__);
  	    return -EINVAL;
      }
      return 0;
  }
  
  
  
  /*** Server Command Event Handlers ***/
  
  static int rtcfg_server_add(struct rtcfg_cmd *cmd_event)
  {
      struct rtcfg_device     *rtcfg_dev;
      struct rtcfg_connection *conn;
      struct rtcfg_connection *new_conn;
      struct list_head        *entry;
      unsigned int            addr_type;
  
      rtcfg_dev = &device[cmd_event->internal.data.ifindex];
      addr_type = cmd_event->args.add.addr_type & RTCFG_ADDR_MASK;
  
      new_conn = cmd_event->args.add.conn_buf;
      memset(new_conn, 0, sizeof(struct rtcfg_connection));
  
      new_conn->ifindex      = cmd_event->internal.data.ifindex;
      new_conn->state        = RTCFG_CONN_SEARCHING;
      new_conn->addr_type    = cmd_event->args.add.addr_type;
  #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_RTIPV4)
      new_conn->addr.ip_addr = cmd_event->args.add.ip_addr;
  #endif
      new_conn->stage1_data  = cmd_event->args.add.stage1_data;
      new_conn->stage1_size  = cmd_event->args.add.stage1_size;
      new_conn->burstrate    = rtcfg_dev->burstrate;
      new_conn->cfg_timeout  = ((u64)cmd_event->args.add.timeout) * 1000000;
  
      if (cmd_event->args.add.addr_type == RTCFG_ADDR_IP) {
  #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_RTIPV4)
  	struct rtnet_device *rtdev;
  
  	/* MAC address yet unknown -> use broadcast address */
  	rtdev = rtdev_get_by_index(cmd_event->internal.data.ifindex);
  	if (rtdev == NULL) {
  	    rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  	    return -ENODEV;
  	}
  	memcpy(new_conn->mac_addr, rtdev->broadcast, MAX_ADDR_LEN);
  	rtdev_dereference(rtdev);
  #else /* !CONFIG_XENO_DRIVERS_NET_RTIPV4 */
  	return -EPROTONOSUPPORT;
  #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4 */
      } else
  	memcpy(new_conn->mac_addr, cmd_event->args.add.mac_addr, MAX_ADDR_LEN);
  
      /* get stage 2 file */
      if (cmd_event->args.add.stage2_file != NULL) {
  	if (cmd_event->args.add.stage2_file->buffer != NULL) {
  	    new_conn->stage2_file = cmd_event->args.add.stage2_file;
  	    rtcfg_add_file(new_conn->stage2_file);
  
  	    cmd_event->args.add.stage2_file = NULL;
  	} else {
  	    new_conn->stage2_file =
  		rtcfg_get_file(cmd_event->args.add.stage2_file->name);
  	    if (new_conn->stage2_file == NULL) {
  		rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  		return 1;
  	    }
  	}
      }
  
      list_for_each(entry, &rtcfg_dev->spec.srv.conn_list) {
  	conn = list_entry(entry, struct rtcfg_connection, entry);
  
  	if (
  #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_RTIPV4)
  	    ((addr_type == RTCFG_ADDR_IP) &&
  	     (conn->addr.ip_addr == cmd_event->args.add.ip_addr)) ||
  #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4 */
  	    ((addr_type == RTCFG_ADDR_MAC) &&
  	     (memcmp(conn->mac_addr, new_conn->mac_addr,
  		     MAX_ADDR_LEN) == 0))
  	   ) {
  	    rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
  	    if ((new_conn->stage2_file) &&
  		(rtcfg_release_file(new_conn->stage2_file) == 0)) {
  		/* Note: This assignment cannot overwrite a valid file pointer.
  		 * Effectively, it will only be executed when
  		 * new_conn->stage2_file is the pointer originally passed by
  		 * rtcfg_ioctl. But checking this assumptions does not cause
  		 * any harm :o)
  		 */
  		RTNET_ASSERT(cmd_event->args.add.stage2_file == NULL, ;);
  
  		cmd_event->args.add.stage2_file = new_conn->stage2_file;
  	    }
  
  	    return -EEXIST;
  	}
      }
  
      list_add_tail(&new_conn->entry, &rtcfg_dev->spec.srv.conn_list);
      rtcfg_dev->other_stations++;
  
      rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
      cmd_event->args.add.conn_buf    = NULL;
      cmd_event->args.add.stage1_data = NULL;
  
      return 0;
  }
  
  
  
  static int rtcfg_server_del(struct rtcfg_cmd *cmd_event)
  {
      struct rtcfg_connection *conn;
      struct list_head        *entry;
      unsigned int            addr_type;
      struct rtcfg_device     *rtcfg_dev;
  
  
      rtcfg_dev = &device[cmd_event->internal.data.ifindex];
      addr_type = cmd_event->args.add.addr_type & RTCFG_ADDR_MASK;
  
      list_for_each(entry, &rtcfg_dev->spec.srv.conn_list) {
  	conn = list_entry(entry, struct rtcfg_connection, entry);
  
  	if ((addr_type == conn->addr_type) && (
  #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_RTIPV4)
  	     ((addr_type == RTCFG_ADDR_IP) &&
  	      (conn->addr.ip_addr == cmd_event->args.add.ip_addr)) ||
  #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4 */
  	     ((addr_type == RTCFG_ADDR_MAC) &&
  	      (memcmp(conn->mac_addr, cmd_event->args.add.mac_addr,
  		      MAX_ADDR_LEN) == 0)))) {
  	    list_del(&conn->entry);
  	    rtcfg_dev->other_stations--;
  
  	    if (conn->state > RTCFG_CONN_SEARCHING) {
  		rtcfg_dev->stations_found--;
  		if (conn->state >= RTCFG_CONN_STAGE_2)
  		    rtcfg_dev->spec.srv.clients_configured--;
  		if (conn->flags & _RTCFG_FLAG_READY)
  		    rtcfg_dev->stations_ready--;
  	    }
  
  	    if ((conn->stage2_file) &&
  		(rtcfg_release_file(conn->stage2_file) == 0))
  		cmd_event->args.del.stage2_file = conn->stage2_file;
  
  	    rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
  	    cmd_event->args.del.conn_buf = conn;
  
  	    return 0;
  	}
      }
  
      rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
      return -ENOENT;
  }
  
  
  
  static int rtcfg_server_detach(int ifindex, struct rtcfg_cmd *cmd_event)
  {
      struct rtcfg_connection *conn;
      struct rtcfg_device     *rtcfg_dev = &device[ifindex];
  
  
      if (!list_empty(&rtcfg_dev->spec.srv.conn_list)) {
  	conn = list_entry(rtcfg_dev->spec.srv.conn_list.next,
  			  struct rtcfg_connection, entry);
  
  	list_del(&conn->entry);
  	rtcfg_dev->other_stations--;
  
  	if (conn->state > RTCFG_CONN_SEARCHING) {
  	    rtcfg_dev->stations_found--;
  	    if (conn->state >= RTCFG_CONN_STAGE_2)
  		rtcfg_dev->spec.srv.clients_configured--;
  	    if (conn->flags & _RTCFG_FLAG_READY)
  		rtcfg_dev->stations_ready--;
  	}
  
  	if ((conn->stage2_file) &&
  	    (rtcfg_release_file(conn->stage2_file) == 0))
  	    cmd_event->args.detach.stage2_file = conn->stage2_file;
  
  	rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
  	cmd_event->args.detach.conn_buf = conn;
  
  	return -EAGAIN;
      }
  
      if (test_and_clear_bit(FLAG_TIMER_STARTED, &rtcfg_dev->flags))
  	rtdm_timer_destroy(&rtcfg_dev->timer);
      rtcfg_reset_device(ifindex);
  
      rtcfg_next_main_state(ifindex, RTCFG_MAIN_OFF);
  
      rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
      return 0;
  }
  
  
  
  /*** Server Frame Event Handlers ***/
  
  static int rtcfg_server_recv_announce(int ifindex, RTCFG_EVENT event_id,
  				      struct rtskb *rtskb)
  {
      struct rtcfg_device       *rtcfg_dev = &device[ifindex];
      struct list_head          *entry;
      struct rtcfg_frm_announce *announce;
      struct rtcfg_connection   *conn;
  
  
      if (rtskb->len < sizeof(struct rtcfg_frm_announce)) {
  	rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  	RTCFG_DEBUG(1, "RTcfg: received invalid announce frame\n");
  	return -EINVAL;
      }
  
      announce = (struct rtcfg_frm_announce *)rtskb->data;
  
      list_for_each(entry, &rtcfg_dev->spec.srv.conn_list) {
  	conn = list_entry(entry, struct rtcfg_connection, entry);
  
  	switch (announce->addr_type) {
  #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_RTIPV4)
  	    u32 announce_addr;
  	    case RTCFG_ADDR_IP:
  		memcpy(&announce_addr, announce->addr, 4);
  
  		if (((conn->addr_type & RTCFG_ADDR_MASK) == RTCFG_ADDR_IP) &&
  		    (announce_addr == conn->addr.ip_addr)) {
  		    /* save MAC address - Ethernet-specific! */
  		    memcpy(conn->mac_addr, rtskb->mac.ethernet->h_source,
  			   ETH_ALEN);
  
  		    /* update routing table */
  		    rt_ip_route_add_host(conn->addr.ip_addr, conn->mac_addr,
  					 rtskb->rtdev);
  
  		    /* remove IP address */
  		    __rtskb_pull(rtskb, RTCFG_ADDRSIZE_IP);
  
  		    rtcfg_do_conn_event(conn, event_id, rtskb);
  
  		    goto out;
  		}
  		break;
  #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4 */
  
  	    case RTCFG_ADDR_MAC:
  		/* Ethernet-specific! */
  		if (memcmp(conn->mac_addr, rtskb->mac.ethernet->h_source,
  			   ETH_ALEN) == 0) {
  		    rtcfg_do_conn_event(conn, event_id, rtskb);
  
  		    goto out;
  		}
  		break;
  	}
      }
  
  out:
      rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
      kfree_rtskb(rtskb);
      return 0;
  }
  
  
  
  static int rtcfg_server_recv_ack(int ifindex, struct rtskb *rtskb)
  {
      struct rtcfg_device     *rtcfg_dev = &device[ifindex];
      struct list_head        *entry;
      struct rtcfg_connection *conn;
  
  
      if (rtskb->len < sizeof(struct rtcfg_frm_ack_cfg)) {
  	rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  	RTCFG_DEBUG(1, "RTcfg: received invalid ack_cfg frame\n");
  	return -EINVAL;
      }
  
      list_for_each(entry, &rtcfg_dev->spec.srv.conn_list) {
  	conn = list_entry(entry, struct rtcfg_connection, entry);
  
  	/* find the corresponding connection - Ethernet-specific! */
  	if (memcmp(conn->mac_addr,
  		   rtskb->mac.ethernet->h_source, ETH_ALEN) != 0)
  	    continue;
  
  	rtcfg_do_conn_event(conn, RTCFG_FRM_ACK_CFG, rtskb);
  
  	break;
      }
  
      rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
      kfree_rtskb(rtskb);
      return 0;
  }
  
  
  
  static int rtcfg_server_recv_simple_frame(int ifindex, RTCFG_EVENT event_id,
  					  struct rtskb *rtskb)
  {
      struct rtcfg_device     *rtcfg_dev = &device[ifindex];
      struct list_head        *entry;
      struct rtcfg_connection *conn;
  
  
      list_for_each(entry, &rtcfg_dev->spec.srv.conn_list) {
  	conn = list_entry(entry, struct rtcfg_connection, entry);
  
  	/* find the corresponding connection - Ethernet-specific! */
  	if (memcmp(conn->mac_addr,
  		   rtskb->mac.ethernet->h_source, ETH_ALEN) != 0)
  	    continue;
  
  	rtcfg_do_conn_event(conn, event_id, rtskb);
  
  	break;
      }
  
      rtdm_mutex_unlock(&rtcfg_dev->dev_mutex);
  
      kfree_rtskb(rtskb);
      return 0;
  }
  
  
  
  /*** Utility Functions ***/
  
  void rtcfg_queue_blocking_call(int ifindex, struct rt_proc_call *call)
  {
      rtdm_lockctx_t      context;
      struct rtcfg_device *rtcfg_dev = &device[ifindex];
  
  
      rtdm_lock_get_irqsave(&rtcfg_dev->event_calls_lock, context);
      list_add_tail(&call->list_entry, &rtcfg_dev->event_calls);
      rtdm_lock_put_irqrestore(&rtcfg_dev->event_calls_lock, context);
  }
  
  
  
  struct rt_proc_call *rtcfg_dequeue_blocking_call(int ifindex)
  {
      rtdm_lockctx_t      context;
      struct rt_proc_call *call;
      struct rtcfg_device *rtcfg_dev = &device[ifindex];
  
  
      rtdm_lock_get_irqsave(&rtcfg_dev->event_calls_lock, context);
      if (!list_empty(&rtcfg_dev->event_calls)) {
  	call = (struct rt_proc_call *)rtcfg_dev->event_calls.next;
  	list_del(&call->list_entry);
      } else
  	call = NULL;
      rtdm_lock_put_irqrestore(&rtcfg_dev->event_calls_lock, context);
  
      return call;
  }
  
  
  
  void rtcfg_complete_cmd(int ifindex, RTCFG_EVENT event_id, int result)
  {
      struct rt_proc_call *call;
      struct rtcfg_cmd    *cmd_event;
  
  
      while (1) {
  	call = rtcfg_dequeue_blocking_call(ifindex);
  	if (call == NULL)
  	    break;
  
  	cmd_event = rtpc_get_priv(call, struct rtcfg_cmd);
  
  	rtpc_complete_call(call,
  	    (cmd_event->internal.data.event_id == event_id) ? result
  							    : -EINVAL);
      }
  }
  
  
  
  void rtcfg_reset_device(int ifindex)
  {
      struct rtcfg_device *rtcfg_dev = &device[ifindex];
  
  
      rtcfg_dev->other_stations = 0;
      rtcfg_dev->stations_found = 0;
      rtcfg_dev->stations_ready = 0;
      rtcfg_dev->flags          = 0;
      rtcfg_dev->burstrate      = 0;
  
      memset(&rtcfg_dev->spec, 0, sizeof(rtcfg_dev->spec));
      INIT_LIST_HEAD(&rtcfg_dev->spec.srv.conn_list);
  }
  
  
  
  void rtcfg_init_state_machines(void)
  {
      int                 i;
      struct rtcfg_device *rtcfg_dev;
  
  
      memset(device, 0, sizeof(device));
  
      for (i = 0; i < MAX_RT_DEVICES; i++) {
  	rtcfg_dev = &device[i];
  	rtcfg_dev->state = RTCFG_MAIN_OFF;
  
  	rtdm_mutex_init(&rtcfg_dev->dev_mutex);
  
  	INIT_LIST_HEAD(&rtcfg_dev->event_calls);
  	rtdm_lock_init(&rtcfg_dev->event_calls_lock);
      }
  }
  
  
  
  void rtcfg_cleanup_state_machines(void)
  {
      int                     i;
      struct rtcfg_device     *rtcfg_dev;
      struct rtcfg_connection *conn;
      struct list_head        *entry;
      struct list_head        *tmp;
      struct rt_proc_call     *call;
  
  
      for (i = 0; i < MAX_RT_DEVICES; i++) {
  	rtcfg_dev = &device[i];
  
  	if (test_and_clear_bit(FLAG_TIMER_STARTED, &rtcfg_dev->flags))
  		rtdm_timer_destroy(&rtcfg_dev->timer);
  
  	/*
  	 * No need to synchronize with rtcfg_timer here: the task running
  	 * rtcfg_timer is already dead.
  	 */
  
  	rtdm_mutex_destroy(&rtcfg_dev->dev_mutex);
  
  	if (rtcfg_dev->state == RTCFG_MAIN_SERVER_RUNNING) {
  	    list_for_each_safe(entry, tmp, &rtcfg_dev->spec.srv.conn_list) {
  		conn = list_entry(entry, struct rtcfg_connection, entry);
  
  		if (conn->stage1_data != NULL)
  		    kfree(conn->stage1_data);
  
  		if ((conn->stage2_file != NULL) &&
  		    (rtcfg_release_file(conn->stage2_file) == 0)){
  		    vfree(conn->stage2_file->buffer);
  		    kfree(conn->stage2_file);
  		}
  
  		kfree(entry);
  	    }
  	} else if (rtcfg_dev->state != RTCFG_MAIN_OFF) {
  	    if (rtcfg_dev->spec.clt.station_addr_list != NULL)
  		kfree(rtcfg_dev->spec.clt.station_addr_list);
  
  	    if (rtcfg_dev->spec.clt.stage2_chain != NULL)
  		kfree_rtskb(rtcfg_dev->spec.clt.stage2_chain);
  	}
  
  	while (1) {
  	    call = rtcfg_dequeue_blocking_call(i);
  	    if (call == NULL)
  		break;
  
  	    rtpc_complete_call_nrt(call, -ENODEV);
  	}
      }
  }
  