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

Next file: linux/net/irda/irproc.c
Previous file: linux/net/irda/irobex/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.131/linux/net/irda/irobex/irobex.c linux/net/irda/irobex/irobex.c
@@ -0,0 +1,1110 @@
+/*********************************************************************
+ *                
+ * Filename:      irobex.c
+ * Version:       0.1
+ * Description:   Kernel side of the IrOBEX layer
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Thu Jun 25 21:21:07 1998
+ * Modified at:   Mon Dec 14 11:56:26 1998
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998 Dag Brattli, 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/module.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/ioctl.h>
+#include <linux/init.h>
+
+#include <asm/byteorder.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/poll.h>
+
+#include <net/irda/irttp.h>
+#include <net/irda/irias_object.h>
+#include <net/irda/iriap.h>
+
+#include <net/irda/irobex.h>
+
+/*
+ *  Master structure, only one instance for now!!
+ */
+struct irobex_cb *irobex;
+
+static int irobex_dev_open( struct inode * inode, struct file *file);
+static int irobex_ioctl( struct inode *inode, struct file *filp, 
+			 unsigned int cmd, unsigned long arg);
+
+static int irobex_dev_close( struct inode *inode, struct file *file);
+static ssize_t irobex_read( struct file *file, char *buffer, size_t count, 
+			    loff_t *noidea);
+static ssize_t irobex_write( struct file *file, const char *buffer,
+			     size_t count, loff_t *noidea);
+static loff_t irobex_seek( struct file *, loff_t, int);
+static u_int irobex_poll( struct file *file, poll_table *wait);
+static int irobex_fasync( int, struct file *, int);
+
+static struct file_operations irobex_fops = {
+	irobex_seek,	/* seek */
+	irobex_read,
+	irobex_write,
+	NULL,		/* readdir */
+	irobex_poll,    /* poll */
+	irobex_ioctl,	/* ioctl */
+	NULL,		/* mmap */
+	irobex_dev_open,
+	NULL,
+	irobex_dev_close,
+	NULL,
+	irobex_fasync,
+};
+
+#ifdef CONFIG_PROC_FS
+static int irobex_proc_read( char *buf, char **start, off_t offset, 
+			     int len, int unused);
+
+extern struct proc_dir_entry proc_irda;
+
+struct proc_dir_entry proc_irobex = {
+	0, 6, "irobex",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, NULL,
+	&irobex_proc_read,
+};
+#endif
+
+/*
+ * Function irobex_init (dev)
+ *
+ *   Initializes the irobex control structure, and registers as a misc
+ *   device
+ *
+ */
+__initfunc(int irobex_init(void))
+{
+	struct irobex_cb *self;
+
+	DEBUG( 4, "--> " __FUNCTION__ "()\n");	
+
+	self = kmalloc(sizeof(struct irobex_cb), GFP_ATOMIC);
+	if ( self == NULL)
+		return -ENOMEM;
+	
+	memset( self, 0, sizeof(struct irobex_cb));
+ 	sprintf( self->devname, "irobex%d", 1); /* Just one instance for now */
+	
+	self->magic = IROBEX_MAGIC;
+	self->rx_flow = self->tx_flow = FLOW_START;
+	
+	self->dev.minor = MISC_DYNAMIC_MINOR;
+	self->dev.name = "irobex";
+	self->dev.fops = &irobex_fops;
+	
+	skb_queue_head_init( &self->rx_queue);
+	init_timer( &self->watchdog_timer);
+
+	irobex = self;
+	
+	misc_register( &self->dev);
+	
+	irobex_register_server( self);
+
+#ifdef CONFIG_PROC_FS
+	proc_register( &proc_irda, &proc_irobex);
+#endif /* CONFIG_PROC_FS */
+		
+	irlmp_register_layer( S_OBEX, CLIENT | SERVER, TRUE, 
+			      irobex_discovery_indication);
+	
+	DEBUG( 4, "irobex_init -->\n");
+	
+	return 0;
+}
+
+/*
+ * Function irobex_cleanup (void)
+ *
+ *     Removes the IrOBEX layer
+ *
+ */
+void irobex_cleanup(void)
+{
+	struct sk_buff *skb;
+	struct irobex_cb *self;
+	
+	DEBUG( 4, "-->" __FUNCTION__ "()\n");
+
+	self = irobex;
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);	
+
+	/*
+	 *  Deregister client and server
+	 */
+	irlmp_unregister_layer( S_OBEX, CLIENT | SERVER);
+
+	if ( self->tsap) {
+		irttp_close_tsap( self->tsap);
+		self->tsap = NULL;
+	}
+
+	/* Stop timers */
+	del_timer( &self->watchdog_timer);
+
+	/*
+	 *  Deallocate buffers
+	 */
+	while (( skb = skb_dequeue( &self->rx_queue)) != NULL) {
+		dev_kfree_skb( skb);
+	}
+
+#ifdef CONFIG_PROC_FS
+	proc_unregister( &proc_irda, proc_irobex.low_ino);
+#endif
+	
+	misc_deregister( &self->dev);
+	
+	kfree( self);
+	
+	DEBUG( 4, __FUNCTION__ "() -->\n");
+}
+
+/*
+ * Function irobex_read (inode, file, buffer, count)
+ *
+ *    User process wants to read some data
+ *
+ */
+static ssize_t irobex_read( struct file *file, char *buffer, size_t count, 
+			    loff_t *noidea)
+{
+	int len=0;
+	struct irobex_cb *self;
+	struct sk_buff *skb = NULL;
+	int ret;
+	
+	self = irobex;
+
+	ASSERT( self != NULL, return -EIO;);
+	ASSERT( self->magic == IROBEX_MAGIC, return -EIO;);
+  
+	DEBUG( 4, __FUNCTION__ ": count=%d, skb_len=%d, conn.=%d, eof=%d\n", 
+	       count, skb_queue_len( &self->rx_queue), self->connected, 
+	       self->eof);
+	
+	/*
+	 *  Check if there is no data to return
+	 */
+	if ( skb_queue_len( &self->rx_queue) == 0) {
+
+		/*
+		 *  Disconnected yet?
+		 */
+		if ( !self->connected) {
+			switch ( self->eof) {
+			case LM_USER_REQUEST:
+				self->eof = FALSE;
+				DEBUG(3, "read_irobex: returning 0\n");
+				ret = 0;
+				break;
+			case LM_LAP_DISCONNECT:
+				self->eof = FALSE;
+				ret = -EIO;
+				break;
+			case LM_LAP_RESET:
+				self->eof = FALSE;
+				ret = -ECONNRESET;
+				break;
+			default:
+				self->eof = FALSE;
+				ret = -EIO;
+				break;
+			}
+			return ret;
+		}
+
+		/* Return if user does not want to block */
+		if ( file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		/* * Go to sleep and wait for data!  */
+		interruptible_sleep_on( &self->read_wait);
+
+		/*
+		 *  Ensure proper reaction to signals, and screen out 
+		 *  blocked signals (page 112. linux device drivers)
+		 */
+		if ( signal_pending( current))
+			return -ERESTARTSYS;
+	}
+	
+	while ( count && skb_queue_len( &self->rx_queue)) {
+
+		skb = skb_dequeue( &self->rx_queue);
+		
+		/*
+		 *  Check if we have previously stopped IrTTP and we know
+		 *  have more free space in our rx_queue. If so tell IrTTP
+		 *  to start delivering frames again before our rx_queue gets
+		 *  empty
+		 */
+		if ( self->rx_flow == FLOW_STOP) {
+			if ( skb_queue_len( &self->rx_queue) < LOW_THRESHOLD) {
+				DEBUG( 0, __FUNCTION__ "(), Starting IrTTP\n");
+				self->rx_flow = FLOW_START;
+				irttp_flow_request( self->tsap, FLOW_START);
+			}
+		}
+
+		/*  
+		 *  Is the request from the user less that the amount in the 
+		 *  current packet?  
+		 */
+		if ( count <  skb->len) {
+			copy_to_user( buffer+len, skb->data, count);
+			len += count;
+
+			/*
+			 *  Remove copied data from skb and queue
+			 *  it for next read
+			 */
+			skb_pull( skb, count);
+			skb_queue_head( &self->rx_queue, skb);
+			
+			return len;
+		} else {
+			copy_to_user( buffer+len, skb->data, skb->len);
+			count -= skb->len;
+			len += skb->len;
+			
+			dev_kfree_skb( skb);
+		}
+	}
+	return len;
+}
+
+/*
+ * Function irobex_write (inode, file, buffer, count)
+ *
+ *    User process wants to write to device
+ *
+ */
+static ssize_t irobex_write( struct file *file, const char *buffer, 
+			     size_t count, loff_t *noidea)
+{
+	struct irobex_cb *self;
+	struct sk_buff *skb;
+	int data_len = 0;
+	int len = 0;
+	
+	self = irobex;
+	
+	ASSERT( self != NULL, return -EIO;);
+	ASSERT( self->magic == IROBEX_MAGIC, return -EIO;);
+
+	DEBUG( 4, __FUNCTION__ ": count = %d\n", count);
+	
+	/*
+	 *  If we are not connected then we just give up!
+	 */
+	if ( !self->connected) {
+		DEBUG( 0, __FUNCTION__ "(): Not connected!\n");
+
+		return -ENOLINK;
+	} 
+
+	/* Check if IrTTP is wants us to slow down */
+	if ( self->tx_flow == FLOW_STOP) {
+		DEBUG( 0, __FUNCTION__ 
+		       "(), IrTTP wants us to slow down, going to sleep\n");
+		interruptible_sleep_on( &self->write_wait);
+	}
+	
+	/* Send data to TTP layer possibly as muliple packets */
+	while ( count) {
+
+		/*
+		 *  Check if request is larger than what fits inside a TTP
+		 *  frame
+		 */
+		if ( count < (self->irlap_data_size - IROBEX_MAX_HEADER))
+			data_len = count;
+		else 
+		        data_len = self->irlap_data_size - IROBEX_MAX_HEADER;
+
+		DEBUG( 4, __FUNCTION__ "(), data_len=%d, header_len = %d\n", 
+		       data_len, IROBEX_MAX_HEADER);
+
+		skb = dev_alloc_skb( data_len + IROBEX_MAX_HEADER);
+		if ( skb == NULL) {
+			DEBUG( 0, "irobex - couldn't allocate skbuff!\n");
+			return 0;
+		}
+		
+		skb_reserve( skb, IROBEX_MAX_HEADER);
+		skb_put( skb, data_len);
+		
+		copy_from_user( skb->data, buffer+len, data_len);
+		len += data_len;
+		count -= data_len;
+
+		DEBUG( 4, __FUNCTION__ "(), skb->len=%d\n", (int) skb->len);
+		ASSERT( skb->len <= (self->irlap_data_size-IROBEX_MAX_HEADER),
+			return len;);
+		irttp_data_request( self->tsap, skb);
+	}
+	return (len);
+}
+
+/*
+ * Function irobex_poll (file, wait)
+ *
+ *    
+ *
+ */
+static u_int irobex_poll(struct file *file, poll_table *wait)
+{
+	DEBUG( 0, __FUNCTION__ "(), Sorry not implemented yet!\n");
+
+	/* check out /usr/src/pcmcia/modules/ds.c for an example */
+	return 0;
+}
+
+/*
+ * Function irobex_fasync (inode, filp, mode)
+ *
+ *    Implementation for SIGIO
+ *
+ */
+static int irobex_fasync( int fd, struct file *filp, int on)
+{
+	struct irobex_cb *self;
+
+	DEBUG( 4, __FUNCTION__ "()\n");
+	
+	self = irobex;
+
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == IROBEX_MAGIC, return -1;);
+
+	return fasync_helper( fd, filp, on, &self->async);
+}
+
+/*
+ * Function irobex_seek (inode, file, buffer, count)
+ *
+ *    Not implemented yet!
+ *
+ */
+static loff_t irobex_seek( struct file *file, loff_t off, int whence)
+{
+	DEBUG( 0, __FUNCTION__ "(), Not implemented yet!\n");
+
+	return -ESPIPE;
+}
+
+/*
+ * Function irobex_ioctl (inode, filp, cmd, arg)
+ *
+ *    Drivers IOCTL handler, used for connecting and disconnecting
+ *    irobex connections
+ *
+ */
+static int irobex_ioctl( struct inode *inode, struct file *filp, 
+			 unsigned int cmd, unsigned long arg)
+{
+	struct irobex_cb *self;
+	int err = 0;
+	int size = _IOC_SIZE(cmd);
+	
+	DEBUG( 0, __FUNCTION__ "()\n");
+
+	self = irobex;
+	
+	ASSERT( self != NULL, return -ENOTTY;);
+	ASSERT( self->magic = IROBEX_MAGIC, return -ENOTTY;);
+	
+	if ( _IOC_TYPE(cmd) != IROBEX_IOC_MAGIC) 
+		return -EINVAL;
+	if ( _IOC_NR(cmd) > IROBEX_IOC_MAXNR)
+		return -EINVAL;
+
+	if ( _IOC_DIR(cmd) & _IOC_READ)
+		err = verify_area( VERIFY_WRITE, (void *) arg, size);
+	else if ( _IOC_DIR(cmd) & _IOC_WRITE)
+		err = verify_area( VERIFY_READ, (void *) arg, size);
+	if ( err)
+		return err;
+
+	switch ( cmd) {
+	case IROBEX_IOCSCONNECT:
+		DEBUG( 0, __FUNCTION__ "(): IROBEX_IOCSCONNECT!\n");
+		
+		/* Already connected? */
+		if ( self->connected)
+			return 0;
+		
+		/*
+		 *  Wait until we have discovered a remote IrOBEX device
+		 */
+		if (( self->daddr == 0) || 
+		    (( jiffies - self->time_discovered) > 500)) {
+			
+			if ( self->daddr != 0) {
+				DEBUG( 0, __FUNCTION__ "(), daddr is old!\n");
+				self->daddr = 0;
+			}
+
+			irlmp_discovery_request( 8);
+			DEBUG( 0, __FUNCTION__ 
+			       "(): Sleep until device discovered\n");
+			
+			/* Timeout after 10 secs. */
+			irobex_start_watchdog_timer( self, 1000);
+			/*
+			 *  Wait for discovery to complete
+			 */
+			interruptible_sleep_on( &self->discover_wait);
+			/* del_timer( &self->watchdog_timer); */
+		}
+		
+		/* Give up if we are unable to discover any remote devices */
+		if ( self->daddr == 0) {
+			DEBUG( 0, __FUNCTION__ 
+			       "(), Unable to discover any devices!\n");
+			return -ENOTTY;
+		}
+		
+		/* Need to find remote destination TSAP selector? */
+		if ( self->dtsap_sel == 0) {
+			
+			DEBUG( 0, __FUNCTION__ "() : Quering remote IAS!\n");
+			
+			/* Timeout after 5 secs. */
+			irobex_start_watchdog_timer( self, 500);
+			iriap_getvaluebyclass_request( self->daddr,
+						       "OBEX", 
+						       "IrDA:TinyTP:LsapSel",
+						       irobex_get_value_confirm,
+						       self);
+			
+			DEBUG( 0, __FUNCTION__ "(): Sleep until IAS answer\n");
+			interruptible_sleep_on( &self->ias_wait);
+			/* del_timer( &self->watchdog_timer); */
+		}	
+		
+		if ( self->dtsap_sel == 0) {
+			DEBUG( 0, __FUNCTION__ 
+			       "(), Unable to query remote LM-IAS!\n");
+			return -ENOTTY;
+		}
+
+
+		/*
+		 *  Try connect
+		 */
+		DEBUG( 0, __FUNCTION__ "(): Connecting ...\n");
+
+		/* Timeout after 5 secs. */
+		irobex_start_watchdog_timer( self, 500);
+
+		irttp_connect_request( self->tsap, self->dtsap_sel, 
+				       self->daddr, NULL, SAR_DISABLE, 
+				       NULL);
+		
+		/*
+		 *  Go to sleep and wait for connection!
+		 */
+		DEBUG( 0, __FUNCTION__ "(): Waiting for connection!\n");
+		interruptible_sleep_on( &self->connect_wait);
+
+		del_timer( &self->watchdog_timer);
+		
+		if ( !self->connected) {
+			DEBUG( 0, __FUNCTION__ 
+			       "(), Unable to connect to remote device!\n");
+			return -ENOTTY;
+		}
+
+		break;
+	case IROBEX_IOCSDISCONNECT:
+		DEBUG( 0, __FUNCTION__ "(): IROBEX_IOCSDISCONNECT!\n");
+		if ( !self->connected)
+			return 0;
+
+		irttp_disconnect_request( self->tsap, NULL, P_NORMAL);
+
+		/* Reset values for this instance */
+		self->connected = FALSE;
+		self->eof = 0;
+		self->daddr = 0;
+		self->dtsap_sel = 0;
+		self->rx_flow = FLOW_START;
+		self->tx_flow = FLOW_START;
+		
+		wake_up_interruptible( &self->read_wait);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * Function irobex_dev_open (inode, file)
+ *
+ *    
+ *
+ */
+static int irobex_dev_open( struct inode * inode, struct file *file)
+{
+	struct irobex_cb *self;
+	
+	DEBUG( 4, "open_irobex:\n");
+	
+	self = irobex;
+
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == IROBEX_MAGIC, return -1;);
+
+	if ( self->count++) {
+		DEBUG( 3, "open_irobex: count not zero; actual = %d\n",
+		       self->count);
+		self->count--;
+		return -EBUSY;
+	}
+	
+	MOD_INC_USE_COUNT;
+	
+	return 0;
+}
+
+static int  irobex_dev_close( struct inode *inode, struct file *file)
+{
+	struct irobex_cb *self;
+	struct sk_buff *skb;
+	
+	DEBUG( 4, "close_irobex()\n");
+
+	self = irobex;
+
+	ASSERT( self != NULL, return -ENODEV;);
+	ASSERT( self->magic == IROBEX_MAGIC, return -EBADR;);
+
+	/* Deallocate buffers */
+	while (( skb = skb_dequeue( &self->rx_queue)) != NULL) {
+		DEBUG( 3, "irobex_close: freeing SKB\n");
+		dev_kfree_skb( skb);
+	}
+
+	/* if ( self->tsap) { */
+/* 		irttp_close_tsap( self->tsap); */
+/* 		self->tsap = NULL; */
+/* 		self->connected = FALSE; */
+/* 	} */
+
+	/* Remove this filp from the asynchronously notified filp's */
+	irobex_fasync( -1, file, 0);
+
+	self->count--;
+	
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+ * Function irobex_discovery_inication (daddr)
+ *
+ *    Remote device discovered, try query the remote IAS to see which
+ *    device it is, and which services it has.
+ *
+ */
+void irobex_discovery_indication( DISCOVERY *discovery) 
+{
+ 	struct irobex_cb *self;
+	
+	DEBUG( 4, __FUNCTION__ "()\n");
+	
+	self = irobex;
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);
+
+	self->daddr = discovery->daddr;
+	self->time_discovered = jiffies;
+	
+	wake_up_interruptible( &self->discover_wait);
+}
+
+/*
+ * Function irobex_disconnect_indication (handle, reason, priv)
+ *
+ *    
+ *
+ */
+void irobex_disconnect_indication( void *instance, void *sap, 
+				   LM_REASON reason, struct sk_buff *userdata)
+{
+	struct irobex_cb *self;
+	
+	DEBUG( 0, __FUNCTION__ "(), reason=%d\n", reason);
+
+	self = ( struct irobex_cb *) instance;
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);
+
+/* 	save_flags(flags); */
+/* 	cli(); */
+	
+	self->connected = FALSE;
+	self->eof = reason;
+	self->daddr = 0;
+	self->dtsap_sel = 0;
+	self->rx_flow = self->tx_flow = FLOW_START;
+	
+/* 	restore_flags(flags); */
+
+	wake_up_interruptible( &self->read_wait);
+	wake_up_interruptible( &self->connect_wait);
+	wake_up_interruptible( &self->write_wait);
+	
+	DEBUG( 4, "irobex_disconnect_indication: skb_queue_len=%d\n",
+	       skb_queue_len( &irobex->rx_queue));
+	
+	if ( userdata) {
+	        dev_kfree_skb( userdata);
+
+	}	
+}
+
+/*
+ * Function irobex_connect_confirm (instance, sap, qos, userdata)
+ *
+ *    Connection to peer IrOBEX layer established
+ *
+ */
+void irobex_connect_confirm( void *instance, void *sap, struct qos_info *qos,
+			     int max_sdu_size, struct sk_buff *userdata)
+{
+	struct irobex_cb *self;
+	
+	DEBUG( 0, __FUNCTION__ "()\n");
+	
+	self = ( struct irobex_cb *) instance;
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);
+	ASSERT( qos != NULL, return;);
+
+	DEBUG( 0, __FUNCTION__ "(), IrLAP data size=%d\n", 
+	       qos->data_size.value);
+
+	self->irlap_data_size = qos->data_size.value;
+	self->connected = TRUE;
+
+	/*
+	 *  Wake up any blocked process wanting to write. Finally this process
+	 *  can start writing since the connection is now open :-)
+	 */
+	wake_up_interruptible( &self->connect_wait);
+
+	if ( userdata) {
+	        dev_kfree_skb( userdata);
+
+	}
+}
+
+/*
+ * Function irobex_connect_response (handle)
+ *
+ *    Accept incomming connection
+ *
+ */
+void irobex_connect_response( struct irobex_cb *self)
+{
+	struct sk_buff *skb;
+/* 	__u8 *frame; */
+
+	DEBUG( 4, "irobex_connect_response()\n");
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);	
+
+	self->connected = TRUE;
+
+	skb = dev_alloc_skb( 64);
+	if (skb == NULL) {
+		DEBUG( 0,"irobex_connect_response: "
+		       "Could not allocate an sk_buff of length %d\n",
+		       64);
+		return;
+	}
+
+	/* Reserve space for MUX_CONTROL and LAP header */
+	skb_reserve( skb, TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER);
+
+	irttp_connect_response( self->tsap, SAR_DISABLE, skb);
+}
+
+/*
+ * Function irobex_connect_indication (handle, skb, priv)
+ *
+ *    Connection request from a remote device
+ *
+ */
+void irobex_connect_indication( void *instance, void *sap, 
+				struct qos_info *qos, int max_sdu_size,
+				struct sk_buff *userdata)
+{
+	struct irmanager_event mgr_event;
+	struct irobex_cb *self;
+	
+	DEBUG( 0, "irobex_connect_indication()\n");
+
+	self = ( struct irobex_cb *) instance;
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);	
+	ASSERT( userdata != NULL, return;);
+
+	self->eof = FALSE;
+
+	DEBUG( 0, "irobex_connect_indication, skb_len = %d\n", 
+	       (int) userdata->len);
+
+	DEBUG( 0, __FUNCTION__ "(), IrLAP data size=%d\n", 
+	       qos->data_size.value);
+
+	ASSERT( qos->data_size.value >= 64, return;);
+
+	self->irlap_data_size = qos->data_size.value;
+
+	irobex_connect_response( self);
+
+	mgr_event.event = EVENT_IROBEX_START;
+	sprintf( mgr_event.devname, "%s", self->devname);
+	irmanager_notify( &mgr_event);
+
+	wake_up_interruptible( &self->read_wait);
+
+	if ( userdata) {
+	        dev_kfree_skb( userdata);
+	}
+}
+
+/*
+ * Function irobex_data_indication (instance, sap, skb)
+ *
+ *    This function gets the data that is received on the data channel
+ *
+ */
+void irobex_data_indication( void *instance, void *sap, struct sk_buff *skb) 
+{
+
+	struct irobex_cb *self;
+	
+	self = ( struct irobex_cb *) instance;
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);
+	ASSERT( skb != NULL, return;);
+	
+	DEBUG( 4, __FUNCTION__ "(), len=%d\n", (int) skb->len);
+	
+	skb_queue_tail( &self->rx_queue, skb);
+	
+	/*
+	 *  Check if queues are beginning to get filled, and inform 
+	 *  IrTTP to slow down if that is the case
+	 */
+	if ( skb_queue_len( &self->rx_queue) > HIGH_THRESHOLD) {
+		DEBUG( 0, __FUNCTION__ 
+		       "(), rx_queue is full, telling IrTTP to slow down\n");
+		self->rx_flow = FLOW_STOP;
+		irttp_flow_request( self->tsap, FLOW_STOP);
+	}
+
+	/*
+	 *  Wake up process blocked on read or select
+	 */
+	wake_up_interruptible( &self->read_wait);
+
+	/* Send signal to asynchronous readers */
+	if ( self->async)
+		kill_fasync( self->async, SIGIO);
+}
+
+/*
+ * Function irobex_flow_indication (instance, sap, cmd)
+ *
+ *    
+ *
+ */
+void irobex_flow_indication( void *instance, void *sap, LOCAL_FLOW flow) 
+{
+	struct irobex_cb *self;
+
+	DEBUG( 0, __FUNCTION__ "()\n");
+	
+	self = ( struct irobex_cb *) instance;
+	
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);
+	
+	switch ( flow) {
+	case FLOW_STOP:
+		DEBUG( 0, __FUNCTION__ "(), IrTTP wants us to slow down\n");
+		self->tx_flow = flow;
+		break;
+	case FLOW_START:
+		self->tx_flow = flow;
+		DEBUG( 0, __FUNCTION__ "(), IrTTP wants us to start again\n");
+		wake_up_interruptible( &self->discover_wait);
+		break;
+	default:
+		DEBUG( 0, __FUNCTION__ "(), Unknown flow command!\n");
+	}
+}
+
+/*
+ * Function irobex_get_value_confirm (obj_id, value)
+ *
+ *    Got results from previous GetValueByClass request
+ *
+ */
+void irobex_get_value_confirm( __u16 obj_id, struct ias_value *value, 
+			       void *priv)
+{
+	struct irobex_cb *self;
+	
+	DEBUG( 4, "irobex_get_value_confirm()\n");
+
+	ASSERT( priv != NULL, return;);
+	self = ( struct irobex_cb *) priv;
+	
+	if ( !self || self->magic != IROBEX_MAGIC) {
+		DEBUG( 0, "irobex_get_value_confirm: bad magic!\n");
+		return;
+	}
+
+	switch ( value->type) {
+	case IAS_INTEGER:
+		DEBUG( 0, "irobex_get_value_confirm() int=%d\n", 
+		       value->t.integer);
+		
+		if ( value->t.integer != -1) {
+			self->dtsap_sel = value->t.integer;
+
+			/*
+			 *  Got the remote TSAP, so wake up any processes
+			 *  blocking on write. We don't do the connect 
+			 *  ourselves since we must make sure there is a 
+			 *  process that wants to make a connection, so we
+			 *  just let that process do the connect itself
+			 */
+			wake_up_interruptible( &self->ias_wait);
+		} else 
+			self->dtsap_sel = 0;
+		break;
+	case IAS_STRING:
+		DEBUG( 0, "irlan_get_value_confirm(), got string %s\n", 
+		       value->t.string);
+		break;
+	case IAS_OCT_SEQ:
+		DEBUG( 0, "irobex_get_value_confirm(), "
+		       "OCT_SEQ not implemented\n");
+		break;
+	case IAS_MISSING:
+		DEBUG( 0, "irobex_get_value_confirm(), "
+		       "MISSING not implemented\n");
+		break;
+	default:
+		DEBUG( 0, "irobex_get_value_confirm(), unknown type!\n");
+		break;
+	}
+}
+
+/*
+ * Function irobex_provider_confirm (dlsap)
+ *
+ *    IrOBEX provider is discovered. We can now establish connections
+ *    TODO: This function is currently not used!
+ */
+void irobex_provider_confirm( struct irobex_cb *self, __u8 dlsap) 
+{
+	/* struct irobex_cb *self = irobex; */
+	struct notify_t notify;
+	
+	DEBUG( 4, __FUNCTION__ "()\n");
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);
+
+	notify.data_indication = irobex_data_indication;
+	notify.connect_confirm = irobex_connect_confirm;
+	notify.connect_indication = irobex_connect_indication;
+	notify.flow_indication = irobex_flow_indication;
+	notify.disconnect_indication = irobex_disconnect_indication;
+	notify.instance = self;
+	
+	/* Create TSAP's */
+	self->tsap = irttp_open_tsap( LSAP_ANY, DEFAULT_INITIAL_CREDIT,
+				      &notify);
+	
+/* 	DEBUG( 0, "OBEX allocated TSAP%d for data\n", self->handle); */
+	
+	/* irlan_do_event( IAS_PROVIDER_AVAIL, NULL, &frame); */
+}
+
+/*
+ * Function irobex_register_server(void)
+ *
+ *    Register server support so we can accept incomming connections. We
+ *    must register both a TSAP for control and data
+ * 
+ */
+void irobex_register_server( struct irobex_cb *self)
+{
+	struct notify_t notify;
+	struct ias_object *obj;
+
+	DEBUG( 4, __FUNCTION__ "()\n");
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);
+
+	irda_notify_init( &notify);
+
+	notify.connect_confirm       = irobex_connect_confirm;
+	notify.connect_indication    = irobex_connect_indication;
+	notify.disconnect_indication = irobex_disconnect_indication;
+	notify.data_indication       = irobex_data_indication;
+	notify.flow_indication       = irobex_flow_indication;
+	notify.instance = self;
+	strcpy( notify.name, "IrOBEX");
+
+	self->tsap = irttp_open_tsap( TSAP_IROBEX, DEFAULT_INITIAL_CREDIT,
+				      &notify);	
+	if ( self->tsap == NULL) {
+		DEBUG( 0, "irobex_register_server(), "
+		       "Unable to allocate TSAP!\n");
+		return;
+	}
+
+	/* 
+	 *  Register with LM-IAS
+	 */
+	obj = irias_new_object( "OBEX", 0x42343);
+	irias_add_integer_attrib( obj, "IrDA:TinyTP:LsapSel", TSAP_IROBEX);
+	irias_insert_object( obj);
+
+}
+
+void irobex_watchdog_timer_expired( unsigned long data)
+{
+	struct irobex_cb *self = ( struct irobex_cb *) data;
+	
+	DEBUG( 0, __FUNCTION__ "()\n");
+
+	ASSERT( self != NULL, return;);
+	ASSERT( self->magic == IROBEX_MAGIC, return;);
+
+	wake_up_interruptible( &self->discover_wait);
+	wake_up_interruptible( &self->ias_wait);
+	wake_up_interruptible( &self->connect_wait);
+
+	/* irlmp_do_lsap_event( self, LM_WATCHDOG_TIMEOUT, NULL); */
+}
+
+#ifdef CONFIG_PROC_FS
+/*
+ * Function irobex_proc_read (buf, start, offset, len, unused)
+ *
+ *    Give some info to the /proc file system
+ */
+static int irobex_proc_read( char *buf, char **start, off_t offset, 
+			     int len, int unused)
+{
+ 	struct irobex_cb *self;
+
+	self = irobex;
+
+	ASSERT( self != NULL, return -1;);
+	ASSERT( self->magic == IROBEX_MAGIC, return -1;);
+
+	len = 0;
+	
+	len += sprintf( buf+len, "ifname: %s ",self->devname);
+	len += sprintf( buf+len, "connected: %s ", 
+			self->connected ? "TRUE": "FALSE");
+	len += sprintf( buf+len, "EOF: %s\n", self->eof ? "TRUE": "FALSE");
+	
+	return len;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("The Linux IrOBEX module"); 
+
+/*
+ * Function init_module (void)
+ *
+ *    Initialize the IrOBEX module, this function is called by the
+ *    modprobe(1) program.
+ */
+int init_module(void) 
+{
+	DEBUG( 4, "--> irobex: init_module\n");
+	
+	irobex_init();
+	
+	DEBUG( 4, "irobex: init_module -->\n");	
+	
+	return 0;
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Remove the IrOBEX module, this function is called by the rmmod(1)
+ *    program
+ */
+void cleanup_module(void) 
+{
+	DEBUG( 4, "--> irobex, cleanup_module\n");
+	/* 
+	 *  No need to check MOD_IN_USE, as sys_delete_module() checks. 
+	 */
+	
+	/* Free some memory */
+	irobex_cleanup();
+	
+	DEBUG( 4, "irobex, cleanup_module -->\n");
+}
+
+#endif /* MODULE */
+
+
+
+

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