patch-2.4.0-test11 linux/net/irda/af_irda.c
Next file: linux/net/irda/compressors/irda_deflate.c
Previous file: linux/net/irda/Makefile
Back to the patch index
Back to the overall index
- Lines: 1110
- Date:
Sat Nov 11 18:11:23 2000
- Orig file:
v2.4.0-test10/linux/net/irda/af_irda.c
- Orig date:
Tue Oct 31 12:42:27 2000
diff -u --recursive --new-file v2.4.0-test10/linux/net/irda/af_irda.c linux/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 @@
#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 @@
/*
* 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 @@
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 @@
/*
* 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 @@
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 @@
*
* 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 @@
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_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 @@
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 @@
{
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 @@
/* 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 @@
*
* 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 @@
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 @@
sti();
+ /* At this point, IrLMP has assigned our source address */
+ self->saddr = irttp_get_saddr(self->tsap);
+
return 0;
}
@@ -957,7 +1083,7 @@
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 @@
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 @@
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 @@
}
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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");
+ if (get_user(opt, (int *)optval))
+ return -EFAULT;
- self->errno = -EHOSTUNREACH;
+ /* 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;
- /* Wake up any processes waiting for result */
- wake_up_interruptible(&self->ias_wait);
-
- 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 @@
/* 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
};
/*
+ * 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 @@
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 */
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)