patch-2.2.6 linux/net/irda/af_irda.c

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

diff -u --recursive --new-file v2.2.5/linux/net/irda/af_irda.c linux/net/irda/af_irda.c
@@ -1,16 +1,16 @@
 /*********************************************************************
  *                
  * Filename:      af_irda.c
- * Version:       0.1
+ * Version:       0.6
  * Description:   IrDA sockets implementation
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun May 31 10:12:43 1998
- * Modified at:   Sat Feb 20 01:31:15 1999
+ * Modified at:   Wed Apr  7 17:32:27 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Sources:       af_netroom.c, af_ax25.x
+ * Sources:       af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
  * 
- *     Copyright (c) 1997 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999 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 
@@ -23,44 +23,365 @@
  *     
  ********************************************************************/
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/init.h>
 #include <linux/if_arp.h>
 #include <linux/net.h>
+#include <linux/irda.h>
 
 #include <asm/uaccess.h>
 
 #include <net/sock.h>
 
 #include <net/irda/irda.h>
-#include <net/irda/irmod.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irias_object.h>
 #include <net/irda/irttp.h>
 
-extern int irda_init(void);
+extern int  irda_init(void);
 extern void irda_cleanup(void);
-extern int irlap_input(struct sk_buff *, struct device *, struct packet_type *);
+extern int  irlap_driver_rcv(struct sk_buff *, struct device *, 
+			     struct packet_type *);
 
 static struct proto_ops irda_proto_ops;
+static hashbin_t *cachelog = NULL;
+static struct wait_queue *discovery_wait; /* Wait for discovery */
 
 #define IRDA_MAX_HEADER (TTP_HEADER+LMP_HEADER+LAP_HEADER)
 
