Commit 7dce0496 authored by Jean Tourrilhes's avatar Jean Tourrilhes Committed by Linus Torvalds

[PATCH] : Discovery locking fixes

	o [CRITICA] Fix remaining locking problem with discovery log
	o [CRITICA] Don't call expiry callback under spinlock
	o [FEATURE] Simplify/cleanup/optimise discovery/expiry code
parent 8d82694d
......@@ -52,8 +52,8 @@ struct irda_sock {
__u8 max_header_size;
struct qos_info qos_tx;
__u16 mask; /* Hint bits mask */
__u16 hints; /* Hint bits */
__u16_host_order mask; /* Hint bits mask */
__u16_host_order hints; /* Hint bits */
void *ckey; /* IrLMP client handle */
void *skey; /* IrLMP service handle */
......@@ -63,7 +63,7 @@ struct irda_sock {
struct ias_value *ias_result; /* Result of remote IAS query */
hashbin_t *cachelog; /* Result of discovery query */
struct discovery_t *cachediscovery; /* Result of selective discovery query */
__u32 cachedaddr; /* Result of selective discovery query */
int nslots; /* Number of slots to use for discovery */
......
......@@ -46,12 +46,20 @@
* little endian format. A little endian machine stores MSB of word in
* byte[1] and LSB in byte[0]. A big endian machine stores MSB in byte[0]
* and LSB in byte[1].
*
* This structure is used in the code for things that are endian neutral
* but that fit in a word so that we can manipulate them efficiently.
* By endian neutral, I mean things that are really an array of bytes,
* and always used as such, for example the hint bits. Jean II
*/
typedef union {
__u16 word;
__u8 byte[2];
} __u16_host_order;
/* Same purpose, different application */
#define u16ho(array) (* ((__u16 *) array))
/* Types of discovery */
typedef enum {
DISCOVERY_LOG, /* What's in our discovery log */
......@@ -62,30 +70,31 @@ typedef enum {
#define NICKNAME_MAX_LEN 21
/* Basic discovery information about a peer */
typedef struct irda_device_info discinfo_t; /* linux/irda.h */
/*
* The DISCOVERY structure is used for both discovery requests and responses
*/
typedef struct discovery_t {
irda_queue_t q; /* Must be first! */
__u32 saddr; /* Which link the device was discovered */
__u32 daddr; /* Remote device address */
LAP_REASON condition; /* More info about the discovery */
__u16_host_order hints; /* Discovery hint bits */
__u8 charset; /* Encoding of nickname */
char nickname[22]; /* The name of the device (21 bytes + \0) */
discinfo_t data; /* Basic discovery information */
int name_len; /* Lenght of nickname */
int gen_addr_bit; /* Need to generate a new device address? */
int nslots; /* Number of slots to use when discovering */
unsigned long timestamp; /* Time discovered */
unsigned long first_timestamp; /* First time discovered */
LAP_REASON condition; /* More info about the discovery */
int gen_addr_bit; /* Need to generate a new device
* address? */
int nslots; /* Number of slots to use when
* discovering */
unsigned long timestamp; /* Last time discovered */
unsigned long firststamp; /* First time discovered */
} discovery_t;
void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *discovery);
void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log);
void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force);
struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 mask);
struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
__u16 mask, int old_entries);
#endif
......@@ -34,7 +34,7 @@
#include <net/irda/irlan_event.h>
void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout);
void irlan_client_discovery_indication(discovery_t *, DISCOVERY_MODE, void *);
void irlan_client_discovery_indication(discinfo_t *, DISCOVERY_MODE, void *);
void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr);
void irlan_client_open_ctrl_tsap( struct irlan_cb *self);
......
......@@ -58,7 +58,7 @@
#define LM_IDLE_TIMEOUT 2*HZ /* 2 seconds for now */
typedef enum {
S_PNP,
S_PNP = 0,
S_PDA,
S_COMPUTER,
S_PRINTER,
......@@ -72,22 +72,24 @@ typedef enum {
S_END,
} SERVICE;
typedef void (*DISCOVERY_CALLBACK1) (discovery_t *, DISCOVERY_MODE, void *);
typedef void (*DISCOVERY_CALLBACK2) (hashbin_t *, void *);
/* For selective discovery */
typedef void (*DISCOVERY_CALLBACK1) (discinfo_t *, DISCOVERY_MODE, void *);
/* For expiry (the same) */
typedef void (*DISCOVERY_CALLBACK2) (discinfo_t *, DISCOVERY_MODE, void *);
typedef struct {
irda_queue_t queue; /* Must be first */
__u16 hints; /* Hint bits */
__u16_host_order hints; /* Hint bits */
} irlmp_service_t;
typedef struct {
irda_queue_t queue; /* Must be first */
__u16 hint_mask;
__u16_host_order hint_mask;
DISCOVERY_CALLBACK1 disco_callback; /* Selective discovery */
DISCOVERY_CALLBACK1 expir_callback; /* Selective expiration */
DISCOVERY_CALLBACK2 expir_callback; /* Selective expiration */
void *priv; /* Used to identify client */
} irlmp_client_t;
......@@ -199,11 +201,11 @@ __u16 irlmp_service_to_hint(int service);
void *irlmp_register_service(__u16 hints);
int irlmp_unregister_service(void *handle);
void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
DISCOVERY_CALLBACK1 expir_clb, void *priv);
DISCOVERY_CALLBACK2 expir_clb, void *priv);
int irlmp_unregister_client(void *handle);
int irlmp_update_client(void *handle, __u16 hint_mask,
DISCOVERY_CALLBACK1 disco_clb,
DISCOVERY_CALLBACK1 expir_clb, void *priv);
DISCOVERY_CALLBACK2 expir_clb, void *priv);
void irlmp_register_link(struct irlap_cb *, __u32 saddr, notify_t *);
void irlmp_unregister_link(__u32 saddr);
......@@ -222,11 +224,11 @@ int irlmp_disconnect_request(struct lsap_cb *, struct sk_buff *userdata);
void irlmp_discovery_confirm(hashbin_t *discovery_log, DISCOVERY_MODE mode);
void irlmp_discovery_request(int nslots);
struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots);
discinfo_t *irlmp_get_discoveries(int *pn, __u16 mask, int nslots);
void irlmp_do_expiry(void);
void irlmp_do_discovery(int nslots);
discovery_t *irlmp_get_discovery_response(void);
void irlmp_discovery_expiry(discovery_t *expiry);
void irlmp_discovery_expiry(discinfo_t *expiry, int number);
int irlmp_data_request(struct lsap_cb *, struct sk_buff *);
void irlmp_data_indication(struct lsap_cb *, struct sk_buff *);
......
......@@ -401,11 +401,10 @@ static void irda_getvalue_confirm(int result, __u16 obj_id,
*
* 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...
* IrLMP is telling us that this node is new and matching our hint bit
* filter. Wake up any process waiting for answer...
*/
static void irda_selective_discovery_indication(discovery_t *discovery,
static void irda_selective_discovery_indication(discinfo_t *discovery,
DISCOVERY_MODE mode,
void *priv)
{
......@@ -419,18 +418,8 @@ static void irda_selective_discovery_indication(discovery_t *discovery,
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;
self->cachedaddr = discovery->daddr;
/* Wake up process if its waiting for device to be discovered */
wake_up_interruptible(&self->query_wait);
......@@ -455,7 +444,7 @@ static void irda_discovery_timeout(u_long priv)
/* Nothing for the caller */
self->cachelog = NULL;
self->cachediscovery = NULL;
self->cachedaddr = 0;
self->errno = -ETIME;
/* Wake up process if its still waiting... */
......@@ -627,7 +616,7 @@ 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)
{
struct irda_device_info *discoveries; /* Copy of the discovery log */
discinfo_t *discoveries; /* Copy of the discovery log */
int number; /* Number of nodes in the log */
int i;
int err = -ENETUNREACH;
......@@ -642,7 +631,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
* 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, self->nslots);
discoveries = irlmp_get_discoveries(&number, self->mask.word,
self->nslots);
/* Check if the we got some results */
if (discoveries == NULL)
return -ENETUNREACH; /* No nodes discovered */
......@@ -1137,7 +1127,7 @@ static int irda_create(struct socket *sock, int protocol)
/* Register as a client with IrLMP */
self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
self->mask = 0xffff;
self->mask.word = 0xffff;
self->rx_flow = self->tx_flow = FLOW_START;
self->nslots = DISCOVERY_DEFAULT_SLOTS;
self->daddr = DEV_ADDR_ANY; /* Until we get connected */
......@@ -1997,6 +1987,7 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
if (optlen < sizeof(int))
return -EINVAL;
/* The input is really a (__u8 hints[2]), easier as an int */
if (get_user(opt, (int *)optval))
return -EFAULT;
......@@ -2015,16 +2006,17 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
if (optlen < sizeof(int))
return -EINVAL;
/* The input is really a (__u8 hints[2]), easier as an int */
if (get_user(opt, (int *)optval))
return -EFAULT;
/* Set the new hint mask */
self->mask = (__u16) opt;
self->mask.word = (__u16) opt;
/* Mask out extension bits */
self->mask &= 0x7f7f;
self->mask.word &= 0x7f7f;
/* Check if no bits */
if(!self->mask)
self->mask = 0xFFFF;
if(!self->mask.word)
self->mask.word = 0xFFFF;
break;
default:
......@@ -2115,7 +2107,7 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
switch (optname) {
case IRLMP_ENUMDEVICES:
/* Ask lmp for the current discovery log */
discoveries = irlmp_get_discoveries(&list.len, self->mask,
discoveries = irlmp_get_discoveries(&list.len, self->mask.word,
self->nslots);
/* Check if the we got some results */
if (discoveries == NULL)
......@@ -2347,7 +2339,7 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
return -EFAULT;
/* Tell IrLMP we want to be notified */
irlmp_update_client(self->ckey, self->mask,
irlmp_update_client(self->ckey, self->mask.word,
irda_selective_discovery_indication,
NULL, (void *) self);
......@@ -2355,7 +2347,7 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
irlmp_discovery_request(self->nslots);
/* Wait until a node is discovered */
if (!self->cachediscovery) {
if (!self->cachedaddr) {
int ret = 0;
IRDA_DEBUG(1, "%s(), nothing discovered yet, going to sleep...\n", __FUNCTION__);
......@@ -2370,7 +2362,7 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
/* Wait for IR-LMP to call us back */
__wait_event_interruptible(self->query_wait,
(self->cachediscovery!=NULL || self->errno==-ETIME),
(self->cachedaddr != 0 || self->errno == -ETIME),
ret);
/* If watchdog is still activated, kill it! */
......@@ -2387,19 +2379,25 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
__FUNCTION__);
/* Tell IrLMP that we have been notified */
irlmp_update_client(self->ckey, self->mask, NULL, NULL, NULL);
irlmp_update_client(self->ckey, self->mask.word,
NULL, NULL, NULL);
/* Check if the we got some results */
if (!self->cachediscovery)
if (!self->cachedaddr)
return -EAGAIN; /* Didn't find any devices */
daddr = self->cachedaddr;
/* Cleanup */
self->cachediscovery = NULL;
self->cachedaddr = 0;
/* 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...
/* We return the daddr of the device that trigger the
* wakeup. As irlmp pass us only the new devices, we
* are sure that it's not an old device.
* If the user want more details, he should query
* the whole discovery log and pick one device...
*/
if (put_user(daddr, (int *)optval))
return -EFAULT;
break;
default:
return -ENOPROTOOPT;
......
......@@ -59,7 +59,7 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
unsigned long flags;
/* Set time of first discovery if node is new (see below) */
new->first_timestamp = new->timestamp;
new->firststamp = new->timestamp;
spin_lock_irqsave(&cachelog->hb_spinlock, flags);
......@@ -76,24 +76,24 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
/* 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)))
if ((node->data.saddr == new->data.saddr) &&
((node->data.daddr == new->data.daddr) ||
(strcmp(node->data.info, new->data.info) == 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)
/* Check if hints bits are unchanged */
if(u16ho(node->data.hints) == u16ho(new->data.hints))
/* Set time of first discovery for this node */
new->first_timestamp = node->first_timestamp;
new->firststamp = node->firststamp;
kfree(node);
}
}
/* Insert the new and updated version */
hashbin_insert(cachelog, (irda_queue_t *) new, new->daddr, NULL);
hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL);
spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
}
......@@ -147,27 +147,50 @@ void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
*/
void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
{
discovery_t *discovery, *curr;
discovery_t * discovery;
discovery_t * curr;
unsigned long flags;
discinfo_t * buffer = NULL;
int n; /* Size of the full log */
int i = 0; /* How many we expired */
ASSERT(log != NULL, return;);
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
spin_lock_irqsave(&log->hb_spinlock, flags);
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
curr = discovery;
/* Be sure to be one item ahead */
curr = discovery;
discovery = (discovery_t *) hashbin_get_next(log);
/* Test if it's time to expire this discovery */
if ((curr->saddr == saddr) &&
if ((curr->data.saddr == saddr) &&
(force ||
((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT)))
{
/* Tell IrLMP and registered clients about it */
irlmp_discovery_expiry(curr);
/* Create buffer as needed.
* As this function get called a lot and most time
* we don't have anything to put in the log (we are
* quite picky), we can save a lot of overhead
* by not calling kmalloc. Jean II */
if(buffer == NULL) {
/* 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(&log->hb_spinlock, flags);
return;
}
}
/* Copy discovery information */
memcpy(&(buffer[i]), &(curr->data),
sizeof(discinfo_t));
i++;
/* Remove it from the log */
curr = hashbin_remove_this(log, (irda_queue_t *) curr);
if (curr)
......@@ -175,9 +198,23 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
}
}
/* Drop the spinlock before calling the higher layers, as
* we can't guarantee they won't call us back and create a
* deadlock. We will work on our own private data, so we
* don't care to be interupted. - Jean II */
spin_unlock_irqrestore(&log->hb_spinlock, flags);
if(buffer == NULL)
return;
/* Tell IrLMP and registered clients about it */
irlmp_discovery_expiry(buffer, i);
/* Free up our buffer */
kfree(buffer);
}
#if 0
/*
* Function irlmp_dump_discoveries (log)
*
......@@ -193,13 +230,14 @@ void irlmp_dump_discoveries(hashbin_t *log)
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
IRDA_DEBUG(0, "Discovery:\n");
IRDA_DEBUG(0, " daddr=%08x\n", discovery->daddr);
IRDA_DEBUG(0, " saddr=%08x\n", discovery->saddr);
IRDA_DEBUG(0, " nickname=%s\n", discovery->nickname);
IRDA_DEBUG(0, " daddr=%08x\n", discovery->data.daddr);
IRDA_DEBUG(0, " saddr=%08x\n", discovery->data.saddr);
IRDA_DEBUG(0, " nickname=%s\n", discovery->data.info);
discovery = (discovery_t *) hashbin_get_next(log);
}
}
#endif
/*
* Function irlmp_copy_discoveries (log, pn, mask)
......@@ -221,23 +259,36 @@ void irlmp_dump_discoveries(hashbin_t *log)
* Note : the client must kfree himself() the log...
* Jean II
*/
struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 mask)
struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
__u16 mask, int old_entries)
{
discovery_t * discovery;
unsigned long flags;
struct irda_device_info * buffer;
int i = 0;
int n;
discinfo_t * buffer = NULL;
int j_timeout = (sysctl_discovery_timeout * HZ);
int n; /* Size of the full log */
int i = 0; /* How many we picked */
ASSERT(pn != NULL, return NULL;);
ASSERT(log != NULL, return NULL;);
/* Check if log is empty */
if(log == NULL)
return NULL;
/* Save spin lock - spinlock should be discovery specific */
/* Save spin lock */
spin_lock_irqsave(&log->hb_spinlock, flags);
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
/* Mask out the ones we don't want :
* We want to match the discovery mask, and to get only
* the most recent one (unless we want old ones) */
if ((u16ho(discovery->data.hints) & mask) &&
((old_entries) ||
((jiffies - discovery->firststamp) < j_timeout)) ) {
/* Create buffer as needed.
* As this function get called a lot and most time
* we don't have anything to put in the log (we are
* quite picky), we can save a lot of overhead
* by not calling kmalloc. Jean II */
if(buffer == NULL) {
/* Create the client specific buffer */
n = HASHBIN_GET_SIZE(log);
buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
......@@ -246,18 +297,11 @@ struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 m
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);
memcpy(&(buffer[i]), &(discovery->data),
sizeof(discinfo_t));
i++;
}
discovery = (discovery_t *) hashbin_get_next(log);
......@@ -288,14 +332,14 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr)
d = (discovery_t *) hashbin_get_first(cachelog);
while (d != NULL) {
IRDA_DEBUG(1, "Discovery:\n");
IRDA_DEBUG(1, " daddr=%08x\n", d->daddr);
IRDA_DEBUG(1, " nickname=%s\n", d->nickname);
IRDA_DEBUG(1, " daddr=%08x\n", d->data.daddr);
IRDA_DEBUG(1, " nickname=%s\n", d->data.info);
if (strcmp(name, d->nickname) == 0) {
*saddr = d->saddr;
if (strcmp(name, d->data.info) == 0) {
*saddr = d->data.saddr;
spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
return d->daddr;
return d->data.daddr;
}
d = (discovery_t *) hashbin_get_next(cachelog);
}
......@@ -328,41 +372,41 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int length,
discovery = (discovery_t *) hashbin_get_first(cachelog);
while (( discovery != NULL) && (len < length)) {
len += sprintf(buf+len, "nickname: %s,", discovery->nickname);
len += sprintf(buf+len, "nickname: %s,", discovery->data.info);
len += sprintf(buf+len, " hint: 0x%02x%02x",
discovery->hints.byte[0],
discovery->hints.byte[1]);
discovery->data.hints[0],
discovery->data.hints[1]);
#if 0
if ( discovery->hints.byte[0] & HINT_PNP)
if ( discovery->data.hints[0] & HINT_PNP)
len += sprintf( buf+len, "PnP Compatible ");
if ( discovery->hints.byte[0] & HINT_PDA)
if ( discovery->data.hints[0] & HINT_PDA)
len += sprintf( buf+len, "PDA/Palmtop ");
if ( discovery->hints.byte[0] & HINT_COMPUTER)
if ( discovery->data.hints[0] & HINT_COMPUTER)
len += sprintf( buf+len, "Computer ");
if ( discovery->hints.byte[0] & HINT_PRINTER)
if ( discovery->data.hints[0] & HINT_PRINTER)
len += sprintf( buf+len, "Printer ");
if ( discovery->hints.byte[0] & HINT_MODEM)
if ( discovery->data.hints[0] & HINT_MODEM)
len += sprintf( buf+len, "Modem ");
if ( discovery->hints.byte[0] & HINT_FAX)
if ( discovery->data.hints[0] & HINT_FAX)
len += sprintf( buf+len, "Fax ");
if ( discovery->hints.byte[0] & HINT_LAN)
if ( discovery->data.hints[0] & HINT_LAN)
len += sprintf( buf+len, "LAN Access ");
if ( discovery->hints.byte[1] & HINT_TELEPHONY)
if ( discovery->data.hints[1] & HINT_TELEPHONY)
len += sprintf( buf+len, "Telephony ");
if ( discovery->hints.byte[1] & HINT_FILE_SERVER)
if ( discovery->data.hints[1] & HINT_FILE_SERVER)
len += sprintf( buf+len, "File Server ");
if ( discovery->hints.byte[1] & HINT_COMM)
if ( discovery->data.hints[1] & HINT_COMM)
len += sprintf( buf+len, "IrCOMM ");
if ( discovery->hints.byte[1] & HINT_OBEX)
if ( discovery->data.hints[1] & HINT_OBEX)
len += sprintf( buf+len, "IrOBEX ");
#endif
len += sprintf(buf+len, ", saddr: 0x%08x",
discovery->saddr);
discovery->data.saddr);
len += sprintf(buf+len, ", daddr: 0x%08x\n",
discovery->daddr);
discovery->data.daddr);
len += sprintf(buf+len, "\n");
......
......@@ -46,7 +46,7 @@
#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(discinfo_t *discovery,
DISCOVERY_MODE mode,
void *priv);
static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
......@@ -305,7 +305,7 @@ 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(discinfo_t *discovery,
DISCOVERY_MODE mode,
void *priv)
{
......
......@@ -145,7 +145,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(discinfo_t *discovery,
DISCOVERY_MODE mode,
void *priv)
{
......
......@@ -419,7 +419,7 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
info->s);
if (self->slot == info->s) {
discovery_rsp = irlmp_get_discovery_response();
discovery_rsp->daddr = info->daddr;
discovery_rsp->data.daddr = info->daddr;
irlap_send_discovery_xid_frame(self, info->S,
self->slot,
......@@ -576,7 +576,7 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
ASSERT(info->discovery != NULL, return -1;);
IRDA_DEBUG(4, "%s(), daddr=%08x\n", __FUNCTION__,
info->discovery->daddr);
info->discovery->data.daddr);
if (!self->discovery_log) {
WARNING("%s: discovery log is gone! "
......@@ -586,7 +586,7 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
}
hashbin_insert(self->discovery_log,
(irda_queue_t *) info->discovery,
info->discovery->daddr, NULL);
info->discovery->data.daddr, NULL);
/* Keep state */
/* irlap_next_state(self, LAP_QUERY); */
......@@ -704,7 +704,7 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
irlap_discovery_indication(self, info->discovery);
} else if ((info->s >= self->slot) && (!self->frame_sent)) {
discovery_rsp = irlmp_get_discovery_response();
discovery_rsp->daddr = info->daddr;
discovery_rsp->data.daddr = info->daddr;
irlap_send_discovery_xid_frame(self, info->S,
self->slot, FALSE,
......
......@@ -335,7 +335,7 @@ void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
if (command)
frame->daddr = cpu_to_le32(bcast);
else
frame->daddr = cpu_to_le32(discovery->daddr);
frame->daddr = cpu_to_le32(discovery->data.daddr);
switch (S) {
case 1:
......@@ -366,20 +366,20 @@ void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
if (!command || (frame->slotnr == 0xff)) {
int len;
if (discovery->hints.byte[0] & HINT_EXTENSION) {
if (discovery->data.hints[0] & HINT_EXTENSION) {
info = skb_put(skb, 2);
info[0] = discovery->hints.byte[0];
info[1] = discovery->hints.byte[1];
info[0] = discovery->data.hints[0];
info[1] = discovery->data.hints[1];
} else {
info = skb_put(skb, 1);
info[0] = discovery->hints.byte[0];
info[0] = discovery->data.hints[0];
}
info = skb_put(skb, 1);
info[0] = discovery->charset;
info[0] = discovery->data.charset;
len = IRDA_MIN(discovery->name_len, skb_tailroom(skb));
info = skb_put(skb, len);
memcpy(info, discovery->nickname, len);
memcpy(info, discovery->data.info, len);
}
irlap_queue_xmit(self, skb);
}
......@@ -422,24 +422,25 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
}
memset(discovery, 0, sizeof(discovery_t));
discovery->daddr = info->daddr;
discovery->saddr = self->saddr;
discovery->data.daddr = info->daddr;
discovery->data.saddr = self->saddr;
discovery->timestamp = jiffies;
IRDA_DEBUG(4, "%s(), daddr=%08x\n", __FUNCTION__, discovery->daddr);
IRDA_DEBUG(4, "%s(), daddr=%08x\n", __FUNCTION__,
discovery->data.daddr);
discovery_info = skb_pull(skb, sizeof(struct xid_frame));
/* Get info returned from peer */
discovery->hints.byte[0] = discovery_info[0];
discovery->data.hints[0] = discovery_info[0];
if (discovery_info[0] & HINT_EXTENSION) {
IRDA_DEBUG(4, "EXTENSION\n");
discovery->hints.byte[1] = discovery_info[1];
discovery->charset = discovery_info[2];
discovery->data.hints[1] = discovery_info[1];
discovery->data.charset = discovery_info[2];
text = (char *) &discovery_info[3];
} else {
discovery->hints.byte[1] = 0;
discovery->charset = discovery_info[1];
discovery->data.hints[1] = 0;
discovery->data.charset = discovery_info[1];
text = (char *) &discovery_info[2];
}
/*
......@@ -447,8 +448,8 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
* FCS bytes resides.
*/
skb->data[skb->len] = '\0';
strncpy(discovery->nickname, text, NICKNAME_MAX_LEN);
discovery->name_len = strlen(discovery->nickname);
strncpy(discovery->data.info, text, NICKNAME_MAX_LEN);
discovery->name_len = strlen(discovery->data.info);
info->discovery = discovery;
......@@ -523,18 +524,18 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
return;
}
discovery->daddr = info->daddr;
discovery->saddr = self->saddr;
discovery->data.daddr = info->daddr;
discovery->data.saddr = self->saddr;
discovery->timestamp = jiffies;
discovery->hints.byte[0] = discovery_info[0];
discovery->data.hints[0] = discovery_info[0];
if (discovery_info[0] & HINT_EXTENSION) {
discovery->hints.byte[1] = discovery_info[1];
discovery->charset = discovery_info[2];
discovery->data.hints[1] = discovery_info[1];
discovery->data.charset = discovery_info[2];
text = (char *) &discovery_info[3];
} else {
discovery->hints.byte[1] = 0;
discovery->charset = discovery_info[1];
discovery->data.hints[1] = 0;
discovery->data.charset = discovery_info[1];
text = (char *) &discovery_info[2];
}
/*
......@@ -542,8 +543,8 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
* FCS bytes resides.
*/
skb->data[skb->len] = '\0';
strncpy(discovery->nickname, text, NICKNAME_MAX_LEN);
discovery->name_len = strlen(discovery->nickname);
strncpy(discovery->data.info, text, NICKNAME_MAX_LEN);
discovery->name_len = strlen(discovery->data.info);
info->discovery = discovery;
} else
......
......@@ -401,8 +401,8 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
}
if (discovery) {
saddr = discovery->saddr;
daddr = discovery->daddr;
saddr = discovery->data.saddr;
daddr = discovery->data.daddr;
}
spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags);
}
......@@ -793,17 +793,17 @@ void irlmp_do_discovery(int nslots)
}
/* Construct new discovery info to be used by IrLAP, */
irlmp->discovery_cmd.hints.word = irlmp->hints.word;
u16ho(irlmp->discovery_cmd.data.hints) = irlmp->hints.word;
/*
* Set character set for device name (we use ASCII), and
* copy device name. Remember to make room for a \0 at the
* end
*/
irlmp->discovery_cmd.charset = CS_ASCII;
strncpy(irlmp->discovery_cmd.nickname, sysctl_devname,
irlmp->discovery_cmd.data.charset = CS_ASCII;
strncpy(irlmp->discovery_cmd.data.info, sysctl_devname,
NICKNAME_MAX_LEN);
irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.nickname);
irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.data.info);
irlmp->discovery_cmd.nslots = nslots;
/*
......@@ -827,10 +827,13 @@ void irlmp_do_discovery(int nslots)
*
* Do a discovery of devices in front of the computer
*
* If the caller has registered a client discovery callback, this
* allow him to receive the full content of the discovery log through
* this callback (as normally he will receive only new discoveries).
*/
void irlmp_discovery_request(int nslots)
{
/* Return current cached discovery log */
/* Return current cached discovery log (in full) */
irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_LOG);
/*
......@@ -854,6 +857,8 @@ void irlmp_discovery_request(int nslots)
*
* Return the current discovery log
*
* If discovery is not enabled, you should call this function again
* after 1 or 2 seconds (i.e. after discovery has been done).
*/
struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots)
{
......@@ -875,49 +880,8 @@ struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots)
}
/* Return current cached discovery log */
return(irlmp_copy_discoveries(irlmp->cachelog, pn, mask));
}
#if 0
/*
* Function irlmp_check_services (discovery)
*/
void irlmp_check_services(discovery_t *discovery)
{
struct irlmp_client *client;
__u8 *service_log;
__u8 service;
int i = 0;
IRDA_DEBUG(1, "IrDA Discovered: %s\n", discovery->info);
IRDA_DEBUG(1, " Services: ");
service_log = irlmp_hint_to_service(discovery->hints.byte);
if (!service_log)
return;
/*
* Check all services on the device
*/
while ((service = service_log[i++]) != S_END) {
IRDA_DEBUG( 4, "service=%02x\n", service);
client = hashbin_lock_find(irlmp->registry, service, NULL);
if (entry && entry->discovery_callback) {
IRDA_DEBUG( 4, "discovery_callback!\n");
entry->discovery_callback(discovery);
} else {
/* Don't notify about the ANY service */
if (service == S_ANY)
continue;
/*
* Found no clients for dealing with this service,
*/
}
}
kfree(service_log);
return(irlmp_copy_discoveries(irlmp->cachelog, pn, mask, TRUE));
}
#endif
/*
* Function irlmp_notify_client (log)
......@@ -935,7 +899,9 @@ static inline void
irlmp_notify_client(irlmp_client_t *client,
hashbin_t *log, DISCOVERY_MODE mode)
{
discovery_t *discovery;
discinfo_t *discoveries; /* Copy of the discovery log */
int number; /* Number of nodes in the log */
int i;
IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
......@@ -944,28 +910,36 @@ irlmp_notify_client(irlmp_client_t *client,
return;
/*
* Now, check all discovered devices (if any), and notify client
* only about the services that the client is interested in
* Note : most often, we will get called immediately following
* a discovery, so the log is not going to expire.
* On the other hand, comming here through irlmp_discovery_request()
* is *very* problematic - Jean II
* Can't use hashbin_find_next(), key is not unique. I'm running
* out of options :-( - Jean II
* Locking notes :
* the old code was manipulating the log directly, which was
* very racy. Now, we use copy_discoveries, that protects
* itself while dumping the log for us.
* The overhead of the copy is compensated by the fact that
* we only pass new discoveries in normal mode and don't
* pass the same old entry every 3s to the caller as we used
* to do (virtual function calling is expensive).
* Jean II
*/
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
IRDA_DEBUG(3, "discovery->daddr = 0x%08x\n", discovery->daddr);
/*
* Any common hint bits? Remember to mask away the extension
* bits ;-)
*/
if (client->hint_mask & discovery->hints.word & 0x7f7f)
client->disco_callback(discovery, mode, client->priv);
discovery = (discovery_t *) hashbin_get_next(log);
}
* Now, check all discovered devices (if any), and notify client
* only about the services that the client is interested in
* We also notify only about the new devices unless the caller
* explicity request a dump of the log. Jean II
*/
discoveries = irlmp_copy_discoveries(log, &number,
client->hint_mask.word,
(mode == DISCOVERY_LOG));
/* Check if the we got some results */
if (discoveries == NULL)
return; /* No nodes discovered */
/* Pass all entries to the listener */
for(i = 0; i < number; i++)
client->disco_callback(&(discoveries[i]), mode, client->priv);
/* Free up our buffer */
kfree(discoveries);
}
/*
......@@ -987,6 +961,7 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
if (!(HASHBIN_GET_SIZE(log)))
return;
/* For each client - notify callback may touch client list */
client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
(void *) &client_next) ) {
......@@ -1005,26 +980,34 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
* 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.
* Note : this is no longer called under discovery spinlock, so the
* client can do whatever he wants in the callback.
*/
void irlmp_discovery_expiry(discovery_t *expiry)
void irlmp_discovery_expiry(discinfo_t *expiries, int number)
{
irlmp_client_t *client;
irlmp_client_t *client_next;
int i;
IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
ASSERT(expiry != NULL, return;);
ASSERT(expiries != NULL, return;);
/* For each client - notify callback may touch client list */
client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
(void *) &client_next) ) {
/* Pass all entries to the listener */
for(i = 0; i < number; i++) {
/* Check if we should notify client */
if ((client->expir_callback) &&
(client->hint_mask & expiry->hints.word & 0x7f7f))
client->expir_callback(expiry, EXPIRY_TIMEOUT,
(client->hint_mask.word & u16ho(expiries[i].hints)
& 0x7f7f) )
client->expir_callback(&(expiries[i]),
EXPIRY_TIMEOUT,
client->priv);
}
/* Next client */
client = client_next;
......@@ -1043,18 +1026,18 @@ discovery_t *irlmp_get_discovery_response()
ASSERT(irlmp != NULL, return NULL;);
irlmp->discovery_rsp.hints.word = irlmp->hints.word;
u16ho(irlmp->discovery_rsp.data.hints) = irlmp->hints.word;
/*
* Set character set for device name (we use ASCII), and
* copy device name. Remember to make room for a \0 at the
* end
*/
irlmp->discovery_rsp.charset = CS_ASCII;
irlmp->discovery_rsp.data.charset = CS_ASCII;
strncpy(irlmp->discovery_rsp.nickname, sysctl_devname,
strncpy(irlmp->discovery_rsp.data.info, sysctl_devname,
NICKNAME_MAX_LEN);
irlmp->discovery_rsp.name_len = strlen(irlmp->discovery_rsp.nickname);
irlmp->discovery_rsp.name_len = strlen(irlmp->discovery_rsp.data.info);
return &irlmp->discovery_rsp;
}
......@@ -1291,6 +1274,7 @@ void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow)
}
}
#if 0
/*
* Function irlmp_hint_to_service (hint)
*
......@@ -1365,6 +1349,21 @@ __u8 *irlmp_hint_to_service(__u8 *hint)
return service;
}
#endif
const __u16 service_hint_mapping[S_END][2] = {
{ HINT_PNP, 0 }, /* S_PNP */
{ HINT_PDA, 0 }, /* S_PDA */
{ HINT_COMPUTER, 0 }, /* S_COMPUTER */
{ HINT_PRINTER, 0 }, /* S_PRINTER */
{ HINT_MODEM, 0 }, /* S_MODEM */
{ HINT_FAX, 0 }, /* S_FAX */
{ HINT_LAN, 0 }, /* S_LAN */
{ HINT_EXTENSION, HINT_TELEPHONY }, /* S_TELEPHONY */
{ HINT_EXTENSION, HINT_COMM }, /* S_COMM */
{ HINT_EXTENSION, HINT_OBEX }, /* S_OBEX */
{ 0xFF, 0xFF }, /* S_ANY */
};
/*
* Function irlmp_service_to_hint (service)
......@@ -1377,46 +1376,9 @@ __u16 irlmp_service_to_hint(int service)
{
__u16_host_order hint;
hint.word = 0;
hint.byte[0] = service_hint_mapping[service][0];
hint.byte[1] = service_hint_mapping[service][1];
switch (service) {
case S_PNP:
hint.byte[0] |= HINT_PNP;
break;
case S_PDA:
hint.byte[0] |= HINT_PDA;
break;
case S_COMPUTER:
hint.byte[0] |= HINT_COMPUTER;
break;
case S_PRINTER:
hint.byte[0] |= HINT_PRINTER;
break;
case S_MODEM:
hint.byte[0] |= HINT_PRINTER;
break;
case S_LAN:
hint.byte[0] |= HINT_LAN;
break;
case S_COMM:
hint.byte[0] |= HINT_EXTENSION;
hint.byte[1] |= HINT_COMM;
break;
case S_OBEX:
hint.byte[0] |= HINT_EXTENSION;
hint.byte[1] |= HINT_OBEX;
break;
case S_TELEPHONY:
hint.byte[0] |= HINT_EXTENSION;
hint.byte[1] |= HINT_TELEPHONY;
break;
case S_ANY:
hint.word = 0xffff;
break;
default:
IRDA_DEBUG( 1, "%s(), Unknown service!\n", __FUNCTION__);
break;
}
return hint.word;
}
......@@ -1438,7 +1400,7 @@ void *irlmp_register_service(__u16 hints)
IRDA_DEBUG(1, "%s(), Unable to kmalloc!\n", __FUNCTION__);
return 0;
}
service->hints = hints;
service->hints.word = hints;
hashbin_insert(irlmp->services, (irda_queue_t *) service,
(long) service, NULL);
......@@ -1481,7 +1443,7 @@ int irlmp_unregister_service(void *handle)
spin_lock_irqsave(&irlmp->services->hb_spinlock, flags);
service = (irlmp_service_t *) hashbin_get_first(irlmp->services);
while (service) {
irlmp->hints.word |= service->hints;
irlmp->hints.word |= service->hints.word;
service = (irlmp_service_t *)hashbin_get_next(irlmp->services);
}
......@@ -1499,7 +1461,7 @@ int irlmp_unregister_service(void *handle)
* Returns: handle > 0 on success, 0 on error
*/
void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
DISCOVERY_CALLBACK1 expir_clb, void *priv)
DISCOVERY_CALLBACK2 expir_clb, void *priv)
{
irlmp_client_t *client;
......@@ -1514,7 +1476,7 @@ void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
}
/* Register the details */
client->hint_mask = hint_mask;
client->hint_mask.word = hint_mask;
client->disco_callback = disco_clb;
client->expir_callback = expir_clb;
client->priv = priv;
......@@ -1535,7 +1497,7 @@ void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
*/
int irlmp_update_client(void *handle, __u16 hint_mask,
DISCOVERY_CALLBACK1 disco_clb,
DISCOVERY_CALLBACK1 expir_clb, void *priv)
DISCOVERY_CALLBACK2 expir_clb, void *priv)
{
irlmp_client_t *client;
......@@ -1548,7 +1510,7 @@ int irlmp_update_client(void *handle, __u16 hint_mask,
return -1;
}
client->hint_mask = hint_mask;
client->hint_mask.word = hint_mask;
client->disco_callback = disco_clb;
client->expir_callback = expir_clb;
client->priv = priv;
......
......@@ -1616,8 +1616,8 @@ irnet_discovervalue_confirm(int result,
*
* 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...
* Note : IrLMP take care of matching the hint mask for us, and also
* check if it is a "new" node for us...
*
* 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
......@@ -1633,7 +1633,7 @@ irnet_discovervalue_confirm(int result,
* is to messy, so we leave that to user space...
*/
static void
irnet_discovery_indication(discovery_t * discovery,
irnet_discovery_indication(discinfo_t * discovery,
DISCOVERY_MODE mode,
void * priv)
{
......@@ -1643,21 +1643,12 @@ irnet_discovery_indication(discovery_t * discovery,
DASSERT(priv == &irnet_server, , IRDA_OCB_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_OCB_INFO, "Discovered new IrNET/IrLAN node %s...\n",
discovery->nickname);
discovery->info);
/* Notify the control channel */
irnet_post_event(NULL, IRNET_DISCOVER,
discovery->saddr, discovery->daddr, discovery->nickname);
discovery->saddr, discovery->daddr, discovery->info);
DEXIT(IRDA_OCB_TRACE, "\n");
}
......@@ -1672,7 +1663,7 @@ irnet_discovery_indication(discovery_t * discovery,
* check if it is a "new" node...
*/
static void
irnet_expiry_indication(discovery_t * expiry,
irnet_expiry_indication(discinfo_t * expiry,
DISCOVERY_MODE mode,
void * priv)
{
......@@ -1683,11 +1674,11 @@ irnet_expiry_indication(discovery_t * expiry,
"Invalid instance (0x%X) !!!\n", (unsigned int) priv);
DEBUG(IRDA_OCB_INFO, "IrNET/IrLAN node %s expired...\n",
expiry->nickname);
expiry->info);
/* Notify the control channel */
irnet_post_event(NULL, IRNET_EXPIRE,
expiry->saddr, expiry->daddr, expiry->nickname);
expiry->saddr, expiry->daddr, expiry->info);
DEXIT(IRDA_OCB_TRACE, "\n");
}
......
......@@ -150,11 +150,11 @@ static void
void *);
#ifdef DISCOVERY_EVENTS
static void
irnet_discovery_indication(discovery_t *,
irnet_discovery_indication(discinfo_t *,
DISCOVERY_MODE,
void *);
static void
irnet_expiry_indication(discovery_t *,
irnet_expiry_indication(discinfo_t *,
DISCOVERY_MODE,
void *);
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment