2007-02-09 17:24:33 +03:00
/*
2005-04-17 02:20:36 +04:00
BlueZ - Bluetooth protocol stack for Linux
Copyright ( C ) 2000 - 2001 Qualcomm Incorporated
2010-07-13 18:57:11 +04:00
Copyright ( C ) 2009 - 2010 Gustavo F . Padovan < gustavo @ padovan . org >
2010-07-16 23:18:39 +04:00
Copyright ( C ) 2010 Google Inc .
2005-04-17 02:20:36 +04:00
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
2007-02-09 17:24:33 +03:00
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
2005-04-17 02:20:36 +04:00
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
2007-02-09 17:24:33 +03:00
ALL LIABILITY , INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS ,
COPYRIGHTS , TRADEMARKS OR OTHER RIGHTS , RELATING TO USE OF THIS
2005-04-17 02:20:36 +04:00
SOFTWARE IS DISCLAIMED .
*/
/* Bluetooth L2CAP core and sockets. */
# include <linux/module.h>
# include <linux/types.h>
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/fcntl.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/socket.h>
# include <linux/skbuff.h>
# include <linux/list.h>
2005-11-08 20:57:38 +03:00
# include <linux/device.h>
2010-03-21 07:27:45 +03:00
# include <linux/debugfs.h>
# include <linux/seq_file.h>
2009-04-20 08:31:08 +04:00
# include <linux/uaccess.h>
2009-08-21 05:26:02 +04:00
# include <linux/crc16.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h>
# include <asm/system.h>
# include <asm/unaligned.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
# include <net/bluetooth/l2cap.h>
2010-07-13 18:57:12 +04:00
# define VERSION "2.15"
2009-05-03 06:09:01 +04:00
2010-07-18 23:25:54 +04:00
static int disable_ertm = 0 ;
2007-10-20 15:38:51 +04:00
2009-05-03 05:57:55 +04:00
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN ;
2009-02-09 11:18:02 +03:00
static u8 l2cap_fixed_chan [ 8 ] = { 0x02 , } ;
2005-04-17 02:20:36 +04:00
2005-12-22 23:49:22 +03:00
static const struct proto_ops l2cap_sock_ops ;
2005-04-17 02:20:36 +04:00
2010-05-01 23:15:44 +04:00
static struct workqueue_struct * _busy_wq ;
2005-04-17 02:20:36 +04:00
static struct bt_sock_list l2cap_sk_list = {
2008-03-29 02:17:38 +03:00
. lock = __RW_LOCK_UNLOCKED ( l2cap_sk_list . lock )
2005-04-17 02:20:36 +04:00
} ;
2010-05-01 23:15:44 +04:00
static void l2cap_busy_work ( struct work_struct * work ) ;
2005-04-17 02:20:36 +04:00
static void __l2cap_sock_close ( struct sock * sk , int reason ) ;
static void l2cap_sock_close ( struct sock * sk ) ;
static void l2cap_sock_kill ( struct sock * sk ) ;
2010-07-09 03:08:18 +04:00
static int l2cap_build_conf_req ( struct sock * sk , void * data ) ;
2005-04-17 02:20:36 +04:00
static struct sk_buff * l2cap_build_cmd ( struct l2cap_conn * conn ,
u8 code , u8 ident , u16 dlen , void * data ) ;
2010-06-22 01:53:22 +04:00
static int l2cap_ertm_data_rcv ( struct sock * sk , struct sk_buff * skb ) ;
2005-04-17 02:20:36 +04:00
/* ---- L2CAP timers ---- */
static void l2cap_sock_timeout ( unsigned long arg )
{
struct sock * sk = ( struct sock * ) arg ;
2008-07-14 22:13:54 +04:00
int reason ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " sock %p state %d " , sk , sk - > sk_state ) ;
bh_lock_sock ( sk ) ;
2008-07-14 22:13:54 +04:00
2009-01-15 23:58:44 +03:00
if ( sk - > sk_state = = BT_CONNECTED | | sk - > sk_state = = BT_CONFIG )
reason = ECONNREFUSED ;
else if ( sk - > sk_state = = BT_CONNECT & &
2009-01-15 23:58:38 +03:00
l2cap_pi ( sk ) - > sec_level ! = BT_SECURITY_SDP )
2008-07-14 22:13:54 +04:00
reason = ECONNREFUSED ;
else
reason = ETIMEDOUT ;
__l2cap_sock_close ( sk , reason ) ;
2005-04-17 02:20:36 +04:00
bh_unlock_sock ( sk ) ;
l2cap_sock_kill ( sk ) ;
sock_put ( sk ) ;
}
static void l2cap_sock_set_timer ( struct sock * sk , long timeout )
{
BT_DBG ( " sk %p state %d timeout %ld " , sk , sk - > sk_state , timeout ) ;
sk_reset_timer ( sk , & sk - > sk_timer , jiffies + timeout ) ;
}
static void l2cap_sock_clear_timer ( struct sock * sk )
{
BT_DBG ( " sock %p state %d " , sk , sk - > sk_state ) ;
sk_stop_timer ( sk , & sk - > sk_timer ) ;
}
2006-07-03 12:02:46 +04:00
/* ---- L2CAP channels ---- */
static struct sock * __l2cap_get_chan_by_dcid ( struct l2cap_chan_list * l , u16 cid )
{
struct sock * s ;
for ( s = l - > head ; s ; s = l2cap_pi ( s ) - > next_c ) {
if ( l2cap_pi ( s ) - > dcid = = cid )
break ;
}
return s ;
}
static struct sock * __l2cap_get_chan_by_scid ( struct l2cap_chan_list * l , u16 cid )
{
struct sock * s ;
for ( s = l - > head ; s ; s = l2cap_pi ( s ) - > next_c ) {
if ( l2cap_pi ( s ) - > scid = = cid )
break ;
}
return s ;
}
/* Find channel with given SCID.
* Returns locked socket */
static inline struct sock * l2cap_get_chan_by_scid ( struct l2cap_chan_list * l , u16 cid )
{
struct sock * s ;
read_lock ( & l - > lock ) ;
s = __l2cap_get_chan_by_scid ( l , cid ) ;
2009-04-20 08:31:08 +04:00
if ( s )
bh_lock_sock ( s ) ;
2006-07-03 12:02:46 +04:00
read_unlock ( & l - > lock ) ;
return s ;
}
static struct sock * __l2cap_get_chan_by_ident ( struct l2cap_chan_list * l , u8 ident )
{
struct sock * s ;
for ( s = l - > head ; s ; s = l2cap_pi ( s ) - > next_c ) {
if ( l2cap_pi ( s ) - > ident = = ident )
break ;
}
return s ;
}
static inline struct sock * l2cap_get_chan_by_ident ( struct l2cap_chan_list * l , u8 ident )
{
struct sock * s ;
read_lock ( & l - > lock ) ;
s = __l2cap_get_chan_by_ident ( l , ident ) ;
2009-04-20 08:31:08 +04:00
if ( s )
bh_lock_sock ( s ) ;
2006-07-03 12:02:46 +04:00
read_unlock ( & l - > lock ) ;
return s ;
}
static u16 l2cap_alloc_cid ( struct l2cap_chan_list * l )
{
2009-04-20 08:31:05 +04:00
u16 cid = L2CAP_CID_DYN_START ;
2006-07-03 12:02:46 +04:00
2009-04-20 08:31:05 +04:00
for ( ; cid < L2CAP_CID_DYN_END ; cid + + ) {
2009-04-20 08:31:08 +04:00
if ( ! __l2cap_get_chan_by_scid ( l , cid ) )
2006-07-03 12:02:46 +04:00
return cid ;
}
return 0 ;
}
static inline void __l2cap_chan_link ( struct l2cap_chan_list * l , struct sock * sk )
{
sock_hold ( sk ) ;
if ( l - > head )
l2cap_pi ( l - > head ) - > prev_c = sk ;
l2cap_pi ( sk ) - > next_c = l - > head ;
l2cap_pi ( sk ) - > prev_c = NULL ;
l - > head = sk ;
}
static inline void l2cap_chan_unlink ( struct l2cap_chan_list * l , struct sock * sk )
{
struct sock * next = l2cap_pi ( sk ) - > next_c , * prev = l2cap_pi ( sk ) - > prev_c ;
2006-07-13 01:00:07 +04:00
write_lock_bh ( & l - > lock ) ;
2006-07-03 12:02:46 +04:00
if ( sk = = l - > head )
l - > head = next ;
if ( next )
l2cap_pi ( next ) - > prev_c = prev ;
if ( prev )
l2cap_pi ( prev ) - > next_c = next ;
2006-07-13 01:00:07 +04:00
write_unlock_bh ( & l - > lock ) ;
2006-07-03 12:02:46 +04:00
__sock_put ( sk ) ;
}
static void __l2cap_chan_add ( struct l2cap_conn * conn , struct sock * sk , struct sock * parent )
{
struct l2cap_chan_list * l = & conn - > chan_list ;
2009-04-20 08:31:08 +04:00
BT_DBG ( " conn %p, psm 0x%2.2x, dcid 0x%4.4x " , conn ,
l2cap_pi ( sk ) - > psm , l2cap_pi ( sk ) - > dcid ) ;
2006-07-03 12:02:46 +04:00
2009-02-12 16:02:50 +03:00
conn - > disc_reason = 0x13 ;
2006-07-03 12:02:46 +04:00
l2cap_pi ( sk ) - > conn = conn ;
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type = = SOCK_SEQPACKET | | sk - > sk_type = = SOCK_STREAM ) {
2006-07-03 12:02:46 +04:00
/* Alloc CID for connection-oriented socket */
l2cap_pi ( sk ) - > scid = l2cap_alloc_cid ( l ) ;
} else if ( sk - > sk_type = = SOCK_DGRAM ) {
/* Connectionless socket */
2009-04-20 08:31:05 +04:00
l2cap_pi ( sk ) - > scid = L2CAP_CID_CONN_LESS ;
l2cap_pi ( sk ) - > dcid = L2CAP_CID_CONN_LESS ;
2006-07-03 12:02:46 +04:00
l2cap_pi ( sk ) - > omtu = L2CAP_DEFAULT_MTU ;
} else {
/* Raw socket can send/recv signalling messages only */
2009-04-20 08:31:05 +04:00
l2cap_pi ( sk ) - > scid = L2CAP_CID_SIGNALING ;
l2cap_pi ( sk ) - > dcid = L2CAP_CID_SIGNALING ;
2006-07-03 12:02:46 +04:00
l2cap_pi ( sk ) - > omtu = L2CAP_DEFAULT_MTU ;
}
__l2cap_chan_link ( l , sk ) ;
if ( parent )
bt_accept_enqueue ( parent , sk ) ;
}
2007-02-09 17:24:33 +03:00
/* Delete channel.
2006-07-03 12:02:46 +04:00
* Must be called on the locked socket . */
static void l2cap_chan_del ( struct sock * sk , int err )
{
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
struct sock * parent = bt_sk ( sk ) - > parent ;
l2cap_sock_clear_timer ( sk ) ;
BT_DBG ( " sk %p, conn %p, err %d " , sk , conn , err ) ;
2007-02-09 17:24:33 +03:00
if ( conn ) {
2006-07-03 12:02:46 +04:00
/* Unlink from channel list */
l2cap_chan_unlink ( & conn - > chan_list , sk ) ;
l2cap_pi ( sk ) - > conn = NULL ;
hci_conn_put ( conn - > hcon ) ;
}
2008-07-14 22:13:54 +04:00
sk - > sk_state = BT_CLOSED ;
2006-07-03 12:02:46 +04:00
sock_set_flag ( sk , SOCK_ZAPPED ) ;
if ( err )
sk - > sk_err = err ;
if ( parent ) {
bt_accept_unlink ( sk ) ;
parent - > sk_data_ready ( parent , 0 ) ;
} else
sk - > sk_state_change ( sk ) ;
2010-05-14 03:50:12 +04:00
skb_queue_purge ( TX_QUEUE ( sk ) ) ;
if ( l2cap_pi ( sk ) - > mode = = L2CAP_MODE_ERTM ) {
struct srej_list * l , * tmp ;
del_timer ( & l2cap_pi ( sk ) - > retrans_timer ) ;
del_timer ( & l2cap_pi ( sk ) - > monitor_timer ) ;
del_timer ( & l2cap_pi ( sk ) - > ack_timer ) ;
skb_queue_purge ( SREJ_QUEUE ( sk ) ) ;
skb_queue_purge ( BUSY_QUEUE ( sk ) ) ;
list_for_each_entry_safe ( l , tmp , SREJ_LIST ( sk ) , list ) {
list_del ( & l - > list ) ;
kfree ( l ) ;
}
}
2006-07-03 12:02:46 +04:00
}
2008-07-14 22:13:44 +04:00
/* Service level security */
2009-01-15 23:58:38 +03:00
static inline int l2cap_check_security ( struct sock * sk )
2008-07-14 22:13:44 +04:00
{
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
2009-02-09 04:48:38 +03:00
__u8 auth_type ;
2009-02-12 18:19:45 +03:00
if ( l2cap_pi ( sk ) - > psm = = cpu_to_le16 ( 0x0001 ) ) {
if ( l2cap_pi ( sk ) - > sec_level = = BT_SECURITY_HIGH )
auth_type = HCI_AT_NO_BONDING_MITM ;
else
2009-04-20 08:31:08 +04:00
auth_type = HCI_AT_NO_BONDING ;
2009-02-12 18:19:45 +03:00
if ( l2cap_pi ( sk ) - > sec_level = = BT_SECURITY_LOW )
l2cap_pi ( sk ) - > sec_level = BT_SECURITY_SDP ;
} else {
switch ( l2cap_pi ( sk ) - > sec_level ) {
case BT_SECURITY_HIGH :
auth_type = HCI_AT_GENERAL_BONDING_MITM ;
break ;
case BT_SECURITY_MEDIUM :
auth_type = HCI_AT_GENERAL_BONDING ;
break ;
default :
auth_type = HCI_AT_NO_BONDING ;
break ;
}
2009-02-09 04:48:38 +03:00
}
2008-07-14 22:13:44 +04:00
2009-02-09 04:48:38 +03:00
return hci_conn_security ( conn - > hcon , l2cap_pi ( sk ) - > sec_level ,
auth_type ) ;
2008-07-14 22:13:44 +04:00
}
2007-10-20 15:37:56 +04:00
static inline u8 l2cap_get_ident ( struct l2cap_conn * conn )
{
u8 id ;
/* Get next available identificator.
* 1 - 128 are used by kernel .
* 129 - 199 are reserved .
* 200 - 254 are used by utilities like l2ping , etc .
*/
spin_lock_bh ( & conn - > lock ) ;
if ( + + conn - > tx_ident > 128 )
conn - > tx_ident = 1 ;
id = conn - > tx_ident ;
spin_unlock_bh ( & conn - > lock ) ;
return id ;
}
2010-05-01 23:15:43 +04:00
static inline void l2cap_send_cmd ( struct l2cap_conn * conn , u8 ident , u8 code , u16 len , void * data )
2007-10-20 15:37:56 +04:00
{
struct sk_buff * skb = l2cap_build_cmd ( conn , code , ident , len , data ) ;
BT_DBG ( " code 0x%2.2x " , code ) ;
if ( ! skb )
2010-05-01 23:15:43 +04:00
return ;
2007-10-20 15:37:56 +04:00
2010-05-01 23:15:43 +04:00
hci_send_acl ( conn - > hcon , skb , 0 ) ;
2007-10-20 15:37:56 +04:00
}
2010-05-01 23:15:43 +04:00
static inline void l2cap_send_sframe ( struct l2cap_pinfo * pi , u16 control )
2009-08-21 05:25:57 +04:00
{
struct sk_buff * skb ;
struct l2cap_hdr * lh ;
struct l2cap_conn * conn = pi - > conn ;
2010-05-14 03:50:12 +04:00
struct sock * sk = ( struct sock * ) pi ;
2009-08-21 05:26:02 +04:00
int count , hlen = L2CAP_HDR_SIZE + 2 ;
2010-05-14 03:50:12 +04:00
if ( sk - > sk_state ! = BT_CONNECTED )
return ;
2009-08-21 05:26:02 +04:00
if ( pi - > fcs = = L2CAP_FCS_CRC16 )
hlen + = 2 ;
2009-08-21 05:25:57 +04:00
BT_DBG ( " pi %p, control 0x%2.2x " , pi , control ) ;
2009-08-21 05:26:02 +04:00
count = min_t ( unsigned int , conn - > mtu , hlen ) ;
2009-08-21 05:25:57 +04:00
control | = L2CAP_CTRL_FRAME_TYPE ;
2010-05-01 23:15:37 +04:00
if ( pi - > conn_state & L2CAP_CONN_SEND_FBIT ) {
control | = L2CAP_CTRL_FINAL ;
pi - > conn_state & = ~ L2CAP_CONN_SEND_FBIT ;
}
2010-05-01 23:15:37 +04:00
if ( pi - > conn_state & L2CAP_CONN_SEND_PBIT ) {
control | = L2CAP_CTRL_POLL ;
pi - > conn_state & = ~ L2CAP_CONN_SEND_PBIT ;
}
2009-08-21 05:25:57 +04:00
skb = bt_skb_alloc ( count , GFP_ATOMIC ) ;
if ( ! skb )
2010-05-01 23:15:43 +04:00
return ;
2009-08-21 05:25:57 +04:00
lh = ( struct l2cap_hdr * ) skb_put ( skb , L2CAP_HDR_SIZE ) ;
2009-08-21 05:26:02 +04:00
lh - > len = cpu_to_le16 ( hlen - L2CAP_HDR_SIZE ) ;
2009-08-21 05:25:57 +04:00
lh - > cid = cpu_to_le16 ( pi - > dcid ) ;
put_unaligned_le16 ( control , skb_put ( skb , 2 ) ) ;
2009-08-21 05:26:02 +04:00
if ( pi - > fcs = = L2CAP_FCS_CRC16 ) {
u16 fcs = crc16 ( 0 , ( u8 * ) lh , count - 2 ) ;
put_unaligned_le16 ( fcs , skb_put ( skb , 2 ) ) ;
}
2010-05-01 23:15:43 +04:00
hci_send_acl ( pi - > conn - > hcon , skb , 0 ) ;
2009-08-21 05:25:57 +04:00
}
2010-05-01 23:15:43 +04:00
static inline void l2cap_send_rr_or_rnr ( struct l2cap_pinfo * pi , u16 control )
2009-08-26 11:04:03 +04:00
{
2010-05-01 23:15:44 +04:00
if ( pi - > conn_state & L2CAP_CONN_LOCAL_BUSY ) {
2009-08-26 11:04:03 +04:00
control | = L2CAP_SUPER_RCV_NOT_READY ;
2010-05-01 23:15:44 +04:00
pi - > conn_state | = L2CAP_CONN_RNR_SENT ;
} else
2009-08-26 11:04:03 +04:00
control | = L2CAP_SUPER_RCV_READY ;
2009-10-03 09:34:40 +04:00
control | = pi - > buffer_seq < < L2CAP_CTRL_REQSEQ_SHIFT ;
2010-05-01 23:15:43 +04:00
l2cap_send_sframe ( pi , control ) ;
2009-08-26 11:04:03 +04:00
}
2010-07-08 13:14:41 +04:00
static inline int __l2cap_no_conn_pending ( struct sock * sk )
{
return ! ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_CONNECT_PEND ) ;
}
2008-07-14 22:13:44 +04:00
static void l2cap_do_start ( struct sock * sk )
{
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
if ( conn - > info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT ) {
2009-02-07 01:35:19 +03:00
if ( ! ( conn - > info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE ) )
return ;
2010-07-08 13:14:41 +04:00
if ( l2cap_check_security ( sk ) & & __l2cap_no_conn_pending ( sk ) ) {
2008-07-14 22:13:54 +04:00
struct l2cap_conn_req req ;
req . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
req . psm = l2cap_pi ( sk ) - > psm ;
2008-07-14 22:13:44 +04:00
2008-07-14 22:13:54 +04:00
l2cap_pi ( sk ) - > ident = l2cap_get_ident ( conn ) ;
2010-07-08 13:14:41 +04:00
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_CONNECT_PEND ;
2008-07-14 22:13:44 +04:00
2008-07-14 22:13:54 +04:00
l2cap_send_cmd ( conn , l2cap_pi ( sk ) - > ident ,
2008-07-14 22:13:44 +04:00
L2CAP_CONN_REQ , sizeof ( req ) , & req ) ;
2008-07-14 22:13:54 +04:00
}
2008-07-14 22:13:44 +04:00
} else {
struct l2cap_info_req req ;
req . type = cpu_to_le16 ( L2CAP_IT_FEAT_MASK ) ;
conn - > info_state | = L2CAP_INFO_FEAT_MASK_REQ_SENT ;
conn - > info_ident = l2cap_get_ident ( conn ) ;
mod_timer ( & conn - > info_timer , jiffies +
msecs_to_jiffies ( L2CAP_INFO_TIMEOUT ) ) ;
l2cap_send_cmd ( conn , conn - > info_ident ,
L2CAP_INFO_REQ , sizeof ( req ) , & req ) ;
}
}
2010-06-08 03:54:45 +04:00
static inline int l2cap_mode_supported ( __u8 mode , __u32 feat_mask )
{
u32 local_feat_mask = l2cap_feat_mask ;
2010-07-18 23:25:54 +04:00
if ( ! disable_ertm )
2010-06-08 03:54:45 +04:00
local_feat_mask | = L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING ;
switch ( mode ) {
case L2CAP_MODE_ERTM :
return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask ;
case L2CAP_MODE_STREAMING :
return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask ;
default :
return 0x00 ;
}
}
2010-05-20 23:21:53 +04:00
static void l2cap_send_disconn_req ( struct l2cap_conn * conn , struct sock * sk , int err )
2009-07-23 17:27:23 +04:00
{
struct l2cap_disconn_req req ;
2010-05-14 03:50:12 +04:00
if ( ! conn )
return ;
skb_queue_purge ( TX_QUEUE ( sk ) ) ;
if ( l2cap_pi ( sk ) - > mode = = L2CAP_MODE_ERTM ) {
del_timer ( & l2cap_pi ( sk ) - > retrans_timer ) ;
del_timer ( & l2cap_pi ( sk ) - > monitor_timer ) ;
del_timer ( & l2cap_pi ( sk ) - > ack_timer ) ;
}
2009-07-23 17:27:23 +04:00
req . dcid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
req . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
l2cap_send_cmd ( conn , l2cap_get_ident ( conn ) ,
L2CAP_DISCONN_REQ , sizeof ( req ) , & req ) ;
2010-05-14 03:50:12 +04:00
sk - > sk_state = BT_DISCONN ;
2010-05-20 23:21:53 +04:00
sk - > sk_err = err ;
2009-07-23 17:27:23 +04:00
}
2005-04-17 02:20:36 +04:00
/* ---- L2CAP connections ---- */
2007-10-20 15:37:56 +04:00
static void l2cap_conn_start ( struct l2cap_conn * conn )
{
struct l2cap_chan_list * l = & conn - > chan_list ;
2010-06-08 03:54:45 +04:00
struct sock_del_list del , * tmp1 , * tmp2 ;
2007-10-20 15:37:56 +04:00
struct sock * sk ;
BT_DBG ( " conn %p " , conn ) ;
2010-06-08 03:54:45 +04:00
INIT_LIST_HEAD ( & del . list ) ;
2007-10-20 15:37:56 +04:00
read_lock ( & l - > lock ) ;
for ( sk = l - > head ; sk ; sk = l2cap_pi ( sk ) - > next_c ) {
bh_lock_sock ( sk ) ;
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type ! = SOCK_SEQPACKET & &
sk - > sk_type ! = SOCK_STREAM ) {
2008-07-14 22:13:44 +04:00
bh_unlock_sock ( sk ) ;
continue ;
}
if ( sk - > sk_state = = BT_CONNECT ) {
2010-07-09 23:38:35 +04:00
struct l2cap_conn_req req ;
2008-07-14 22:13:44 +04:00
2010-07-09 23:38:35 +04:00
if ( ! l2cap_check_security ( sk ) | |
! __l2cap_no_conn_pending ( sk ) ) {
bh_unlock_sock ( sk ) ;
continue ;
}
2008-07-14 22:13:44 +04:00
2010-07-09 23:38:35 +04:00
if ( ! l2cap_mode_supported ( l2cap_pi ( sk ) - > mode ,
conn - > feat_mask )
& & l2cap_pi ( sk ) - > conf_state &
L2CAP_CONF_STATE2_DEVICE ) {
tmp1 = kzalloc ( sizeof ( struct sock_del_list ) ,
GFP_ATOMIC ) ;
tmp1 - > sk = sk ;
list_add_tail ( & tmp1 - > list , & del . list ) ;
bh_unlock_sock ( sk ) ;
continue ;
2008-07-14 22:13:54 +04:00
}
2010-07-09 23:38:35 +04:00
req . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
req . psm = l2cap_pi ( sk ) - > psm ;
l2cap_pi ( sk ) - > ident = l2cap_get_ident ( conn ) ;
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_CONNECT_PEND ;
l2cap_send_cmd ( conn , l2cap_pi ( sk ) - > ident ,
L2CAP_CONN_REQ , sizeof ( req ) , & req ) ;
2008-07-14 22:13:44 +04:00
} else if ( sk - > sk_state = = BT_CONNECT2 ) {
struct l2cap_conn_rsp rsp ;
2010-07-09 03:08:18 +04:00
char buf [ 128 ] ;
2008-07-14 22:13:44 +04:00
rsp . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
rsp . dcid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
2009-01-15 23:58:38 +03:00
if ( l2cap_check_security ( sk ) ) {
2009-01-15 23:57:00 +03:00
if ( bt_sk ( sk ) - > defer_setup ) {
struct sock * parent = bt_sk ( sk ) - > parent ;
rsp . result = cpu_to_le16 ( L2CAP_CR_PEND ) ;
rsp . status = cpu_to_le16 ( L2CAP_CS_AUTHOR_PEND ) ;
parent - > sk_data_ready ( parent , 0 ) ;
} else {
sk - > sk_state = BT_CONFIG ;
rsp . result = cpu_to_le16 ( L2CAP_CR_SUCCESS ) ;
rsp . status = cpu_to_le16 ( L2CAP_CS_NO_INFO ) ;
}
2008-07-14 22:13:44 +04:00
} else {
rsp . result = cpu_to_le16 ( L2CAP_CR_PEND ) ;
rsp . status = cpu_to_le16 ( L2CAP_CS_AUTHEN_PEND ) ;
}
l2cap_send_cmd ( conn , l2cap_pi ( sk ) - > ident ,
L2CAP_CONN_RSP , sizeof ( rsp ) , & rsp ) ;
2010-07-09 03:08:18 +04:00
if ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_REQ_SENT | |
rsp . result ! = L2CAP_CR_SUCCESS ) {
bh_unlock_sock ( sk ) ;
continue ;
}
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_REQ_SENT ;
l2cap_send_cmd ( conn , l2cap_get_ident ( conn ) , L2CAP_CONF_REQ ,
l2cap_build_conf_req ( sk , buf ) , buf ) ;
l2cap_pi ( sk ) - > num_conf_req + + ;
2007-10-20 15:37:56 +04:00
}
bh_unlock_sock ( sk ) ;
}
read_unlock ( & l - > lock ) ;
2010-06-08 03:54:45 +04:00
list_for_each_entry_safe ( tmp1 , tmp2 , & del . list , list ) {
bh_lock_sock ( tmp1 - > sk ) ;
__l2cap_sock_close ( tmp1 - > sk , ECONNRESET ) ;
bh_unlock_sock ( tmp1 - > sk ) ;
list_del ( & tmp1 - > list ) ;
kfree ( tmp1 ) ;
}
2007-10-20 15:37:56 +04:00
}
static void l2cap_conn_ready ( struct l2cap_conn * conn )
{
2008-07-14 22:13:44 +04:00
struct l2cap_chan_list * l = & conn - > chan_list ;
struct sock * sk ;
2007-10-20 15:37:56 +04:00
2008-07-14 22:13:44 +04:00
BT_DBG ( " conn %p " , conn ) ;
2007-10-20 15:37:56 +04:00
2008-07-14 22:13:44 +04:00
read_lock ( & l - > lock ) ;
2007-10-20 15:37:56 +04:00
2008-07-14 22:13:44 +04:00
for ( sk = l - > head ; sk ; sk = l2cap_pi ( sk ) - > next_c ) {
bh_lock_sock ( sk ) ;
2007-10-20 15:37:56 +04:00
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type ! = SOCK_SEQPACKET & &
sk - > sk_type ! = SOCK_STREAM ) {
2008-07-14 22:13:44 +04:00
l2cap_sock_clear_timer ( sk ) ;
sk - > sk_state = BT_CONNECTED ;
sk - > sk_state_change ( sk ) ;
} else if ( sk - > sk_state = = BT_CONNECT )
l2cap_do_start ( sk ) ;
2007-10-20 15:37:56 +04:00
2008-07-14 22:13:44 +04:00
bh_unlock_sock ( sk ) ;
2007-10-20 15:37:56 +04:00
}
2008-07-14 22:13:44 +04:00
read_unlock ( & l - > lock ) ;
2007-10-20 15:37:56 +04:00
}
/* Notify sockets that we cannot guaranty reliability anymore */
static void l2cap_conn_unreliable ( struct l2cap_conn * conn , int err )
{
struct l2cap_chan_list * l = & conn - > chan_list ;
struct sock * sk ;
BT_DBG ( " conn %p " , conn ) ;
read_lock ( & l - > lock ) ;
for ( sk = l - > head ; sk ; sk = l2cap_pi ( sk ) - > next_c ) {
2009-01-15 23:58:38 +03:00
if ( l2cap_pi ( sk ) - > force_reliable )
2007-10-20 15:37:56 +04:00
sk - > sk_err = err ;
}
read_unlock ( & l - > lock ) ;
}
static void l2cap_info_timeout ( unsigned long arg )
{
struct l2cap_conn * conn = ( void * ) arg ;
2009-02-07 01:35:19 +03:00
conn - > info_state | = L2CAP_INFO_FEAT_MASK_REQ_DONE ;
2009-02-09 11:18:02 +03:00
conn - > info_ident = 0 ;
2009-02-07 01:35:19 +03:00
2007-10-20 15:37:56 +04:00
l2cap_conn_start ( conn ) ;
}
2005-04-17 02:20:36 +04:00
static struct l2cap_conn * l2cap_conn_add ( struct hci_conn * hcon , u8 status )
{
2006-07-03 12:02:46 +04:00
struct l2cap_conn * conn = hcon - > l2cap_data ;
2005-04-17 02:20:36 +04:00
2006-07-03 12:02:46 +04:00
if ( conn | | status )
2005-04-17 02:20:36 +04:00
return conn ;
2006-07-03 12:02:46 +04:00
conn = kzalloc ( sizeof ( struct l2cap_conn ) , GFP_ATOMIC ) ;
if ( ! conn )
2005-04-17 02:20:36 +04:00
return NULL ;
hcon - > l2cap_data = conn ;
conn - > hcon = hcon ;
2006-07-03 12:02:46 +04:00
BT_DBG ( " hcon %p conn %p " , hcon , conn ) ;
2005-04-17 02:20:36 +04:00
conn - > mtu = hcon - > hdev - > acl_mtu ;
conn - > src = & hcon - > hdev - > bdaddr ;
conn - > dst = & hcon - > dst ;
2007-10-20 15:37:56 +04:00
conn - > feat_mask = 0 ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & conn - > lock ) ;
rwlock_init ( & conn - > chan_list . lock ) ;
2009-10-19 00:28:30 +04:00
setup_timer ( & conn - > info_timer , l2cap_info_timeout ,
( unsigned long ) conn ) ;
2009-02-12 16:02:50 +03:00
conn - > disc_reason = 0x13 ;
2005-04-17 02:20:36 +04:00
return conn ;
}
2006-07-03 12:02:46 +04:00
static void l2cap_conn_del ( struct hci_conn * hcon , int err )
2005-04-17 02:20:36 +04:00
{
2006-07-03 12:02:46 +04:00
struct l2cap_conn * conn = hcon - > l2cap_data ;
2005-04-17 02:20:36 +04:00
struct sock * sk ;
2006-07-03 12:02:46 +04:00
if ( ! conn )
return ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " hcon %p conn %p, err %d " , hcon , conn , err ) ;
2009-02-25 13:29:52 +03:00
kfree_skb ( conn - > rx_skb ) ;
2005-04-17 02:20:36 +04:00
/* Kill channels */
while ( ( sk = conn - > chan_list . head ) ) {
bh_lock_sock ( sk ) ;
l2cap_chan_del ( sk , err ) ;
bh_unlock_sock ( sk ) ;
l2cap_sock_kill ( sk ) ;
}
2008-03-03 23:18:55 +03:00
if ( conn - > info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT )
del_timer_sync ( & conn - > info_timer ) ;
2008-02-27 04:42:56 +03:00
2005-04-17 02:20:36 +04:00
hcon - > l2cap_data = NULL ;
kfree ( conn ) ;
}
static inline void l2cap_chan_add ( struct l2cap_conn * conn , struct sock * sk , struct sock * parent )
{
struct l2cap_chan_list * l = & conn - > chan_list ;
2006-07-13 01:00:07 +04:00
write_lock_bh ( & l - > lock ) ;
2005-04-17 02:20:36 +04:00
__l2cap_chan_add ( conn , sk , parent ) ;
2006-07-13 01:00:07 +04:00
write_unlock_bh ( & l - > lock ) ;
2005-04-17 02:20:36 +04:00
}
/* ---- Socket interface ---- */
2007-07-29 11:16:36 +04:00
static struct sock * __l2cap_get_sock_by_addr ( __le16 psm , bdaddr_t * src )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
struct hlist_node * node ;
sk_for_each ( sk , node , & l2cap_sk_list . head )
if ( l2cap_pi ( sk ) - > sport = = psm & & ! bacmp ( & bt_sk ( sk ) - > src , src ) )
goto found ;
sk = NULL ;
found :
return sk ;
}
/* Find socket with psm and source bdaddr.
* Returns closest match .
*/
2007-07-29 11:16:36 +04:00
static struct sock * __l2cap_get_sock_by_psm ( int state , __le16 psm , bdaddr_t * src )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = NULL , * sk1 = NULL ;
struct hlist_node * node ;
sk_for_each ( sk , node , & l2cap_sk_list . head ) {
if ( state & & sk - > sk_state ! = state )
continue ;
if ( l2cap_pi ( sk ) - > psm = = psm ) {
/* Exact match. */
if ( ! bacmp ( & bt_sk ( sk ) - > src , src ) )
break ;
/* Closest match */
if ( ! bacmp ( & bt_sk ( sk ) - > src , BDADDR_ANY ) )
sk1 = sk ;
}
}
return node ? sk : sk1 ;
}
/* Find socket with given address (psm, src).
* Returns locked socket */
2007-07-29 11:16:36 +04:00
static inline struct sock * l2cap_get_sock_by_psm ( int state , __le16 psm , bdaddr_t * src )
2005-04-17 02:20:36 +04:00
{
struct sock * s ;
read_lock ( & l2cap_sk_list . lock ) ;
s = __l2cap_get_sock_by_psm ( state , psm , src ) ;
2009-04-20 08:31:08 +04:00
if ( s )
bh_lock_sock ( s ) ;
2005-04-17 02:20:36 +04:00
read_unlock ( & l2cap_sk_list . lock ) ;
return s ;
}
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 ) ;
}
static void l2cap_sock_cleanup_listen ( struct sock * parent )
{
struct sock * sk ;
BT_DBG ( " parent %p " , parent ) ;
/* Close not yet accepted channels */
while ( ( sk = bt_accept_dequeue ( parent , NULL ) ) )
l2cap_sock_close ( sk ) ;
2008-07-14 22:13:54 +04:00
parent - > sk_state = BT_CLOSED ;
2005-04-17 02:20:36 +04:00
sock_set_flag ( parent , SOCK_ZAPPED ) ;
}
/* Kill socket (only if zapped and orphan)
* Must be called on unlocked socket .
*/
static void l2cap_sock_kill ( struct sock * sk )
{
if ( ! sock_flag ( sk , SOCK_ZAPPED ) | | sk - > sk_socket )
return ;
BT_DBG ( " sk %p state %d " , sk , sk - > sk_state ) ;
/* Kill poor orphan */
bt_sock_unlink ( & l2cap_sk_list , sk ) ;
sock_set_flag ( sk , SOCK_DEAD ) ;
sock_put ( sk ) ;
}
static void __l2cap_sock_close ( struct sock * sk , int reason )
{
BT_DBG ( " sk %p state %d socket %p " , sk , sk - > sk_state , sk - > sk_socket ) ;
switch ( sk - > sk_state ) {
case BT_LISTEN :
l2cap_sock_cleanup_listen ( sk ) ;
break ;
case BT_CONNECTED :
case BT_CONFIG :
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type = = SOCK_SEQPACKET | |
sk - > sk_type = = SOCK_STREAM ) {
2005-04-17 02:20:36 +04:00
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
l2cap_sock_set_timer ( sk , sk - > sk_sndtimeo ) ;
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( conn , sk , reason ) ;
2008-07-14 22:13:54 +04:00
} else
2005-04-17 02:20:36 +04:00
l2cap_chan_del ( sk , reason ) ;
break ;
2009-01-15 23:57:00 +03:00
case BT_CONNECT2 :
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type = = SOCK_SEQPACKET | |
sk - > sk_type = = SOCK_STREAM ) {
2009-01-15 23:57:00 +03:00
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
struct l2cap_conn_rsp rsp ;
__u16 result ;
if ( bt_sk ( sk ) - > defer_setup )
result = L2CAP_CR_SEC_BLOCK ;
else
result = L2CAP_CR_BAD_PSM ;
rsp . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
rsp . dcid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
rsp . result = cpu_to_le16 ( result ) ;
rsp . status = cpu_to_le16 ( L2CAP_CS_NO_INFO ) ;
l2cap_send_cmd ( conn , l2cap_pi ( sk ) - > ident ,
L2CAP_CONN_RSP , sizeof ( rsp ) , & rsp ) ;
} else
l2cap_chan_del ( sk , reason ) ;
break ;
2005-04-17 02:20:36 +04:00
case BT_CONNECT :
case BT_DISCONN :
l2cap_chan_del ( sk , reason ) ;
break ;
default :
sock_set_flag ( sk , SOCK_ZAPPED ) ;
break ;
}
}
/* Must be called on unlocked socket. */
static void l2cap_sock_close ( struct sock * sk )
{
l2cap_sock_clear_timer ( sk ) ;
lock_sock ( sk ) ;
__l2cap_sock_close ( sk , ECONNRESET ) ;
release_sock ( sk ) ;
l2cap_sock_kill ( sk ) ;
}
static 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 ;
2009-01-15 23:57:00 +03:00
bt_sk ( sk ) - > defer_setup = bt_sk ( parent ) - > defer_setup ;
2005-04-17 02:20:36 +04:00
pi - > imtu = l2cap_pi ( parent ) - > imtu ;
pi - > omtu = l2cap_pi ( parent ) - > omtu ;
2010-06-04 01:43:28 +04:00
pi - > conf_state = l2cap_pi ( parent ) - > conf_state ;
2009-05-03 09:31:10 +04:00
pi - > mode = l2cap_pi ( parent ) - > mode ;
pi - > fcs = l2cap_pi ( parent ) - > fcs ;
2010-05-01 23:15:41 +04:00
pi - > max_tx = l2cap_pi ( parent ) - > max_tx ;
2010-05-01 23:15:40 +04:00
pi - > tx_win = l2cap_pi ( parent ) - > tx_win ;
2009-01-15 23:58:38 +03:00
pi - > sec_level = l2cap_pi ( parent ) - > sec_level ;
pi - > role_switch = l2cap_pi ( parent ) - > role_switch ;
pi - > force_reliable = l2cap_pi ( parent ) - > force_reliable ;
2005-04-17 02:20:36 +04:00
} else {
pi - > imtu = L2CAP_DEFAULT_MTU ;
pi - > omtu = 0 ;
2010-07-18 23:25:54 +04:00
if ( ! disable_ertm & & sk - > sk_type = = SOCK_STREAM ) {
2010-05-01 23:15:42 +04:00
pi - > mode = L2CAP_MODE_ERTM ;
2010-06-04 01:43:28 +04:00
pi - > conf_state | = L2CAP_CONF_STATE2_DEVICE ;
} else {
2010-05-01 23:15:42 +04:00
pi - > mode = L2CAP_MODE_BASIC ;
2010-06-04 01:43:28 +04:00
}
2010-05-10 21:22:56 +04:00
pi - > max_tx = L2CAP_DEFAULT_MAX_TX ;
2009-05-03 09:31:10 +04:00
pi - > fcs = L2CAP_FCS_CRC16 ;
2010-05-10 21:22:56 +04:00
pi - > tx_win = L2CAP_DEFAULT_TX_WINDOW ;
2009-01-15 23:58:38 +03:00
pi - > sec_level = BT_SECURITY_LOW ;
pi - > role_switch = 0 ;
pi - > force_reliable = 0 ;
2005-04-17 02:20:36 +04:00
}
/* Default config options */
2007-05-24 16:27:19 +04:00
pi - > conf_len = 0 ;
2005-04-17 02:20:36 +04:00
pi - > flush_to = L2CAP_DEFAULT_FLUSH_TO ;
2009-10-19 00:28:30 +04:00
skb_queue_head_init ( TX_QUEUE ( sk ) ) ;
skb_queue_head_init ( SREJ_QUEUE ( sk ) ) ;
2010-05-01 23:15:44 +04:00
skb_queue_head_init ( BUSY_QUEUE ( sk ) ) ;
2009-10-19 00:28:30 +04:00
INIT_LIST_HEAD ( SREJ_LIST ( sk ) ) ;
2005-04-17 02:20:36 +04:00
}
static struct proto l2cap_proto = {
. name = " L2CAP " ,
. owner = THIS_MODULE ,
. obj_size = sizeof ( struct l2cap_pinfo )
} ;
2007-10-09 10:24:22 +04:00
static struct sock * l2cap_sock_alloc ( struct net * net , struct socket * sock , int proto , gfp_t prio )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
2007-11-01 10:39:31 +03:00
sk = sk_alloc ( net , PF_BLUETOOTH , prio , & l2cap_proto ) ;
2005-04-17 02:20:36 +04:00
if ( ! sk )
return NULL ;
sock_init_data ( sock , sk ) ;
INIT_LIST_HEAD ( & bt_sk ( sk ) - > accept_q ) ;
sk - > sk_destruct = l2cap_sock_destruct ;
2007-10-20 15:37:56 +04:00
sk - > sk_sndtimeo = msecs_to_jiffies ( L2CAP_CONN_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
sock_reset_flag ( sk , SOCK_ZAPPED ) ;
sk - > sk_protocol = proto ;
2008-07-14 22:13:54 +04:00
sk - > sk_state = BT_OPEN ;
2005-04-17 02:20:36 +04:00
2008-07-14 22:13:54 +04:00
setup_timer ( & sk - > sk_timer , l2cap_sock_timeout , ( unsigned long ) sk ) ;
2005-04-17 02:20:36 +04:00
bt_sock_link ( & l2cap_sk_list , sk ) ;
return sk ;
}
2009-11-06 09:18:14 +03:00
static int l2cap_sock_create ( struct net * net , struct socket * sock , int protocol ,
int kern )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
BT_DBG ( " sock %p " , sock ) ;
sock - > state = SS_UNCONNECTED ;
2010-05-01 23:15:42 +04:00
if ( sock - > type ! = SOCK_SEQPACKET & & sock - > type ! = SOCK_STREAM & &
2005-04-17 02:20:36 +04:00
sock - > type ! = SOCK_DGRAM & & sock - > type ! = SOCK_RAW )
return - ESOCKTNOSUPPORT ;
2009-11-06 07:45:52 +03:00
if ( sock - > type = = SOCK_RAW & & ! kern & & ! capable ( CAP_NET_RAW ) )
2005-04-17 02:20:36 +04:00
return - EPERM ;
sock - > ops = & l2cap_sock_ops ;
2007-10-09 10:24:22 +04:00
sk = l2cap_sock_alloc ( net , sock , protocol , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! sk )
return - ENOMEM ;
l2cap_sock_init ( sk , NULL ) ;
return 0 ;
}
2009-02-12 07:07:45 +03:00
static int l2cap_sock_bind ( struct socket * sock , struct sockaddr * addr , int alen )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sock - > sk ;
2009-02-12 07:07:45 +03:00
struct sockaddr_l2 la ;
int len , err = 0 ;
2005-04-17 02:20:36 +04:00
2009-02-12 07:07:45 +03:00
BT_DBG ( " sk %p " , sk ) ;
2005-04-17 02:20:36 +04:00
if ( ! addr | | addr - > sa_family ! = AF_BLUETOOTH )
return - EINVAL ;
2009-02-12 07:07:45 +03:00
memset ( & la , 0 , sizeof ( la ) ) ;
len = min_t ( unsigned int , sizeof ( la ) , alen ) ;
memcpy ( & la , addr , len ) ;
2009-02-16 05:20:31 +03:00
if ( la . l2_cid )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
lock_sock ( sk ) ;
if ( sk - > sk_state ! = BT_OPEN ) {
err = - EBADFD ;
goto done ;
}
2009-06-07 20:06:51 +04:00
if ( la . l2_psm & & __le16_to_cpu ( la . l2_psm ) < 0x1001 & &
2007-01-23 00:00:45 +03:00
! capable ( CAP_NET_BIND_SERVICE ) ) {
err = - EACCES ;
goto done ;
}
2007-02-09 17:24:33 +03:00
2005-04-17 02:20:36 +04:00
write_lock_bh ( & l2cap_sk_list . lock ) ;
2009-02-12 07:07:45 +03:00
if ( la . l2_psm & & __l2cap_get_sock_by_addr ( la . l2_psm , & la . l2_bdaddr ) ) {
2005-04-17 02:20:36 +04:00
err = - EADDRINUSE ;
} else {
/* Save source address */
2009-02-12 07:07:45 +03:00
bacpy ( & bt_sk ( sk ) - > src , & la . l2_bdaddr ) ;
l2cap_pi ( sk ) - > psm = la . l2_psm ;
l2cap_pi ( sk ) - > sport = la . l2_psm ;
2005-04-17 02:20:36 +04:00
sk - > sk_state = BT_BOUND ;
2009-01-15 23:58:38 +03:00
2009-06-07 20:06:51 +04:00
if ( __le16_to_cpu ( la . l2_psm ) = = 0x0001 | |
__le16_to_cpu ( la . l2_psm ) = = 0x0003 )
2009-01-15 23:58:38 +03:00
l2cap_pi ( sk ) - > sec_level = BT_SECURITY_SDP ;
2005-04-17 02:20:36 +04:00
}
write_unlock_bh ( & l2cap_sk_list . lock ) ;
done :
release_sock ( sk ) ;
return err ;
}
static int l2cap_do_connect ( struct sock * sk )
{
bdaddr_t * src = & bt_sk ( sk ) - > src ;
bdaddr_t * dst = & bt_sk ( sk ) - > dst ;
struct l2cap_conn * conn ;
struct hci_conn * hcon ;
struct hci_dev * hdev ;
2008-09-09 09:19:20 +04:00
__u8 auth_type ;
2009-04-20 09:09:16 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2009-02-12 07:07:45 +03:00
BT_DBG ( " %s -> %s psm 0x%2.2x " , batostr ( src ) , batostr ( dst ) ,
l2cap_pi ( sk ) - > psm ) ;
2005-04-17 02:20:36 +04:00
2009-04-20 08:31:08 +04:00
hdev = hci_get_route ( dst , src ) ;
if ( ! hdev )
2005-04-17 02:20:36 +04:00
return - EHOSTUNREACH ;
hci_dev_lock_bh ( hdev ) ;
err = - ENOMEM ;
2009-01-15 23:58:04 +03:00
if ( sk - > sk_type = = SOCK_RAW ) {
2009-01-15 23:58:38 +03:00
switch ( l2cap_pi ( sk ) - > sec_level ) {
case BT_SECURITY_HIGH :
2009-01-15 23:58:04 +03:00
auth_type = HCI_AT_DEDICATED_BONDING_MITM ;
2009-01-15 23:58:38 +03:00
break ;
case BT_SECURITY_MEDIUM :
2009-01-15 23:58:04 +03:00
auth_type = HCI_AT_DEDICATED_BONDING ;
2009-01-15 23:58:38 +03:00
break ;
default :
2008-09-09 09:19:20 +04:00
auth_type = HCI_AT_NO_BONDING ;
2009-01-15 23:58:38 +03:00
break ;
}
2009-01-15 23:58:04 +03:00
} else if ( l2cap_pi ( sk ) - > psm = = cpu_to_le16 ( 0x0001 ) ) {
2009-01-15 23:58:38 +03:00
if ( l2cap_pi ( sk ) - > sec_level = = BT_SECURITY_HIGH )
2009-01-15 23:58:04 +03:00
auth_type = HCI_AT_NO_BONDING_MITM ;
2008-09-09 09:19:20 +04:00
else
2009-01-15 23:58:04 +03:00
auth_type = HCI_AT_NO_BONDING ;
2009-02-09 05:55:28 +03:00
if ( l2cap_pi ( sk ) - > sec_level = = BT_SECURITY_LOW )
l2cap_pi ( sk ) - > sec_level = BT_SECURITY_SDP ;
2009-01-15 23:58:04 +03:00
} else {
2009-01-15 23:58:38 +03:00
switch ( l2cap_pi ( sk ) - > sec_level ) {
case BT_SECURITY_HIGH :
2009-01-15 23:58:04 +03:00
auth_type = HCI_AT_GENERAL_BONDING_MITM ;
2009-01-15 23:58:38 +03:00
break ;
case BT_SECURITY_MEDIUM :
2008-09-09 09:19:20 +04:00
auth_type = HCI_AT_GENERAL_BONDING ;
2009-01-15 23:58:38 +03:00
break ;
default :
2009-01-15 23:58:04 +03:00
auth_type = HCI_AT_NO_BONDING ;
2009-01-15 23:58:38 +03:00
break ;
}
2008-09-09 09:19:20 +04:00
}
2009-01-15 23:58:38 +03:00
hcon = hci_connect ( hdev , ACL_LINK , dst ,
l2cap_pi ( sk ) - > sec_level , auth_type ) ;
2005-04-17 02:20:36 +04:00
if ( ! hcon )
goto done ;
conn = l2cap_conn_add ( hcon , 0 ) ;
if ( ! conn ) {
hci_conn_put ( hcon ) ;
goto done ;
}
err = 0 ;
/* Update source addr of the socket */
bacpy ( src , conn - > src ) ;
l2cap_chan_add ( conn , sk , NULL ) ;
sk - > sk_state = BT_CONNECT ;
l2cap_sock_set_timer ( sk , sk - > sk_sndtimeo ) ;
if ( hcon - > state = = BT_CONNECTED ) {
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type ! = SOCK_SEQPACKET & &
sk - > sk_type ! = SOCK_STREAM ) {
2005-04-17 02:20:36 +04:00
l2cap_sock_clear_timer ( sk ) ;
sk - > sk_state = BT_CONNECTED ;
2008-07-14 22:13:44 +04:00
} else
l2cap_do_start ( sk ) ;
2005-04-17 02:20:36 +04:00
}
done :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
static int l2cap_sock_connect ( struct socket * sock , struct sockaddr * addr , int alen , int flags )
{
struct sock * sk = sock - > sk ;
2009-02-12 07:07:45 +03:00
struct sockaddr_l2 la ;
int len , err = 0 ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " sk %p " , sk ) ;
2010-04-01 02:58:26 +04:00
if ( ! addr | | alen < sizeof ( addr - > sa_family ) | |
addr - > sa_family ! = AF_BLUETOOTH )
2009-02-16 05:20:31 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2009-02-12 07:07:45 +03:00
memset ( & la , 0 , sizeof ( la ) ) ;
len = min_t ( unsigned int , sizeof ( la ) , alen ) ;
memcpy ( & la , addr , len ) ;
2009-02-16 05:20:31 +03:00
if ( la . l2_cid )
return - EINVAL ;
lock_sock ( sk ) ;
2010-05-01 23:15:42 +04:00
if ( ( sk - > sk_type = = SOCK_SEQPACKET | | sk - > sk_type = = SOCK_STREAM )
& & ! la . l2_psm ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto done ;
}
2009-05-03 09:31:10 +04:00
switch ( l2cap_pi ( sk ) - > mode ) {
case L2CAP_MODE_BASIC :
break ;
case L2CAP_MODE_ERTM :
2009-07-04 22:06:24 +04:00
case L2CAP_MODE_STREAMING :
2010-07-18 23:25:54 +04:00
if ( ! disable_ertm )
2009-05-03 09:31:10 +04:00
break ;
/* fall through */
default :
err = - ENOTSUPP ;
goto done ;
}
2009-04-20 08:31:08 +04:00
switch ( sk - > sk_state ) {
2005-04-17 02:20:36 +04:00
case BT_CONNECT :
case BT_CONNECT2 :
case BT_CONFIG :
/* Already connecting */
goto wait ;
case BT_CONNECTED :
/* Already connected */
2010-06-22 20:56:22 +04:00
err = - EISCONN ;
2005-04-17 02:20:36 +04:00
goto done ;
case BT_OPEN :
case BT_BOUND :
/* Can connect */
break ;
default :
err = - EBADFD ;
goto done ;
}
/* Set destination address and psm */
2009-02-12 07:07:45 +03:00
bacpy ( & bt_sk ( sk ) - > dst , & la . l2_bdaddr ) ;
l2cap_pi ( sk ) - > psm = la . l2_psm ;
2005-04-17 02:20:36 +04:00
2009-04-20 08:31:08 +04:00
err = l2cap_do_connect ( sk ) ;
if ( err )
2005-04-17 02:20:36 +04:00
goto done ;
wait :
err = bt_sock_wait_state ( sk , BT_CONNECTED ,
sock_sndtimeo ( sk , flags & O_NONBLOCK ) ) ;
done :
release_sock ( sk ) ;
return err ;
}
static int l2cap_sock_listen ( struct socket * sock , int backlog )
{
struct sock * sk = sock - > sk ;
int err = 0 ;
BT_DBG ( " sk %p backlog %d " , sk , backlog ) ;
lock_sock ( sk ) ;
2010-05-01 23:15:42 +04:00
if ( ( sock - > type ! = SOCK_SEQPACKET & & sock - > type ! = SOCK_STREAM )
| | sk - > sk_state ! = BT_BOUND ) {
2005-04-17 02:20:36 +04:00
err = - EBADFD ;
goto done ;
}
2009-05-03 09:31:10 +04:00
switch ( l2cap_pi ( sk ) - > mode ) {
case L2CAP_MODE_BASIC :
break ;
case L2CAP_MODE_ERTM :
2009-07-04 22:06:24 +04:00
case L2CAP_MODE_STREAMING :
2010-07-18 23:25:54 +04:00
if ( ! disable_ertm )
2009-05-03 09:31:10 +04:00
break ;
/* fall through */
default :
err = - ENOTSUPP ;
goto done ;
}
2005-04-17 02:20:36 +04:00
if ( ! l2cap_pi ( sk ) - > psm ) {
bdaddr_t * src = & bt_sk ( sk ) - > src ;
u16 psm ;
err = - EINVAL ;
write_lock_bh ( & l2cap_sk_list . lock ) ;
for ( psm = 0x1001 ; psm < 0x1100 ; psm + = 2 )
2009-06-07 20:06:51 +04:00
if ( ! __l2cap_get_sock_by_addr ( cpu_to_le16 ( psm ) , src ) ) {
l2cap_pi ( sk ) - > psm = cpu_to_le16 ( psm ) ;
l2cap_pi ( sk ) - > sport = cpu_to_le16 ( psm ) ;
2005-04-17 02:20:36 +04:00
err = 0 ;
break ;
}
write_unlock_bh ( & l2cap_sk_list . lock ) ;
if ( err < 0 )
goto done ;
}
sk - > sk_max_ack_backlog = backlog ;
sk - > sk_ack_backlog = 0 ;
sk - > sk_state = BT_LISTEN ;
done :
release_sock ( sk ) ;
return err ;
}
static int l2cap_sock_accept ( struct socket * sock , struct socket * newsock , int flags )
{
DECLARE_WAITQUEUE ( wait , current ) ;
struct sock * sk = sock - > sk , * nsk ;
long timeo ;
int err = 0 ;
2006-11-09 09:44:35 +03:00
lock_sock_nested ( sk , SINGLE_DEPTH_NESTING ) ;
2005-04-17 02:20:36 +04:00
if ( sk - > sk_state ! = BT_LISTEN ) {
err = - EBADFD ;
goto done ;
}
timeo = sock_rcvtimeo ( sk , flags & O_NONBLOCK ) ;
BT_DBG ( " sk %p timeo %ld " , sk , timeo ) ;
/* Wait for an incoming connection. (wake-one). */
2010-04-20 17:03:51 +04:00
add_wait_queue_exclusive ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
while ( ! ( nsk = bt_accept_dequeue ( sk , newsock ) ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( ! timeo ) {
err = - EAGAIN ;
break ;
}
release_sock ( sk ) ;
timeo = schedule_timeout ( timeo ) ;
2006-11-09 09:44:35 +03:00
lock_sock_nested ( sk , SINGLE_DEPTH_NESTING ) ;
2005-04-17 02:20:36 +04:00
if ( sk - > sk_state ! = BT_LISTEN ) {
err = - EBADFD ;
break ;
}
if ( signal_pending ( current ) ) {
err = sock_intr_errno ( timeo ) ;
break ;
}
}
set_current_state ( TASK_RUNNING ) ;
2010-04-20 17:03:51 +04:00
remove_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto done ;
newsock - > state = SS_CONNECTED ;
BT_DBG ( " new socket %p " , nsk ) ;
done :
release_sock ( sk ) ;
return err ;
}
static int l2cap_sock_getname ( struct socket * sock , struct sockaddr * addr , int * len , int peer )
{
struct sockaddr_l2 * la = ( struct sockaddr_l2 * ) addr ;
struct sock * sk = sock - > sk ;
BT_DBG ( " sock %p, sk %p " , sock , sk ) ;
addr - > sa_family = AF_BLUETOOTH ;
* len = sizeof ( struct sockaddr_l2 ) ;
2009-02-12 07:07:45 +03:00
if ( peer ) {
la - > l2_psm = l2cap_pi ( sk ) - > psm ;
2005-04-17 02:20:36 +04:00
bacpy ( & la - > l2_bdaddr , & bt_sk ( sk ) - > dst ) ;
2009-06-07 20:06:51 +04:00
la - > l2_cid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
2009-02-12 07:07:45 +03:00
} else {
la - > l2_psm = l2cap_pi ( sk ) - > sport ;
2005-04-17 02:20:36 +04:00
bacpy ( & la - > l2_bdaddr , & bt_sk ( sk ) - > src ) ;
2009-06-07 20:06:51 +04:00
la - > l2_cid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
2009-02-12 07:07:45 +03:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-05-01 23:15:44 +04:00
static int __l2cap_wait_ack ( struct sock * sk )
{
DECLARE_WAITQUEUE ( wait , current ) ;
int err = 0 ;
int timeo = HZ / 5 ;
2010-05-10 13:33:10 +04:00
add_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2010-05-01 23:15:44 +04:00
while ( ( l2cap_pi ( sk ) - > unacked_frames > 0 & & l2cap_pi ( sk ) - > conn ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( ! timeo )
timeo = HZ / 5 ;
if ( signal_pending ( current ) ) {
err = sock_intr_errno ( timeo ) ;
break ;
}
release_sock ( sk ) ;
timeo = schedule_timeout ( timeo ) ;
lock_sock ( sk ) ;
err = sock_error ( sk ) ;
if ( err )
break ;
}
set_current_state ( TASK_RUNNING ) ;
2010-05-10 13:33:10 +04:00
remove_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2010-05-01 23:15:44 +04:00
return err ;
}
2009-08-21 05:26:00 +04:00
static void l2cap_monitor_timeout ( unsigned long arg )
{
struct sock * sk = ( void * ) arg ;
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p " , sk ) ;
2009-08-24 07:45:19 +04:00
bh_lock_sock ( sk ) ;
2009-08-21 05:26:00 +04:00
if ( l2cap_pi ( sk ) - > retry_count > = l2cap_pi ( sk ) - > remote_max_tx ) {
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( l2cap_pi ( sk ) - > conn , sk , ECONNABORTED ) ;
2009-12-15 12:38:04 +03:00
bh_unlock_sock ( sk ) ;
2009-08-21 05:26:00 +04:00
return ;
}
l2cap_pi ( sk ) - > retry_count + + ;
__mod_monitor_timer ( ) ;
2010-05-06 02:58:27 +04:00
l2cap_send_rr_or_rnr ( l2cap_pi ( sk ) , L2CAP_CTRL_POLL ) ;
2009-08-24 07:45:19 +04:00
bh_unlock_sock ( sk ) ;
2009-08-21 05:26:00 +04:00
}
static void l2cap_retrans_timeout ( unsigned long arg )
{
struct sock * sk = ( void * ) arg ;
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p " , sk ) ;
2009-08-24 07:45:19 +04:00
bh_lock_sock ( sk ) ;
2009-08-21 05:26:00 +04:00
l2cap_pi ( sk ) - > retry_count = 1 ;
__mod_monitor_timer ( ) ;
l2cap_pi ( sk ) - > conn_state | = L2CAP_CONN_WAIT_F ;
2010-05-06 02:58:27 +04:00
l2cap_send_rr_or_rnr ( l2cap_pi ( sk ) , L2CAP_CTRL_POLL ) ;
2009-08-24 07:45:19 +04:00
bh_unlock_sock ( sk ) ;
2009-08-21 05:26:00 +04:00
}
2009-08-21 05:25:57 +04:00
static void l2cap_drop_acked_frames ( struct sock * sk )
2005-04-17 02:20:36 +04:00
{
2009-08-21 05:25:57 +04:00
struct sk_buff * skb ;
2005-04-17 02:20:36 +04:00
2010-05-01 23:15:42 +04:00
while ( ( skb = skb_peek ( TX_QUEUE ( sk ) ) ) & &
l2cap_pi ( sk ) - > unacked_frames ) {
2009-08-21 05:25:57 +04:00
if ( bt_cb ( skb ) - > tx_seq = = l2cap_pi ( sk ) - > expected_ack_seq )
break ;
2005-04-17 02:20:36 +04:00
2009-08-21 05:25:57 +04:00
skb = skb_dequeue ( TX_QUEUE ( sk ) ) ;
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
2009-08-21 05:25:57 +04:00
l2cap_pi ( sk ) - > unacked_frames - - ;
}
2005-04-17 02:20:36 +04:00
2009-08-21 05:26:00 +04:00
if ( ! l2cap_pi ( sk ) - > unacked_frames )
del_timer ( & l2cap_pi ( sk ) - > retrans_timer ) ;
2009-08-21 05:25:57 +04:00
}
2005-04-17 02:20:36 +04:00
2010-05-01 23:15:43 +04:00
static inline void l2cap_do_send ( struct sock * sk , struct sk_buff * skb )
2009-08-21 05:25:57 +04:00
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
BT_DBG ( " sk %p, skb %p len %d " , sk , skb , skb - > len ) ;
2005-04-17 02:20:36 +04:00
2010-05-01 23:15:43 +04:00
hci_send_acl ( pi - > conn - > hcon , skb , 0 ) ;
2009-08-21 05:25:57 +04:00
}
2010-06-22 20:56:23 +04:00
static void l2cap_streaming_send ( struct sock * sk )
2009-08-21 05:26:01 +04:00
{
2010-08-31 01:44:44 +04:00
struct sk_buff * skb ;
2009-08-21 05:26:01 +04:00
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
2009-08-21 05:26:02 +04:00
u16 control , fcs ;
2009-08-21 05:26:01 +04:00
2010-08-31 01:44:44 +04:00
while ( ( skb = skb_dequeue ( TX_QUEUE ( sk ) ) ) ) {
control = get_unaligned_le16 ( skb - > data + L2CAP_HDR_SIZE ) ;
2009-08-21 05:26:01 +04:00
control | = pi - > next_tx_seq < < L2CAP_CTRL_TXSEQ_SHIFT ;
2010-08-31 01:44:44 +04:00
put_unaligned_le16 ( control , skb - > data + L2CAP_HDR_SIZE ) ;
2009-08-21 05:26:01 +04:00
2010-05-01 23:15:36 +04:00
if ( pi - > fcs = = L2CAP_FCS_CRC16 ) {
2010-08-31 01:44:44 +04:00
fcs = crc16 ( 0 , ( u8 * ) skb - > data , skb - > len - 2 ) ;
put_unaligned_le16 ( fcs , skb - > data + skb - > len - 2 ) ;
2009-08-21 05:26:02 +04:00
}
2010-08-31 01:44:44 +04:00
l2cap_do_send ( sk , skb ) ;
2009-08-21 05:26:01 +04:00
pi - > next_tx_seq = ( pi - > next_tx_seq + 1 ) % 64 ;
}
}
2010-05-01 23:15:45 +04:00
static void l2cap_retransmit_one_frame ( struct sock * sk , u8 tx_seq )
2009-08-21 05:26:03 +04:00
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct sk_buff * skb , * tx_skb ;
u16 control , fcs ;
skb = skb_peek ( TX_QUEUE ( sk ) ) ;
2010-05-01 23:15:44 +04:00
if ( ! skb )
return ;
2009-08-21 05:26:03 +04:00
2010-05-01 23:15:44 +04:00
do {
if ( bt_cb ( skb ) - > tx_seq = = tx_seq )
2009-08-21 05:26:03 +04:00
break ;
2010-05-01 23:15:44 +04:00
if ( skb_queue_is_last ( TX_QUEUE ( sk ) , skb ) )
return ;
2009-08-21 05:26:03 +04:00
2010-05-01 23:15:44 +04:00
} while ( ( skb = skb_queue_next ( TX_QUEUE ( sk ) , skb ) ) ) ;
2009-08-21 05:26:03 +04:00
2010-05-01 23:15:44 +04:00
if ( pi - > remote_max_tx & &
bt_cb ( skb ) - > retries = = pi - > remote_max_tx ) {
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( pi - > conn , sk , ECONNABORTED ) ;
2010-05-01 23:15:44 +04:00
return ;
}
tx_skb = skb_clone ( skb , GFP_ATOMIC ) ;
bt_cb ( skb ) - > retries + + ;
control = get_unaligned_le16 ( tx_skb - > data + L2CAP_HDR_SIZE ) ;
2010-05-29 09:24:35 +04:00
if ( pi - > conn_state & L2CAP_CONN_SEND_FBIT ) {
control | = L2CAP_CTRL_FINAL ;
pi - > conn_state & = ~ L2CAP_CONN_SEND_FBIT ;
}
2010-06-19 03:37:33 +04:00
2010-05-01 23:15:44 +04:00
control | = ( pi - > buffer_seq < < L2CAP_CTRL_REQSEQ_SHIFT )
| ( tx_seq < < L2CAP_CTRL_TXSEQ_SHIFT ) ;
2010-05-29 09:24:35 +04:00
2010-05-01 23:15:44 +04:00
put_unaligned_le16 ( control , tx_skb - > data + L2CAP_HDR_SIZE ) ;
if ( pi - > fcs = = L2CAP_FCS_CRC16 ) {
fcs = crc16 ( 0 , ( u8 * ) tx_skb - > data , tx_skb - > len - 2 ) ;
put_unaligned_le16 ( fcs , tx_skb - > data + tx_skb - > len - 2 ) ;
}
l2cap_do_send ( sk , tx_skb ) ;
2009-08-21 05:26:03 +04:00
}
2009-08-21 05:25:57 +04:00
static int l2cap_ertm_send ( struct sock * sk )
{
struct sk_buff * skb , * tx_skb ;
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
2009-08-21 05:26:02 +04:00
u16 control , fcs ;
2010-05-01 23:15:43 +04:00
int nsent = 0 ;
2009-08-21 05:25:57 +04:00
2010-05-14 03:50:12 +04:00
if ( sk - > sk_state ! = BT_CONNECTED )
return - ENOTCONN ;
2009-08-21 05:26:00 +04:00
2010-06-02 01:52:58 +04:00
while ( ( skb = sk - > sk_send_head ) & & ( ! l2cap_tx_window_full ( sk ) ) ) {
2009-08-21 05:25:57 +04:00
2009-08-21 05:26:00 +04:00
if ( pi - > remote_max_tx & &
bt_cb ( skb ) - > retries = = pi - > remote_max_tx ) {
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( pi - > conn , sk , ECONNABORTED ) ;
2009-08-21 05:26:00 +04:00
break ;
}
2009-12-23 14:07:14 +03:00
tx_skb = skb_clone ( skb , GFP_ATOMIC ) ;
2009-08-21 05:26:00 +04:00
bt_cb ( skb ) - > retries + + ;
2009-08-21 05:25:57 +04:00
control = get_unaligned_le16 ( tx_skb - > data + L2CAP_HDR_SIZE ) ;
2010-06-19 03:37:33 +04:00
control & = L2CAP_CTRL_SAR ;
2010-05-01 23:15:36 +04:00
if ( pi - > conn_state & L2CAP_CONN_SEND_FBIT ) {
control | = L2CAP_CTRL_FINAL ;
pi - > conn_state & = ~ L2CAP_CONN_SEND_FBIT ;
}
2009-10-03 09:34:38 +04:00
control | = ( pi - > buffer_seq < < L2CAP_CTRL_REQSEQ_SHIFT )
2009-08-21 05:25:57 +04:00
| ( pi - > next_tx_seq < < L2CAP_CTRL_TXSEQ_SHIFT ) ;
put_unaligned_le16 ( control , tx_skb - > data + L2CAP_HDR_SIZE ) ;
2009-08-21 05:26:00 +04:00
2010-05-01 23:15:36 +04:00
if ( pi - > fcs = = L2CAP_FCS_CRC16 ) {
2009-08-21 05:26:02 +04:00
fcs = crc16 ( 0 , ( u8 * ) skb - > data , tx_skb - > len - 2 ) ;
put_unaligned_le16 ( fcs , skb - > data + tx_skb - > len - 2 ) ;
}
2010-05-01 23:15:43 +04:00
l2cap_do_send ( sk , tx_skb ) ;
2009-08-21 05:26:00 +04:00
__mod_retrans_timer ( ) ;
2009-08-21 05:25:57 +04:00
bt_cb ( skb ) - > tx_seq = pi - > next_tx_seq ;
pi - > next_tx_seq = ( pi - > next_tx_seq + 1 ) % 64 ;
pi - > unacked_frames + + ;
2010-05-01 23:15:36 +04:00
pi - > frames_sent + + ;
2009-08-21 05:25:57 +04:00
if ( skb_queue_is_last ( TX_QUEUE ( sk ) , skb ) )
sk - > sk_send_head = NULL ;
else
sk - > sk_send_head = skb_queue_next ( TX_QUEUE ( sk ) , skb ) ;
2010-05-01 23:15:37 +04:00
nsent + + ;
2009-08-21 05:25:57 +04:00
}
2010-05-01 23:15:37 +04:00
return nsent ;
}
2010-05-01 23:15:45 +04:00
static int l2cap_retransmit_frames ( struct sock * sk )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
int ret ;
if ( ! skb_queue_empty ( TX_QUEUE ( sk ) ) )
sk - > sk_send_head = TX_QUEUE ( sk ) - > next ;
pi - > next_tx_seq = pi - > expected_ack_seq ;
ret = l2cap_ertm_send ( sk ) ;
return ret ;
}
2010-05-01 23:15:43 +04:00
static void l2cap_send_ack ( struct l2cap_pinfo * pi )
2010-05-01 23:15:37 +04:00
{
struct sock * sk = ( struct sock * ) pi ;
u16 control = 0 ;
control | = pi - > buffer_seq < < L2CAP_CTRL_REQSEQ_SHIFT ;
if ( pi - > conn_state & L2CAP_CONN_LOCAL_BUSY ) {
control | = L2CAP_SUPER_RCV_NOT_READY ;
2010-05-01 23:15:44 +04:00
pi - > conn_state | = L2CAP_CONN_RNR_SENT ;
2010-05-01 23:15:43 +04:00
l2cap_send_sframe ( pi , control ) ;
return ;
2010-05-01 23:15:37 +04:00
}
2010-05-01 23:15:45 +04:00
2010-06-22 01:50:49 +04:00
if ( l2cap_ertm_send ( sk ) > 0 )
2010-05-01 23:15:45 +04:00
return ;
control | = L2CAP_SUPER_RCV_READY ;
l2cap_send_sframe ( pi , control ) ;
2009-08-21 05:25:57 +04:00
}
2010-05-01 23:15:43 +04:00
static void l2cap_send_srejtail ( struct sock * sk )
2010-05-01 23:15:38 +04:00
{
struct srej_list * tail ;
u16 control ;
control = L2CAP_SUPER_SELECT_REJECT ;
control | = L2CAP_CTRL_FINAL ;
tail = list_entry ( SREJ_LIST ( sk ) - > prev , struct srej_list , list ) ;
control | = tail - > tx_seq < < L2CAP_CTRL_REQSEQ_SHIFT ;
l2cap_send_sframe ( l2cap_pi ( sk ) , control ) ;
}
2009-08-21 05:25:57 +04:00
static inline int l2cap_skbuff_fromiovec ( struct sock * sk , struct msghdr * msg , int len , int count , struct sk_buff * skb )
{
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
struct sk_buff * * frag ;
int err , sent = 0 ;
2005-04-17 02:20:36 +04:00
2010-05-01 23:15:43 +04:00
if ( memcpy_fromiovec ( skb_put ( skb , count ) , msg - > msg_iov , count ) )
2009-08-21 05:25:57 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
sent + = count ;
len - = count ;
/* Continuation fragments (no L2CAP header) */
frag = & skb_shinfo ( skb ) - > frag_list ;
while ( len ) {
count = min_t ( unsigned int , conn - > mtu , len ) ;
* frag = bt_skb_send_alloc ( sk , count , msg - > msg_flags & MSG_DONTWAIT , & err ) ;
if ( ! * frag )
2009-08-21 05:25:57 +04:00
return - EFAULT ;
if ( memcpy_fromiovec ( skb_put ( * frag , count ) , msg - > msg_iov , count ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
sent + = count ;
len - = count ;
frag = & ( * frag ) - > next ;
}
return sent ;
2009-08-21 05:25:57 +04:00
}
2005-04-17 02:20:36 +04:00
2009-08-21 05:25:57 +04:00
static struct sk_buff * l2cap_create_connless_pdu ( struct sock * sk , struct msghdr * msg , size_t len )
{
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
struct sk_buff * skb ;
int err , count , hlen = L2CAP_HDR_SIZE + 2 ;
struct l2cap_hdr * lh ;
BT_DBG ( " sk %p len %d " , sk , ( int ) len ) ;
count = min_t ( unsigned int , ( conn - > mtu - hlen ) , len ) ;
skb = bt_skb_send_alloc ( sk , count + hlen ,
msg - > msg_flags & MSG_DONTWAIT , & err ) ;
if ( ! skb )
return ERR_PTR ( - ENOMEM ) ;
/* Create L2CAP header */
lh = ( struct l2cap_hdr * ) skb_put ( skb , L2CAP_HDR_SIZE ) ;
lh - > cid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
lh - > len = cpu_to_le16 ( len + ( hlen - L2CAP_HDR_SIZE ) ) ;
put_unaligned_le16 ( l2cap_pi ( sk ) - > psm , skb_put ( skb , 2 ) ) ;
err = l2cap_skbuff_fromiovec ( sk , msg , len , count , skb ) ;
if ( unlikely ( err < 0 ) ) {
kfree_skb ( skb ) ;
return ERR_PTR ( err ) ;
}
return skb ;
}
static struct sk_buff * l2cap_create_basic_pdu ( struct sock * sk , struct msghdr * msg , size_t len )
{
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
struct sk_buff * skb ;
int err , count , hlen = L2CAP_HDR_SIZE ;
struct l2cap_hdr * lh ;
BT_DBG ( " sk %p len %d " , sk , ( int ) len ) ;
count = min_t ( unsigned int , ( conn - > mtu - hlen ) , len ) ;
skb = bt_skb_send_alloc ( sk , count + hlen ,
msg - > msg_flags & MSG_DONTWAIT , & err ) ;
if ( ! skb )
return ERR_PTR ( - ENOMEM ) ;
/* Create L2CAP header */
lh = ( struct l2cap_hdr * ) skb_put ( skb , L2CAP_HDR_SIZE ) ;
lh - > cid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
lh - > len = cpu_to_le16 ( len + ( hlen - L2CAP_HDR_SIZE ) ) ;
err = l2cap_skbuff_fromiovec ( sk , msg , len , count , skb ) ;
if ( unlikely ( err < 0 ) ) {
kfree_skb ( skb ) ;
return ERR_PTR ( err ) ;
}
return skb ;
}
2009-08-21 05:26:01 +04:00
static struct sk_buff * l2cap_create_iframe_pdu ( struct sock * sk , struct msghdr * msg , size_t len , u16 control , u16 sdulen )
2009-08-21 05:25:57 +04:00
{
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
struct sk_buff * skb ;
int err , count , hlen = L2CAP_HDR_SIZE + 2 ;
struct l2cap_hdr * lh ;
BT_DBG ( " sk %p len %d " , sk , ( int ) len ) ;
2010-05-01 23:15:41 +04:00
if ( ! conn )
return ERR_PTR ( - ENOTCONN ) ;
2009-08-21 05:25:58 +04:00
if ( sdulen )
hlen + = 2 ;
2009-08-21 05:26:02 +04:00
if ( l2cap_pi ( sk ) - > fcs = = L2CAP_FCS_CRC16 )
hlen + = 2 ;
2009-08-21 05:25:57 +04:00
count = min_t ( unsigned int , ( conn - > mtu - hlen ) , len ) ;
skb = bt_skb_send_alloc ( sk , count + hlen ,
msg - > msg_flags & MSG_DONTWAIT , & err ) ;
if ( ! skb )
return ERR_PTR ( - ENOMEM ) ;
/* Create L2CAP header */
lh = ( struct l2cap_hdr * ) skb_put ( skb , L2CAP_HDR_SIZE ) ;
lh - > cid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
lh - > len = cpu_to_le16 ( len + ( hlen - L2CAP_HDR_SIZE ) ) ;
put_unaligned_le16 ( control , skb_put ( skb , 2 ) ) ;
2009-08-21 05:25:58 +04:00
if ( sdulen )
put_unaligned_le16 ( sdulen , skb_put ( skb , 2 ) ) ;
2009-08-21 05:25:57 +04:00
err = l2cap_skbuff_fromiovec ( sk , msg , len , count , skb ) ;
if ( unlikely ( err < 0 ) ) {
kfree_skb ( skb ) ;
return ERR_PTR ( err ) ;
}
2009-08-21 05:26:00 +04:00
2009-08-21 05:26:02 +04:00
if ( l2cap_pi ( sk ) - > fcs = = L2CAP_FCS_CRC16 )
put_unaligned_le16 ( 0 , skb_put ( skb , 2 ) ) ;
2009-08-21 05:26:00 +04:00
bt_cb ( skb ) - > retries = 0 ;
2009-08-21 05:25:57 +04:00
return skb ;
2005-04-17 02:20:36 +04:00
}
2009-08-21 05:25:58 +04:00
static inline int l2cap_sar_segment_sdu ( struct sock * sk , struct msghdr * msg , size_t len )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct sk_buff * skb ;
struct sk_buff_head sar_queue ;
u16 control ;
size_t size = 0 ;
2010-05-06 05:09:15 +04:00
skb_queue_head_init ( & sar_queue ) ;
2009-08-21 05:25:58 +04:00
control = L2CAP_SDU_START ;
2010-05-01 23:15:40 +04:00
skb = l2cap_create_iframe_pdu ( sk , msg , pi - > remote_mps , control , len ) ;
2009-08-21 05:25:58 +04:00
if ( IS_ERR ( skb ) )
return PTR_ERR ( skb ) ;
__skb_queue_tail ( & sar_queue , skb ) ;
2010-05-01 23:15:40 +04:00
len - = pi - > remote_mps ;
size + = pi - > remote_mps ;
2009-08-21 05:25:58 +04:00
while ( len > 0 ) {
size_t buflen ;
2010-05-01 23:15:40 +04:00
if ( len > pi - > remote_mps ) {
2010-05-01 23:15:43 +04:00
control = L2CAP_SDU_CONTINUE ;
2010-05-01 23:15:40 +04:00
buflen = pi - > remote_mps ;
2009-08-21 05:25:58 +04:00
} else {
2010-05-01 23:15:43 +04:00
control = L2CAP_SDU_END ;
2009-08-21 05:25:58 +04:00
buflen = len ;
}
2009-08-21 05:26:01 +04:00
skb = l2cap_create_iframe_pdu ( sk , msg , buflen , control , 0 ) ;
2009-08-21 05:25:58 +04:00
if ( IS_ERR ( skb ) ) {
skb_queue_purge ( & sar_queue ) ;
return PTR_ERR ( skb ) ;
}
__skb_queue_tail ( & sar_queue , skb ) ;
len - = buflen ;
size + = buflen ;
}
skb_queue_splice_tail ( & sar_queue , TX_QUEUE ( sk ) ) ;
if ( sk - > sk_send_head = = NULL )
sk - > sk_send_head = sar_queue . next ;
return size ;
}
2005-04-17 02:20:36 +04:00
static int l2cap_sock_sendmsg ( struct kiocb * iocb , struct socket * sock , struct msghdr * msg , size_t len )
{
struct sock * sk = sock - > sk ;
2009-08-21 05:25:57 +04:00
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct sk_buff * skb ;
u16 control ;
int err ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " sock %p, sk %p " , sock , sk ) ;
2005-12-14 10:22:19 +03:00
err = sock_error ( sk ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
if ( msg - > msg_flags & MSG_OOB )
return - EOPNOTSUPP ;
lock_sock ( sk ) ;
2009-08-21 05:25:57 +04:00
if ( sk - > sk_state ! = BT_CONNECTED ) {
2005-04-17 02:20:36 +04:00
err = - ENOTCONN ;
2009-08-21 05:25:57 +04:00
goto done ;
}
2005-04-17 02:20:36 +04:00
2009-08-21 05:25:57 +04:00
/* Connectionless channel */
if ( sk - > sk_type = = SOCK_DGRAM ) {
skb = l2cap_create_connless_pdu ( sk , msg , len ) ;
2010-05-01 23:15:43 +04:00
if ( IS_ERR ( skb ) ) {
2010-04-22 03:52:01 +04:00
err = PTR_ERR ( skb ) ;
2010-05-01 23:15:43 +04:00
} else {
l2cap_do_send ( sk , skb ) ;
err = len ;
}
2009-08-21 05:25:57 +04:00
goto done ;
}
switch ( pi - > mode ) {
case L2CAP_MODE_BASIC :
2010-05-01 23:15:35 +04:00
/* Check outgoing MTU */
if ( len > pi - > omtu ) {
2010-06-22 20:56:24 +04:00
err = - EMSGSIZE ;
2010-05-01 23:15:35 +04:00
goto done ;
}
2009-08-21 05:25:57 +04:00
/* Create a basic PDU */
skb = l2cap_create_basic_pdu ( sk , msg , len ) ;
if ( IS_ERR ( skb ) ) {
err = PTR_ERR ( skb ) ;
goto done ;
}
2010-05-01 23:15:43 +04:00
l2cap_do_send ( sk , skb ) ;
err = len ;
2009-08-21 05:25:57 +04:00
break ;
case L2CAP_MODE_ERTM :
2009-08-21 05:26:01 +04:00
case L2CAP_MODE_STREAMING :
2009-08-21 05:25:57 +04:00
/* Entire SDU fits into one PDU */
2010-05-01 23:15:40 +04:00
if ( len < = pi - > remote_mps ) {
2009-08-21 05:25:57 +04:00
control = L2CAP_SDU_UNSEGMENTED ;
2009-08-21 05:26:01 +04:00
skb = l2cap_create_iframe_pdu ( sk , msg , len , control , 0 ) ;
2009-08-21 05:25:57 +04:00
if ( IS_ERR ( skb ) ) {
err = PTR_ERR ( skb ) ;
goto done ;
}
2009-08-21 05:25:58 +04:00
__skb_queue_tail ( TX_QUEUE ( sk ) , skb ) ;
2010-05-01 23:15:45 +04:00
2009-08-21 05:25:58 +04:00
if ( sk - > sk_send_head = = NULL )
sk - > sk_send_head = skb ;
2010-05-01 23:15:45 +04:00
2009-08-21 05:25:57 +04:00
} else {
2009-08-21 05:25:58 +04:00
/* Segment SDU into multiples PDUs */
err = l2cap_sar_segment_sdu ( sk , msg , len ) ;
if ( err < 0 )
goto done ;
2009-08-21 05:25:57 +04:00
}
2010-05-01 23:15:45 +04:00
if ( pi - > mode = = L2CAP_MODE_STREAMING ) {
2010-06-22 20:56:23 +04:00
l2cap_streaming_send ( sk ) ;
2010-05-01 23:15:45 +04:00
} else {
2010-06-02 01:52:58 +04:00
if ( pi - > conn_state & L2CAP_CONN_REMOTE_BUSY & &
pi - > conn_state & & L2CAP_CONN_WAIT_F ) {
err = len ;
break ;
}
2009-08-21 05:26:01 +04:00
err = l2cap_ertm_send ( sk ) ;
2010-05-01 23:15:45 +04:00
}
2009-08-21 05:26:01 +04:00
2010-05-01 23:15:37 +04:00
if ( err > = 0 )
2009-08-21 05:25:57 +04:00
err = len ;
break ;
default :
BT_DBG ( " bad state %1.1x " , pi - > mode ) ;
2010-06-22 20:56:25 +04:00
err = - EBADFD ;
2009-08-21 05:25:57 +04:00
}
done :
2005-04-17 02:20:36 +04:00
release_sock ( sk ) ;
return err ;
}
2009-01-15 23:57:00 +03:00
static int l2cap_sock_recvmsg ( struct kiocb * iocb , struct socket * sock , struct msghdr * msg , size_t len , int flags )
{
struct sock * sk = sock - > sk ;
lock_sock ( sk ) ;
if ( sk - > sk_state = = BT_CONNECT2 & & bt_sk ( sk ) - > defer_setup ) {
struct l2cap_conn_rsp rsp ;
2010-07-09 03:08:18 +04:00
struct l2cap_conn * conn = l2cap_pi ( sk ) - > conn ;
u8 buf [ 128 ] ;
2009-01-15 23:57:00 +03:00
sk - > sk_state = BT_CONFIG ;
rsp . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
rsp . dcid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
rsp . result = cpu_to_le16 ( L2CAP_CR_SUCCESS ) ;
rsp . status = cpu_to_le16 ( L2CAP_CS_NO_INFO ) ;
l2cap_send_cmd ( l2cap_pi ( sk ) - > conn , l2cap_pi ( sk ) - > ident ,
L2CAP_CONN_RSP , sizeof ( rsp ) , & rsp ) ;
2010-07-09 03:08:18 +04:00
if ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_REQ_SENT ) {
release_sock ( sk ) ;
return 0 ;
}
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_REQ_SENT ;
l2cap_send_cmd ( conn , l2cap_get_ident ( conn ) , L2CAP_CONF_REQ ,
l2cap_build_conf_req ( sk , buf ) , buf ) ;
l2cap_pi ( sk ) - > num_conf_req + + ;
2009-01-15 23:57:00 +03:00
release_sock ( sk ) ;
return 0 ;
}
release_sock ( sk ) ;
return bt_sock_recvmsg ( iocb , sock , msg , len , flags ) ;
}
2009-10-01 03:12:20 +04:00
static int l2cap_sock_setsockopt_old ( struct socket * sock , int optname , char __user * optval , unsigned int optlen )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sock - > sk ;
struct l2cap_options opts ;
2009-02-12 07:07:45 +03:00
int len , err = 0 ;
2005-04-17 02:20:36 +04:00
u32 opt ;
BT_DBG ( " sk %p " , sk ) ;
lock_sock ( sk ) ;
switch ( optname ) {
case L2CAP_OPTIONS :
2010-10-05 02:28:52 +04:00
if ( sk - > sk_state = = BT_CONNECTED ) {
err = - EINVAL ;
break ;
}
2007-05-05 02:35:59 +04:00
opts . imtu = l2cap_pi ( sk ) - > imtu ;
opts . omtu = l2cap_pi ( sk ) - > omtu ;
opts . flush_to = l2cap_pi ( sk ) - > flush_to ;
2009-05-03 09:31:10 +04:00
opts . mode = l2cap_pi ( sk ) - > mode ;
2009-08-21 05:26:02 +04:00
opts . fcs = l2cap_pi ( sk ) - > fcs ;
2010-05-01 23:15:41 +04:00
opts . max_tx = l2cap_pi ( sk ) - > max_tx ;
2010-05-01 23:15:40 +04:00
opts . txwin_size = ( __u16 ) l2cap_pi ( sk ) - > tx_win ;
2007-05-05 02:35:59 +04:00
2005-04-17 02:20:36 +04:00
len = min_t ( unsigned int , sizeof ( opts ) , optlen ) ;
if ( copy_from_user ( ( char * ) & opts , optval , len ) ) {
err = - EFAULT ;
break ;
}
2007-05-05 02:35:59 +04:00
2010-06-08 02:21:30 +04:00
if ( opts . txwin_size > L2CAP_DEFAULT_TX_WINDOW ) {
err = - EINVAL ;
break ;
}
2010-05-01 23:15:42 +04:00
l2cap_pi ( sk ) - > mode = opts . mode ;
switch ( l2cap_pi ( sk ) - > mode ) {
case L2CAP_MODE_BASIC :
2010-06-04 01:43:28 +04:00
l2cap_pi ( sk ) - > conf_state & = ~ L2CAP_CONF_STATE2_DEVICE ;
2010-05-01 23:15:42 +04:00
break ;
case L2CAP_MODE_ERTM :
case L2CAP_MODE_STREAMING :
2010-07-18 23:25:54 +04:00
if ( ! disable_ertm )
2010-05-01 23:15:42 +04:00
break ;
/* fall through */
default :
err = - EINVAL ;
break ;
}
2009-05-03 09:31:10 +04:00
l2cap_pi ( sk ) - > imtu = opts . imtu ;
l2cap_pi ( sk ) - > omtu = opts . omtu ;
2009-08-21 05:26:02 +04:00
l2cap_pi ( sk ) - > fcs = opts . fcs ;
2010-05-01 23:15:41 +04:00
l2cap_pi ( sk ) - > max_tx = opts . max_tx ;
2010-05-01 23:15:40 +04:00
l2cap_pi ( sk ) - > tx_win = ( __u8 ) opts . txwin_size ;
2005-04-17 02:20:36 +04:00
break ;
case L2CAP_LM :
if ( get_user ( opt , ( u32 __user * ) optval ) ) {
err = - EFAULT ;
break ;
}
2009-01-15 23:58:38 +03:00
if ( opt & L2CAP_LM_AUTH )
l2cap_pi ( sk ) - > sec_level = BT_SECURITY_LOW ;
if ( opt & L2CAP_LM_ENCRYPT )
l2cap_pi ( sk ) - > sec_level = BT_SECURITY_MEDIUM ;
if ( opt & L2CAP_LM_SECURE )
l2cap_pi ( sk ) - > sec_level = BT_SECURITY_HIGH ;
l2cap_pi ( sk ) - > role_switch = ( opt & L2CAP_LM_MASTER ) ;
l2cap_pi ( sk ) - > force_reliable = ( opt & L2CAP_LM_RELIABLE ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
err = - ENOPROTOOPT ;
break ;
}
release_sock ( sk ) ;
return err ;
}
2009-10-01 03:12:20 +04:00
static int l2cap_sock_setsockopt ( struct socket * sock , int level , int optname , char __user * optval , unsigned int optlen )
2009-01-15 23:52:14 +03:00
{
struct sock * sk = sock - > sk ;
2009-01-15 23:58:38 +03:00
struct bt_security sec ;
int len , err = 0 ;
2009-01-15 23:57:00 +03:00
u32 opt ;
2009-01-15 23:52:14 +03:00
BT_DBG ( " sk %p " , sk ) ;
if ( level = = SOL_L2CAP )
return l2cap_sock_setsockopt_old ( sock , optname , optval , optlen ) ;
2009-01-16 12:06:13 +03:00
if ( level ! = SOL_BLUETOOTH )
return - ENOPROTOOPT ;
2009-01-15 23:52:14 +03:00
lock_sock ( sk ) ;
switch ( optname ) {
2009-01-15 23:58:38 +03:00
case BT_SECURITY :
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type ! = SOCK_SEQPACKET & & sk - > sk_type ! = SOCK_STREAM
& & sk - > sk_type ! = SOCK_RAW ) {
2009-01-16 12:06:13 +03:00
err = - EINVAL ;
break ;
}
2009-01-15 23:58:38 +03:00
sec . level = BT_SECURITY_LOW ;
len = min_t ( unsigned int , sizeof ( sec ) , optlen ) ;
if ( copy_from_user ( ( char * ) & sec , optval , len ) ) {
err = - EFAULT ;
break ;
}
if ( sec . level < BT_SECURITY_LOW | |
sec . level > BT_SECURITY_HIGH ) {
err = - EINVAL ;
break ;
}
l2cap_pi ( sk ) - > sec_level = sec . level ;
break ;
2009-01-15 23:57:00 +03:00
case BT_DEFER_SETUP :
if ( sk - > sk_state ! = BT_BOUND & & sk - > sk_state ! = BT_LISTEN ) {
err = - EINVAL ;
break ;
}
if ( get_user ( opt , ( u32 __user * ) optval ) ) {
err = - EFAULT ;
break ;
}
bt_sk ( sk ) - > defer_setup = opt ;
break ;
2009-01-15 23:52:14 +03:00
default :
err = - ENOPROTOOPT ;
break ;
}
release_sock ( sk ) ;
return err ;
}
static int l2cap_sock_getsockopt_old ( struct socket * sock , int optname , char __user * optval , int __user * optlen )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sock - > sk ;
struct l2cap_options opts ;
struct l2cap_conninfo cinfo ;
int len , err = 0 ;
2009-01-15 23:58:38 +03:00
u32 opt ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " sk %p " , sk ) ;
if ( get_user ( len , optlen ) )
return - EFAULT ;
lock_sock ( sk ) ;
switch ( optname ) {
case L2CAP_OPTIONS :
opts . imtu = l2cap_pi ( sk ) - > imtu ;
opts . omtu = l2cap_pi ( sk ) - > omtu ;
opts . flush_to = l2cap_pi ( sk ) - > flush_to ;
2009-05-03 09:31:10 +04:00
opts . mode = l2cap_pi ( sk ) - > mode ;
2009-08-21 05:26:02 +04:00
opts . fcs = l2cap_pi ( sk ) - > fcs ;
2010-05-01 23:15:41 +04:00
opts . max_tx = l2cap_pi ( sk ) - > max_tx ;
2010-05-01 23:15:40 +04:00
opts . txwin_size = ( __u16 ) l2cap_pi ( sk ) - > tx_win ;
2005-04-17 02:20:36 +04:00
len = min_t ( unsigned int , len , sizeof ( opts ) ) ;
if ( copy_to_user ( optval , ( char * ) & opts , len ) )
err = - EFAULT ;
break ;
case L2CAP_LM :
2009-01-15 23:58:38 +03:00
switch ( l2cap_pi ( sk ) - > sec_level ) {
case BT_SECURITY_LOW :
opt = L2CAP_LM_AUTH ;
break ;
case BT_SECURITY_MEDIUM :
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT ;
break ;
case BT_SECURITY_HIGH :
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
L2CAP_LM_SECURE ;
break ;
default :
opt = 0 ;
break ;
}
if ( l2cap_pi ( sk ) - > role_switch )
opt | = L2CAP_LM_MASTER ;
if ( l2cap_pi ( sk ) - > force_reliable )
opt | = L2CAP_LM_RELIABLE ;
if ( put_user ( opt , ( u32 __user * ) optval ) )
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
break ;
case L2CAP_CONNINFO :
2009-01-15 23:57:00 +03:00
if ( sk - > sk_state ! = BT_CONNECTED & &
! ( sk - > sk_state = = BT_CONNECT2 & &
bt_sk ( sk ) - > defer_setup ) ) {
2005-04-17 02:20:36 +04:00
err = - ENOTCONN ;
break ;
}
cinfo . hci_handle = l2cap_pi ( sk ) - > conn - > hcon - > handle ;
memcpy ( cinfo . dev_class , l2cap_pi ( sk ) - > conn - > hcon - > dev_class , 3 ) ;
len = min_t ( unsigned int , len , sizeof ( cinfo ) ) ;
if ( copy_to_user ( optval , ( char * ) & cinfo , len ) )
err = - EFAULT ;
break ;
default :
err = - ENOPROTOOPT ;
break ;
}
release_sock ( sk ) ;
return err ;
}
2009-01-15 23:52:14 +03:00
static int l2cap_sock_getsockopt ( struct socket * sock , int level , int optname , char __user * optval , int __user * optlen )
{
struct sock * sk = sock - > sk ;
2009-01-15 23:58:38 +03:00
struct bt_security sec ;
2009-01-15 23:52:14 +03:00
int len , err = 0 ;
BT_DBG ( " sk %p " , sk ) ;
if ( level = = SOL_L2CAP )
return l2cap_sock_getsockopt_old ( sock , optname , optval , optlen ) ;
2009-01-16 12:06:13 +03:00
if ( level ! = SOL_BLUETOOTH )
return - ENOPROTOOPT ;
2009-01-15 23:52:14 +03:00
if ( get_user ( len , optlen ) )
return - EFAULT ;
lock_sock ( sk ) ;
switch ( optname ) {
2009-01-15 23:58:38 +03:00
case BT_SECURITY :
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type ! = SOCK_SEQPACKET & & sk - > sk_type ! = SOCK_STREAM
& & sk - > sk_type ! = SOCK_RAW ) {
2009-01-16 12:06:13 +03:00
err = - EINVAL ;
break ;
}
2009-01-15 23:58:38 +03:00
sec . level = l2cap_pi ( sk ) - > sec_level ;
len = min_t ( unsigned int , len , sizeof ( sec ) ) ;
if ( copy_to_user ( optval , ( char * ) & sec , len ) )
err = - EFAULT ;
break ;
2009-01-15 23:57:00 +03:00
case BT_DEFER_SETUP :
if ( sk - > sk_state ! = BT_BOUND & & sk - > sk_state ! = BT_LISTEN ) {
err = - EINVAL ;
break ;
}
if ( put_user ( bt_sk ( sk ) - > defer_setup , ( u32 __user * ) optval ) )
err = - EFAULT ;
break ;
2009-01-15 23:52:14 +03:00
default :
err = - ENOPROTOOPT ;
break ;
}
release_sock ( sk ) ;
return err ;
}
2005-04-17 02:20:36 +04:00
static int l2cap_sock_shutdown ( struct socket * sock , int how )
{
struct sock * sk = sock - > sk ;
int err = 0 ;
BT_DBG ( " sock %p, sk %p " , sock , sk ) ;
if ( ! sk )
return 0 ;
lock_sock ( sk ) ;
if ( ! sk - > sk_shutdown ) {
2010-05-01 23:15:44 +04:00
if ( l2cap_pi ( sk ) - > mode = = L2CAP_MODE_ERTM )
err = __l2cap_wait_ack ( sk ) ;
2005-04-17 02:20:36 +04:00
sk - > sk_shutdown = SHUTDOWN_MASK ;
l2cap_sock_clear_timer ( sk ) ;
__l2cap_sock_close ( sk , 0 ) ;
if ( sock_flag ( sk , SOCK_LINGER ) & & sk - > sk_lingertime )
2008-07-14 22:13:54 +04:00
err = bt_sock_wait_state ( sk , BT_CLOSED ,
sk - > sk_lingertime ) ;
2005-04-17 02:20:36 +04:00
}
2010-05-20 23:21:53 +04:00
if ( ! err & & sk - > sk_err )
err = - sk - > sk_err ;
2005-04-17 02:20:36 +04:00
release_sock ( sk ) ;
return err ;
}
static int l2cap_sock_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
int err ;
BT_DBG ( " sock %p, sk %p " , sock , sk ) ;
if ( ! sk )
return 0 ;
err = l2cap_sock_shutdown ( sock , 2 ) ;
sock_orphan ( sk ) ;
l2cap_sock_kill ( sk ) ;
return err ;
}
static void l2cap_chan_ready ( struct sock * sk )
{
struct sock * parent = bt_sk ( sk ) - > parent ;
BT_DBG ( " sk %p, parent %p " , sk , parent ) ;
l2cap_pi ( sk ) - > conf_state = 0 ;
l2cap_sock_clear_timer ( sk ) ;
if ( ! parent ) {
/* Outgoing channel.
* Wake up socket sleeping on connect .
*/
sk - > sk_state = BT_CONNECTED ;
sk - > sk_state_change ( sk ) ;
} else {
/* Incoming channel.
* Wake up socket sleeping on accept .
*/
parent - > sk_data_ready ( parent , 0 ) ;
}
}
/* Copy frame to all raw sockets on that connection */
static void l2cap_raw_recv ( struct l2cap_conn * conn , struct sk_buff * skb )
{
struct l2cap_chan_list * l = & conn - > chan_list ;
struct sk_buff * nskb ;
2009-04-20 08:31:08 +04:00
struct sock * sk ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " conn %p " , conn ) ;
read_lock ( & l - > lock ) ;
for ( sk = l - > head ; sk ; sk = l2cap_pi ( sk ) - > next_c ) {
if ( sk - > sk_type ! = SOCK_RAW )
continue ;
/* Don't send frame to the socket it came from */
if ( skb - > sk = = sk )
continue ;
2009-04-20 08:31:08 +04:00
nskb = skb_clone ( skb , GFP_ATOMIC ) ;
if ( ! nskb )
2005-04-17 02:20:36 +04:00
continue ;
if ( sock_queue_rcv_skb ( sk , nskb ) )
kfree_skb ( nskb ) ;
}
read_unlock ( & l - > lock ) ;
}
/* ---- L2CAP signalling commands ---- */
static struct sk_buff * l2cap_build_cmd ( struct l2cap_conn * conn ,
u8 code , u8 ident , u16 dlen , void * data )
{
struct sk_buff * skb , * * frag ;
struct l2cap_cmd_hdr * cmd ;
struct l2cap_hdr * lh ;
int len , count ;
2009-04-20 08:31:08 +04:00
BT_DBG ( " conn %p, code 0x%2.2x, ident 0x%2.2x, len %d " ,
conn , code , ident , dlen ) ;
2005-04-17 02:20:36 +04:00
len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen ;
count = min_t ( unsigned int , conn - > mtu , len ) ;
skb = bt_skb_alloc ( count , GFP_ATOMIC ) ;
if ( ! skb )
return NULL ;
lh = ( struct l2cap_hdr * ) skb_put ( skb , L2CAP_HDR_SIZE ) ;
2007-03-26 07:12:50 +04:00
lh - > len = cpu_to_le16 ( L2CAP_CMD_HDR_SIZE + dlen ) ;
2009-04-20 08:31:05 +04:00
lh - > cid = cpu_to_le16 ( L2CAP_CID_SIGNALING ) ;
2005-04-17 02:20:36 +04:00
cmd = ( struct l2cap_cmd_hdr * ) skb_put ( skb , L2CAP_CMD_HDR_SIZE ) ;
cmd - > code = code ;
cmd - > ident = ident ;
2007-03-26 07:12:50 +04:00
cmd - > len = cpu_to_le16 ( dlen ) ;
2005-04-17 02:20:36 +04:00
if ( dlen ) {
count - = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE ;
memcpy ( skb_put ( skb , count ) , data , count ) ;
data + = count ;
}
len - = skb - > len ;
/* Continuation fragments (no L2CAP header) */
frag = & skb_shinfo ( skb ) - > frag_list ;
while ( len ) {
count = min_t ( unsigned int , conn - > mtu , len ) ;
* frag = bt_skb_alloc ( count , GFP_ATOMIC ) ;
if ( ! * frag )
goto fail ;
memcpy ( skb_put ( * frag , count ) , data , count ) ;
len - = count ;
data + = count ;
frag = & ( * frag ) - > next ;
}
return skb ;
fail :
kfree_skb ( skb ) ;
return NULL ;
}
static inline int l2cap_get_conf_opt ( void * * ptr , int * type , int * olen , unsigned long * val )
{
struct l2cap_conf_opt * opt = * ptr ;
int len ;
len = L2CAP_CONF_OPT_SIZE + opt - > len ;
* ptr + = len ;
* type = opt - > type ;
* olen = opt - > len ;
switch ( opt - > len ) {
case 1 :
* val = * ( ( u8 * ) opt - > val ) ;
break ;
case 2 :
2007-10-20 15:37:06 +04:00
* val = __le16_to_cpu ( * ( ( __le16 * ) opt - > val ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case 4 :
2007-10-20 15:37:06 +04:00
* val = __le32_to_cpu ( * ( ( __le32 * ) opt - > val ) ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
* val = ( unsigned long ) opt - > val ;
break ;
}
BT_DBG ( " type 0x%2.2x len %d val 0x%lx " , * type , opt - > len , * val ) ;
return len ;
}
static void l2cap_add_conf_opt ( void * * ptr , u8 type , u8 len , unsigned long val )
{
struct l2cap_conf_opt * opt = * ptr ;
BT_DBG ( " type 0x%2.2x len %d val 0x%lx " , type , len , val ) ;
opt - > type = type ;
opt - > len = len ;
switch ( len ) {
case 1 :
* ( ( u8 * ) opt - > val ) = val ;
break ;
case 2 :
2007-07-29 11:16:36 +04:00
* ( ( __le16 * ) opt - > val ) = cpu_to_le16 ( val ) ;
2005-04-17 02:20:36 +04:00
break ;
case 4 :
2007-07-29 11:16:36 +04:00
* ( ( __le32 * ) opt - > val ) = cpu_to_le32 ( val ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
memcpy ( opt - > val , ( void * ) val , len ) ;
break ;
}
* ptr + = L2CAP_CONF_OPT_SIZE + len ;
}
2010-05-01 23:15:39 +04:00
static void l2cap_ack_timeout ( unsigned long arg )
{
struct sock * sk = ( void * ) arg ;
bh_lock_sock ( sk ) ;
l2cap_send_ack ( l2cap_pi ( sk ) ) ;
bh_unlock_sock ( sk ) ;
}
2009-10-03 09:34:36 +04:00
static inline void l2cap_ertm_init ( struct sock * sk )
{
l2cap_pi ( sk ) - > expected_ack_seq = 0 ;
l2cap_pi ( sk ) - > unacked_frames = 0 ;
l2cap_pi ( sk ) - > buffer_seq = 0 ;
2010-05-01 23:15:41 +04:00
l2cap_pi ( sk ) - > num_acked = 0 ;
2010-05-01 23:15:36 +04:00
l2cap_pi ( sk ) - > frames_sent = 0 ;
2009-10-03 09:34:36 +04:00
setup_timer ( & l2cap_pi ( sk ) - > retrans_timer ,
l2cap_retrans_timeout , ( unsigned long ) sk ) ;
setup_timer ( & l2cap_pi ( sk ) - > monitor_timer ,
l2cap_monitor_timeout , ( unsigned long ) sk ) ;
2010-05-01 23:15:39 +04:00
setup_timer ( & l2cap_pi ( sk ) - > ack_timer ,
l2cap_ack_timeout , ( unsigned long ) sk ) ;
2009-10-03 09:34:36 +04:00
__skb_queue_head_init ( SREJ_QUEUE ( sk ) ) ;
2010-05-01 23:15:44 +04:00
__skb_queue_head_init ( BUSY_QUEUE ( sk ) ) ;
INIT_WORK ( & l2cap_pi ( sk ) - > busy_work , l2cap_busy_work ) ;
2010-06-22 01:53:22 +04:00
sk - > sk_backlog_rcv = l2cap_ertm_data_rcv ;
2009-10-03 09:34:36 +04:00
}
2009-07-04 22:06:24 +04:00
static inline __u8 l2cap_select_mode ( __u8 mode , __u16 remote_feat_mask )
{
switch ( mode ) {
case L2CAP_MODE_STREAMING :
case L2CAP_MODE_ERTM :
if ( l2cap_mode_supported ( mode , remote_feat_mask ) )
return mode ;
/* fall through */
default :
return L2CAP_MODE_BASIC ;
}
}
2005-04-17 02:20:36 +04:00
static int l2cap_build_conf_req ( struct sock * sk , void * data )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct l2cap_conf_req * req = data ;
2010-05-01 23:15:42 +04:00
struct l2cap_conf_rfc rfc = { . mode = pi - > mode } ;
2005-04-17 02:20:36 +04:00
void * ptr = req - > data ;
BT_DBG ( " sk %p " , sk ) ;
2009-07-04 22:06:24 +04:00
if ( pi - > num_conf_req | | pi - > num_conf_rsp )
goto done ;
switch ( pi - > mode ) {
case L2CAP_MODE_STREAMING :
case L2CAP_MODE_ERTM :
2010-06-09 23:39:05 +04:00
if ( pi - > conf_state & L2CAP_CONF_STATE2_DEVICE )
2010-06-04 01:43:28 +04:00
break ;
2010-06-09 23:39:05 +04:00
/* fall through */
2009-07-04 22:06:24 +04:00
default :
pi - > mode = l2cap_select_mode ( rfc . mode , pi - > conn - > feat_mask ) ;
break ;
}
done :
2009-05-03 10:07:53 +04:00
switch ( pi - > mode ) {
case L2CAP_MODE_BASIC :
if ( pi - > imtu ! = L2CAP_DEFAULT_MTU )
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_MTU , 2 , pi - > imtu ) ;
2010-06-09 03:05:31 +04:00
2010-08-04 06:49:29 +04:00
if ( ! ( pi - > conn - > feat_mask & L2CAP_FEAT_ERTM ) & &
! ( pi - > conn - > feat_mask & L2CAP_FEAT_STREAMING ) )
break ;
2010-06-09 03:05:31 +04:00
rfc . mode = L2CAP_MODE_BASIC ;
rfc . txwin_size = 0 ;
rfc . max_transmit = 0 ;
rfc . retrans_timeout = 0 ;
rfc . monitor_timeout = 0 ;
rfc . max_pdu_size = 0 ;
2010-08-04 06:49:29 +04:00
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_RFC , sizeof ( rfc ) ,
( unsigned long ) & rfc ) ;
2009-05-03 10:07:53 +04:00
break ;
case L2CAP_MODE_ERTM :
rfc . mode = L2CAP_MODE_ERTM ;
2010-05-01 23:15:40 +04:00
rfc . txwin_size = pi - > tx_win ;
2010-05-01 23:15:41 +04:00
rfc . max_transmit = pi - > max_tx ;
2009-07-04 22:06:24 +04:00
rfc . retrans_timeout = 0 ;
rfc . monitor_timeout = 0 ;
2009-08-21 05:25:58 +04:00
rfc . max_pdu_size = cpu_to_le16 ( L2CAP_DEFAULT_MAX_PDU_SIZE ) ;
2010-05-01 23:15:36 +04:00
if ( L2CAP_DEFAULT_MAX_PDU_SIZE > pi - > conn - > mtu - 10 )
2010-05-01 23:15:40 +04:00
rfc . max_pdu_size = cpu_to_le16 ( pi - > conn - > mtu - 10 ) ;
2009-07-04 22:06:24 +04:00
2010-08-04 06:49:29 +04:00
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_RFC , sizeof ( rfc ) ,
( unsigned long ) & rfc ) ;
2009-08-21 05:26:02 +04:00
if ( ! ( pi - > conn - > feat_mask & L2CAP_FEAT_FCS ) )
break ;
if ( pi - > fcs = = L2CAP_FCS_NONE | |
pi - > conf_state & L2CAP_CONF_NO_FCS_RECV ) {
pi - > fcs = L2CAP_FCS_NONE ;
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_FCS , 1 , pi - > fcs ) ;
}
2009-07-04 22:06:24 +04:00
break ;
case L2CAP_MODE_STREAMING :
rfc . mode = L2CAP_MODE_STREAMING ;
rfc . txwin_size = 0 ;
rfc . max_transmit = 0 ;
rfc . retrans_timeout = 0 ;
rfc . monitor_timeout = 0 ;
2009-08-21 05:25:58 +04:00
rfc . max_pdu_size = cpu_to_le16 ( L2CAP_DEFAULT_MAX_PDU_SIZE ) ;
2010-05-01 23:15:36 +04:00
if ( L2CAP_DEFAULT_MAX_PDU_SIZE > pi - > conn - > mtu - 10 )
2010-05-01 23:15:40 +04:00
rfc . max_pdu_size = cpu_to_le16 ( pi - > conn - > mtu - 10 ) ;
2009-05-03 10:07:53 +04:00
2010-08-04 06:49:29 +04:00
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_RFC , sizeof ( rfc ) ,
( unsigned long ) & rfc ) ;
2009-08-21 05:26:02 +04:00
if ( ! ( pi - > conn - > feat_mask & L2CAP_FEAT_FCS ) )
break ;
if ( pi - > fcs = = L2CAP_FCS_NONE | |
pi - > conf_state & L2CAP_CONF_NO_FCS_RECV ) {
pi - > fcs = L2CAP_FCS_NONE ;
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_FCS , 1 , pi - > fcs ) ;
}
2009-05-03 10:07:53 +04:00
break ;
}
2005-04-17 02:20:36 +04:00
/* FIXME: Need actual value of the flush timeout */
//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
// l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
2007-03-26 07:12:50 +04:00
req - > dcid = cpu_to_le16 ( pi - > dcid ) ;
req - > flags = cpu_to_le16 ( 0 ) ;
2005-04-17 02:20:36 +04:00
return ptr - data ;
}
2007-05-24 16:27:19 +04:00
static int l2cap_parse_conf_req ( struct sock * sk , void * data )
2005-04-17 02:20:36 +04:00
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
2007-05-24 16:27:19 +04:00
struct l2cap_conf_rsp * rsp = data ;
void * ptr = rsp - > data ;
void * req = pi - > conf_req ;
int len = pi - > conf_len ;
int type , hint , olen ;
unsigned long val ;
2007-10-20 15:39:51 +04:00
struct l2cap_conf_rfc rfc = { . mode = L2CAP_MODE_BASIC } ;
2007-10-20 15:37:06 +04:00
u16 mtu = L2CAP_DEFAULT_MTU ;
2007-05-24 16:27:19 +04:00
u16 result = L2CAP_CONF_SUCCESS ;
2005-04-17 02:20:36 +04:00
2007-05-24 16:27:19 +04:00
BT_DBG ( " sk %p " , sk ) ;
2006-11-19 00:15:00 +03:00
2007-05-24 16:27:19 +04:00
while ( len > = L2CAP_CONF_OPT_SIZE ) {
len - = l2cap_get_conf_opt ( & req , & type , & olen , & val ) ;
2005-04-17 02:20:36 +04:00
2009-04-20 08:31:07 +04:00
hint = type & L2CAP_CONF_HINT ;
2009-05-03 05:57:55 +04:00
type & = L2CAP_CONF_MASK ;
2007-05-24 16:27:19 +04:00
switch ( type ) {
case L2CAP_CONF_MTU :
2007-10-20 15:37:06 +04:00
mtu = val ;
2007-05-24 16:27:19 +04:00
break ;
case L2CAP_CONF_FLUSH_TO :
pi - > flush_to = val ;
break ;
case L2CAP_CONF_QOS :
break ;
2007-10-20 15:39:51 +04:00
case L2CAP_CONF_RFC :
if ( olen = = sizeof ( rfc ) )
memcpy ( & rfc , ( void * ) val , olen ) ;
break ;
2009-08-21 05:26:02 +04:00
case L2CAP_CONF_FCS :
if ( val = = L2CAP_FCS_NONE )
pi - > conf_state | = L2CAP_CONF_NO_FCS_RECV ;
break ;
2007-05-24 16:27:19 +04:00
default :
if ( hint )
break ;
result = L2CAP_CONF_UNKNOWN ;
* ( ( u8 * ) ptr + + ) = type ;
break ;
}
}
2010-07-09 03:08:18 +04:00
if ( pi - > num_conf_rsp | | pi - > num_conf_req > 1 )
2009-07-04 22:06:24 +04:00
goto done ;
switch ( pi - > mode ) {
case L2CAP_MODE_STREAMING :
case L2CAP_MODE_ERTM :
2010-06-04 01:43:28 +04:00
if ( ! ( pi - > conf_state & L2CAP_CONF_STATE2_DEVICE ) ) {
pi - > mode = l2cap_select_mode ( rfc . mode ,
pi - > conn - > feat_mask ) ;
break ;
}
2010-06-09 02:09:48 +04:00
if ( pi - > mode ! = rfc . mode )
2009-07-04 22:06:24 +04:00
return - ECONNREFUSED ;
2010-06-09 02:09:48 +04:00
2009-07-04 22:06:24 +04:00
break ;
}
done :
if ( pi - > mode ! = rfc . mode ) {
result = L2CAP_CONF_UNACCEPT ;
rfc . mode = pi - > mode ;
if ( pi - > num_conf_rsp = = 1 )
return - ECONNREFUSED ;
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_RFC ,
sizeof ( rfc ) , ( unsigned long ) & rfc ) ;
}
2007-05-24 16:27:19 +04:00
if ( result = = L2CAP_CONF_SUCCESS ) {
/* Configure output options and let the other side know
* which ones we don ' t like . */
2009-07-04 22:06:24 +04:00
if ( mtu < L2CAP_DEFAULT_MIN_MTU )
result = L2CAP_CONF_UNACCEPT ;
else {
pi - > omtu = mtu ;
pi - > conf_state | = L2CAP_CONF_MTU_DONE ;
}
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_MTU , 2 , pi - > omtu ) ;
2007-10-20 15:39:51 +04:00
2009-07-04 22:06:24 +04:00
switch ( rfc . mode ) {
case L2CAP_MODE_BASIC :
pi - > fcs = L2CAP_FCS_NONE ;
pi - > conf_state | = L2CAP_CONF_MODE_DONE ;
break ;
case L2CAP_MODE_ERTM :
pi - > remote_tx_win = rfc . txwin_size ;
pi - > remote_max_tx = rfc . max_transmit ;
2010-08-06 02:54:22 +04:00
if ( le16_to_cpu ( rfc . max_pdu_size ) > pi - > conn - > mtu - 10 )
rfc . max_pdu_size = cpu_to_le16 ( pi - > conn - > mtu - 10 ) ;
2010-05-01 23:15:40 +04:00
pi - > remote_mps = le16_to_cpu ( rfc . max_pdu_size ) ;
2009-07-04 22:06:24 +04:00
2010-05-01 23:15:40 +04:00
rfc . retrans_timeout =
le16_to_cpu ( L2CAP_DEFAULT_RETRANS_TO ) ;
rfc . monitor_timeout =
le16_to_cpu ( L2CAP_DEFAULT_MONITOR_TO ) ;
2009-07-04 22:06:24 +04:00
pi - > conf_state | = L2CAP_CONF_MODE_DONE ;
2009-10-18 04:41:01 +04:00
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_RFC ,
sizeof ( rfc ) , ( unsigned long ) & rfc ) ;
2009-07-04 22:06:24 +04:00
break ;
case L2CAP_MODE_STREAMING :
2010-08-06 02:54:22 +04:00
if ( le16_to_cpu ( rfc . max_pdu_size ) > pi - > conn - > mtu - 10 )
rfc . max_pdu_size = cpu_to_le16 ( pi - > conn - > mtu - 10 ) ;
2010-05-01 23:15:40 +04:00
pi - > remote_mps = le16_to_cpu ( rfc . max_pdu_size ) ;
2009-07-04 22:06:24 +04:00
pi - > conf_state | = L2CAP_CONF_MODE_DONE ;
2009-10-18 04:41:01 +04:00
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_RFC ,
sizeof ( rfc ) , ( unsigned long ) & rfc ) ;
2009-07-04 22:06:24 +04:00
break ;
default :
2007-05-24 16:27:19 +04:00
result = L2CAP_CONF_UNACCEPT ;
2007-10-20 15:39:51 +04:00
memset ( & rfc , 0 , sizeof ( rfc ) ) ;
2009-07-04 22:06:24 +04:00
rfc . mode = pi - > mode ;
}
2007-10-20 15:39:51 +04:00
2009-07-04 22:06:24 +04:00
if ( result = = L2CAP_CONF_SUCCESS )
pi - > conf_state | = L2CAP_CONF_OUTPUT_DONE ;
}
2007-05-24 16:27:19 +04:00
rsp - > scid = cpu_to_le16 ( pi - > dcid ) ;
rsp - > result = cpu_to_le16 ( result ) ;
rsp - > flags = cpu_to_le16 ( 0x0000 ) ;
return ptr - data ;
2005-04-17 02:20:36 +04:00
}
2009-07-04 22:06:24 +04:00
static int l2cap_parse_conf_rsp ( struct sock * sk , void * rsp , int len , void * data , u16 * result )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct l2cap_conf_req * req = data ;
void * ptr = req - > data ;
int type , olen ;
unsigned long val ;
struct l2cap_conf_rfc rfc ;
BT_DBG ( " sk %p, rsp %p, len %d, req %p " , sk , rsp , len , data ) ;
while ( len > = L2CAP_CONF_OPT_SIZE ) {
len - = l2cap_get_conf_opt ( & rsp , & type , & olen , & val ) ;
switch ( type ) {
case L2CAP_CONF_MTU :
if ( val < L2CAP_DEFAULT_MIN_MTU ) {
* result = L2CAP_CONF_UNACCEPT ;
2010-09-01 16:17:25 +04:00
pi - > imtu = L2CAP_DEFAULT_MIN_MTU ;
2009-07-04 22:06:24 +04:00
} else
2010-09-01 16:17:25 +04:00
pi - > imtu = val ;
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_MTU , 2 , pi - > imtu ) ;
2009-07-04 22:06:24 +04:00
break ;
case L2CAP_CONF_FLUSH_TO :
pi - > flush_to = val ;
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_FLUSH_TO ,
2 , pi - > flush_to ) ;
break ;
case L2CAP_CONF_RFC :
if ( olen = = sizeof ( rfc ) )
memcpy ( & rfc , ( void * ) val , olen ) ;
if ( ( pi - > conf_state & L2CAP_CONF_STATE2_DEVICE ) & &
rfc . mode ! = pi - > mode )
return - ECONNREFUSED ;
pi - > fcs = 0 ;
l2cap_add_conf_opt ( & ptr , L2CAP_CONF_RFC ,
sizeof ( rfc ) , ( unsigned long ) & rfc ) ;
break ;
}
}
2010-06-09 03:08:49 +04:00
if ( pi - > mode = = L2CAP_MODE_BASIC & & pi - > mode ! = rfc . mode )
return - ECONNREFUSED ;
pi - > mode = rfc . mode ;
2009-07-04 22:06:24 +04:00
if ( * result = = L2CAP_CONF_SUCCESS ) {
switch ( rfc . mode ) {
case L2CAP_MODE_ERTM :
2010-05-01 23:15:40 +04:00
pi - > retrans_timeout = le16_to_cpu ( rfc . retrans_timeout ) ;
pi - > monitor_timeout = le16_to_cpu ( rfc . monitor_timeout ) ;
2010-05-01 23:15:40 +04:00
pi - > mps = le16_to_cpu ( rfc . max_pdu_size ) ;
2009-07-04 22:06:24 +04:00
break ;
case L2CAP_MODE_STREAMING :
2010-05-01 23:15:40 +04:00
pi - > mps = le16_to_cpu ( rfc . max_pdu_size ) ;
2009-07-04 22:06:24 +04:00
}
}
req - > dcid = cpu_to_le16 ( pi - > dcid ) ;
req - > flags = cpu_to_le16 ( 0x0000 ) ;
return ptr - data ;
}
2007-05-24 16:27:19 +04:00
static int l2cap_build_conf_rsp ( struct sock * sk , void * data , u16 result , u16 flags )
2005-04-17 02:20:36 +04:00
{
struct l2cap_conf_rsp * rsp = data ;
void * ptr = rsp - > data ;
2007-05-24 16:27:19 +04:00
BT_DBG ( " sk %p " , sk ) ;
2005-04-17 02:20:36 +04:00
2007-03-26 07:12:50 +04:00
rsp - > scid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
2007-05-24 16:27:19 +04:00
rsp - > result = cpu_to_le16 ( result ) ;
2007-03-26 07:12:50 +04:00
rsp - > flags = cpu_to_le16 ( flags ) ;
2005-04-17 02:20:36 +04:00
return ptr - data ;
}
2010-05-01 23:15:39 +04:00
static void l2cap_conf_rfc_get ( struct sock * sk , void * rsp , int len )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
int type , olen ;
unsigned long val ;
struct l2cap_conf_rfc rfc ;
BT_DBG ( " sk %p, rsp %p, len %d " , sk , rsp , len ) ;
if ( ( pi - > mode ! = L2CAP_MODE_ERTM ) & & ( pi - > mode ! = L2CAP_MODE_STREAMING ) )
return ;
while ( len > = L2CAP_CONF_OPT_SIZE ) {
len - = l2cap_get_conf_opt ( & rsp , & type , & olen , & val ) ;
switch ( type ) {
case L2CAP_CONF_RFC :
if ( olen = = sizeof ( rfc ) )
memcpy ( & rfc , ( void * ) val , olen ) ;
goto done ;
}
}
done :
switch ( rfc . mode ) {
case L2CAP_MODE_ERTM :
2010-05-01 23:15:40 +04:00
pi - > retrans_timeout = le16_to_cpu ( rfc . retrans_timeout ) ;
pi - > monitor_timeout = le16_to_cpu ( rfc . monitor_timeout ) ;
2010-05-01 23:15:39 +04:00
pi - > mps = le16_to_cpu ( rfc . max_pdu_size ) ;
break ;
case L2CAP_MODE_STREAMING :
pi - > mps = le16_to_cpu ( rfc . max_pdu_size ) ;
}
}
2007-10-20 15:37:56 +04:00
static inline int l2cap_command_rej ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u8 * data )
{
struct l2cap_cmd_rej * rej = ( struct l2cap_cmd_rej * ) data ;
if ( rej - > reason ! = 0x0000 )
return 0 ;
if ( ( conn - > info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT ) & &
cmd - > ident = = conn - > info_ident ) {
del_timer ( & conn - > info_timer ) ;
2009-02-07 01:35:19 +03:00
conn - > info_state | = L2CAP_INFO_FEAT_MASK_REQ_DONE ;
2009-02-09 11:18:02 +03:00
conn - > info_ident = 0 ;
2009-02-07 01:35:19 +03:00
2007-10-20 15:37:56 +04:00
l2cap_conn_start ( conn ) ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
static inline int l2cap_connect_req ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u8 * data )
{
struct l2cap_chan_list * list = & conn - > chan_list ;
struct l2cap_conn_req * req = ( struct l2cap_conn_req * ) data ;
struct l2cap_conn_rsp rsp ;
2010-07-09 03:08:18 +04:00
struct sock * parent , * uninitialized_var ( sk ) ;
2008-09-09 09:19:20 +04:00
int result , status = L2CAP_CS_NO_INFO ;
2005-04-17 02:20:36 +04:00
u16 dcid = 0 , scid = __le16_to_cpu ( req - > scid ) ;
2008-09-09 09:19:20 +04:00
__le16 psm = req - > psm ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " psm 0x%2.2x scid 0x%4.4x " , psm , scid ) ;
/* Check if we have socket listening on psm */
parent = l2cap_get_sock_by_psm ( BT_LISTEN , psm , conn - > src ) ;
if ( ! parent ) {
result = L2CAP_CR_BAD_PSM ;
goto sendresp ;
}
2008-09-09 09:19:20 +04:00
/* Check if the ACL is secure enough (if not SDP) */
if ( psm ! = cpu_to_le16 ( 0x0001 ) & &
! hci_conn_check_link_mode ( conn - > hcon ) ) {
2009-02-12 16:02:50 +03:00
conn - > disc_reason = 0x05 ;
2008-09-09 09:19:20 +04:00
result = L2CAP_CR_SEC_BLOCK ;
goto response ;
}
2005-04-17 02:20:36 +04:00
result = L2CAP_CR_NO_MEM ;
/* Check for backlog size */
if ( sk_acceptq_is_full ( parent ) ) {
2007-02-09 17:24:33 +03:00
BT_DBG ( " backlog full %d " , parent - > sk_ack_backlog ) ;
2005-04-17 02:20:36 +04:00
goto response ;
}
2008-03-25 20:26:21 +03:00
sk = l2cap_sock_alloc ( sock_net ( parent ) , NULL , BTPROTO_L2CAP , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! sk )
goto response ;
2006-07-13 01:00:07 +04:00
write_lock_bh ( & list - > lock ) ;
2005-04-17 02:20:36 +04:00
/* Check if we already have channel with that dcid */
if ( __l2cap_get_chan_by_dcid ( list , scid ) ) {
2006-07-13 01:00:07 +04:00
write_unlock_bh ( & list - > lock ) ;
2005-04-17 02:20:36 +04:00
sock_set_flag ( sk , SOCK_ZAPPED ) ;
l2cap_sock_kill ( sk ) ;
goto response ;
}
hci_conn_hold ( conn - > hcon ) ;
l2cap_sock_init ( sk , parent ) ;
bacpy ( & bt_sk ( sk ) - > src , conn - > src ) ;
bacpy ( & bt_sk ( sk ) - > dst , conn - > dst ) ;
l2cap_pi ( sk ) - > psm = psm ;
l2cap_pi ( sk ) - > dcid = scid ;
__l2cap_chan_add ( conn , sk , parent ) ;
dcid = l2cap_pi ( sk ) - > scid ;
l2cap_sock_set_timer ( sk , sk - > sk_sndtimeo ) ;
l2cap_pi ( sk ) - > ident = cmd - > ident ;
2009-02-07 01:35:19 +03:00
if ( conn - > info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE ) {
2009-01-15 23:58:38 +03:00
if ( l2cap_check_security ( sk ) ) {
2009-01-15 23:57:00 +03:00
if ( bt_sk ( sk ) - > defer_setup ) {
sk - > sk_state = BT_CONNECT2 ;
result = L2CAP_CR_PEND ;
status = L2CAP_CS_AUTHOR_PEND ;
parent - > sk_data_ready ( parent , 0 ) ;
} else {
sk - > sk_state = BT_CONFIG ;
result = L2CAP_CR_SUCCESS ;
status = L2CAP_CS_NO_INFO ;
}
2008-07-14 22:13:44 +04:00
} else {
sk - > sk_state = BT_CONNECT2 ;
result = L2CAP_CR_PEND ;
status = L2CAP_CS_AUTHEN_PEND ;
}
} else {
sk - > sk_state = BT_CONNECT2 ;
result = L2CAP_CR_PEND ;
status = L2CAP_CS_NO_INFO ;
2005-04-17 02:20:36 +04:00
}
2006-07-13 01:00:07 +04:00
write_unlock_bh ( & list - > lock ) ;
2005-04-17 02:20:36 +04:00
response :
bh_unlock_sock ( parent ) ;
sendresp :
2007-03-26 07:12:50 +04:00
rsp . scid = cpu_to_le16 ( scid ) ;
rsp . dcid = cpu_to_le16 ( dcid ) ;
rsp . result = cpu_to_le16 ( result ) ;
rsp . status = cpu_to_le16 ( status ) ;
2005-04-17 02:20:36 +04:00
l2cap_send_cmd ( conn , cmd - > ident , L2CAP_CONN_RSP , sizeof ( rsp ) , & rsp ) ;
2008-07-14 22:13:44 +04:00
if ( result = = L2CAP_CR_PEND & & status = = L2CAP_CS_NO_INFO ) {
struct l2cap_info_req info ;
info . type = cpu_to_le16 ( L2CAP_IT_FEAT_MASK ) ;
conn - > info_state | = L2CAP_INFO_FEAT_MASK_REQ_SENT ;
conn - > info_ident = l2cap_get_ident ( conn ) ;
mod_timer ( & conn - > info_timer , jiffies +
msecs_to_jiffies ( L2CAP_INFO_TIMEOUT ) ) ;
l2cap_send_cmd ( conn , conn - > info_ident ,
L2CAP_INFO_REQ , sizeof ( info ) , & info ) ;
}
2010-07-09 03:08:18 +04:00
if ( ! ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_REQ_SENT ) & &
result = = L2CAP_CR_SUCCESS ) {
u8 buf [ 128 ] ;
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_REQ_SENT ;
l2cap_send_cmd ( conn , l2cap_get_ident ( conn ) , L2CAP_CONF_REQ ,
l2cap_build_conf_req ( sk , buf ) , buf ) ;
l2cap_pi ( sk ) - > num_conf_req + + ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
static inline int l2cap_connect_rsp ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u8 * data )
{
struct l2cap_conn_rsp * rsp = ( struct l2cap_conn_rsp * ) data ;
u16 scid , dcid , result , status ;
struct sock * sk ;
u8 req [ 128 ] ;
scid = __le16_to_cpu ( rsp - > scid ) ;
dcid = __le16_to_cpu ( rsp - > dcid ) ;
result = __le16_to_cpu ( rsp - > result ) ;
status = __le16_to_cpu ( rsp - > status ) ;
BT_DBG ( " dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x " , dcid , scid , result , status ) ;
if ( scid ) {
2009-04-20 08:31:08 +04:00
sk = l2cap_get_chan_by_scid ( & conn - > chan_list , scid ) ;
if ( ! sk )
2010-06-22 20:56:26 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
} else {
2009-04-20 08:31:08 +04:00
sk = l2cap_get_chan_by_ident ( & conn - > chan_list , cmd - > ident ) ;
if ( ! sk )
2010-06-22 20:56:26 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}
switch ( result ) {
case L2CAP_CR_SUCCESS :
sk - > sk_state = BT_CONFIG ;
l2cap_pi ( sk ) - > ident = 0 ;
l2cap_pi ( sk ) - > dcid = dcid ;
2009-02-07 01:56:36 +03:00
l2cap_pi ( sk ) - > conf_state & = ~ L2CAP_CONF_CONNECT_PEND ;
2010-07-09 03:08:18 +04:00
if ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_REQ_SENT )
break ;
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_REQ_SENT ;
2005-04-17 02:20:36 +04:00
l2cap_send_cmd ( conn , l2cap_get_ident ( conn ) , L2CAP_CONF_REQ ,
l2cap_build_conf_req ( sk , req ) , req ) ;
2009-07-04 22:06:24 +04:00
l2cap_pi ( sk ) - > num_conf_req + + ;
2005-04-17 02:20:36 +04:00
break ;
case L2CAP_CR_PEND :
2009-02-07 01:56:36 +03:00
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_CONNECT_PEND ;
2005-04-17 02:20:36 +04:00
break ;
default :
l2cap_chan_del ( sk , ECONNREFUSED ) ;
break ;
}
bh_unlock_sock ( sk ) ;
return 0 ;
}
2010-08-25 02:35:42 +04:00
static inline void set_default_fcs ( struct l2cap_pinfo * pi )
{
/* FCS is enabled only in ERTM or streaming mode, if one or both
* sides request it .
*/
if ( pi - > mode ! = L2CAP_MODE_ERTM & & pi - > mode ! = L2CAP_MODE_STREAMING )
pi - > fcs = L2CAP_FCS_NONE ;
else if ( ! ( pi - > conf_state & L2CAP_CONF_NO_FCS_RECV ) )
pi - > fcs = L2CAP_FCS_CRC16 ;
}
2007-07-29 11:17:25 +04:00
static inline int l2cap_config_req ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u16 cmd_len , u8 * data )
2005-04-17 02:20:36 +04:00
{
struct l2cap_conf_req * req = ( struct l2cap_conf_req * ) data ;
u16 dcid , flags ;
u8 rsp [ 64 ] ;
struct sock * sk ;
2007-05-24 16:27:19 +04:00
int len ;
2005-04-17 02:20:36 +04:00
dcid = __le16_to_cpu ( req - > dcid ) ;
flags = __le16_to_cpu ( req - > flags ) ;
BT_DBG ( " dcid 0x%4.4x flags 0x%2.2x " , dcid , flags ) ;
2009-04-20 08:31:08 +04:00
sk = l2cap_get_chan_by_scid ( & conn - > chan_list , dcid ) ;
if ( ! sk )
2005-04-17 02:20:36 +04:00
return - ENOENT ;
2010-09-08 21:59:44 +04:00
if ( sk - > sk_state = = BT_DISCONN )
2006-11-19 00:15:20 +03:00
goto unlock ;
2007-05-24 16:27:19 +04:00
/* Reject if config buffer is too small. */
2007-07-29 11:17:25 +04:00
len = cmd_len - sizeof ( * req ) ;
2007-05-24 16:27:19 +04:00
if ( l2cap_pi ( sk ) - > conf_len + len > sizeof ( l2cap_pi ( sk ) - > conf_req ) ) {
l2cap_send_cmd ( conn , cmd - > ident , L2CAP_CONF_RSP ,
l2cap_build_conf_rsp ( sk , rsp ,
L2CAP_CONF_REJECT , flags ) , rsp ) ;
goto unlock ;
}
/* Store config. */
memcpy ( l2cap_pi ( sk ) - > conf_req + l2cap_pi ( sk ) - > conf_len , req - > data , len ) ;
l2cap_pi ( sk ) - > conf_len + = len ;
2005-04-17 02:20:36 +04:00
if ( flags & 0x0001 ) {
/* Incomplete config. Send empty response. */
l2cap_send_cmd ( conn , cmd - > ident , L2CAP_CONF_RSP ,
2007-05-24 16:27:19 +04:00
l2cap_build_conf_rsp ( sk , rsp ,
L2CAP_CONF_SUCCESS , 0x0001 ) , rsp ) ;
2005-04-17 02:20:36 +04:00
goto unlock ;
}
/* Complete config. */
2007-05-24 16:27:19 +04:00
len = l2cap_parse_conf_req ( sk , rsp ) ;
2009-07-04 22:06:24 +04:00
if ( len < 0 ) {
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( conn , sk , ECONNRESET ) ;
2005-04-17 02:20:36 +04:00
goto unlock ;
2009-07-04 22:06:24 +04:00
}
2005-04-17 02:20:36 +04:00
2007-05-24 16:27:19 +04:00
l2cap_send_cmd ( conn , cmd - > ident , L2CAP_CONF_RSP , len , rsp ) ;
2009-07-04 22:06:24 +04:00
l2cap_pi ( sk ) - > num_conf_rsp + + ;
2007-05-24 16:27:19 +04:00
/* Reset config buffer. */
l2cap_pi ( sk ) - > conf_len = 0 ;
2007-10-20 15:35:42 +04:00
if ( ! ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_OUTPUT_DONE ) )
goto unlock ;
2005-04-17 02:20:36 +04:00
if ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_INPUT_DONE ) {
2010-08-25 02:35:42 +04:00
set_default_fcs ( l2cap_pi ( sk ) ) ;
2009-08-21 05:26:02 +04:00
2005-04-17 02:20:36 +04:00
sk - > sk_state = BT_CONNECTED ;
2009-08-21 05:26:00 +04:00
2009-10-03 09:34:36 +04:00
l2cap_pi ( sk ) - > next_tx_seq = 0 ;
l2cap_pi ( sk ) - > expected_tx_seq = 0 ;
2009-08-21 05:25:57 +04:00
__skb_queue_head_init ( TX_QUEUE ( sk ) ) ;
2009-10-03 09:34:36 +04:00
if ( l2cap_pi ( sk ) - > mode = = L2CAP_MODE_ERTM )
l2cap_ertm_init ( sk ) ;
2005-04-17 02:20:36 +04:00
l2cap_chan_ready ( sk ) ;
2007-10-20 15:35:42 +04:00
goto unlock ;
}
if ( ! ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_REQ_SENT ) ) {
2008-07-14 22:13:44 +04:00
u8 buf [ 64 ] ;
2005-04-17 02:20:36 +04:00
l2cap_send_cmd ( conn , l2cap_get_ident ( conn ) , L2CAP_CONF_REQ ,
2008-07-14 22:13:44 +04:00
l2cap_build_conf_req ( sk , buf ) , buf ) ;
2009-07-04 22:06:24 +04:00
l2cap_pi ( sk ) - > num_conf_req + + ;
2005-04-17 02:20:36 +04:00
}
unlock :
bh_unlock_sock ( sk ) ;
return 0 ;
}
static inline int l2cap_config_rsp ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u8 * data )
{
struct l2cap_conf_rsp * rsp = ( struct l2cap_conf_rsp * ) data ;
u16 scid , flags , result ;
struct sock * sk ;
2010-05-01 23:15:39 +04:00
int len = cmd - > len - sizeof ( * rsp ) ;
2005-04-17 02:20:36 +04:00
scid = __le16_to_cpu ( rsp - > scid ) ;
flags = __le16_to_cpu ( rsp - > flags ) ;
result = __le16_to_cpu ( rsp - > result ) ;
2009-04-20 08:31:08 +04:00
BT_DBG ( " scid 0x%4.4x flags 0x%2.2x result 0x%2.2x " ,
scid , flags , result ) ;
2005-04-17 02:20:36 +04:00
2009-04-20 08:31:08 +04:00
sk = l2cap_get_chan_by_scid ( & conn - > chan_list , scid ) ;
if ( ! sk )
2005-04-17 02:20:36 +04:00
return 0 ;
switch ( result ) {
case L2CAP_CONF_SUCCESS :
2010-05-01 23:15:39 +04:00
l2cap_conf_rfc_get ( sk , rsp - > data , len ) ;
2005-04-17 02:20:36 +04:00
break ;
case L2CAP_CONF_UNACCEPT :
2009-07-04 22:06:24 +04:00
if ( l2cap_pi ( sk ) - > num_conf_rsp < = L2CAP_CONF_MAX_CONF_RSP ) {
char req [ 64 ] ;
2010-03-19 11:26:28 +03:00
if ( len > sizeof ( req ) - sizeof ( struct l2cap_conf_req ) ) {
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( conn , sk , ECONNRESET ) ;
2010-03-19 11:26:28 +03:00
goto done ;
}
2009-07-04 22:06:24 +04:00
/* throw out any old stored conf requests */
result = L2CAP_CONF_SUCCESS ;
len = l2cap_parse_conf_rsp ( sk , rsp - > data ,
len , req , & result ) ;
if ( len < 0 ) {
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( conn , sk , ECONNRESET ) ;
2009-07-04 22:06:24 +04:00
goto done ;
}
l2cap_send_cmd ( conn , l2cap_get_ident ( conn ) ,
L2CAP_CONF_REQ , len , req ) ;
l2cap_pi ( sk ) - > num_conf_req + + ;
if ( result ! = L2CAP_CONF_SUCCESS )
goto done ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 17:24:33 +03:00
default :
2008-07-14 22:13:54 +04:00
sk - > sk_err = ECONNRESET ;
2005-04-17 02:20:36 +04:00
l2cap_sock_set_timer ( sk , HZ * 5 ) ;
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( conn , sk , ECONNRESET ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
if ( flags & 0x01 )
goto done ;
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_INPUT_DONE ;
if ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_OUTPUT_DONE ) {
2010-08-25 02:35:42 +04:00
set_default_fcs ( l2cap_pi ( sk ) ) ;
2009-08-21 05:26:02 +04:00
2005-04-17 02:20:36 +04:00
sk - > sk_state = BT_CONNECTED ;
2009-10-03 09:34:36 +04:00
l2cap_pi ( sk ) - > next_tx_seq = 0 ;
2009-08-21 05:25:57 +04:00
l2cap_pi ( sk ) - > expected_tx_seq = 0 ;
__skb_queue_head_init ( TX_QUEUE ( sk ) ) ;
2009-10-03 09:34:36 +04:00
if ( l2cap_pi ( sk ) - > mode = = L2CAP_MODE_ERTM )
l2cap_ertm_init ( sk ) ;
2005-04-17 02:20:36 +04:00
l2cap_chan_ready ( sk ) ;
}
done :
bh_unlock_sock ( sk ) ;
return 0 ;
}
static inline int l2cap_disconnect_req ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u8 * data )
{
struct l2cap_disconn_req * req = ( struct l2cap_disconn_req * ) data ;
struct l2cap_disconn_rsp rsp ;
u16 dcid , scid ;
struct sock * sk ;
scid = __le16_to_cpu ( req - > scid ) ;
dcid = __le16_to_cpu ( req - > dcid ) ;
BT_DBG ( " scid 0x%4.4x dcid 0x%4.4x " , scid , dcid ) ;
2009-04-20 08:31:08 +04:00
sk = l2cap_get_chan_by_scid ( & conn - > chan_list , dcid ) ;
if ( ! sk )
2005-04-17 02:20:36 +04:00
return 0 ;
2007-03-26 07:12:50 +04:00
rsp . dcid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
rsp . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
2005-04-17 02:20:36 +04:00
l2cap_send_cmd ( conn , cmd - > ident , L2CAP_DISCONN_RSP , sizeof ( rsp ) , & rsp ) ;
sk - > sk_shutdown = SHUTDOWN_MASK ;
l2cap_chan_del ( sk , ECONNRESET ) ;
bh_unlock_sock ( sk ) ;
l2cap_sock_kill ( sk ) ;
return 0 ;
}
static inline int l2cap_disconnect_rsp ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u8 * data )
{
struct l2cap_disconn_rsp * rsp = ( struct l2cap_disconn_rsp * ) data ;
u16 dcid , scid ;
struct sock * sk ;
scid = __le16_to_cpu ( rsp - > scid ) ;
dcid = __le16_to_cpu ( rsp - > dcid ) ;
BT_DBG ( " dcid 0x%4.4x scid 0x%4.4x " , dcid , scid ) ;
2009-04-20 08:31:08 +04:00
sk = l2cap_get_chan_by_scid ( & conn - > chan_list , scid ) ;
if ( ! sk )
2005-04-17 02:20:36 +04:00
return 0 ;
l2cap_chan_del ( sk , 0 ) ;
bh_unlock_sock ( sk ) ;
l2cap_sock_kill ( sk ) ;
return 0 ;
}
static inline int l2cap_information_req ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u8 * data )
{
struct l2cap_info_req * req = ( struct l2cap_info_req * ) data ;
u16 type ;
type = __le16_to_cpu ( req - > type ) ;
BT_DBG ( " type 0x%4.4x " , type ) ;
2007-10-20 15:38:51 +04:00
if ( type = = L2CAP_IT_FEAT_MASK ) {
u8 buf [ 8 ] ;
2009-05-03 06:09:01 +04:00
u32 feat_mask = l2cap_feat_mask ;
2007-10-20 15:38:51 +04:00
struct l2cap_info_rsp * rsp = ( struct l2cap_info_rsp * ) buf ;
rsp - > type = cpu_to_le16 ( L2CAP_IT_FEAT_MASK ) ;
rsp - > result = cpu_to_le16 ( L2CAP_IR_SUCCESS ) ;
2010-07-18 23:25:54 +04:00
if ( ! disable_ertm )
2009-08-21 05:26:02 +04:00
feat_mask | = L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
| L2CAP_FEAT_FCS ;
2009-08-24 07:45:20 +04:00
put_unaligned_le32 ( feat_mask , rsp - > data ) ;
2007-10-20 15:38:51 +04:00
l2cap_send_cmd ( conn , cmd - > ident ,
L2CAP_INFO_RSP , sizeof ( buf ) , buf ) ;
2009-02-09 11:18:02 +03:00
} else if ( type = = L2CAP_IT_FIXED_CHAN ) {
u8 buf [ 12 ] ;
struct l2cap_info_rsp * rsp = ( struct l2cap_info_rsp * ) buf ;
rsp - > type = cpu_to_le16 ( L2CAP_IT_FIXED_CHAN ) ;
rsp - > result = cpu_to_le16 ( L2CAP_IR_SUCCESS ) ;
memcpy ( buf + 4 , l2cap_fixed_chan , 8 ) ;
l2cap_send_cmd ( conn , cmd - > ident ,
L2CAP_INFO_RSP , sizeof ( buf ) , buf ) ;
2007-10-20 15:38:51 +04:00
} else {
struct l2cap_info_rsp rsp ;
rsp . type = cpu_to_le16 ( type ) ;
rsp . result = cpu_to_le16 ( L2CAP_IR_NOTSUPP ) ;
l2cap_send_cmd ( conn , cmd - > ident ,
L2CAP_INFO_RSP , sizeof ( rsp ) , & rsp ) ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
static inline int l2cap_information_rsp ( struct l2cap_conn * conn , struct l2cap_cmd_hdr * cmd , u8 * data )
{
struct l2cap_info_rsp * rsp = ( struct l2cap_info_rsp * ) data ;
u16 type , result ;
type = __le16_to_cpu ( rsp - > type ) ;
result = __le16_to_cpu ( rsp - > result ) ;
BT_DBG ( " type 0x%4.4x result 0x%2.2x " , type , result ) ;
2007-10-20 15:37:56 +04:00
del_timer ( & conn - > info_timer ) ;
2010-08-04 10:43:33 +04:00
if ( result ! = L2CAP_IR_SUCCESS ) {
conn - > info_state | = L2CAP_INFO_FEAT_MASK_REQ_DONE ;
conn - > info_ident = 0 ;
l2cap_conn_start ( conn ) ;
return 0 ;
}
2009-02-07 01:35:19 +03:00
if ( type = = L2CAP_IT_FEAT_MASK ) {
2008-05-03 03:25:46 +04:00
conn - > feat_mask = get_unaligned_le32 ( rsp - > data ) ;
2007-10-20 15:37:56 +04:00
2009-05-03 05:57:55 +04:00
if ( conn - > feat_mask & L2CAP_FEAT_FIXED_CHAN ) {
2009-02-09 11:18:02 +03:00
struct l2cap_info_req req ;
req . type = cpu_to_le16 ( L2CAP_IT_FIXED_CHAN ) ;
conn - > info_ident = l2cap_get_ident ( conn ) ;
l2cap_send_cmd ( conn , conn - > info_ident ,
L2CAP_INFO_REQ , sizeof ( req ) , & req ) ;
} else {
conn - > info_state | = L2CAP_INFO_FEAT_MASK_REQ_DONE ;
conn - > info_ident = 0 ;
l2cap_conn_start ( conn ) ;
}
} else if ( type = = L2CAP_IT_FIXED_CHAN ) {
2009-02-07 01:35:19 +03:00
conn - > info_state | = L2CAP_INFO_FEAT_MASK_REQ_DONE ;
2009-02-09 11:18:02 +03:00
conn - > info_ident = 0 ;
2009-02-07 01:35:19 +03:00
l2cap_conn_start ( conn ) ;
}
2007-10-20 15:37:56 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
static inline void l2cap_sig_channel ( struct l2cap_conn * conn , struct sk_buff * skb )
{
u8 * data = skb - > data ;
int len = skb - > len ;
struct l2cap_cmd_hdr cmd ;
int err = 0 ;
l2cap_raw_recv ( conn , skb ) ;
while ( len > = L2CAP_CMD_HDR_SIZE ) {
2007-07-29 11:17:25 +04:00
u16 cmd_len ;
2005-04-17 02:20:36 +04:00
memcpy ( & cmd , data , L2CAP_CMD_HDR_SIZE ) ;
data + = L2CAP_CMD_HDR_SIZE ;
len - = L2CAP_CMD_HDR_SIZE ;
2007-07-29 11:17:25 +04:00
cmd_len = le16_to_cpu ( cmd . len ) ;
2005-04-17 02:20:36 +04:00
2007-07-29 11:17:25 +04:00
BT_DBG ( " code 0x%2.2x len %d id 0x%2.2x " , cmd . code , cmd_len , cmd . ident ) ;
2005-04-17 02:20:36 +04:00
2007-07-29 11:17:25 +04:00
if ( cmd_len > len | | ! cmd . ident ) {
2005-04-17 02:20:36 +04:00
BT_DBG ( " corrupted command " ) ;
break ;
}
switch ( cmd . code ) {
case L2CAP_COMMAND_REJ :
2007-10-20 15:37:56 +04:00
l2cap_command_rej ( conn , & cmd , data ) ;
2005-04-17 02:20:36 +04:00
break ;
case L2CAP_CONN_REQ :
err = l2cap_connect_req ( conn , & cmd , data ) ;
break ;
case L2CAP_CONN_RSP :
err = l2cap_connect_rsp ( conn , & cmd , data ) ;
break ;
case L2CAP_CONF_REQ :
2007-07-29 11:17:25 +04:00
err = l2cap_config_req ( conn , & cmd , cmd_len , data ) ;
2005-04-17 02:20:36 +04:00
break ;
case L2CAP_CONF_RSP :
err = l2cap_config_rsp ( conn , & cmd , data ) ;
break ;
case L2CAP_DISCONN_REQ :
err = l2cap_disconnect_req ( conn , & cmd , data ) ;
break ;
case L2CAP_DISCONN_RSP :
err = l2cap_disconnect_rsp ( conn , & cmd , data ) ;
break ;
case L2CAP_ECHO_REQ :
2007-07-29 11:17:25 +04:00
l2cap_send_cmd ( conn , cmd . ident , L2CAP_ECHO_RSP , cmd_len , data ) ;
2005-04-17 02:20:36 +04:00
break ;
case L2CAP_ECHO_RSP :
break ;
case L2CAP_INFO_REQ :
err = l2cap_information_req ( conn , & cmd , data ) ;
break ;
case L2CAP_INFO_RSP :
err = l2cap_information_rsp ( conn , & cmd , data ) ;
break ;
default :
BT_ERR ( " Unknown signaling command 0x%2.2x " , cmd . code ) ;
err = - EINVAL ;
break ;
}
if ( err ) {
struct l2cap_cmd_rej rej ;
BT_DBG ( " error %d " , err ) ;
/* FIXME: Map err to a valid reason */
2007-03-26 07:12:50 +04:00
rej . reason = cpu_to_le16 ( 0 ) ;
2005-04-17 02:20:36 +04:00
l2cap_send_cmd ( conn , cmd . ident , L2CAP_COMMAND_REJ , sizeof ( rej ) , & rej ) ;
}
2007-07-29 11:17:25 +04:00
data + = cmd_len ;
len - = cmd_len ;
2005-04-17 02:20:36 +04:00
}
kfree_skb ( skb ) ;
}
2009-08-21 05:26:02 +04:00
static int l2cap_check_fcs ( struct l2cap_pinfo * pi , struct sk_buff * skb )
{
u16 our_fcs , rcv_fcs ;
int hdr_size = L2CAP_HDR_SIZE + 2 ;
if ( pi - > fcs = = L2CAP_FCS_CRC16 ) {
skb_trim ( skb , skb - > len - 2 ) ;
rcv_fcs = get_unaligned_le16 ( skb - > data + skb - > len ) ;
our_fcs = crc16 ( 0 , skb - > data - hdr_size , skb - > len + hdr_size ) ;
if ( our_fcs ! = rcv_fcs )
2010-06-22 20:56:27 +04:00
return - EBADMSG ;
2009-08-21 05:26:02 +04:00
}
return 0 ;
}
2010-05-01 23:15:36 +04:00
static inline void l2cap_send_i_or_rr_or_rnr ( struct sock * sk )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
u16 control = 0 ;
pi - > frames_sent = 0 ;
control | = pi - > buffer_seq < < L2CAP_CTRL_REQSEQ_SHIFT ;
if ( pi - > conn_state & L2CAP_CONN_LOCAL_BUSY ) {
2010-05-10 21:54:14 +04:00
control | = L2CAP_SUPER_RCV_NOT_READY ;
2010-05-01 23:15:36 +04:00
l2cap_send_sframe ( pi , control ) ;
2010-05-01 23:15:44 +04:00
pi - > conn_state | = L2CAP_CONN_RNR_SENT ;
2010-05-01 23:15:36 +04:00
}
2010-06-03 23:34:20 +04:00
if ( pi - > conn_state & L2CAP_CONN_REMOTE_BUSY )
l2cap_retransmit_frames ( sk ) ;
2010-05-01 23:15:36 +04:00
l2cap_ertm_send ( sk ) ;
if ( ! ( pi - > conn_state & L2CAP_CONN_LOCAL_BUSY ) & &
pi - > frames_sent = = 0 ) {
control | = L2CAP_SUPER_RCV_READY ;
l2cap_send_sframe ( pi , control ) ;
}
}
2010-05-01 23:15:44 +04:00
static int l2cap_add_to_srej_queue ( struct sock * sk , struct sk_buff * skb , u8 tx_seq , u8 sar )
2009-08-21 05:26:03 +04:00
{
struct sk_buff * next_skb ;
2010-06-01 01:35:44 +04:00
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
int tx_seq_offset , next_tx_seq_offset ;
2009-08-21 05:26:03 +04:00
bt_cb ( skb ) - > tx_seq = tx_seq ;
bt_cb ( skb ) - > sar = sar ;
next_skb = skb_peek ( SREJ_QUEUE ( sk ) ) ;
if ( ! next_skb ) {
__skb_queue_tail ( SREJ_QUEUE ( sk ) , skb ) ;
2010-05-01 23:15:44 +04:00
return 0 ;
2009-08-21 05:26:03 +04:00
}
2010-06-01 01:35:44 +04:00
tx_seq_offset = ( tx_seq - pi - > buffer_seq ) % 64 ;
if ( tx_seq_offset < 0 )
tx_seq_offset + = 64 ;
2009-08-21 05:26:03 +04:00
do {
2010-05-01 23:15:44 +04:00
if ( bt_cb ( next_skb ) - > tx_seq = = tx_seq )
return - EINVAL ;
2010-06-01 01:35:44 +04:00
next_tx_seq_offset = ( bt_cb ( next_skb ) - > tx_seq -
pi - > buffer_seq ) % 64 ;
if ( next_tx_seq_offset < 0 )
next_tx_seq_offset + = 64 ;
if ( next_tx_seq_offset > tx_seq_offset ) {
2009-08-21 05:26:03 +04:00
__skb_queue_before ( SREJ_QUEUE ( sk ) , next_skb , skb ) ;
2010-05-01 23:15:44 +04:00
return 0 ;
2009-08-21 05:26:03 +04:00
}
if ( skb_queue_is_last ( SREJ_QUEUE ( sk ) , next_skb ) )
break ;
2010-05-01 23:15:43 +04:00
} while ( ( next_skb = skb_queue_next ( SREJ_QUEUE ( sk ) , next_skb ) ) ) ;
2009-08-21 05:26:03 +04:00
__skb_queue_tail ( SREJ_QUEUE ( sk ) , skb ) ;
2010-05-01 23:15:44 +04:00
return 0 ;
2009-08-21 05:26:03 +04:00
}
2010-05-01 23:15:44 +04:00
static int l2cap_ertm_reassembly_sdu ( struct sock * sk , struct sk_buff * skb , u16 control )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct sk_buff * _skb ;
2010-05-01 23:15:44 +04:00
int err ;
2010-05-01 23:15:44 +04:00
switch ( control & L2CAP_CTRL_SAR ) {
case L2CAP_SDU_UNSEGMENTED :
if ( pi - > conn_state & L2CAP_CONN_SAR_SDU )
goto drop ;
err = sock_queue_rcv_skb ( sk , skb ) ;
if ( ! err )
return err ;
break ;
case L2CAP_SDU_START :
if ( pi - > conn_state & L2CAP_CONN_SAR_SDU )
goto drop ;
pi - > sdu_len = get_unaligned_le16 ( skb - > data ) ;
if ( pi - > sdu_len > pi - > imtu )
goto disconnect ;
pi - > sdu = bt_skb_alloc ( pi - > sdu_len , GFP_ATOMIC ) ;
2010-05-01 23:15:44 +04:00
if ( ! pi - > sdu )
return - ENOMEM ;
/* pull sdu_len bytes only after alloc, because of Local Busy
* condition we have to be sure that this will be executed
* only once , i . e . , when alloc does not fail */
skb_pull ( skb , 2 ) ;
2010-05-01 23:15:44 +04:00
memcpy ( skb_put ( pi - > sdu , skb - > len ) , skb - > data , skb - > len ) ;
pi - > conn_state | = L2CAP_CONN_SAR_SDU ;
pi - > partial_sdu_len = skb - > len ;
break ;
case L2CAP_SDU_CONTINUE :
if ( ! ( pi - > conn_state & L2CAP_CONN_SAR_SDU ) )
goto disconnect ;
if ( ! pi - > sdu )
goto disconnect ;
pi - > partial_sdu_len + = skb - > len ;
if ( pi - > partial_sdu_len > pi - > sdu_len )
goto drop ;
2010-05-01 23:15:45 +04:00
memcpy ( skb_put ( pi - > sdu , skb - > len ) , skb - > data , skb - > len ) ;
2010-05-01 23:15:44 +04:00
break ;
case L2CAP_SDU_END :
if ( ! ( pi - > conn_state & L2CAP_CONN_SAR_SDU ) )
goto disconnect ;
if ( ! pi - > sdu )
goto disconnect ;
2010-05-01 23:15:44 +04:00
if ( ! ( pi - > conn_state & L2CAP_CONN_SAR_RETRY ) ) {
pi - > partial_sdu_len + = skb - > len ;
2010-05-01 23:15:44 +04:00
2010-05-01 23:15:44 +04:00
if ( pi - > partial_sdu_len > pi - > imtu )
goto drop ;
2010-05-01 23:15:44 +04:00
2010-05-01 23:15:44 +04:00
if ( pi - > partial_sdu_len ! = pi - > sdu_len )
goto drop ;
2010-05-01 23:15:45 +04:00
memcpy ( skb_put ( pi - > sdu , skb - > len ) , skb - > data , skb - > len ) ;
2010-05-01 23:15:44 +04:00
}
2010-05-01 23:15:44 +04:00
_skb = skb_clone ( pi - > sdu , GFP_ATOMIC ) ;
2010-05-01 23:15:44 +04:00
if ( ! _skb ) {
pi - > conn_state | = L2CAP_CONN_SAR_RETRY ;
return - ENOMEM ;
}
2010-05-01 23:15:44 +04:00
err = sock_queue_rcv_skb ( sk , _skb ) ;
2010-05-01 23:15:44 +04:00
if ( err < 0 ) {
2010-05-01 23:15:44 +04:00
kfree_skb ( _skb ) ;
2010-05-01 23:15:44 +04:00
pi - > conn_state | = L2CAP_CONN_SAR_RETRY ;
return err ;
}
pi - > conn_state & = ~ L2CAP_CONN_SAR_RETRY ;
pi - > conn_state & = ~ L2CAP_CONN_SAR_SDU ;
2010-05-01 23:15:44 +04:00
kfree_skb ( pi - > sdu ) ;
break ;
}
kfree_skb ( skb ) ;
2010-05-01 23:15:44 +04:00
return 0 ;
2010-05-01 23:15:44 +04:00
drop :
kfree_skb ( pi - > sdu ) ;
pi - > sdu = NULL ;
disconnect :
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( pi - > conn , sk , ECONNRESET ) ;
2010-05-01 23:15:44 +04:00
kfree_skb ( skb ) ;
return 0 ;
}
2010-06-22 02:39:50 +04:00
static int l2cap_try_push_rx_skb ( struct sock * sk )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct sk_buff * skb ;
u16 control ;
int err ;
while ( ( skb = skb_dequeue ( BUSY_QUEUE ( sk ) ) ) ) {
control = bt_cb ( skb ) - > sar < < L2CAP_CTRL_SAR_SHIFT ;
err = l2cap_ertm_reassembly_sdu ( sk , skb , control ) ;
if ( err < 0 ) {
skb_queue_head ( BUSY_QUEUE ( sk ) , skb ) ;
return - EBUSY ;
}
pi - > buffer_seq = ( pi - > buffer_seq + 1 ) % 64 ;
}
if ( ! ( pi - > conn_state & L2CAP_CONN_RNR_SENT ) )
goto done ;
control = pi - > buffer_seq < < L2CAP_CTRL_REQSEQ_SHIFT ;
control | = L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL ;
l2cap_send_sframe ( pi , control ) ;
l2cap_pi ( sk ) - > retry_count = 1 ;
del_timer ( & pi - > retrans_timer ) ;
__mod_monitor_timer ( ) ;
l2cap_pi ( sk ) - > conn_state | = L2CAP_CONN_WAIT_F ;
done :
pi - > conn_state & = ~ L2CAP_CONN_LOCAL_BUSY ;
pi - > conn_state & = ~ L2CAP_CONN_RNR_SENT ;
BT_DBG ( " sk %p, Exit local busy " , sk ) ;
return 0 ;
}
2010-05-01 23:15:44 +04:00
static void l2cap_busy_work ( struct work_struct * work )
{
DECLARE_WAITQUEUE ( wait , current ) ;
struct l2cap_pinfo * pi =
container_of ( work , struct l2cap_pinfo , busy_work ) ;
struct sock * sk = ( struct sock * ) pi ;
int n_tries = 0 , timeo = HZ / 5 , err ;
struct sk_buff * skb ;
lock_sock ( sk ) ;
2010-05-10 13:33:10 +04:00
add_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2010-05-01 23:15:44 +04:00
while ( ( skb = skb_peek ( BUSY_QUEUE ( sk ) ) ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( n_tries + + > L2CAP_LOCAL_BUSY_TRIES ) {
err = - EBUSY ;
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( pi - > conn , sk , EBUSY ) ;
2010-06-22 02:39:50 +04:00
break ;
2010-05-01 23:15:44 +04:00
}
if ( ! timeo )
timeo = HZ / 5 ;
if ( signal_pending ( current ) ) {
err = sock_intr_errno ( timeo ) ;
2010-06-22 02:39:50 +04:00
break ;
2010-05-01 23:15:44 +04:00
}
release_sock ( sk ) ;
timeo = schedule_timeout ( timeo ) ;
lock_sock ( sk ) ;
err = sock_error ( sk ) ;
if ( err )
2010-06-22 02:39:50 +04:00
break ;
2010-05-01 23:15:44 +04:00
2010-06-22 02:39:50 +04:00
if ( l2cap_try_push_rx_skb ( sk ) = = 0 )
2010-05-01 23:15:44 +04:00
break ;
}
set_current_state ( TASK_RUNNING ) ;
2010-05-10 13:33:10 +04:00
remove_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2010-05-01 23:15:44 +04:00
release_sock ( sk ) ;
}
static int l2cap_push_rx_skb ( struct sock * sk , struct sk_buff * skb , u16 control )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
int sctrl , err ;
if ( pi - > conn_state & L2CAP_CONN_LOCAL_BUSY ) {
bt_cb ( skb ) - > sar = control > > L2CAP_CTRL_SAR_SHIFT ;
__skb_queue_tail ( BUSY_QUEUE ( sk ) , skb ) ;
2010-06-22 02:39:50 +04:00
return l2cap_try_push_rx_skb ( sk ) ;
2010-05-01 23:15:44 +04:00
}
err = l2cap_ertm_reassembly_sdu ( sk , skb , control ) ;
if ( err > = 0 ) {
pi - > buffer_seq = ( pi - > buffer_seq + 1 ) % 64 ;
return err ;
}
/* Busy Condition */
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p, Enter local busy " , sk ) ;
2010-05-01 23:15:44 +04:00
pi - > conn_state | = L2CAP_CONN_LOCAL_BUSY ;
bt_cb ( skb ) - > sar = control > > L2CAP_CTRL_SAR_SHIFT ;
__skb_queue_tail ( BUSY_QUEUE ( sk ) , skb ) ;
sctrl = pi - > buffer_seq < < L2CAP_CTRL_REQSEQ_SHIFT ;
sctrl | = L2CAP_SUPER_RCV_NOT_READY ;
l2cap_send_sframe ( pi , sctrl ) ;
pi - > conn_state | = L2CAP_CONN_RNR_SENT ;
2010-05-13 01:32:04 +04:00
del_timer ( & pi - > ack_timer ) ;
2010-05-01 23:15:44 +04:00
queue_work ( _busy_wq , & pi - > busy_work ) ;
return err ;
}
2010-05-01 23:15:44 +04:00
static int l2cap_streaming_reassembly_sdu ( struct sock * sk , struct sk_buff * skb , u16 control )
2009-08-21 05:25:58 +04:00
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct sk_buff * _skb ;
int err = - EINVAL ;
2010-05-01 23:15:44 +04:00
/*
* TODO : We have to notify the userland if some data is lost with the
* Streaming Mode .
*/
2009-08-21 05:25:58 +04:00
switch ( control & L2CAP_CTRL_SAR ) {
case L2CAP_SDU_UNSEGMENTED :
if ( pi - > conn_state & L2CAP_CONN_SAR_SDU ) {
kfree_skb ( pi - > sdu ) ;
break ;
}
err = sock_queue_rcv_skb ( sk , skb ) ;
if ( ! err )
return 0 ;
break ;
case L2CAP_SDU_START :
if ( pi - > conn_state & L2CAP_CONN_SAR_SDU ) {
kfree_skb ( pi - > sdu ) ;
break ;
}
pi - > sdu_len = get_unaligned_le16 ( skb - > data ) ;
skb_pull ( skb , 2 ) ;
2010-05-01 23:15:40 +04:00
if ( pi - > sdu_len > pi - > imtu ) {
err = - EMSGSIZE ;
break ;
}
2009-08-21 05:25:58 +04:00
pi - > sdu = bt_skb_alloc ( pi - > sdu_len , GFP_ATOMIC ) ;
if ( ! pi - > sdu ) {
err = - ENOMEM ;
break ;
}
memcpy ( skb_put ( pi - > sdu , skb - > len ) , skb - > data , skb - > len ) ;
pi - > conn_state | = L2CAP_CONN_SAR_SDU ;
pi - > partial_sdu_len = skb - > len ;
err = 0 ;
break ;
case L2CAP_SDU_CONTINUE :
if ( ! ( pi - > conn_state & L2CAP_CONN_SAR_SDU ) )
break ;
memcpy ( skb_put ( pi - > sdu , skb - > len ) , skb - > data , skb - > len ) ;
pi - > partial_sdu_len + = skb - > len ;
if ( pi - > partial_sdu_len > pi - > sdu_len )
kfree_skb ( pi - > sdu ) ;
else
err = 0 ;
break ;
case L2CAP_SDU_END :
if ( ! ( pi - > conn_state & L2CAP_CONN_SAR_SDU ) )
break ;
memcpy ( skb_put ( pi - > sdu , skb - > len ) , skb - > data , skb - > len ) ;
pi - > conn_state & = ~ L2CAP_CONN_SAR_SDU ;
pi - > partial_sdu_len + = skb - > len ;
2010-05-01 23:15:37 +04:00
if ( pi - > partial_sdu_len > pi - > imtu )
goto drop ;
2009-08-21 05:25:58 +04:00
if ( pi - > partial_sdu_len = = pi - > sdu_len ) {
_skb = skb_clone ( pi - > sdu , GFP_ATOMIC ) ;
err = sock_queue_rcv_skb ( sk , _skb ) ;
if ( err < 0 )
kfree_skb ( _skb ) ;
}
err = 0 ;
2010-05-01 23:15:37 +04:00
drop :
kfree_skb ( pi - > sdu ) ;
2009-08-21 05:25:58 +04:00
break ;
}
kfree_skb ( skb ) ;
return err ;
}
2009-08-21 05:26:03 +04:00
static void l2cap_check_srej_gap ( struct sock * sk , u8 tx_seq )
{
struct sk_buff * skb ;
2010-05-01 23:15:43 +04:00
u16 control ;
2009-08-21 05:26:03 +04:00
2010-05-01 23:15:43 +04:00
while ( ( skb = skb_peek ( SREJ_QUEUE ( sk ) ) ) ) {
2009-08-21 05:26:03 +04:00
if ( bt_cb ( skb ) - > tx_seq ! = tx_seq )
break ;
skb = skb_dequeue ( SREJ_QUEUE ( sk ) ) ;
2010-05-01 23:15:43 +04:00
control = bt_cb ( skb ) - > sar < < L2CAP_CTRL_SAR_SHIFT ;
2010-05-01 23:15:44 +04:00
l2cap_ertm_reassembly_sdu ( sk , skb , control ) ;
2009-08-21 05:26:03 +04:00
l2cap_pi ( sk ) - > buffer_seq_srej =
( l2cap_pi ( sk ) - > buffer_seq_srej + 1 ) % 64 ;
2010-05-11 02:34:11 +04:00
tx_seq = ( tx_seq + 1 ) % 64 ;
2009-08-21 05:26:03 +04:00
}
}
static void l2cap_resend_srejframe ( struct sock * sk , u8 tx_seq )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct srej_list * l , * tmp ;
u16 control ;
2010-05-01 23:15:43 +04:00
list_for_each_entry_safe ( l , tmp , SREJ_LIST ( sk ) , list ) {
2009-08-21 05:26:03 +04:00
if ( l - > tx_seq = = tx_seq ) {
list_del ( & l - > list ) ;
kfree ( l ) ;
return ;
}
control = L2CAP_SUPER_SELECT_REJECT ;
control | = l - > tx_seq < < L2CAP_CTRL_REQSEQ_SHIFT ;
l2cap_send_sframe ( pi , control ) ;
list_del ( & l - > list ) ;
list_add_tail ( & l - > list , SREJ_LIST ( sk ) ) ;
}
}
static void l2cap_send_srejframe ( struct sock * sk , u8 tx_seq )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
struct srej_list * new ;
u16 control ;
while ( tx_seq ! = pi - > expected_tx_seq ) {
control = L2CAP_SUPER_SELECT_REJECT ;
control | = pi - > expected_tx_seq < < L2CAP_CTRL_REQSEQ_SHIFT ;
l2cap_send_sframe ( pi , control ) ;
new = kzalloc ( sizeof ( struct srej_list ) , GFP_ATOMIC ) ;
2010-05-11 02:34:11 +04:00
new - > tx_seq = pi - > expected_tx_seq ;
pi - > expected_tx_seq = ( pi - > expected_tx_seq + 1 ) % 64 ;
2009-08-21 05:26:03 +04:00
list_add_tail ( & new - > list , SREJ_LIST ( sk ) ) ;
}
2010-05-11 02:34:11 +04:00
pi - > expected_tx_seq = ( pi - > expected_tx_seq + 1 ) % 64 ;
2009-08-21 05:26:03 +04:00
}
2009-08-21 05:25:57 +04:00
static inline int l2cap_data_channel_iframe ( struct sock * sk , u16 rx_control , struct sk_buff * skb )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
u8 tx_seq = __get_txseq ( rx_control ) ;
2009-10-03 09:34:38 +04:00
u8 req_seq = __get_reqseq ( rx_control ) ;
2009-08-21 05:26:03 +04:00
u8 sar = rx_control > > L2CAP_CTRL_SAR_SHIFT ;
2010-05-11 01:32:04 +04:00
int tx_seq_offset , expected_tx_seq_offset ;
2010-05-01 23:15:41 +04:00
int num_to_ack = ( pi - > tx_win / 6 ) + 1 ;
2009-08-21 05:25:57 +04:00
int err = 0 ;
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p len %d tx_seq %d rx_control 0x%4.4x " , sk , skb - > len , tx_seq ,
rx_control ) ;
2009-08-21 05:25:57 +04:00
2010-05-06 03:05:57 +04:00
if ( L2CAP_CTRL_FINAL & rx_control & &
l2cap_pi ( sk ) - > conn_state & L2CAP_CONN_WAIT_F ) {
2010-05-01 23:15:37 +04:00
del_timer ( & pi - > monitor_timer ) ;
if ( pi - > unacked_frames > 0 )
__mod_retrans_timer ( ) ;
pi - > conn_state & = ~ L2CAP_CONN_WAIT_F ;
}
2009-10-03 09:34:38 +04:00
pi - > expected_ack_seq = req_seq ;
l2cap_drop_acked_frames ( sk ) ;
2009-08-21 05:26:03 +04:00
if ( tx_seq = = pi - > expected_tx_seq )
goto expected ;
2009-08-21 05:25:57 +04:00
2010-05-01 23:15:44 +04:00
tx_seq_offset = ( tx_seq - pi - > buffer_seq ) % 64 ;
if ( tx_seq_offset < 0 )
tx_seq_offset + = 64 ;
/* invalid tx_seq */
if ( tx_seq_offset > = pi - > tx_win ) {
2010-05-20 23:21:53 +04:00
l2cap_send_disconn_req ( pi - > conn , sk , ECONNRESET ) ;
2010-05-01 23:15:44 +04:00
goto drop ;
}
2010-05-01 23:15:44 +04:00
if ( pi - > conn_state = = L2CAP_CONN_LOCAL_BUSY )
goto drop ;
2009-08-21 05:26:03 +04:00
if ( pi - > conn_state & L2CAP_CONN_SREJ_SENT ) {
struct srej_list * first ;
2009-08-21 05:25:59 +04:00
2009-08-21 05:26:03 +04:00
first = list_first_entry ( SREJ_LIST ( sk ) ,
struct srej_list , list ) ;
if ( tx_seq = = first - > tx_seq ) {
l2cap_add_to_srej_queue ( sk , skb , tx_seq , sar ) ;
l2cap_check_srej_gap ( sk , tx_seq ) ;
list_del ( & first - > list ) ;
kfree ( first ) ;
if ( list_empty ( SREJ_LIST ( sk ) ) ) {
pi - > buffer_seq = pi - > buffer_seq_srej ;
pi - > conn_state & = ~ L2CAP_CONN_SREJ_SENT ;
2010-05-01 23:15:40 +04:00
l2cap_send_ack ( pi ) ;
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p, Exit SREJ_SENT " , sk ) ;
2009-08-21 05:26:03 +04:00
}
} else {
struct srej_list * l ;
2010-05-01 23:15:44 +04:00
/* duplicated tx_seq */
if ( l2cap_add_to_srej_queue ( sk , skb , tx_seq , sar ) < 0 )
goto drop ;
2009-08-21 05:26:03 +04:00
list_for_each_entry ( l , SREJ_LIST ( sk ) , list ) {
if ( l - > tx_seq = = tx_seq ) {
l2cap_resend_srejframe ( sk , tx_seq ) ;
return 0 ;
}
}
l2cap_send_srejframe ( sk , tx_seq ) ;
2009-08-21 05:25:59 +04:00
}
} else {
2010-05-01 23:15:44 +04:00
expected_tx_seq_offset =
( pi - > expected_tx_seq - pi - > buffer_seq ) % 64 ;
if ( expected_tx_seq_offset < 0 )
expected_tx_seq_offset + = 64 ;
/* duplicated tx_seq */
if ( tx_seq_offset < expected_tx_seq_offset )
goto drop ;
2009-08-21 05:26:03 +04:00
pi - > conn_state | = L2CAP_CONN_SREJ_SENT ;
2009-08-21 05:25:57 +04:00
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p, Enter SREJ " , sk ) ;
2009-08-21 05:26:03 +04:00
INIT_LIST_HEAD ( SREJ_LIST ( sk ) ) ;
pi - > buffer_seq_srej = pi - > buffer_seq ;
__skb_queue_head_init ( SREJ_QUEUE ( sk ) ) ;
2010-05-01 23:15:44 +04:00
__skb_queue_head_init ( BUSY_QUEUE ( sk ) ) ;
2009-08-21 05:26:03 +04:00
l2cap_add_to_srej_queue ( sk , skb , tx_seq , sar ) ;
2009-08-21 05:26:04 +04:00
pi - > conn_state | = L2CAP_CONN_SEND_PBIT ;
2009-08-21 05:26:03 +04:00
l2cap_send_srejframe ( sk , tx_seq ) ;
2010-05-13 01:32:04 +04:00
del_timer ( & pi - > ack_timer ) ;
2009-08-21 05:25:57 +04:00
}
2009-08-21 05:25:59 +04:00
return 0 ;
2009-08-21 05:26:03 +04:00
expected :
pi - > expected_tx_seq = ( pi - > expected_tx_seq + 1 ) % 64 ;
if ( pi - > conn_state & L2CAP_CONN_SREJ_SENT ) {
2010-05-01 23:15:42 +04:00
bt_cb ( skb ) - > tx_seq = tx_seq ;
bt_cb ( skb ) - > sar = sar ;
__skb_queue_tail ( SREJ_QUEUE ( sk ) , skb ) ;
2009-08-21 05:26:03 +04:00
return 0 ;
}
2010-06-17 00:21:44 +04:00
err = l2cap_push_rx_skb ( sk , skb , rx_control ) ;
if ( err < 0 )
return 0 ;
2009-10-03 09:34:39 +04:00
if ( rx_control & L2CAP_CTRL_FINAL ) {
if ( pi - > conn_state & L2CAP_CONN_REJ_ACT )
pi - > conn_state & = ~ L2CAP_CONN_REJ_ACT ;
2010-05-01 23:15:45 +04:00
else
l2cap_retransmit_frames ( sk ) ;
2009-10-03 09:34:39 +04:00
}
2010-05-01 23:15:39 +04:00
__mod_ack_timer ( ) ;
2010-05-01 23:15:41 +04:00
pi - > num_acked = ( pi - > num_acked + 1 ) % num_to_ack ;
if ( pi - > num_acked = = num_to_ack - 1 )
2010-05-01 23:15:37 +04:00
l2cap_send_ack ( pi ) ;
2009-08-21 05:26:03 +04:00
return 0 ;
2010-05-01 23:15:44 +04:00
drop :
kfree_skb ( skb ) ;
return 0 ;
2009-08-21 05:25:57 +04:00
}
2010-05-01 23:15:38 +04:00
static inline void l2cap_data_channel_rrframe ( struct sock * sk , u16 rx_control )
2009-08-21 05:25:57 +04:00
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
2010-05-01 23:15:38 +04:00
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p, req_seq %d ctrl 0x%4.4x " , sk , __get_reqseq ( rx_control ) ,
rx_control ) ;
2010-05-01 23:15:38 +04:00
pi - > expected_ack_seq = __get_reqseq ( rx_control ) ;
l2cap_drop_acked_frames ( sk ) ;
2009-08-21 05:25:57 +04:00
2010-05-01 23:15:38 +04:00
if ( rx_control & L2CAP_CTRL_POLL ) {
2010-05-29 09:24:35 +04:00
pi - > conn_state | = L2CAP_CONN_SEND_FBIT ;
2010-05-01 23:15:39 +04:00
if ( pi - > conn_state & L2CAP_CONN_SREJ_SENT ) {
if ( ( pi - > conn_state & L2CAP_CONN_REMOTE_BUSY ) & &
( pi - > unacked_frames > 0 ) )
__mod_retrans_timer ( ) ;
pi - > conn_state & = ~ L2CAP_CONN_REMOTE_BUSY ;
l2cap_send_srejtail ( sk ) ;
} else {
l2cap_send_i_or_rr_or_rnr ( sk ) ;
}
2010-05-01 23:15:37 +04:00
2010-05-01 23:15:38 +04:00
} else if ( rx_control & L2CAP_CTRL_FINAL ) {
pi - > conn_state & = ~ L2CAP_CONN_REMOTE_BUSY ;
2009-10-03 09:34:39 +04:00
2010-05-01 23:15:38 +04:00
if ( pi - > conn_state & L2CAP_CONN_REJ_ACT )
pi - > conn_state & = ~ L2CAP_CONN_REJ_ACT ;
2010-05-01 23:15:45 +04:00
else
l2cap_retransmit_frames ( sk ) ;
2009-08-26 11:04:02 +04:00
2010-05-01 23:15:38 +04:00
} else {
if ( ( pi - > conn_state & L2CAP_CONN_REMOTE_BUSY ) & &
( pi - > unacked_frames > 0 ) )
__mod_retrans_timer ( ) ;
2009-08-21 05:25:57 +04:00
2009-08-26 11:04:02 +04:00
pi - > conn_state & = ~ L2CAP_CONN_REMOTE_BUSY ;
2010-05-01 23:15:45 +04:00
if ( pi - > conn_state & L2CAP_CONN_SREJ_SENT ) {
2010-05-01 23:15:38 +04:00
l2cap_send_ack ( pi ) ;
2010-05-01 23:15:45 +04:00
} else {
2010-05-01 23:15:38 +04:00
l2cap_ertm_send ( sk ) ;
2010-05-01 23:15:45 +04:00
}
2010-05-01 23:15:38 +04:00
}
}
2009-08-26 11:04:02 +04:00
2010-05-01 23:15:38 +04:00
static inline void l2cap_data_channel_rejframe ( struct sock * sk , u16 rx_control )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
u8 tx_seq = __get_reqseq ( rx_control ) ;
2009-08-21 05:25:59 +04:00
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p, req_seq %d ctrl 0x%4.4x " , sk , tx_seq , rx_control ) ;
2010-05-01 23:15:38 +04:00
pi - > conn_state & = ~ L2CAP_CONN_REMOTE_BUSY ;
2010-05-01 23:15:38 +04:00
pi - > expected_ack_seq = tx_seq ;
2010-05-01 23:15:38 +04:00
l2cap_drop_acked_frames ( sk ) ;
if ( rx_control & L2CAP_CTRL_FINAL ) {
if ( pi - > conn_state & L2CAP_CONN_REJ_ACT )
pi - > conn_state & = ~ L2CAP_CONN_REJ_ACT ;
2010-05-01 23:15:45 +04:00
else
l2cap_retransmit_frames ( sk ) ;
2010-05-01 23:15:38 +04:00
} else {
2010-05-01 23:15:45 +04:00
l2cap_retransmit_frames ( sk ) ;
2009-08-21 05:25:59 +04:00
2010-05-06 03:56:43 +04:00
if ( pi - > conn_state & L2CAP_CONN_WAIT_F )
2010-05-01 23:15:38 +04:00
pi - > conn_state | = L2CAP_CONN_REJ_ACT ;
}
}
static inline void l2cap_data_channel_srejframe ( struct sock * sk , u16 rx_control )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
u8 tx_seq = __get_reqseq ( rx_control ) ;
2009-08-21 05:25:59 +04:00
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p, req_seq %d ctrl 0x%4.4x " , sk , tx_seq , rx_control ) ;
2010-05-01 23:15:38 +04:00
pi - > conn_state & = ~ L2CAP_CONN_REMOTE_BUSY ;
2009-08-26 11:04:02 +04:00
2010-05-01 23:15:38 +04:00
if ( rx_control & L2CAP_CTRL_POLL ) {
pi - > expected_ack_seq = tx_seq ;
l2cap_drop_acked_frames ( sk ) ;
2010-05-29 09:24:35 +04:00
pi - > conn_state | = L2CAP_CONN_SEND_FBIT ;
2010-05-01 23:15:45 +04:00
l2cap_retransmit_one_frame ( sk , tx_seq ) ;
2010-05-01 23:15:38 +04:00
l2cap_ertm_send ( sk ) ;
2010-05-01 23:15:45 +04:00
2010-05-01 23:15:38 +04:00
if ( pi - > conn_state & L2CAP_CONN_WAIT_F ) {
pi - > srej_save_reqseq = tx_seq ;
pi - > conn_state | = L2CAP_CONN_SREJ_ACT ;
2009-08-21 05:26:04 +04:00
}
2010-05-01 23:15:38 +04:00
} else if ( rx_control & L2CAP_CTRL_FINAL ) {
if ( ( pi - > conn_state & L2CAP_CONN_SREJ_ACT ) & &
pi - > srej_save_reqseq = = tx_seq )
pi - > conn_state & = ~ L2CAP_CONN_SREJ_ACT ;
else
2010-05-01 23:15:45 +04:00
l2cap_retransmit_one_frame ( sk , tx_seq ) ;
2010-05-01 23:15:38 +04:00
} else {
2010-05-01 23:15:45 +04:00
l2cap_retransmit_one_frame ( sk , tx_seq ) ;
2010-05-01 23:15:38 +04:00
if ( pi - > conn_state & L2CAP_CONN_WAIT_F ) {
pi - > srej_save_reqseq = tx_seq ;
pi - > conn_state | = L2CAP_CONN_SREJ_ACT ;
2009-08-21 05:26:04 +04:00
}
2010-05-01 23:15:38 +04:00
}
}
static inline void l2cap_data_channel_rnrframe ( struct sock * sk , u16 rx_control )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
u8 tx_seq = __get_reqseq ( rx_control ) ;
2010-04-19 21:45:38 +04:00
BT_DBG ( " sk %p, req_seq %d ctrl 0x%4.4x " , sk , tx_seq , rx_control ) ;
2010-05-01 23:15:38 +04:00
pi - > conn_state | = L2CAP_CONN_REMOTE_BUSY ;
pi - > expected_ack_seq = tx_seq ;
l2cap_drop_acked_frames ( sk ) ;
2010-05-29 09:24:35 +04:00
if ( rx_control & L2CAP_CTRL_POLL )
pi - > conn_state | = L2CAP_CONN_SEND_FBIT ;
2010-05-01 23:15:38 +04:00
if ( ! ( pi - > conn_state & L2CAP_CONN_SREJ_SENT ) ) {
del_timer ( & pi - > retrans_timer ) ;
2010-05-06 02:58:27 +04:00
if ( rx_control & L2CAP_CTRL_POLL )
l2cap_send_rr_or_rnr ( pi , L2CAP_CTRL_FINAL ) ;
2010-05-01 23:15:38 +04:00
return ;
2010-05-01 23:15:38 +04:00
}
2010-05-01 23:15:38 +04:00
if ( rx_control & L2CAP_CTRL_POLL )
l2cap_send_srejtail ( sk ) ;
else
l2cap_send_sframe ( pi , L2CAP_SUPER_RCV_READY ) ;
2010-05-01 23:15:38 +04:00
}
static inline int l2cap_data_channel_sframe ( struct sock * sk , u16 rx_control , struct sk_buff * skb )
{
BT_DBG ( " sk %p rx_control 0x%4.4x len %d " , sk , rx_control , skb - > len ) ;
2010-05-06 03:05:57 +04:00
if ( L2CAP_CTRL_FINAL & rx_control & &
l2cap_pi ( sk ) - > conn_state & L2CAP_CONN_WAIT_F ) {
2010-05-01 23:15:38 +04:00
del_timer ( & l2cap_pi ( sk ) - > monitor_timer ) ;
if ( l2cap_pi ( sk ) - > unacked_frames > 0 )
__mod_retrans_timer ( ) ;
l2cap_pi ( sk ) - > conn_state & = ~ L2CAP_CONN_WAIT_F ;
}
switch ( rx_control & L2CAP_CTRL_SUPERVISE ) {
case L2CAP_SUPER_RCV_READY :
l2cap_data_channel_rrframe ( sk , rx_control ) ;
2009-08-21 05:26:03 +04:00
break ;
2010-05-01 23:15:38 +04:00
case L2CAP_SUPER_REJECT :
l2cap_data_channel_rejframe ( sk , rx_control ) ;
break ;
2009-08-26 11:04:02 +04:00
2010-05-01 23:15:38 +04:00
case L2CAP_SUPER_SELECT_REJECT :
l2cap_data_channel_srejframe ( sk , rx_control ) ;
break ;
case L2CAP_SUPER_RCV_NOT_READY :
l2cap_data_channel_rnrframe ( sk , rx_control ) ;
2009-08-21 05:25:57 +04:00
break ;
}
2010-05-01 23:15:35 +04:00
kfree_skb ( skb ) ;
2009-08-21 05:25:57 +04:00
return 0 ;
}
2010-06-22 01:53:22 +04:00
static int l2cap_ertm_data_rcv ( struct sock * sk , struct sk_buff * skb )
{
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
u16 control ;
u8 req_seq ;
int len , next_tx_seq_offset , req_seq_offset ;
control = get_unaligned_le16 ( skb - > data ) ;
skb_pull ( skb , 2 ) ;
len = skb - > len ;
/*
* We can just drop the corrupted I - frame here .
* Receiver will miss it and start proper recovery
* procedures and ask retransmission .
*/
if ( l2cap_check_fcs ( pi , skb ) )
goto drop ;
if ( __is_sar_start ( control ) & & __is_iframe ( control ) )
len - = 2 ;
if ( pi - > fcs = = L2CAP_FCS_CRC16 )
len - = 2 ;
if ( len > pi - > mps ) {
l2cap_send_disconn_req ( pi - > conn , sk , ECONNRESET ) ;
goto drop ;
}
req_seq = __get_reqseq ( control ) ;
req_seq_offset = ( req_seq - pi - > expected_ack_seq ) % 64 ;
if ( req_seq_offset < 0 )
req_seq_offset + = 64 ;
next_tx_seq_offset =
( pi - > next_tx_seq - pi - > expected_ack_seq ) % 64 ;
if ( next_tx_seq_offset < 0 )
next_tx_seq_offset + = 64 ;
/* check for invalid req-seq */
if ( req_seq_offset > next_tx_seq_offset ) {
l2cap_send_disconn_req ( pi - > conn , sk , ECONNRESET ) ;
goto drop ;
}
if ( __is_iframe ( control ) ) {
if ( len < 0 ) {
l2cap_send_disconn_req ( pi - > conn , sk , ECONNRESET ) ;
goto drop ;
}
l2cap_data_channel_iframe ( sk , control , skb ) ;
} else {
if ( len ! = 0 ) {
BT_ERR ( " %d " , len ) ;
l2cap_send_disconn_req ( pi - > conn , sk , ECONNRESET ) ;
goto drop ;
}
l2cap_data_channel_sframe ( sk , control , skb ) ;
}
return 0 ;
drop :
kfree_skb ( skb ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static inline int l2cap_data_channel ( struct l2cap_conn * conn , u16 cid , struct sk_buff * skb )
{
struct sock * sk ;
2009-08-21 05:26:01 +04:00
struct l2cap_pinfo * pi ;
2010-06-09 23:46:25 +04:00
u16 control ;
2010-06-22 01:53:22 +04:00
u8 tx_seq ;
int len ;
2005-04-17 02:20:36 +04:00
sk = l2cap_get_chan_by_scid ( & conn - > chan_list , cid ) ;
if ( ! sk ) {
BT_DBG ( " unknown cid 0x%4.4x " , cid ) ;
goto drop ;
}
2009-08-21 05:26:01 +04:00
pi = l2cap_pi ( sk ) ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " sk %p, len %d " , sk , skb - > len ) ;
if ( sk - > sk_state ! = BT_CONNECTED )
goto drop ;
2009-08-21 05:26:01 +04:00
switch ( pi - > mode ) {
2009-08-21 05:25:57 +04:00
case L2CAP_MODE_BASIC :
/* If socket recv buffers overflows we drop data here
* which is * bad * because L2CAP has to be reliable .
* But we don ' t have any other choice . L2CAP doesn ' t
* provide flow control mechanism . */
2005-04-17 02:20:36 +04:00
2009-08-21 05:26:01 +04:00
if ( pi - > imtu < skb - > len )
2009-08-21 05:25:57 +04:00
goto drop ;
2005-04-17 02:20:36 +04:00
2009-08-21 05:25:57 +04:00
if ( ! sock_queue_rcv_skb ( sk , skb ) )
goto done ;
break ;
case L2CAP_MODE_ERTM :
2010-06-22 01:53:22 +04:00
if ( ! sock_owned_by_user ( sk ) ) {
l2cap_ertm_data_rcv ( sk , skb ) ;
2010-05-01 23:15:37 +04:00
} else {
2010-06-22 01:53:22 +04:00
if ( sk_add_backlog ( sk , skb ) )
2010-05-01 23:15:37 +04:00
goto drop ;
}
2009-08-21 05:25:57 +04:00
2009-12-22 16:58:08 +03:00
goto done ;
2009-08-21 05:25:57 +04:00
2009-08-21 05:26:01 +04:00
case L2CAP_MODE_STREAMING :
control = get_unaligned_le16 ( skb - > data ) ;
skb_pull ( skb , 2 ) ;
len = skb - > len ;
2010-05-12 05:02:00 +04:00
if ( l2cap_check_fcs ( pi , skb ) )
goto drop ;
2009-08-21 05:26:01 +04:00
if ( __is_sar_start ( control ) )
len - = 2 ;
2009-08-21 05:26:02 +04:00
if ( pi - > fcs = = L2CAP_FCS_CRC16 )
len - = 2 ;
2010-06-09 23:46:25 +04:00
if ( len > pi - > mps | | len < 0 | | __is_sframe ( control ) )
2009-08-21 05:26:01 +04:00
goto drop ;
tx_seq = __get_txseq ( control ) ;
if ( pi - > expected_tx_seq = = tx_seq )
pi - > expected_tx_seq = ( pi - > expected_tx_seq + 1 ) % 64 ;
else
2010-05-01 23:15:36 +04:00
pi - > expected_tx_seq = ( tx_seq + 1 ) % 64 ;
2009-08-21 05:26:01 +04:00
2010-05-01 23:15:44 +04:00
l2cap_streaming_reassembly_sdu ( sk , skb , control ) ;
2009-08-21 05:26:01 +04:00
goto done ;
2009-08-21 05:25:57 +04:00
default :
2010-05-01 23:15:36 +04:00
BT_DBG ( " sk %p: bad mode 0x%2.2x " , sk , pi - > mode ) ;
2009-08-21 05:25:57 +04:00
break ;
}
2005-04-17 02:20:36 +04:00
drop :
kfree_skb ( skb ) ;
done :
2006-07-03 12:02:46 +04:00
if ( sk )
bh_unlock_sock ( sk ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-07-29 11:16:36 +04:00
static inline int l2cap_conless_channel ( struct l2cap_conn * conn , __le16 psm , struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
sk = l2cap_get_sock_by_psm ( 0 , psm , conn - > src ) ;
if ( ! sk )
goto drop ;
BT_DBG ( " sk %p, len %d " , sk , skb - > len ) ;
if ( sk - > sk_state ! = BT_BOUND & & sk - > sk_state ! = BT_CONNECTED )
goto drop ;
if ( l2cap_pi ( sk ) - > imtu < skb - > len )
goto drop ;
if ( ! sock_queue_rcv_skb ( sk , skb ) )
goto done ;
drop :
kfree_skb ( skb ) ;
done :
2009-04-20 08:31:08 +04:00
if ( sk )
bh_unlock_sock ( sk ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void l2cap_recv_frame ( struct l2cap_conn * conn , struct sk_buff * skb )
{
struct l2cap_hdr * lh = ( void * ) skb - > data ;
2007-07-29 11:16:36 +04:00
u16 cid , len ;
__le16 psm ;
2005-04-17 02:20:36 +04:00
skb_pull ( skb , L2CAP_HDR_SIZE ) ;
cid = __le16_to_cpu ( lh - > cid ) ;
len = __le16_to_cpu ( lh - > len ) ;
2009-08-21 05:25:57 +04:00
if ( len ! = skb - > len ) {
kfree_skb ( skb ) ;
return ;
}
2005-04-17 02:20:36 +04:00
BT_DBG ( " len %d, cid 0x%4.4x " , len , cid ) ;
switch ( cid ) {
2009-04-20 08:31:05 +04:00
case L2CAP_CID_SIGNALING :
2005-04-17 02:20:36 +04:00
l2cap_sig_channel ( conn , skb ) ;
break ;
2009-04-20 08:31:05 +04:00
case L2CAP_CID_CONN_LESS :
2009-08-24 07:45:20 +04:00
psm = get_unaligned_le16 ( skb - > data ) ;
2005-04-17 02:20:36 +04:00
skb_pull ( skb , 2 ) ;
l2cap_conless_channel ( conn , psm , skb ) ;
break ;
default :
l2cap_data_channel ( conn , cid , skb ) ;
break ;
}
}
/* ---- L2CAP interface with lower layer (HCI) ---- */
static int l2cap_connect_ind ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 type )
{
int exact = 0 , lm1 = 0 , lm2 = 0 ;
register struct sock * sk ;
struct hlist_node * node ;
if ( type ! = ACL_LINK )
2010-06-22 20:56:28 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " hdev %s, bdaddr %s " , hdev - > name , batostr ( bdaddr ) ) ;
/* Find listening sockets and check their link_mode */
read_lock ( & l2cap_sk_list . lock ) ;
sk_for_each ( sk , node , & l2cap_sk_list . head ) {
if ( sk - > sk_state ! = BT_LISTEN )
continue ;
if ( ! bacmp ( & bt_sk ( sk ) - > src , & hdev - > bdaddr ) ) {
2009-01-15 23:58:38 +03:00
lm1 | = HCI_LM_ACCEPT ;
if ( l2cap_pi ( sk ) - > role_switch )
lm1 | = HCI_LM_MASTER ;
2005-04-17 02:20:36 +04:00
exact + + ;
2009-01-15 23:58:38 +03:00
} else if ( ! bacmp ( & bt_sk ( sk ) - > src , BDADDR_ANY ) ) {
lm2 | = HCI_LM_ACCEPT ;
if ( l2cap_pi ( sk ) - > role_switch )
lm2 | = HCI_LM_MASTER ;
}
2005-04-17 02:20:36 +04:00
}
read_unlock ( & l2cap_sk_list . lock ) ;
return exact ? lm1 : lm2 ;
}
static int l2cap_connect_cfm ( struct hci_conn * hcon , u8 status )
{
2006-07-03 12:02:46 +04:00
struct l2cap_conn * conn ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " hcon %p bdaddr %s status %d " , hcon , batostr ( & hcon - > dst ) , status ) ;
if ( hcon - > type ! = ACL_LINK )
2010-06-22 20:56:28 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( ! status ) {
conn = l2cap_conn_add ( hcon , status ) ;
if ( conn )
l2cap_conn_ready ( conn ) ;
2006-07-03 12:02:46 +04:00
} else
2005-04-17 02:20:36 +04:00
l2cap_conn_del ( hcon , bt_err ( status ) ) ;
return 0 ;
}
2009-02-12 16:02:50 +03:00
static int l2cap_disconn_ind ( struct hci_conn * hcon )
{
struct l2cap_conn * conn = hcon - > l2cap_data ;
BT_DBG ( " hcon %p " , hcon ) ;
if ( hcon - > type ! = ACL_LINK | | ! conn )
return 0x13 ;
return conn - > disc_reason ;
}
static int l2cap_disconn_cfm ( struct hci_conn * hcon , u8 reason )
2005-04-17 02:20:36 +04:00
{
BT_DBG ( " hcon %p reason %d " , hcon , reason ) ;
if ( hcon - > type ! = ACL_LINK )
2010-06-22 20:56:28 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
l2cap_conn_del ( hcon , bt_err ( reason ) ) ;
2006-07-03 12:02:46 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-01-15 23:58:44 +03:00
static inline void l2cap_check_encryption ( struct sock * sk , u8 encrypt )
{
2010-05-01 23:15:42 +04:00
if ( sk - > sk_type ! = SOCK_SEQPACKET & & sk - > sk_type ! = SOCK_STREAM )
2009-02-04 23:07:19 +03:00
return ;
2009-01-15 23:58:44 +03:00
if ( encrypt = = 0x00 ) {
if ( l2cap_pi ( sk ) - > sec_level = = BT_SECURITY_MEDIUM ) {
l2cap_sock_clear_timer ( sk ) ;
l2cap_sock_set_timer ( sk , HZ * 5 ) ;
} else if ( l2cap_pi ( sk ) - > sec_level = = BT_SECURITY_HIGH )
__l2cap_sock_close ( sk , ECONNREFUSED ) ;
} else {
if ( l2cap_pi ( sk ) - > sec_level = = BT_SECURITY_MEDIUM )
l2cap_sock_clear_timer ( sk ) ;
}
}
2009-01-15 23:58:04 +03:00
static int l2cap_security_cfm ( struct hci_conn * hcon , u8 status , u8 encrypt )
2005-04-17 02:20:36 +04:00
{
struct l2cap_chan_list * l ;
2006-07-03 12:02:46 +04:00
struct l2cap_conn * conn = hcon - > l2cap_data ;
2005-04-17 02:20:36 +04:00
struct sock * sk ;
2006-07-03 12:02:46 +04:00
if ( ! conn )
2005-04-17 02:20:36 +04:00
return 0 ;
2006-07-03 12:02:46 +04:00
2005-04-17 02:20:36 +04:00
l = & conn - > chan_list ;
BT_DBG ( " conn %p " , conn ) ;
read_lock ( & l - > lock ) ;
for ( sk = l - > head ; sk ; sk = l2cap_pi ( sk ) - > next_c ) {
bh_lock_sock ( sk ) ;
2009-02-07 01:56:36 +03:00
if ( l2cap_pi ( sk ) - > conf_state & L2CAP_CONF_CONNECT_PEND ) {
bh_unlock_sock ( sk ) ;
continue ;
}
2009-01-15 23:58:44 +03:00
if ( ! status & & ( sk - > sk_state = = BT_CONNECTED | |
2009-01-15 23:58:04 +03:00
sk - > sk_state = = BT_CONFIG ) ) {
2009-01-15 23:58:44 +03:00
l2cap_check_encryption ( sk , encrypt ) ;
2008-07-14 22:13:45 +04:00
bh_unlock_sock ( sk ) ;
continue ;
}
2008-07-14 22:13:54 +04:00
if ( sk - > sk_state = = BT_CONNECT ) {
if ( ! status ) {
struct l2cap_conn_req req ;
req . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
req . psm = l2cap_pi ( sk ) - > psm ;
2005-04-17 02:20:36 +04:00
2008-07-14 22:13:54 +04:00
l2cap_pi ( sk ) - > ident = l2cap_get_ident ( conn ) ;
2010-07-08 13:14:41 +04:00
l2cap_pi ( sk ) - > conf_state | = L2CAP_CONF_CONNECT_PEND ;
2005-04-17 02:20:36 +04:00
2008-07-14 22:13:54 +04:00
l2cap_send_cmd ( conn , l2cap_pi ( sk ) - > ident ,
L2CAP_CONN_REQ , sizeof ( req ) , & req ) ;
} else {
l2cap_sock_clear_timer ( sk ) ;
l2cap_sock_set_timer ( sk , HZ / 10 ) ;
}
} else if ( sk - > sk_state = = BT_CONNECT2 ) {
struct l2cap_conn_rsp rsp ;
__u16 result ;
2005-04-17 02:20:36 +04:00
2008-07-14 22:13:54 +04:00
if ( ! status ) {
sk - > sk_state = BT_CONFIG ;
result = L2CAP_CR_SUCCESS ;
} else {
sk - > sk_state = BT_DISCONN ;
l2cap_sock_set_timer ( sk , HZ / 10 ) ;
result = L2CAP_CR_SEC_BLOCK ;
}
rsp . scid = cpu_to_le16 ( l2cap_pi ( sk ) - > dcid ) ;
rsp . dcid = cpu_to_le16 ( l2cap_pi ( sk ) - > scid ) ;
rsp . result = cpu_to_le16 ( result ) ;
2008-09-09 09:19:20 +04:00
rsp . status = cpu_to_le16 ( L2CAP_CS_NO_INFO ) ;
2008-07-14 22:13:54 +04:00
l2cap_send_cmd ( conn , l2cap_pi ( sk ) - > ident ,
L2CAP_CONN_RSP , sizeof ( rsp ) , & rsp ) ;
}
2005-04-17 02:20:36 +04:00
bh_unlock_sock ( sk ) ;
}
read_unlock ( & l - > lock ) ;
2008-07-14 22:13:54 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int l2cap_recv_acldata ( struct hci_conn * hcon , struct sk_buff * skb , u16 flags )
{
struct l2cap_conn * conn = hcon - > l2cap_data ;
if ( ! conn & & ! ( conn = l2cap_conn_add ( hcon , 0 ) ) )
goto drop ;
BT_DBG ( " conn %p len %d flags 0x%x " , conn , skb - > len , flags ) ;
if ( flags & ACL_START ) {
struct l2cap_hdr * hdr ;
int len ;
if ( conn - > rx_len ) {
BT_ERR ( " Unexpected start frame (len %d) " , skb - > len ) ;
kfree_skb ( conn - > rx_skb ) ;
conn - > rx_skb = NULL ;
conn - > rx_len = 0 ;
l2cap_conn_unreliable ( conn , ECOMM ) ;
}
if ( skb - > len < 2 ) {
BT_ERR ( " Frame is too short (len %d) " , skb - > len ) ;
l2cap_conn_unreliable ( conn , ECOMM ) ;
goto drop ;
}
hdr = ( struct l2cap_hdr * ) skb - > data ;
len = __le16_to_cpu ( hdr - > len ) + L2CAP_HDR_SIZE ;
if ( len = = skb - > len ) {
/* Complete frame received */
l2cap_recv_frame ( conn , skb ) ;
return 0 ;
}
BT_DBG ( " Start: total len %d, frag len %d " , len , skb - > len ) ;
if ( skb - > len > len ) {
BT_ERR ( " Frame is too long (len %d, expected len %d) " ,
skb - > len , len ) ;
l2cap_conn_unreliable ( conn , ECOMM ) ;
goto drop ;
}
/* Allocate skb for the complete frame (with header) */
2009-04-20 08:31:08 +04:00
conn - > rx_skb = bt_skb_alloc ( len , GFP_ATOMIC ) ;
if ( ! conn - > rx_skb )
2005-04-17 02:20:36 +04:00
goto drop ;
2007-03-28 01:55:52 +04:00
skb_copy_from_linear_data ( skb , skb_put ( conn - > rx_skb , skb - > len ) ,
2009-02-09 11:18:02 +03:00
skb - > len ) ;
2005-04-17 02:20:36 +04:00
conn - > rx_len = len - skb - > len ;
} else {
BT_DBG ( " Cont: frag len %d (expecting %d) " , skb - > len , conn - > rx_len ) ;
if ( ! conn - > rx_len ) {
BT_ERR ( " Unexpected continuation frame (len %d) " , skb - > len ) ;
l2cap_conn_unreliable ( conn , ECOMM ) ;
goto drop ;
}
if ( skb - > len > conn - > rx_len ) {
BT_ERR ( " Fragment is too long (len %d, expected %d) " ,
skb - > len , conn - > rx_len ) ;
kfree_skb ( conn - > rx_skb ) ;
conn - > rx_skb = NULL ;
conn - > rx_len = 0 ;
l2cap_conn_unreliable ( conn , ECOMM ) ;
goto drop ;
}
2007-03-28 01:55:52 +04:00
skb_copy_from_linear_data ( skb , skb_put ( conn - > rx_skb , skb - > len ) ,
2009-02-09 11:18:02 +03:00
skb - > len ) ;
2005-04-17 02:20:36 +04:00
conn - > rx_len - = skb - > len ;
if ( ! conn - > rx_len ) {
/* Complete frame received */
l2cap_recv_frame ( conn , conn - > rx_skb ) ;
conn - > rx_skb = NULL ;
}
}
drop :
kfree_skb ( skb ) ;
return 0 ;
}
2010-03-21 07:27:45 +03:00
static int l2cap_debugfs_show ( struct seq_file * f , void * p )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
struct hlist_node * node ;
read_lock_bh ( & l2cap_sk_list . lock ) ;
2005-11-08 20:57:38 +03:00
sk_for_each ( sk , node , & l2cap_sk_list . head ) {
struct l2cap_pinfo * pi = l2cap_pi ( sk ) ;
2010-03-16 00:12:58 +03:00
2010-03-21 07:27:45 +03:00
seq_printf ( f , " %s %s %d %d 0x%4.4x 0x%4.4x %d %d %d \n " ,
batostr ( & bt_sk ( sk ) - > src ) ,
batostr ( & bt_sk ( sk ) - > dst ) ,
sk - > sk_state , __le16_to_cpu ( pi - > psm ) ,
pi - > scid , pi - > dcid ,
pi - > imtu , pi - > omtu , pi - > sec_level ) ;
2005-11-08 20:57:38 +03:00
}
2005-04-17 02:20:36 +04:00
read_unlock_bh ( & l2cap_sk_list . lock ) ;
2010-03-21 07:27:45 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-03-21 07:27:45 +03:00
static int l2cap_debugfs_open ( struct inode * inode , struct file * file )
{
return single_open ( file , l2cap_debugfs_show , inode - > i_private ) ;
}
static const struct file_operations l2cap_debugfs_fops = {
. open = l2cap_debugfs_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static struct dentry * l2cap_debugfs ;
2005-04-17 02:20:36 +04:00
2005-12-22 23:49:22 +03:00
static const struct proto_ops l2cap_sock_ops = {
2005-04-17 02:20:36 +04:00
. 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 ,
2009-01-15 23:57:00 +03:00
. recvmsg = l2cap_sock_recvmsg ,
2005-04-17 02:20:36 +04:00
. poll = bt_sock_poll ,
2008-07-14 22:13:50 +04:00
. ioctl = bt_sock_ioctl ,
2005-04-17 02:20:36 +04:00
. mmap = sock_no_mmap ,
. socketpair = sock_no_socketpair ,
. shutdown = l2cap_sock_shutdown ,
. setsockopt = l2cap_sock_setsockopt ,
. getsockopt = l2cap_sock_getsockopt
} ;
2009-10-05 09:58:39 +04:00
static const struct net_proto_family l2cap_sock_family_ops = {
2005-04-17 02:20:36 +04:00
. family = PF_BLUETOOTH ,
. owner = THIS_MODULE ,
. create = l2cap_sock_create ,
} ;
static struct hci_proto l2cap_hci_proto = {
. name = " L2CAP " ,
. id = HCI_PROTO_L2CAP ,
. connect_ind = l2cap_connect_ind ,
. connect_cfm = l2cap_connect_cfm ,
. disconn_ind = l2cap_disconn_ind ,
2009-02-12 16:02:50 +03:00
. disconn_cfm = l2cap_disconn_cfm ,
2009-01-15 23:58:04 +03:00
. security_cfm = l2cap_security_cfm ,
2005-04-17 02:20:36 +04:00
. recv_acldata = l2cap_recv_acldata
} ;
static int __init l2cap_init ( void )
{
int err ;
2005-11-08 20:57:38 +03:00
2005-04-17 02:20:36 +04:00
err = proto_register ( & l2cap_proto , 0 ) ;
if ( err < 0 )
return err ;
2010-05-01 23:15:44 +04:00
_busy_wq = create_singlethread_workqueue ( " l2cap " ) ;
if ( ! _busy_wq )
goto error ;
2005-04-17 02:20:36 +04:00
err = bt_sock_register ( BTPROTO_L2CAP , & l2cap_sock_family_ops ) ;
if ( err < 0 ) {
BT_ERR ( " L2CAP socket registration failed " ) ;
goto error ;
}
err = hci_register_proto ( & l2cap_hci_proto ) ;
if ( err < 0 ) {
BT_ERR ( " L2CAP protocol registration failed " ) ;
bt_sock_unregister ( BTPROTO_L2CAP ) ;
goto error ;
}
2010-03-21 07:27:45 +03:00
if ( bt_debugfs ) {
l2cap_debugfs = debugfs_create_file ( " l2cap " , 0444 ,
bt_debugfs , NULL , & l2cap_debugfs_fops ) ;
if ( ! l2cap_debugfs )
BT_ERR ( " Failed to create L2CAP debug file " ) ;
}
2005-04-17 02:20:36 +04:00
BT_INFO ( " L2CAP ver %s " , VERSION ) ;
BT_INFO ( " L2CAP socket layer initialized " ) ;
return 0 ;
error :
proto_unregister ( & l2cap_proto ) ;
return err ;
}
static void __exit l2cap_exit ( void )
{
2010-03-21 07:27:45 +03:00
debugfs_remove ( l2cap_debugfs ) ;
2005-04-17 02:20:36 +04:00
2010-05-01 23:15:44 +04:00
flush_workqueue ( _busy_wq ) ;
destroy_workqueue ( _busy_wq ) ;
2005-04-17 02:20:36 +04:00
if ( bt_sock_unregister ( BTPROTO_L2CAP ) < 0 )
BT_ERR ( " L2CAP socket unregistration failed " ) ;
if ( hci_unregister_proto ( & l2cap_hci_proto ) < 0 )
BT_ERR ( " L2CAP protocol unregistration failed " ) ;
proto_unregister ( & l2cap_proto ) ;
}
void l2cap_load ( void )
{
/* Dummy function to trigger automatic L2CAP module loading by
* other modules that use L2CAP sockets but don ' t use any other
* symbols from it . */
}
EXPORT_SYMBOL ( l2cap_load ) ;
module_init ( l2cap_init ) ;
module_exit ( l2cap_exit ) ;
2010-07-18 23:25:54 +04:00
module_param ( disable_ertm , bool , 0644 ) ;
MODULE_PARM_DESC ( disable_ertm , " Disable enhanced retransmission mode " ) ;
2009-05-03 06:09:01 +04:00
2008-08-18 15:23:53 +04:00
MODULE_AUTHOR ( " Marcel Holtmann <marcel@holtmann.org> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " Bluetooth L2CAP ver " VERSION ) ;
MODULE_VERSION ( VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " bt-proto-0 " ) ;