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 : ircomm_tty_attach . c
2007-02-09 17:24:53 +03:00
* Version :
2005-04-17 02:20:36 +04:00
* Description : Code for attaching the serial driver to IrCOMM
* Status : Experimental .
* Author : Dag Brattli < dagb @ cs . uit . no >
* Created at : Sat Jun 5 17 : 42 : 00 1999
* Modified at : Tue Jan 4 14 : 20 : 49 2000
* Modified by : Dag Brattli < dagb @ cs . uit . no >
2007-02-09 17:24:53 +03:00
*
2005-04-17 02:20:36 +04:00
* Copyright ( c ) 1999 - 2000 Dag Brattli , All Rights Reserved .
* Copyright ( c ) 2000 - 2003 Jean Tourrilhes < jt @ hpl . hp . com >
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/init.h>
2009-10-07 17:09:06 +04:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <net/irda/irda.h>
# include <net/irda/irlmp.h>
# include <net/irda/iriap.h>
# include <net/irda/irttp.h>
# include <net/irda/irias_object.h>
# include <net/irda/parameters.h>
# include <net/irda/ircomm_core.h>
# include <net/irda/ircomm_param.h>
# include <net/irda/ircomm_event.h>
# include <net/irda/ircomm_tty.h>
# include <net/irda/ircomm_tty_attach.h>
static void ircomm_tty_ias_register ( struct ircomm_tty_cb * self ) ;
static void ircomm_tty_discovery_indication ( discinfo_t * discovery ,
DISCOVERY_MODE mode ,
void * priv ) ;
2007-02-09 17:24:53 +03:00
static void ircomm_tty_getvalue_confirm ( int result , __u16 obj_id ,
2005-04-17 02:20:36 +04:00
struct ias_value * value , void * priv ) ;
static void ircomm_tty_start_watchdog_timer ( struct ircomm_tty_cb * self ,
int timeout ) ;
static void ircomm_tty_watchdog_timer_expired ( void * data ) ;
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_idle ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info ) ;
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_search ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info ) ;
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_query_parameters ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info ) ;
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_query_lsap_sel ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info ) ;
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_setup ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info ) ;
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_ready ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info ) ;
2009-08-05 21:42:58 +04:00
const char * const ircomm_tty_state [ ] = {
2005-04-17 02:20:36 +04:00
" IRCOMM_TTY_IDLE " ,
" IRCOMM_TTY_SEARCH " ,
" IRCOMM_TTY_QUERY_PARAMETERS " ,
" IRCOMM_TTY_QUERY_LSAP_SEL " ,
" IRCOMM_TTY_SETUP " ,
" IRCOMM_TTY_READY " ,
" *** ERROR *** " ,
} ;
# ifdef CONFIG_IRDA_DEBUG
2009-08-05 21:42:58 +04:00
static const char * const ircomm_tty_event [ ] = {
2005-04-17 02:20:36 +04:00
" IRCOMM_TTY_ATTACH_CABLE " ,
" IRCOMM_TTY_DETACH_CABLE " ,
" IRCOMM_TTY_DATA_REQUEST " ,
" IRCOMM_TTY_DATA_INDICATION " ,
" IRCOMM_TTY_DISCOVERY_REQUEST " ,
" IRCOMM_TTY_DISCOVERY_INDICATION " ,
" IRCOMM_TTY_CONNECT_CONFIRM " ,
" IRCOMM_TTY_CONNECT_INDICATION " ,
" IRCOMM_TTY_DISCONNECT_REQUEST " ,
" IRCOMM_TTY_DISCONNECT_INDICATION " ,
" IRCOMM_TTY_WD_TIMER_EXPIRED " ,
" IRCOMM_TTY_GOT_PARAMETERS " ,
" IRCOMM_TTY_GOT_LSAPSEL " ,
" *** ERROR **** " ,
} ;
# endif /* CONFIG_IRDA_DEBUG */
static int ( * state [ ] ) ( struct ircomm_tty_cb * self , IRCOMM_TTY_EVENT event ,
2007-02-09 17:24:53 +03:00
struct sk_buff * skb , struct ircomm_tty_info * info ) =
2005-04-17 02:20:36 +04:00
{
ircomm_tty_state_idle ,
ircomm_tty_state_search ,
ircomm_tty_state_query_parameters ,
ircomm_tty_state_query_lsap_sel ,
ircomm_tty_state_setup ,
ircomm_tty_state_ready ,
} ;
/*
* Function ircomm_tty_attach_cable ( driver )
*
* Try to attach cable ( IrCOMM link ) . This function will only return
2007-02-09 17:24:53 +03:00
* when the link has been connected , or if an error condition occurs .
2005-04-17 02:20:36 +04:00
* If success , the return value is the resulting service type .
*/
int ircomm_tty_attach_cable ( struct ircomm_tty_cb * self )
{
2012-06-04 15:35:21 +04:00
struct tty_struct * tty ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return - 1 ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return - 1 ; ) ;
2007-02-09 17:24:53 +03:00
/* Check if somebody has already connected to us */
2005-04-17 02:20:36 +04:00
if ( ircomm_is_connected ( self - > ircomm ) ) {
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s(), already connected! \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* Make sure nobody tries to write before the link is up */
2012-06-04 15:35:21 +04:00
tty = tty_port_tty_get ( & self - > port ) ;
if ( tty ) {
tty - > hw_stopped = 1 ;
tty_kref_put ( tty ) ;
}
2005-04-17 02:20:36 +04:00
ircomm_tty_ias_register ( self ) ;
ircomm_tty_do_event ( self , IRCOMM_TTY_ATTACH_CABLE , NULL , NULL ) ;
return 0 ;
}
/*
* Function ircomm_detach_cable ( driver )
*
* Detach cable , or cable has been detached by peer
*
*/
void ircomm_tty_detach_cable ( struct ircomm_tty_cb * self )
{
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
del_timer ( & self - > watchdog_timer ) ;
/* Remove discovery handler */
if ( self - > ckey ) {
irlmp_unregister_client ( self - > ckey ) ;
self - > ckey = NULL ;
}
/* Remove IrCOMM hint bits */
if ( self - > skey ) {
irlmp_unregister_service ( self - > skey ) ;
self - > skey = NULL ;
}
2007-02-09 17:24:53 +03:00
if ( self - > iriap ) {
2005-04-17 02:20:36 +04:00
iriap_close ( self - > iriap ) ;
self - > iriap = NULL ;
}
/* Remove LM-IAS object */
if ( self - > obj ) {
irias_delete_object ( self - > obj ) ;
self - > obj = NULL ;
}
ircomm_tty_do_event ( self , IRCOMM_TTY_DETACH_CABLE , NULL , NULL ) ;
/* Reset some values */
self - > daddr = self - > saddr = 0 ;
self - > dlsap_sel = self - > slsap_sel = 0 ;
memset ( & self - > settings , 0 , sizeof ( struct ircomm_params ) ) ;
}
/*
* Function ircomm_tty_ias_register ( self )
*
* Register with LM - IAS depending on which service type we are
*
*/
static void ircomm_tty_ias_register ( struct ircomm_tty_cb * self )
{
__u8 oct_seq [ 6 ] ;
__u16 hints ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
/* Compute hint bits based on service */
hints = irlmp_service_to_hint ( S_COMM ) ;
if ( self - > service_type & IRCOMM_3_WIRE_RAW )
hints | = irlmp_service_to_hint ( S_PRINTER ) ;
/* Advertise IrCOMM hint bit in discovery */
if ( ! self - > skey )
self - > skey = irlmp_register_service ( hints ) ;
/* Set up a discovery handler */
if ( ! self - > ckey )
self - > ckey = irlmp_register_client ( hints ,
ircomm_tty_discovery_indication ,
NULL , ( void * ) self ) ;
/* If already done, no need to do it again */
if ( self - > obj )
return ;
if ( self - > service_type & IRCOMM_3_WIRE_RAW ) {
/* Register IrLPT with LM-IAS */
self - > obj = irias_new_object ( " IrLPT " , IAS_IRLPT_ID ) ;
2007-02-09 17:24:53 +03:00
irias_add_integer_attrib ( self - > obj , " IrDA:IrLMP:LsapSel " ,
2005-04-17 02:20:36 +04:00
self - > slsap_sel , IAS_KERNEL_ATTR ) ;
} else {
/* Register IrCOMM with LM-IAS */
self - > obj = irias_new_object ( " IrDA:IrCOMM " , IAS_IRCOMM_ID ) ;
2007-02-09 17:24:53 +03:00
irias_add_integer_attrib ( self - > obj , " IrDA:TinyTP:LsapSel " ,
2005-04-17 02:20:36 +04:00
self - > slsap_sel , IAS_KERNEL_ATTR ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
/* Code the parameters into the buffer */
2007-02-09 17:24:53 +03:00
irda_param_pack ( oct_seq , " bbbbbb " ,
2005-04-17 02:20:36 +04:00
IRCOMM_SERVICE_TYPE , 1 , self - > service_type ,
IRCOMM_PORT_TYPE , 1 , IRCOMM_SERIAL ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
/* Register parameters with LM-IAS */
irias_add_octseq_attrib ( self - > obj , " Parameters " , oct_seq , 6 ,
IAS_KERNEL_ATTR ) ;
}
irias_insert_object ( self - > obj ) ;
}
/*
* Function ircomm_tty_ias_unregister ( self )
*
* Remove our IAS object and client hook while connected .
*
*/
static void ircomm_tty_ias_unregister ( struct ircomm_tty_cb * self )
{
/* Remove LM-IAS object now so it is not reused.
* IrCOMM deals very poorly with multiple incoming connections .
* It should looks a lot more like IrNET , and " dup " a server TSAP
* to the application TSAP ( based on various rules ) .
* This is a cheap workaround allowing multiple clients to
* connect to us . It will not always work .
* Each IrCOMM socket has an IAS entry . Incoming connection will
* pick the first one found . So , when we are fully connected ,
* we remove our IAS entries so that the next IAS entry is used .
* We do that for * both * client and server , because a server
* can also create client instances .
* Jean II */
if ( self - > obj ) {
irias_delete_object ( self - > obj ) ;
self - > obj = NULL ;
}
#if 0
/* Remove discovery handler.
* While we are connected , we no longer need to receive
* discovery events . This would be the case if there is
* multiple IrLAP interfaces . Jean II */
if ( self - > ckey ) {
irlmp_unregister_client ( self - > ckey ) ;
self - > ckey = NULL ;
}
# endif
}
/*
* Function ircomm_send_initial_parameters ( self )
*
* Send initial parameters to the remote IrCOMM device . These parameters
* must be sent before any data .
*/
int ircomm_tty_send_initial_parameters ( struct ircomm_tty_cb * self )
{
IRDA_ASSERT ( self ! = NULL , return - 1 ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return - 1 ; ) ;
2007-02-09 17:24:53 +03:00
if ( self - > service_type & IRCOMM_3_WIRE_RAW )
2005-04-17 02:20:36 +04:00
return 0 ;
2007-02-09 17:24:53 +03:00
/*
* Set default values , but only if the application for some reason
2005-04-17 02:20:36 +04:00
* haven ' t set them already
*/
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), data-rate = %d \n " , __func__ ,
2005-04-17 02:20:36 +04:00
self - > settings . data_rate ) ;
if ( ! self - > settings . data_rate )
self - > settings . data_rate = 9600 ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), data-format = %d \n " , __func__ ,
2005-04-17 02:20:36 +04:00
self - > settings . data_format ) ;
if ( ! self - > settings . data_format )
self - > settings . data_format = IRCOMM_WSIZE_8 ; /* 8N1 */
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), flow-control = %d \n " , __func__ ,
2005-04-17 02:20:36 +04:00
self - > settings . flow_control ) ;
/*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
/* Do not set delta values for the initial parameters */
self - > settings . dte = IRCOMM_DTR | IRCOMM_RTS ;
/* Only send service type parameter when we are the client */
if ( self - > client )
ircomm_param_request ( self , IRCOMM_SERVICE_TYPE , FALSE ) ;
ircomm_param_request ( self , IRCOMM_DATA_RATE , FALSE ) ;
ircomm_param_request ( self , IRCOMM_DATA_FORMAT , FALSE ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
/* For a 3 wire service, we just flush the last parameter and return */
if ( self - > settings . service_type = = IRCOMM_3_WIRE ) {
ircomm_param_request ( self , IRCOMM_FLOW_CONTROL , TRUE ) ;
return 0 ;
}
/* Only 9-wire service types continue here */
ircomm_param_request ( self , IRCOMM_FLOW_CONTROL , FALSE ) ;
#if 0
ircomm_param_request ( self , IRCOMM_XON_XOFF , FALSE ) ;
ircomm_param_request ( self , IRCOMM_ENQ_ACK , FALSE ) ;
2007-02-09 17:24:53 +03:00
# endif
2005-04-17 02:20:36 +04:00
/* Notify peer that we are ready to receive data */
ircomm_param_request ( self , IRCOMM_DTE , TRUE ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Function ircomm_tty_discovery_indication ( discovery )
*
* Remote device is discovered , try query the remote IAS to see which
* device it is , and which services it has .
*
*/
static void ircomm_tty_discovery_indication ( discinfo_t * discovery ,
DISCOVERY_MODE mode ,
void * priv )
{
struct ircomm_tty_cb * self ;
struct ircomm_tty_info info ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
/* Important note :
* We need to drop all passive discoveries .
* The LSAP management of IrComm is deficient and doesn ' t deal
* with the case of two instance connecting to each other
* simultaneously ( it will deadlock in LMP ) .
* The proper fix would be to use the same technique as in IrNET ,
* to have one server socket and separate instances for the
* connecting / connected socket .
* The workaround is to drop passive discovery , which drastically
* reduce the probability of this happening .
* Jean II */
if ( mode = = DISCOVERY_PASSIVE )
return ;
info . daddr = discovery - > daddr ;
info . saddr = discovery - > saddr ;
2011-06-13 20:21:26 +04:00
self = priv ;
2007-10-18 06:34:11 +04:00
ircomm_tty_do_event ( self , IRCOMM_TTY_DISCOVERY_INDICATION ,
NULL , & info ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Function ircomm_tty_disconnect_indication ( instance , sap , reason , skb )
*
* Link disconnected
*
*/
2007-02-09 17:24:53 +03:00
void ircomm_tty_disconnect_indication ( void * instance , void * sap ,
2005-04-17 02:20:36 +04:00
LM_REASON reason ,
struct sk_buff * skb )
{
struct ircomm_tty_cb * self = ( struct ircomm_tty_cb * ) instance ;
2012-06-04 15:35:21 +04:00
struct tty_struct * tty ;
2005-04-17 02:20:36 +04:00
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
2012-06-04 15:35:21 +04:00
tty = tty_port_tty_get ( & self - > port ) ;
if ( ! tty )
2005-04-17 02:20:36 +04:00
return ;
/* This will stop control data transfers */
self - > flow = FLOW_STOP ;
/* Stop data transfers */
2012-06-04 15:35:21 +04:00
tty - > hw_stopped = 1 ;
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:53 +03:00
ircomm_tty_do_event ( self , IRCOMM_TTY_DISCONNECT_INDICATION , NULL ,
2005-04-17 02:20:36 +04:00
NULL ) ;
2012-06-04 15:35:21 +04:00
tty_kref_put ( tty ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Function ircomm_tty_getvalue_confirm ( result , obj_id , value , priv )
*
* Got result from the IAS query we make
*
*/
2007-02-09 17:24:53 +03:00
static void ircomm_tty_getvalue_confirm ( int result , __u16 obj_id ,
struct ias_value * value ,
2005-04-17 02:20:36 +04:00
void * priv )
{
struct ircomm_tty_cb * self = ( struct ircomm_tty_cb * ) priv ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
/* We probably don't need to make any more queries */
iriap_close ( self - > iriap ) ;
self - > iriap = NULL ;
/* Check if request succeeded */
if ( result ! = IAS_SUCCESS ) {
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 4 , " %s(), got NULL value! \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return ;
}
switch ( value - > type ) {
2007-02-09 17:24:53 +03:00
case IAS_OCT_SEQ :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), got octet sequence \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
irda_param_extract_all ( self , value - > t . oct_seq , value - > len ,
& ircomm_param_info ) ;
2007-02-09 17:24:53 +03:00
ircomm_tty_do_event ( self , IRCOMM_TTY_GOT_PARAMETERS , NULL ,
2005-04-17 02:20:36 +04:00
NULL ) ;
break ;
case IAS_INTEGER :
2007-02-09 17:24:53 +03:00
/* Got LSAP selector */
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), got lsapsel = %d \n " , __func__ ,
2005-04-17 02:20:36 +04:00
value - > t . integer ) ;
if ( value - > t . integer = = - 1 ) {
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s(), invalid value! \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
} else
self - > dlsap_sel = value - > t . integer ;
ircomm_tty_do_event ( self , IRCOMM_TTY_GOT_LSAPSEL , NULL , NULL ) ;
break ;
case IAS_MISSING :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s(), got IAS_MISSING \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s(), got unknown type! \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
break ;
}
irias_delete_value ( value ) ;
}
/*
* Function ircomm_tty_connect_confirm ( instance , sap , qos , max_sdu_size , skb )
*
* Connection confirmed
*
*/
2007-02-09 17:24:53 +03:00
void ircomm_tty_connect_confirm ( void * instance , void * sap ,
struct qos_info * qos ,
__u32 max_data_size ,
__u8 max_header_size ,
2005-04-17 02:20:36 +04:00
struct sk_buff * skb )
{
struct ircomm_tty_cb * self = ( struct ircomm_tty_cb * ) instance ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
self - > client = TRUE ;
self - > max_data_size = max_data_size ;
self - > max_header_size = max_header_size ;
self - > flow = FLOW_START ;
ircomm_tty_do_event ( self , IRCOMM_TTY_CONNECT_CONFIRM , NULL , NULL ) ;
/* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
}
/*
2007-02-09 17:24:53 +03:00
* Function ircomm_tty_connect_indication ( instance , sap , qos , max_sdu_size ,
2005-04-17 02:20:36 +04:00
* skb )
*
* we are discovered and being requested to connect by remote device !
*
*/
2007-02-09 17:24:53 +03:00
void ircomm_tty_connect_indication ( void * instance , void * sap ,
struct qos_info * qos ,
2005-04-17 02:20:36 +04:00
__u32 max_data_size ,
2007-02-09 17:24:53 +03:00
__u8 max_header_size ,
2005-04-17 02:20:36 +04:00
struct sk_buff * skb )
{
struct ircomm_tty_cb * self = ( struct ircomm_tty_cb * ) instance ;
int clen ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
self - > client = FALSE ;
self - > max_data_size = max_data_size ;
self - > max_header_size = max_header_size ;
self - > flow = FLOW_START ;
clen = skb - > data [ 0 ] ;
if ( clen )
2007-02-09 17:24:53 +03:00
irda_param_extract_all ( self , skb - > data + 1 ,
IRDA_MIN ( skb - > len , clen ) ,
2005-04-17 02:20:36 +04:00
& ircomm_param_info ) ;
ircomm_tty_do_event ( self , IRCOMM_TTY_CONNECT_INDICATION , NULL , NULL ) ;
/* No need to kfree_skb - see ircomm_ttp_connect_indication() */
}
/*
* Function ircomm_tty_link_established ( self )
*
* Called when the IrCOMM link is established
*
*/
void ircomm_tty_link_established ( struct ircomm_tty_cb * self )
{
2012-06-04 15:35:21 +04:00
struct tty_struct * tty ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
2012-06-04 15:35:21 +04:00
tty = tty_port_tty_get ( & self - > port ) ;
if ( ! tty )
2005-04-17 02:20:36 +04:00
return ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
del_timer ( & self - > watchdog_timer ) ;
2007-02-09 17:24:53 +03:00
/*
2005-04-17 02:20:36 +04:00
* IrCOMM link is now up , and if we are not using hardware
* flow - control , then declare the hardware as running . Otherwise we
* will have to wait for the peer device ( DCE ) to raise the CTS
2007-02-09 17:24:53 +03:00
* line .
2005-04-17 02:20:36 +04:00
*/
2012-06-04 15:35:19 +04:00
if ( ( self - > port . flags & ASYNC_CTS_FLOW ) & &
( ( self - > settings . dce & IRCOMM_CTS ) = = 0 ) ) {
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s(), waiting for CTS ... \n " , __func__ ) ;
2012-06-04 15:35:21 +04:00
goto put ;
2005-04-17 02:20:36 +04:00
} else {
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 1 , " %s(), starting hardware! \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2012-06-04 15:35:21 +04:00
tty - > hw_stopped = 0 ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
/* Wake up processes blocked on open */
2012-06-04 15:35:16 +04:00
wake_up_interruptible ( & self - > port . open_wait ) ;
2005-04-17 02:20:36 +04:00
}
schedule_work ( & self - > tqueue ) ;
2012-06-04 15:35:21 +04:00
put :
tty_kref_put ( tty ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Function ircomm_tty_start_watchdog_timer ( self , timeout )
*
2007-02-09 17:24:53 +03:00
* Start the watchdog timer . This timer is used to make sure that any
* connection attempt is successful , and if not , we will retry after
2005-04-17 02:20:36 +04:00
* the timeout
*/
static void ircomm_tty_start_watchdog_timer ( struct ircomm_tty_cb * self ,
int timeout )
{
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
irda_start_timer ( & self - > watchdog_timer , timeout , ( void * ) self ,
ircomm_tty_watchdog_timer_expired ) ;
}
/*
* Function ircomm_tty_watchdog_timer_expired ( data )
*
* Called when the connect procedure have taken to much time .
*
*/
static void ircomm_tty_watchdog_timer_expired ( void * data )
{
struct ircomm_tty_cb * self = ( struct ircomm_tty_cb * ) data ;
2007-02-09 17:24:53 +03:00
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
ircomm_tty_do_event ( self , IRCOMM_TTY_WD_TIMER_EXPIRED , NULL , NULL ) ;
}
/*
* Function ircomm_tty_do_event ( self , event , skb )
*
* Process event
*
*/
int ircomm_tty_do_event ( struct ircomm_tty_cb * self , IRCOMM_TTY_EVENT event ,
2007-02-09 17:24:53 +03:00
struct sk_buff * skb , struct ircomm_tty_info * info )
2005-04-17 02:20:36 +04:00
{
IRDA_ASSERT ( self ! = NULL , return - 1 ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return - 1 ; ) ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s: state=%s, event=%s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_state [ self - > state ] , ircomm_tty_event [ event ] ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
return ( * state [ self - > state ] ) ( self , event , skb , info ) ;
}
/*
* Function ircomm_tty_next_state ( self , state )
*
* Switch state
*
*/
static inline void ircomm_tty_next_state ( struct ircomm_tty_cb * self , IRCOMM_TTY_STATE state )
{
/*
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRCOMM_TTY_MAGIC , return ; ) ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s: next state=%s, service type=%d \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_state [ self - > state ] , self - > service_type ) ;
*/
self - > state = state ;
}
/*
* Function ircomm_tty_state_idle ( self , event , skb , info )
*
* Just hanging around
*
*/
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_idle ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info )
{
int ret = 0 ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s: state=%s, event=%s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_state [ self - > state ] , ircomm_tty_event [ event ] ) ;
switch ( event ) {
case IRCOMM_TTY_ATTACH_CABLE :
2007-02-09 17:24:53 +03:00
/* Try to discover any remote devices */
2005-04-17 02:20:36 +04:00
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_SEARCH ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
irlmp_discovery_request ( DISCOVERY_DEFAULT_SLOTS ) ;
break ;
case IRCOMM_TTY_DISCOVERY_INDICATION :
self - > daddr = info - > daddr ;
self - > saddr = info - > saddr ;
if ( self - > iriap ) {
IRDA_WARNING ( " %s(), busy with a previous query \n " ,
2008-03-06 07:47:47 +03:00
__func__ ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
self - > iriap = iriap_open ( LSAP_ANY , IAS_CLIENT , self ,
ircomm_tty_getvalue_confirm ) ;
iriap_getvaluebyclass_request ( self - > iriap ,
self - > saddr , self - > daddr ,
" IrDA:IrCOMM " , " Parameters " ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_QUERY_PARAMETERS ) ;
break ;
case IRCOMM_TTY_CONNECT_INDICATION :
del_timer ( & self - > watchdog_timer ) ;
/* Accept connection */
ircomm_connect_response ( self - > ircomm , NULL ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_READY ) ;
break ;
case IRCOMM_TTY_WD_TIMER_EXPIRED :
/* Just stay idle */
break ;
case IRCOMM_TTY_DETACH_CABLE :
ircomm_tty_next_state ( self , IRCOMM_TTY_IDLE ) ;
break ;
default :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), unknown event: %s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_event [ event ] ) ;
ret = - EINVAL ;
}
return ret ;
}
/*
* Function ircomm_tty_state_search ( self , event , skb , info )
*
* Trying to discover an IrCOMM device
*
*/
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_search ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info )
{
int ret = 0 ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s: state=%s, event=%s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_state [ self - > state ] , ircomm_tty_event [ event ] ) ;
switch ( event ) {
case IRCOMM_TTY_DISCOVERY_INDICATION :
self - > daddr = info - > daddr ;
self - > saddr = info - > saddr ;
if ( self - > iriap ) {
IRDA_WARNING ( " %s(), busy with a previous query \n " ,
2008-03-06 07:47:47 +03:00
__func__ ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
self - > iriap = iriap_open ( LSAP_ANY , IAS_CLIENT , self ,
ircomm_tty_getvalue_confirm ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
if ( self - > service_type = = IRCOMM_3_WIRE_RAW ) {
iriap_getvaluebyclass_request ( self - > iriap , self - > saddr ,
2007-02-09 17:24:53 +03:00
self - > daddr , " IrLPT " ,
2005-04-17 02:20:36 +04:00
" IrDA:IrLMP:LsapSel " ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_QUERY_LSAP_SEL ) ;
} else {
iriap_getvaluebyclass_request ( self - > iriap , self - > saddr ,
2007-02-09 17:24:53 +03:00
self - > daddr ,
" IrDA:IrCOMM " ,
2005-04-17 02:20:36 +04:00
" Parameters " ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_QUERY_PARAMETERS ) ;
}
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
break ;
case IRCOMM_TTY_CONNECT_INDICATION :
del_timer ( & self - > watchdog_timer ) ;
ircomm_tty_ias_unregister ( self ) ;
/* Accept connection */
ircomm_connect_response ( self - > ircomm , NULL ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_READY ) ;
break ;
case IRCOMM_TTY_WD_TIMER_EXPIRED :
# if 1
/* Give up */
# else
2007-02-09 17:24:53 +03:00
/* Try to discover any remote devices */
2005-04-17 02:20:36 +04:00
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
irlmp_discovery_request ( DISCOVERY_DEFAULT_SLOTS ) ;
# endif
break ;
case IRCOMM_TTY_DETACH_CABLE :
ircomm_tty_next_state ( self , IRCOMM_TTY_IDLE ) ;
break ;
default :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), unknown event: %s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_event [ event ] ) ;
ret = - EINVAL ;
}
return ret ;
}
/*
* Function ircomm_tty_state_query ( self , event , skb , info )
*
* Querying the remote LM - IAS for IrCOMM parameters
*
*/
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_query_parameters ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info )
{
int ret = 0 ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s: state=%s, event=%s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_state [ self - > state ] , ircomm_tty_event [ event ] ) ;
switch ( event ) {
case IRCOMM_TTY_GOT_PARAMETERS :
if ( self - > iriap ) {
IRDA_WARNING ( " %s(), busy with a previous query \n " ,
2008-03-06 07:47:47 +03:00
__func__ ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
self - > iriap = iriap_open ( LSAP_ANY , IAS_CLIENT , self ,
ircomm_tty_getvalue_confirm ) ;
2007-02-09 17:24:53 +03:00
iriap_getvaluebyclass_request ( self - > iriap , self - > saddr ,
self - > daddr , " IrDA:IrCOMM " ,
2005-04-17 02:20:36 +04:00
" IrDA:TinyTP:LsapSel " ) ;
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_QUERY_LSAP_SEL ) ;
break ;
case IRCOMM_TTY_WD_TIMER_EXPIRED :
/* Go back to search mode */
ircomm_tty_next_state ( self , IRCOMM_TTY_SEARCH ) ;
2007-02-09 17:24:53 +03:00
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
2005-04-17 02:20:36 +04:00
break ;
case IRCOMM_TTY_CONNECT_INDICATION :
del_timer ( & self - > watchdog_timer ) ;
ircomm_tty_ias_unregister ( self ) ;
/* Accept connection */
ircomm_connect_response ( self - > ircomm , NULL ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_READY ) ;
break ;
case IRCOMM_TTY_DETACH_CABLE :
ircomm_tty_next_state ( self , IRCOMM_TTY_IDLE ) ;
break ;
default :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), unknown event: %s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_event [ event ] ) ;
ret = - EINVAL ;
}
return ret ;
}
/*
* Function ircomm_tty_state_query_lsap_sel ( self , event , skb , info )
*
* Query remote LM - IAS for the LSAP selector which we can connect to
*
*/
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_query_lsap_sel ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info )
{
int ret = 0 ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s: state=%s, event=%s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_state [ self - > state ] , ircomm_tty_event [ event ] ) ;
switch ( event ) {
case IRCOMM_TTY_GOT_LSAPSEL :
/* Connect to remote device */
ret = ircomm_connect_request ( self - > ircomm , self - > dlsap_sel ,
2007-02-09 17:24:53 +03:00
self - > saddr , self - > daddr ,
2005-04-17 02:20:36 +04:00
NULL , self - > service_type ) ;
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_SETUP ) ;
break ;
case IRCOMM_TTY_WD_TIMER_EXPIRED :
/* Go back to search mode */
ircomm_tty_next_state ( self , IRCOMM_TTY_SEARCH ) ;
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
break ;
case IRCOMM_TTY_CONNECT_INDICATION :
del_timer ( & self - > watchdog_timer ) ;
ircomm_tty_ias_unregister ( self ) ;
/* Accept connection */
ircomm_connect_response ( self - > ircomm , NULL ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_READY ) ;
break ;
case IRCOMM_TTY_DETACH_CABLE :
ircomm_tty_next_state ( self , IRCOMM_TTY_IDLE ) ;
break ;
default :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), unknown event: %s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_event [ event ] ) ;
ret = - EINVAL ;
}
return ret ;
}
/*
* Function ircomm_tty_state_setup ( self , event , skb , info )
*
* Trying to connect
*
*/
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_setup ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info )
{
int ret = 0 ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s: state=%s, event=%s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_state [ self - > state ] , ircomm_tty_event [ event ] ) ;
switch ( event ) {
case IRCOMM_TTY_CONNECT_CONFIRM :
del_timer ( & self - > watchdog_timer ) ;
ircomm_tty_ias_unregister ( self ) ;
2007-02-09 17:24:53 +03:00
/*
2005-04-17 02:20:36 +04:00
* Send initial parameters . This will also send out queued
2007-02-09 17:24:53 +03:00
* parameters waiting for the connection to come up
2005-04-17 02:20:36 +04:00
*/
ircomm_tty_send_initial_parameters ( self ) ;
ircomm_tty_link_established ( self ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_READY ) ;
break ;
case IRCOMM_TTY_CONNECT_INDICATION :
del_timer ( & self - > watchdog_timer ) ;
ircomm_tty_ias_unregister ( self ) ;
2007-02-09 17:24:53 +03:00
2005-04-17 02:20:36 +04:00
/* Accept connection */
ircomm_connect_response ( self - > ircomm , NULL ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_READY ) ;
break ;
case IRCOMM_TTY_WD_TIMER_EXPIRED :
/* Go back to search mode */
ircomm_tty_next_state ( self , IRCOMM_TTY_SEARCH ) ;
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
break ;
case IRCOMM_TTY_DETACH_CABLE :
/* ircomm_disconnect_request(self->ircomm, NULL); */
ircomm_tty_next_state ( self , IRCOMM_TTY_IDLE ) ;
break ;
default :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), unknown event: %s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_event [ event ] ) ;
ret = - EINVAL ;
}
return ret ;
}
/*
* Function ircomm_tty_state_ready ( self , event , skb , info )
*
* IrCOMM is now connected
*
*/
2007-02-09 17:24:53 +03:00
static int ircomm_tty_state_ready ( struct ircomm_tty_cb * self ,
IRCOMM_TTY_EVENT event ,
struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct ircomm_tty_info * info )
{
int ret = 0 ;
switch ( event ) {
case IRCOMM_TTY_DATA_REQUEST :
ret = ircomm_data_request ( self - > ircomm , skb ) ;
2007-02-09 17:24:53 +03:00
break ;
2005-04-17 02:20:36 +04:00
case IRCOMM_TTY_DETACH_CABLE :
ircomm_disconnect_request ( self - > ircomm , NULL ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_IDLE ) ;
break ;
case IRCOMM_TTY_DISCONNECT_INDICATION :
ircomm_tty_ias_register ( self ) ;
ircomm_tty_next_state ( self , IRCOMM_TTY_SEARCH ) ;
ircomm_tty_start_watchdog_timer ( self , 3 * HZ ) ;
2012-06-04 15:35:19 +04:00
if ( self - > port . flags & ASYNC_CHECK_CD ) {
2005-04-17 02:20:36 +04:00
/* Drop carrier */
self - > settings . dce = IRCOMM_DELTA_CD ;
ircomm_tty_check_modem_status ( self ) ;
} else {
2012-06-04 15:35:21 +04:00
struct tty_struct * tty = tty_port_tty_get ( & self - > port ) ;
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 0 , " %s(), hanging up! \n " , __func__ ) ;
2012-06-04 15:35:21 +04:00
if ( tty ) {
tty_hangup ( tty ) ;
tty_kref_put ( tty ) ;
}
2005-04-17 02:20:36 +04:00
}
break ;
default :
2008-03-06 07:47:47 +03:00
IRDA_DEBUG ( 2 , " %s(), unknown event: %s \n " , __func__ ,
2005-04-17 02:20:36 +04:00
ircomm_tty_event [ event ] ) ;
ret = - EINVAL ;
}
return ret ;
}