-#define IRDA_SOCKETS
+/*
+ * Function irda_data_indication (instance, sap, skb)
+ *
+ *    Received some data from TinyTP. Just queue it on the receive queue
+ *
+ */
+static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb)
+{
+	struct irda_sock *self;
+	struct sock *sk;
+	int err;
+
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	self = (struct irda_sock *) instance;
+	ASSERT(self != NULL, return -1;);
+
+	sk = self->sk;
+	ASSERT(sk != NULL, return -1;);
+
+	err = sock_queue_rcv_skb(sk, skb);
+	if (err) {
+		DEBUG(1, __FUNCTION__ "(), error: no more mem!\n");
+		self->rx_flow = FLOW_STOP;
+
+		/* When we return error, TTP will need to requeue the skb */
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * Function irda_disconnect_indication (instance, sap, reason, skb)
+ *
+ *    Connection has been closed. Chech reason to find out why
+ *
+ */
+static void irda_disconnect_indication(void *instance, void *sap, 
+				       LM_REASON reason, struct sk_buff *skb)
+{
+	struct irda_sock *self;
+	struct sock *sk;
+
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	self = (struct irda_sock *) instance;
+
+	sk = self->sk;
+	if (sk == NULL)
+		return;
+
+	sk->state     = TCP_CLOSE;
+        sk->err       = reason;
+        sk->shutdown |= SEND_SHUTDOWN;
+	if (!sk->dead) {
+		sk->state_change(sk);
+                sk->dead = 1;
+        }
+}
+
+/*
+ * Function irda_connect_confirm (instance, sap, qos, max_sdu_size, skb)
+ *
+ *    Connections has been confirmed by the remote device
+ *
+ */
+static void irda_connect_confirm(void *instance, void *sap, 
+				 struct qos_info *qos,
+				 __u32 max_sdu_size, struct sk_buff *skb)
+{
+	struct irda_sock *self;
+	struct sock *sk;
+
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	self = (struct irda_sock *) instance;
+
+	self->max_sdu_size_tx = max_sdu_size;
+	memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
+
+	sk = self->sk;
+	if (sk == NULL)
+		return;
+
+	/* We are now connected! */
+	sk->state = TCP_ESTABLISHED;
+	sk->state_change(sk);
+}
+
+/*
+ * Function irda_connect_indication(instance, sap, qos, max_sdu_size, userdata)
+ *
+ *    Incomming connection
+ *
+ */
+static void irda_connect_indication(void *instance, void *sap, 
+				    struct qos_info *qos, __u32 max_sdu_size,
+				    struct sk_buff *skb)
+{
+	struct irda_sock *self;
+	struct sock *sk;
+
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	self = (struct irda_sock *) instance;
+	
+	self->max_sdu_size_tx = max_sdu_size;
+	memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
+
+	sk = self->sk;
+	if (sk == NULL)
+		return;
+	
+	skb_queue_tail(&sk->receive_queue, skb);
+
+	sk->state_change(sk);
+}
+
+/*
+ * Function irda_connect_response (handle)
+ *
+ *    Accept incomming connection
+ *
+ */
+void irda_connect_response(struct irda_sock *self)
+{
+	struct sk_buff *skb;
+
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	ASSERT(self != NULL, return;);
+
+	skb = dev_alloc_skb(64);
+	if (skb == NULL) {
+		DEBUG( 0, __FUNCTION__ "() Could not allocate sk_buff!\n");
+		return;
+	}
+
+	/* Reserve space for MUX_CONTROL and LAP header */
+	skb_reserve(skb, TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER);
+
+	irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb);
+}
+
+
+/*
+ * Function irda_flow_indication (instance, sap, flow)
+ *
+ *    Used by TinyTP to tell us if it can accept more data or not
+ *
+ */
+static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) 
+{
+	struct irda_sock *self;
+	struct sock *sk;
+
+	DEBUG(1, __FUNCTION__ "()\n");
+	
+	self = (struct irda_sock *) instance;
+	ASSERT(self != NULL, return;);
+
+	sk = self->sk;
+	ASSERT(sk != NULL, 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(sk->sleep);
+		break;
+	default:
+		DEBUG( 0, __FUNCTION__ "(), Unknown flow command!\n");
+	}
+}
+
+/*
+ * Function irda_get_value_confirm (obj_id, value, priv)
+ *
+ *    Got answer from remote LM-IAS
+ *
+ */
+static void irda_get_value_confirm(__u16 obj_id, struct ias_value *value, 
+				   void *priv)
+{
+	struct irda_sock *self;
+	
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	ASSERT(priv != NULL, return;);
+	self = (struct irda_sock *) priv;
+	
+	if (!self)
+		return;
+
+	/* Check if request succeeded */
+	if (!value) {
+		DEBUG(0, __FUNCTION__ "(), IAS query failed!\n");
+
+		/* Wake up any processes waiting for result */
+		wake_up_interruptible(&self->ias_wait);
+		return;
+	}
+
+	switch (value->type) {
+	case IAS_INTEGER:
+		DEBUG(4, __FUNCTION__ "() int=%d\n", value->t.integer);
+		
+		if (value->t.integer != -1) {
+			self->dtsap_sel = value->t.integer;
+		} else 
+			self->dtsap_sel = 0;
+		break;
+	default:
+		DEBUG(0, __FUNCTION__ "(), bad type!\n");
+		break;
+	}
+	/* Wake up any processes waiting for result */
+	wake_up_interruptible(&self->ias_wait);
+}
+
+/*
+ * Function irda_discovery_indication (log)
+ *
+ *    Got a discovery log from IrLMP, wake ut any process waiting for answer
+ *
+ */
+static void irda_discovery_indication(hashbin_t *log)
+{
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	cachelog = log;
+
+	/* Wake up process if its waiting for device to be discovered */
+	wake_up_interruptible(&discovery_wait);
+}
+
+/*
+ * Function irda_open_tsap (self)
+ *
+ *    Open local Transport Service Access Point (TSAP)
+ *
+ */
+static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name)
+{
+	struct notify_t notify;
+	
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	/* Initialize callbacks to be used by the IrDA stack */
+	irda_notify_init(&notify);
+	notify.connect_confirm       = irda_connect_confirm;
+	notify.connect_indication    = irda_connect_indication;
+	notify.disconnect_indication = irda_disconnect_indication;
+	notify.data_indication       = irda_data_indication;
+	notify.flow_indication       = irda_flow_indication;
+	notify.instance = self;
+	strncpy(notify.name, name, NOTIFY_MAX_NAME);
+
+	self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT,
+				     &notify);	
+	if (self->tsap == NULL) {
+		DEBUG( 0, __FUNCTION__ "(), Unable to allocate TSAP!\n");
+		return -1;
+	}
+	/* Remember which TSAP selector we actually got */
+	self->stsap_sel = self->tsap->stsap_sel;
 
