2005-04-16 15:20:36 -07:00
/*********************************************************************
2007-02-09 23:24:53 +09:00
*
2005-04-16 15:20:36 -07:00
* Filename : irlan_provider . c
* Version : 0.9
* Description : IrDA LAN Access Protocol Implementation
* Status : Experimental .
* Author : Dag Brattli < dagb @ cs . uit . no >
* Created at : Sun Aug 31 20 : 14 : 37 1997
* Modified at : Sat Oct 30 12 : 52 : 10 1999
* Modified by : Dag Brattli < dagb @ cs . uit . no >
* Sources : skeleton . c by Donald Becker < becker @ CESDIS . gsfc . nasa . gov >
* slip . c by Laurence Culhane , < loz @ holmes . demon . co . uk >
* Fred N . van Kempen , < waltje @ uwalt . nl . mugnet . org >
2007-02-09 23:24:53 +09:00
*
* Copyright ( c ) 1998 - 1999 Dag Brattli < dagb @ cs . uit . no > ,
2005-04-16 15:20:36 -07:00
* All Rights Reserved .
2007-02-09 23:24:53 +09: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-16 15:20:36 -07:00
* the License , or ( at your option ) any later version .
*
2007-10-19 23:21:04 +02:00
* Neither Dag Brattli nor University of Tromsø admit liability nor
2007-02-09 23:24:53 +09:00
* provide warranty for any of this software . This material is
2005-04-16 15:20:36 -07:00
* provided " AS-IS " and at no charge .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/init.h>
# include <linux/random.h>
# include <linux/bitops.h>
# include <asm/system.h>
# include <asm/byteorder.h>
# include <net/irda/irda.h>
# include <net/irda/irttp.h>
# include <net/irda/irlmp.h>
# include <net/irda/irias_object.h>
# include <net/irda/iriap.h>
# include <net/irda/timer.h>
# include <net/irda/irlan_common.h>
# include <net/irda/irlan_eth.h>
# include <net/irda/irlan_event.h>
# include <net/irda/irlan_provider.h>
# include <net/irda/irlan_filter.h>
# include <net/irda/irlan_client.h>
2007-02-09 23:24:53 +09:00
static void irlan_provider_connect_indication ( void * instance , void * sap ,
struct qos_info * qos ,
2005-04-16 15:20:36 -07:00
__u32 max_sdu_size ,
__u8 max_header_size ,
struct sk_buff * skb ) ;
/*
* Function irlan_provider_control_data_indication ( handle , skb )
*
* This function gets the data that is received on the control channel
*
*/
2007-02-09 23:24:53 +09:00
static int irlan_provider_data_indication ( void * instance , void * sap ,
struct sk_buff * skb )
2005-04-16 15:20:36 -07:00
{
struct irlan_cb * self ;
__u8 code ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
self = ( struct irlan_cb * ) instance ;
IRDA_ASSERT ( self ! = NULL , return - 1 ; ) ;
IRDA_ASSERT ( self - > magic = = IRLAN_MAGIC , return - 1 ; ) ;
IRDA_ASSERT ( skb ! = NULL , return - 1 ; ) ;
code = skb - > data [ 0 ] ;
switch ( code ) {
case CMD_GET_PROVIDER_INFO :
IRDA_DEBUG ( 4 , " Got GET_PROVIDER_INFO command! \n " ) ;
2007-02-09 23:24:53 +09:00
irlan_do_provider_event ( self , IRLAN_GET_INFO_CMD , skb ) ;
2005-04-16 15:20:36 -07:00
break ;
case CMD_GET_MEDIA_CHAR :
IRDA_DEBUG ( 4 , " Got GET_MEDIA_CHAR command! \n " ) ;
2007-02-09 23:24:53 +09:00
irlan_do_provider_event ( self , IRLAN_GET_MEDIA_CMD , skb ) ;
2005-04-16 15:20:36 -07:00
break ;
case CMD_OPEN_DATA_CHANNEL :
IRDA_DEBUG ( 4 , " Got OPEN_DATA_CHANNEL command! \n " ) ;
2007-02-09 23:24:53 +09:00
irlan_do_provider_event ( self , IRLAN_OPEN_DATA_CMD , skb ) ;
2005-04-16 15:20:36 -07:00
break ;
case CMD_FILTER_OPERATION :
IRDA_DEBUG ( 4 , " Got FILTER_OPERATION command! \n " ) ;
irlan_do_provider_event ( self , IRLAN_FILTER_CONFIG_CMD , skb ) ;
break ;
case CMD_RECONNECT_DATA_CHAN :
IRDA_DEBUG ( 2 , " %s(), Got RECONNECT_DATA_CHAN command \n " , __FUNCTION__ ) ;
IRDA_DEBUG ( 2 , " %s(), NOT IMPLEMENTED \n " , __FUNCTION__ ) ;
break ;
case CMD_CLOSE_DATA_CHAN :
IRDA_DEBUG ( 2 , " Got CLOSE_DATA_CHAN command! \n " ) ;
IRDA_DEBUG ( 2 , " %s(), NOT IMPLEMENTED \n " , __FUNCTION__ ) ;
break ;
default :
IRDA_DEBUG ( 2 , " %s(), Unknown command! \n " , __FUNCTION__ ) ;
break ;
}
return 0 ;
}
/*
* Function irlan_provider_connect_indication ( handle , skb , priv )
*
* Got connection from peer IrLAN client
*
*/
2007-02-09 23:24:53 +09:00
static void irlan_provider_connect_indication ( void * instance , void * sap ,
2005-04-16 15:20:36 -07:00
struct qos_info * qos ,
2007-02-09 23:24:53 +09:00
__u32 max_sdu_size ,
2005-04-16 15:20:36 -07:00
__u8 max_header_size ,
struct sk_buff * skb )
{
struct irlan_cb * self ;
struct tsap_cb * tsap ;
__u32 saddr , daddr ;
IRDA_DEBUG ( 0 , " %s() \n " , __FUNCTION__ ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
self = ( struct irlan_cb * ) instance ;
tsap = ( struct tsap_cb * ) sap ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRLAN_MAGIC , return ; ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
IRDA_ASSERT ( tsap = = self - > provider . tsap_ctrl , return ; ) ;
IRDA_ASSERT ( self - > provider . state = = IRLAN_IDLE , return ; ) ;
daddr = irttp_get_daddr ( tsap ) ;
saddr = irttp_get_saddr ( tsap ) ;
self - > provider . max_sdu_size = max_sdu_size ;
self - > provider . max_header_size = max_header_size ;
irlan_do_provider_event ( self , IRLAN_CONNECT_INDICATION , NULL ) ;
2007-02-09 23:24:53 +09:00
/*
2005-04-16 15:20:36 -07:00
* If we are in peer mode , the client may not have got the discovery
2007-02-09 23:24:53 +09:00
* indication it needs to make progress . If the client is still in
* IDLE state , we must kick it .
2005-04-16 15:20:36 -07:00
*/
2007-02-09 23:24:53 +09:00
if ( ( self - > provider . access_type = = ACCESS_PEER ) & &
( self - > client . state = = IRLAN_IDLE ) )
2005-04-16 15:20:36 -07:00
{
irlan_client_wakeup ( self , self - > saddr , self - > daddr ) ;
}
}
/*
* Function irlan_provider_connect_response ( handle )
*
* Accept incoming connection
*
*/
void irlan_provider_connect_response ( struct irlan_cb * self ,
struct tsap_cb * tsap )
{
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRLAN_MAGIC , return ; ) ;
/* Just accept */
irttp_connect_response ( tsap , IRLAN_MTU , NULL ) ;
}
2007-02-09 23:24:53 +09:00
static void irlan_provider_disconnect_indication ( void * instance , void * sap ,
LM_REASON reason ,
struct sk_buff * userdata )
2005-04-16 15:20:36 -07:00
{
struct irlan_cb * self ;
struct tsap_cb * tsap ;
IRDA_DEBUG ( 4 , " %s(), reason=%d \n " , __FUNCTION__ , reason ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
self = ( struct irlan_cb * ) instance ;
tsap = ( struct tsap_cb * ) sap ;
IRDA_ASSERT ( self ! = NULL , return ; ) ;
2007-02-09 23:24:53 +09:00
IRDA_ASSERT ( self - > magic = = IRLAN_MAGIC , return ; ) ;
2005-04-16 15:20:36 -07:00
IRDA_ASSERT ( tsap ! = NULL , return ; ) ;
IRDA_ASSERT ( tsap - > magic = = TTP_TSAP_MAGIC , return ; ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
IRDA_ASSERT ( tsap = = self - > provider . tsap_ctrl , return ; ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
irlan_do_provider_event ( self , IRLAN_LMP_DISCONNECT , NULL ) ;
}
/*
* Function irlan_parse_open_data_cmd ( self , skb )
*
2007-02-09 23:24:53 +09:00
*
2005-04-16 15:20:36 -07:00
*
*/
int irlan_parse_open_data_cmd ( struct irlan_cb * self , struct sk_buff * skb )
{
int ret ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
ret = irlan_provider_parse_command ( self , CMD_OPEN_DATA_CHANNEL , skb ) ;
/* Open data channel */
irlan_open_data_tsap ( self ) ;
return ret ;
}
/*
* Function parse_command ( skb )
*
2007-02-09 23:24:53 +09:00
* Extract all parameters from received buffer , then feed them to
2005-04-16 15:20:36 -07:00
* check_params for parsing
*
*/
int irlan_provider_parse_command ( struct irlan_cb * self , int cmd ,
2007-02-09 23:24:53 +09:00
struct sk_buff * skb )
2005-04-16 15:20:36 -07:00
{
__u8 * frame ;
__u8 * ptr ;
int count ;
__u16 val_len ;
int i ;
char * name ;
2007-02-09 23:24:53 +09:00
char * value ;
2005-04-16 15:20:36 -07:00
int ret = RSP_SUCCESS ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
IRDA_ASSERT ( skb ! = NULL , return - RSP_PROTOCOL_ERROR ; ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
IRDA_DEBUG ( 4 , " %s(), skb->len=%d \n " , __FUNCTION__ , ( int ) skb - > len ) ;
IRDA_ASSERT ( self ! = NULL , return - RSP_PROTOCOL_ERROR ; ) ;
IRDA_ASSERT ( self - > magic = = IRLAN_MAGIC , return - RSP_PROTOCOL_ERROR ; ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
if ( ! skb )
return - RSP_PROTOCOL_ERROR ;
frame = skb - > data ;
name = kmalloc ( 255 , GFP_ATOMIC ) ;
if ( ! name )
return - RSP_INSUFFICIENT_RESOURCES ;
value = kmalloc ( 1016 , GFP_ATOMIC ) ;
if ( ! value ) {
kfree ( name ) ;
return - RSP_INSUFFICIENT_RESOURCES ;
}
/* How many parameters? */
count = frame [ 1 ] ;
IRDA_DEBUG ( 4 , " Got %d parameters \n " , count ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
ptr = frame + 2 ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
/* For all parameters */
2007-02-09 23:24:53 +09:00
for ( i = 0 ; i < count ; i + + ) {
2005-04-16 15:20:36 -07:00
ret = irlan_extract_param ( ptr , name , value , & val_len ) ;
if ( ret < 0 ) {
IRDA_DEBUG ( 2 , " %s(), IrLAN, Error! \n " , __FUNCTION__ ) ;
break ;
}
ptr + = ret ;
ret = RSP_SUCCESS ;
irlan_check_command_param ( self , name , value ) ;
}
/* Cleanup */
kfree ( name ) ;
kfree ( value ) ;
return ret ;
}
/*
* Function irlan_provider_send_reply ( self , info )
*
* Send reply to query to peer IrLAN layer
*
*/
2007-02-09 23:24:53 +09:00
void irlan_provider_send_reply ( struct irlan_cb * self , int command ,
2005-04-16 15:20:36 -07:00
int ret_code )
{
struct sk_buff * skb ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRLAN_MAGIC , return ; ) ;
2006-09-27 20:06:44 -07:00
skb = alloc_skb ( IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
/* Bigger param length comes from CMD_GET_MEDIA_CHAR */
IRLAN_STRING_PARAMETER_LEN ( " FILTER_TYPE " , " DIRECTED " ) +
IRLAN_STRING_PARAMETER_LEN ( " FILTER_TYPE " , " BORADCAST " ) +
IRLAN_STRING_PARAMETER_LEN ( " FILTER_TYPE " , " MULTICAST " ) +
IRLAN_STRING_PARAMETER_LEN ( " ACCESS_TYPE " , " HOSTED " ) ,
GFP_ATOMIC ) ;
2005-04-16 15:20:36 -07:00
if ( ! skb )
return ;
/* Reserve space for TTP, LMP, and LAP header */
skb_reserve ( skb , self - > provider . max_header_size ) ;
skb_put ( skb , 2 ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
switch ( command ) {
case CMD_GET_PROVIDER_INFO :
skb - > data [ 0 ] = 0x00 ; /* Success */
skb - > data [ 1 ] = 0x02 ; /* 2 parameters */
switch ( self - > media ) {
case MEDIA_802_3 :
irlan_insert_string_param ( skb , " MEDIA " , " 802.3 " ) ;
break ;
case MEDIA_802_5 :
irlan_insert_string_param ( skb , " MEDIA " , " 802.5 " ) ;
break ;
default :
IRDA_DEBUG ( 2 , " %s(), unknown media type! \n " , __FUNCTION__ ) ;
break ;
}
irlan_insert_short_param ( skb , " IRLAN_VER " , 0x0101 ) ;
break ;
case CMD_GET_MEDIA_CHAR :
skb - > data [ 0 ] = 0x00 ; /* Success */
skb - > data [ 1 ] = 0x05 ; /* 5 parameters */
irlan_insert_string_param ( skb , " FILTER_TYPE " , " DIRECTED " ) ;
irlan_insert_string_param ( skb , " FILTER_TYPE " , " BROADCAST " ) ;
irlan_insert_string_param ( skb , " FILTER_TYPE " , " MULTICAST " ) ;
switch ( self - > provider . access_type ) {
case ACCESS_DIRECT :
irlan_insert_string_param ( skb , " ACCESS_TYPE " , " DIRECT " ) ;
break ;
case ACCESS_PEER :
irlan_insert_string_param ( skb , " ACCESS_TYPE " , " PEER " ) ;
break ;
case ACCESS_HOSTED :
irlan_insert_string_param ( skb , " ACCESS_TYPE " , " HOSTED " ) ;
break ;
default :
IRDA_DEBUG ( 2 , " %s(), Unknown access type \n " , __FUNCTION__ ) ;
break ;
}
irlan_insert_short_param ( skb , " MAX_FRAME " , 0x05ee ) ;
break ;
case CMD_OPEN_DATA_CHANNEL :
skb - > data [ 0 ] = 0x00 ; /* Success */
if ( self - > provider . send_arb_val ) {
skb - > data [ 1 ] = 0x03 ; /* 3 parameters */
2007-02-09 23:24:53 +09:00
irlan_insert_short_param ( skb , " CON_ARB " ,
2005-04-16 15:20:36 -07:00
self - > provider . send_arb_val ) ;
} else
skb - > data [ 1 ] = 0x02 ; /* 2 parameters */
irlan_insert_byte_param ( skb , " DATA_CHAN " , self - > stsap_sel_data ) ;
2006-09-27 20:06:44 -07:00
irlan_insert_string_param ( skb , " RECONNECT_KEY " , " LINUX RULES! " ) ;
2005-04-16 15:20:36 -07:00
break ;
case CMD_FILTER_OPERATION :
irlan_filter_request ( self , skb ) ;
break ;
default :
IRDA_DEBUG ( 2 , " %s(), Unknown command! \n " , __FUNCTION__ ) ;
break ;
}
irttp_data_request ( self - > provider . tsap_ctrl , skb ) ;
}
/*
* Function irlan_provider_register ( void )
*
* Register provider support so we can accept incoming connections .
2007-02-09 23:24:53 +09:00
*
2005-04-16 15:20:36 -07:00
*/
int irlan_provider_open_ctrl_tsap ( struct irlan_cb * self )
{
struct tsap_cb * tsap ;
notify_t notify ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
IRDA_ASSERT ( self ! = NULL , return - 1 ; ) ;
IRDA_ASSERT ( self - > magic = = IRLAN_MAGIC , return - 1 ; ) ;
/* Check if already open */
if ( self - > provider . tsap_ctrl )
return - 1 ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
/*
* First register well known control TSAP
*/
irda_notify_init ( & notify ) ;
notify . data_indication = irlan_provider_data_indication ;
notify . connect_indication = irlan_provider_connect_indication ;
notify . disconnect_indication = irlan_provider_disconnect_indication ;
notify . instance = self ;
strlcpy ( notify . name , " IrLAN ctrl (p) " , sizeof ( notify . name ) ) ;
tsap = irttp_open_tsap ( LSAP_ANY , 1 , & notify ) ;
if ( ! tsap ) {
IRDA_DEBUG ( 2 , " %s(), Got no tsap! \n " , __FUNCTION__ ) ;
return - 1 ;
}
self - > provider . tsap_ctrl = tsap ;
/* Register with LM-IAS */
irlan_ias_register ( self , tsap - > stsap_sel ) ;
return 0 ;
}