patch-2.1.132 linux/net/irda/irlap_event.c

Next file: linux/net/irda/irlap_frame.c
Previous file: linux/net/irda/irlap_comp.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.131/linux/net/irda/irlap_event.c linux/net/irda/irlap_event.c
@@ -0,0 +1,1831 @@
+/*********************************************************************
+ *                
+ * Filename:      irlap_event.c
+ * Version:       0.1
+ * Description:   IrLAP state machine implementation
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sat Aug 16 00:59:29 1997
+ * Modified at:   Mon Dec 14 14:16:00 1998
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
+ *                        Thomas Davis <ratbert@radiks.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.
+ *
+ *     Neither Dag Brattli nor University of Tromsų admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlap_event.h>
+
+#include <net/irda/timer.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/qos.h>
+
+#include <net/irda/irda_device.h>
+
+static int irlap_state_ndm    ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_query  ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reply  ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_conn   ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_setup  ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_offline( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_xmit_p ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_pclose ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_nrm_p  ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, 
+				  struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reset  ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_nrm_s  ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_xmit_s ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_sclose ( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info);
+
+static char *irlap_event[] = {
+	"DISCOVERY_REQUEST",
+	"CONNECT_REQUEST",
+	"CONNECT_RESPONSE",
+	"DISCONNECT_REQUEST",
+	"DATA_REQUEST",
+	"RESET_REQUEST",
+	"SEND_I_CMD",
+	"RECV_DISCOVERY_XID_CMD",
+	"RECV_DISCOVERY_XID_RSP",
+	"RECV_SNRM_CMD",
+	"RECV_TEST_CMD",
+	"RECV_UA_RSP",
+	"RECV_DM_RSP",
+	"RECV_I_CMD",
+	"RECV_I_RSP",
+	"RECV_UI_FRAME",
+	"RECV_FRMR_RSP",
+	"RECV_RR_CMD",
+	"RECV_RR_RSP",
+	"RECV_RNR_FRAME",
+	"RECV_DISC_FRAME",
+	"SLOT_TIMER_EXPIRED",
+	"QUERY_TIMER_EXPIRED",
+	"FINAL_TIMER_EXPIRED",
+	"POLL_TIMER_EXPIRED",
+	"DISCOVERY_TIMER_EXPIRED",
+	"WD_TIMER_EXPIRED",
+	"BACKOFF_TIMER_EXPIRED",
+};
+
+char *irlap_state[] = {
+	"LAP_NDM",
+	"LAP_QUERY",
+	"LAP_REPLY",
+	"LAP_CONN",
+	"LAP_SETUP",
+	"LAP_OFFLINE",
+	"LAP_XMIT_P",
+	"LAP_PCLOSE",
+	"LAP_NRM_P",
+	"LAP_RESET_WAIT",
+	"LAP_RESET",
+	"LAP_NRM_S",
+	"LAP_XMIT_S",
+	"LAP_SCLOSE",
+};
+
+static int (*state[])( struct irlap_cb *self, IRLAP_EVENT event, 
+		       struct sk_buff *skb, struct irlap_info *info) = 
+{ 
+	irlap_state_ndm,
+	irlap_state_query,
+	irlap_state_reply,
+	irlap_state_conn,
+	irlap_state_setup,
+	irlap_state_offline,
+	irlap_state_xmit_p,
+	irlap_state_pclose,
+	irlap_state_nrm_p,
+	irlap_state_reset_wait,
+	irlap_state_reset,
+	irlap_state_nrm_s,
+	irlap_state_xmit_s,
+	irlap_state_sclose,
+};
+
+/*
+ * Function irda_poll_timer_expired (data)
+ *
+ *    
+ *
+ */
+static void irlap_poll_timer_expired( unsigned long data)
+{
+	struct irlap_cb *self = (struct irlap_cb *) data;
+	
+	DEBUG( 4, "Poll timer expired!\n");
+	
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == LAP_MAGIC, return;);
+	
+	irlap_do_event( self, POLL_TIMER_EXPIRED, NULL, NULL);
+}
+
+void irlap_start_poll_timer( struct irlap_cb *self, int timeout)
+{
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == LAP_MAGIC, return;);
+
+#ifdef CONFIG_IRDA_FAST_RR
+	if ( skb_queue_len( &self->tx_list) == 0) {
+		if ( self->fast_RR == TRUE) {
+			/*
+			 *  Assert that the fast poll timer has not reached the
+			 *  normal poll timer yet
+			 */
+			if ( self->fast_RR_timeout < timeout) {
+				/*
+				 *  FIXME: this should be a more configurable
+				 *         function
+				 */
+				self->fast_RR_timeout += 15;
+
+				/* Use this fast(er) timeout instead */
+				timeout = self->fast_RR_timeout;
+			}
+		} else {
+			self->fast_RR = TRUE;
+
+			/* Start with just 1 ms */
+			self->fast_RR_timeout = 1;
+			timeout = 1;
+		}
+	} else
+		self->fast_RR = FALSE;
+
+	DEBUG( 4, __FUNCTION__ "(), Timeout=%d\n", timeout);
+#endif
+	irda_start_timer( &self->poll_timer, timeout, 
+			  (unsigned long) self, irlap_poll_timer_expired);
+}
+
+/*
+ * Function irlap_do_event (event, skb, info)
+ *
+ *    Rushes through the state machine without any delay. If state = XMIT
+ *    then send queued data frames. 
+ */
+void irlap_do_event( struct irlap_cb *self, IRLAP_EVENT event, 
+		     struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret;
+	int iter = 0;
+	
+	if ( !self || self->magic != LAP_MAGIC) {
+		DEBUG( 0, "irlap_do_event: bad pointer *self\n");
+		return;
+	}
+		
+  	DEBUG( 4, "irlap_do_event: event = %s, state = %s\n", 
+	       irlap_event[ event], irlap_state[ self->state]); 
+
+	/* 
+	 *  Do event, this implementation does not deal with pending events. 
+	 *  This is because I don't see the need for this. DB
+	 */
+	ret = (*state[ self->state]) ( self, event, skb, info);
+	
+	/* 
+	 *  Check if we have switched to XMIT state? If so, send queued data 
+	 *  frames if any, if -1 is returned it means that we are not allowed 
+	 *  to send any more frames.  
+	 */
+	while (( self->state == LAP_XMIT_P) || ( self->state == LAP_XMIT_S)) { 
+		if ( skb_queue_len( &self->tx_list) > 0) {
+			
+			struct sk_buff *skb = skb_dequeue( &self->tx_list); 
+			ASSERT( skb != NULL, return;);
+			
+			DEBUG( 4, "** Sending queued data frames\n");
+			ret = (*state[ self->state])( self, SEND_I_CMD, skb,
+						      NULL);
+			if ( ret == -EPROTO)
+				return; /* Try again later! */
+		} else
+			return;
+
+		/* Just in case :-) */
+		if (iter++ > 100) {
+			DEBUG( 0, __FUNCTION__ "(), *** breaking!! ***\n");
+			return;
+		}
+	}
+}
+
+/*
+ * Function irlap_next_state (self, state)
+ *
+ *    Switches state and provides debug information
+ *
+ */
+void irlap_next_state( struct irlap_cb *self, IRLAP_STATE state) 
+{
+	
+	if ( !self || self->magic != LAP_MAGIC) {
+		DEBUG( 4, "irlap_next_state: I have lost myself!\n");
+		return;
+	}
+	
+	DEBUG( 4, "next LAP state = %s\n", irlap_state[ state]);
+
+	self->state = state;
+
+	/*
+	 *  If we are swithing away from a XMIT state then we are allowed to 
+	 *  transmit a maximum number of bytes again when we enter the XMIT 
+	 *  state again. Since its possible to "switch" from XMIT to XMIT and
+	 *  we cannot do this when swithing into the XMIT state :-)
+	 */
+	if (( state != LAP_XMIT_P) && ( state != LAP_XMIT_S))
+		self->bytes_left = self->window_bytes;
+}
+
+/*
+ * Function irlap_state_ndm (event, skb, frame)
+ *
+ *    NDM (Normal Disconnected Mode) state
+ *
+ */
+static int irlap_state_ndm( struct irlap_cb *self, IRLAP_EVENT event, 
+			    struct sk_buff *skb,  struct irlap_info *info) 
+{
+	DISCOVERY *discovery_rsp;
+	int ret = 0;
+	
+	DEBUG( 4, "irlap_state_ndm()\n");
+	
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+
+	switch( event) {
+	case CONNECT_REQUEST:
+		ASSERT( self->irdev != NULL, return -1;);
+
+		if ( irda_device_is_media_busy( self->irdev)) {
+			DEBUG( 0, __FUNCTION__
+			      "(), CONNECT_REQUEST: media busy!\n");
+			
+			/* Always switch state before calling upper layers */
+			irlap_next_state( self, LAP_NDM);
+			
+			irlap_disconnect_indication( self, LAP_MEDIA_BUSY);
+		} else {
+			irlap_send_snrm_frame( self, &self->qos_rx);
+			
+			/* Start Final-bit timer */
+			irlap_start_final_timer( self, self->final_timeout);
+
+			self->retry_count = 0;
+			irlap_next_state( self, LAP_SETUP);
+		}
+		break;
+
+	case RECV_SNRM_CMD:
+		self->daddr = info->daddr;
+		self->caddr = info->caddr;
+		
+		irlap_next_state( self, LAP_CONN);
+
+		irlap_connect_indication( self, skb);
+		break;
+
+	case DISCOVERY_REQUEST:		
+		ASSERT( info != NULL, return -1;);
+
+	 	if ( irda_device_is_media_busy( self->irdev)) {
+ 			DEBUG(0, "irlap_discovery_request: media busy!\n"); 
+			/* irlap->log.condition = MEDIA_BUSY; */
+			
+			/* Always switch state before calling upper layers */
+			irlap_next_state( self, LAP_NDM); 
+			
+			/* This will make IrLMP try again */
+ 			irlap_discovery_confirm( self, NULL);
+			return 0;
+	 	} 
+		
+		self->S = info->S;
+		self->s = info->s;
+		irlap_send_discovery_xid_frame( self, info->S, info->s, TRUE,
+						info->discovery);
+		self->s++;
+
+		irlap_start_slot_timer( self, SLOT_TIMEOUT);
+		irlap_next_state( self, LAP_QUERY);
+		break;
+
+	case RECV_DISCOVERY_XID_CMD:
+		ASSERT( info != NULL, return -1;);
+
+		/* Assert that this is not the final slot */
+		if ( info->s <= info->S) {
+			self->daddr = info->daddr; 
+			self->slot = irlap_generate_rand_time_slot( info->S,
+								    info->s);
+			DEBUG( 4, "XID_CMD: S=%d, s=%d, slot %d\n", info->S, 
+			       info->s, self->slot);
+
+			if ( self->slot == info->s) {
+				discovery_rsp = irlmp_get_discovery_response();
+				
+				DEBUG( 4, "Sending XID rsp 1\n");
+				irlap_send_discovery_xid_frame( self, info->S, 
+								self->slot, 
+								FALSE,
+								discovery_rsp);
+				self->frame_sent = TRUE;
+			} else
+				self->frame_sent = FALSE;
+			
+			irlap_start_query_timer( self, QUERY_TIMEOUT);
+			irlap_next_state( self, LAP_REPLY);
+		}
+
+		dev_kfree_skb( skb);
+		break;
+		
+	default:
+		/* 	DEBUG( 0, "irlap_state_ndm: Unknown event"); */
+		ret = -1;
+		break;
+	}	
+	return ret;
+}
+
+/*
+ * Function irlap_state_query (event, skb, info)
+ *
+ *    QUERY state
+ *
+ */
+static int irlap_state_query( struct irlap_cb *self, IRLAP_EVENT event, 
+			      struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret = 0;
+
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+
+	switch( event) {
+	case RECV_DISCOVERY_XID_RSP:
+		ASSERT( info != NULL, return -1;);
+		ASSERT( info->discovery != NULL, return -1;);
+
+		DEBUG( 4, "irlap_state_query(), daddr=%08x\n", 
+		       info->discovery->daddr);
+
+		hashbin_insert( self->discovery_log, 
+				(QUEUE *) info->discovery,
+				info->discovery->daddr, NULL);
+
+		dev_kfree_skb( skb);
+
+		/* Keep state */
+		irlap_next_state( self, LAP_QUERY); 
+		break;
+	case SLOT_TIMER_EXPIRED:
+		if ( self->s < self->S) {
+			irlap_send_discovery_xid_frame( self, self->S, 
+							self->s, TRUE,
+							self->discovery_cmd);
+			self->s++;
+			irlap_start_slot_timer( self, SLOT_TIMEOUT);
+			
+			/* Keep state */
+			irlap_next_state( self, LAP_QUERY);
+		} else {
+			/* This is the final slot! */
+			irlap_send_discovery_xid_frame( self, self->S, 0xff, 
+							TRUE,
+							self->discovery_cmd);
+
+			/* Always switch state before calling upper layers */
+			irlap_next_state( self, LAP_NDM);
+	
+			/*
+			 *  We are now finished with the discovery procedure, 
+			 *  so now we must return the results
+			 */
+			irlap_discovery_confirm( self, self->discovery_log);
+		}
+		break;
+	default:
+		DEBUG( 4, "irlap_state_query: Unknown event %d, %s\n", event, 
+		       irlap_event[event]);
+
+		if ( skb != NULL) {
+			dev_kfree_skb( skb);
+		}
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_reply (self, event, skb, info)
+ *
+ *    REPLY, we have received a XID discovery frame from a device and we
+ *    are waiting for the right time slot to send a response XID frame
+ * 
+ */
+static int irlap_state_reply( struct irlap_cb *self, IRLAP_EVENT event, 
+			      struct sk_buff *skb, struct irlap_info *info) 
+{
+	DISCOVERY *discovery_rsp;
+	int ret=0;
+
+	DEBUG( 4, "irlap_state_reply()\n");
+
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+
+	switch( event) {
+	case QUERY_TIMER_EXPIRED:
+		DEBUG( 0, "irlap_state_reply: QUERY_TIMER_EXPIRED <%ld>\n",
+		       jiffies);
+		irlap_next_state( self, LAP_NDM);
+		break;
+	case RECV_DISCOVERY_XID_CMD:
+		ASSERT( info != NULL, return -1;);
+		/*
+		 *  Last frame?
+		 */
+		if ( info->s == 0xff) {
+			del_timer( &self->query_timer);
+			
+			/* info->log.condition = REMOTE; */
+
+			/* Always switch state before calling upper layers */
+			irlap_next_state( self, LAP_NDM);
+
+			irlap_discovery_indication( self, info->discovery); 
+		} else if (( info->s >= self->slot) && 
+			   ( !self->frame_sent)) {
+			DEBUG( 4, "Sending XID rsp 2, s=%d\n", info->s); 
+			discovery_rsp = irlmp_get_discovery_response();
+
+			irlap_send_discovery_xid_frame( self, info->S, 
+							self->slot, FALSE,
+							discovery_rsp);
+
+			self->frame_sent = TRUE;
+			irlap_next_state( self, LAP_REPLY);
+		}
+		dev_kfree_skb( skb);
+		break;
+	default:
+		DEBUG( 0, __FUNCTION__ "(), Unknown event %d, %s\n", event,
+		       irlap_event[event]);
+
+		if ( skb != NULL)
+			dev_kfree_skb( skb);
+
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_conn (event, skb, info)
+ *
+ *    CONN, we have received a SNRM command and is waiting for the upper
+ *    layer to accept or refuse connection 
+ *
+ */
+static int irlap_state_conn( struct irlap_cb *self, IRLAP_EVENT event, 
+			     struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret = 0;
+
+	DEBUG( 0, __FUNCTION__ "(), event=%s\n", irlap_event[ event]);
+
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+
+	switch( event) {
+	case CONNECT_RESPONSE:
+		skb_pull( skb, 11);
+
+		ASSERT( self->irdev != NULL, return -1;);
+		irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb);
+
+		irlap_initiate_connection_state( self);
+
+		/*
+		 * We are allowed to send two frames!
+		 */
+		irlap_send_ua_response_frame( self, &self->qos_rx);
+		irlap_send_ua_response_frame( self, &self->qos_rx);
+		
+		irlap_apply_connection_parameters( self, &self->qos_tx);
+
+		/*
+		 *  The WD-timer could be set to the duration of the P-timer 
+		 *  for this case, but it is recommomended to use twice the 
+		 *  value (note 3 IrLAP p. 60). 
+		 */
+		irlap_start_wd_timer( self, self->wd_timeout);
+		irlap_next_state( self, LAP_NRM_S);
+		break;
+
+	case RECV_SNRM_CMD:
+		DEBUG( 3, "irlap_state_conn: event RECV_SNRM_CMD!\n");
+#if 0
+		irlap_next_state( self, LAP_NDM);
+#endif
+		break;
+
+	case RECV_DISCOVERY_XID_CMD:
+		DEBUG( 3, "irlap_state_conn: event RECV_DISCOVER_XID_CMD!\n");
+		irlap_next_state( self, LAP_NDM);
+		break;
+
+	case DISCONNECT_REQUEST:
+		irlap_send_dm_frame( self);
+		irlap_next_state( self, LAP_CONN);
+		break;
+
+	default:
+		DEBUG( 0, "irlap_state_conn: Unknown event %d, %s\n", event, 
+		       irlap_event[event]);
+		ret = -1;
+		break;
+	}
+	
+	return ret;
+}
+
+/*
+ * Function irlap_state_setup (event, skb, frame)
+ *
+ *    SETUP state, The local layer has transmitted a SNRM command frame to
+ *    a remote peer layer and is awaiting a reply .
+ *
+ */
+static int irlap_state_setup( struct irlap_cb *self, IRLAP_EVENT event, 
+			      struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret = 0;
+
+	DEBUG( 4, __FUNCTION__ "()\n");
+	
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+
+	switch( event) {
+	case FINAL_TIMER_EXPIRED:
+		if ( self->retry_count < self->N3) {
+/* 
+ *  Perform random backoff, Wait a random number of time units, minimum 
+ *  duration half the time taken to transmitt a SNRM frame, maximum duration 
+ *  1.5 times the time taken to transmit a SNRM frame. So this time should 
+ *  between 15 msecs and 45 msecs.
+ */
+			irlap_start_backoff_timer( self, 2 + (jiffies % 3));
+		} else {
+			/* Always switch state before calling upper layers */
+			irlap_next_state( self, LAP_NDM);
+
+			irlap_disconnect_indication( self, LAP_FOUND_NONE);
+		}
+		break;
+	case BACKOFF_TIMER_EXPIRED:
+		irlap_send_snrm_frame( self, &self->qos_rx);
+		irlap_start_final_timer( self, self->final_timeout);
+		self->retry_count++;
+		break;
+
+	case RECV_SNRM_CMD:
+		DEBUG( 0, "irlap_state_setup: SNRM battle!\n");
+
+		ASSERT( skb != NULL, return 0;);
+		ASSERT( info != NULL, return 0;);
+
+		/*
+		 *  The device with the largest device address wins the battle
+		 *  (both have sent a SNRM command!)
+		 */
+		if ( info->daddr > self->saddr) {
+			del_timer( &self->final_timer);
+			irlap_initiate_connection_state( self);
+
+			ASSERT( self->irdev != NULL, return -1;);
+			irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb);
+			
+			irlap_send_ua_response_frame(self, &self->qos_rx);
+			irlap_apply_connection_parameters( self, &self->qos_tx);
+			irlap_connect_confirm( self, skb);
+			
+			/* 
+			 *  The WD-timer could be set to the duration of the
+			 *  P-timer for this case, but it is recommomended
+			 *  to use twice the value (note 3 IrLAP p. 60).  
+			 */
+			irlap_start_wd_timer( self, self->wd_timeout);
+			
+			irlap_next_state( self, LAP_NRM_S);
+		} else {
+			/* We just ignore the other device! */
+			irlap_next_state( self, LAP_SETUP);
+		}
+		break;
+	case RECV_UA_RSP:
+		/* Stop F-timer */
+		del_timer( &self->final_timer);
+
+		/* Initiate connection state */
+		irlap_initiate_connection_state( self);
+
+		/* Negotiate connection parameters */
+		IS_SKB( skb, return -1;);
+		ASSERT( skb->len > 10, return -1;);
+		skb_pull( skb, 10);
+
+		ASSERT( self->irdev != NULL, return -1;);
+		irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb);
+
+		irlap_apply_connection_parameters( self, &self->qos_tx); 
+		self->retry_count = 0;
+		irlap_send_rr_frame( self, CMD_FRAME);
+
+		irlap_start_final_timer( self, self->final_timeout/2);
+		irlap_next_state( self, LAP_NRM_P);
+
+		irlap_connect_confirm( self, skb);
+		break;
+
+	case RECV_DISC_FRAME:
+		del_timer( &self->final_timer);
+		irlap_next_state( self, LAP_NDM);
+
+		irlap_disconnect_indication( self, LAP_DISC_INDICATION);
+		break;
+
+       /* DM handled in irlap_frame.c, irlap_input() */
+		
+	default:
+		DEBUG( 4, "irlap_state_setup: Unknown event");
+		ret = -1;
+		break;
+	}	
+	return ret;
+}
+
+/*
+ * Function irlap_state_offline (self, event, skb, info)
+ *
+ *    OFFLINE state, not used for now!
+ *
+ */
+static int irlap_state_offline( struct irlap_cb *self, IRLAP_EVENT event, 
+				struct sk_buff *skb, struct irlap_info *info) 
+{
+	DEBUG( 0, __FUNCTION__ "(), Unknown event\n");
+
+	return -1;
+}
+
+/*
+ * Function irlap_state_xmit_p (self, event, skb, info)
+ * 
+ *    XMIT, Only the primary station has right to transmit, and we therefor
+ *    do not expect to receive any transmissions from other stations.  
+ *
+ */
+static int irlap_state_xmit_p( struct irlap_cb *self, IRLAP_EVENT event, 
+			       struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret = 0;
+	
+	ASSERT( self != NULL, return -ENODEV;);
+	ASSERT( self->magic == LAP_MAGIC, return -EBADR;);
+
+	DEBUG( 4, __FUNCTION__ "(), event=%s, vs=%d, vr=%d", 
+	       irlap_event[ event], self->vs, self->vr); 
+		
+	switch( event) {
+	case SEND_I_CMD:
+		ASSERT( skb != NULL, return -1;);
+		DEBUG( 4, "irlap_state_xmit: Window=%d\n", self->window);
+		
+		/*
+		 *  Only send frame if send-window > 0.
+		 */ 
+		if (( self->window > 0) && ( !self->remote_busy)) {
+
+			/*
+			 *  Test if we have transmitted more bytes over the 
+			 *  link than its possible to do with the current 
+			 *  speed and turn-around-time.
+			 */
+			if (( skb->len+self->bofs_count) > self->bytes_left) {
+				DEBUG( 4, __FUNCTION__ "(), Not allowed to "
+				       "transmit more bytes!\n");
+				skb_queue_head( &self->tx_list, skb);
+
+				/*
+				 *  We should switch state to LAP_NRM_P, but
+				 *  that is not possible since we must be sure
+				 *  that we poll the other side. Since we have
+				 *  used up our time, the poll timer should
+				 *  trigger anyway now,so we just wait for it
+				 *  DB
+				 */
+				return -EPROTO;
+			}
+			self->bytes_left -= ( skb->len + self->bofs_count);
+
+			/*
+			 *  Send data with poll bit cleared only if window > 1
+			 *  and there is more frames after this one to be sent
+			 */
+			if (( self->window > 1) && 
+			    skb_queue_len( &self->tx_list) > 0) 
+			{   
+				DEBUG( 4, __FUNCTION__ "(), window > 1\n");
+				irlap_send_data_primary( self, skb);
+				irlap_next_state( self, LAP_XMIT_P);
+			} else {
+				DEBUG( 4, __FUNCTION__ "(), window <= 1\n");
+				irlap_send_data_primary_poll( self, skb);
+				irlap_next_state( self, LAP_NRM_P);
+			}
+#ifdef CONFIG_IRDA_FAST_RR
+			/* Peer may want to reply immediately */
+			self->fast_RR = FALSE;
+#endif
+		} else {
+			DEBUG( 0, __FUNCTION__ 
+			       "(), Unable to send! remote busy?\n");
+			skb_queue_head( &self->tx_list, skb);
+
+			/*
+			 *  The next ret is important, because it tells 
+			 *  irlap_next_state _not_ to deliver more frames
+			 */
+			ret = -EPROTO;
+		}
+		break;
+	case DISCONNECT_REQUEST:
+		del_timer( &self->poll_timer);
+		irlap_wait_min_turn_around( self, &self->qos_tx);
+		irlap_send_disc_frame( self);
+		irlap_flush_all_queues( self);
+		irlap_start_final_timer( self, self->final_timeout);
+		self->retry_count = 0;
+		irlap_next_state( self, LAP_PCLOSE);
+		break;
+	case POLL_TIMER_EXPIRED:
+		irlap_send_rr_frame( self, CMD_FRAME);
+		irlap_start_final_timer( self, self->final_timeout);
+		irlap_next_state( self, LAP_NRM_P);
+		break;
+	default:
+		/* DEBUG( 0, "irlap_state_xmit: Unknown event"); */
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_pclose (event, skb, info)
+ *
+ *    PCLOSE state
+ */
+static int irlap_state_pclose( struct irlap_cb *self, IRLAP_EVENT event, 
+			       struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret = 0;
+
+	DEBUG( 0, __FUNCTION__ "()\n");
+	
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);	
+
+	switch( event) {
+	case RECV_UA_RSP:
+		del_timer( &self->final_timer);
+		
+		irlap_apply_default_connection_parameters( self);
+
+		/* Always switch state before calling upper layers */
+		irlap_next_state( self, LAP_NDM);
+
+		irlap_disconnect_indication( self, LAP_DISC_INDICATION);
+
+		break;
+	case FINAL_TIMER_EXPIRED:
+		if ( self->retry_count < self->N3) {
+			irlap_wait_min_turn_around( self, &self->qos_tx);
+			irlap_send_disc_frame( self);
+			irlap_start_final_timer( self, self->final_timeout);
+			self->retry_count++;
+			/* Keep state */
+		} else {
+			irlap_apply_default_connection_parameters( self);
+
+			/* 
+			 *  Always switch state before calling upper layers 
+			 */
+			irlap_next_state( self, LAP_NDM);
+
+			irlap_disconnect_indication( self, LAP_NO_RESPONSE);
+		}
+		break;
+	default:
+		DEBUG( 0, "irlap_state_pclose: Unknown event %d\n", event);
+		ret = -1;
+		break;	
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_nrm_p (self, event, skb, info)
+ *
+ *   NRM_P (Normal Response Mode as Primary), The primary station has given
+ *   permissions to a secondary station to transmit IrLAP resonse frames
+ *   (by sending a frame with the P bit set). The primary station will not
+ *   transmit any frames and is expecting to receive frames only from the
+ *   secondary to which transmission permissions has been given.
+ */
+static int irlap_state_nrm_p( struct irlap_cb *self, IRLAP_EVENT event, 
+			      struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret = 0;
+	int ns_status;
+	int nr_status;
+
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+
+	switch( event) {
+	case RECV_RR_RSP:
+		DEBUG( 4, "irlap_state_nrm_p: RECV_RR_FRAME: "
+		       "Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n",
+		       self->retry_count, info->nr, self->va, self->vs, 
+		       self->vr);
+
+		ASSERT( info != NULL, return -1;);
+
+		/*  
+		 *  If you get a RR, the remote isn't busy anymore, 
+		 *  no matter what the NR 
+		 */
+		self->remote_busy = FALSE;
+
+		/* 
+		 *  Nr as expected? 
+		 */
+		ret = irlap_validate_nr_received( self, info->nr);
+		if ( ret == NR_EXPECTED) {	
+			/* Stop final timer */
+			del_timer( &self->final_timer);
+			
+			/* Update Nr received */
+			irlap_update_nr_received( self, info->nr);
+			
+			/*
+			 *  Got expected NR, so reset the retry_count. This 
+			 *  is not done by the IrLAP standard , which is 
+			 *  strange! DB.
+			 */
+			self->retry_count = 0;			
+			irlap_wait_min_turn_around( self, &self->qos_tx);
+
+			/* Start poll timer */
+			irlap_start_poll_timer( self, self->poll_timeout);
+
+			irlap_next_state( self, LAP_XMIT_P);
+		} else if ( ret == NR_UNEXPECTED) {
+			ASSERT( info != NULL, return -1;);	
+			/* 
+			 *  Unexpected nr! 
+			 */
+			
+			/* Update Nr received */
+			irlap_update_nr_received( self, info->nr);
+
+			DEBUG( 4, "RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, "
+			       "vs=%d, vr=%d\n",
+			       self->retry_count, info->nr, self->va, 
+			       self->vs, self->vr);
+			
+			/* Resend rejected frames */
+			irlap_resend_rejected_frames( self, CMD_FRAME);
+			
+			/*
+			 *  Start only if not running, DB
+			 *  TODO: Should this one be here?
+			 */
+			/* if ( !self->final_timer.prev) */
+/* 				irda_start_timer( FINAL_TIMER, self->final_timeout);  */
+
+			/* Keep state */
+			irlap_next_state( self, LAP_NRM_P);
+		} else if ( ret == NR_INVALID) {
+			DEBUG( 0, "irlap_state_nrm_p: received RR with "
+			       "invalid nr !\n");
+			del_timer( &self->final_timer);
+
+			irlap_next_state( self, LAP_RESET_WAIT);
+
+			irlap_disconnect_indication( self, 
+						     LAP_RESET_INDICATION);
+			self->xmitflag = TRUE;
+		}
+		if (skb)
+			dev_kfree_skb( skb);
+		break;
+	case RECV_RNR_FRAME:
+		DEBUG( 4, "irlap_state_nrm_p: RECV_RNR_FRAME: Retrans:%d, "
+		       "nr=%d, va=%d, vs=%d, vr=%d\n",
+		       self->retry_count, info->nr, self->va, self->vs, 
+		       self->vr);
+
+		ASSERT( info != NULL, return -1;);
+
+		/* Stop final timer */
+		del_timer( &self->final_timer);
+		self->remote_busy = TRUE;
+
+		/* Update Nr received */
+		irlap_update_nr_received( self, info->nr);
+			
+		/* Start poll timer */
+		irlap_start_poll_timer( self, self->poll_timeout);
+
+		irlap_next_state( self, LAP_XMIT_P);
+
+		dev_kfree_skb( skb);
+		break;
+	case RECV_I_RSP:
+		/* FIXME: must check for remote_busy below */
+#ifdef CONFIG_IRDA_FAST_RR
+		/* 
+		 *  Reset the fast_RR so we can use the fast RR code with
+		 *  full speed the next time since peer may have more frames
+		 *  to transmitt
+		 */
+		self->fast_RR = FALSE;
+#endif
+
+		ASSERT( info != NULL, return -1;);
+
+		ns_status = irlap_validate_ns_received( self, info->ns);
+		nr_status = irlap_validate_nr_received( self, info->nr);
+
+		/* 
+		 *  Check for expected I(nformation) frame
+		 */
+		if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) {
+			/* 
+			 *  poll bit cleared?
+			 */
+			if ( !info->pf) {
+				self->vr = (self->vr + 1) % 8;
+			
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				
+				self->ack_required = TRUE;
+				
+				/* Keep state, do not move this line */
+				irlap_next_state( self, LAP_NRM_P);
+				
+				irlap_data_indication( self, skb);
+			} else {
+				del_timer( &self->final_timer);
+
+				self->vr = (self->vr + 1) % 8;
+			
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+		
+				/*  
+				 *  Got expected NR, so reset the
+				 *  retry_count. This is not done by IrLAP,
+				 *  which is strange!  
+				 */
+				self->retry_count = 0;
+				self->ack_required = TRUE;
+			
+				/* This is the last frame */
+				irlap_start_poll_timer( self, self->poll_timeout);
+				irlap_wait_min_turn_around( self, &self->qos_tx);
+				/* Do not move this line */
+				irlap_next_state( self, LAP_XMIT_P);
+			
+				irlap_data_indication( self, skb);
+			}
+			break;
+			
+		}
+		/*
+		 *  Unexpected next to send (Ns)
+		 */
+		if (( ns_status == NS_UNEXPECTED) && 
+		    ( nr_status == NR_EXPECTED)) 
+		{
+			if ( !info->pf) {
+				irlap_update_nr_received( self, info->nr);
+				
+				/*
+				 *  Wait until the last frame before doing 
+				 *  anything
+				 */
+
+				/* Keep state */
+				irlap_next_state( self, LAP_NRM_P);
+			} else {
+				DEBUG( 4, "*** irlap_state_nrm_p:"
+				       " missing or duplicate frame!\n");
+				
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				
+				irlap_wait_min_turn_around( self, &self->qos_tx);
+				irlap_send_rr_frame( self, CMD_FRAME);
+				
+				self->ack_required = FALSE;
+			
+				irlap_start_final_timer( self, self->final_timeout);
+				irlap_next_state( self, LAP_NRM_P);
+			}
+			dev_kfree_skb( skb);
+			break;
+		}
+		/* 
+		 *  Unexpected next to receive (Nr) 
+		 */
+		if (( ns_status == NS_EXPECTED) && 
+		    ( nr_status == NR_UNEXPECTED))
+		{
+			if ( info->pf) {
+				self->vr = (self->vr + 1) % 8;
+			
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+			
+				/* Resend rejected frames */
+				irlap_resend_rejected_frames( self, CMD_FRAME);
+				
+				self->ack_required = FALSE;
+				irlap_start_final_timer( self, self->final_timeout);
+				
+				/* Keep state, do not move this line */
+				irlap_next_state( self, LAP_NRM_P);
+				
+				irlap_data_indication( self, skb);
+			} else {
+				/* 
+				 *  Do not resend frames until the last
+				 *  frame has arrived from the other
+				 *  device. This is not documented in
+				 *  IrLAP!!  
+				 */
+				self->vr = (self->vr + 1) % 8;
+
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				
+				self->ack_required = FALSE;
+
+				/* Keep state, do not move this line!*/
+				irlap_next_state( self, LAP_NRM_P); 
+				
+				irlap_data_indication( self, skb);
+			}
+			break;
+		}
+		/*
+		 *  Unexpected next to send (Ns) and next to receive (Nr)
+		 *  Not documented by IrLAP!
+		 */
+		if (( ns_status == NS_UNEXPECTED) && 
+		    ( nr_status == NR_UNEXPECTED)) 
+		{
+			DEBUG( 4, "IrLAP: unexpected nr and ns!\n");
+			if ( info->pf) {
+				/* Resend rejected frames */
+				irlap_resend_rejected_frames( self, CMD_FRAME);
+				
+				/* Give peer some time to retransmit! */
+				irlap_start_final_timer( self, self->final_timeout);
+
+				/* Keep state, do not move this line */
+				irlap_next_state( self, LAP_NRM_P);
+			} else {
+				/* Update Nr received */
+				/* irlap_update_nr_received( info->nr); */
+				
+				self->ack_required = FALSE;
+			}
+			break;
+		}
+
+		/*
+		 *  Invalid NR or NS
+		 */
+		if (( nr_status == NR_INVALID) || ( ns_status == NS_INVALID)) {
+			if ( info->pf) {
+				del_timer( &self->final_timer);
+				
+				irlap_next_state( self, LAP_RESET_WAIT);
+
+				irlap_disconnect_indication( self, LAP_RESET_INDICATION);
+				self->xmitflag = TRUE;
+			} else {
+				del_timer( &self->final_timer);
+				
+				irlap_disconnect_indication( self, LAP_RESET_INDICATION);
+				
+				self->xmitflag = FALSE;
+			}
+			break;
+		}
+		DEBUG( 0, "irlap_state_nrm_p: Not implemented!\n");
+		DEBUG( 0, "event=%s, ns_status=%d, nr_status=%d\n", 
+		       irlap_event[ event], ns_status, nr_status);
+		break;
+	case RECV_UI_FRAME:
+		/*  poll bit cleared?  */
+		if ( !info->pf) {
+			irlap_unit_data_indication( self, skb);
+			irlap_next_state( self, LAP_NRM_P);
+		} else {
+			del_timer( &self->final_timer);
+			irlap_unit_data_indication( self, skb);
+			irlap_start_poll_timer( self, self->poll_timeout);
+		}
+		break;
+	case RECV_FRMR_RSP:
+		del_timer( &self->final_timer);
+		self->xmitflag = TRUE;
+		irlap_next_state( self, LAP_RESET_WAIT);
+		irlap_reset_indication( self);
+		break;
+	case FINAL_TIMER_EXPIRED:
+		/* 
+		 *  We are allowed to wait for additional 300 ms if
+		 *  final timer expires when we are in the middle
+		 *  of receiving a frame (page 45, IrLAP). Check that
+		 *  we only do this once for each frame.
+		 */
+		if ( irda_device_is_receiving( self->irdev) && 
+		     !self->add_wait) {
+			DEBUG( 4, "FINAL_TIMER_EXPIRED when receiving a "
+			       "frame! Waiting a little bit more!\n");
+			irlap_start_final_timer( self, 30);
+
+			/*
+			 *  Don't allow this to happen one more time in a row, 
+			 *  or else we can get a pretty tight loop here if 
+			 *  if we only receive half a frame. DB.
+			 */
+			self->add_wait = TRUE;
+			break;
+		}
+		self->add_wait = FALSE;
+
+		if (( self->retry_count < self->N2) && 
+		    ( self->retry_count != self->N1)) {
+			
+			irlap_wait_min_turn_around( self, &self->qos_tx);
+			irlap_send_rr_frame( self, CMD_FRAME);
+			
+			irlap_start_final_timer( self, self->final_timeout);
+		 	self->retry_count++;
+
+			DEBUG( 4, "irlap_state_nrm_p: FINAL_TIMER_EXPIRED:"
+			       " retry_count=%d\n", self->retry_count);
+			/* Keep state */
+		} else if ( self->retry_count == self->N1) {
+			irlap_status_indication( STATUS_NO_ACTIVITY);
+			irlap_wait_min_turn_around( self, &self->qos_tx);
+			irlap_send_rr_frame( self, CMD_FRAME);
+			
+			irlap_start_final_timer( self, self->final_timeout);
+			self->retry_count++;
+
+			DEBUG( 4, "retry count = N1; retry_count=%d\n", 
+			       self->retry_count);
+			/* Keep state */
+		} else if ( self->retry_count >= self->N2) {
+			irlap_apply_default_connection_parameters( self);
+
+			/* Always switch state before calling upper layers */
+			irlap_next_state( self, LAP_NDM);
+			irlap_disconnect_indication( self, LAP_NO_RESPONSE);
+		}
+		break;
+	case RECV_DISC_FRAME: /* FIXME: Check how this is in the standard! */
+		DEBUG( 0, __FUNCTION__ "(), RECV_DISC_FRAME()\n");
+
+		/* Always switch state before calling upper layers */
+		irlap_next_state( self, LAP_NDM);
+		
+		irlap_wait_min_turn_around( self, &self->qos_tx);
+		irlap_send_ua_response_frame( self, NULL);
+
+ 		del_timer( &self->final_timer);
+		/* del_timer( &self->poll_timer); */
+
+		irlap_flush_all_queues( self);
+		irlap_apply_default_connection_parameters( self);
+
+		irlap_disconnect_indication( self, LAP_DISC_INDICATION);
+		if (skb)
+			dev_kfree_skb( skb);
+		
+		break;
+	default:
+		/* DEBUG( 0, "irlap_state_nrm_p: Unknown event"); */
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_reset_wait (event, skb, info)
+ *
+ *    We have informed the service user of a reset condition, and is
+ *    awaiting reset of disconnect request.
+ *
+ */
+int irlap_state_reset_wait( struct irlap_cb *self, IRLAP_EVENT event, 
+			    struct sk_buff *skb, struct irlap_info *info)
+{
+	int ret = 0;
+	
+	DEBUG( 3, __FUNCTION__ "(), event = %s\n", irlap_event[event]);
+	
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+	
+	switch( event) {
+	case RESET_REQUEST:
+		if ( self->xmitflag) {
+			irlap_wait_min_turn_around( self, &self->qos_tx);
+			irlap_send_snrm_frame( self, NULL);
+			irlap_start_final_timer( self, self->final_timeout);
+			irlap_next_state( self, LAP_RESET);
+		} else {
+			irlap_start_final_timer( self, self->final_timeout);
+			irlap_next_state( self, LAP_RESET);
+		}
+		break;
+	case DISCONNECT_REQUEST:
+		irlap_wait_min_turn_around( self, &self->qos_tx);
+		irlap_send_disc_frame( self);
+		irlap_flush_all_queues( self);
+		irlap_start_final_timer( self, self->final_timeout);
+		self->retry_count = 0;
+		irlap_next_state( self, LAP_PCLOSE);
+		break;
+	default:
+		DEBUG( 0, "irlap_state_reset_wait: Unknown event %s\n", 
+		       irlap_event[event]);
+		ret = -1;
+		break;	
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_reset (self, event, skb, info)
+ *
+ *    We have sent a SNRM reset command to the peer layer, and is awaiting
+ *    reply.
+ *
+ */
+int irlap_state_reset( struct irlap_cb *self, IRLAP_EVENT event, 
+		       struct sk_buff *skb, struct irlap_info *info)
+{
+	int ret = 0;
+	
+	DEBUG( 3, __FUNCTION__ "(), event = %s\n", irlap_event[event]);
+	
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+	
+	switch( event) {
+	case RECV_DISC_FRAME:
+		del_timer( &self->final_timer);
+
+		irlap_apply_default_connection_parameters( self);
+
+		/* Always switch state before calling upper layers */
+		irlap_next_state( self, LAP_NDM);
+
+		irlap_disconnect_indication( self, LAP_NO_RESPONSE);
+		break;
+	case RECV_UA_RSP:
+		del_timer( &self->final_timer);
+		
+		/* Initiate connection state */
+		irlap_initiate_connection_state( self);
+		
+		irlap_reset_confirm();
+		
+		self->remote_busy = FALSE;
+		irlap_start_poll_timer( self, self->poll_timeout);
+		irlap_next_state( self, LAP_XMIT_P);
+		break;
+	case FINAL_TIMER_EXPIRED:
+		if ( self->retry_count < 3) {
+			irlap_wait_min_turn_around( self, &self->qos_tx);
+
+			ASSERT( self->irdev != NULL, return -1;);
+			irlap_send_snrm_frame(self, 
+					      irda_device_get_qos( self->irdev));
+
+			self->retry_count++; /* Experimental!! */
+
+			irlap_start_final_timer( self, self->final_timeout);
+			irlap_next_state( self, LAP_RESET);
+		} else if ( self->retry_count >= self->N3) {
+			irlap_apply_default_connection_parameters( self);
+			
+			/* Always switch state before calling upper layers */
+			irlap_next_state( self, LAP_NDM);
+			
+			irlap_disconnect_indication( self, LAP_NO_RESPONSE);
+		}
+		break;
+
+	case RECV_SNRM_CMD:
+		DEBUG(3, "lap_reset: RECV_SNRM_CMD\n");
+		irlap_initiate_connection_state( self);
+		irlap_wait_min_turn_around( self, &self->qos_tx);
+		irlap_send_ua_response_frame( self, &self->qos_rx);
+		irlap_reset_confirm();
+		irlap_start_wd_timer( self, self->wd_timeout);
+		irlap_next_state( self, LAP_NDM);
+		break;
+
+	default:
+		DEBUG( 0, "irlap_state_reset: Unknown event %s\n", 
+		       irlap_event[ event]);
+		ret = -1;
+		break;	
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_xmit_s (event, skb, info)
+ * 
+ *   XMIT_S, The secondary station has been given the right to transmit,
+ *   and we therefor do not expect to receive any transmissions from other
+ *   stations.  
+ */
+static int irlap_state_xmit_s( struct irlap_cb *self, IRLAP_EVENT event, 
+			       struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret = 0;
+	
+	DEBUG( 4, __FUNCTION__ "(), event=%s\n", irlap_event[ event]); 
+
+	ASSERT( self != NULL, return -ENODEV;);
+	ASSERT( self->magic == LAP_MAGIC, return -EBADR;);
+	
+	switch( event) {
+	case SEND_I_CMD:
+		ASSERT( skb != NULL, return -1;);
+		DEBUG( 4, "irlap_state_xmit: Window=%d\n", self->window);
+		
+		/*
+		 *  Send frame only if send window > 1
+		 */ 
+		if (( self->window > 0) && ( !self->remote_busy)) {
+			/*
+			 *  Test if we have transmitted more bytes over the 
+			 *  link than its possible to do with the current 
+			 *  speed and turn-around-time.
+			 */
+			if (( skb->len+self->bofs_count) > self->bytes_left) {
+				DEBUG( 4, "IrDA: Not allowed to transmit more bytes!\n");
+				skb_queue_head( &self->tx_list, skb);
+				/*
+				 *  Switch to NRM_S, this is only possible
+				 *  when we are in secondary mode, since we 
+				 *  must be sure that we don't miss any RR
+				 *  frames
+				 */
+				irlap_next_state( self, LAP_NRM_S);
+
+				return -EPROTO; /* Try again later */
+			}
+			self->bytes_left -= ( skb->len + self->bofs_count);
+
+			/*
+			 *  Send data with final bit cleared only if window > 1
+			 *  and there is more frames to be sent
+			 */
+			if (( self->window > 1) && 
+			    skb_queue_len( &self->tx_list) > 0) 
+			{   
+				DEBUG( 4, "irlap_state_xmit: window > 1\n");
+				irlap_send_data_secondary( self, skb);
+				irlap_next_state( self, LAP_XMIT_S);
+			} else {
+				DEBUG( 4, "irlap_state_xmit: window <= 1\n");
+				irlap_send_data_secondary_final( self, skb);
+				irlap_next_state( self, LAP_NRM_S);
+			}
+		} else {
+			DEBUG( 0, "Unable to send!\n");
+			skb_queue_head( &self->tx_list, skb);
+			ret = -EPROTO;
+		}
+		break;
+	default:
+		DEBUG( 0, __FUNCTION__ "(), Unknown event %s\n", 
+		       irlap_event[ event]);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_nrm_s (event, skb, info)
+ *
+ *    NRM_S (Normal Response Mode as Secondary) state, in this state we are 
+ *    expecting to receive frames from the primary station
+ *
+ */
+static int irlap_state_nrm_s( struct irlap_cb *self, IRLAP_EVENT event, 
+			      struct sk_buff *skb, struct irlap_info *info) 
+{
+	int ret = 0;
+	int ns_status;
+	int nr_status;
+
+	DEBUG( 4, __FUNCTION__ "(), event=%s\n", irlap_event[ event]);
+
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == LAP_MAGIC, return -1;);
+
+	switch( event) {
+	case RECV_RR_CMD:
+		self->retry_count = 0;
+
+		/* 
+		 *  Nr as expected? 
+		 */
+		nr_status = irlap_validate_nr_received( self, info->nr);
+		if ( nr_status == NR_EXPECTED) {
+			if (( skb_queue_len( &self->tx_list) > 0) && 
+			    ( self->window > 0)) {
+				self->remote_busy = FALSE;
+				
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				del_timer( &self->wd_timer);
+				
+				irlap_wait_min_turn_around( self, &self->qos_tx);
+				irlap_next_state( self, LAP_XMIT_S);
+			} else {			
+				self->remote_busy = FALSE;
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				irlap_wait_min_turn_around( self, &self->qos_tx);
+				
+				irlap_send_rr_frame( self, RSP_FRAME);
+				
+				irlap_start_wd_timer( self, self->wd_timeout);
+				irlap_next_state( self, LAP_NRM_S);
+			}
+		} else if ( nr_status == NR_UNEXPECTED) {
+			self->remote_busy = FALSE;
+			irlap_update_nr_received( self, info->nr);
+			irlap_resend_rejected_frames( self, RSP_FRAME);
+
+			irlap_start_wd_timer( self, self->wd_timeout);
+
+			/* Keep state */
+			irlap_next_state( self, LAP_NRM_S); 
+		} else {
+			DEBUG( 0, "irlap_state_nrm_s: **** "
+			       "invalid nr not implemented!\n");
+		} 
+		if ( skb)
+			dev_kfree_skb( skb);
+
+		break;
+	case RECV_I_CMD:
+		/* FIXME: must check for remote_busy below */
+		DEBUG( 4, __FUNCTION__ "(), event=%s nr=%d, vs=%d, ns=%d, "
+		       "vr=%d, pf=%d\n", irlap_event[event], info->nr, 
+		       self->vs, info->ns, self->vr, info->pf);
+
+		self->retry_count = 0;
+
+		ns_status = irlap_validate_ns_received( self, info->ns);
+		nr_status = irlap_validate_nr_received( self, info->nr);
+		/* 
+		 *  Check for expected I(nformation) frame
+		 */
+		if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) {
+			/* 
+			 *  poll bit cleared?
+			 */
+			if ( !info->pf) {
+				self->vr = (self->vr + 1) % 8;
+				
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				
+				self->ack_required = TRUE;
+				
+				/*
+				 *  Starting WD-timer here is optional, but
+				 *  not recommended. Note 6 IrLAP p. 83
+				 */
+				/* irda_start_timer( WD_TIMER, self->wd_timeout); */
+
+				/* Keep state, do not move this line */
+				irlap_next_state( self, LAP_NRM_S);
+				
+				irlap_data_indication( self, skb);
+				break;
+			} else {
+				self->vr = (self->vr + 1) % 8;
+				
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				
+				/* 
+				 *  We should wait before sending RR, and
+				 *  also before changing to XMIT_S
+				 *  state. (note 1, IrLAP p. 82) 
+				 */
+				irlap_wait_min_turn_around( self, &self->qos_tx);
+				/*
+				 *  Any pending data requests?
+				 */
+				if (( skb_queue_len( &self->tx_list) > 0) && 
+				    ( self->window > 0)) 
+				{
+					self->ack_required = TRUE;
+					
+					del_timer( &self->wd_timer);
+					
+					irlap_next_state( self, LAP_XMIT_S);
+				} else {
+					irlap_send_rr_frame( self, RSP_FRAME);
+					irlap_start_wd_timer( self, self->wd_timeout);
+
+					/* Keep the state */
+					irlap_next_state( self, LAP_NRM_S);
+				}
+				irlap_data_indication( self, skb);
+
+				break;
+			}
+		}
+		/*
+		 *  Check for Unexpected next to send (Ns)
+		 */
+		if (( ns_status == NS_UNEXPECTED) && 
+		    ( nr_status == NR_EXPECTED)) 
+		{
+			/* Unexpected next to send, with final bit cleared */
+			if ( !info->pf) {
+				irlap_update_nr_received( self, info->nr);
+				
+				irlap_start_wd_timer( self, self->wd_timeout);
+			} else {
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+			
+				irlap_wait_min_turn_around( self, &self->qos_tx);
+				irlap_send_rr_frame( self, CMD_FRAME);
+			
+				irlap_start_wd_timer( self, self->wd_timeout);
+			}
+			dev_kfree_skb( skb);
+			break;
+		}
+
+		/* 
+		 *  Unexpected Next to Receive(NR) ?
+		 */
+		if (( ns_status == NS_EXPECTED) && 
+		    ( nr_status == NR_UNEXPECTED))
+		{
+			if ( info->pf) {
+				DEBUG( 4, "RECV_I_RSP: frame(s) lost\n");
+				
+				self->vr = (self->vr + 1) % 8;
+				
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				
+				/* Resend rejected frames */
+				irlap_resend_rejected_frames( self, RSP_FRAME);
+
+				/* Keep state, do not move this line */
+				irlap_next_state( self, LAP_NRM_S);
+
+				irlap_data_indication( self, skb);
+				irlap_start_wd_timer( self, self->wd_timeout);
+
+				break;
+			}
+			/*
+			 *  This is not documented in IrLAP!! Unexpected NR
+			 *  with poll bit cleared
+			 */
+			if ( !info->pf) {
+				self->vr = (self->vr + 1) % 8;
+				
+				/* Update Nr received */
+				irlap_update_nr_received( self, info->nr);
+				
+				/* Keep state, do not move this line */
+				irlap_next_state( self, LAP_NRM_S);
+				
+				irlap_data_indication( self, skb);
+				irlap_start_wd_timer( self, self->wd_timeout);
+			}
+		}
+		
+		if ( ret == NR_INVALID) {
+			DEBUG( 0, "NRM_S, NR_INVALID not implemented!\n");
+		}
+		if ( ret == NS_INVALID) {
+			DEBUG( 0, "NRM_S, NS_INVALID not implemented!\n");
+		}
+		break;
+	case RECV_UI_FRAME:
+		/* 
+		 *  poll bit cleared?
+		 */
+		if ( !info->pf) {
+			irlap_unit_data_indication( self, skb);
+			irlap_next_state( self, LAP_NRM_S); /* Keep state */
+		} else {
+			/*
+			 *  Any pending data requests?
+			 */
+			if (( skb_queue_len( &self->tx_list) > 0) && 
+			    ( self->window > 0) && !self->remote_busy) 
+			{
+				irlap_unit_data_indication( self, skb);
+				
+				del_timer( &self->wd_timer);
+
+				irlap_next_state( self, LAP_XMIT_S);
+			} else {
+				irlap_unit_data_indication( self, skb);
+
+				irlap_wait_min_turn_around( self, &self->qos_tx);
+
+				irlap_send_rr_frame( self, RSP_FRAME);
+				self->ack_required = FALSE;
+				
+				irlap_start_wd_timer( self, self->wd_timeout);
+
+				/* Keep the state */
+				irlap_next_state( self, LAP_NRM_S);
+			}
+		}
+		break;
+	case RECV_SNRM_CMD:
+#if 1
+		del_timer( &self->wd_timer);
+		DEBUG( 0, "irlap_state_nrm_s: received SNRM cmd\n");
+		irlap_next_state( self, LAP_RESET);
+#else
+		irlap_wait_min_turn_around( &self->qos_session);
+		irlap_send_ua_response_frame( &self->qos_session);
+		irda_start_timer( WD_TIMER, self->wd_timeout);
+		irlap_next_state( self, LAP_SCLOSE)
+		
+#endif
+		break;
+	case WD_TIMER_EXPIRED:
+		DEBUG( 4, "WD_TIMER_EXPIRED: %ld\n", jiffies);
+	
+		/*
+		 *  Wait until  retry_count * n matches negotiated threshold/
+		 *  disconnect time (note 2 in IrLAP p. 82)
+		 */
+		DEBUG( 0, "retry_count = %d\n", self->retry_count);
+
+		if (( self->retry_count < (self->N2/2))  && 
+		    ( self->retry_count != self->N1/2)) {
+			
+			irlap_start_wd_timer( self, self->wd_timeout);
+			self->retry_count++;		
+		} else if ( self->retry_count == (self->N1/2)) {
+			irlap_status_indication( STATUS_NO_ACTIVITY);
+			irlap_start_wd_timer( self, self->wd_timeout);
+			self->retry_count++;
+		} else if ( self->retry_count >= self->N2/2) {
+			irlap_apply_default_connection_parameters( self);
+			
+			/* Always switch state before calling upper layers */
+			irlap_next_state( self, LAP_NDM);
+			
+			irlap_disconnect_indication( self, LAP_NO_RESPONSE);
+		}
+		break;
+
+	case RECV_DISC_FRAME:
+		/* Always switch state before calling upper layers */
+		irlap_next_state( self, LAP_NDM);
+
+		irlap_wait_min_turn_around( self, &self->qos_tx);
+		irlap_send_ua_response_frame( self, NULL);
+		del_timer( &self->wd_timer);
+		irlap_flush_all_queues( self);
+		irlap_apply_default_connection_parameters( self);
+
+		irlap_disconnect_indication( self, LAP_DISC_INDICATION);
+		if (skb)
+			dev_kfree_skb( skb);
+		
+		break;
+
+	case RECV_DISCOVERY_XID_CMD:
+		DEBUG( 3, "irlap_state_nrm_s: got event RECV_DISCOVER_XID_CMD!\n");
+		del_timer( &self->final_timer);
+		
+		irlap_apply_default_connection_parameters( self);
+
+		/* Always switch state before calling upper layers */
+		irlap_next_state( self, LAP_NDM);
+
+		irlap_disconnect_indication( self, LAP_DISC_INDICATION);
+		
+#if 0
+		irlap_wait_min_turn_around( self, &self->qos_session);
+		irlap_send_rr_frame( RSP_FRAME);
+		irda_start_timer( WD_TIMER, self->wd_timeout);
+		irlap_next_state( self, LAP_NRM_S);
+#endif
+		break;
+
+	default:
+		DEBUG( 0, "irlap_state_nrm_s: Unknown event %d, (%s)\n", 
+		       event, irlap_event[event]);
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function irlap_state_sclose (self, event, skb, info)
+ *
+ *    
+ *
+ */
+static int irlap_state_sclose( struct irlap_cb *self, IRLAP_EVENT event, 
+			       struct sk_buff *skb, struct irlap_info *info) 
+{
+	DEBUG( 0, __FUNCTION__ "(), Not implemented!\n");
+
+	return -1;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov