2010-12-08 01:21:06 +03:00
/*
BlueZ - Bluetooth protocol stack for Linux
2012-02-17 16:50:39 +04:00
2010-12-08 01:21:06 +03:00
Copyright ( C ) 2010 Nokia Corporation
2012-02-17 16:50:39 +04:00
Copyright ( C ) 2011 - 2012 Intel Corporation
2010-12-08 01:21:06 +03:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation ;
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS
OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS .
IN NO EVENT SHALL THE COPYRIGHT HOLDER ( S ) AND AUTHOR ( S ) BE LIABLE FOR ANY
CLAIM , OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES , OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
ALL LIABILITY , INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS ,
COPYRIGHTS , TRADEMARKS OR OTHER RIGHTS , RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED .
*/
/* Bluetooth HCI Management interface */
2011-05-27 17:12:25 +04:00
# include <linux/module.h>
2010-12-08 01:21:06 +03:00
# include <asm/unaligned.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
2015-02-20 14:26:23 +03:00
# include <net/bluetooth/hci_sock.h>
2014-05-20 10:45:47 +04:00
# include <net/bluetooth/l2cap.h>
2010-12-08 01:21:06 +03:00
# include <net/bluetooth/mgmt.h>
2013-10-11 01:54:16 +04:00
2014-12-19 14:40:20 +03:00
# include "hci_request.h"
2013-10-11 01:54:16 +04:00
# include "smp.h"
2015-03-17 14:48:48 +03:00
# include "mgmt_util.h"
2010-12-08 01:21:06 +03:00
2012-02-17 16:39:28 +04:00
# define MGMT_VERSION 1
2016-09-18 13:50:07 +03:00
# define MGMT_REVISION 14
2010-12-13 22:07:04 +03:00
2012-02-13 18:59:33 +04:00
static const u16 mgmt_commands [ ] = {
MGMT_OP_READ_INDEX_LIST ,
MGMT_OP_READ_INFO ,
MGMT_OP_SET_POWERED ,
MGMT_OP_SET_DISCOVERABLE ,
MGMT_OP_SET_CONNECTABLE ,
MGMT_OP_SET_FAST_CONNECTABLE ,
2014-07-30 10:22:23 +04:00
MGMT_OP_SET_BONDABLE ,
2012-02-13 18:59:33 +04:00
MGMT_OP_SET_LINK_SECURITY ,
MGMT_OP_SET_SSP ,
MGMT_OP_SET_HS ,
MGMT_OP_SET_LE ,
MGMT_OP_SET_DEV_CLASS ,
MGMT_OP_SET_LOCAL_NAME ,
MGMT_OP_ADD_UUID ,
MGMT_OP_REMOVE_UUID ,
MGMT_OP_LOAD_LINK_KEYS ,
MGMT_OP_LOAD_LONG_TERM_KEYS ,
MGMT_OP_DISCONNECT ,
MGMT_OP_GET_CONNECTIONS ,
MGMT_OP_PIN_CODE_REPLY ,
MGMT_OP_PIN_CODE_NEG_REPLY ,
MGMT_OP_SET_IO_CAPABILITY ,
MGMT_OP_PAIR_DEVICE ,
MGMT_OP_CANCEL_PAIR_DEVICE ,
MGMT_OP_UNPAIR_DEVICE ,
MGMT_OP_USER_CONFIRM_REPLY ,
MGMT_OP_USER_CONFIRM_NEG_REPLY ,
MGMT_OP_USER_PASSKEY_REPLY ,
MGMT_OP_USER_PASSKEY_NEG_REPLY ,
MGMT_OP_READ_LOCAL_OOB_DATA ,
MGMT_OP_ADD_REMOTE_OOB_DATA ,
MGMT_OP_REMOVE_REMOTE_OOB_DATA ,
MGMT_OP_START_DISCOVERY ,
MGMT_OP_STOP_DISCOVERY ,
MGMT_OP_CONFIRM_NAME ,
MGMT_OP_BLOCK_DEVICE ,
MGMT_OP_UNBLOCK_DEVICE ,
2012-03-12 07:00:29 +04:00
MGMT_OP_SET_DEVICE_ID ,
2013-09-25 14:26:10 +04:00
MGMT_OP_SET_ADVERTISING ,
2013-10-02 14:43:14 +04:00
MGMT_OP_SET_BREDR ,
2013-10-02 15:41:30 +04:00
MGMT_OP_SET_STATIC_ADDRESS ,
2013-10-12 01:44:58 +04:00
MGMT_OP_SET_SCAN_PARAMS ,
2014-01-10 14:07:22 +04:00
MGMT_OP_SET_SECURE_CONN ,
2014-01-31 23:55:22 +04:00
MGMT_OP_SET_DEBUG_KEYS ,
2014-02-23 21:42:27 +04:00
MGMT_OP_SET_PRIVACY ,
2014-02-18 12:19:35 +04:00
MGMT_OP_LOAD_IRKS ,
2014-05-14 15:43:03 +04:00
MGMT_OP_GET_CONN_INFO ,
2014-06-28 18:54:07 +04:00
MGMT_OP_GET_CLOCK_INFO ,
2014-06-29 21:44:03 +04:00
MGMT_OP_ADD_DEVICE ,
MGMT_OP_REMOVE_DEVICE ,
2014-07-02 18:37:29 +04:00
MGMT_OP_LOAD_CONN_PARAM ,
2014-07-03 00:10:52 +04:00
MGMT_OP_READ_UNCONF_INDEX_LIST ,
2014-07-04 02:46:56 +04:00
MGMT_OP_READ_CONFIG_INFO ,
2014-07-04 20:11:55 +04:00
MGMT_OP_SET_EXTERNAL_CONFIG ,
2014-07-06 14:11:15 +04:00
MGMT_OP_SET_PUBLIC_ADDRESS ,
2014-12-05 12:55:59 +03:00
MGMT_OP_START_SERVICE_DISCOVERY ,
2015-03-15 08:43:19 +03:00
MGMT_OP_READ_LOCAL_OOB_EXT_DATA ,
2015-03-15 05:27:57 +03:00
MGMT_OP_READ_EXT_INDEX_LIST ,
2015-03-15 06:53:25 +03:00
MGMT_OP_READ_ADV_FEATURES ,
2015-03-24 01:57:12 +03:00
MGMT_OP_ADD_ADVERTISING ,
2015-03-24 01:57:13 +03:00
MGMT_OP_REMOVE_ADVERTISING ,
2015-11-19 18:16:43 +03:00
MGMT_OP_GET_ADV_SIZE_INFO ,
2016-01-05 14:19:32 +03:00
MGMT_OP_START_LIMITED_DISCOVERY ,
2016-09-01 17:46:23 +03:00
MGMT_OP_READ_EXT_INFO ,
2016-09-18 13:50:03 +03:00
MGMT_OP_SET_APPEARANCE ,
2012-02-13 18:59:33 +04:00
} ;
static const u16 mgmt_events [ ] = {
MGMT_EV_CONTROLLER_ERROR ,
MGMT_EV_INDEX_ADDED ,
MGMT_EV_INDEX_REMOVED ,
MGMT_EV_NEW_SETTINGS ,
MGMT_EV_CLASS_OF_DEV_CHANGED ,
MGMT_EV_LOCAL_NAME_CHANGED ,
MGMT_EV_NEW_LINK_KEY ,
MGMT_EV_NEW_LONG_TERM_KEY ,
MGMT_EV_DEVICE_CONNECTED ,
MGMT_EV_DEVICE_DISCONNECTED ,
MGMT_EV_CONNECT_FAILED ,
MGMT_EV_PIN_CODE_REQUEST ,
MGMT_EV_USER_CONFIRM_REQUEST ,
MGMT_EV_USER_PASSKEY_REQUEST ,
MGMT_EV_AUTH_FAILED ,
MGMT_EV_DEVICE_FOUND ,
MGMT_EV_DISCOVERING ,
MGMT_EV_DEVICE_BLOCKED ,
MGMT_EV_DEVICE_UNBLOCKED ,
MGMT_EV_DEVICE_UNPAIRED ,
2012-09-06 19:39:26 +04:00
MGMT_EV_PASSKEY_NOTIFY ,
2014-02-22 09:35:30 +04:00
MGMT_EV_NEW_IRK ,
2014-03-09 23:19:17 +04:00
MGMT_EV_NEW_CSRK ,
2014-06-30 00:28:34 +04:00
MGMT_EV_DEVICE_ADDED ,
MGMT_EV_DEVICE_REMOVED ,
2014-07-02 01:10:11 +04:00
MGMT_EV_NEW_CONN_PARAM ,
2014-07-02 23:30:54 +04:00
MGMT_EV_UNCONF_INDEX_ADDED ,
2014-07-02 23:30:55 +04:00
MGMT_EV_UNCONF_INDEX_REMOVED ,
2014-07-04 21:06:23 +04:00
MGMT_EV_NEW_CONFIG_OPTIONS ,
2015-03-15 05:27:56 +03:00
MGMT_EV_EXT_INDEX_ADDED ,
MGMT_EV_EXT_INDEX_REMOVED ,
2015-03-17 02:11:21 +03:00
MGMT_EV_LOCAL_OOB_DATA_UPDATED ,
2015-03-24 01:57:12 +03:00
MGMT_EV_ADVERTISING_ADDED ,
MGMT_EV_ADVERTISING_REMOVED ,
2016-09-01 17:46:23 +03:00
MGMT_EV_EXT_INFO_CHANGED ,
2012-02-13 18:59:33 +04:00
} ;
2015-03-25 03:31:03 +03:00
static const u16 mgmt_untrusted_commands [ ] = {
MGMT_OP_READ_INDEX_LIST ,
MGMT_OP_READ_INFO ,
MGMT_OP_READ_UNCONF_INDEX_LIST ,
MGMT_OP_READ_CONFIG_INFO ,
MGMT_OP_READ_EXT_INDEX_LIST ,
2016-09-01 17:46:23 +03:00
MGMT_OP_READ_EXT_INFO ,
2015-03-25 03:31:03 +03:00
} ;
static const u16 mgmt_untrusted_events [ ] = {
MGMT_EV_INDEX_ADDED ,
MGMT_EV_INDEX_REMOVED ,
MGMT_EV_NEW_SETTINGS ,
MGMT_EV_CLASS_OF_DEV_CHANGED ,
MGMT_EV_LOCAL_NAME_CHANGED ,
MGMT_EV_UNCONF_INDEX_ADDED ,
MGMT_EV_UNCONF_INDEX_REMOVED ,
MGMT_EV_NEW_CONFIG_OPTIONS ,
MGMT_EV_EXT_INDEX_ADDED ,
MGMT_EV_EXT_INDEX_REMOVED ,
2016-09-01 17:46:23 +03:00
MGMT_EV_EXT_INFO_CHANGED ,
2015-03-25 03:31:03 +03:00
} ;
2012-03-02 02:32:37 +04:00
# define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
2011-12-15 02:47:39 +04:00
2015-01-27 13:55:52 +03:00
# define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
" \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 "
2011-11-11 20:10:00 +04:00
/* HCI to MGMT error code conversion table */
static u8 mgmt_status_table [ ] = {
MGMT_STATUS_SUCCESS ,
MGMT_STATUS_UNKNOWN_COMMAND , /* Unknown Command */
MGMT_STATUS_NOT_CONNECTED , /* No Connection */
MGMT_STATUS_FAILED , /* Hardware Failure */
MGMT_STATUS_CONNECT_FAILED , /* Page Timeout */
MGMT_STATUS_AUTH_FAILED , /* Authentication Failed */
2014-01-13 19:15:53 +04:00
MGMT_STATUS_AUTH_FAILED , /* PIN or Key Missing */
2011-11-11 20:10:00 +04:00
MGMT_STATUS_NO_RESOURCES , /* Memory Full */
MGMT_STATUS_TIMEOUT , /* Connection Timeout */
MGMT_STATUS_NO_RESOURCES , /* Max Number of Connections */
MGMT_STATUS_NO_RESOURCES , /* Max Number of SCO Connections */
MGMT_STATUS_ALREADY_CONNECTED , /* ACL Connection Exists */
MGMT_STATUS_BUSY , /* Command Disallowed */
MGMT_STATUS_NO_RESOURCES , /* Rejected Limited Resources */
MGMT_STATUS_REJECTED , /* Rejected Security */
MGMT_STATUS_REJECTED , /* Rejected Personal */
MGMT_STATUS_TIMEOUT , /* Host Timeout */
MGMT_STATUS_NOT_SUPPORTED , /* Unsupported Feature */
MGMT_STATUS_INVALID_PARAMS , /* Invalid Parameters */
MGMT_STATUS_DISCONNECTED , /* OE User Ended Connection */
MGMT_STATUS_NO_RESOURCES , /* OE Low Resources */
MGMT_STATUS_DISCONNECTED , /* OE Power Off */
MGMT_STATUS_DISCONNECTED , /* Connection Terminated */
MGMT_STATUS_BUSY , /* Repeated Attempts */
MGMT_STATUS_REJECTED , /* Pairing Not Allowed */
MGMT_STATUS_FAILED , /* Unknown LMP PDU */
MGMT_STATUS_NOT_SUPPORTED , /* Unsupported Remote Feature */
MGMT_STATUS_REJECTED , /* SCO Offset Rejected */
MGMT_STATUS_REJECTED , /* SCO Interval Rejected */
MGMT_STATUS_REJECTED , /* Air Mode Rejected */
MGMT_STATUS_INVALID_PARAMS , /* Invalid LMP Parameters */
MGMT_STATUS_FAILED , /* Unspecified Error */
MGMT_STATUS_NOT_SUPPORTED , /* Unsupported LMP Parameter Value */
MGMT_STATUS_FAILED , /* Role Change Not Allowed */
MGMT_STATUS_TIMEOUT , /* LMP Response Timeout */
MGMT_STATUS_FAILED , /* LMP Error Transaction Collision */
MGMT_STATUS_FAILED , /* LMP PDU Not Allowed */
MGMT_STATUS_REJECTED , /* Encryption Mode Not Accepted */
MGMT_STATUS_FAILED , /* Unit Link Key Used */
MGMT_STATUS_NOT_SUPPORTED , /* QoS Not Supported */
MGMT_STATUS_TIMEOUT , /* Instant Passed */
MGMT_STATUS_NOT_SUPPORTED , /* Pairing Not Supported */
MGMT_STATUS_FAILED , /* Transaction Collision */
MGMT_STATUS_INVALID_PARAMS , /* Unacceptable Parameter */
MGMT_STATUS_REJECTED , /* QoS Rejected */
MGMT_STATUS_NOT_SUPPORTED , /* Classification Not Supported */
MGMT_STATUS_REJECTED , /* Insufficient Security */
MGMT_STATUS_INVALID_PARAMS , /* Parameter Out Of Range */
MGMT_STATUS_BUSY , /* Role Switch Pending */
MGMT_STATUS_FAILED , /* Slot Violation */
MGMT_STATUS_FAILED , /* Role Switch Failed */
MGMT_STATUS_INVALID_PARAMS , /* EIR Too Large */
MGMT_STATUS_NOT_SUPPORTED , /* Simple Pairing Not Supported */
MGMT_STATUS_BUSY , /* Host Busy Pairing */
MGMT_STATUS_REJECTED , /* Rejected, No Suitable Channel */
MGMT_STATUS_BUSY , /* Controller Busy */
MGMT_STATUS_INVALID_PARAMS , /* Unsuitable Connection Interval */
MGMT_STATUS_TIMEOUT , /* Directed Advertising Timeout */
MGMT_STATUS_AUTH_FAILED , /* Terminated Due to MIC Failure */
MGMT_STATUS_CONNECT_FAILED , /* Connection Establishment Failed */
MGMT_STATUS_CONNECT_FAILED , /* MAC Connection Failed */
} ;
static u8 mgmt_status ( u8 hci_status )
{
if ( hci_status < ARRAY_SIZE ( mgmt_status_table ) )
return mgmt_status_table [ hci_status ] ;
return MGMT_STATUS_FAILED ;
}
2015-03-15 05:27:59 +03:00
static int mgmt_index_event ( u16 event , struct hci_dev * hdev , void * data ,
u16 len , int flag )
2015-03-15 05:27:55 +03:00
{
2015-03-15 05:27:59 +03:00
return mgmt_send_event ( event , hdev , HCI_CHANNEL_CONTROL , data , len ,
flag , NULL ) ;
2015-03-15 05:27:55 +03:00
}
2015-03-17 02:11:21 +03:00
static int mgmt_limited_event ( u16 event , struct hci_dev * hdev , void * data ,
u16 len , int flag , struct sock * skip_sk )
{
return mgmt_send_event ( event , hdev , HCI_CHANNEL_CONTROL , data , len ,
flag , skip_sk ) ;
}
2015-03-06 22:08:56 +03:00
static int mgmt_event ( u16 event , struct hci_dev * hdev , void * data , u16 len ,
struct sock * skip_sk )
{
return mgmt_send_event ( event , hdev , HCI_CHANNEL_CONTROL , data , len ,
2015-03-15 05:27:59 +03:00
HCI_SOCK_TRUSTED , skip_sk ) ;
2015-03-06 22:08:56 +03:00
}
2015-10-21 18:02:59 +03:00
static u8 le_addr_type ( u8 mgmt_addr_type )
{
if ( mgmt_addr_type = = BDADDR_LE_PUBLIC )
return ADDR_LE_DEV_PUBLIC ;
else
return ADDR_LE_DEV_RANDOM ;
}
2016-08-27 21:23:39 +03:00
void mgmt_fill_version_info ( void * ver )
{
struct mgmt_rp_read_version * rp = ver ;
rp - > version = MGMT_VERSION ;
rp - > revision = cpu_to_le16 ( MGMT_REVISION ) ;
}
2012-03-08 08:25:00 +04:00
static int read_version ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 data_len )
2011-01-22 07:46:43 +03:00
{
struct mgmt_rp_read_version rp ;
BT_DBG ( " sock %p " , sk ) ;
2016-08-27 21:23:39 +03:00
mgmt_fill_version_info ( & rp ) ;
2011-01-22 07:46:43 +03:00
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , MGMT_INDEX_NONE , MGMT_OP_READ_VERSION , 0 ,
& rp , sizeof ( rp ) ) ;
2011-01-22 07:46:43 +03:00
}
2012-03-08 08:25:00 +04:00
static int read_commands ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 data_len )
2012-02-13 18:59:33 +04:00
{
struct mgmt_rp_read_commands * rp ;
2015-03-25 03:31:03 +03:00
u16 num_commands , num_events ;
2012-02-13 18:59:33 +04:00
size_t rp_size ;
int i , err ;
BT_DBG ( " sock %p " , sk ) ;
2015-03-25 03:31:03 +03:00
if ( hci_sock_test_flag ( sk , HCI_SOCK_TRUSTED ) ) {
num_commands = ARRAY_SIZE ( mgmt_commands ) ;
num_events = ARRAY_SIZE ( mgmt_events ) ;
} else {
num_commands = ARRAY_SIZE ( mgmt_untrusted_commands ) ;
num_events = ARRAY_SIZE ( mgmt_untrusted_events ) ;
}
2012-02-13 18:59:33 +04:00
rp_size = sizeof ( * rp ) + ( ( num_commands + num_events ) * sizeof ( u16 ) ) ;
rp = kmalloc ( rp_size , GFP_KERNEL ) ;
if ( ! rp )
return - ENOMEM ;
2014-03-12 21:52:35 +04:00
rp - > num_commands = cpu_to_le16 ( num_commands ) ;
rp - > num_events = cpu_to_le16 ( num_events ) ;
2012-02-13 18:59:33 +04:00
2015-03-25 03:31:03 +03:00
if ( hci_sock_test_flag ( sk , HCI_SOCK_TRUSTED ) ) {
__le16 * opcode = rp - > opcodes ;
for ( i = 0 ; i < num_commands ; i + + , opcode + + )
put_unaligned_le16 ( mgmt_commands [ i ] , opcode ) ;
2012-02-13 18:59:33 +04:00
2015-03-25 03:31:03 +03:00
for ( i = 0 ; i < num_events ; i + + , opcode + + )
put_unaligned_le16 ( mgmt_events [ i ] , opcode ) ;
} else {
__le16 * opcode = rp - > opcodes ;
for ( i = 0 ; i < num_commands ; i + + , opcode + + )
put_unaligned_le16 ( mgmt_untrusted_commands [ i ] , opcode ) ;
for ( i = 0 ; i < num_events ; i + + , opcode + + )
put_unaligned_le16 ( mgmt_untrusted_events [ i ] , opcode ) ;
}
2012-02-13 18:59:33 +04:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , MGMT_INDEX_NONE , MGMT_OP_READ_COMMANDS , 0 ,
rp , rp_size ) ;
2012-02-13 18:59:33 +04:00
kfree ( rp ) ;
return err ;
}
2012-03-08 08:25:00 +04:00
static int read_index_list ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 data_len )
2010-12-13 22:07:05 +03:00
{
struct mgmt_rp_read_index_list * rp ;
2011-11-01 12:58:56 +04:00
struct hci_dev * d ;
2011-01-22 07:46:43 +03:00
size_t rp_len ;
2010-12-13 22:07:05 +03:00
u16 count ;
2012-10-19 21:10:46 +04:00
int err ;
2010-12-13 22:07:05 +03:00
BT_DBG ( " sock %p " , sk ) ;
read_lock ( & hci_dev_list_lock ) ;
count = 0 ;
2012-07-19 18:03:40 +04:00
list_for_each_entry ( d , & hci_dev_list , list ) {
2016-07-05 15:30:14 +03:00
if ( d - > dev_type = = HCI_PRIMARY & &
2015-03-13 12:11:00 +03:00
! hci_dev_test_flag ( d , HCI_UNCONFIGURED ) )
2013-10-06 19:25:01 +04:00
count + + ;
2010-12-13 22:07:05 +03:00
}
2011-01-22 07:46:43 +03:00
rp_len = sizeof ( * rp ) + ( 2 * count ) ;
rp = kmalloc ( rp_len , GFP_ATOMIC ) ;
if ( ! rp ) {
2011-01-14 02:18:49 +03:00
read_unlock ( & hci_dev_list_lock ) ;
2010-12-13 22:07:05 +03:00
return - ENOMEM ;
2011-01-14 02:18:49 +03:00
}
2010-12-13 22:07:05 +03:00
2012-10-19 21:10:46 +04:00
count = 0 ;
2011-11-01 12:58:56 +04:00
list_for_each_entry ( d , & hci_dev_list , list ) {
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( d , HCI_SETUP ) | |
hci_dev_test_flag ( d , HCI_CONFIG ) | |
hci_dev_test_flag ( d , HCI_USER_CHANNEL ) )
2010-12-15 14:53:18 +03:00
continue ;
2014-07-03 00:10:52 +04:00
/* Devices marked as raw-only are neither configured
* nor unconfigured controllers .
*/
if ( test_bit ( HCI_QUIRK_RAW_DEVICE , & d - > quirks ) )
2014-06-29 14:13:05 +04:00
continue ;
2016-07-05 15:30:14 +03:00
if ( d - > dev_type = = HCI_PRIMARY & &
2015-03-13 12:11:00 +03:00
! hci_dev_test_flag ( d , HCI_UNCONFIGURED ) ) {
2013-10-06 19:25:01 +04:00
rp - > index [ count + + ] = cpu_to_le16 ( d - > id ) ;
BT_DBG ( " Added hci%u " , d - > id ) ;
}
2010-12-13 22:07:05 +03:00
}
2012-10-19 21:10:46 +04:00
rp - > num_controllers = cpu_to_le16 ( count ) ;
rp_len = sizeof ( * rp ) + ( 2 * count ) ;
2010-12-13 22:07:05 +03:00
read_unlock ( & hci_dev_list_lock ) ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , MGMT_INDEX_NONE , MGMT_OP_READ_INDEX_LIST ,
0 , rp , rp_len ) ;
2010-12-13 22:07:05 +03:00
2011-01-22 07:46:43 +03:00
kfree ( rp ) ;
return err ;
2010-12-13 22:07:05 +03:00
}
2014-07-03 00:10:52 +04:00
static int read_unconf_index_list ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
struct mgmt_rp_read_unconf_index_list * rp ;
struct hci_dev * d ;
size_t rp_len ;
u16 count ;
int err ;
BT_DBG ( " sock %p " , sk ) ;
read_lock ( & hci_dev_list_lock ) ;
count = 0 ;
list_for_each_entry ( d , & hci_dev_list , list ) {
2016-07-05 15:30:14 +03:00
if ( d - > dev_type = = HCI_PRIMARY & &
2015-03-13 12:11:00 +03:00
hci_dev_test_flag ( d , HCI_UNCONFIGURED ) )
2014-07-03 00:10:52 +04:00
count + + ;
}
rp_len = sizeof ( * rp ) + ( 2 * count ) ;
rp = kmalloc ( rp_len , GFP_ATOMIC ) ;
if ( ! rp ) {
read_unlock ( & hci_dev_list_lock ) ;
return - ENOMEM ;
}
count = 0 ;
list_for_each_entry ( d , & hci_dev_list , list ) {
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( d , HCI_SETUP ) | |
hci_dev_test_flag ( d , HCI_CONFIG ) | |
hci_dev_test_flag ( d , HCI_USER_CHANNEL ) )
2014-07-03 00:10:52 +04:00
continue ;
/* Devices marked as raw-only are neither configured
* nor unconfigured controllers .
*/
if ( test_bit ( HCI_QUIRK_RAW_DEVICE , & d - > quirks ) )
continue ;
2016-07-05 15:30:14 +03:00
if ( d - > dev_type = = HCI_PRIMARY & &
2015-03-13 12:11:00 +03:00
hci_dev_test_flag ( d , HCI_UNCONFIGURED ) ) {
2014-07-03 00:10:52 +04:00
rp - > index [ count + + ] = cpu_to_le16 ( d - > id ) ;
BT_DBG ( " Added hci%u " , d - > id ) ;
}
}
rp - > num_controllers = cpu_to_le16 ( count ) ;
rp_len = sizeof ( * rp ) + ( 2 * count ) ;
read_unlock ( & hci_dev_list_lock ) ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , MGMT_INDEX_NONE ,
MGMT_OP_READ_UNCONF_INDEX_LIST , 0 , rp , rp_len ) ;
2014-07-03 00:10:52 +04:00
kfree ( rp ) ;
return err ;
}
2015-03-15 05:27:57 +03:00
static int read_ext_index_list ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
struct mgmt_rp_read_ext_index_list * rp ;
struct hci_dev * d ;
size_t rp_len ;
u16 count ;
int err ;
BT_DBG ( " sock %p " , sk ) ;
read_lock ( & hci_dev_list_lock ) ;
count = 0 ;
list_for_each_entry ( d , & hci_dev_list , list ) {
2016-07-05 15:30:14 +03:00
if ( d - > dev_type = = HCI_PRIMARY | | d - > dev_type = = HCI_AMP )
2015-03-15 05:27:57 +03:00
count + + ;
}
rp_len = sizeof ( * rp ) + ( sizeof ( rp - > entry [ 0 ] ) * count ) ;
rp = kmalloc ( rp_len , GFP_ATOMIC ) ;
if ( ! rp ) {
read_unlock ( & hci_dev_list_lock ) ;
return - ENOMEM ;
}
count = 0 ;
list_for_each_entry ( d , & hci_dev_list , list ) {
if ( hci_dev_test_flag ( d , HCI_SETUP ) | |
hci_dev_test_flag ( d , HCI_CONFIG ) | |
hci_dev_test_flag ( d , HCI_USER_CHANNEL ) )
continue ;
/* Devices marked as raw-only are neither configured
* nor unconfigured controllers .
*/
if ( test_bit ( HCI_QUIRK_RAW_DEVICE , & d - > quirks ) )
continue ;
2016-07-05 15:30:14 +03:00
if ( d - > dev_type = = HCI_PRIMARY ) {
2015-03-15 05:27:57 +03:00
if ( hci_dev_test_flag ( d , HCI_UNCONFIGURED ) )
rp - > entry [ count ] . type = 0x01 ;
else
rp - > entry [ count ] . type = 0x00 ;
} else if ( d - > dev_type = = HCI_AMP ) {
rp - > entry [ count ] . type = 0x02 ;
} else {
continue ;
}
rp - > entry [ count ] . bus = d - > bus ;
rp - > entry [ count + + ] . index = cpu_to_le16 ( d - > id ) ;
BT_DBG ( " Added hci%u " , d - > id ) ;
}
rp - > num_controllers = cpu_to_le16 ( count ) ;
rp_len = sizeof ( * rp ) + ( sizeof ( rp - > entry [ 0 ] ) * count ) ;
read_unlock ( & hci_dev_list_lock ) ;
/* If this command is called at least once, then all the
* default index and unconfigured index events are disabled
* and from now on only extended index events are used .
*/
hci_sock_set_flag ( sk , HCI_MGMT_EXT_INDEX_EVENTS ) ;
hci_sock_clear_flag ( sk , HCI_MGMT_INDEX_EVENTS ) ;
hci_sock_clear_flag ( sk , HCI_MGMT_UNCONF_INDEX_EVENTS ) ;
err = mgmt_cmd_complete ( sk , MGMT_INDEX_NONE ,
MGMT_OP_READ_EXT_INDEX_LIST , 0 , rp , rp_len ) ;
kfree ( rp ) ;
return err ;
}
2014-07-04 20:11:55 +04:00
static bool is_configured ( struct hci_dev * hdev )
{
if ( test_bit ( HCI_QUIRK_EXTERNAL_CONFIG , & hdev - > quirks ) & &
2015-03-13 12:11:00 +03:00
! hci_dev_test_flag ( hdev , HCI_EXT_CONFIGURED ) )
2014-07-04 20:11:55 +04:00
return false ;
if ( test_bit ( HCI_QUIRK_INVALID_BDADDR , & hdev - > quirks ) & &
! bacmp ( & hdev - > public_addr , BDADDR_ANY ) )
return false ;
return true ;
}
2014-07-04 18:54:37 +04:00
static __le32 get_missing_options ( struct hci_dev * hdev )
{
u32 options = 0 ;
2014-07-04 20:11:55 +04:00
if ( test_bit ( HCI_QUIRK_EXTERNAL_CONFIG , & hdev - > quirks ) & &
2015-03-13 12:11:00 +03:00
! hci_dev_test_flag ( hdev , HCI_EXT_CONFIGURED ) )
2014-07-04 19:23:33 +04:00
options | = MGMT_OPTION_EXTERNAL_CONFIG ;
2014-07-04 18:54:37 +04:00
if ( test_bit ( HCI_QUIRK_INVALID_BDADDR , & hdev - > quirks ) & &
! bacmp ( & hdev - > public_addr , BDADDR_ANY ) )
options | = MGMT_OPTION_PUBLIC_ADDRESS ;
return cpu_to_le32 ( options ) ;
}
2014-07-04 21:06:23 +04:00
static int new_options ( struct hci_dev * hdev , struct sock * skip )
{
__le32 options = get_missing_options ( hdev ) ;
2016-08-29 07:19:46 +03:00
return mgmt_limited_event ( MGMT_EV_NEW_CONFIG_OPTIONS , hdev , & options ,
sizeof ( options ) , HCI_MGMT_OPTION_EVENTS , skip ) ;
2014-07-04 21:06:23 +04:00
}
2014-07-04 20:11:55 +04:00
static int send_options_rsp ( struct sock * sk , u16 opcode , struct hci_dev * hdev )
{
__le32 options = get_missing_options ( hdev ) ;
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , opcode , 0 , & options ,
sizeof ( options ) ) ;
2014-07-04 20:11:55 +04:00
}
2014-07-04 02:46:56 +04:00
static int read_config_info ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
struct mgmt_rp_read_config_info rp ;
2014-07-04 18:54:37 +04:00
u32 options = 0 ;
2014-07-04 02:46:56 +04:00
BT_DBG ( " sock %p %s " , sk , hdev - > name ) ;
hci_dev_lock ( hdev ) ;
memset ( & rp , 0 , sizeof ( rp ) ) ;
rp . manufacturer = cpu_to_le16 ( hdev - > manufacturer ) ;
2014-07-04 18:54:37 +04:00
2014-07-04 19:23:33 +04:00
if ( test_bit ( HCI_QUIRK_EXTERNAL_CONFIG , & hdev - > quirks ) )
options | = MGMT_OPTION_EXTERNAL_CONFIG ;
2014-07-04 02:46:56 +04:00
if ( hdev - > set_bdaddr )
2014-07-04 18:54:37 +04:00
options | = MGMT_OPTION_PUBLIC_ADDRESS ;
rp . supported_options = cpu_to_le32 ( options ) ;
rp . missing_options = get_missing_options ( hdev ) ;
2014-07-04 02:46:56 +04:00
hci_dev_unlock ( hdev ) ;
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_READ_CONFIG_INFO , 0 ,
& rp , sizeof ( rp ) ) ;
2014-07-04 02:46:56 +04:00
}
2011-12-15 02:47:35 +04:00
static u32 get_supported_settings ( struct hci_dev * hdev )
{
u32 settings = 0 ;
settings | = MGMT_SETTING_POWERED ;
2014-07-30 10:22:23 +04:00
settings | = MGMT_SETTING_BONDABLE ;
2014-01-31 23:55:21 +04:00
settings | = MGMT_SETTING_DEBUG_KEYS ;
2014-07-08 17:07:34 +04:00
settings | = MGMT_SETTING_CONNECTABLE ;
settings | = MGMT_SETTING_DISCOVERABLE ;
2011-12-15 02:47:35 +04:00
2012-07-24 22:03:46 +04:00
if ( lmp_bredr_capable ( hdev ) ) {
2013-03-16 02:07:06 +04:00
if ( hdev - > hci_ver > = BLUETOOTH_VER_1_2 )
settings | = MGMT_SETTING_FAST_CONNECTABLE ;
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_BREDR ;
settings | = MGMT_SETTING_LINK_SECURITY ;
2013-10-11 20:48:47 +04:00
if ( lmp_ssp_capable ( hdev ) ) {
settings | = MGMT_SETTING_SSP ;
settings | = MGMT_SETTING_HS ;
}
2014-01-10 14:07:22 +04:00
2015-01-01 01:43:18 +03:00
if ( lmp_sc_capable ( hdev ) )
2014-01-10 14:07:22 +04:00
settings | = MGMT_SETTING_SECURE_CONN ;
2013-10-02 09:59:22 +04:00
}
2012-02-21 00:47:49 +04:00
2013-09-25 14:26:09 +04:00
if ( lmp_le_capable ( hdev ) ) {
2012-05-03 09:12:31 +04:00
settings | = MGMT_SETTING_LE ;
2013-09-25 14:26:09 +04:00
settings | = MGMT_SETTING_ADVERTISING ;
2014-05-26 12:23:35 +04:00
settings | = MGMT_SETTING_SECURE_CONN ;
2014-02-22 21:06:35 +04:00
settings | = MGMT_SETTING_PRIVACY ;
2015-03-06 21:11:21 +03:00
settings | = MGMT_SETTING_STATIC_ADDRESS ;
2013-09-25 14:26:09 +04:00
}
2011-12-15 02:47:35 +04:00
2014-07-04 19:23:33 +04:00
if ( test_bit ( HCI_QUIRK_EXTERNAL_CONFIG , & hdev - > quirks ) | |
hdev - > set_bdaddr )
2014-07-04 02:46:56 +04:00
settings | = MGMT_SETTING_CONFIGURATION ;
2011-12-15 02:47:35 +04:00
return settings ;
}
static u32 get_current_settings ( struct hci_dev * hdev )
{
u32 settings = 0 ;
2012-02-21 19:15:41 +04:00
if ( hdev_is_powered ( hdev ) )
2012-02-21 15:14:25 +04:00
settings | = MGMT_SETTING_POWERED ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_CONNECTABLE ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_CONNECTABLE ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_FAST_CONNECTABLE ) )
2013-03-16 02:07:08 +04:00
settings | = MGMT_SETTING_FAST_CONNECTABLE ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_DISCOVERABLE ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_DISCOVERABLE ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_BONDABLE ) )
2014-07-30 10:22:23 +04:00
settings | = MGMT_SETTING_BONDABLE ;
2011-12-15 02:47:35 +04:00
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_BREDR ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_LE ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LINK_SECURITY ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_LINK_SECURITY ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_SSP_ENABLED ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_SSP ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_HS_ENABLED ) )
2012-02-21 01:50:38 +04:00
settings | = MGMT_SETTING_HS ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_ADVERTISING ) )
2013-09-25 14:26:09 +04:00
settings | = MGMT_SETTING_ADVERTISING ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_SC_ENABLED ) )
2014-01-10 14:07:22 +04:00
settings | = MGMT_SETTING_SECURE_CONN ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_KEEP_DEBUG_KEYS ) )
2014-01-31 23:55:21 +04:00
settings | = MGMT_SETTING_DEBUG_KEYS ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_PRIVACY ) )
2014-02-22 21:06:35 +04:00
settings | = MGMT_SETTING_PRIVACY ;
2015-03-06 21:11:21 +03:00
/* The current setting for static address has two purposes. The
* first is to indicate if the static address will be used and
* the second is to indicate if it is actually set .
*
* This means if the static address is not configured , this flag
2015-03-26 04:32:13 +03:00
* will never be set . If the address is configured , then if the
2015-03-06 21:11:21 +03:00
* address is actually used decides if the flag is set or not .
*
* For single mode LE only controllers and dual - mode controllers
* with BR / EDR disabled , the existence of the static address will
* be evaluated .
*/
2015-03-13 20:20:35 +03:00
if ( hci_dev_test_flag ( hdev , HCI_FORCE_STATIC_ADDR ) | |
2015-03-13 12:11:00 +03:00
! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) | |
2015-03-06 21:11:21 +03:00
! bacmp ( & hdev - > bdaddr , BDADDR_ANY ) ) {
if ( bacmp ( & hdev - > static_addr , BDADDR_ANY ) )
settings | = MGMT_SETTING_STATIC_ADDRESS ;
}
2011-12-15 02:47:35 +04:00
return settings ;
}
2015-03-17 14:48:47 +03:00
static struct mgmt_pending_cmd * pending_find ( u16 opcode , struct hci_dev * hdev )
{
return mgmt_pending_find ( HCI_CHANNEL_CONTROL , opcode , hdev ) ;
}
static struct mgmt_pending_cmd * pending_find_data ( u16 opcode ,
struct hci_dev * hdev ,
const void * data )
{
return mgmt_pending_find_data ( HCI_CHANNEL_CONTROL , opcode , hdev , data ) ;
}
2015-11-18 13:49:20 +03:00
u8 mgmt_get_adv_discov_flags ( struct hci_dev * hdev )
2013-10-20 20:00:07 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-10-20 20:00:07 +04:00
/* If there's a pending mgmt command the flags will not yet have
* their final values , so check for this first .
*/
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_DISCOVERABLE , hdev ) ;
2013-10-20 20:00:07 +04:00
if ( cmd ) {
struct mgmt_mode * cp = cmd - > param ;
if ( cp - > val = = 0x01 )
return LE_AD_GENERAL ;
else if ( cp - > val = = 0x02 )
return LE_AD_LIMITED ;
} else {
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LIMITED_DISCOVERABLE ) )
2013-10-20 20:00:07 +04:00
return LE_AD_LIMITED ;
2015-03-13 12:11:00 +03:00
else if ( hci_dev_test_flag ( hdev , HCI_DISCOVERABLE ) )
2013-10-20 20:00:07 +04:00
return LE_AD_GENERAL ;
}
return 0 ;
}
2015-11-18 13:49:20 +03:00
bool mgmt_get_connectable ( struct hci_dev * hdev )
2015-03-26 04:53:46 +03:00
{
struct mgmt_pending_cmd * cmd ;
2013-10-15 17:33:52 +04:00
2015-03-26 04:53:46 +03:00
/* If there's a pending mgmt command the flag will not yet have
* it ' s final value , so check for this first .
*/
cmd = pending_find ( MGMT_OP_SET_CONNECTABLE , hdev ) ;
if ( cmd ) {
struct mgmt_mode * cp = cmd - > param ;
2013-10-15 17:33:52 +04:00
2015-03-26 04:53:46 +03:00
return cp - > val ;
2013-10-15 17:33:52 +04:00
}
2015-03-26 04:53:46 +03:00
return hci_dev_test_flag ( hdev , HCI_CONNECTABLE ) ;
}
2013-10-15 17:33:52 +04:00
2011-12-15 02:47:39 +04:00
static void service_cache_off ( struct work_struct * work )
{
struct hci_dev * hdev = container_of ( work , struct hci_dev ,
2012-03-08 08:25:00 +04:00
service_cache . work ) ;
2013-03-16 02:06:52 +04:00
struct hci_request req ;
2011-12-15 02:47:39 +04:00
2015-03-13 12:11:05 +03:00
if ( ! hci_dev_test_and_clear_flag ( hdev , HCI_SERVICE_CACHE ) )
2011-12-15 02:47:39 +04:00
return ;
2013-03-16 02:06:52 +04:00
hci_req_init ( & req , hdev ) ;
2011-12-15 02:47:39 +04:00
hci_dev_lock ( hdev ) ;
2015-11-25 17:15:42 +03:00
__hci_req_update_eir ( & req ) ;
2015-11-22 20:00:22 +03:00
__hci_req_update_class ( & req ) ;
2011-12-15 02:47:39 +04:00
hci_dev_unlock ( hdev ) ;
2013-03-16 02:06:52 +04:00
hci_req_run ( & req , NULL ) ;
2011-12-15 02:47:39 +04:00
}
2014-02-23 21:42:20 +04:00
static void rpa_expired ( struct work_struct * work )
{
struct hci_dev * hdev = container_of ( work , struct hci_dev ,
rpa_expired . work ) ;
struct hci_request req ;
BT_DBG ( " " ) ;
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_RPA_EXPIRED ) ;
2014-02-23 21:42:20 +04:00
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_ADVERTISING ) )
2014-02-23 21:42:20 +04:00
return ;
/* The generation of a new RPA and programming it into the
2015-11-18 13:49:20 +03:00
* controller happens in the hci_req_enable_advertising ( )
* function .
2014-02-23 21:42:20 +04:00
*/
hci_req_init ( & req , hdev ) ;
2015-11-18 13:49:20 +03:00
__hci_req_enable_advertising ( & req ) ;
2014-02-23 21:42:20 +04:00
hci_req_run ( & req , NULL ) ;
}
2012-02-28 08:17:26 +04:00
static void mgmt_init_hdev ( struct sock * sk , struct hci_dev * hdev )
2011-12-15 02:47:39 +04:00
{
2015-03-13 12:11:06 +03:00
if ( hci_dev_test_and_set_flag ( hdev , HCI_MGMT ) )
2012-02-28 08:17:26 +04:00
return ;
2012-03-02 21:55:56 +04:00
INIT_DELAYED_WORK ( & hdev - > service_cache , service_cache_off ) ;
2014-02-23 21:42:20 +04:00
INIT_DELAYED_WORK ( & hdev - > rpa_expired , rpa_expired ) ;
2011-12-15 02:47:39 +04:00
2012-03-02 21:55:56 +04:00
/* Non-mgmt controlled devices get this bit set
* implicitly so that pairing works for them , however
* for mgmt we require user - space to explicitly enable
* it
*/
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_BONDABLE ) ;
2011-12-15 02:47:39 +04:00
}
2012-02-28 19:18:30 +04:00
static int read_controller_info ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void * data , u16 data_len )
2010-12-08 01:21:06 +03:00
{
2011-01-22 07:46:43 +03:00
struct mgmt_rp_read_info rp ;
2010-12-13 22:07:06 +03:00
2012-02-28 08:13:32 +04:00
BT_DBG ( " sock %p %s " , sk , hdev - > name ) ;
2010-12-13 22:07:06 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2010-12-13 22:07:06 +03:00
2011-03-16 15:29:36 +03:00
memset ( & rp , 0 , sizeof ( rp ) ) ;
2011-12-15 02:47:35 +04:00
bacpy ( & rp . bdaddr , & hdev - > bdaddr ) ;
2010-12-13 22:07:06 +03:00
2011-12-15 02:47:35 +04:00
rp . version = hdev - > hci_ver ;
2012-03-14 20:08:46 +04:00
rp . manufacturer = cpu_to_le16 ( hdev - > manufacturer ) ;
2011-12-15 02:47:35 +04:00
rp . supported_settings = cpu_to_le32 ( get_supported_settings ( hdev ) ) ;
rp . current_settings = cpu_to_le32 ( get_current_settings ( hdev ) ) ;
2010-12-13 22:07:06 +03:00
2011-01-22 07:46:43 +03:00
memcpy ( rp . dev_class , hdev - > dev_class , 3 ) ;
2010-12-13 22:07:06 +03:00
2011-03-16 15:29:36 +03:00
memcpy ( rp . name , hdev - > dev_name , sizeof ( hdev - > dev_name ) ) ;
2012-02-22 23:46:22 +04:00
memcpy ( rp . short_name , hdev - > short_name , sizeof ( hdev - > short_name ) ) ;
2011-03-16 15:29:36 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2010-12-08 01:21:06 +03:00
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_READ_INFO , 0 , & rp ,
sizeof ( rp ) ) ;
2010-12-08 01:21:06 +03:00
}
2016-09-19 21:25:53 +03:00
static u16 append_eir_data_to_buf ( struct hci_dev * hdev , u8 * eir )
{
u16 eir_len = 0 ;
size_t name_len ;
if ( hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) )
eir_len = eir_append_data ( eir , eir_len , EIR_CLASS_OF_DEV ,
hdev - > dev_class , 3 ) ;
2016-09-19 21:25:54 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) )
eir_len = eir_append_le16 ( eir , eir_len , EIR_APPEARANCE ,
hdev - > appearance ) ;
2016-09-19 21:25:53 +03:00
name_len = strlen ( hdev - > dev_name ) ;
eir_len = eir_append_data ( eir , eir_len , EIR_NAME_COMPLETE ,
hdev - > dev_name , name_len ) ;
name_len = strlen ( hdev - > short_name ) ;
eir_len = eir_append_data ( eir , eir_len , EIR_NAME_SHORT ,
hdev - > short_name , name_len ) ;
return eir_len ;
}
2016-09-01 17:46:23 +03:00
static int read_ext_controller_info ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
2016-09-19 21:25:52 +03:00
char buf [ 512 ] ;
struct mgmt_rp_read_ext_info * rp = ( void * ) buf ;
2016-09-19 21:25:53 +03:00
u16 eir_len ;
2016-09-01 17:46:23 +03:00
BT_DBG ( " sock %p %s " , sk , hdev - > name ) ;
2016-09-19 21:25:52 +03:00
memset ( & buf , 0 , sizeof ( buf ) ) ;
2016-09-01 17:46:23 +03:00
hci_dev_lock ( hdev ) ;
2016-09-19 21:25:52 +03:00
bacpy ( & rp - > bdaddr , & hdev - > bdaddr ) ;
rp - > version = hdev - > hci_ver ;
rp - > manufacturer = cpu_to_le16 ( hdev - > manufacturer ) ;
rp - > supported_settings = cpu_to_le32 ( get_supported_settings ( hdev ) ) ;
rp - > current_settings = cpu_to_le32 ( get_current_settings ( hdev ) ) ;
2016-09-01 17:46:23 +03:00
2016-09-19 21:25:53 +03:00
eir_len = append_eir_data_to_buf ( hdev , rp - > eir ) ;
2016-09-01 17:46:24 +03:00
rp - > eir_len = cpu_to_le16 ( eir_len ) ;
2016-09-01 17:46:23 +03:00
hci_dev_unlock ( hdev ) ;
/* If this command is called at least once, then the events
* for class of device and local name changes are disabled
* and only the new extended controller information event
* is used .
*/
hci_sock_set_flag ( sk , HCI_MGMT_EXT_INFO_EVENTS ) ;
hci_sock_clear_flag ( sk , HCI_MGMT_DEV_CLASS_EVENTS ) ;
hci_sock_clear_flag ( sk , HCI_MGMT_LOCAL_NAME_EVENTS ) ;
2016-09-01 17:46:24 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_READ_EXT_INFO , 0 , rp ,
sizeof ( * rp ) + eir_len ) ;
2016-09-01 17:46:23 +03:00
}
static int ext_info_changed ( struct hci_dev * hdev , struct sock * skip )
{
2016-09-19 21:25:55 +03:00
char buf [ 512 ] ;
struct mgmt_ev_ext_info_changed * ev = ( void * ) buf ;
u16 eir_len ;
memset ( buf , 0 , sizeof ( buf ) ) ;
2016-09-01 17:46:23 +03:00
2016-09-19 21:25:55 +03:00
eir_len = append_eir_data_to_buf ( hdev , ev - > eir ) ;
ev - > eir_len = cpu_to_le16 ( eir_len ) ;
2016-09-01 17:46:23 +03:00
2016-09-19 21:25:55 +03:00
return mgmt_limited_event ( MGMT_EV_EXT_INFO_CHANGED , hdev , ev ,
sizeof ( * ev ) + eir_len ,
HCI_MGMT_EXT_INFO_EVENTS , skip ) ;
2016-09-01 17:46:23 +03:00
}
2011-12-15 02:47:35 +04:00
static int send_settings_rsp ( struct sock * sk , u16 opcode , struct hci_dev * hdev )
2011-11-11 18:18:52 +04:00
{
2011-12-15 02:47:35 +04:00
__le32 settings = cpu_to_le32 ( get_current_settings ( hdev ) ) ;
2011-11-11 18:18:52 +04:00
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , opcode , 0 , & settings ,
sizeof ( settings ) ) ;
2011-11-11 18:18:52 +04:00
}
2015-01-12 00:50:44 +03:00
static void clean_up_hci_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
2014-02-24 16:52:22 +04:00
{
BT_DBG ( " %s status 0x%02x " , hdev - > name , status ) ;
2014-02-28 11:33:44 +04:00
if ( hci_conn_count ( hdev ) = = 0 ) {
cancel_delayed_work ( & hdev - > power_off ) ;
2014-02-24 16:52:22 +04:00
queue_work ( hdev - > req_workqueue , & hdev - > power_off . work ) ;
2014-02-28 11:33:44 +04:00
}
2014-02-24 16:52:22 +04:00
}
2015-11-18 13:49:20 +03:00
void mgmt_advertising_added ( struct sock * sk , struct hci_dev * hdev , u8 instance )
2015-03-24 01:57:15 +03:00
{
struct mgmt_ev_advertising_added ev ;
ev . instance = instance ;
mgmt_event ( MGMT_EV_ADVERTISING_ADDED , hdev , & ev , sizeof ( ev ) , sk ) ;
}
2015-11-18 13:49:20 +03:00
void mgmt_advertising_removed ( struct sock * sk , struct hci_dev * hdev ,
u8 instance )
2015-03-24 01:57:15 +03:00
{
struct mgmt_ev_advertising_removed ev ;
ev . instance = instance ;
mgmt_event ( MGMT_EV_ADVERTISING_REMOVED , hdev , & ev , sizeof ( ev ) , sk ) ;
}
2015-06-18 04:16:45 +03:00
static void cancel_adv_timeout ( struct hci_dev * hdev )
{
if ( hdev - > adv_instance_timeout ) {
hdev - > adv_instance_timeout = 0 ;
cancel_delayed_work ( & hdev - > adv_instance_expire ) ;
}
}
2014-02-24 16:52:22 +04:00
static int clean_up_hci_state ( struct hci_dev * hdev )
{
struct hci_request req ;
struct hci_conn * conn ;
2014-07-08 17:05:06 +04:00
bool discov_stopped ;
int err ;
2014-02-24 16:52:22 +04:00
hci_req_init ( & req , hdev ) ;
if ( test_bit ( HCI_ISCAN , & hdev - > flags ) | |
test_bit ( HCI_PSCAN , & hdev - > flags ) ) {
u8 scan = 0x00 ;
hci_req_add ( & req , HCI_OP_WRITE_SCAN_ENABLE , 1 , & scan ) ;
}
2016-08-28 20:53:34 +03:00
hci_req_clear_adv_instance ( hdev , NULL , NULL , 0x00 , false ) ;
2015-03-24 01:57:15 +03:00
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LE_ADV ) )
2015-11-18 13:49:20 +03:00
__hci_req_disable_advertising ( & req ) ;
2014-02-24 16:52:22 +04:00
2015-11-11 09:30:45 +03:00
discov_stopped = hci_req_stop_discovery ( & req ) ;
2014-02-24 16:52:22 +04:00
list_for_each_entry ( conn , & hdev - > conn_hash . list , list ) {
2015-10-22 10:49:38 +03:00
/* 0x15 == Terminated due to Power Off */
__hci_abort_conn ( & req , conn , 0x15 ) ;
2014-02-24 16:52:22 +04:00
}
2014-07-08 17:05:06 +04:00
err = hci_req_run ( & req , clean_up_hci_complete ) ;
if ( ! err & & discov_stopped )
hci_discovery_set_state ( hdev , DISCOVERY_STOPPING ) ;
return err ;
2014-02-24 16:52:22 +04:00
}
2012-02-28 08:13:32 +04:00
static int set_powered ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2010-12-16 11:17:38 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_mode * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2012-02-21 16:13:02 +04:00
int err ;
2010-12-16 11:17:38 +03:00
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2010-12-16 11:17:38 +03:00
2013-01-09 18:05:19 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_POWERED ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2010-12-16 11:17:38 +03:00
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_POWERED , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_POWERED ,
MGMT_STATUS_BUSY ) ;
2013-09-25 14:26:06 +04:00
goto failed ;
}
2012-02-21 16:13:02 +04:00
if ( ! ! cp - > val = = hdev_is_powered ( hdev ) ) {
2011-12-15 02:47:35 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_POWERED , hdev ) ;
2010-12-16 11:17:38 +03:00
goto failed ;
}
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_POWERED , hdev , data , len ) ;
2011-02-19 18:05:55 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
2010-12-16 11:17:38 +03:00
goto failed ;
2011-02-19 18:05:55 +03:00
}
2010-12-16 11:17:38 +03:00
2014-02-24 16:52:22 +04:00
if ( cp - > val ) {
2013-01-15 00:33:51 +04:00
queue_work ( hdev - > req_workqueue , & hdev - > power_on ) ;
2014-02-24 16:52:22 +04:00
err = 0 ;
} else {
/* Disconnect connections, stop scans, etc */
err = clean_up_hci_state ( hdev ) ;
2014-02-28 11:33:44 +04:00
if ( ! err )
queue_delayed_work ( hdev - > req_workqueue , & hdev - > power_off ,
HCI_POWER_OFF_TIMEOUT ) ;
2010-12-16 11:17:38 +03:00
2014-02-24 16:52:22 +04:00
/* ENODATA means there were no HCI commands queued */
if ( err = = - ENODATA ) {
2014-02-28 11:33:44 +04:00
cancel_delayed_work ( & hdev - > power_off ) ;
2014-02-24 16:52:22 +04:00
queue_work ( hdev - > req_workqueue , & hdev - > power_off . work ) ;
err = 0 ;
}
}
2010-12-16 11:17:38 +03:00
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-02-19 18:05:55 +03:00
return err ;
2010-12-16 11:17:38 +03:00
}
2012-02-21 18:55:31 +04:00
static int new_settings ( struct hci_dev * hdev , struct sock * skip )
{
2015-03-15 05:28:05 +03:00
__le32 ev = cpu_to_le32 ( get_current_settings ( hdev ) ) ;
2012-02-21 18:55:31 +04:00
2016-08-29 07:19:46 +03:00
return mgmt_limited_event ( MGMT_EV_NEW_SETTINGS , hdev , & ev ,
sizeof ( ev ) , HCI_MGMT_SETTING_EVENTS , skip ) ;
2012-02-21 18:55:31 +04:00
}
2014-07-09 14:28:26 +04:00
int mgmt_new_settings ( struct hci_dev * hdev )
{
return new_settings ( hdev , NULL ) ;
}
2013-09-25 14:26:07 +04:00
struct cmd_lookup {
struct sock * sk ;
struct hci_dev * hdev ;
u8 mgmt_status ;
} ;
2015-03-06 22:08:55 +03:00
static void settings_rsp ( struct mgmt_pending_cmd * cmd , void * data )
2013-09-25 14:26:07 +04:00
{
struct cmd_lookup * match = data ;
send_settings_rsp ( cmd - > sk , cmd - > opcode , match - > hdev ) ;
list_del ( & cmd - > list ) ;
if ( match - > sk = = NULL ) {
match - > sk = cmd - > sk ;
sock_hold ( match - > sk ) ;
}
mgmt_pending_free ( cmd ) ;
}
2015-03-06 22:08:55 +03:00
static void cmd_status_rsp ( struct mgmt_pending_cmd * cmd , void * data )
2013-09-25 14:26:07 +04:00
{
u8 * status = data ;
2015-03-06 22:08:53 +03:00
mgmt_cmd_status ( cmd - > sk , cmd - > index , cmd - > opcode , * status ) ;
2013-09-25 14:26:07 +04:00
mgmt_pending_remove ( cmd ) ;
}
2015-03-06 22:08:55 +03:00
static void cmd_complete_rsp ( struct mgmt_pending_cmd * cmd , void * data )
2014-12-05 14:36:00 +03:00
{
if ( cmd - > cmd_complete ) {
u8 * status = data ;
cmd - > cmd_complete ( cmd , * status ) ;
mgmt_pending_remove ( cmd ) ;
return ;
}
cmd_status_rsp ( cmd , data ) ;
}
2015-03-06 22:08:55 +03:00
static int generic_cmd_complete ( struct mgmt_pending_cmd * cmd , u8 status )
2014-12-05 14:36:02 +03:00
{
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode , status ,
cmd - > param , cmd - > param_len ) ;
2014-12-05 14:36:02 +03:00
}
2015-03-06 22:08:55 +03:00
static int addr_cmd_complete ( struct mgmt_pending_cmd * cmd , u8 status )
2014-12-05 14:36:03 +03:00
{
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode , status ,
cmd - > param , sizeof ( struct mgmt_addr_info ) ) ;
2014-12-05 14:36:03 +03:00
}
2013-10-02 16:45:22 +04:00
static u8 mgmt_bredr_support ( struct hci_dev * hdev )
{
if ( ! lmp_bredr_capable ( hdev ) )
return MGMT_STATUS_NOT_SUPPORTED ;
2015-03-13 12:11:00 +03:00
else if ( ! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) )
2013-10-02 16:45:22 +04:00
return MGMT_STATUS_REJECTED ;
else
return MGMT_STATUS_SUCCESS ;
}
static u8 mgmt_le_support ( struct hci_dev * hdev )
{
if ( ! lmp_le_capable ( hdev ) )
return MGMT_STATUS_NOT_SUPPORTED ;
2015-03-13 12:11:00 +03:00
else if ( ! hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) )
2013-10-02 16:45:22 +04:00
return MGMT_STATUS_REJECTED ;
else
return MGMT_STATUS_SUCCESS ;
}
2015-11-22 17:24:44 +03:00
void mgmt_set_discoverable_complete ( struct hci_dev * hdev , u8 status )
2013-10-14 22:15:27 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-10-14 22:15:27 +04:00
BT_DBG ( " status 0x%02x " , status ) ;
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_DISCOVERABLE , hdev ) ;
2013-10-14 22:15:27 +04:00
if ( ! cmd )
goto unlock ;
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
2015-03-06 22:08:53 +03:00
mgmt_cmd_status ( cmd - > sk , cmd - > index , cmd - > opcode , mgmt_err ) ;
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_LIMITED_DISCOVERABLE ) ;
2013-10-14 22:15:27 +04:00
goto remove_cmd ;
}
2015-11-22 17:24:44 +03:00
if ( hci_dev_test_flag ( hdev , HCI_DISCOVERABLE ) & &
hdev - > discov_timeout > 0 ) {
int to = msecs_to_jiffies ( hdev - > discov_timeout * 1000 ) ;
queue_delayed_work ( hdev - > req_workqueue , & hdev - > discov_off , to ) ;
2013-10-15 19:11:02 +04:00
}
2013-10-14 22:15:27 +04:00
send_settings_rsp ( cmd - > sk , MGMT_OP_SET_DISCOVERABLE , hdev ) ;
2015-11-22 17:24:44 +03:00
new_settings ( hdev , cmd - > sk ) ;
2013-10-15 17:33:57 +04:00
2013-10-14 22:15:27 +04:00
remove_cmd :
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
2012-02-28 08:13:32 +04:00
static int set_discoverable ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2010-12-29 17:00:25 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_set_discoverable * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2012-02-21 18:01:30 +04:00
u16 timeout ;
2010-12-29 17:00:25 +03:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2010-12-29 17:00:25 +03:00
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) & &
! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
MGMT_STATUS_REJECTED ) ;
2012-10-24 22:11:58 +04:00
2013-10-15 20:13:39 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 & & cp - > val ! = 0x02 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2012-03-13 07:31:08 +04:00
timeout = __le16_to_cpu ( cp - > timeout ) ;
2013-10-15 20:13:39 +04:00
/* Disabling discoverable requires that no timeout is set,
* and enabling limited discoverable requires a timeout .
*/
if ( ( cp - > val = = 0x00 & & timeout > 0 ) | |
( cp - > val = = 0x02 & & timeout = = 0 ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
MGMT_STATUS_INVALID_PARAMS ) ;
2010-12-29 17:00:25 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2010-12-29 17:00:25 +03:00
2012-02-21 18:01:30 +04:00
if ( ! hdev_is_powered ( hdev ) & & timeout > 0 ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
MGMT_STATUS_NOT_POWERED ) ;
2010-12-29 17:00:25 +03:00
goto failed ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_DISCOVERABLE , hdev ) | |
pending_find ( MGMT_OP_SET_CONNECTABLE , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
MGMT_STATUS_BUSY ) ;
2010-12-29 17:00:25 +03:00
goto failed ;
}
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_CONNECTABLE ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
MGMT_STATUS_REJECTED ) ;
2012-02-21 18:01:30 +04:00
goto failed ;
}
if ( ! hdev_is_powered ( hdev ) ) {
2012-02-21 21:40:05 +04:00
bool changed = false ;
2013-10-15 20:13:39 +04:00
/* Setting limited discoverable when powered off is
* not a valid operation since it requires a timeout
* and so no need to check HCI_LIMITED_DISCOVERABLE .
*/
2015-03-13 12:11:00 +03:00
if ( ! ! cp - > val ! = hci_dev_test_flag ( hdev , HCI_DISCOVERABLE ) ) {
2015-03-13 12:11:03 +03:00
hci_dev_change_flag ( hdev , HCI_DISCOVERABLE ) ;
2012-02-21 21:40:05 +04:00
changed = true ;
}
2012-02-21 18:01:30 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_DISCOVERABLE , hdev ) ;
2012-02-21 21:40:05 +04:00
if ( err < 0 )
goto failed ;
if ( changed )
err = new_settings ( hdev , sk ) ;
2012-02-21 18:01:30 +04:00
goto failed ;
}
2013-10-15 20:13:39 +04:00
/* If the current mode is the same, then just update the timeout
* value with the new value . And if only the timeout gets updated ,
* then no need for any HCI transactions .
*/
2015-03-13 12:11:00 +03:00
if ( ! ! cp - > val = = hci_dev_test_flag ( hdev , HCI_DISCOVERABLE ) & &
( cp - > val = = 0x02 ) = = hci_dev_test_flag ( hdev ,
HCI_LIMITED_DISCOVERABLE ) ) {
2013-10-15 19:28:51 +04:00
cancel_delayed_work ( & hdev - > discov_off ) ;
hdev - > discov_timeout = timeout ;
2012-02-22 21:21:00 +04:00
2013-10-15 19:28:51 +04:00
if ( cp - > val & & hdev - > discov_timeout > 0 ) {
int to = msecs_to_jiffies ( hdev - > discov_timeout * 1000 ) ;
2015-11-23 16:43:06 +03:00
queue_delayed_work ( hdev - > req_workqueue ,
& hdev - > discov_off , to ) ;
2012-02-22 21:21:00 +04:00
}
2011-12-15 02:47:35 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_DISCOVERABLE , hdev ) ;
2010-12-29 17:00:25 +03:00
goto failed ;
}
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_DISCOVERABLE , hdev , data , len ) ;
2011-02-19 18:05:55 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
2010-12-29 17:00:25 +03:00
goto failed ;
2011-02-19 18:05:55 +03:00
}
2010-12-29 17:00:25 +03:00
2013-10-15 20:13:39 +04:00
/* Cancel any potential discoverable timeout that might be
* still active and store new timeout value . The arming of
* the timeout happens in the complete handler .
*/
cancel_delayed_work ( & hdev - > discov_off ) ;
hdev - > discov_timeout = timeout ;
2015-11-22 17:24:44 +03:00
if ( cp - > val )
hci_dev_set_flag ( hdev , HCI_DISCOVERABLE ) ;
else
hci_dev_clear_flag ( hdev , HCI_DISCOVERABLE ) ;
2013-10-20 00:38:22 +04:00
/* Limited discoverable mode */
if ( cp - > val = = 0x02 )
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_LIMITED_DISCOVERABLE ) ;
2013-10-20 00:38:22 +04:00
else
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_LIMITED_DISCOVERABLE ) ;
2013-10-20 00:38:22 +04:00
2015-11-22 17:24:44 +03:00
queue_work ( hdev - > req_workqueue , & hdev - > discoverable_update ) ;
err = 0 ;
2010-12-29 17:00:25 +03:00
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2010-12-29 17:00:25 +03:00
return err ;
}
2015-11-22 16:43:43 +03:00
void mgmt_set_connectable_complete ( struct hci_dev * hdev , u8 status )
2013-03-16 02:07:04 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:07:04 +04:00
BT_DBG ( " status 0x%02x " , status ) ;
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_CONNECTABLE , hdev ) ;
2013-03-16 02:07:04 +04:00
if ( ! cmd )
goto unlock ;
2013-10-14 17:20:05 +04:00
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
2015-03-06 22:08:53 +03:00
mgmt_cmd_status ( cmd - > sk , cmd - > index , cmd - > opcode , mgmt_err ) ;
2013-10-14 17:20:05 +04:00
goto remove_cmd ;
}
2013-03-16 02:07:04 +04:00
send_settings_rsp ( cmd - > sk , MGMT_OP_SET_CONNECTABLE , hdev ) ;
2015-11-22 16:43:43 +03:00
new_settings ( hdev , cmd - > sk ) ;
2013-10-14 17:20:04 +04:00
2013-10-14 17:20:05 +04:00
remove_cmd :
2013-03-16 02:07:04 +04:00
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
2013-10-20 00:38:18 +04:00
static int set_connectable_update_settings ( struct hci_dev * hdev ,
struct sock * sk , u8 val )
{
bool changed = false ;
int err ;
2015-03-13 12:11:00 +03:00
if ( ! ! val ! = hci_dev_test_flag ( hdev , HCI_CONNECTABLE ) )
2013-10-20 00:38:18 +04:00
changed = true ;
if ( val ) {
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_CONNECTABLE ) ;
2013-10-20 00:38:18 +04:00
} else {
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_CONNECTABLE ) ;
hci_dev_clear_flag ( hdev , HCI_DISCOVERABLE ) ;
2013-10-20 00:38:18 +04:00
}
err = send_settings_rsp ( sk , MGMT_OP_SET_CONNECTABLE , hdev ) ;
if ( err < 0 )
return err ;
2014-07-08 17:35:34 +04:00
if ( changed ) {
2015-11-16 13:52:21 +03:00
hci_req_update_scan ( hdev ) ;
2014-07-08 17:35:34 +04:00
hci_update_background_scan ( hdev ) ;
2013-10-20 00:38:18 +04:00
return new_settings ( hdev , sk ) ;
2014-07-08 17:35:34 +04:00
}
2013-10-20 00:38:18 +04:00
return 0 ;
}
2012-02-28 08:13:32 +04:00
static int set_connectable ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2010-12-30 01:18:33 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_mode * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2010-12-30 01:18:33 +03:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2010-12-30 01:18:33 +03:00
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) & &
! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_CONNECTABLE ,
MGMT_STATUS_REJECTED ) ;
2012-10-24 22:11:58 +04:00
2013-01-09 18:05:19 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_CONNECTABLE ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2010-12-30 01:18:33 +03:00
2012-02-21 16:13:02 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2013-10-20 00:38:18 +04:00
err = set_connectable_update_settings ( hdev , sk , cp - > val ) ;
2010-12-30 01:18:33 +03:00
goto failed ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_DISCOVERABLE , hdev ) | |
pending_find ( MGMT_OP_SET_CONNECTABLE , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_CONNECTABLE ,
MGMT_STATUS_BUSY ) ;
2010-12-30 01:18:33 +03:00
goto failed ;
}
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_CONNECTABLE , hdev , data , len ) ;
2011-02-19 18:05:55 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
2010-12-30 01:18:33 +03:00
goto failed ;
2011-02-19 18:05:55 +03:00
}
2010-12-30 01:18:33 +03:00
2015-11-22 16:43:43 +03:00
if ( cp - > val ) {
hci_dev_set_flag ( hdev , HCI_CONNECTABLE ) ;
} else {
if ( hdev - > discov_timeout > 0 )
cancel_delayed_work ( & hdev - > discov_off ) ;
2013-03-16 02:07:04 +04:00
2015-11-22 16:43:43 +03:00
hci_dev_clear_flag ( hdev , HCI_LIMITED_DISCOVERABLE ) ;
hci_dev_clear_flag ( hdev , HCI_DISCOVERABLE ) ;
hci_dev_clear_flag ( hdev , HCI_CONNECTABLE ) ;
2013-10-14 17:20:03 +04:00
}
2013-03-16 02:07:04 +04:00
2015-11-22 16:43:43 +03:00
queue_work ( hdev - > req_workqueue , & hdev - > connectable_update ) ;
err = 0 ;
2010-12-30 01:18:33 +03:00
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2010-12-30 01:18:33 +03:00
return err ;
}
2014-07-30 10:22:23 +04:00
static int set_bondable ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-01-26 14:11:03 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_mode * cp = data ;
2013-10-07 03:11:57 +04:00
bool changed ;
2011-01-26 14:11:03 +03:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2011-01-26 14:11:03 +03:00
2013-01-09 18:05:19 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_BONDABLE ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-26 14:11:03 +03:00
if ( cp - > val )
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_BONDABLE ) ;
2011-01-26 14:11:03 +03:00
else
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev , HCI_BONDABLE ) ;
2011-01-26 14:11:03 +03:00
2014-07-30 10:22:23 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_BONDABLE , hdev ) ;
2011-01-26 14:11:03 +03:00
if ( err < 0 )
2013-10-07 03:11:57 +04:00
goto unlock ;
2011-01-26 14:11:03 +03:00
2016-03-09 18:30:34 +03:00
if ( changed ) {
/* In limited privacy mode the change of bondable mode
* may affect the local advertising address .
*/
if ( hdev_is_powered ( hdev ) & &
hci_dev_test_flag ( hdev , HCI_ADVERTISING ) & &
hci_dev_test_flag ( hdev , HCI_DISCOVERABLE ) & &
hci_dev_test_flag ( hdev , HCI_LIMITED_PRIVACY ) )
queue_work ( hdev - > req_workqueue ,
& hdev - > discoverable_update ) ;
2013-10-07 03:11:57 +04:00
err = new_settings ( hdev , sk ) ;
2016-03-09 18:30:34 +03:00
}
2011-01-26 14:11:03 +03:00
2013-10-07 03:11:57 +04:00
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-26 14:11:03 +03:00
return err ;
}
2012-03-08 08:25:00 +04:00
static int set_link_security ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
2012-02-17 01:56:27 +04:00
{
struct mgmt_mode * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-10-02 16:45:22 +04:00
u8 val , status ;
2012-02-17 01:56:27 +04:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2012-02-17 01:56:27 +04:00
2013-10-02 16:45:22 +04:00
status = mgmt_bredr_support ( hdev ) ;
if ( status )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_LINK_SECURITY ,
status ) ;
2012-10-24 22:11:58 +04:00
2013-01-09 18:05:19 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_LINK_SECURITY ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2012-02-17 01:56:27 +04:00
hci_dev_lock ( hdev ) ;
2012-02-21 16:13:02 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2012-02-22 13:58:37 +04:00
bool changed = false ;
2015-03-13 12:11:00 +03:00
if ( ! ! cp - > val ! = hci_dev_test_flag ( hdev , HCI_LINK_SECURITY ) ) {
2015-03-13 12:11:03 +03:00
hci_dev_change_flag ( hdev , HCI_LINK_SECURITY ) ;
2012-02-22 13:58:37 +04:00
changed = true ;
}
err = send_settings_rsp ( sk , MGMT_OP_SET_LINK_SECURITY , hdev ) ;
if ( err < 0 )
goto failed ;
if ( changed )
err = new_settings ( hdev , sk ) ;
2012-02-17 01:56:27 +04:00
goto failed ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_LINK_SECURITY , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_LINK_SECURITY ,
MGMT_STATUS_BUSY ) ;
2012-02-17 01:56:27 +04:00
goto failed ;
}
val = ! ! cp - > val ;
if ( test_bit ( HCI_AUTH , & hdev - > flags ) = = val ) {
err = send_settings_rsp ( sk , MGMT_OP_SET_LINK_SECURITY , hdev ) ;
goto failed ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_LINK_SECURITY , hdev , data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto failed ;
}
err = hci_send_cmd ( hdev , HCI_OP_WRITE_AUTH_ENABLE , sizeof ( val ) , & val ) ;
if ( err < 0 ) {
mgmt_pending_remove ( cmd ) ;
goto failed ;
}
failed :
hci_dev_unlock ( hdev ) ;
return err ;
}
2012-02-28 08:13:32 +04:00
static int set_ssp ( struct sock * sk , struct hci_dev * hdev , void * data , u16 len )
2012-02-17 02:56:28 +04:00
{
struct mgmt_mode * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-10-10 14:08:10 +04:00
u8 status ;
2012-02-17 02:56:28 +04:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2012-02-17 02:56:28 +04:00
2013-10-03 08:31:52 +04:00
status = mgmt_bredr_support ( hdev ) ;
if ( status )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SSP , status ) ;
2013-10-03 08:31:52 +04:00
2013-01-09 17:29:38 +04:00
if ( ! lmp_ssp_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SSP ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2012-02-17 02:56:28 +04:00
2013-01-09 18:05:19 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SSP ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2013-01-09 17:29:38 +04:00
hci_dev_lock ( hdev ) ;
2012-02-22 18:35:26 +04:00
2012-02-21 16:13:02 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2013-10-10 14:08:11 +04:00
bool changed ;
2012-02-22 14:38:31 +04:00
2013-10-10 14:08:11 +04:00
if ( cp - > val ) {
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev ,
HCI_SSP_ENABLED ) ;
2013-10-10 14:08:11 +04:00
} else {
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev ,
HCI_SSP_ENABLED ) ;
2013-10-10 14:08:11 +04:00
if ( ! changed )
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev ,
HCI_HS_ENABLED ) ;
2013-10-10 14:08:11 +04:00
else
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_HS_ENABLED ) ;
2012-02-22 14:38:31 +04:00
}
err = send_settings_rsp ( sk , MGMT_OP_SET_SSP , hdev ) ;
if ( err < 0 )
goto failed ;
if ( changed )
err = new_settings ( hdev , sk ) ;
2012-02-17 02:56:28 +04:00
goto failed ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_SSP , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SSP ,
MGMT_STATUS_BUSY ) ;
2012-02-17 02:56:28 +04:00
goto failed ;
}
2015-03-13 12:11:00 +03:00
if ( ! ! cp - > val = = hci_dev_test_flag ( hdev , HCI_SSP_ENABLED ) ) {
2012-02-17 02:56:28 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_SSP , hdev ) ;
goto failed ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_SSP , hdev , data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto failed ;
}
2015-03-13 12:11:00 +03:00
if ( ! cp - > val & & hci_dev_test_flag ( hdev , HCI_USE_DEBUG_KEYS ) )
2014-06-24 15:00:27 +04:00
hci_send_cmd ( hdev , HCI_OP_WRITE_SSP_DEBUG_MODE ,
sizeof ( cp - > val ) , & cp - > val ) ;
2013-10-10 14:08:10 +04:00
err = hci_send_cmd ( hdev , HCI_OP_WRITE_SSP_MODE , 1 , & cp - > val ) ;
2012-02-17 02:56:28 +04:00
if ( err < 0 ) {
mgmt_pending_remove ( cmd ) ;
goto failed ;
}
failed :
hci_dev_unlock ( hdev ) ;
return err ;
}
2012-02-28 08:13:32 +04:00
static int set_hs ( struct sock * sk , struct hci_dev * hdev , void * data , u16 len )
2012-02-21 01:50:38 +04:00
{
struct mgmt_mode * cp = data ;
2013-10-02 09:59:23 +04:00
bool changed ;
2013-10-02 16:45:22 +04:00
u8 status ;
2013-10-02 09:59:23 +04:00
int err ;
2012-02-21 01:50:38 +04:00
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2012-02-21 01:50:38 +04:00
2013-10-02 16:45:22 +04:00
status = mgmt_bredr_support ( hdev ) ;
if ( status )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_HS , status ) ;
2012-02-21 01:50:38 +04:00
2013-10-10 14:08:11 +04:00
if ( ! lmp_ssp_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_HS ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2013-10-10 14:08:11 +04:00
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_SSP_ENABLED ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_HS ,
MGMT_STATUS_REJECTED ) ;
2013-10-10 14:08:11 +04:00
2013-01-09 18:05:19 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_HS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2013-10-02 09:59:23 +04:00
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_SSP , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_HS ,
MGMT_STATUS_BUSY ) ;
2015-02-19 18:38:07 +03:00
goto unlock ;
}
2013-10-02 11:27:02 +04:00
if ( cp - > val ) {
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_HS_ENABLED ) ;
2013-10-02 11:27:02 +04:00
} else {
if ( hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_HS ,
MGMT_STATUS_REJECTED ) ;
2013-10-02 11:27:02 +04:00
goto unlock ;
}
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev , HCI_HS_ENABLED ) ;
2013-10-02 11:27:02 +04:00
}
2013-10-02 09:59:23 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_HS , hdev ) ;
if ( err < 0 )
goto unlock ;
if ( changed )
err = new_settings ( hdev , sk ) ;
2012-02-21 01:50:38 +04:00
2013-10-02 09:59:23 +04:00
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
2012-02-21 01:50:38 +04:00
}
2015-01-12 00:50:44 +03:00
static void le_enable_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
2013-09-25 14:26:08 +04:00
{
struct cmd_lookup match = { NULL , hdev } ;
2014-12-11 09:13:12 +03:00
hci_dev_lock ( hdev ) ;
2013-09-25 14:26:08 +04:00
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
mgmt_pending_foreach ( MGMT_OP_SET_LE , hdev , cmd_status_rsp ,
& mgmt_err ) ;
2014-12-11 09:13:12 +03:00
goto unlock ;
2013-09-25 14:26:08 +04:00
}
mgmt_pending_foreach ( MGMT_OP_SET_LE , hdev , settings_rsp , & match ) ;
new_settings ( hdev , match . sk ) ;
if ( match . sk )
sock_put ( match . sk ) ;
2013-10-15 17:33:52 +04:00
/* Make sure the controller has a good default for
* advertising data . Restrict the update to when LE
* has actually been enabled . During power on , the
* update in powered_update_hci will take care of it .
*/
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) ) {
2013-10-15 17:33:52 +04:00
struct hci_request req ;
hci_req_init ( & req , hdev ) ;
2015-11-30 12:21:45 +03:00
__hci_req_update_adv_data ( & req , 0x00 ) ;
__hci_req_update_scan_rsp_data ( & req , 0x00 ) ;
2013-10-15 17:33:52 +04:00
hci_req_run ( & req , NULL ) ;
2015-11-11 09:11:17 +03:00
hci_update_background_scan ( hdev ) ;
2013-10-15 17:33:52 +04:00
}
2014-12-11 09:13:12 +03:00
unlock :
hci_dev_unlock ( hdev ) ;
2013-09-25 14:26:08 +04:00
}
2012-02-28 08:13:32 +04:00
static int set_le ( struct sock * sk , struct hci_dev * hdev , void * data , u16 len )
2012-02-22 18:37:11 +04:00
{
struct mgmt_mode * cp = data ;
struct hci_cp_write_le_host_supported hci_cp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-09-25 14:26:08 +04:00
struct hci_request req ;
2012-02-22 18:37:11 +04:00
int err ;
2012-02-28 02:57:24 +04:00
u8 val , enabled ;
2012-02-22 18:37:11 +04:00
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2012-02-22 18:37:11 +04:00
2013-01-09 17:29:38 +04:00
if ( ! lmp_le_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_LE ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2012-03-01 07:55:35 +04:00
2013-01-09 18:05:19 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_LE ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2015-03-19 02:15:07 +03:00
/* Bluetooth single mode LE only controllers or dual-mode
* controllers configured as LE only devices , do not allow
* switching LE off . These have either LE enabled explicitly
* or BR / EDR has been previously switched off .
*
* When trying to enable an already enabled LE , then gracefully
* send a positive response . Trying to disable it however will
* result into rejection .
*/
if ( ! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) ) {
if ( cp - > val = = 0x01 )
return send_settings_rsp ( sk , MGMT_OP_SET_LE , hdev ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_LE ,
MGMT_STATUS_REJECTED ) ;
2015-03-19 02:15:07 +03:00
}
2013-04-19 19:35:21 +04:00
2013-01-09 17:29:38 +04:00
hci_dev_lock ( hdev ) ;
2012-02-22 18:37:11 +04:00
val = ! ! cp - > val ;
2012-11-23 22:50:51 +04:00
enabled = lmp_host_le_capable ( hdev ) ;
2012-02-22 18:37:11 +04:00
2015-06-18 04:16:46 +03:00
if ( ! val )
2016-08-28 20:53:34 +03:00
hci_req_clear_adv_instance ( hdev , NULL , NULL , 0x00 , true ) ;
2015-06-18 04:16:46 +03:00
2012-02-28 02:57:24 +04:00
if ( ! hdev_is_powered ( hdev ) | | val = = enabled ) {
2012-02-22 18:37:11 +04:00
bool changed = false ;
2015-03-13 12:11:00 +03:00
if ( val ! = hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) ) {
2015-03-13 12:11:03 +03:00
hci_dev_change_flag ( hdev , HCI_LE_ENABLED ) ;
2012-02-22 18:37:11 +04:00
changed = true ;
}
2015-03-13 12:11:00 +03:00
if ( ! val & & hci_dev_test_flag ( hdev , HCI_ADVERTISING ) ) {
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_ADVERTISING ) ;
2013-09-25 14:26:09 +04:00
changed = true ;
}
2012-02-22 18:37:11 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_LE , hdev ) ;
if ( err < 0 )
2012-03-01 07:55:35 +04:00
goto unlock ;
2012-02-22 18:37:11 +04:00
if ( changed )
err = new_settings ( hdev , sk ) ;
2012-03-01 07:55:35 +04:00
goto unlock ;
2012-02-22 18:37:11 +04:00
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_LE , hdev ) | |
pending_find ( MGMT_OP_SET_ADVERTISING , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_LE ,
MGMT_STATUS_BUSY ) ;
2012-03-01 07:55:35 +04:00
goto unlock ;
2012-02-22 18:37:11 +04:00
}
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_LE , hdev , data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
2012-03-01 07:55:35 +04:00
goto unlock ;
2012-02-22 18:37:11 +04:00
}
2013-10-15 17:33:52 +04:00
hci_req_init ( & req , hdev ) ;
2012-02-22 18:37:11 +04:00
memset ( & hci_cp , 0 , sizeof ( hci_cp ) ) ;
if ( val ) {
hci_cp . le = val ;
2014-07-24 22:04:16 +04:00
hci_cp . simul = 0x00 ;
2013-10-15 17:33:52 +04:00
} else {
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LE_ADV ) )
2015-11-18 13:49:20 +03:00
__hci_req_disable_advertising ( & req ) ;
2012-02-22 18:37:11 +04:00
}
2013-09-25 14:26:08 +04:00
hci_req_add ( & req , HCI_OP_WRITE_LE_HOST_SUPPORTED , sizeof ( hci_cp ) ,
& hci_cp ) ;
err = hci_req_run ( & req , le_enable_complete ) ;
2012-04-12 19:03:21 +04:00
if ( err < 0 )
2012-02-22 18:37:11 +04:00
mgmt_pending_remove ( cmd ) ;
2012-03-01 07:55:35 +04:00
unlock :
hci_dev_unlock ( hdev ) ;
2012-02-22 18:37:11 +04:00
return err ;
}
2013-03-16 02:06:54 +04:00
/* This is a helper function to test for pending mgmt commands that can
* cause CoD or EIR HCI commands . We can only allow one such pending
* mgmt command at a time since otherwise we cannot easily track what
* the current values are , will be , and based on that calculate if a new
* HCI command needs to be sent and if yes with what value .
*/
static bool pending_eir_or_class ( struct hci_dev * hdev )
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:06:54 +04:00
list_for_each_entry ( cmd , & hdev - > mgmt_pending , list ) {
switch ( cmd - > opcode ) {
case MGMT_OP_ADD_UUID :
case MGMT_OP_REMOVE_UUID :
case MGMT_OP_SET_DEV_CLASS :
case MGMT_OP_SET_POWERED :
return true ;
}
}
return false ;
}
2013-01-27 02:31:29 +04:00
static const u8 bluetooth_base_uuid [ ] = {
0xfb , 0x34 , 0x9b , 0x5f , 0x80 , 0x00 , 0x00 , 0x80 ,
0x00 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
} ;
static u8 get_uuid_size ( const u8 * uuid )
{
u32 val ;
if ( memcmp ( uuid , bluetooth_base_uuid , 12 ) )
return 128 ;
val = get_unaligned_le32 ( & uuid [ 12 ] ) ;
if ( val > 0xffff )
return 32 ;
return 16 ;
}
2013-03-16 02:06:55 +04:00
static void mgmt_class_complete ( struct hci_dev * hdev , u16 mgmt_op , u8 status )
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:06:55 +04:00
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( mgmt_op , hdev ) ;
2013-03-16 02:06:55 +04:00
if ( ! cmd )
goto unlock ;
2015-03-06 22:08:54 +03:00
mgmt_cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode ,
mgmt_status ( status ) , hdev - > dev_class , 3 ) ;
2013-03-16 02:06:55 +04:00
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
2015-01-12 00:50:44 +03:00
static void add_uuid_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
2013-03-16 02:06:55 +04:00
{
BT_DBG ( " status 0x%02x " , status ) ;
mgmt_class_complete ( hdev , MGMT_OP_ADD_UUID , status ) ;
}
2012-02-28 08:13:32 +04:00
static int add_uuid ( struct sock * sk , struct hci_dev * hdev , void * data , u16 len )
2011-01-04 13:08:51 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_add_uuid * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:06:52 +04:00
struct hci_request req ;
2011-01-04 13:08:51 +03:00
struct bt_uuid * uuid ;
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2011-01-04 13:08:51 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-04 13:08:51 +03:00
2013-03-16 02:06:54 +04:00
if ( pending_eir_or_class ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_UUID ,
MGMT_STATUS_BUSY ) ;
2012-02-24 00:54:38 +04:00
goto failed ;
}
2012-06-08 02:05:44 +04:00
uuid = kmalloc ( sizeof ( * uuid ) , GFP_KERNEL ) ;
2011-01-04 13:08:51 +03:00
if ( ! uuid ) {
err = - ENOMEM ;
goto failed ;
}
memcpy ( uuid - > uuid , cp - > uuid , 16 ) ;
2011-01-13 22:56:52 +03:00
uuid - > svc_hint = cp - > svc_hint ;
2013-01-27 02:31:29 +04:00
uuid - > size = get_uuid_size ( cp - > uuid ) ;
2011-01-04 13:08:51 +03:00
2013-01-27 02:31:27 +04:00
list_add_tail ( & uuid - > list , & hdev - > uuids ) ;
2011-01-04 13:08:51 +03:00
2013-03-16 02:06:52 +04:00
hci_req_init ( & req , hdev ) ;
2011-01-13 22:56:52 +03:00
2015-11-22 20:00:22 +03:00
__hci_req_update_class ( & req ) ;
2015-11-25 17:15:42 +03:00
__hci_req_update_eir ( & req ) ;
2013-03-16 02:06:52 +04:00
2013-03-16 02:06:55 +04:00
err = hci_req_run ( & req , add_uuid_complete ) ;
if ( err < 0 ) {
if ( err ! = - ENODATA )
goto failed ;
2011-03-28 15:07:23 +04:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_UUID , 0 ,
hdev - > dev_class , 3 ) ;
2012-02-24 01:09:40 +04:00
goto failed ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_ADD_UUID , hdev , data , len ) ;
2013-03-16 02:06:52 +04:00
if ( ! cmd ) {
2012-02-24 01:09:40 +04:00
err = - ENOMEM ;
2013-03-16 02:06:52 +04:00
goto failed ;
}
err = 0 ;
2011-01-04 13:08:51 +03:00
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-04 13:08:51 +03:00
return err ;
}
2012-02-24 01:24:30 +04:00
static bool enable_service_cache ( struct hci_dev * hdev )
{
if ( ! hdev_is_powered ( hdev ) )
return false ;
2015-03-13 12:11:06 +03:00
if ( ! hci_dev_test_and_set_flag ( hdev , HCI_SERVICE_CACHE ) ) {
2013-01-15 00:33:52 +04:00
queue_delayed_work ( hdev - > workqueue , & hdev - > service_cache ,
CACHE_TIMEOUT ) ;
2012-02-24 01:24:30 +04:00
return true ;
}
return false ;
}
2015-01-12 00:50:44 +03:00
static void remove_uuid_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
2013-03-16 02:06:55 +04:00
{
BT_DBG ( " status 0x%02x " , status ) ;
mgmt_class_complete ( hdev , MGMT_OP_REMOVE_UUID , status ) ;
}
2012-02-28 08:13:32 +04:00
static int remove_uuid ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-05-17 07:36:20 +04:00
u16 len )
2011-01-04 13:08:51 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_remove_uuid * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-01-27 02:31:30 +04:00
struct bt_uuid * match , * tmp ;
2011-01-04 13:08:51 +03:00
u8 bt_uuid_any [ ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2013-03-16 02:06:52 +04:00
struct hci_request req ;
2011-01-04 13:08:51 +03:00
int err , found ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2011-01-04 13:08:51 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-04 13:08:51 +03:00
2013-03-16 02:06:54 +04:00
if ( pending_eir_or_class ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_REMOVE_UUID ,
MGMT_STATUS_BUSY ) ;
2012-02-24 00:54:38 +04:00
goto unlock ;
}
2011-01-04 13:08:51 +03:00
if ( memcmp ( cp - > uuid , bt_uuid_any , 16 ) = = 0 ) {
2014-02-18 19:14:32 +04:00
hci_uuids_clear ( hdev ) ;
2012-02-23 23:30:12 +04:00
2012-02-24 01:24:30 +04:00
if ( enable_service_cache ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_UUID ,
0 , hdev - > dev_class , 3 ) ;
2012-02-24 01:24:30 +04:00
goto unlock ;
}
2012-02-23 23:30:12 +04:00
2012-02-23 23:33:16 +04:00
goto update_class ;
2011-01-04 13:08:51 +03:00
}
found = 0 ;
2013-01-27 02:31:30 +04:00
list_for_each_entry_safe ( match , tmp , & hdev - > uuids , list ) {
2011-01-04 13:08:51 +03:00
if ( memcmp ( match - > uuid , cp - > uuid , 16 ) ! = 0 )
continue ;
list_del ( & match - > list ) ;
2012-11-08 13:25:26 +04:00
kfree ( match ) ;
2011-01-04 13:08:51 +03:00
found + + ;
}
if ( found = = 0 ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_REMOVE_UUID ,
MGMT_STATUS_INVALID_PARAMS ) ;
2011-01-04 13:08:51 +03:00
goto unlock ;
}
2012-02-23 23:33:16 +04:00
update_class :
2013-03-16 02:06:52 +04:00
hci_req_init ( & req , hdev ) ;
2011-01-13 22:56:52 +03:00
2015-11-22 20:00:22 +03:00
__hci_req_update_class ( & req ) ;
2015-11-25 17:15:42 +03:00
__hci_req_update_eir ( & req ) ;
2013-03-16 02:06:52 +04:00
2013-03-16 02:06:55 +04:00
err = hci_req_run ( & req , remove_uuid_complete ) ;
if ( err < 0 ) {
if ( err ! = - ENODATA )
goto unlock ;
2011-03-28 15:07:23 +04:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_REMOVE_UUID , 0 ,
hdev - > dev_class , 3 ) ;
2012-02-24 01:09:40 +04:00
goto unlock ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_REMOVE_UUID , hdev , data , len ) ;
2013-03-16 02:06:52 +04:00
if ( ! cmd ) {
2012-02-24 01:09:40 +04:00
err = - ENOMEM ;
2013-03-16 02:06:52 +04:00
goto unlock ;
}
err = 0 ;
2011-01-04 13:08:51 +03:00
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-04 13:08:51 +03:00
return err ;
}
2015-01-12 00:50:44 +03:00
static void set_class_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
2013-03-16 02:06:55 +04:00
{
BT_DBG ( " status 0x%02x " , status ) ;
mgmt_class_complete ( hdev , MGMT_OP_SET_DEV_CLASS , status ) ;
}
2012-02-28 08:13:32 +04:00
static int set_dev_class ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-01-13 22:56:52 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_set_dev_class * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:06:52 +04:00
struct hci_request req ;
2011-01-13 22:56:52 +03:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2011-01-13 22:56:52 +03:00
2013-10-03 10:37:29 +04:00
if ( ! lmp_bredr_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DEV_CLASS ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2011-01-13 22:56:52 +03:00
2013-03-16 02:06:54 +04:00
hci_dev_lock ( hdev ) ;
2013-01-09 17:29:36 +04:00
2013-03-16 02:06:54 +04:00
if ( pending_eir_or_class ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DEV_CLASS ,
MGMT_STATUS_BUSY ) ;
2013-03-16 02:06:54 +04:00
goto unlock ;
}
2012-02-24 00:54:38 +04:00
2013-03-16 02:06:54 +04:00
if ( ( cp - > minor & 0x03 ) ! = 0 | | ( cp - > major & 0xe0 ) ! = 0 ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DEV_CLASS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-03-16 02:06:54 +04:00
goto unlock ;
}
2013-01-09 17:29:35 +04:00
2012-02-23 00:11:32 +04:00
hdev - > major_class = cp - > major ;
hdev - > minor_class = cp - > minor ;
2012-02-21 16:32:24 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_SET_DEV_CLASS , 0 ,
hdev - > dev_class , 3 ) ;
2012-02-21 16:32:24 +04:00
goto unlock ;
}
2013-03-16 02:06:52 +04:00
hci_req_init ( & req , hdev ) ;
2015-03-13 12:11:05 +03:00
if ( hci_dev_test_and_clear_flag ( hdev , HCI_SERVICE_CACHE ) ) {
2011-12-15 02:47:39 +04:00
hci_dev_unlock ( hdev ) ;
cancel_delayed_work_sync ( & hdev - > service_cache ) ;
hci_dev_lock ( hdev ) ;
2015-11-25 17:15:42 +03:00
__hci_req_update_eir ( & req ) ;
2011-12-15 02:47:39 +04:00
}
2011-12-15 02:47:37 +04:00
2015-11-22 20:00:22 +03:00
__hci_req_update_class ( & req ) ;
2013-03-16 02:06:52 +04:00
2013-03-16 02:06:55 +04:00
err = hci_req_run ( & req , set_class_complete ) ;
if ( err < 0 ) {
if ( err ! = - ENODATA )
goto unlock ;
2011-01-13 22:56:52 +03:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_SET_DEV_CLASS , 0 ,
hdev - > dev_class , 3 ) ;
2012-02-24 01:09:40 +04:00
goto unlock ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_DEV_CLASS , hdev , data , len ) ;
2013-03-16 02:06:52 +04:00
if ( ! cmd ) {
2012-02-24 01:09:40 +04:00
err = - ENOMEM ;
2013-03-16 02:06:52 +04:00
goto unlock ;
}
err = 0 ;
2011-01-13 22:56:52 +03:00
2012-02-21 16:32:24 +04:00
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-13 22:56:52 +03:00
return err ;
}
2012-02-28 08:13:32 +04:00
static int load_link_keys ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-05-17 07:36:20 +04:00
u16 len )
2011-01-17 15:41:05 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_load_link_keys * cp = data ;
2014-07-03 14:52:27 +04:00
const u16 max_key_count = ( ( U16_MAX - sizeof ( * cp ) ) /
sizeof ( struct mgmt_link_key_info ) ) ;
2011-02-25 21:05:48 +03:00
u16 key_count , expected_len ;
2014-01-31 23:55:21 +04:00
bool changed ;
2011-08-26 03:02:29 +04:00
int i ;
2011-01-17 15:41:05 +03:00
2013-10-03 08:16:07 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
if ( ! lmp_bredr_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LINK_KEYS ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2013-10-03 08:16:07 +04:00
2012-03-13 07:31:08 +04:00
key_count = __le16_to_cpu ( cp - > key_count ) ;
2014-07-03 14:52:27 +04:00
if ( key_count > max_key_count ) {
BT_ERR ( " load_link_keys: too big key_count value %u " ,
key_count ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LINK_KEYS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-07-03 14:52:27 +04:00
}
2011-01-17 15:41:05 +03:00
2011-11-08 01:13:38 +04:00
expected_len = sizeof ( * cp ) + key_count *
sizeof ( struct mgmt_link_key_info ) ;
2011-08-26 03:02:29 +04:00
if ( expected_len ! = len ) {
2011-11-08 01:13:38 +04:00
BT_ERR ( " load_link_keys: expected %u bytes, got %u bytes " ,
2014-03-07 17:04:13 +04:00
expected_len , len ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LINK_KEYS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2011-01-17 15:41:05 +03:00
}
2013-01-20 16:27:13 +04:00
if ( cp - > debug_keys ! = 0x00 & & cp - > debug_keys ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LINK_KEYS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-20 16:27:13 +04:00
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s debug_keys %u key_count %u " , hdev - > name , cp - > debug_keys ,
2012-05-17 07:36:20 +04:00
key_count ) ;
2011-01-17 15:41:05 +03:00
2013-01-20 16:27:19 +04:00
for ( i = 0 ; i < key_count ; i + + ) {
struct mgmt_link_key_info * key = & cp - > keys [ i ] ;
2014-01-10 14:07:25 +04:00
if ( key - > addr . type ! = BDADDR_BREDR | | key - > type > 0x08 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id ,
MGMT_OP_LOAD_LINK_KEYS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-20 16:27:19 +04:00
}
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-17 15:41:05 +03:00
hci_link_keys_clear ( hdev ) ;
if ( cp - > debug_keys )
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_KEEP_DEBUG_KEYS ) ;
2011-01-17 15:41:05 +03:00
else
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev ,
HCI_KEEP_DEBUG_KEYS ) ;
2014-01-31 23:55:21 +04:00
if ( changed )
new_settings ( hdev , NULL ) ;
2011-01-17 15:41:05 +03:00
2011-08-26 03:02:29 +04:00
for ( i = 0 ; i < key_count ; i + + ) {
2011-11-08 01:13:38 +04:00
struct mgmt_link_key_info * key = & cp - > keys [ i ] ;
2011-01-17 15:41:05 +03:00
2014-06-24 15:00:26 +04:00
/* Always ignore debug keys and require a new pairing if
* the user wants to use them .
*/
if ( key - > type = = HCI_LK_DEBUG_COMBINATION )
continue ;
2014-06-24 14:15:49 +04:00
hci_add_link_key ( hdev , NULL , & key - > addr . bdaddr , key - > val ,
key - > type , key - > pin_len , NULL ) ;
2011-01-17 15:41:05 +03:00
}
2015-03-06 22:08:54 +03:00
mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_LOAD_LINK_KEYS , 0 , NULL , 0 ) ;
2011-11-11 18:18:54 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-17 15:41:05 +03:00
2011-08-26 03:02:29 +04:00
return 0 ;
2011-01-17 15:41:05 +03:00
}
2012-02-09 19:21:16 +04:00
static int device_unpaired ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 addr_type , struct sock * skip_sk )
2012-02-09 19:21:16 +04:00
{
struct mgmt_ev_device_unpaired ev ;
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = addr_type ;
return mgmt_event ( MGMT_EV_DEVICE_UNPAIRED , hdev , & ev , sizeof ( ev ) ,
2012-03-08 08:25:00 +04:00
skip_sk ) ;
2012-02-09 19:21:16 +04:00
}
2012-02-28 08:13:32 +04:00
static int unpair_device ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-01-17 15:41:05 +03:00
{
2012-02-09 15:50:12 +04:00
struct mgmt_cp_unpair_device * cp = data ;
struct mgmt_rp_unpair_device rp ;
2015-10-22 09:38:31 +03:00
struct hci_conn_params * params ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-01-17 15:41:05 +03:00
struct hci_conn * conn ;
2015-10-21 18:03:03 +03:00
u8 addr_type ;
2011-01-17 15:41:05 +03:00
int err ;
2011-11-10 17:54:38 +04:00
memset ( & rp , 0 , sizeof ( rp ) ) ;
2012-02-09 15:50:12 +04:00
bacpy ( & rp . addr . bdaddr , & cp - > addr . bdaddr ) ;
rp . addr . type = cp - > addr . type ;
2011-11-10 17:54:38 +04:00
2013-01-20 16:27:19 +04:00
if ( ! bdaddr_type_is_valid ( cp - > addr . type ) )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_UNPAIR_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& rp , sizeof ( rp ) ) ;
2013-01-20 16:27:19 +04:00
2013-01-20 16:27:20 +04:00
if ( cp - > disconnect ! = 0x00 & & cp - > disconnect ! = 0x01 )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_UNPAIR_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& rp , sizeof ( rp ) ) ;
2013-01-20 16:27:20 +04:00
2013-01-20 16:27:19 +04:00
hci_dev_lock ( hdev ) ;
2012-02-23 00:53:34 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_UNPAIR_DEVICE ,
MGMT_STATUS_NOT_POWERED , & rp ,
sizeof ( rp ) ) ;
2012-02-23 00:53:34 +04:00
goto unlock ;
}
2014-02-18 19:14:31 +04:00
if ( cp - > addr . type = = BDADDR_BREDR ) {
2014-10-12 01:44:47 +04:00
/* If disconnection is requested, then look up the
* connection . If the remote device is connected , it
* will be later used to terminate the link .
*
* Setting it to NULL explicitly will cause no
* termination of the link .
*/
if ( cp - > disconnect )
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK ,
& cp - > addr . bdaddr ) ;
else
conn = NULL ;
2012-02-09 15:50:12 +04:00
err = hci_remove_link_key ( hdev , & cp - > addr . bdaddr ) ;
2015-10-21 18:03:03 +03:00
if ( err < 0 ) {
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_UNPAIR_DEVICE ,
MGMT_STATUS_NOT_PAIRED , & rp ,
sizeof ( rp ) ) ;
goto unlock ;
2014-10-12 01:44:47 +04:00
}
2015-10-21 18:03:03 +03:00
goto done ;
2014-02-18 19:14:31 +04:00
}
2012-02-03 04:08:03 +04:00
2015-10-21 18:03:03 +03:00
/* LE address type */
addr_type = le_addr_type ( cp - > addr . type ) ;
hci_remove_irk ( hdev , & cp - > addr . bdaddr , addr_type ) ;
err = hci_remove_ltk ( hdev , & cp - > addr . bdaddr , addr_type ) ;
2011-01-17 15:41:05 +03:00
if ( err < 0 ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_UNPAIR_DEVICE ,
MGMT_STATUS_NOT_PAIRED , & rp ,
sizeof ( rp ) ) ;
2011-01-17 15:41:05 +03:00
goto unlock ;
}
2015-10-21 18:03:03 +03:00
conn = hci_conn_hash_lookup_le ( hdev , & cp - > addr . bdaddr , addr_type ) ;
if ( ! conn ) {
hci_conn_params_del ( hdev , & cp - > addr . bdaddr , addr_type ) ;
goto done ;
}
2015-10-22 09:38:35 +03:00
/* Abort any ongoing SMP pairing */
smp_cancel_pairing ( conn ) ;
2015-10-21 18:03:03 +03:00
/* Defer clearing up the connection parameters until closing to
* give a chance of keeping them if a repairing happens .
*/
set_bit ( HCI_CONN_PARAM_REMOVAL_PEND , & conn - > flags ) ;
2015-10-22 09:38:31 +03:00
/* Disable auto-connection parameters if present */
params = hci_conn_params_lookup ( hdev , & cp - > addr . bdaddr , addr_type ) ;
if ( params ) {
if ( params - > explicit_connect )
params - > auto_connect = HCI_AUTO_CONN_EXPLICIT ;
else
params - > auto_connect = HCI_AUTO_CONN_DISABLED ;
}
2015-10-21 18:03:03 +03:00
/* If disconnection is not requested, then clear the connection
* variable so that the link is not terminated .
*/
if ( ! cp - > disconnect )
conn = NULL ;
done :
2014-10-12 01:44:47 +04:00
/* If the connection variable is set, then termination of the
* link is requested .
*/
2011-11-10 17:54:38 +04:00
if ( ! conn ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_UNPAIR_DEVICE , 0 ,
& rp , sizeof ( rp ) ) ;
2012-02-09 19:21:16 +04:00
device_unpaired ( hdev , & cp - > addr . bdaddr , cp - > addr . type , sk ) ;
2011-11-10 17:54:38 +04:00
goto unlock ;
}
2011-01-17 15:41:05 +03:00
2012-02-09 15:50:12 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_UNPAIR_DEVICE , hdev , cp ,
2012-03-08 08:25:00 +04:00
sizeof ( * cp ) ) ;
2011-11-10 17:54:38 +04:00
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
2011-01-17 15:41:05 +03:00
}
2014-12-05 14:36:05 +03:00
cmd - > cmd_complete = addr_cmd_complete ;
2015-10-22 10:49:38 +03:00
err = hci_abort_conn ( conn , HCI_ERROR_REMOTE_USER_TERM ) ;
2011-11-10 17:54:38 +04:00
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
2011-01-17 15:41:05 +03:00
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-17 15:41:05 +03:00
return err ;
}
2012-02-28 08:13:32 +04:00
static int disconnect ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-01-20 13:40:27 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_disconnect * cp = data ;
2013-01-20 16:27:21 +04:00
struct mgmt_rp_disconnect rp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-01-20 13:40:27 +03:00
struct hci_conn * conn ;
int err ;
BT_DBG ( " " ) ;
2013-01-20 16:27:21 +04:00
memset ( & rp , 0 , sizeof ( rp ) ) ;
bacpy ( & rp . addr . bdaddr , & cp - > addr . bdaddr ) ;
rp . addr . type = cp - > addr . type ;
2013-01-20 16:27:19 +04:00
if ( ! bdaddr_type_is_valid ( cp - > addr . type ) )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_DISCONNECT ,
MGMT_STATUS_INVALID_PARAMS ,
& rp , sizeof ( rp ) ) ;
2013-01-20 16:27:19 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-20 13:40:27 +03:00
if ( ! test_bit ( HCI_UP , & hdev - > flags ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_DISCONNECT ,
MGMT_STATUS_NOT_POWERED , & rp ,
sizeof ( rp ) ) ;
2011-01-20 13:40:27 +03:00
goto failed ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_DISCONNECT , hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_DISCONNECT ,
MGMT_STATUS_BUSY , & rp , sizeof ( rp ) ) ;
2011-01-20 13:40:27 +03:00
goto failed ;
}
2012-04-25 04:02:49 +04:00
if ( cp - > addr . type = = BDADDR_BREDR )
2012-05-23 11:04:21 +04:00
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK ,
& cp - > addr . bdaddr ) ;
2012-02-09 16:27:38 +04:00
else
2015-10-21 18:03:01 +03:00
conn = hci_conn_hash_lookup_le ( hdev , & cp - > addr . bdaddr ,
le_addr_type ( cp - > addr . type ) ) ;
2011-05-07 01:41:44 +04:00
2012-06-13 04:02:43 +04:00
if ( ! conn | | conn - > state = = BT_OPEN | | conn - > state = = BT_CLOSED ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_DISCONNECT ,
MGMT_STATUS_NOT_CONNECTED , & rp ,
sizeof ( rp ) ) ;
2011-01-20 13:40:27 +03:00
goto failed ;
}
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_DISCONNECT , hdev , data , len ) ;
2011-02-19 18:05:55 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
2011-01-20 13:40:27 +03:00
goto failed ;
2011-02-19 18:05:55 +03:00
}
2011-01-20 13:40:27 +03:00
2014-12-05 14:36:02 +03:00
cmd - > cmd_complete = generic_cmd_complete ;
2014-08-18 21:33:33 +04:00
err = hci_disconnect ( conn , HCI_ERROR_REMOTE_USER_TERM ) ;
2011-01-20 13:40:27 +03:00
if ( err < 0 )
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-20 13:40:27 +03:00
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-20 13:40:27 +03:00
return err ;
}
2012-04-25 04:02:50 +04:00
static u8 link_to_bdaddr ( u8 link_type , u8 addr_type )
2011-11-08 01:13:39 +04:00
{
switch ( link_type ) {
case LE_LINK :
2011-11-09 15:58:58 +04:00
switch ( addr_type ) {
case ADDR_LE_DEV_PUBLIC :
2012-04-25 04:02:49 +04:00
return BDADDR_LE_PUBLIC ;
2012-04-03 15:46:54 +04:00
2011-11-09 15:58:58 +04:00
default :
2012-04-03 15:46:54 +04:00
/* Fallback to LE Random address type */
2012-04-25 04:02:49 +04:00
return BDADDR_LE_RANDOM ;
2011-11-09 15:58:58 +04:00
}
2012-04-03 15:46:54 +04:00
2011-11-08 01:13:39 +04:00
default :
2012-04-03 15:46:54 +04:00
/* Fallback to BR/EDR type */
2012-04-25 04:02:49 +04:00
return BDADDR_BREDR ;
2011-11-08 01:13:39 +04:00
}
}
2012-03-08 08:25:00 +04:00
static int get_connections ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 data_len )
2011-01-21 14:56:35 +03:00
{
struct mgmt_rp_get_connections * rp ;
2011-11-01 12:58:56 +04:00
struct hci_conn * c ;
2011-01-22 07:46:43 +03:00
size_t rp_len ;
2012-02-23 11:52:28 +04:00
int err ;
u16 i ;
2011-01-21 14:56:35 +03:00
BT_DBG ( " " ) ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-21 14:56:35 +03:00
2012-02-23 00:41:18 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_GET_CONNECTIONS ,
MGMT_STATUS_NOT_POWERED ) ;
2012-02-23 00:41:18 +04:00
goto unlock ;
}
2012-02-23 11:52:28 +04:00
i = 0 ;
2012-01-17 23:48:47 +04:00
list_for_each_entry ( c , & hdev - > conn_hash . list , list ) {
if ( test_bit ( HCI_CONN_MGMT_CONNECTED , & c - > flags ) )
2012-02-23 11:52:28 +04:00
i + + ;
2011-01-21 14:56:35 +03:00
}
2012-02-23 11:52:28 +04:00
rp_len = sizeof ( * rp ) + ( i * sizeof ( struct mgmt_addr_info ) ) ;
2012-06-08 02:05:44 +04:00
rp = kmalloc ( rp_len , GFP_KERNEL ) ;
2011-01-22 07:46:43 +03:00
if ( ! rp ) {
2011-01-21 14:56:35 +03:00
err = - ENOMEM ;
goto unlock ;
}
i = 0 ;
2011-11-08 01:13:39 +04:00
list_for_each_entry ( c , & hdev - > conn_hash . list , list ) {
2012-01-17 23:48:47 +04:00
if ( ! test_bit ( HCI_CONN_MGMT_CONNECTED , & c - > flags ) )
continue ;
2011-11-08 01:13:39 +04:00
bacpy ( & rp - > addr [ i ] . bdaddr , & c - > dst ) ;
2012-04-25 04:02:50 +04:00
rp - > addr [ i ] . type = link_to_bdaddr ( c - > type , c - > dst_type ) ;
2012-04-03 15:46:54 +04:00
if ( c - > type = = SCO_LINK | | c - > type = = ESCO_LINK )
2011-11-08 01:13:39 +04:00
continue ;
i + + ;
}
2012-03-14 20:08:46 +04:00
rp - > conn_count = cpu_to_le16 ( i ) ;
2012-02-23 11:52:28 +04:00
2011-11-08 01:13:39 +04:00
/* Recalculate length in case of filtered SCO connections, etc */
rp_len = sizeof ( * rp ) + ( i * sizeof ( struct mgmt_addr_info ) ) ;
2011-01-21 14:56:35 +03:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CONNECTIONS , 0 , rp ,
rp_len ) ;
2011-01-21 14:56:35 +03:00
2011-01-22 07:46:43 +03:00
kfree ( rp ) ;
2012-02-23 00:41:18 +04:00
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-21 14:56:35 +03:00
return err ;
}
2012-02-28 08:13:32 +04:00
static int send_pin_code_neg_reply ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
struct mgmt_cp_pin_code_neg_reply * cp )
2011-06-01 19:28:48 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-06-01 19:28:48 +04:00
int err ;
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_PIN_CODE_NEG_REPLY , hdev , cp ,
2012-03-08 08:25:00 +04:00
sizeof ( * cp ) ) ;
2011-06-01 19:28:48 +04:00
if ( ! cmd )
return - ENOMEM ;
2016-09-22 15:08:05 +03:00
cmd - > cmd_complete = addr_cmd_complete ;
2012-02-17 16:24:57 +04:00
err = hci_send_cmd ( hdev , HCI_OP_PIN_CODE_NEG_REPLY ,
2012-03-08 08:25:00 +04:00
sizeof ( cp - > addr . bdaddr ) , & cp - > addr . bdaddr ) ;
2011-06-01 19:28:48 +04:00
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
return err ;
}
2012-02-28 08:13:32 +04:00
static int pin_code_reply ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-01-22 07:10:07 +03:00
{
2011-06-01 19:28:48 +04:00
struct hci_conn * conn ;
2012-02-03 04:07:59 +04:00
struct mgmt_cp_pin_code_reply * cp = data ;
2011-01-22 07:10:07 +03:00
struct hci_cp_pin_code_reply reply ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-01-22 07:10:07 +03:00
int err ;
BT_DBG ( " " ) ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-22 07:10:07 +03:00
2012-02-21 16:13:02 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_PIN_CODE_REPLY ,
MGMT_STATUS_NOT_POWERED ) ;
2011-01-22 07:10:07 +03:00
goto failed ;
}
2012-02-17 16:24:57 +04:00
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , & cp - > addr . bdaddr ) ;
2011-06-01 19:28:48 +04:00
if ( ! conn ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_PIN_CODE_REPLY ,
MGMT_STATUS_NOT_CONNECTED ) ;
2011-06-01 19:28:48 +04:00
goto failed ;
}
if ( conn - > pending_sec_level = = BT_SECURITY_HIGH & & cp - > pin_len ! = 16 ) {
2012-02-17 16:24:57 +04:00
struct mgmt_cp_pin_code_neg_reply ncp ;
memcpy ( & ncp . addr , & cp - > addr , sizeof ( ncp . addr ) ) ;
2011-06-01 19:28:48 +04:00
BT_ERR ( " PIN code is not 16 bytes long " ) ;
2012-02-28 08:13:32 +04:00
err = send_pin_code_neg_reply ( sk , hdev , & ncp ) ;
2011-06-01 19:28:48 +04:00
if ( err > = 0 )
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_PIN_CODE_REPLY ,
MGMT_STATUS_INVALID_PARAMS ) ;
2011-06-01 19:28:48 +04:00
goto failed ;
}
2012-03-01 07:37:10 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_PIN_CODE_REPLY , hdev , data , len ) ;
2011-02-19 18:05:55 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
2011-01-22 07:10:07 +03:00
goto failed ;
2011-02-19 18:05:55 +03:00
}
2011-01-22 07:10:07 +03:00
2014-12-05 14:36:03 +03:00
cmd - > cmd_complete = addr_cmd_complete ;
2012-02-17 16:24:57 +04:00
bacpy ( & reply . bdaddr , & cp - > addr . bdaddr ) ;
2011-01-22 07:10:07 +03:00
reply . pin_len = cp - > pin_len ;
2011-06-01 19:28:47 +04:00
memcpy ( reply . pin_code , cp - > pin_code , sizeof ( reply . pin_code ) ) ;
2011-01-22 07:10:07 +03:00
err = hci_send_cmd ( hdev , HCI_OP_PIN_CODE_REPLY , sizeof ( reply ) , & reply ) ;
if ( err < 0 )
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-22 07:10:07 +03:00
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-22 07:10:07 +03:00
return err ;
}
2012-03-08 08:25:00 +04:00
static int set_io_capability ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
2011-01-25 14:28:33 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_set_io_capability * cp = data ;
2011-01-25 14:28:33 +03:00
BT_DBG ( " " ) ;
2014-06-17 16:14:48 +04:00
if ( cp - > io_capability > SMP_IO_KEYBOARD_DISPLAY )
2016-08-29 07:31:57 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_IO_CAPABILITY ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-06-17 16:14:48 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-25 14:28:33 +03:00
hdev - > io_capability = cp - > io_capability ;
BT_DBG ( " %s IO capability set to 0x%02x " , hdev - > name ,
2012-05-17 07:36:20 +04:00
hdev - > io_capability ) ;
2011-01-25 14:28:33 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-01-25 14:28:33 +03:00
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_SET_IO_CAPABILITY , 0 ,
NULL , 0 ) ;
2011-01-25 14:28:33 +03:00
}
2015-03-06 22:08:55 +03:00
static struct mgmt_pending_cmd * find_pairing ( struct hci_conn * conn )
2011-02-19 18:05:56 +03:00
{
struct hci_dev * hdev = conn - > hdev ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-02-19 18:05:56 +03:00
2011-11-08 22:40:15 +04:00
list_for_each_entry ( cmd , & hdev - > mgmt_pending , list ) {
2011-02-19 18:05:56 +03:00
if ( cmd - > opcode ! = MGMT_OP_PAIR_DEVICE )
continue ;
if ( cmd - > user_data ! = conn )
continue ;
return cmd ;
}
return NULL ;
}
2015-03-06 22:08:55 +03:00
static int pairing_complete ( struct mgmt_pending_cmd * cmd , u8 status )
2011-02-19 18:05:56 +03:00
{
struct mgmt_rp_pair_device rp ;
struct hci_conn * conn = cmd - > user_data ;
2014-12-19 23:26:03 +03:00
int err ;
2011-02-19 18:05:56 +03:00
2014-03-20 14:54:16 +04:00
bacpy ( & rp . addr . bdaddr , & conn - > dst ) ;
rp . addr . type = link_to_bdaddr ( conn - > type , conn - > dst_type ) ;
2011-02-19 18:05:56 +03:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( cmd - > sk , cmd - > index , MGMT_OP_PAIR_DEVICE ,
status , & rp , sizeof ( rp ) ) ;
2011-02-19 18:05:56 +03:00
/* So we don't get further callbacks for this connection */
conn - > connect_cfm_cb = NULL ;
conn - > security_cfm_cb = NULL ;
conn - > disconn_cfm_cb = NULL ;
2013-04-06 22:28:37 +04:00
hci_conn_drop ( conn ) ;
2014-10-12 01:44:47 +04:00
/* The device is paired so there is no need to remove
* its connection parameters anymore .
*/
clear_bit ( HCI_CONN_PARAM_REMOVAL_PEND , & conn - > flags ) ;
2014-12-11 22:45:44 +03:00
hci_conn_put ( conn ) ;
2014-12-19 23:26:03 +03:00
return err ;
2011-02-19 18:05:56 +03:00
}
2014-02-18 23:41:34 +04:00
void mgmt_smp_complete ( struct hci_conn * conn , bool complete )
{
u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-02-18 23:41:34 +04:00
cmd = find_pairing ( conn ) ;
2014-12-11 22:45:45 +03:00
if ( cmd ) {
2014-12-05 14:36:04 +03:00
cmd - > cmd_complete ( cmd , status ) ;
2014-12-11 22:45:45 +03:00
mgmt_pending_remove ( cmd ) ;
}
2014-02-18 23:41:34 +04:00
}
2011-02-19 18:05:56 +03:00
static void pairing_complete_cb ( struct hci_conn * conn , u8 status )
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-02-19 18:05:56 +03:00
BT_DBG ( " status %u " , status ) ;
cmd = find_pairing ( conn ) ;
2014-12-11 22:45:45 +03:00
if ( ! cmd ) {
2011-02-19 18:05:56 +03:00
BT_DBG ( " Unable to find a pending command " ) ;
2014-12-11 22:45:45 +03:00
return ;
}
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
mgmt_pending_remove ( cmd ) ;
2011-02-19 18:05:56 +03:00
}
2014-02-18 23:41:34 +04:00
static void le_pairing_complete_cb ( struct hci_conn * conn , u8 status )
2012-06-07 18:57:35 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2012-06-07 18:57:35 +04:00
BT_DBG ( " status %u " , status ) ;
if ( ! status )
return ;
cmd = find_pairing ( conn ) ;
2014-12-11 22:45:45 +03:00
if ( ! cmd ) {
2012-06-07 18:57:35 +04:00
BT_DBG ( " Unable to find a pending command " ) ;
2014-12-11 22:45:45 +03:00
return ;
}
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
mgmt_pending_remove ( cmd ) ;
2012-06-07 18:57:35 +04:00
}
2012-02-28 08:13:32 +04:00
static int pair_device ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-02-19 18:05:56 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_pair_device * cp = data ;
2011-11-11 02:07:35 +04:00
struct mgmt_rp_pair_device rp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-02-19 18:05:56 +03:00
u8 sec_level , auth_type ;
struct hci_conn * conn ;
int err ;
BT_DBG ( " " ) ;
2013-01-18 15:48:07 +04:00
memset ( & rp , 0 , sizeof ( rp ) ) ;
bacpy ( & rp . addr . bdaddr , & cp - > addr . bdaddr ) ;
rp . addr . type = cp - > addr . type ;
2013-01-20 16:27:19 +04:00
if ( ! bdaddr_type_is_valid ( cp - > addr . type ) )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& rp , sizeof ( rp ) ) ;
2013-01-20 16:27:19 +04:00
2014-06-17 16:14:48 +04:00
if ( cp - > io_cap > SMP_IO_KEYBOARD_DISPLAY )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& rp , sizeof ( rp ) ) ;
2014-06-17 16:14:48 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-02-19 18:05:56 +03:00
2012-02-23 00:41:18 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
MGMT_STATUS_NOT_POWERED , & rp ,
sizeof ( rp ) ) ;
2012-02-23 00:41:18 +04:00
goto unlock ;
}
2015-03-10 23:34:40 +03:00
if ( hci_bdaddr_is_paired ( hdev , & cp - > addr . bdaddr , cp - > addr . type ) ) {
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
MGMT_STATUS_ALREADY_PAIRED , & rp ,
sizeof ( rp ) ) ;
goto unlock ;
}
2011-09-02 21:51:22 +04:00
sec_level = BT_SECURITY_MEDIUM ;
2014-04-08 16:21:32 +04:00
auth_type = HCI_AT_DEDICATED_BONDING ;
2011-02-19 18:05:56 +03:00
2014-02-27 03:21:45 +04:00
if ( cp - > addr . type = = BDADDR_BREDR ) {
2014-02-27 03:21:44 +04:00
conn = hci_connect_acl ( hdev , & cp - > addr . bdaddr , sec_level ,
auth_type ) ;
2014-02-27 03:21:45 +04:00
} else {
2015-10-21 18:02:59 +03:00
u8 addr_type = le_addr_type ( cp - > addr . type ) ;
2015-10-16 10:07:54 +03:00
struct hci_conn_params * p ;
2014-02-27 03:21:45 +04:00
2014-06-30 14:34:40 +04:00
/* When pairing a new device, it is expected to remember
* this device for future connections . Adding the connection
* parameter information ahead of time allows tracking
* of the slave preferred values and will speed up any
* further connection establishment .
*
* If connection parameters already exist , then they
* will be kept and this function does nothing .
*/
2015-10-16 10:07:54 +03:00
p = hci_conn_params_add ( hdev , & cp - > addr . bdaddr , addr_type ) ;
if ( p - > auto_connect = = HCI_AUTO_CONN_EXPLICIT )
p - > auto_connect = HCI_AUTO_CONN_DISABLED ;
2014-06-30 14:34:40 +04:00
2015-08-07 21:22:56 +03:00
conn = hci_connect_le_scan ( hdev , & cp - > addr . bdaddr ,
addr_type , sec_level ,
2015-11-11 15:44:57 +03:00
HCI_LE_CONN_TIMEOUT ) ;
2014-02-27 03:21:45 +04:00
}
2011-08-20 04:06:54 +04:00
2011-02-22 22:10:53 +03:00
if ( IS_ERR ( conn ) ) {
2012-05-30 17:39:22 +04:00
int status ;
if ( PTR_ERR ( conn ) = = - EBUSY )
status = MGMT_STATUS_BUSY ;
2015-02-11 14:31:42 +03:00
else if ( PTR_ERR ( conn ) = = - EOPNOTSUPP )
status = MGMT_STATUS_NOT_SUPPORTED ;
else if ( PTR_ERR ( conn ) = = - ECONNREFUSED )
status = MGMT_STATUS_REJECTED ;
2012-05-30 17:39:22 +04:00
else
status = MGMT_STATUS_CONNECT_FAILED ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
status , & rp , sizeof ( rp ) ) ;
2011-02-19 18:05:56 +03:00
goto unlock ;
}
if ( conn - > connect_cfm_cb ) {
2013-04-06 22:28:37 +04:00
hci_conn_drop ( conn ) ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
MGMT_STATUS_BUSY , & rp , sizeof ( rp ) ) ;
2011-02-19 18:05:56 +03:00
goto unlock ;
}
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_PAIR_DEVICE , hdev , data , len ) ;
2011-02-19 18:05:56 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
2013-04-06 22:28:37 +04:00
hci_conn_drop ( conn ) ;
2011-02-19 18:05:56 +03:00
goto unlock ;
}
2014-12-05 14:36:04 +03:00
cmd - > cmd_complete = pairing_complete ;
2011-08-20 04:06:54 +04:00
/* For LE, just connecting isn't a proof that the pairing finished */
2014-02-18 23:41:34 +04:00
if ( cp - > addr . type = = BDADDR_BREDR ) {
2011-08-20 04:06:54 +04:00
conn - > connect_cfm_cb = pairing_complete_cb ;
2014-02-18 23:41:34 +04:00
conn - > security_cfm_cb = pairing_complete_cb ;
conn - > disconn_cfm_cb = pairing_complete_cb ;
} else {
conn - > connect_cfm_cb = le_pairing_complete_cb ;
conn - > security_cfm_cb = le_pairing_complete_cb ;
conn - > disconn_cfm_cb = le_pairing_complete_cb ;
}
2011-08-20 04:06:54 +04:00
2011-02-19 18:05:56 +03:00
conn - > io_capability = cp - > io_cap ;
2014-08-18 00:28:57 +04:00
cmd - > user_data = hci_conn_get ( conn ) ;
2011-02-19 18:05:56 +03:00
2014-07-30 09:35:48 +04:00
if ( ( conn - > state = = BT_CONNECTED | | conn - > state = = BT_CONFIG ) & &
2014-12-11 22:45:45 +03:00
hci_conn_security ( conn , sec_level , auth_type , true ) ) {
cmd - > cmd_complete ( cmd , 0 ) ;
mgmt_pending_remove ( cmd ) ;
}
2011-02-19 18:05:56 +03:00
err = 0 ;
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-02-19 18:05:56 +03:00
return err ;
}
2012-03-08 08:25:00 +04:00
static int cancel_pair_device ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
2012-02-02 06:02:29 +04:00
{
2012-02-28 19:18:30 +04:00
struct mgmt_addr_info * addr = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2012-02-02 06:02:29 +04:00
struct hci_conn * conn ;
int err ;
BT_DBG ( " " ) ;
hci_dev_lock ( hdev ) ;
2012-02-23 00:41:18 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_CANCEL_PAIR_DEVICE ,
MGMT_STATUS_NOT_POWERED ) ;
2012-02-23 00:41:18 +04:00
goto unlock ;
}
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_PAIR_DEVICE , hdev ) ;
2012-02-02 06:02:29 +04:00
if ( ! cmd ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_CANCEL_PAIR_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ) ;
2012-02-02 06:02:29 +04:00
goto unlock ;
}
conn = cmd - > user_data ;
if ( bacmp ( & addr - > bdaddr , & conn - > dst ) ! = 0 ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_CANCEL_PAIR_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ) ;
2012-02-02 06:02:29 +04:00
goto unlock ;
}
2014-12-11 22:45:45 +03:00
cmd - > cmd_complete ( cmd , MGMT_STATUS_CANCELLED ) ;
mgmt_pending_remove ( cmd ) ;
2012-02-02 06:02:29 +04:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_CANCEL_PAIR_DEVICE , 0 ,
addr , sizeof ( * addr ) ) ;
2012-02-02 06:02:29 +04:00
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2012-02-28 08:13:32 +04:00
static int user_pairing_resp ( struct sock * sk , struct hci_dev * hdev ,
2013-03-16 02:07:15 +04:00
struct mgmt_addr_info * addr , u16 mgmt_op ,
2012-03-08 08:25:00 +04:00
u16 hci_op , __le32 passkey )
2011-02-19 18:05:57 +03:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-11-17 01:53:13 +04:00
struct hci_conn * conn ;
2011-02-19 18:05:57 +03:00
int err ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-03-16 15:29:34 +03:00
2012-02-21 16:13:02 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , mgmt_op ,
MGMT_STATUS_NOT_POWERED , addr ,
sizeof ( * addr ) ) ;
2011-11-17 01:53:13 +04:00
goto done ;
2011-02-19 18:05:57 +03:00
}
2013-03-16 02:07:15 +04:00
if ( addr - > type = = BDADDR_BREDR )
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , & addr - > bdaddr ) ;
2012-02-09 17:26:12 +04:00
else
2015-10-21 18:03:01 +03:00
conn = hci_conn_hash_lookup_le ( hdev , & addr - > bdaddr ,
le_addr_type ( addr - > type ) ) ;
2012-02-09 17:26:12 +04:00
if ( ! conn ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , mgmt_op ,
MGMT_STATUS_NOT_CONNECTED , addr ,
sizeof ( * addr ) ) ;
2012-02-09 17:26:12 +04:00
goto done ;
}
2011-11-17 01:53:14 +04:00
2013-03-16 02:07:15 +04:00
if ( addr - > type = = BDADDR_LE_PUBLIC | | addr - > type = = BDADDR_LE_RANDOM ) {
2011-12-22 04:12:13 +04:00
err = smp_user_confirm_reply ( conn , mgmt_op , passkey ) ;
if ( ! err )
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , mgmt_op ,
MGMT_STATUS_SUCCESS , addr ,
sizeof ( * addr ) ) ;
2011-12-22 04:12:13 +04:00
else
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , mgmt_op ,
MGMT_STATUS_FAILED , addr ,
sizeof ( * addr ) ) ;
2011-11-17 01:53:14 +04:00
goto done ;
}
2013-03-16 02:07:15 +04:00
cmd = mgmt_pending_add ( sk , mgmt_op , hdev , addr , sizeof ( * addr ) ) ;
2011-02-19 18:05:57 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
2011-11-17 01:53:13 +04:00
goto done ;
2011-02-19 18:05:57 +03:00
}
2014-12-05 14:36:03 +03:00
cmd - > cmd_complete = addr_cmd_complete ;
2011-11-17 01:53:13 +04:00
/* Continue with pairing via HCI */
2011-11-23 20:28:33 +04:00
if ( hci_op = = HCI_OP_USER_PASSKEY_REPLY ) {
struct hci_cp_user_passkey_reply cp ;
2013-03-16 02:07:15 +04:00
bacpy ( & cp . bdaddr , & addr - > bdaddr ) ;
2011-11-23 20:28:33 +04:00
cp . passkey = passkey ;
err = hci_send_cmd ( hdev , hci_op , sizeof ( cp ) , & cp ) ;
} else
2013-03-16 02:07:15 +04:00
err = hci_send_cmd ( hdev , hci_op , sizeof ( addr - > bdaddr ) ,
& addr - > bdaddr ) ;
2011-11-23 20:28:33 +04:00
2011-02-19 18:06:02 +03:00
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
2011-02-19 18:05:57 +03:00
2011-11-17 01:53:13 +04:00
done :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-02-19 18:05:57 +03:00
return err ;
}
2012-07-09 14:41:51 +04:00
static int pin_code_neg_reply ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_cp_pin_code_neg_reply * cp = data ;
BT_DBG ( " " ) ;
2013-03-16 02:07:15 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr ,
2012-07-09 14:41:51 +04:00
MGMT_OP_PIN_CODE_NEG_REPLY ,
HCI_OP_PIN_CODE_NEG_REPLY , 0 ) ;
}
2012-03-08 08:25:00 +04:00
static int user_confirm_reply ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
2011-11-17 01:53:13 +04:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_user_confirm_reply * cp = data ;
2011-11-17 01:53:13 +04:00
BT_DBG ( " " ) ;
if ( len ! = sizeof ( * cp ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_USER_CONFIRM_REPLY ,
MGMT_STATUS_INVALID_PARAMS ) ;
2011-11-17 01:53:13 +04:00
2013-03-16 02:07:15 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr ,
2012-03-08 08:25:00 +04:00
MGMT_OP_USER_CONFIRM_REPLY ,
HCI_OP_USER_CONFIRM_REPLY , 0 ) ;
2011-11-17 01:53:13 +04:00
}
2012-02-28 08:13:32 +04:00
static int user_confirm_neg_reply ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void * data , u16 len )
2011-11-17 01:53:13 +04:00
{
2011-12-15 02:47:41 +04:00
struct mgmt_cp_user_confirm_neg_reply * cp = data ;
2011-11-17 01:53:13 +04:00
BT_DBG ( " " ) ;
2013-03-16 02:07:15 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr ,
2012-03-08 08:25:00 +04:00
MGMT_OP_USER_CONFIRM_NEG_REPLY ,
HCI_OP_USER_CONFIRM_NEG_REPLY , 0 ) ;
2011-11-17 01:53:13 +04:00
}
2012-03-08 08:25:00 +04:00
static int user_passkey_reply ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
2011-11-23 20:28:33 +04:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_user_passkey_reply * cp = data ;
2011-11-23 20:28:33 +04:00
BT_DBG ( " " ) ;
2013-03-16 02:07:15 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr ,
2012-03-08 08:25:00 +04:00
MGMT_OP_USER_PASSKEY_REPLY ,
HCI_OP_USER_PASSKEY_REPLY , cp - > passkey ) ;
2011-11-23 20:28:33 +04:00
}
2012-02-28 08:13:32 +04:00
static int user_passkey_neg_reply ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void * data , u16 len )
2011-11-23 20:28:33 +04:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_user_passkey_neg_reply * cp = data ;
2011-11-23 20:28:33 +04:00
BT_DBG ( " " ) ;
2013-03-16 02:07:15 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr ,
2012-03-08 08:25:00 +04:00
MGMT_OP_USER_PASSKEY_NEG_REPLY ,
HCI_OP_USER_PASSKEY_NEG_REPLY , 0 ) ;
2011-11-23 20:28:33 +04:00
}
2016-09-18 13:50:02 +03:00
static void adv_expire ( struct hci_dev * hdev , u32 flags )
{
struct adv_info * adv_instance ;
struct hci_request req ;
int err ;
adv_instance = hci_find_adv_instance ( hdev , hdev - > cur_adv_instance ) ;
if ( ! adv_instance )
return ;
/* stop if current instance doesn't need to be changed */
if ( ! ( adv_instance - > flags & flags ) )
return ;
cancel_adv_timeout ( hdev ) ;
adv_instance = hci_get_next_instance ( hdev , adv_instance - > instance ) ;
if ( ! adv_instance )
return ;
hci_req_init ( & req , hdev ) ;
err = __hci_req_schedule_adv_instance ( & req , adv_instance - > instance ,
true ) ;
if ( err )
return ;
hci_req_run ( & req , NULL ) ;
}
2015-01-12 00:50:44 +03:00
static void set_name_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
2013-03-16 02:07:00 +04:00
{
struct mgmt_cp_set_local_name * cp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:07:00 +04:00
BT_DBG ( " status 0x%02x " , status ) ;
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_LOCAL_NAME , hdev ) ;
2013-03-16 02:07:00 +04:00
if ( ! cmd )
goto unlock ;
cp = cmd - > param ;
2016-09-18 13:50:02 +03:00
if ( status ) {
2015-03-06 22:08:53 +03:00
mgmt_cmd_status ( cmd - > sk , hdev - > id , MGMT_OP_SET_LOCAL_NAME ,
mgmt_status ( status ) ) ;
2016-09-18 13:50:02 +03:00
} else {
2015-03-06 22:08:54 +03:00
mgmt_cmd_complete ( cmd - > sk , hdev - > id , MGMT_OP_SET_LOCAL_NAME , 0 ,
cp , sizeof ( * cp ) ) ;
2013-03-16 02:07:00 +04:00
2016-09-18 13:50:02 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LE_ADV ) )
adv_expire ( hdev , MGMT_ADV_FLAG_LOCAL_NAME ) ;
}
2013-03-16 02:07:00 +04:00
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
2012-02-28 08:13:32 +04:00
static int set_local_name ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-03-16 15:29:37 +03:00
{
2012-03-03 02:19:06 +04:00
struct mgmt_cp_set_local_name * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:06:52 +04:00
struct hci_request req ;
2011-03-16 15:29:37 +03:00
int err ;
BT_DBG ( " " ) ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-03-16 15:29:37 +03:00
2013-03-16 02:07:03 +04:00
/* If the old values are the same as the new ones just return a
* direct command complete event .
*/
if ( ! memcmp ( hdev - > dev_name , cp - > name , sizeof ( hdev - > dev_name ) ) & &
! memcmp ( hdev - > short_name , cp - > short_name ,
sizeof ( hdev - > short_name ) ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_SET_LOCAL_NAME , 0 ,
data , len ) ;
2013-03-16 02:07:03 +04:00
goto failed ;
}
2012-03-03 02:19:06 +04:00
memcpy ( hdev - > short_name , cp - > short_name , sizeof ( hdev - > short_name ) ) ;
2012-02-22 23:06:55 +04:00
2012-02-21 16:32:24 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2012-03-03 02:19:06 +04:00
memcpy ( hdev - > dev_name , cp - > name , sizeof ( hdev - > dev_name ) ) ;
2012-02-22 23:06:55 +04:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_SET_LOCAL_NAME , 0 ,
data , len ) ;
2012-02-22 23:06:55 +04:00
if ( err < 0 )
goto failed ;
2016-08-29 07:19:46 +03:00
err = mgmt_limited_event ( MGMT_EV_LOCAL_NAME_CHANGED , hdev , data ,
len , HCI_MGMT_LOCAL_NAME_EVENTS , sk ) ;
2016-09-01 17:46:23 +03:00
ext_info_changed ( hdev , sk ) ;
2012-02-22 23:06:55 +04:00
2012-02-21 16:32:24 +04:00
goto failed ;
}
2012-02-22 23:06:55 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_LOCAL_NAME , hdev , data , len ) ;
2011-03-16 15:29:37 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
goto failed ;
}
2013-03-16 02:07:00 +04:00
memcpy ( hdev - > dev_name , cp - > name , sizeof ( hdev - > dev_name ) ) ;
2013-03-16 02:06:52 +04:00
hci_req_init ( & req , hdev ) ;
2013-03-16 02:07:02 +04:00
if ( lmp_bredr_capable ( hdev ) ) {
2015-11-25 17:15:41 +03:00
__hci_req_update_name ( & req ) ;
2015-11-25 17:15:42 +03:00
__hci_req_update_eir ( & req ) ;
2013-03-16 02:07:02 +04:00
}
2013-10-16 11:16:49 +04:00
/* The name is stored in the scan response data and so
* no need to udpate the advertising data here .
*/
2016-09-22 17:01:39 +03:00
if ( lmp_le_capable ( hdev ) & & hci_dev_test_flag ( hdev , HCI_ADVERTISING ) )
2015-11-30 12:21:45 +03:00
__hci_req_update_scan_rsp_data ( & req , hdev - > cur_adv_instance ) ;
2013-03-16 02:07:02 +04:00
2013-03-16 02:07:00 +04:00
err = hci_req_run ( & req , set_name_complete ) ;
2011-03-16 15:29:37 +03:00
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-03-16 15:29:37 +03:00
return err ;
}
2016-09-18 13:50:03 +03:00
static int set_appearance ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
{
struct mgmt_cp_set_appearance * cp = data ;
u16 apperance ;
int err ;
BT_DBG ( " " ) ;
2016-09-19 15:33:33 +03:00
if ( ! lmp_le_capable ( hdev ) )
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_APPEARANCE ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2016-09-18 13:50:03 +03:00
apperance = le16_to_cpu ( cp - > appearance ) ;
hci_dev_lock ( hdev ) ;
if ( hdev - > appearance ! = apperance ) {
hdev - > appearance = apperance ;
if ( hci_dev_test_flag ( hdev , HCI_LE_ADV ) )
adv_expire ( hdev , MGMT_ADV_FLAG_APPEARANCE ) ;
2016-09-19 21:25:56 +03:00
ext_info_changed ( hdev , sk ) ;
2016-09-18 13:50:03 +03:00
}
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_SET_APPEARANCE , 0 , NULL ,
0 ) ;
hci_dev_unlock ( hdev ) ;
return err ;
}
2015-04-02 13:41:13 +03:00
static void read_local_oob_data_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode , struct sk_buff * skb )
{
struct mgmt_rp_read_local_oob_data mgmt_rp ;
size_t rp_size = sizeof ( mgmt_rp ) ;
struct mgmt_pending_cmd * cmd ;
BT_DBG ( " %s status %u " , hdev - > name , status ) ;
cmd = pending_find ( MGMT_OP_READ_LOCAL_OOB_DATA , hdev ) ;
if ( ! cmd )
return ;
if ( status | | ! skb ) {
mgmt_cmd_status ( cmd - > sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
status ? mgmt_status ( status ) : MGMT_STATUS_FAILED ) ;
goto remove ;
}
memset ( & mgmt_rp , 0 , sizeof ( mgmt_rp ) ) ;
if ( opcode = = HCI_OP_READ_LOCAL_OOB_DATA ) {
struct hci_rp_read_local_oob_data * rp = ( void * ) skb - > data ;
if ( skb - > len < sizeof ( * rp ) ) {
mgmt_cmd_status ( cmd - > sk , hdev - > id ,
MGMT_OP_READ_LOCAL_OOB_DATA ,
MGMT_STATUS_FAILED ) ;
goto remove ;
}
memcpy ( mgmt_rp . hash192 , rp - > hash , sizeof ( rp - > hash ) ) ;
memcpy ( mgmt_rp . rand192 , rp - > rand , sizeof ( rp - > rand ) ) ;
rp_size - = sizeof ( mgmt_rp . hash256 ) + sizeof ( mgmt_rp . rand256 ) ;
} else {
struct hci_rp_read_local_oob_ext_data * rp = ( void * ) skb - > data ;
if ( skb - > len < sizeof ( * rp ) ) {
mgmt_cmd_status ( cmd - > sk , hdev - > id ,
MGMT_OP_READ_LOCAL_OOB_DATA ,
MGMT_STATUS_FAILED ) ;
goto remove ;
}
memcpy ( mgmt_rp . hash192 , rp - > hash192 , sizeof ( rp - > hash192 ) ) ;
memcpy ( mgmt_rp . rand192 , rp - > rand192 , sizeof ( rp - > rand192 ) ) ;
memcpy ( mgmt_rp . hash256 , rp - > hash256 , sizeof ( rp - > hash256 ) ) ;
memcpy ( mgmt_rp . rand256 , rp - > rand256 , sizeof ( rp - > rand256 ) ) ;
}
mgmt_cmd_complete ( cmd - > sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
MGMT_STATUS_SUCCESS , & mgmt_rp , rp_size ) ;
remove :
mgmt_pending_remove ( cmd ) ;
}
2012-02-28 19:18:30 +04:00
static int read_local_oob_data ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void * data , u16 data_len )
2011-03-22 15:12:21 +03:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2015-04-02 13:41:13 +03:00
struct hci_request req ;
2011-03-22 15:12:21 +03:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-03-22 15:12:21 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-03-22 15:12:21 +03:00
2012-02-21 16:13:02 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
MGMT_STATUS_NOT_POWERED ) ;
2011-03-22 15:12:21 +03:00
goto unlock ;
}
2012-07-24 22:03:48 +04:00
if ( ! lmp_ssp_capable ( hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2011-03-22 15:12:21 +03:00
goto unlock ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_READ_LOCAL_OOB_DATA , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
MGMT_STATUS_BUSY ) ;
2011-03-22 15:12:21 +03:00
goto unlock ;
}
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_READ_LOCAL_OOB_DATA , hdev , NULL , 0 ) ;
2011-03-22 15:12:21 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
2015-04-02 13:41:13 +03:00
hci_req_init ( & req , hdev ) ;
2014-05-26 12:21:22 +04:00
if ( bredr_sc_enabled ( hdev ) )
2015-04-02 13:41:13 +03:00
hci_req_add ( & req , HCI_OP_READ_LOCAL_OOB_EXT_DATA , 0 , NULL ) ;
2014-01-10 14:07:26 +04:00
else
2015-04-02 13:41:13 +03:00
hci_req_add ( & req , HCI_OP_READ_LOCAL_OOB_DATA , 0 , NULL ) ;
2014-01-10 14:07:26 +04:00
2015-04-02 13:41:13 +03:00
err = hci_req_run_skb ( & req , read_local_oob_data_complete ) ;
2011-03-22 15:12:21 +03:00
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-03-22 15:12:21 +03:00
return err ;
}
2012-02-28 08:13:32 +04:00
static int add_remote_oob_data ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void * data , u16 len )
2011-03-22 15:12:22 +03:00
{
2015-01-23 11:10:38 +03:00
struct mgmt_addr_info * addr = data ;
2011-03-22 15:12:22 +03:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-03-22 15:12:22 +03:00
2015-01-23 11:10:38 +03:00
if ( ! bdaddr_type_is_valid ( addr - > type ) )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_ADD_REMOTE_OOB_DATA ,
MGMT_STATUS_INVALID_PARAMS ,
addr , sizeof ( * addr ) ) ;
2015-01-23 11:10:38 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-03-22 15:12:22 +03:00
2014-01-10 14:07:30 +04:00
if ( len = = MGMT_ADD_REMOTE_OOB_DATA_SIZE ) {
struct mgmt_cp_add_remote_oob_data * cp = data ;
u8 status ;
2012-02-19 15:16:14 +04:00
2014-11-17 21:52:19 +03:00
if ( cp - > addr . type ! = BDADDR_BREDR ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_ADD_REMOTE_OOB_DATA ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-11-17 21:52:19 +03:00
goto unlock ;
}
2014-01-10 14:07:30 +04:00
err = hci_add_remote_oob_data ( hdev , & cp - > addr . bdaddr ,
2014-10-26 22:46:09 +03:00
cp - > addr . type , cp - > hash ,
cp - > rand , NULL , NULL ) ;
2014-01-10 14:07:30 +04:00
if ( err < 0 )
status = MGMT_STATUS_FAILED ;
else
status = MGMT_STATUS_SUCCESS ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_ADD_REMOTE_OOB_DATA , status ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-01-10 14:07:30 +04:00
} else if ( len = = MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE ) {
struct mgmt_cp_add_remote_oob_ext_data * cp = data ;
2015-01-31 11:37:02 +03:00
u8 * rand192 , * hash192 , * rand256 , * hash256 ;
2014-01-10 14:07:30 +04:00
u8 status ;
2014-10-26 22:52:27 +03:00
if ( bdaddr_type_is_le ( cp - > addr . type ) ) {
2015-01-27 13:55:52 +03:00
/* Enforce zero-valued 192-bit parameters as
* long as legacy SMP OOB isn ' t implemented .
*/
if ( memcmp ( cp - > rand192 , ZERO_KEY , 16 ) | |
memcmp ( cp - > hash192 , ZERO_KEY , 16 ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_ADD_REMOTE_OOB_DATA ,
MGMT_STATUS_INVALID_PARAMS ,
addr , sizeof ( * addr ) ) ;
2015-01-27 13:55:52 +03:00
goto unlock ;
}
2014-10-26 22:52:27 +03:00
rand192 = NULL ;
hash192 = NULL ;
} else {
2015-01-31 11:37:02 +03:00
/* In case one of the P-192 values is set to zero,
* then just disable OOB data for P - 192.
*/
if ( ! memcmp ( cp - > rand192 , ZERO_KEY , 16 ) | |
! memcmp ( cp - > hash192 , ZERO_KEY , 16 ) ) {
rand192 = NULL ;
hash192 = NULL ;
} else {
rand192 = cp - > rand192 ;
hash192 = cp - > hash192 ;
}
}
/* In case one of the P-256 values is set to zero, then just
* disable OOB data for P - 256.
*/
if ( ! memcmp ( cp - > rand256 , ZERO_KEY , 16 ) | |
! memcmp ( cp - > hash256 , ZERO_KEY , 16 ) ) {
rand256 = NULL ;
hash256 = NULL ;
} else {
rand256 = cp - > rand256 ;
hash256 = cp - > hash256 ;
2014-10-26 22:52:27 +03:00
}
2014-10-26 22:33:47 +03:00
err = hci_add_remote_oob_data ( hdev , & cp - > addr . bdaddr ,
2014-10-26 22:52:27 +03:00
cp - > addr . type , hash192 , rand192 ,
2015-01-31 11:37:02 +03:00
hash256 , rand256 ) ;
2014-01-10 14:07:30 +04:00
if ( err < 0 )
status = MGMT_STATUS_FAILED ;
else
status = MGMT_STATUS_SUCCESS ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_ADD_REMOTE_OOB_DATA ,
status , & cp - > addr , sizeof ( cp - > addr ) ) ;
2014-01-10 14:07:30 +04:00
} else {
BT_ERR ( " add_remote_oob_data: invalid length of %u bytes " , len ) ;
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_REMOTE_OOB_DATA ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-01-10 14:07:30 +04:00
}
2011-03-22 15:12:22 +03:00
2014-11-17 21:52:19 +03:00
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-03-22 15:12:22 +03:00
return err ;
}
2012-02-28 08:13:32 +04:00
static int remove_remote_oob_data ( struct sock * sk , struct hci_dev * hdev ,
2012-05-17 07:36:20 +04:00
void * data , u16 len )
2011-03-22 15:12:22 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_remove_remote_oob_data * cp = data ;
2012-02-19 15:16:14 +04:00
u8 status ;
2011-03-22 15:12:22 +03:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-03-22 15:12:22 +03:00
2014-11-17 21:52:19 +03:00
if ( cp - > addr . type ! = BDADDR_BREDR )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_REMOTE_OOB_DATA ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-11-17 21:52:19 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-03-22 15:12:22 +03:00
2014-11-15 10:34:23 +03:00
if ( ! bacmp ( & cp - > addr . bdaddr , BDADDR_ANY ) ) {
hci_remote_oob_data_clear ( hdev ) ;
status = MGMT_STATUS_SUCCESS ;
goto done ;
}
2014-10-26 22:46:09 +03:00
err = hci_remove_remote_oob_data ( hdev , & cp - > addr . bdaddr , cp - > addr . type ) ;
2011-03-22 15:12:22 +03:00
if ( err < 0 )
2012-02-19 15:16:14 +04:00
status = MGMT_STATUS_INVALID_PARAMS ;
2011-03-22 15:12:22 +03:00
else
2012-12-13 18:11:21 +04:00
status = MGMT_STATUS_SUCCESS ;
2012-02-19 15:16:14 +04:00
2014-11-15 10:34:23 +03:00
done :
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_REMOVE_REMOTE_OOB_DATA ,
status , & cp - > addr , sizeof ( cp - > addr ) ) ;
2011-03-22 15:12:22 +03:00
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-03-22 15:12:22 +03:00
return err ;
}
2015-11-11 09:30:30 +03:00
void mgmt_start_discovery_complete ( struct hci_dev * hdev , u8 status )
2013-04-30 22:29:28 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-03-27 23:55:19 +04:00
2013-04-30 22:29:28 +04:00
BT_DBG ( " status %d " , status ) ;
2014-12-04 13:36:35 +03:00
hci_dev_lock ( hdev ) ;
2013-04-30 22:29:30 +04:00
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_START_DISCOVERY , hdev ) ;
2014-12-05 12:55:59 +03:00
if ( ! cmd )
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_START_SERVICE_DISCOVERY , hdev ) ;
2014-12-05 12:55:59 +03:00
2016-01-05 14:19:32 +03:00
if ( ! cmd )
cmd = pending_find ( MGMT_OP_START_LIMITED_DISCOVERY , hdev ) ;
2014-12-04 13:36:35 +03:00
if ( cmd ) {
2014-12-05 14:36:06 +03:00
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
2014-12-04 13:36:35 +03:00
mgmt_pending_remove ( cmd ) ;
}
2013-04-30 22:29:28 +04:00
2014-12-04 13:36:35 +03:00
hci_dev_unlock ( hdev ) ;
2013-04-30 22:29:28 +04:00
}
2015-11-11 09:11:24 +03:00
static bool discovery_type_is_valid ( struct hci_dev * hdev , uint8_t type ,
uint8_t * mgmt_status )
{
switch ( type ) {
case DISCOV_TYPE_LE :
* mgmt_status = mgmt_le_support ( hdev ) ;
if ( * mgmt_status )
return false ;
break ;
case DISCOV_TYPE_INTERLEAVED :
* mgmt_status = mgmt_le_support ( hdev ) ;
if ( * mgmt_status )
return false ;
/* Intentional fall-through */
case DISCOV_TYPE_BREDR :
* mgmt_status = mgmt_bredr_support ( hdev ) ;
if ( * mgmt_status )
return false ;
break ;
default :
* mgmt_status = MGMT_STATUS_INVALID_PARAMS ;
return false ;
}
return true ;
}
2016-01-05 14:19:32 +03:00
static int start_discovery_internal ( struct sock * sk , struct hci_dev * hdev ,
u16 op , void * data , u16 len )
2011-04-27 18:29:56 +04:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_start_discovery * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-12-04 13:36:36 +03:00
u8 status ;
2011-04-27 18:29:56 +04:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-04-27 18:29:56 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-04-27 18:29:56 +04:00
2012-02-21 16:13:02 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2016-01-05 14:19:32 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , op ,
2015-03-06 22:08:54 +03:00
MGMT_STATUS_NOT_POWERED ,
& cp - > type , sizeof ( cp - > type ) ) ;
2011-11-08 01:13:37 +04:00
goto failed ;
}
2014-12-04 13:36:34 +03:00
if ( hdev - > discovery . state ! = DISCOVERY_STOPPED | |
2015-03-13 12:11:00 +03:00
hci_dev_test_flag ( hdev , HCI_PERIODIC_INQ ) ) {
2016-01-05 14:19:32 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , op , MGMT_STATUS_BUSY ,
& cp - > type , sizeof ( cp - > type ) ) ;
2012-03-21 07:03:37 +04:00
goto failed ;
}
2015-11-11 09:11:24 +03:00
if ( ! discovery_type_is_valid ( hdev , cp - > type , & status ) ) {
2016-01-05 14:19:32 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , op , status ,
& cp - > type , sizeof ( cp - > type ) ) ;
2015-11-11 09:11:24 +03:00
goto failed ;
}
2014-12-05 13:45:22 +03:00
/* Clear the discovery filter first to free any previously
* allocated memory for the UUID list .
*/
hci_discovery_filter_clear ( hdev ) ;
2012-02-18 03:39:36 +04:00
hdev - > discovery . type = cp - > type ;
2014-12-05 15:03:35 +03:00
hdev - > discovery . report_invalid_rssi = false ;
2016-01-05 14:19:32 +03:00
if ( op = = MGMT_OP_START_LIMITED_DISCOVERY )
hdev - > discovery . limited = true ;
else
hdev - > discovery . limited = false ;
2012-02-18 03:39:36 +04:00
2016-01-05 14:19:32 +03:00
cmd = mgmt_pending_add ( sk , op , hdev , data , len ) ;
2015-11-11 09:30:30 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
2013-01-10 16:54:09 +04:00
goto failed ;
2012-02-18 03:39:35 +04:00
}
2013-01-10 16:54:09 +04:00
2015-11-11 09:30:30 +03:00
cmd - > cmd_complete = generic_cmd_complete ;
2013-04-30 22:29:28 +04:00
2014-12-04 13:36:34 +03:00
hci_discovery_set_state ( hdev , DISCOVERY_STARTING ) ;
2015-11-11 09:30:30 +03:00
queue_work ( hdev - > req_workqueue , & hdev - > discov_update ) ;
err = 0 ;
2013-04-30 22:29:28 +04:00
2011-04-27 18:29:56 +04:00
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-04-27 18:29:56 +04:00
return err ;
}
2012-02-18 03:39:35 +04:00
2016-01-05 14:19:32 +03:00
static int start_discovery ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
return start_discovery_internal ( sk , hdev , MGMT_OP_START_DISCOVERY ,
data , len ) ;
}
static int start_limited_discovery ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
return start_discovery_internal ( sk , hdev ,
MGMT_OP_START_LIMITED_DISCOVERY ,
data , len ) ;
}
2015-03-06 22:08:55 +03:00
static int service_discovery_cmd_complete ( struct mgmt_pending_cmd * cmd ,
u8 status )
2014-12-05 14:36:06 +03:00
{
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode , status ,
cmd - > param , 1 ) ;
2014-12-05 14:36:06 +03:00
}
2013-01-10 16:54:09 +04:00
2014-12-05 12:55:59 +03:00
static int start_service_discovery ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_cp_start_service_discovery * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-12-05 12:55:59 +03:00
const u16 max_uuid_count = ( ( U16_MAX - sizeof ( * cp ) ) / 16 ) ;
u16 uuid_count , expected_len ;
u8 status ;
int err ;
2013-01-10 16:54:09 +04:00
2014-12-05 12:55:59 +03:00
BT_DBG ( " %s " , hdev - > name ) ;
2014-07-08 16:07:53 +04:00
2014-12-05 12:55:59 +03:00
hci_dev_lock ( hdev ) ;
2013-04-30 22:29:28 +04:00
2014-12-05 12:55:59 +03:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_START_SERVICE_DISCOVERY ,
MGMT_STATUS_NOT_POWERED ,
& cp - > type , sizeof ( cp - > type ) ) ;
2014-12-05 12:55:59 +03:00
goto failed ;
}
2013-04-30 22:29:28 +04:00
2014-12-05 12:55:59 +03:00
if ( hdev - > discovery . state ! = DISCOVERY_STOPPED | |
2015-03-13 12:11:00 +03:00
hci_dev_test_flag ( hdev , HCI_PERIODIC_INQ ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_START_SERVICE_DISCOVERY ,
MGMT_STATUS_BUSY , & cp - > type ,
sizeof ( cp - > type ) ) ;
2014-12-05 12:55:59 +03:00
goto failed ;
}
2014-02-23 21:42:24 +04:00
2014-12-05 12:55:59 +03:00
uuid_count = __le16_to_cpu ( cp - > uuid_count ) ;
if ( uuid_count > max_uuid_count ) {
BT_ERR ( " service_discovery: too big uuid_count value %u " ,
uuid_count ) ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_START_SERVICE_DISCOVERY ,
MGMT_STATUS_INVALID_PARAMS , & cp - > type ,
sizeof ( cp - > type ) ) ;
2014-12-05 12:55:59 +03:00
goto failed ;
}
expected_len = sizeof ( * cp ) + uuid_count * 16 ;
if ( expected_len ! = len ) {
BT_ERR ( " service_discovery: expected %u bytes, got %u bytes " ,
expected_len , len ) ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_START_SERVICE_DISCOVERY ,
MGMT_STATUS_INVALID_PARAMS , & cp - > type ,
sizeof ( cp - > type ) ) ;
2014-12-05 12:55:59 +03:00
goto failed ;
}
2015-11-11 09:11:24 +03:00
if ( ! discovery_type_is_valid ( hdev , cp - > type , & status ) ) {
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_START_SERVICE_DISCOVERY ,
status , & cp - > type , sizeof ( cp - > type ) ) ;
goto failed ;
}
2014-12-05 12:55:59 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_START_SERVICE_DISCOVERY ,
2014-12-05 14:36:06 +03:00
hdev , data , len ) ;
2014-12-05 12:55:59 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
goto failed ;
}
2014-12-05 14:36:06 +03:00
cmd - > cmd_complete = service_discovery_cmd_complete ;
2014-12-05 13:45:22 +03:00
/* Clear the discovery filter first to free any previously
* allocated memory for the UUID list .
*/
hci_discovery_filter_clear ( hdev ) ;
2015-03-05 03:24:26 +03:00
hdev - > discovery . result_filtering = true ;
2014-12-05 12:55:59 +03:00
hdev - > discovery . type = cp - > type ;
hdev - > discovery . rssi = cp - > rssi ;
hdev - > discovery . uuid_count = uuid_count ;
if ( uuid_count > 0 ) {
hdev - > discovery . uuids = kmemdup ( cp - > uuids , uuid_count * 16 ,
GFP_KERNEL ) ;
if ( ! hdev - > discovery . uuids ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_START_SERVICE_DISCOVERY ,
MGMT_STATUS_FAILED ,
& cp - > type , sizeof ( cp - > type ) ) ;
2014-02-23 21:42:24 +04:00
mgmt_pending_remove ( cmd ) ;
goto failed ;
}
2014-12-05 12:55:59 +03:00
}
2014-02-23 21:42:24 +04:00
2014-12-05 12:55:59 +03:00
hci_discovery_set_state ( hdev , DISCOVERY_STARTING ) ;
2015-11-11 09:30:30 +03:00
queue_work ( hdev - > req_workqueue , & hdev - > discov_update ) ;
err = 0 ;
2011-04-27 18:29:56 +04:00
failed :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-04-27 18:29:56 +04:00
return err ;
}
2015-11-11 09:30:45 +03:00
void mgmt_stop_discovery_complete ( struct hci_dev * hdev , u8 status )
2013-04-30 22:29:35 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-04-30 22:29:35 +04:00
2013-04-30 22:29:33 +04:00
BT_DBG ( " status %d " , status ) ;
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_STOP_DISCOVERY , hdev ) ;
2014-12-04 13:36:35 +03:00
if ( cmd ) {
2014-12-05 14:36:06 +03:00
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
2014-12-04 13:36:35 +03:00
mgmt_pending_remove ( cmd ) ;
2013-04-30 22:29:33 +04:00
}
hci_dev_unlock ( hdev ) ;
}
2012-02-28 08:13:32 +04:00
static int stop_discovery ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-04-27 18:29:56 +04:00
{
2012-02-21 01:25:18 +04:00
struct mgmt_cp_stop_discovery * mgmt_cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-04-27 18:29:56 +04:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-04-27 18:29:56 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-04-27 18:29:56 +04:00
2012-01-04 17:44:20 +04:00
if ( ! hci_discovery_active ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_STOP_DISCOVERY ,
MGMT_STATUS_REJECTED , & mgmt_cp - > type ,
sizeof ( mgmt_cp - > type ) ) ;
2012-02-21 01:25:18 +04:00
goto unlock ;
}
if ( hdev - > discovery . type ! = mgmt_cp - > type ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_STOP_DISCOVERY ,
MGMT_STATUS_INVALID_PARAMS ,
& mgmt_cp - > type , sizeof ( mgmt_cp - > type ) ) ;
2012-01-04 17:44:20 +04:00
goto unlock ;
2012-01-04 16:23:45 +04:00
}
2014-12-05 14:36:06 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_STOP_DISCOVERY , hdev , data , len ) ;
2011-04-27 18:29:56 +04:00
if ( ! cmd ) {
err = - ENOMEM ;
2012-01-04 17:44:20 +04:00
goto unlock ;
}
2014-12-05 14:36:06 +03:00
cmd - > cmd_complete = generic_cmd_complete ;
2015-11-11 09:30:45 +03:00
hci_discovery_set_state ( hdev , DISCOVERY_STOPPING ) ;
queue_work ( hdev - > req_workqueue , & hdev - > discov_update ) ;
err = 0 ;
2011-04-27 18:29:56 +04:00
2012-01-04 17:44:20 +04:00
unlock :
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-04-27 18:29:56 +04:00
return err ;
}
2012-02-28 08:13:32 +04:00
static int confirm_name ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2012-01-04 15:31:59 +04:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_confirm_name * cp = data ;
2012-01-04 15:31:59 +04:00
struct inquiry_entry * e ;
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2012-01-04 15:31:59 +04:00
hci_dev_lock ( hdev ) ;
2012-01-04 17:44:20 +04:00
if ( ! hci_discovery_active ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_CONFIRM_NAME ,
MGMT_STATUS_FAILED , & cp - > addr ,
sizeof ( cp - > addr ) ) ;
2012-01-04 17:44:20 +04:00
goto failed ;
}
2012-02-17 16:27:06 +04:00
e = hci_inquiry_cache_lookup_unknown ( hdev , & cp - > addr . bdaddr ) ;
2012-01-04 15:31:59 +04:00
if ( ! e ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_CONFIRM_NAME ,
MGMT_STATUS_INVALID_PARAMS , & cp - > addr ,
sizeof ( cp - > addr ) ) ;
2012-01-04 15:31:59 +04:00
goto failed ;
}
if ( cp - > name_known ) {
e - > name_state = NAME_KNOWN ;
list_del ( & e - > list ) ;
} else {
e - > name_state = NAME_NEEDED ;
2012-01-09 02:53:02 +04:00
hci_inquiry_cache_update_resolve ( hdev , e ) ;
2012-01-04 15:31:59 +04:00
}
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_CONFIRM_NAME , 0 ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2012-01-04 15:31:59 +04:00
failed :
hci_dev_unlock ( hdev ) ;
return err ;
}
2012-02-28 08:13:32 +04:00
static int block_device ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-06-15 13:01:15 +04:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_block_device * cp = data ;
2012-02-19 14:58:54 +04:00
u8 status ;
2011-06-15 13:01:15 +04:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-06-15 13:01:15 +04:00
2013-01-20 16:27:19 +04:00
if ( ! bdaddr_type_is_valid ( cp - > addr . type ) )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_BLOCK_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2013-01-20 16:27:19 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-08-25 17:48:02 +04:00
2014-07-09 13:59:13 +04:00
err = hci_bdaddr_list_add ( & hdev - > blacklist , & cp - > addr . bdaddr ,
cp - > addr . type ) ;
2014-07-01 23:09:47 +04:00
if ( err < 0 ) {
2012-02-19 14:58:54 +04:00
status = MGMT_STATUS_FAILED ;
2014-07-01 23:09:47 +04:00
goto done ;
}
mgmt_event ( MGMT_EV_DEVICE_BLOCKED , hdev , & cp - > addr , sizeof ( cp - > addr ) ,
sk ) ;
status = MGMT_STATUS_SUCCESS ;
2012-02-19 14:58:54 +04:00
2014-07-01 23:09:47 +04:00
done :
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_BLOCK_DEVICE , status ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2011-08-25 17:48:02 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-06-15 13:01:15 +04:00
return err ;
}
2012-02-28 08:13:32 +04:00
static int unblock_device ( struct sock * sk , struct hci_dev * hdev , void * data ,
2012-03-08 08:25:00 +04:00
u16 len )
2011-06-15 13:01:15 +04:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_unblock_device * cp = data ;
2012-02-19 14:58:54 +04:00
u8 status ;
2011-06-15 13:01:15 +04:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-06-15 13:01:15 +04:00
2013-01-20 16:27:19 +04:00
if ( ! bdaddr_type_is_valid ( cp - > addr . type ) )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_UNBLOCK_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2013-01-20 16:27:19 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-08-25 17:48:02 +04:00
2014-07-09 13:59:13 +04:00
err = hci_bdaddr_list_del ( & hdev - > blacklist , & cp - > addr . bdaddr ,
cp - > addr . type ) ;
2014-07-01 23:09:47 +04:00
if ( err < 0 ) {
2012-02-19 14:58:54 +04:00
status = MGMT_STATUS_INVALID_PARAMS ;
2014-07-01 23:09:47 +04:00
goto done ;
}
mgmt_event ( MGMT_EV_DEVICE_UNBLOCKED , hdev , & cp - > addr , sizeof ( cp - > addr ) ,
sk ) ;
status = MGMT_STATUS_SUCCESS ;
2012-02-19 14:58:54 +04:00
2014-07-01 23:09:47 +04:00
done :
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_UNBLOCK_DEVICE , status ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2011-08-25 17:48:02 +04:00
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2011-06-15 13:01:15 +04:00
return err ;
}
2012-03-12 07:00:29 +04:00
static int set_device_id ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
{
struct mgmt_cp_set_device_id * cp = data ;
2013-03-16 02:06:52 +04:00
struct hci_request req ;
2012-03-12 07:00:29 +04:00
int err ;
2012-03-16 19:02:57 +04:00
__u16 source ;
2012-03-12 07:00:29 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2012-03-16 19:02:57 +04:00
source = __le16_to_cpu ( cp - > source ) ;
if ( source > 0x0002 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DEVICE_ID ,
MGMT_STATUS_INVALID_PARAMS ) ;
2012-03-16 19:02:57 +04:00
2012-03-12 07:00:29 +04:00
hci_dev_lock ( hdev ) ;
2012-03-16 19:02:57 +04:00
hdev - > devid_source = source ;
2012-03-12 07:00:29 +04:00
hdev - > devid_vendor = __le16_to_cpu ( cp - > vendor ) ;
hdev - > devid_product = __le16_to_cpu ( cp - > product ) ;
hdev - > devid_version = __le16_to_cpu ( cp - > version ) ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_SET_DEVICE_ID , 0 ,
NULL , 0 ) ;
2012-03-12 07:00:29 +04:00
2013-03-16 02:06:52 +04:00
hci_req_init ( & req , hdev ) ;
2015-11-25 17:15:42 +03:00
__hci_req_update_eir ( & req ) ;
2013-03-16 02:06:52 +04:00
hci_req_run ( & req , NULL ) ;
2012-03-12 07:00:29 +04:00
hci_dev_unlock ( hdev ) ;
return err ;
}
2015-03-24 01:57:12 +03:00
static void enable_advertising_instance ( struct hci_dev * hdev , u8 status ,
u16 opcode )
{
BT_DBG ( " status %d " , status ) ;
}
2015-01-12 00:50:44 +03:00
static void set_advertising_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode )
2013-09-25 14:26:10 +04:00
{
struct cmd_lookup match = { NULL , hdev } ;
2015-03-24 01:57:12 +03:00
struct hci_request req ;
2015-06-18 04:16:45 +03:00
u8 instance ;
struct adv_info * adv_instance ;
int err ;
2013-09-25 14:26:10 +04:00
2014-12-11 09:13:12 +03:00
hci_dev_lock ( hdev ) ;
2013-09-25 14:26:10 +04:00
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
mgmt_pending_foreach ( MGMT_OP_SET_ADVERTISING , hdev ,
cmd_status_rsp , & mgmt_err ) ;
2014-12-11 09:13:12 +03:00
goto unlock ;
2013-09-25 14:26:10 +04:00
}
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LE_ADV ) )
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_ADVERTISING ) ;
2014-07-08 16:07:48 +04:00
else
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_ADVERTISING ) ;
2014-07-08 16:07:48 +04:00
2013-09-25 14:26:10 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_ADVERTISING , hdev , settings_rsp ,
& match ) ;
new_settings ( hdev , match . sk ) ;
if ( match . sk )
sock_put ( match . sk ) ;
2014-12-11 09:13:12 +03:00
2015-03-24 01:57:12 +03:00
/* If "Set Advertising" was just disabled and instance advertising was
2015-06-18 04:16:45 +03:00
* set up earlier , then re - enable multi - instance advertising .
2015-03-24 01:57:12 +03:00
*/
if ( hci_dev_test_flag ( hdev , HCI_ADVERTISING ) | |
2015-06-18 04:16:45 +03:00
list_empty ( & hdev - > adv_instances ) )
2015-03-24 01:57:12 +03:00
goto unlock ;
2015-06-18 04:16:45 +03:00
instance = hdev - > cur_adv_instance ;
if ( ! instance ) {
adv_instance = list_first_entry_or_null ( & hdev - > adv_instances ,
struct adv_info , list ) ;
if ( ! adv_instance )
goto unlock ;
instance = adv_instance - > instance ;
}
2015-03-24 01:57:12 +03:00
hci_req_init ( & req , hdev ) ;
2015-11-18 13:49:20 +03:00
err = __hci_req_schedule_adv_instance ( & req , instance , true ) ;
2015-06-18 04:16:45 +03:00
if ( ! err )
err = hci_req_run ( & req , enable_advertising_instance ) ;
2015-03-24 01:57:12 +03:00
2015-06-18 04:16:45 +03:00
if ( err )
2015-03-24 01:57:12 +03:00
BT_ERR ( " Failed to re-configure advertising " ) ;
2014-12-11 09:13:12 +03:00
unlock :
hci_dev_unlock ( hdev ) ;
2013-09-25 14:26:10 +04:00
}
2013-10-10 20:47:53 +04:00
static int set_advertising ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
2013-09-25 14:26:10 +04:00
{
struct mgmt_mode * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-09-25 14:26:10 +04:00
struct hci_request req ;
2015-03-13 08:30:58 +03:00
u8 val , status ;
2013-09-25 14:26:10 +04:00
int err ;
BT_DBG ( " request for %s " , hdev - > name ) ;
2013-10-02 16:45:22 +04:00
status = mgmt_le_support ( hdev ) ;
if ( status )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_ADVERTISING ,
status ) ;
2013-09-25 14:26:10 +04:00
2015-03-13 08:30:58 +03:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 & & cp - > val ! = 0x02 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_ADVERTISING ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-09-25 14:26:10 +04:00
hci_dev_lock ( hdev ) ;
val = ! ! cp - > val ;
2013-10-08 17:52:18 +04:00
/* The following conditions are ones which mean that we should
* not do any HCI communication but directly send a mgmt
* response to user space ( after toggling the flag if
* necessary ) .
*/
2015-03-13 08:30:58 +03:00
if ( ! hdev_is_powered ( hdev ) | |
2015-03-13 12:11:00 +03:00
( val = = hci_dev_test_flag ( hdev , HCI_ADVERTISING ) & &
( cp - > val = = 0x02 ) = = hci_dev_test_flag ( hdev , HCI_ADVERTISING_CONNECTABLE ) ) | |
2014-07-08 16:07:53 +04:00
hci_conn_num ( hdev , LE_LINK ) > 0 | |
2015-03-13 12:11:00 +03:00
( hci_dev_test_flag ( hdev , HCI_LE_SCAN ) & &
2014-07-08 16:07:53 +04:00
hdev - > le_scan_type = = LE_SCAN_ACTIVE ) ) {
2015-03-13 08:30:58 +03:00
bool changed ;
2013-09-25 14:26:10 +04:00
2015-03-13 08:30:58 +03:00
if ( cp - > val ) {
2015-11-30 12:21:45 +03:00
hdev - > cur_adv_instance = 0x00 ;
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_ADVERTISING ) ;
2015-03-13 08:30:58 +03:00
if ( cp - > val = = 0x02 )
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_ADVERTISING_CONNECTABLE ) ;
2015-03-13 08:30:58 +03:00
else
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_ADVERTISING_CONNECTABLE ) ;
2015-03-13 08:30:58 +03:00
} else {
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev , HCI_ADVERTISING ) ;
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_ADVERTISING_CONNECTABLE ) ;
2013-09-25 14:26:10 +04:00
}
err = send_settings_rsp ( sk , MGMT_OP_SET_ADVERTISING , hdev ) ;
if ( err < 0 )
goto unlock ;
if ( changed )
err = new_settings ( hdev , sk ) ;
goto unlock ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_ADVERTISING , hdev ) | |
pending_find ( MGMT_OP_SET_LE , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_ADVERTISING ,
MGMT_STATUS_BUSY ) ;
2013-09-25 14:26:10 +04:00
goto unlock ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_ADVERTISING , hdev , data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
hci_req_init ( & req , hdev ) ;
2015-03-13 08:30:58 +03:00
if ( cp - > val = = 0x02 )
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_ADVERTISING_CONNECTABLE ) ;
2015-03-13 08:30:58 +03:00
else
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_ADVERTISING_CONNECTABLE ) ;
2015-03-13 08:30:58 +03:00
2015-06-18 04:16:45 +03:00
cancel_adv_timeout ( hdev ) ;
2015-03-24 01:57:12 +03:00
if ( val ) {
2015-06-18 04:16:45 +03:00
/* Switch to instance "0" for the Set Advertising setting.
* We cannot use update_ [ adv | scan_rsp ] _data ( ) here as the
* HCI_ADVERTISING flag is not yet set .
*/
2015-11-30 12:21:45 +03:00
hdev - > cur_adv_instance = 0x00 ;
2015-11-18 13:49:20 +03:00
__hci_req_update_adv_data ( & req , 0x00 ) ;
__hci_req_update_scan_rsp_data ( & req , 0x00 ) ;
__hci_req_enable_advertising ( & req ) ;
2015-03-24 01:57:12 +03:00
} else {
2015-11-18 13:49:20 +03:00
__hci_req_disable_advertising ( & req ) ;
2015-03-24 01:57:12 +03:00
}
2013-09-25 14:26:10 +04:00
err = hci_req_run ( & req , set_advertising_complete ) ;
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2013-10-02 15:41:30 +04:00
static int set_static_address ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_cp_set_static_address * cp = data ;
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
2013-10-03 09:10:32 +04:00
if ( ! lmp_le_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_STATIC_ADDRESS ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2013-10-02 15:41:30 +04:00
if ( hdev_is_powered ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_STATIC_ADDRESS ,
MGMT_STATUS_REJECTED ) ;
2013-10-02 15:41:30 +04:00
if ( bacmp ( & cp - > bdaddr , BDADDR_ANY ) ) {
if ( ! bacmp ( & cp - > bdaddr , BDADDR_NONE ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id ,
MGMT_OP_SET_STATIC_ADDRESS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-10-02 15:41:30 +04:00
/* Two most significant bits shall be set */
if ( ( cp - > bdaddr . b [ 5 ] & 0xc0 ) ! = 0xc0 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id ,
MGMT_OP_SET_STATIC_ADDRESS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-10-02 15:41:30 +04:00
}
hci_dev_lock ( hdev ) ;
bacpy ( & hdev - > static_addr , & cp - > bdaddr ) ;
2015-03-06 21:11:21 +03:00
err = send_settings_rsp ( sk , MGMT_OP_SET_STATIC_ADDRESS , hdev ) ;
if ( err < 0 )
goto unlock ;
err = new_settings ( hdev , sk ) ;
2013-10-02 15:41:30 +04:00
2015-03-06 21:11:21 +03:00
unlock :
2013-10-02 15:41:30 +04:00
hci_dev_unlock ( hdev ) ;
return err ;
}
2013-10-11 19:23:20 +04:00
static int set_scan_params ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_cp_set_scan_params * cp = data ;
__u16 interval , window ;
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
if ( ! lmp_le_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SCAN_PARAMS ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2013-10-11 19:23:20 +04:00
interval = __le16_to_cpu ( cp - > interval ) ;
if ( interval < 0x0004 | | interval > 0x4000 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SCAN_PARAMS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-10-11 19:23:20 +04:00
window = __le16_to_cpu ( cp - > window ) ;
if ( window < 0x0004 | | window > 0x4000 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SCAN_PARAMS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-10-11 19:23:20 +04:00
2013-10-14 20:55:32 +04:00
if ( window > interval )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SCAN_PARAMS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-10-14 20:55:32 +04:00
2013-10-11 19:23:20 +04:00
hci_dev_lock ( hdev ) ;
hdev - > le_scan_interval = interval ;
hdev - > le_scan_window = window ;
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_SET_SCAN_PARAMS , 0 ,
NULL , 0 ) ;
2013-10-11 19:23:20 +04:00
2014-02-27 03:21:56 +04:00
/* If background scan is running, restart it so new parameters are
* loaded .
*/
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_LE_SCAN ) & &
2014-02-27 03:21:56 +04:00
hdev - > discovery . state = = DISCOVERY_STOPPED ) {
struct hci_request req ;
hci_req_init ( & req , hdev ) ;
hci_req_add_le_scan_disable ( & req ) ;
hci_req_add_le_passive_scan ( & req ) ;
hci_req_run ( & req , NULL ) ;
}
2013-10-11 19:23:20 +04:00
hci_dev_unlock ( hdev ) ;
return err ;
}
2015-01-12 00:50:44 +03:00
static void fast_connectable_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode )
2013-03-16 02:07:05 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:07:05 +04:00
BT_DBG ( " status 0x%02x " , status ) ;
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_FAST_CONNECTABLE , hdev ) ;
2013-03-16 02:07:05 +04:00
if ( ! cmd )
goto unlock ;
if ( status ) {
2015-03-06 22:08:53 +03:00
mgmt_cmd_status ( cmd - > sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
mgmt_status ( status ) ) ;
2013-03-16 02:07:05 +04:00
} else {
2013-03-16 02:07:08 +04:00
struct mgmt_mode * cp = cmd - > param ;
if ( cp - > val )
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_FAST_CONNECTABLE ) ;
2013-03-16 02:07:08 +04:00
else
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_FAST_CONNECTABLE ) ;
2013-03-16 02:07:08 +04:00
2013-03-16 02:07:05 +04:00
send_settings_rsp ( cmd - > sk , MGMT_OP_SET_FAST_CONNECTABLE , hdev ) ;
new_settings ( hdev , cmd - > sk ) ;
}
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
2012-02-28 08:13:32 +04:00
static int set_fast_connectable ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void * data , u16 len )
2011-06-22 14:11:56 +04:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_mode * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-03-16 02:07:05 +04:00
struct hci_request req ;
2011-06-22 14:11:56 +04:00
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-06-22 14:11:56 +04:00
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) | |
2013-10-02 14:43:13 +04:00
hdev - > hci_ver < BLUETOOTH_VER_1_2 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2012-10-24 22:11:58 +04:00
2013-01-09 18:05:19 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-09 18:05:19 +04:00
2011-06-22 14:11:56 +04:00
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_FAST_CONNECTABLE , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
MGMT_STATUS_BUSY ) ;
2013-03-16 02:07:07 +04:00
goto unlock ;
}
2015-03-13 12:11:00 +03:00
if ( ! ! cp - > val = = hci_dev_test_flag ( hdev , HCI_FAST_CONNECTABLE ) ) {
2013-03-16 02:07:08 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_FAST_CONNECTABLE ,
hdev ) ;
goto unlock ;
}
2015-03-10 21:14:27 +03:00
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-13 12:11:03 +03:00
hci_dev_change_flag ( hdev , HCI_FAST_CONNECTABLE ) ;
2015-03-10 21:14:27 +03:00
err = send_settings_rsp ( sk , MGMT_OP_SET_FAST_CONNECTABLE ,
hdev ) ;
new_settings ( hdev , sk ) ;
goto unlock ;
}
2013-03-16 02:07:05 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_FAST_CONNECTABLE , hdev ,
data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
2011-06-22 14:11:56 +04:00
}
2013-03-16 02:07:05 +04:00
hci_req_init ( & req , hdev ) ;
2015-11-25 17:15:43 +03:00
__hci_req_write_fast_connectable ( & req , cp - > val ) ;
2013-03-16 02:07:05 +04:00
err = hci_req_run ( & req , fast_connectable_complete ) ;
2011-06-22 14:11:56 +04:00
if ( err < 0 ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
MGMT_STATUS_FAILED ) ;
2013-03-16 02:07:05 +04:00
mgmt_pending_remove ( cmd ) ;
2011-06-22 14:11:56 +04:00
}
2013-03-16 02:07:05 +04:00
unlock :
2011-06-22 14:11:56 +04:00
hci_dev_unlock ( hdev ) ;
2013-03-16 02:07:05 +04:00
2011-06-22 14:11:56 +04:00
return err ;
}
2015-01-12 00:50:44 +03:00
static void set_bredr_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
2013-10-02 14:43:14 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-10-02 14:43:14 +04:00
BT_DBG ( " status 0x%02x " , status ) ;
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_BREDR , hdev ) ;
2013-10-02 14:43:14 +04:00
if ( ! cmd )
goto unlock ;
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
/* We need to restore the flag if related HCI commands
* failed .
*/
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_BREDR_ENABLED ) ;
2013-10-02 14:43:14 +04:00
2015-03-06 22:08:53 +03:00
mgmt_cmd_status ( cmd - > sk , cmd - > index , cmd - > opcode , mgmt_err ) ;
2013-10-02 14:43:14 +04:00
} else {
send_settings_rsp ( cmd - > sk , MGMT_OP_SET_BREDR , hdev ) ;
new_settings ( hdev , cmd - > sk ) ;
}
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
static int set_bredr ( struct sock * sk , struct hci_dev * hdev , void * data , u16 len )
{
struct mgmt_mode * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-10-02 14:43:14 +04:00
struct hci_request req ;
int err ;
BT_DBG ( " request for %s " , hdev - > name ) ;
if ( ! lmp_bredr_capable ( hdev ) | | ! lmp_le_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_BREDR ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2013-10-02 14:43:14 +04:00
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_BREDR ,
MGMT_STATUS_REJECTED ) ;
2013-10-02 14:43:14 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_BREDR ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-10-02 14:43:14 +04:00
hci_dev_lock ( hdev ) ;
2015-03-13 12:11:00 +03:00
if ( cp - > val = = hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) ) {
2013-10-02 14:43:14 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_BREDR , hdev ) ;
goto unlock ;
}
if ( ! hdev_is_powered ( hdev ) ) {
if ( ! cp - > val ) {
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_DISCOVERABLE ) ;
hci_dev_clear_flag ( hdev , HCI_SSP_ENABLED ) ;
hci_dev_clear_flag ( hdev , HCI_LINK_SECURITY ) ;
hci_dev_clear_flag ( hdev , HCI_FAST_CONNECTABLE ) ;
hci_dev_clear_flag ( hdev , HCI_HS_ENABLED ) ;
2013-10-02 14:43:14 +04:00
}
2015-03-13 12:11:03 +03:00
hci_dev_change_flag ( hdev , HCI_BREDR_ENABLED ) ;
2013-10-02 14:43:14 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_BREDR , hdev ) ;
if ( err < 0 )
goto unlock ;
err = new_settings ( hdev , sk ) ;
goto unlock ;
}
/* Reject disabling when powered on */
if ( ! cp - > val ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_BREDR ,
MGMT_STATUS_REJECTED ) ;
2013-10-02 14:43:14 +04:00
goto unlock ;
2015-01-15 01:40:42 +03:00
} else {
/* When configuring a dual-mode controller to operate
* with LE only and using a static address , then switching
* BR / EDR back on is not allowed .
*
* Dual - mode controllers shall operate with the public
* address as its identity address for BR / EDR and LE . So
* reject the attempt to create an invalid configuration .
2015-01-22 22:15:21 +03:00
*
* The same restrictions applies when secure connections
* has been enabled . For BR / EDR this is a controller feature
* while for LE it is a host stack feature . This means that
* switching BR / EDR back on when secure connections has been
* enabled is not a supported transaction .
2015-01-15 01:40:42 +03:00
*/
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) & &
2015-01-22 22:15:21 +03:00
( bacmp ( & hdev - > static_addr , BDADDR_ANY ) | |
2015-03-13 12:11:00 +03:00
hci_dev_test_flag ( hdev , HCI_SC_ENABLED ) ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_BREDR ,
MGMT_STATUS_REJECTED ) ;
2015-01-15 01:40:42 +03:00
goto unlock ;
}
2013-10-02 14:43:14 +04:00
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_BREDR , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_BREDR ,
MGMT_STATUS_BUSY ) ;
2013-10-02 14:43:14 +04:00
goto unlock ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_BREDR , hdev , data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
2015-11-18 13:49:20 +03:00
/* We need to flip the bit already here so that
* hci_req_update_adv_data generates the correct flags .
2013-10-02 14:43:14 +04:00
*/
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_BREDR_ENABLED ) ;
2013-10-02 14:43:14 +04:00
hci_req_init ( & req , hdev ) ;
2013-10-14 22:15:26 +04:00
2015-11-25 17:15:43 +03:00
__hci_req_write_fast_connectable ( & req , false ) ;
2015-11-16 13:52:21 +03:00
__hci_req_update_scan ( & req ) ;
2013-10-14 22:15:26 +04:00
2013-10-16 11:16:48 +04:00
/* Since only the advertising data flags will change, there
* is no need to update the scan response data .
*/
2015-11-30 12:21:45 +03:00
__hci_req_update_adv_data ( & req , hdev - > cur_adv_instance ) ;
2013-10-14 22:15:26 +04:00
2013-10-02 14:43:14 +04:00
err = hci_req_run ( & req , set_bredr_complete ) ;
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2015-01-23 16:42:46 +03:00
static void sc_enable_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2015-01-23 16:42:46 +03:00
struct mgmt_mode * cp ;
BT_DBG ( " %s status %u " , hdev - > name , status ) ;
hci_dev_lock ( hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_SECURE_CONN , hdev ) ;
2015-01-23 16:42:46 +03:00
if ( ! cmd )
goto unlock ;
if ( status ) {
2015-03-06 22:08:53 +03:00
mgmt_cmd_status ( cmd - > sk , cmd - > index , cmd - > opcode ,
mgmt_status ( status ) ) ;
2015-01-23 16:42:46 +03:00
goto remove ;
}
cp = cmd - > param ;
switch ( cp - > val ) {
case 0x00 :
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_SC_ENABLED ) ;
hci_dev_clear_flag ( hdev , HCI_SC_ONLY ) ;
2015-01-23 16:42:46 +03:00
break ;
case 0x01 :
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_SC_ENABLED ) ;
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_SC_ONLY ) ;
2015-01-23 16:42:46 +03:00
break ;
case 0x02 :
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_SC_ENABLED ) ;
hci_dev_set_flag ( hdev , HCI_SC_ONLY ) ;
2015-01-23 16:42:46 +03:00
break ;
}
send_settings_rsp ( cmd - > sk , MGMT_OP_SET_SECURE_CONN , hdev ) ;
new_settings ( hdev , cmd - > sk ) ;
remove :
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
2014-01-10 14:07:23 +04:00
static int set_secure_conn ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_mode * cp = data ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2015-01-23 16:42:46 +03:00
struct hci_request req ;
2014-05-26 12:23:35 +04:00
u8 val ;
2014-01-10 14:07:23 +04:00
int err ;
BT_DBG ( " request for %s " , hdev - > name ) ;
2015-01-01 01:43:18 +03:00
if ( ! lmp_sc_capable ( hdev ) & &
2015-03-13 12:11:00 +03:00
! hci_dev_test_flag ( hdev , HCI_LE_ENABLED ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SECURE_CONN ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2014-01-10 14:07:23 +04:00
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) & &
2015-01-28 20:56:00 +03:00
lmp_sc_capable ( hdev ) & &
2015-03-13 12:11:00 +03:00
! hci_dev_test_flag ( hdev , HCI_SSP_ENABLED ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SECURE_CONN ,
MGMT_STATUS_REJECTED ) ;
2015-01-22 22:15:22 +03:00
2014-02-01 21:19:57 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 & & cp - > val ! = 0x02 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SECURE_CONN ,
2014-01-10 14:07:23 +04:00
MGMT_STATUS_INVALID_PARAMS ) ;
hci_dev_lock ( hdev ) ;
2015-01-01 01:43:18 +03:00
if ( ! hdev_is_powered ( hdev ) | | ! lmp_sc_capable ( hdev ) | |
2015-03-13 12:11:00 +03:00
! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) ) {
2014-01-10 14:07:23 +04:00
bool changed ;
2014-02-01 21:19:57 +04:00
if ( cp - > val ) {
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev ,
HCI_SC_ENABLED ) ;
2014-02-01 21:19:57 +04:00
if ( cp - > val = = 0x02 )
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_SC_ONLY ) ;
2014-02-01 21:19:57 +04:00
else
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_SC_ONLY ) ;
2014-02-01 21:19:57 +04:00
} else {
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev ,
HCI_SC_ENABLED ) ;
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_SC_ONLY ) ;
2014-02-01 21:19:57 +04:00
}
2014-01-10 14:07:23 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_SECURE_CONN , hdev ) ;
if ( err < 0 )
goto failed ;
if ( changed )
err = new_settings ( hdev , sk ) ;
goto failed ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_SECURE_CONN , hdev ) ) {
2015-03-06 22:08:53 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_SECURE_CONN ,
MGMT_STATUS_BUSY ) ;
2014-01-10 14:07:23 +04:00
goto failed ;
}
2014-02-01 21:19:57 +04:00
val = ! ! cp - > val ;
2015-03-13 12:11:00 +03:00
if ( val = = hci_dev_test_flag ( hdev , HCI_SC_ENABLED ) & &
( cp - > val = = 0x02 ) = = hci_dev_test_flag ( hdev , HCI_SC_ONLY ) ) {
2014-01-10 14:07:23 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_SECURE_CONN , hdev ) ;
goto failed ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_SECURE_CONN , hdev , data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto failed ;
}
2015-01-23 16:42:46 +03:00
hci_req_init ( & req , hdev ) ;
hci_req_add ( & req , HCI_OP_WRITE_SC_SUPPORT , 1 , & val ) ;
err = hci_req_run ( & req , sc_enable_complete ) ;
2014-01-10 14:07:23 +04:00
if ( err < 0 ) {
mgmt_pending_remove ( cmd ) ;
goto failed ;
}
failed :
hci_dev_unlock ( hdev ) ;
return err ;
}
2014-01-31 23:55:22 +04:00
static int set_debug_keys ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_mode * cp = data ;
2014-06-24 15:00:28 +04:00
bool changed , use_changed ;
2014-01-31 23:55:22 +04:00
int err ;
BT_DBG ( " request for %s " , hdev - > name ) ;
2014-06-24 15:00:28 +04:00
if ( cp - > val ! = 0x00 & & cp - > val ! = 0x01 & & cp - > val ! = 0x02 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_DEBUG_KEYS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-01-31 23:55:22 +04:00
hci_dev_lock ( hdev ) ;
if ( cp - > val )
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_KEEP_DEBUG_KEYS ) ;
2014-01-31 23:55:22 +04:00
else
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev ,
HCI_KEEP_DEBUG_KEYS ) ;
2014-01-31 23:55:22 +04:00
2014-06-24 15:00:28 +04:00
if ( cp - > val = = 0x02 )
2015-03-13 12:11:06 +03:00
use_changed = ! hci_dev_test_and_set_flag ( hdev ,
HCI_USE_DEBUG_KEYS ) ;
2014-06-24 15:00:28 +04:00
else
2015-03-13 12:11:05 +03:00
use_changed = hci_dev_test_and_clear_flag ( hdev ,
HCI_USE_DEBUG_KEYS ) ;
2014-06-24 15:00:28 +04:00
if ( hdev_is_powered ( hdev ) & & use_changed & &
2015-03-13 12:11:00 +03:00
hci_dev_test_flag ( hdev , HCI_SSP_ENABLED ) ) {
2014-06-24 15:00:28 +04:00
u8 mode = ( cp - > val = = 0x02 ) ? 0x01 : 0x00 ;
hci_send_cmd ( hdev , HCI_OP_WRITE_SSP_DEBUG_MODE ,
sizeof ( mode ) , & mode ) ;
}
2014-01-31 23:55:22 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_DEBUG_KEYS , hdev ) ;
if ( err < 0 )
goto unlock ;
if ( changed )
err = new_settings ( hdev , sk ) ;
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2014-02-23 21:42:27 +04:00
static int set_privacy ( struct sock * sk , struct hci_dev * hdev , void * cp_data ,
u16 len )
{
struct mgmt_cp_set_privacy * cp = cp_data ;
bool changed ;
int err ;
BT_DBG ( " request for %s " , hdev - > name ) ;
if ( ! lmp_le_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_PRIVACY ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2014-02-23 21:42:27 +04:00
2016-03-09 18:30:34 +03:00
if ( cp - > privacy ! = 0x00 & & cp - > privacy ! = 0x01 & & cp - > privacy ! = 0x02 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_PRIVACY ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-02-23 21:42:27 +04:00
if ( hdev_is_powered ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_PRIVACY ,
MGMT_STATUS_REJECTED ) ;
2014-02-23 21:42:27 +04:00
hci_dev_lock ( hdev ) ;
2014-02-24 13:10:30 +04:00
/* If user space supports this command it is also expected to
* handle IRKs . Therefore , set the HCI_RPA_RESOLVING flag .
*/
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_RPA_RESOLVING ) ;
2014-02-24 13:10:30 +04:00
2014-02-23 21:42:27 +04:00
if ( cp - > privacy ) {
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_PRIVACY ) ;
2014-02-23 21:42:27 +04:00
memcpy ( hdev - > irk , cp - > irk , sizeof ( hdev - > irk ) ) ;
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_RPA_EXPIRED ) ;
2016-03-09 18:30:34 +03:00
if ( cp - > privacy = = 0x02 )
hci_dev_set_flag ( hdev , HCI_LIMITED_PRIVACY ) ;
else
hci_dev_clear_flag ( hdev , HCI_LIMITED_PRIVACY ) ;
2014-02-23 21:42:27 +04:00
} else {
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev , HCI_PRIVACY ) ;
2014-02-23 21:42:27 +04:00
memset ( hdev - > irk , 0 , sizeof ( hdev - > irk ) ) ;
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_RPA_EXPIRED ) ;
2016-03-09 18:30:34 +03:00
hci_dev_clear_flag ( hdev , HCI_LIMITED_PRIVACY ) ;
2014-02-23 21:42:27 +04:00
}
err = send_settings_rsp ( sk , MGMT_OP_SET_PRIVACY , hdev ) ;
if ( err < 0 )
goto unlock ;
if ( changed )
err = new_settings ( hdev , sk ) ;
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2014-02-18 12:19:35 +04:00
static bool irk_is_valid ( struct mgmt_irk_info * irk )
{
switch ( irk - > addr . type ) {
case BDADDR_LE_PUBLIC :
return true ;
case BDADDR_LE_RANDOM :
/* Two most significant bits shall be set */
if ( ( irk - > addr . bdaddr . b [ 5 ] & 0xc0 ) ! = 0xc0 )
return false ;
return true ;
}
return false ;
}
static int load_irks ( struct sock * sk , struct hci_dev * hdev , void * cp_data ,
u16 len )
{
struct mgmt_cp_load_irks * cp = cp_data ;
2014-07-03 14:52:27 +04:00
const u16 max_irk_count = ( ( U16_MAX - sizeof ( * cp ) ) /
sizeof ( struct mgmt_irk_info ) ) ;
2014-02-18 12:19:35 +04:00
u16 irk_count , expected_len ;
int i , err ;
BT_DBG ( " request for %s " , hdev - > name ) ;
if ( ! lmp_le_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_IRKS ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2014-02-18 12:19:35 +04:00
irk_count = __le16_to_cpu ( cp - > irk_count ) ;
2014-07-03 14:52:27 +04:00
if ( irk_count > max_irk_count ) {
BT_ERR ( " load_irks: too big irk_count value %u " , irk_count ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_IRKS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-07-03 14:52:27 +04:00
}
2014-02-18 12:19:35 +04:00
expected_len = sizeof ( * cp ) + irk_count * sizeof ( struct mgmt_irk_info ) ;
if ( expected_len ! = len ) {
BT_ERR ( " load_irks: expected %u bytes, got %u bytes " ,
2014-03-07 17:04:13 +04:00
expected_len , len ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_IRKS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-02-18 12:19:35 +04:00
}
BT_DBG ( " %s irk_count %u " , hdev - > name , irk_count ) ;
for ( i = 0 ; i < irk_count ; i + + ) {
struct mgmt_irk_info * key = & cp - > irks [ i ] ;
if ( ! irk_is_valid ( key ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id ,
MGMT_OP_LOAD_IRKS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-02-18 12:19:35 +04:00
}
hci_dev_lock ( hdev ) ;
hci_smp_irks_clear ( hdev ) ;
for ( i = 0 ; i < irk_count ; i + + ) {
struct mgmt_irk_info * irk = & cp - > irks [ i ] ;
2015-10-21 18:02:59 +03:00
hci_add_irk ( hdev , & irk - > addr . bdaddr ,
le_addr_type ( irk - > addr . type ) , irk - > val ,
2014-02-18 12:19:35 +04:00
BDADDR_ANY ) ;
}
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_RPA_RESOLVING ) ;
2014-02-18 12:19:35 +04:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_LOAD_IRKS , 0 , NULL , 0 ) ;
2014-02-18 12:19:35 +04:00
hci_dev_unlock ( hdev ) ;
return err ;
}
2013-01-20 16:27:16 +04:00
static bool ltk_is_valid ( struct mgmt_ltk_info * key )
{
if ( key - > master ! = 0x00 & & key - > master ! = 0x01 )
return false ;
2014-02-17 00:59:05 +04:00
switch ( key - > addr . type ) {
case BDADDR_LE_PUBLIC :
return true ;
case BDADDR_LE_RANDOM :
/* Two most significant bits shall be set */
if ( ( key - > addr . bdaddr . b [ 5 ] & 0xc0 ) ! = 0xc0 )
return false ;
return true ;
}
return false ;
2013-01-20 16:27:16 +04:00
}
2012-02-28 08:13:32 +04:00
static int load_long_term_keys ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void * cp_data , u16 len )
2012-02-03 04:08:02 +04:00
{
struct mgmt_cp_load_long_term_keys * cp = cp_data ;
2014-07-03 14:52:27 +04:00
const u16 max_key_count = ( ( U16_MAX - sizeof ( * cp ) ) /
sizeof ( struct mgmt_ltk_info ) ) ;
2012-02-03 04:08:02 +04:00
u16 key_count , expected_len ;
2013-01-09 17:29:34 +04:00
int i , err ;
2012-02-03 04:08:02 +04:00
2013-10-03 08:16:08 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
if ( ! lmp_le_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LONG_TERM_KEYS ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2013-10-03 08:16:08 +04:00
2012-03-13 07:31:08 +04:00
key_count = __le16_to_cpu ( cp - > key_count ) ;
2014-07-03 14:52:27 +04:00
if ( key_count > max_key_count ) {
BT_ERR ( " load_ltks: too big key_count value %u " , key_count ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LONG_TERM_KEYS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-07-03 14:52:27 +04:00
}
2012-02-03 04:08:02 +04:00
expected_len = sizeof ( * cp ) + key_count *
sizeof ( struct mgmt_ltk_info ) ;
if ( expected_len ! = len ) {
BT_ERR ( " load_keys: expected %u bytes, got %u bytes " ,
2014-03-07 17:04:13 +04:00
expected_len , len ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LONG_TERM_KEYS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2012-02-03 04:08:02 +04:00
}
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s key_count %u " , hdev - > name , key_count ) ;
2012-02-03 04:08:02 +04:00
2013-01-20 16:27:15 +04:00
for ( i = 0 ; i < key_count ; i + + ) {
struct mgmt_ltk_info * key = & cp - > keys [ i ] ;
2013-01-20 16:27:16 +04:00
if ( ! ltk_is_valid ( key ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id ,
MGMT_OP_LOAD_LONG_TERM_KEYS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2013-01-20 16:27:15 +04:00
}
2012-02-03 04:08:02 +04:00
hci_dev_lock ( hdev ) ;
hci_smp_ltks_clear ( hdev ) ;
for ( i = 0 ; i < key_count ; i + + ) {
struct mgmt_ltk_info * key = & cp - > keys [ i ] ;
2015-10-21 18:02:59 +03:00
u8 type , authenticated ;
2012-02-03 04:08:02 +04:00
2014-05-29 20:36:53 +04:00
switch ( key - > type ) {
case MGMT_LTK_UNAUTHENTICATED :
2014-05-23 14:19:53 +04:00
authenticated = 0x00 ;
2014-05-23 14:15:37 +04:00
type = key - > master ? SMP_LTK : SMP_LTK_SLAVE ;
2014-05-29 20:36:53 +04:00
break ;
case MGMT_LTK_AUTHENTICATED :
2014-05-23 14:19:53 +04:00
authenticated = 0x01 ;
2014-05-23 14:15:37 +04:00
type = key - > master ? SMP_LTK : SMP_LTK_SLAVE ;
break ;
case MGMT_LTK_P256_UNAUTH :
authenticated = 0x00 ;
type = SMP_LTK_P256 ;
2014-05-29 20:36:53 +04:00
break ;
2014-05-23 14:15:37 +04:00
case MGMT_LTK_P256_AUTH :
authenticated = 0x01 ;
type = SMP_LTK_P256 ;
2014-05-29 20:36:53 +04:00
break ;
2014-05-23 14:15:37 +04:00
case MGMT_LTK_P256_DEBUG :
authenticated = 0x00 ;
type = SMP_LTK_P256_DEBUG ;
2014-05-29 20:36:53 +04:00
default :
continue ;
}
2014-05-23 14:19:53 +04:00
2015-10-21 18:02:59 +03:00
hci_add_ltk ( hdev , & key - > addr . bdaddr ,
le_addr_type ( key - > addr . type ) , type , authenticated ,
key - > val , key - > enc_size , key - > ediv , key - > rand ) ;
2012-02-03 04:08:02 +04:00
}
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_LOAD_LONG_TERM_KEYS , 0 ,
2013-01-09 17:29:34 +04:00
NULL , 0 ) ;
2012-02-03 04:08:02 +04:00
hci_dev_unlock ( hdev ) ;
2013-01-09 17:29:34 +04:00
return err ;
2012-02-03 04:08:02 +04:00
}
2015-03-06 22:08:55 +03:00
static int conn_info_cmd_complete ( struct mgmt_pending_cmd * cmd , u8 status )
2014-05-14 15:43:03 +04:00
{
struct hci_conn * conn = cmd - > user_data ;
2014-12-05 14:42:57 +03:00
struct mgmt_rp_get_conn_info rp ;
2014-12-19 23:26:03 +03:00
int err ;
2014-05-14 15:43:03 +04:00
2014-12-05 14:42:57 +03:00
memcpy ( & rp . addr , cmd - > param , sizeof ( rp . addr ) ) ;
2014-05-14 15:43:03 +04:00
2014-12-05 14:42:57 +03:00
if ( status = = MGMT_STATUS_SUCCESS ) {
2014-05-14 15:43:03 +04:00
rp . rssi = conn - > rssi ;
2014-12-05 14:42:57 +03:00
rp . tx_power = conn - > tx_power ;
rp . max_tx_power = conn - > max_tx_power ;
} else {
rp . rssi = HCI_RSSI_INVALID ;
rp . tx_power = HCI_TX_POWER_INVALID ;
rp . max_tx_power = HCI_TX_POWER_INVALID ;
2014-05-14 15:43:03 +04:00
}
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( cmd - > sk , cmd - > index , MGMT_OP_GET_CONN_INFO ,
status , & rp , sizeof ( rp ) ) ;
2014-05-14 15:43:03 +04:00
hci_conn_drop ( conn ) ;
2014-08-18 00:28:57 +04:00
hci_conn_put ( conn ) ;
2014-12-19 23:26:03 +03:00
return err ;
2014-05-14 15:43:03 +04:00
}
2015-01-12 00:50:44 +03:00
static void conn_info_refresh_complete ( struct hci_dev * hdev , u8 hci_status ,
u16 opcode )
2014-05-14 15:43:03 +04:00
{
struct hci_cp_read_rssi * cp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-05-14 15:43:03 +04:00
struct hci_conn * conn ;
u16 handle ;
2014-12-05 14:42:57 +03:00
u8 status ;
2014-05-14 15:43:03 +04:00
2014-12-05 14:42:57 +03:00
BT_DBG ( " status 0x%02x " , hci_status ) ;
2014-05-14 15:43:03 +04:00
hci_dev_lock ( hdev ) ;
/* Commands sent in request are either Read RSSI or Read Transmit Power
* Level so we check which one was last sent to retrieve connection
* handle . Both commands have handle as first parameter so it ' s safe to
* cast data on the same command struct .
*
* First command sent is always Read RSSI and we fail only if it fails .
* In other case we simply override error to indicate success as we
* already remembered if TX power value is actually valid .
*/
cp = hci_sent_cmd_data ( hdev , HCI_OP_READ_RSSI ) ;
if ( ! cp ) {
cp = hci_sent_cmd_data ( hdev , HCI_OP_READ_TX_POWER ) ;
2014-12-05 14:42:57 +03:00
status = MGMT_STATUS_SUCCESS ;
} else {
status = mgmt_status ( hci_status ) ;
2014-05-14 15:43:03 +04:00
}
if ( ! cp ) {
2014-12-05 14:42:57 +03:00
BT_ERR ( " invalid sent_cmd in conn_info response " ) ;
2014-05-14 15:43:03 +04:00
goto unlock ;
}
handle = __le16_to_cpu ( cp - > handle ) ;
conn = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( ! conn ) {
2014-12-05 14:42:57 +03:00
BT_ERR ( " unknown handle (%d) in conn_info response " , handle ) ;
2014-05-14 15:43:03 +04:00
goto unlock ;
}
2015-03-17 14:48:47 +03:00
cmd = pending_find_data ( MGMT_OP_GET_CONN_INFO , hdev , conn ) ;
2014-12-05 14:42:57 +03:00
if ( ! cmd )
goto unlock ;
2014-05-14 15:43:03 +04:00
2014-12-05 14:42:57 +03:00
cmd - > cmd_complete ( cmd , status ) ;
mgmt_pending_remove ( cmd ) ;
2014-05-14 15:43:03 +04:00
unlock :
hci_dev_unlock ( hdev ) ;
}
static int get_conn_info ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
{
struct mgmt_cp_get_conn_info * cp = data ;
struct mgmt_rp_get_conn_info rp ;
struct hci_conn * conn ;
unsigned long conn_info_age ;
int err = 0 ;
BT_DBG ( " %s " , hdev - > name ) ;
memset ( & rp , 0 , sizeof ( rp ) ) ;
bacpy ( & rp . addr . bdaddr , & cp - > addr . bdaddr ) ;
rp . addr . type = cp - > addr . type ;
if ( ! bdaddr_type_is_valid ( cp - > addr . type ) )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CONN_INFO ,
MGMT_STATUS_INVALID_PARAMS ,
& rp , sizeof ( rp ) ) ;
2014-05-14 15:43:03 +04:00
hci_dev_lock ( hdev ) ;
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CONN_INFO ,
MGMT_STATUS_NOT_POWERED , & rp ,
sizeof ( rp ) ) ;
2014-05-14 15:43:03 +04:00
goto unlock ;
}
if ( cp - > addr . type = = BDADDR_BREDR )
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK ,
& cp - > addr . bdaddr ) ;
else
conn = hci_conn_hash_lookup_ba ( hdev , LE_LINK , & cp - > addr . bdaddr ) ;
if ( ! conn | | conn - > state ! = BT_CONNECTED ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CONN_INFO ,
MGMT_STATUS_NOT_CONNECTED , & rp ,
sizeof ( rp ) ) ;
2014-05-14 15:43:03 +04:00
goto unlock ;
}
2015-03-17 14:48:47 +03:00
if ( pending_find_data ( MGMT_OP_GET_CONN_INFO , hdev , conn ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CONN_INFO ,
MGMT_STATUS_BUSY , & rp , sizeof ( rp ) ) ;
2014-12-05 14:42:57 +03:00
goto unlock ;
}
2014-05-14 15:43:03 +04:00
/* To avoid client trying to guess when to poll again for information we
* calculate conn info age as random value between min / max set in hdev .
*/
conn_info_age = hdev - > conn_info_min_age +
prandom_u32_max ( hdev - > conn_info_max_age -
hdev - > conn_info_min_age ) ;
/* Query controller to refresh cached values if they are too old or were
* never read .
*/
2014-05-16 18:48:57 +04:00
if ( time_after ( jiffies , conn - > conn_info_timestamp +
msecs_to_jiffies ( conn_info_age ) ) | |
2014-05-14 15:43:03 +04:00
! conn - > conn_info_timestamp ) {
struct hci_request req ;
struct hci_cp_read_tx_power req_txp_cp ;
struct hci_cp_read_rssi req_rssi_cp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-05-14 15:43:03 +04:00
hci_req_init ( & req , hdev ) ;
req_rssi_cp . handle = cpu_to_le16 ( conn - > handle ) ;
hci_req_add ( & req , HCI_OP_READ_RSSI , sizeof ( req_rssi_cp ) ,
& req_rssi_cp ) ;
2014-05-14 15:43:04 +04:00
/* For LE links TX power does not change thus we don't need to
* query for it once value is known .
*/
if ( ! bdaddr_type_is_le ( cp - > addr . type ) | |
conn - > tx_power = = HCI_TX_POWER_INVALID ) {
req_txp_cp . handle = cpu_to_le16 ( conn - > handle ) ;
req_txp_cp . type = 0x00 ;
hci_req_add ( & req , HCI_OP_READ_TX_POWER ,
sizeof ( req_txp_cp ) , & req_txp_cp ) ;
}
2014-05-14 15:43:03 +04:00
2014-05-14 15:43:06 +04:00
/* Max TX power needs to be read only once per connection */
if ( conn - > max_tx_power = = HCI_TX_POWER_INVALID ) {
req_txp_cp . handle = cpu_to_le16 ( conn - > handle ) ;
req_txp_cp . type = 0x01 ;
hci_req_add ( & req , HCI_OP_READ_TX_POWER ,
sizeof ( req_txp_cp ) , & req_txp_cp ) ;
}
2014-05-14 15:43:03 +04:00
err = hci_req_run ( & req , conn_info_refresh_complete ) ;
if ( err < 0 )
goto unlock ;
cmd = mgmt_pending_add ( sk , MGMT_OP_GET_CONN_INFO , hdev ,
data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
hci_conn_hold ( conn ) ;
2014-08-18 00:28:57 +04:00
cmd - > user_data = hci_conn_get ( conn ) ;
2014-12-05 14:42:57 +03:00
cmd - > cmd_complete = conn_info_cmd_complete ;
2014-05-14 15:43:03 +04:00
conn - > conn_info_timestamp = jiffies ;
} else {
/* Cache is valid, just reply with values cached in hci_conn */
rp . rssi = conn - > rssi ;
rp . tx_power = conn - > tx_power ;
2014-05-14 15:43:06 +04:00
rp . max_tx_power = conn - > max_tx_power ;
2014-05-14 15:43:03 +04:00
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CONN_INFO ,
MGMT_STATUS_SUCCESS , & rp , sizeof ( rp ) ) ;
2014-05-14 15:43:03 +04:00
}
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2015-03-06 22:08:55 +03:00
static int clock_info_cmd_complete ( struct mgmt_pending_cmd * cmd , u8 status )
2014-06-28 18:54:07 +04:00
{
2014-12-05 14:36:07 +03:00
struct hci_conn * conn = cmd - > user_data ;
2014-06-28 18:54:07 +04:00
struct mgmt_rp_get_clock_info rp ;
2014-12-05 14:36:07 +03:00
struct hci_dev * hdev ;
2014-12-19 23:26:03 +03:00
int err ;
2014-12-05 14:36:07 +03:00
memset ( & rp , 0 , sizeof ( rp ) ) ;
2016-08-29 07:19:47 +03:00
memcpy ( & rp . addr , cmd - > param , sizeof ( rp . addr ) ) ;
2014-12-05 14:36:07 +03:00
if ( status )
goto complete ;
hdev = hci_dev_get ( cmd - > index ) ;
if ( hdev ) {
rp . local_clock = cpu_to_le32 ( hdev - > clock ) ;
hci_dev_put ( hdev ) ;
}
if ( conn ) {
rp . piconet_clock = cpu_to_le32 ( conn - > clock ) ;
rp . accuracy = cpu_to_le16 ( conn - > clock_accuracy ) ;
}
complete :
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode , status , & rp ,
sizeof ( rp ) ) ;
2014-12-05 14:36:07 +03:00
if ( conn ) {
hci_conn_drop ( conn ) ;
hci_conn_put ( conn ) ;
}
2014-12-19 23:26:03 +03:00
return err ;
2014-12-05 14:36:07 +03:00
}
2015-01-12 00:50:44 +03:00
static void get_clock_info_complete ( struct hci_dev * hdev , u8 status , u16 opcode )
2014-12-05 14:36:07 +03:00
{
2014-06-28 18:54:07 +04:00
struct hci_cp_read_clock * hci_cp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-06-28 18:54:07 +04:00
struct hci_conn * conn ;
BT_DBG ( " %s status %u " , hdev - > name , status ) ;
hci_dev_lock ( hdev ) ;
hci_cp = hci_sent_cmd_data ( hdev , HCI_OP_READ_CLOCK ) ;
if ( ! hci_cp )
goto unlock ;
if ( hci_cp - > which ) {
u16 handle = __le16_to_cpu ( hci_cp - > handle ) ;
conn = hci_conn_hash_lookup_handle ( hdev , handle ) ;
} else {
conn = NULL ;
}
2015-03-17 14:48:47 +03:00
cmd = pending_find_data ( MGMT_OP_GET_CLOCK_INFO , hdev , conn ) ;
2014-06-28 18:54:07 +04:00
if ( ! cmd )
goto unlock ;
2014-12-05 14:36:07 +03:00
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
2014-06-28 18:54:07 +04:00
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
static int get_clock_info ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
{
struct mgmt_cp_get_clock_info * cp = data ;
struct mgmt_rp_get_clock_info rp ;
struct hci_cp_read_clock hci_cp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-06-28 18:54:07 +04:00
struct hci_request req ;
struct hci_conn * conn ;
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
memset ( & rp , 0 , sizeof ( rp ) ) ;
bacpy ( & rp . addr . bdaddr , & cp - > addr . bdaddr ) ;
rp . addr . type = cp - > addr . type ;
if ( cp - > addr . type ! = BDADDR_BREDR )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CLOCK_INFO ,
MGMT_STATUS_INVALID_PARAMS ,
& rp , sizeof ( rp ) ) ;
2014-06-28 18:54:07 +04:00
hci_dev_lock ( hdev ) ;
if ( ! hdev_is_powered ( hdev ) ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CLOCK_INFO ,
MGMT_STATUS_NOT_POWERED , & rp ,
sizeof ( rp ) ) ;
2014-06-28 18:54:07 +04:00
goto unlock ;
}
if ( bacmp ( & cp - > addr . bdaddr , BDADDR_ANY ) ) {
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK ,
& cp - > addr . bdaddr ) ;
if ( ! conn | | conn - > state ! = BT_CONNECTED ) {
2015-03-06 22:08:54 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_GET_CLOCK_INFO ,
MGMT_STATUS_NOT_CONNECTED ,
& rp , sizeof ( rp ) ) ;
2014-06-28 18:54:07 +04:00
goto unlock ;
}
} else {
conn = NULL ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_GET_CLOCK_INFO , hdev , data , len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
2014-12-05 14:36:07 +03:00
cmd - > cmd_complete = clock_info_cmd_complete ;
2014-06-28 18:54:07 +04:00
hci_req_init ( & req , hdev ) ;
memset ( & hci_cp , 0 , sizeof ( hci_cp ) ) ;
hci_req_add ( & req , HCI_OP_READ_CLOCK , sizeof ( hci_cp ) , & hci_cp ) ;
if ( conn ) {
hci_conn_hold ( conn ) ;
2014-08-18 00:28:57 +04:00
cmd - > user_data = hci_conn_get ( conn ) ;
2014-06-28 18:54:07 +04:00
hci_cp . handle = cpu_to_le16 ( conn - > handle ) ;
hci_cp . which = 0x01 ; /* Piconet clock */
hci_req_add ( & req , HCI_OP_READ_CLOCK , sizeof ( hci_cp ) , & hci_cp ) ;
}
err = hci_req_run ( & req , get_clock_info_complete ) ;
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2014-12-19 23:26:02 +03:00
static bool is_connected ( struct hci_dev * hdev , bdaddr_t * addr , u8 type )
{
struct hci_conn * conn ;
conn = hci_conn_hash_lookup_ba ( hdev , LE_LINK , addr ) ;
if ( ! conn )
return false ;
if ( conn - > dst_type ! = type )
return false ;
if ( conn - > state ! = BT_CONNECTED )
return false ;
return true ;
}
/* This function requires the caller holds hdev->lock */
2015-11-11 09:11:18 +03:00
static int hci_conn_params_set ( struct hci_dev * hdev , bdaddr_t * addr ,
2014-12-19 23:26:02 +03:00
u8 addr_type , u8 auto_connect )
{
struct hci_conn_params * params ;
params = hci_conn_params_add ( hdev , addr , addr_type ) ;
if ( ! params )
return - EIO ;
if ( params - > auto_connect = = auto_connect )
return 0 ;
list_del_init ( & params - > action ) ;
switch ( auto_connect ) {
case HCI_AUTO_CONN_DISABLED :
case HCI_AUTO_CONN_LINK_LOSS :
2015-08-07 21:22:54 +03:00
/* If auto connect is being disabled when we're trying to
* connect to device , keep connecting .
*/
if ( params - > explicit_connect )
list_add ( & params - > action , & hdev - > pend_le_conns ) ;
2014-12-19 23:26:02 +03:00
break ;
case HCI_AUTO_CONN_REPORT :
2015-10-16 10:07:51 +03:00
if ( params - > explicit_connect )
list_add ( & params - > action , & hdev - > pend_le_conns ) ;
else
list_add ( & params - > action , & hdev - > pend_le_reports ) ;
2014-12-19 23:26:02 +03:00
break ;
case HCI_AUTO_CONN_DIRECT :
case HCI_AUTO_CONN_ALWAYS :
2015-11-11 09:11:18 +03:00
if ( ! is_connected ( hdev , addr , addr_type ) )
2014-12-19 23:26:02 +03:00
list_add ( & params - > action , & hdev - > pend_le_conns ) ;
break ;
}
params - > auto_connect = auto_connect ;
BT_DBG ( " addr %pMR (type %u) auto_connect %u " , addr , addr_type ,
auto_connect ) ;
return 0 ;
}
2014-06-30 00:28:34 +04:00
static void device_added ( struct sock * sk , struct hci_dev * hdev ,
bdaddr_t * bdaddr , u8 type , u8 action )
{
struct mgmt_ev_device_added ev ;
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = type ;
ev . action = action ;
mgmt_event ( MGMT_EV_DEVICE_ADDED , hdev , & ev , sizeof ( ev ) , sk ) ;
}
2014-06-29 21:44:03 +04:00
static int add_device ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_cp_add_device * cp = data ;
u8 auto_conn , addr_type ;
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
2014-07-09 13:59:14 +04:00
if ( ! bdaddr_type_is_valid ( cp - > addr . type ) | |
2014-06-29 21:44:03 +04:00
! bacmp ( & cp - > addr . bdaddr , BDADDR_ANY ) )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-06-29 21:44:03 +04:00
2014-07-23 23:55:23 +04:00
if ( cp - > action ! = 0x00 & & cp - > action ! = 0x01 & & cp - > action ! = 0x02 )
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-06-29 21:44:03 +04:00
hci_dev_lock ( hdev ) ;
2014-07-09 13:59:14 +04:00
if ( cp - > addr . type = = BDADDR_BREDR ) {
2014-07-23 23:55:23 +04:00
/* Only incoming connections action is supported for now */
2014-07-09 13:59:14 +04:00
if ( cp - > action ! = 0x01 ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_ADD_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-07-09 13:59:14 +04:00
goto unlock ;
}
err = hci_bdaddr_list_add ( & hdev - > whitelist , & cp - > addr . bdaddr ,
cp - > addr . type ) ;
if ( err )
goto unlock ;
2014-07-09 13:59:15 +04:00
2015-11-16 13:52:21 +03:00
hci_req_update_scan ( hdev ) ;
2014-07-09 13:59:15 +04:00
2014-07-09 13:59:14 +04:00
goto added ;
}
2015-10-21 18:02:59 +03:00
addr_type = le_addr_type ( cp - > addr . type ) ;
2014-06-29 21:44:03 +04:00
2014-07-23 23:55:23 +04:00
if ( cp - > action = = 0x02 )
2014-06-29 21:44:03 +04:00
auto_conn = HCI_AUTO_CONN_ALWAYS ;
2014-07-23 23:55:23 +04:00
else if ( cp - > action = = 0x01 )
auto_conn = HCI_AUTO_CONN_DIRECT ;
2014-06-29 21:44:03 +04:00
else
2014-07-02 18:37:27 +04:00
auto_conn = HCI_AUTO_CONN_REPORT ;
2014-06-29 21:44:03 +04:00
2015-07-20 14:12:49 +03:00
/* Kernel internally uses conn_params with resolvable private
* address , but Add Device allows only identity addresses .
* Make sure it is enforced before calling
* hci_conn_params_lookup .
*/
if ( ! hci_is_identity_address ( & cp - > addr . bdaddr , addr_type ) ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2015-07-20 14:12:49 +03:00
goto unlock ;
}
2014-06-30 14:34:39 +04:00
/* If the connection parameters don't exist for this device,
* they will be created and configured with defaults .
*/
2015-11-11 09:11:18 +03:00
if ( hci_conn_params_set ( hdev , & cp - > addr . bdaddr , addr_type ,
2014-07-01 14:11:06 +04:00
auto_conn ) < 0 ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_DEVICE ,
MGMT_STATUS_FAILED , & cp - > addr ,
sizeof ( cp - > addr ) ) ;
2014-06-29 21:44:03 +04:00
goto unlock ;
}
2015-11-11 09:11:18 +03:00
hci_update_background_scan ( hdev ) ;
2014-07-09 13:59:14 +04:00
added :
2014-06-30 00:28:34 +04:00
device_added ( sk , hdev , & cp - > addr . bdaddr , cp - > addr . type , cp - > action ) ;
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_DEVICE ,
MGMT_STATUS_SUCCESS , & cp - > addr ,
sizeof ( cp - > addr ) ) ;
2014-06-29 21:44:03 +04:00
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2014-06-30 00:28:34 +04:00
static void device_removed ( struct sock * sk , struct hci_dev * hdev ,
bdaddr_t * bdaddr , u8 type )
{
struct mgmt_ev_device_removed ev ;
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = type ;
mgmt_event ( MGMT_EV_DEVICE_REMOVED , hdev , & ev , sizeof ( ev ) , sk ) ;
}
2014-06-29 21:44:03 +04:00
static int remove_device ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_cp_remove_device * cp = data ;
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
hci_dev_lock ( hdev ) ;
if ( bacmp ( & cp - > addr . bdaddr , BDADDR_ANY ) ) {
2014-07-02 18:37:28 +04:00
struct hci_conn_params * params ;
2014-06-29 21:44:03 +04:00
u8 addr_type ;
2014-07-09 13:59:14 +04:00
if ( ! bdaddr_type_is_valid ( cp - > addr . type ) ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-06-29 21:44:03 +04:00
goto unlock ;
}
2014-07-09 13:59:14 +04:00
if ( cp - > addr . type = = BDADDR_BREDR ) {
err = hci_bdaddr_list_del ( & hdev - > whitelist ,
& cp - > addr . bdaddr ,
cp - > addr . type ) ;
if ( err ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr ,
sizeof ( cp - > addr ) ) ;
2014-07-09 13:59:14 +04:00
goto unlock ;
}
2015-11-16 13:52:21 +03:00
hci_req_update_scan ( hdev ) ;
2014-07-09 13:59:15 +04:00
2014-07-09 13:59:14 +04:00
device_removed ( sk , hdev , & cp - > addr . bdaddr ,
cp - > addr . type ) ;
goto complete ;
}
2015-10-21 18:02:59 +03:00
addr_type = le_addr_type ( cp - > addr . type ) ;
2014-06-29 21:44:03 +04:00
2015-07-20 14:12:49 +03:00
/* Kernel internally uses conn_params with resolvable private
* address , but Remove Device allows only identity addresses .
* Make sure it is enforced before calling
* hci_conn_params_lookup .
*/
if ( ! hci_is_identity_address ( & cp - > addr . bdaddr , addr_type ) ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2015-07-20 14:12:49 +03:00
goto unlock ;
}
2014-07-02 18:37:28 +04:00
params = hci_conn_params_lookup ( hdev , & cp - > addr . bdaddr ,
addr_type ) ;
if ( ! params ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-07-02 18:37:28 +04:00
goto unlock ;
}
2015-10-16 10:07:52 +03:00
if ( params - > auto_connect = = HCI_AUTO_CONN_DISABLED | |
params - > auto_connect = = HCI_AUTO_CONN_EXPLICIT ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-07-02 18:37:28 +04:00
goto unlock ;
}
2014-07-04 17:17:23 +04:00
list_del ( & params - > action ) ;
2014-07-02 18:37:28 +04:00
list_del ( & params - > list ) ;
kfree ( params ) ;
2015-11-11 09:11:18 +03:00
hci_update_background_scan ( hdev ) ;
2014-06-30 00:28:34 +04:00
device_removed ( sk , hdev , & cp - > addr . bdaddr , cp - > addr . type ) ;
2014-06-29 21:44:03 +04:00
} else {
2014-07-06 14:06:51 +04:00
struct hci_conn_params * p , * tmp ;
2014-07-09 13:59:14 +04:00
struct bdaddr_list * b , * btmp ;
2014-07-06 14:06:51 +04:00
2014-06-29 21:44:03 +04:00
if ( cp - > addr . type ) {
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_DEVICE ,
MGMT_STATUS_INVALID_PARAMS ,
& cp - > addr , sizeof ( cp - > addr ) ) ;
2014-06-29 21:44:03 +04:00
goto unlock ;
}
2014-07-09 13:59:14 +04:00
list_for_each_entry_safe ( b , btmp , & hdev - > whitelist , list ) {
device_removed ( sk , hdev , & b - > bdaddr , b - > bdaddr_type ) ;
list_del ( & b - > list ) ;
kfree ( b ) ;
}
2015-11-16 13:52:21 +03:00
hci_req_update_scan ( hdev ) ;
2014-07-09 13:59:15 +04:00
2014-07-06 14:06:51 +04:00
list_for_each_entry_safe ( p , tmp , & hdev - > le_conn_params , list ) {
if ( p - > auto_connect = = HCI_AUTO_CONN_DISABLED )
continue ;
device_removed ( sk , hdev , & p - > addr , p - > addr_type ) ;
2015-10-16 10:07:52 +03:00
if ( p - > explicit_connect ) {
p - > auto_connect = HCI_AUTO_CONN_EXPLICIT ;
continue ;
}
2014-07-06 14:06:51 +04:00
list_del ( & p - > action ) ;
list_del ( & p - > list ) ;
kfree ( p ) ;
}
BT_DBG ( " All LE connection parameters were removed " ) ;
2015-11-11 09:11:18 +03:00
hci_update_background_scan ( hdev ) ;
2014-06-29 21:44:03 +04:00
}
2014-07-09 13:59:14 +04:00
complete :
2015-11-11 09:11:18 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_REMOVE_DEVICE ,
MGMT_STATUS_SUCCESS , & cp - > addr ,
sizeof ( cp - > addr ) ) ;
2014-06-29 21:44:03 +04:00
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2014-07-02 18:37:29 +04:00
static int load_conn_param ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 len )
{
struct mgmt_cp_load_conn_param * cp = data ;
2014-07-03 14:52:27 +04:00
const u16 max_param_count = ( ( U16_MAX - sizeof ( * cp ) ) /
sizeof ( struct mgmt_conn_param ) ) ;
2014-07-02 18:37:29 +04:00
u16 param_count , expected_len ;
int i ;
if ( ! lmp_le_capable ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_CONN_PARAM ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2014-07-02 18:37:29 +04:00
param_count = __le16_to_cpu ( cp - > param_count ) ;
2014-07-03 14:52:27 +04:00
if ( param_count > max_param_count ) {
BT_ERR ( " load_conn_param: too big param_count value %u " ,
param_count ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_CONN_PARAM ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-07-03 14:52:27 +04:00
}
2014-07-02 18:37:29 +04:00
expected_len = sizeof ( * cp ) + param_count *
sizeof ( struct mgmt_conn_param ) ;
if ( expected_len ! = len ) {
BT_ERR ( " load_conn_param: expected %u bytes, got %u bytes " ,
expected_len , len ) ;
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_CONN_PARAM ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-07-02 18:37:29 +04:00
}
BT_DBG ( " %s param_count %u " , hdev - > name , param_count ) ;
hci_dev_lock ( hdev ) ;
hci_conn_params_clear_disabled ( hdev ) ;
for ( i = 0 ; i < param_count ; i + + ) {
struct mgmt_conn_param * param = & cp - > params [ i ] ;
struct hci_conn_params * hci_param ;
u16 min , max , latency , timeout ;
u8 addr_type ;
BT_DBG ( " Adding %pMR (type %u) " , & param - > addr . bdaddr ,
param - > addr . type ) ;
if ( param - > addr . type = = BDADDR_LE_PUBLIC ) {
addr_type = ADDR_LE_DEV_PUBLIC ;
} else if ( param - > addr . type = = BDADDR_LE_RANDOM ) {
addr_type = ADDR_LE_DEV_RANDOM ;
} else {
BT_ERR ( " Ignoring invalid connection parameters " ) ;
continue ;
}
min = le16_to_cpu ( param - > min_interval ) ;
max = le16_to_cpu ( param - > max_interval ) ;
latency = le16_to_cpu ( param - > latency ) ;
timeout = le16_to_cpu ( param - > timeout ) ;
BT_DBG ( " min 0x%04x max 0x%04x latency 0x%04x timeout 0x%04x " ,
min , max , latency , timeout ) ;
if ( hci_check_conn_params ( min , max , latency , timeout ) < 0 ) {
BT_ERR ( " Ignoring invalid connection parameters " ) ;
continue ;
}
hci_param = hci_conn_params_add ( hdev , & param - > addr . bdaddr ,
addr_type ) ;
if ( ! hci_param ) {
BT_ERR ( " Failed to add connection parameters " ) ;
continue ;
}
hci_param - > conn_min_interval = min ;
hci_param - > conn_max_interval = max ;
hci_param - > conn_latency = latency ;
hci_param - > supervision_timeout = timeout ;
}
hci_dev_unlock ( hdev ) ;
2015-03-06 22:08:54 +03:00
return mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_LOAD_CONN_PARAM , 0 ,
NULL , 0 ) ;
2014-07-02 18:37:29 +04:00
}
2014-07-04 20:11:55 +04:00
static int set_external_config ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_cp_set_external_config * cp = data ;
bool changed ;
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
if ( hdev_is_powered ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_EXTERNAL_CONFIG ,
MGMT_STATUS_REJECTED ) ;
2014-07-04 20:11:55 +04:00
if ( cp - > config ! = 0x00 & & cp - > config ! = 0x01 )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_EXTERNAL_CONFIG ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-07-04 20:11:55 +04:00
if ( ! test_bit ( HCI_QUIRK_EXTERNAL_CONFIG , & hdev - > quirks ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_EXTERNAL_CONFIG ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2014-07-04 20:11:55 +04:00
hci_dev_lock ( hdev ) ;
if ( cp - > config )
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_EXT_CONFIGURED ) ;
2014-07-04 20:11:55 +04:00
else
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev , HCI_EXT_CONFIGURED ) ;
2014-07-04 20:11:55 +04:00
err = send_options_rsp ( sk , MGMT_OP_SET_EXTERNAL_CONFIG , hdev ) ;
if ( err < 0 )
goto unlock ;
if ( ! changed )
goto unlock ;
2014-07-04 21:06:23 +04:00
err = new_options ( hdev , sk ) ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_UNCONFIGURED ) = = is_configured ( hdev ) ) {
2014-07-04 20:11:55 +04:00
mgmt_index_removed ( hdev ) ;
2014-07-06 14:11:14 +04:00
2015-03-13 12:11:04 +03:00
if ( hci_dev_test_and_change_flag ( hdev , HCI_UNCONFIGURED ) ) {
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_CONFIG ) ;
hci_dev_set_flag ( hdev , HCI_AUTO_OFF ) ;
2014-07-06 14:11:14 +04:00
queue_work ( hdev - > req_workqueue , & hdev - > power_on ) ;
} else {
2014-07-06 14:11:16 +04:00
set_bit ( HCI_RAW , & hdev - > flags ) ;
2014-07-06 14:11:14 +04:00
mgmt_index_added ( hdev ) ;
}
2014-07-04 20:11:55 +04:00
}
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2014-07-06 14:11:15 +04:00
static int set_public_address ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 len )
{
struct mgmt_cp_set_public_address * cp = data ;
bool changed ;
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
if ( hdev_is_powered ( hdev ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_PUBLIC_ADDRESS ,
MGMT_STATUS_REJECTED ) ;
2014-07-06 14:11:15 +04:00
if ( ! bacmp ( & cp - > bdaddr , BDADDR_ANY ) )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_PUBLIC_ADDRESS ,
MGMT_STATUS_INVALID_PARAMS ) ;
2014-07-06 14:11:15 +04:00
if ( ! hdev - > set_bdaddr )
2015-03-06 22:08:53 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_SET_PUBLIC_ADDRESS ,
MGMT_STATUS_NOT_SUPPORTED ) ;
2014-07-06 14:11:15 +04:00
hci_dev_lock ( hdev ) ;
changed = ! ! bacmp ( & hdev - > public_addr , & cp - > bdaddr ) ;
bacpy ( & hdev - > public_addr , & cp - > bdaddr ) ;
err = send_options_rsp ( sk , MGMT_OP_SET_PUBLIC_ADDRESS , hdev ) ;
if ( err < 0 )
goto unlock ;
if ( ! changed )
goto unlock ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_UNCONFIGURED ) )
2014-07-06 14:11:15 +04:00
err = new_options ( hdev , sk ) ;
if ( is_configured ( hdev ) ) {
mgmt_index_removed ( hdev ) ;
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_UNCONFIGURED ) ;
2014-07-06 14:11:15 +04:00
2015-03-13 12:11:01 +03:00
hci_dev_set_flag ( hdev , HCI_CONFIG ) ;
hci_dev_set_flag ( hdev , HCI_AUTO_OFF ) ;
2014-07-06 14:11:15 +04:00
queue_work ( hdev - > req_workqueue , & hdev - > power_on ) ;
}
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2015-04-07 21:52:22 +03:00
static void read_local_oob_ext_data_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode , struct sk_buff * skb )
{
const struct mgmt_cp_read_local_oob_ext_data * mgmt_cp ;
struct mgmt_rp_read_local_oob_ext_data * mgmt_rp ;
u8 * h192 , * r192 , * h256 , * r256 ;
struct mgmt_pending_cmd * cmd ;
u16 eir_len ;
int err ;
BT_DBG ( " %s status %u " , hdev - > name , status ) ;
cmd = pending_find ( MGMT_OP_READ_LOCAL_OOB_EXT_DATA , hdev ) ;
if ( ! cmd )
return ;
mgmt_cp = cmd - > param ;
if ( status ) {
status = mgmt_status ( status ) ;
eir_len = 0 ;
h192 = NULL ;
r192 = NULL ;
h256 = NULL ;
r256 = NULL ;
} else if ( opcode = = HCI_OP_READ_LOCAL_OOB_DATA ) {
struct hci_rp_read_local_oob_data * rp ;
if ( skb - > len ! = sizeof ( * rp ) ) {
status = MGMT_STATUS_FAILED ;
eir_len = 0 ;
} else {
status = MGMT_STATUS_SUCCESS ;
rp = ( void * ) skb - > data ;
eir_len = 5 + 18 + 18 ;
h192 = rp - > hash ;
r192 = rp - > rand ;
h256 = NULL ;
r256 = NULL ;
}
} else {
struct hci_rp_read_local_oob_ext_data * rp ;
if ( skb - > len ! = sizeof ( * rp ) ) {
status = MGMT_STATUS_FAILED ;
eir_len = 0 ;
} else {
status = MGMT_STATUS_SUCCESS ;
rp = ( void * ) skb - > data ;
if ( hci_dev_test_flag ( hdev , HCI_SC_ONLY ) ) {
eir_len = 5 + 18 + 18 ;
h192 = NULL ;
r192 = NULL ;
} else {
eir_len = 5 + 18 + 18 + 18 + 18 ;
h192 = rp - > hash192 ;
r192 = rp - > rand192 ;
}
h256 = rp - > hash256 ;
r256 = rp - > rand256 ;
}
}
mgmt_rp = kmalloc ( sizeof ( * mgmt_rp ) + eir_len , GFP_KERNEL ) ;
if ( ! mgmt_rp )
goto done ;
if ( status )
goto send_rsp ;
eir_len = eir_append_data ( mgmt_rp - > eir , 0 , EIR_CLASS_OF_DEV ,
hdev - > dev_class , 3 ) ;
if ( h192 & & r192 ) {
eir_len = eir_append_data ( mgmt_rp - > eir , eir_len ,
EIR_SSP_HASH_C192 , h192 , 16 ) ;
eir_len = eir_append_data ( mgmt_rp - > eir , eir_len ,
EIR_SSP_RAND_R192 , r192 , 16 ) ;
}
if ( h256 & & r256 ) {
eir_len = eir_append_data ( mgmt_rp - > eir , eir_len ,
EIR_SSP_HASH_C256 , h256 , 16 ) ;
eir_len = eir_append_data ( mgmt_rp - > eir , eir_len ,
EIR_SSP_RAND_R256 , r256 , 16 ) ;
}
send_rsp :
mgmt_rp - > type = mgmt_cp - > type ;
mgmt_rp - > eir_len = cpu_to_le16 ( eir_len ) ;
err = mgmt_cmd_complete ( cmd - > sk , hdev - > id ,
MGMT_OP_READ_LOCAL_OOB_EXT_DATA , status ,
mgmt_rp , sizeof ( * mgmt_rp ) + eir_len ) ;
if ( err < 0 | | status )
goto done ;
hci_sock_set_flag ( cmd - > sk , HCI_MGMT_OOB_DATA_EVENTS ) ;
err = mgmt_limited_event ( MGMT_EV_LOCAL_OOB_DATA_UPDATED , hdev ,
mgmt_rp , sizeof ( * mgmt_rp ) + eir_len ,
HCI_MGMT_OOB_DATA_EVENTS , cmd - > sk ) ;
done :
kfree ( mgmt_rp ) ;
mgmt_pending_remove ( cmd ) ;
}
static int read_local_ssp_oob_req ( struct hci_dev * hdev , struct sock * sk ,
struct mgmt_cp_read_local_oob_ext_data * cp )
{
struct mgmt_pending_cmd * cmd ;
struct hci_request req ;
int err ;
cmd = mgmt_pending_add ( sk , MGMT_OP_READ_LOCAL_OOB_EXT_DATA , hdev ,
cp , sizeof ( * cp ) ) ;
if ( ! cmd )
return - ENOMEM ;
hci_req_init ( & req , hdev ) ;
if ( bredr_sc_enabled ( hdev ) )
hci_req_add ( & req , HCI_OP_READ_LOCAL_OOB_EXT_DATA , 0 , NULL ) ;
else
hci_req_add ( & req , HCI_OP_READ_LOCAL_OOB_DATA , 0 , NULL ) ;
err = hci_req_run_skb ( & req , read_local_oob_ext_data_complete ) ;
if ( err < 0 ) {
mgmt_pending_remove ( cmd ) ;
return err ;
}
return 0 ;
}
2015-03-15 08:43:19 +03:00
static int read_local_oob_ext_data ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
struct mgmt_cp_read_local_oob_ext_data * cp = data ;
struct mgmt_rp_read_local_oob_ext_data * rp ;
size_t rp_len ;
u16 eir_len ;
2015-03-16 11:10:23 +03:00
u8 status , flags , role , addr [ 7 ] , hash [ 16 ] , rand [ 16 ] ;
2015-03-15 08:43:19 +03:00
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
2015-03-29 01:18:59 +03:00
if ( hdev_is_powered ( hdev ) ) {
switch ( cp - > type ) {
case BIT ( BDADDR_BREDR ) :
status = mgmt_bredr_support ( hdev ) ;
if ( status )
eir_len = 0 ;
else
eir_len = 5 ;
break ;
case ( BIT ( BDADDR_LE_PUBLIC ) | BIT ( BDADDR_LE_RANDOM ) ) :
status = mgmt_le_support ( hdev ) ;
if ( status )
eir_len = 0 ;
else
eir_len = 9 + 3 + 18 + 18 + 3 ;
break ;
default :
status = MGMT_STATUS_INVALID_PARAMS ;
eir_len = 0 ;
break ;
}
} else {
status = MGMT_STATUS_NOT_POWERED ;
eir_len = 0 ;
2015-03-15 08:43:19 +03:00
}
rp_len = sizeof ( * rp ) + eir_len ;
rp = kmalloc ( rp_len , GFP_ATOMIC ) ;
2015-03-29 01:18:58 +03:00
if ( ! rp )
2015-03-15 08:43:19 +03:00
return - ENOMEM ;
2015-03-29 01:18:58 +03:00
2015-03-29 01:18:59 +03:00
if ( status )
goto complete ;
2015-03-29 01:18:58 +03:00
hci_dev_lock ( hdev ) ;
2015-03-15 08:43:19 +03:00
eir_len = 0 ;
switch ( cp - > type ) {
case BIT ( BDADDR_BREDR ) :
2015-04-07 21:52:22 +03:00
if ( hci_dev_test_flag ( hdev , HCI_SSP_ENABLED ) ) {
err = read_local_ssp_oob_req ( hdev , sk , cp ) ;
hci_dev_unlock ( hdev ) ;
if ( ! err )
goto done ;
status = MGMT_STATUS_FAILED ;
goto complete ;
} else {
eir_len = eir_append_data ( rp - > eir , eir_len ,
EIR_CLASS_OF_DEV ,
hdev - > dev_class , 3 ) ;
}
2015-03-15 08:43:19 +03:00
break ;
case ( BIT ( BDADDR_LE_PUBLIC ) | BIT ( BDADDR_LE_RANDOM ) ) :
2015-03-16 22:39:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_SC_ENABLED ) & &
smp_generate_oob ( hdev , hash , rand ) < 0 ) {
2015-03-16 11:10:23 +03:00
hci_dev_unlock ( hdev ) ;
2015-03-29 01:18:59 +03:00
status = MGMT_STATUS_FAILED ;
goto complete ;
2015-03-16 11:10:23 +03:00
}
2015-04-02 22:00:58 +03:00
/* This should return the active RPA, but since the RPA
* is only programmed on demand , it is really hard to fill
* this in at the moment . For now disallow retrieving
* local out - of - band data when privacy is in use .
*
* Returning the identity address will not help here since
* pairing happens before the identity resolving key is
* known and thus the connection establishment happens
* based on the RPA and not the identity address .
*/
2015-03-15 08:43:19 +03:00
if ( hci_dev_test_flag ( hdev , HCI_PRIVACY ) ) {
2015-04-02 22:00:58 +03:00
hci_dev_unlock ( hdev ) ;
status = MGMT_STATUS_REJECTED ;
goto complete ;
}
if ( hci_dev_test_flag ( hdev , HCI_FORCE_STATIC_ADDR ) | |
! bacmp ( & hdev - > bdaddr , BDADDR_ANY ) | |
( ! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) & &
bacmp ( & hdev - > static_addr , BDADDR_ANY ) ) ) {
2015-03-15 08:43:19 +03:00
memcpy ( addr , & hdev - > static_addr , 6 ) ;
addr [ 6 ] = 0x01 ;
} else {
memcpy ( addr , & hdev - > bdaddr , 6 ) ;
addr [ 6 ] = 0x00 ;
}
eir_len = eir_append_data ( rp - > eir , eir_len , EIR_LE_BDADDR ,
addr , sizeof ( addr ) ) ;
if ( hci_dev_test_flag ( hdev , HCI_ADVERTISING ) )
role = 0x02 ;
else
role = 0x01 ;
eir_len = eir_append_data ( rp - > eir , eir_len , EIR_LE_ROLE ,
& role , sizeof ( role ) ) ;
2015-03-16 22:39:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_SC_ENABLED ) ) {
eir_len = eir_append_data ( rp - > eir , eir_len ,
EIR_LE_SC_CONFIRM ,
hash , sizeof ( hash ) ) ;
2015-03-16 11:10:23 +03:00
2015-03-16 22:39:00 +03:00
eir_len = eir_append_data ( rp - > eir , eir_len ,
EIR_LE_SC_RANDOM ,
rand , sizeof ( rand ) ) ;
}
2015-03-16 11:10:23 +03:00
2015-11-18 13:49:20 +03:00
flags = mgmt_get_adv_discov_flags ( hdev ) ;
2015-03-15 08:43:19 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_BREDR_ENABLED ) )
flags | = LE_AD_NO_BREDR ;
eir_len = eir_append_data ( rp - > eir , eir_len , EIR_FLAGS ,
& flags , sizeof ( flags ) ) ;
break ;
}
hci_dev_unlock ( hdev ) ;
2015-03-17 02:11:21 +03:00
hci_sock_set_flag ( sk , HCI_MGMT_OOB_DATA_EVENTS ) ;
2015-03-29 01:18:59 +03:00
status = MGMT_STATUS_SUCCESS ;
complete :
2015-03-29 01:18:58 +03:00
rp - > type = cp - > type ;
rp - > eir_len = cpu_to_le16 ( eir_len ) ;
2015-03-15 08:43:19 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_EXT_DATA ,
2015-03-29 01:18:59 +03:00
status , rp , sizeof ( * rp ) + eir_len ) ;
if ( err < 0 | | status )
2015-03-17 02:11:21 +03:00
goto done ;
err = mgmt_limited_event ( MGMT_EV_LOCAL_OOB_DATA_UPDATED , hdev ,
rp , sizeof ( * rp ) + eir_len ,
HCI_MGMT_OOB_DATA_EVENTS , sk ) ;
2015-03-15 08:43:19 +03:00
2015-03-16 11:10:23 +03:00
done :
2015-03-15 08:43:19 +03:00
kfree ( rp ) ;
return err ;
}
2015-03-26 04:53:45 +03:00
static u32 get_supported_adv_flags ( struct hci_dev * hdev )
{
u32 flags = 0 ;
flags | = MGMT_ADV_FLAG_CONNECTABLE ;
flags | = MGMT_ADV_FLAG_DISCOV ;
flags | = MGMT_ADV_FLAG_LIMITED_DISCOV ;
flags | = MGMT_ADV_FLAG_MANAGED_FLAGS ;
2016-09-18 13:50:03 +03:00
flags | = MGMT_ADV_FLAG_APPEARANCE ;
2016-09-18 13:50:02 +03:00
flags | = MGMT_ADV_FLAG_LOCAL_NAME ;
2015-03-26 04:53:45 +03:00
if ( hdev - > adv_tx_power ! = HCI_TX_POWER_INVALID )
flags | = MGMT_ADV_FLAG_TX_POWER ;
return flags ;
}
2015-03-15 06:53:25 +03:00
static int read_adv_features ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
struct mgmt_rp_read_adv_features * rp ;
size_t rp_len ;
2015-11-26 13:15:58 +03:00
int err ;
2015-06-18 04:16:38 +03:00
struct adv_info * adv_instance ;
2015-03-26 04:53:45 +03:00
u32 supported_flags ;
2015-11-26 13:15:58 +03:00
u8 * instance ;
2015-03-15 06:53:25 +03:00
BT_DBG ( " %s " , hdev - > name ) ;
2015-03-26 04:53:45 +03:00
if ( ! lmp_le_capable ( hdev ) )
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_READ_ADV_FEATURES ,
MGMT_STATUS_REJECTED ) ;
2015-03-15 06:53:25 +03:00
hci_dev_lock ( hdev ) ;
2015-11-26 13:15:58 +03:00
rp_len = sizeof ( * rp ) + hdev - > adv_instance_cnt ;
2015-03-15 06:53:25 +03:00
rp = kmalloc ( rp_len , GFP_ATOMIC ) ;
if ( ! rp ) {
hci_dev_unlock ( hdev ) ;
return - ENOMEM ;
}
2015-03-26 04:53:45 +03:00
supported_flags = get_supported_adv_flags ( hdev ) ;
rp - > supported_flags = cpu_to_le32 ( supported_flags ) ;
2015-03-20 03:22:25 +03:00
rp - > max_adv_data_len = HCI_MAX_AD_LENGTH ;
rp - > max_scan_rsp_len = HCI_MAX_AD_LENGTH ;
2015-06-18 04:16:34 +03:00
rp - > max_instances = HCI_MAX_ADV_INSTANCES ;
2015-11-26 13:15:58 +03:00
rp - > num_instances = hdev - > adv_instance_cnt ;
2015-03-24 01:57:12 +03:00
2015-11-26 13:15:58 +03:00
instance = rp - > instance ;
list_for_each_entry ( adv_instance , & hdev - > adv_instances , list ) {
* instance = adv_instance - > instance ;
instance + + ;
2015-03-24 01:57:12 +03:00
}
2015-03-15 06:53:25 +03:00
hci_dev_unlock ( hdev ) ;
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_READ_ADV_FEATURES ,
MGMT_STATUS_SUCCESS , rp , rp_len ) ;
kfree ( rp ) ;
return err ;
}
2016-09-18 13:50:05 +03:00
static u8 tlv_data_max_len ( u32 adv_flags , bool is_adv_data )
2015-03-24 01:57:12 +03:00
{
2015-03-24 01:57:14 +03:00
u8 max_len = HCI_MAX_AD_LENGTH ;
2015-03-24 01:57:12 +03:00
2015-11-19 18:16:42 +03:00
if ( is_adv_data ) {
if ( adv_flags & ( MGMT_ADV_FLAG_DISCOV |
MGMT_ADV_FLAG_LIMITED_DISCOV |
2016-09-18 13:50:05 +03:00
MGMT_ADV_FLAG_MANAGED_FLAGS ) )
2015-11-19 18:16:42 +03:00
max_len - = 3 ;
2015-03-24 01:57:12 +03:00
2016-09-18 13:50:05 +03:00
if ( adv_flags & MGMT_ADV_FLAG_TX_POWER )
2015-11-19 18:16:42 +03:00
max_len - = 3 ;
2016-09-18 13:50:02 +03:00
} else {
/* at least 1 byte of name should fit in */
if ( adv_flags & MGMT_ADV_FLAG_LOCAL_NAME )
max_len - = 3 ;
2016-09-18 13:50:03 +03:00
2016-09-18 13:50:05 +03:00
if ( adv_flags & ( MGMT_ADV_FLAG_APPEARANCE ) )
2016-09-18 13:50:03 +03:00
max_len - = 4 ;
2015-03-26 04:53:44 +03:00
}
2016-09-18 13:50:05 +03:00
return max_len ;
}
static bool flags_managed ( u32 adv_flags )
{
return adv_flags & ( MGMT_ADV_FLAG_DISCOV |
MGMT_ADV_FLAG_LIMITED_DISCOV |
MGMT_ADV_FLAG_MANAGED_FLAGS ) ;
}
static bool tx_power_managed ( u32 adv_flags )
{
return adv_flags & MGMT_ADV_FLAG_TX_POWER ;
}
static bool name_managed ( u32 adv_flags )
{
return adv_flags & MGMT_ADV_FLAG_LOCAL_NAME ;
}
static bool appearance_managed ( u32 adv_flags )
{
return adv_flags & MGMT_ADV_FLAG_APPEARANCE ;
}
static bool tlv_data_is_valid ( u32 adv_flags , u8 * data , u8 len , bool is_adv_data )
{
int i , cur_len ;
u8 max_len ;
max_len = tlv_data_max_len ( adv_flags , is_adv_data ) ;
2015-03-24 01:57:14 +03:00
if ( len > max_len )
2015-03-24 01:57:12 +03:00
return false ;
2015-03-24 01:57:14 +03:00
/* Make sure that the data is correctly formatted. */
for ( i = 0 , cur_len = 0 ; i < len ; i + = ( cur_len + 1 ) ) {
cur_len = data [ i ] ;
2015-03-24 01:57:12 +03:00
2016-09-18 13:50:06 +03:00
if ( data [ i + 1 ] = = EIR_FLAGS & &
( ! is_adv_data | | flags_managed ( adv_flags ) ) )
2016-09-18 13:50:05 +03:00
return false ;
if ( data [ i + 1 ] = = EIR_TX_POWER & & tx_power_managed ( adv_flags ) )
return false ;
if ( data [ i + 1 ] = = EIR_NAME_COMPLETE & & name_managed ( adv_flags ) )
return false ;
if ( data [ i + 1 ] = = EIR_NAME_SHORT & & name_managed ( adv_flags ) )
2015-03-26 04:53:41 +03:00
return false ;
2016-09-18 13:50:05 +03:00
if ( data [ i + 1 ] = = EIR_APPEARANCE & &
appearance_managed ( adv_flags ) )
2015-03-26 04:53:44 +03:00
return false ;
2015-03-24 01:57:12 +03:00
/* If the current field length would exceed the total data
* length , then it ' s invalid .
*/
2015-03-24 01:57:14 +03:00
if ( i + cur_len > = len )
2015-03-24 01:57:12 +03:00
return false ;
}
return true ;
}
static void add_advertising_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode )
{
struct mgmt_pending_cmd * cmd ;
2015-06-18 04:16:47 +03:00
struct mgmt_cp_add_advertising * cp ;
2015-03-24 01:57:12 +03:00
struct mgmt_rp_add_advertising rp ;
2015-06-18 04:16:47 +03:00
struct adv_info * adv_instance , * n ;
u8 instance ;
2015-03-24 01:57:12 +03:00
BT_DBG ( " status %d " , status ) ;
hci_dev_lock ( hdev ) ;
cmd = pending_find ( MGMT_OP_ADD_ADVERTISING , hdev ) ;
2015-06-18 04:16:47 +03:00
list_for_each_entry_safe ( adv_instance , n , & hdev - > adv_instances , list ) {
if ( ! adv_instance - > pending )
continue ;
if ( ! status ) {
adv_instance - > pending = false ;
continue ;
}
instance = adv_instance - > instance ;
if ( hdev - > cur_adv_instance = = instance )
cancel_adv_timeout ( hdev ) ;
hci_remove_adv_instance ( hdev , instance ) ;
2015-11-18 13:49:20 +03:00
mgmt_advertising_removed ( cmd ? cmd - > sk : NULL , hdev , instance ) ;
2015-03-24 01:57:12 +03:00
}
if ( ! cmd )
goto unlock ;
2015-06-18 04:16:47 +03:00
cp = cmd - > param ;
rp . instance = cp - > instance ;
2015-03-24 01:57:12 +03:00
if ( status )
mgmt_cmd_status ( cmd - > sk , cmd - > index , cmd - > opcode ,
mgmt_status ( status ) ) ;
else
mgmt_cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode ,
mgmt_status ( status ) , & rp , sizeof ( rp ) ) ;
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
static int add_advertising ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
struct mgmt_cp_add_advertising * cp = data ;
struct mgmt_rp_add_advertising rp ;
u32 flags ;
2015-03-26 04:53:45 +03:00
u32 supported_flags ;
2015-03-24 01:57:12 +03:00
u8 status ;
2015-06-18 04:16:47 +03:00
u16 timeout , duration ;
unsigned int prev_instance_cnt = hdev - > adv_instance_cnt ;
u8 schedule_instance = 0 ;
struct adv_info * next_instance ;
2015-03-24 01:57:12 +03:00
int err ;
struct mgmt_pending_cmd * cmd ;
struct hci_request req ;
BT_DBG ( " %s " , hdev - > name ) ;
status = mgmt_le_support ( hdev ) ;
if ( status )
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
status ) ;
2015-11-19 18:16:41 +03:00
if ( cp - > instance < 1 | | cp - > instance > HCI_MAX_ADV_INSTANCES )
2016-03-11 10:56:33 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
MGMT_STATUS_INVALID_PARAMS ) ;
if ( data_len ! = sizeof ( * cp ) + cp - > adv_data_len + cp - > scan_rsp_len )
2015-11-19 18:16:41 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
MGMT_STATUS_INVALID_PARAMS ) ;
2015-03-24 01:57:12 +03:00
flags = __le32_to_cpu ( cp - > flags ) ;
2015-03-24 01:57:15 +03:00
timeout = __le16_to_cpu ( cp - > timeout ) ;
2015-06-18 04:16:47 +03:00
duration = __le16_to_cpu ( cp - > duration ) ;
2015-03-24 01:57:12 +03:00
2015-06-18 04:16:47 +03:00
/* The current implementation only supports a subset of the specified
* flags .
2015-03-26 04:53:45 +03:00
*/
supported_flags = get_supported_adv_flags ( hdev ) ;
2015-06-18 04:16:47 +03:00
if ( flags & ~ supported_flags )
2015-03-24 01:57:12 +03:00
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
MGMT_STATUS_INVALID_PARAMS ) ;
hci_dev_lock ( hdev ) ;
2015-03-24 01:57:15 +03:00
if ( timeout & & ! hdev_is_powered ( hdev ) ) {
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
MGMT_STATUS_REJECTED ) ;
goto unlock ;
}
2015-03-24 01:57:12 +03:00
if ( pending_find ( MGMT_OP_ADD_ADVERTISING , hdev ) | |
2015-03-24 01:57:13 +03:00
pending_find ( MGMT_OP_REMOVE_ADVERTISING , hdev ) | |
2015-03-24 01:57:12 +03:00
pending_find ( MGMT_OP_SET_LE , hdev ) ) {
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
MGMT_STATUS_BUSY ) ;
goto unlock ;
}
2016-09-18 13:50:04 +03:00
if ( ! tlv_data_is_valid ( flags , cp - > data , cp - > adv_data_len , true ) | |
! tlv_data_is_valid ( flags , cp - > data + cp - > adv_data_len ,
2015-03-26 04:53:41 +03:00
cp - > scan_rsp_len , false ) ) {
2015-03-24 01:57:12 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
MGMT_STATUS_INVALID_PARAMS ) ;
goto unlock ;
}
2015-06-18 04:16:47 +03:00
err = hci_add_adv_instance ( hdev , cp - > instance , flags ,
cp - > adv_data_len , cp - > data ,
cp - > scan_rsp_len ,
cp - > data + cp - > adv_data_len ,
timeout , duration ) ;
if ( err < 0 ) {
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
MGMT_STATUS_FAILED ) ;
goto unlock ;
}
2015-03-24 01:57:12 +03:00
2015-06-18 04:16:47 +03:00
/* Only trigger an advertising added event if a new instance was
* actually added .
*/
if ( hdev - > adv_instance_cnt > prev_instance_cnt )
2015-11-18 13:49:20 +03:00
mgmt_advertising_added ( sk , hdev , cp - > instance ) ;
2015-03-24 01:57:15 +03:00
2015-06-18 04:16:47 +03:00
if ( hdev - > cur_adv_instance = = cp - > instance ) {
/* If the currently advertised instance is being changed then
* cancel the current advertising and schedule the next
* instance . If there is only one instance then the overridden
* advertising data will be visible right away .
*/
cancel_adv_timeout ( hdev ) ;
2015-03-24 01:57:15 +03:00
2015-06-18 04:16:47 +03:00
next_instance = hci_get_next_instance ( hdev , cp - > instance ) ;
if ( next_instance )
schedule_instance = next_instance - > instance ;
} else if ( ! hdev - > adv_instance_timeout ) {
/* Immediately advertise the new instance if no other
* instance is currently being advertised .
*/
schedule_instance = cp - > instance ;
}
2015-03-24 01:57:12 +03:00
2015-06-18 04:16:47 +03:00
/* If the HCI_ADVERTISING flag is set or the device isn't powered or
* there is no instance to be advertised then we have no HCI
* communication to make . Simply return .
2015-03-24 01:57:12 +03:00
*/
if ( ! hdev_is_powered ( hdev ) | |
2015-06-18 04:16:47 +03:00
hci_dev_test_flag ( hdev , HCI_ADVERTISING ) | |
! schedule_instance ) {
rp . instance = cp - > instance ;
2015-03-24 01:57:12 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_ADVERTISING ,
MGMT_STATUS_SUCCESS , & rp , sizeof ( rp ) ) ;
goto unlock ;
}
/* We're good to go, update advertising data, parameters, and start
* advertising .
*/
cmd = mgmt_pending_add ( sk , MGMT_OP_ADD_ADVERTISING , hdev , data ,
data_len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
hci_req_init ( & req , hdev ) ;
2015-11-18 13:49:20 +03:00
err = __hci_req_schedule_adv_instance ( & req , schedule_instance , true ) ;
2015-06-18 04:16:47 +03:00
if ( ! err )
err = hci_req_run ( & req , add_advertising_complete ) ;
2015-03-24 01:57:12 +03:00
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2015-03-24 01:57:13 +03:00
static void remove_advertising_complete ( struct hci_dev * hdev , u8 status ,
u16 opcode )
{
struct mgmt_pending_cmd * cmd ;
2015-06-18 04:16:48 +03:00
struct mgmt_cp_remove_advertising * cp ;
2015-03-24 01:57:13 +03:00
struct mgmt_rp_remove_advertising rp ;
BT_DBG ( " status %d " , status ) ;
hci_dev_lock ( hdev ) ;
/* A failure status here only means that we failed to disable
* advertising . Otherwise , the advertising instance has been removed ,
* so report success .
*/
cmd = pending_find ( MGMT_OP_REMOVE_ADVERTISING , hdev ) ;
if ( ! cmd )
goto unlock ;
2015-06-18 04:16:48 +03:00
cp = cmd - > param ;
rp . instance = cp - > instance ;
2015-03-24 01:57:13 +03:00
mgmt_cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode , MGMT_STATUS_SUCCESS ,
& rp , sizeof ( rp ) ) ;
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
}
static int remove_advertising ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
struct mgmt_cp_remove_advertising * cp = data ;
struct mgmt_rp_remove_advertising rp ;
struct mgmt_pending_cmd * cmd ;
struct hci_request req ;
2015-06-18 21:05:31 +03:00
int err ;
2015-03-24 01:57:13 +03:00
BT_DBG ( " %s " , hdev - > name ) ;
hci_dev_lock ( hdev ) ;
2015-06-18 21:05:31 +03:00
if ( cp - > instance & & ! hci_find_adv_instance ( hdev , cp - > instance ) ) {
2015-06-18 04:16:48 +03:00
err = mgmt_cmd_status ( sk , hdev - > id ,
MGMT_OP_REMOVE_ADVERTISING ,
MGMT_STATUS_INVALID_PARAMS ) ;
goto unlock ;
}
2015-03-24 01:57:13 +03:00
if ( pending_find ( MGMT_OP_ADD_ADVERTISING , hdev ) | |
pending_find ( MGMT_OP_REMOVE_ADVERTISING , hdev ) | |
pending_find ( MGMT_OP_SET_LE , hdev ) ) {
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_REMOVE_ADVERTISING ,
MGMT_STATUS_BUSY ) ;
goto unlock ;
}
2015-11-26 13:15:59 +03:00
if ( list_empty ( & hdev - > adv_instances ) ) {
2015-03-24 01:57:13 +03:00
err = mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_REMOVE_ADVERTISING ,
MGMT_STATUS_INVALID_PARAMS ) ;
goto unlock ;
}
2015-06-18 04:16:48 +03:00
hci_req_init ( & req , hdev ) ;
2015-03-24 01:57:13 +03:00
2016-08-28 20:53:34 +03:00
hci_req_clear_adv_instance ( hdev , sk , & req , cp - > instance , true ) ;
2015-03-24 01:57:13 +03:00
2015-06-18 04:16:48 +03:00
if ( list_empty ( & hdev - > adv_instances ) )
2015-11-18 13:49:20 +03:00
__hci_req_disable_advertising ( & req ) ;
2015-03-24 01:57:13 +03:00
2015-06-18 04:16:48 +03:00
/* If no HCI commands have been collected so far or the HCI_ADVERTISING
* flag is set or the device isn ' t powered then we have no HCI
* communication to make . Simply return .
2015-03-24 01:57:13 +03:00
*/
2015-06-18 04:16:48 +03:00
if ( skb_queue_empty ( & req . cmd_q ) | |
! hdev_is_powered ( hdev ) | |
2015-03-24 01:57:13 +03:00
hci_dev_test_flag ( hdev , HCI_ADVERTISING ) ) {
2015-06-18 04:16:48 +03:00
rp . instance = cp - > instance ;
2015-03-24 01:57:13 +03:00
err = mgmt_cmd_complete ( sk , hdev - > id ,
MGMT_OP_REMOVE_ADVERTISING ,
MGMT_STATUS_SUCCESS , & rp , sizeof ( rp ) ) ;
goto unlock ;
}
cmd = mgmt_pending_add ( sk , MGMT_OP_REMOVE_ADVERTISING , hdev , data ,
data_len ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
err = hci_req_run ( & req , remove_advertising_complete ) ;
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
unlock :
hci_dev_unlock ( hdev ) ;
return err ;
}
2015-11-19 18:16:43 +03:00
static int get_adv_size_info ( struct sock * sk , struct hci_dev * hdev ,
void * data , u16 data_len )
{
struct mgmt_cp_get_adv_size_info * cp = data ;
struct mgmt_rp_get_adv_size_info rp ;
u32 flags , supported_flags ;
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
if ( ! lmp_le_capable ( hdev ) )
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_GET_ADV_SIZE_INFO ,
MGMT_STATUS_REJECTED ) ;
if ( cp - > instance < 1 | | cp - > instance > HCI_MAX_ADV_INSTANCES )
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_GET_ADV_SIZE_INFO ,
MGMT_STATUS_INVALID_PARAMS ) ;
flags = __le32_to_cpu ( cp - > flags ) ;
/* The current implementation only supports a subset of the specified
* flags .
*/
supported_flags = get_supported_adv_flags ( hdev ) ;
if ( flags & ~ supported_flags )
return mgmt_cmd_status ( sk , hdev - > id , MGMT_OP_GET_ADV_SIZE_INFO ,
MGMT_STATUS_INVALID_PARAMS ) ;
rp . instance = cp - > instance ;
rp . flags = cp - > flags ;
rp . max_adv_data_len = tlv_data_max_len ( flags , true ) ;
rp . max_scan_rsp_len = tlv_data_max_len ( flags , false ) ;
err = mgmt_cmd_complete ( sk , hdev - > id , MGMT_OP_GET_ADV_SIZE_INFO ,
MGMT_STATUS_SUCCESS , & rp , sizeof ( rp ) ) ;
return err ;
}
2015-03-06 22:08:51 +03:00
static const struct hci_mgmt_handler mgmt_handlers [ ] = {
2012-02-28 19:18:30 +04:00
{ NULL } , /* 0x0000 (no command) */
2015-03-06 22:08:52 +03:00
{ read_version , MGMT_READ_VERSION_SIZE ,
2015-03-15 05:28:01 +03:00
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED } ,
2015-03-06 22:08:52 +03:00
{ read_commands , MGMT_READ_COMMANDS_SIZE ,
2015-03-15 05:28:01 +03:00
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED } ,
2015-03-06 22:08:52 +03:00
{ read_index_list , MGMT_READ_INDEX_LIST_SIZE ,
2015-03-15 05:28:01 +03:00
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED } ,
{ read_controller_info , MGMT_READ_INFO_SIZE ,
HCI_MGMT_UNTRUSTED } ,
2015-03-15 05:28:02 +03:00
{ set_powered , MGMT_SETTING_SIZE } ,
{ set_discoverable , MGMT_SET_DISCOVERABLE_SIZE } ,
{ set_connectable , MGMT_SETTING_SIZE } ,
{ set_fast_connectable , MGMT_SETTING_SIZE } ,
{ set_bondable , MGMT_SETTING_SIZE } ,
{ set_link_security , MGMT_SETTING_SIZE } ,
{ set_ssp , MGMT_SETTING_SIZE } ,
{ set_hs , MGMT_SETTING_SIZE } ,
{ set_le , MGMT_SETTING_SIZE } ,
{ set_dev_class , MGMT_SET_DEV_CLASS_SIZE } ,
{ set_local_name , MGMT_SET_LOCAL_NAME_SIZE } ,
{ add_uuid , MGMT_ADD_UUID_SIZE } ,
{ remove_uuid , MGMT_REMOVE_UUID_SIZE } ,
2015-03-06 22:08:52 +03:00
{ load_link_keys , MGMT_LOAD_LINK_KEYS_SIZE ,
HCI_MGMT_VAR_LEN } ,
{ load_long_term_keys , MGMT_LOAD_LONG_TERM_KEYS_SIZE ,
HCI_MGMT_VAR_LEN } ,
2015-03-15 05:28:02 +03:00
{ disconnect , MGMT_DISCONNECT_SIZE } ,
{ get_connections , MGMT_GET_CONNECTIONS_SIZE } ,
{ pin_code_reply , MGMT_PIN_CODE_REPLY_SIZE } ,
{ pin_code_neg_reply , MGMT_PIN_CODE_NEG_REPLY_SIZE } ,
{ set_io_capability , MGMT_SET_IO_CAPABILITY_SIZE } ,
{ pair_device , MGMT_PAIR_DEVICE_SIZE } ,
{ cancel_pair_device , MGMT_CANCEL_PAIR_DEVICE_SIZE } ,
{ unpair_device , MGMT_UNPAIR_DEVICE_SIZE } ,
{ user_confirm_reply , MGMT_USER_CONFIRM_REPLY_SIZE } ,
{ user_confirm_neg_reply , MGMT_USER_CONFIRM_NEG_REPLY_SIZE } ,
{ user_passkey_reply , MGMT_USER_PASSKEY_REPLY_SIZE } ,
{ user_passkey_neg_reply , MGMT_USER_PASSKEY_NEG_REPLY_SIZE } ,
2015-03-06 22:08:52 +03:00
{ read_local_oob_data , MGMT_READ_LOCAL_OOB_DATA_SIZE } ,
{ add_remote_oob_data , MGMT_ADD_REMOTE_OOB_DATA_SIZE ,
HCI_MGMT_VAR_LEN } ,
2015-03-15 05:28:02 +03:00
{ remove_remote_oob_data , MGMT_REMOVE_REMOTE_OOB_DATA_SIZE } ,
{ start_discovery , MGMT_START_DISCOVERY_SIZE } ,
{ stop_discovery , MGMT_STOP_DISCOVERY_SIZE } ,
{ confirm_name , MGMT_CONFIRM_NAME_SIZE } ,
{ block_device , MGMT_BLOCK_DEVICE_SIZE } ,
{ unblock_device , MGMT_UNBLOCK_DEVICE_SIZE } ,
{ set_device_id , MGMT_SET_DEVICE_ID_SIZE } ,
{ set_advertising , MGMT_SETTING_SIZE } ,
{ set_bredr , MGMT_SETTING_SIZE } ,
{ set_static_address , MGMT_SET_STATIC_ADDRESS_SIZE } ,
{ set_scan_params , MGMT_SET_SCAN_PARAMS_SIZE } ,
{ set_secure_conn , MGMT_SETTING_SIZE } ,
{ set_debug_keys , MGMT_SETTING_SIZE } ,
{ set_privacy , MGMT_SET_PRIVACY_SIZE } ,
2015-03-06 22:08:52 +03:00
{ load_irks , MGMT_LOAD_IRKS_SIZE ,
HCI_MGMT_VAR_LEN } ,
2015-03-15 05:28:02 +03:00
{ get_conn_info , MGMT_GET_CONN_INFO_SIZE } ,
{ get_clock_info , MGMT_GET_CLOCK_INFO_SIZE } ,
{ add_device , MGMT_ADD_DEVICE_SIZE } ,
{ remove_device , MGMT_REMOVE_DEVICE_SIZE } ,
2015-03-06 22:08:52 +03:00
{ load_conn_param , MGMT_LOAD_CONN_PARAM_SIZE ,
HCI_MGMT_VAR_LEN } ,
{ read_unconf_index_list , MGMT_READ_UNCONF_INDEX_LIST_SIZE ,
2015-03-15 05:28:01 +03:00
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED } ,
2015-03-06 22:08:52 +03:00
{ read_config_info , MGMT_READ_CONFIG_INFO_SIZE ,
2015-03-15 05:28:01 +03:00
HCI_MGMT_UNCONFIGURED |
HCI_MGMT_UNTRUSTED } ,
2015-03-06 22:08:52 +03:00
{ set_external_config , MGMT_SET_EXTERNAL_CONFIG_SIZE ,
HCI_MGMT_UNCONFIGURED } ,
{ set_public_address , MGMT_SET_PUBLIC_ADDRESS_SIZE ,
HCI_MGMT_UNCONFIGURED } ,
{ start_service_discovery , MGMT_START_SERVICE_DISCOVERY_SIZE ,
HCI_MGMT_VAR_LEN } ,
2015-03-15 08:43:19 +03:00
{ read_local_oob_ext_data , MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE } ,
2015-03-15 05:27:57 +03:00
{ read_ext_index_list , MGMT_READ_EXT_INDEX_LIST_SIZE ,
2015-03-15 05:28:01 +03:00
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED } ,
2015-03-15 06:53:25 +03:00
{ read_adv_features , MGMT_READ_ADV_FEATURES_SIZE } ,
2015-03-24 01:57:12 +03:00
{ add_advertising , MGMT_ADD_ADVERTISING_SIZE ,
HCI_MGMT_VAR_LEN } ,
2015-03-24 01:57:13 +03:00
{ remove_advertising , MGMT_REMOVE_ADVERTISING_SIZE } ,
2015-11-19 18:16:43 +03:00
{ get_adv_size_info , MGMT_GET_ADV_SIZE_INFO_SIZE } ,
2016-01-05 14:19:32 +03:00
{ start_limited_discovery , MGMT_START_DISCOVERY_SIZE } ,
2016-09-01 17:46:23 +03:00
{ read_ext_controller_info , MGMT_READ_EXT_INFO_SIZE ,
HCI_MGMT_UNTRUSTED } ,
2016-09-18 13:50:03 +03:00
{ set_appearance , MGMT_SET_APPEARANCE_SIZE } ,
2012-02-28 19:18:30 +04:00
} ;
2013-10-07 10:55:45 +04:00
void mgmt_index_added ( struct hci_dev * hdev )
2010-12-13 22:07:07 +03:00
{
2015-03-15 05:27:56 +03:00
struct mgmt_ev_ext_index ev ;
2012-07-19 18:03:40 +04:00
2014-07-02 23:30:54 +04:00
if ( test_bit ( HCI_QUIRK_RAW_DEVICE , & hdev - > quirks ) )
return ;
2015-03-15 05:27:55 +03:00
switch ( hdev - > dev_type ) {
2016-07-05 15:30:14 +03:00
case HCI_PRIMARY :
2015-03-15 05:27:55 +03:00
if ( hci_dev_test_flag ( hdev , HCI_UNCONFIGURED ) ) {
mgmt_index_event ( MGMT_EV_UNCONF_INDEX_ADDED , hdev ,
NULL , 0 , HCI_MGMT_UNCONF_INDEX_EVENTS ) ;
2015-03-15 05:27:56 +03:00
ev . type = 0x01 ;
2015-03-15 05:27:55 +03:00
} else {
mgmt_index_event ( MGMT_EV_INDEX_ADDED , hdev , NULL , 0 ,
HCI_MGMT_INDEX_EVENTS ) ;
2015-03-15 05:27:56 +03:00
ev . type = 0x00 ;
2015-03-15 05:27:55 +03:00
}
break ;
2015-03-15 05:27:56 +03:00
case HCI_AMP :
ev . type = 0x02 ;
break ;
default :
return ;
2015-03-15 05:27:55 +03:00
}
2015-03-15 05:27:56 +03:00
ev . bus = hdev - > bus ;
mgmt_index_event ( MGMT_EV_EXT_INDEX_ADDED , hdev , & ev , sizeof ( ev ) ,
HCI_MGMT_EXT_INDEX_EVENTS ) ;
2010-12-13 22:07:07 +03:00
}
2013-10-07 10:55:45 +04:00
void mgmt_index_removed ( struct hci_dev * hdev )
2010-12-13 22:07:07 +03:00
{
2015-03-15 05:27:56 +03:00
struct mgmt_ev_ext_index ev ;
2012-03-02 05:13:19 +04:00
u8 status = MGMT_STATUS_INVALID_INDEX ;
2011-11-03 16:40:33 +04:00
2014-07-02 23:30:54 +04:00
if ( test_bit ( HCI_QUIRK_RAW_DEVICE , & hdev - > quirks ) )
return ;
2015-03-15 05:27:55 +03:00
switch ( hdev - > dev_type ) {
2016-07-05 15:30:14 +03:00
case HCI_PRIMARY :
2015-03-15 05:27:55 +03:00
mgmt_pending_foreach ( 0 , hdev , cmd_complete_rsp , & status ) ;
2011-11-03 16:40:33 +04:00
2015-03-15 05:27:55 +03:00
if ( hci_dev_test_flag ( hdev , HCI_UNCONFIGURED ) ) {
mgmt_index_event ( MGMT_EV_UNCONF_INDEX_REMOVED , hdev ,
NULL , 0 , HCI_MGMT_UNCONF_INDEX_EVENTS ) ;
2015-03-15 05:27:56 +03:00
ev . type = 0x01 ;
2015-03-15 05:27:55 +03:00
} else {
mgmt_index_event ( MGMT_EV_INDEX_REMOVED , hdev , NULL , 0 ,
HCI_MGMT_INDEX_EVENTS ) ;
2015-03-15 05:27:56 +03:00
ev . type = 0x00 ;
2015-03-15 05:27:55 +03:00
}
break ;
2015-03-15 05:27:56 +03:00
case HCI_AMP :
ev . type = 0x02 ;
break ;
default :
return ;
2015-03-15 05:27:55 +03:00
}
2015-03-15 05:27:56 +03:00
ev . bus = hdev - > bus ;
mgmt_index_event ( MGMT_EV_EXT_INDEX_REMOVED , hdev , & ev , sizeof ( ev ) ,
HCI_MGMT_EXT_INDEX_EVENTS ) ;
2010-12-16 11:17:38 +03:00
}
2014-02-27 03:21:51 +04:00
/* This function requires the caller holds hdev->lock */
2015-11-11 09:11:21 +03:00
static void restart_le_actions ( struct hci_dev * hdev )
2014-02-27 03:21:51 +04:00
{
struct hci_conn_params * p ;
list_for_each_entry ( p , & hdev - > le_conn_params , list ) {
2014-07-04 13:37:23 +04:00
/* Needed for AUTO_OFF case where might not "really"
* have been powered off .
*/
list_del_init ( & p - > action ) ;
switch ( p - > auto_connect ) {
2014-07-23 23:55:23 +04:00
case HCI_AUTO_CONN_DIRECT :
2014-07-04 13:37:23 +04:00
case HCI_AUTO_CONN_ALWAYS :
list_add ( & p - > action , & hdev - > pend_le_conns ) ;
break ;
case HCI_AUTO_CONN_REPORT :
list_add ( & p - > action , & hdev - > pend_le_reports ) ;
break ;
default :
break ;
2014-07-01 21:28:24 +04:00
}
2014-02-27 03:21:51 +04:00
}
}
2015-11-25 17:15:44 +03:00
void mgmt_power_on ( struct hci_dev * hdev , int err )
2013-03-16 02:06:53 +04:00
{
struct cmd_lookup match = { NULL , hdev } ;
2015-11-25 17:15:44 +03:00
BT_DBG ( " err %d " , err ) ;
2013-03-16 02:06:53 +04:00
2015-11-25 17:15:44 +03:00
hci_dev_lock ( hdev ) ;
if ( ! err ) {
2015-11-11 09:11:21 +03:00
restart_le_actions ( hdev ) ;
hci_update_background_scan ( hdev ) ;
2015-01-15 02:43:11 +03:00
}
2013-03-16 02:06:53 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_POWERED , hdev , settings_rsp , & match ) ;
new_settings ( hdev , match . sk ) ;
if ( match . sk )
sock_put ( match . sk ) ;
2015-11-25 17:15:44 +03:00
hci_dev_unlock ( hdev ) ;
2013-03-16 02:06:51 +04:00
}
2012-08-29 12:02:09 +04:00
2015-11-25 17:15:44 +03:00
void __mgmt_power_off ( struct hci_dev * hdev )
2013-03-16 02:06:51 +04:00
{
struct cmd_lookup match = { NULL , hdev } ;
2014-12-12 12:15:21 +03:00
u8 status , zero_cod [ ] = { 0 , 0 , 0 } ;
2011-11-03 16:40:33 +04:00
2013-03-16 02:06:53 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_POWERED , hdev , settings_rsp , & match ) ;
2014-12-12 12:15:21 +03:00
/* If the power off is because of hdev unregistration let
* use the appropriate INVALID_INDEX status . Otherwise use
* NOT_POWERED . We cover both scenarios here since later in
* mgmt_index_removed ( ) any hci_conn callbacks will have already
* been triggered , potentially causing misleading DISCONNECTED
* status responses .
*/
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_UNREGISTER ) )
2014-12-12 12:15:21 +03:00
status = MGMT_STATUS_INVALID_INDEX ;
else
status = MGMT_STATUS_NOT_POWERED ;
mgmt_pending_foreach ( 0 , hdev , cmd_complete_rsp , & status ) ;
2013-03-16 02:06:53 +04:00
2016-09-01 17:46:23 +03:00
if ( memcmp ( hdev - > dev_class , zero_cod , sizeof ( zero_cod ) ) ! = 0 ) {
2016-08-29 07:19:46 +03:00
mgmt_limited_event ( MGMT_EV_CLASS_OF_DEV_CHANGED , hdev ,
zero_cod , sizeof ( zero_cod ) ,
HCI_MGMT_DEV_CLASS_EVENTS , NULL ) ;
2016-09-01 17:46:23 +03:00
ext_info_changed ( hdev , NULL ) ;
}
2013-03-16 02:06:53 +04:00
2015-11-25 17:15:44 +03:00
new_settings ( hdev , match . sk ) ;
2010-12-16 11:17:38 +03:00
if ( match . sk )
sock_put ( match . sk ) ;
2010-12-16 11:00:37 +03:00
}
2010-12-29 17:00:25 +03:00
2013-10-07 10:55:46 +04:00
void mgmt_set_powered_failed ( struct hci_dev * hdev , int err )
2013-05-29 10:51:29 +04:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2013-05-29 10:51:29 +04:00
u8 status ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_POWERED , hdev ) ;
2013-05-29 10:51:29 +04:00
if ( ! cmd )
2013-10-07 10:55:46 +04:00
return ;
2013-05-29 10:51:29 +04:00
if ( err = = - ERFKILL )
status = MGMT_STATUS_RFKILLED ;
else
status = MGMT_STATUS_FAILED ;
2015-03-06 22:08:53 +03:00
mgmt_cmd_status ( cmd - > sk , hdev - > id , MGMT_OP_SET_POWERED , status ) ;
2013-05-29 10:51:29 +04:00
mgmt_pending_remove ( cmd ) ;
}
2013-10-15 21:15:57 +04:00
void mgmt_new_link_key ( struct hci_dev * hdev , struct link_key * key ,
bool persistent )
2011-01-17 15:41:05 +03:00
{
2011-11-08 01:13:38 +04:00
struct mgmt_ev_new_link_key ev ;
2011-01-17 15:41:05 +03:00
2011-08-26 03:02:29 +04:00
memset ( & ev , 0 , sizeof ( ev ) ) ;
2011-01-17 15:41:05 +03:00
2011-08-26 03:02:29 +04:00
ev . store_hint = persistent ;
2012-02-17 16:06:34 +04:00
bacpy ( & ev . key . addr . bdaddr , & key - > bdaddr ) ;
2012-04-25 04:02:49 +04:00
ev . key . addr . type = BDADDR_BREDR ;
2011-08-26 03:02:29 +04:00
ev . key . type = key - > type ;
2012-05-23 12:31:20 +04:00
memcpy ( ev . key . val , key - > val , HCI_LINK_KEY_SIZE ) ;
2011-08-26 03:02:29 +04:00
ev . key . pin_len = key - > pin_len ;
2011-01-17 15:41:05 +03:00
2013-10-15 21:15:57 +04:00
mgmt_event ( MGMT_EV_NEW_LINK_KEY , hdev , & ev , sizeof ( ev ) , NULL ) ;
2011-01-17 15:41:05 +03:00
}
2011-01-20 13:34:39 +03:00
2014-05-23 14:19:53 +04:00
static u8 mgmt_ltk_type ( struct smp_ltk * ltk )
{
2014-05-23 14:15:37 +04:00
switch ( ltk - > type ) {
case SMP_LTK :
case SMP_LTK_SLAVE :
if ( ltk - > authenticated )
return MGMT_LTK_AUTHENTICATED ;
return MGMT_LTK_UNAUTHENTICATED ;
case SMP_LTK_P256 :
if ( ltk - > authenticated )
return MGMT_LTK_P256_AUTH ;
return MGMT_LTK_P256_UNAUTH ;
case SMP_LTK_P256_DEBUG :
return MGMT_LTK_P256_DEBUG ;
}
2014-05-23 14:19:53 +04:00
return MGMT_LTK_UNAUTHENTICATED ;
}
2014-03-10 10:38:42 +04:00
void mgmt_new_ltk ( struct hci_dev * hdev , struct smp_ltk * key , bool persistent )
2012-02-03 04:08:02 +04:00
{
struct mgmt_ev_new_long_term_key ev ;
memset ( & ev , 0 , sizeof ( ev ) ) ;
2014-02-20 05:11:58 +04:00
/* Devices using resolvable or non-resolvable random addresses
2015-05-26 04:31:09 +03:00
* without providing an identity resolving key don ' t require
2014-02-20 05:11:58 +04:00
* to store long term keys . Their addresses will change the
* next time around .
*
* Only when a remote device provides an identity address
* make sure the long term key is stored . If the remote
* identity is known , the long term keys are internally
* mapped to the identity address . So allow static random
* and public addresses here .
*/
2014-02-19 16:57:45 +04:00
if ( key - > bdaddr_type = = ADDR_LE_DEV_RANDOM & &
( key - > bdaddr . b [ 5 ] & 0xc0 ) ! = 0xc0 )
ev . store_hint = 0x00 ;
else
2014-03-10 10:38:42 +04:00
ev . store_hint = persistent ;
2014-02-19 16:57:45 +04:00
2012-02-03 04:08:02 +04:00
bacpy ( & ev . key . addr . bdaddr , & key - > bdaddr ) ;
2012-04-25 04:02:50 +04:00
ev . key . addr . type = link_to_bdaddr ( LE_LINK , key - > bdaddr_type ) ;
2014-05-23 14:19:53 +04:00
ev . key . type = mgmt_ltk_type ( key ) ;
2012-02-03 04:08:02 +04:00
ev . key . enc_size = key - > enc_size ;
ev . key . ediv = key - > ediv ;
2014-02-28 04:00:28 +04:00
ev . key . rand = key - > rand ;
2012-02-03 04:08:02 +04:00
2014-06-16 20:25:16 +04:00
if ( key - > type = = SMP_LTK )
2012-02-03 04:08:02 +04:00
ev . key . master = 1 ;
2015-06-10 11:11:20 +03:00
/* Make sure we copy only the significant bytes based on the
* encryption key size , and set the rest of the value to zeroes .
*/
2015-08-06 00:16:29 +03:00
memcpy ( ev . key . val , key - > val , key - > enc_size ) ;
2015-06-10 11:11:20 +03:00
memset ( ev . key . val + key - > enc_size , 0 ,
sizeof ( ev . key . val ) - key - > enc_size ) ;
2012-02-03 04:08:02 +04:00
2013-10-16 01:26:29 +04:00
mgmt_event ( MGMT_EV_NEW_LONG_TERM_KEY , hdev , & ev , sizeof ( ev ) , NULL ) ;
2012-02-03 04:08:02 +04:00
}
2015-10-12 14:36:19 +03:00
void mgmt_new_irk ( struct hci_dev * hdev , struct smp_irk * irk , bool persistent )
2014-02-19 17:18:31 +04:00
{
struct mgmt_ev_new_irk ev ;
memset ( & ev , 0 , sizeof ( ev ) ) ;
2015-10-12 14:36:19 +03:00
ev . store_hint = persistent ;
2014-02-19 23:51:54 +04:00
2014-02-19 17:18:31 +04:00
bacpy ( & ev . rpa , & irk - > rpa ) ;
bacpy ( & ev . irk . addr . bdaddr , & irk - > bdaddr ) ;
ev . irk . addr . type = link_to_bdaddr ( LE_LINK , irk - > addr_type ) ;
memcpy ( ev . irk . val , irk - > val , sizeof ( irk - > val ) ) ;
mgmt_event ( MGMT_EV_NEW_IRK , hdev , & ev , sizeof ( ev ) , NULL ) ;
}
2014-03-10 10:38:42 +04:00
void mgmt_new_csrk ( struct hci_dev * hdev , struct smp_csrk * csrk ,
bool persistent )
2014-03-09 23:19:17 +04:00
{
struct mgmt_ev_new_csrk ev ;
memset ( & ev , 0 , sizeof ( ev ) ) ;
/* Devices using resolvable or non-resolvable random addresses
2015-05-26 04:31:09 +03:00
* without providing an identity resolving key don ' t require
2014-03-09 23:19:17 +04:00
* to store signature resolving keys . Their addresses will change
* the next time around .
*
* Only when a remote device provides an identity address
* make sure the signature resolving key is stored . So allow
* static random and public addresses here .
*/
if ( csrk - > bdaddr_type = = ADDR_LE_DEV_RANDOM & &
( csrk - > bdaddr . b [ 5 ] & 0xc0 ) ! = 0xc0 )
ev . store_hint = 0x00 ;
else
2014-03-10 10:38:42 +04:00
ev . store_hint = persistent ;
2014-03-09 23:19:17 +04:00
bacpy ( & ev . key . addr . bdaddr , & csrk - > bdaddr ) ;
ev . key . addr . type = link_to_bdaddr ( LE_LINK , csrk - > bdaddr_type ) ;
2015-02-27 11:11:13 +03:00
ev . key . type = csrk - > type ;
2014-03-09 23:19:17 +04:00
memcpy ( ev . key . val , csrk - > val , sizeof ( csrk - > val ) ) ;
mgmt_event ( MGMT_EV_NEW_CSRK , hdev , & ev , sizeof ( ev ) , NULL ) ;
}
2014-07-02 01:10:11 +04:00
void mgmt_new_conn_param ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2014-07-02 18:37:32 +04:00
u8 bdaddr_type , u8 store_hint , u16 min_interval ,
u16 max_interval , u16 latency , u16 timeout )
2014-07-02 01:10:11 +04:00
{
struct mgmt_ev_new_conn_param ev ;
2014-07-02 18:37:34 +04:00
if ( ! hci_is_identity_address ( bdaddr , bdaddr_type ) )
return ;
2014-07-02 01:10:11 +04:00
memset ( & ev , 0 , sizeof ( ev ) ) ;
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = link_to_bdaddr ( LE_LINK , bdaddr_type ) ;
2014-07-02 18:37:32 +04:00
ev . store_hint = store_hint ;
2014-07-02 01:10:11 +04:00
ev . min_interval = cpu_to_le16 ( min_interval ) ;
ev . max_interval = cpu_to_le16 ( max_interval ) ;
ev . latency = cpu_to_le16 ( latency ) ;
ev . timeout = cpu_to_le16 ( timeout ) ;
mgmt_event ( MGMT_EV_NEW_CONN_PARAM , hdev , & ev , sizeof ( ev ) , NULL ) ;
}
2014-10-07 12:44:10 +04:00
void mgmt_device_connected ( struct hci_dev * hdev , struct hci_conn * conn ,
u32 flags , u8 * name , u8 name_len )
2011-01-20 13:34:39 +03:00
{
2012-01-17 23:48:47 +04:00
char buf [ 512 ] ;
struct mgmt_ev_device_connected * ev = ( void * ) buf ;
u16 eir_len = 0 ;
2011-01-20 13:34:39 +03:00
2014-10-07 12:44:10 +04:00
bacpy ( & ev - > addr . bdaddr , & conn - > dst ) ;
ev - > addr . type = link_to_bdaddr ( conn - > type , conn - > dst_type ) ;
2011-01-20 13:34:39 +03:00
2012-02-24 00:54:38 +04:00
ev - > flags = __cpu_to_le32 ( flags ) ;
2012-02-24 00:31:51 +04:00
2014-10-07 12:44:11 +04:00
/* We must ensure that the EIR Data fields are ordered and
* unique . Keep it simple for now and avoid the problem by not
* adding any BR / EDR data to the LE adv .
*/
if ( conn - > le_adv_data_len > 0 ) {
memcpy ( & ev - > eir [ eir_len ] ,
conn - > le_adv_data , conn - > le_adv_data_len ) ;
eir_len = conn - > le_adv_data_len ;
} else {
if ( name_len > 0 )
eir_len = eir_append_data ( ev - > eir , 0 , EIR_NAME_COMPLETE ,
name , name_len ) ;
2012-01-17 23:48:47 +04:00
2014-10-07 12:44:12 +04:00
if ( memcmp ( conn - > dev_class , " \0 \0 \0 " , 3 ) ! = 0 )
2014-10-07 12:44:11 +04:00
eir_len = eir_append_data ( ev - > eir , eir_len ,
EIR_CLASS_OF_DEV ,
conn - > dev_class , 3 ) ;
}
2012-01-17 23:48:47 +04:00
2012-03-14 20:08:46 +04:00
ev - > eir_len = cpu_to_le16 ( eir_len ) ;
2012-01-17 23:48:47 +04:00
2013-10-07 10:55:49 +04:00
mgmt_event ( MGMT_EV_DEVICE_CONNECTED , hdev , buf ,
sizeof ( * ev ) + eir_len , NULL ) ;
2011-01-20 13:34:39 +03:00
}
2015-03-06 22:08:55 +03:00
static void disconnect_rsp ( struct mgmt_pending_cmd * cmd , void * data )
2011-01-20 13:40:27 +03:00
{
struct sock * * sk = data ;
2014-12-05 14:36:02 +03:00
cmd - > cmd_complete ( cmd , 0 ) ;
2011-01-20 13:40:27 +03:00
* sk = cmd - > sk ;
sock_hold ( * sk ) ;
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-20 13:40:27 +03:00
}
2015-03-06 22:08:55 +03:00
static void unpair_device_rsp ( struct mgmt_pending_cmd * cmd , void * data )
2011-11-10 17:54:38 +04:00
{
2012-02-09 19:21:16 +04:00
struct hci_dev * hdev = data ;
2012-02-09 15:50:12 +04:00
struct mgmt_cp_unpair_device * cp = cmd - > param ;
2011-11-10 17:54:38 +04:00
2012-02-09 19:21:16 +04:00
device_unpaired ( hdev , & cp - > addr . bdaddr , cp - > addr . type , cmd - > sk ) ;
2014-12-05 14:36:05 +03:00
cmd - > cmd_complete ( cmd , 0 ) ;
2011-11-10 17:54:38 +04:00
mgmt_pending_remove ( cmd ) ;
}
2014-08-01 12:13:30 +04:00
bool mgmt_powering_down ( struct hci_dev * hdev )
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-08-01 12:13:30 +04:00
struct mgmt_mode * cp ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_POWERED , hdev ) ;
2014-08-01 12:13:30 +04:00
if ( ! cmd )
return false ;
cp = cmd - > param ;
if ( ! cp - > val )
return true ;
return false ;
}
2013-10-07 10:55:50 +04:00
void mgmt_device_disconnected ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2014-02-24 16:52:18 +04:00
u8 link_type , u8 addr_type , u8 reason ,
bool mgmt_connected )
2011-01-20 13:34:39 +03:00
{
2012-08-09 11:52:30 +04:00
struct mgmt_ev_device_disconnected ev ;
2011-01-20 13:40:27 +03:00
struct sock * sk = NULL ;
2014-08-01 12:13:30 +04:00
/* The connection is still in hci_conn_hash so test for 1
* instead of 0 to know if this is the last one .
*/
if ( mgmt_powering_down ( hdev ) & & hci_conn_count ( hdev ) = = 1 ) {
cancel_delayed_work ( & hdev - > power_off ) ;
queue_work ( hdev - > req_workqueue , & hdev - > power_off . work ) ;
2014-02-24 16:52:22 +04:00
}
2014-02-24 16:52:18 +04:00
if ( ! mgmt_connected )
return ;
2013-10-31 02:01:41 +04:00
if ( link_type ! = ACL_LINK & & link_type ! = LE_LINK )
return ;
2011-11-08 22:40:14 +04:00
mgmt_pending_foreach ( MGMT_OP_DISCONNECT , hdev , disconnect_rsp , & sk ) ;
2011-01-20 13:34:39 +03:00
2012-08-09 11:52:30 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = link_to_bdaddr ( link_type , addr_type ) ;
ev . reason = reason ;
2011-01-20 13:34:39 +03:00
2013-10-07 10:55:50 +04:00
mgmt_event ( MGMT_EV_DEVICE_DISCONNECTED , hdev , & ev , sizeof ( ev ) , sk ) ;
2011-01-20 13:40:27 +03:00
if ( sk )
2012-03-16 19:02:56 +04:00
sock_put ( sk ) ;
2011-01-20 13:40:27 +03:00
2012-02-09 15:50:12 +04:00
mgmt_pending_foreach ( MGMT_OP_UNPAIR_DEVICE , hdev , unpair_device_rsp ,
2012-03-08 08:25:00 +04:00
hdev ) ;
2011-01-20 13:40:27 +03:00
}
2013-10-07 10:55:47 +04:00
void mgmt_disconnect_failed ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
u8 link_type , u8 addr_type , u8 status )
2011-01-20 13:40:27 +03:00
{
2013-10-31 02:01:40 +04:00
u8 bdaddr_type = link_to_bdaddr ( link_type , addr_type ) ;
struct mgmt_cp_disconnect * cp ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-01-20 13:40:27 +03:00
2012-09-18 21:36:54 +04:00
mgmt_pending_foreach ( MGMT_OP_UNPAIR_DEVICE , hdev , unpair_device_rsp ,
hdev ) ;
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_DISCONNECT , hdev ) ;
2011-01-20 13:40:27 +03:00
if ( ! cmd )
2013-10-07 10:55:47 +04:00
return ;
2011-01-20 13:40:27 +03:00
2013-10-31 02:01:40 +04:00
cp = cmd - > param ;
if ( bacmp ( bdaddr , & cp - > addr . bdaddr ) )
return ;
if ( cp - > addr . type ! = bdaddr_type )
return ;
2014-12-05 14:36:02 +03:00
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-20 13:34:39 +03:00
}
2011-01-22 07:09:08 +03:00
2013-10-07 10:55:48 +04:00
void mgmt_connect_failed ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 link_type ,
u8 addr_type , u8 status )
2011-01-22 07:09:08 +03:00
{
struct mgmt_ev_connect_failed ev ;
2014-02-27 16:35:12 +04:00
2014-08-01 12:13:30 +04:00
/* The connection is still in hci_conn_hash so test for 1
* instead of 0 to know if this is the last one .
*/
if ( mgmt_powering_down ( hdev ) & & hci_conn_count ( hdev ) = = 1 ) {
cancel_delayed_work ( & hdev - > power_off ) ;
queue_work ( hdev - > req_workqueue , & hdev - > power_off . work ) ;
2014-02-27 16:35:12 +04:00
}
2011-01-22 07:09:08 +03:00
2011-11-08 01:13:39 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
2012-04-25 04:02:50 +04:00
ev . addr . type = link_to_bdaddr ( link_type , addr_type ) ;
2011-11-11 20:10:00 +04:00
ev . status = mgmt_status ( status ) ;
2011-01-22 07:09:08 +03:00
2013-10-07 10:55:48 +04:00
mgmt_event ( MGMT_EV_CONNECT_FAILED , hdev , & ev , sizeof ( ev ) , NULL ) ;
2011-01-22 07:09:08 +03:00
}
2011-01-22 07:10:07 +03:00
2013-10-16 01:26:20 +04:00
void mgmt_pin_code_request ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 secure )
2011-01-22 07:10:07 +03:00
{
struct mgmt_ev_pin_code_request ev ;
2012-02-17 16:24:57 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
2012-04-25 04:02:49 +04:00
ev . addr . type = BDADDR_BREDR ;
2011-04-28 14:07:59 +04:00
ev . secure = secure ;
2011-01-22 07:10:07 +03:00
2013-10-16 01:26:20 +04:00
mgmt_event ( MGMT_EV_PIN_CODE_REQUEST , hdev , & ev , sizeof ( ev ) , NULL ) ;
2011-01-22 07:10:07 +03:00
}
2013-10-16 01:26:21 +04:00
void mgmt_pin_code_reply_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
u8 status )
2011-01-22 07:10:07 +03:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-01-22 07:10:07 +03:00
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_PIN_CODE_REPLY , hdev ) ;
2011-01-22 07:10:07 +03:00
if ( ! cmd )
2013-10-16 01:26:21 +04:00
return ;
2011-01-22 07:10:07 +03:00
2014-12-05 14:36:03 +03:00
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-22 07:10:07 +03:00
}
2013-10-16 01:26:22 +04:00
void mgmt_pin_code_neg_reply_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
u8 status )
2011-01-22 07:10:07 +03:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-01-22 07:10:07 +03:00
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_PIN_CODE_NEG_REPLY , hdev ) ;
2011-01-22 07:10:07 +03:00
if ( ! cmd )
2013-10-16 01:26:22 +04:00
return ;
2011-01-22 07:10:07 +03:00
2014-12-05 14:36:03 +03:00
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-22 07:10:07 +03:00
}
2011-02-19 18:05:57 +03:00
2011-11-08 22:40:14 +04:00
int mgmt_user_confirm_request ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2014-03-20 10:18:14 +04:00
u8 link_type , u8 addr_type , u32 value ,
2012-03-08 08:25:00 +04:00
u8 confirm_hint )
2011-02-19 18:05:57 +03:00
{
struct mgmt_ev_user_confirm_request ev ;
2011-11-08 22:40:14 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-02-19 18:05:57 +03:00
2012-02-09 17:26:12 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
2012-04-25 04:02:50 +04:00
ev . addr . type = link_to_bdaddr ( link_type , addr_type ) ;
2011-04-28 22:28:56 +04:00
ev . confirm_hint = confirm_hint ;
2014-03-20 10:18:14 +04:00
ev . value = cpu_to_le32 ( value ) ;
2011-02-19 18:05:57 +03:00
2011-11-08 22:40:14 +04:00
return mgmt_event ( MGMT_EV_USER_CONFIRM_REQUEST , hdev , & ev , sizeof ( ev ) ,
2012-03-08 08:25:00 +04:00
NULL ) ;
2011-02-19 18:05:57 +03:00
}
2012-02-09 17:26:12 +04:00
int mgmt_user_passkey_request ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-05-17 07:36:20 +04:00
u8 link_type , u8 addr_type )
2011-11-23 20:28:33 +04:00
{
struct mgmt_ev_user_passkey_request ev ;
BT_DBG ( " %s " , hdev - > name ) ;
2012-02-09 17:26:12 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
2012-04-25 04:02:50 +04:00
ev . addr . type = link_to_bdaddr ( link_type , addr_type ) ;
2011-11-23 20:28:33 +04:00
return mgmt_event ( MGMT_EV_USER_PASSKEY_REQUEST , hdev , & ev , sizeof ( ev ) ,
2012-03-08 08:25:00 +04:00
NULL ) ;
2011-11-23 20:28:33 +04:00
}
2011-11-17 01:53:13 +04:00
static int user_pairing_resp_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-05-17 07:36:20 +04:00
u8 link_type , u8 addr_type , u8 status ,
u8 opcode )
2011-02-19 18:05:57 +03:00
{
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2011-02-19 18:05:57 +03:00
2015-03-17 14:48:47 +03:00
cmd = pending_find ( opcode , hdev ) ;
2011-02-19 18:05:57 +03:00
if ( ! cmd )
return - ENOENT ;
2014-12-05 14:36:03 +03:00
cmd - > cmd_complete ( cmd , mgmt_status ( status ) ) ;
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-02-19 18:05:57 +03:00
2014-12-05 14:36:03 +03:00
return 0 ;
2011-02-19 18:05:57 +03:00
}
2011-11-08 22:40:14 +04:00
int mgmt_user_confirm_reply_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 link_type , u8 addr_type , u8 status )
2011-02-19 18:05:57 +03:00
{
2012-02-09 17:26:12 +04:00
return user_pairing_resp_complete ( hdev , bdaddr , link_type , addr_type ,
2012-03-08 08:25:00 +04:00
status , MGMT_OP_USER_CONFIRM_REPLY ) ;
2011-02-19 18:05:57 +03:00
}
2012-02-09 17:26:12 +04:00
int mgmt_user_confirm_neg_reply_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 link_type , u8 addr_type , u8 status )
2011-02-19 18:05:57 +03:00
{
2012-02-09 17:26:12 +04:00
return user_pairing_resp_complete ( hdev , bdaddr , link_type , addr_type ,
2012-05-23 11:04:21 +04:00
status ,
MGMT_OP_USER_CONFIRM_NEG_REPLY ) ;
2011-02-19 18:05:57 +03:00
}
2011-02-19 18:06:00 +03:00
2011-11-23 20:28:33 +04:00
int mgmt_user_passkey_reply_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 link_type , u8 addr_type , u8 status )
2011-11-23 20:28:33 +04:00
{
2012-02-09 17:26:12 +04:00
return user_pairing_resp_complete ( hdev , bdaddr , link_type , addr_type ,
2012-03-08 08:25:00 +04:00
status , MGMT_OP_USER_PASSKEY_REPLY ) ;
2011-11-23 20:28:33 +04:00
}
2012-02-09 17:26:12 +04:00
int mgmt_user_passkey_neg_reply_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 link_type , u8 addr_type , u8 status )
2011-11-23 20:28:33 +04:00
{
2012-02-09 17:26:12 +04:00
return user_pairing_resp_complete ( hdev , bdaddr , link_type , addr_type ,
2012-05-23 11:04:21 +04:00
status ,
MGMT_OP_USER_PASSKEY_NEG_REPLY ) ;
2011-11-23 20:28:33 +04:00
}
2012-09-06 19:39:26 +04:00
int mgmt_user_passkey_notify ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
u8 link_type , u8 addr_type , u32 passkey ,
u8 entered )
{
struct mgmt_ev_passkey_notify ev ;
BT_DBG ( " %s " , hdev - > name ) ;
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = link_to_bdaddr ( link_type , addr_type ) ;
ev . passkey = __cpu_to_le32 ( passkey ) ;
ev . entered = entered ;
return mgmt_event ( MGMT_EV_PASSKEY_NOTIFY , hdev , & ev , sizeof ( ev ) , NULL ) ;
}
2014-09-09 04:09:49 +04:00
void mgmt_auth_failed ( struct hci_conn * conn , u8 hci_status )
2011-02-19 18:06:00 +03:00
{
struct mgmt_ev_auth_failed ev ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2014-09-09 04:09:49 +04:00
u8 status = mgmt_status ( hci_status ) ;
2011-02-19 18:06:00 +03:00
2014-09-09 04:09:49 +04:00
bacpy ( & ev . addr . bdaddr , & conn - > dst ) ;
ev . addr . type = link_to_bdaddr ( conn - > type , conn - > dst_type ) ;
ev . status = status ;
2011-02-19 18:06:00 +03:00
2014-09-09 04:09:49 +04:00
cmd = find_pairing ( conn ) ;
mgmt_event ( MGMT_EV_AUTH_FAILED , conn - > hdev , & ev , sizeof ( ev ) ,
cmd ? cmd - > sk : NULL ) ;
2014-12-11 22:45:45 +03:00
if ( cmd ) {
cmd - > cmd_complete ( cmd , status ) ;
mgmt_pending_remove ( cmd ) ;
}
2011-02-19 18:06:00 +03:00
}
2011-03-16 15:29:37 +03:00
2013-10-16 01:26:24 +04:00
void mgmt_auth_enable_complete ( struct hci_dev * hdev , u8 status )
2012-02-17 01:56:27 +04:00
{
struct cmd_lookup match = { NULL , hdev } ;
2013-10-16 01:26:24 +04:00
bool changed ;
2012-02-17 01:56:27 +04:00
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
mgmt_pending_foreach ( MGMT_OP_SET_LINK_SECURITY , hdev ,
2012-03-08 08:25:00 +04:00
cmd_status_rsp , & mgmt_err ) ;
2013-10-16 01:26:24 +04:00
return ;
2012-02-17 01:56:27 +04:00
}
2013-10-16 01:26:24 +04:00
if ( test_bit ( HCI_AUTH , & hdev - > flags ) )
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_LINK_SECURITY ) ;
2013-10-16 01:26:24 +04:00
else
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev , HCI_LINK_SECURITY ) ;
2012-02-22 13:58:37 +04:00
2012-02-17 01:56:27 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_LINK_SECURITY , hdev , settings_rsp ,
2012-03-08 08:25:00 +04:00
& match ) ;
2012-02-17 01:56:27 +04:00
2012-02-22 13:58:37 +04:00
if ( changed )
2013-10-16 01:26:24 +04:00
new_settings ( hdev , match . sk ) ;
2012-02-17 01:56:27 +04:00
if ( match . sk )
sock_put ( match . sk ) ;
}
2013-03-16 02:06:52 +04:00
static void clear_eir ( struct hci_request * req )
2012-02-21 02:52:42 +04:00
{
2013-03-16 02:06:52 +04:00
struct hci_dev * hdev = req - > hdev ;
2012-02-21 02:52:42 +04:00
struct hci_cp_write_eir cp ;
2012-10-24 22:12:01 +04:00
if ( ! lmp_ext_inq_capable ( hdev ) )
2013-03-16 02:06:52 +04:00
return ;
2012-02-21 02:52:42 +04:00
2012-02-22 17:38:48 +04:00
memset ( hdev - > eir , 0 , sizeof ( hdev - > eir ) ) ;
2012-02-21 02:52:42 +04:00
memset ( & cp , 0 , sizeof ( cp ) ) ;
2013-03-16 02:06:52 +04:00
hci_req_add ( req , HCI_OP_WRITE_EIR , sizeof ( cp ) , & cp ) ;
2012-02-21 02:52:42 +04:00
}
2013-10-16 01:26:25 +04:00
void mgmt_ssp_enable_complete ( struct hci_dev * hdev , u8 enable , u8 status )
2012-02-17 02:56:28 +04:00
{
struct cmd_lookup match = { NULL , hdev } ;
2013-03-16 02:06:52 +04:00
struct hci_request req ;
2012-02-22 14:38:31 +04:00
bool changed = false ;
2012-02-17 02:56:28 +04:00
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
2012-02-22 14:38:31 +04:00
2015-03-13 12:11:05 +03:00
if ( enable & & hci_dev_test_and_clear_flag ( hdev ,
HCI_SSP_ENABLED ) ) {
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_HS_ENABLED ) ;
2013-10-16 01:26:25 +04:00
new_settings ( hdev , NULL ) ;
2013-10-10 14:08:11 +04:00
}
2012-02-22 14:38:31 +04:00
2012-03-08 08:25:00 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_SSP , hdev , cmd_status_rsp ,
& mgmt_err ) ;
2013-10-16 01:26:25 +04:00
return ;
2012-02-22 14:38:31 +04:00
}
if ( enable ) {
2015-03-13 12:11:06 +03:00
changed = ! hci_dev_test_and_set_flag ( hdev , HCI_SSP_ENABLED ) ;
2012-02-22 14:38:31 +04:00
} else {
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev , HCI_SSP_ENABLED ) ;
2013-10-10 14:08:11 +04:00
if ( ! changed )
2015-03-13 12:11:05 +03:00
changed = hci_dev_test_and_clear_flag ( hdev ,
HCI_HS_ENABLED ) ;
2013-10-10 14:08:11 +04:00
else
2015-03-13 12:11:02 +03:00
hci_dev_clear_flag ( hdev , HCI_HS_ENABLED ) ;
2012-02-17 02:56:28 +04:00
}
mgmt_pending_foreach ( MGMT_OP_SET_SSP , hdev , settings_rsp , & match ) ;
2012-02-22 14:38:31 +04:00
if ( changed )
2013-10-16 01:26:25 +04:00
new_settings ( hdev , match . sk ) ;
2012-02-17 02:56:28 +04:00
2012-02-22 17:10:59 +04:00
if ( match . sk )
2012-02-17 02:56:28 +04:00
sock_put ( match . sk ) ;
2013-03-16 02:06:52 +04:00
hci_req_init ( & req , hdev ) ;
2015-03-13 12:11:00 +03:00
if ( hci_dev_test_flag ( hdev , HCI_SSP_ENABLED ) ) {
if ( hci_dev_test_flag ( hdev , HCI_USE_DEBUG_KEYS ) )
2014-06-24 15:00:27 +04:00
hci_req_add ( & req , HCI_OP_WRITE_SSP_DEBUG_MODE ,
sizeof ( enable ) , & enable ) ;
2015-11-25 17:15:42 +03:00
__hci_req_update_eir ( & req ) ;
2014-06-24 15:00:27 +04:00
} else {
2013-03-16 02:06:52 +04:00
clear_eir ( & req ) ;
2014-06-24 15:00:27 +04:00
}
2013-03-16 02:06:52 +04:00
hci_req_run ( & req , NULL ) ;
2012-02-17 02:56:28 +04:00
}
2015-03-06 22:08:55 +03:00
static void sk_lookup ( struct mgmt_pending_cmd * cmd , void * data )
2012-02-24 01:09:40 +04:00
{
struct cmd_lookup * match = data ;
if ( match - > sk = = NULL ) {
match - > sk = cmd - > sk ;
sock_hold ( match - > sk ) ;
}
}
2013-10-16 01:26:26 +04:00
void mgmt_set_class_of_dev_complete ( struct hci_dev * hdev , u8 * dev_class ,
u8 status )
2012-02-22 21:38:01 +04:00
{
2012-02-24 01:09:40 +04:00
struct cmd_lookup match = { NULL , hdev , mgmt_status ( status ) } ;
2012-02-22 21:38:01 +04:00
2013-03-16 02:06:55 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_DEV_CLASS , hdev , sk_lookup , & match ) ;
mgmt_pending_foreach ( MGMT_OP_ADD_UUID , hdev , sk_lookup , & match ) ;
mgmt_pending_foreach ( MGMT_OP_REMOVE_UUID , hdev , sk_lookup , & match ) ;
2012-02-24 01:09:40 +04:00
2016-09-01 17:46:23 +03:00
if ( ! status ) {
2016-08-29 07:19:46 +03:00
mgmt_limited_event ( MGMT_EV_CLASS_OF_DEV_CHANGED , hdev , dev_class ,
3 , HCI_MGMT_DEV_CLASS_EVENTS , NULL ) ;
2016-09-01 17:46:23 +03:00
ext_info_changed ( hdev , NULL ) ;
}
2012-02-24 01:09:40 +04:00
if ( match . sk )
sock_put ( match . sk ) ;
2012-02-22 21:38:01 +04:00
}
2013-10-16 01:26:27 +04:00
void mgmt_set_local_name_complete ( struct hci_dev * hdev , u8 * name , u8 status )
2011-03-16 15:29:37 +03:00
{
struct mgmt_cp_set_local_name ev ;
2015-03-06 22:08:55 +03:00
struct mgmt_pending_cmd * cmd ;
2012-02-22 23:06:55 +04:00
2013-03-16 02:07:00 +04:00
if ( status )
2013-10-16 01:26:27 +04:00
return ;
2011-03-16 15:29:37 +03:00
memset ( & ev , 0 , sizeof ( ev ) ) ;
memcpy ( ev . name , name , HCI_MAX_NAME_LENGTH ) ;
2012-02-22 23:06:55 +04:00
memcpy ( ev . short_name , hdev - > short_name , HCI_MAX_SHORT_NAME_LENGTH ) ;
2011-03-16 15:29:37 +03:00
2015-03-17 14:48:47 +03:00
cmd = pending_find ( MGMT_OP_SET_LOCAL_NAME , hdev ) ;
2013-03-16 02:07:00 +04:00
if ( ! cmd ) {
memcpy ( hdev - > dev_name , name , sizeof ( hdev - > dev_name ) ) ;
2012-02-22 23:06:55 +04:00
2013-03-16 02:07:00 +04:00
/* If this is a HCI command related to powering on the
* HCI dev don ' t send any mgmt signals .
*/
2015-03-17 14:48:47 +03:00
if ( pending_find ( MGMT_OP_SET_POWERED , hdev ) )
2013-10-16 01:26:27 +04:00
return ;
2013-03-16 02:06:52 +04:00
}
2011-03-16 15:29:37 +03:00
2016-08-29 07:19:46 +03:00
mgmt_limited_event ( MGMT_EV_LOCAL_NAME_CHANGED , hdev , & ev , sizeof ( ev ) ,
HCI_MGMT_LOCAL_NAME_EVENTS , cmd ? cmd - > sk : NULL ) ;
2016-09-01 17:46:23 +03:00
ext_info_changed ( hdev , cmd ? cmd - > sk : NULL ) ;
2011-03-16 15:29:37 +03:00
}
2011-03-22 15:12:21 +03:00
2014-12-05 12:55:58 +03:00
static inline bool has_uuid ( u8 * uuid , u16 uuid_count , u8 ( * uuids ) [ 16 ] )
{
int i ;
for ( i = 0 ; i < uuid_count ; i + + ) {
if ( ! memcmp ( uuid , uuids [ i ] , 16 ) )
return true ;
}
return false ;
}
2014-12-05 12:55:57 +03:00
static bool eir_has_uuids ( u8 * eir , u16 eir_len , u16 uuid_count , u8 ( * uuids ) [ 16 ] )
{
2014-12-05 12:55:58 +03:00
u16 parsed = 0 ;
while ( parsed < eir_len ) {
u8 field_len = eir [ 0 ] ;
u8 uuid [ 16 ] ;
int i ;
if ( field_len = = 0 )
break ;
if ( eir_len - parsed < field_len + 1 )
break ;
switch ( eir [ 1 ] ) {
case EIR_UUID16_ALL :
case EIR_UUID16_SOME :
for ( i = 0 ; i + 3 < = field_len ; i + = 2 ) {
2014-12-05 14:40:01 +03:00
memcpy ( uuid , bluetooth_base_uuid , 16 ) ;
2014-12-05 12:55:58 +03:00
uuid [ 13 ] = eir [ i + 3 ] ;
uuid [ 12 ] = eir [ i + 2 ] ;
if ( has_uuid ( uuid , uuid_count , uuids ) )
return true ;
}
break ;
case EIR_UUID32_ALL :
case EIR_UUID32_SOME :
for ( i = 0 ; i + 5 < = field_len ; i + = 4 ) {
2014-12-05 14:40:01 +03:00
memcpy ( uuid , bluetooth_base_uuid , 16 ) ;
2014-12-05 12:55:58 +03:00
uuid [ 15 ] = eir [ i + 5 ] ;
uuid [ 14 ] = eir [ i + 4 ] ;
uuid [ 13 ] = eir [ i + 3 ] ;
uuid [ 12 ] = eir [ i + 2 ] ;
if ( has_uuid ( uuid , uuid_count , uuids ) )
return true ;
}
break ;
case EIR_UUID128_ALL :
case EIR_UUID128_SOME :
for ( i = 0 ; i + 17 < = field_len ; i + = 16 ) {
memcpy ( uuid , eir + i + 2 , 16 ) ;
if ( has_uuid ( uuid , uuid_count , uuids ) )
return true ;
}
break ;
}
parsed + = field_len + 1 ;
eir + = field_len + 1 ;
}
2014-12-05 12:55:57 +03:00
return false ;
}
2015-02-02 10:07:55 +03:00
static void restart_le_scan ( struct hci_dev * hdev )
{
/* If controller is not scanning we are done. */
2015-03-13 12:11:00 +03:00
if ( ! hci_dev_test_flag ( hdev , HCI_LE_SCAN ) )
2015-02-02 10:07:55 +03:00
return ;
if ( time_after ( jiffies + DISCOV_LE_RESTART_DELAY ,
hdev - > discovery . scan_start +
hdev - > discovery . scan_duration ) )
return ;
2015-11-11 09:11:23 +03:00
queue_delayed_work ( hdev - > req_workqueue , & hdev - > le_scan_restart ,
2015-02-02 10:07:55 +03:00
DISCOV_LE_RESTART_DELAY ) ;
}
2015-03-05 03:24:24 +03:00
static bool is_filter_match ( struct hci_dev * hdev , s8 rssi , u8 * eir ,
u16 eir_len , u8 * scan_rsp , u8 scan_rsp_len )
2011-03-31 00:57:16 +04:00
{
2015-03-05 03:24:24 +03:00
/* If a RSSI threshold has been specified, and
* HCI_QUIRK_STRICT_DUPLICATE_FILTER is not set , then all results with
* a RSSI smaller than the RSSI threshold will be dropped . If the quirk
* is set , let it through for further processing , as we might need to
* restart the scan .
2014-12-05 15:03:34 +03:00
*
* For BR / EDR devices ( pre 1.2 ) providing no RSSI during inquiry ,
* the results are also dropped .
2014-12-05 12:55:56 +03:00
*/
if ( hdev - > discovery . rssi ! = HCI_RSSI_INVALID & &
2015-02-02 10:07:55 +03:00
( rssi = = HCI_RSSI_INVALID | |
( rssi < hdev - > discovery . rssi & &
! test_bit ( HCI_QUIRK_STRICT_DUPLICATE_FILTER , & hdev - > quirks ) ) ) )
2015-03-05 03:24:24 +03:00
return false ;
2014-12-05 15:03:34 +03:00
2015-03-05 03:24:25 +03:00
if ( hdev - > discovery . uuid_count ! = 0 ) {
/* If a list of UUIDs is provided in filter, results with no
* matching UUID should be dropped .
2014-12-05 12:55:57 +03:00
*/
2015-03-05 03:24:25 +03:00
if ( ! eir_has_uuids ( eir , eir_len , hdev - > discovery . uuid_count ,
hdev - > discovery . uuids ) & &
! eir_has_uuids ( scan_rsp , scan_rsp_len ,
hdev - > discovery . uuid_count ,
hdev - > discovery . uuids ) )
return false ;
2014-12-05 12:55:57 +03:00
}
2014-03-25 12:30:47 +04:00
2015-03-05 03:24:25 +03:00
/* If duplicate filtering does not report RSSI changes, then restart
* scanning to ensure updated result with updated RSSI values .
2015-02-02 10:07:55 +03:00
*/
2015-03-05 03:24:25 +03:00
if ( test_bit ( HCI_QUIRK_STRICT_DUPLICATE_FILTER , & hdev - > quirks ) ) {
restart_le_scan ( hdev ) ;
/* Validate RSSI value against the RSSI threshold once more. */
if ( hdev - > discovery . rssi ! = HCI_RSSI_INVALID & &
rssi < hdev - > discovery . rssi )
return false ;
}
2015-03-05 03:24:24 +03:00
return true ;
}
void mgmt_device_found ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 link_type ,
u8 addr_type , u8 * dev_class , s8 rssi , u32 flags ,
u8 * eir , u16 eir_len , u8 * scan_rsp , u8 scan_rsp_len )
{
char buf [ 512 ] ;
struct mgmt_ev_device_found * ev = ( void * ) buf ;
size_t ev_size ;
/* Don't send events for a non-kernel initiated discovery. With
* LE one exception is if we have pend_le_reports > 0 in which
* case we ' re doing passive scanning and want these events .
*/
if ( ! hci_discovery_active ( hdev ) ) {
if ( link_type = = ACL_LINK )
return ;
if ( link_type = = LE_LINK & & list_empty ( & hdev - > pend_le_reports ) )
return ;
}
2015-03-05 03:24:26 +03:00
if ( hdev - > discovery . result_filtering ) {
2015-03-05 03:24:24 +03:00
/* We are using service discovery */
if ( ! is_filter_match ( hdev , rssi , eir , eir_len , scan_rsp ,
scan_rsp_len ) )
return ;
}
2016-01-05 14:19:32 +03:00
if ( hdev - > discovery . limited ) {
/* Check for limited discoverable bit */
if ( dev_class ) {
if ( ! ( dev_class [ 1 ] & 0x20 ) )
return ;
} else {
u8 * flags = eir_get_data ( eir , eir_len , EIR_FLAGS , NULL ) ;
if ( ! flags | | ! ( flags [ 0 ] & LE_AD_LIMITED ) )
return ;
}
}
2015-03-05 03:24:24 +03:00
/* Make sure that the buffer is big enough. The 5 extra bytes
* are for the potential CoD field .
*/
if ( sizeof ( * ev ) + eir_len + scan_rsp_len + 5 > sizeof ( buf ) )
2015-02-02 10:07:55 +03:00
return ;
2015-03-05 03:24:24 +03:00
memset ( buf , 0 , sizeof ( buf ) ) ;
/* In case of device discovery with BR/EDR devices (pre 1.2), the
* RSSI value was reported as 0 when not available . This behavior
* is kept when using device discovery . This is required for full
* backwards compatibility with the API .
*
* However when using service discovery , the value 127 will be
* returned when the RSSI is not available .
*/
if ( rssi = = HCI_RSSI_INVALID & & ! hdev - > discovery . report_invalid_rssi & &
link_type = = ACL_LINK )
rssi = 0 ;
bacpy ( & ev - > addr . bdaddr , bdaddr ) ;
ev - > addr . type = link_to_bdaddr ( link_type , addr_type ) ;
ev - > rssi = rssi ;
ev - > flags = cpu_to_le32 ( flags ) ;
if ( eir_len > 0 )
/* Copy EIR or advertising data into event */
memcpy ( ev - > eir , eir , eir_len ) ;
2016-01-05 14:19:31 +03:00
if ( dev_class & & ! eir_get_data ( ev - > eir , eir_len , EIR_CLASS_OF_DEV ,
NULL ) )
2015-03-05 03:24:24 +03:00
eir_len = eir_append_data ( ev - > eir , eir_len , EIR_CLASS_OF_DEV ,
dev_class , 3 ) ;
if ( scan_rsp_len > 0 )
/* Append scan response data to event */
memcpy ( ev - > eir + eir_len , scan_rsp , scan_rsp_len ) ;
2014-03-25 12:30:47 +04:00
ev - > eir_len = cpu_to_le16 ( eir_len + scan_rsp_len ) ;
ev_size = sizeof ( * ev ) + eir_len + scan_rsp_len ;
2011-09-10 01:56:26 +04:00
2013-10-07 10:55:51 +04:00
mgmt_event ( MGMT_EV_DEVICE_FOUND , hdev , ev , ev_size , NULL ) ;
2011-03-31 00:57:16 +04:00
}
2011-03-30 14:18:12 +04:00
2013-10-07 10:55:52 +04:00
void mgmt_remote_name ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 link_type ,
u8 addr_type , s8 rssi , u8 * name , u8 name_len )
2011-03-30 14:18:12 +04:00
{
2012-01-17 23:48:47 +04:00
struct mgmt_ev_device_found * ev ;
char buf [ sizeof ( * ev ) + HCI_MAX_NAME_LENGTH + 2 ] ;
u16 eir_len ;
2011-03-30 14:18:12 +04:00
2012-01-17 23:48:47 +04:00
ev = ( struct mgmt_ev_device_found * ) buf ;
2011-03-30 14:18:12 +04:00
2012-01-17 23:48:47 +04:00
memset ( buf , 0 , sizeof ( buf ) ) ;
bacpy ( & ev - > addr . bdaddr , bdaddr ) ;
2012-04-25 04:02:50 +04:00
ev - > addr . type = link_to_bdaddr ( link_type , addr_type ) ;
2012-01-17 23:48:47 +04:00
ev - > rssi = rssi ;
eir_len = eir_append_data ( ev - > eir , 0 , EIR_NAME_COMPLETE , name ,
2012-03-08 08:25:00 +04:00
name_len ) ;
2012-01-17 23:48:47 +04:00
2012-03-14 20:08:46 +04:00
ev - > eir_len = cpu_to_le16 ( eir_len ) ;
2011-03-30 14:18:12 +04:00
2013-10-07 10:55:52 +04:00
mgmt_event ( MGMT_EV_DEVICE_FOUND , hdev , ev , sizeof ( * ev ) + eir_len , NULL ) ;
2011-03-30 14:18:12 +04:00
}
2011-04-27 18:29:57 +04:00
2013-10-07 10:55:53 +04:00
void mgmt_discovering ( struct hci_dev * hdev , u8 discovering )
2011-04-27 18:29:57 +04:00
{
2012-02-21 01:30:44 +04:00
struct mgmt_ev_discovering ev ;
2011-11-01 19:06:44 +04:00
2011-11-23 00:14:19 +04:00
BT_DBG ( " %s discovering %u " , hdev - > name , discovering ) ;
2012-02-21 01:30:44 +04:00
memset ( & ev , 0 , sizeof ( ev ) ) ;
ev . type = hdev - > discovery . type ;
ev . discovering = discovering ;
2013-10-07 10:55:53 +04:00
mgmt_event ( MGMT_EV_DISCOVERING , hdev , & ev , sizeof ( ev ) , NULL ) ;
2011-04-27 18:29:57 +04:00
}
2011-08-25 17:48:02 +04:00
2015-03-06 22:08:51 +03:00
static struct hci_mgmt_chan chan = {
. channel = HCI_CHANNEL_CONTROL ,
. handler_count = ARRAY_SIZE ( mgmt_handlers ) ,
. handlers = mgmt_handlers ,
2015-03-17 14:48:49 +03:00
. hdev_init = mgmt_init_hdev ,
2015-03-06 22:08:51 +03:00
} ;
int mgmt_init ( void )
{
return hci_mgmt_chan_register ( & chan ) ;
}
void mgmt_exit ( void )
{
hci_mgmt_chan_unregister ( & chan ) ;
}