2005-04-17 02:20:36 +04:00
/*********************************************************************
2007-02-09 17:24:53 +03:00
*
2005-04-17 02:20:36 +04:00
* Filename : discovery . c
* Version : 0.1
* Description : Routines for handling discoveries at the IrLMP layer
* Status : Experimental .
* Author : Dag Brattli < dagb @ cs . uit . no >
* Created at : Tue Apr 6 15 : 33 : 50 1999
* Modified at : Sat Oct 9 17 : 11 : 31 1999
* Modified by : Dag Brattli < dagb @ cs . uit . no >
* Modified at : Fri May 28 3 : 11 CST 1999
* Modified by : Horst von Brand < vonbrand @ sleipnir . valparaiso . cl >
2007-02-09 17:24:53 +03:00
*
2005-04-17 02:20:36 +04:00
* Copyright ( c ) 1999 Dag Brattli , All Rights Reserved .
2007-02-09 17:24:53 +03:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
2005-04-17 02:20:36 +04:00
* the License , or ( at your option ) any later version .
2007-02-09 17:24:53 +03:00
*
2005-04-17 02:20:36 +04:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2007-02-09 17:24:53 +03:00
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston ,
2005-04-17 02:20:36 +04:00
* MA 02111 - 1307 USA
2007-02-09 17:24:53 +03:00
*
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/string.h>
# include <linux/socket.h>
2006-12-04 07:15:30 +03:00
# include <linux/fs.h>
2005-04-17 02:20:36 +04:00
# include <linux/seq_file.h>
# include <net/irda/irda.h>
# include <net/irda/irlmp.h>
# include <net/irda/discovery.h>
2008-05-14 10:25:57 +04:00
# include <asm/unaligned.h>
2005-04-17 02:20:36 +04:00
/*
* Function irlmp_add_discovery ( cachelog , discovery )
*
* 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 ;
/* Set time of first discovery if node is new (see below) */
new - > firststamp = new - > timestamp ;
spin_lock_irqsave ( & cachelog - > hb_spinlock , flags ) ;
2007-02-09 17:24:53 +03:00
/*
* Remove all discoveries of devices that has previously been
* discovered on the same link with the same name ( info ) , or the
2005-04-17 02:20:36 +04:00
* same daddr . We do this since some devices ( mostly PDAs ) change
* their device address between every discovery .
*/
discovery = ( discovery_t * ) hashbin_get_first ( cachelog ) ;
while ( discovery ! = NULL ) {
node = discovery ;
/* Be sure to stay one item ahead */
discovery = ( discovery_t * ) hashbin_get_next ( cachelog ) ;
if ( ( node - > data . saddr = = new - > data . saddr ) & &
2007-02-09 17:24:53 +03:00
( ( node - > data . daddr = = new - > data . daddr ) | |
2005-04-17 02:20:36 +04:00
( strcmp ( node - > data . info , new - > data . info ) = = 0 ) ) )
{
2007-02-09 17:24:53 +03:00
/* This discovery is a previous discovery
2005-04-17 02:20:36 +04:00
* from the same device , so just remove it
*/
hashbin_remove_this ( cachelog , ( irda_queue_t * ) node ) ;
/* Check if hints bits are unchanged */
2008-05-14 10:25:57 +04:00
if ( get_unaligned ( ( __u16 * ) node - > data . hints ) = = get_unaligned ( ( __u16 * ) new - > data . hints ) )
2005-04-17 02:20:36 +04:00
/* Set time of first discovery for this node */
new - > firststamp = node - > firststamp ;
kfree ( node ) ;
}
}
/* Insert the new and updated version */
hashbin_insert ( cachelog , ( irda_queue_t * ) new , new - > data . daddr , NULL ) ;
spin_unlock_irqrestore ( & cachelog - > hb_spinlock , flags ) ;
}
/*
* Function irlmp_add_discovery_log ( cachelog , log )
*
* Merge a disovery log into the cachelog .
*
*/
void irlmp_add_discovery_log ( hashbin_t * cachelog , hashbin_t * log )
{
discovery_t * discovery ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 4 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
/*
* If log is missing this means that IrLAP was unable to perform the
* discovery , so restart discovery again with just the half timeout
* of the normal one .
*/
/* Well... It means that there was nobody out there - Jean II */
if ( log = = NULL ) {
/* irlmp_start_discovery_timer(irlmp, 150); */
return ;
}
/*
* Locking : we are the only owner of this discovery log , so
* no need to lock it .
* We just need to lock the global log in irlmp_add_discovery ( ) .
*/
discovery = ( discovery_t * ) hashbin_remove_first ( log ) ;
while ( discovery ! = NULL ) {
irlmp_add_discovery ( cachelog , discovery ) ;
discovery = ( discovery_t * ) hashbin_remove_first ( log ) ;
}
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
/* Delete the now empty log */
hashbin_delete ( log , ( FREE_FUNC ) kfree ) ;
}
/*
* Function irlmp_expire_discoveries ( log , saddr , force )
*
* Go through all discoveries and expire all that has stayed too 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 ;
discovery_t * curr ;
unsigned long flags ;
discinfo_t * buffer = NULL ;
int n ; /* Size of the full log */
int i = 0 ; /* How many we expired */
IRDA_ASSERT ( log ! = NULL , return ; ) ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 4 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & log - > hb_spinlock , flags ) ;
discovery = ( discovery_t * ) hashbin_get_first ( log ) ;
while ( discovery ! = NULL ) {
/* 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 - > data . saddr = = saddr ) & &
( force | |
( ( jiffies - curr - > timestamp ) > DISCOVERY_EXPIRE_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 ) ;
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 ) ;
2005-11-08 20:41:34 +03:00
kfree ( curr ) ;
2005-04-17 02:20:36 +04:00
}
}
/* 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
2007-10-20 01:10:43 +04:00
* don ' t care to be interrupted . - Jean II */
2005-04-17 02:20:36 +04:00
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 )
*
* Print out all discoveries in log
*
*/
void irlmp_dump_discoveries ( hashbin_t * log )
{
discovery_t * discovery ;
IRDA_ASSERT ( log ! = NULL , return ; ) ;
discovery = ( discovery_t * ) hashbin_get_first ( log ) ;
while ( discovery ! = NULL ) {
IRDA_DEBUG ( 0 , " Discovery: \n " ) ;
IRDA_DEBUG ( 0 , " daddr=%08x \n " , discovery - > data . daddr ) ;
2007-02-09 17:24:53 +03:00
IRDA_DEBUG ( 0 , " saddr=%08x \n " , discovery - > data . saddr ) ;
2005-04-17 02:20:36 +04:00
IRDA_DEBUG ( 0 , " nickname=%s \n " , discovery - > data . info ) ;
discovery = ( discovery_t * ) hashbin_get_next ( log ) ;
}
}
# endif
/*
* 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 , int old_entries )
{
discovery_t * discovery ;
unsigned long flags ;
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 */
IRDA_ASSERT ( pn ! = NULL , return NULL ; ) ;
IRDA_ASSERT ( log ! = NULL , return NULL ; ) ;
/* 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 ) */
2008-05-14 10:25:57 +04:00
if ( ( get_unaligned ( ( __u16 * ) discovery - > data . hints ) & mask ) & &
2005-04-17 02:20:36 +04:00
( ( old_entries ) | |
2008-05-14 10:25:57 +04:00
( ( jiffies - discovery - > firststamp ) < j_timeout ) ) ) {
2005-04-17 02:20:36 +04:00
/* 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 NULL ;
}
}
/* Copy discovery information */
memcpy ( & ( buffer [ i ] ) , & ( discovery - > data ) ,
sizeof ( discinfo_t ) ) ;
i + + ;
}
discovery = ( discovery_t * ) hashbin_get_next ( log ) ;
}
spin_unlock_irqrestore ( & log - > hb_spinlock , flags ) ;
/* Get the actual number of device in the buffer and return */
* pn = i ;
return ( buffer ) ;
}
# ifdef CONFIG_PROC_FS
static inline discovery_t * discovery_seq_idx ( loff_t pos )
{
discovery_t * discovery ;
2007-02-09 17:24:53 +03:00
for ( discovery = ( discovery_t * ) hashbin_get_first ( irlmp - > cachelog ) ;
2005-04-17 02:20:36 +04:00
discovery ! = NULL ;
discovery = ( discovery_t * ) hashbin_get_next ( irlmp - > cachelog ) ) {
if ( pos - - = = 0 )
break ;
}
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
return discovery ;
}
static void * discovery_seq_start ( struct seq_file * seq , loff_t * pos )
{
spin_lock_irq ( & irlmp - > cachelog - > hb_spinlock ) ;
2007-02-09 17:24:53 +03:00
return * pos ? discovery_seq_idx ( * pos - 1 ) : SEQ_START_TOKEN ;
2005-04-17 02:20:36 +04:00
}
static void * discovery_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
+ + * pos ;
2007-02-09 17:24:53 +03:00
return ( v = = SEQ_START_TOKEN )
2005-04-17 02:20:36 +04:00
? ( void * ) hashbin_get_first ( irlmp - > cachelog )
: ( void * ) hashbin_get_next ( irlmp - > cachelog ) ;
}
static void discovery_seq_stop ( struct seq_file * seq , void * v )
{
spin_unlock_irq ( & irlmp - > cachelog - > hb_spinlock ) ;
}
static int discovery_seq_show ( struct seq_file * seq , void * v )
{
if ( v = = SEQ_START_TOKEN )
seq_puts ( seq , " IrLMP: Discovery log: \n \n " ) ;
else {
const discovery_t * discovery = v ;
2007-02-09 17:24:53 +03:00
seq_printf ( seq , " nickname: %s, hint: 0x%02x%02x " ,
2005-04-17 02:20:36 +04:00
discovery - > data . info ,
2007-02-09 17:24:53 +03:00
discovery - > data . hints [ 0 ] ,
2005-04-17 02:20:36 +04:00
discovery - > data . hints [ 1 ] ) ;
#if 0
if ( discovery - > data . hints [ 0 ] & HINT_PNP )
seq_puts ( seq , " PnP Compatible " ) ;
if ( discovery - > data . hints [ 0 ] & HINT_PDA )
seq_puts ( seq , " PDA/Palmtop " ) ;
if ( discovery - > data . hints [ 0 ] & HINT_COMPUTER )
seq_puts ( seq , " Computer " ) ;
if ( discovery - > data . hints [ 0 ] & HINT_PRINTER )
seq_puts ( seq , " Printer " ) ;
if ( discovery - > data . hints [ 0 ] & HINT_MODEM )
seq_puts ( seq , " Modem " ) ;
if ( discovery - > data . hints [ 0 ] & HINT_FAX )
seq_puts ( seq , " Fax " ) ;
if ( discovery - > data . hints [ 0 ] & HINT_LAN )
seq_puts ( seq , " LAN Access " ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
if ( discovery - > data . hints [ 1 ] & HINT_TELEPHONY )
seq_puts ( seq , " Telephony " ) ;
if ( discovery - > data . hints [ 1 ] & HINT_FILE_SERVER )
2007-02-09 17:24:53 +03:00
seq_puts ( seq , " File Server " ) ;
2005-04-17 02:20:36 +04:00
if ( discovery - > data . hints [ 1 ] & HINT_COMM )
seq_puts ( seq , " IrCOMM " ) ;
if ( discovery - > data . hints [ 1 ] & HINT_OBEX )
seq_puts ( seq , " IrOBEX " ) ;
2007-02-09 17:24:53 +03:00
# endif
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " , saddr: 0x%08x, daddr: 0x%08x \n \n " ,
discovery - > data . saddr ,
discovery - > data . daddr ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
seq_putc ( seq , ' \n ' ) ;
}
return 0 ;
}
2007-07-11 10:07:31 +04:00
static const struct seq_operations discovery_seq_ops = {
2005-04-17 02:20:36 +04:00
. start = discovery_seq_start ,
. next = discovery_seq_next ,
. stop = discovery_seq_stop ,
. show = discovery_seq_show ,
} ;
static int discovery_seq_open ( struct inode * inode , struct file * file )
{
IRDA_ASSERT ( irlmp ! = NULL , return - EINVAL ; ) ;
return seq_open ( file , & discovery_seq_ops ) ;
}
2007-02-12 11:55:35 +03:00
const struct file_operations discovery_seq_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = discovery_seq_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
# endif