2007-02-09 17:24:33 +03:00
/*
2005-04-17 02:20:36 +04:00
BlueZ - Bluetooth protocol stack for Linux
2010-05-28 19:53:46 +04:00
Copyright ( c ) 2000 - 2001 , 2010 , Code Aurora Forum . All rights reserved .
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 HCI connection handling. */
# include <linux/module.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/fcntl.h>
# include <linux/init.h>
# include <linux/skbuff.h>
# include <linux/interrupt.h>
# include <linux/notifier.h>
# include <net/sock.h>
# include <asm/system.h>
2010-12-01 17:58:25 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <asm/unaligned.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
2011-02-11 04:38:47 +03:00
static void hci_le_connect ( struct hci_conn * conn )
{
struct hci_dev * hdev = conn - > hdev ;
struct hci_cp_le_create_conn cp ;
conn - > state = BT_CONNECT ;
2012-01-16 11:49:58 +04:00
conn - > out = true ;
2011-02-11 04:38:52 +03:00
conn - > link_mode | = HCI_LM_MASTER ;
2011-06-10 01:50:50 +04:00
conn - > sec_level = BT_SECURITY_LOW ;
2011-02-11 04:38:47 +03:00
memset ( & cp , 0 , sizeof ( cp ) ) ;
2011-07-28 01:40:09 +04:00
cp . scan_interval = cpu_to_le16 ( 0x0060 ) ;
cp . scan_window = cpu_to_le16 ( 0x0030 ) ;
2011-02-11 04:38:47 +03:00
bacpy ( & cp . peer_addr , & conn - > dst ) ;
2011-05-31 21:20:57 +04:00
cp . peer_addr_type = conn - > dst_type ;
2011-07-28 01:40:09 +04:00
cp . conn_interval_min = cpu_to_le16 ( 0x0028 ) ;
cp . conn_interval_max = cpu_to_le16 ( 0x0038 ) ;
cp . supervision_timeout = cpu_to_le16 ( 0x002a ) ;
cp . min_ce_len = cpu_to_le16 ( 0x0000 ) ;
cp . max_ce_len = cpu_to_le16 ( 0x0000 ) ;
2011-02-11 04:38:47 +03:00
hci_send_cmd ( hdev , HCI_OP_LE_CREATE_CONN , sizeof ( cp ) , & cp ) ;
}
static void hci_le_connect_cancel ( struct hci_conn * conn )
{
hci_send_cmd ( conn - > hdev , HCI_OP_LE_CREATE_CONN_CANCEL , 0 , NULL ) ;
}
2006-10-15 19:30:56 +04:00
void hci_acl_connect ( struct hci_conn * conn )
2005-04-17 02:20:36 +04:00
{
struct hci_dev * hdev = conn - > hdev ;
struct inquiry_entry * ie ;
struct hci_cp_create_conn cp ;
BT_DBG ( " %p " , conn ) ;
conn - > state = BT_CONNECT ;
2012-01-16 11:49:58 +04:00
conn - > out = true ;
2008-07-14 22:13:46 +04:00
2005-04-17 02:20:36 +04:00
conn - > link_mode = HCI_LM_MASTER ;
2006-10-15 19:30:56 +04:00
conn - > attempt + + ;
2008-07-14 22:13:47 +04:00
conn - > link_policy = hdev - > link_policy ;
2005-04-17 02:20:36 +04:00
memset ( & cp , 0 , sizeof ( cp ) ) ;
bacpy ( & cp . bdaddr , & conn - > dst ) ;
cp . pscan_rep_mode = 0x02 ;
2010-12-01 17:58:25 +03:00
ie = hci_inquiry_cache_lookup ( hdev , & conn - > dst ) ;
if ( ie ) {
2008-07-14 22:13:48 +04:00
if ( inquiry_entry_age ( ie ) < = INQUIRY_ENTRY_AGE_MAX ) {
cp . pscan_rep_mode = ie - > data . pscan_rep_mode ;
cp . pscan_mode = ie - > data . pscan_mode ;
cp . clock_offset = ie - > data . clock_offset |
cpu_to_le16 ( 0x8000 ) ;
}
2005-04-17 02:20:36 +04:00
memcpy ( conn - > dev_class , ie - > data . dev_class , 3 ) ;
2012-01-16 08:47:28 +04:00
if ( ie - > data . ssp_mode > 0 )
set_bit ( HCI_CONN_SSP_ENABLED , & conn - > flags ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-14 22:13:46 +04:00
cp . pkt_type = cpu_to_le16 ( conn - > pkt_type ) ;
2005-04-17 02:20:36 +04:00
if ( lmp_rswitch_capable ( hdev ) & & ! ( hdev - > link_mode & HCI_LM_MASTER ) )
2007-10-20 16:55:10 +04:00
cp . role_switch = 0x01 ;
2005-04-17 02:20:36 +04:00
else
2007-10-20 16:55:10 +04:00
cp . role_switch = 0x00 ;
2006-10-15 19:30:56 +04:00
2007-10-20 15:33:56 +04:00
hci_send_cmd ( hdev , HCI_OP_CREATE_CONN , sizeof ( cp ) , & cp ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-26 11:43:48 +04:00
static void hci_acl_connect_cancel ( struct hci_conn * conn )
{
struct hci_cp_create_conn_cancel cp ;
BT_DBG ( " %p " , conn ) ;
2011-12-01 16:33:27 +04:00
if ( conn - > hdev - > hci_ver < BLUETOOTH_VER_1_2 )
2006-09-26 11:43:48 +04:00
return ;
bacpy ( & cp . bdaddr , & conn - > dst ) ;
2007-10-20 15:33:56 +04:00
hci_send_cmd ( conn - > hdev , HCI_OP_CREATE_CONN_CANCEL , sizeof ( cp ) , & cp ) ;
2006-09-26 11:43:48 +04:00
}
2005-04-17 02:20:36 +04:00
void hci_acl_disconn ( struct hci_conn * conn , __u8 reason )
{
struct hci_cp_disconnect cp ;
BT_DBG ( " %p " , conn ) ;
conn - > state = BT_DISCONN ;
2007-03-26 07:12:50 +04:00
cp . handle = cpu_to_le16 ( conn - > handle ) ;
2005-04-17 02:20:36 +04:00
cp . reason = reason ;
2007-10-20 15:33:56 +04:00
hci_send_cmd ( conn - > hdev , HCI_OP_DISCONNECT , sizeof ( cp ) , & cp ) ;
2005-04-17 02:20:36 +04:00
}
void hci_add_sco ( struct hci_conn * conn , __u16 handle )
{
struct hci_dev * hdev = conn - > hdev ;
struct hci_cp_add_sco cp ;
BT_DBG ( " %p " , conn ) ;
conn - > state = BT_CONNECT ;
2012-01-16 11:49:58 +04:00
conn - > out = true ;
2005-04-17 02:20:36 +04:00
2009-02-06 11:13:37 +03:00
conn - > attempt + + ;
2007-03-26 07:12:50 +04:00
cp . handle = cpu_to_le16 ( handle ) ;
2008-07-14 22:13:46 +04:00
cp . pkt_type = cpu_to_le16 ( conn - > pkt_type ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:33:56 +04:00
hci_send_cmd ( hdev , HCI_OP_ADD_SCO , sizeof ( cp ) , & cp ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-20 16:55:10 +04:00
void hci_setup_sync ( struct hci_conn * conn , __u16 handle )
{
struct hci_dev * hdev = conn - > hdev ;
struct hci_cp_setup_sync_conn cp ;
BT_DBG ( " %p " , conn ) ;
conn - > state = BT_CONNECT ;
2012-01-16 11:49:58 +04:00
conn - > out = true ;
2007-10-20 16:55:10 +04:00
2009-02-06 11:13:37 +03:00
conn - > attempt + + ;
2007-10-20 16:55:10 +04:00
cp . handle = cpu_to_le16 ( handle ) ;
2008-07-14 22:13:46 +04:00
cp . pkt_type = cpu_to_le16 ( conn - > pkt_type ) ;
2007-10-20 16:55:10 +04:00
cp . tx_bandwidth = cpu_to_le32 ( 0x00001f40 ) ;
cp . rx_bandwidth = cpu_to_le32 ( 0x00001f40 ) ;
cp . max_latency = cpu_to_le16 ( 0xffff ) ;
cp . voice_setting = cpu_to_le16 ( hdev - > voice_setting ) ;
cp . retrans_effort = 0xff ;
hci_send_cmd ( hdev , HCI_OP_SETUP_SYNC_CONN , sizeof ( cp ) , & cp ) ;
}
2011-02-17 01:44:53 +03:00
void hci_le_conn_update ( struct hci_conn * conn , u16 min , u16 max ,
u16 latency , u16 to_multiplier )
{
struct hci_cp_le_conn_update cp ;
struct hci_dev * hdev = conn - > hdev ;
memset ( & cp , 0 , sizeof ( cp ) ) ;
cp . handle = cpu_to_le16 ( conn - > handle ) ;
cp . conn_interval_min = cpu_to_le16 ( min ) ;
cp . conn_interval_max = cpu_to_le16 ( max ) ;
cp . conn_latency = cpu_to_le16 ( latency ) ;
cp . supervision_timeout = cpu_to_le16 ( to_multiplier ) ;
cp . min_ce_len = cpu_to_le16 ( 0x0001 ) ;
cp . max_ce_len = cpu_to_le16 ( 0x0001 ) ;
hci_send_cmd ( hdev , HCI_OP_LE_CONN_UPDATE , sizeof ( cp ) , & cp ) ;
}
EXPORT_SYMBOL ( hci_le_conn_update ) ;
2011-06-10 01:50:47 +04:00
void hci_le_start_enc ( struct hci_conn * conn , __le16 ediv , __u8 rand [ 8 ] ,
__u8 ltk [ 16 ] )
{
struct hci_dev * hdev = conn - > hdev ;
struct hci_cp_le_start_enc cp ;
BT_DBG ( " %p " , conn ) ;
memset ( & cp , 0 , sizeof ( cp ) ) ;
cp . handle = cpu_to_le16 ( conn - > handle ) ;
memcpy ( cp . ltk , ltk , sizeof ( cp . ltk ) ) ;
cp . ediv = ediv ;
2011-09-19 22:41:09 +04:00
memcpy ( cp . rand , rand , sizeof ( cp . rand ) ) ;
2011-06-10 01:50:47 +04:00
hci_send_cmd ( hdev , HCI_OP_LE_START_ENC , sizeof ( cp ) , & cp ) ;
}
EXPORT_SYMBOL ( hci_le_start_enc ) ;
void hci_le_ltk_reply ( struct hci_conn * conn , u8 ltk [ 16 ] )
{
struct hci_dev * hdev = conn - > hdev ;
struct hci_cp_le_ltk_reply cp ;
BT_DBG ( " %p " , conn ) ;
memset ( & cp , 0 , sizeof ( cp ) ) ;
cp . handle = cpu_to_le16 ( conn - > handle ) ;
memcpy ( cp . ltk , ltk , sizeof ( ltk ) ) ;
hci_send_cmd ( hdev , HCI_OP_LE_LTK_REPLY , sizeof ( cp ) , & cp ) ;
}
EXPORT_SYMBOL ( hci_le_ltk_reply ) ;
void hci_le_ltk_neg_reply ( struct hci_conn * conn )
{
struct hci_dev * hdev = conn - > hdev ;
struct hci_cp_le_ltk_neg_reply cp ;
BT_DBG ( " %p " , conn ) ;
memset ( & cp , 0 , sizeof ( cp ) ) ;
cp . handle = cpu_to_le16 ( conn - > handle ) ;
hci_send_cmd ( hdev , HCI_OP_LE_LTK_NEG_REPLY , sizeof ( cp ) , & cp ) ;
}
2010-07-26 18:06:00 +04:00
/* Device _must_ be locked */
void hci_sco_setup ( struct hci_conn * conn , __u8 status )
{
struct hci_conn * sco = conn - > link ;
BT_DBG ( " %p " , conn ) ;
if ( ! sco )
return ;
if ( ! status ) {
if ( lmp_esco_capable ( conn - > hdev ) )
hci_setup_sync ( sco , conn - > handle ) ;
else
hci_add_sco ( sco , conn - > handle ) ;
} else {
hci_proto_connect_cfm ( sco , status ) ;
hci_conn_del ( sco ) ;
}
}
2011-06-17 20:03:21 +04:00
static void hci_conn_timeout ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
2011-06-17 20:03:21 +04:00
struct hci_conn * conn = container_of ( work , struct hci_conn ,
disc_work . work ) ;
2009-02-12 16:02:50 +03:00
__u8 reason ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " conn %p state %d " , conn , conn - > state ) ;
if ( atomic_read ( & conn - > refcnt ) )
return ;
2006-09-26 11:43:48 +04:00
switch ( conn - > state ) {
case BT_CONNECT :
2008-07-14 22:13:49 +04:00
case BT_CONNECT2 :
2011-02-11 04:38:47 +03:00
if ( conn - > out ) {
if ( conn - > type = = ACL_LINK )
hci_acl_connect_cancel ( conn ) ;
else if ( conn - > type = = LE_LINK )
hci_le_connect_cancel ( conn ) ;
}
2006-09-26 11:43:48 +04:00
break ;
2008-07-14 22:13:49 +04:00
case BT_CONFIG :
2007-02-09 17:24:33 +03:00
case BT_CONNECTED :
2009-02-12 16:02:50 +03:00
reason = hci_proto_disconn_ind ( conn ) ;
hci_acl_disconn ( conn , reason ) ;
2006-09-26 11:43:48 +04:00
break ;
default :
2005-04-17 02:20:36 +04:00
conn - > state = BT_CLOSED ;
2006-09-26 11:43:48 +04:00
break ;
}
2005-04-17 02:20:36 +04:00
}
2011-12-07 19:24:33 +04:00
/* Enter sniff mode */
static void hci_conn_enter_sniff_mode ( struct hci_conn * conn )
{
struct hci_dev * hdev = conn - > hdev ;
BT_DBG ( " conn %p mode %d " , conn , conn - > mode ) ;
if ( test_bit ( HCI_RAW , & hdev - > flags ) )
return ;
if ( ! lmp_sniff_capable ( hdev ) | | ! lmp_sniff_capable ( conn ) )
return ;
if ( conn - > mode ! = HCI_CM_ACTIVE | | ! ( conn - > link_policy & HCI_LP_SNIFF ) )
return ;
if ( lmp_sniffsubr_capable ( hdev ) & & lmp_sniffsubr_capable ( conn ) ) {
struct hci_cp_sniff_subrate cp ;
cp . handle = cpu_to_le16 ( conn - > handle ) ;
cp . max_latency = cpu_to_le16 ( 0 ) ;
cp . min_remote_timeout = cpu_to_le16 ( 0 ) ;
cp . min_local_timeout = cpu_to_le16 ( 0 ) ;
hci_send_cmd ( hdev , HCI_OP_SNIFF_SUBRATE , sizeof ( cp ) , & cp ) ;
}
2012-01-16 08:10:31 +04:00
if ( ! test_and_set_bit ( HCI_CONN_MODE_CHANGE_PEND , & conn - > flags ) ) {
2011-12-07 19:24:33 +04:00
struct hci_cp_sniff_mode cp ;
cp . handle = cpu_to_le16 ( conn - > handle ) ;
cp . max_interval = cpu_to_le16 ( hdev - > sniff_max_interval ) ;
cp . min_interval = cpu_to_le16 ( hdev - > sniff_min_interval ) ;
cp . attempt = cpu_to_le16 ( 4 ) ;
cp . timeout = cpu_to_le16 ( 1 ) ;
hci_send_cmd ( hdev , HCI_OP_SNIFF_MODE , sizeof ( cp ) , & cp ) ;
}
}
2006-07-03 12:02:33 +04:00
static void hci_conn_idle ( unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2006-07-03 12:02:33 +04:00
struct hci_conn * conn = ( void * ) arg ;
BT_DBG ( " conn %p mode %d " , conn , conn - > mode ) ;
hci_conn_enter_sniff_mode ( conn ) ;
2005-04-17 02:20:36 +04:00
}
2011-04-28 22:28:54 +04:00
static void hci_conn_auto_accept ( unsigned long arg )
{
struct hci_conn * conn = ( void * ) arg ;
struct hci_dev * hdev = conn - > hdev ;
hci_send_cmd ( hdev , HCI_OP_USER_CONFIRM_REPLY , sizeof ( conn - > dst ) ,
& conn - > dst ) ;
}
2005-04-17 02:20:36 +04:00
struct hci_conn * hci_conn_add ( struct hci_dev * hdev , int type , bdaddr_t * dst )
{
struct hci_conn * conn ;
BT_DBG ( " %s dst %s " , hdev - > name , batostr ( dst ) ) ;
2012-01-30 16:22:09 +04:00
conn = kzalloc ( sizeof ( struct hci_conn ) , GFP_KERNEL ) ;
2006-07-03 12:02:33 +04:00
if ( ! conn )
2005-04-17 02:20:36 +04:00
return NULL ;
bacpy ( & conn - > dst , dst ) ;
2008-07-14 22:13:46 +04:00
conn - > hdev = hdev ;
conn - > type = type ;
conn - > mode = HCI_CM_ACTIVE ;
conn - > state = BT_OPEN ;
2009-09-03 13:34:19 +04:00
conn - > auth_type = HCI_AT_GENERAL_BONDING ;
2011-01-25 14:28:33 +03:00
conn - > io_capability = hdev - > io_capability ;
2011-02-19 18:06:01 +03:00
conn - > remote_auth = 0xff ;
2011-04-28 14:07:55 +04:00
conn - > key_type = 0xff ;
2005-04-17 02:20:36 +04:00
2012-01-16 08:47:28 +04:00
set_bit ( HCI_CONN_POWER_SAVE , & conn - > flags ) ;
2009-04-26 22:01:22 +04:00
conn - > disc_timeout = HCI_DISCONN_TIMEOUT ;
2006-07-03 12:02:33 +04:00
2008-07-14 22:13:46 +04:00
switch ( type ) {
case ACL_LINK :
conn - > pkt_type = hdev - > pkt_type & ACL_PTYPE_MASK ;
break ;
case SCO_LINK :
if ( lmp_esco_capable ( hdev ) )
2009-02-06 11:13:37 +03:00
conn - > pkt_type = ( hdev - > esco_type & SCO_ESCO_MASK ) |
( hdev - > esco_type & EDR_ESCO_MASK ) ;
2008-07-14 22:13:46 +04:00
else
conn - > pkt_type = hdev - > pkt_type & SCO_PTYPE_MASK ;
break ;
case ESCO_LINK :
2009-02-06 11:13:37 +03:00
conn - > pkt_type = hdev - > esco_type & ~ EDR_ESCO_MASK ;
2008-07-14 22:13:46 +04:00
break ;
}
2005-04-17 02:20:36 +04:00
skb_queue_head_init ( & conn - > data_q ) ;
2006-07-03 12:02:33 +04:00
2011-12-14 19:02:51 +04:00
INIT_LIST_HEAD ( & conn - > chan_list ) ; ;
2011-11-02 17:52:01 +04:00
2011-06-17 20:03:21 +04:00
INIT_DELAYED_WORK ( & conn - > disc_work , hci_conn_timeout ) ;
2008-01-24 08:20:07 +03:00
setup_timer ( & conn - > idle_timer , hci_conn_idle , ( unsigned long ) conn ) ;
2011-04-28 22:28:54 +04:00
setup_timer ( & conn - > auto_accept_timer , hci_conn_auto_accept ,
( unsigned long ) conn ) ;
2005-04-17 02:20:36 +04:00
atomic_set ( & conn - > refcnt , 0 ) ;
hci_dev_hold ( hdev ) ;
hci_conn_hash_add ( hdev , conn ) ;
2011-12-15 04:58:44 +04:00
if ( hdev - > notify )
2005-04-17 02:20:36 +04:00
hdev - > notify ( hdev , HCI_NOTIFY_CONN_ADD ) ;
2009-08-23 01:19:26 +04:00
atomic_set ( & conn - > devref , 0 ) ;
2009-05-03 05:24:06 +04:00
hci_conn_init_sysfs ( conn ) ;
2005-04-17 02:20:36 +04:00
return conn ;
}
int hci_conn_del ( struct hci_conn * conn )
{
struct hci_dev * hdev = conn - > hdev ;
BT_DBG ( " %s conn %p handle %d " , hdev - > name , conn , conn - > handle ) ;
2006-07-03 12:02:33 +04:00
del_timer ( & conn - > idle_timer ) ;
2011-06-17 20:03:21 +04:00
cancel_delayed_work_sync ( & conn - > disc_work ) ;
2005-04-17 02:20:36 +04:00
2011-04-28 22:28:54 +04:00
del_timer ( & conn - > auto_accept_timer ) ;
2007-07-11 11:51:55 +04:00
if ( conn - > type = = ACL_LINK ) {
2005-04-17 02:20:36 +04:00
struct hci_conn * sco = conn - > link ;
if ( sco )
sco - > link = NULL ;
/* Unacked frames */
hdev - > acl_cnt + = conn - > sent ;
2011-02-11 04:38:48 +03:00
} else if ( conn - > type = = LE_LINK ) {
if ( hdev - > le_pkts )
hdev - > le_cnt + = conn - > sent ;
else
hdev - > acl_cnt + = conn - > sent ;
2007-07-11 11:51:55 +04:00
} else {
struct hci_conn * acl = conn - > link ;
if ( acl ) {
acl - > link = NULL ;
hci_conn_put ( acl ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-07-14 22:13:51 +04:00
2011-12-14 19:02:51 +04:00
hci_chan_list_flush ( conn ) ;
2011-11-02 17:52:01 +04:00
2005-04-17 02:20:36 +04:00
hci_conn_hash_del ( hdev , conn ) ;
2011-12-15 04:58:44 +04:00
if ( hdev - > notify )
2005-04-17 02:20:36 +04:00
hdev - > notify ( hdev , HCI_NOTIFY_CONN_DEL ) ;
2008-07-14 22:13:51 +04:00
2005-04-17 02:20:36 +04:00
skb_queue_purge ( & conn - > data_q ) ;
2009-08-23 01:19:26 +04:00
hci_conn_put_device ( conn ) ;
2009-02-21 11:13:34 +03:00
2009-05-09 05:20:43 +04:00
hci_dev_put ( hdev ) ;
2011-06-30 23:30:44 +04:00
if ( conn - > handle = = 0 )
kfree ( conn ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
struct hci_dev * hci_get_route ( bdaddr_t * dst , bdaddr_t * src )
{
int use_src = bacmp ( src , BDADDR_ANY ) ;
2011-11-01 12:58:56 +04:00
struct hci_dev * hdev = NULL , * d ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " %s -> %s " , batostr ( src ) , batostr ( dst ) ) ;
2011-12-22 22:30:27 +04:00
read_lock ( & hci_dev_list_lock ) ;
2005-04-17 02:20:36 +04:00
2011-11-01 12:58:56 +04:00
list_for_each_entry ( d , & hci_dev_list , list ) {
2005-04-17 02:20:36 +04:00
if ( ! test_bit ( HCI_UP , & d - > flags ) | | test_bit ( HCI_RAW , & d - > flags ) )
continue ;
2007-02-09 17:24:33 +03:00
/* Simple routing:
2005-04-17 02:20:36 +04:00
* No source address - find interface with bdaddr ! = dst
* Source address - find interface with bdaddr = = src
*/
if ( use_src ) {
if ( ! bacmp ( & d - > bdaddr , src ) ) {
hdev = d ; break ;
}
} else {
if ( bacmp ( & d - > bdaddr , dst ) ) {
hdev = d ; break ;
}
}
}
if ( hdev )
hdev = hci_dev_hold ( hdev ) ;
2011-12-22 22:30:27 +04:00
read_unlock ( & hci_dev_list_lock ) ;
2005-04-17 02:20:36 +04:00
return hdev ;
}
EXPORT_SYMBOL ( hci_get_route ) ;
2011-02-11 04:38:47 +03:00
/* Create SCO, ACL or LE connection.
2005-04-17 02:20:36 +04:00
* Device _must_ be locked */
2009-01-15 23:58:04 +03:00
struct hci_conn * hci_connect ( struct hci_dev * hdev , int type , bdaddr_t * dst , __u8 sec_level , __u8 auth_type )
2005-04-17 02:20:36 +04:00
{
struct hci_conn * acl ;
2007-07-11 11:51:55 +04:00
struct hci_conn * sco ;
2011-02-11 04:38:47 +03:00
struct hci_conn * le ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " %s dst %s " , hdev - > name , batostr ( dst ) ) ;
2011-02-11 04:38:47 +03:00
if ( type = = LE_LINK ) {
2011-05-31 21:20:56 +04:00
struct adv_entry * entry ;
2011-02-11 04:38:47 +03:00
le = hci_conn_hash_lookup_ba ( hdev , LE_LINK , dst ) ;
2011-02-21 21:09:23 +03:00
if ( le )
2011-02-22 22:10:53 +03:00
return ERR_PTR ( - EBUSY ) ;
2011-05-31 21:20:56 +04:00
entry = hci_find_adv_entry ( hdev , dst ) ;
if ( ! entry )
return ERR_PTR ( - EHOSTUNREACH ) ;
2011-02-21 21:09:23 +03:00
le = hci_conn_add ( hdev , LE_LINK , dst ) ;
2011-02-11 04:38:47 +03:00
if ( ! le )
2011-02-22 22:10:53 +03:00
return ERR_PTR ( - ENOMEM ) ;
2011-05-31 21:20:55 +04:00
2011-05-31 21:20:56 +04:00
le - > dst_type = entry - > bdaddr_type ;
2011-05-31 21:20:55 +04:00
hci_le_connect ( le ) ;
2011-02-11 04:38:47 +03:00
hci_conn_hold ( le ) ;
return le ;
}
2010-12-01 17:58:25 +03:00
acl = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , dst ) ;
if ( ! acl ) {
acl = hci_conn_add ( hdev , ACL_LINK , dst ) ;
if ( ! acl )
2005-04-17 02:20:36 +04:00
return NULL ;
}
hci_conn_hold ( acl ) ;
2008-09-09 09:19:20 +04:00
if ( acl - > state = = BT_OPEN | | acl - > state = = BT_CLOSED ) {
2011-01-19 09:36:52 +03:00
acl - > sec_level = BT_SECURITY_LOW ;
acl - > pending_sec_level = sec_level ;
2008-09-09 09:19:20 +04:00
acl - > auth_type = auth_type ;
2005-04-17 02:20:36 +04:00
hci_acl_connect ( acl ) ;
2008-09-09 09:19:20 +04:00
}
2005-04-17 02:20:36 +04:00
2007-07-11 11:51:55 +04:00
if ( type = = ACL_LINK )
return acl ;
2005-04-17 02:20:36 +04:00
2010-12-01 17:58:25 +03:00
sco = hci_conn_hash_lookup_ba ( hdev , type , dst ) ;
if ( ! sco ) {
sco = hci_conn_add ( hdev , type , dst ) ;
if ( ! sco ) {
2007-07-11 11:51:55 +04:00
hci_conn_put ( acl ) ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
2007-07-11 11:51:55 +04:00
}
2005-04-17 02:20:36 +04:00
2007-07-11 11:51:55 +04:00
acl - > link = sco ;
sco - > link = acl ;
2005-04-17 02:20:36 +04:00
2007-07-11 11:51:55 +04:00
hci_conn_hold ( sco ) ;
2005-04-17 02:20:36 +04:00
2007-07-11 11:51:55 +04:00
if ( acl - > state = = BT_CONNECTED & &
2007-10-20 16:55:10 +04:00
( sco - > state = = BT_OPEN | | sco - > state = = BT_CLOSED ) ) {
2012-01-16 08:47:28 +04:00
set_bit ( HCI_CONN_POWER_SAVE , & acl - > flags ) ;
2011-05-24 05:06:04 +04:00
hci_conn_enter_active_mode ( acl , BT_POWER_FORCE_ACTIVE_ON ) ;
2009-11-14 01:16:32 +03:00
2012-01-16 08:10:31 +04:00
if ( test_bit ( HCI_CONN_MODE_CHANGE_PEND , & acl - > flags ) ) {
2010-07-26 18:06:00 +04:00
/* defer SCO setup until mode change completed */
2012-01-16 08:10:31 +04:00
set_bit ( HCI_CONN_SCO_SETUP_PEND , & acl - > flags ) ;
2010-07-26 18:06:00 +04:00
return sco ;
}
hci_sco_setup ( acl , 0x00 ) ;
2007-10-20 16:55:10 +04:00
}
2007-07-11 11:51:55 +04:00
return sco ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( hci_connect ) ;
2008-09-09 09:19:20 +04:00
/* Check link security requirement */
int hci_conn_check_link_mode ( struct hci_conn * conn )
{
BT_DBG ( " conn %p " , conn ) ;
2012-01-18 23:33:12 +04:00
if ( hci_conn_ssp_enabled ( conn ) & & ! ( conn - > link_mode & HCI_LM_ENCRYPT ) )
2008-09-09 09:19:20 +04:00
return 0 ;
return 1 ;
}
EXPORT_SYMBOL ( hci_conn_check_link_mode ) ;
2005-04-17 02:20:36 +04:00
/* Authenticate remote device */
2009-02-09 04:48:38 +03:00
static int hci_conn_auth ( struct hci_conn * conn , __u8 sec_level , __u8 auth_type )
2005-04-17 02:20:36 +04:00
{
BT_DBG ( " conn %p " , conn ) ;
2011-01-19 09:36:52 +03:00
if ( conn - > pending_sec_level > sec_level )
sec_level = conn - > pending_sec_level ;
2009-02-12 18:23:03 +03:00
if ( sec_level > conn - > sec_level )
2011-01-19 09:36:52 +03:00
conn - > pending_sec_level = sec_level ;
2009-02-12 18:23:03 +03:00
else if ( conn - > link_mode & HCI_LM_AUTH )
2005-04-17 02:20:36 +04:00
return 1 ;
2011-01-19 09:36:49 +03:00
/* Make sure we preserve an existing MITM requirement*/
auth_type | = ( conn - > auth_type & 0x01 ) ;
2009-02-12 18:23:03 +03:00
conn - > auth_type = auth_type ;
2012-01-16 08:10:31 +04:00
if ( ! test_and_set_bit ( HCI_CONN_AUTH_PEND , & conn - > flags ) ) {
2005-04-17 02:20:36 +04:00
struct hci_cp_auth_requested cp ;
2012-01-13 18:11:30 +04:00
/* encrypt must be pending if auth is also pending */
set_bit ( HCI_CONN_ENCRYPT_PEND , & conn - > flags ) ;
2007-03-26 07:12:50 +04:00
cp . handle = cpu_to_le16 ( conn - > handle ) ;
2008-07-14 22:13:50 +04:00
hci_send_cmd ( conn - > hdev , HCI_OP_AUTH_REQUESTED ,
sizeof ( cp ) , & cp ) ;
2011-05-31 17:49:25 +04:00
if ( conn - > key_type ! = 0xff )
2012-01-16 08:10:31 +04:00
set_bit ( HCI_CONN_REAUTH_PEND , & conn - > flags ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-15 23:58:04 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-04-28 14:07:55 +04:00
/* Encrypt the the link */
static void hci_conn_encrypt ( struct hci_conn * conn )
{
BT_DBG ( " conn %p " , conn ) ;
2012-01-16 08:10:31 +04:00
if ( ! test_and_set_bit ( HCI_CONN_ENCRYPT_PEND , & conn - > flags ) ) {
2011-04-28 14:07:55 +04:00
struct hci_cp_set_conn_encrypt cp ;
cp . handle = cpu_to_le16 ( conn - > handle ) ;
cp . encrypt = 0x01 ;
hci_send_cmd ( conn - > hdev , HCI_OP_SET_CONN_ENCRYPT , sizeof ( cp ) ,
& cp ) ;
}
}
2009-01-15 23:58:04 +03:00
/* Enable security */
2009-02-09 04:48:38 +03:00
int hci_conn_security ( struct hci_conn * conn , __u8 sec_level , __u8 auth_type )
2005-04-17 02:20:36 +04:00
{
BT_DBG ( " conn %p " , conn ) ;
2011-04-28 14:07:55 +04:00
/* For sdp we don't need the link key. */
2009-01-15 23:58:04 +03:00
if ( sec_level = = BT_SECURITY_SDP )
return 1 ;
2011-04-28 14:07:55 +04:00
/* For non 2.1 devices and low security level we don't need the link
key . */
2012-01-18 23:33:12 +04:00
if ( sec_level = = BT_SECURITY_LOW & & ! hci_conn_ssp_enabled ( conn ) )
2009-04-28 20:04:55 +04:00
return 1 ;
2009-01-15 23:58:04 +03:00
2011-04-28 14:07:55 +04:00
/* For other security levels we need the link key. */
if ( ! ( conn - > link_mode & HCI_LM_AUTH ) )
goto auth ;
/* An authenticated combination key has sufficient security for any
security level . */
if ( conn - > key_type = = HCI_LK_AUTH_COMBINATION )
goto encrypt ;
/* An unauthenticated combination key has sufficient security for
security level 1 and 2. */
if ( conn - > key_type = = HCI_LK_UNAUTH_COMBINATION & &
( sec_level = = BT_SECURITY_MEDIUM | |
sec_level = = BT_SECURITY_LOW ) )
goto encrypt ;
/* A combination key has always sufficient security for the security
levels 1 or 2. High security level requires the combination key
is generated using maximum PIN code length ( 16 ) .
For pre 2.1 units . */
if ( conn - > key_type = = HCI_LK_COMBINATION & &
( sec_level ! = BT_SECURITY_HIGH | |
conn - > pin_length = = 16 ) )
goto encrypt ;
auth :
2012-01-16 08:10:31 +04:00
if ( test_bit ( HCI_CONN_ENCRYPT_PEND , & conn - > flags ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2011-06-13 16:37:35 +04:00
if ( ! hci_conn_auth ( conn , sec_level , auth_type ) )
return 0 ;
2011-04-28 14:07:55 +04:00
encrypt :
if ( conn - > link_mode & HCI_LM_ENCRYPT )
return 1 ;
2009-01-15 23:58:04 +03:00
2011-04-28 14:07:55 +04:00
hci_conn_encrypt ( conn ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-01-15 23:58:04 +03:00
EXPORT_SYMBOL ( hci_conn_security ) ;
2005-04-17 02:20:36 +04:00
2011-05-06 11:42:31 +04:00
/* Check secure link requirement */
int hci_conn_check_secure ( struct hci_conn * conn , __u8 sec_level )
{
BT_DBG ( " conn %p " , conn ) ;
if ( sec_level ! = BT_SECURITY_HIGH )
return 1 ; /* Accept if non-secure is required */
2011-06-02 16:24:52 +04:00
if ( conn - > sec_level = = BT_SECURITY_HIGH )
2011-05-06 11:42:31 +04:00
return 1 ;
return 0 ; /* Reject not secure link */
}
EXPORT_SYMBOL ( hci_conn_check_secure ) ;
2005-04-17 02:20:36 +04:00
/* Change link key */
int hci_conn_change_link_key ( struct hci_conn * conn )
{
BT_DBG ( " conn %p " , conn ) ;
2012-01-16 08:10:31 +04:00
if ( ! test_and_set_bit ( HCI_CONN_AUTH_PEND , & conn - > flags ) ) {
2005-04-17 02:20:36 +04:00
struct hci_cp_change_conn_link_key cp ;
2007-03-26 07:12:50 +04:00
cp . handle = cpu_to_le16 ( conn - > handle ) ;
2008-07-14 22:13:50 +04:00
hci_send_cmd ( conn - > hdev , HCI_OP_CHANGE_CONN_LINK_KEY ,
sizeof ( cp ) , & cp ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-15 23:58:04 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
EXPORT_SYMBOL ( hci_conn_change_link_key ) ;
/* Switch role */
2009-01-15 23:58:04 +03:00
int hci_conn_switch_role ( struct hci_conn * conn , __u8 role )
2005-04-17 02:20:36 +04:00
{
BT_DBG ( " conn %p " , conn ) ;
if ( ! role & & conn - > link_mode & HCI_LM_MASTER )
return 1 ;
2012-01-16 08:10:31 +04:00
if ( ! test_and_set_bit ( HCI_CONN_RSWITCH_PEND , & conn - > flags ) ) {
2005-04-17 02:20:36 +04:00
struct hci_cp_switch_role cp ;
bacpy ( & cp . bdaddr , & conn - > dst ) ;
cp . role = role ;
2007-10-20 15:33:56 +04:00
hci_send_cmd ( conn - > hdev , HCI_OP_SWITCH_ROLE , sizeof ( cp ) , & cp ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-15 23:58:04 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
EXPORT_SYMBOL ( hci_conn_switch_role ) ;
2006-07-03 12:02:33 +04:00
/* Enter active mode */
2011-05-24 05:06:04 +04:00
void hci_conn_enter_active_mode ( struct hci_conn * conn , __u8 force_active )
2006-07-03 12:02:33 +04:00
{
struct hci_dev * hdev = conn - > hdev ;
BT_DBG ( " conn %p mode %d " , conn , conn - > mode ) ;
if ( test_bit ( HCI_RAW , & hdev - > flags ) )
return ;
2011-05-24 05:06:04 +04:00
if ( conn - > mode ! = HCI_CM_SNIFF )
goto timer ;
2012-01-16 08:47:28 +04:00
if ( ! test_bit ( HCI_CONN_POWER_SAVE , & conn - > flags ) & & ! force_active )
2006-07-03 12:02:33 +04:00
goto timer ;
2012-01-16 08:10:31 +04:00
if ( ! test_and_set_bit ( HCI_CONN_MODE_CHANGE_PEND , & conn - > flags ) ) {
2006-07-03 12:02:33 +04:00
struct hci_cp_exit_sniff_mode cp ;
2007-03-26 07:12:50 +04:00
cp . handle = cpu_to_le16 ( conn - > handle ) ;
2007-10-20 15:33:56 +04:00
hci_send_cmd ( hdev , HCI_OP_EXIT_SNIFF_MODE , sizeof ( cp ) , & cp ) ;
2006-07-03 12:02:33 +04:00
}
timer :
if ( hdev - > idle_timeout > 0 )
mod_timer ( & conn - > idle_timer ,
jiffies + msecs_to_jiffies ( hdev - > idle_timeout ) ) ;
}
2005-04-17 02:20:36 +04:00
/* Drop all connection on the device */
void hci_conn_hash_flush ( struct hci_dev * hdev )
{
struct hci_conn_hash * h = & hdev - > conn_hash ;
2011-12-15 04:52:31 +04:00
struct hci_conn * c ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " hdev %s " , hdev - > name ) ;
2011-12-15 04:54:12 +04:00
list_for_each_entry_rcu ( c , & h - > list , list ) {
2005-04-17 02:20:36 +04:00
c - > state = BT_CLOSED ;
2011-11-07 16:20:25 +04:00
hci_proto_disconn_cfm ( c , HCI_ERROR_LOCAL_HOST_TERM ) ;
2005-04-17 02:20:36 +04:00
hci_conn_del ( c ) ;
}
}
2007-10-20 15:33:56 +04:00
/* Check pending connect attempts */
void hci_conn_check_pending ( struct hci_dev * hdev )
{
struct hci_conn * conn ;
BT_DBG ( " hdev %s " , hdev - > name ) ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_state ( hdev , ACL_LINK , BT_CONNECT2 ) ;
if ( conn )
hci_acl_connect ( conn ) ;
hci_dev_unlock ( hdev ) ;
}
2009-08-23 01:19:26 +04:00
void hci_conn_hold_device ( struct hci_conn * conn )
{
atomic_inc ( & conn - > devref ) ;
}
EXPORT_SYMBOL ( hci_conn_hold_device ) ;
void hci_conn_put_device ( struct hci_conn * conn )
{
if ( atomic_dec_and_test ( & conn - > devref ) )
hci_conn_del_sysfs ( conn ) ;
}
EXPORT_SYMBOL ( hci_conn_put_device ) ;
2005-04-17 02:20:36 +04:00
int hci_get_conn_list ( void __user * arg )
{
2011-11-01 12:58:56 +04:00
register struct hci_conn * c ;
2005-04-17 02:20:36 +04:00
struct hci_conn_list_req req , * cl ;
struct hci_conn_info * ci ;
struct hci_dev * hdev ;
int n = 0 , size , err ;
if ( copy_from_user ( & req , arg , sizeof ( req ) ) )
return - EFAULT ;
if ( ! req . conn_num | | req . conn_num > ( PAGE_SIZE * 2 ) / sizeof ( * ci ) )
return - EINVAL ;
size = sizeof ( req ) + req . conn_num * sizeof ( * ci ) ;
2010-12-01 17:58:25 +03:00
cl = kmalloc ( size , GFP_KERNEL ) ;
if ( ! cl )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2010-12-01 17:58:25 +03:00
hdev = hci_dev_get ( req . dev_id ) ;
if ( ! hdev ) {
2005-04-17 02:20:36 +04:00
kfree ( cl ) ;
return - ENODEV ;
}
ci = cl - > conn_info ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-11-01 12:58:56 +04:00
list_for_each_entry ( c , & hdev - > conn_hash . list , list ) {
2005-04-17 02:20:36 +04:00
bacpy ( & ( ci + n ) - > bdaddr , & c - > dst ) ;
( ci + n ) - > handle = c - > handle ;
( ci + n ) - > type = c - > type ;
( ci + n ) - > out = c - > out ;
( ci + n ) - > state = c - > state ;
( ci + n ) - > link_mode = c - > link_mode ;
if ( + + n > = req . conn_num )
break ;
}
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2005-04-17 02:20:36 +04:00
cl - > dev_id = hdev - > id ;
cl - > conn_num = n ;
size = sizeof ( req ) + n * sizeof ( * ci ) ;
hci_dev_put ( hdev ) ;
err = copy_to_user ( arg , cl , size ) ;
kfree ( cl ) ;
return err ? - EFAULT : 0 ;
}
int hci_get_conn_info ( struct hci_dev * hdev , void __user * arg )
{
struct hci_conn_info_req req ;
struct hci_conn_info ci ;
struct hci_conn * conn ;
char __user * ptr = arg + sizeof ( req ) ;
if ( copy_from_user ( & req , arg , sizeof ( req ) ) )
return - EFAULT ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2005-04-17 02:20:36 +04:00
conn = hci_conn_hash_lookup_ba ( hdev , req . type , & req . bdaddr ) ;
if ( conn ) {
bacpy ( & ci . bdaddr , & conn - > dst ) ;
ci . handle = conn - > handle ;
ci . type = conn - > type ;
ci . out = conn - > out ;
ci . state = conn - > state ;
ci . link_mode = conn - > link_mode ;
}
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2005-04-17 02:20:36 +04:00
if ( ! conn )
return - ENOENT ;
return copy_to_user ( ptr , & ci , sizeof ( ci ) ) ? - EFAULT : 0 ;
}
2008-07-14 22:13:50 +04:00
int hci_get_auth_info ( struct hci_dev * hdev , void __user * arg )
{
struct hci_auth_info_req req ;
struct hci_conn * conn ;
if ( copy_from_user ( & req , arg , sizeof ( req ) ) )
return - EFAULT ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2008-07-14 22:13:50 +04:00
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , & req . bdaddr ) ;
if ( conn )
req . type = conn - > auth_type ;
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2008-07-14 22:13:50 +04:00
if ( ! conn )
return - ENOENT ;
return copy_to_user ( arg , & req , sizeof ( req ) ) ? - EFAULT : 0 ;
}
2011-11-02 17:52:01 +04:00
struct hci_chan * hci_chan_create ( struct hci_conn * conn )
{
struct hci_dev * hdev = conn - > hdev ;
struct hci_chan * chan ;
BT_DBG ( " %s conn %p " , hdev - > name , conn ) ;
2012-01-30 16:22:10 +04:00
chan = kzalloc ( sizeof ( struct hci_chan ) , GFP_KERNEL ) ;
2011-11-02 17:52:01 +04:00
if ( ! chan )
return NULL ;
chan - > conn = conn ;
skb_queue_head_init ( & chan - > data_q ) ;
2011-12-14 21:08:48 +04:00
list_add_rcu ( & chan - > list , & conn - > chan_list ) ;
2011-11-02 17:52:01 +04:00
return chan ;
}
int hci_chan_del ( struct hci_chan * chan )
{
struct hci_conn * conn = chan - > conn ;
struct hci_dev * hdev = conn - > hdev ;
BT_DBG ( " %s conn %p chan %p " , hdev - > name , conn , chan ) ;
2011-12-14 21:08:48 +04:00
list_del_rcu ( & chan - > list ) ;
synchronize_rcu ( ) ;
2011-11-02 17:52:01 +04:00
skb_queue_purge ( & chan - > data_q ) ;
kfree ( chan ) ;
return 0 ;
}
2011-12-14 19:02:51 +04:00
void hci_chan_list_flush ( struct hci_conn * conn )
2011-11-02 17:52:01 +04:00
{
2011-12-14 21:08:48 +04:00
struct hci_chan * chan ;
2011-11-02 17:52:01 +04:00
BT_DBG ( " conn %p " , conn ) ;
2011-12-14 21:08:48 +04:00
list_for_each_entry_rcu ( chan , & conn - > chan_list , list )
2011-11-02 17:52:01 +04:00
hci_chan_del ( chan ) ;
}