2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-09-27 18:26:09 +04:00
/*
Copyright ( c ) 2011 , 2012 Intel Corp .
*/
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci.h>
# include <net/bluetooth/hci_core.h>
2012-09-27 18:26:17 +04:00
# include <crypto/hash.h>
2012-09-27 18:26:09 +04:00
2015-07-24 12:10:50 +03:00
# include "hci_request.h"
2013-10-11 01:54:15 +04:00
# include "a2mp.h"
2013-10-11 01:54:14 +04:00
# include "amp.h"
2012-09-27 18:26:12 +04:00
/* Remote AMP Controllers interface */
2012-09-27 18:26:24 +04:00
void amp_ctrl_get ( struct amp_ctrl * ctrl )
2012-09-27 18:26:12 +04:00
{
BT_DBG ( " ctrl %p orig refcnt %d " , ctrl ,
2016-11-14 19:29:48 +03:00
kref_read ( & ctrl - > kref ) ) ;
2012-09-27 18:26:12 +04:00
kref_get ( & ctrl - > kref ) ;
}
static void amp_ctrl_destroy ( struct kref * kref )
{
struct amp_ctrl * ctrl = container_of ( kref , struct amp_ctrl , kref ) ;
BT_DBG ( " ctrl %p " , ctrl ) ;
kfree ( ctrl - > assoc ) ;
kfree ( ctrl ) ;
}
int amp_ctrl_put ( struct amp_ctrl * ctrl )
{
BT_DBG ( " ctrl %p orig refcnt %d " , ctrl ,
2016-11-14 19:29:48 +03:00
kref_read ( & ctrl - > kref ) ) ;
2012-09-27 18:26:12 +04:00
return kref_put ( & ctrl - > kref , & amp_ctrl_destroy ) ;
}
2012-10-05 17:56:55 +04:00
struct amp_ctrl * amp_ctrl_add ( struct amp_mgr * mgr , u8 id )
2012-09-27 18:26:12 +04:00
{
struct amp_ctrl * ctrl ;
ctrl = kzalloc ( sizeof ( * ctrl ) , GFP_KERNEL ) ;
if ( ! ctrl )
return NULL ;
2012-10-05 17:56:55 +04:00
kref_init ( & ctrl - > kref ) ;
ctrl - > id = id ;
2012-09-27 18:26:12 +04:00
mutex_lock ( & mgr - > amp_ctrls_lock ) ;
list_add ( & ctrl - > list , & mgr - > amp_ctrls ) ;
mutex_unlock ( & mgr - > amp_ctrls_lock ) ;
BT_DBG ( " mgr %p ctrl %p " , mgr , ctrl ) ;
return ctrl ;
}
void amp_ctrl_list_flush ( struct amp_mgr * mgr )
{
struct amp_ctrl * ctrl , * n ;
BT_DBG ( " mgr %p " , mgr ) ;
mutex_lock ( & mgr - > amp_ctrls_lock ) ;
list_for_each_entry_safe ( ctrl , n , & mgr - > amp_ctrls , list ) {
list_del ( & ctrl - > list ) ;
amp_ctrl_put ( ctrl ) ;
}
mutex_unlock ( & mgr - > amp_ctrls_lock ) ;
}
struct amp_ctrl * amp_ctrl_lookup ( struct amp_mgr * mgr , u8 id )
{
struct amp_ctrl * ctrl ;
BT_DBG ( " mgr %p id %d " , mgr , id ) ;
mutex_lock ( & mgr - > amp_ctrls_lock ) ;
list_for_each_entry ( ctrl , & mgr - > amp_ctrls , list ) {
if ( ctrl - > id = = id ) {
amp_ctrl_get ( ctrl ) ;
mutex_unlock ( & mgr - > amp_ctrls_lock ) ;
return ctrl ;
}
}
mutex_unlock ( & mgr - > amp_ctrls_lock ) ;
return NULL ;
}
2012-09-27 18:26:11 +04:00
/* Physical Link interface */
static u8 __next_handle ( struct amp_mgr * mgr )
{
if ( + + mgr - > handle = = 0 )
mgr - > handle = 1 ;
return mgr - > handle ;
}
struct hci_conn * phylink_add ( struct hci_dev * hdev , struct amp_mgr * mgr ,
2012-10-05 17:56:56 +04:00
u8 remote_id , bool out )
2012-09-27 18:26:11 +04:00
{
2013-10-13 13:23:38 +04:00
bdaddr_t * dst = & mgr - > l2cap_conn - > hcon - > dst ;
2012-09-27 18:26:11 +04:00
struct hci_conn * hcon ;
2014-07-16 12:56:07 +04:00
u8 role = out ? HCI_ROLE_MASTER : HCI_ROLE_SLAVE ;
2012-09-27 18:26:11 +04:00
2014-07-16 12:56:07 +04:00
hcon = hci_conn_add ( hdev , AMP_LINK , dst , role ) ;
2012-09-27 18:26:11 +04:00
if ( ! hcon )
return NULL ;
2012-10-05 17:56:56 +04:00
BT_DBG ( " hcon %p dst %pMR " , hcon , dst ) ;
2012-09-27 18:26:11 +04:00
hcon - > state = BT_CONNECT ;
hcon - > attempt + + ;
hcon - > handle = __next_handle ( mgr ) ;
hcon - > remote_id = remote_id ;
2012-10-18 14:16:19 +04:00
hcon - > amp_mgr = amp_mgr_get ( mgr ) ;
2012-09-27 18:26:11 +04:00
return hcon ;
}
2012-09-27 18:26:17 +04:00
/* AMP crypto key generation interface */
static int hmac_sha256 ( u8 * key , u8 ksize , char * plaintext , u8 psize , u8 * output )
{
struct crypto_shash * tfm ;
2014-11-10 16:53:45 +03:00
struct shash_desc * shash ;
2014-07-20 19:43:07 +04:00
int ret ;
2012-09-27 18:26:17 +04:00
if ( ! ksize )
return - EINVAL ;
tfm = crypto_alloc_shash ( " hmac(sha256) " , 0 , 0 ) ;
if ( IS_ERR ( tfm ) ) {
BT_DBG ( " crypto_alloc_ahash failed: err %ld " , PTR_ERR ( tfm ) ) ;
return PTR_ERR ( tfm ) ;
}
ret = crypto_shash_setkey ( tfm , key , ksize ) ;
if ( ret ) {
BT_DBG ( " crypto_ahash_setkey failed: err %d " , ret ) ;
2014-11-10 16:53:45 +03:00
goto failed ;
}
2012-09-27 18:26:17 +04:00
2014-11-10 16:53:45 +03:00
shash = kzalloc ( sizeof ( * shash ) + crypto_shash_descsize ( tfm ) ,
GFP_KERNEL ) ;
if ( ! shash ) {
ret = - ENOMEM ;
goto failed ;
2012-09-27 18:26:17 +04:00
}
2014-11-10 16:53:45 +03:00
shash - > tfm = tfm ;
ret = crypto_shash_digest ( shash , plaintext , psize , output ) ;
kfree ( shash ) ;
failed :
2012-09-27 18:26:17 +04:00
crypto_free_shash ( tfm ) ;
return ret ;
}
2012-09-27 18:26:18 +04:00
int phylink_gen_key ( struct hci_conn * conn , u8 * data , u8 * len , u8 * type )
{
struct hci_dev * hdev = conn - > hdev ;
struct link_key * key ;
u8 keybuf [ HCI_AMP_LINK_KEY_SIZE ] ;
u8 gamp_key [ HCI_AMP_LINK_KEY_SIZE ] ;
int err ;
if ( ! hci_conn_check_link_mode ( conn ) )
return - EACCES ;
BT_DBG ( " conn %p key_type %d " , conn , conn - > key_type ) ;
/* Legacy key */
if ( conn - > key_type < 3 ) {
2017-10-30 12:42:59 +03:00
bt_dev_err ( hdev , " legacy key type %d " , conn - > key_type ) ;
2012-09-27 18:26:18 +04:00
return - EACCES ;
}
* type = conn - > key_type ;
* len = HCI_AMP_LINK_KEY_SIZE ;
key = hci_find_link_key ( hdev , & conn - > dst ) ;
2012-10-05 17:56:53 +04:00
if ( ! key ) {
BT_DBG ( " No Link key for conn %p dst %pMR " , conn , & conn - > dst ) ;
return - EACCES ;
}
2012-09-27 18:26:18 +04:00
/* BR/EDR Link Key concatenated together with itself */
memcpy ( & keybuf [ 0 ] , key - > val , HCI_LINK_KEY_SIZE ) ;
memcpy ( & keybuf [ HCI_LINK_KEY_SIZE ] , key - > val , HCI_LINK_KEY_SIZE ) ;
/* Derive Generic AMP Link Key (gamp) */
err = hmac_sha256 ( keybuf , HCI_AMP_LINK_KEY_SIZE , " gamp " , 4 , gamp_key ) ;
if ( err ) {
2017-10-30 12:42:59 +03:00
bt_dev_err ( hdev , " could not derive Generic AMP Key: err %d " , err ) ;
2012-09-27 18:26:18 +04:00
return err ;
}
if ( conn - > key_type = = HCI_LK_DEBUG_COMBINATION ) {
BT_DBG ( " Use Generic AMP Key (gamp) " ) ;
memcpy ( data , gamp_key , HCI_AMP_LINK_KEY_SIZE ) ;
return err ;
}
/* Derive Dedicated AMP Link Key: "802b" is 802.11 PAL keyID */
return hmac_sha256 ( gamp_key , HCI_AMP_LINK_KEY_SIZE , " 802b " , 4 , data ) ;
}
2015-07-24 12:10:50 +03:00
static void read_local_amp_assoc_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode , struct sk_buff * skb )
{
struct hci_rp_read_local_amp_assoc * rp = ( void * ) skb - > data ;
struct amp_assoc * assoc = & hdev - > loc_assoc ;
size_t rem_len , frag_len ;
BT_DBG ( " %s status 0x%2.2x " , hdev - > name , rp - > status ) ;
if ( rp - > status )
goto send_rsp ;
frag_len = skb - > len - sizeof ( * rp ) ;
rem_len = __le16_to_cpu ( rp - > rem_len ) ;
if ( rem_len > frag_len ) {
BT_DBG ( " frag_len %zu rem_len %zu " , frag_len , rem_len ) ;
memcpy ( assoc - > data + assoc - > offset , rp - > frag , frag_len ) ;
assoc - > offset + = frag_len ;
/* Read other fragments */
amp_read_loc_assoc_frag ( hdev , rp - > phy_handle ) ;
return ;
}
memcpy ( assoc - > data + assoc - > offset , rp - > frag , rem_len ) ;
assoc - > len = assoc - > offset + rem_len ;
assoc - > offset = 0 ;
send_rsp :
/* Send A2MP Rsp when all fragments are received */
a2mp_send_getampassoc_rsp ( hdev , rp - > status ) ;
a2mp_send_create_phy_link_req ( hdev , rp - > status ) ;
}
2012-09-27 18:26:09 +04:00
void amp_read_loc_assoc_frag ( struct hci_dev * hdev , u8 phy_handle )
{
struct hci_cp_read_local_amp_assoc cp ;
struct amp_assoc * loc_assoc = & hdev - > loc_assoc ;
2015-07-24 12:10:50 +03:00
struct hci_request req ;
2017-03-01 18:10:53 +03:00
int err ;
2012-09-27 18:26:09 +04:00
BT_DBG ( " %s handle %d " , hdev - > name , phy_handle ) ;
cp . phy_handle = phy_handle ;
cp . max_len = cpu_to_le16 ( hdev - > amp_assoc_size ) ;
cp . len_so_far = cpu_to_le16 ( loc_assoc - > offset ) ;
2015-07-24 12:10:50 +03:00
hci_req_init ( & req , hdev ) ;
hci_req_add ( & req , HCI_OP_READ_LOCAL_AMP_ASSOC , sizeof ( cp ) , & cp ) ;
err = hci_req_run_skb ( & req , read_local_amp_assoc_complete ) ;
if ( err < 0 )
a2mp_send_getampassoc_rsp ( hdev , A2MP_STATUS_INVALID_CTRL_ID ) ;
2012-09-27 18:26:09 +04:00
}
void amp_read_loc_assoc ( struct hci_dev * hdev , struct amp_mgr * mgr )
{
struct hci_cp_read_local_amp_assoc cp ;
2015-07-24 12:10:50 +03:00
struct hci_request req ;
2017-03-01 18:10:53 +03:00
int err ;
2012-09-27 18:26:09 +04:00
memset ( & hdev - > loc_assoc , 0 , sizeof ( struct amp_assoc ) ) ;
memset ( & cp , 0 , sizeof ( cp ) ) ;
cp . max_len = cpu_to_le16 ( hdev - > amp_assoc_size ) ;
2012-12-07 16:59:08 +04:00
set_bit ( READ_LOC_AMP_ASSOC , & mgr - > state ) ;
2015-07-24 12:10:50 +03:00
hci_req_init ( & req , hdev ) ;
hci_req_add ( & req , HCI_OP_READ_LOCAL_AMP_ASSOC , sizeof ( cp ) , & cp ) ;
2017-03-01 18:10:53 +03:00
err = hci_req_run_skb ( & req , read_local_amp_assoc_complete ) ;
2015-07-24 12:10:50 +03:00
if ( err < 0 )
a2mp_send_getampassoc_rsp ( hdev , A2MP_STATUS_INVALID_CTRL_ID ) ;
2012-09-27 18:26:09 +04:00
}
2012-09-27 18:26:19 +04:00
2012-09-27 18:26:22 +04:00
void amp_read_loc_assoc_final_data ( struct hci_dev * hdev ,
struct hci_conn * hcon )
{
struct hci_cp_read_local_amp_assoc cp ;
struct amp_mgr * mgr = hcon - > amp_mgr ;
2015-07-24 12:10:50 +03:00
struct hci_request req ;
2017-03-01 18:10:53 +03:00
int err ;
2012-09-27 18:26:22 +04:00
cp . phy_handle = hcon - > handle ;
cp . len_so_far = cpu_to_le16 ( 0 ) ;
cp . max_len = cpu_to_le16 ( hdev - > amp_assoc_size ) ;
2012-12-07 16:59:08 +04:00
set_bit ( READ_LOC_AMP_ASSOC_FINAL , & mgr - > state ) ;
2012-09-27 18:26:22 +04:00
/* Read Local AMP Assoc final link information data */
2015-07-24 12:10:50 +03:00
hci_req_init ( & req , hdev ) ;
hci_req_add ( & req , HCI_OP_READ_LOCAL_AMP_ASSOC , sizeof ( cp ) , & cp ) ;
2017-03-01 18:10:53 +03:00
err = hci_req_run_skb ( & req , read_local_amp_assoc_complete ) ;
2015-07-24 12:10:50 +03:00
if ( err < 0 )
a2mp_send_getampassoc_rsp ( hdev , A2MP_STATUS_INVALID_CTRL_ID ) ;
}
static void write_remote_amp_assoc_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode , struct sk_buff * skb )
{
struct hci_rp_write_remote_amp_assoc * rp = ( void * ) skb - > data ;
BT_DBG ( " %s status 0x%2.2x phy_handle 0x%2.2x " ,
hdev - > name , rp - > status , rp - > phy_handle ) ;
if ( rp - > status )
return ;
amp_write_rem_assoc_continue ( hdev , rp - > phy_handle ) ;
2012-09-27 18:26:22 +04:00
}
2012-09-27 18:26:20 +04:00
/* Write AMP Assoc data fragments, returns true with last fragment written*/
static bool amp_write_rem_assoc_frag ( struct hci_dev * hdev ,
struct hci_conn * hcon )
{
struct hci_cp_write_remote_amp_assoc * cp ;
struct amp_mgr * mgr = hcon - > amp_mgr ;
struct amp_ctrl * ctrl ;
2015-07-24 12:10:50 +03:00
struct hci_request req ;
2012-09-27 18:26:20 +04:00
u16 frag_len , len ;
ctrl = amp_ctrl_lookup ( mgr , hcon - > remote_id ) ;
if ( ! ctrl )
return false ;
if ( ! ctrl - > assoc_rem_len ) {
BT_DBG ( " all fragments are written " ) ;
ctrl - > assoc_rem_len = ctrl - > assoc_len ;
ctrl - > assoc_len_so_far = 0 ;
amp_ctrl_put ( ctrl ) ;
return true ;
}
frag_len = min_t ( u16 , 248 , ctrl - > assoc_rem_len ) ;
len = frag_len + sizeof ( * cp ) ;
cp = kzalloc ( len , GFP_KERNEL ) ;
if ( ! cp ) {
amp_ctrl_put ( ctrl ) ;
return false ;
}
BT_DBG ( " hcon %p ctrl %p frag_len %u assoc_len %u rem_len %u " ,
hcon , ctrl , frag_len , ctrl - > assoc_len , ctrl - > assoc_rem_len ) ;
cp - > phy_handle = hcon - > handle ;
cp - > len_so_far = cpu_to_le16 ( ctrl - > assoc_len_so_far ) ;
cp - > rem_len = cpu_to_le16 ( ctrl - > assoc_rem_len ) ;
memcpy ( cp - > frag , ctrl - > assoc , frag_len ) ;
ctrl - > assoc_len_so_far + = frag_len ;
ctrl - > assoc_rem_len - = frag_len ;
amp_ctrl_put ( ctrl ) ;
2015-07-24 12:10:50 +03:00
hci_req_init ( & req , hdev ) ;
2015-08-01 15:30:08 +03:00
hci_req_add ( & req , HCI_OP_WRITE_REMOTE_AMP_ASSOC , len , cp ) ;
2015-07-24 12:10:50 +03:00
hci_req_run_skb ( & req , write_remote_amp_assoc_complete ) ;
2012-09-27 18:26:20 +04:00
kfree ( cp ) ;
return false ;
}
void amp_write_rem_assoc_continue ( struct hci_dev * hdev , u8 handle )
{
struct hci_conn * hcon ;
BT_DBG ( " %s phy handle 0x%2.2x " , hdev - > name , handle ) ;
hcon = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( ! hcon )
return ;
2012-12-07 16:59:05 +04:00
/* Send A2MP create phylink rsp when all fragments are written */
if ( amp_write_rem_assoc_frag ( hdev , hcon ) )
a2mp_send_create_phy_link_rsp ( hdev , 0 ) ;
2012-09-27 18:26:20 +04:00
}
void amp_write_remote_assoc ( struct hci_dev * hdev , u8 handle )
{
struct hci_conn * hcon ;
BT_DBG ( " %s phy handle 0x%2.2x " , hdev - > name , handle ) ;
hcon = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( ! hcon )
return ;
BT_DBG ( " %s phy handle 0x%2.2x hcon %p " , hdev - > name , handle , hcon ) ;
amp_write_rem_assoc_frag ( hdev , hcon ) ;
}
2015-07-24 12:11:01 +03:00
static void create_phylink_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode )
{
struct hci_cp_create_phy_link * cp ;
BT_DBG ( " %s status 0x%2.2x " , hdev - > name , status ) ;
cp = hci_sent_cmd_data ( hdev , HCI_OP_CREATE_PHY_LINK ) ;
if ( ! cp )
return ;
hci_dev_lock ( hdev ) ;
if ( status ) {
struct hci_conn * hcon ;
hcon = hci_conn_hash_lookup_handle ( hdev , cp - > phy_handle ) ;
if ( hcon )
hci_conn_del ( hcon ) ;
} else {
amp_write_remote_assoc ( hdev , cp - > phy_handle ) ;
}
hci_dev_unlock ( hdev ) ;
}
2012-09-27 18:26:19 +04:00
void amp_create_phylink ( struct hci_dev * hdev , struct amp_mgr * mgr ,
struct hci_conn * hcon )
{
struct hci_cp_create_phy_link cp ;
2015-07-24 12:11:01 +03:00
struct hci_request req ;
2012-09-27 18:26:19 +04:00
cp . phy_handle = hcon - > handle ;
BT_DBG ( " %s hcon %p phy handle 0x%2.2x " , hdev - > name , hcon ,
hcon - > handle ) ;
if ( phylink_gen_key ( mgr - > l2cap_conn - > hcon , cp . key , & cp . key_len ,
& cp . key_type ) ) {
BT_DBG ( " Cannot create link key " ) ;
return ;
}
2015-07-24 12:11:01 +03:00
hci_req_init ( & req , hdev ) ;
hci_req_add ( & req , HCI_OP_CREATE_PHY_LINK , sizeof ( cp ) , & cp ) ;
hci_req_run ( & req , create_phylink_complete ) ;
}
static void accept_phylink_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode )
{
struct hci_cp_accept_phy_link * cp ;
BT_DBG ( " %s status 0x%2.2x " , hdev - > name , status ) ;
if ( status )
return ;
cp = hci_sent_cmd_data ( hdev , HCI_OP_ACCEPT_PHY_LINK ) ;
if ( ! cp )
return ;
amp_write_remote_assoc ( hdev , cp - > phy_handle ) ;
2012-09-27 18:26:19 +04:00
}
2012-09-27 18:26:23 +04:00
void amp_accept_phylink ( struct hci_dev * hdev , struct amp_mgr * mgr ,
struct hci_conn * hcon )
{
struct hci_cp_accept_phy_link cp ;
2015-07-24 12:11:01 +03:00
struct hci_request req ;
2012-09-27 18:26:23 +04:00
cp . phy_handle = hcon - > handle ;
BT_DBG ( " %s hcon %p phy handle 0x%2.2x " , hdev - > name , hcon ,
hcon - > handle ) ;
if ( phylink_gen_key ( mgr - > l2cap_conn - > hcon , cp . key , & cp . key_len ,
& cp . key_type ) ) {
BT_DBG ( " Cannot create link key " ) ;
return ;
}
2015-07-24 12:11:01 +03:00
hci_req_init ( & req , hdev ) ;
hci_req_add ( & req , HCI_OP_ACCEPT_PHY_LINK , sizeof ( cp ) , & cp ) ;
hci_req_run ( & req , accept_phylink_complete ) ;
2012-09-27 18:26:23 +04:00
}
2012-10-31 17:46:30 +04:00
2012-10-31 17:46:36 +04:00
void amp_physical_cfm ( struct hci_conn * bredr_hcon , struct hci_conn * hs_hcon )
{
struct hci_dev * bredr_hdev = hci_dev_hold ( bredr_hcon - > hdev ) ;
struct amp_mgr * mgr = hs_hcon - > amp_mgr ;
struct l2cap_chan * bredr_chan ;
BT_DBG ( " bredr_hcon %p hs_hcon %p mgr %p " , bredr_hcon , hs_hcon , mgr ) ;
if ( ! bredr_hdev | | ! mgr | | ! mgr - > bredr_chan )
return ;
bredr_chan = mgr - > bredr_chan ;
2012-11-14 19:39:30 +04:00
l2cap_chan_lock ( bredr_chan ) ;
2012-10-31 17:46:36 +04:00
set_bit ( FLAG_EFS_ENABLE , & bredr_chan - > flags ) ;
2012-11-01 17:37:03 +04:00
bredr_chan - > remote_amp_id = hs_hcon - > remote_id ;
2012-11-20 19:16:20 +04:00
bredr_chan - > local_amp_id = hs_hcon - > hdev - > id ;
2012-10-31 17:46:36 +04:00
bredr_chan - > hs_hcon = hs_hcon ;
bredr_chan - > conn - > mtu = hs_hcon - > hdev - > block_mtu ;
2012-11-14 19:39:30 +04:00
__l2cap_physical_cfm ( bredr_chan , 0 ) ;
l2cap_chan_unlock ( bredr_chan ) ;
2012-10-31 17:46:36 +04:00
hci_dev_put ( bredr_hdev ) ;
}
2012-10-31 17:46:30 +04:00
void amp_create_logical_link ( struct l2cap_chan * chan )
{
2012-12-07 16:59:06 +04:00
struct hci_conn * hs_hcon = chan - > hs_hcon ;
2012-10-31 17:46:30 +04:00
struct hci_cp_create_accept_logical_link cp ;
struct hci_dev * hdev ;
2013-10-13 13:23:38 +04:00
BT_DBG ( " chan %p hs_hcon %p dst %pMR " , chan , hs_hcon ,
& chan - > conn - > hcon - > dst ) ;
2012-10-31 17:46:30 +04:00
2012-12-07 16:59:06 +04:00
if ( ! hs_hcon )
2012-10-31 17:46:30 +04:00
return ;
hdev = hci_dev_hold ( chan - > hs_hcon - > hdev ) ;
if ( ! hdev )
return ;
2012-12-07 16:59:06 +04:00
cp . phy_handle = hs_hcon - > handle ;
2012-10-31 17:46:30 +04:00
cp . tx_flow_spec . id = chan - > local_id ;
cp . tx_flow_spec . stype = chan - > local_stype ;
cp . tx_flow_spec . msdu = cpu_to_le16 ( chan - > local_msdu ) ;
cp . tx_flow_spec . sdu_itime = cpu_to_le32 ( chan - > local_sdu_itime ) ;
cp . tx_flow_spec . acc_lat = cpu_to_le32 ( chan - > local_acc_lat ) ;
cp . tx_flow_spec . flush_to = cpu_to_le32 ( chan - > local_flush_to ) ;
cp . rx_flow_spec . id = chan - > remote_id ;
cp . rx_flow_spec . stype = chan - > remote_stype ;
cp . rx_flow_spec . msdu = cpu_to_le16 ( chan - > remote_msdu ) ;
cp . rx_flow_spec . sdu_itime = cpu_to_le32 ( chan - > remote_sdu_itime ) ;
cp . rx_flow_spec . acc_lat = cpu_to_le32 ( chan - > remote_acc_lat ) ;
cp . rx_flow_spec . flush_to = cpu_to_le32 ( chan - > remote_flush_to ) ;
2012-12-07 16:59:06 +04:00
if ( hs_hcon - > out )
2012-10-31 17:46:30 +04:00
hci_send_cmd ( hdev , HCI_OP_CREATE_LOGICAL_LINK , sizeof ( cp ) ,
& cp ) ;
else
hci_send_cmd ( hdev , HCI_OP_ACCEPT_LOGICAL_LINK , sizeof ( cp ) ,
& cp ) ;
hci_dev_put ( hdev ) ;
}
2012-10-31 17:46:31 +04:00
2012-10-31 17:46:34 +04:00
void amp_disconnect_logical_link ( struct hci_chan * hchan )
{
struct hci_conn * hcon = hchan - > conn ;
struct hci_cp_disconn_logical_link cp ;
if ( hcon - > state ! = BT_CONNECTED ) {
BT_DBG ( " hchan %p not connected " , hchan ) ;
return ;
}
cp . log_handle = cpu_to_le16 ( hchan - > handle ) ;
hci_send_cmd ( hcon - > hdev , HCI_OP_DISCONN_LOGICAL_LINK , sizeof ( cp ) , & cp ) ;
}
2012-10-31 17:46:31 +04:00
void amp_destroy_logical_link ( struct hci_chan * hchan , u8 reason )
{
BT_DBG ( " hchan %p " , hchan ) ;
hci_chan_del ( hchan ) ;
}