-#ifdef IRDA_SOCKETS
+	return 0;
+}
+
+/*
+ * Function irda_find_lsap_sel (self, name)
+ *
+ *    Try to lookup LSAP selector in remote LM-IAS
+ *
+ */
+static int irda_find_lsap_sel(struct irda_sock *self, char *name)
+{
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	ASSERT(self != NULL, return -1;);
+
+	/* Query remote LM-IAS */
+	iriap_getvaluebyclass_request(name, "IrDA:TinyTP:LsapSel",
+				      self->saddr, self->daddr,
+				      irda_get_value_confirm, self);
+	/* Wait for answer */
+	interruptible_sleep_on(&self->ias_wait);
+
+	if (self->dtsap_sel)
+		return 0;
+
+	return -ENETUNREACH; /* May not be true */
+}
 
 /*
  * Function irda_getname (sock, uaddr, uaddr_len, peer)
  *
- *    
+ *    Return the our own, or peers socket address (sockaddr_irda)
  *
  */
-static int irda_getname( struct socket *sock, struct sockaddr *uaddr,
-			 int *uaddr_len, int peer)
+static int irda_getname(struct socket *sock, struct sockaddr *uaddr,
+			int *uaddr_len, int peer)
 {
-	DEBUG( 0, __FUNCTION__ "(), Not implemented!\n");
+	struct sockaddr_irda saddr;
+	struct sock *sk = sock->sk;
+
+	if (peer) {
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+		
+		saddr.sir_family = AF_IRDA;
+		saddr.sir_lsap_sel = sk->protinfo.irda->dtsap_sel;
+		saddr.sir_addr = sk->protinfo.irda->daddr;
+	} else {
+		saddr.sir_family = AF_IRDA;
+		saddr.sir_lsap_sel = sk->protinfo.irda->stsap_sel;
+		saddr.sir_addr = sk->protinfo.irda->saddr;
+	}
+	
+	DEBUG(1, __FUNCTION__ "(), tsap_sel = %#x\n", saddr.sir_lsap_sel);
+	DEBUG(1, __FUNCTION__ "(), addr = %08x\n", saddr.sir_addr);
+
+	if (*uaddr_len > sizeof (struct sockaddr_irda))
+		*uaddr_len = sizeof (struct sockaddr_irda);
+	memcpy(uaddr, &saddr, *uaddr_len);
 
 	return 0;
 }
@@ -68,22 +389,148 @@
 /*
  * Function irda_listen (sock, backlog)
  *
- *    
+ *    Just move to the listen state
  *
  */
 static int irda_listen( struct socket *sock, int backlog)
 {
 	struct sock *sk = sock->sk;
 
-	if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) {
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	if (sk->type == SOCK_STREAM && sk->state != TCP_LISTEN) {
 		sk->max_ack_backlog = backlog;
 		sk->state           = TCP_LISTEN;
+
 		return 0;
 	}
-
+	
 	return -EOPNOTSUPP;
 }
 
