2005-04-16 15:20:36 -07:00
/*
* IrNET protocol module : Synchronous PPP over an IrDA socket .
*
* Jean II - HPL ` 00 - < jt @ hpl . hp . com >
*
* This file implement the IRDA interface of IrNET .
* Basically , we sit on top of IrTTP . We set up IrTTP , IrIAS properly ,
* and exchange frames with IrTTP .
*/
# include "irnet_irda.h" /* Private header */
2009-10-07 17:09:06 +04:00
# include <linux/sched.h>
2008-04-29 01:01:52 -07:00
# include <linux/seq_file.h>
2008-05-14 16:05:59 -07:00
# include <asm/unaligned.h>
2005-04-16 15:20:36 -07:00
2007-03-16 20:31:03 -07:00
/*
* PPP disconnect work : we need to make sure we ' re in
* process context when calling ppp_unregister_channel ( ) .
*/
static void irnet_ppp_disconnect ( struct work_struct * work )
{
irnet_socket * self =
container_of ( work , irnet_socket , disconnect_work ) ;
if ( self = = NULL )
return ;
/*
* If we were connected , cleanup & close the PPP
* channel , which will kill pppd ( hangup ) and the rest .
*/
if ( self - > ppp_open & & ! self - > ttp_open & & ! self - > ttp_connect ) {
ppp_unregister_channel ( & self - > chan ) ;
self - > ppp_open = 0 ;
}
}
2005-04-16 15:20:36 -07:00
/************************* CONTROL CHANNEL *************************/
/*
* When ppp is not active , / dev / irnet act as a control channel .
* Writing allow to set up the IrDA destination of the IrNET channel ,
* and any application may be read events happening on IrNET . . .
*/
/*------------------------------------------------------------------*/
/*
* Post an event to the control channel . . .
* Put the event in the log , and then wait all process blocked on read
* so they can read the log . . .
*/
static void
irnet_post_event ( irnet_socket * ap ,
irnet_event event ,
__u32 saddr ,
__u32 daddr ,
char * name ,
__u16 hints )
{
int index ; /* In the log */
DENTER ( CTRL_TRACE , " (ap=0x%p, event=%d, daddr=%08x, name=``%s'') \n " ,
ap , event , daddr , name ) ;
/* Protect this section via spinlock.
* Note : as we are the only event producer , we only need to exclude
* ourself when touching the log , which is nice and easy .
*/
spin_lock_bh ( & irnet_events . spinlock ) ;
/* Copy the event in the log */
index = irnet_events . index ;
irnet_events . log [ index ] . event = event ;
irnet_events . log [ index ] . daddr = daddr ;
irnet_events . log [ index ] . saddr = saddr ;
/* Try to copy IrDA nickname */
if ( name )
strcpy ( irnet_events . log [ index ] . name , name ) ;
else
irnet_events . log [ index ] . name [ 0 ] = ' \0 ' ;
/* Copy hints */
irnet_events . log [ index ] . hints . word = hints ;
/* Try to get ppp unit number */
if ( ( ap ! = ( irnet_socket * ) NULL ) & & ( ap - > ppp_open ) )
irnet_events . log [ index ] . unit = ppp_unit_number ( & ap - > chan ) ;
else
irnet_events . log [ index ] . unit = - 1 ;
/* Increment the index
* Note that we increment the index only after the event is written ,
* to make sure that the readers don ' t get garbage . . . */
irnet_events . index = ( index + 1 ) % IRNET_MAX_EVENTS ;
DEBUG ( CTRL_INFO , " New event index is %d \n " , irnet_events . index ) ;
/* Spin lock end */
spin_unlock_bh ( & irnet_events . spinlock ) ;
/* Now : wake up everybody waiting for events... */
wake_up_interruptible_all ( & irnet_events . rwait ) ;
DEXIT ( CTRL_TRACE , " \n " ) ;
}
/************************* IRDA SUBROUTINES *************************/
/*
* These are a bunch of subroutines called from other functions
* down there , mostly common code or to improve readability . . .
*
* Note : we duplicate quite heavily some routines of af_irda . c ,
* because our input structure ( self ) is quite different
* ( struct irnet instead of struct irda_sock ) , which make sharing
* the same code impossible ( at least , without templates ) .
*/
/*------------------------------------------------------------------*/
/*
* Function irda_open_tsap ( self )
*
* Open local Transport Service Access Point ( TSAP )
*
* Create a IrTTP instance for us and set all the IrTTP callbacks .
*/
static inline int
irnet_open_tsap ( irnet_socket * self )
{
notify_t notify ; /* Callback structure */
DENTER ( IRDA_SR_TRACE , " (self=0x%p) \n " , self ) ;
DABORT ( self - > tsap ! = NULL , - EBUSY , IRDA_SR_ERROR , " Already busy ! \n " ) ;
/* Initialize IrTTP callbacks to be used by the IrDA stack */
irda_notify_init ( & notify ) ;
notify . connect_confirm = irnet_connect_confirm ;
notify . connect_indication = irnet_connect_indication ;
notify . disconnect_indication = irnet_disconnect_indication ;
notify . data_indication = irnet_data_indication ;
/*notify.udata_indication = NULL;*/
notify . flow_indication = irnet_flow_indication ;
notify . status_indication = irnet_status_indication ;
notify . instance = self ;
strlcpy ( notify . name , IRNET_NOTIFY_NAME , sizeof ( notify . name ) ) ;
/* Open an IrTTP instance */
self - > tsap = irttp_open_tsap ( LSAP_ANY , DEFAULT_INITIAL_CREDIT ,
2007-02-09 23:24:53 +09:00
& notify ) ;
2005-04-16 15:20:36 -07:00
DABORT ( self - > tsap = = NULL , - ENOMEM ,
IRDA_SR_ERROR , " Unable to allocate TSAP ! \n " ) ;
/* Remember which TSAP selector we actually got */
self - > stsap_sel = self - > tsap - > stsap_sel ;
DEXIT ( IRDA_SR_TRACE , " - tsap=0x%p, sel=0x%X \n " ,
self - > tsap , self - > stsap_sel ) ;
return 0 ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_ias_to_tsap ( self , result , value )
*
* Examine an IAS object and extract TSAP
*
* We do an IAP query to find the TSAP associated with the IrNET service .
* When IrIAP pass us the result of the query , this function look at
* the return values to check for failures and extract the TSAP if
* possible .
* Also deallocate value
* The failure is in self - > errno
* Return TSAP or - 1
*/
static inline __u8
irnet_ias_to_tsap ( irnet_socket * self ,
int result ,
struct ias_value * value )
{
__u8 dtsap_sel = 0 ; /* TSAP we are looking for */
DENTER ( IRDA_SR_TRACE , " (self=0x%p) \n " , self ) ;
/* By default, no error */
self - > errno = 0 ;
/* Check if request succeeded */
switch ( result )
{
/* Standard errors : service not available */
case IAS_CLASS_UNKNOWN :
case IAS_ATTRIB_UNKNOWN :
DEBUG ( IRDA_SR_INFO , " IAS object doesn't exist ! (%d) \n " , result ) ;
self - > errno = - EADDRNOTAVAIL ;
break ;
/* Other errors, most likely IrDA stack failure */
default :
DEBUG ( IRDA_SR_INFO , " IAS query failed ! (%d) \n " , result ) ;
self - > errno = - EHOSTUNREACH ;
break ;
/* Success : we got what we wanted */
case IAS_SUCCESS :
break ;
}
/* Check what was returned to us */
if ( value ! = NULL )
{
/* What type of argument have we got ? */
switch ( value - > type )
{
case IAS_INTEGER :
DEBUG ( IRDA_SR_INFO , " result=%d \n " , value - > t . integer ) ;
if ( value - > t . integer ! = - 1 )
/* Get the remote TSAP selector */
dtsap_sel = value - > t . integer ;
2007-02-09 23:24:53 +09:00
else
2005-04-16 15:20:36 -07:00
self - > errno = - EADDRNOTAVAIL ;
break ;
default :
self - > errno = - EADDRNOTAVAIL ;
DERROR ( IRDA_SR_ERROR , " bad type ! (0x%X) \n " , value - > type ) ;
break ;
}
/* Cleanup */
irias_delete_value ( value ) ;
}
else /* value == NULL */
{
/* Nothing returned to us - usually result != SUCCESS */
if ( ! ( self - > errno ) )
{
DERROR ( IRDA_SR_ERROR ,
" IrDA bug : result == SUCCESS && value == NULL \n " ) ;
self - > errno = - EHOSTUNREACH ;
}
}
DEXIT ( IRDA_SR_TRACE , " \n " ) ;
/* Return the TSAP */
return ( dtsap_sel ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_find_lsap_sel ( self )
*
* Try to lookup LSAP selector in remote LM - IAS
*
* Basically , we start a IAP query , and then go to sleep . When the query
* return , irnet_getvalue_confirm will wake us up , and we can examine the
* result of the query . . .
* Note that in some case , the query fail even before we go to sleep ,
* creating some races . . .
*/
static inline int
irnet_find_lsap_sel ( irnet_socket * self )
{
DENTER ( IRDA_SR_TRACE , " (self=0x%p) \n " , self ) ;
/* This should not happen */
DABORT ( self - > iriap , - EBUSY , IRDA_SR_ERROR , " busy with a previous query. \n " ) ;
/* Create an IAP instance, will be closed in irnet_getvalue_confirm() */
self - > iriap = iriap_open ( LSAP_ANY , IAS_CLIENT , self ,
irnet_getvalue_confirm ) ;
/* Treat unexpected signals as disconnect */
self - > errno = - EHOSTUNREACH ;
/* Query remote LM-IAS */
iriap_getvaluebyclass_request ( self - > iriap , self - > rsaddr , self - > daddr ,
IRNET_SERVICE_NAME , IRNET_IAS_VALUE ) ;
/* The above request is non-blocking.
* After a while , IrDA will call us back in irnet_getvalue_confirm ( )
* We will then call irnet_ias_to_tsap ( ) and finish the
* connection procedure */
DEXIT ( IRDA_SR_TRACE , " \n " ) ;
return 0 ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_connect_tsap ( self )
*
* Initialise the TTP socket and initiate TTP connection
*
*/
static inline int
irnet_connect_tsap ( irnet_socket * self )
{
int err ;
DENTER ( IRDA_SR_TRACE , " (self=0x%p) \n " , self ) ;
/* Open a local TSAP (an IrTTP instance) */
err = irnet_open_tsap ( self ) ;
if ( err ! = 0 )
{
clear_bit ( 0 , & self - > ttp_connect ) ;
DERROR ( IRDA_SR_ERROR , " connect aborted! \n " ) ;
return ( err ) ;
}
/* Connect to remote device */
2007-02-09 23:24:53 +09:00
err = irttp_connect_request ( self - > tsap , self - > dtsap_sel ,
self - > rsaddr , self - > daddr , NULL ,
2005-04-16 15:20:36 -07:00
self - > max_sdu_size_rx , NULL ) ;
if ( err ! = 0 )
{
clear_bit ( 0 , & self - > ttp_connect ) ;
DERROR ( IRDA_SR_ERROR , " connect aborted! \n " ) ;
return ( err ) ;
}
/* The above call is non-blocking.
* After a while , the IrDA stack will either call us back in
* irnet_connect_confirm ( ) or irnet_disconnect_indication ( )
* See you there ; - ) */
DEXIT ( IRDA_SR_TRACE , " \n " ) ;
return ( err ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_discover_next_daddr ( self )
*
* Query the IrNET TSAP of the next device in the log .
*
* Used in the TSAP discovery procedure .
*/
static inline int
irnet_discover_next_daddr ( irnet_socket * self )
{
/* Close the last instance of IrIAP, and open a new one.
* We can ' t reuse the IrIAP instance in the IrIAP callback */
if ( self - > iriap )
{
iriap_close ( self - > iriap ) ;
self - > iriap = NULL ;
}
/* Create a new IAP instance */
self - > iriap = iriap_open ( LSAP_ANY , IAS_CLIENT , self ,
irnet_discovervalue_confirm ) ;
if ( self - > iriap = = NULL )
return - ENOMEM ;
/* Next discovery - before the call to avoid races */
self - > disco_index + + ;
/* Check if we have one more address to try */
if ( self - > disco_index < self - > disco_number )
{
/* Query remote LM-IAS */
iriap_getvaluebyclass_request ( self - > iriap ,
self - > discoveries [ self - > disco_index ] . saddr ,
self - > discoveries [ self - > disco_index ] . daddr ,
IRNET_SERVICE_NAME , IRNET_IAS_VALUE ) ;
/* The above request is non-blocking.
* After a while , IrDA will call us back in irnet_discovervalue_confirm ( )
* We will then call irnet_ias_to_tsap ( ) and come back here again . . . */
return ( 0 ) ;
}
else
return ( 1 ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_discover_daddr_and_lsap_sel ( self )
*
* This try to find a device with the requested service .
*
* Initiate a TSAP discovery procedure .
* It basically look into the discovery log . For each address in the list ,
* it queries the LM - IAS of the device to find if this device offer
* the requested service .
* If there is more than one node supporting the service , we complain
* to the user ( it should move devices around ) .
* If we find one node which have the requested TSAP , we connect to it .
*
* This function just start the whole procedure . It request the discovery
* log and submit the first IAS query .
* The bulk of the job is handled in irnet_discovervalue_confirm ( )
*
* Note : this procedure fails if there is more than one device in range
* on the same dongle , because IrLMP doesn ' t disconnect the LAP when the
* last LSAP is closed . Moreover , we would need to wait the LAP
* disconnection . . .
*/
static inline int
irnet_discover_daddr_and_lsap_sel ( irnet_socket * self )
{
int ret ;
DENTER ( IRDA_SR_TRACE , " (self=0x%p) \n " , self ) ;
/* Ask lmp for the current discovery log */
self - > discoveries = irlmp_get_discoveries ( & self - > disco_number , self - > mask ,
DISCOVERY_DEFAULT_SLOTS ) ;
/* Check if the we got some results */
if ( self - > discoveries = = NULL )
{
self - > disco_number = - 1 ;
clear_bit ( 0 , & self - > ttp_connect ) ;
DRETURN ( - ENETUNREACH , IRDA_SR_INFO , " No Cachelog... \n " ) ;
}
DEBUG ( IRDA_SR_INFO , " Got the log (0x%p), size is %d \n " ,
self - > discoveries , self - > disco_number ) ;
/* Start with the first discovery */
self - > disco_index = - 1 ;
self - > daddr = DEV_ADDR_ANY ;
/* This will fail if the log is empty - this is non-blocking */
ret = irnet_discover_next_daddr ( self ) ;
if ( ret )
{
/* Close IAP */
if ( self - > iriap )
iriap_close ( self - > iriap ) ;
self - > iriap = NULL ;
/* Cleanup our copy of the discovery log */
kfree ( self - > discoveries ) ;
self - > discoveries = NULL ;
clear_bit ( 0 , & self - > ttp_connect ) ;
DRETURN ( - ENETUNREACH , IRDA_SR_INFO , " Cachelog empty... \n " ) ;
}
/* Follow me in irnet_discovervalue_confirm() */
DEXIT ( IRDA_SR_TRACE , " \n " ) ;
return ( 0 ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_dname_to_daddr ( self )
*
* Convert an IrDA nickname to a valid IrDA address
*
* It basically look into the discovery log until there is a match .
*/
static inline int
irnet_dname_to_daddr ( irnet_socket * self )
{
struct irda_device_info * discoveries ; /* Copy of the discovery log */
int number ; /* Number of nodes in the log */
int i ;
DENTER ( IRDA_SR_TRACE , " (self=0x%p) \n " , self ) ;
/* Ask lmp for the current discovery log */
discoveries = irlmp_get_discoveries ( & number , 0xffff ,
DISCOVERY_DEFAULT_SLOTS ) ;
/* Check if the we got some results */
if ( discoveries = = NULL )
DRETURN ( - ENETUNREACH , IRDA_SR_INFO , " Cachelog empty... \n " ) ;
2007-02-09 23:24:53 +09:00
/*
2005-04-16 15:20:36 -07:00
* Now , check all discovered devices ( if any ) , and connect
* client only about the services that the client is
* interested in . . .
*/
for ( i = 0 ; i < number ; i + + )
{
/* Does the name match ? */
if ( ! strncmp ( discoveries [ i ] . info , self - > rname , NICKNAME_MAX_LEN ) )
{
/* Yes !!! Get it.. */
self - > daddr = discoveries [ i ] . daddr ;
DEBUG ( IRDA_SR_INFO , " discovered device ``%s'' at address 0x%08x. \n " ,
self - > rname , self - > daddr ) ;
kfree ( discoveries ) ;
DEXIT ( IRDA_SR_TRACE , " \n " ) ;
return 0 ;
}
}
/* No luck ! */
DEBUG ( IRDA_SR_INFO , " cannot discover device ``%s'' !!! \n " , self - > rname ) ;
kfree ( discoveries ) ;
return ( - EADDRNOTAVAIL ) ;
}
/************************* SOCKET ROUTINES *************************/
/*
* This are the main operations on IrNET sockets , basically to create
* and destroy IrNET sockets . These are called from the PPP part . . .
*/
/*------------------------------------------------------------------*/
/*
* Create a IrNET instance : just initialise some parameters . . .
*/
int
irda_irnet_create ( irnet_socket * self )
{
DENTER ( IRDA_SOCK_TRACE , " (self=0x%p) \n " , self ) ;
self - > magic = IRNET_MAGIC ; /* Paranoia */
self - > ttp_open = 0 ; /* Prevent higher layer from accessing IrTTP */
self - > ttp_connect = 0 ; /* Not connecting yet */
self - > rname [ 0 ] = ' \0 ' ; /* May be set via control channel */
self - > rdaddr = DEV_ADDR_ANY ; /* May be set via control channel */
self - > rsaddr = DEV_ADDR_ANY ; /* May be set via control channel */
self - > daddr = DEV_ADDR_ANY ; /* Until we get connected */
self - > saddr = DEV_ADDR_ANY ; /* Until we get connected */
self - > max_sdu_size_rx = TTP_SAR_UNBOUND ;
/* Register as a client with IrLMP */
self - > ckey = irlmp_register_client ( 0 , NULL , NULL , NULL ) ;
# ifdef DISCOVERY_NOMASK
self - > mask = 0xffff ; /* For W2k compatibility */
# else /* DISCOVERY_NOMASK */
self - > mask = irlmp_service_to_hint ( S_LAN ) ;
# endif /* DISCOVERY_NOMASK */
self - > tx_flow = FLOW_START ; /* Flow control from IrTTP */
2007-03-16 20:31:03 -07:00
INIT_WORK ( & self - > disconnect_work , irnet_ppp_disconnect ) ;
2005-04-16 15:20:36 -07:00
DEXIT ( IRDA_SOCK_TRACE , " \n " ) ;
return ( 0 ) ;
}
/*------------------------------------------------------------------*/
/*
* Connect to the other side :
* o convert device name to an address
* o find the socket number ( dlsap )
* o Establish the connection
*
* Note : We no longer mimic af_irda . The IAS query for finding the TSAP
* is done asynchronously , like the TTP connection . This allow us to
* call this function from any context ( not only process ) .
* The downside is that following what ' s happening in there is tricky
* because it involve various functions all over the place . . .
*/
int
irda_irnet_connect ( irnet_socket * self )
{
int err ;
DENTER ( IRDA_SOCK_TRACE , " (self=0x%p) \n " , self ) ;
/* Check if we are already trying to connect.
* Because irda_irnet_connect ( ) can be called directly by pppd plus
* packet retries in ppp_generic and connect may take time , plus we may
* race with irnet_connect_indication ( ) , we need to be careful there . . . */
if ( test_and_set_bit ( 0 , & self - > ttp_connect ) )
DRETURN ( - EBUSY , IRDA_SOCK_INFO , " Already connecting... \n " ) ;
if ( ( self - > iriap ! = NULL ) | | ( self - > tsap ! = NULL ) )
DERROR ( IRDA_SOCK_ERROR , " Socket not cleaned up... \n " ) ;
/* Insert ourselves in the hashbin so that the IrNET server can find us.
* Notes : 4 th arg is string of 32 char max and must be null terminated
* When 4 th arg is used ( string ) , 3 rd arg isn ' t ( int )
* Can ' t re - insert ( MUST remove first ) so check for that . . . */
if ( ( irnet_server . running ) & & ( self - > q . q_next = = NULL ) )
{
spin_lock_bh ( & irnet_server . spinlock ) ;
hashbin_insert ( irnet_server . list , ( irda_queue_t * ) self , 0 , self - > rname ) ;
spin_unlock_bh ( & irnet_server . spinlock ) ;
DEBUG ( IRDA_SOCK_INFO , " Inserted ``%s'' in hashbin... \n " , self - > rname ) ;
}
/* If we don't have anything (no address, no name) */
if ( ( self - > rdaddr = = DEV_ADDR_ANY ) & & ( self - > rname [ 0 ] = = ' \0 ' ) )
{
/* Try to find a suitable address */
if ( ( err = irnet_discover_daddr_and_lsap_sel ( self ) ) ! = 0 )
DRETURN ( err , IRDA_SOCK_INFO , " auto-connect failed! \n " ) ;
/* In most cases, the call above is non-blocking */
}
else
{
/* If we have only the name (no address), try to get an address */
if ( self - > rdaddr = = DEV_ADDR_ANY )
{
if ( ( err = irnet_dname_to_daddr ( self ) ) ! = 0 )
DRETURN ( err , IRDA_SOCK_INFO , " name connect failed! \n " ) ;
}
else
/* Use the requested destination address */
self - > daddr = self - > rdaddr ;
/* Query remote LM-IAS to find LSAP selector */
irnet_find_lsap_sel ( self ) ;
/* The above call is non blocking */
}
/* At this point, we are waiting for the IrDA stack to call us back,
* or we have already failed .
* We will finish the connection procedure in irnet_connect_tsap ( ) .
*/
DEXIT ( IRDA_SOCK_TRACE , " \n " ) ;
return ( 0 ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irda_irnet_destroy ( self )
*
* Destroy irnet instance
*
* Note : this need to be called from a process context .
*/
void
irda_irnet_destroy ( irnet_socket * self )
{
DENTER ( IRDA_SOCK_TRACE , " (self=0x%p) \n " , self ) ;
if ( self = = NULL )
return ;
/* Remove ourselves from hashbin (if we are queued in hashbin)
* Note : ` irnet_server . running ' protect us from calls in hashbin_delete ( ) */
if ( ( irnet_server . running ) & & ( self - > q . q_next ! = NULL ) )
{
struct irnet_socket * entry ;
DEBUG ( IRDA_SOCK_INFO , " Removing from hash.. \n " ) ;
spin_lock_bh ( & irnet_server . spinlock ) ;
entry = hashbin_remove_this ( irnet_server . list , ( irda_queue_t * ) self ) ;
self - > q . q_next = NULL ;
spin_unlock_bh ( & irnet_server . spinlock ) ;
DASSERT ( entry = = self , , IRDA_SOCK_ERROR , " Can't remove from hash. \n " ) ;
}
/* If we were connected, post a message */
if ( test_bit ( 0 , & self - > ttp_open ) )
{
/* Note : as the disconnect comes from ppp_generic, the unit number
* doesn ' t exist anymore when we post the event , so we need to pass
* NULL as the first arg . . . */
irnet_post_event ( NULL , IRNET_DISCONNECT_TO ,
self - > saddr , self - > daddr , self - > rname , 0 ) ;
}
/* Prevent various IrDA callbacks from messing up things
* Need to be first */
clear_bit ( 0 , & self - > ttp_connect ) ;
/* Prevent higher layer from accessing IrTTP */
clear_bit ( 0 , & self - > ttp_open ) ;
/* Unregister with IrLMP */
irlmp_unregister_client ( self - > ckey ) ;
/* Unregister with LM-IAS */
if ( self - > iriap )
2007-02-09 23:24:53 +09:00
{
2005-04-16 15:20:36 -07:00
iriap_close ( self - > iriap ) ;
self - > iriap = NULL ;
}
/* Cleanup eventual discoveries from connection attempt or control channel */
if ( self - > discoveries ! = NULL )
{
/* Cleanup our copy of the discovery log */
kfree ( self - > discoveries ) ;
self - > discoveries = NULL ;
}
/* Close our IrTTP connection */
if ( self - > tsap )
{
DEBUG ( IRDA_SOCK_INFO , " Closing our TTP connection. \n " ) ;
irttp_disconnect_request ( self - > tsap , NULL , P_NORMAL ) ;
irttp_close_tsap ( self - > tsap ) ;
self - > tsap = NULL ;
}
self - > stsap_sel = 0 ;
DEXIT ( IRDA_SOCK_TRACE , " \n " ) ;
return ;
}
/************************** SERVER SOCKET **************************/
/*
* The IrNET service is composed of one server socket and a variable
* number of regular IrNET sockets . The server socket is supposed to
* handle incoming connections and redirect them to one IrNET sockets .
* It ' s a superset of the regular IrNET socket , but has a very distinct
* behaviour . . .
*/
/*------------------------------------------------------------------*/
/*
* Function irnet_daddr_to_dname ( self )
*
* Convert an IrDA address to a IrDA nickname
*
* It basically look into the discovery log until there is a match .
*/
static inline int
irnet_daddr_to_dname ( irnet_socket * self )
{
struct irda_device_info * discoveries ; /* Copy of the discovery log */
int number ; /* Number of nodes in the log */
int i ;
DENTER ( IRDA_SERV_TRACE , " (self=0x%p) \n " , self ) ;
/* Ask lmp for the current discovery log */
discoveries = irlmp_get_discoveries ( & number , 0xffff ,
DISCOVERY_DEFAULT_SLOTS ) ;
/* Check if the we got some results */
if ( discoveries = = NULL )
DRETURN ( - ENETUNREACH , IRDA_SERV_INFO , " Cachelog empty... \n " ) ;
/* Now, check all discovered devices (if any) */
for ( i = 0 ; i < number ; i + + )
{
/* Does the name match ? */
if ( discoveries [ i ] . daddr = = self - > daddr )
{
/* Yes !!! Get it.. */
strlcpy ( self - > rname , discoveries [ i ] . info , sizeof ( self - > rname ) ) ;
2006-02-09 16:59:48 -08:00
self - > rname [ sizeof ( self - > rname ) - 1 ] = ' \0 ' ;
2005-04-16 15:20:36 -07:00
DEBUG ( IRDA_SERV_INFO , " Device 0x%08x is in fact ``%s''. \n " ,
self - > daddr , self - > rname ) ;
kfree ( discoveries ) ;
DEXIT ( IRDA_SERV_TRACE , " \n " ) ;
return 0 ;
}
}
/* No luck ! */
DEXIT ( IRDA_SERV_INFO , " : cannot discover device 0x%08x !!! \n " , self - > daddr ) ;
kfree ( discoveries ) ;
return ( - EADDRNOTAVAIL ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irda_find_socket ( self )
*
* Find the correct IrNET socket
*
* Look into the list of IrNET sockets and finds one with the right
* properties . . .
*/
static inline irnet_socket *
irnet_find_socket ( irnet_socket * self )
{
irnet_socket * new = ( irnet_socket * ) NULL ;
int err ;
DENTER ( IRDA_SERV_TRACE , " (self=0x%p) \n " , self ) ;
/* Get the addresses of the requester */
self - > daddr = irttp_get_daddr ( self - > tsap ) ;
self - > saddr = irttp_get_saddr ( self - > tsap ) ;
/* Try to get the IrDA nickname of the requester */
err = irnet_daddr_to_dname ( self ) ;
/* Protect access to the instance list */
spin_lock_bh ( & irnet_server . spinlock ) ;
/* So now, try to get an socket having specifically
* requested that nickname */
if ( err = = 0 )
{
new = ( irnet_socket * ) hashbin_find ( irnet_server . list ,
0 , self - > rname ) ;
if ( new )
DEBUG ( IRDA_SERV_INFO , " Socket 0x%p matches rname ``%s''. \n " ,
new , new - > rname ) ;
}
/* If no name matches, try to find an socket by the destination address */
/* It can be either the requested destination address (set via the
* control channel ) , or the current destination address if the
* socket is in the middle of a connection request */
if ( new = = ( irnet_socket * ) NULL )
{
new = ( irnet_socket * ) hashbin_get_first ( irnet_server . list ) ;
while ( new ! = ( irnet_socket * ) NULL )
{
/* Does it have the same address ? */
if ( ( new - > rdaddr = = self - > daddr ) | | ( new - > daddr = = self - > daddr ) )
{
/* Yes !!! Get it.. */
DEBUG ( IRDA_SERV_INFO , " Socket 0x%p matches daddr %#08x. \n " ,
new , self - > daddr ) ;
break ;
}
new = ( irnet_socket * ) hashbin_get_next ( irnet_server . list ) ;
}
}
/* If we don't have any socket, get the first unconnected socket */
if ( new = = ( irnet_socket * ) NULL )
{
new = ( irnet_socket * ) hashbin_get_first ( irnet_server . list ) ;
while ( new ! = ( irnet_socket * ) NULL )
{
/* Is it available ? */
if ( ! ( test_bit ( 0 , & new - > ttp_open ) ) & & ( new - > rdaddr = = DEV_ADDR_ANY ) & &
( new - > rname [ 0 ] = = ' \0 ' ) & & ( new - > ppp_open ) )
{
/* Yes !!! Get it.. */
DEBUG ( IRDA_SERV_INFO , " Socket 0x%p is free. \n " ,
new ) ;
break ;
}
new = ( irnet_socket * ) hashbin_get_next ( irnet_server . list ) ;
}
}
/* Spin lock end */
spin_unlock_bh ( & irnet_server . spinlock ) ;
DEXIT ( IRDA_SERV_TRACE , " - new = 0x%p \n " , new ) ;
return new ;
}
/*------------------------------------------------------------------*/
/*
* Function irda_connect_socket ( self )
*
* Connect an incoming connection to the socket
*
*/
static inline int
irnet_connect_socket ( irnet_socket * server ,
irnet_socket * new ,
struct qos_info * qos ,
__u32 max_sdu_size ,
__u8 max_header_size )
{
DENTER ( IRDA_SERV_TRACE , " (server=0x%p, new=0x%p) \n " ,
server , new ) ;
/* Now attach up the new socket */
new - > tsap = irttp_dup ( server - > tsap , new ) ;
DABORT ( new - > tsap = = NULL , - 1 , IRDA_SERV_ERROR , " dup failed! \n " ) ;
/* Set up all the relevant parameters on the new socket */
new - > stsap_sel = new - > tsap - > stsap_sel ;
new - > dtsap_sel = new - > tsap - > dtsap_sel ;
new - > saddr = irttp_get_saddr ( new - > tsap ) ;
new - > daddr = irttp_get_daddr ( new - > tsap ) ;
new - > max_header_size = max_header_size ;
new - > max_sdu_size_tx = max_sdu_size ;
new - > max_data_size = max_sdu_size ;
# ifdef STREAM_COMPAT
/* If we want to receive "stream sockets" */
if ( max_sdu_size = = 0 )
new - > max_data_size = irttp_get_max_seg_size ( new - > tsap ) ;
# endif /* STREAM_COMPAT */
/* Clean up the original one to keep it in listen state */
irttp_listen ( server - > tsap ) ;
/* Send a connection response on the new socket */
irttp_connect_response ( new - > tsap , new - > max_sdu_size_rx , NULL ) ;
/* Allow PPP to send its junk over the new socket... */
set_bit ( 0 , & new - > ttp_open ) ;
/* Not connecting anymore, and clean up last possible remains
* of connection attempts on the socket */
clear_bit ( 0 , & new - > ttp_connect ) ;
if ( new - > iriap )
{
iriap_close ( new - > iriap ) ;
new - > iriap = NULL ;
}
if ( new - > discoveries ! = NULL )
{
kfree ( new - > discoveries ) ;
new - > discoveries = NULL ;
}
# ifdef CONNECT_INDIC_KICK
/* As currently we don't block packets in ppp_irnet_send() while passive,
* this is not really needed . . .
* Also , not doing it give IrDA a chance to finish the setup properly
* before being swamped with packets . . . */
ppp_output_wakeup ( & new - > chan ) ;
# endif /* CONNECT_INDIC_KICK */
/* Notify the control channel */
irnet_post_event ( new , IRNET_CONNECT_FROM ,
new - > saddr , new - > daddr , server - > rname , 0 ) ;
DEXIT ( IRDA_SERV_TRACE , " \n " ) ;
return 0 ;
}
/*------------------------------------------------------------------*/
/*
* Function irda_disconnect_server ( self )
*
* Cleanup the server socket when the incoming connection abort
*
*/
static inline void
irnet_disconnect_server ( irnet_socket * self ,
struct sk_buff * skb )
{
DENTER ( IRDA_SERV_TRACE , " (self=0x%p) \n " , self ) ;
/* Put the received packet in the black hole */
kfree_skb ( skb ) ;
# ifdef FAIL_SEND_DISCONNECT
/* Tell the other party we don't want to be connected */
/* Hum... Is it the right thing to do ? And do we need to send
* a connect response before ? It looks ok without this . . . */
irttp_disconnect_request ( self - > tsap , NULL , P_NORMAL ) ;
# endif /* FAIL_SEND_DISCONNECT */
/* Notify the control channel (see irnet_find_socket()) */
irnet_post_event ( NULL , IRNET_REQUEST_FROM ,
self - > saddr , self - > daddr , self - > rname , 0 ) ;
/* Clean up the server to keep it in listen state */
irttp_listen ( self - > tsap ) ;
DEXIT ( IRDA_SERV_TRACE , " \n " ) ;
return ;
}
/*------------------------------------------------------------------*/
/*
* Function irda_setup_server ( self )
*
* Create a IrTTP server and set it up . . .
*
* Register the IrLAN hint bit , create a IrTTP instance for us ,
* set all the IrTTP callbacks and create an IrIAS entry . . .
*/
static inline int
irnet_setup_server ( void )
{
__u16 hints ;
DENTER ( IRDA_SERV_TRACE , " () \n " ) ;
/* Initialise the regular socket part of the server */
irda_irnet_create ( & irnet_server . s ) ;
/* Open a local TSAP (an IrTTP instance) for the server */
irnet_open_tsap ( & irnet_server . s ) ;
/* PPP part setup */
irnet_server . s . ppp_open = 0 ;
irnet_server . s . chan . private = NULL ;
irnet_server . s . file = NULL ;
/* Get the hint bit corresponding to IrLAN */
/* Note : we overload the IrLAN hint bit. As it is only a "hint", and as
* we provide roughly the same functionality as IrLAN , this is ok .
* In fact , the situation is similar as JetSend overloading the Obex hint
*/
hints = irlmp_service_to_hint ( S_LAN ) ;
# ifdef ADVERTISE_HINT
/* Register with IrLMP as a service (advertise our hint bit) */
irnet_server . skey = irlmp_register_service ( hints ) ;
# endif /* ADVERTISE_HINT */
/* Register with LM-IAS (so that people can connect to us) */
irnet_server . ias_obj = irias_new_object ( IRNET_SERVICE_NAME , jiffies ) ;
2007-02-09 23:24:53 +09:00
irias_add_integer_attrib ( irnet_server . ias_obj , IRNET_IAS_VALUE ,
2005-04-16 15:20:36 -07:00
irnet_server . s . stsap_sel , IAS_KERNEL_ATTR ) ;
irias_insert_object ( irnet_server . ias_obj ) ;
# ifdef DISCOVERY_EVENTS
/* Tell IrLMP we want to be notified of newly discovered nodes */
irlmp_update_client ( irnet_server . s . ckey , hints ,
irnet_discovery_indication , irnet_expiry_indication ,
( void * ) & irnet_server . s ) ;
# endif
DEXIT ( IRDA_SERV_TRACE , " - self=0x%p \n " , & irnet_server . s ) ;
return 0 ;
}
/*------------------------------------------------------------------*/
/*
* Function irda_destroy_server ( self )
*
* Destroy the IrTTP server . . .
*
* Reverse of the previous function . . .
*/
static inline void
irnet_destroy_server ( void )
{
DENTER ( IRDA_SERV_TRACE , " () \n " ) ;
# ifdef ADVERTISE_HINT
/* Unregister with IrLMP */
irlmp_unregister_service ( irnet_server . skey ) ;
# endif /* ADVERTISE_HINT */
/* Unregister with LM-IAS */
if ( irnet_server . ias_obj )
irias_delete_object ( irnet_server . ias_obj ) ;
/* Cleanup the socket part */
irda_irnet_destroy ( & irnet_server . s ) ;
DEXIT ( IRDA_SERV_TRACE , " \n " ) ;
return ;
}
/************************ IRDA-TTP CALLBACKS ************************/
/*
* When we create a IrTTP instance , we pass to it a set of callbacks
* that IrTTP will call in case of various events .
* We take care of those events here .
*/
/*------------------------------------------------------------------*/
/*
* Function irnet_data_indication ( instance , sap , skb )
*
* Received some data from TinyTP . Just queue it on the receive queue
*
*/
static int
irnet_data_indication ( void * instance ,
void * sap ,
struct sk_buff * skb )
{
irnet_socket * ap = ( irnet_socket * ) instance ;
unsigned char * p ;
int code = 0 ;
DENTER ( IRDA_TCB_TRACE , " (self/ap=0x%p, skb=0x%p) \n " ,
ap , skb ) ;
DASSERT ( skb ! = NULL , 0 , IRDA_CB_ERROR , " skb is NULL !!! \n " ) ;
/* Check is ppp is ready to receive our packet */
if ( ! ap - > ppp_open )
{
DERROR ( IRDA_CB_ERROR , " PPP not ready, dropping packet... \n " ) ;
/* When we return error, TTP will need to requeue the skb and
* will stop the sender . IrTTP will stall until we send it a
* flow control request . . . */
return - ENOMEM ;
}
/* strip address/control field if present */
p = skb - > data ;
if ( ( p [ 0 ] = = PPP_ALLSTATIONS ) & & ( p [ 1 ] = = PPP_UI ) )
{
/* chop off address/control */
if ( skb - > len < 3 )
goto err_exit ;
p = skb_pull ( skb , 2 ) ;
}
/* decompress protocol field if compressed */
if ( p [ 0 ] & 1 )
{
/* protocol is compressed */
skb_push ( skb , 1 ) [ 0 ] = 0 ;
}
else
if ( skb - > len < 2 )
goto err_exit ;
/* pass to generic ppp layer */
/* Note : how do I know if ppp can accept or not the packet ? This is
* essential if I want to manage flow control smoothly . . . */
ppp_input ( & ap - > chan , skb ) ;
DEXIT ( IRDA_TCB_TRACE , " \n " ) ;
return 0 ;
err_exit :
DERROR ( IRDA_CB_ERROR , " Packet too small, dropping... \n " ) ;
kfree_skb ( skb ) ;
ppp_input_error ( & ap - > chan , code ) ;
return 0 ; /* Don't return an error code, only for flow control... */
}
/*------------------------------------------------------------------*/
/*
* Function irnet_disconnect_indication ( instance , sap , reason , skb )
*
* Connection has been closed . Chech reason to find out why
*
* Note : there are many cases where we come here :
* o attempted to connect , timeout
* o connected , link is broken , LAP has timeout
* o connected , other side close the link
* o connection request on the server not handled
*/
static void
irnet_disconnect_indication ( void * instance ,
2007-02-09 23:24:53 +09:00
void * sap ,
2005-04-16 15:20:36 -07:00
LM_REASON reason ,
struct sk_buff * skb )
{
irnet_socket * self = ( irnet_socket * ) instance ;
int test_open ;
int test_connect ;
DENTER ( IRDA_TCB_TRACE , " (self=0x%p) \n " , self ) ;
DASSERT ( self ! = NULL , , IRDA_CB_ERROR , " Self is NULL !!! \n " ) ;
/* Don't care about it, but let's not leak it */
if ( skb )
dev_kfree_skb ( skb ) ;
/* Prevent higher layer from accessing IrTTP */
test_open = test_and_clear_bit ( 0 , & self - > ttp_open ) ;
/* Not connecting anymore...
* ( note : TSAP is open , so IAP callbacks are no longer pending . . . ) */
test_connect = test_and_clear_bit ( 0 , & self - > ttp_connect ) ;
/* If both self->ttp_open and self->ttp_connect are NULL, it mean that we
* have a race condition with irda_irnet_destroy ( ) or
* irnet_connect_indication ( ) , so don ' t mess up tsap . . .
*/
if ( ! ( test_open | | test_connect ) )
{
DERROR ( IRDA_CB_ERROR , " Race condition detected... \n " ) ;
return ;
}
/* If we were active, notify the control channel */
if ( test_open )
irnet_post_event ( self , IRNET_DISCONNECT_FROM ,
self - > saddr , self - > daddr , self - > rname , 0 ) ;
else
/* If we were trying to connect, notify the control channel */
if ( ( self - > tsap ) & & ( self ! = & irnet_server . s ) )
irnet_post_event ( self , IRNET_NOANSWER_FROM ,
self - > saddr , self - > daddr , self - > rname , 0 ) ;
/* Close our IrTTP connection, cleanup tsap */
if ( ( self - > tsap ) & & ( self ! = & irnet_server . s ) )
{
DEBUG ( IRDA_CB_INFO , " Closing our TTP connection. \n " ) ;
irttp_close_tsap ( self - > tsap ) ;
self - > tsap = NULL ;
}
/* Cleanup the socket in case we want to reconnect in ppp_output_wakeup() */
self - > stsap_sel = 0 ;
self - > daddr = DEV_ADDR_ANY ;
self - > tx_flow = FLOW_START ;
/* Deal with the ppp instance if it's still alive */
if ( self - > ppp_open )
{
if ( test_open )
{
2007-03-16 20:31:03 -07:00
/* ppp_unregister_channel() wants a user context. */
schedule_work ( & self - > disconnect_work ) ;
2005-04-16 15:20:36 -07:00
}
else
{
/* If we were trying to connect, flush (drain) ppp_generic
* Tx queue ( most often we have blocked it ) , which will
* trigger an other attempt to connect . If we are passive ,
* this will empty the Tx queue after last try . */
ppp_output_wakeup ( & self - > chan ) ;
}
}
DEXIT ( IRDA_TCB_TRACE , " \n " ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_connect_confirm ( instance , sap , qos , max_sdu_size , skb )
*
* Connections has been confirmed by the remote device
*
*/
static void
irnet_connect_confirm ( void * instance ,
2007-02-09 23:24:53 +09:00
void * sap ,
2005-04-16 15:20:36 -07:00
struct qos_info * qos ,
__u32 max_sdu_size ,
2007-02-09 23:24:53 +09:00
__u8 max_header_size ,
2005-04-16 15:20:36 -07:00
struct sk_buff * skb )
{
irnet_socket * self = ( irnet_socket * ) instance ;
DENTER ( IRDA_TCB_TRACE , " (self=0x%p) \n " , self ) ;
/* Check if socket is closing down (via irda_irnet_destroy()) */
if ( ! test_bit ( 0 , & self - > ttp_connect ) )
{
DERROR ( IRDA_CB_ERROR , " Socket no longer connecting. Ouch ! \n " ) ;
return ;
}
/* How much header space do we need to reserve */
self - > max_header_size = max_header_size ;
/* IrTTP max SDU size in transmit direction */
self - > max_sdu_size_tx = max_sdu_size ;
self - > max_data_size = max_sdu_size ;
# ifdef STREAM_COMPAT
if ( max_sdu_size = = 0 )
self - > max_data_size = irttp_get_max_seg_size ( self - > tsap ) ;
# endif /* STREAM_COMPAT */
/* At this point, IrLMP has assigned our source address */
self - > saddr = irttp_get_saddr ( self - > tsap ) ;
/* Allow higher layer to access IrTTP */
set_bit ( 0 , & self - > ttp_open ) ;
clear_bit ( 0 , & self - > ttp_connect ) ; /* Not racy, IrDA traffic is serial */
/* Give a kick in the ass of ppp_generic so that he sends us some data */
ppp_output_wakeup ( & self - > chan ) ;
/* Check size of received packet */
if ( skb - > len > 0 )
{
# ifdef PASS_CONNECT_PACKETS
DEBUG ( IRDA_CB_INFO , " Passing connect packet to PPP. \n " ) ;
/* Try to pass it to PPP */
irnet_data_indication ( instance , sap , skb ) ;
# else /* PASS_CONNECT_PACKETS */
DERROR ( IRDA_CB_ERROR , " Dropping non empty packet. \n " ) ;
kfree_skb ( skb ) ; /* Note : will be optimised with other kfree... */
# endif /* PASS_CONNECT_PACKETS */
}
else
kfree_skb ( skb ) ;
/* Notify the control channel */
irnet_post_event ( self , IRNET_CONNECT_TO ,
self - > saddr , self - > daddr , self - > rname , 0 ) ;
DEXIT ( IRDA_TCB_TRACE , " \n " ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_flow_indication ( instance , sap , flow )
*
* Used by TinyTP to tell us if it can accept more data or not
*
*/
static void
irnet_flow_indication ( void * instance ,
void * sap ,
2007-02-09 23:24:53 +09:00
LOCAL_FLOW flow )
2005-04-16 15:20:36 -07:00
{
irnet_socket * self = ( irnet_socket * ) instance ;
LOCAL_FLOW oldflow = self - > tx_flow ;
DENTER ( IRDA_TCB_TRACE , " (self=0x%p, flow=%d) \n " , self , flow ) ;
/* Update our state */
self - > tx_flow = flow ;
/* Check what IrTTP want us to do... */
switch ( flow )
{
case FLOW_START :
DEBUG ( IRDA_CB_INFO , " IrTTP wants us to start again \n " ) ;
/* Check if we really need to wake up PPP */
if ( oldflow = = FLOW_STOP )
ppp_output_wakeup ( & self - > chan ) ;
else
DEBUG ( IRDA_CB_INFO , " But we were already transmitting !!! \n " ) ;
break ;
case FLOW_STOP :
DEBUG ( IRDA_CB_INFO , " IrTTP wants us to slow down \n " ) ;
break ;
default :
DEBUG ( IRDA_CB_INFO , " Unknown flow command! \n " ) ;
break ;
}
DEXIT ( IRDA_TCB_TRACE , " \n " ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_status_indication ( instance , sap , reason , skb )
*
* Link ( IrLAP ) status report .
*
*/
static void
irnet_status_indication ( void * instance ,
LINK_STATUS link ,
LOCK_STATUS lock )
{
irnet_socket * self = ( irnet_socket * ) instance ;
DENTER ( IRDA_TCB_TRACE , " (self=0x%p) \n " , self ) ;
DASSERT ( self ! = NULL , , IRDA_CB_ERROR , " Self is NULL !!! \n " ) ;
/* We can only get this event if we are connected */
switch ( link )
{
case STATUS_NO_ACTIVITY :
irnet_post_event ( self , IRNET_BLOCKED_LINK ,
self - > saddr , self - > daddr , self - > rname , 0 ) ;
break ;
default :
DEBUG ( IRDA_CB_INFO , " Unknown status... \n " ) ;
}
DEXIT ( IRDA_TCB_TRACE , " \n " ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_connect_indication ( instance , sap , qos , max_sdu_size , userdata )
*
* Incoming connection
*
* In theory , this function is called only on the server socket .
* Some other node is attempting to connect to the IrNET service , and has
* sent a connection request on our server socket .
* We just redirect the connection to the relevant IrNET socket .
2007-02-09 23:24:53 +09:00
*
2005-04-16 15:20:36 -07:00
* Note : we also make sure that between 2 irnet nodes , there can
* exist only one irnet connection .
*/
static void
irnet_connect_indication ( void * instance ,
2007-02-09 23:24:53 +09:00
void * sap ,
2005-04-16 15:20:36 -07:00
struct qos_info * qos ,
__u32 max_sdu_size ,
__u8 max_header_size ,
struct sk_buff * skb )
{
irnet_socket * server = & irnet_server . s ;
irnet_socket * new = ( irnet_socket * ) NULL ;
DENTER ( IRDA_TCB_TRACE , " (server=0x%p) \n " , server ) ;
DASSERT ( instance = = & irnet_server , , IRDA_CB_ERROR ,
" Invalid instance (0x%p) !!! \n " , instance ) ;
DASSERT ( sap = = irnet_server . s . tsap , , IRDA_CB_ERROR , " Invalid sap !!! \n " ) ;
/* Try to find the most appropriate IrNET socket */
new = irnet_find_socket ( server ) ;
/* After all this hard work, do we have an socket ? */
if ( new = = ( irnet_socket * ) NULL )
{
DEXIT ( IRDA_CB_INFO , " : No socket waiting for this connection. \n " ) ;
irnet_disconnect_server ( server , skb ) ;
return ;
}
/* Is the socket already busy ? */
if ( test_bit ( 0 , & new - > ttp_open ) )
{
DEXIT ( IRDA_CB_INFO , " : Socket already connected. \n " ) ;
irnet_disconnect_server ( server , skb ) ;
return ;
}
/* The following code is a bit tricky, so need comments ;-)
*/
/* If ttp_connect is set, the socket is trying to connect to the other
* end and may have sent a IrTTP connection request and is waiting for
* a connection response ( that may never come ) .
* Now , the pain is that the socket may have opened a tsap and is
* waiting on it , while the other end is trying to connect to it on
* another tsap .
* Because IrNET can be peer to peer , we need to workaround this .
* Furthermore , the way the irnetd script is implemented , the
* target will create a second IrNET connection back to the
* originator and expect the originator to bind this new connection
* to the original PPPD instance .
* And of course , if we don ' t use irnetd , we can have a race when
* both side try to connect simultaneously , which could leave both
* connections half closed ( yuck ) .
* Conclusions :
* 1 ) The " originator " must accept the new connection and get rid
* of the old one so that irnetd works
* 2 ) One side must deny the new connection to avoid races ,
* but both side must agree on which side it is . . .
* Most often , the originator is primary at the LAP layer .
* Jean II
*/
/* Now, let's look at the way I wrote the test...
* We need to clear up the ttp_connect flag atomically to prevent
* irnet_disconnect_indication ( ) to mess up the tsap we are going to close .
* We want to clear the ttp_connect flag only if we close the tsap ,
* otherwise we will never close it , so we need to check for primary
* * before * doing the test on the flag .
* And of course , ALLOW_SIMULT_CONNECT can disable this entirely . . .
* Jean II
*/
/* Socket already connecting ? On primary ? */
if ( 0
# ifdef ALLOW_SIMULT_CONNECT
2009-11-29 16:55:45 -08:00
| | ( ( irttp_is_primary ( server - > tsap ) = = 1 ) & & /* primary */
( test_and_clear_bit ( 0 , & new - > ttp_connect ) ) )
2005-04-16 15:20:36 -07:00
# endif /* ALLOW_SIMULT_CONNECT */
)
{
DERROR ( IRDA_CB_ERROR , " Socket already connecting, but going to reuse it ! \n " ) ;
/* Cleanup the old TSAP if necessary - IrIAP will be cleaned up later */
if ( new - > tsap ! = NULL )
{
/* Close the old connection the new socket was attempting,
* so that we can hook it up to the new connection .
* It ' s now safe to do it . . . */
irttp_close_tsap ( new - > tsap ) ;
new - > tsap = NULL ;
}
}
else
{
/* Three options :
* 1 ) socket was not connecting or connected : ttp_connect should be 0.
* 2 ) we don ' t want to connect the socket because we are secondary or
* ALLOW_SIMULT_CONNECT is undefined . ttp_connect should be 1.
* 3 ) we are half way in irnet_disconnect_indication ( ) , and it ' s a
* nice race condition . . . Fortunately , we can detect that by checking
* if tsap is still alive . On the other hand , we can ' t be in
* irda_irnet_destroy ( ) otherwise we would not have found this
* socket in the hashbin .
* Jean II */
if ( ( test_bit ( 0 , & new - > ttp_connect ) ) | | ( new - > tsap ! = NULL ) )
{
/* Don't mess this socket, somebody else in in charge... */
DERROR ( IRDA_CB_ERROR , " Race condition detected, socket in use, abort connect... \n " ) ;
irnet_disconnect_server ( server , skb ) ;
return ;
}
}
/* So : at this point, we have a socket, and it is idle. Good ! */
irnet_connect_socket ( server , new , qos , max_sdu_size , max_header_size ) ;
/* Check size of received packet */
if ( skb - > len > 0 )
{
# ifdef PASS_CONNECT_PACKETS
DEBUG ( IRDA_CB_INFO , " Passing connect packet to PPP. \n " ) ;
/* Try to pass it to PPP */
irnet_data_indication ( new , new - > tsap , skb ) ;
# else /* PASS_CONNECT_PACKETS */
DERROR ( IRDA_CB_ERROR , " Dropping non empty packet. \n " ) ;
kfree_skb ( skb ) ; /* Note : will be optimised with other kfree... */
# endif /* PASS_CONNECT_PACKETS */
}
else
kfree_skb ( skb ) ;
DEXIT ( IRDA_TCB_TRACE , " \n " ) ;
}
/********************** IRDA-IAS/LMP CALLBACKS **********************/
/*
* These are the callbacks called by other layers of the IrDA stack ,
* mainly LMP for discovery and IAS for name queries .
*/
/*------------------------------------------------------------------*/
/*
* Function irnet_getvalue_confirm ( result , obj_id , value , priv )
*
* Got answer from remote LM - IAS , just connect
*
* This is the reply to a IAS query we were doing to find the TSAP of
* the device we want to connect to .
* If we have found a valid TSAP , just initiate the TTP connection
* on this TSAP .
*/
static void
irnet_getvalue_confirm ( int result ,
2007-02-09 23:24:53 +09:00
__u16 obj_id ,
2005-04-16 15:20:36 -07:00
struct ias_value * value ,
void * priv )
{
irnet_socket * self = ( irnet_socket * ) priv ;
DENTER ( IRDA_OCB_TRACE , " (self=0x%p) \n " , self ) ;
DASSERT ( self ! = NULL , , IRDA_OCB_ERROR , " Self is NULL !!! \n " ) ;
/* Check if already connected (via irnet_connect_socket())
* or socket is closing down ( via irda_irnet_destroy ( ) ) */
if ( ! test_bit ( 0 , & self - > ttp_connect ) )
{
DERROR ( IRDA_OCB_ERROR , " Socket no longer connecting. Ouch ! \n " ) ;
return ;
}
/* We probably don't need to make any more queries */
iriap_close ( self - > iriap ) ;
self - > iriap = NULL ;
/* Post process the IAS reply */
self - > dtsap_sel = irnet_ias_to_tsap ( self , result , value ) ;
/* If error, just go out */
if ( self - > errno )
{
clear_bit ( 0 , & self - > ttp_connect ) ;
DERROR ( IRDA_OCB_ERROR , " IAS connect failed ! (0x%X) \n " , self - > errno ) ;
return ;
}
DEBUG ( IRDA_OCB_INFO , " daddr = %08x, lsap = %d, starting IrTTP connection \n " ,
self - > daddr , self - > dtsap_sel ) ;
/* Start up TTP - non blocking */
irnet_connect_tsap ( self ) ;
DEXIT ( IRDA_OCB_TRACE , " \n " ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_discovervalue_confirm ( result , obj_id , value , priv )
*
* Handle the TSAP discovery procedure state machine .
* Got answer from remote LM - IAS , try next device
*
* We are doing a TSAP discovery procedure , and we got an answer to
* a IAS query we were doing to find the TSAP on one of the address
* in the discovery log .
*
* If we have found a valid TSAP for the first time , save it . If it ' s
* not the first time we found one , complain .
*
* If we have more addresses in the log , just initiate a new query .
* Note that those query may fail ( see irnet_discover_daddr_and_lsap_sel ( ) )
*
* Otherwise , wrap up the procedure ( cleanup ) , check if we have found
* any device and connect to it .
*/
static void
irnet_discovervalue_confirm ( int result ,
2007-02-09 23:24:53 +09:00
__u16 obj_id ,
2005-04-16 15:20:36 -07:00
struct ias_value * value ,
void * priv )
{
irnet_socket * self = ( irnet_socket * ) priv ;
__u8 dtsap_sel ; /* TSAP we are looking for */
DENTER ( IRDA_OCB_TRACE , " (self=0x%p) \n " , self ) ;
DASSERT ( self ! = NULL , , IRDA_OCB_ERROR , " Self is NULL !!! \n " ) ;
/* Check if already connected (via irnet_connect_socket())
* or socket is closing down ( via irda_irnet_destroy ( ) ) */
if ( ! test_bit ( 0 , & self - > ttp_connect ) )
{
DERROR ( IRDA_OCB_ERROR , " Socket no longer connecting. Ouch ! \n " ) ;
return ;
}
/* Post process the IAS reply */
dtsap_sel = irnet_ias_to_tsap ( self , result , value ) ;
/* Have we got something ? */
if ( self - > errno = = 0 )
{
/* We found the requested service */
if ( self - > daddr ! = DEV_ADDR_ANY )
{
DERROR ( IRDA_OCB_ERROR , " More than one device in range supports IrNET... \n " ) ;
}
else
{
/* First time we found that one, save it ! */
self - > daddr = self - > discoveries [ self - > disco_index ] . daddr ;
self - > dtsap_sel = dtsap_sel ;
}
}
/* If no failure */
if ( ( self - > errno = = - EADDRNOTAVAIL ) | | ( self - > errno = = 0 ) )
{
int ret ;
/* Search the next node */
ret = irnet_discover_next_daddr ( self ) ;
if ( ! ret )
{
/* In this case, the above request was non-blocking.
* We will return here after a while . . . */
return ;
}
/* In this case, we have processed the last discovery item */
}
/* No more queries to be done (failure or last one) */
/* We probably don't need to make any more queries */
iriap_close ( self - > iriap ) ;
self - > iriap = NULL ;
/* No more items : remove the log and signal termination */
DEBUG ( IRDA_OCB_INFO , " Cleaning up log (0x%p) \n " ,
self - > discoveries ) ;
if ( self - > discoveries ! = NULL )
{
/* Cleanup our copy of the discovery log */
kfree ( self - > discoveries ) ;
self - > discoveries = NULL ;
}
self - > disco_number = - 1 ;
/* Check out what we found */
if ( self - > daddr = = DEV_ADDR_ANY )
{
self - > daddr = DEV_ADDR_ANY ;
clear_bit ( 0 , & self - > ttp_connect ) ;
DEXIT ( IRDA_OCB_TRACE , " : cannot discover IrNET in any device !!! \n " ) ;
return ;
}
/* We have a valid address - just connect */
DEBUG ( IRDA_OCB_INFO , " daddr = %08x, lsap = %d, starting IrTTP connection \n " ,
self - > daddr , self - > dtsap_sel ) ;
/* Start up TTP - non blocking */
irnet_connect_tsap ( self ) ;
DEXIT ( IRDA_OCB_TRACE , " \n " ) ;
}
# ifdef DISCOVERY_EVENTS
/*------------------------------------------------------------------*/
/*
* Function irnet_discovery_indication ( discovery )
*
* Got a discovery indication from IrLMP , post an event
*
* Note : IrLMP take care of matching the hint mask for us , 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
* node support IrNET , IrLAN or both . The other solution is to check
* in IAS the PNP ids and service name .
* Note : even if a node support IrNET ( or IrLAN ) , it ' s no guarantee
* that we will be able to connect to it , the node might already be
* busy . . .
*
* One last thing : in some case , this function will trigger duplicate
* discovery events . On the other hand , we should catch all
* discoveries properly ( i . e . not miss one ) . Filtering duplicate here
* is to messy , so we leave that to user space . . .
*/
static void
irnet_discovery_indication ( discinfo_t * discovery ,
DISCOVERY_MODE mode ,
void * priv )
{
irnet_socket * self = & irnet_server . s ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
DENTER ( IRDA_OCB_TRACE , " (self=0x%p) \n " , self ) ;
DASSERT ( priv = = & irnet_server , , IRDA_OCB_ERROR ,
" Invalid instance (0x%p) !!! \n " , priv ) ;
DEBUG ( IRDA_OCB_INFO , " Discovered new IrNET/IrLAN node %s... \n " ,
discovery - > info ) ;
/* Notify the control channel */
irnet_post_event ( NULL , IRNET_DISCOVER ,
discovery - > saddr , discovery - > daddr , discovery - > info ,
2008-05-13 23:25:57 -07:00
get_unaligned ( ( __u16 * ) discovery - > hints ) ) ;
2005-04-16 15:20:36 -07:00
DEXIT ( IRDA_OCB_TRACE , " \n " ) ;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_expiry_indication ( expiry )
*
* Got a expiry indication from IrLMP , post an event
*
* Note : IrLMP take care of matching the hint mask for us , we only
* check if it is a " new " node . . .
*/
static void
irnet_expiry_indication ( discinfo_t * expiry ,
DISCOVERY_MODE mode ,
void * priv )
{
irnet_socket * self = & irnet_server . s ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
DENTER ( IRDA_OCB_TRACE , " (self=0x%p) \n " , self ) ;
DASSERT ( priv = = & irnet_server , , IRDA_OCB_ERROR ,
" Invalid instance (0x%p) !!! \n " , priv ) ;
DEBUG ( IRDA_OCB_INFO , " IrNET/IrLAN node %s expired... \n " ,
expiry - > info ) ;
/* Notify the control channel */
irnet_post_event ( NULL , IRNET_EXPIRE ,
expiry - > saddr , expiry - > daddr , expiry - > info ,
2008-05-13 23:25:57 -07:00
get_unaligned ( ( __u16 * ) expiry - > hints ) ) ;
2005-04-16 15:20:36 -07:00
DEXIT ( IRDA_OCB_TRACE , " \n " ) ;
}
# endif /* DISCOVERY_EVENTS */
/*********************** PROC ENTRY CALLBACKS ***********************/
/*
* We create a instance in the / proc filesystem , and here we take care
* of that . . .
*/
# ifdef CONFIG_PROC_FS
static int
2008-04-29 01:01:52 -07:00
irnet_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
irnet_socket * self ;
char * state ;
int i = 0 ;
/* Get the IrNET server information... */
2008-04-29 01:01:52 -07:00
seq_printf ( m , " IrNET server - " ) ;
seq_printf ( m , " IrDA state: %s, " ,
2005-04-16 15:20:36 -07:00
( irnet_server . running ? " running " : " dead " ) ) ;
2008-04-29 01:01:52 -07:00
seq_printf ( m , " stsap_sel: %02x, " , irnet_server . s . stsap_sel ) ;
seq_printf ( m , " dtsap_sel: %02x \n " , irnet_server . s . dtsap_sel ) ;
2005-04-16 15:20:36 -07:00
/* Do we need to continue ? */
if ( ! irnet_server . running )
2008-04-29 01:01:52 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
/* Protect access to the instance list */
spin_lock_bh ( & irnet_server . spinlock ) ;
/* Get the sockets one by one... */
self = ( irnet_socket * ) hashbin_get_first ( irnet_server . list ) ;
while ( self ! = NULL )
{
/* Start printing info about the socket. */
2008-04-29 01:01:52 -07:00
seq_printf ( m , " \n IrNET socket %d - " , i + + ) ;
2005-04-16 15:20:36 -07:00
/* First, get the requested configuration */
2008-04-29 01:01:52 -07:00
seq_printf ( m , " Requested IrDA name: \" %s \" , " , self - > rname ) ;
seq_printf ( m , " daddr: %08x, " , self - > rdaddr ) ;
seq_printf ( m , " saddr: %08x \n " , self - > rsaddr ) ;
2005-04-16 15:20:36 -07:00
/* Second, get all the PPP info */
2008-04-29 01:01:52 -07:00
seq_printf ( m , " PPP state: %s " ,
2005-04-16 15:20:36 -07:00
( self - > ppp_open ? " registered " : " unregistered " ) ) ;
if ( self - > ppp_open )
{
2008-04-29 01:01:52 -07:00
seq_printf ( m , " , unit: ppp%d " ,
2005-04-16 15:20:36 -07:00
ppp_unit_number ( & self - > chan ) ) ;
2008-04-29 01:01:52 -07:00
seq_printf ( m , " , channel: %d " ,
2005-04-16 15:20:36 -07:00
ppp_channel_index ( & self - > chan ) ) ;
2008-04-29 01:01:52 -07:00
seq_printf ( m , " , mru: %d " ,
2005-04-16 15:20:36 -07:00
self - > mru ) ;
/* Maybe add self->flags ? Later... */
}
/* Then, get all the IrDA specific info... */
if ( self - > ttp_open )
state = " connected " ;
else
if ( self - > tsap ! = NULL )
state = " connecting " ;
else
if ( self - > iriap ! = NULL )
state = " searching " ;
else
if ( self - > ttp_connect )
state = " weird " ;
else
state = " idle " ;
2008-04-29 01:01:52 -07:00
seq_printf ( m , " \n IrDA state: %s, " , state ) ;
seq_printf ( m , " daddr: %08x, " , self - > daddr ) ;
seq_printf ( m , " stsap_sel: %02x, " , self - > stsap_sel ) ;
seq_printf ( m , " dtsap_sel: %02x \n " , self - > dtsap_sel ) ;
2005-04-16 15:20:36 -07:00
/* Next socket, please... */
self = ( irnet_socket * ) hashbin_get_next ( irnet_server . list ) ;
}
/* Spin lock end */
spin_unlock_bh ( & irnet_server . spinlock ) ;
2008-04-29 01:01:52 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-04-29 01:01:52 -07:00
static int irnet_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , irnet_proc_show , NULL ) ;
}
static const struct file_operations irnet_proc_fops = {
. owner = THIS_MODULE ,
. open = irnet_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2005-04-16 15:20:36 -07:00
# endif /* PROC_FS */
/********************** CONFIGURATION/CLEANUP **********************/
/*
* Initialisation and teardown of the IrDA part , called at module
* insertion and removal . . .
*/
/*------------------------------------------------------------------*/
/*
* Prepare the IrNET layer for operation . . .
*/
int __init
irda_irnet_init ( void )
{
int err = 0 ;
DENTER ( MODULE_TRACE , " () \n " ) ;
/* Pure paranoia - should be redundant */
memset ( & irnet_server , 0 , sizeof ( struct irnet_root ) ) ;
/* Setup start of irnet instance list */
2007-02-09 23:24:53 +09:00
irnet_server . list = hashbin_new ( HB_NOLOCK ) ;
2005-04-16 15:20:36 -07:00
DABORT ( irnet_server . list = = NULL , - ENOMEM ,
MODULE_ERROR , " Can't allocate hashbin! \n " ) ;
/* Init spinlock for instance list */
spin_lock_init ( & irnet_server . spinlock ) ;
/* Initialise control channel */
init_waitqueue_head ( & irnet_events . rwait ) ;
irnet_events . index = 0 ;
/* Init spinlock for event logging */
spin_lock_init ( & irnet_events . spinlock ) ;
# ifdef CONFIG_PROC_FS
/* Add a /proc file for irnet infos */
2008-04-29 01:01:52 -07:00
proc_create ( " irnet " , 0 , proc_irda , & irnet_proc_fops ) ;
2005-04-16 15:20:36 -07:00
# endif /* CONFIG_PROC_FS */
/* Setup the IrNET server */
err = irnet_setup_server ( ) ;
if ( ! err )
/* We are no longer functional... */
irnet_server . running = 1 ;
DEXIT ( MODULE_TRACE , " \n " ) ;
return err ;
}
/*------------------------------------------------------------------*/
/*
* Cleanup at exit . . .
*/
void __exit
irda_irnet_cleanup ( void )
{
DENTER ( MODULE_TRACE , " () \n " ) ;
/* We are no longer there... */
irnet_server . running = 0 ;
# ifdef CONFIG_PROC_FS
/* Remove our /proc file */
remove_proc_entry ( " irnet " , proc_irda ) ;
# endif /* CONFIG_PROC_FS */
/* Remove our IrNET server from existence */
irnet_destroy_server ( ) ;
/* Remove all instances of IrNET socket still present */
hashbin_delete ( irnet_server . list , ( FREE_FUNC ) irda_irnet_destroy ) ;
DEXIT ( MODULE_TRACE , " \n " ) ;
}