2011-02-04 01:50:35 +03:00
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright ( C ) 2000 - 2001 Qualcomm Incorporated
Copyright ( C ) 2009 - 2010 Gustavo F . Padovan < gustavo @ padovan . org >
Copyright ( C ) 2010 Google Inc .
Written 2000 , 2001 by Maxim Krasnyansky < maxk @ qualcomm . com >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation ;
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS
OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS .
IN NO EVENT SHALL THE COPYRIGHT HOLDER ( S ) AND AUTHOR ( S ) BE LIABLE FOR ANY
CLAIM , OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES , OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
ALL LIABILITY , INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS ,
COPYRIGHTS , TRADEMARKS OR OTHER RIGHTS , RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED .
*/
/* Bluetooth L2CAP sockets. */
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/l2cap.h>
static void l2cap_sock_timeout ( unsigned long arg )
{
struct sock * sk = ( struct sock * ) arg ;
int reason ;
BT_DBG ( " sock %p state %d " , sk , sk - > sk_state ) ;
bh_lock_sock ( sk ) ;
if ( sock_owned_by_user ( sk ) ) {
/* sk is owned by user. Try again later */
l2cap_sock_set_timer ( sk , HZ / 5 ) ;
bh_unlock_sock ( sk ) ;
sock_put ( sk ) ;
return ;
}
if ( sk - > sk_state = = BT_CONNECTED | | sk - > sk_state = = BT_CONFIG )
reason = ECONNREFUSED ;
else if ( sk - > sk_state = = BT_CONNECT & &
l2cap_pi ( sk ) - > sec_level ! = BT_SECURITY_SDP )
reason = ECONNREFUSED ;
else
reason = ETIMEDOUT ;
__l2cap_sock_close ( sk , reason ) ;
bh_unlock_sock ( sk ) ;
l2cap_sock_kill ( sk ) ;
sock_put ( sk ) ;
}
static void l2cap_sock_destruct ( struct sock * sk )
{
BT_DBG ( " sk %p " , sk ) ;
skb_queue_purge ( & sk - > sk_receive_queue ) ;
skb_queue_purge ( & sk - > sk_write_queue ) ;
}
void l2cap_sock_init ( struct sock * sk , struct sock * parent )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
BT_DBG ( " sk %p " , sk ) ;
if ( parent ) {
sk - > sk_type = parent - > sk_type ;
bt_sk ( sk ) - > defer_setup = bt_sk ( parent ) - > defer_setup ;
pi - > imtu = l2cap_pi ( parent ) - > imtu ;
pi - > omtu = l2cap_pi ( parent ) - > omtu ;
pi - > conf_state = l2cap_pi ( parent ) - > conf_state ;
pi - > mode = l2cap_pi ( parent ) - > mode ;
pi - > fcs = l2cap_pi ( parent ) - > fcs ;
pi - > max_tx = l2cap_pi ( parent ) - > max_tx ;
pi - > tx_win = l2cap_pi ( parent ) - > tx_win ;
pi - > sec_level = l2cap_pi ( parent ) - > sec_level ;
pi - > role_switch = l2cap_pi ( parent ) - > role_switch ;
pi - > force_reliable = l2cap_pi ( parent ) - > force_reliable ;
pi - > flushable = l2cap_pi ( parent ) - > flushable ;
} else {
pi - > imtu = L2CAP_DEFAULT_MTU ;
pi - > omtu = 0 ;
if ( ! disable_ertm & & sk - > sk_type = = SOCK_STREAM ) {
pi - > mode = L2CAP_MODE_ERTM ;
pi - > conf_state | = L2CAP_CONF_STATE2_DEVICE ;
} else {
pi - > mode = L2CAP_MODE_BASIC ;
}
pi - > max_tx = L2CAP_DEFAULT_MAX_TX ;
pi - > fcs = L2CAP_FCS_CRC16 ;
pi - > tx_win = L2CAP_DEFAULT_TX_WINDOW ;
pi - > sec_level = BT_SECURITY_LOW ;
pi - > role_switch = 0 ;
pi - > force_reliable = 0 ;
pi - > flushable = BT_FLUSHABLE_OFF ;
}
/* Default config options */
pi - > conf_len = 0 ;
pi - > flush_to = L2CAP_DEFAULT_FLUSH_TO ;
skb_queue_head_init ( TX_QUEUE ( sk ) ) ;
skb_queue_head_init ( SREJ_QUEUE ( sk ) ) ;
skb_queue_head_init ( BUSY_QUEUE ( sk ) ) ;
INIT_LIST_HEAD ( SREJ_LIST ( sk ) ) ;
}
static struct proto l2cap_proto = {
. name = " L2CAP " ,
. owner = THIS_MODULE ,
. obj_size = sizeof ( struct l2cap_pinfo )
} ;
struct sock * l2cap_sock_alloc ( struct net * net , struct socket * sock , int proto , gfp_t prio )
{
struct sock * sk ;
sk = sk_alloc ( net , PF_BLUETOOTH , prio , & l2cap_proto ) ;
if ( ! sk )
return NULL ;
sock_init_data ( sock , sk ) ;
INIT_LIST_HEAD ( & bt_sk ( sk ) - > accept_q ) ;
sk - > sk_destruct = l2cap_sock_destruct ;
sk - > sk_sndtimeo = msecs_to_jiffies ( L2CAP_CONN_TIMEOUT ) ;
sock_reset_flag ( sk , SOCK_ZAPPED ) ;
sk - > sk_protocol = proto ;
sk - > sk_state = BT_OPEN ;
setup_timer ( & sk - > sk_timer , l2cap_sock_timeout , ( unsigned long ) sk ) ;
bt_sock_link ( & l2cap_sk_list , sk ) ;
return sk ;
}
static int l2cap_sock_create ( struct net * net , struct socket * sock , int protocol ,
int kern )
{
struct sock * sk ;
BT_DBG ( " sock %p " , sock ) ;
sock - > state = SS_UNCONNECTED ;
if ( sock - > type ! = SOCK_SEQPACKET & & sock - > type ! = SOCK_STREAM & &
sock - > type ! = SOCK_DGRAM & & sock - > type ! = SOCK_RAW )
return - ESOCKTNOSUPPORT ;
if ( sock - > type = = SOCK_RAW & & ! kern & & ! capable ( CAP_NET_RAW ) )
return - EPERM ;
sock - > ops = & l2cap_sock_ops ;
sk = l2cap_sock_alloc ( net , sock , protocol , GFP_ATOMIC ) ;
if ( ! sk )
return - ENOMEM ;
l2cap_sock_init ( sk , NULL ) ;
return 0 ;
}
2011-02-04 07:33:56 +03:00
const struct proto_ops l2cap_sock_ops = {
. family = PF_BLUETOOTH ,
. owner = THIS_MODULE ,
. release = l2cap_sock_release ,
. bind = l2cap_sock_bind ,
. connect = l2cap_sock_connect ,
. listen = l2cap_sock_listen ,
. accept = l2cap_sock_accept ,
. getname = l2cap_sock_getname ,
. sendmsg = l2cap_sock_sendmsg ,
. recvmsg = l2cap_sock_recvmsg ,
. poll = bt_sock_poll ,
. ioctl = bt_sock_ioctl ,
. mmap = sock_no_mmap ,
. socketpair = sock_no_socketpair ,
. shutdown = l2cap_sock_shutdown ,
. setsockopt = l2cap_sock_setsockopt ,
. getsockopt = l2cap_sock_getsockopt
} ;
2011-02-04 01:50:35 +03:00
static const struct net_proto_family l2cap_sock_family_ops = {
. family = PF_BLUETOOTH ,
. owner = THIS_MODULE ,
. create = l2cap_sock_create ,
} ;
int __init l2cap_init_sockets ( void )
{
int err ;
err = proto_register ( & l2cap_proto , 0 ) ;
if ( err < 0 )
return err ;
err = bt_sock_register ( BTPROTO_L2CAP , & l2cap_sock_family_ops ) ;
if ( err < 0 )
goto error ;
BT_INFO ( " L2CAP socket layer initialized " ) ;
return 0 ;
error :
BT_ERR ( " L2CAP socket registration failed " ) ;
proto_unregister ( & l2cap_proto ) ;
return err ;
}
void l2cap_cleanup_sockets ( void )
{
if ( bt_sock_unregister ( BTPROTO_L2CAP ) < 0 )
BT_ERR ( " L2CAP socket unregistration failed " ) ;
proto_unregister ( & l2cap_proto ) ;
}