+/*
+ * Function irda_bind (sock, uaddr, addr_len)
+ *
+ *    Used by servers to register their well known TSAP
+ *
+ */
+static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sock *sk = sock->sk;
+	struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
+	struct irda_sock *self;
+	__u16 hints = 0;
+	int err;
+
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	self = sk->protinfo.irda;
+	ASSERT(self != NULL, return -1;);
+
+	if ((addr_len < sizeof(struct sockaddr_irda)) || 
+	    (addr_len > sizeof(struct sockaddr_irda)))
+		return -EINVAL;
+
+	err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name);
+	if (err < 0)
+		return -ENOMEM;
+	
+	/*  Register with LM-IAS */
+	self->ias_obj = irias_new_object(addr->sir_name, jiffies);
+	irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel", 
+				 self->stsap_sel);
+	irias_insert_object(self->ias_obj);
+
+	/* Fill in some default hint bits values */
+	if (strncmp(addr->sir_name, "OBEX", 4) == 0)
+		hints = irlmp_service_to_hint(S_OBEX);
+	
+	if (hints)
+		self->skey = irlmp_register_service(hints);
+
+	return 0;
+}
+
+/*
+ * Function irda_accept (sock, newsock, flags)
+ *
+ *    Wait for incomming connection
+ *
+ */
+static int irda_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	struct irda_sock *self, *new;
+	struct sock *sk = sock->sk;
+	struct sock *newsk;
+	struct sk_buff *skb;
+
+	self = sk->protinfo.irda;
+	ASSERT(self != NULL, return -1;);
+
+	if (sock->state != SS_UNCONNECTED)
+		return -EINVAL;
+
+	if ((sk = sock->sk) == NULL)
+		return -EINVAL;
+
+	if (sk->type != SOCK_STREAM)
+		return -EOPNOTSUPP;
+
+	if (sk->state != TCP_LISTEN)
+		return -EINVAL;
+
+	/*
+	 *	The read queue this time is holding sockets ready to use
+	 *	hooked into the SABM we saved
+	 */
+	do {
+		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
+			if (flags & O_NONBLOCK)
+				return -EWOULDBLOCK;
+
+			interruptible_sleep_on(sk->sleep);
+			if (signal_pending(current)) 
+				return -ERESTARTSYS;
+		}
+	} while (skb == NULL);
+
+ 	newsk = newsock->sk;
+	newsk->state = TCP_ESTABLISHED;
+
+	new = newsk->protinfo.irda;
+	ASSERT(new != NULL, return -1;);
+
+	/* Now attach up the new socket */
+	new->tsap = irttp_dup(self->tsap, new);
+	if (!new->tsap) {
+		DEBUG(0, __FUNCTION__ "(), dup failed!\n");
+		return -1;
+	}
+		
+	new->stsap_sel = new->tsap->stsap_sel;
+	new->dtsap_sel = new->tsap->dtsap_sel;
+	new->saddr = irttp_get_saddr(new->tsap);
+	new->saddr = irttp_get_saddr(new->tsap);
+
+	new->max_sdu_size_tx = self->max_sdu_size_tx;
+	new->max_sdu_size_rx = self->max_sdu_size_rx;
+	memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info));
+
+	/* Clean up the original one to keep it in listen state */
+	self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY;
+	self->tsap->lsap->lsap_state = LSAP_DISCONNECTED;
+
+	skb->sk = NULL;
+	skb->destructor = NULL;
+	kfree_skb(skb);
+	sk->ack_backlog--;
+
+	newsock->state = SS_CONNECTED;
+
+	irda_connect_response(new);
+
+	return 0;
+}
 
 /*
  * Function irda_connect (sock, uaddr, addr_len, flags)
@@ -91,17 +538,18 @@
  *    Connect to a IrDA device
  *
  */
