summaryrefslogtreecommitdiffstats
path: root/net/irda
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-11-28 03:58:46 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-11-28 03:58:46 +0000
commitb63ad0882a16a5d28003e57f2b0b81dee3fb322b (patch)
tree0a343ce219e2b8b38a5d702d66032c57b83d9720 /net/irda
parenta9d7bff9a84dba79609a0002e5321b74c4d64c64 (diff)
Merge with 2.4.0-test11.
Diffstat (limited to 'net/irda')
-rw-r--r--net/irda/Config.in1
-rw-r--r--net/irda/Makefile13
-rw-r--r--net/irda/af_irda.c713
-rw-r--r--net/irda/compressors/irda_deflate.c56
-rw-r--r--net/irda/discovery.c154
-rw-r--r--net/irda/ircomm/ircomm_core.c5
-rw-r--r--net/irda/ircomm/ircomm_tty.c15
-rw-r--r--net/irda/ircomm/ircomm_tty_attach.c15
-rw-r--r--net/irda/irda_device.c8
-rw-r--r--net/irda/iriap.c13
-rw-r--r--net/irda/irias_object.c55
-rw-r--r--net/irda/irlan/irlan_client.c90
-rw-r--r--net/irda/irlan/irlan_common.c253
-rw-r--r--net/irda/irlan/irlan_eth.c41
-rw-r--r--net/irda/irlan/irlan_provider.c79
-rw-r--r--net/irda/irlap.c49
-rw-r--r--net/irda/irlap_comp.c2
-rw-r--r--net/irda/irlap_event.c212
-rw-r--r--net/irda/irlap_frame.c64
-rw-r--r--net/irda/irlmp.c168
-rw-r--r--net/irda/irlmp_event.c15
-rw-r--r--net/irda/irlmp_frame.c67
-rw-r--r--net/irda/irnet/Config.in1
-rw-r--r--net/irda/irnet/Makefile22
-rw-r--r--net/irda/irnet/irnet.h453
-rw-r--r--net/irda/irnet/irnet_irda.c1481
-rw-r--r--net/irda/irnet/irnet_irda.h169
-rw-r--r--net/irda/irnet/irnet_ppp.c1052
-rw-r--r--net/irda/irnet/irnet_ppp.h130
-rw-r--r--net/irda/irqueue.c126
-rw-r--r--net/irda/irsyms.c (renamed from net/irda/irmod.c)299
-rw-r--r--net/irda/irsysctl.c27
-rw-r--r--net/irda/irttp.c34
-rw-r--r--net/irda/parameters.c10
-rw-r--r--net/irda/qos.c86
-rw-r--r--net/irda/timer.c32
-rw-r--r--net/irda/wrapper.c10
37 files changed, 4809 insertions, 1211 deletions
diff --git a/net/irda/Config.in b/net/irda/Config.in
index 6f4f716f8..5ab40b985 100644
--- a/net/irda/Config.in
+++ b/net/irda/Config.in
@@ -11,6 +11,7 @@ if [ "$CONFIG_NET" != "n" ]; then
if [ "$CONFIG_IRDA" != "n" ]; then
comment 'IrDA protocols'
source net/irda/irlan/Config.in
+ source net/irda/irnet/Config.in
source net/irda/ircomm/Config.in
bool ' Ultra (connectionless) protocol' CONFIG_IRDA_ULTRA
bool ' IrDA protocol options' CONFIG_IRDA_OPTIONS
diff --git a/net/irda/Makefile b/net/irda/Makefile
index ac2bdc063..3ab07c88d 100644
--- a/net/irda/Makefile
+++ b/net/irda/Makefile
@@ -7,7 +7,7 @@
#
# Note 2! The CFLAGS definition is now in the main makefile...
-ALL_SUB_DIRS := irlan ircomm compressors
+ALL_SUB_DIRS := irlan irnet ircomm compressors
SUB_DIRS :=
MOD_SUB_DIRS :=
OX_OBJS :=
@@ -17,7 +17,7 @@ O_OBJS := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \
irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \
irttp.o irda_device.o irias_object.o crc.o wrapper.o af_irda.o \
discovery.o parameters.o
-OX_OBJS := irmod.o
+OX_OBJS := irsyms.o
ifeq ($(CONFIG_IRDA),m)
M_OBJS := $(O_TARGET)
@@ -44,6 +44,15 @@ else
endif
endif
+ifeq ($(CONFIG_IRNET),y)
+SUB_DIRS += irnet
+O_OBJS += irnet/irnet.o
+else
+ ifeq ($(CONFIG_IRNET),m)
+ MOD_SUB_DIRS += irnet
+ endif
+endif
+
ifeq ($(CONFIG_IRDA_COMPRESSION),y)
SUB_DIRS += compressors
MOD_IN_SUB_DIRS += compressors
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index 69db79a0e..e5504ee96 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -7,11 +7,11 @@
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun May 31 10:12:43 1998
* Modified at: Sat Dec 25 21:10:23 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
+ * Modified by: Dag Brattli <dag@brattli.net>
* Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
*
* Copyright (c) 1999 Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1999 Jean Tourrilhes <jeant@rockfort.hpl.hp.com>
+ * Copyright (c) 1999 Jean Tourrilhes <jt@hpl.hp.com>
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -43,10 +43,11 @@
********************************************************************/
#include <linux/config.h>
-#include <linux/init.h>
+#include <linux/module.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>
@@ -79,11 +80,12 @@ static struct proto_ops irda_ultra_ops;
#define ULTRA_MAX_DATA 382
#endif /* CONFIG_IRDA_ULTRA */
-static hashbin_t *cachelog = NULL;
-static DECLARE_WAIT_QUEUE_HEAD(discovery_wait); /* Wait for discovery */
-
#define IRDA_MAX_HEADER (TTP_MAX_HEADER)
+#ifdef CONFIG_IRDA_DEBUG
+__u32 irda_debug = IRDA_DEBUG_LEVEL;
+#endif
+
/*
* Function irda_data_indication (instance, sap, skb)
*
@@ -117,7 +119,7 @@ static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb)
/*
* Function irda_disconnect_indication (instance, sap, reason, skb)
*
- * Connection has been closed. Chech reason to find out why
+ * Connection has been closed. Check reason to find out why
*
*/
static void irda_disconnect_indication(void *instance, void *sap,
@@ -141,6 +143,27 @@ static void irda_disconnect_indication(void *instance, void *sap,
sk->state_change(sk);
sk->dead = 1;
}
+
+ /* Close our TSAP.
+ * If we leave it open, IrLMP put it back into the list of
+ * unconnected LSAPs. The problem is that any incomming request
+ * can then be matched to this socket (and it will be, because
+ * it is at the head of the list). This would prevent any
+ * listening socket waiting on the same TSAP to get those requests.
+ * Some apps forget to close sockets, or hang to it a bit too long,
+ * so we may stay in this dead state long enough to be noticed...
+ * Note : all socket function do check sk->state, so we are safe...
+ * Jean II
+ */
+ irttp_close_tsap(self->tsap);
+ self->tsap = NULL;
+
+ /* Note : once we are there, there is not much you want to do
+ * with the socket anymore, apart from closing it.
+ * For example, bind() and connect() won't reset sk->err,
+ * sk->shutdown and sk->dead to valid values...
+ * Jean II
+ */
}
/*
@@ -326,19 +349,19 @@ static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
/*
* Function irda_getvalue_confirm (obj_id, value, priv)
*
- * Got answer from remote LM-IAS
+ * Got answer from remote LM-IAS, just pass object to requester...
*
+ * Note : duplicate from above, but we need our own version that
+ * doesn't touch the dtsap_sel and save the full value structure...
*/
static void irda_getvalue_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv)
+ struct ias_value *value, void *priv)
{
struct irda_sock *self;
IRDA_DEBUG(2, __FUNCTION__ "()\n");
- ASSERT(priv != NULL, return;);
self = (struct irda_sock *) priv;
-
if (!self) {
WARNING(__FUNCTION__ "(), lost myself!\n");
return;
@@ -348,51 +371,90 @@ static void irda_getvalue_confirm(int result, __u16 obj_id,
iriap_close(self->iriap);
self->iriap = NULL;
- self->errno = result;
-
/* Check if request succeeded */
if (result != IAS_SUCCESS) {
- IRDA_DEBUG(0, __FUNCTION__ "(), IAS query failed!\n");
+ IRDA_DEBUG(1, __FUNCTION__ "(), IAS query failed! (%d)\n",
+ result);
+
+ self->errno = result; /* We really need it later */
/* Wake up any processes waiting for result */
- wake_up_interruptible(&self->ias_wait);
+ wake_up_interruptible(&self->query_wait);
return;
}
- switch (value->type) {
- case IAS_INTEGER:
- IRDA_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:
- IRDA_DEBUG(0, __FUNCTION__ "(), bad type!\n");
- break;
- }
- irias_delete_value(value);
+ /* Pass the object to the caller (so the caller must delete it) */
+ self->ias_result = value;
+ self->errno = 0;
/* Wake up any processes waiting for result */
- wake_up_interruptible(&self->ias_wait);
+ wake_up_interruptible(&self->query_wait);
}
/*
- * Function irda_discovery_indication (log)
+ * Function irda_selective_discovery_indication (discovery)
*
- * Got a discovery log from IrLMP, wake ut any process waiting for answer
+ * Got a selective discovery indication from IrLMP.
*
+ * IrLMP is telling us that this node is matching our hint bit
+ * filter. Check if it's a newly discovered node (or if node changed its
+ * hint bits), and then wake up any process waiting for answer...
*/
-static void irda_discovery_indication(hashbin_t *log)
+static void irda_selective_discovery_indication(discovery_t *discovery,
+ void *priv)
{
+ struct irda_sock *self;
+
IRDA_DEBUG(2, __FUNCTION__ "()\n");
- cachelog = log;
+ self = (struct irda_sock *) priv;
+ if (!self) {
+ WARNING(__FUNCTION__ "(), lost myself!\n");
+ return;
+ }
+
+ /* Check if node is discovered is a new one or an old one.
+ * We check when how long ago this node was discovered, with a
+ * coarse timeout (we may miss some discovery events or be delayed).
+ * Note : by doing this test here, we avoid waking up a process ;-)
+ */
+ if((jiffies - discovery->first_timestamp) >
+ (sysctl_discovery_timeout * HZ)) {
+ return; /* Too old, not interesting -> goodbye */
+ }
+
+ /* Pass parameter to the caller */
+ self->cachediscovery = discovery;
/* Wake up process if its waiting for device to be discovered */
- wake_up_interruptible(&discovery_wait);
+ wake_up_interruptible(&self->query_wait);
+}
+
+/*
+ * Function irda_discovery_timeout (priv)
+ *
+ * Timeout in the selective discovery process
+ *
+ * We were waiting for a node to be discovered, but nothing has come up
+ * so far. Wake up the user and tell him that we failed...
+ */
+static void irda_discovery_timeout(u_long priv)
+{
+ struct irda_sock *self;
+
+ IRDA_DEBUG(2, __FUNCTION__ "()\n");
+
+ self = (struct irda_sock *) priv;
+ ASSERT(self != NULL, return;);
+
+ /* Nothing for the caller */
+ self->cachelog = NULL;
+ self->cachediscovery = NULL;
+ self->errno = -ETIME;
+
+ /* Wake up process if its still waiting... */
+ wake_up_interruptible(&self->query_wait);
}
/*
@@ -470,6 +532,11 @@ static int irda_open_lsap(struct irda_sock *self, int pid)
*
* Try to lookup LSAP selector in remote LM-IAS
*
+ * Basically, we start a IAP query, and then go to sleep. When the query
+ * return, irda_getvalue_confirm will wake us up, and we can examine the
+ * result of the query...
+ * Note that in some case, the query fail even before we go to sleep,
+ * creating some races...
*/
static int irda_find_lsap_sel(struct irda_sock *self, char *name)
{
@@ -485,19 +552,53 @@ static int irda_find_lsap_sel(struct irda_sock *self, char *name)
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
irda_getvalue_confirm);
+ /* Treat unexpected signals as disconnect */
+ self->errno = -EHOSTUNREACH;
+
/* Query remote LM-IAS */
iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr,
name, "IrDA:TinyTP:LsapSel");
- /* Wait for answer */
- interruptible_sleep_on(&self->ias_wait);
+ /* Wait for answer (if not already failed) */
+ if(self->iriap != NULL)
+ interruptible_sleep_on(&self->query_wait);
+
+ /* Check what happened */
+ if (self->errno)
+ {
+ /* Requested object/attribute doesn't exist */
+ if((self->errno == IAS_CLASS_UNKNOWN) ||
+ (self->errno == IAS_ATTRIB_UNKNOWN))
+ return (-EADDRNOTAVAIL);
+ else
+ return (-EHOSTUNREACH);
+ }
+
+ /* Get the remote TSAP selector */
+ switch (self->ias_result->type) {
+ case IAS_INTEGER:
+ IRDA_DEBUG(4, __FUNCTION__ "() int=%d\n",
+ self->ias_result->t.integer);
+
+ if (self->ias_result->t.integer != -1)
+ self->dtsap_sel = self->ias_result->t.integer;
+ else
+ self->dtsap_sel = 0;
+ break;
+ default:
+ self->dtsap_sel = 0;
+ IRDA_DEBUG(0, __FUNCTION__ "(), bad type!\n");
+ break;
+ }
+ if (self->ias_result)
+ irias_delete_value(self->ias_result);
if (self->dtsap_sel)
return 0;
- return -ENETUNREACH; /* May not be true */
+ return -EADDRNOTAVAIL;
}
- /*
+/*
* Function irda_discover_daddr_and_lsap_sel (self, name)
*
* This try to find a device with the requested service.
@@ -516,71 +617,78 @@ static int irda_find_lsap_sel(struct irda_sock *self, char *name)
*/
static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
{
- discovery_t *discovery;
- int err = -ENETUNREACH;
- __u32 daddr = 0x0; /* Address we found the service on */
+ struct irda_device_info *discoveries; /* Copy of the discovery log */
+ int number; /* Number of nodes in the log */
+ int i;
+ int err = -ENETUNREACH;
+ __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */
__u8 dtsap_sel = 0x0; /* TSAP associated with it */
IRDA_DEBUG(2, __FUNCTION__ "(), name=%s\n", name);
ASSERT(self != NULL, return -1;);
- /* 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);
-
+ /* Ask lmp for the current discovery log
+ * Note : we have to use irlmp_get_discoveries(), as opposed
+ * to play with the cachelog directly, because while we are
+ * making our ias query, le log might change... */
+ discoveries = irlmp_get_discoveries(&number, self->mask);
/* Check if the we got some results */
- if (!cachelog)
- /* Wait for answer */
- /*interruptible_sleep_on(&self->discovery_wait);*/
- return -EAGAIN;
+ if (discoveries == NULL)
+ return -ENETUNREACH; /* No nodes discovered */
/*
* Now, check all discovered devices (if any), and connect
* 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) {
- /* Try this address */
- self->daddr = discovery->daddr;
- self->saddr = 0x0;
- IRDA_DEBUG(1, __FUNCTION__ "(), trying daddr = %08x\n",
- self->daddr);
-
- /* Query remote LM-IAS for this service */
- err = irda_find_lsap_sel(self, name);
- if (err == 0) {
- /* We found the requested service */
- if(daddr != 0x0) {
- IRDA_DEBUG(0, __FUNCTION__
- "(), discovered service ''%s'' in two different devices !!!\n",
- name);
- return(-ENOTUNIQ);
- }
- /* First time we foun that one, save it ! */
- daddr = self->daddr;
- dtsap_sel = self->dtsap_sel;
+ for(i = 0; i < number; i++) {
+ /* Try the address in the log */
+ self->daddr = discoveries[i].daddr;
+ self->saddr = 0x0;
+ IRDA_DEBUG(1, __FUNCTION__ "(), trying daddr = %08x\n",
+ self->daddr);
+
+ /* Query remote LM-IAS for this service */
+ err = irda_find_lsap_sel(self, name);
+ switch (err) {
+ case 0:
+ /* We found the requested service */
+ if(daddr != DEV_ADDR_ANY) {
+ IRDA_DEBUG(1, __FUNCTION__
+ "(), discovered service ''%s'' in two different devices !!!\n",
+ name);
+ self->daddr = DEV_ADDR_ANY;
+ kfree(discoveries);
+ return(-ENOTUNIQ);
}
+ /* First time we found that one, save it ! */
+ daddr = self->daddr;
+ dtsap_sel = self->dtsap_sel;
+ break;
+ case -EADDRNOTAVAIL:
+ /* Requested service simply doesn't exist on this node */
+ break;
+ default:
+ /* Something bad did happen :-( */
+ IRDA_DEBUG(0, __FUNCTION__
+ "(), unexpected IAS query failure\n");
+ self->daddr = DEV_ADDR_ANY;
+ kfree(discoveries);
+ return(-EHOSTUNREACH);
+ break;
}
-
- /* Next node, maybe we will be more lucky... */
- discovery = (discovery_t *) hashbin_get_next(cachelog);
}
- cachelog = NULL;
+ /* Cleanup our copy of the discovery log */
+ kfree(discoveries);
/* Check out what we found */
- if(daddr == 0x0) {
- IRDA_DEBUG(0, __FUNCTION__
+ if(daddr == DEV_ADDR_ANY) {
+ IRDA_DEBUG(1, __FUNCTION__
"(), cannot discover service ''%s'' in any device !!!\n",
name);
- self->daddr = 0; /* Guessing */
- return(-ENETUNREACH);
+ self->daddr = DEV_ADDR_ANY;
+ return(-EADDRNOTAVAIL);
}
/* Revert back to discovered device & service */
@@ -588,7 +696,7 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
self->saddr = 0x0;
self->dtsap_sel = dtsap_sel;
- IRDA_DEBUG(0, __FUNCTION__
+ IRDA_DEBUG(1, __FUNCTION__
"(), discovered requested service ''%s'' at address %08x\n",
name, self->daddr);
@@ -606,25 +714,26 @@ static int irda_getname(struct socket *sock, struct sockaddr *uaddr,
{
struct sockaddr_irda saddr;
struct sock *sk = sock->sk;
+ struct irda_sock *self = sk->protinfo.irda;
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;
+ saddr.sir_lsap_sel = self->dtsap_sel;
+ saddr.sir_addr = self->daddr;
} else {
saddr.sir_family = AF_IRDA;
- saddr.sir_lsap_sel = sk->protinfo.irda->stsap_sel;
- saddr.sir_addr = sk->protinfo.irda->saddr;
+ saddr.sir_lsap_sel = self->stsap_sel;
+ saddr.sir_addr = self->saddr;
}
IRDA_DEBUG(1, __FUNCTION__ "(), tsap_sel = %#x\n", saddr.sir_lsap_sel);
IRDA_DEBUG(1, __FUNCTION__ "(), addr = %08x\n", saddr.sir_addr);
- if (*uaddr_len > sizeof (struct sockaddr_irda))
- *uaddr_len = sizeof (struct sockaddr_irda);
+ /* uaddr_len come to us uninitialised */
+ *uaddr_len = sizeof (struct sockaddr_irda);
memcpy(uaddr, &saddr, *uaddr_len);
return 0;
@@ -709,7 +818,7 @@ static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
/* 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);
+ self->stsap_sel, IAS_KERNEL_ATTR);
irias_insert_object(self->ias_obj);
#if 1 /* Will be removed in near future */
@@ -821,6 +930,20 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags)
*
* Connect to a IrDA device
*
+ * The main difference with a "standard" connect is that with IrDA we need
+ * to resolve the service name into a TSAP selector (in TCP, port number
+ * doesn't have to be resolved).
+ * Because of this service name resoltion, we can offer "auto-connect",
+ * where we connect to a service without specifying a destination address.
+ *
+ * Note : by consulting "errno", the user space caller may learn the cause
+ * of the failure. Most of them are visible in the function, others may come
+ * from subroutines called and are listed here :
+ * o EBUSY : already processing a connect
+ * o EHOSTUNREACH : bad addr->sir_addr argument
+ * o EADDRNOTAVAIL : bad addr->sir_name argument
+ * o ENOTUNIQ : more than one node has addr->sir_name (auto-connect)
+ * o ENETUNREACH : no node found on the network (auto-connect)
*/
static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
@@ -858,13 +981,13 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
return -EINVAL;
/* Check if user supplied any destination device address */
- if (!addr->sir_addr) {
+ if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) {
/* Try to find one suitable */
err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name);
if (err) {
IRDA_DEBUG(0, __FUNCTION__
"(), auto-connect failed!\n");
- return -EINVAL;
+ return err;
}
} else {
/* Use the one provided by the user */
@@ -922,6 +1045,9 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
sti();
+ /* At this point, IrLMP has assigned our source address */
+ self->saddr = irttp_get_saddr(self->tsap);
+
return 0;
}
@@ -957,7 +1083,7 @@ static int irda_create(struct socket *sock, int protocol)
return -ENOMEM;
memset(self, 0, sizeof(struct irda_sock));
- init_waitqueue_head(&self->ias_wait);
+ init_waitqueue_head(&self->query_wait);
self->sk = sk;
sk->protinfo.irda = self;
@@ -996,14 +1122,14 @@ static int irda_create(struct socket *sock, int protocol)
sk->protocol = protocol;
/* Register as a client with IrLMP */
- self->ckey = irlmp_register_client(0, NULL, NULL);
+ self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
self->mask = 0xffff;
self->rx_flow = self->tx_flow = FLOW_START;
self->nslots = DISCOVERY_DEFAULT_SLOTS;
- self->daddr = DEV_ADDR_ANY;
+ self->daddr = DEV_ADDR_ANY; /* Until we get connected */
+ self->saddr = 0x0; /* so IrLMP assign us any link */
- /* Notify that we are using the irda module, so nobody removes it */
- irda_mod_inc_use_count();
+ MOD_INC_USE_COUNT;
return 0;
}
@@ -1025,11 +1151,15 @@ void irda_destroy_socket(struct irda_sock *self)
irlmp_unregister_service(self->skey);
/* Unregister with LM-IAS */
- if (self->ias_obj)
+ if (self->ias_obj) {
irias_delete_object(self->ias_obj);
+ self->ias_obj = NULL;
+ }
- if (self->iriap)
+ if (self->iriap) {
iriap_close(self->iriap);
+ self->iriap = NULL;
+ }
if (self->tsap) {
irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
@@ -1043,10 +1173,8 @@ void irda_destroy_socket(struct irda_sock *self)
}
#endif /* CONFIG_IRDA_ULTRA */
kfree(self);
-
- /* Notify that we are not using the irda module anymore */
- irda_mod_dec_use_count();
-
+ MOD_DEC_USE_COUNT;
+
return;
}
@@ -1096,7 +1224,8 @@ static int irda_sendmsg(struct socket *sock, struct msghdr *msg, int len,
IRDA_DEBUG(4, __FUNCTION__ "(), len=%d\n", len);
- if (msg->msg_flags & ~MSG_DONTWAIT)
+ /* Note : socket.c set MSG_EOR on SEQPACKET sockets */
+ if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR))
return -EINVAL;
if (sk->shutdown & SEND_SHUTDOWN) {
@@ -1474,15 +1603,22 @@ static int irda_shutdown(struct socket *sock, int how)
sk->shutdown |= SEND_SHUTDOWN;
sk->state_change(sk);
- if (self->iriap)
+ if (self->iriap) {
iriap_close(self->iriap);
-
+ self->iriap = NULL;
+ }
+
if (self->tsap) {
irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
irttp_close_tsap(self->tsap);
self->tsap = NULL;
}
+ /* A few cleanup so the socket look as good as new... */
+ self->rx_flow = self->tx_flow = FLOW_START; /* needed ??? */
+ self->daddr = DEV_ADDR_ANY; /* Until we get re-connected */
+ self->saddr = 0x0; /* so IrLMP assign us any link */
+
return 0;
}
@@ -1606,6 +1742,7 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
struct irda_sock *self;
struct irda_ias_set ias_opt;
struct ias_object *ias_obj;
+ struct ias_attrib * ias_attr; /* Attribute in IAS object */
int opt;
self = sk->protinfo.irda;
@@ -1616,6 +1753,13 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
switch (optname) {
case IRLMP_IAS_SET:
+ /* The user want to add an attribute to an existing IAS object
+ * (in the IAS database) or to create a new object with this
+ * attribute.
+ * We first query IAS to know if the object exist, and then
+ * create the right attribute...
+ */
+
if (optlen != sizeof(struct irda_ias_set))
return -EINVAL;
@@ -1639,9 +1783,11 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
switch(ias_opt.irda_attrib_type) {
case IAS_INTEGER:
/* Add an integer attribute */
- irias_add_integer_attrib(ias_obj,
- ias_opt.irda_attrib_name,
- ias_opt.attribute.irda_attrib_int);
+ irias_add_integer_attrib(
+ ias_obj,
+ ias_opt.irda_attrib_name,
+ ias_opt.attribute.irda_attrib_int,
+ IAS_USER_ATTR);
break;
case IAS_OCT_SEQ:
/* Check length */
@@ -1653,7 +1799,8 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
ias_obj,
ias_opt.irda_attrib_name,
ias_opt.attribute.irda_attrib_octet_seq.octet_seq,
- ias_opt.attribute.irda_attrib_octet_seq.len);
+ ias_opt.attribute.irda_attrib_octet_seq.len,
+ IAS_USER_ATTR);
break;
case IAS_STRING:
/* Should check charset & co */
@@ -1667,16 +1814,49 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
irias_add_string_attrib(
ias_obj,
ias_opt.irda_attrib_name,
- ias_opt.attribute.irda_attrib_string.string);
+ ias_opt.attribute.irda_attrib_string.string,
+ IAS_USER_ATTR);
break;
default :
return -EINVAL;
}
irias_insert_object(ias_obj);
break;
+ case IRLMP_IAS_DEL:
+ /* The user want to delete an object from our local IAS
+ * database. We just need to query the IAS, check is the
+ * object is not owned by the kernel and delete it.
+ */
- IRDA_DEBUG(0, __FUNCTION__ "(), sorry not impl. yet!\n");
- return -ENOPROTOOPT;
+ if (optlen != sizeof(struct irda_ias_set))
+ return -EINVAL;
+
+ /* Copy query to the driver. */
+ if (copy_from_user(&ias_opt, (char *)optval, optlen))
+ return -EFAULT;
+
+ /* Find the object we target */
+ ias_obj = irias_find_object(ias_opt.irda_class_name);
+ if(ias_obj == (struct ias_object *) NULL)
+ return -EINVAL;
+
+ /* Find the attribute (in the object) we target */
+ ias_attr = irias_find_attrib(ias_obj,
+ ias_opt.irda_attrib_name);
+ if(ias_attr == (struct ias_attrib *) NULL)
+ return -EINVAL;
+
+ /* Check is the user space own the object */
+ if(ias_attr->value->owner != IAS_USER_ATTR) {
+ IRDA_DEBUG(1, __FUNCTION__
+ "(), attempting to delete a kernel attribute\n");
+ return -EPERM;
+ }
+
+ /* Remove the attribute (and maybe the object) */
+ irias_delete_attrib(ias_obj, ias_attr);
+
+ break;
case IRLMP_MAX_SDU_SIZE:
if (optlen < sizeof(int))
return -EINVAL;
@@ -1709,60 +1889,31 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
self->skey = irlmp_register_service((__u16) opt);
break;
- default:
- return -ENOPROTOOPT;
- }
- return 0;
-}
-
- /*
- * Function irda_simple_getvalue_confirm (obj_id, value, priv)
- *
- * Got answer from remote LM-IAS, just copy object to requester...
- *
- * Note : duplicate from above, but we need our own version that
- * doesn't touch the dtsap_sel and save the full value structure...
- */
-static void irda_simple_getvalue_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv)
-{
- struct irda_sock *self;
-
- IRDA_DEBUG(2, __FUNCTION__ "()\n");
-
- ASSERT(priv != NULL, return;);
- self = (struct irda_sock *) priv;
+ case IRLMP_HINT_MASK_SET:
+ /* As opposed to the previous case which set the hint bits
+ * that we advertise, this one set the filter we use when
+ * making a discovery (nodes which don't match any hint
+ * bit in the mask are not reported).
+ */
+ if (optlen < sizeof(int))
+ return -EINVAL;
- if (!self) {
- WARNING(__FUNCTION__ "(), lost myself!\n");
- return;
- }
-
- /* We probably don't need to make any more queries */
- iriap_close(self->iriap);
- self->iriap = NULL;
-
- /* Check if request succeeded */
- if (result != IAS_SUCCESS) {
- IRDA_DEBUG(0, __FUNCTION__ "(), IAS query failed!\n");
-
- self->errno = -EHOSTUNREACH;
+ if (get_user(opt, (int *)optval))
+ return -EFAULT;
- /* Wake up any processes waiting for result */
- wake_up_interruptible(&self->ias_wait);
+ /* Set the new hint mask */
+ self->mask = (__u16) opt;
+ /* Mask out extension bits */
+ self->mask &= 0x7f7f;
+ /* Check if no bits */
+ if(!self->mask)
+ self->mask = 0xFFFF;
- return;
+ break;
+ default:
+ return -ENOPROTOOPT;
}
-
- /* Clone the object (so the requester can free it) */
- self->ias_result = kmalloc(sizeof(struct ias_value), GFP_ATOMIC);
- memcpy(self->ias_result, value, sizeof(struct ias_value));
- irias_delete_value(value);
-
- self->errno = 0;
-
- /* Wake up any processes waiting for result */
- wake_up_interruptible(&self->ias_wait);
+ return 0;
}
/*
@@ -1803,6 +1954,7 @@ static int irda_extract_ias_value(struct irda_ias_set *ias_opt,
/* NULL terminate the string (avoid troubles) */
ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0';
break;
+ case IAS_MISSING:
default :
return -EINVAL;
}
@@ -1825,11 +1977,11 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
struct sock *sk = sock->sk;
struct irda_sock *self;
struct irda_device_list list;
- struct irda_device_info *info;
- discovery_t *discovery;
+ struct irda_device_info *discoveries;
struct irda_ias_set ias_opt; /* IAS get/query params */
struct ias_object * ias_obj; /* Object in IAS */
struct ias_attrib * ias_attr; /* Attribute in IAS object */
+ int daddr = DEV_ADDR_ANY; /* Dest address for IAS queries */
int val = 0;
int len = 0;
int err;
@@ -1845,67 +1997,38 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
switch (optname) {
case IRLMP_ENUMDEVICES:
- /* 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);
-
+ /* Ask lmp for the current discovery log */
+ discoveries = irlmp_get_discoveries(&list.len, self->mask);
/* Check if the we got some results */
- if (!cachelog)
- return -EAGAIN;
+ if (discoveries == NULL)
+ return -EAGAIN; /* Didn't find any devices */
+ err = 0;
- info = &list.dev[0];
+ /* Write total list length back to client */
+ if (copy_to_user(optval, &list,
+ sizeof(struct irda_device_list) -
+ sizeof(struct irda_device_info)))
+ err = -EFAULT;
/* Offset to first device entry */
offset = sizeof(struct irda_device_list) -
sizeof(struct irda_device_info);
- total = offset; /* Initialized to size of the device list */
- list.len = 0; /* Initialize lenght of list */
-
- /*
- * 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) {
- /* Check if room for this device entry */
- if (len-total<sizeof(struct irda_device_info))
- break;
-
- /* Copy discovery information */
- info->saddr = discovery->saddr;
- info->daddr = discovery->daddr;
- info->charset = discovery->charset;
- info->hints[0] = discovery->hints.byte[0];
- info->hints[1] = discovery->hints.byte[1];
- strncpy(info->info, discovery->nickname,
- NICKNAME_MAX_LEN);
-
- if (copy_to_user(optval+total, info,
- sizeof(struct irda_device_info)))
- return -EFAULT;
- list.len++;
- total += sizeof(struct irda_device_info);
- }
- discovery = (discovery_t *) hashbin_get_next(cachelog);
- }
- cachelog = NULL;
+ /* Copy the list itself */
+ total = offset + (list.len * sizeof(struct irda_device_info));
+ if (total > len)
+ total = len;
+ if (copy_to_user(optval+offset, discoveries, total - offset))
+ err = -EFAULT;
/* Write total number of bytes used back to client */
if (put_user(total, optlen))
- return -EFAULT;
+ err = -EFAULT;
- /* Write total list length back to client */
- if (copy_to_user(optval, &list,
- sizeof(struct irda_device_list) -
- sizeof(struct irda_device_info)))
- return -EFAULT;
+ /* Free up our buffer */
+ kfree(discoveries);
+ if (err)
+ return err;
break;
case IRLMP_MAX_SDU_SIZE:
val = self->max_data_size;
@@ -1964,6 +2087,26 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
if (copy_from_user((char *) &ias_opt, (char *)optval, len))
return -EFAULT;
+ /* At this point, there are two cases...
+ * 1) the socket is connected - that's the easy case, we
+ * just query the device we are connected to...
+ * 2) the socket is not connected - the user doesn't want
+ * to connect and/or may not have a valid service name
+ * (so can't create a fake connection). In this case,
+ * we assume that the user pass us a valid destination
+ * address in the requesting structure...
+ */
+ if(self->daddr != DEV_ADDR_ANY) {
+ /* We are connected - reuse known daddr */
+ daddr = self->daddr;
+ } else {
+ /* We are not connected, we must specify a valid
+ * destination address */
+ daddr = ias_opt.daddr;
+ if((!daddr) || (daddr == DEV_ADDR_ANY))
+ return -EINVAL;
+ }
+
/* Check that we can proceed with IAP */
if (self->iriap) {
WARNING(__FUNCTION__
@@ -1972,26 +2115,34 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
}
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- irda_simple_getvalue_confirm);
+ irda_getvalue_confirm);
/* Treat unexpected signals as disconnect */
self->errno = -EHOSTUNREACH;
/* Query remote LM-IAS */
- iriap_getvaluebyclass_request(self->iriap,
- self->saddr, self->daddr,
+ iriap_getvaluebyclass_request(self->iriap,
+ self->saddr, daddr,
ias_opt.irda_class_name,
ias_opt.irda_attrib_name);
- /* Wait for answer */
- interruptible_sleep_on(&self->ias_wait);
+ /* Wait for answer (if not already failed) */
+ if(self->iriap != NULL)
+ interruptible_sleep_on(&self->query_wait);
/* Check what happened */
if (self->errno)
- return (self->errno);
+ {
+ /* Requested object/attribute doesn't exist */
+ if((self->errno == IAS_CLASS_UNKNOWN) ||
+ (self->errno == IAS_ATTRIB_UNKNOWN))
+ return (-EADDRNOTAVAIL);
+ else
+ return (-EHOSTUNREACH);
+ }
/* Translate from internal to user structure */
err = irda_extract_ias_value(&ias_opt, self->ias_result);
if (self->ias_result)
- kfree(self->ias_result);
+ irias_delete_value(self->ias_result);
if (err)
return err;
@@ -2001,6 +2152,76 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
return -EFAULT;
/* Note : don't need to put optlen, we checked it */
break;
+ case IRLMP_WAITDEVICE:
+ /* This function is just another way of seeing life ;-)
+ * IRLMP_ENUMDEVICES assumes that you have a static network,
+ * and that you just want to pick one of the devices present.
+ * On the other hand, in here we assume that no device is
+ * present and that at some point in the future a device will
+ * come into range. When this device arrive, we just wake
+ * up the caller, so that he has time to connect to it before
+ * the device goes away...
+ * Note : once the node has been discovered for more than a
+ * few second, it won't trigger this function, unless it
+ * goes away and come back changes its hint bits (so we
+ * might call it IRLMP_WAITNEWDEVICE).
+ */
+
+ /* Check that the user is passing us an int */
+ if (len != sizeof(int))
+ return -EINVAL;
+ /* Get timeout in ms (max time we block the caller) */
+ if (get_user(val, (int *)optval))
+ return -EFAULT;
+
+ /* Tell IrLMP we want to be notified */
+ irlmp_update_client(self->ckey, self->mask,
+ irda_selective_discovery_indication,
+ NULL, (void *) self);
+
+ /* Do some discovery (and also return cached results) */
+ irlmp_discovery_request(self->nslots);
+
+ /* Wait until a node is discovered */
+ if (!self->cachediscovery) {
+ IRDA_DEBUG(1, __FUNCTION__
+ "(), nothing discovered yet, going to sleep...\n");
+
+ /* Set watchdog timer to expire in <val> ms. */
+ self->watchdog.function = irda_discovery_timeout;
+ self->watchdog.data = (unsigned long) self;
+ self->watchdog.expires = jiffies + (val * HZ/1000);
+ add_timer(&(self->watchdog));
+
+ /* Wait for IR-LMP to call us back */
+ interruptible_sleep_on(&self->query_wait);
+
+ /* If watchdog is still activated, kill it! */
+ if(timer_pending(&(self->watchdog)))
+ del_timer(&(self->watchdog));
+
+ IRDA_DEBUG(1, __FUNCTION__
+ "(), ...waking up !\n");
+ }
+ else
+ IRDA_DEBUG(1, __FUNCTION__
+ "(), found immediately !\n");
+
+ /* Tell IrLMP that we have been notified */
+ irlmp_update_client(self->ckey, self->mask, NULL, NULL, NULL);
+
+ /* Check if the we got some results */
+ if (!self->cachediscovery)
+ return -EAGAIN; /* Didn't find any devices */
+ /* Cleanup */
+ self->cachediscovery = NULL;
+
+ /* Note : We don't return anything to the user.
+ * We could return the device that triggered the wake up,
+ * but it's probably better to force the user to query
+ * the whole discovery log and let him pick one device...
+ */
+ break;
default:
return -ENOPROTOOPT;
}
@@ -2150,24 +2371,44 @@ static struct notifier_block irda_dev_notifier = {
};
/*
+ * Function irda_proc_modcount (inode, fill)
+ *
+ * Use by the proc file system functions to prevent the irda module
+ * being removed while the use is standing in the net/irda directory
+ */
+void irda_proc_modcount(struct inode *inode, int fill)
+{
+#ifdef MODULE
+#ifdef CONFIG_PROC_FS
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+#endif /* CONFIG_PROC_FS */
+#endif /* MODULE */
+}
+
+/*
* Function irda_proto_init (pro)
*
* Initialize IrDA protocol layer
*
*/
-static int __init irda_proto_init(void)
+int __init irda_proto_init(void)
{
- sock_register(&irda_family_ops);
-
- irda_packet_type.type = htons(ETH_P_IRDA);
+ sock_register(&irda_family_ops);
+
+ irda_packet_type.type = htons(ETH_P_IRDA);
dev_add_pack(&irda_packet_type);
-
- register_netdevice_notifier(&irda_dev_notifier);
-
- irda_init();
+
+ register_netdevice_notifier(&irda_dev_notifier);
+
+ irda_init();
+#ifdef MODULE
+ irda_device_init(); /* Called by init/main.c when non-modular */
+#endif
return 0;
}
-module_init(irda_proto_init);
/*
* Function irda_proto_cleanup (void)
@@ -2188,5 +2429,11 @@ void irda_proto_cleanup(void)
return;
}
+module_init(irda_proto_init);
module_exit(irda_proto_cleanup);
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("The Linux IrDA Protocol Subsystem");
+MODULE_PARM(irda_debug, "1l");
#endif /* MODULE */
+
diff --git a/net/irda/compressors/irda_deflate.c b/net/irda/compressors/irda_deflate.c
index 245185500..f0d2cbb0d 100644
--- a/net/irda/compressors/irda_deflate.c
+++ b/net/irda/compressors/irda_deflate.c
@@ -561,37 +561,37 @@ extern void irda_unregister_compressor (struct compressor *cp);
* Procedures exported to if_ppp.c.
*/
static struct compressor irda_deflate = {
- CI_DEFLATE, /* compress_proto */
- z_comp_alloc, /* comp_alloc */
- z_comp_free, /* comp_free */
- z_comp_init, /* comp_init */
- z_comp_reset, /* comp_reset */
- z_compress, /* compress */
- z_comp_stats, /* comp_stat */
- z_decomp_alloc, /* decomp_alloc */
- z_decomp_free, /* decomp_free */
- z_decomp_init, /* decomp_init */
- z_decomp_reset, /* decomp_reset */
- z_decompress, /* decompress */
- z_incomp, /* incomp */
- z_comp_stats, /* decomp_stat */
+compress_proto: CI_DEFLATE,
+comp_alloc: z_comp_alloc,
+comp_free: z_comp_free,
+comp_init: z_comp_init,
+comp_reset: z_comp_reset,
+compress: z_compress,
+comp_stat: z_comp_stats,
+decomp_alloc: z_decomp_alloc,
+decomp_free: z_decomp_free,
+decomp_init: z_decomp_init,
+decomp_reset: z_decomp_reset,
+decompress: z_decompress,
+incomp: z_incomp,
+decomp_stat: z_comp_stats
};
static struct compressor irda_deflate_draft = {
- CI_DEFLATE_DRAFT, /* compress_proto */
- z_comp_alloc, /* comp_alloc */
- z_comp_free, /* comp_free */
- z_comp_init, /* comp_init */
- z_comp_reset, /* comp_reset */
- z_compress, /* compress */
- z_comp_stats, /* comp_stat */
- z_decomp_alloc, /* decomp_alloc */
- z_decomp_free, /* decomp_free */
- z_decomp_init, /* decomp_init */
- z_decomp_reset, /* decomp_reset */
- z_decompress, /* decompress */
- z_incomp, /* incomp */
- z_comp_stats, /* decomp_stat */
+compress_proto: CI_DEFLATE_DRAFT,
+comp_alloc: z_comp_alloc,
+comp_free: z_comp_free,
+comp_init: z_comp_init,
+comp_reset: z_comp_reset,
+compress: z_compress,
+comp_stat: z_comp_stats,
+decomp_alloc: z_decomp_alloc,
+decomp_free: z_decomp_free,
+decomp_init: z_decomp_init,
+decomp_reset: z_decomp_reset,
+decompress: z_decompress,
+incomp: z_incomp,
+decomp_stat: z_comp_stats
};
int __init irda_deflate_init(void)
diff --git a/net/irda/discovery.c b/net/irda/discovery.c
index 957426154..d2fe17ff8 100644
--- a/net/irda/discovery.c
+++ b/net/irda/discovery.c
@@ -43,13 +43,25 @@
*
* Add a new discovery to the cachelog, and remove any old discoveries
* from the same device
+ *
+ * Note : we try to preserve the time this device was *first* discovered
+ * (as opposed to the time of last discovery used for cleanup). This is
+ * used by clients waiting for discovery events to tell if the device
+ * discovered is "new" or just the same old one. They can't rely there
+ * on a binary flag (new/old), because not all discovery events are
+ * propagated to them, and they might not always listen, so they would
+ * miss some new devices popping up...
+ * Jean II
*/
void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
{
discovery_t *discovery, *node;
unsigned long flags;
- spin_lock_irqsave(&irlmp->lock, flags);
+ /* Set time of first discovery if node is new (see below) */
+ new->first_timestamp = new->timestamp;
+
+ spin_lock_irqsave(&irlmp->log_lock, flags);
/*
* Remove all discoveries of devices that has previously been
@@ -59,27 +71,31 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
*/
discovery = (discovery_t *) hashbin_get_first(cachelog);
while (discovery != NULL ) {
- node = discovery;
+ node = discovery;
- /* Be sure to stay one item ahead */
- discovery = (discovery_t *) hashbin_get_next(cachelog);
-
- if ((node->daddr == new->daddr) ||
- (strcmp(node->nickname, new->nickname) == 0))
- {
- /* This discovery is a previous discovery
- * from the same device, so just remove it
- */
- hashbin_remove(cachelog, node->daddr, NULL);
- kfree(node);
- }
- }
+ /* Be sure to stay one item ahead */
+ discovery = (discovery_t *) hashbin_get_next(cachelog);
+ if ((node->saddr == new->saddr) &&
+ ((node->daddr == new->daddr) ||
+ (strcmp(node->nickname, new->nickname) == 0)))
+ {
+ /* This discovery is a previous discovery
+ * from the same device, so just remove it
+ */
+ hashbin_remove_this(cachelog, (irda_queue_t *) node);
+ /* Check if hints bits have changed */
+ if(node->hints.word == new->hints.word)
+ /* Set time of first discovery for this node */
+ new->first_timestamp = node->first_timestamp;
+ kfree(node);
+ }
+ }
/* Insert the new and updated version */
- hashbin_insert(cachelog, (queue_t *) new, new->daddr, NULL);
+ hashbin_insert(cachelog, (irda_queue_t *) new, new->daddr, NULL);
- spin_unlock_irqrestore(&irlmp->lock, flags);
+ spin_unlock_irqrestore(&irlmp->log_lock, flags);
}
/*
@@ -120,13 +136,18 @@ void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
*
* Go through all discoveries and expire all that has stayed to long
*
+ * Note : this assume that IrLAP won't change its saddr, which
+ * currently is a valid assumption...
*/
void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
{
discovery_t *discovery, *curr;
+ unsigned long flags;
IRDA_DEBUG(4, __FUNCTION__ "()\n");
+ spin_lock_irqsave(&irlmp->log_lock, flags);
+
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
curr = discovery;
@@ -135,14 +156,20 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
discovery = (discovery_t *) hashbin_get_next(log);
/* Test if it's time to expire this discovery */
- if ((curr->saddr == saddr) && (force ||
- ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT)))
+ if ((curr->saddr == saddr) &&
+ (force ||
+ ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT)))
{
- curr = hashbin_remove(log, curr->daddr, NULL);
+ /* Tell IrLMP and registered clients about it */
+ irlmp_discovery_expiry(curr);
+ /* Remove it from the log */
+ curr = hashbin_remove_this(log, (irda_queue_t *) curr);
if (curr)
kfree(curr);
}
}
+
+ spin_unlock_irqrestore(&irlmp->log_lock, flags);
}
/*
@@ -169,6 +196,75 @@ void irlmp_dump_discoveries(hashbin_t *log)
}
/*
+ * Function irlmp_copy_discoveries (log, pn, mask)
+ *
+ * Copy all discoveries in a buffer
+ *
+ * This function implement a safe way for lmp clients to access the
+ * discovery log. The basic problem is that we don't want the log
+ * to change (add/remove) while the client is reading it. If the
+ * lmp client manipulate directly the hashbin, he is sure to get
+ * into troubles...
+ * The idea is that we copy all the current discovery log in a buffer
+ * which is specific to the client and pass this copy to him. As we
+ * do this operation with the spinlock grabbed, we are safe...
+ * Note : we don't want those clients to grab the spinlock, because
+ * we have no control on how long they will hold it...
+ * Note : we choose to copy the log in "struct irda_device_info" to
+ * save space...
+ * Note : the client must kfree himself() the log...
+ * Jean II
+ */
+struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 mask)
+{
+ discovery_t * discovery;
+ unsigned long flags;
+ struct irda_device_info * buffer;
+ int i = 0;
+ int n;
+
+ ASSERT(pn != NULL, return NULL;);
+
+ /* Check if log is empty */
+ if(log == NULL)
+ return NULL;
+
+ /* Save spin lock - spinlock should be discovery specific */
+ spin_lock_irqsave(&irlmp->log_lock, flags);
+
+ /* Create the client specific buffer */
+ n = HASHBIN_GET_SIZE(log);
+ buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
+ if (buffer == NULL) {
+ spin_unlock_irqrestore(&irlmp->log_lock, flags);
+ return NULL;
+ }
+
+ discovery = (discovery_t *) hashbin_get_first(log);
+ while ((discovery != NULL) && (i < n)) {
+ /* Mask out the ones we don't want */
+ if (discovery->hints.word & mask) {
+ /* Copy discovery information */
+ buffer[i].saddr = discovery->saddr;
+ buffer[i].daddr = discovery->daddr;
+ buffer[i].charset = discovery->charset;
+ buffer[i].hints[0] = discovery->hints.byte[0];
+ buffer[i].hints[1] = discovery->hints.byte[1];
+ strncpy(buffer[i].info, discovery->nickname,
+ NICKNAME_MAX_LEN);
+ i++;
+ }
+ discovery = (discovery_t *) hashbin_get_next(log);
+ }
+
+ spin_unlock_irqrestore(&irlmp->log_lock, flags);
+
+ /* Get the actual number of device in the buffer and return */
+ *pn = i;
+ return(buffer);
+}
+
+/*
* Function irlmp_find_device (name, saddr)
*
* Look through the discovery log at each of the links and try to find
@@ -180,7 +276,7 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr)
unsigned long flags;
discovery_t *d;
- spin_lock_irqsave(&irlmp->lock, flags);
+ spin_lock_irqsave(&irlmp->log_lock, flags);
/* Look at all discoveries for that link */
d = (discovery_t *) hashbin_get_first(cachelog);
@@ -192,13 +288,13 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr)
if (strcmp(name, d->nickname) == 0) {
*saddr = d->saddr;
- spin_unlock_irqrestore(&irlmp->lock, flags);
+ spin_unlock_irqrestore(&irlmp->log_lock, flags);
return d->daddr;
}
d = (discovery_t *) hashbin_get_next(cachelog);
}
- spin_unlock_irqrestore(&irlmp->lock, flags);
+ spin_unlock_irqrestore(&irlmp->log_lock, flags);
return 0;
}
@@ -209,23 +305,23 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr)
* Print discovery information in /proc file system
*
*/
-int discovery_proc_read(char *buf, char **start, off_t offset, int len,
+int discovery_proc_read(char *buf, char **start, off_t offset, int length,
int unused)
{
discovery_t *discovery;
unsigned long flags;
hashbin_t *cachelog = irlmp_get_cachelog();
+ int len = 0;
if (!irlmp)
return len;
len = sprintf(buf, "IrLMP: Discovery log:\n\n");
- save_flags(flags);
- cli();
-
+ spin_lock_irqsave(&irlmp->log_lock, flags);
+
discovery = (discovery_t *) hashbin_get_first(cachelog);
- while ( discovery != NULL) {
+ while (( discovery != NULL) && (len < length)) {
len += sprintf(buf+len, "nickname: %s,", discovery->nickname);
len += sprintf(buf+len, " hint: 0x%02x%02x",
@@ -266,7 +362,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int len,
discovery = (discovery_t *) hashbin_get_next(cachelog);
}
- restore_flags(flags);
+ spin_unlock_irqrestore(&irlmp->log_lock, flags);
return len;
}
diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c
index 805186128..ae626e08b 100644
--- a/net/irda/ircomm/ircomm_core.c
+++ b/net/irda/ircomm/ircomm_core.c
@@ -127,7 +127,7 @@ struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
self->service_type = service_type;
self->line = line;
- hashbin_insert(ircomm, (queue_t *) self, line, NULL);
+ hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
ircomm_next_state(self, IRCOMM_IDLE);
@@ -512,6 +512,9 @@ int ircomm_proc_read(char *buf, char **start, off_t offset, int len)
#endif /* CONFIG_PROC_FS */
#ifdef MODULE
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("IrCOMM protocol");
+
int init_module(void)
{
return ircomm_init();
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
index d785983da..67925c5b5 100644
--- a/net/irda/ircomm/ircomm_tty.c
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -29,6 +29,7 @@
*
********************************************************************/
+#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
@@ -69,9 +70,10 @@ static int ircomm_tty_control_indication(void *instance, void *sap,
struct sk_buff *skb);
static void ircomm_tty_flow_indication(void *instance, void *sap,
LOCAL_FLOW cmd);
+#ifdef CONFIG_PROC_FS
static int ircomm_tty_read_proc(char *buf, char **start, off_t offset, int len,
int *eof, void *unused);
-
+#endif /* CONFIG_PROC_FS */
static struct tty_driver driver;
static int ircomm_tty_refcount; /* If we manage several devices */
@@ -126,8 +128,9 @@ int __init ircomm_tty_init(void)
driver.start = ircomm_tty_start;
driver.hangup = ircomm_tty_hangup;
driver.wait_until_sent = ircomm_tty_wait_until_sent;
+#ifdef CONFIG_PROC_FS
driver.read_proc = ircomm_tty_read_proc;
-
+#endif /* CONFIG_PROC_FS */
if (tty_register_driver(&driver)) {
ERROR(__FUNCTION__ "Couldn't register serial driver\n");
return -1;
@@ -429,7 +432,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
tty->termios->c_oflag = 0;
/* Insert into hash */
- hashbin_insert(ircomm_tty, (queue_t *) self, line, NULL);
+ hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL);
}
self->open_count++;
@@ -1319,6 +1322,7 @@ static int ircomm_tty_line_info(struct ircomm_tty_cb *self, char *buf)
*
*
*/
+#ifdef CONFIG_PROC_FS
static int ircomm_tty_read_proc(char *buf, char **start, off_t offset, int len,
int *eof, void *unused)
{
@@ -1349,9 +1353,12 @@ done:
*start = buf + (offset-begin);
return ((len < begin+count-offset) ? len : begin+count-offset);
}
-
+#endif /* CONFIG_PROC_FS */
#ifdef MODULE
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("IrCOMM serial TTY driver");
+
int init_module(void)
{
return ircomm_tty_init();
diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c
index 94c9fee09..ad5ee4b0f 100644
--- a/net/irda/ircomm/ircomm_tty_attach.c
+++ b/net/irda/ircomm/ircomm_tty_attach.c
@@ -46,7 +46,8 @@
#include <net/irda/ircomm_tty_attach.h>
static void ircomm_tty_ias_register(struct ircomm_tty_cb *self);
-static void ircomm_tty_discovery_indication(discovery_t *discovery);
+static void ircomm_tty_discovery_indication(discovery_t *discovery,
+ void *priv);
static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
struct ias_value *value, void *priv);
void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, int timeout);
@@ -213,7 +214,7 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
/* Register IrLPT with LM-IAS */
self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID);
irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel",
- self->slsap_sel);
+ self->slsap_sel, IAS_KERNEL_ATTR);
irias_insert_object(self->obj);
} else {
hints = irlmp_service_to_hint(S_COMM);
@@ -221,7 +222,7 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
/* Register IrCOMM with LM-IAS */
self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID);
irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel",
- self->slsap_sel);
+ self->slsap_sel, IAS_KERNEL_ATTR);
/* Code the parameters into the buffer */
irda_param_pack(oct_seq, "bbbbbb",
@@ -229,12 +230,13 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL);
/* Register parameters with LM-IAS */
- irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6);
+ irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6,
+ IAS_KERNEL_ATTR);
irias_insert_object(self->obj);
}
self->skey = irlmp_register_service(hints);
self->ckey = irlmp_register_client(
- hints, ircomm_tty_discovery_indication, NULL);
+ hints, ircomm_tty_discovery_indication, NULL, (void *) self);
}
/*
@@ -302,7 +304,8 @@ int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
* device it is, and which services it has.
*
*/
-static void ircomm_tty_discovery_indication(discovery_t *discovery)
+static void ircomm_tty_discovery_indication(discovery_t *discovery,
+ void *priv)
{
struct ircomm_tty_cb *self;
struct ircomm_tty_info info;
diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c
index ca2903316..c77ca6268 100644
--- a/net/irda/irda_device.c
+++ b/net/irda/irda_device.c
@@ -58,6 +58,8 @@
extern int irtty_init(void);
extern int nsc_ircc_init(void);
extern int ircc_init(void);
+extern int toshoboe_init(void);
+extern int litelink_init(void);
extern int w83977af_init(void);
extern int esi_init(void);
extern int tekram_init(void);
@@ -183,7 +185,7 @@ void irda_device_set_media_busy(struct net_device *dev, int status)
IRDA_DEBUG( 4, "Media busy!\n");
} else {
self->media_busy = FALSE;
- del_timer(&self->media_busy_timer);
+ irlap_stop_mbusy_timer(self);
}
}
@@ -379,7 +381,7 @@ struct irda_task *irda_task_execute(void *instance,
init_timer(&task->timer);
/* Register task */
- hashbin_insert(tasks, (queue_t *) task, (int) task, NULL);
+ hashbin_insert(tasks, (irda_queue_t *) task, (int) task, NULL);
/* No time to waste, so lets get going! */
ret = irda_task_kick(task);
@@ -518,7 +520,7 @@ int irda_device_register_dongle(struct dongle_reg *new)
}
/* Insert IrDA dongle into hashbin */
- hashbin_insert(dongles, (queue_t *) new, new->type, NULL);
+ hashbin_insert(dongles, (irda_queue_t *) new, new->type, NULL);
return 0;
}
diff --git a/net/irda/iriap.c b/net/irda/iriap.c
index 5f1140525..03014ac0d 100644
--- a/net/irda/iriap.c
+++ b/net/irda/iriap.c
@@ -107,7 +107,7 @@ int __init iriap_init(void)
/* Register the Device object with LM-IAS */
obj = irias_new_object("Device", IAS_DEVICE_ID);
- irias_add_string_attrib(obj, "DeviceName", "Linux");
+ irias_add_string_attrib(obj, "DeviceName", "Linux", IAS_KERNEL_ATTR);
oct_seq[0] = 0x01; /* Version 1 */
oct_seq[1] = 0x00; /* IAS support bits */
@@ -115,7 +115,8 @@ int __init iriap_init(void)
#ifdef CONFIG_IRDA_ULTRA
oct_seq[2] |= 0x04; /* Connectionless Data support */
#endif
- irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3);
+ irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3,
+ IAS_KERNEL_ATTR);
irias_insert_object(obj);
/*
@@ -179,7 +180,7 @@ struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv,
init_timer(&self->watchdog_timer);
- hashbin_insert(iriap, (queue_t *) self, (int) self, NULL);
+ hashbin_insert(iriap, (irda_queue_t *) self, (int) self, NULL);
/* Initialize state machines */
iriap_next_client_state(self, S_DISCONNECT);
@@ -866,7 +867,7 @@ static int iriap_data_indication(void *instance, void *sap,
iriap_getvaluebyclass_confirm(self, skb);
break;
case IAS_CLASS_UNKNOWN:
- WARNING(__FUNCTION__ "(), No such class!\n");
+ IRDA_DEBUG(1, __FUNCTION__ "(), No such class!\n");
/* Finished, close connection! */
iriap_disconnect_request(self);
@@ -880,7 +881,7 @@ static int iriap_data_indication(void *instance, void *sap,
dev_kfree_skb(skb);
break;
case IAS_ATTRIB_UNKNOWN:
- WARNING(__FUNCTION__ "(), No such attribute!\n");
+ IRDA_DEBUG(1, __FUNCTION__ "(), No such attribute!\n");
/* Finished, close connection! */
iriap_disconnect_request(self);
@@ -889,7 +890,7 @@ static int iriap_data_indication(void *instance, void *sap,
* no to use self anymore after calling confirm
*/
if (self->confirm)
- self->confirm(IAS_CLASS_UNKNOWN, 0, NULL,
+ self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL,
self->priv);
dev_kfree_skb(skb);
break;
diff --git a/net/irda/irias_object.c b/net/irda/irias_object.c
index 17ad3801d..4a8fbed4c 100644
--- a/net/irda/irias_object.c
+++ b/net/irda/irias_object.c
@@ -148,6 +148,37 @@ int irias_delete_object(struct ias_object *obj)
}
/*
+ * Function irias_delete_attrib (obj)
+ *
+ * Remove attribute from hashbin and, if it was the last attribute of
+ * the object, remove the object as well.
+ *
+ */
+int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib)
+{
+ struct ias_attrib *node;
+
+ ASSERT(obj != NULL, return -1;);
+ ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
+ ASSERT(attrib != NULL, return -1;);
+
+ /* Remove atribute from object */
+ node = hashbin_remove(obj->attribs, 0, attrib->name);
+ if (!node)
+ return 0; /* Already removed or non-existent */
+
+ /* Deallocate attribute */
+ __irias_delete_attrib(node);
+
+ /* Check if object has still some attributes */
+ node = (struct ias_attrib *) hashbin_get_first(obj->attribs);
+ if (!node)
+ irias_delete_object(obj);
+
+ return 0;
+}
+
+/*
* Function irias_insert_object (obj)
*
* Insert an object into the LM-IAS database
@@ -158,7 +189,7 @@ void irias_insert_object(struct ias_object *obj)
ASSERT(obj != NULL, return;);
ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
- hashbin_insert(objects, (queue_t *) obj, 0, obj->name);
+ hashbin_insert(objects, (irda_queue_t *) obj, 0, obj->name);
}
/*
@@ -201,7 +232,8 @@ struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name)
* Add attribute to object
*
*/
-void irias_add_attrib( struct ias_object *obj, struct ias_attrib *attrib)
+void irias_add_attrib( struct ias_object *obj, struct ias_attrib *attrib,
+ int owner)
{
ASSERT(obj != NULL, return;);
ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
@@ -209,7 +241,10 @@ void irias_add_attrib( struct ias_object *obj, struct ias_attrib *attrib)
ASSERT(attrib != NULL, return;);
ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
- hashbin_insert(obj->attribs, (queue_t *) attrib, 0, attrib->name);
+ /* Set if attrib is owned by kernel or user space */
+ attrib->value->owner = owner;
+
+ hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name);
}
/*
@@ -262,7 +297,8 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name,
* Add an integer attribute to an LM-IAS object
*
*/
-void irias_add_integer_attrib(struct ias_object *obj, char *name, int value)
+void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
+ int owner)
{
struct ias_attrib *attrib;
@@ -284,7 +320,7 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value)
/* Insert value */
attrib->value = irias_new_integer_value(value);
- irias_add_attrib(obj, attrib);
+ irias_add_attrib(obj, attrib, owner);
}
/*
@@ -295,7 +331,7 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value)
*/
void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
- int len)
+ int len, int owner)
{
struct ias_attrib *attrib;
@@ -319,7 +355,7 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
attrib->value = irias_new_octseq_value( octets, len);
- irias_add_attrib(obj, attrib);
+ irias_add_attrib(obj, attrib, owner);
}
/*
@@ -328,7 +364,8 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
* Add a string attribute to an LM-IAS object
*
*/
-void irias_add_string_attrib(struct ias_object *obj, char *name, char *value)
+void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
+ int owner)
{
struct ias_attrib *attrib;
@@ -351,7 +388,7 @@ void irias_add_string_attrib(struct ias_object *obj, char *name, char *value)
attrib->value = irias_new_string_value(value);
- irias_add_attrib(obj, attrib);
+ irias_add_attrib(obj, attrib, owner);
}
/*
diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c
index 7c2d5b94e..e3a3ada15 100644
--- a/net/irda/irlan/irlan_client.c
+++ b/net/irda/irlan/irlan_client.c
@@ -104,8 +104,6 @@ void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout)
*/
void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
{
- struct irmanager_event mgr_event;
-
IRDA_DEBUG(1, __FUNCTION__ "()\n");
ASSERT(self != NULL, return;);
@@ -117,41 +115,24 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
*/
if ((self->client.state != IRLAN_IDLE) ||
(self->provider.access_type == ACCESS_DIRECT))
- return;
+ {
+ IRDA_DEBUG(0, __FUNCTION__ "(), already awake!\n");
+ return;
+ }
- /* saddr may have changed! */
+ /* Address may have changed! */
self->saddr = saddr;
-
- /* Before we try to connect, we check if network device is up. If it
- * is up, that means that the "user" really wants to connect. If not
- * we notify the user about the possibility of an IrLAN connection
- */
- if (netif_running(&self->dev)) {
- /* Open TSAPs */
- irlan_client_open_ctrl_tsap(self);
- irlan_open_data_tsap(self);
-
- irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL);
- } else if (self->notify_irmanager) {
- /*
- * Tell irmanager that the device can now be
- * configured but only if the device was not taken
- * down by the user
- */
- mgr_event.event = EVENT_IRLAN_START;
- strcpy(mgr_event.devname, self->dev.name);
- irmanager_notify(&mgr_event);
-
- /*
- * We set this so that we only notify once, since if
- * configuration of the network device fails, the user
- * will have to sort it out first anyway. No need to
- * try again.
- */
- self->notify_irmanager = FALSE;
+
+ if (self->disconnect_reason == LM_USER_REQUEST) {
+ IRDA_DEBUG(0, __FUNCTION__ "(), still stopped by user\n");
+ return;
}
- /* Restart watchdog timer */
- irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
+
+ /* Open TSAPs */
+ irlan_client_open_ctrl_tsap(self);
+ irlan_open_data_tsap(self);
+
+ irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL);
/* Start kick timer */
irlan_client_start_kick_timer(self, 2*HZ);
@@ -163,7 +144,7 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
* Remote device with IrLAN server support discovered
*
*/
-void irlan_client_discovery_indication(discovery_t *discovery)
+void irlan_client_discovery_indication(discovery_t *discovery, void *priv)
{
struct irlan_cb *self;
__u32 saddr, daddr;
@@ -176,29 +157,16 @@ void irlan_client_discovery_indication(discovery_t *discovery)
saddr = discovery->saddr;
daddr = discovery->daddr;
- /*
- * Check if we already dealing with this provider.
- */
- self = (struct irlan_cb *) hashbin_find(irlan, daddr, NULL);
- if (self) {
+ /* Find instance */
+ self = (struct irlan_cb *) hashbin_get_first(irlan);
+ if (self) {
ASSERT(self->magic == IRLAN_MAGIC, return;);
IRDA_DEBUG(1, __FUNCTION__ "(), Found instance (%08x)!\n",
daddr);
irlan_client_wakeup(self, saddr, daddr);
-
- return;
}
-
- /*
- * We have no instance for daddr, so start a new one
- */
- IRDA_DEBUG(1, __FUNCTION__ "(), starting new instance!\n");
- self = irlan_open(saddr, daddr, TRUE);
-
- /* Restart watchdog timer */
- irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
}
/*
@@ -449,9 +417,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRLAN_MAGIC, return;);
- /*
- * Media type
- */
+ /* Media type */
if (strcmp(param, "MEDIA") == 0) {
if (strcmp(value, "802.3") == 0)
self->media = MEDIA_802_3;
@@ -487,9 +453,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
IRDA_DEBUG(2, __FUNCTION__ "(), unknown access type!\n");
}
}
- /*
- * IRLAN version
- */
+ /* IRLAN version */
if (strcmp(param, "IRLAN_VER") == 0) {
IRDA_DEBUG(4, "IrLAN version %d.%d\n", (__u8) value[0],
(__u8) value[1]);
@@ -498,9 +462,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
self->version[1] = value[1];
return;
}
- /*
- * Which remote TSAP to use for data channel
- */
+ /* Which remote TSAP to use for data channel */
if (strcmp(param, "DATA_CHAN") == 0) {
self->dtsap_sel_data = value[0];
IRDA_DEBUG(4, "Data TSAP = %02x\n", self->dtsap_sel_data);
@@ -521,9 +483,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
self->client.max_frame);
}
- /*
- * RECONNECT_KEY, in case the link goes down!
- */
+ /* RECONNECT_KEY, in case the link goes down! */
if (strcmp(param, "RECONNECT_KEY") == 0) {
IRDA_DEBUG(4, "Got reconnect key: ");
/* for (i = 0; i < val_len; i++) */
@@ -532,9 +492,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
self->client.key_len = val_len;
IRDA_DEBUG(4, "\n");
}
- /*
- * FILTER_ENTRY, have we got an ethernet address?
- */
+ /* FILTER_ENTRY, have we got an ethernet address? */
if (strcmp(param, "FILTER_ENTRY") == 0) {
bytes = value;
IRDA_DEBUG(4, "Ethernet address = %02x:%02x:%02x:%02x:%02x:%02x\n",
diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c
index 5975909fe..686c163dc 100644
--- a/net/irda/irlan/irlan_common.c
+++ b/net/irda/irlan/irlan_common.c
@@ -50,6 +50,14 @@
#include <net/irda/irlan_eth.h>
#include <net/irda/irlan_filter.h>
+
+/*
+ * Send gratuitous ARP when connected to a new AP or not. May be a clever
+ * thing to do, but for some reason the machine crashes if you use DHCP. So
+ * lets not use it by default.
+ */
+#undef CONFIG_IRLAN_SEND_GRATUITOUS_ARP
+
/* extern char sysctl_devname[]; */
/*
@@ -62,6 +70,7 @@ static __u32 ckey, skey;
static int eth = 0; /* Use "eth" or "irlan" name for devices */
static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */
+#ifdef CONFIG_PROC_FS
static char *irlan_state[] = {
"IRLAN_IDLE",
"IRLAN_QUERY",
@@ -88,6 +97,7 @@ static char *irlan_media[] = {
"802.3",
"802.5"
};
+#endif /* CONFIG_PROC_FS */
static void __irlan_close(struct irlan_cb *self);
static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
@@ -102,59 +112,6 @@ extern struct proc_dir_entry *proc_irda;
#endif /* CONFIG_PROC_FS */
/*
- * Function irlan_watchdog_timer_expired (data)
- *
- * Something has gone wrong during the connection establishment
- *
- */
-void irlan_watchdog_timer_expired(void *data)
-{
- struct irmanager_event mgr_event;
- struct irlan_cb *self;
-
- IRDA_DEBUG(0, __FUNCTION__ "()\n");
-
- self = (struct irlan_cb *) data;
-
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* Check if device still configured */
- if (netif_running(&self->dev)) {
- IRDA_DEBUG(0, __FUNCTION__
- "(), notifying irmanager to stop irlan!\n");
- mgr_event.event = EVENT_IRLAN_STOP;
- sprintf(mgr_event.devname, "%s", self->dev.name);
- irmanager_notify(&mgr_event);
-
- /*
- * We set this to false, so that irlan_dev_close known that
- * notify_irmanager should actually be set to TRUE again
- * instead of FALSE, since this close has not been initiated
- * by the user.
- */
- self->notify_irmanager = FALSE;
- } else {
- IRDA_DEBUG(0, __FUNCTION__ "(), closing instance!\n");
- /*irlan_close(self);*/
- }
-}
-
-/*
- * Function irlan_start_watchdog_timer (self, timeout)
- *
- *
- *
- */
-void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout)
-{
- IRDA_DEBUG(4, __FUNCTION__ "()\n");
-
- irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
- irlan_watchdog_timer_expired);
-}
-
-/*
* Function irlan_init (void)
*
* Initialize IrLAN layer
@@ -165,8 +122,7 @@ int __init irlan_init(void)
struct irlan_cb *new;
__u16 hints;
- IRDA_DEBUG(4, __FUNCTION__"()\n");
-
+ IRDA_DEBUG(0, __FUNCTION__ "()\n");
/* Allocate master structure */
irlan = hashbin_new(HB_LOCAL);
if (irlan == NULL) {
@@ -178,22 +134,20 @@ int __init irlan_init(void)
#endif /* CONFIG_PROC_FS */
IRDA_DEBUG(4, __FUNCTION__ "()\n");
-
hints = irlmp_service_to_hint(S_LAN);
/* Register with IrLMP as a client */
- ckey = irlmp_register_client(hints, irlan_client_discovery_indication,
- NULL);
+ ckey = irlmp_register_client(hints, &irlan_client_discovery_indication,
+ NULL, NULL);
/* Register with IrLMP as a service */
skey = irlmp_register_service(hints);
- /* Start the master IrLAN instance */
- new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY, FALSE);
+ /* Start the master IrLAN instance (the only one for now) */
+ new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY);
/* The master will only open its (listen) control TSAP */
irlan_provider_open_ctrl_tsap(new);
- new->master = TRUE;
/* Do some fast discovery! */
irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
@@ -206,7 +160,6 @@ void irlan_cleanup(void)
IRDA_DEBUG(4, __FUNCTION__ "()\n");
irlmp_unregister_client(ckey);
-
irlmp_unregister_service(skey);
#ifdef CONFIG_PROC_FS
@@ -242,8 +195,6 @@ int irlan_register_netdev(struct irlan_cb *self)
IRDA_DEBUG(2, __FUNCTION__ "(), register_netdev() failed!\n");
return -1;
}
- self->netdev_registered = TRUE;
-
return 0;
}
@@ -253,7 +204,7 @@ int irlan_register_netdev(struct irlan_cb *self)
* Open new instance of a client/provider, we should only register the
* network device if this instance is ment for a particular client/provider
*/
-struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr, int netdev)
+struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr)
{
struct irlan_cb *self;
@@ -287,32 +238,28 @@ struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr, int netdev)
/* Provider access can only be PEER, DIRECT, or HOSTED */
self->provider.access_type = access;
self->media = MEDIA_802_3;
-
- self->notify_irmanager = TRUE;
-
+ self->disconnect_reason = LM_USER_REQUEST;
init_timer(&self->watchdog_timer);
init_timer(&self->client.kick_timer);
+ init_waitqueue_head(&self->open_wait);
- hashbin_insert(irlan, (queue_t *) self, daddr, NULL);
+ hashbin_insert(irlan, (irda_queue_t *) self, daddr, NULL);
skb_queue_head_init(&self->client.txq);
irlan_next_client_state(self, IRLAN_IDLE);
irlan_next_provider_state(self, IRLAN_IDLE);
- /* Register network device now, or wait until some later time? */
- if (netdev)
- irlan_register_netdev(self);
+ irlan_register_netdev(self);
return self;
}
/*
- * Function irlan_close (self)
+ * Function __irlan_close (self)
*
* This function closes and deallocates the IrLAN client instances. Be
* aware that other functions which calles client_close() must call
* hashbin_remove() first!!!
- *
*/
static void __irlan_close(struct irlan_cb *self)
{
@@ -333,49 +280,13 @@ static void __irlan_close(struct irlan_cb *self)
iriap_close(self->client.iriap);
/* Remove frames queued on the control channel */
- while ((skb = skb_dequeue(&self->client.txq))) {
+ while ((skb = skb_dequeue(&self->client.txq)))
dev_kfree_skb(skb);
- }
- if (self->netdev_registered) {
- unregister_netdev(&self->dev);
- self->netdev_registered = FALSE;
- }
+ unregister_netdev(&self->dev);
self->magic = 0;
- kfree(self);
-}
-
-/*
- * Function irlan_close (self)
- *
- * Close instance
- *
- */
-void irlan_close(struct irlan_cb *self)
-{
- struct irlan_cb *entry;
-
- IRDA_DEBUG(0, __FUNCTION__ "()\n");
-
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* Check if device is still configured */
- if (netif_running(&self->dev)) {
- IRDA_DEBUG(0, __FUNCTION__
- "(), Device still configured, closing later!\n");
-
- /* Give it a chance to reconnect */
- irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
- return;
- }
- IRDA_DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
- entry = hashbin_remove(irlan, self->daddr, NULL);
-
- ASSERT(entry == self, return;);
-
- __irlan_close(self);
+ kfree(self);
}
/*
@@ -419,7 +330,7 @@ void irlan_connect_indication(void *instance, void *sap, struct qos_info *qos,
irlan_open_unicast_addr(self);
}
/* Ready to transfer Ethernet frames (at last) */
- netif_start_queue(&self->dev);
+ netif_start_queue(&self->dev); /* Clear reason */
}
void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos,
@@ -454,7 +365,11 @@ void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos,
/* Ready to transfer Ethernet frames */
netif_start_queue(&self->dev);
+ self->disconnect_reason = 0; /* Clear reason */
+#ifdef CONFIG_IRLAN_SEND_GRATUITOUS_ARP
irlan_eth_send_gratuitous_arp(&self->dev);
+#endif
+ wake_up_interruptible(&self->open_wait);
}
/*
@@ -483,28 +398,34 @@ void irlan_disconnect_indication(void *instance, void *sap, LM_REASON reason,
IRDA_DEBUG(2, "IrLAN, data channel disconnected by peer!\n");
- switch(reason) {
+ /* Save reason so we know if we should try to reconnect or not */
+ self->disconnect_reason = reason;
+
+ switch (reason) {
case LM_USER_REQUEST: /* User request */
- irlan_close(self);
+ IRDA_DEBUG(2, __FUNCTION__ "(), User requested\n");
break;
case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */
- irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
+ IRDA_DEBUG(2, __FUNCTION__ "(), Unexpected IrLAP disconnect\n");
break;
case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */
- IRDA_DEBUG(2, __FUNCTION__ "(), LM_CONNECT_FAILURE not impl\n");
+ IRDA_DEBUG(2, __FUNCTION__ "(), IrLAP connect failed\n");
break;
case LM_LAP_RESET: /* IrLAP reset */
- IRDA_DEBUG(2, __FUNCTION__ "(), LM_CONNECT_FAILURE not impl\n");
+ IRDA_DEBUG(2, __FUNCTION__ "(), IrLAP reset\n");
break;
case LM_INIT_DISCONNECT:
- IRDA_DEBUG(2, __FUNCTION__ "(), LM_CONNECT_FAILURE not impl\n");
+ IRDA_DEBUG(2, __FUNCTION__ "(), IrLMP connect failed\n");
break;
default:
+ ERROR(__FUNCTION__ "(), Unknown disconnect reason\n");
break;
}
irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
+
+ wake_up_interruptible(&self->open_wait);
}
void irlan_open_data_tsap(struct irlan_cb *self)
@@ -553,9 +474,7 @@ void irlan_close_tsaps(struct irlan_cb *self)
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRLAN_MAGIC, return;);
- /*
- * Disconnect and close all open TSAP connections
- */
+ /* Disconnect and close all open TSAP connections */
if (self->tsap_data) {
irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL);
irttp_close_tsap(self->tsap_data);
@@ -573,6 +492,7 @@ void irlan_close_tsaps(struct irlan_cb *self)
irttp_close_tsap(self->provider.tsap_ctrl);
self->provider.tsap_ctrl = NULL;
}
+ self->disconnect_reason = LM_USER_REQUEST;
}
/*
@@ -595,7 +515,8 @@ void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel)
*/
if (!irias_find_object("IrLAN")) {
obj = irias_new_object("IrLAN", IAS_IRLAN_ID);
- irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel);
+ irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel,
+ IAS_KERNEL_ATTR);
irias_insert_object(obj);
} else {
new_value = irias_new_integer_value(tsap_sel);
@@ -607,18 +528,23 @@ void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel)
if (!irias_find_object("PnP")) {
obj = irias_new_object("PnP", IAS_PNP_ID);
#if 0
- irias_add_string_attrib(obj, "Name", sysctl_devname);
+ irias_add_string_attrib(obj, "Name", sysctl_devname,
+ IAS_KERNEL_ATTR);
#else
- irias_add_string_attrib(obj, "Name", "Linux");
+ irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR);
#endif
- irias_add_string_attrib(obj, "DeviceID", "HWP19F0");
- irias_add_integer_attrib(obj, "CompCnt", 1);
+ irias_add_string_attrib(obj, "DeviceID", "HWP19F0",
+ IAS_KERNEL_ATTR);
+ irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR);
if (self->provider.access_type == ACCESS_PEER)
- irias_add_string_attrib(obj, "Comp#01", "PNP8389");
+ irias_add_string_attrib(obj, "Comp#01", "PNP8389",
+ IAS_KERNEL_ATTR);
else
- irias_add_string_attrib(obj, "Comp#01", "PNP8294");
+ irias_add_string_attrib(obj, "Comp#01", "PNP8294",
+ IAS_KERNEL_ATTR);
- irias_add_string_attrib(obj, "Manufacturer", "Linux-IrDA Project");
+ irias_add_string_attrib(obj, "Manufacturer",
+ "Linux-IrDA Project", IAS_KERNEL_ATTR);
irias_insert_object(obj);
}
}
@@ -633,7 +559,7 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
{
struct sk_buff *skb;
- IRDA_DEBUG(3, __FUNCTION__ "()\n");
+ IRDA_DEBUG(2, __FUNCTION__ "()\n");
if (irda_lock(&self->client.tx_busy) == FALSE)
return -EBUSY;
@@ -652,7 +578,7 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
dev_kfree_skb(skb);
return -1;
}
- IRDA_DEBUG(3, __FUNCTION__ "(), sending ...\n");
+ IRDA_DEBUG(2, __FUNCTION__ "(), sending ...\n");
return irttp_data_request(self->client.tsap_ctrl, skb);
}
@@ -741,7 +667,6 @@ void irlan_open_data_channel(struct irlan_cb *self)
/* self->use_udata = TRUE; */
- /* irttp_data_request(self->client.tsap_ctrl, skb); */
irlan_ctrl_data_request(self, skb);
}
@@ -810,7 +735,6 @@ void irlan_open_unicast_addr(struct irlan_cb *self)
irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
irlan_insert_string_param(skb, "FILTER_MODE", "FILTER");
- /* irttp_data_request(self->client.tsap_ctrl, skb); */
irlan_ctrl_data_request(self, skb);
}
@@ -852,7 +776,6 @@ void irlan_set_broadcast_filter(struct irlan_cb *self, int status)
else
irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
- /* irttp_data_request(self->client.tsap_ctrl, skb); */
irlan_ctrl_data_request(self, skb);
}
@@ -892,7 +815,6 @@ void irlan_set_multicast_filter(struct irlan_cb *self, int status)
else
irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
- /* irttp_data_request(self->client.tsap_ctrl, skb); */
irlan_ctrl_data_request(self, skb);
}
@@ -930,7 +852,6 @@ void irlan_get_unicast_addr(struct irlan_cb *self)
irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC");
- /* irttp_data_request(self->client.tsap_ctrl, skb); */
irlan_ctrl_data_request(self, skb);
}
@@ -965,8 +886,6 @@ void irlan_get_media_char(struct irlan_cb *self)
frame[1] = 0x01; /* One parameter */
irlan_insert_string_param(skb, "MEDIA", "802.3");
-
- /* irttp_data_request(self->client.tsap_ctrl, skb); */
irlan_ctrl_data_request(self, skb);
}
@@ -1169,35 +1088,32 @@ static int irlan_proc_read(char *buf, char **start, off_t offset, int len)
while (self != NULL) {
ASSERT(self->magic == IRLAN_MAGIC, return len;);
- /* Don't display the master server */
- if (self->master == 0) {
- len += sprintf(buf+len, "ifname: %s,\n",
- self->dev.name);
- len += sprintf(buf+len, "client state: %s, ",
- irlan_state[ self->client.state]);
- len += sprintf(buf+len, "provider state: %s,\n",
- irlan_state[ self->provider.state]);
- len += sprintf(buf+len, "saddr: %#08x, ",
- self->saddr);
- len += sprintf(buf+len, "daddr: %#08x\n",
- self->daddr);
- len += sprintf(buf+len, "version: %d.%d,\n",
- self->version[1], self->version[0]);
- len += sprintf(buf+len, "access type: %s\n",
- irlan_access[self->client.access_type]);
- len += sprintf(buf+len, "media: %s\n",
- irlan_media[self->media]);
-
- len += sprintf(buf+len, "local filter:\n");
- len += sprintf(buf+len, "remote filter: ");
- len += irlan_print_filter(self->client.filter_type,
- buf+len);
+ len += sprintf(buf+len, "ifname: %s,\n",
+ self->dev.name);
+ len += sprintf(buf+len, "client state: %s, ",
+ irlan_state[ self->client.state]);
+ len += sprintf(buf+len, "provider state: %s,\n",
+ irlan_state[ self->provider.state]);
+ len += sprintf(buf+len, "saddr: %#08x, ",
+ self->saddr);
+ len += sprintf(buf+len, "daddr: %#08x\n",
+ self->daddr);
+ len += sprintf(buf+len, "version: %d.%d,\n",
+ self->version[1], self->version[0]);
+ len += sprintf(buf+len, "access type: %s\n",
+ irlan_access[self->client.access_type]);
+ len += sprintf(buf+len, "media: %s\n",
+ irlan_media[self->media]);
+
+ len += sprintf(buf+len, "local filter:\n");
+ len += sprintf(buf+len, "remote filter: ");
+ len += irlan_print_filter(self->client.filter_type,
+ buf+len);
- len += sprintf(buf+len, "tx busy: %s\n",
- netif_queue_stopped(&self->dev) ? "TRUE" : "FALSE");
+ len += sprintf(buf+len, "tx busy: %s\n",
+ netif_queue_stopped(&self->dev) ? "TRUE" : "FALSE");
- len += sprintf(buf+len, "\n");
- }
+ len += sprintf(buf+len, "\n");
self = (struct irlan_cb *) hashbin_get_next(irlan);
}
@@ -1272,8 +1188,9 @@ MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
MODULE_DESCRIPTION("The Linux IrDA LAN protocol");
MODULE_PARM(eth, "i");
+MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)");
MODULE_PARM(access, "i");
-MODULE_PARM(timeout, "i");
+MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3");
/*
* Function init_module (void)
diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c
index 1b46ab5b8..007c4acfd 100644
--- a/net/irda/irlan/irlan_eth.c
+++ b/net/irda/irlan/irlan_eth.c
@@ -48,7 +48,6 @@
*/
int irlan_eth_init(struct net_device *dev)
{
- struct irmanager_event mgr_event;
struct irlan_cb *self;
IRDA_DEBUG(2, __FUNCTION__"()\n");
@@ -85,22 +84,6 @@ int irlan_eth_init(struct net_device *dev)
get_random_bytes(dev->dev_addr+5, 1);
}
- /*
- * Network device has now been registered, so tell irmanager about
- * it, so it can be configured with network parameters
- */
- mgr_event.event = EVENT_IRLAN_START;
- sprintf(mgr_event.devname, "%s", self->dev.name);
- irmanager_notify(&mgr_event);
-
- /*
- * We set this so that we only notify once, since if
- * configuration of the network device fails, the user
- * will have to sort it out first anyway. No need to
- * try again.
- */
- self->notify_irmanager = FALSE;
-
return 0;
}
@@ -123,14 +106,16 @@ int irlan_eth_open(struct net_device *dev)
ASSERT(self != NULL, return -1;);
/* Ready to play! */
-/* netif_start_queue(dev) */ /* Wait until data link is ready */
-
- self->notify_irmanager = TRUE;
+ netif_stop_queue(dev); /* Wait until data link is ready */
/* We are now open, so time to do some work */
+ self->disconnect_reason = 0;
irlan_client_wakeup(self, self->saddr, self->daddr);
irlan_mod_inc_use_count();
+
+ /* Make sure we have a hardware address before we return, so DHCP clients gets happy */
+ interruptible_sleep_on(&self->open_wait);
return 0;
}
@@ -146,7 +131,8 @@ int irlan_eth_open(struct net_device *dev)
int irlan_eth_close(struct net_device *dev)
{
struct irlan_cb *self = (struct irlan_cb *) dev->priv;
-
+ struct sk_buff *skb;
+
IRDA_DEBUG(2, __FUNCTION__ "()\n");
/* Stop device */
@@ -155,20 +141,17 @@ int irlan_eth_close(struct net_device *dev)
irlan_mod_dec_use_count();
irlan_close_data_channel(self);
-
irlan_close_tsaps(self);
irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
- irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
-
- /* Device closed by user! */
- if (self->notify_irmanager)
- self->notify_irmanager = FALSE;
- else
- self->notify_irmanager = TRUE;
+ /* Remove frames queued on the control channel */
+ while ((skb = skb_dequeue(&self->client.txq)))
+ dev_kfree_skb(skb);
+ self->client.tx_busy = 0;
+
return 0;
}
diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c
index aeac03f7b..e06807bd9 100644
--- a/net/irda/irlan/irlan_provider.c
+++ b/net/irda/irlan/irlan_provider.c
@@ -116,16 +116,16 @@ static int irlan_provider_data_indication(void *instance, void *sap,
/*
* Function irlan_provider_connect_indication (handle, skb, priv)
*
- * Got connection from peer IrLAN layer
+ * Got connection from peer IrLAN client
*
*/
static void irlan_provider_connect_indication(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
- struct irlan_cb *self, *new;
+ struct irlan_cb *self;
struct tsap_cb *tsap;
__u32 saddr, daddr;
@@ -137,82 +137,24 @@ static void irlan_provider_connect_indication(void *instance, void *sap,
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRLAN_MAGIC, return;);
- self->provider.max_sdu_size = max_sdu_size;
- self->provider.max_header_size = max_header_size;
-
ASSERT(tsap == self->provider.tsap_ctrl,return;);
ASSERT(self->provider.state == IRLAN_IDLE, return;);
daddr = irttp_get_daddr(tsap);
saddr = irttp_get_saddr(tsap);
+ self->provider.max_sdu_size = max_sdu_size;
+ self->provider.max_header_size = max_header_size;
- /* Check if we already dealing with this client or peer */
- new = (struct irlan_cb *) hashbin_find(irlan, daddr, NULL);
- if (new) {
- ASSERT(new->magic == IRLAN_MAGIC, return;);
- IRDA_DEBUG(0, __FUNCTION__ "(), found instance!\n");
-
- /* Update saddr, since client may have moved to a new link */
- new->saddr = saddr;
- IRDA_DEBUG(2, __FUNCTION__ "(), saddr=%08x\n", new->saddr);
-
- /* Make sure that any old provider control TSAP is removed */
- if ((new != self) && new->provider.tsap_ctrl) {
- irttp_disconnect_request(new->provider.tsap_ctrl,
- NULL, P_NORMAL);
- irttp_close_tsap(new->provider.tsap_ctrl);
- new->provider.tsap_ctrl = NULL;
- }
- } else {
- /* This must be the master instance, so start a new instance */
- IRDA_DEBUG(0, __FUNCTION__ "(), starting new provider!\n");
-
- new = irlan_open(saddr, daddr, TRUE);
- }
-
- /*
- * Check if the connection came in on the master server, or the
- * slave server. If it came on the slave, then everything is
- * really, OK (reconnect), if not we need to dup the connection and
- * hand it over to the slave.
- */
- if (new != self) {
-
- /* Now attach up the new "socket" */
- new->provider.tsap_ctrl = irttp_dup(self->provider.tsap_ctrl,
- new);
- if (!new->provider.tsap_ctrl) {
- IRDA_DEBUG(0, __FUNCTION__ "(), dup failed!\n");
- return;
- }
-
- /* new->stsap_sel = new->tsap->stsap_sel; */
- new->dtsap_sel_ctrl = new->provider.tsap_ctrl->dtsap_sel;
-
- /* Clean up the original one to keep it in listen state */
- self->provider.tsap_ctrl->dtsap_sel = LSAP_ANY;
- self->provider.tsap_ctrl->lsap->dlsap_sel = LSAP_ANY;
- self->provider.tsap_ctrl->lsap->lsap_state = LSAP_DISCONNECTED;
-
- /*
- * Use the new instance from here instead of the master
- * struct!
- */
- self = new;
- }
- /* Check if network device has been registered */
- if (!self->netdev_registered)
- irlan_register_netdev(self);
-
irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL);
/*
* If we are in peer mode, the client may not have got the discovery
* indication it needs to make progress. If the client is still in
- * IDLE state, we must kick it to
+ * IDLE state, we must kick it.
*/
if ((self->provider.access_type == ACCESS_PEER) &&
- (self->client.state == IRLAN_IDLE)) {
+ (self->client.state == IRLAN_IDLE))
+ {
irlan_client_wakeup(self, self->saddr, self->daddr);
}
}
@@ -231,11 +173,6 @@ void irlan_provider_connect_response(struct irlan_cb *self,
/* Just accept */
irttp_connect_response(tsap, IRLAN_MTU, NULL);
-
- /* Check if network device has been registered */
- if (!self->netdev_registered)
- irlan_register_netdev(self);
-
}
void irlan_provider_disconnect_indication(void *instance, void *sap,
diff --git a/net/irda/irlap.c b/net/irda/irlap.c
index 1c2958c4b..38d66d327 100644
--- a/net/irda/irlap.c
+++ b/net/irda/irlap.c
@@ -154,7 +154,7 @@ struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos)
irlap_next_state(self, LAP_NDM);
- hashbin_insert(irlap, (queue_t *) self, self->saddr, NULL);
+ hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL);
irlmp_register_link(self, self->saddr, &self->notify);
@@ -232,7 +232,8 @@ void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb)
ASSERT(self->magic == LAP_MAGIC, return;);
irlap_init_qos_capabilities(self, NULL); /* No user QoS! */
-
+
+ skb_get(skb); /*LEVEL4*/
irlmp_link_connect_indication(self->notify.instance, self->saddr,
self->daddr, &self->qos_tx, skb);
}
@@ -248,6 +249,7 @@ void irlap_connect_response(struct irlap_cb *self, struct sk_buff *skb)
IRDA_DEBUG(4, __FUNCTION__ "()\n");
irlap_do_event(self, CONNECT_RESPONSE, skb, NULL);
+ kfree_skb(skb);
}
/*
@@ -292,6 +294,7 @@ void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb)
ASSERT(self != NULL, return;);
ASSERT(self->magic == LAP_MAGIC, return;);
+ skb_get(skb); /*LEVEL4*/
irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb);
}
@@ -310,6 +313,7 @@ void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb,
#ifdef CONFIG_IRDA_COMPRESSION
if (self->qos_tx.compression.value) {
+ skb_get(skb); /*LEVEL4*/
skb = irlap_decompress_frame(self, skb);
if (!skb) {
IRDA_DEBUG(1, __FUNCTION__ "(), Decompress error!\n");
@@ -317,6 +321,7 @@ void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb,
}
}
#endif
+ skb_get(skb); /*LEVEL4*/
irlmp_link_data_indication(self->notify.instance, skb, unreliable);
}
@@ -373,6 +378,7 @@ void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb,
ASSERT(skb != NULL, return;);
}
irlap_do_event(self, SEND_I_CMD, skb, NULL);
+ kfree_skb(skb);
} else
skb_queue_tail(&self->txq, skb);
}
@@ -422,6 +428,7 @@ void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb)
/* Hide LAP header from IrLMP layer */
skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
+ skb_get(skb); /*LEVEL4*/
irlmp_link_unitdata_indication(self->notify.instance, skb);
}
#endif /* CONFIG_IRDA_ULTRA */
@@ -610,7 +617,7 @@ void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery)
*
*
*/
-void irlap_status_indication(int quality_of_link)
+void irlap_status_indication(struct irlap_cb *self, int quality_of_link)
{
switch (quality_of_link) {
case STATUS_NO_ACTIVITY:
@@ -622,7 +629,8 @@ void irlap_status_indication(int quality_of_link)
default:
break;
}
- irlmp_status_indication(quality_of_link, LOCK_NO_CHANGE);
+ irlmp_status_indication(self->notify.instance,
+ quality_of_link, LOCK_NO_CHANGE);
}
/*
@@ -664,11 +672,16 @@ void irlap_reset_confirm(void)
*/
int irlap_generate_rand_time_slot(int S, int s)
{
+ static int rand;
int slot;
ASSERT((S - s) > 0, return 0;);
- slot = s + jiffies % (S-s);
+ rand += jiffies;
+ rand ^= (rand << 12);
+ rand ^= (rand >> 20);
+
+ slot = s + rand % (S-s);
ASSERT((slot >= s) || (slot < S), return 0;);
@@ -863,6 +876,8 @@ void irlap_flush_all_queues(struct irlap_cb *self)
*/
void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now)
{
+ struct sk_buff *skb;
+
IRDA_DEBUG(0, __FUNCTION__ "(), setting speed to %d\n", speed);
ASSERT(self != NULL, return;);
@@ -871,8 +886,11 @@ void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now)
self->speed = speed;
/* Change speed now, or just piggyback speed on frames */
- if (now)
- irda_device_change_speed(self->netdev, speed);
+ if (now) {
+ /* Send down empty frame to trigger speed change */
+ skb = dev_alloc_skb(0);
+ irlap_queue_xmit(self, skb);
+ }
}
#ifdef CONFIG_IRDA_COMPRESSION
@@ -973,8 +991,8 @@ void irlap_init_qos_capabilities(struct irlap_cb *self,
/* Set data size */
/*self->qos_rx.data_size.bits &= 0x03;*/
- /* Set disconnect time */
- self->qos_rx.link_disc_time.bits &= 0x07;
+ /* Set disconnect time -> done properly in qos.c */
+ /*self->qos_rx.link_disc_time.bits &= 0x07;*/
irda_qos_bits_to_value(&self->qos_rx);
}
@@ -998,7 +1016,7 @@ void irlap_apply_default_connection_parameters(struct irlap_cb *self)
irda_device_set_media_busy(self->netdev, TRUE);
/* Default value in NDM */
- self->bofs_count = 11;
+ self->bofs_count = 12;
/*
* Generate random connection address for this session, which must
@@ -1026,8 +1044,8 @@ void irlap_apply_default_connection_parameters(struct irlap_cb *self)
self->qos_rx.data_size.value = 64;
self->qos_tx.window_size.value = 1;
self->qos_rx.window_size.value = 1;
- self->qos_tx.additional_bofs.value = 11;
- self->qos_rx.additional_bofs.value = 11;
+ self->qos_tx.additional_bofs.value = 12;
+ self->qos_rx.additional_bofs.value = 12;
self->qos_tx.link_disc_time.value = 0;
self->qos_rx.link_disc_time.value = 0;
@@ -1071,7 +1089,12 @@ void irlap_apply_connection_parameters(struct irlap_cb *self)
*/
ASSERT(self->qos_tx.max_turn_time.value != 0, return;);
if (self->qos_tx.link_disc_time.value == 3)
- self->N1 = 0;
+ /*
+ * If we set N1 to 0, it will trigger immediately, which is
+ * not what we want. What we really want is to disable it,
+ * Jean II
+ */
+ self->N1 = -1; /* Disable */
else
self->N1 = 3000 / self->qos_tx.max_turn_time.value;
diff --git a/net/irda/irlap_comp.c b/net/irda/irlap_comp.c
index 942949ac8..e81fc7e4a 100644
--- a/net/irda/irlap_comp.c
+++ b/net/irda/irlap_comp.c
@@ -63,7 +63,7 @@ int irda_register_compressor( struct compressor *cp)
new->cp = cp;
/* Insert IrDA compressor into hashbin */
- hashbin_insert( irlap_compressors, (queue_t *) new, cp->compress_proto,
+ hashbin_insert( irlap_compressors, (irda_queue_t *) new, cp->compress_proto,
NULL);
return 0;
diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c
index 08501162e..ec5f6611c 100644
--- a/net/irda/irlap_event.c
+++ b/net/irda/irlap_event.c
@@ -9,8 +9,8 @@
* Modified at: Sat Dec 25 21:07:57 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * Thomas Davis <ratbert@radiks.net>
+ * Copyright (c) 1998-2000 Dag Brattli <dag@brattli.net>,
+ * Copyright (c) 1998 Thomas Davis <ratbert@radiks.net>
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -230,7 +230,7 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
if (!self || self->magic != LAP_MAGIC)
return;
-
+
IRDA_DEBUG(3, __FUNCTION__ "(), event = %s, state = %s\n",
irlap_event[event], irlap_state[self->state]);
@@ -252,6 +252,7 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
while ((skb = skb_dequeue(&self->txq)) != NULL) {
ret = (*state[self->state])(self, SEND_I_CMD,
skb, NULL);
+ kfree_skb(skb);
if (ret == -EPROTO)
break; /* Try again later! */
}
@@ -304,6 +305,17 @@ void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state)
if ((state != LAP_XMIT_P) && (state != LAP_XMIT_S))
self->bytes_left = self->line_capacity;
#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+#ifdef CONFIG_IRDA_ULTRA
+ /* Send any pending Ultra frames if any */
+ /* The higher layers may have sent a few Ultra frames while we
+ * were doing discovery (either query or reply). Those frames
+ * have been queued, but were never sent. It is now time to
+ * send them...
+ * Jean II */
+ if ((state == LAP_NDM) && (!skb_queue_empty(&self->txq_ultra)))
+ /* Force us to listen 500 ms before sending Ultra */
+ irda_device_set_media_busy(self->netdev, TRUE);
+#endif /* CONFIG_IRDA_ULTRA */
}
/*
@@ -351,12 +363,11 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
self->caddr = info->caddr;
irlap_next_state(self, LAP_CONN);
-
+
irlap_connect_indication(self, skb);
} else {
IRDA_DEBUG(0, __FUNCTION__ "(), SNRM frame does not "
"contain an I field!\n");
- dev_kfree_skb(skb);
}
break;
case DISCOVERY_REQUEST:
@@ -375,6 +386,7 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
self->s = info->s;
irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE,
info->discovery);
+ self->frame_sent = FALSE;
self->s++;
irlap_start_slot_timer(self, self->slot_timeout);
@@ -385,12 +397,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
/* 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);
- IRDA_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();
discovery_rsp->daddr = info->daddr;
@@ -410,14 +418,34 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_query_timer(self, QUERY_TIMEOUT*info->S);
irlap_next_state(self, LAP_REPLY);
}
- dev_kfree_skb(skb);
+ else {
+ /* This is the final slot. How is it possible ?
+ * This would happen is both discoveries are just slightly
+ * offset (if they are in sync, all packets are lost).
+ * Most often, all the discovery requests will be received
+ * in QUERY state (see my comment there), except for the
+ * last frame that will come here.
+ * The big trouble when it happen is that active discovery
+ * doesn't happen, because nobody answer the discoveries
+ * frame of the other guy, so the log shows up empty.
+ * What should we do ?
+ * Not much. It's too late to answer those discovery frames,
+ * so we just pass the info to IrLMP who will put it in the
+ * log (and post an event).
+ * Jean II
+ */
+ IRDA_DEBUG(1, __FUNCTION__ "(), Receiving final discovery request, missed the discovery slots :-(\n");
+
+ /* Last discovery request -> in the log */
+ irlap_discovery_indication(self, info->discovery);
+ }
break;
#ifdef CONFIG_IRDA_ULTRA
case SEND_UI_FRAME:
/* Only allowed to repeat an operation twice */
for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) {
skb = skb_dequeue(&self->txq_ultra);
- if (skb)
+ if (skb)
irlap_send_ui_frame(self, skb, CBROADCAST,
CMD_FRAME);
else
@@ -433,7 +461,6 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
if (info->caddr != CBROADCAST) {
IRDA_DEBUG(0, __FUNCTION__
"(), not a broadcast frame!\n");
- dev_kfree_skb(skb);
} else
irlap_unitdata_indication(self, skb);
break;
@@ -447,19 +474,14 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
* will only be used to send out the same info as the cmd
*/
irlap_send_test_frame(self, CBROADCAST, info->daddr, skb);
- dev_kfree_skb(skb);
break;
case RECV_TEST_RSP:
IRDA_DEBUG(0, __FUNCTION__ "() not implemented!\n");
- dev_kfree_skb(skb);
break;
default:
IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n",
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
-
ret = -1;
break;
}
@@ -492,19 +514,51 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
WARNING(__FUNCTION__ "(), discovery log is gone! "
"maybe the discovery timeout has been set to "
"short?\n");
- dev_kfree_skb(skb);
break;
}
hashbin_insert(self->discovery_log,
- (queue_t *) info->discovery,
+ (irda_queue_t *) info->discovery,
info->discovery->daddr, NULL);
/* Keep state */
/* irlap_next_state(self, LAP_QUERY); */
- dev_kfree_skb(skb);
+ break;
+ case RECV_DISCOVERY_XID_CMD:
+ /* Yes, it is possible to receive those frames in this mode.
+ * Note that most often the last discovery request won't
+ * occur here but in NDM state (see my comment there).
+ * What should we do ?
+ * Not much. We are currently performing our own discovery,
+ * therefore we can't answer those frames. We don't want
+ * to change state either. We just pass the info to
+ * IrLMP who will put it in the log (and post an event).
+ * Jean II
+ */
+
+ ASSERT(info != NULL, return -1;);
+
+ IRDA_DEBUG(1, __FUNCTION__ "(), Receiving discovery request (s = %d) while performing discovery :-(\n", info->s);
+
+ /* Last discovery request ? */
+ if (info->s == 0xff)
+ irlap_discovery_indication(self, info->discovery);
break;
case SLOT_TIMER_EXPIRED:
+ /*
+ * Wait a little longer if we detect an incomming frame. This
+ * is not mentioned in the spec, but is a good thing to do,
+ * since we want to work even with devices that violate the
+ * timing requirements.
+ */
+ if (irda_device_is_receiving(self->netdev)) {
+ IRDA_DEBUG(1, __FUNCTION__
+ "(), device is slow to answer, "
+ "waiting some more!\n");
+ irlap_start_slot_timer(self, MSECS_TO_JIFFIES(10));
+ return ret;
+ }
+
if (self->s < self->S) {
irlap_send_discovery_xid_frame(self, self->S,
self->s, TRUE,
@@ -537,9 +591,6 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n",
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
-
ret = -1;
break;
}
@@ -572,9 +623,7 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
break;
case RECV_DISCOVERY_XID_CMD:
ASSERT(info != NULL, return -1;);
- /*
- * Last frame?
- */
+ /* Last frame? */
if (info->s == 0xff) {
del_timer(&self->query_timer);
@@ -595,15 +644,11 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
self->frame_sent = TRUE;
irlap_next_state(self, LAP_REPLY);
}
- dev_kfree_skb(skb);
break;
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, %s\n", event,
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
-
ret = -1;
break;
}
@@ -665,14 +710,12 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_wd_timer(self, self->wd_timeout);
irlap_next_state(self, LAP_NRM_S);
- dev_kfree_skb(skb);
break;
case RECV_DISCOVERY_XID_CMD:
IRDA_DEBUG(3, __FUNCTION__
"(), event RECV_DISCOVER_XID_CMD!\n");
irlap_next_state(self, LAP_NDM);
- dev_kfree_skb(skb);
break;
case DISCONNECT_REQUEST:
irlap_send_dm_frame(self);
@@ -682,9 +725,6 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event,
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, %s\n", event,
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
-
ret = -1;
break;
}
@@ -766,7 +806,6 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_wd_timer(self, self->wd_timeout);
} else {
/* We just ignore the other device! */
- dev_kfree_skb(skb);
irlap_next_state(self, LAP_SETUP);
}
break;
@@ -803,14 +842,11 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
irlap_next_state(self, LAP_NDM);
irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- dev_kfree_skb(skb);
break;
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, %s\n", event,
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
-
+
ret = -1;
break;
}
@@ -860,14 +896,13 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
IRDA_DEBUG(4, __FUNCTION__
"(), Not allowed to transmit more "
"bytes!\n");
- skb_queue_head(&self->txq, skb);
-
+ skb_queue_head(&self->txq, skb_get(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
+ * trigger anyway now, so we just wait for it
* DB
*/
return -EPROTO;
@@ -900,7 +935,7 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
} else {
IRDA_DEBUG(4, __FUNCTION__
"(), Unable to send! remote busy?\n");
- skb_queue_head(&self->txq, skb);
+ skb_queue_head(&self->txq, skb_get(skb));
/*
* The next ret is important, because it tells
@@ -929,9 +964,6 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %s\n",
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
-
ret = -EINVAL;
break;
}
@@ -964,7 +996,6 @@ static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event,
irlap_next_state(self, LAP_NDM);
irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- dev_kfree_skb(skb);
break;
case FINAL_TIMER_EXPIRED:
if (self->retry_count < self->N3) {
@@ -985,9 +1016,6 @@ static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event,
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d\n", event);
- if (skb)
- dev_kfree_skb(skb);
-
ret = -1;
break;
}
@@ -1041,7 +1069,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
/* Keep state, do not move this line */
irlap_next_state(self, LAP_NRM_P);
-
+
irlap_data_indication(self, skb, FALSE);
} else {
del_timer(&self->final_timer);
@@ -1065,7 +1093,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
* upper layers
*/
irlap_next_state(self, LAP_XMIT_P);
-
+
irlap_data_indication(self, skb, FALSE);
/* This is the last frame */
@@ -1102,7 +1130,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_final_timer(self, self->final_timeout);
irlap_next_state(self, LAP_NRM_P);
}
- dev_kfree_skb(skb);
break;
}
/*
@@ -1124,7 +1151,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
/* Keep state, do not move this line */
irlap_next_state(self, LAP_NRM_P);
-
+
irlap_data_indication(self, skb, FALSE);
} else {
/*
@@ -1142,7 +1169,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
/* Keep state, do not move this line!*/
irlap_next_state(self, LAP_NRM_P);
-
+
irlap_data_indication(self, skb, FALSE);
}
break;
@@ -1171,7 +1198,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
self->ack_required = FALSE;
}
- dev_kfree_skb(skb);
break;
}
@@ -1193,7 +1219,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
self->xmitflag = FALSE;
}
- dev_kfree_skb(skb);
break;
}
IRDA_DEBUG(1, __FUNCTION__ "(), Not implemented!\n");
@@ -1209,6 +1234,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
} else {
del_timer(&self->final_timer);
irlap_data_indication(self, skb, TRUE);
+ printk(__FUNCTION__ "(): RECV_UI_FRAME: next state %s\n", irlap_state[self->state]);
irlap_start_poll_timer(self, self->poll_timeout);
}
break;
@@ -1270,7 +1296,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
irlap_disconnect_indication(self, LAP_RESET_INDICATION);
self->xmitflag = TRUE;
}
- dev_kfree_skb(skb);
break;
case RECV_RNR_RSP:
ASSERT(info != NULL, return -1;);
@@ -1285,14 +1310,12 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
/* Start poll timer */
irlap_start_poll_timer(self, self->poll_timeout);
- dev_kfree_skb(skb);
break;
case RECV_FRMR_RSP:
del_timer(&self->final_timer);
self->xmitflag = TRUE;
irlap_next_state(self, LAP_RESET_WAIT);
irlap_reset_indication(self);
- dev_kfree_skb(skb);
break;
case FINAL_TIMER_EXPIRED:
/*
@@ -1331,7 +1354,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
" retry_count=%d\n", self->retry_count);
/* Keep state */
} else if (self->retry_count == self->N1) {
- irlap_status_indication(STATUS_NO_ACTIVITY);
+ irlap_status_indication(self, STATUS_NO_ACTIVITY);
irlap_wait_min_turn_around(self, &self->qos_tx);
irlap_send_rr_frame(self, CMD_FRAME);
@@ -1357,7 +1380,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
} else
irlap_resend_rejected_frames(self, CMD_FRAME);
irlap_start_final_timer(self, self->final_timeout);
- dev_kfree_skb(skb);
break;
case RECV_SREJ_RSP:
irlap_update_nr_received(self, info->nr);
@@ -1367,7 +1389,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
} else
irlap_resend_rejected_frame(self, CMD_FRAME);
irlap_start_final_timer(self, self->final_timeout);
- dev_kfree_skb(skb);
break;
case RECV_RD_RSP:
IRDA_DEBUG(0, __FUNCTION__ "(), RECV_RD_RSP\n");
@@ -1381,8 +1402,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %s\n",
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -1;
break;
@@ -1428,10 +1447,8 @@ static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
irlap_next_state( self, LAP_PCLOSE);
break;
default:
- IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %s\n",
+ IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n",
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -1;
break;
@@ -1467,7 +1484,6 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
irlap_disconnect_indication(self, LAP_NO_RESPONSE);
- dev_kfree_skb(skb);
break;
case RECV_UA_RSP:
del_timer(&self->final_timer);
@@ -1483,7 +1499,6 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_poll_timer(self, self->poll_timeout);
- dev_kfree_skb(skb);
break;
case FINAL_TIMER_EXPIRED:
if (self->retry_count < 3) {
@@ -1522,13 +1537,10 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
IRDA_DEBUG(0, __FUNCTION__
"(), SNRM frame contained an I field!\n");
}
- dev_kfree_skb(skb);
break;
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %s\n",
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -1;
break;
@@ -1566,7 +1578,7 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
* speed and turn-around-time.
*/
if (skb->len > self->bytes_left) {
- skb_queue_head(&self->txq, skb);
+ skb_queue_head(&self->txq, skb_get(skb));
/*
* Switch to NRM_S, this is only possible
* when we are in secondary mode, since we
@@ -1600,7 +1612,7 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
}
} else {
IRDA_DEBUG(2, __FUNCTION__ "(), Unable to send!\n");
- skb_queue_head(&self->txq, skb);
+ skb_queue_head(&self->txq, skb_get(skb));
ret = -EPROTO;
}
break;
@@ -1613,8 +1625,6 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
default:
IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n",
irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -EINVAL;
break;
@@ -1676,7 +1686,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
#endif
/* Keep state, do not move this line */
irlap_next_state(self, LAP_NRM_S);
-
+
irlap_data_indication(self, skb, FALSE);
break;
} else {
@@ -1739,7 +1749,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_wd_timer(self, self->wd_timeout);
}
- dev_kfree_skb(skb);
break;
}
@@ -1778,7 +1787,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
/* Keep state, do not move this line */
irlap_next_state(self, LAP_NRM_S);
-
+
irlap_data_indication(self, skb, FALSE);
irlap_start_wd_timer(self, self->wd_timeout);
}
@@ -1787,11 +1796,9 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
if (ret == NR_INVALID) {
IRDA_DEBUG(0, "NRM_S, NR_INVALID not implemented!\n");
- dev_kfree_skb(skb);
}
if (ret == NS_INVALID) {
IRDA_DEBUG(0, "NRM_S, NS_INVALID not implemented!\n");
- dev_kfree_skb(skb);
}
break;
case RECV_UI_FRAME:
@@ -1870,7 +1877,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
IRDA_DEBUG(1, __FUNCTION__
"(), invalid nr not implemented!\n");
}
- dev_kfree_skb(skb);
break;
case RECV_SNRM_CMD:
/* SNRM frame is not allowed to contain an I-field */
@@ -1885,7 +1891,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
"(), SNRM frame contained an I-field!\n");
}
- dev_kfree_skb(skb);
break;
case RECV_REJ_CMD:
irlap_update_nr_received(self, info->nr);
@@ -1895,7 +1900,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
} else
irlap_resend_rejected_frames(self, CMD_FRAME);
irlap_start_wd_timer(self, self->wd_timeout);
- dev_kfree_skb(skb);
break;
case RECV_SREJ_CMD:
irlap_update_nr_received(self, info->nr);
@@ -1905,31 +1909,34 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
} else
irlap_resend_rejected_frame(self, CMD_FRAME);
irlap_start_wd_timer(self, self->wd_timeout);
- dev_kfree_skb(skb);
break;
case WD_TIMER_EXPIRED:
/*
* Wait until retry_count * n matches negotiated threshold/
* disconnect time (note 2 in IrLAP p. 82)
+ *
+ * Note : self->wd_timeout = (self->poll_timeout * 2),
+ * and self->final_timeout == self->poll_timeout,
+ * which explain why we use (self->retry_count * 2) here !!!
+ * Jean II
*/
IRDA_DEBUG(1, __FUNCTION__ "(), retry_count = %d\n",
self->retry_count);
- if ((self->retry_count < (self->N2/2)) &&
- (self->retry_count != self->N1/2)) {
+ if (((self->retry_count * 2) < self->N2) &&
+ ((self->retry_count * 2) != self->N1)) {
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);
+ self->retry_count++;
+ } else if ((self->retry_count * 2) == self->N1) {
+ irlap_status_indication(self, STATUS_NO_ACTIVITY);
irlap_start_wd_timer(self, self->wd_timeout);
self->retry_count++;
- } else if (self->retry_count >= self->N2/2) {
+ } else if ((self->retry_count * 2) >= 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;
@@ -1944,7 +1951,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
irlap_apply_default_connection_parameters(self);
irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- dev_kfree_skb(skb);
break;
case RECV_DISCOVERY_XID_CMD:
irlap_wait_min_turn_around(self, &self->qos_tx);
@@ -1953,24 +1959,20 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_wd_timer(self, self->wd_timeout);
irlap_next_state(self, LAP_NRM_S);
- dev_kfree_skb(skb);
break;
case RECV_TEST_CMD:
- /* Remove test frame header */
- skb_pull(skb, sizeof(struct test_frame));
+ /* Remove test frame header (only LAP header in NRM) */
+ skb_pull(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
irlap_wait_min_turn_around(self, &self->qos_tx);
irlap_start_wd_timer(self, self->wd_timeout);
/* Send response (info will be copied) */
irlap_send_test_frame(self, self->caddr, info->daddr, skb);
- dev_kfree_skb(skb);
break;
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, (%s)\n",
event, irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -EINVAL;
break;
@@ -2005,7 +2007,6 @@ static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event,
irlap_apply_default_connection_parameters(self);
irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- dev_kfree_skb(skb);
break;
case RECV_DM_RSP:
/* Always switch state before calling upper layers */
@@ -2015,7 +2016,6 @@ static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event,
irlap_apply_default_connection_parameters(self);
irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- dev_kfree_skb(skb);
break;
case WD_TIMER_EXPIRED:
irlap_apply_default_connection_parameters(self);
@@ -2025,9 +2025,7 @@ static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event,
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, (%s)\n",
event, irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
-
+
ret = -EINVAL;
break;
}
@@ -2064,8 +2062,6 @@ static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event,
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, (%s)\n",
event, irlap_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -EINVAL;
break;
diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c
index 830fe6ef7..fff5bef93 100644
--- a/net/irda/irlap_frame.c
+++ b/net/irda/irlap_frame.c
@@ -69,7 +69,8 @@ static inline void irlap_insert_info(struct irlap_cb *self,
* Delay equals negotiated BOFs count, plus the number of BOFs to
* force the negotiated minimum turnaround time
*/
- cb->xbofs = self->bofs_count+self->xbofs_delay;
+ cb->xbofs = self->bofs_count;
+ cb->xbofs_delay = self->xbofs_delay;
/* Reset XBOF's delay (used only for getting min turn time) */
self->xbofs_delay = 0;
@@ -164,7 +165,6 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
if ((info->caddr == 0x00) || (info->caddr == 0xfe)) {
IRDA_DEBUG(3, __FUNCTION__
"(), invalid connection address!\n");
- dev_kfree_skb(skb);
return;
}
@@ -175,13 +175,13 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
/* Only accept if addressed directly to us */
if (info->saddr != self->saddr) {
IRDA_DEBUG(2, __FUNCTION__ "(), not addressed to us!\n");
- dev_kfree_skb(skb);
return;
}
irlap_do_event(self, RECV_SNRM_CMD, skb, info);
- } else
+ } else {
/* Signal that this SNRM frame does not contain and I-field */
irlap_do_event(self, RECV_SNRM_CMD, skb, NULL);
+ }
}
/*
@@ -408,13 +408,11 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
IRDA_DEBUG(0, __FUNCTION__
"(), frame is not addressed to us!\n");
- dev_kfree_skb(skb);
return;
}
if ((discovery = kmalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) {
WARNING(__FUNCTION__ "(), kmalloc failed!\n");
- dev_kfree_skb(skb);
return;
}
memset(discovery, 0, sizeof(discovery_t));
@@ -476,7 +474,6 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
IRDA_DEBUG(0, __FUNCTION__
"(), frame is not addressed to us!\n");
- dev_kfree_skb(skb);
return;
}
@@ -506,13 +503,18 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
* Check if last frame
*/
if (info->s == 0xff) {
+ /* Check if things are sane at this point... */
+ if((discovery_info == NULL) || (skb->len < 3)) {
+ ERROR(__FUNCTION__ "(), discovery frame to short!\n");
+ return;
+ }
+
/*
* We now have some discovery info to deliver!
*/
discovery = kmalloc(sizeof(discovery_t), GFP_ATOMIC);
if (!discovery) {
WARNING(__FUNCTION__ "(), unable to malloc!\n");
- dev_kfree_skb(skb);
return;
}
@@ -541,7 +543,7 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
info->discovery = discovery;
} else
info->discovery = NULL;
-
+
irlap_do_event(self, RECV_DISCOVERY_XID_CMD, skb, info);
}
@@ -734,7 +736,6 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb)
/* Copy buffer */
tx_skb = skb_clone(skb, GFP_ATOMIC);
if (tx_skb == NULL) {
- dev_kfree_skb(skb);
return;
}
@@ -747,7 +748,7 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb)
/*
* Insert frame in store, in case of retransmissions
*/
- skb_queue_tail(&self->wx_list, skb);
+ skb_queue_tail(&self->wx_list, skb_get(skb));
self->vs = (self->vs + 1) % 8;
self->ack_required = FALSE;
@@ -756,7 +757,7 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb)
irlap_send_i_frame( self, tx_skb, CMD_FRAME);
} else {
IRDA_DEBUG(4, __FUNCTION__ "(), sending unreliable frame\n");
- irlap_send_ui_frame(self, skb, self->caddr, CMD_FRAME);
+ irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
self->window -= 1;
}
}
@@ -781,7 +782,6 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
/* Copy buffer */
tx_skb = skb_clone(skb, GFP_ATOMIC);
if (tx_skb == NULL) {
- dev_kfree_skb(skb);
return;
}
@@ -794,7 +794,7 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
/*
* Insert frame in store, in case of retransmissions
*/
- skb_queue_tail(&self->wx_list, skb);
+ skb_queue_tail(&self->wx_list, skb_get(skb));
/*
* Set poll bit if necessary. We do this to the copied
@@ -819,12 +819,12 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
del_timer(&self->poll_timer);
if (self->ack_required) {
- irlap_send_ui_frame(self, skb, self->caddr, CMD_FRAME);
+ irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
irlap_send_rr_frame(self, CMD_FRAME);
self->ack_required = FALSE;
} else {
skb->data[1] |= PF_BIT;
- irlap_send_ui_frame(self, skb, self->caddr, CMD_FRAME);
+ irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
}
self->window = self->window_size;
irlap_start_final_timer(self, self->final_timeout);
@@ -857,7 +857,6 @@ void irlap_send_data_secondary_final(struct irlap_cb *self,
tx_skb = skb_clone(skb, GFP_ATOMIC);
if (tx_skb == NULL) {
- dev_kfree_skb(skb);
return;
}
@@ -865,7 +864,7 @@ void irlap_send_data_secondary_final(struct irlap_cb *self,
skb_set_owner_w(tx_skb, skb->sk);
/* Insert frame in store */
- skb_queue_tail(&self->wx_list, skb);
+ skb_queue_tail(&self->wx_list, skb_get(skb));
tx_skb->data[1] |= PF_BIT;
@@ -878,12 +877,12 @@ void irlap_send_data_secondary_final(struct irlap_cb *self,
irlap_send_i_frame(self, tx_skb, RSP_FRAME);
} else {
if (self->ack_required) {
- irlap_send_ui_frame(self, skb, self->caddr, RSP_FRAME);
+ irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
irlap_send_rr_frame(self, RSP_FRAME);
self->ack_required = FALSE;
} else {
skb->data[1] |= PF_BIT;
- irlap_send_ui_frame(self, skb, self->caddr, RSP_FRAME);
+ irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
}
self->window = self->window_size;
@@ -912,7 +911,6 @@ void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb)
tx_skb = skb_clone(skb, GFP_ATOMIC);
if (tx_skb == NULL) {
- dev_kfree_skb(skb);
return;
}
@@ -920,7 +918,7 @@ void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb)
skb_set_owner_w(tx_skb, skb->sk);
/* Insert frame in store */
- skb_queue_tail(&self->wx_list, skb);
+ skb_queue_tail(&self->wx_list, skb_get(skb));
self->vs = (self->vs + 1) % 8;
self->ack_required = FALSE;
@@ -928,7 +926,7 @@ void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb)
irlap_send_i_frame(self, tx_skb, RSP_FRAME);
} else {
- irlap_send_ui_frame(self, skb, self->caddr, RSP_FRAME);
+ irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
self->window -= 1;
}
}
@@ -1023,6 +1021,7 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
} else {
irlap_send_data_primary_poll(self, skb);
}
+ kfree_skb(skb);
}
}
#endif
@@ -1212,7 +1211,7 @@ void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
struct test_frame *frame;
__u8 *info;
- skb = dev_alloc_skb(32);
+ skb = dev_alloc_skb(cmd->len+sizeof(struct test_frame));
if (!skb)
return;
@@ -1225,10 +1224,10 @@ void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
frame->saddr = cpu_to_le32(self->saddr);
frame->daddr = cpu_to_le32(daddr);
} else
- frame = (struct test_frame *) skb_put(skb, LAP_MAX_HEADER);
+ frame = (struct test_frame *) skb_put(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
frame->caddr = caddr;
- frame->control = TEST_RSP;
+ frame->control = TEST_RSP | PF_BIT;
/* Copy info */
info = skb_put(skb, cmd->len);
@@ -1259,7 +1258,6 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
if (skb->len < sizeof(struct test_frame)) {
IRDA_DEBUG(0, __FUNCTION__
"() test frame to short!\n");
- dev_kfree_skb(skb);
return;
}
@@ -1270,7 +1268,6 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
/* Make sure frame is addressed to us */
if ((info->saddr != self->saddr) &&
(info->saddr != BROADCAST)) {
- dev_kfree_skb(skb);
return;
}
}
@@ -1323,8 +1320,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
/* First we check if this frame has a valid connection address */
if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) {
IRDA_DEBUG(0, __FUNCTION__ "(), wrong connection address!\n");
- dev_kfree_skb(skb);
- return 0;
+ goto out;
}
/*
* Optimize for the common case and check if the frame is an
@@ -1332,7 +1328,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
*/
if (~control & 0x01) {
irlap_recv_i_frame(self, skb, &info, command);
- return 0;
+ goto out;
}
/*
* We now check is the frame is an S(upervisory) frame. Only
@@ -1360,10 +1356,9 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
WARNING(__FUNCTION__
"() Unknown S-frame %02x received!\n",
info.control);
- dev_kfree_skb(skb);
break;
}
- return 0;
+ goto out;
}
/*
* This must be a C(ontrol) frame
@@ -1399,8 +1394,9 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
default:
WARNING(__FUNCTION__ "(), Unknown frame %02x received!\n",
info.control);
- dev_kfree_skb(skb);
break;
}
+out:
+ dev_kfree_skb(skb);
return 0;
}
diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c
index 5be0298a9..8b4d47caa 100644
--- a/net/irda/irlmp.c
+++ b/net/irda/irlmp.c
@@ -74,6 +74,7 @@ int irlmp_proc_read(char *buf, char **start, off_t offst, int len);
*/
int __init irlmp_init(void)
{
+ IRDA_DEBUG(0, __FUNCTION__ "()\n");
/* Initialize the irlmp structure. */
irlmp = kmalloc( sizeof(struct irlmp_cb), GFP_KERNEL);
if (irlmp == NULL)
@@ -81,7 +82,7 @@ int __init irlmp_init(void)
memset(irlmp, 0, sizeof(struct irlmp_cb));
irlmp->magic = LMP_MAGIC;
- spin_lock_init(&irlmp->lock);
+ spin_lock_init(&irlmp->log_lock);
irlmp->clients = hashbin_new(HB_GLOBAL);
irlmp->services = hashbin_new(HB_GLOBAL);
@@ -178,7 +179,7 @@ struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid)
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
/* Insert into queue of unconnected LSAPs */
- hashbin_insert(irlmp->unconnected_lsaps, (queue_t *) self, (int) self,
+ hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, (int) self,
NULL);
return self;
@@ -284,9 +285,9 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify)
init_timer(&lap->idle_timer);
/*
- * Insert into queue of unconnected LSAPs
+ * Insert into queue of LMP links
*/
- hashbin_insert(irlmp->links, (queue_t *) lap, lap->saddr, NULL);
+ hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL);
/*
* We set only this variable so IrLAP can tell us on which link the
@@ -395,9 +396,25 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
return -EHOSTUNREACH;
}
+ /* Check if LAP is disconnected or already connected */
if (lap->daddr == DEV_ADDR_ANY)
lap->daddr = daddr;
else if (lap->daddr != daddr) {
+ struct lsap_cb *any_lsap;
+
+ /* Check if some LSAPs are active on this LAP */
+ any_lsap = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
+ if (any_lsap == NULL) {
+ /* No active connection, but LAP hasn't been
+ * disconnected yet (waiting for timeout in LAP).
+ * Maybe we could give LAP a bit of help in this case.
+ */
+ IRDA_DEBUG(0, __FUNCTION__ "(), sorry, but I'm waiting for LAP to timeout!\n");
+ return -EAGAIN;
+ }
+
+ /* LAP is already connected to a different node, and LAP
+ * can only talk to one node at a time */
IRDA_DEBUG(0, __FUNCTION__ "(), sorry, but link is busy!\n");
return -EBUSY;
}
@@ -415,7 +432,7 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
ASSERT(lsap->lap != NULL, return -1;);
ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;);
- hashbin_insert(self->lap->lsaps, (queue_t *) self, (int) self, NULL);
+ hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (int) self, NULL);
self->connected = TRUE;
@@ -557,7 +574,7 @@ struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
init_timer(&new->watchdog_timer);
- hashbin_insert(irlmp->unconnected_lsaps, (queue_t *) new, (int) new,
+ hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, (int) new,
NULL);
/* Make sure that we invalidate the cache */
@@ -612,7 +629,7 @@ int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata)
ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;);
ASSERT(lsap == self, return -1;);
- hashbin_insert(irlmp->unconnected_lsaps, (queue_t *) self, (int) self,
+ hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, (int) self,
NULL);
/* Reset some values */
@@ -658,7 +675,7 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
ASSERT(lsap != NULL, return;);
ASSERT(lsap == self, return;);
- hashbin_insert(irlmp->unconnected_lsaps, (queue_t *) lsap, (int) lsap,
+ hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap, (int) lsap,
NULL);
self->lap = NULL;
@@ -749,6 +766,18 @@ void irlmp_discovery_request(int nslots)
irlmp_do_discovery(nslots);
}
+/*
+ * Function irlmp_get_discoveries (pn, mask)
+ *
+ * Return the current discovery log
+ *
+ */
+struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask)
+{
+ /* Return current cached discovery log */
+ return(irlmp_copy_discoveries(irlmp->cachelog, pn, mask));
+}
+
#if 0
/*
* Function irlmp_check_services (discovery)
@@ -759,7 +788,6 @@ void irlmp_discovery_request(int nslots)
void irlmp_check_services(discovery_t *discovery)
{
struct irlmp_client *client;
- struct irmanager_event event;
__u8 *service_log;
__u8 service;
int i = 0;
@@ -787,14 +815,7 @@ void irlmp_check_services(discovery_t *discovery)
continue;
/*
* Found no clients for dealing with this service,
- * so ask the user space irmanager to try to load
- * the right module for us
*/
- event.event = EVENT_DEVICE_DISCOVERED;
- event.service = service;
- event.daddr = discovery->daddr;
- sprintf(event.info, "%s", discovery->info);
- irmanager_notify(&event);
}
}
kfree(service_log);
@@ -805,17 +826,24 @@ void irlmp_check_services(discovery_t *discovery)
*
* Notify all about discovered devices
*
+ * Clients registered with IrLMP are :
+ * o IrComm
+ * o IrLAN
+ * o Any socket (in any state - ouch, that may be a lot !)
+ * The client may have defined a callback to be notified in case of
+ * partial/selective discovery based on the hints that it passed to IrLMP.
*/
-void irlmp_notify_client(irlmp_client_t *client, hashbin_t *log)
+static inline void
+irlmp_notify_client(irlmp_client_t *client, hashbin_t *log)
{
discovery_t *discovery;
IRDA_DEBUG(3, __FUNCTION__ "()\n");
- /* Check if client wants the whole log */
- if (client->callback2)
- client->callback2(log);
-
+ /* Check if client wants or not partial/selective log (optimisation) */
+ if (!client->disco_callback)
+ return;
+
/*
* Now, check all discovered devices (if any), and notify client
* only about the services that the client is interested in
@@ -828,10 +856,9 @@ void irlmp_notify_client(irlmp_client_t *client, hashbin_t *log)
* Any common hint bits? Remember to mask away the extension
* bits ;-)
*/
- if (client->hint_mask & discovery->hints.word & 0x7f7f) {
- if (client->callback1)
- client->callback1(discovery);
- }
+ if (client->hint_mask & discovery->hints.word & 0x7f7f)
+ client->disco_callback(discovery, client->priv);
+
discovery = (discovery_t *) hashbin_get_next(log);
}
}
@@ -864,9 +891,40 @@ void irlmp_discovery_confirm(hashbin_t *log)
}
/*
+ * Function irlmp_discovery_expiry (expiry)
+ *
+ * This device is no longer been discovered, and therefore it is beeing
+ * purged from the discovery log. Inform all clients who have
+ * registered for this event...
+ *
+ * Note : called exclusively from discovery.c
+ * Note : as we are currently processing the log, the clients callback
+ * should *NOT* attempt to touch the log now.
+ */
+void irlmp_discovery_expiry(discovery_t *expiry)
+{
+ irlmp_client_t *client;
+
+ IRDA_DEBUG(3, __FUNCTION__ "()\n");
+
+ ASSERT(expiry != NULL, return;);
+
+ client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
+ while (client != NULL) {
+ /* Check if we should notify client */
+ if ((client->expir_callback) &&
+ (client->hint_mask & expiry->hints.word & 0x7f7f))
+ client->expir_callback(expiry, client->priv);
+
+ /* Next client */
+ client = (irlmp_client_t *) hashbin_get_next(irlmp->clients);
+ }
+}
+
+/*
* Function irlmp_get_discovery_response ()
*
- * Used by IrLAP to get the disocvery info it needs when answering
+ * Used by IrLAP to get the discovery info it needs when answering
* discovery requests by other devices.
*/
discovery_t *irlmp_get_discovery_response()
@@ -1046,9 +1104,35 @@ void irlmp_status_request(void)
IRDA_DEBUG(0, __FUNCTION__ "(), Not implemented\n");
}
-void irlmp_status_indication(LINK_STATUS link, LOCK_STATUS lock)
+/*
+ * Propagate status indication from LAP to LSAPs (via LMP)
+ * This don't trigger any change of state in lap_cb, lmp_cb or lsap_cb,
+ * and the event is stateless, therefore we can bypass both state machines
+ * and send the event direct to the LSAP user.
+ * Jean II
+ */
+void irlmp_status_indication(struct lap_cb *self,
+ LINK_STATUS link, LOCK_STATUS lock)
{
- IRDA_DEBUG(1, __FUNCTION__ "(), Not implemented\n");
+ struct lsap_cb *next;
+ struct lsap_cb *curr;
+
+ /* Send status_indication to all LSAPs using this link */
+ next = (struct lsap_cb *) hashbin_get_first( self->lsaps);
+ while (next != NULL ) {
+ curr = next;
+ next = (struct lsap_cb *) hashbin_get_next(self->lsaps);
+
+ ASSERT(curr->magic == LMP_LSAP_MAGIC, return;);
+ /*
+ * Inform service user if he has requested it
+ */
+ if (curr->notify.status_indication != NULL)
+ curr->notify.status_indication(curr->notify.instance,
+ link, lock);
+ else
+ IRDA_DEBUG(2, __FUNCTION__ "(), no handler\n");
+ }
}
/*
@@ -1207,7 +1291,7 @@ __u32 irlmp_register_service(__u16 hints)
return 0;
}
service->hints = hints;
- hashbin_insert(irlmp->services, (queue_t *) service, handle, NULL);
+ hashbin_insert(irlmp->services, (irda_queue_t *) service, handle, NULL);
return handle;
}
@@ -1255,15 +1339,20 @@ int irlmp_unregister_service(__u32 handle)
* Function irlmp_register_client (hint_mask, callback1, callback2)
*
* Register a local client with IrLMP
+ * First callback is selective discovery (based on hints)
+ * Second callback is for selective discovery expiries
*
* Returns: handle > 0 on success, 0 on error
*/
-__u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 callback1,
- DISCOVERY_CALLBACK2 callback2)
+__u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
+ DISCOVERY_CALLBACK1 expir_clb, void *priv)
{
irlmp_client_t *client;
__u32 handle;
+ IRDA_DEBUG(0, __FUNCTION__ "()\n");
+ ASSERT(irlmp != NULL, return 0;);
+
/* Get a unique handle for this client */
get_random_bytes(&handle, sizeof(handle));
while (hashbin_find(irlmp->clients, handle, NULL) || !handle)
@@ -1273,16 +1362,16 @@ __u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 callback1,
client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC);
if (!client) {
IRDA_DEBUG( 1, __FUNCTION__ "(), Unable to kmalloc!\n");
-
return 0;
}
/* Register the details */
client->hint_mask = hint_mask;
- client->callback1 = callback1;
- client->callback2 = callback2;
+ client->disco_callback = disco_clb;
+ client->expir_callback = expir_clb;
+ client->priv = priv;
- hashbin_insert(irlmp->clients, (queue_t *) client, handle, NULL);
+ hashbin_insert(irlmp->clients, (irda_queue_t *) client, handle, NULL);
return handle;
}
@@ -1296,8 +1385,8 @@ __u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 callback1,
* Returns: 0 on success, -1 on error
*/
int irlmp_update_client(__u32 handle, __u16 hint_mask,
- DISCOVERY_CALLBACK1 callback1,
- DISCOVERY_CALLBACK2 callback2)
+ DISCOVERY_CALLBACK1 disco_clb,
+ DISCOVERY_CALLBACK1 expir_clb, void *priv)
{
irlmp_client_t *client;
@@ -1311,8 +1400,9 @@ int irlmp_update_client(__u32 handle, __u16 hint_mask,
}
client->hint_mask = hint_mask;
- client->callback1 = callback1;
- client->callback2 = callback2;
+ client->disco_callback = disco_clb;
+ client->expir_callback = expir_clb;
+ client->priv = priv;
return 0;
}
diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c
index ace20d70e..a4646b274 100644
--- a/net/irda/irlmp_event.c
+++ b/net/irda/irlmp_event.c
@@ -122,7 +122,7 @@ int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
IRDA_DEBUG(4, __FUNCTION__ "(), EVENT = %s, STATE = %s\n",
- irlmp_event[event], irlmp_state[ self->lsap_state]);
+ irlmp_event[event], irlsap_state[ self->lsap_state]);
return (*lsap_state[self->lsap_state]) (self, event, skb);
}
@@ -393,6 +393,14 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
irlmp_next_lap_state(self, LAP_STANDBY);
self->refcount = 0;
+ /* In some case, at this point our side has already closed
+ * all lsaps, and we are waiting for the idle_timer to
+ * expire. If another device reconnect immediately, the
+ * idle timer will expire in the midle of the connection
+ * initialisation, screwing up things a lot...
+ * Therefore, we must stop the timer... */
+ irlmp_stop_idle_timer(self);
+
/*
* Inform all connected LSAP's using this link
*/
@@ -410,7 +418,8 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
}
break;
default:
- IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %d\n", event);
+ IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %s\n",
+ irlmp_event[event]);
if (skb)
dev_kfree_skb(skb);
break;
@@ -514,7 +523,7 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
ASSERT(self->lap != NULL, return -1;);
ASSERT(self->lap->lsaps != NULL, return -1;);
- hashbin_insert(self->lap->lsaps, (queue_t *) self, (int) self,
+ hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (int) self,
NULL);
irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c
index c9bca1ea2..56287afbb 100644
--- a/net/irda/irlmp_frame.c
+++ b/net/irda/irlmp_frame.c
@@ -34,6 +34,9 @@
#include <net/irda/irlmp_frame.h>
#include <net/irda/discovery.h>
+#define DISCO_SMALL_DELAY 250 /* Delay for some discoveries in ms */
+struct timer_list disco_delay; /* The timer associated */
+
static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap,
__u8 slsap, int status, hashbin_t *);
@@ -124,9 +127,11 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
irlmp->unconnected_lsaps);
/* Maybe LSAP was already connected, so try one more time */
- if (!lsap)
+ if (!lsap) {
+ IRDA_DEBUG(1, __FUNCTION__ "(), incoming connection for LSAP already connected\n");
lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
self->lsaps);
+ }
} else
lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
self->lsaps);
@@ -338,10 +343,50 @@ void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
}
/*
+ * Function irlmp_discovery_timeout (priv)
+ *
+ * Create a discovery event to the state machine (called after a delay)
+ *
+ * Note : irlmp_do_lap_event will handle the very rare case where the LAP
+ * is destroyed while we were sleeping.
+ */
+static void irlmp_discovery_timeout(u_long priv)
+{
+ struct lap_cb *self;
+
+ IRDA_DEBUG(2, __FUNCTION__ "()\n");
+
+ self = (struct lap_cb *) priv;
+ ASSERT(self != NULL, return;);
+
+ /* Just handle it the same way as a discovery confirm */
+ irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL);
+}
+
+/*
* Function irlmp_link_discovery_indication (self, log)
*
* Device is discovering us
*
+ * It's not an answer to our own discoveries, just another device trying
+ * to perform discovery, but we don't want to miss the opportunity
+ * to exploit this information, because :
+ * o We may not actively perform discovery (just passive discovery)
+ * o This type of discovery is much more reliable. In some cases, it
+ * seem that less than 50% of our discoveries get an answer, while
+ * we always get ~100% of these.
+ * o Make faster discovery, statistically divide time of discovery
+ * events by 2 (important for the latency aspect and user feel)
+ * However, when both devices discover each other, they might attempt to
+ * connect to each other, and it would create collisions on the medium.
+ * The trick here is to defer the event by a little delay to avoid both
+ * devices to jump in exactly at the same time...
+ *
+ * The delay is currently set to 0.25s, which leave enough time to perform
+ * a connection and don't interfer with next discovery (the lowest discovery
+ * period/timeout that may be set is 1s). The message triggering this
+ * event was the last of the discovery, so the medium is now free...
+ * Maybe more testing is needed to get the value right...
*/
void irlmp_link_discovery_indication(struct lap_cb *self,
discovery_t *discovery)
@@ -351,11 +396,14 @@ void irlmp_link_discovery_indication(struct lap_cb *self,
irlmp_add_discovery(irlmp->cachelog, discovery);
-#if 0 /* This will just cause a lot of connection collisions */
-
- /* Just handle it the same way as a discovery confirm */
- irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL);
-#endif
+ /* If delay was activated, kill it! */
+ if(timer_pending(&disco_delay))
+ del_timer(&disco_delay);
+ /* Set delay timer to expire in 0.25s. */
+ disco_delay.expires = jiffies + (DISCO_SMALL_DELAY * HZ/1000);
+ disco_delay.function = irlmp_discovery_timeout;
+ disco_delay.data = (unsigned long) self;
+ add_timer(&disco_delay);
}
/*
@@ -374,7 +422,12 @@ void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
ASSERT(self->magic == LMP_LAP_MAGIC, return;);
irlmp_add_discovery_log(irlmp->cachelog, log);
-
+
+ /* If discovery delay was activated, kill it! */
+ if(timer_pending(&disco_delay))
+ del_timer(&disco_delay);
+
+ /* Propagate event to the state machine */
irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL);
}
diff --git a/net/irda/irnet/Config.in b/net/irda/irnet/Config.in
new file mode 100644
index 000000000..d578593f1
--- /dev/null
+++ b/net/irda/irnet/Config.in
@@ -0,0 +1 @@
+dep_tristate ' IrNET protocol' CONFIG_IRNET $CONFIG_IRDA
diff --git a/net/irda/irnet/Makefile b/net/irda/irnet/Makefile
new file mode 100644
index 000000000..50554ac72
--- /dev/null
+++ b/net/irda/irnet/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the Linux IrDA IrNET protocol layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+MOD_LIST_NAME := IRDA_MODULES
+O_TARGET := irnet.o
+#O_OBJS := irnet_ppp.o
+O_OBJS := irnet_ppp.o irnet_irda.o
+M_OBJS := $(O_TARGET)
+MI_OBJS :=
+
+OX_OBJS +=
+
+include $(TOPDIR)/Rules.make
+
+tar:
+ tar -cvf /dev/f1 .
diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h
new file mode 100644
index 000000000..6c9df66b3
--- /dev/null
+++ b/net/irda/irnet/irnet.h
@@ -0,0 +1,453 @@
+/*
+ * IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ * Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file contains definitions and declarations global to the IrNET module,
+ * all grouped in one place...
+ * This file is a private header, so other modules don't want to know
+ * what's in there...
+ *
+ * Note : as most part of the Linux kernel, this module is available
+ * under the GNU Public License (GPL).
+ */
+
+#ifndef IRNET_H
+#define IRNET_H
+
+/************************** DOCUMENTATION ***************************/
+/*
+ * What is IrNET
+ * -------------
+ * IrNET is a protocol allowing to carry TCP/IP traffic between two
+ * IrDA peers in an efficient fashion. It is a thin layer, passing PPP
+ * packets to IrTTP and vice versa. It uses PPP in synchronous mode,
+ * because IrTTP offer a reliable sequenced packet service (as opposed
+ * to a byte stream). In fact, you could see IrNET as carrying TCP/IP
+ * in a IrDA socket, using PPP to provide the glue.
+ *
+ * The main difference with traditional PPP over IrCOMM is that we
+ * avoid the framing and serial emulation which are a performance
+ * bottleneck. It also allows multipoint communications in a sensible
+ * fashion.
+ *
+ * The main difference with IrLAN is that we use PPP for the link
+ * management, which is more standard, interoperable and flexible than
+ * the IrLAN protocol. For example, PPP adds authentication,
+ * encryption, compression, header compression and automated routing
+ * setup. And, as IrNET let PPP do the hard work, the implementation
+ * is much simpler than IrLAN.
+ *
+ * The Linux implementation
+ * ------------------------
+ * IrNET is written on top of the Linux-IrDA stack, and interface with
+ * the generic Linux PPP driver. Because IrNET depend on recent
+ * changes of the PPP driver interface, IrNET will work only with very
+ * recent kernel (2.3.99-pre6 and up).
+ *
+ * The present implementation offer the following features :
+ * o simple user interface using pppd
+ * o efficient implementation (interface directly to PPP and IrTTP)
+ * o addressing (you can specify the name of the IrNET recipient)
+ * o multipoint operation (limited by IrLAP specification)
+ * o information in /proc/net/irda/irnet
+ * o IrNET events on /dev/irnet (for user space daemon)
+ * o IrNET deamon (irnetd) to automatically handle incomming requests
+ * o Windows 2000 compatibility (tested, but need more work)
+ * Currently missing :
+ * o Lot's of testing (that's your job)
+ * o Connection retries (may be too hard to do)
+ * o Check pppd persist mode
+ * o User space deamon (to automatically handle incomming requests)
+ * o A registered device number (comming, waiting from an answer)
+ * o Final integration in Linux-IrDA (up to Dag)
+ *
+ * The setup is not currently the most easy, but this should get much
+ * better when everything will get integrated...
+ *
+ * Acknowledgements
+ * ----------------
+ * This module is based on :
+ * o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras
+ * o The IrLAN protocol (irlan_common/XXX) by Dag Brattli
+ * o The IrSock interface (af_irda) by Dag Brattli
+ * o Some other bits from the kernel and my drivers...
+ * Infinite thanks to those brave souls for providing the infrastructure
+ * upon which IrNET is built.
+ *
+ * Thanks to all my collegues in HP for helping me. In particular,
+ * thanks to Salil Pradhan and Bill Serra for W2k testing...
+ * Thanks to Luiz Magalhaes for irnetd and much testing...
+ *
+ * Thanks to Alan Cox for answering lot's of my stupid questions, and
+ * to Paul Mackerras answering my questions on how to best integrate
+ * IrNET and pppd.
+ *
+ * Jean II
+ *
+ * Note on some implementations choices...
+ * ------------------------------------
+ * 1) Direct interface vs tty/socket
+ * I could have used a tty interface to hook to ppp and use the full
+ * socket API to connect to IrDA. The code would have been easier to
+ * maintain, and maybe the code would have been smaller...
+ * Instead, we hook directly to ppp_generic and to IrTTP, which make
+ * things more complicated...
+ *
+ * The first reason is flexibility : this allow us to create IrNET
+ * instances on demand (no /dev/ircommX crap) and to allow linkname
+ * specification on pppd command line...
+ *
+ * Second reason is speed optimisation. If you look closely at the
+ * transmit and receive paths, you will notice that they are "super lean"
+ * (that's why they look ugly), with no function calls and as little data
+ * copy and modification as I could...
+ *
+ * 2) irnetd in user space
+ * irnetd is implemented in user space, which is necessary to call pppd.
+ * This also give maximum benefits in term of flexibility and customability,
+ * and allow to offer the event channel, useful for other stuff like debug.
+ *
+ * On the other hand, this require a loose coordination between the
+ * present module and irnetd. One critical area is how incomming request
+ * are handled.
+ * When irnet receive an incomming request, it send an event to irnetd and
+ * drop the incomming IrNET socket.
+ * irnetd start a pppd instance, which create a new IrNET socket. This new
+ * socket is then connected in the originating node to the pppd instance.
+ * At this point, in the originating node, the first socket is closed.
+ *
+ * I admit, this is a bit messy and waste some ressources. The alternative
+ * is caching incomming socket, and that's also quite messy and waste
+ * ressources.
+ * We also make connection time slower. For example, on a 115 kb/s link it
+ * adds 60ms to the connection time (770 ms). However, this is slower than
+ * the time it takes to fire up pppd on my P133...
+ *
+ *
+ * History :
+ * -------
+ *
+ * v1 - 15/5/00 - Jean II
+ * o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint)
+ * o control channel on /dev/irnet (set name/address)
+ * o event channel on /dev/irnet (for user space daemon)
+ *
+ * v2 - 5/6/00 - Jean II
+ * o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness...
+ * o Add DISCONNECT_TO event and rename DISCONNECT_FROM.
+ * o Set official device number alloaction on /dev/irnet
+ *
+ * v3 - 30/8/00 - Jean II
+ * o Update to latest Linux-IrDA changes :
+ * - queue_t => irda_queue_t
+ * o Update to ppp-2.4.0 :
+ * - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD
+ * o Add EXPIRE event (depend on new IrDA-Linux patch)
+ * o Switch from `hashbin_remove' to `hashbin_remove_this' to fix
+ * a multilink bug... (depend on new IrDA-Linux patch)
+ * o fix a self->daddr to self->raddr in irda_irnet_connect to fix
+ * another multilink bug (darn !)
+ * o Remove LINKNAME_IOCTL cruft
+ *
+ * v3b - 31/8/00 - Jean II
+ * o Dump discovery log at event channel startup
+ *
+ * v4 - 28/9/00 - Jean II
+ * o Fix interaction between poll/select and dump discovery log
+ * o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch)
+ * o Add IRNET_NOANSWER_FROM event (mostly to help support)
+ * o Release flow control in disconnect_indication
+ * o Block packets while connecting (speed up connections)
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/tty.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <asm/uaccess.h>
+
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/ppp_channel.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irias_object.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irttp.h>
+#include <net/irda/discovery.h>
+
+/***************************** OPTIONS *****************************/
+/*
+ * Define or undefine to compile or not some optional part of the
+ * IrNET driver...
+ * Note : the present defaults make sense, play with that at your
+ * own risk...
+ */
+/* IrDA side of the business... */
+#define DISCOVERY_NOMASK /* To enable W2k compatibility... */
+#define ADVERTISE_HINT /* Advertise IrLAN hint bit */
+#define ALLOW_SIMULT_CONNECT /* This seem to work, cross fingers... */
+#define DISCOVERY_EVENTS /* Query the discovery log to post events */
+#define INITIAL_DISCOVERY /* Dump current discovery log as events */
+#undef STREAM_COMPAT /* Not needed - potentially messy */
+#undef CONNECT_INDIC_KICK /* Might mess IrDA, not needed */
+#undef FAIL_SEND_DISCONNECT /* Might mess IrDA, not needed */
+#undef PASS_CONNECT_PACKETS /* Not needed ? Safe */
+
+/* PPP side of the business */
+#define BLOCK_WHEN_CONNECT /* Block packets when connecting */
+#undef CONNECT_IN_SEND /* Will crash hard your box... */
+#undef FLUSH_TO_PPP /* Not sure about this one, let's play safe */
+#undef SECURE_DEVIRNET /* Bah... */
+
+/****************************** DEBUG ******************************/
+
+/*
+ * This set of flags enable and disable all the various warning,
+ * error and debug message of this driver.
+ * Each section can be enabled and disabled independantly
+ */
+/* In the PPP part */
+#define DEBUG_CTRL_TRACE 0 /* Control channel */
+#define DEBUG_CTRL_INFO 0 /* various info */
+#define DEBUG_CTRL_ERROR 1 /* problems */
+#define DEBUG_FS_TRACE 0 /* filesystem callbacks */
+#define DEBUG_FS_INFO 0 /* various info */
+#define DEBUG_FS_ERROR 1 /* problems */
+#define DEBUG_PPP_TRACE 0 /* PPP related functions */
+#define DEBUG_PPP_INFO 0 /* various info */
+#define DEBUG_PPP_ERROR 1 /* problems */
+#define DEBUG_MODULE_TRACE 0 /* module insertion/removal */
+#define DEBUG_MODULE_ERROR 1 /* problems */
+
+/* In the IrDA part */
+#define DEBUG_IRDA_SR_TRACE 0 /* IRDA subroutines */
+#define DEBUG_IRDA_SR_INFO 0 /* various info */
+#define DEBUG_IRDA_SR_ERROR 1 /* problems */
+#define DEBUG_IRDA_SOCK_TRACE 0 /* IRDA main socket functions */
+#define DEBUG_IRDA_SOCK_INFO 0 /* various info */
+#define DEBUG_IRDA_SOCK_ERROR 1 /* problems */
+#define DEBUG_IRDA_SERV_TRACE 0 /* The IrNET server */
+#define DEBUG_IRDA_SERV_INFO 0 /* various info */
+#define DEBUG_IRDA_SERV_ERROR 1 /* problems */
+#define DEBUG_IRDA_TCB_TRACE 0 /* IRDA IrTTP callbacks */
+#define DEBUG_IRDA_OCB_TRACE 0 /* IRDA other callbacks */
+#define DEBUG_IRDA_CB_INFO 0 /* various info */
+#define DEBUG_IRDA_CB_ERROR 1 /* problems */
+
+#define DEBUG_ASSERT 0 /* Verify all assertions */
+
+/*
+ * These are the macros we are using to actually print the debug
+ * statements. Don't look at it, it's ugly...
+ *
+ * One of the trick is that, as the DEBUG_XXX are constant, the
+ * compiler will optimise away the if() in all cases.
+ */
+/* All error messages (will show up in the normal logs) */
+#define DERROR(dbg, args...) \
+ {if(DEBUG_##dbg) \
+ printk(KERN_INFO "irnet: " __FUNCTION__ "(): " args);}
+
+/* Normal debug message (will show up in /var/log/debug) */
+#define DEBUG(dbg, args...) \
+ {if(DEBUG_##dbg) \
+ printk(KERN_DEBUG "irnet: " __FUNCTION__ "(): " args);}
+
+/* Entering a function (trace) */
+#define DENTER(dbg, args...) \
+ {if(DEBUG_##dbg) \
+ printk(KERN_DEBUG "irnet: ->" __FUNCTION__ args);}
+
+/* Entering and exiting a function in one go (trace) */
+#define DPASS(dbg, args...) \
+ {if(DEBUG_##dbg) \
+ printk(KERN_DEBUG "irnet: <>" __FUNCTION__ args);}
+
+/* Exiting a function (trace) */
+#define DEXIT(dbg, args...) \
+ {if(DEBUG_##dbg) \
+ printk(KERN_DEBUG "irnet: <-" __FUNCTION__ "()" args);}
+
+/* Exit a function with debug */
+#define DRETURN(ret, dbg, args...) \
+ {DEXIT(dbg, ": " args);\
+ return(ret); }
+
+/* Exit a function on failed condition */
+#define DABORT(cond, ret, dbg, args...) \
+ {if(cond) {\
+ DERROR(dbg, args);\
+ return(ret); }}
+
+/* Invalid assertion, print out an error and exit... */
+#define DASSERT(cond, ret, dbg, args...) \
+ {if((DEBUG_ASSERT) && !(cond)) {\
+ DERROR(dbg, "Invalid assertion: " args);\
+ return ret; }}
+
+/************************ CONSTANTS & MACROS ************************/
+
+/* Paranoia */
+#define IRNET_MAGIC 0xB00754
+
+/* Number of control events in the control channel buffer... */
+#define IRNET_MAX_EVENTS 8 /* Should be more than enough... */
+
+/****************************** TYPES ******************************/
+
+/*
+ * This is the main structure where we store all the data pertaining to
+ * one instance of irnet.
+ * Note : in irnet functions, a pointer this structure is usually called
+ * "ap" or "self". If the code is borrowed from the IrDA stack, it tend
+ * to be called "self", and if it is borrowed from the PPP driver it is
+ * "ap". Apart from that, it's exactly the same structure ;-)
+ */
+typedef struct irnet_socket
+{
+ /* ------------------- Instance management ------------------- */
+ /* We manage a linked list of IrNET socket instances */
+ irda_queue_t q; /* Must be first - for hasbin */
+ int magic; /* Paranoia */
+
+ /* --------------------- FileSystem part --------------------- */
+ /* "pppd" interact directly with us on a /dev/ file */
+ struct file * file; /* File descriptor of this instance */
+ /* TTY stuff - to keep "pppd" happy */
+ struct termios termios; /* Various tty flags */
+ /* Stuff for the control channel */
+ int event_index; /* Last read in the event log */
+
+ /* ------------------------- PPP part ------------------------- */
+ /* We interface directly to the ppp_generic driver in the kernel */
+ int ppp_open; /* registered with ppp_generic */
+ struct ppp_channel chan; /* Interface to generic ppp layer */
+
+ int mru; /* Max size of PPP payload */
+ u32 xaccm[8]; /* Asynchronous character map (just */
+ u32 raccm; /* to please pppd - dummy) */
+ unsigned int flags; /* PPP flags (compression, ...) */
+ unsigned int rbits; /* Unused receive flags ??? */
+
+ /* ------------------------ IrTTP part ------------------------ */
+ /* We create a pseudo "socket" over the IrDA tranport */
+ int ttp_open; /* Set when IrTTP is ready */
+ struct tsap_cb * tsap; /* IrTTP instance (the connection) */
+
+ char rname[NICKNAME_MAX_LEN + 1];
+ /* IrDA nickname of destination */
+ __u32 raddr; /* Requested peer IrDA address */
+ __u32 saddr; /* my local IrDA address */
+ __u32 daddr; /* actual peer IrDA address */
+ __u8 dtsap_sel; /* Remote TSAP selector */
+ __u8 stsap_sel; /* Local TSAP selector */
+
+ __u32 max_sdu_size_rx;/* Socket parameters used for IrTTP */
+ __u32 max_sdu_size_tx;
+ __u32 max_data_size;
+ __u8 max_header_size;
+ LOCAL_FLOW tx_flow; /* State of the Tx path in IrTTP */
+
+ /* ------------------- IrLMP and IrIAS part ------------------- */
+ /* Used for IrDA Discovery and socket name resolution */
+ __u32 ckey; /* IrLMP client handle */
+ __u16 mask; /* Hint bits mask (filter discov.)*/
+ int nslots; /* Number of slots for discovery */
+
+ struct iriap_cb * iriap; /* Used to query remote IAS */
+ wait_queue_head_t query_wait; /* Wait for the answer to a query */
+ struct ias_value * ias_result; /* Result of remote IAS query */
+ int errno; /* status of the IAS query */
+
+ /* ---------------------- Optional parts ---------------------- */
+#ifdef INITIAL_DISCOVERY
+ /* Stuff used to dump discovery log */
+ struct irda_device_info *discoveries; /* Copy of the discovery log */
+ int disco_index; /* Last read in the discovery log */
+ int disco_number; /* Size of the discovery log */
+#endif INITIAL_DISCOVERY
+
+} irnet_socket;
+
+/*
+ * This is the various event that we will generate on the control channel
+ */
+typedef enum irnet_event
+{
+ IRNET_DISCOVER, /* New IrNET node discovered */
+ IRNET_EXPIRE, /* IrNET node expired */
+ IRNET_CONNECT_TO, /* IrNET socket has connected to other node */
+ IRNET_CONNECT_FROM, /* Other node has connected to IrNET socket */
+ IRNET_REQUEST_FROM, /* Non satisfied connection request */
+ IRNET_NOANSWER_FROM, /* Failed connection request */
+ IRNET_BLOCKED_LINK, /* Link (IrLAP) is blocked for > 3s */
+ IRNET_DISCONNECT_FROM, /* IrNET socket has disconnected */
+ IRNET_DISCONNECT_TO /* Closing IrNET socket */
+} irnet_event;
+
+/*
+ * This is the storage for an event and its arguments
+ */
+typedef struct irnet_log
+{
+ irnet_event event;
+ int unit;
+ __u32 addr;
+ char name[NICKNAME_MAX_LEN + 1];
+} irnet_log;
+
+/*
+ * This is the storage for all events and related stuff...
+ */
+typedef struct irnet_ctrl_channel
+{
+ irnet_log log[IRNET_MAX_EVENTS]; /* Event log */
+ int index; /* Current index in log */
+ spinlock_t spinlock; /* Serialize access to the event log */
+ wait_queue_head_t rwait; /* processes blocked on read (or poll) */
+} irnet_ctrl_channel;
+
+/**************************** PROTOTYPES ****************************/
+/*
+ * Global functions of the IrNET module
+ * Note : we list here also functions called from one file to the other.
+ */
+
+/* -------------------------- IRDA PART -------------------------- */
+extern int
+ irda_irnet_create(irnet_socket *); /* Initialise a IrNET socket */
+extern int
+ irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */
+extern void
+ irda_irnet_destroy(irnet_socket *); /* Teardown a IrNET socket */
+extern int
+ irda_irnet_init(void); /* Initialise IrDA part of IrNET */
+extern void
+ irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */
+/* --------------------------- PPP PART --------------------------- */
+extern int
+ ppp_irnet_init(void); /* Initialise PPP part of IrNET */
+extern void
+ ppp_irnet_cleanup(void); /* Teardown PPP part of IrNET */
+/* ---------------------------- MODULE ---------------------------- */
+extern int
+ init_module(void); /* Initialise IrNET module */
+extern void
+ cleanup_module(void); /* Teardown IrNET module */
+
+/**************************** VARIABLES ****************************/
+
+/* Control channel stuff - allocated in irnet_irda.h */
+extern struct irnet_ctrl_channel irnet_events;
+
+#endif IRNET_H
diff --git a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c
new file mode 100644
index 000000000..ead7ef678
--- /dev/null
+++ b/net/irda/irnet/irnet_irda.c
@@ -0,0 +1,1481 @@
+/*
+ * IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ * Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file implement the IRDA interface of IrNET.
+ * Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly,
+ * and exchange frames with IrTTP.
+ */
+
+#include <linux/config.h>
+#include "irnet_irda.h" /* Private header */
+
+/************************* CONTROL CHANNEL *************************/
+/*
+ * When ppp is not active, /dev/irnet act as a control channel.
+ * Writting allow to set up the IrDA destination of the IrNET channel,
+ * and any application may be read events happening on IrNET...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Post an event to the control channel...
+ * Put the event in the log, and then wait all process blocked on read
+ * so they can read the log...
+ */
+static void
+irnet_post_event(irnet_socket * ap,
+ irnet_event event,
+ __u32 addr,
+ char * name)
+{
+ unsigned long flags; /* For spinlock */
+ int index; /* In the log */
+
+ DENTER(CTRL_TRACE, "(ap=0x%X, event=%d, addr=%08x, name=``%s'')\n",
+ (unsigned int) ap, event, addr, name);
+
+ /* Protect this section via spinlock.
+ * Note : as we are the only event producer, we only need to exclude
+ * ourself when touching the log, which is nice and easy.
+ */
+ spin_lock_irqsave(&irnet_events.spinlock, flags);
+
+ /* Copy the event in the log */
+ index = irnet_events.index;
+ irnet_events.log[index].event = event;
+ irnet_events.log[index].addr = addr;
+ /* Try to copy IrDA nickname */
+ if(name)
+ strcpy(irnet_events.log[index].name, name);
+ else
+ irnet_events.log[index].name[0] = '\0';
+ /* Try to get ppp unit number */
+ if((ap != (irnet_socket *) NULL) && (ap->ppp_open))
+ irnet_events.log[index].unit = ppp_unit_number(&ap->chan);
+ else
+ irnet_events.log[index].unit = -1;
+
+ /* Increment the index
+ * Note that we increment the index only after the event is written,
+ * to make sure that the readers don't get garbage... */
+ irnet_events.index = (index + 1) % IRNET_MAX_EVENTS;
+
+ DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index);
+
+ /* Spin lock end */
+ spin_unlock_irqrestore(&irnet_events.spinlock, flags);
+
+ /* Now : wake up everybody waiting for events... */
+ wake_up_interruptible_all(&irnet_events.rwait);
+
+ DEXIT(CTRL_TRACE, "\n");
+}
+
+/************************* IRDA SUBROUTINES *************************/
+/*
+ * These are a bunch of subroutines called from other functions
+ * down there, mostly common code or to improve readability...
+ *
+ * Note : we duplicate quite heavily some routines of af_irda.c,
+ * because our input structure (self) is quite different
+ * (struct irnet instead of struct irda_sock), which make sharing
+ * the same code impossible (at least, without templates).
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_open_tsap (self)
+ *
+ * Open local Transport Service Access Point (TSAP)
+ *
+ * Create a IrTTP instance for us and set all the IrTTP callbacks.
+ */
+static inline int
+irnet_open_tsap(irnet_socket * self)
+{
+ notify_t notify; /* Callback structure */
+
+ DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n");
+
+ /* Initialize IrTTP callbacks to be used by the IrDA stack */
+ irda_notify_init(&notify);
+ notify.connect_confirm = irnet_connect_confirm;
+ notify.connect_indication = irnet_connect_indication;
+ notify.disconnect_indication = irnet_disconnect_indication;
+ notify.data_indication = irnet_data_indication;
+ /*notify.udata_indication = NULL;*/
+ notify.flow_indication = irnet_flow_indication;
+ notify.status_indication = irnet_status_indication;
+ notify.instance = self;
+ strncpy(notify.name, IRNET_NOTIFY_NAME, NOTIFY_MAX_NAME);
+
+ /* Open an IrTTP instance */
+ self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
+ &notify);
+ DABORT(self->tsap == NULL, -ENOMEM,
+ IRDA_SR_ERROR, "Unable to allocate TSAP !\n");
+
+ /* Remember which TSAP selector we actually got */
+ self->stsap_sel = self->tsap->stsap_sel;
+
+ DEXIT(IRDA_SR_TRACE, " - tsap=0x%X, sel=0x%X\n",
+ (unsigned int) self->tsap, self->stsap_sel);
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_find_lsap_sel (self)
+ *
+ * Try to lookup LSAP selector in remote LM-IAS
+ *
+ * Basically, we start a IAP query, and then go to sleep. When the query
+ * return, irnet_getvalue_confirm will wake us up, and we can examine the
+ * result of the query...
+ * Note that in some case, the query fail even before we go to sleep,
+ * creating some races...
+ */
+static int
+irnet_find_lsap_sel(irnet_socket * self)
+{
+ DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ /* This should not happen */
+ DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n");
+
+ /* Create an IAP instance, will be closed in irnet_getvalue_confirm() */
+ self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+ irnet_getvalue_confirm);
+
+ /* Treat unexpected signals as disconnect */
+ self->errno = -EHOSTUNREACH;
+
+ /* Query remote LM-IAS */
+ iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr,
+ IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
+ /* Wait for answer (if not already failed) */
+ if(self->iriap != NULL)
+ interruptible_sleep_on(&self->query_wait);
+
+ /* Check what happened */
+ if(self->errno)
+ {
+ DEBUG(IRDA_SR_INFO, "IAS query failed! (%d)\n", self->errno);
+ /* Requested object/attribute doesn't exist */
+ if((self->errno == IAS_CLASS_UNKNOWN) ||
+ (self->errno == IAS_ATTRIB_UNKNOWN))
+ return (-EADDRNOTAVAIL);
+ else
+ return (-EHOSTUNREACH);
+ }
+
+ /* Get the remote TSAP selector */
+ switch(self->ias_result->type)
+ {
+ case IAS_INTEGER:
+ DEBUG(IRDA_SR_INFO, "result=%d\n", self->ias_result->t.integer);
+ if(self->ias_result->t.integer != -1)
+ self->dtsap_sel = self->ias_result->t.integer;
+ else
+ self->dtsap_sel = 0;
+ break;
+ default:
+ self->dtsap_sel = 0;
+ DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", self->ias_result->type);
+ break;
+ }
+ /* Cleanup */
+ if(self->ias_result)
+ irias_delete_value(self->ias_result);
+
+ DEXIT(IRDA_SR_TRACE, "\n");
+ if(self->dtsap_sel)
+ return 0;
+
+ return -EADDRNOTAVAIL;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_discover_daddr_and_lsap_sel (self)
+ *
+ * This try to find a device with the requested service.
+ *
+ * It basically look into the discovery log. For each address in the list,
+ * it queries the LM-IAS of the device to find if this device offer
+ * the requested service.
+ * If there is more than one node supporting the service, we complain
+ * to the user (it should move devices around).
+ * The, we set both the destination address and the lsap selector to point
+ * on the service on the unique device we have found.
+ *
+ * Note : this function fails if there is more than one device in range,
+ * because IrLMP doesn't disconnect the LAP when the last LSAP is closed.
+ * Moreover, we would need to wait the LAP disconnection...
+ */
+static inline int
+irnet_discover_daddr_and_lsap_sel(irnet_socket * self)
+{
+ struct irda_device_info *discoveries; /* Copy of the discovery log */
+ int number; /* Number of nodes in the log */
+ int i;
+ int err = -ENETUNREACH;
+ __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */
+ __u8 dtsap_sel = 0x0; /* TSAP associated with it */
+
+ DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ /* Ask lmp for the current discovery log
+ * Note : we have to use irlmp_get_discoveries(), as opposed
+ * to play with the cachelog directly, because while we are
+ * making our ias query, le log might change... */
+ discoveries = irlmp_get_discoveries(&number, self->mask);
+ /* Check if the we got some results */
+ if (discoveries == NULL)
+ DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
+
+ /*
+ * Now, check all discovered devices (if any), and connect
+ * client only about the services that the client is
+ * interested in...
+ */
+ for(i = 0; i < number; i++)
+ {
+ /* Try the address in the log */
+ self->daddr = discoveries[i].daddr;
+ self->saddr = 0x0;
+ DEBUG(IRDA_SR_INFO, "trying daddr = %08x\n", self->daddr);
+
+ /* Query remote LM-IAS for this service */
+ err = irnet_find_lsap_sel(self);
+ switch(err)
+ {
+ case 0:
+ /* We found the requested service */
+ if(daddr != DEV_ADDR_ANY)
+ {
+ DEBUG(IRDA_SR_INFO, "More than one device in range supports IrNET...\n");
+ }
+ else
+ {
+ /* First time we found that one, save it ! */
+ daddr = self->daddr;
+ dtsap_sel = self->dtsap_sel;
+ }
+ break;
+ case -EADDRNOTAVAIL:
+ /* Requested service simply doesn't exist on this node */
+ break;
+ default:
+ /* Something bad did happen :-( */
+ DERROR(IRDA_SR_ERROR, "unexpected IAS query failure\n");
+ self->daddr = DEV_ADDR_ANY;
+ kfree(discoveries);
+ return(-EHOSTUNREACH);
+ break;
+ }
+ }
+ /* Cleanup our copy of the discovery log */
+ kfree(discoveries);
+
+ /* Check out what we found */
+ if(daddr == DEV_ADDR_ANY)
+ {
+ self->daddr = DEV_ADDR_ANY;
+ DEXIT(IRDA_SR_INFO, "cannot discover IrNET in any device !!!\n");
+ return(-EADDRNOTAVAIL);
+ }
+
+ /* Revert back to discovered device & service */
+ self->daddr = daddr;
+ self->saddr = 0x0;
+ self->dtsap_sel = dtsap_sel;
+
+ DEBUG(IRDA_SR_INFO, "discovered IrNET at address %08x\n", self->daddr);
+ DEXIT(IRDA_SR_TRACE, "\n");
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_dname_to_daddr (self)
+ *
+ * Convert an IrDA nickname to a valid IrDA address
+ *
+ * It basically look into the discovery log until there is a match.
+ */
+static inline int
+irnet_dname_to_daddr(irnet_socket * self)
+{
+ struct irda_device_info *discoveries; /* Copy of the discovery log */
+ int number; /* Number of nodes in the log */
+ int i;
+
+ DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ /* Ask lmp for the current discovery log */
+ discoveries = irlmp_get_discoveries(&number, 0xffff);
+ /* Check if the we got some results */
+ if(discoveries == NULL)
+ DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
+
+ /*
+ * Now, check all discovered devices (if any), and connect
+ * client only about the services that the client is
+ * interested in...
+ */
+ for(i = 0; i < number; i++)
+ {
+ /* Does the name match ? */
+ if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN))
+ {
+ /* Yes !!! Get it.. */
+ self->daddr = discoveries[i].daddr;
+ DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n",
+ self->rname, self->daddr);
+ kfree(discoveries);
+ DEXIT(IRDA_SR_TRACE, "\n");
+ return 0;
+ }
+ }
+ /* No luck ! */
+ DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname);
+ kfree(discoveries);
+ return(-EADDRNOTAVAIL);
+}
+
+
+/************************* SOCKET ROUTINES *************************/
+/*
+ * This are the main operations on IrNET sockets, basically to create
+ * and destroy IrNET sockets. These are called from the PPP part...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Create a IrNET instance : just initialise some parameters...
+ */
+int
+irda_irnet_create(irnet_socket * self)
+{
+ DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ self->magic = IRNET_MAGIC; /* Paranoia */
+
+ init_waitqueue_head(&self->query_wait);
+
+ self->ttp_open = 0; /* Prevent higher layer from accessing IrTTP */
+ self->rname[0] = '\0'; /* May be set via control channel */
+ self->raddr = DEV_ADDR_ANY; /* May be set via control channel */
+ self->daddr = DEV_ADDR_ANY; /* Until we get connected */
+ self->saddr = 0x0; /* so IrLMP assign us any link */
+ self->max_sdu_size_rx = TTP_SAR_UNBOUND;
+
+ /* Register as a client with IrLMP */
+ self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
+#ifdef DISCOVERY_NOMASK
+ self->mask = 0xffff; /* For W2k compatibility */
+#else DISCOVERY_NOMASK
+ self->mask = irlmp_service_to_hint(S_LAN);
+#endif DISCOVERY_NOMASK
+ self->tx_flow = FLOW_START; /* Flow control from IrTTP */
+
+ DEXIT(IRDA_SOCK_TRACE, "\n");
+ return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Connect to the other side :
+ * o convert device name to an address
+ * o find the socket number (dlsap)
+ * o Establish the connection
+ */
+int
+irda_irnet_connect(irnet_socket * self)
+{
+ int err;
+
+ DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ /* Check if we have opened a local TSAP :
+ * If we have already opened a TSAP, it means that either we are already
+ * connected or in the process of doing so... */
+ if(self->tsap != NULL)
+ DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n");
+
+ /* Insert ourselves in the hashbin so that the IrNET server can find us.
+ * Notes : 4th arg is string of 32 char max and must be null terminated
+ * When 4th arg is used (string), 3rd arg isn't (int)
+ * Can't re-insert (MUST remove first) so check for that... */
+ if((irnet_server.running) && (self->q.q_next == NULL))
+ {
+ unsigned long flags;
+ spin_lock_irqsave(&irnet_server.spinlock, flags);
+ hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname);
+ spin_unlock_irqrestore(&irnet_server.spinlock, flags);
+ DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname);
+ }
+
+ /* If we don't have anything (no address, no name) */
+ if((self->raddr == DEV_ADDR_ANY) && (self->rname[0] == '\0'))
+ {
+ /* Try to find a suitable address */
+ if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0)
+ DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n");
+ }
+ else
+ {
+ /* If we have only the name (no address), try to get an address */
+ if(self->raddr == DEV_ADDR_ANY)
+ {
+ if((err = irnet_dname_to_daddr(self)) != 0)
+ DRETURN(err, IRDA_SOCK_INFO, "name-connect failed!\n");
+ }
+ else
+ /* Use the requested destination address */
+ self->daddr = self->raddr;
+
+ /* Query remote LM-IAS to find LSAP selector */
+ if((err = irnet_find_lsap_sel(self)) != 0)
+ DRETURN(err, IRDA_SOCK_INFO, "connect failed!\n");
+ }
+ DEBUG(IRDA_SOCK_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
+ self->daddr, self->dtsap_sel);
+
+ /* Open a local TSAP (an IrTTP instance) */
+ err = irnet_open_tsap(self);
+ DABORT(err != 0, err, IRDA_SOCK_ERROR, "connect aborted!\n");
+
+ /* Connect to remote device */
+ err = irttp_connect_request(self->tsap, self->dtsap_sel,
+ self->saddr, self->daddr, NULL,
+ self->max_sdu_size_rx, NULL);
+ DABORT(err != 0, err, IRDA_SOCK_ERROR, "connect aborted!\n");
+
+ DEXIT(IRDA_SOCK_TRACE, "\n");
+ return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_irnet_destroy(self)
+ *
+ * Destroy irnet instance
+ *
+ */
+void
+irda_irnet_destroy(irnet_socket * self)
+{
+ DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) self);
+ if(self == NULL)
+ return;
+
+ /* Remove ourselves from hashbin (if we are queued in hashbin)
+ * Note : `irnet_server.running' protect us from calls in hashbin_delete() */
+ if((irnet_server.running) && (self->q.q_next != NULL))
+ {
+ struct irnet_socket * entry;
+ unsigned long flags;
+ DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n");
+ spin_lock_irqsave(&irnet_server.spinlock, flags);
+ entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self);
+ self->q.q_next = NULL;
+ spin_unlock_irqrestore(&irnet_server.spinlock, flags);
+ DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n");
+ }
+
+ /* Unregister with IrLMP */
+ irlmp_unregister_client(self->ckey);
+
+ /* Unregister with LM-IAS */
+ if(self->iriap)
+ iriap_close(self->iriap);
+
+ /* Prevent higher layer from accessing IrTTP */
+ self->ttp_open = 0;
+
+ /* Close our IrTTP connection */
+ if(self->tsap)
+ {
+ DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n");
+ irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
+ irttp_close_tsap(self->tsap);
+ self->tsap = NULL;
+ /* Note : as the disconnect comes from ppp_generic, the unit number
+ * doesn't exist anymore when we post the event, so we need to pass
+ * NULL as the first arg... */
+ irnet_post_event(NULL, IRNET_DISCONNECT_TO, self->daddr, self->rname);
+ }
+ self->stsap_sel = 0;
+
+ DEXIT(IRDA_SOCK_TRACE, "\n");
+ return;
+}
+
+
+/************************** SERVER SOCKET **************************/
+/*
+ * The IrNET service is composed of one server socket and a variable
+ * number of regular IrNET sockets. The server socket is supposed to
+ * handle incomming connections and redirect them to one IrNET sockets.
+ * It's a superset of the regular IrNET socket, but has a very distinct
+ * behaviour...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_daddr_to_dname (self)
+ *
+ * Convert an IrDA address to a IrDA nickname
+ *
+ * It basically look into the discovery log until there is a match.
+ */
+static inline int
+irnet_daddr_to_dname(irnet_socket * self)
+{
+ struct irda_device_info *discoveries; /* Copy of the discovery log */
+ int number; /* Number of nodes in the log */
+ int i;
+
+ DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ /* Ask lmp for the current discovery log */
+ discoveries = irlmp_get_discoveries(&number, 0xffff);
+ /* Check if the we got some results */
+ if (discoveries == NULL)
+ DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n");
+
+ /* Now, check all discovered devices (if any) */
+ for(i = 0; i < number; i++)
+ {
+ /* Does the name match ? */
+ if(discoveries[i].daddr == self->daddr)
+ {
+ /* Yes !!! Get it.. */
+ strncpy(self->rname, discoveries[i].info, NICKNAME_MAX_LEN);
+ self->rname[NICKNAME_MAX_LEN + 1] = '\0';
+ DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n",
+ self->daddr, self->rname);
+ kfree(discoveries);
+ DEXIT(IRDA_SERV_TRACE, "\n");
+ return 0;
+ }
+ }
+ /* No luck ! */
+ DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr);
+ kfree(discoveries);
+ return(-EADDRNOTAVAIL);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_find_socket (self)
+ *
+ * Find the correct IrNET socket
+ *
+ * Look into the list of IrNET sockets and finds one with the right
+ * properties...
+ */
+static inline irnet_socket *
+irnet_find_socket(irnet_socket * self)
+{
+ irnet_socket * new = (irnet_socket *) NULL;
+ unsigned long flags;
+ int err;
+
+ DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ /* Get the address of the requester */
+ self->daddr = irttp_get_daddr(self->tsap);
+
+ /* Try to get the IrDA nickname of the requester */
+ err = irnet_daddr_to_dname(self);
+
+ /* Protect access to the instance list */
+ spin_lock_irqsave(&irnet_server.spinlock, flags);
+
+ /* So now, try to get an socket having specifically
+ * requested that nickname */
+ if(err == 0)
+ {
+ new = (irnet_socket *) hashbin_find(irnet_server.list,
+ 0, self->rname);
+ if(new)
+ DEBUG(IRDA_SERV_INFO, "Socket 0x%X matches rname ``%s''.\n",
+ (unsigned int) new, new->rname);
+ }
+
+ /* If no name matches, try to find an socket by the destination address */
+ /* It can be either the requested destination address (set via the
+ * control channel), or the current destination address if the
+ * socket is in the middle of a connection request */
+ if(new == (irnet_socket *) NULL)
+ {
+ new = (irnet_socket *) hashbin_get_first(irnet_server.list);
+ while(new !=(irnet_socket *) NULL)
+ {
+ /* Does it have the same address ? */
+ if((new->raddr == self->daddr) || (new->daddr == self->daddr))
+ {
+ /* Yes !!! Get it.. */
+ DEBUG(IRDA_SERV_INFO, "Socket 0x%X matches daddr %#08x.\n",
+ (unsigned int) new, self->daddr);
+ break;
+ }
+ new = (irnet_socket *) hashbin_get_next(irnet_server.list);
+ }
+ }
+
+ /* If we don't have any socket, get the first unconnected socket */
+ if(new == (irnet_socket *) NULL)
+ {
+ new = (irnet_socket *) hashbin_get_first(irnet_server.list);
+ while(new !=(irnet_socket *) NULL)
+ {
+ /* Is it available ? */
+ if(!(new->ttp_open) && (new->raddr == DEV_ADDR_ANY) &&
+ (new->rname[0] == '\0') && (new->ppp_open))
+ {
+ /* Yes !!! Get it.. */
+ DEBUG(IRDA_SERV_INFO, "Socket 0x%X is free.\n",
+ (unsigned int) new);
+ break;
+ }
+ new = (irnet_socket *) hashbin_get_next(irnet_server.list);
+ }
+ }
+
+ /* Spin lock end */
+ spin_unlock_irqrestore(&irnet_server.spinlock, flags);
+
+ DEXIT(IRDA_SERV_TRACE, " - new = 0x%X\n", (unsigned int) new);
+ return new;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_connect_socket (self)
+ *
+ * Connect an incomming connection to the socket
+ *
+ */
+static inline int
+irnet_connect_socket(irnet_socket * self,
+ irnet_socket * new,
+ struct qos_info * qos,
+ __u32 max_sdu_size,
+ __u8 max_header_size)
+{
+ DENTER(IRDA_SERV_TRACE, "(self=0x%X, new=0x%X)\n",
+ (unsigned int) self, (unsigned int) new);
+
+ /* Now attach up the new socket */
+ new->tsap = irttp_dup(self->tsap, new);
+ DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n");
+
+ /* Set up all the relevant parameters on the new socket */
+ new->stsap_sel = new->tsap->stsap_sel;
+ new->dtsap_sel = new->tsap->dtsap_sel;
+ new->saddr = irttp_get_saddr(new->tsap);
+ new->daddr = irttp_get_daddr(new->tsap);
+
+ new->max_header_size = max_header_size;
+ new->max_sdu_size_tx = max_sdu_size;
+ new->max_data_size = max_sdu_size;
+#ifdef STREAM_COMPAT
+ /* If we want to receive "stream sockets" */
+ if(max_sdu_size == 0)
+ new->max_data_size = irttp_get_max_seg_size(new->tsap);
+#endif STREAM_COMPAT
+
+ /* 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;
+
+ /* Send a connection response on the new socket */
+ irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL);
+
+ /* Allow PPP to send its junk over the new socket... */
+ new->ttp_open = 1;
+#ifdef CONNECT_INDIC_KICK
+ /* As currently we don't packets in ppp_irnet_send(), this is not needed...
+ * Also, not doing it give IrDA a chance to finish the setup properly
+ * before beeing swamped with packets... */
+ ppp_output_wakeup(&new->chan);
+#endif CONNECT_INDIC_KICK
+
+ /* Notify the control channel */
+ irnet_post_event(new, IRNET_CONNECT_FROM, new->daddr, self->rname);
+
+ DEXIT(IRDA_SERV_TRACE, "\n");
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_disconnect_server (self)
+ *
+ * Cleanup the server socket when the incomming connection abort
+ *
+ */
+static inline void
+irnet_disconnect_server(irnet_socket * self,
+ struct sk_buff *skb)
+{
+ DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ /* Put the received packet in the black hole */
+ kfree_skb(skb);
+
+#ifdef FAIL_SEND_DISCONNECT
+ /* Tell the other party we don't want to be connected */
+ /* Hum... Is it the right thing to do ? And do we need to send
+ * a connect response before ? It looks ok without this... */
+ irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
+#endif FAIL_SEND_DISCONNECT
+
+ /* Clean up the server to keep it in listen state */
+ self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY;
+ self->tsap->lsap->lsap_state = LSAP_DISCONNECTED;
+
+ /* Notify the control channel */
+ irnet_post_event(NULL, IRNET_REQUEST_FROM, self->daddr, self->rname);
+
+ DEXIT(IRDA_SERV_TRACE, "\n");
+ return;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_setup_server (self)
+ *
+ * Create a IrTTP server and set it up...
+ *
+ * Register the IrLAN hint bit, create a IrTTP instance for us,
+ * set all the IrTTP callbacks and create an IrIAS entry...
+ */
+static inline int
+irnet_setup_server(void)
+{
+ __u16 hints;
+
+ DENTER(IRDA_SERV_TRACE, "()\n");
+
+ /* Initialise the regular socket part of the server */
+ irda_irnet_create(&irnet_server.s);
+
+ /* Open a local TSAP (an IrTTP instance) for the server */
+ irnet_open_tsap(&irnet_server.s);
+
+ /* PPP part setup */
+ irnet_server.s.ppp_open = 0;
+ irnet_server.s.chan.private = NULL;
+ irnet_server.s.file = NULL;
+
+ /* Get the hint bit corresponding to IrLAN */
+ /* Note : we overload the IrLAN hint bit. As it is only a "hint", and as
+ * we provide roughly the same functionality as IrLAN, this is ok.
+ * In fact, the situation is similar as JetSend overloading the Obex hint
+ */
+ hints = irlmp_service_to_hint(S_LAN);
+
+#ifdef ADVERTISE_HINT
+ /* Register with IrLMP as a service (advertise our hint bit) */
+ irnet_server.skey = irlmp_register_service(hints);
+#endif ADVERTISE_HINT
+
+ /* Register with LM-IAS (so that people can connect to us) */
+ irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies);
+ irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE,
+ irnet_server.s.stsap_sel, IAS_KERNEL_ATTR);
+ irias_insert_object(irnet_server.ias_obj);
+
+#ifdef DISCOVERY_EVENTS
+ /* Tell IrLMP we want to be notified of newly discovered nodes */
+ irlmp_update_client(irnet_server.s.ckey, hints,
+ irnet_discovery_indication, irnet_expiry_indication,
+ (void *) &irnet_server.s);
+#endif
+
+ DEXIT(IRDA_SERV_TRACE, " - self=0x%X\n", (unsigned int) &irnet_server.s);
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_destroy_server (self)
+ *
+ * Destroy the IrTTP server...
+ *
+ * Reverse of the previous function...
+ */
+static inline void
+irnet_destroy_server(void)
+{
+ DENTER(IRDA_SERV_TRACE, "()\n");
+
+#ifdef ADVERTISE_HINT
+ /* Unregister with IrLMP */
+ irlmp_unregister_service(irnet_server.skey);
+#endif ADVERTISE_HINT
+
+ /* Unregister with LM-IAS */
+ if(irnet_server.ias_obj)
+ irias_delete_object(irnet_server.ias_obj);
+
+ /* Cleanup the socket part */
+ irda_irnet_destroy(&irnet_server.s);
+
+ DEXIT(IRDA_SERV_TRACE, "\n");
+ return;
+}
+
+
+/************************ IRDA-TTP CALLBACKS ************************/
+/*
+ * When we create a IrTTP instance, we pass to it a set of callbacks
+ * that IrTTP will call in case of various events.
+ * We take care of those events here.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_data_indication (instance, sap, skb)
+ *
+ * Received some data from TinyTP. Just queue it on the receive queue
+ *
+ */
+static int
+irnet_data_indication(void * instance,
+ void * sap,
+ struct sk_buff *skb)
+{
+ irnet_socket * ap = (irnet_socket *) instance;
+ unsigned char * p;
+ int code = 0;
+
+ DENTER(IRDA_TCB_TRACE, "(self/ap=0x%X, skb=0x%X)\n",
+ (unsigned int) ap,(unsigned int) skb);
+ DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n");
+
+ /* Check is ppp is ready to receive our packet */
+ if(!ap->ppp_open)
+ {
+ DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n");
+ /* When we return error, TTP will need to requeue the skb and
+ * will stop the sender. IrTTP will stall until we send it a
+ * flow control request... */
+ return -ENOMEM;
+ }
+
+ /* strip address/control field if present */
+ p = skb->data;
+ if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI))
+ {
+ /* chop off address/control */
+ if(skb->len < 3)
+ goto err_exit;
+ p = skb_pull(skb, 2);
+ }
+
+ /* decompress protocol field if compressed */
+ if(p[0] & 1)
+ {
+ /* protocol is compressed */
+ skb_push(skb, 1)[0] = 0;
+ }
+ else
+ if(skb->len < 2)
+ goto err_exit;
+
+ /* pass to generic ppp layer */
+ /* Note : how do I know if ppp can accept or not the packet ? This is
+ * essential if I want to manage flow control smoothly... */
+ ppp_input(&ap->chan, skb);
+
+ DEXIT(IRDA_TCB_TRACE, "\n");
+ return 0;
+
+ err_exit:
+ DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n");
+ kfree_skb(skb);
+ ppp_input_error(&ap->chan, code);
+ return 0; /* Don't return an error code, only for flow control... */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_disconnect_indication (instance, sap, reason, skb)
+ *
+ * Connection has been closed. Chech reason to find out why
+ *
+ * Note : there are many cases where we come here :
+ * o attempted to connect, timeout
+ * o connected, link is broken, LAP has timeout
+ * o connected, other side close the link
+ * o connection request on the server no handled
+ */
+static void
+irnet_disconnect_indication(void * instance,
+ void * sap,
+ LM_REASON reason,
+ struct sk_buff *skb)
+{
+ irnet_socket * self = (irnet_socket *) instance;
+
+ DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
+ DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
+
+ /* If we were active, notify the control channel */
+ if(self->ttp_open)
+ irnet_post_event(self, IRNET_DISCONNECT_FROM, self->daddr, self->rname);
+ else
+ /* If we were trying to connect, notify the control channel */
+ if((self->tsap) && (self != &irnet_server.s))
+ irnet_post_event(self, IRNET_NOANSWER_FROM, self->daddr, self->rname);
+
+ /* Prevent higher layer from accessing IrTTP */
+ self->ttp_open = 0;
+
+ /* Close our IrTTP connection */
+ if((self->tsap) && (self != &irnet_server.s))
+ {
+ DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n");
+ irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
+ irttp_close_tsap(self->tsap);
+ self->tsap = NULL;
+
+ /* Flush (drain) ppp_generic Tx queue (most often we have blocked it) */
+ if(self->ppp_open)
+ ppp_output_wakeup(&self->chan);
+ }
+ /* Cleanup the socket in case we want to reconnect */
+ self->stsap_sel = 0;
+ self->daddr = DEV_ADDR_ANY;
+ self->tx_flow = FLOW_START;
+
+ /* Note : what should we say to ppp ?
+ * It seem the ppp_generic and pppd are happy that way and will eventually
+ * timeout gracefully, so don't bother them... */
+
+ DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb)
+ *
+ * Connections has been confirmed by the remote device
+ *
+ */
+static void
+irnet_connect_confirm(void * instance,
+ void * sap,
+ struct qos_info *qos,
+ __u32 max_sdu_size,
+ __u8 max_header_size,
+ struct sk_buff *skb)
+{
+ irnet_socket * self = (irnet_socket *) instance;
+
+ DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
+
+ /* How much header space do we need to reserve */
+ self->max_header_size = max_header_size;
+
+ /* IrTTP max SDU size in transmit direction */
+ self->max_sdu_size_tx = max_sdu_size;
+ self->max_data_size = max_sdu_size;
+#ifdef STREAM_COMPAT
+ if(max_sdu_size == 0)
+ self->max_data_size = irttp_get_max_seg_size(self->tsap);
+#endif STREAM_COMPAT
+
+ /* At this point, IrLMP has assigned our source address */
+ self->saddr = irttp_get_saddr(self->tsap);
+
+ /* Allow higher layer to access IrTTP */
+ self->ttp_open = 1;
+ /* Give a kick in the ass of ppp_generic so that he sends us some data */
+ ppp_output_wakeup(&self->chan);
+
+ /* Check size of received packet */
+ if(skb->len > 0)
+ {
+#ifdef PASS_CONNECT_PACKETS
+ DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
+ /* Try to pass it to PPP */
+ irnet_data_indication(instance, sap, skb);
+#else PASS_CONNECT_PACKETS
+ DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
+ kfree_skb(skb); /* Note : will be optimised with other kfree... */
+#endif PASS_CONNECT_PACKETS
+ }
+ else
+ kfree_skb(skb);
+
+ /* Notify the control channel */
+ irnet_post_event(self, IRNET_CONNECT_TO, self->daddr, self->rname);
+
+ DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_flow_indication (instance, sap, flow)
+ *
+ * Used by TinyTP to tell us if it can accept more data or not
+ *
+ */
+static void
+irnet_flow_indication(void * instance,
+ void * sap,
+ LOCAL_FLOW flow)
+{
+ irnet_socket * self = (irnet_socket *) instance;
+
+ DENTER(IRDA_TCB_TRACE, "(self=0x%X, flow=%d)\n", (unsigned int) self, flow);
+
+ /* Update our state */
+ self->tx_flow = flow;
+
+ /* Check what IrTTP want us to do... */
+ switch(flow)
+ {
+ case FLOW_START:
+ DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n");
+ ppp_output_wakeup(&self->chan);
+ break;
+ case FLOW_STOP:
+ DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n");
+ break;
+ default:
+ DEBUG(IRDA_CB_INFO, "Unknown flow command!\n");
+ break;
+ }
+
+ DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_status_indication (instance, sap, reason, skb)
+ *
+ * Link (IrLAP) status report.
+ *
+ */
+static void
+irnet_status_indication(void * instance,
+ LINK_STATUS link,
+ LOCK_STATUS lock)
+{
+ irnet_socket * self = (irnet_socket *) instance;
+
+ DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
+ DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
+
+ /* We can only get this event if we are connected */
+ switch(link)
+ {
+ case STATUS_NO_ACTIVITY:
+ irnet_post_event(self, IRNET_BLOCKED_LINK, self->daddr, self->rname);
+ break;
+ default:
+ DEBUG(IRDA_CB_INFO, "Unknown status...\n");
+ }
+
+ DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata)
+ *
+ * Incomming connection
+ *
+ * In theory, this function is called only on the server socket.
+ * Some other node is attempting to connect to the IrNET service, and has
+ * sent a connection request on our server socket.
+ * We just redirect the connection to the relevant IrNET socket.
+ *
+ * Note : we also make sure that between 2 irnet nodes, there can
+ * exist only one irnet connection.
+ */
+static void
+irnet_connect_indication(void * instance,
+ void * sap,
+ struct qos_info *qos,
+ __u32 max_sdu_size,
+ __u8 max_header_size,
+ struct sk_buff *skb)
+{
+ irnet_socket * self = &irnet_server.s;
+ irnet_socket * new = (irnet_socket *) NULL;
+
+ DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
+ DASSERT(instance == &irnet_server, , IRDA_CB_ERROR,
+ "Invalid instance (0x%X) !!!\n", (unsigned int) instance);
+ DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n");
+
+ /* Try to find the most appropriate IrNET socket */
+ new = irnet_find_socket(self);
+
+ /* After all this hard work, do we have an socket ? */
+ if(new == (irnet_socket *) NULL)
+ {
+ DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n");
+ irnet_disconnect_server(self, skb);
+ return;
+ }
+
+ /* Is the socket already busy ? */
+ if(new->ttp_open)
+ {
+ DEXIT(IRDA_CB_INFO, ": Socket already connected.\n");
+ irnet_disconnect_server(self, skb);
+ return;
+ }
+
+ /* Socket connecting */
+ if(new->tsap != NULL)
+ {
+ /* The socket has sent a IrTTP connection request and is waiting for
+ * a connection response (that may never come).
+ * Now, the pain is that the socket has open a tsap and is waiting on it,
+ * while the other end is trying to connect to it on another tsap.
+ * Argh ! We will deal with that later...
+ */
+ DERROR(IRDA_CB_ERROR, "Socket already connecting. Ouch !\n");
+#ifdef ALLOW_SIMULT_CONNECT
+ /* Close the connection the new socket was attempting.
+ * WARNING : This need more testing ! */
+ irttp_close_tsap(new->tsap);
+ /* Note : no return, fall through... */
+#else ALLOW_SIMULT_CONNECT
+ irnet_disconnect_server(self, skb);
+ return;
+#endif ALLOW_SIMULT_CONNECT
+ }
+
+ /* So : at this point, we have a socket, and it is idle. Good ! */
+ irnet_connect_socket(self, new, qos, max_sdu_size, max_header_size);
+
+ /* Check size of received packet */
+ if(skb->len > 0)
+ {
+#ifdef PASS_CONNECT_PACKETS
+ DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
+ /* Try to pass it to PPP */
+ irnet_data_indication(new, new->tsap, skb);
+#else PASS_CONNECT_PACKETS
+ DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
+ kfree_skb(skb); /* Note : will be optimised with other kfree... */
+#endif PASS_CONNECT_PACKETS
+ }
+ else
+ kfree_skb(skb);
+
+ DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+
+/********************** IRDA-IAS/LMP CALLBACKS **********************/
+/*
+ * These are the callbacks called by other layers of the IrDA stack,
+ * mainly LMP for discovery and IAS for name queries.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_getvalue_confirm (obj_id, value, priv)
+ *
+ * Got answer from remote LM-IAS, just pass object to requester...
+ *
+ */
+static void
+irnet_getvalue_confirm(int result,
+ __u16 obj_id,
+ struct ias_value *value,
+ void * priv)
+{
+ irnet_socket * self = (irnet_socket *) priv;
+
+ DENTER(IRDA_OCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
+ DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
+
+ /* We probably don't need to make any more queries */
+ iriap_close(self->iriap);
+ self->iriap = NULL;
+
+ /* Check if request succeeded */
+ if(result != IAS_SUCCESS)
+ {
+ DEBUG(IRDA_CB_INFO, "IAS query failed! (%d)\n", result);
+ self->errno = result; /* We really need it later */
+ }
+ else
+ {
+ /* Pass the object to the caller (so the caller must delete it) */
+ self->ias_result = value;
+ self->errno = 0;
+ }
+
+ /* Wake up any processes waiting for result */
+ wake_up_interruptible(&self->query_wait);
+
+ DEXIT(IRDA_OCB_TRACE, "\n");
+}
+
+#ifdef DISCOVERY_EVENTS
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_discovery_indication (discovery)
+ *
+ * Got a discovery indication from IrLMP, post an event
+ *
+ * Note : IrLMP take care of matching the hint mask for us, we only
+ * check if it is a "new" node...
+ *
+ * As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET
+ * nodes, so it's only at connection time that we will know if the
+ * node support IrNET, IrLAN or both. The other solution is to check
+ * in IAS the PNP ids and service name.
+ * Note : even if a node support IrNET (or IrLAN), it's no guarantee
+ * that we will be able to connect to it, the node might already be
+ * busy...
+ *
+ * One last thing : in some case, this function will trigger duplicate
+ * discovery events. On the other hand, we should catch all
+ * discoveries properly (i.e. not miss one). Filtering duplicate here
+ * is to messy, so we leave that to user space...
+ */
+static void
+irnet_discovery_indication(discovery_t *discovery,
+ void * priv)
+{
+ irnet_socket * self = &irnet_server.s;
+
+ DENTER(IRDA_OCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
+ DASSERT(priv == &irnet_server, , IRDA_CB_ERROR,
+ "Invalid instance (0x%X) !!!\n", (unsigned int) priv);
+
+ /* Check if node is discovered is a new one or an old one.
+ * We check when how long ago this node was discovered, with a
+ * coarse timeout (we may miss some discovery events or be delayed).
+ */
+ if((jiffies - discovery->first_timestamp) >= (sysctl_discovery_timeout * HZ))
+ {
+ return; /* Too old, not interesting -> goodbye */
+ }
+
+ DEBUG(IRDA_CB_INFO, "Discovered new IrNET/IrLAN node %s...\n",
+ discovery->nickname);
+
+ /* Notify the control channel */
+ irnet_post_event(NULL, IRNET_DISCOVER, discovery->daddr,
+ discovery->nickname);
+
+ DEXIT(IRDA_OCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_expiry_indication (expiry)
+ *
+ * Got a expiry indication from IrLMP, post an event
+ *
+ * Note : IrLMP take care of matching the hint mask for us, we only
+ * check if it is a "new" node...
+ */
+static void
+irnet_expiry_indication(discovery_t * expiry,
+ void * priv)
+{
+ irnet_socket * self = &irnet_server.s;
+
+ DENTER(IRDA_OCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
+ DASSERT(priv == &irnet_server, , IRDA_CB_ERROR,
+ "Invalid instance (0x%X) !!!\n", (unsigned int) priv);
+
+ DEBUG(IRDA_CB_INFO, "IrNET/IrLAN node %s expired...\n",
+ expiry->nickname);
+
+ /* Notify the control channel */
+ irnet_post_event(NULL, IRNET_EXPIRE, expiry->daddr,
+ expiry->nickname);
+
+ DEXIT(IRDA_OCB_TRACE, "\n");
+}
+#endif DISCOVERY_EVENTS
+
+
+/*********************** PROC ENTRY CALLBACKS ***********************/
+/*
+ * We create a instance in the /proc filesystem, and here we take care
+ * of that...
+ */
+
+#ifdef CONFIG_PROC_FS
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_proc_read (buf, start, offset, len, unused)
+ *
+ * Give some info to the /proc file system
+ */
+static int
+irnet_proc_read(char * buf,
+ char ** start,
+ off_t offset,
+ int len)
+{
+ irnet_socket * self;
+ char * state;
+ unsigned long flags;
+ int i = 0;
+
+ len = 0;
+
+ /* Get the IrNET server information... */
+ len += sprintf(buf+len, "IrNET server - ");
+ len += sprintf(buf+len, "IrDA state: %s, ",
+ (irnet_server.running ? "running" : "dead"));
+ len += sprintf(buf+len, "stsap_sel: %02x, ", irnet_server.s.stsap_sel);
+ len += sprintf(buf+len, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel);
+
+ /* Do we need to continue ? */
+ if(!irnet_server.running)
+ return len;
+
+ /* Protect access to the instance list */
+ spin_lock_irqsave(&irnet_server.spinlock, flags);
+
+ /* Get the sockets one by one... */
+ self = (irnet_socket *) hashbin_get_first(irnet_server.list);
+ while(self != NULL)
+ {
+ /* Start printing info about the socket. */
+ len += sprintf(buf+len, "\nIrNET socket %d - ", i++);
+
+ /* First, get the requested configuration */
+ len += sprintf(buf+len, "Requested IrDA name: \"%s\", ", self->rname);
+ len += sprintf(buf+len, "addr: %08x\n", self->raddr);
+
+ /* Second, get all the PPP info */
+ len += sprintf(buf+len, " PPP state: %s",
+ (self->ppp_open ? "registered" : "unregistered"));
+ if(self->ppp_open)
+ {
+ len += sprintf(buf+len, ", unit: ppp%d",
+ ppp_unit_number(&self->chan));
+ len += sprintf(buf+len, ", channel: %d",
+ ppp_channel_index(&self->chan));
+ len += sprintf(buf+len, ", mru: %d",
+ self->mru);
+ /* Maybe add self->flags ? Later... */
+ }
+
+ /* Then, get all the IrDA specific info... */
+ if(self->ttp_open)
+ state = "connected";
+ else
+ if(self->tsap != NULL)
+ state = "connecting";
+ else
+ state = "idle";
+ len += sprintf(buf+len, "\n IrDA state: %s, ", state);
+ len += sprintf(buf+len, "daddr: %08x, ", self->daddr);
+ len += sprintf(buf+len, "stsap_sel: %02x, ", self->stsap_sel);
+ len += sprintf(buf+len, "dtsap_sel: %02x\n", self->dtsap_sel);
+
+ /* Next socket, please... */
+ self = (irnet_socket *) hashbin_get_next(irnet_server.list);
+ }
+
+ /* Spin lock end */
+ spin_unlock_irqrestore(&irnet_server.spinlock, flags);
+
+ return len;
+}
+#endif /* PROC_FS */
+
+
+/********************** CONFIGURATION/CLEANUP **********************/
+/*
+ * Initialisation and teardown of the IrDA part, called at module
+ * insertion and removal...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare the IrNET layer for operation...
+ */
+int
+irda_irnet_init(void)
+{
+ int err = 0;
+
+ DENTER(MODULE_TRACE, "()\n");
+
+ /* Pure paranoia - should be redundant */
+ memset(&irnet_server, 0, sizeof(struct irnet_root));
+
+ /* Setup start of irnet instance list */
+ irnet_server.list = hashbin_new(HB_LOCAL);
+ DABORT(irnet_server.list == NULL, -ENOMEM,
+ MODULE_ERROR, "Can't allocate hashbin!\n");
+ /* Init spinlock for instance list */
+ spin_lock_init(&irnet_server.spinlock);
+
+ /* Initialise control channel */
+ init_waitqueue_head(&irnet_events.rwait);
+ irnet_events.index = 0;
+ /* Init spinlock for event logging */
+ spin_lock_init(&irnet_events.spinlock);
+
+#ifdef CONFIG_PROC_FS
+ /* Add a /proc file for irnet infos */
+ create_proc_info_entry("irnet", 0, proc_irda, irnet_proc_read);
+#endif /* CONFIG_PROC_FS */
+
+ /* Setup the IrNET server */
+ err = irnet_setup_server();
+
+ if(!err)
+ /* We are no longer functional... */
+ irnet_server.running = 1;
+
+ DEXIT(MODULE_TRACE, "\n");
+ return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Cleanup at exit...
+ */
+void
+irda_irnet_cleanup(void)
+{
+ DENTER(MODULE_TRACE, "()\n");
+
+ /* We are no longer there... */
+ irnet_server.running = 0;
+
+#ifdef CONFIG_PROC_FS
+ /* Remove our /proc file */
+ remove_proc_entry("irnet", proc_irda);
+#endif CONFIG_PROC_FS
+
+ /* Remove our IrNET server from existence */
+ irnet_destroy_server();
+
+ /* Remove all instances of IrNET socket still present */
+ hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy);
+
+ DEXIT(MODULE_TRACE, "\n");
+}
diff --git a/net/irda/irnet/irnet_irda.h b/net/irda/irnet/irnet_irda.h
new file mode 100644
index 000000000..0e5e9a36f
--- /dev/null
+++ b/net/irda/irnet/irnet_irda.h
@@ -0,0 +1,169 @@
+/*
+ * IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ * Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file contains all definitions and declarations necessary for the
+ * IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co).
+ * This file is a private header, so other modules don't want to know
+ * what's in there...
+ */
+
+#ifndef IRNET_IRDA_H
+#define IRNET_IRDA_H
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/config.h>
+#include "irnet.h" /* Module global include */
+
+/************************ CONSTANTS & MACROS ************************/
+
+/*
+ * Name of the service (socket name) used by IrNET
+ */
+/* IAS object name (or part of it) */
+#define IRNET_SERVICE_NAME "IrNetv1"
+/* IAS attribute */
+#define IRNET_IAS_VALUE "IrDA:TinyTP:LsapSel"
+/* LMP notify name for client (only for /proc/net/irda/irlmp) */
+#define IRNET_NOTIFY_NAME "IrNET socket"
+/* LMP notify name for server (only for /proc/net/irda/irlmp) */
+#define IRNET_NOTIFY_NAME_SERV "IrNET server"
+
+/****************************** TYPES ******************************/
+
+/*
+ * This is the main structure where we store all the data pertaining to
+ * the IrNET server (listen for connection requests) and the root
+ * of the IrNET socket list
+ */
+typedef struct irnet_root
+{
+ irnet_socket s; /* To pretend we are a client... */
+
+ /* Generic stuff */
+ int magic; /* Paranoia */
+ int running; /* Are we operational ? */
+
+ /* Link list of all IrNET instances opened */
+ hashbin_t * list;
+ spinlock_t spinlock; /* Serialize access to the list */
+ /* Note : the way hashbin has been designed is absolutely not
+ * reentrant, beware... So, we blindly protect all with spinlock */
+
+ /* Handle for the hint bit advertised in IrLMP */
+ __u32 skey;
+
+ /* Server socket part */
+ struct ias_object * ias_obj; /* Our service name + lsap in IAS */
+
+} irnet_root;
+
+
+/**************************** PROTOTYPES ****************************/
+
+/* ----------------------- CONTROL CHANNEL ----------------------- */
+static void
+ irnet_post_event(irnet_socket *,
+ irnet_event,
+ __u32,
+ char *);
+/* ----------------------- IRDA SUBROUTINES ----------------------- */
+static inline int
+ irnet_open_tsap(irnet_socket *);
+static int
+ irnet_find_lsap_sel(irnet_socket *);
+static inline int
+ irnet_discover_daddr_and_lsap_sel(irnet_socket *);
+static inline int
+ irnet_dname_to_daddr(irnet_socket *);
+/* ------------------------ SERVER SOCKET ------------------------ */
+static inline int
+ irnet_daddr_to_dname(irnet_socket *);
+static inline irnet_socket *
+ irnet_find_socket(irnet_socket *);
+static inline int
+ irnet_connect_socket(irnet_socket *,
+ irnet_socket *,
+ struct qos_info *,
+ __u32,
+ __u8);
+static inline void
+ irnet_disconnect_server(irnet_socket *,
+ struct sk_buff *);
+static inline int
+ irnet_setup_server(void);
+static inline void
+ irnet_destroy_server(void);
+/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */
+static int
+ irnet_data_indication(void *, /* instance */
+ void *, /* sap */
+ struct sk_buff *);
+static void
+ irnet_disconnect_indication(void *,
+ void *,
+ LM_REASON,
+ struct sk_buff *);
+static void
+ irnet_connect_confirm(void *,
+ void *,
+ struct qos_info *,
+ __u32,
+ __u8,
+ struct sk_buff *);
+static void
+ irnet_flow_indication(void *,
+ void *,
+ LOCAL_FLOW);
+static void
+ irnet_status_indication(void *,
+ LINK_STATUS,
+ LOCK_STATUS);
+static void
+ irnet_connect_indication(void *,
+ void *,
+ struct qos_info *,
+ __u32,
+ __u8,
+ struct sk_buff *);
+/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */
+static void
+ irnet_getvalue_confirm(int,
+ __u16,
+ struct ias_value *,
+ void *);
+#ifdef DISCOVERY_EVENTS
+static void
+ irnet_discovery_indication(discovery_t *,
+ void *);
+static void
+ irnet_expiry_indication(discovery_t *,
+ void *);
+#endif
+/* -------------------------- PROC ENTRY -------------------------- */
+#ifdef CONFIG_PROC_FS
+static int
+ irnet_proc_read(char *,
+ char **,
+ off_t,
+ int);
+#endif CONFIG_PROC_FS
+
+/**************************** VARIABLES ****************************/
+
+/*
+ * The IrNET server. Listen to connection requests and co...
+ */
+static struct irnet_root irnet_server;
+
+/* Control channel stuff (note : extern) */
+struct irnet_ctrl_channel irnet_events;
+
+/* The /proc/net/irda directory, defined elsewhere... */
+#ifdef CONFIG_PROC_FS
+extern struct proc_dir_entry *proc_irda;
+#endif CONFIG_PROC_FS
+
+#endif IRNET_IRDA_H
diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c
new file mode 100644
index 000000000..96a5c4114
--- /dev/null
+++ b/net/irda/irnet/irnet_ppp.c
@@ -0,0 +1,1052 @@
+/*
+ * IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ * Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file implement the PPP interface and /dev/irnet character device.
+ * The PPP interface hook to the ppp_generic module, handle all our
+ * relationship to the PPP code in the kernel (and by extension to pppd),
+ * and exchange PPP frames with this module (send/receive).
+ * The /dev/irnet device is used primarily for 2 functions :
+ * 1) as a stub for pppd (the ppp daemon), so that we can appropriately
+ * generate PPP sessions (we pretend we are a tty).
+ * 2) as a control channel (write commands, read events)
+ */
+
+#include "irnet_ppp.h" /* Private header */
+
+/************************* CONTROL CHANNEL *************************/
+/*
+ * When a pppd instance is not active on /dev/irnet, it acts as a control
+ * channel.
+ * Writting allow to set up the IrDA destination of the IrNET channel,
+ * and any application may be read events happening in IrNET...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write is used to send a command to configure a IrNET channel
+ * before it is open by pppd. The syntax is : "command argument"
+ * Currently there is only two defined commands :
+ * o name : set the requested IrDA nickname of the IrNET peer.
+ * o addr : set the requested IrDA address of the IrNET peer.
+ * Note : the code is crude, but effective...
+ */
+static inline ssize_t
+irnet_ctrl_write(irnet_socket * ap,
+ const char * buf,
+ size_t count)
+{
+ char command[5 + NICKNAME_MAX_LEN + 2];
+ int length = count;
+
+ DENTER(CTRL_TRACE, "(ap=0x%X, count=%d)\n", (unsigned int) ap, count);
+
+ /* Check for overflow... */
+ DABORT(count > (5 + NICKNAME_MAX_LEN + 1), -ENOMEM,
+ CTRL_ERROR, "Too much data !!!\n");
+
+ /* Get the data in the driver */
+ if(copy_from_user(command, buf, count))
+ {
+ DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
+ return -EFAULT;
+ }
+
+ /* Strip out '\n' if needed, and safe terminate the string */
+ if(command[length - 1] == '\0')
+ length--;
+ if(command[length - 1] == '\n')
+ length--;
+ command[length] = '\0';
+ DEBUG(CTRL_INFO, "Command received is ``%s'' (%d-%d).\n",
+ command, length, count);
+
+ /* Check if we recognised the command */
+ /* First command : name */
+ if(!strncmp(command, "name", 4))
+ {
+ /* Copy the name only if is included and not "any" */
+ if((length > 5) && (strcmp(command + 5, "any")))
+ {
+ /* Copy the name for later reuse (including the '/0') */
+ memcpy(ap->rname, command + 5, length - 5 + 1);
+ }
+ else
+ ap->rname[0] = '\0';
+ DEXIT(CTRL_TRACE, " - rname = ``%s''\n", ap->rname);
+ return(count);
+ }
+
+ /* Second command : addr */
+ if(!strncmp(command, "addr", 4))
+ {
+ /* Copy the address only if is included and not "any" */
+ if((length > 5) && (strcmp(command + 5, "any")))
+ {
+ char * endp;
+ __u32 daddr;
+
+ /* Convert argument to a number (last arg is the base) */
+ daddr = simple_strtoul(command + 5, &endp, 16);
+ /* Has it worked ? (endp should be command + count) */
+ DABORT(endp <= (command + 5), -EINVAL,
+ CTRL_ERROR, "Invalid address.\n");
+ /* Save it */
+ ap->raddr = daddr;
+ }
+ else
+ ap->raddr = DEV_ADDR_ANY;
+ DEXIT(CTRL_TRACE, " - raddr = %08x\n", ap->raddr);
+ return(count);
+ }
+
+ /* Other possible command : connect N (number of retries) */
+
+ /* Failed... */
+ DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n");
+}
+
+#ifdef INITIAL_DISCOVERY
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_read_discovery_log (self)
+ *
+ * Read the content on the discovery log
+ *
+ * This function dump the current content of the discovery log
+ * at the startup of the event channel.
+ * Return 1 if written on the control channel...
+ *
+ * State of the ap->disco_XXX variables :
+ * at socket creation : disco_index = 0 ; disco_number = 0
+ * while reading : disco_index = X ; disco_number = Y
+ * After reading : disco_index = Y ; disco_number = -1
+ */
+static inline int
+irnet_read_discovery_log(irnet_socket * ap,
+ char * event)
+{
+ int done_event = 0;
+
+ DENTER(CTRL_TRACE, "(ap=0x%X, event=0x%X)\n",
+ (unsigned int) ap, (unsigned int) event);
+
+ /* Test if we have some work to do or we have already finished */
+ if(ap->disco_number == -1)
+ {
+ DEBUG(CTRL_INFO, "Already done\n");
+ return 0;
+ }
+
+ /* Test if it's the first time and therefore we need to get the log */
+ if(ap->disco_index == 0)
+ {
+ __u16 mask = irlmp_service_to_hint(S_LAN);
+
+ /* Ask IrLMP for the current discovery log */
+ ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask);
+ /* Check if the we got some results */
+ if(ap->discoveries == NULL)
+ ap->disco_number = -1;
+ DEBUG(CTRL_INFO, "Got the log (0x%X), size is %d\n",
+ (unsigned int) ap->discoveries, ap->disco_number);
+ }
+
+ /* Check if we have more item to dump */
+ if(ap->disco_index < ap->disco_number)
+ {
+ /* Write an event */
+ sprintf(event, "Found %08x (%s)\n",
+ ap->discoveries[ap->disco_index].daddr,
+ ap->discoveries[ap->disco_index].info);
+ DEBUG(CTRL_INFO, "Writing discovery %d : %s\n",
+ ap->disco_index, ap->discoveries[ap->disco_index].info);
+
+ /* We have an event */
+ done_event = 1;
+ /* Next discovery */
+ ap->disco_index++;
+ }
+
+ /* Check if we have done the last item */
+ if(ap->disco_index >= ap->disco_number)
+ {
+ /* No more items : remove the log and signal termination */
+ DEBUG(CTRL_INFO, "Cleaning up log (0x%X)\n",
+ (unsigned int) ap->discoveries);
+ if(ap->discoveries != NULL)
+ {
+ /* Cleanup our copy of the discovery log */
+ kfree(ap->discoveries);
+ ap->discoveries = NULL;
+ }
+ ap->disco_number = -1;
+ }
+
+ return done_event;
+}
+#endif INITIAL_DISCOVERY
+
+/*------------------------------------------------------------------*/
+/*
+ * Read is used to get IrNET events
+ */
+static inline ssize_t
+irnet_ctrl_read(irnet_socket * ap,
+ struct file * file,
+ char * buf,
+ size_t count)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ char event[64]; /* Max event is 61 char */
+ ssize_t ret = 0;
+
+ DENTER(CTRL_TRACE, "(ap=0x%X, count=%d)\n", (unsigned int) ap, count);
+
+ /* Check if we can write an event out in one go */
+ DABORT(count < sizeof(event), -EOVERFLOW, CTRL_ERROR, "Buffer to small.\n");
+
+#ifdef INITIAL_DISCOVERY
+ /* Check if we have read the log */
+ if(irnet_read_discovery_log(ap, event))
+ {
+ /* We have an event !!! Copy it to the user */
+ if(copy_to_user(buf, event, strlen(event)))
+ {
+ DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
+ return -EFAULT;
+ }
+
+ DEXIT(CTRL_TRACE, "\n");
+ return(strlen(event));
+ }
+#endif INITIAL_DISCOVERY
+
+ /* Put ourselves on the wait queue to be woken up */
+ add_wait_queue(&irnet_events.rwait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+ for(;;)
+ {
+ /* If there is unread events */
+ ret = 0;
+ if(ap->event_index != irnet_events.index)
+ break;
+ ret = -EAGAIN;
+ if(file->f_flags & O_NONBLOCK)
+ break;
+ ret = -ERESTARTSYS;
+ if(signal_pending(current))
+ break;
+ /* Yield and wait to be woken up */
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&irnet_events.rwait, &wait);
+
+ /* Did we got it ? */
+ if(ret != 0)
+ {
+ /* No, return the error code */
+ DEXIT(CTRL_TRACE, " - ret %d\n", ret);
+ return ret;
+ }
+
+ /* Which event is it ? */
+ switch(irnet_events.log[ap->event_index].event)
+ {
+ case IRNET_DISCOVER:
+ sprintf(event, "Discovered %08x (%s)\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name);
+ break;
+ case IRNET_EXPIRE:
+ sprintf(event, "Expired %08x (%s)\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name);
+ break;
+ case IRNET_CONNECT_TO:
+ sprintf(event, "Connected to %08x (%s) on ppp%d\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name,
+ irnet_events.log[ap->event_index].unit);
+ break;
+ case IRNET_CONNECT_FROM:
+ sprintf(event, "Connection from %08x (%s) on ppp%d\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name,
+ irnet_events.log[ap->event_index].unit);
+ break;
+ case IRNET_REQUEST_FROM:
+ sprintf(event, "Request from %08x (%s)\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name);
+ break;
+ case IRNET_NOANSWER_FROM:
+ sprintf(event, "No-answer from %08x (%s) on ppp%d\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name,
+ irnet_events.log[ap->event_index].unit);
+ break;
+ case IRNET_BLOCKED_LINK:
+ sprintf(event, "Blocked link with %08x (%s) on ppp%d\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name,
+ irnet_events.log[ap->event_index].unit);
+ break;
+ case IRNET_DISCONNECT_FROM:
+ sprintf(event, "Disconnection from %08x (%s) on ppp%d\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name,
+ irnet_events.log[ap->event_index].unit);
+ break;
+ case IRNET_DISCONNECT_TO:
+ sprintf(event, "Disconnected to %08x (%s)\n",
+ irnet_events.log[ap->event_index].addr,
+ irnet_events.log[ap->event_index].name);
+ break;
+ default:
+ sprintf(event, "Bug\n");
+ }
+ /* Increment our event index */
+ ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS;
+
+ DEBUG(CTRL_INFO, "Event is :%s", event);
+
+ /* Copy it to the user */
+ if(copy_to_user(buf, event, strlen(event)))
+ {
+ DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
+ return -EFAULT;
+ }
+
+ DEXIT(CTRL_TRACE, "\n");
+ return(strlen(event));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Poll : called when someone do a select on /dev/irnet.
+ * Just check if there are new events...
+ */
+static inline unsigned int
+irnet_ctrl_poll(irnet_socket * ap,
+ struct file * file,
+ poll_table * wait)
+{
+ unsigned int mask;
+
+ DENTER(CTRL_TRACE, "(ap=0x%X)\n", (unsigned int) ap);
+
+ poll_wait(file, &irnet_events.rwait, wait);
+ mask = POLLOUT | POLLWRNORM;
+ /* If there is unread events */
+ if(ap->event_index != irnet_events.index)
+ mask |= POLLIN | POLLRDNORM;
+#ifdef INITIAL_DISCOVERY
+ if(ap->disco_number != -1)
+ mask |= POLLIN | POLLRDNORM;
+#endif INITIAL_DISCOVERY
+
+ DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask);
+ return mask;
+}
+
+
+/*********************** FILESYSTEM CALLBACKS ***********************/
+/*
+ * Implement the usual open, read, write functions that will be called
+ * by the file system when some action is performed on /dev/irnet.
+ * Most of those actions will in fact be performed by "pppd" or
+ * the control channel, we just act as a redirector...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Open : when somebody open /dev/irnet
+ * We basically create a new instance of irnet and initialise it.
+ */
+static int
+dev_irnet_open(struct inode * inode,
+ struct file * file)
+{
+ struct irnet_socket * ap;
+ int err;
+
+ DENTER(FS_TRACE, "(file=0x%X)\n", (unsigned int) file);
+
+#ifdef SECURE_DEVIRNET
+ /* This could (should?) be enforced by the permissions on /dev/irnet. */
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+#endif SECURE_DEVIRNET
+
+ /* Allocate a private structure for this IrNET instance */
+ ap = kmalloc(sizeof(*ap), GFP_KERNEL);
+ DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n");
+
+ MOD_INC_USE_COUNT;
+
+ /* initialize the irnet structure */
+ memset(ap, 0, sizeof(*ap));
+ ap->file = file;
+
+ /* PPP channel setup */
+ ap->ppp_open = 0;
+ ap->chan.private = ap;
+ /* PPP parameters */
+ ap->mru = PPP_MRU;
+ ap->xaccm[0] = ~0U;
+ ap->xaccm[3] = 0x60000000U;
+ ap->raccm = ~0U;
+
+ /* Setup the IrDA part... */
+ err = irda_irnet_create(ap);
+ if(err)
+ {
+ DERROR(FS_ERROR, "Can't setup IrDA link...\n");
+ kfree(ap);
+ MOD_DEC_USE_COUNT;
+ return err;
+ }
+
+ /* For the control channel */
+ ap->event_index = irnet_events.index; /* Cancel all past events */
+
+ /* Put our stuff where we will be able to find it later */
+ file->private_data = ap;
+
+ DEXIT(FS_TRACE, " - ap=0x%X\n", (unsigned int) ap);
+ return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Close : when somebody close /dev/irnet
+ * Destroy the instance of /dev/irnet
+ */
+static int
+dev_irnet_close(struct inode * inode,
+ struct file * file)
+{
+ irnet_socket * ap = (struct irnet_socket *) file->private_data;
+
+ DENTER(FS_TRACE, "(file=0x%X, ap=0x%X)\n",
+ (unsigned int) file, (unsigned int) ap);
+ DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n");
+
+ /* Detach ourselves */
+ file->private_data = NULL;
+
+ /* Close IrDA stuff */
+ irda_irnet_destroy(ap);
+
+ /* Disconnect from the generic PPP layer if not already done */
+ if(ap->ppp_open)
+ {
+ DERROR(FS_ERROR, "Channel still registered - deregistering !\n");
+ ppp_unregister_channel(&ap->chan);
+ ap->ppp_open = 0;
+ }
+
+ kfree(ap);
+ MOD_DEC_USE_COUNT;
+
+ DEXIT(FS_TRACE, "\n");
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Write does nothing.
+ * (we receive packet from ppp_generic through ppp_irnet_send())
+ */
+static ssize_t
+dev_irnet_write(struct file * file,
+ const char * buf,
+ size_t count,
+ loff_t * ppos)
+{
+ irnet_socket * ap = (struct irnet_socket *) file->private_data;
+
+ DPASS(FS_TRACE, "(file=0x%X, ap=0x%X, count=%d)\n",
+ (unsigned int) file, (unsigned int) ap, count);
+ DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n");
+
+ /* If we are connected to ppp_generic, let it handle the job */
+ if(ap->ppp_open)
+ return ppp_channel_write(&ap->chan, buf, count);
+ else
+ return irnet_ctrl_write(ap, buf, count);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read doesn't do much either.
+ * (pppd poll us, but ultimately reads through /dev/ppp)
+ */
+static ssize_t
+dev_irnet_read(struct file * file,
+ char * buf,
+ size_t count,
+ loff_t * ppos)
+{
+ irnet_socket * ap = (struct irnet_socket *) file->private_data;
+
+ DPASS(FS_TRACE, "(file=0x%X, ap=0x%X, count=%d)\n",
+ (unsigned int) file, (unsigned int) ap, count);
+ DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n");
+
+ /* If we are connected to ppp_generic, let it handle the job */
+ if(ap->ppp_open)
+ return ppp_channel_read(&ap->chan, file, buf, count);
+ else
+ return irnet_ctrl_read(ap, file, buf, count);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Poll : called when someone do a select on /dev/irnet
+ */
+static unsigned int
+dev_irnet_poll(struct file * file,
+ poll_table * wait)
+{
+ irnet_socket * ap = (struct irnet_socket *) file->private_data;
+ unsigned int mask;
+
+ DENTER(FS_TRACE, "(file=0x%X, ap=0x%X)\n",
+ (unsigned int) file, (unsigned int) ap);
+
+ mask = POLLOUT | POLLWRNORM;
+ DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n");
+
+ /* If we are connected to ppp_generic, let it handle the job */
+ if(ap->ppp_open)
+ mask |= ppp_channel_poll(&ap->chan, file, wait);
+ else
+ mask |= irnet_ctrl_poll(ap, file, wait);
+
+ DEXIT(FS_TRACE, " - mask=0x%X\n", mask);
+ return(mask);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * IOCtl : Called when someone does some ioctls on /dev/irnet
+ * This is the way pppd configure us and control us while the PPP
+ * instance is active.
+ */
+static int
+dev_irnet_ioctl(struct inode * inode,
+ struct file * file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ irnet_socket * ap = (struct irnet_socket *) file->private_data;
+ int err;
+ int val;
+
+ DENTER(FS_TRACE, "(file=0x%X, ap=0x%X, cmd=0x%X)\n",
+ (unsigned int) file, (unsigned int) ap, cmd);
+
+ /* Basic checks... */
+ DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");
+#ifdef SECURE_DEVIRNET
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+#endif SECURE_DEVIRNET
+
+ err = -EFAULT;
+ switch(cmd)
+ {
+ /* Set discipline (should be N_SYNC_PPP or N_TTY) */
+ case TIOCSETD:
+ if(get_user(val, (int *) arg))
+ break;
+ if((val == N_SYNC_PPP) || (val == N_PPP))
+ {
+ DEBUG(FS_INFO, "Entering PPP discipline.\n");
+ /* PPP channel setup */
+ ap->chan.private = ap;
+ ap->chan.ops = &irnet_ppp_ops;
+ ap->chan.mtu = PPP_MRU;
+ err = ppp_register_channel(&ap->chan);
+ if(err == 0)
+ {
+ /* Our ppp side is active */
+ ap->ppp_open = 1;
+
+ DEBUG(FS_INFO, "Trying to establish a connection.\n");
+ /* Setup the IrDA link now - may fail... */
+ irda_irnet_connect(ap);
+ }
+ else
+ DERROR(FS_ERROR, "Can't setup PPP channel...\n");
+ }
+ else
+ {
+ /* In theory, should be N_TTY */
+ DEBUG(FS_INFO, "Exiting PPP discipline.\n");
+ /* Disconnect from the generic PPP layer */
+ if(ap->ppp_open)
+ ppp_unregister_channel(&ap->chan);
+ else
+ DERROR(FS_ERROR, "Channel not registered !\n");
+ ap->ppp_open = 0;
+ err = 0;
+ }
+ break;
+
+ /* Attach this PPP instance to the PPP driver (set it active) */
+ case PPPIOCATTACH:
+ case PPPIOCDETACH:
+ if(ap->ppp_open)
+ err = ppp_channel_ioctl(&ap->chan, cmd, arg);
+ else
+ DERROR(FS_ERROR, "Channel not registered !\n");
+ break;
+
+ /* Query PPP channel and unit number */
+ case PPPIOCGCHAN:
+ if(!ap->ppp_open)
+ break;
+ if(put_user(ppp_channel_index(&ap->chan), (int *) arg))
+ break;
+ DEBUG(FS_INFO, "Query channel.\n");
+ err = 0;
+ break;
+ case PPPIOCGUNIT:
+ if(!ap->ppp_open)
+ break;
+ if(put_user(ppp_unit_number(&ap->chan), (int *) arg))
+ break;
+ DEBUG(FS_INFO, "Query unit number.\n");
+ err = 0;
+ break;
+
+ /* All these ioctls can be passed both directly and from ppp_generic,
+ * so we just deal with them in one place...
+ */
+ case PPPIOCGFLAGS:
+ case PPPIOCSFLAGS:
+ case PPPIOCGASYNCMAP:
+ case PPPIOCSASYNCMAP:
+ case PPPIOCGRASYNCMAP:
+ case PPPIOCSRASYNCMAP:
+ case PPPIOCGXASYNCMAP:
+ case PPPIOCSXASYNCMAP:
+ case PPPIOCGMRU:
+ case PPPIOCSMRU:
+ DEBUG(FS_INFO, "Standard PPP ioctl.\n");
+ if(!capable(CAP_NET_ADMIN))
+ err = -EPERM;
+ else
+ err = ppp_irnet_ioctl(&ap->chan, cmd, arg);
+ break;
+
+ /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */
+ /* Get termios */
+ case TCGETS:
+ DEBUG(FS_INFO, "Get termios.\n");
+ if(kernel_termios_to_user_termios((struct termios *)arg, &ap->termios))
+ break;
+ err = 0;
+ break;
+ /* Set termios */
+ case TCSETSF:
+ DEBUG(FS_INFO, "Set termios.\n");
+ if(user_termios_to_kernel_termios(&ap->termios, (struct termios *) arg))
+ break;
+ err = 0;
+ break;
+
+ /* Set DTR/RTS */
+ case TIOCMBIS:
+ case TIOCMBIC:
+ /* Set exclusive/non-exclusive mode */
+ case TIOCEXCL:
+ case TIOCNXCL:
+ DEBUG(FS_INFO, "TTY compatibility.\n");
+ err = 0;
+ break;
+
+ case TCGETA:
+ DEBUG(FS_INFO, "TCGETA\n");
+ break;
+
+ case TCFLSH:
+ DEBUG(FS_INFO, "TCFLSH\n");
+ /* Note : this will flush buffers in PPP, so it *must* be done
+ * We should also worry that we don't accept junk here and that
+ * we get rid of our own buffers */
+#ifdef FLUSH_TO_PPP
+ ppp_output_wakeup(&ap->chan);
+#endif FLUSH_TO_PPP
+ err = 0;
+ break;
+
+ case FIONREAD:
+ DEBUG(FS_INFO, "FIONREAD\n");
+ val = 0;
+ if(put_user(val, (int *) arg))
+ break;
+ err = 0;
+ break;
+
+ default:
+ DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd);
+ err = -ENOIOCTLCMD;
+ }
+
+ DEXIT(FS_TRACE, " - err = 0x%X\n", err);
+ return err;
+}
+
+/************************** PPP CALLBACKS **************************/
+/*
+ * This are the functions that the generic PPP driver in the kernel
+ * will call to communicate to us.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare the ppp frame for transmission over the IrDA socket.
+ * We make sure that the header space is enough, and we change ppp header
+ * according to flags passed by pppd.
+ * This is not a callback, but just a helper function used in ppp_irnet_send()
+ */
+static inline struct sk_buff *
+irnet_prepare_skb(irnet_socket * ap,
+ struct sk_buff * skb)
+{
+ unsigned char * data;
+ int proto; /* PPP protocol */
+ int islcp; /* Protocol == LCP */
+ int needaddr; /* Need PPP address */
+
+ DENTER(PPP_TRACE, "(ap=0x%X, skb=0x%X)\n",
+ (unsigned int) ap, (unsigned int) skb);
+
+ /* Extract PPP protocol from the frame */
+ data = skb->data;
+ proto = (data[0] << 8) + data[1];
+
+ /* LCP packets with codes between 1 (configure-request)
+ * and 7 (code-reject) must be sent as though no options
+ * have been negotiated. */
+ islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7);
+
+ /* compress protocol field if option enabled */
+ if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp))
+ skb_pull(skb,1);
+
+ /* Check if we need address/control fields */
+ needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp);
+
+ /* Is the skb headroom large enough to contain all IrDA-headers? */
+ if((skb_headroom(skb) < (ap->max_header_size + needaddr)) ||
+ (skb_shared(skb)))
+ {
+ struct sk_buff * new_skb;
+
+ DEBUG(PPP_INFO, "Reallocating skb\n");
+
+ /* Create a new skb */
+ new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr);
+
+ /* We have to free the original skb anyway */
+ dev_kfree_skb(skb);
+
+ /* Did the realloc succeed ? */
+ DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n");
+
+ /* Use the new skb instead */
+ skb = new_skb;
+ }
+
+ /* prepend address/control fields if necessary */
+ if(needaddr)
+ {
+ skb_push(skb,2);
+ skb->data[0] = PPP_ALLSTATIONS;
+ skb->data[1] = PPP_UI;
+ }
+
+ DEXIT(PPP_TRACE, "\n");
+
+ return skb;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Send a packet to the peer over the IrTTP connection.
+ * Returns 1 iff the packet was accepted.
+ * Returns 0 iff packet was not consumed.
+ * If the packet was not accepted, we will call ppp_output_wakeup
+ * at some later time to reactivate flow control in ppp_generic.
+ */
+static int
+ppp_irnet_send(struct ppp_channel * chan,
+ struct sk_buff * skb)
+{
+ irnet_socket * self = (struct irnet_socket *) chan->private;
+ int ret;
+
+ DENTER(PPP_TRACE, "(channel=0x%X, ap/self=0x%X)\n",
+ (unsigned int) chan, (unsigned int) self);
+
+ /* Check if things are somewhat valid... */
+ DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n");
+
+ /* Check if we are connected */
+ if(self->ttp_open == 0)
+ {
+#ifdef CONNECT_IN_SEND
+ /* Let's try to connect one more time... */
+ /* Note : we won't connect fully yet, but we should be ready for
+ * next packet... */
+ /* Note : we can't do that, we need to have a process context to
+ * go through interruptible_sleep_on() in irnet_find_lsap_sel()
+ * We need to find another way... */
+ irda_irnet_connect(self);
+#endif CONNECT_IN_SEND
+
+ DEBUG(PPP_INFO, "IrTTP not ready ! (%d-0x%X)\n",
+ self->ttp_open, (unsigned int) self->tsap);
+
+ /* Note : we can either drop the packet or block the packet.
+ *
+ * Blocking the packet allow us a better connection time,
+ * because by calling ppp_output_wakeup() we can have
+ * ppp_generic resending the LCP request immediately to us,
+ * rather than waiting for one of pppd periodic transmission of
+ * LCP request.
+ *
+ * On the other hand, if we block all packet, all those periodic
+ * transmissions of pppd accumulate in ppp_generic, creating a
+ * backlog of LCP request. When we eventually connect later on,
+ * we have to transmit all this backlog before we can connect
+ * proper (if we don't timeout before).
+ *
+ * The current strategy is as follow :
+ * While we are attempting to connect, we block packets to get
+ * a better connection time.
+ * If we fail to connect, we drain the queue and start dropping packets
+ */
+#ifdef BLOCK_WHEN_CONNECT
+ /* If we are attempting to connect */
+ if(self->tsap)
+ {
+ /* Blocking packet, ppp_generic will retry later */
+ return 0;
+ }
+#endif BLOCK_WHEN_CONNECT
+
+ /* Dropping packet, pppd will retry later */
+ dev_kfree_skb(skb);
+ return 1;
+ }
+
+ /* Check if the queue can accept any packet, otherwise block */
+ if(self->tx_flow != FLOW_START)
+ DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n",
+ skb_queue_len(&self->tsap->tx_queue));
+
+ /* Prepare ppp frame for transmission */
+ skb = irnet_prepare_skb(self, skb);
+ DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n");
+
+ /* Send the packet to IrTTP */
+ ret = irttp_data_request(self->tsap, skb);
+ if(ret < 0)
+ {
+ /*
+ * > IrTTPs tx queue is full, so we just have to
+ * > drop the frame! You might think that we should
+ * > just return -1 and don't deallocate the frame,
+ * > but that is dangerous since it's possible that
+ * > we have replaced the original skb with a new
+ * > one with larger headroom, and that would really
+ * > confuse do_dev_queue_xmit() in dev.c! I have
+ * > tried :-) DB
+ * Correction : we verify the flow control above (self->tx_flow),
+ * so we come here only if IrTTP doesn't like the packet (empty,
+ * too large, IrTTP not connected). In those rare cases, it's ok
+ * to drop it, we don't want to see it here again...
+ * Jean II
+ */
+ DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret);
+ dev_kfree_skb(skb);
+ }
+
+ DEXIT(PPP_TRACE, "\n");
+ return 1; /* Packet has been consumed */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Take care of the ioctls that ppp_generic doesn't want to deal with...
+ * Note : we are also called from dev_irnet_ioctl().
+ */
+static int
+ppp_irnet_ioctl(struct ppp_channel * chan,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ irnet_socket * ap = (struct irnet_socket *) chan->private;
+ int err;
+ int val;
+ u32 accm[8];
+
+ DENTER(PPP_TRACE, "(channel=0x%X, ap=0x%X, cmd=0x%X)\n",
+ (unsigned int) chan, (unsigned int) ap, cmd);
+
+ /* Basic checks... */
+ DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");
+
+ err = -EFAULT;
+ switch(cmd)
+ {
+ /* PPP flags */
+ case PPPIOCGFLAGS:
+ val = ap->flags | ap->rbits;
+ if(put_user(val, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSFLAGS:
+ if(get_user(val, (int *) arg))
+ break;
+ ap->flags = val & ~SC_RCV_BITS;
+ ap->rbits = val & SC_RCV_BITS;
+ err = 0;
+ break;
+
+ /* Async map stuff - all dummy to please pppd */
+ case PPPIOCGASYNCMAP:
+ if(put_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSASYNCMAP:
+ if(get_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCGRASYNCMAP:
+ if(put_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSRASYNCMAP:
+ if(get_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCGXASYNCMAP:
+ if(copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSXASYNCMAP:
+ if(copy_from_user(accm, (void *) arg, sizeof(accm)))
+ break;
+ accm[2] &= ~0x40000000U; /* can't escape 0x5e */
+ accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
+ memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+ err = 0;
+ break;
+
+ /* Max PPP frame size */
+ case PPPIOCGMRU:
+ if(put_user(ap->mru, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSMRU:
+ if(get_user(val, (int *) arg))
+ break;
+ if(val < PPP_MRU)
+ val = PPP_MRU;
+ ap->mru = val;
+ err = 0;
+ break;
+
+ default:
+ DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd);
+ err = -ENOIOCTLCMD;
+ }
+
+ DEXIT(PPP_TRACE, " - err = 0x%X\n", err);
+ return err;
+}
+
+/************************** INITIALISATION **************************/
+/*
+ * Module initialisation and all that jazz...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Hook our device callbacks in the filesystem, to connect our code
+ * to /dev/irnet
+ */
+int
+ppp_irnet_init(void)
+{
+ int err = 0;
+
+ DENTER(MODULE_TRACE, "()\n");
+
+ /* Allocate ourselves as a minor in the misc range */
+ err = misc_register(&irnet_misc_device);
+
+ DEXIT(MODULE_TRACE, "\n");
+ return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Cleanup at exit...
+ */
+void
+ppp_irnet_cleanup(void)
+{
+ DENTER(MODULE_TRACE, "()\n");
+
+ /* De-allocate /dev/irnet minor in misc range */
+ misc_deregister(&irnet_misc_device);
+
+ DEXIT(MODULE_TRACE, "\n");
+}
+
+#ifdef MODULE
+/*------------------------------------------------------------------*/
+/*
+ * Module main entry point
+ */
+int
+init_module(void)
+{
+ int err;
+
+ /* Initialise both parts... */
+ err = irda_irnet_init();
+ if(!err)
+ err = ppp_irnet_init();
+ return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Module exit
+ */
+void
+cleanup_module(void)
+{
+ irda_irnet_cleanup();
+ return ppp_irnet_cleanup();
+}
+#endif /* MODULE */
diff --git a/net/irda/irnet/irnet_ppp.h b/net/irda/irnet/irnet_ppp.h
new file mode 100644
index 000000000..8e8365374
--- /dev/null
+++ b/net/irda/irnet/irnet_ppp.h
@@ -0,0 +1,130 @@
+/*
+ * IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ * Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file contains all definitions and declarations necessary for the
+ * PPP part of the IrNET module.
+ * This file is a private header, so other modules don't want to know
+ * what's in there...
+ */
+
+#ifndef IRNET_PPP_H
+#define IRNET_PPP_H
+
+/***************************** INCLUDES *****************************/
+
+#include "irnet.h" /* Module global include */
+
+/************************ CONSTANTS & MACROS ************************/
+
+/* /dev/irnet file constants */
+#define IRNET_MAJOR 10 /* Misc range */
+#define IRNET_MINOR 187 /* Official allocation */
+
+#ifdef LINKNAME_IOCTL
+/* Compatibility with old ppp drivers
+ * Should be defined in <linux/if_ppp.h> */
+#ifndef PPPIOCSLINKNAME
+#define PPPIOCSLINKNAME _IOW('t', 74, struct ppp_option_data)
+#endif PPPIOCSLINKNAME
+#endif LINKNAME_IOCTL
+
+/* PPP hardcore stuff */
+
+/* Bits in rbits (PPP flags in irnet struct) */
+#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
+
+/* Bit numbers in busy */
+#define XMIT_BUSY 0
+#define RECV_BUSY 1
+#define XMIT_WAKEUP 2
+#define XMIT_FULL 3
+
+/* Queue management */
+#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */
+
+/****************************** TYPES ******************************/
+
+
+/**************************** PROTOTYPES ****************************/
+
+/* ----------------------- CONTROL CHANNEL ----------------------- */
+static inline ssize_t
+ irnet_ctrl_write(irnet_socket *,
+ const char *,
+ size_t);
+static inline ssize_t
+ irnet_ctrl_read(irnet_socket *,
+ struct file *,
+ char *,
+ size_t);
+static inline unsigned int
+ irnet_ctrl_poll(irnet_socket *,
+ struct file *,
+ poll_table *);
+/* ----------------------- CHARACTER DEVICE ----------------------- */
+static int
+ dev_irnet_open(struct inode *, /* fs callback : open */
+ struct file *),
+ dev_irnet_close(struct inode *,
+ struct file *);
+static ssize_t
+ dev_irnet_write(struct file *,
+ const char *,
+ size_t,
+ loff_t *),
+ dev_irnet_read(struct file *,
+ char *,
+ size_t,
+ loff_t *);
+static unsigned int
+ dev_irnet_poll(struct file *,
+ poll_table *);
+static int
+ dev_irnet_ioctl(struct inode *,
+ struct file *,
+ unsigned int,
+ unsigned long);
+/* ------------------------ PPP INTERFACE ------------------------ */
+static inline struct sk_buff *
+ irnet_prepare_skb(irnet_socket *,
+ struct sk_buff *);
+static int
+ ppp_irnet_send(struct ppp_channel *,
+ struct sk_buff *);
+static int
+ ppp_irnet_ioctl(struct ppp_channel *,
+ unsigned int,
+ unsigned long);
+
+/**************************** VARIABLES ****************************/
+
+/* Filesystem callbacks (to call us) */
+static struct file_operations irnet_device_fops =
+{
+ read: dev_irnet_read,
+ write: dev_irnet_write,
+ poll: dev_irnet_poll,
+ ioctl: dev_irnet_ioctl,
+ open: dev_irnet_open,
+ release: dev_irnet_close
+ /* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */
+};
+
+/* Structure so that the misc major (drivers/char/misc.c) take care of us... */
+static struct miscdevice irnet_misc_device =
+{
+ IRNET_MINOR,
+ "irnet",
+ &irnet_device_fops
+};
+
+/* Generic PPP callbacks (to call us) */
+struct ppp_channel_ops irnet_ppp_ops =
+{
+ ppp_irnet_send,
+ ppp_irnet_ioctl
+};
+
+#endif IRNET_PPP_H
diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c
index 1d26d8f19..d2e4da720 100644
--- a/net/irda/irqueue.c
+++ b/net/irda/irqueue.c
@@ -36,7 +36,7 @@
#include <net/irda/irqueue.h>
#include <net/irda/irmod.h>
-static queue_t *dequeue_general( queue_t **queue, queue_t* element);
+static irda_queue_t *dequeue_general( irda_queue_t **queue, irda_queue_t* element);
static __u32 hash( char* name);
/*
@@ -79,7 +79,7 @@ hashbin_t *hashbin_new(int type)
*/
int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func)
{
- queue_t* queue;
+ irda_queue_t* queue;
int i;
ASSERT(hashbin != NULL, return -1;);
@@ -89,12 +89,12 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func)
* Free the entries in the hashbin
*/
for (i = 0; i < HASHBIN_SIZE; i ++ ) {
- queue = dequeue_first( (queue_t**) &hashbin->hb_queue[i]);
+ queue = dequeue_first( (irda_queue_t**) &hashbin->hb_queue[i]);
while (queue) {
if (free_func)
(*free_func)(queue);
queue = dequeue_first(
- (queue_t**) &hashbin->hb_queue[i]);
+ (irda_queue_t**) &hashbin->hb_queue[i]);
}
}
hashbin->hb_size = 0;
@@ -112,7 +112,7 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func)
*/
int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
{
- queue_t* queue;
+ irda_queue_t* queue;
int i;
ASSERT(hashbin != NULL, return -1;);
@@ -123,12 +123,12 @@ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
* it has been shown to work
*/
for (i = 0; i < HASHBIN_SIZE; i ++ ) {
- queue = dequeue_first((queue_t**) &hashbin->hb_queue[i]);
+ queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
while (queue ) {
if (free_func)
(*free_func)(queue);
queue = dequeue_first(
- (queue_t**) &hashbin->hb_queue[i]);
+ (irda_queue_t**) &hashbin->hb_queue[i]);
}
}
@@ -210,7 +210,7 @@ void hashbin_unlock(hashbin_t* hashbin, __u32 hashv, char* name,
* Insert an entry into the hashbin
*
*/
-void hashbin_insert(hashbin_t* hashbin, queue_t* entry, __u32 hashv, char* name)
+void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, __u32 hashv, char* name)
{
unsigned long flags = 0;
int bin;
@@ -250,7 +250,7 @@ void hashbin_insert(hashbin_t* hashbin, queue_t* entry, __u32 hashv, char* name)
*/
if ( hashbin->hb_type & HB_SORTED) {
} else {
- enqueue_first( (queue_t**) &hashbin->hb_queue[ bin ],
+ enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ],
entry);
}
hashbin->hb_size++;
@@ -275,7 +275,7 @@ void* hashbin_find( hashbin_t* hashbin, __u32 hashv, char* name )
{
int bin, found = FALSE;
unsigned long flags = 0;
- queue_t* entry;
+ irda_queue_t* entry;
IRDA_DEBUG( 4, "hashbin_find()\n");
@@ -342,7 +342,7 @@ void* hashbin_find( hashbin_t* hashbin, __u32 hashv, char* name )
void *hashbin_remove_first( hashbin_t *hashbin)
{
unsigned long flags;
- queue_t *entry = NULL;
+ irda_queue_t *entry = NULL;
save_flags(flags);
cli();
@@ -367,7 +367,7 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name)
{
int bin, found = FALSE;
unsigned long flags = 0;
- queue_t* entry;
+ irda_queue_t* entry;
IRDA_DEBUG( 4, __FUNCTION__ "()\n");
@@ -381,6 +381,7 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name)
hashv = hash( name );
bin = GET_HASHBIN( hashv );
+ /* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) {
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
@@ -421,8 +422,8 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name)
* If entry was found, dequeue it
*/
if ( found ) {
- dequeue_general( (queue_t**) &hashbin->hb_queue[ bin ],
- (queue_t*) entry );
+ dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
+ (irda_queue_t*) entry );
hashbin->hb_size--;
/*
@@ -450,6 +451,75 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name)
}
+/*
+ * Function hashbin_remove (hashbin, hashv, name)
+ *
+ * Remove entry with the given name
+ *
+ * In some cases, the user of hashbin can't guarantee the unicity
+ * of either the hashv or name.
+ * In those cases, using the above function is guaranteed to cause troubles,
+ * so we use this one instead...
+ * And by the way, it's also faster, because we skip the search phase ;-)
+ */
+void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
+{
+ unsigned long flags = 0;
+ int bin;
+ __u32 hashv;
+
+ IRDA_DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( hashbin != NULL, return NULL;);
+ ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+ ASSERT( entry != NULL, return NULL;);
+
+ /* Check if valid and not already removed... */
+ if((entry->q_next == NULL) || (entry->q_prev == NULL))
+ return NULL;
+
+ /*
+ * Locate hashbin
+ */
+ hashv = entry->q_hash;
+ bin = GET_HASHBIN( hashv );
+
+ /* Synchronize */
+ if ( hashbin->hb_type & HB_GLOBAL ) {
+ spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
+
+ } else if ( hashbin->hb_type & HB_LOCAL ) {
+ save_flags(flags);
+ cli();
+ } /* Default is no-lock */
+
+ /*
+ * Dequeue the entry...
+ */
+ dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
+ (irda_queue_t*) entry );
+ hashbin->hb_size--;
+ entry->q_next = NULL;
+ entry->q_prev = NULL;
+
+ /*
+ * Check if this item is the currently selected item, and in
+ * that case we must reset hb_current
+ */
+ if ( entry == hashbin->hb_current)
+ hashbin->hb_current = NULL;
+
+ /* Release lock */
+ if ( hashbin->hb_type & HB_GLOBAL) {
+ spin_unlock_irq( &hashbin->hb_mutex[ bin]);
+
+ } else if ( hashbin->hb_type & HB_LOCAL) {
+ restore_flags( flags);
+ }
+
+ return entry;
+}
+
/*
* Function hashbin_get_first (hashbin)
*
@@ -457,9 +527,9 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name)
* called before any calls to hashbin_get_next()!
*
*/
-queue_t *hashbin_get_first( hashbin_t* hashbin)
+irda_queue_t *hashbin_get_first( hashbin_t* hashbin)
{
- queue_t *entry;
+ irda_queue_t *entry;
int i;
ASSERT( hashbin != NULL, return NULL;);
@@ -489,9 +559,9 @@ queue_t *hashbin_get_first( hashbin_t* hashbin)
* NULL when all items have been traversed
*
*/
-queue_t *hashbin_get_next( hashbin_t *hashbin)
+irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
{
- queue_t* entry;
+ irda_queue_t* entry;
int bin;
int i;
@@ -542,7 +612,7 @@ queue_t *hashbin_get_next( hashbin_t *hashbin)
* Insert item into end of queue.
*
*/
-static void __enqueue_last( queue_t **queue, queue_t* element)
+static void __enqueue_last( irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 4, __FUNCTION__ "()\n");
@@ -566,7 +636,7 @@ static void __enqueue_last( queue_t **queue, queue_t* element)
}
}
-inline void enqueue_last( queue_t **queue, queue_t* element)
+inline void enqueue_last( irda_queue_t **queue, irda_queue_t* element)
{
unsigned long flags;
@@ -584,7 +654,7 @@ inline void enqueue_last( queue_t **queue, queue_t* element)
* Insert item first in queue.
*
*/
-void enqueue_first(queue_t **queue, queue_t* element)
+void enqueue_first(irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 4, __FUNCTION__ "()\n");
@@ -616,9 +686,9 @@ void enqueue_first(queue_t **queue, queue_t* element)
* Insert a queue (list) into the start of the first queue
*
*/
-void enqueue_queue( queue_t** queue, queue_t** list )
+void enqueue_queue( irda_queue_t** queue, irda_queue_t** list )
{
- queue_t* tmp;
+ irda_queue_t* tmp;
/*
* Check if queue is empty
@@ -643,7 +713,7 @@ void enqueue_queue( queue_t** queue, queue_t** list )
*
*/
#if 0
-static void enqueue_second(queue_t **queue, queue_t* element)
+static void enqueue_second(irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 0, "enqueue_second()\n");
@@ -674,9 +744,9 @@ static void enqueue_second(queue_t **queue, queue_t* element)
* Remove first entry in queue
*
*/
-queue_t *dequeue_first(queue_t **queue)
+irda_queue_t *dequeue_first(irda_queue_t **queue)
{
- queue_t *ret;
+ irda_queue_t *ret;
IRDA_DEBUG( 4, "dequeue_first()\n");
@@ -715,9 +785,9 @@ queue_t *dequeue_first(queue_t **queue)
*
*
*/
-static queue_t *dequeue_general(queue_t **queue, queue_t* element)
+static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element)
{
- queue_t *ret;
+ irda_queue_t *ret;
IRDA_DEBUG( 4, "dequeue_general()\n");
diff --git a/net/irda/irmod.c b/net/irda/irsyms.c
index 39257f87a..117f23ecc 100644
--- a/net/irda/irmod.c
+++ b/net/irda/irsyms.c
@@ -1,8 +1,8 @@
/*********************************************************************
*
- * Filename: irmod.c
- * Version: 0.8
- * Description: IrDA module code and some other stuff
+ * Filename: irsyms.c
+ * Version: 0.9
+ * Description: IrDA module symbols
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Dec 15 13:55:39 1997
@@ -23,7 +23,7 @@
********************************************************************/
#include <linux/config.h>
-#include <linux/module.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/poll.h>
@@ -49,18 +49,12 @@
extern struct proc_dir_entry *proc_irda;
-struct irda_cb irda; /* One global instance */
-
-#ifdef CONFIG_IRDA_DEBUG
-__u32 irda_debug = IRDA_DEBUG_LEVEL;
-#endif
-
extern void irda_proc_register(void);
extern void irda_proc_unregister(void);
extern int irda_sysctl_register(void);
extern void irda_sysctl_unregister(void);
-extern void irda_proto_init(struct net_proto *pro);
+extern int irda_proto_init(void);
extern void irda_proto_cleanup(void);
extern int irda_device_init(void);
@@ -78,26 +72,6 @@ extern irda_deflate_init();
#endif /* CONFIG_IRDA_DEFLATE */
#endif /* CONFIG_IRDA_COMPRESSION */
-static int irda_open(struct inode * inode, struct file *file);
-static int irda_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg);
-static int irda_close(struct inode *inode, struct file *file);
-static ssize_t irda_read(struct file *file, char *buffer, size_t count,
- loff_t *noidea);
-static ssize_t irda_write(struct file *file, const char *buffer,
- size_t count, loff_t *noidea);
-static u_int irda_poll(struct file *file, poll_table *wait);
-
-static struct file_operations irda_fops = {
- owner: THIS_MODULE,
- read: irda_read,
- write: irda_write,
- poll: irda_poll,
- ioctl: irda_ioctl,
- open: irda_open,
- release: irda_close,
-};
-
/* IrTTP */
EXPORT_SYMBOL(irttp_open_tsap);
EXPORT_SYMBOL(irttp_close_tsap);
@@ -114,7 +88,6 @@ EXPORT_SYMBOL(irttp_dup);
EXPORT_SYMBOL(irda_debug);
#endif
EXPORT_SYMBOL(irda_notify_init);
-EXPORT_SYMBOL(irmanager_notify);
EXPORT_SYMBOL(irda_lock);
#ifdef CONFIG_PROC_FS
EXPORT_SYMBOL(proc_irda);
@@ -145,6 +118,8 @@ EXPORT_SYMBOL(irias_new_octseq_value);
/* IrLMP */
EXPORT_SYMBOL(irlmp_discovery_request);
+EXPORT_SYMBOL(irlmp_get_discoveries);
+EXPORT_SYMBOL(sysctl_discovery_timeout);
EXPORT_SYMBOL(irlmp_register_client);
EXPORT_SYMBOL(irlmp_unregister_client);
EXPORT_SYMBOL(irlmp_update_client);
@@ -168,6 +143,7 @@ EXPORT_SYMBOL(hashbin_new);
EXPORT_SYMBOL(hashbin_insert);
EXPORT_SYMBOL(hashbin_delete);
EXPORT_SYMBOL(hashbin_remove);
+EXPORT_SYMBOL(hashbin_remove_this);
EXPORT_SYMBOL(hashbin_get_next);
EXPORT_SYMBOL(hashbin_get_first);
@@ -208,14 +184,11 @@ EXPORT_SYMBOL(irtty_set_packet_mode);
int __init irda_init(void)
{
- MESSAGE("IrDA (tm) Protocols for Linux-2.3 (Dag Brattli)\n");
-
+ IRDA_DEBUG(0, __FUNCTION__ "()\n");
+
irlmp_init();
irlap_init();
-#ifdef MODULE
- irda_device_init(); /* Called by init/main.c when non-modular */
-#endif
iriap_init();
irttp_init();
@@ -225,17 +198,6 @@ int __init irda_init(void)
#ifdef CONFIG_SYSCTL
irda_sysctl_register();
#endif
- init_waitqueue_head(&irda.wait_queue);
- irda.dev.minor = MISC_DYNAMIC_MINOR;
- irda.dev.name = "irda";
- irda.dev.fops = &irda_fops;
-
- misc_register(&irda.dev);
-
- irda.in_use = FALSE;
-
- init_waitqueue_head(&irda.wait_queue);
-
/*
* Initialize modules that got compiled into the kernel
*/
@@ -256,11 +218,8 @@ int __init irda_init(void)
return 0;
}
-#ifdef MODULE
-void irda_cleanup(void)
+static void __exit irda_cleanup(void)
{
- misc_deregister(&irda.dev);
-
#ifdef CONFIG_SYSCTL
irda_sysctl_unregister();
#endif
@@ -279,7 +238,6 @@ void irda_cleanup(void)
/* Remove middle layer */
irlmp_cleanup();
}
-#endif /* MODULE */
/*
* Function irda_unlock (lock)
@@ -310,241 +268,8 @@ void irda_notify_init(notify_t *notify)
notify->connect_indication = NULL;
notify->disconnect_indication = NULL;
notify->flow_indication = NULL;
+ notify->status_indication = NULL;
notify->instance = NULL;
strncpy(notify->name, "Unknown", NOTIFY_MAX_NAME);
}
-/*
- * Function irda_execute_as_process (self, callback, param)
- *
- * If a layer needs to have a function executed with a process context,
- * then it can register the function here, and the function will then
- * be executed as fast as possible.
- *
- */
-void irda_execute_as_process( void *self, TODO_CALLBACK callback, __u32 param)
-{
- struct irda_todo *new;
- struct irmanager_event event;
-
- /* Make sure irmanager is running */
- if (!irda.in_use) {
- return;
- }
-
- /* Make new todo event */
- new = (struct irda_todo *) kmalloc( sizeof(struct irda_todo),
- GFP_ATOMIC);
- if ( new == NULL) {
- return;
- }
- memset( new, 0, sizeof( struct irda_todo));
-
- new->self = self;
- new->callback = callback;
- new->param = param;
-
- /* Queue todo */
- enqueue_last(&irda.todo_queue, (queue_t *) new);
-
- event.event = EVENT_NEED_PROCESS_CONTEXT;
-
- /* Notify the user space manager */
- irmanager_notify(&event);
-}
-
-/*
- * Function irmanger_notify (event)
- *
- * Send an event to the user space manager
- *
- */
-void irmanager_notify( struct irmanager_event *event)
-{
- struct irda_event *new;
-
- IRDA_DEBUG(4, __FUNCTION__ "()\n");
-
- /* Make sure irmanager is running */
- if (!irda.in_use) {
- return;
- }
-
- /* Make new IrDA Event */
- new = (struct irda_event *) kmalloc( sizeof(struct irda_event),
- GFP_ATOMIC);
- if ( new == NULL) {
- return;
- }
- memset(new, 0, sizeof( struct irda_event));
- new->event = *event;
-
- /* Queue event */
- enqueue_last(&irda.event_queue, (queue_t *) new);
-
- /* Wake up irmanager sleeping on read */
- wake_up_interruptible(&irda.wait_queue);
-}
-
-static int irda_open( struct inode * inode, struct file *file)
-{
- IRDA_DEBUG( 4, __FUNCTION__ "()\n");
-
- lock_kernel();
- if (irda.in_use) {
- unlock_kernel();
- IRDA_DEBUG(0, __FUNCTION__
- "(), irmanager is already running!\n");
- return -1;
- }
- irda.in_use = TRUE;
- unlock_kernel();
-
- return 0;
-}
-
-/*
- * Function irda_ioctl (inode, filp, cmd, arg)
- *
- * Ioctl, used by irmanager to ...
- *
- */
-static int irda_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
-{
- struct irda_todo *todo;
- int err = 0;
- int size = _IOC_SIZE(cmd);
-
- IRDA_DEBUG(4, __FUNCTION__ "()\n");
-
- 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 IRMGR_IOCTNPC:
- /* Got process context! */
- IRDA_DEBUG(4, __FUNCTION__ "(), got process context!\n");
-
- while ((todo = (struct irda_todo *) dequeue_first(
- &irda.todo_queue)) != NULL)
- {
- todo->callback(todo->self, todo->param);
-
- kfree(todo);
- }
- break;
-
- default:
- return -ENOIOCTLCMD;
- }
-
- return 0;
-}
-
-static int irda_close(struct inode *inode, struct file *file)
-{
- IRDA_DEBUG(4, __FUNCTION__ "()\n");
-
- lock_kernel();
- irda.in_use = FALSE;
- unlock_kernel();
-
- return 0;
-}
-
-static ssize_t irda_read(struct file *file, char *buffer, size_t count,
- loff_t *noidea)
-{
- struct irda_event *event;
- unsigned long flags;
- int len;
-
- IRDA_DEBUG(4, __FUNCTION__ "()\n");
-
- /* * Go to sleep and wait for event if there is no event to be read! */
- save_flags( flags);
- cli();
- if ( !irda.event_queue)
- interruptible_sleep_on( &irda.wait_queue);
- restore_flags(flags);
-
- /*
- * Ensure proper reaction to signals, and screen out
- * blocked signals (page 112. linux device drivers)
- */
- if (signal_pending( current))
- return -ERESTARTSYS;
-
- event = (struct irda_event *) dequeue_first( &irda.event_queue);
- if (!event)
- return 0;
-
- len = sizeof(struct irmanager_event);
- copy_to_user(buffer, &event->event, len);
-
- /* Finished with event */
- kfree(event);
-
- return len;
-}
-
-static ssize_t irda_write(struct file *file, const char *buffer,
- size_t count, loff_t *noidea)
-{
- IRDA_DEBUG(0, __FUNCTION__ "()\n");
-
- return 0;
-}
-
-static u_int irda_poll(struct file *file, poll_table *wait)
-{
- IRDA_DEBUG(0, __FUNCTION__ "(), Sorry not implemented yet!\n");
-
- return 0;
-}
-
-void irda_mod_inc_use_count(void)
-{
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
-}
-
-void irda_mod_dec_use_count(void)
-{
-#ifdef MODULE
- MOD_DEC_USE_COUNT;
-#endif
-}
-
-/*
- * Function irda_proc_modcount (inode, fill)
- *
- * Use by the proc file system functions to prevent the irda module
- * being removed while the use is standing in the net/irda directory
- */
-void irda_proc_modcount(struct inode *inode, int fill)
-{
-#ifdef MODULE
-#ifdef CONFIG_PROC_FS
- if (fill)
- MOD_INC_USE_COUNT;
- else
- MOD_DEC_USE_COUNT;
-#endif /* CONFIG_PROC_FS */
-#endif /* MODULE */
-}
-
-#ifdef MODULE
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("The Linux IrDA Protocol Subsystem");
-MODULE_PARM(irda_debug, "1l");
-module_exit(irda_proto_cleanup);
-#endif /* MODULE */
-
diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c
index 1efeedc69..4c6218527 100644
--- a/net/irda/irsysctl.c
+++ b/net/irda/irsysctl.c
@@ -29,10 +29,11 @@
#include <asm/segment.h>
#include <net/irda/irda.h>
+#include <net/irda/irias_object.h>
#define NET_IRDA 412 /* Random number */
enum { DISCOVERY=1, DEVNAME, COMPRESSION, DEBUG, SLOTS, DISCOVERY_TIMEOUT,
- SLOT_TIMEOUT };
+ SLOT_TIMEOUT, MAX_BAUD_RATE, MAX_INACTIVE_TIME };
extern int sysctl_discovery;
extern int sysctl_discovery_slots;
@@ -41,17 +42,35 @@ extern int sysctl_slot_timeout;
extern int sysctl_fast_poll_increase;
int sysctl_compression = 0;
extern char sysctl_devname[];
+extern int sysctl_max_baud_rate;
+extern int sysctl_max_inactive_time;
#ifdef CONFIG_IRDA_DEBUG
extern unsigned int irda_debug;
#endif
+static int do_devname(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ int ret;
+
+ ret = proc_dostring(table, write, filp, buffer, lenp);
+ if (ret == 0 && write) {
+ struct ias_value *val;
+
+ val = irias_new_string_value(sysctl_devname);
+ if (val)
+ irias_object_change_attribute("Device", "DeviceName", val);
+ }
+ return ret;
+}
+
/* One file */
static ctl_table irda_table[] = {
{ DISCOVERY, "discovery", &sysctl_discovery,
sizeof(int), 0644, NULL, &proc_dointvec },
{ DEVNAME, "devname", sysctl_devname,
- 65, 0644, NULL, &proc_dostring, &sysctl_string},
+ 65, 0644, NULL, &do_devname, &sysctl_string},
{ COMPRESSION, "compression", &sysctl_compression,
sizeof(int), 0644, NULL, &proc_dointvec },
#ifdef CONFIG_IRDA_DEBUG
@@ -68,6 +87,10 @@ static ctl_table irda_table[] = {
sizeof(int), 0644, NULL, &proc_dointvec },
{ SLOT_TIMEOUT, "slot_timeout", &sysctl_slot_timeout,
sizeof(int), 0644, NULL, &proc_dointvec },
+ { MAX_BAUD_RATE, "max_baud_rate", &sysctl_max_baud_rate,
+ sizeof(int), 0644, NULL, &proc_dointvec },
+ { MAX_INACTIVE_TIME, "max_inactive_time", &sysctl_max_inactive_time,
+ sizeof(int), 0644, NULL, &proc_dointvec },
{ 0 }
};
diff --git a/net/irda/irttp.c b/net/irda/irttp.c
index e9d120efb..645815835 100644
--- a/net/irda/irttp.c
+++ b/net/irda/irttp.c
@@ -156,6 +156,8 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
ttp_notify.disconnect_indication = irttp_disconnect_indication;
ttp_notify.data_indication = irttp_data_indication;
ttp_notify.udata_indication = irttp_udata_indication;
+ if(notify->status_indication != NULL)
+ ttp_notify.status_indication = irttp_status_indication;
ttp_notify.instance = self;
strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME);
@@ -185,7 +187,7 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
self->notify = *notify;
self->lsap = lsap;
- hashbin_insert(irttp->tsaps, (queue_t *) self, (int) self, NULL);
+ hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (int) self, NULL);
if (credit > TTP_MAX_QUEUE)
self->initial_credit = TTP_MAX_QUEUE;
@@ -608,6 +610,34 @@ static int irttp_data_indication(void *instance, void *sap,
}
/*
+ * Function irttp_status_indication (self, reason)
+ *
+ * Status_indication, just pass to the higher layer...
+ *
+ */
+void irttp_status_indication(void *instance,
+ LINK_STATUS link, LOCK_STATUS lock)
+{
+ struct tsap_cb *self;
+
+ IRDA_DEBUG(4, __FUNCTION__ "()\n");
+
+ self = (struct tsap_cb *) instance;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+ /*
+ * Inform service user if he has requested it
+ */
+ if (self->notify.status_indication != NULL)
+ self->notify.status_indication(self->notify.instance,
+ link, lock);
+ else
+ IRDA_DEBUG(2, __FUNCTION__ "(), no handler\n");
+}
+
+/*
* Function irttp_flow_request (self, command)
*
* This funtion could be used by the upper layers to tell IrTTP to stop
@@ -1002,7 +1032,7 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
skb_queue_head_init(&new->tx_queue);
skb_queue_head_init(&new->rx_fragments);
- hashbin_insert(irttp->tsaps, (queue_t *) new, (int) new, NULL);
+ hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (int) new, NULL);
return new;
}
diff --git a/net/irda/parameters.c b/net/irda/parameters.c
index b43bbd70e..2b013ff43 100644
--- a/net/irda/parameters.c
+++ b/net/irda/parameters.c
@@ -513,10 +513,7 @@ int irda_param_extract(void *self, __u8 *buf, int len, pi_param_info_t *info)
buf[0]);
/* Skip this parameter */
- n += (2 + buf[n+1]);
- len -= (2 + buf[n+1]);
-
- return 0; /* Continue */
+ return 2 + buf[n + 1]; /* Continue */
}
/* Lookup the info on how to parse this parameter */
@@ -532,10 +529,7 @@ int irda_param_extract(void *self, __u8 *buf, int len, pi_param_info_t *info)
if (!pi_minor_info->func) {
MESSAGE(__FUNCTION__"(), no handler for pi=%#x\n", buf[n]);
/* Skip this parameter */
- n += (2 + buf[n+1]);
- len -= (2 + buf[n+1]);
-
- return 0; /* Continue */
+ return 2 + buf[n + 1]; /* Continue */
}
/* Parse parameter value */
diff --git a/net/irda/qos.c b/net/irda/qos.c
index 10a7765cb..997b3e8ff 100644
--- a/net/irda/qos.c
+++ b/net/irda/qos.c
@@ -43,6 +43,21 @@
#define CI_BZIP2 27 /* Random pick */
#endif
+/*
+ * Maximum values of the baud rate we negociate with the other end.
+ * Most often, you don't have to change that, because Linux-IrDA will
+ * use the maximum offered by the link layer, which usually works fine.
+ * In some very rare cases, you may want to limit it to lower speeds...
+ */
+int sysctl_max_baud_rate = 16000000;
+/*
+ * Maximum value of the lap disconnect timer we negociate with the other end.
+ * Most often, the value below represent the best compromise, but some user
+ * may want to keep the LAP alive longuer or shorter in case of link failure.
+ * Remember that the threshold time (early warning) is fixed to 3s...
+ */
+int sysctl_max_inactive_time = 12;
+
static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
static int irlap_param_link_disconnect(void *instance, irda_param_t *parm,
int get);
@@ -55,6 +70,10 @@ static int irlap_param_additional_bofs(void *instance, irda_param_t *parm,
int get);
static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
int get);
+static int value_index(__u32 value, __u32 *array, int size);
+static __u32 byte_value(__u8 byte, __u32 *array);
+static __u32 index_value(int index, __u32 *array);
+static int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field);
__u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */
__u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000,
@@ -147,19 +166,31 @@ void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new)
*/
void irda_init_max_qos_capabilies(struct qos_info *qos)
{
+ int i;
/*
* These are the maximum supported values as specified on pages
* 39-43 in IrLAP
*/
+ /* Use sysctl to set some configurable values... */
+ /* Set configured max speed */
+ i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10,
+ &qos->baud_rate.bits);
+ sysctl_max_baud_rate = index_value(i, baud_rates);
+
+ /* Set configured max disc time */
+ i = value_lower_bits(sysctl_max_inactive_time, link_disc_times, 8,
+ &qos->link_disc_time.bits);
+ sysctl_max_inactive_time = index_value(i, link_disc_times);
+
/* LSB is first byte, MSB is second byte */
- qos->baud_rate.bits = 0x01ff;
+ qos->baud_rate.bits &= 0x03ff;
qos->window_size.bits = 0x7f;
qos->min_turn_time.bits = 0xff;
qos->max_turn_time.bits = 0x0f;
qos->data_size.bits = 0x3f;
- qos->link_disc_time.bits = 0xff;
+ qos->link_disc_time.bits &= 0xff;
qos->additional_bofs.bits = 0xff;
#ifdef CONFIG_IRDA_COMPRESSION
@@ -197,7 +228,7 @@ void irlap_adjust_qos_settings(struct qos_info *qos)
* The data size must be adjusted according to the baud rate and max
* turn time
*/
- index = value_index(qos->data_size.value, data_sizes);
+ index = value_index(qos->data_size.value, data_sizes, 6);
line_capacity = irlap_max_line_capacity(qos->baud_rate.value,
qos->max_turn_time.value);
@@ -537,8 +568,8 @@ __u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time)
IRDA_DEBUG(2, __FUNCTION__ "(), speed=%d, max_turn_time=%d\n",
speed, max_turn_time);
- i = value_index(speed, baud_rates);
- j = value_index(max_turn_time, max_turn_times);
+ i = value_index(speed, baud_rates, 10);
+ j = value_index(max_turn_time, max_turn_times, 4);
ASSERT(((i >=0) && (i <=10)), return 0;);
ASSERT(((j >=0) && (j <=4)), return 0;);
@@ -574,7 +605,7 @@ __u32 irlap_min_turn_time_in_bytes(__u32 speed, __u32 min_turn_time)
return bytes;
}
-__u32 byte_value(__u8 byte, __u32 *array)
+static __u32 byte_value(__u8 byte, __u32 *array)
{
int index;
@@ -602,20 +633,19 @@ int msb_index (__u16 word)
msb >>=1;
index--;
}
-
return index;
}
/*
- * Function value_index (value, array)
+ * Function value_index (value, array, size)
*
* Returns the index to the value in the specified array
*/
-int value_index(__u32 value, __u32 *array)
+static int value_index(__u32 value, __u32 *array, int size)
{
int i;
- for (i=0;i<8;i++)
+ for (i=0; i < size; i++)
if (array[i] == value)
break;
return i;
@@ -627,11 +657,38 @@ int value_index(__u32 value, __u32 *array)
* Returns value to index in array, easy!
*
*/
-__u32 index_value(int index, __u32 *array)
+static __u32 index_value(int index, __u32 *array)
{
return array[index];
}
+/*
+ * Function value_lower_bits (value, array)
+ *
+ * Returns a bit field marking all possibility lower than value.
+ * We may need a "value_higher_bits" in the future...
+ */
+static int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field)
+{
+ int i;
+ __u16 mask = 0x1;
+ __u16 result = 0x0;
+
+ for (i=0; i < size; i++) {
+ /* Add the current value to the bit field, shift mask */
+ result |= mask;
+ mask <<= 1;
+ /* Finished ? */
+ if (array[i] >= value)
+ break;
+ }
+ /* Send back a valid index */
+ if(i >= size)
+ i = size - 1; /* Last item */
+ *field = result;
+ return i;
+}
+
void irda_qos_bits_to_value(struct qos_info *qos)
{
int index;
@@ -667,10 +724,3 @@ void irda_qos_bits_to_value(struct qos_info *qos)
qos->compression.value = 0;
#endif
}
-
-
-
-
-
-
-
diff --git a/net/irda/timer.c b/net/irda/timer.c
index 7625eecfc..ac80c1a23 100644
--- a/net/irda/timer.c
+++ b/net/irda/timer.c
@@ -24,6 +24,7 @@
********************************************************************/
#include <asm/system.h>
+#include <linux/config.h>
#include <linux/delay.h>
#include <net/irda/timer.h>
@@ -99,6 +100,25 @@ void irlap_start_mbusy_timer(struct irlap_cb *self)
(void *) self, irlap_media_busy_expired);
}
+void irlap_stop_mbusy_timer(struct irlap_cb *self)
+{
+ /* If timer is activated, kill it! */
+ if(timer_pending(&self->media_busy_timer))
+ del_timer(&self->media_busy_timer);
+
+#ifdef CONFIG_IRDA_ULTRA
+ /* Send any pending Ultra frames if any */
+ if (!skb_queue_empty(&self->txq_ultra))
+ /* Note : we don't send the frame, just post an event.
+ * Frames will be sent only if we are in NDM mode (see
+ * irlap_event.c).
+ * Also, moved this code from irlap_media_busy_expired()
+ * to here to catch properly all cases...
+ * Jean II */
+ irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);
+#endif /* CONFIG_IRDA_ULTRA */
+}
+
void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout)
{
irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
@@ -117,6 +137,13 @@ void irlmp_start_idle_timer(struct lap_cb *self, int timeout)
irlmp_idle_timer_expired);
}
+void irlmp_stop_idle_timer(struct lap_cb *self)
+{
+ /* If timer is activated, kill it! */
+ if(timer_pending(&self->idle_timer))
+ del_timer(&self->idle_timer);
+}
+
/*
* Function irlap_slot_timer_expired (data)
*
@@ -210,8 +237,5 @@ void irlap_media_busy_expired(void* data)
ASSERT(self != NULL, return;);
irda_device_set_media_busy(self->netdev, FALSE);
-
- /* Send any pending Ultra frames if any */
- if (!skb_queue_empty(&self->txq_ultra))
- irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);
+ /* Note : will deal with Ultra frames */
}
diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c
index 623328af1..a9f6d7328 100644
--- a/net/irda/wrapper.c
+++ b/net/irda/wrapper.c
@@ -69,6 +69,7 @@ static void (*state[])(struct net_device *dev, struct net_device_stats *stats,
*/
int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
{
+ struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
int xbofs;
int i;
int n;
@@ -85,7 +86,8 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
* Send XBOF's for required min. turn time and for the negotiated
* additional XBOFS
*/
- if (((struct irda_skb_cb *)(skb->cb))->magic != LAP_MAGIC) {
+
+ if (cb->magic != LAP_MAGIC) {
/*
* This will happen for all frames sent from user-space.
* Nothing to worry about, but we set the default number of
@@ -94,7 +96,7 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
IRDA_DEBUG(1, __FUNCTION__ "(), wrong magic in skb!\n");
xbofs = 10;
} else
- xbofs = ((struct irda_skb_cb *)(skb->cb))->xbofs;
+ xbofs = cb->xbofs + cb->xbofs_delay;
IRDA_DEBUG(4, __FUNCTION__ "(), xbofs=%d\n", xbofs);
@@ -287,6 +289,8 @@ static void state_link_escape(struct net_device *dev,
{
switch (byte) {
case BOF: /* New frame? */
+ IRDA_DEBUG(1, __FUNCTION__
+ "(), Discarding incomplete frame\n");
rx_buff->state = BEGIN_FRAME;
irda_device_set_media_busy(dev, TRUE);
break;
@@ -328,6 +332,8 @@ static void state_inside_frame(struct net_device *dev,
switch (byte) {
case BOF: /* New frame? */
+ IRDA_DEBUG(1, __FUNCTION__
+ "(), Discarding incomplete frame\n");
rx_buff->state = BEGIN_FRAME;
irda_device_set_media_busy(dev, TRUE);
break;