Hi Dag,
I was fixing the last multilink bugs of IrNET, and I've had to
make a few changes to the IrDA stack to get everything proper. I took
advantage to nail a few suttle bugs, as usual.
Patch attached...
Enjoy...
Jean
diff -u -p linux/include/net/irda/irlmp.d4.h linux/include/net/irda/irlmp.h
--- linux/include/net/irda/irlmp.d4.h Tue Aug 29 11:46:24 2000
+++ linux/include/net/irda/irlmp.h Wed Aug 30 12:13:27 2000
@@ -85,8 +85,8 @@ typedef struct {
__u16 hint_mask;
- DISCOVERY_CALLBACK1 callback1;
- DISCOVERY_CALLBACK2 callback2;
+ DISCOVERY_CALLBACK1 disco_callback; /* Selective discovery */
+ DISCOVERY_CALLBACK1 expir_callback; /* Selective expiration */
void *priv; /* Used to identify client */
} irlmp_client_t;
@@ -192,11 +192,12 @@ void irlmp_close_lsap( struct lsap_cb *s
__u16 irlmp_service_to_hint(int service);
__u32 irlmp_register_service(__u16 hints);
int irlmp_unregister_service(__u32 handle);
-__u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 callback1,
- DISCOVERY_CALLBACK2 callback2, void *priv);
+__u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
+ DISCOVERY_CALLBACK1 expir_clb, void *priv);
int irlmp_unregister_client(__u32 handle);
int irlmp_update_client(__u32 handle, __u16 hint_mask,
- DISCOVERY_CALLBACK1, DISCOVERY_CALLBACK2, void *priv);
+ DISCOVERY_CALLBACK1 disco_clb,
+ DISCOVERY_CALLBACK1 expir_clb, void *priv);
void irlmp_register_link(struct irlap_cb *, __u32 saddr, notify_t *);
void irlmp_unregister_link(__u32 saddr);
@@ -218,6 +219,7 @@ void irlmp_discovery_request(int nslots)
struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask);
void irlmp_do_discovery(int nslots);
discovery_t *irlmp_get_discovery_response(void);
+void irlmp_discovery_expiry(discovery_t *expiry);
int irlmp_data_request(struct lsap_cb *, struct sk_buff *);
void irlmp_data_indication(struct lsap_cb *, struct sk_buff *);
diff -u -p linux/include/net/irda/irqueue.d4.h linux/include/net/irda/irqueue.h
--- linux/include/net/irda/irqueue.d4.h Tue Aug 29 17:54:53 2000
+++ linux/include/net/irda/irqueue.h Wed Aug 30 12:10:01 2000
@@ -89,6 +89,7 @@ void hashbin_insert(hashbin_t* hashb
void* hashbin_find(hashbin_t* hashbin, __u32 hashv, char* name);
void* hashbin_remove(hashbin_t* hashbin, __u32 hashv, char* name);
void* hashbin_remove_first(hashbin_t *hashbin);
+void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry);
irda_queue_t *hashbin_get_first(hashbin_t *hashbin);
irda_queue_t *hashbin_get_next(hashbin_t *hashbin);
diff -u -p linux/net/irda/af_irda.d4.c linux/net/irda/af_irda.c
--- linux/net/irda/af_irda.d4.c Tue Aug 29 10:13:55 2000
+++ linux/net/irda/af_irda.c Wed Aug 30 09:42:33 2000
@@ -366,33 +366,6 @@ static void irda_getvalue_confirm(int re
wake_up_interruptible(&self->query_wait);
}
-#if 0
-/* Obsolete */
-/*
- * Function irda_discovery_indication (log)
- *
- * Got a discovery log from IrLMP, wake up any process waiting for answer
- *
- */
-static void irda_discovery_indication(hashbin_t *log, void *priv)
-{
- struct irda_sock *self;
-
- IRDA_DEBUG(2, __FUNCTION__ "()\n");
-
- self = (struct irda_sock *) priv;
- if (!self) {
- WARNING(__FUNCTION__ "(), lost myself!\n");
- return;
- }
-
- self->cachelog = log;
-
- /* Wake up process if its waiting for device to be discovered */
- wake_up_interruptible(&self->query_wait);
-}
-#endif
-
/*
* Function irda_selective_discovery_indication (discovery)
*
@@ -2174,8 +2147,8 @@ static int irda_getsockopt(struct socket
/* Tell IrLMP we want to be notified */
irlmp_update_client(self->ckey, self->mask,
- irda_selective_discovery_indication, NULL,
- (void *) self);
+ irda_selective_discovery_indication,
+ NULL, (void *) self);
/* Do some discovery (and also return cached results) */
irlmp_discovery_request(self->nslots);
diff -u -p linux/net/irda/discovery.d4.c linux/net/irda/discovery.c
--- linux/net/irda/discovery.d4.c Tue Aug 29 15:22:05 2000
+++ linux/net/irda/discovery.c Tue Aug 29 18:17:12 2000
@@ -75,14 +75,15 @@ void irlmp_add_discovery(hashbin_t *cach
/* 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))
+
+ 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(cachelog, node->daddr, NULL);
+ 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 */
@@ -135,6 +136,8 @@ void irlmp_add_discovery_log(hashbin_t *
*
* 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)
{
@@ -153,10 +156,14 @@ void irlmp_expire_discoveries(hashbin_t
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);
}
@@ -298,23 +305,23 @@ __u32 irlmp_find_device(hashbin_t *cache
* 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",
@@ -355,7 +362,7 @@ int discovery_proc_read(char *buf, char
discovery = (discovery_t *) hashbin_get_next(cachelog);
}
- restore_flags(flags);
+ spin_unlock_irqrestore(&irlmp->log_lock, flags);
return len;
}
diff -u -p linux/net/irda/irlmp.d4.c linux/net/irda/irlmp.c
--- linux/net/irda/irlmp.d4.c Tue Aug 29 11:52:49 2000
+++ linux/net/irda/irlmp.c Wed Aug 30 10:03:09 2000
@@ -837,22 +837,18 @@ void irlmp_check_services(discovery_t *d
* o IrComm
* o IrLAN
* o Any socket (in any state - ouch, that may be a lot !)
- * The client may have defined one of two callback to be notified in case
- * of discovery, one callback for any discovery and one for partial/selective
- * discovery based on the hints that it passed to IrLMP.
+ * 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, client->priv);
-
/* Check if client wants or not partial/selective log (optimisation) */
- if (!client->callback1)
+ if (!client->disco_callback)
return;
/*
@@ -867,10 +863,9 @@ void irlmp_notify_client(irlmp_client_t
* 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, client->priv);
- }
+ if (client->hint_mask & discovery->hints.word & 0x7f7f)
+ client->disco_callback(discovery, client->priv);
+
discovery = (discovery_t *) hashbin_get_next(log);
}
}
@@ -903,9 +898,40 @@ void irlmp_discovery_confirm(hashbin_t *
}
/*
+ * 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()
@@ -1294,11 +1320,13 @@ int irlmp_unregister_service(__u32 handl
* 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, void *priv)
+__u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
+ DISCOVERY_CALLBACK1 expir_clb, void *priv)
{
irlmp_client_t *client;
__u32 handle;
@@ -1318,8 +1346,8 @@ __u32 irlmp_register_client(__u16 hint_m
/* 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, (irda_queue_t *) client, handle, NULL);
@@ -1336,8 +1364,8 @@ __u32 irlmp_register_client(__u16 hint_m
* Returns: 0 on success, -1 on error
*/
int irlmp_update_client(__u32 handle, __u16 hint_mask,
- DISCOVERY_CALLBACK1 callback1,
- DISCOVERY_CALLBACK2 callback2, void *priv)
+ DISCOVERY_CALLBACK1 disco_clb,
+ DISCOVERY_CALLBACK1 expir_clb, void *priv)
{
irlmp_client_t *client;
@@ -1351,8 +1379,8 @@ int irlmp_update_client(__u32 handle, __
}
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 -u -p linux/net/irda/irlap_event.d4.c linux/net/irda/irlap_event.c
--- linux/net/irda/irlap_event.d4.c Tue Aug 29 15:48:02 2000
+++ linux/net/irda/irlap_event.c Tue Aug 29 16:05:03 2000
@@ -492,6 +492,30 @@ static int irlap_state_query(struct irla
/* irlap_next_state(self, LAP_QUERY); */
break;
+ case RECV_DISCOVERY_XID_CMD:
+ /* Yes, it is possible to receive those frames in this mode.
+ * This would happen is both discoveries are just slightly
+ * offset (if they are in sync, all packets are lost).
+ * The big trouble when it happen is that passive 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. 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 event %d, %s\n",
+ event, irlap_event[event]);
+
+ /* Last discovery frame? */
+ 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
diff -u -p linux/net/irda/irlmp_frame.d4.c linux/net/irda/irlmp_frame.c
--- linux/net/irda/irlmp_frame.d4.c Tue Aug 29 16:12:07 2000
+++ linux/net/irda/irlmp_frame.c Tue Aug 29 16:16:27 2000
@@ -397,7 +397,7 @@ void irlmp_link_discovery_indication(str
/* If delay was activated, kill it! */
if(timer_pending(&disco_delay))
del_timer(&disco_delay);
- /* Set delay timer to expire in 0.5s. */
+ /* 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;
@@ -420,8 +420,8 @@ void irlmp_link_discovery_confirm(struct
ASSERT(self->magic == LMP_LAP_MAGIC, return;);
irlmp_add_discovery_log(irlmp->cachelog, log);
-
- /* If delay was activated, kill it! */
+
+ /* If discovery delay was activated, kill it! */
if(timer_pending(&disco_delay))
del_timer(&disco_delay);
diff -u -p linux/net/irda/irqueue.d4.c linux/net/irda/irqueue.c
--- linux/net/irda/irqueue.d4.c Tue Aug 29 17:28:07 2000
+++ linux/net/irda/irqueue.c Tue Aug 29 18:37:05 2000
@@ -381,6 +381,7 @@ void* hashbin_remove( hashbin_t* hashbin
hashv = hash( name );
bin = GET_HASHBIN( hashv );
+ /* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) {
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
@@ -448,6 +449,75 @@ void* hashbin_remove( hashbin_t* hashbin
else
return NULL;
+}
+
+/*
+ * 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;
}
/*
diff -u -p linux/net/irda/irmod.d4.c linux/net/irda/irmod.c
--- linux/net/irda/irmod.d4.c Tue Aug 29 17:53:41 2000
+++ linux/net/irda/irmod.c Tue Aug 29 17:54:11 2000
@@ -170,6 +170,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);