-static int irda_connect( struct socket *sock, struct sockaddr *uaddr,
-	int addr_len, int flags)
+static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
+			int addr_len, int flags)
 {
-#if 0
-	struct sock *sk = (struct sock *)sock->data;
-	struct sockaddr_irda *addr = (struct sockaddr_irda *)uaddr;
-	irda_address *user, *source = NULL;
-	struct device *dev;
+	struct sock *sk = sock->sk;
+	struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
+	struct irda_sock *self;
+	int err;
+
+	self = sk->protinfo.irda;
+
+	DEBUG(1, __FUNCTION__ "()\n");
 
-	DEBUG( 0, __FUNCTION__ "()\n");
-	
 	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
 		sock->state = SS_CONNECTED;
 		return 0;   /* Connect completed during a ERESTARTSYS event */
@@ -121,6 +569,36 @@
 	if (addr_len != sizeof(struct sockaddr_irda))
 		return -EINVAL;
 
+	/* Check if user supplied the required destination device address */
+	if (!addr->sir_addr)
+		return -EINVAL;
+
+	self->daddr = addr->sir_addr;
+	DEBUG(1, __FUNCTION__ "(), daddr = %08x\n", self->daddr);
+
+	/* Query remote LM-IAS */
+	err = irda_find_lsap_sel(self, addr->sir_name);
+	if (err) {
+		DEBUG(0, __FUNCTION__ "(), connect failed!\n");
+		return err;
+	}
+
+	/* Check if we have opened a local TSAP */
+	if (!self->tsap)
+		irda_open_tsap(self, LSAP_ANY, addr->sir_name);
+	
+	/* Move to connecting socket, start sending Connect Requests */
+	sock->state = SS_CONNECTING;
+	sk->state   = TCP_SYN_SENT;
+
+	/* Connect to remote device */
+	err = irttp_connect_request(self->tsap, self->dtsap_sel, 
+				    self->saddr, self->daddr, NULL, 
+				    self->max_sdu_size_rx, NULL);
+	if (err) {
+		DEBUG(0, __FUNCTION__ "(), connect failed!\n");
+		return err;
+	}
 
 	/* Now the loop */
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
@@ -130,9 +608,9 @@
 	
 	/* A Connect Ack with Choke or timeout or failed routing will go to
 	 * closed.  */
-	while ( sk->state == TCP_SYN_SENT) {
-		interruptible_sleep_on( sk->sleep);
-		if (current->signal & ~current->blocked) {
+	while (sk->state == TCP_SYN_SENT) {
+		interruptible_sleep_on(sk->sleep);
+		if (signal_pending(current)) {
 			sti();
 			return -ERESTARTSYS;
 		}
@@ -141,13 +619,13 @@
 	if (sk->state != TCP_ESTABLISHED) {
 		sti();
 		sock->state = SS_UNCONNECTED;
-		return sock_error( sk);	/* Always set at this point */
+		return sock_error(sk);	/* Always set at this point */
 	}
 	
 	sock->state = SS_CONNECTED;
-
+	
 	sti();
-#endif
+	
 	return 0;
 }
 
@@ -160,51 +638,79 @@
 static int irda_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
+	struct irda_sock *self;
 
-	DEBUG( 0, __FUNCTION__ "()\n");
-
-	/* Compatibility */
-	if (sock->type == SOCK_PACKET) {
-		static int warned; 
-		if (net_families[PF_PACKET]==NULL)
-		{
-#if defined(CONFIG_KMOD) && defined(CONFIG_PACKET_MODULE)
-			char module_name[30];
-			sprintf(module_name,"net-pf-%d", PF_PACKET);
-			request_module(module_name);
-			if (net_families[PF_PACKET] == NULL)
-#endif
-				return -ESOCKTNOSUPPORT;
-		}
-		if (!warned++)
-			printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n", current->comm);
-		return net_families[PF_PACKET]->create(sock, protocol);
-	}
+	DEBUG(2, __FUNCTION__ "()\n");
 	
-/* 	if (sock->type != SOCK_SEQPACKET || protocol != 0) */
-/* 		return -ESOCKTNOSUPPORT; */
+	/* Check for valid socket type */
+	switch (sock->type) {
+	case SOCK_STREAM:   /* FALLTHROUGH */
+	case SOCK_SEQPACKET:
+		break;
+	default:
+		return -ESOCKTNOSUPPORT;
+	}
 
 	/* Allocate socket */
 	if ((sk = sk_alloc(PF_IRDA, GFP_ATOMIC, 1)) == NULL)
 		return -ENOMEM;
 	
+	self = kmalloc(sizeof(struct irda_sock), GFP_ATOMIC);
+	if (self == NULL)
+		return -ENOMEM;
+	memset(self, 0, sizeof(struct irda_sock));
+
+	self->sk = sk;
+	sk->protinfo.irda = self;
+
 	sock_init_data(sock, sk);
 
 	sock->ops    = &irda_proto_ops;
 	sk->protocol = protocol;
 
+	/* Register as a client with IrLMP */
+	self->ckey = irlmp_register_client(0, NULL, NULL);
+	self->mask = 0xffff;
+	self->rx_flow = self->tx_flow = FLOW_START;
+	self->max_sdu_size_rx = SAR_DISABLE; /* Default value */
+	self->nslots = 6; /* Default for now */
+
+	/* Notify that we are using the irda module, so nobody removes it */
+	irda_mod_inc_use_count();
+
 	return 0;
 }
 
 /*
- * Function irda_destroy_socket (tsap)
+ * Function irda_destroy_socket (self)
  *
  *    Destroy socket
  *
  */
-void irda_destroy_socket(struct tsap_cb *tsap)
+void irda_destroy_socket(struct irda_sock *self)
 {
-	DEBUG( 0, __FUNCTION__ "()\n");
+	DEBUG(2, __FUNCTION__ "()\n");
+
+	ASSERT(self != NULL, return;);
+
+	/* Unregister with IrLMP */
+	irlmp_unregister_client(self->ckey);
+	irlmp_unregister_service(self->skey);
+
+	/* Unregister with LM-IAS */
+	if (self->ias_obj)
+		irias_delete_object(self->ias_obj->name);
+
+	if (self->tsap) {
+		irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
+		irttp_close_tsap(self->tsap);
+		self->tsap = NULL;
+	}
+
+	kfree(self);
+
+	/* Notify that we are not using the irda module anymore */
+	irda_mod_dec_use_count();
 
 	return;
 }
@@ -215,41 +721,108 @@
  *    
  *
  */
-static int irda_release( struct socket *sock, struct socket *peer)
+static int irda_release(struct socket *sock, struct socket *peer)
 {
-#if 0
-        struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 	
-	DEBUG( 0, __FUNCTION__ "()\n");
+	DEBUG(1, __FUNCTION__ "()\n");
 
-
-        if (sk == NULL) return 0;
+        if (sk == NULL) 
+		return 0;
 	
-        if (sk->type == SOCK_SEQPACKET) {                    
+	sk->state       = TCP_CLOSE;
+	sk->shutdown   |= SEND_SHUTDOWN;
+	sk->state_change(sk);
+	sk->dead        = 1;
 
-	
-	} else {
-		sk->state       = TCP_CLOSE;
-                sk->shutdown   |= SEND_SHUTDOWN;
-                sk->state_change(sk);
-                sk->dead        = 1;
-                irda_destroy_socket( sk->protinfo.irda);
-        }
+	irda_destroy_socket(sk->protinfo.irda);
+
+        sock->sk   = NULL;      
+        sk->socket = NULL;      /* Not used, but we should do this. */
 
-        sock->data = NULL;      
-        sk->socket = NULL;      /* Not used, but we should do this. **/
-#endif
         return 0;
 }
 
+/*
+ * Function irda_sendmsg (sock, msg, len, scm)
+ *
+ *    Send message down to TinyTP
+ *
+ */
+static int irda_sendmsg(struct socket *sock, struct msghdr *msg, int len, 
+			struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+/* 	struct sockaddr_irda *addr = (struct sockaddr_irda *) msg->msg_name; */
+	struct irda_sock *self;
+	struct sk_buff *skb;
+	unsigned char *asmptr;
+	int err;
+
+	DEBUG(4, __FUNCTION__ "(), len=%d\n", len);
+
+	if (msg->msg_flags & ~MSG_DONTWAIT)
+		return -EINVAL;
+
+	if (sk->shutdown & SEND_SHUTDOWN) {
+		send_sig(SIGPIPE, current, 0);
+		return -EPIPE;
+	}
+
+	self = sk->protinfo.irda;
+	ASSERT(self != NULL, return -1;);
+
+	/* Check if IrTTP is wants us to slow down */
+	while (self->tx_flow == FLOW_STOP) {
+		DEBUG(2, __FUNCTION__ "(), IrTTP is busy, going to sleep!\n");
+		interruptible_sleep_on(sk->sleep);
+		
+		/* Check if we are still connected */
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+	}
+
+	skb = sock_alloc_send_skb(sk, len + IRDA_MAX_HEADER, 0, 
+				  msg->msg_flags & MSG_DONTWAIT, &err);
+	if (!skb)
+		return -ENOBUFS;
+
+	skb_reserve(skb, IRDA_MAX_HEADER);
+	
+	DEBUG(4, __FUNCTION__ "(), appending user data\n");
+	asmptr = skb->h.raw = skb_put(skb, len);
+	memcpy_fromiovec(asmptr, msg->msg_iov, len);
+
+	/* 
+	 * Just send the message to TinyTP, and let it deal with possible 
+	 * errors. No need to duplicate all that here
+	 */
+	err = irttp_data_request(self->tsap, skb);
+	if (err) {
+		DEBUG(0, __FUNCTION__ "(), err=%d\n", err);
+		return err;
+	}
+	return len;
+}
+
+/*
+ * Function irda_recvmsg (sock, msg, size, flags, scm)
+ *
+ *    Try to receive message and copy it to user
+ *
+ */
 static int irda_recvmsg(struct socket *sock, struct msghdr *msg, int size,
 			int flags, struct scm_cookie *scm)
 {
+	struct irda_sock *self;
 	struct sock *sk = sock->sk;
 	struct sk_buff *skb;
 	int copied, err;
 
-	DEBUG(0, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
+
+	self = sk->protinfo.irda;
+	ASSERT(self != NULL, return -1;);
 
 	skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, 
 				flags & MSG_DONTWAIT, &err);
@@ -267,6 +840,20 @@
 
 	skb_free_datagram(sk, skb);
 
+	/*
+	 *  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 ((atomic_read(&sk->rmem_alloc) << 2) <= sk->rcvbuf) {
+			DEBUG(2, __FUNCTION__ "(), Starting IrTTP\n");
+			self->rx_flow = FLOW_START;
+			irttp_flow_request(self->tsap, FLOW_START);
+		}
+	}
+
 	return copied;
 }
 
@@ -279,10 +866,11 @@
 }
 
 
-unsigned int irda_poll( struct file *file, struct socket *sock, 
-			struct poll_table_struct *wait)
+unsigned int irda_poll(struct file *file, struct socket *sock, 
+		       struct poll_table_struct *wait)
 {
 	DEBUG(0, __FUNCTION__ "()\n");
+
 	return 0;
 }
 
@@ -296,7 +884,7 @@
 {
 	struct sock *sk = sock->sk;
 
-	DEBUG( 0, __FUNCTION__ "()\n");
+	DEBUG(0, __FUNCTION__ "(), cmd=%#x\n", cmd);
 	
 	switch (cmd) {
 	case TIOCOUTQ: {
@@ -343,13 +931,163 @@
 		return -EINVAL;
 		
 	default:
-		return dev_ioctl(cmd, (void *)arg);
+		return dev_ioctl(cmd, (void *) arg);
 	}
 
 	/*NOTREACHED*/
 	return 0;
 }
 
+/*
+ * Function irda_setsockopt (sock, level, optname, optval, optlen)
+ *
+ *    Set some options for the socket
+ *
+ */
+static int irda_setsockopt(struct socket *sock, int level, int optname, 
+			   char *optval, int optlen)
+{
+ 	struct sock *sk = sock->sk;
+	struct irda_sock *self;
+	int opt;
+	
+	DEBUG(0, __FUNCTION__ "()\n");
+
+	self = sk->protinfo.irda;
+	ASSERT(self != NULL, return -1;);
+
+	if (level != SOL_IRLMP)
+		return -ENOPROTOOPT;
+	
+	if (optlen < sizeof(int))
+		return -EINVAL;
+	
+	if (get_user(opt, (int *)optval))
+		return -EFAULT;
+	
+	switch (optname) {
+	case IRLMP_IAS_SET:
+		DEBUG(0, __FUNCTION__ "(), sorry not impl. yet!\n");
+		return 0;
+	case IRTTP_MAX_SDU_SIZE:
+		DEBUG(0, __FUNCTION__ "(), setting max_sdu_size = %d\n", opt);
+		self->max_sdu_size_rx = opt;
+		break;
+	default:
+		return -ENOPROTOOPT;
+	}
+	return 0;
+}
+
+/*
+ * Function irda_getsockopt (sock, level, optname, optval, optlen)
+ *
+ *    
+ *
+ */
+static int irda_getsockopt(struct socket *sock, int level, int optname, 
+			   char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
+	struct irda_sock *self;
+	struct irda_device_list *list;
+	__u8 optbuf[sizeof(struct irda_device_list) +
+		   sizeof(struct irda_device_info)*10];
+	discovery_t *discovery;
+	int val = 0;
+	int len = 0;
+	int i = 0;
+
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	self = sk->protinfo.irda;
+
+	if (level != SOL_IRLMP)
+		return -ENOPROTOOPT;
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	switch (optname) {
+	case IRLMP_ENUMDEVICES:
+		DEBUG(1, __FUNCTION__ "(), IRLMP_ENUMDEVICES\n");
+		
+		/* Tell IrLMP we want to be notified */
+		irlmp_update_client(self->ckey, self->mask, NULL, 
+				    irda_discovery_indication);
+
+		/* Do some discovery */
+		irlmp_discovery_request(self->nslots);
+
+		/* Devices my be discovered already */
+		if (!cachelog) {
+			DEBUG(2, __FUNCTION__ "(), no log!\n");
+
+			/* Sleep until device(s) discovered */
+			interruptible_sleep_on(&discovery_wait);
+			if (!cachelog)
+				return -1;
+		}
+
+		list = (struct irda_device_list *) optbuf;
+		/* 
+		 * Now, check all discovered devices (if any), and notify
+		 * client only about the services that the client is
+		 * interested in 
+		 */
+		discovery = (discovery_t *) hashbin_get_first(cachelog);
+		while (discovery != NULL) {
+			/* Mask out the ones we don't want */
+			if (discovery->hints.word & self->mask) {
+				/* Copy discovery information */
+				list->dev[i].saddr = discovery->saddr;
+				list->dev[i].daddr = discovery->daddr;
+				list->dev[i].charset = discovery->charset;
+				list->dev[i].hints[0] = discovery->hints.byte[0];
+				list->dev[i].hints[1] = discovery->hints.byte[1];
+				strncpy(list->dev[i].info, discovery->info, 22);
+				if (++i >= 10)
+					break;
+			}
+			discovery = (discovery_t *) hashbin_get_next(cachelog);
+		}
+		cachelog = NULL;
+
+		list->len = i;
+		len = sizeof(struct irda_device_list) +
+			sizeof(struct irda_device_info) * i;
+
+		DEBUG(1, __FUNCTION__ "(), len=%d, i=%d\n", len, i);
+
+		if (put_user(len, optlen))
+			return -EFAULT;
+		
+		if (copy_to_user(optval, &optbuf, len))
+			return -EFAULT;
+		break;
+	case IRTTP_MAX_SDU_SIZE:
+		if (self->max_sdu_size_tx != SAR_DISABLE)
+			val = self->max_sdu_size_tx;
+		else
+			/* SAR is disabled, so use the IrLAP data size
+			 * instead */
+			val = self->qos_tx.data_size.value - IRDA_MAX_HEADER;
+
+		DEBUG(0, __FUNCTION__ "(), getting max_sdu_size = %d\n", val);
+		len = sizeof(int);
+		if (put_user(len, optlen))
+			return -EFAULT;
+		
+		if (copy_to_user(optval, &val, len))
+			return -EFAULT;
+		break;
+	default:
+		return -ENOPROTOOPT;
+	}
+
+	return 0;
+}
+
 static struct net_proto_family irda_family_ops =
 {
 	PF_IRDA,
@@ -361,24 +1099,28 @@
 	
 	sock_no_dup,
 	irda_release,
-	sock_no_bind,
+	irda_bind,
 	irda_connect,
 	sock_no_socketpair,
-	sock_no_accept,
+	irda_accept,
 	irda_getname,
 	irda_poll,
 	irda_ioctl,
 	irda_listen,
 	irda_shutdown,
-	sock_no_setsockopt,
-	sock_no_getsockopt,
+	irda_setsockopt,
+	irda_getsockopt,
 	sock_no_fcntl,
-	sock_no_sendmsg,
+	irda_sendmsg,
 	irda_recvmsg
 };
 
-#endif /* IRDA_SOCKETS */
-
+/*
+ * Function irda_device_event (this, event, ptr)
+ *
+ *    
+ *
+ */
 static int irda_device_event(struct notifier_block *this, unsigned long event,
 			     void *ptr)
 {
@@ -412,7 +1154,7 @@
 {
 	0,	/* MUTTER ntohs(ETH_P_IRDA),*/
 	NULL,
-	irlap_input, /* irda_driver_rcv, */
+	irlap_driver_rcv,
 	NULL,
 	NULL,
 };
@@ -449,6 +1191,7 @@
  *    Remove IrDA protocol layer
  *
  */
+#ifdef MODULE
 void irda_proto_cleanup(void)
 {
 	DEBUG( 4, __FUNCTION__ "\n");
@@ -463,4 +1206,4 @@
 	
         return;
 }
-
+#endif /* MODULE */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)