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-11-11 20:10:00 +04:00
# include <linux/kernel.h>
2011-02-17 16:16:32 +03:00
# include <linux/uaccess.h>
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>
# include <net/bluetooth/mgmt.h>
2011-12-22 04:12:13 +04:00
# include <net/bluetooth/smp.h>
2010-12-08 01:21:06 +03:00
2012-02-21 00:47:49 +04:00
bool enable_hs ;
bool enable_le ;
2012-02-17 16:39:28 +04:00
# define MGMT_VERSION 1
2012-03-27 20:49:02 +04:00
# define MGMT_REVISION 1
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 ,
MGMT_OP_SET_PAIRABLE ,
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 ,
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-02-04 00:48:01 +04:00
/*
* These LE scan and inquiry parameters were chosen according to LE General
* Discovery Procedure specification .
*/
# define LE_SCAN_TYPE 0x01
# define LE_SCAN_WIN 0x12
# define LE_SCAN_INT 0x12
# define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */
2012-02-18 03:39:38 +04:00
# define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */
2012-02-04 00:48:01 +04:00
2012-02-04 00:48:02 +04:00
# define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
2012-02-18 03:39:38 +04:00
# define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */
2011-11-07 18:45:24 +04: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
2012-02-21 16:13:02 +04:00
# define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
! test_bit ( HCI_AUTO_OFF , & hdev - > dev_flags ) )
2010-12-16 11:17:38 +03:00
struct pending_cmd {
struct list_head list ;
2011-11-09 15:58:56 +04:00
u16 opcode ;
2010-12-16 11:17:38 +03:00
int index ;
2011-03-22 15:12:19 +03:00
void * param ;
2010-12-16 11:17:38 +03:00
struct sock * sk ;
2011-02-19 18:05:56 +03:00
void * user_data ;
2010-12-16 11:17:38 +03:00
} ;
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 */
MGMT_STATUS_NOT_PAIRED , /* PIN or Key Missing */
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 ;
}
2011-02-25 21:05:48 +03:00
static int cmd_status ( struct sock * sk , u16 index , u16 cmd , u8 status )
2010-12-13 22:07:06 +03:00
{
struct sk_buff * skb ;
struct mgmt_hdr * hdr ;
struct mgmt_ev_cmd_status * ev ;
2011-10-15 02:20:01 +04:00
int err ;
2010-12-13 22:07:06 +03:00
2011-02-28 16:10:08 +03:00
BT_DBG ( " sock %p, index %u, cmd %u, status %u " , sk , index , cmd , status ) ;
2010-12-13 22:07:06 +03:00
skb = alloc_skb ( sizeof ( * hdr ) + sizeof ( * ev ) , GFP_ATOMIC ) ;
if ( ! skb )
return - ENOMEM ;
hdr = ( void * ) skb_put ( skb , sizeof ( * hdr ) ) ;
hdr - > opcode = cpu_to_le16 ( MGMT_EV_CMD_STATUS ) ;
2011-02-25 21:05:48 +03:00
hdr - > index = cpu_to_le16 ( index ) ;
2010-12-13 22:07:06 +03:00
hdr - > len = cpu_to_le16 ( sizeof ( * ev ) ) ;
ev = ( void * ) skb_put ( skb , sizeof ( * ev ) ) ;
ev - > status = status ;
2012-03-14 20:08:46 +04:00
ev - > opcode = cpu_to_le16 ( cmd ) ;
2010-12-13 22:07:06 +03:00
2011-10-15 02:20:01 +04:00
err = sock_queue_rcv_skb ( sk , skb ) ;
if ( err < 0 )
2010-12-13 22:07:06 +03:00
kfree_skb ( skb ) ;
2011-10-15 02:20:01 +04:00
return err ;
2010-12-13 22:07:06 +03:00
}
2012-02-18 17:07:59 +04:00
static int cmd_complete ( struct sock * sk , u16 index , u16 cmd , u8 status ,
2012-03-08 08:25:00 +04:00
void * rp , size_t rp_len )
2010-12-13 22:07:04 +03:00
{
struct sk_buff * skb ;
struct mgmt_hdr * hdr ;
struct mgmt_ev_cmd_complete * ev ;
2011-10-15 02:20:01 +04:00
int err ;
2010-12-13 22:07:04 +03:00
BT_DBG ( " sock %p " , sk ) ;
2011-01-22 07:46:43 +03:00
skb = alloc_skb ( sizeof ( * hdr ) + sizeof ( * ev ) + rp_len , GFP_ATOMIC ) ;
2010-12-13 22:07:04 +03:00
if ( ! skb )
return - ENOMEM ;
hdr = ( void * ) skb_put ( skb , sizeof ( * hdr ) ) ;
2011-01-22 07:46:43 +03:00
hdr - > opcode = cpu_to_le16 ( MGMT_EV_CMD_COMPLETE ) ;
2011-02-25 21:05:48 +03:00
hdr - > index = cpu_to_le16 ( index ) ;
2011-01-22 07:46:43 +03:00
hdr - > len = cpu_to_le16 ( sizeof ( * ev ) + rp_len ) ;
2010-12-13 22:07:04 +03:00
2011-01-22 07:46:43 +03:00
ev = ( void * ) skb_put ( skb , sizeof ( * ev ) + rp_len ) ;
2012-03-14 20:08:46 +04:00
ev - > opcode = cpu_to_le16 ( cmd ) ;
2012-02-18 17:07:59 +04:00
ev - > status = status ;
2011-02-28 16:09:50 +03:00
if ( rp )
memcpy ( ev - > data , rp , rp_len ) ;
2010-12-13 22:07:04 +03:00
2011-10-15 02:20:01 +04:00
err = sock_queue_rcv_skb ( sk , skb ) ;
if ( err < 0 )
2010-12-13 22:07:04 +03:00
kfree_skb ( skb ) ;
2012-02-22 14:59:01 +04:00
return err ;
2010-12-13 22:07:04 +03:00
}
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 ) ;
rp . version = MGMT_VERSION ;
2012-03-14 20:08:46 +04:00
rp . revision = __constant_cpu_to_le16 ( MGMT_REVISION ) ;
2011-01-22 07:46:43 +03:00
2012-02-18 17:07:59 +04:00
return cmd_complete ( sk , MGMT_INDEX_NONE , MGMT_OP_READ_VERSION , 0 , & rp ,
2012-03-08 08:25:00 +04:00
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 ;
2012-03-14 20:08:46 +04:00
const u16 num_commands = ARRAY_SIZE ( mgmt_commands ) ;
const u16 num_events = ARRAY_SIZE ( mgmt_events ) ;
2012-03-14 20:54:15 +04:00
__le16 * opcode ;
2012-02-13 18:59:33 +04:00
size_t rp_size ;
int i , err ;
BT_DBG ( " sock %p " , sk ) ;
rp_size = sizeof ( * rp ) + ( ( num_commands + num_events ) * sizeof ( u16 ) ) ;
rp = kmalloc ( rp_size , GFP_KERNEL ) ;
if ( ! rp )
return - ENOMEM ;
2012-03-14 20:08:46 +04:00
rp - > num_commands = __constant_cpu_to_le16 ( num_commands ) ;
rp - > num_events = __constant_cpu_to_le16 ( num_events ) ;
2012-02-13 18:59:33 +04:00
for ( i = 0 , opcode = rp - > opcodes ; i < num_commands ; i + + , opcode + + )
put_unaligned_le16 ( mgmt_commands [ i ] , opcode ) ;
for ( i = 0 ; i < num_events ; i + + , opcode + + )
put_unaligned_le16 ( mgmt_events [ i ] , opcode ) ;
2012-02-18 17:07:59 +04:00
err = cmd_complete ( sk , MGMT_INDEX_NONE , MGMT_OP_READ_COMMANDS , 0 , rp ,
2012-03-08 08:25:00 +04:00
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 ;
struct list_head * p ;
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 ;
2011-01-22 07:46:43 +03:00
int i , err ;
2010-12-13 22:07:05 +03:00
BT_DBG ( " sock %p " , sk ) ;
read_lock ( & hci_dev_list_lock ) ;
count = 0 ;
list_for_each ( p , & hci_dev_list ) {
count + + ;
}
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-03-14 20:08:46 +04:00
rp - > num_controllers = cpu_to_le16 ( count ) ;
2010-12-13 22:07:05 +03:00
i = 0 ;
2011-11-01 12:58:56 +04:00
list_for_each_entry ( d , & hci_dev_list , list ) {
2012-01-09 01:11:15 +04:00
if ( test_bit ( HCI_SETUP , & d - > dev_flags ) )
2010-12-15 14:53:18 +03:00
continue ;
2012-03-14 20:08:46 +04:00
rp - > index [ i + + ] = cpu_to_le16 ( d - > id ) ;
2010-12-13 22:07:05 +03:00
BT_DBG ( " Added hci%u " , d - > id ) ;
}
read_unlock ( & hci_dev_list_lock ) ;
2012-02-18 17:07:59 +04:00
err = cmd_complete ( sk , MGMT_INDEX_NONE , MGMT_OP_READ_INDEX_LIST , 0 , rp ,
2012-03-08 08:25:00 +04:00
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
}
2011-12-15 02:47:35 +04:00
static u32 get_supported_settings ( struct hci_dev * hdev )
{
u32 settings = 0 ;
settings | = MGMT_SETTING_POWERED ;
settings | = MGMT_SETTING_CONNECTABLE ;
settings | = MGMT_SETTING_FAST_CONNECTABLE ;
settings | = MGMT_SETTING_DISCOVERABLE ;
settings | = MGMT_SETTING_PAIRABLE ;
if ( hdev - > features [ 6 ] & LMP_SIMPLE_PAIR )
settings | = MGMT_SETTING_SSP ;
if ( ! ( hdev - > features [ 4 ] & LMP_NO_BREDR ) ) {
settings | = MGMT_SETTING_BREDR ;
settings | = MGMT_SETTING_LINK_SECURITY ;
}
2012-02-21 00:47:49 +04:00
if ( enable_hs )
settings | = MGMT_SETTING_HS ;
if ( enable_le ) {
if ( hdev - > features [ 4 ] & LMP_LE )
settings | = MGMT_SETTING_LE ;
}
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 ;
2012-02-21 18:01:30 +04:00
if ( test_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_CONNECTABLE ;
2012-02-21 18:01:30 +04:00
if ( test_bit ( HCI_DISCOVERABLE , & hdev - > dev_flags ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_DISCOVERABLE ;
2012-01-09 01:11:15 +04:00
if ( test_bit ( HCI_PAIRABLE , & hdev - > dev_flags ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_PAIRABLE ;
if ( ! ( hdev - > features [ 4 ] & LMP_NO_BREDR ) )
settings | = MGMT_SETTING_BREDR ;
2012-02-22 18:37:11 +04:00
if ( test_bit ( HCI_LE_ENABLED , & hdev - > dev_flags ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_LE ;
2012-02-22 13:58:37 +04:00
if ( test_bit ( HCI_LINK_SECURITY , & hdev - > dev_flags ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_LINK_SECURITY ;
2012-01-25 16:21:06 +04:00
if ( test_bit ( HCI_SSP_ENABLED , & hdev - > dev_flags ) )
2011-12-15 02:47:35 +04:00
settings | = MGMT_SETTING_SSP ;
2012-02-21 01:50:38 +04:00
if ( test_bit ( HCI_HS_ENABLED , & hdev - > dev_flags ) )
settings | = MGMT_SETTING_HS ;
2011-12-15 02:47:35 +04:00
return settings ;
}
2011-12-15 02:47:38 +04:00
# define PNP_INFO_SVCLASS_ID 0x1200
static u8 bluetooth_base_uuid [ ] = {
0xFB , 0x34 , 0x9B , 0x5F , 0x80 , 0x00 , 0x00 , 0x80 ,
0x00 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
} ;
static u16 get_uuid16 ( u8 * uuid128 )
{
u32 val ;
int i ;
for ( i = 0 ; i < 12 ; i + + ) {
if ( bluetooth_base_uuid [ i ] ! = uuid128 [ i ] )
return 0 ;
}
2012-03-20 12:32:25 +04:00
val = get_unaligned_le32 ( & uuid128 [ 12 ] ) ;
2011-12-15 02:47:38 +04:00
if ( val > 0xffff )
return 0 ;
return ( u16 ) val ;
}
static void create_eir ( struct hci_dev * hdev , u8 * data )
{
u8 * ptr = data ;
u16 eir_len = 0 ;
u16 uuid16_list [ HCI_MAX_EIR_LENGTH / sizeof ( u16 ) ] ;
int i , truncated = 0 ;
struct bt_uuid * uuid ;
size_t name_len ;
name_len = strlen ( hdev - > dev_name ) ;
if ( name_len > 0 ) {
/* EIR Data type */
if ( name_len > 48 ) {
name_len = 48 ;
ptr [ 1 ] = EIR_NAME_SHORT ;
} else
ptr [ 1 ] = EIR_NAME_COMPLETE ;
/* EIR Data length */
ptr [ 0 ] = name_len + 1 ;
memcpy ( ptr + 2 , hdev - > dev_name , name_len ) ;
eir_len + = ( name_len + 2 ) ;
ptr + = ( name_len + 2 ) ;
}
2012-03-12 06:27:21 +04:00
if ( hdev - > inq_tx_power ) {
ptr [ 0 ] = 2 ;
ptr [ 1 ] = EIR_TX_POWER ;
ptr [ 2 ] = ( u8 ) hdev - > inq_tx_power ;
eir_len + = 3 ;
ptr + = 3 ;
}
2012-03-12 06:32:12 +04:00
if ( hdev - > devid_source > 0 ) {
ptr [ 0 ] = 9 ;
ptr [ 1 ] = EIR_DEVICE_ID ;
put_unaligned_le16 ( hdev - > devid_source , ptr + 2 ) ;
put_unaligned_le16 ( hdev - > devid_vendor , ptr + 4 ) ;
put_unaligned_le16 ( hdev - > devid_product , ptr + 6 ) ;
put_unaligned_le16 ( hdev - > devid_version , ptr + 8 ) ;
eir_len + = 10 ;
ptr + = 10 ;
}
2011-12-15 02:47:38 +04:00
memset ( uuid16_list , 0 , sizeof ( uuid16_list ) ) ;
/* Group all UUID16 types */
list_for_each_entry ( uuid , & hdev - > uuids , list ) {
u16 uuid16 ;
uuid16 = get_uuid16 ( uuid - > uuid ) ;
if ( uuid16 = = 0 )
return ;
if ( uuid16 < 0x1100 )
continue ;
if ( uuid16 = = PNP_INFO_SVCLASS_ID )
continue ;
/* Stop if not enough space to put next UUID */
if ( eir_len + 2 + sizeof ( u16 ) > HCI_MAX_EIR_LENGTH ) {
truncated = 1 ;
break ;
}
/* Check for duplicates */
for ( i = 0 ; uuid16_list [ i ] ! = 0 ; i + + )
if ( uuid16_list [ i ] = = uuid16 )
break ;
if ( uuid16_list [ i ] = = 0 ) {
uuid16_list [ i ] = uuid16 ;
eir_len + = sizeof ( u16 ) ;
}
}
if ( uuid16_list [ 0 ] ! = 0 ) {
u8 * length = ptr ;
/* EIR Data type */
ptr [ 1 ] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL ;
ptr + = 2 ;
eir_len + = 2 ;
for ( i = 0 ; uuid16_list [ i ] ! = 0 ; i + + ) {
* ptr + + = ( uuid16_list [ i ] & 0x00ff ) ;
* ptr + + = ( uuid16_list [ i ] & 0xff00 ) > > 8 ;
}
/* EIR Data length */
* length = ( i * sizeof ( u16 ) ) + 1 ;
}
}
static int update_eir ( struct hci_dev * hdev )
{
struct hci_cp_write_eir cp ;
2012-02-23 15:30:41 +04:00
if ( ! hdev_is_powered ( hdev ) )
2012-02-23 00:06:38 +04:00
return 0 ;
2011-12-15 02:47:38 +04:00
if ( ! ( hdev - > features [ 6 ] & LMP_EXT_INQ ) )
return 0 ;
2012-01-25 16:21:06 +04:00
if ( ! test_bit ( HCI_SSP_ENABLED , & hdev - > dev_flags ) )
2011-12-15 02:47:38 +04:00
return 0 ;
2012-01-09 01:11:15 +04:00
if ( test_bit ( HCI_SERVICE_CACHE , & hdev - > dev_flags ) )
2011-12-15 02:47:38 +04:00
return 0 ;
memset ( & cp , 0 , sizeof ( cp ) ) ;
create_eir ( hdev , cp . data ) ;
if ( memcmp ( cp . data , hdev - > eir , sizeof ( cp . data ) ) = = 0 )
return 0 ;
memcpy ( hdev - > eir , cp . data , sizeof ( cp . data ) ) ;
return hci_send_cmd ( hdev , HCI_OP_WRITE_EIR , sizeof ( cp ) , & cp ) ;
}
static u8 get_service_classes ( struct hci_dev * hdev )
{
struct bt_uuid * uuid ;
u8 val = 0 ;
list_for_each_entry ( uuid , & hdev - > uuids , list )
val | = uuid - > svc_hint ;
return val ;
}
static int update_class ( struct hci_dev * hdev )
{
u8 cod [ 3 ] ;
2012-02-24 00:54:38 +04:00
int err ;
2011-12-15 02:47:38 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2012-02-23 15:30:41 +04:00
if ( ! hdev_is_powered ( hdev ) )
2012-02-23 00:06:38 +04:00
return 0 ;
2012-01-09 01:11:15 +04:00
if ( test_bit ( HCI_SERVICE_CACHE , & hdev - > dev_flags ) )
2011-12-15 02:47:38 +04:00
return 0 ;
cod [ 0 ] = hdev - > minor_class ;
cod [ 1 ] = hdev - > major_class ;
cod [ 2 ] = get_service_classes ( hdev ) ;
if ( memcmp ( cod , hdev - > dev_class , 3 ) = = 0 )
return 0 ;
2012-02-24 00:54:38 +04:00
err = hci_send_cmd ( hdev , HCI_OP_WRITE_CLASS_OF_DEV , sizeof ( cod ) , cod ) ;
if ( err = = 0 )
set_bit ( HCI_PENDING_CLASS , & hdev - > dev_flags ) ;
return err ;
2011-12-15 02:47:38 +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 ) ;
2011-12-15 02:47:39 +04:00
2012-01-09 01:11:15 +04:00
if ( ! test_and_clear_bit ( HCI_SERVICE_CACHE , & hdev - > dev_flags ) )
2011-12-15 02:47:39 +04:00
return ;
hci_dev_lock ( hdev ) ;
update_eir ( hdev ) ;
update_class ( hdev ) ;
hci_dev_unlock ( hdev ) ;
}
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
{
2012-03-02 21:55:56 +04:00
if ( test_and_set_bit ( HCI_MGMT , & hdev - > dev_flags ) )
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 ) ;
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
*/
clear_bit ( HCI_PAIRABLE , & hdev - > dev_flags ) ;
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
2012-02-28 08:13:32 +04:00
return cmd_complete ( sk , hdev - > id , MGMT_OP_READ_INFO , 0 , & rp ,
2012-03-08 08:25:00 +04:00
sizeof ( rp ) ) ;
2010-12-08 01:21:06 +03:00
}
2010-12-16 11:17:38 +03:00
static void mgmt_pending_free ( struct pending_cmd * cmd )
{
sock_put ( cmd - > sk ) ;
2011-03-22 15:12:19 +03:00
kfree ( cmd - > param ) ;
2010-12-16 11:17:38 +03:00
kfree ( cmd ) ;
}
2011-02-19 18:05:55 +03:00
static struct pending_cmd * mgmt_pending_add ( struct sock * sk , u16 opcode ,
2012-03-08 08:25:00 +04:00
struct hci_dev * hdev , void * data ,
u16 len )
2010-12-16 11:17:38 +03:00
{
struct pending_cmd * cmd ;
cmd = kmalloc ( sizeof ( * cmd ) , GFP_ATOMIC ) ;
if ( ! cmd )
2011-02-19 18:05:55 +03:00
return NULL ;
2010-12-16 11:17:38 +03:00
cmd - > opcode = opcode ;
2011-11-08 22:40:15 +04:00
cmd - > index = hdev - > id ;
2010-12-16 11:17:38 +03:00
2011-03-22 15:12:19 +03:00
cmd - > param = kmalloc ( len , GFP_ATOMIC ) ;
if ( ! cmd - > param ) {
2010-12-16 11:17:38 +03:00
kfree ( cmd ) ;
2011-02-19 18:05:55 +03:00
return NULL ;
2010-12-16 11:17:38 +03:00
}
2011-03-22 15:12:20 +03:00
if ( data )
memcpy ( cmd - > param , data , len ) ;
2010-12-16 11:17:38 +03:00
cmd - > sk = sk ;
sock_hold ( sk ) ;
2011-11-08 22:40:15 +04:00
list_add ( & cmd - > list , & hdev - > mgmt_pending ) ;
2010-12-16 11:17:38 +03:00
2011-02-19 18:05:55 +03:00
return cmd ;
2010-12-16 11:17:38 +03:00
}
2011-11-08 22:40:14 +04:00
static void mgmt_pending_foreach ( u16 opcode , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void ( * cb ) ( struct pending_cmd * cmd , void * data ) ,
void * data )
2010-12-16 11:17:38 +03:00
{
struct list_head * p , * n ;
2011-11-08 22:40:15 +04:00
list_for_each_safe ( p , n , & hdev - > mgmt_pending ) {
2010-12-16 11:17:38 +03:00
struct pending_cmd * cmd ;
cmd = list_entry ( p , struct pending_cmd , list ) ;
2011-11-03 16:40:33 +04:00
if ( opcode > 0 & & cmd - > opcode ! = opcode )
2010-12-16 11:17:38 +03:00
continue ;
cb ( cmd , data ) ;
}
}
2011-11-08 22:40:15 +04:00
static struct pending_cmd * mgmt_pending_find ( u16 opcode , struct hci_dev * hdev )
2010-12-16 11:17:38 +03:00
{
2011-11-01 12:58:56 +04:00
struct pending_cmd * cmd ;
2010-12-16 11:17:38 +03:00
2011-11-08 22:40:15 +04:00
list_for_each_entry ( cmd , & hdev - > mgmt_pending , list ) {
2011-11-09 15:58:57 +04:00
if ( cmd - > opcode = = opcode )
return cmd ;
2010-12-16 11:17:38 +03:00
}
return NULL ;
}
2011-02-19 18:06:02 +03:00
static void mgmt_pending_remove ( struct pending_cmd * cmd )
2010-12-29 17:00:25 +03:00
{
list_del ( & cmd - > list ) ;
mgmt_pending_free ( cmd ) ;
}
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
2012-02-18 17:07:59 +04:00
return cmd_complete ( sk , hdev - > id , opcode , 0 , & settings ,
2012-03-08 08:25:00 +04:00
sizeof ( settings ) ) ;
2011-11-11 18:18:52 +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 ;
2011-02-19 18:05:55 +03:00
struct 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
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2010-12-16 11:17:38 +03:00
2012-02-21 15:14:25 +04:00
if ( test_and_clear_bit ( HCI_AUTO_OFF , & hdev - > dev_flags ) ) {
cancel_delayed_work ( & hdev - > power_off ) ;
if ( cp - > val ) {
err = send_settings_rsp ( sk , MGMT_OP_SET_POWERED , hdev ) ;
mgmt_powered ( hdev , 1 ) ;
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
if ( mgmt_pending_find ( MGMT_OP_SET_POWERED , hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_POWERED ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_BUSY ) ;
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
2010-12-30 01:38:22 +03:00
if ( cp - > val )
2011-12-18 18:40:32 +04:00
schedule_work ( & hdev - > power_on ) ;
2010-12-16 11:17:38 +03:00
else
2011-12-17 20:52:27 +04:00
schedule_work ( & hdev - > power_off . work ) ;
2010-12-16 11:17:38 +03:00
2011-02-19 18:05:55 +03:00
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-03-08 08:25:00 +04:00
static int mgmt_event ( u16 event , struct hci_dev * hdev , void * data , u16 data_len ,
struct sock * skip_sk )
2012-02-21 18:55:31 +04:00
{
struct sk_buff * skb ;
struct mgmt_hdr * hdr ;
skb = alloc_skb ( sizeof ( * hdr ) + data_len , GFP_ATOMIC ) ;
if ( ! skb )
return - ENOMEM ;
hdr = ( void * ) skb_put ( skb , sizeof ( * hdr ) ) ;
hdr - > opcode = cpu_to_le16 ( event ) ;
if ( hdev )
hdr - > index = cpu_to_le16 ( hdev - > id ) ;
else
hdr - > index = cpu_to_le16 ( MGMT_INDEX_NONE ) ;
hdr - > len = cpu_to_le16 ( data_len ) ;
if ( data )
memcpy ( skb_put ( skb , data_len ) , data , data_len ) ;
2012-02-22 16:49:28 +04:00
/* Time stamp */
__net_timestamp ( skb ) ;
2012-02-21 18:55:31 +04:00
hci_send_to_control ( skb , skip_sk ) ;
kfree_skb ( skb ) ;
return 0 ;
}
static int new_settings ( struct hci_dev * hdev , struct sock * skip )
{
__le32 ev ;
ev = cpu_to_le32 ( get_current_settings ( hdev ) ) ;
return mgmt_event ( MGMT_EV_NEW_SETTINGS , hdev , & ev , sizeof ( ev ) , skip ) ;
}
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 ;
2011-02-19 18:05:55 +03:00
struct pending_cmd * cmd ;
2012-02-21 18:01:30 +04:00
u16 timeout ;
2010-12-29 17:00:25 +03:00
u8 scan ;
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
2012-03-13 07:31:08 +04:00
timeout = __le16_to_cpu ( cp - > timeout ) ;
2012-02-22 21:06:34 +04:00
if ( ! cp - > val & & timeout > 0 )
2012-02-28 08:13:32 +04:00
return cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
2012-03-08 08:25:00 +04:00
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 ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2010-12-29 17:00:25 +03:00
goto failed ;
}
2011-11-08 22:40:15 +04:00
if ( mgmt_pending_find ( MGMT_OP_SET_DISCOVERABLE , hdev ) | |
mgmt_pending_find ( MGMT_OP_SET_CONNECTABLE , hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_BUSY ) ;
2010-12-29 17:00:25 +03:00
goto failed ;
}
2012-02-21 18:01:30 +04:00
if ( ! test_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_DISCOVERABLE ,
2012-03-08 08:25:00 +04:00
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 ;
if ( ! ! cp - > val ! = test_bit ( HCI_DISCOVERABLE , & hdev - > dev_flags ) ) {
change_bit ( HCI_DISCOVERABLE , & hdev - > dev_flags ) ;
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 ;
}
if ( ! ! cp - > val = = test_bit ( HCI_DISCOVERABLE , & hdev - > dev_flags ) ) {
2012-02-22 21:21:00 +04:00
if ( hdev - > discov_timeout > 0 ) {
cancel_delayed_work ( & hdev - > discov_off ) ;
hdev - > discov_timeout = 0 ;
}
if ( cp - > val & & timeout > 0 ) {
hdev - > discov_timeout = timeout ;
queue_delayed_work ( hdev - > workqueue , & hdev - > discov_off ,
msecs_to_jiffies ( hdev - > discov_timeout * 1000 ) ) ;
}
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
scan = SCAN_PAGE ;
2010-12-30 01:38:22 +03:00
if ( cp - > val )
2010-12-29 17:00:25 +03:00
scan | = SCAN_INQUIRY ;
2011-11-08 00:16:02 +04:00
else
2011-11-09 03:44:22 +04:00
cancel_delayed_work ( & hdev - > discov_off ) ;
2010-12-29 17:00:25 +03:00
err = hci_send_cmd ( hdev , HCI_OP_WRITE_SCAN_ENABLE , 1 , & scan ) ;
if ( err < 0 )
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2010-12-29 17:00:25 +03:00
2011-11-08 00:16:02 +04:00
if ( cp - > val )
2012-02-21 18:01:30 +04:00
hdev - > discov_timeout = timeout ;
2011-11-08 00:16:02 +04:00
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 ;
}
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 ;
2011-02-19 18:05:55 +03:00
struct pending_cmd * cmd ;
2010-12-30 01:18:33 +03:00
u8 scan ;
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
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 ) ) {
2012-02-21 21:40:05 +04:00
bool changed = false ;
if ( ! ! cp - > val ! = test_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) )
changed = true ;
2012-02-22 15:21:16 +04:00
if ( cp - > val ) {
2012-02-21 18:01:30 +04:00
set_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) ;
2012-02-22 15:21:16 +04:00
} else {
2012-02-21 18:01:30 +04:00
clear_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) ;
clear_bit ( HCI_DISCOVERABLE , & hdev - > dev_flags ) ;
}
2012-02-21 21:40:05 +04:00
2012-02-21 18:01:30 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_CONNECTABLE , hdev ) ;
2012-02-21 21:40:05 +04:00
if ( err < 0 )
goto failed ;
if ( changed )
err = new_settings ( hdev , sk ) ;
2010-12-30 01:18:33 +03:00
goto failed ;
}
2011-11-08 22:40:15 +04:00
if ( mgmt_pending_find ( MGMT_OP_SET_DISCOVERABLE , hdev ) | |
mgmt_pending_find ( MGMT_OP_SET_CONNECTABLE , hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_CONNECTABLE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_BUSY ) ;
2010-12-30 01:18:33 +03:00
goto failed ;
}
2012-02-21 18:01:30 +04:00
if ( ! ! cp - > val = = test_bit ( HCI_PSCAN , & hdev - > flags ) ) {
2011-12-15 02:47:35 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_CONNECTABLE , hdev ) ;
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
2012-02-22 15:21:16 +04:00
if ( cp - > val ) {
2010-12-30 01:18:33 +03:00
scan = SCAN_PAGE ;
2012-02-22 15:21:16 +04:00
} else {
2010-12-30 01:18:33 +03:00
scan = 0 ;
2012-02-21 21:15:49 +04:00
if ( test_bit ( HCI_ISCAN , & hdev - > flags ) & &
hdev - > discov_timeout > 0 )
cancel_delayed_work ( & hdev - > discov_off ) ;
}
2010-12-30 01:18:33 +03:00
err = hci_send_cmd ( hdev , HCI_OP_WRITE_SCAN_ENABLE , 1 , & scan ) ;
if ( err < 0 )
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
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 ;
}
2012-02-28 08:13:32 +04:00
static int set_pairable ( 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 ;
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
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-26 14:11:03 +03:00
if ( cp - > val )
2012-01-09 01:11:15 +04:00
set_bit ( HCI_PAIRABLE , & hdev - > dev_flags ) ;
2011-01-26 14:11:03 +03:00
else
2012-01-09 01:11:15 +04:00
clear_bit ( HCI_PAIRABLE , & hdev - > dev_flags ) ;
2011-01-26 14:11:03 +03:00
2011-12-15 02:47:35 +04:00
err = send_settings_rsp ( sk , MGMT_OP_SET_PAIRABLE , hdev ) ;
2011-01-26 14:11:03 +03:00
if ( err < 0 )
goto failed ;
2012-02-21 18:55:31 +04:00
err = new_settings ( hdev , sk ) ;
2011-01-26 14:11:03 +03:00
failed :
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 ;
struct pending_cmd * cmd ;
2012-02-26 15:04:52 +04:00
u8 val ;
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
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 ;
if ( ! ! cp - > val ! = test_bit ( HCI_LINK_SECURITY ,
& hdev - > dev_flags ) ) {
change_bit ( HCI_LINK_SECURITY , & hdev - > dev_flags ) ;
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 ;
}
if ( mgmt_pending_find ( MGMT_OP_SET_LINK_SECURITY , hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_LINK_SECURITY ,
2012-03-08 08:25:00 +04:00
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 ;
struct pending_cmd * cmd ;
2012-02-26 15:04:52 +04:00
u8 val ;
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
hci_dev_lock ( hdev ) ;
2012-02-22 18:35:26 +04:00
if ( ! ( hdev - > features [ 6 ] & LMP_SIMPLE_PAIR ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_SSP ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_SUPPORTED ) ;
2012-02-22 18:35:26 +04:00
goto failed ;
}
2012-02-22 14:38:31 +04:00
val = ! ! cp - > val ;
2012-02-21 16:13:02 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2012-02-22 14:38:31 +04:00
bool changed = false ;
if ( val ! = test_bit ( HCI_SSP_ENABLED , & hdev - > dev_flags ) ) {
change_bit ( HCI_SSP_ENABLED , & hdev - > dev_flags ) ;
changed = true ;
}
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 ;
}
if ( mgmt_pending_find ( MGMT_OP_SET_SSP , hdev ) ) {
2012-03-16 19:02:56 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_SSP ,
MGMT_STATUS_BUSY ) ;
2012-02-17 02:56:28 +04:00
goto failed ;
}
if ( test_bit ( HCI_SSP_ENABLED , & hdev - > dev_flags ) = = val ) {
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 ;
}
err = hci_send_cmd ( hdev , HCI_OP_WRITE_SSP_MODE , 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_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 ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " request for %s " , hdev - > name ) ;
2012-02-21 01:50:38 +04:00
2012-02-28 08:13:32 +04:00
if ( ! enable_hs )
return cmd_status ( sk , hdev - > id , MGMT_OP_SET_HS ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_SUPPORTED ) ;
2012-02-21 01:50:38 +04:00
if ( cp - > val )
set_bit ( HCI_HS_ENABLED , & hdev - > dev_flags ) ;
else
clear_bit ( HCI_HS_ENABLED , & hdev - > dev_flags ) ;
2012-02-28 08:13:32 +04:00
return send_settings_rsp ( sk , MGMT_OP_SET_HS , hdev ) ;
2012-02-21 01:50:38 +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 ;
struct pending_cmd * cmd ;
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
2012-03-01 07:55:35 +04:00
hci_dev_lock ( hdev ) ;
2012-02-22 18:37:11 +04:00
if ( ! enable_le | | ! ( hdev - > features [ 4 ] & LMP_LE ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_LE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_SUPPORTED ) ;
2012-03-01 07:55:35 +04:00
goto unlock ;
2012-02-22 18:37:11 +04:00
}
val = ! ! cp - > val ;
2012-02-28 02:57:24 +04:00
enabled = ! ! ( hdev - > host_features [ 0 ] & LMP_HOST_LE ) ;
2012-02-22 18:37:11 +04: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 ;
if ( val ! = test_bit ( HCI_LE_ENABLED , & hdev - > dev_flags ) ) {
change_bit ( HCI_LE_ENABLED , & hdev - > dev_flags ) ;
changed = true ;
}
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
}
if ( mgmt_pending_find ( MGMT_OP_SET_LE , hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_LE ,
2012-03-08 08:25:00 +04:00
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
}
memset ( & hci_cp , 0 , sizeof ( hci_cp ) ) ;
if ( val ) {
hci_cp . le = val ;
hci_cp . simul = ! ! ( hdev - > features [ 6 ] & LMP_SIMUL_LE_BR ) ;
}
2012-03-08 08:25:00 +04:00
err = hci_send_cmd ( hdev , HCI_OP_WRITE_LE_HOST_SUPPORTED , sizeof ( hci_cp ) ,
& hci_cp ) ;
2012-02-22 18:37:11 +04:00
if ( err < 0 ) {
mgmt_pending_remove ( cmd ) ;
2012-03-01 07:55:35 +04:00
goto unlock ;
2012-02-22 18:37:11 +04:00
}
2012-03-01 07:55:35 +04:00
unlock :
hci_dev_unlock ( hdev ) ;
2012-02-22 18:37:11 +04:00
return err ;
}
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 ;
2012-02-24 01:09:40 +04:00
struct pending_cmd * cmd ;
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
2012-02-24 00:54:38 +04:00
if ( test_bit ( HCI_PENDING_CLASS , & hdev - > dev_flags ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_ADD_UUID ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_BUSY ) ;
2012-02-24 00:54:38 +04:00
goto failed ;
}
2011-01-04 13:08:51 +03:00
uuid = kmalloc ( sizeof ( * uuid ) , GFP_ATOMIC ) ;
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 ;
2011-01-04 13:08:51 +03:00
list_add ( & uuid - > list , & hdev - > uuids ) ;
2011-01-13 22:56:52 +03:00
err = update_class ( hdev ) ;
if ( err < 0 )
goto failed ;
2011-03-28 15:07:23 +04:00
err = update_eir ( hdev ) ;
if ( err < 0 )
goto failed ;
2012-02-24 01:09:40 +04:00
if ( ! test_bit ( HCI_PENDING_CLASS , & hdev - > dev_flags ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_UUID , 0 ,
2012-03-08 08:25:00 +04:00
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 ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto failed ;
}
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 ;
if ( ! test_and_set_bit ( HCI_SERVICE_CACHE , & hdev - > dev_flags ) ) {
2012-03-02 02:32:37 +04:00
schedule_delayed_work ( & hdev - > service_cache , CACHE_TIMEOUT ) ;
2012-02-24 01:24:30 +04:00
return true ;
}
return false ;
}
2012-02-28 08:13:32 +04:00
static int remove_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_remove_uuid * cp = data ;
2012-02-24 01:09:40 +04:00
struct pending_cmd * cmd ;
2011-01-04 13:08:51 +03:00
struct list_head * p , * n ;
u8 bt_uuid_any [ ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
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
2012-02-24 00:54:38 +04:00
if ( test_bit ( HCI_PENDING_CLASS , & hdev - > dev_flags ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_REMOVE_UUID ,
2012-03-08 08:25:00 +04:00
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 ) {
err = 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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_REMOVE_UUID ,
2012-03-08 08:25:00 +04:00
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 ;
list_for_each_safe ( p , n , & hdev - > uuids ) {
struct bt_uuid * match = list_entry ( p , struct bt_uuid , list ) ;
if ( memcmp ( match - > uuid , cp - > uuid , 16 ) ! = 0 )
continue ;
list_del ( & match - > list ) ;
found + + ;
}
if ( found = = 0 ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_REMOVE_UUID ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_INVALID_PARAMS ) ;
2011-01-04 13:08:51 +03:00
goto unlock ;
}
2012-02-23 23:33:16 +04:00
update_class :
2011-01-13 22:56:52 +03:00
err = update_class ( hdev ) ;
if ( err < 0 )
goto unlock ;
2011-03-28 15:07:23 +04:00
err = update_eir ( hdev ) ;
if ( err < 0 )
goto unlock ;
2012-02-24 01:09:40 +04:00
if ( ! test_bit ( HCI_PENDING_CLASS , & hdev - > dev_flags ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_REMOVE_UUID , 0 ,
2012-03-08 08:25:00 +04:00
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 ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
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 ;
}
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 ;
2012-02-24 01:09:40 +04:00
struct pending_cmd * cmd ;
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
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-13 22:56:52 +03:00
2012-02-24 00:54:38 +04:00
if ( test_bit ( HCI_PENDING_CLASS , & hdev - > dev_flags ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_DEV_CLASS ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_BUSY ) ;
2012-02-24 00:54:38 +04:00
goto unlock ;
}
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_SET_DEV_CLASS , 0 ,
2012-03-08 08:25:00 +04:00
hdev - > dev_class , 3 ) ;
2012-02-21 16:32:24 +04:00
goto unlock ;
}
2012-01-09 01:11:15 +04:00
if ( test_and_clear_bit ( HCI_SERVICE_CACHE , & hdev - > dev_flags ) ) {
2011-12-15 02:47:39 +04:00
hci_dev_unlock ( hdev ) ;
cancel_delayed_work_sync ( & hdev - > service_cache ) ;
hci_dev_lock ( hdev ) ;
2011-12-15 02:47:37 +04:00
update_eir ( hdev ) ;
2011-12-15 02:47:39 +04:00
}
2011-12-15 02:47:37 +04:00
2011-01-13 22:56:52 +03:00
err = update_class ( hdev ) ;
2012-02-24 01:09:40 +04:00
if ( err < 0 )
goto unlock ;
2011-01-13 22:56:52 +03:00
2012-02-24 01:09:40 +04:00
if ( ! test_bit ( HCI_PENDING_CLASS , & hdev - > dev_flags ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_SET_DEV_CLASS , 0 ,
2012-03-08 08:25:00 +04:00
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 ) ;
if ( ! cmd ) {
err = - ENOMEM ;
goto unlock ;
}
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 ,
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 ;
2011-02-25 21:05:48 +03:00
u16 key_count , expected_len ;
2011-08-26 03:02:29 +04:00
int i ;
2011-01-17 15:41:05 +03:00
2012-03-13 07:31:08 +04:00
key_count = __le16_to_cpu ( cp - > key_count ) ;
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 " ,
2011-08-26 03:02:29 +04:00
len , expected_len ) ;
2012-02-28 08:13:32 +04:00
return cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LINK_KEYS ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_INVALID_PARAMS ) ;
2011-01-17 15:41:05 +03:00
}
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s debug_keys %u key_count %u " , hdev - > name , cp - > debug_keys ,
2011-01-17 15:41:05 +03:00
key_count ) ;
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 ) ;
2012-01-09 01:11:15 +04:00
set_bit ( HCI_LINK_KEYS , & hdev - > dev_flags ) ;
2011-01-17 15:41:05 +03:00
if ( cp - > debug_keys )
2012-01-09 01:11:15 +04:00
set_bit ( HCI_DEBUG_KEYS , & hdev - > dev_flags ) ;
2011-01-17 15:41:05 +03:00
else
2012-01-09 01:11:15 +04:00
clear_bit ( HCI_DEBUG_KEYS , & hdev - > dev_flags ) ;
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
2012-02-17 16:06:34 +04:00
hci_add_link_key ( hdev , NULL , 0 , & key - > addr . bdaddr , key - > val ,
2012-03-08 08:25:00 +04:00
key - > type , key - > pin_len ) ;
2011-01-17 15:41:05 +03:00
}
2012-02-28 08:13:32 +04:00
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 ;
2011-11-10 17:54:38 +04:00
struct hci_cp_disconnect dc ;
struct pending_cmd * cmd ;
2011-01-17 15:41:05 +03:00
struct hci_conn * conn ;
int err ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-01-17 15:41:05 +03:00
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
2012-02-23 00:53:34 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_UNPAIR_DEVICE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED , & rp , sizeof ( rp ) ) ;
2012-02-23 00:53:34 +04:00
goto unlock ;
}
2012-02-09 15:50:12 +04:00
if ( cp - > addr . type = = MGMT_ADDR_BREDR )
err = hci_remove_link_key ( hdev , & cp - > addr . bdaddr ) ;
else
err = hci_remove_ltk ( hdev , & cp - > addr . bdaddr ) ;
2012-02-03 04:08:03 +04:00
2011-01-17 15:41:05 +03:00
if ( err < 0 ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_UNPAIR_DEVICE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_PAIRED , & rp , sizeof ( rp ) ) ;
2011-01-17 15:41:05 +03:00
goto unlock ;
}
2012-02-23 00:53:34 +04:00
if ( cp - > disconnect ) {
if ( cp - > addr . type = = MGMT_ADDR_BREDR )
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK ,
2012-02-09 15:50:12 +04:00
& cp - > addr . bdaddr ) ;
2012-02-23 00:53:34 +04:00
else
conn = hci_conn_hash_lookup_ba ( hdev , LE_LINK ,
2012-02-09 15:50:12 +04:00
& cp - > addr . bdaddr ) ;
2012-02-23 00:53:34 +04:00
} else {
conn = NULL ;
}
2012-02-09 15:50:12 +04:00
2011-11-10 17:54:38 +04:00
if ( ! conn ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_UNPAIR_DEVICE , 0 ,
2012-03-08 08:25:00 +04:00
& 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
}
2012-03-14 20:08:46 +04:00
dc . handle = cpu_to_le16 ( conn - > handle ) ;
2011-11-10 17:54:38 +04:00
dc . reason = 0x13 ; /* Remote User Terminated Connection */
err = hci_send_cmd ( hdev , HCI_OP_DISCONNECT , sizeof ( dc ) , & dc ) ;
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 ;
2011-01-20 13:40:27 +03:00
struct hci_cp_disconnect dc ;
2011-02-19 18:05:55 +03:00
struct pending_cmd * cmd ;
2011-01-20 13:40:27 +03:00
struct hci_conn * conn ;
int err ;
BT_DBG ( " " ) ;
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_DISCONNECT ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2011-01-20 13:40:27 +03:00
goto failed ;
}
2011-11-08 22:40:15 +04:00
if ( mgmt_pending_find ( MGMT_OP_DISCONNECT , hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_DISCONNECT ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_BUSY ) ;
2011-01-20 13:40:27 +03:00
goto failed ;
}
2012-02-09 16:27:38 +04:00
if ( cp - > addr . type = = MGMT_ADDR_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 ) ;
2011-05-07 01:41:44 +04:00
2011-01-20 13:40:27 +03:00
if ( ! conn ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_DISCONNECT ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_CONNECTED ) ;
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
2012-03-14 20:08:46 +04:00
dc . handle = cpu_to_le16 ( conn - > handle ) ;
2011-01-20 13:40:27 +03:00
dc . reason = 0x13 ; /* Remote User Terminated Connection */
err = hci_send_cmd ( hdev , HCI_OP_DISCONNECT , sizeof ( dc ) , & dc ) ;
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 ;
}
2011-11-09 15:58:58 +04:00
static u8 link_to_mgmt ( 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 :
return MGMT_ADDR_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 */
return MGMT_ADDR_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 */
return MGMT_ADDR_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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_GET_CONNECTIONS ,
2012-03-08 08:25:00 +04:00
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 ) ) ;
2011-01-22 07:46:43 +03:00
rp = kmalloc ( rp_len , GFP_ATOMIC ) ;
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 ) ;
2011-11-09 15:58:58 +04:00
rp - > addr [ i ] . type = link_to_mgmt ( 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
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_GET_CONNECTIONS , 0 , rp ,
2012-03-08 08:25:00 +04:00
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
{
struct pending_cmd * cmd ;
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 ;
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 ;
2011-02-19 18:05:55 +03:00
struct 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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_PIN_CODE_REPLY ,
2012-03-08 08:25:00 +04:00
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 ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_PIN_CODE_REPLY ,
2012-03-08 08:25:00 +04:00
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 )
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_PIN_CODE_REPLY ,
2012-03-08 08:25:00 +04:00
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
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-02-28 08:13:32 +04:00
static int pin_code_neg_reply ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
void * data , u16 len )
2011-01-22 07:10:07 +03:00
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_pin_code_neg_reply * cp = data ;
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_PIN_CODE_NEG_REPLY ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2011-01-22 07:10:07 +03:00
goto failed ;
}
2012-02-28 08:13:32 +04:00
err = send_pin_code_neg_reply ( sk , hdev , cp ) ;
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 ( " " ) ;
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 ,
2011-03-01 18:55:34 +03: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
2012-03-08 08:25:00 +04:00
return cmd_complete ( sk , hdev - > id , MGMT_OP_SET_IO_CAPABILITY , 0 , NULL ,
0 ) ;
2011-01-25 14:28:33 +03:00
}
2011-02-19 18:05:56 +03:00
static inline struct pending_cmd * find_pairing ( struct hci_conn * conn )
{
struct hci_dev * hdev = conn - > hdev ;
2011-11-01 12:58:56 +04:00
struct 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 ;
}
static void pairing_complete ( struct pending_cmd * cmd , u8 status )
{
struct mgmt_rp_pair_device rp ;
struct hci_conn * conn = cmd - > user_data ;
2011-11-11 02:07:34 +04:00
bacpy ( & rp . addr . bdaddr , & conn - > dst ) ;
rp . addr . type = link_to_mgmt ( conn - > type , conn - > dst_type ) ;
2011-02-19 18:05:56 +03:00
2012-02-18 17:07:59 +04:00
cmd_complete ( cmd - > sk , cmd - > index , MGMT_OP_PAIR_DEVICE , status ,
2012-03-08 08:25:00 +04:00
& 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 ;
hci_conn_put ( conn ) ;
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-02-19 18:05:56 +03:00
}
static void pairing_complete_cb ( struct hci_conn * conn , u8 status )
{
struct pending_cmd * cmd ;
BT_DBG ( " status %u " , status ) ;
cmd = find_pairing ( conn ) ;
2011-11-08 22:40:16 +04:00
if ( ! cmd )
2011-02-19 18:05:56 +03:00
BT_DBG ( " Unable to find a pending command " ) ;
2011-11-08 22:40:16 +04:00
else
2012-02-18 17:20:03 +04:00
pairing_complete ( cmd , mgmt_status ( status ) ) ;
2011-02-19 18:05:56 +03: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 ;
2011-02-19 18:05:56 +03:00
struct pending_cmd * cmd ;
u8 sec_level , auth_type ;
struct hci_conn * conn ;
int err ;
BT_DBG ( " " ) ;
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2012-02-23 00:41:18 +04:00
goto unlock ;
}
2011-09-02 21:51:22 +04:00
sec_level = BT_SECURITY_MEDIUM ;
if ( cp - > io_cap = = 0x03 )
2011-02-19 18:05:56 +03:00
auth_type = HCI_AT_DEDICATED_BONDING ;
2011-09-02 21:51:22 +04:00
else
2011-02-19 18:05:56 +03:00
auth_type = HCI_AT_DEDICATED_BONDING_MITM ;
2011-11-11 02:07:34 +04:00
if ( cp - > addr . type = = MGMT_ADDR_BREDR )
conn = hci_connect ( hdev , ACL_LINK , & cp - > addr . bdaddr , sec_level ,
2012-03-08 08:25:00 +04:00
auth_type ) ;
2011-08-20 04:06:54 +04:00
else
2011-11-11 02:07:34 +04:00
conn = hci_connect ( hdev , LE_LINK , & cp - > addr . bdaddr , sec_level ,
2012-03-08 08:25:00 +04:00
auth_type ) ;
2011-08-20 04:06:54 +04:00
2011-11-11 02:07:35 +04:00
memset ( & rp , 0 , sizeof ( rp ) ) ;
bacpy ( & rp . addr . bdaddr , & cp - > addr . bdaddr ) ;
rp . addr . type = cp - > addr . type ;
2011-02-22 22:10:53 +03:00
if ( IS_ERR ( conn ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_CONNECT_FAILED , & rp ,
sizeof ( rp ) ) ;
2011-02-19 18:05:56 +03:00
goto unlock ;
}
if ( conn - > connect_cfm_cb ) {
hci_conn_put ( conn ) ;
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_PAIR_DEVICE ,
2012-03-08 08:25:00 +04:00
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 ;
hci_conn_put ( conn ) ;
goto unlock ;
}
2011-08-20 04:06:54 +04:00
/* For LE, just connecting isn't a proof that the pairing finished */
2011-11-11 02:07:34 +04:00
if ( cp - > addr . type = = MGMT_ADDR_BREDR )
2011-08-20 04:06:54 +04:00
conn - > connect_cfm_cb = pairing_complete_cb ;
2011-02-19 18:05:56 +03:00
conn - > security_cfm_cb = pairing_complete_cb ;
conn - > disconn_cfm_cb = pairing_complete_cb ;
conn - > io_capability = cp - > io_cap ;
cmd - > user_data = conn ;
if ( conn - > state = = BT_CONNECTED & &
hci_conn_security ( conn , sec_level , auth_type ) )
pairing_complete ( cmd , 0 ) ;
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 ;
2012-02-02 06:02:29 +04:00
struct pending_cmd * cmd ;
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_CANCEL_PAIR_DEVICE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2012-02-23 00:41:18 +04:00
goto unlock ;
}
2012-02-02 06:02:29 +04:00
cmd = mgmt_pending_find ( MGMT_OP_PAIR_DEVICE , hdev ) ;
if ( ! cmd ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_CANCEL_PAIR_DEVICE ,
2012-03-08 08:25:00 +04:00
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 ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_CANCEL_PAIR_DEVICE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_INVALID_PARAMS ) ;
2012-02-02 06:02:29 +04:00
goto unlock ;
}
pairing_complete ( cmd , MGMT_STATUS_CANCELLED ) ;
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_CANCEL_PAIR_DEVICE , 0 ,
2012-03-08 08:25:00 +04:00
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 ,
2012-03-08 08:25:00 +04:00
bdaddr_t * bdaddr , u8 type , u16 mgmt_op ,
u16 hci_op , __le32 passkey )
2011-02-19 18:05:57 +03:00
{
struct 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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , mgmt_op ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2011-11-17 01:53:13 +04:00
goto done ;
2011-02-19 18:05:57 +03:00
}
2012-02-09 17:26:12 +04:00
if ( type = = MGMT_ADDR_BREDR )
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , bdaddr ) ;
else
2011-11-17 01:53:14 +04:00
conn = hci_conn_hash_lookup_ba ( hdev , LE_LINK , bdaddr ) ;
2012-02-09 17:26:12 +04:00
if ( ! conn ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , mgmt_op ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_CONNECTED ) ;
2012-02-09 17:26:12 +04:00
goto done ;
}
2011-11-17 01:53:14 +04:00
2012-02-09 17:26:12 +04:00
if ( type = = MGMT_ADDR_LE_PUBLIC | | type = = MGMT_ADDR_LE_RANDOM ) {
2011-11-17 01:53:14 +04:00
/* Continue with pairing via SMP */
2011-12-22 04:12:13 +04:00
err = smp_user_confirm_reply ( conn , mgmt_op , passkey ) ;
if ( ! err )
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , mgmt_op ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_SUCCESS ) ;
2011-12-22 04:12:13 +04:00
else
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , mgmt_op ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_FAILED ) ;
2011-11-17 01:53:14 +04:00
goto done ;
}
2011-11-17 01:53:13 +04:00
cmd = mgmt_pending_add ( sk , mgmt_op , hdev , bdaddr , sizeof ( * bdaddr ) ) ;
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
}
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 ;
bacpy ( & cp . bdaddr , bdaddr ) ;
cp . passkey = passkey ;
err = hci_send_cmd ( hdev , hci_op , sizeof ( cp ) , & cp ) ;
} else
err = hci_send_cmd ( hdev , hci_op , sizeof ( * bdaddr ) , bdaddr ) ;
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-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 ) )
2012-02-28 08:13:32 +04:00
return cmd_status ( sk , hdev - > id , MGMT_OP_USER_CONFIRM_REPLY ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_INVALID_PARAMS ) ;
2011-11-17 01:53:13 +04:00
2012-02-28 08:13:32 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr . bdaddr , cp - > addr . type ,
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 ( " " ) ;
2012-02-28 08:13:32 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr . bdaddr , cp - > addr . type ,
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 ( " " ) ;
2012-02-28 08:13:32 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr . bdaddr , cp - > addr . type ,
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 ( " " ) ;
2012-02-28 08:13:32 +04:00
return user_pairing_resp ( sk , hdev , & cp - > addr . bdaddr , cp - > addr . type ,
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
}
2012-03-03 02:19:06 +04:00
static int update_name ( struct hci_dev * hdev , const char * name )
{
struct hci_cp_write_local_name cp ;
memcpy ( cp . name , name , sizeof ( cp . name ) ) ;
return hci_send_cmd ( hdev , HCI_OP_WRITE_LOCAL_NAME , sizeof ( cp ) , & cp ) ;
}
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 ;
2011-03-16 15:29:37 +03:00
struct pending_cmd * cmd ;
int err ;
BT_DBG ( " " ) ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-03-16 15:29:37 +03:00
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
err = cmd_complete ( sk , hdev - > id , MGMT_OP_SET_LOCAL_NAME , 0 ,
2012-03-08 08:25:00 +04:00
data , len ) ;
2012-02-22 23:06:55 +04:00
if ( err < 0 )
goto failed ;
err = mgmt_event ( MGMT_EV_LOCAL_NAME_CHANGED , hdev , data , len ,
2012-03-08 08:25:00 +04:00
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 ;
}
2012-03-03 02:19:06 +04:00
err = update_name ( hdev , cp - > name ) ;
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 ;
}
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
{
struct pending_cmd * cmd ;
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2011-03-22 15:12:21 +03:00
goto unlock ;
}
if ( ! ( hdev - > features [ 6 ] & LMP_SIMPLE_PAIR ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_SUPPORTED ) ;
2011-03-22 15:12:21 +03:00
goto unlock ;
}
2011-11-08 22:40:15 +04:00
if ( mgmt_pending_find ( MGMT_OP_READ_LOCAL_OOB_DATA , hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
2012-03-08 08:25:00 +04:00
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 ;
}
err = hci_send_cmd ( hdev , HCI_OP_READ_LOCAL_OOB_DATA , 0 , NULL ) ;
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
{
2012-02-03 04:07:59 +04:00
struct mgmt_cp_add_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
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-03-22 15:12:22 +03:00
2012-02-23 00:41:18 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_REMOTE_OOB_DATA ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED , & cp - > addr ,
sizeof ( cp - > addr ) ) ;
2012-02-23 00:41:18 +04:00
goto unlock ;
}
2012-02-09 17:44:09 +04:00
err = hci_add_remote_oob_data ( hdev , & cp - > addr . bdaddr , cp - > hash ,
2012-03-08 08:25:00 +04:00
cp - > randomizer ) ;
2011-03-22 15:12:22 +03:00
if ( err < 0 )
2012-02-19 15:16:14 +04:00
status = MGMT_STATUS_FAILED ;
2011-03-22 15:12:22 +03:00
else
2012-02-19 15:16:14 +04:00
status = 0 ;
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_ADD_REMOTE_OOB_DATA , status ,
2012-03-08 08:25:00 +04:00
& cp - > addr , sizeof ( cp - > addr ) ) ;
2011-03-22 15:12:22 +03:00
2012-02-23 00:41:18 +04: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-02-03 04:07:59 +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
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-03-22 15:12:22 +03:00
2012-02-23 00:41:18 +04:00
if ( ! hdev_is_powered ( hdev ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id ,
2012-03-08 08:25:00 +04:00
MGMT_OP_REMOVE_REMOTE_OOB_DATA ,
MGMT_STATUS_NOT_POWERED , & cp - > addr ,
sizeof ( cp - > addr ) ) ;
2012-02-23 00:41:18 +04:00
goto unlock ;
}
2012-02-09 17:44:09 +04:00
err = hci_remove_remote_oob_data ( hdev , & cp - > addr . bdaddr ) ;
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-02-19 15:16:14 +04:00
status = 0 ;
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_REMOVE_REMOTE_OOB_DATA ,
2012-03-08 08:25:00 +04:00
status , & cp - > addr , sizeof ( cp - > addr ) ) ;
2011-03-22 15:12:22 +03:00
2012-02-23 00:41:18 +04: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-18 03:39:38 +04:00
int mgmt_interleaved_discovery ( struct hci_dev * hdev )
{
int err ;
BT_DBG ( " %s " , hdev - > name ) ;
hci_dev_lock ( hdev ) ;
err = hci_do_inquiry ( hdev , INQUIRY_LEN_BREDR_LE ) ;
if ( err < 0 )
hci_discovery_set_state ( hdev , DISCOVERY_STOPPED ) ;
hci_dev_unlock ( hdev ) ;
return err ;
}
2012-02-28 08:13:32 +04:00
static int start_discovery ( struct sock * sk , struct hci_dev * hdev ,
2012-03-08 08:25:00 +04:00
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 ;
2011-04-27 18:29:56 +04:00
struct pending_cmd * cmd ;
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_START_DISCOVERY ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2011-11-08 01:13:37 +04:00
goto failed ;
}
2012-03-21 07:03:37 +04:00
if ( test_bit ( HCI_PERIODIC_INQ , & hdev - > dev_flags ) ) {
err = cmd_status ( sk , hdev - > id , MGMT_OP_START_DISCOVERY ,
MGMT_STATUS_BUSY ) ;
goto failed ;
}
2012-01-04 16:23:45 +04:00
if ( hdev - > discovery . state ! = DISCOVERY_STOPPED ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_START_DISCOVERY ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_BUSY ) ;
2012-01-04 16:23:45 +04:00
goto failed ;
}
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_START_DISCOVERY , hdev , NULL , 0 ) ;
2011-04-27 18:29:56 +04:00
if ( ! cmd ) {
err = - ENOMEM ;
goto failed ;
}
2012-02-18 03:39:36 +04:00
hdev - > discovery . type = cp - > type ;
switch ( hdev - > discovery . type ) {
2012-02-18 03:39:35 +04:00
case DISCOV_TYPE_BREDR :
2012-02-24 01:09:27 +04:00
if ( lmp_bredr_capable ( hdev ) )
err = hci_do_inquiry ( hdev , INQUIRY_LEN_BREDR ) ;
else
err = - ENOTSUPP ;
2012-02-18 03:39:35 +04:00
break ;
case DISCOV_TYPE_LE :
2012-02-24 01:09:27 +04:00
if ( lmp_host_le_capable ( hdev ) )
err = hci_le_scan ( hdev , LE_SCAN_TYPE , LE_SCAN_INT ,
2012-03-08 08:25:00 +04:00
LE_SCAN_WIN , LE_SCAN_TIMEOUT_LE_ONLY ) ;
2012-02-24 01:09:27 +04:00
else
err = - ENOTSUPP ;
2012-02-18 03:39:35 +04:00
break ;
2012-02-18 03:39:38 +04:00
case DISCOV_TYPE_INTERLEAVED :
2012-02-24 18:41:04 +04:00
if ( lmp_host_le_capable ( hdev ) & & lmp_bredr_capable ( hdev ) )
err = hci_le_scan ( hdev , LE_SCAN_TYPE , LE_SCAN_INT ,
2012-03-08 08:25:00 +04:00
LE_SCAN_WIN ,
LE_SCAN_TIMEOUT_BREDR_LE ) ;
2012-02-24 18:41:04 +04:00
else
err = - ENOTSUPP ;
2012-02-18 03:39:38 +04:00
break ;
2012-02-18 03:39:35 +04:00
default :
2012-02-04 00:48:01 +04:00
err = - EINVAL ;
2012-02-18 03:39:35 +04:00
}
2012-02-04 00:48:01 +04:00
2011-04-27 18:29:56 +04:00
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
2012-01-04 16:23:45 +04:00
else
hci_discovery_set_state ( hdev , DISCOVERY_STARTING ) ;
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-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 ;
2011-04-27 18:29:56 +04:00
struct pending_cmd * cmd ;
2012-01-04 17:44:20 +04:00
struct hci_cp_remote_name_req_cancel cp ;
struct inquiry_entry * e ;
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_STOP_DISCOVERY ,
2012-03-08 08:25:00 +04:00
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 ) {
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_STOP_DISCOVERY ,
2012-03-08 08:25:00 +04:00
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
}
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_add ( sk , MGMT_OP_STOP_DISCOVERY , hdev , NULL , 0 ) ;
2011-04-27 18:29:56 +04:00
if ( ! cmd ) {
err = - ENOMEM ;
2012-01-04 17:44:20 +04:00
goto unlock ;
}
2012-03-20 22:15:36 +04:00
switch ( hdev - > discovery . state ) {
case DISCOVERY_FINDING :
2012-03-15 23:52:08 +04:00
if ( test_bit ( HCI_INQUIRY , & hdev - > flags ) )
err = hci_cancel_inquiry ( hdev ) ;
else
err = hci_cancel_le_scan ( hdev ) ;
2012-03-20 22:15:36 +04:00
break ;
case DISCOVERY_RESOLVING :
e = hci_inquiry_cache_lookup_resolve ( hdev , BDADDR_ANY ,
NAME_PENDING ) ;
if ( ! e ) {
2012-01-04 17:44:20 +04:00
mgmt_pending_remove ( cmd ) ;
2012-03-20 22:15:36 +04:00
err = cmd_complete ( sk , hdev - > id ,
MGMT_OP_STOP_DISCOVERY , 0 ,
& mgmt_cp - > type ,
sizeof ( mgmt_cp - > type ) ) ;
hci_discovery_set_state ( hdev , DISCOVERY_STOPPED ) ;
goto unlock ;
}
2012-01-04 17:44:20 +04:00
2012-03-20 22:15:36 +04:00
bacpy ( & cp . bdaddr , & e - > data . bdaddr ) ;
err = hci_send_cmd ( hdev , HCI_OP_REMOTE_NAME_REQ_CANCEL ,
sizeof ( cp ) , & cp ) ;
break ;
default :
BT_DBG ( " unknown discovery state %u " , hdev - > discovery . state ) ;
err = - EFAULT ;
2011-04-27 18:29:56 +04:00
}
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
2012-01-04 16:23:45 +04:00
else
hci_discovery_set_state ( hdev , DISCOVERY_STOPPING ) ;
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 ) ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_CONFIRM_NAME ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_FAILED ) ;
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 ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_CONFIRM_NAME ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_INVALID_PARAMS ) ;
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
}
err = 0 ;
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
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-08-25 17:48:02 +04:00
2012-02-09 17:56:11 +04:00
err = hci_blacklist_add ( hdev , & cp - > addr . bdaddr , cp - > addr . type ) ;
2011-06-15 13:01:15 +04:00
if ( err < 0 )
2012-02-19 14:58:54 +04:00
status = MGMT_STATUS_FAILED ;
2011-06-15 13:01:15 +04:00
else
2012-02-19 14:58:54 +04:00
status = 0 ;
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_BLOCK_DEVICE , status ,
2012-03-08 08:25:00 +04:00
& 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
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-08-25 17:48:02 +04:00
2012-02-09 17:56:11 +04:00
err = hci_blacklist_del ( hdev , & cp - > addr . bdaddr , cp - > addr . type ) ;
2011-06-15 13:01:15 +04:00
if ( err < 0 )
2012-02-19 14:58:54 +04:00
status = MGMT_STATUS_INVALID_PARAMS ;
2011-06-15 13:01:15 +04:00
else
2012-02-19 14:58:54 +04:00
status = 0 ;
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_UNBLOCK_DEVICE , status ,
2012-03-08 08:25:00 +04:00
& 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 ;
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 )
return cmd_status ( sk , hdev - > id , MGMT_OP_SET_DEVICE_ID ,
MGMT_STATUS_INVALID_PARAMS ) ;
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 ) ;
err = cmd_complete ( sk , hdev - > id , MGMT_OP_SET_DEVICE_ID , 0 , NULL , 0 ) ;
update_eir ( hdev ) ;
hci_dev_unlock ( hdev ) ;
return err ;
}
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 ;
2011-06-22 14:11:56 +04:00
struct hci_cp_write_page_scan_activity acp ;
u8 type ;
int err ;
2012-02-28 08:13:32 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2011-06-22 14:11:56 +04:00
2012-02-21 18:40:33 +04:00
if ( ! hdev_is_powered ( hdev ) )
2012-02-28 08:13:32 +04:00
return cmd_status ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_NOT_POWERED ) ;
2012-02-21 18:40:33 +04:00
if ( ! test_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) )
2012-02-28 08:13:32 +04:00
return cmd_status ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_REJECTED ) ;
2011-06-22 14:11:56 +04:00
hci_dev_lock ( hdev ) ;
2011-12-15 02:47:36 +04:00
if ( cp - > val ) {
2011-06-22 14:11:56 +04:00
type = PAGE_SCAN_TYPE_INTERLACED ;
2012-03-12 14:13:11 +04:00
/* 22.5 msec page scan interval */
acp . interval = __constant_cpu_to_le16 ( 0x0024 ) ;
2011-06-22 14:11:56 +04:00
} else {
type = PAGE_SCAN_TYPE_STANDARD ; /* default */
2012-03-12 14:13:11 +04:00
/* default 1.28 sec page scan */
acp . interval = __constant_cpu_to_le16 ( 0x0800 ) ;
2011-06-22 14:11:56 +04:00
}
2012-03-12 14:13:11 +04:00
/* default 11.25 msec page scan window */
acp . window = __constant_cpu_to_le16 ( 0x0012 ) ;
2011-06-22 14:11:56 +04:00
2012-03-08 08:25:00 +04:00
err = hci_send_cmd ( hdev , HCI_OP_WRITE_PAGE_SCAN_ACTIVITY , sizeof ( acp ) ,
& acp ) ;
2011-06-22 14:11:56 +04:00
if ( err < 0 ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_FAILED ) ;
2011-06-22 14:11:56 +04:00
goto done ;
}
err = hci_send_cmd ( hdev , HCI_OP_WRITE_PAGE_SCAN_TYPE , 1 , & type ) ;
if ( err < 0 ) {
2012-02-28 08:13:32 +04:00
err = cmd_status ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_FAILED ) ;
2011-06-22 14:11:56 +04:00
goto done ;
}
2012-02-28 08:13:32 +04:00
err = cmd_complete ( sk , hdev - > id , MGMT_OP_SET_FAST_CONNECTABLE , 0 ,
2012-03-08 08:25:00 +04:00
NULL , 0 ) ;
2011-06-22 14:11:56 +04:00
done :
hci_dev_unlock ( hdev ) ;
return err ;
}
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 ;
u16 key_count , expected_len ;
int i ;
2012-03-13 07:31:08 +04:00
key_count = __le16_to_cpu ( cp - > key_count ) ;
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 " ,
len , expected_len ) ;
2012-02-28 08:13:32 +04:00
return cmd_status ( sk , hdev - > id , MGMT_OP_LOAD_LONG_TERM_KEYS ,
2012-03-08 08:25:00 +04:00
EINVAL ) ;
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
hci_dev_lock ( hdev ) ;
hci_smp_ltks_clear ( hdev ) ;
for ( i = 0 ; i < key_count ; i + + ) {
struct mgmt_ltk_info * key = & cp - > keys [ i ] ;
u8 type ;
if ( key - > master )
type = HCI_SMP_LTK ;
else
type = HCI_SMP_LTK_SLAVE ;
hci_add_ltk ( hdev , & key - > addr . bdaddr , key - > addr . type ,
2012-03-08 08:25:00 +04:00
type , 0 , key - > authenticated , key - > val ,
key - > enc_size , key - > ediv , key - > rand ) ;
2012-02-03 04:08:02 +04:00
}
hci_dev_unlock ( hdev ) ;
return 0 ;
}
2012-03-14 20:54:15 +04:00
static const struct mgmt_handler {
2012-03-08 08:25:00 +04:00
int ( * func ) ( struct sock * sk , struct hci_dev * hdev , void * data ,
u16 data_len ) ;
2012-03-02 00:24:41 +04:00
bool var_len ;
size_t data_len ;
2012-02-28 19:18:30 +04:00
} mgmt_handlers [ ] = {
{ NULL } , /* 0x0000 (no command) */
2012-03-02 00:24:41 +04:00
{ read_version , false , MGMT_READ_VERSION_SIZE } ,
{ read_commands , false , MGMT_READ_COMMANDS_SIZE } ,
{ read_index_list , false , MGMT_READ_INDEX_LIST_SIZE } ,
{ read_controller_info , false , MGMT_READ_INFO_SIZE } ,
{ set_powered , false , MGMT_SETTING_SIZE } ,
{ set_discoverable , false , MGMT_SET_DISCOVERABLE_SIZE } ,
{ set_connectable , false , MGMT_SETTING_SIZE } ,
{ set_fast_connectable , false , MGMT_SETTING_SIZE } ,
{ set_pairable , false , MGMT_SETTING_SIZE } ,
{ set_link_security , false , MGMT_SETTING_SIZE } ,
{ set_ssp , false , MGMT_SETTING_SIZE } ,
{ set_hs , false , MGMT_SETTING_SIZE } ,
{ set_le , false , MGMT_SETTING_SIZE } ,
{ set_dev_class , false , MGMT_SET_DEV_CLASS_SIZE } ,
{ set_local_name , false , MGMT_SET_LOCAL_NAME_SIZE } ,
{ add_uuid , false , MGMT_ADD_UUID_SIZE } ,
{ remove_uuid , false , MGMT_REMOVE_UUID_SIZE } ,
{ load_link_keys , true , MGMT_LOAD_LINK_KEYS_SIZE } ,
{ load_long_term_keys , true , MGMT_LOAD_LONG_TERM_KEYS_SIZE } ,
{ disconnect , false , MGMT_DISCONNECT_SIZE } ,
{ get_connections , false , MGMT_GET_CONNECTIONS_SIZE } ,
{ pin_code_reply , false , MGMT_PIN_CODE_REPLY_SIZE } ,
{ pin_code_neg_reply , false , MGMT_PIN_CODE_NEG_REPLY_SIZE } ,
{ set_io_capability , false , MGMT_SET_IO_CAPABILITY_SIZE } ,
{ pair_device , false , MGMT_PAIR_DEVICE_SIZE } ,
{ cancel_pair_device , false , MGMT_CANCEL_PAIR_DEVICE_SIZE } ,
{ unpair_device , false , MGMT_UNPAIR_DEVICE_SIZE } ,
{ user_confirm_reply , false , MGMT_USER_CONFIRM_REPLY_SIZE } ,
{ user_confirm_neg_reply , false , MGMT_USER_CONFIRM_NEG_REPLY_SIZE } ,
{ user_passkey_reply , false , MGMT_USER_PASSKEY_REPLY_SIZE } ,
{ user_passkey_neg_reply , false , MGMT_USER_PASSKEY_NEG_REPLY_SIZE } ,
{ read_local_oob_data , false , MGMT_READ_LOCAL_OOB_DATA_SIZE } ,
{ add_remote_oob_data , false , MGMT_ADD_REMOTE_OOB_DATA_SIZE } ,
{ remove_remote_oob_data , false , MGMT_REMOVE_REMOTE_OOB_DATA_SIZE } ,
{ start_discovery , false , MGMT_START_DISCOVERY_SIZE } ,
{ stop_discovery , false , MGMT_STOP_DISCOVERY_SIZE } ,
{ confirm_name , false , MGMT_CONFIRM_NAME_SIZE } ,
{ block_device , false , MGMT_BLOCK_DEVICE_SIZE } ,
{ unblock_device , false , MGMT_UNBLOCK_DEVICE_SIZE } ,
2012-03-12 07:00:29 +04:00
{ set_device_id , false , MGMT_SET_DEVICE_ID_SIZE } ,
2012-02-28 19:18:30 +04:00
} ;
2010-12-08 01:21:06 +03:00
int mgmt_control ( struct sock * sk , struct msghdr * msg , size_t msglen )
{
2012-02-03 04:07:59 +04:00
void * buf ;
u8 * cp ;
2010-12-08 01:21:06 +03:00
struct mgmt_hdr * hdr ;
2011-02-25 21:05:48 +03:00
u16 opcode , index , len ;
2012-02-28 08:13:32 +04:00
struct hci_dev * hdev = NULL ;
2012-03-14 20:54:15 +04:00
const struct mgmt_handler * handler ;
2010-12-08 01:21:06 +03:00
int err ;
BT_DBG ( " got %zu bytes " , msglen ) ;
if ( msglen < sizeof ( * hdr ) )
return - EINVAL ;
2011-04-05 01:56:53 +04:00
buf = kmalloc ( msglen , GFP_KERNEL ) ;
2010-12-08 01:21:06 +03:00
if ( ! buf )
return - ENOMEM ;
if ( memcpy_fromiovec ( buf , msg - > msg_iov , msglen ) ) {
err = - EFAULT ;
goto done ;
}
2012-02-03 04:07:59 +04:00
hdr = buf ;
2012-03-13 07:31:08 +04:00
opcode = __le16_to_cpu ( hdr - > opcode ) ;
index = __le16_to_cpu ( hdr - > index ) ;
len = __le16_to_cpu ( hdr - > len ) ;
2010-12-08 01:21:06 +03:00
if ( len ! = msglen - sizeof ( * hdr ) ) {
err = - EINVAL ;
goto done ;
}
2012-02-28 19:18:30 +04:00
if ( index ! = MGMT_INDEX_NONE ) {
2012-02-28 08:13:32 +04:00
hdev = hci_dev_get ( index ) ;
if ( ! hdev ) {
err = cmd_status ( sk , index , opcode ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_INVALID_INDEX ) ;
2012-02-28 08:13:32 +04:00
goto done ;
}
}
2012-02-28 19:18:30 +04:00
if ( opcode > = ARRAY_SIZE ( mgmt_handlers ) | |
mgmt_handlers [ opcode ] . func = = NULL ) {
2010-12-08 01:21:06 +03:00
BT_DBG ( " Unknown op %u " , opcode ) ;
2011-11-11 20:10:00 +04:00
err = cmd_status ( sk , index , opcode ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_UNKNOWN_COMMAND ) ;
2012-02-28 19:18:30 +04:00
goto done ;
}
if ( ( hdev & & opcode < MGMT_OP_READ_INFO ) | |
( ! hdev & & opcode > = MGMT_OP_READ_INFO ) ) {
err = cmd_status ( sk , index , opcode ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_INVALID_INDEX ) ;
2012-02-28 19:18:30 +04:00
goto done ;
2010-12-08 01:21:06 +03:00
}
2012-03-02 00:24:41 +04:00
handler = & mgmt_handlers [ opcode ] ;
if ( ( handler - > var_len & & len < handler - > data_len ) | |
( ! handler - > var_len & & len ! = handler - > data_len ) ) {
err = cmd_status ( sk , index , opcode ,
2012-03-08 08:25:00 +04:00
MGMT_STATUS_INVALID_PARAMS ) ;
2012-03-02 00:24:41 +04:00
goto done ;
}
2012-02-28 19:18:30 +04:00
if ( hdev )
mgmt_init_hdev ( sk , hdev ) ;
cp = buf + sizeof ( * hdr ) ;
2012-03-02 00:24:41 +04:00
err = handler - > func ( sk , hdev , cp , len ) ;
2010-12-13 22:07:03 +03:00
if ( err < 0 )
goto done ;
2010-12-08 01:21:06 +03:00
err = msglen ;
done :
2012-02-28 08:13:32 +04:00
if ( hdev )
hci_dev_put ( hdev ) ;
2010-12-08 01:21:06 +03:00
kfree ( buf ) ;
return err ;
}
2010-12-13 22:07:07 +03:00
2011-11-03 16:40:33 +04:00
static void cmd_status_rsp ( struct pending_cmd * cmd , void * data )
{
u8 * status = data ;
cmd_status ( cmd - > sk , cmd - > index , cmd - > opcode , * status ) ;
mgmt_pending_remove ( cmd ) ;
}
2011-11-08 22:40:14 +04:00
int mgmt_index_added ( struct hci_dev * hdev )
2010-12-13 22:07:07 +03:00
{
2011-11-08 22:40:14 +04:00
return mgmt_event ( MGMT_EV_INDEX_ADDED , hdev , NULL , 0 , NULL ) ;
2010-12-13 22:07:07 +03:00
}
2011-11-08 22:40:14 +04:00
int mgmt_index_removed ( struct hci_dev * hdev )
2010-12-13 22:07:07 +03:00
{
2012-03-02 05:13:19 +04:00
u8 status = MGMT_STATUS_INVALID_INDEX ;
2011-11-03 16:40:33 +04:00
2011-11-08 22:40:14 +04:00
mgmt_pending_foreach ( 0 , hdev , cmd_status_rsp , & status ) ;
2011-11-03 16:40:33 +04:00
2011-11-08 22:40:14 +04:00
return mgmt_event ( MGMT_EV_INDEX_REMOVED , hdev , NULL , 0 , NULL ) ;
2010-12-16 11:17:38 +03:00
}
2010-12-29 17:00:25 +03:00
struct cmd_lookup {
2010-12-16 11:17:38 +03:00
struct sock * sk ;
2011-12-15 02:47:35 +04:00
struct hci_dev * hdev ;
2012-02-24 01:09:40 +04:00
u8 mgmt_status ;
2010-12-16 11:17:38 +03:00
} ;
2011-12-15 02:47:35 +04:00
static void settings_rsp ( struct pending_cmd * cmd , void * data )
2010-12-16 11:17:38 +03:00
{
2010-12-29 17:00:25 +03:00
struct cmd_lookup * match = data ;
2010-12-16 11:17:38 +03:00
2011-12-15 02:47:35 +04:00
send_settings_rsp ( cmd - > sk , cmd - > opcode , match - > hdev ) ;
2010-12-16 11:17:38 +03:00
list_del ( & cmd - > list ) ;
if ( match - > sk = = NULL ) {
match - > sk = cmd - > sk ;
sock_hold ( match - > sk ) ;
}
mgmt_pending_free ( cmd ) ;
2010-12-13 22:07:07 +03:00
}
2010-12-16 11:00:37 +03:00
2011-11-08 22:40:14 +04:00
int mgmt_powered ( struct hci_dev * hdev , u8 powered )
2010-12-16 11:00:37 +03:00
{
2012-02-17 02:34:40 +04:00
struct cmd_lookup match = { NULL , hdev } ;
2012-02-17 03:20:00 +04:00
int err ;
2010-12-16 11:00:37 +03:00
2012-02-21 18:01:30 +04:00
if ( ! test_bit ( HCI_MGMT , & hdev - > dev_flags ) )
return 0 ;
2011-12-15 02:47:35 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_POWERED , hdev , settings_rsp , & match ) ;
2010-12-16 11:00:37 +03:00
2012-02-21 18:01:30 +04:00
if ( powered ) {
u8 scan = 0 ;
if ( test_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) )
scan | = SCAN_PAGE ;
if ( test_bit ( HCI_DISCOVERABLE , & hdev - > dev_flags ) )
scan | = SCAN_INQUIRY ;
if ( scan )
hci_send_cmd ( hdev , HCI_OP_WRITE_SCAN_ENABLE , 1 , & scan ) ;
2012-02-23 15:30:41 +04:00
update_class ( hdev ) ;
2012-03-03 02:19:06 +04:00
update_name ( hdev , hdev - > dev_name ) ;
2012-02-23 15:30:41 +04:00
update_eir ( hdev ) ;
2012-02-21 18:01:30 +04:00
} else {
2012-03-02 05:07:07 +04:00
u8 status = MGMT_STATUS_NOT_POWERED ;
2011-11-08 22:40:14 +04:00
mgmt_pending_foreach ( 0 , hdev , cmd_status_rsp , & status ) ;
2011-11-03 16:40:33 +04:00
}
2012-02-21 18:55:31 +04:00
err = new_settings ( hdev , match . sk ) ;
2010-12-16 11:17:38 +03:00
if ( match . sk )
sock_put ( match . sk ) ;
2012-02-17 03:20:00 +04:00
return err ;
2010-12-16 11:00:37 +03:00
}
2010-12-29 17:00:25 +03:00
2011-11-08 22:40:14 +04:00
int mgmt_discoverable ( struct hci_dev * hdev , u8 discoverable )
2010-12-29 17:00:25 +03:00
{
2012-02-17 02:34:40 +04:00
struct cmd_lookup match = { NULL , hdev } ;
2012-02-21 18:01:30 +04:00
bool changed = false ;
int err = 0 ;
2010-12-29 17:00:25 +03:00
2012-02-21 18:01:30 +04:00
if ( discoverable ) {
if ( ! test_and_set_bit ( HCI_DISCOVERABLE , & hdev - > dev_flags ) )
changed = true ;
} else {
if ( test_and_clear_bit ( HCI_DISCOVERABLE , & hdev - > dev_flags ) )
changed = true ;
}
2010-12-29 17:00:25 +03:00
2012-02-21 22:47:06 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_DISCOVERABLE , hdev , settings_rsp ,
2012-03-08 08:25:00 +04:00
& match ) ;
2012-02-21 22:47:06 +04:00
2012-02-21 18:55:31 +04:00
if ( changed )
err = new_settings ( hdev , match . sk ) ;
2012-02-21 18:01:30 +04:00
2010-12-29 17:00:25 +03:00
if ( match . sk )
sock_put ( match . sk ) ;
2012-02-17 03:20:00 +04:00
return err ;
2010-12-29 17:00:25 +03:00
}
2010-12-30 01:18:33 +03:00
2011-11-08 22:40:14 +04:00
int mgmt_connectable ( struct hci_dev * hdev , u8 connectable )
2010-12-30 01:18:33 +03:00
{
2012-02-17 02:34:40 +04:00
struct cmd_lookup match = { NULL , hdev } ;
2012-02-21 18:01:30 +04:00
bool changed = false ;
int err = 0 ;
2010-12-30 01:18:33 +03:00
2012-02-21 18:01:30 +04:00
if ( connectable ) {
if ( ! test_and_set_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) )
changed = true ;
} else {
if ( test_and_clear_bit ( HCI_CONNECTABLE , & hdev - > dev_flags ) )
changed = true ;
}
2010-12-30 01:18:33 +03:00
2012-02-21 22:47:06 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_CONNECTABLE , hdev , settings_rsp ,
2012-03-08 08:25:00 +04:00
& match ) ;
2012-02-21 22:47:06 +04:00
2012-02-21 18:55:31 +04:00
if ( changed )
err = new_settings ( hdev , match . sk ) ;
2010-12-30 01:18:33 +03:00
if ( match . sk )
sock_put ( match . sk ) ;
2012-02-17 03:20:00 +04:00
return err ;
2010-12-30 01:18:33 +03:00
}
2011-01-17 15:41:05 +03:00
2011-11-08 22:40:14 +04:00
int mgmt_write_scan_failed ( struct hci_dev * hdev , u8 scan , u8 status )
2011-11-08 00:16:03 +04:00
{
2011-11-11 20:10:00 +04:00
u8 mgmt_err = mgmt_status ( status ) ;
2011-11-08 00:16:03 +04:00
if ( scan & SCAN_PAGE )
2011-11-08 22:40:14 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_CONNECTABLE , hdev ,
2012-03-08 08:25:00 +04:00
cmd_status_rsp , & mgmt_err ) ;
2011-11-08 00:16:03 +04:00
if ( scan & SCAN_INQUIRY )
2011-11-08 22:40:14 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_DISCOVERABLE , hdev ,
2012-03-08 08:25:00 +04:00
cmd_status_rsp , & mgmt_err ) ;
2011-11-08 00:16:03 +04:00
return 0 ;
}
2012-04-13 16:13:22 +04:00
int 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 ) ;
ev . key . addr . type = MGMT_ADDR_BREDR ;
2011-08-26 03:02:29 +04:00
ev . key . type = key - > type ;
memcpy ( ev . key . val , key - > val , 16 ) ;
ev . key . pin_len = key - > pin_len ;
2011-01-17 15:41:05 +03:00
2011-11-08 22:40:14 +04:00
return 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
2012-02-03 04:08:02 +04:00
int mgmt_new_ltk ( struct hci_dev * hdev , struct smp_ltk * key , u8 persistent )
{
struct mgmt_ev_new_long_term_key ev ;
memset ( & ev , 0 , sizeof ( ev ) ) ;
ev . store_hint = persistent ;
bacpy ( & ev . key . addr . bdaddr , & key - > bdaddr ) ;
2012-03-30 15:53:35 +04:00
ev . key . addr . type = link_to_mgmt ( LE_LINK , key - > bdaddr_type ) ;
2012-02-03 04:08:02 +04:00
ev . key . authenticated = key - > authenticated ;
ev . key . enc_size = key - > enc_size ;
ev . key . ediv = key - > ediv ;
if ( key - > type = = HCI_SMP_LTK )
ev . key . master = 1 ;
memcpy ( ev . key . rand , key - > rand , sizeof ( key - > rand ) ) ;
memcpy ( ev . key . val , key - > val , sizeof ( key - > val ) ) ;
2012-03-08 08:25:00 +04:00
return mgmt_event ( MGMT_EV_NEW_LONG_TERM_KEY , hdev , & ev , sizeof ( ev ) ,
NULL ) ;
2012-02-03 04:08:02 +04:00
}
2012-01-15 20:11:07 +04:00
int mgmt_device_connected ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 link_type ,
2012-03-08 08:25:00 +04:00
u8 addr_type , u32 flags , u8 * name , u8 name_len ,
u8 * dev_class )
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
2012-01-17 23:48:47 +04:00
bacpy ( & ev - > addr . bdaddr , bdaddr ) ;
ev - > addr . type = link_to_mgmt ( link_type , addr_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
2012-01-17 23:48:47 +04:00
if ( name_len > 0 )
eir_len = eir_append_data ( ev - > eir , 0 , EIR_NAME_COMPLETE ,
2012-03-08 08:25:00 +04:00
name , name_len ) ;
2012-01-17 23:48:47 +04:00
if ( dev_class & & memcmp ( dev_class , " \0 \0 \0 " , 3 ) ! = 0 )
2012-03-10 02:07:03 +04:00
eir_len = eir_append_data ( ev - > eir , eir_len ,
2012-03-08 08:25:00 +04:00
EIR_CLASS_OF_DEV , 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
return mgmt_event ( MGMT_EV_DEVICE_CONNECTED , hdev , buf ,
2012-03-08 08:25:00 +04:00
sizeof ( * ev ) + eir_len , NULL ) ;
2011-01-20 13:34:39 +03:00
}
2011-01-20 13:40:27 +03:00
static void disconnect_rsp ( struct pending_cmd * cmd , void * data )
{
2011-03-22 15:12:19 +03:00
struct mgmt_cp_disconnect * cp = cmd - > param ;
2011-01-20 13:40:27 +03:00
struct sock * * sk = data ;
2011-01-22 07:46:43 +03:00
struct mgmt_rp_disconnect rp ;
2011-01-20 13:40:27 +03:00
2012-02-09 16:27:38 +04:00
bacpy ( & rp . addr . bdaddr , & cp - > addr . bdaddr ) ;
rp . addr . type = cp - > addr . type ;
2011-01-20 13:40:27 +03:00
2012-02-18 17:07:59 +04:00
cmd_complete ( cmd - > sk , cmd - > index , MGMT_OP_DISCONNECT , 0 , & rp ,
2012-03-08 08:25:00 +04:00
sizeof ( rp ) ) ;
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
}
2012-02-09 15:50:12 +04:00
static void unpair_device_rsp ( struct 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 ;
struct mgmt_rp_unpair_device rp ;
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
2012-02-09 19:21:16 +04:00
device_unpaired ( hdev , & cp - > addr . bdaddr , cp - > addr . type , cmd - > sk ) ;
2012-02-18 17:07:59 +04:00
cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode , 0 , & rp , sizeof ( rp ) ) ;
2011-11-10 17:54:38 +04:00
mgmt_pending_remove ( cmd ) ;
}
2012-01-15 20:11:07 +04:00
int mgmt_device_disconnected ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 link_type , u8 addr_type )
2011-01-20 13:34:39 +03:00
{
2011-11-08 01:13:39 +04:00
struct mgmt_addr_info ev ;
2011-01-20 13:40:27 +03:00
struct sock * sk = NULL ;
int err ;
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
bacpy ( & ev . bdaddr , bdaddr ) ;
2011-11-09 15:58:58 +04:00
ev . type = link_to_mgmt ( link_type , addr_type ) ;
2011-01-20 13:34:39 +03:00
2012-01-15 20:11:07 +04:00
err = mgmt_event ( MGMT_EV_DEVICE_DISCONNECTED , hdev , & ev , sizeof ( ev ) ,
2012-03-08 08:25:00 +04:00
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-11-10 17:54:38 +04:00
2011-01-20 13:40:27 +03:00
return err ;
}
2012-02-09 16:27:38 +04:00
int mgmt_disconnect_failed ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 link_type , u8 addr_type , u8 status )
2011-01-20 13:40:27 +03:00
{
2012-02-09 16:27:38 +04:00
struct mgmt_rp_disconnect rp ;
2011-01-20 13:40:27 +03:00
struct pending_cmd * cmd ;
int err ;
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_DISCONNECT , hdev ) ;
2011-01-20 13:40:27 +03:00
if ( ! cmd )
return - ENOENT ;
2012-02-09 16:27:38 +04:00
bacpy ( & rp . addr . bdaddr , bdaddr ) ;
rp . addr . type = link_to_mgmt ( link_type , addr_type ) ;
2011-11-10 17:54:39 +04:00
2012-02-09 16:27:38 +04:00
err = cmd_complete ( cmd - > sk , cmd - > index , MGMT_OP_DISCONNECT ,
2012-03-08 08:25:00 +04:00
mgmt_status ( status ) , & rp , sizeof ( rp ) ) ;
2011-01-20 13:40:27 +03:00
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-20 13:40:27 +03:00
2012-02-09 19:21:16 +04:00
mgmt_pending_foreach ( MGMT_OP_UNPAIR_DEVICE , hdev , unpair_device_rsp ,
hdev ) ;
2011-01-20 13:40:27 +03:00
return err ;
2011-01-20 13:34:39 +03:00
}
2011-01-22 07:09:08 +03:00
2011-11-09 15:58:58 +04:00
int mgmt_connect_failed ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 link_type ,
2012-03-08 08:25:00 +04:00
u8 addr_type , u8 status )
2011-01-22 07:09:08 +03:00
{
struct mgmt_ev_connect_failed ev ;
2011-11-08 01:13:39 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
2011-11-09 15:58:58 +04:00
ev . addr . type = link_to_mgmt ( 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
2011-11-08 22:40:14 +04:00
return 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
2011-11-08 22:40:14 +04:00
int 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 ) ;
ev . addr . type = MGMT_ADDR_BREDR ;
2011-04-28 14:07:59 +04:00
ev . secure = secure ;
2011-01-22 07:10:07 +03:00
2011-11-08 22:40:14 +04:00
return mgmt_event ( MGMT_EV_PIN_CODE_REQUEST , hdev , & ev , sizeof ( ev ) ,
2012-03-08 08:25:00 +04:00
NULL ) ;
2011-01-22 07:10:07 +03:00
}
2011-11-08 22:40:14 +04:00
int mgmt_pin_code_reply_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 status )
2011-01-22 07:10:07 +03:00
{
struct pending_cmd * cmd ;
2011-02-19 18:05:59 +03:00
struct mgmt_rp_pin_code_reply rp ;
2011-01-22 07:10:07 +03:00
int err ;
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_PIN_CODE_REPLY , hdev ) ;
2011-01-22 07:10:07 +03:00
if ( ! cmd )
return - ENOENT ;
2012-02-17 16:24:57 +04:00
bacpy ( & rp . addr . bdaddr , bdaddr ) ;
rp . addr . type = MGMT_ADDR_BREDR ;
2011-02-19 18:05:59 +03:00
2012-02-18 17:07:59 +04:00
err = cmd_complete ( cmd - > sk , hdev - > id , MGMT_OP_PIN_CODE_REPLY ,
2012-03-08 08:25:00 +04:00
mgmt_status ( status ) , & rp , sizeof ( rp ) ) ;
2011-01-22 07:10:07 +03:00
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-22 07:10:07 +03:00
return err ;
}
2011-11-08 22:40:14 +04:00
int mgmt_pin_code_neg_reply_complete ( struct hci_dev * hdev , bdaddr_t * bdaddr ,
2012-03-08 08:25:00 +04:00
u8 status )
2011-01-22 07:10:07 +03:00
{
struct pending_cmd * cmd ;
2011-02-19 18:05:59 +03:00
struct mgmt_rp_pin_code_reply rp ;
2011-01-22 07:10:07 +03:00
int err ;
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_PIN_CODE_NEG_REPLY , hdev ) ;
2011-01-22 07:10:07 +03:00
if ( ! cmd )
return - ENOENT ;
2012-02-17 16:24:57 +04:00
bacpy ( & rp . addr . bdaddr , bdaddr ) ;
rp . addr . type = MGMT_ADDR_BREDR ;
2011-02-19 18:05:59 +03:00
2012-02-18 17:07:59 +04:00
err = cmd_complete ( cmd - > sk , hdev - > id , MGMT_OP_PIN_CODE_NEG_REPLY ,
2012-03-08 08:25:00 +04:00
mgmt_status ( status ) , & rp , sizeof ( rp ) ) ;
2011-01-22 07:10:07 +03:00
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-22 07:10:07 +03:00
return err ;
}
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 ,
2012-03-08 08:25:00 +04:00
u8 link_type , u8 addr_type , __le32 value ,
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 ) ;
ev . addr . type = link_to_mgmt ( link_type , addr_type ) ;
2011-04-28 22:28:56 +04:00
ev . confirm_hint = confirm_hint ;
2012-03-09 15:00:50 +04:00
ev . value = 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 ,
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 ) ;
ev . addr . type = link_to_mgmt ( 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-02-09 17:26:12 +04:00
u8 link_type , u8 addr_type , u8 status ,
u8 opcode )
2011-02-19 18:05:57 +03:00
{
struct pending_cmd * cmd ;
struct mgmt_rp_user_confirm_reply rp ;
int err ;
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( opcode , hdev ) ;
2011-02-19 18:05:57 +03:00
if ( ! cmd )
return - ENOENT ;
2012-02-09 17:26:12 +04:00
bacpy ( & rp . addr . bdaddr , bdaddr ) ;
rp . addr . type = link_to_mgmt ( link_type , addr_type ) ;
2012-02-18 17:07:59 +04:00
err = cmd_complete ( cmd - > sk , hdev - > id , opcode , mgmt_status ( status ) ,
2012-03-08 08:25:00 +04:00
& rp , sizeof ( rp ) ) ;
2011-02-19 18:05:57 +03:00
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-02-19 18:05:57 +03:00
return err ;
}
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-03-08 08:25:00 +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-03-08 08:25:00 +04:00
status , MGMT_OP_USER_PASSKEY_NEG_REPLY ) ;
2011-11-23 20:28:33 +04:00
}
2012-02-09 18:07:29 +04:00
int mgmt_auth_failed ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 link_type ,
2012-03-08 08:25:00 +04:00
u8 addr_type , u8 status )
2011-02-19 18:06:00 +03:00
{
struct mgmt_ev_auth_failed ev ;
2012-02-09 18:07:29 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = link_to_mgmt ( link_type , addr_type ) ;
2011-11-11 20:10:00 +04:00
ev . status = mgmt_status ( status ) ;
2011-02-19 18:06:00 +03:00
2011-11-08 22:40:14 +04:00
return mgmt_event ( MGMT_EV_AUTH_FAILED , hdev , & ev , sizeof ( ev ) , NULL ) ;
2011-02-19 18:06:00 +03:00
}
2011-03-16 15:29:37 +03:00
2012-02-17 01:56:27 +04:00
int mgmt_auth_enable_complete ( struct hci_dev * hdev , u8 status )
{
struct cmd_lookup match = { NULL , hdev } ;
2012-02-22 13:58:37 +04:00
bool changed = false ;
int err = 0 ;
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 ) ;
2012-02-17 01:56:27 +04:00
return 0 ;
}
2012-02-22 13:58:37 +04:00
if ( test_bit ( HCI_AUTH , & hdev - > flags ) ) {
if ( ! test_and_set_bit ( HCI_LINK_SECURITY , & hdev - > dev_flags ) )
changed = true ;
} else {
if ( test_and_clear_bit ( HCI_LINK_SECURITY , & hdev - > dev_flags ) )
changed = true ;
}
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 )
err = new_settings ( hdev , match . sk ) ;
2012-02-17 01:56:27 +04:00
if ( match . sk )
sock_put ( match . sk ) ;
return err ;
}
2012-02-21 02:52:42 +04:00
static int clear_eir ( struct hci_dev * hdev )
{
struct hci_cp_write_eir cp ;
if ( ! ( hdev - > features [ 6 ] & LMP_EXT_INQ ) )
return 0 ;
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 ) ) ;
return hci_send_cmd ( hdev , HCI_OP_WRITE_EIR , sizeof ( cp ) , & cp ) ;
}
2012-02-22 14:38:31 +04:00
int 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 } ;
2012-02-22 14:38:31 +04:00
bool changed = false ;
int err = 0 ;
2012-02-17 02:56:28 +04:00
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
2012-02-22 14:38:31 +04:00
if ( enable & & test_and_clear_bit ( HCI_SSP_ENABLED ,
2012-03-08 08:25:00 +04:00
& hdev - > dev_flags ) )
2012-02-22 14:38:31 +04:00
err = new_settings ( hdev , NULL ) ;
2012-03-08 08:25:00 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_SSP , hdev , cmd_status_rsp ,
& mgmt_err ) ;
2012-02-22 14:38:31 +04:00
return err ;
}
if ( enable ) {
if ( ! test_and_set_bit ( HCI_SSP_ENABLED , & hdev - > dev_flags ) )
changed = true ;
} else {
if ( test_and_clear_bit ( HCI_SSP_ENABLED , & hdev - > dev_flags ) )
changed = true ;
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 )
err = 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 ) ;
2012-02-22 17:10:59 +04:00
if ( test_bit ( HCI_SSP_ENABLED , & hdev - > dev_flags ) )
update_eir ( hdev ) ;
else
clear_eir ( hdev ) ;
2012-02-21 02:52:42 +04:00
2012-02-17 02:56:28 +04:00
return err ;
}
2012-02-24 01:09:40 +04:00
static void class_rsp ( struct pending_cmd * cmd , void * data )
{
struct cmd_lookup * match = data ;
cmd_complete ( cmd - > sk , cmd - > index , cmd - > opcode , match - > mgmt_status ,
2012-03-08 08:25:00 +04:00
match - > hdev - > dev_class , 3 ) ;
2012-02-24 01:09:40 +04:00
list_del ( & cmd - > list ) ;
if ( match - > sk = = NULL ) {
match - > sk = cmd - > sk ;
sock_hold ( match - > sk ) ;
}
mgmt_pending_free ( cmd ) ;
}
2012-02-22 21:38:01 +04:00
int mgmt_set_class_of_dev_complete ( struct hci_dev * hdev , u8 * dev_class ,
2012-03-08 08:25:00 +04:00
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 ) } ;
int err = 0 ;
2012-02-22 21:38:01 +04:00
2012-02-24 00:54:38 +04:00
clear_bit ( HCI_PENDING_CLASS , & hdev - > dev_flags ) ;
2012-02-24 01:09:40 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_DEV_CLASS , hdev , class_rsp , & match ) ;
mgmt_pending_foreach ( MGMT_OP_ADD_UUID , hdev , class_rsp , & match ) ;
mgmt_pending_foreach ( MGMT_OP_REMOVE_UUID , hdev , class_rsp , & match ) ;
if ( ! status )
2012-03-08 08:25:00 +04:00
err = mgmt_event ( MGMT_EV_CLASS_OF_DEV_CHANGED , hdev , dev_class ,
3 , NULL ) ;
2012-02-24 01:09:40 +04:00
if ( match . sk )
sock_put ( match . sk ) ;
2012-02-22 21:38:01 +04:00
return err ;
}
2011-11-08 22:40:14 +04:00
int mgmt_set_local_name_complete ( struct hci_dev * hdev , u8 * name , u8 status )
2011-03-16 15:29:37 +03:00
{
struct pending_cmd * cmd ;
struct mgmt_cp_set_local_name ev ;
2012-02-22 23:06:55 +04:00
bool changed = false ;
int err = 0 ;
if ( memcmp ( name , hdev - > dev_name , sizeof ( hdev - > dev_name ) ) ! = 0 ) {
memcpy ( hdev - > dev_name , name , sizeof ( hdev - > dev_name ) ) ;
changed = true ;
}
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
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_SET_LOCAL_NAME , hdev ) ;
2011-03-16 15:29:37 +03:00
if ( ! cmd )
goto send_event ;
2012-02-22 23:39:58 +04:00
/* Always assume that either the short or the complete name has
* changed if there was a pending mgmt command */
changed = true ;
2011-03-16 15:29:37 +03:00
if ( status ) {
2011-11-08 22:40:14 +04:00
err = cmd_status ( cmd - > sk , hdev - > id , MGMT_OP_SET_LOCAL_NAME ,
2012-03-08 08:25:00 +04:00
mgmt_status ( status ) ) ;
2011-03-16 15:29:37 +03:00
goto failed ;
}
2012-02-18 17:07:59 +04:00
err = cmd_complete ( cmd - > sk , hdev - > id , MGMT_OP_SET_LOCAL_NAME , 0 , & ev ,
2012-03-08 08:25:00 +04:00
sizeof ( ev ) ) ;
2011-03-16 15:29:37 +03:00
if ( err < 0 )
goto failed ;
send_event :
2012-02-22 23:06:55 +04:00
if ( changed )
err = mgmt_event ( MGMT_EV_LOCAL_NAME_CHANGED , hdev , & ev ,
2012-03-08 08:25:00 +04:00
sizeof ( ev ) , cmd ? cmd - > sk : NULL ) ;
2012-02-22 23:06:55 +04:00
2012-02-22 20:17:32 +04:00
update_eir ( hdev ) ;
2011-03-16 15:29:37 +03:00
failed :
if ( cmd )
mgmt_pending_remove ( cmd ) ;
return err ;
}
2011-03-22 15:12:21 +03:00
2011-11-08 22:40:14 +04:00
int mgmt_read_local_oob_data_reply_complete ( struct hci_dev * hdev , u8 * hash ,
2012-03-08 08:25:00 +04:00
u8 * randomizer , u8 status )
2011-03-22 15:12:21 +03:00
{
struct pending_cmd * cmd ;
int err ;
2011-11-08 22:40:14 +04:00
BT_DBG ( " %s status %u " , hdev - > name , status ) ;
2011-03-22 15:12:21 +03:00
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_READ_LOCAL_OOB_DATA , hdev ) ;
2011-03-22 15:12:21 +03:00
if ( ! cmd )
return - ENOENT ;
if ( status ) {
2012-03-08 08:25:00 +04:00
err = cmd_status ( cmd - > sk , hdev - > id , MGMT_OP_READ_LOCAL_OOB_DATA ,
mgmt_status ( status ) ) ;
2011-03-22 15:12:21 +03:00
} else {
struct mgmt_rp_read_local_oob_data rp ;
memcpy ( rp . hash , hash , sizeof ( rp . hash ) ) ;
memcpy ( rp . randomizer , randomizer , sizeof ( rp . randomizer ) ) ;
2011-11-08 22:40:14 +04:00
err = cmd_complete ( cmd - > sk , hdev - > id ,
2012-03-08 08:25:00 +04:00
MGMT_OP_READ_LOCAL_OOB_DATA , 0 , & rp ,
sizeof ( rp ) ) ;
2011-03-22 15:12:21 +03:00
}
mgmt_pending_remove ( cmd ) ;
return err ;
}
2011-03-31 00:57:16 +04:00
2012-02-22 18:37:11 +04:00
int mgmt_le_enable_complete ( struct hci_dev * hdev , u8 enable , u8 status )
{
struct cmd_lookup match = { NULL , hdev } ;
bool changed = false ;
int err = 0 ;
if ( status ) {
u8 mgmt_err = mgmt_status ( status ) ;
if ( enable & & test_and_clear_bit ( HCI_LE_ENABLED ,
2012-03-08 08:25:00 +04:00
& hdev - > dev_flags ) )
2012-03-16 19:02:56 +04:00
err = new_settings ( hdev , NULL ) ;
2012-02-22 18:37:11 +04:00
2012-03-16 19:02:56 +04:00
mgmt_pending_foreach ( MGMT_OP_SET_LE , hdev , cmd_status_rsp ,
& mgmt_err ) ;
2012-02-22 18:37:11 +04:00
return err ;
}
if ( enable ) {
if ( ! test_and_set_bit ( HCI_LE_ENABLED , & hdev - > dev_flags ) )
changed = true ;
} else {
if ( test_and_clear_bit ( HCI_LE_ENABLED , & hdev - > dev_flags ) )
changed = true ;
}
mgmt_pending_foreach ( MGMT_OP_SET_LE , hdev , settings_rsp , & match ) ;
if ( changed )
err = new_settings ( hdev , match . sk ) ;
if ( match . sk )
sock_put ( match . sk ) ;
return err ;
}
2011-11-09 15:58:58 +04:00
int mgmt_device_found ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 link_type ,
2012-03-08 08:25:00 +04:00
u8 addr_type , u8 * dev_class , s8 rssi , u8 cfm_name , u8
ssp , u8 * eir , u16 eir_len )
2011-03-31 00:57:16 +04:00
{
2012-01-15 21:51:59 +04:00
char buf [ 512 ] ;
struct mgmt_ev_device_found * ev = ( void * ) buf ;
2012-01-15 23:01:23 +04:00
size_t ev_size ;
2011-03-31 00:57:16 +04:00
2012-01-15 23:01:23 +04:00
/* Leave 5 bytes for a potential CoD field */
if ( sizeof ( * ev ) + eir_len + 5 > sizeof ( buf ) )
2012-01-11 01:20:49 +04:00
return - EINVAL ;
2012-01-15 23:01:23 +04:00
memset ( buf , 0 , sizeof ( buf ) ) ;
2012-01-15 21:51:59 +04:00
bacpy ( & ev - > addr . bdaddr , bdaddr ) ;
ev - > addr . type = link_to_mgmt ( link_type , addr_type ) ;
ev - > rssi = rssi ;
2012-02-23 02:00:32 +04:00
if ( cfm_name )
ev - > flags [ 0 ] | = MGMT_DEV_FOUND_CONFIRM_NAME ;
2012-02-23 02:38:59 +04:00
if ( ! ssp )
ev - > flags [ 0 ] | = MGMT_DEV_FOUND_LEGACY_PAIRING ;
2011-03-31 00:57:16 +04:00
2012-01-15 23:01:23 +04:00
if ( eir_len > 0 )
2012-01-15 21:51:59 +04:00
memcpy ( ev - > eir , eir , eir_len ) ;
2011-03-31 00:57:16 +04:00
2012-01-15 23:01:23 +04:00
if ( dev_class & & ! eir_has_data_type ( ev - > eir , eir_len , EIR_CLASS_OF_DEV ) )
eir_len = eir_append_data ( ev - > eir , eir_len , EIR_CLASS_OF_DEV ,
2012-03-08 08:25:00 +04:00
dev_class , 3 ) ;
2012-01-15 23:01:23 +04:00
2012-03-14 20:08:46 +04:00
ev - > eir_len = cpu_to_le16 ( eir_len ) ;
2012-01-15 23:01:23 +04:00
ev_size = sizeof ( * ev ) + eir_len ;
2011-09-10 01:56:26 +04:00
2012-01-15 21:51:59 +04:00
return 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
2012-01-17 23:48:47 +04:00
int mgmt_remote_name ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 link_type ,
2012-03-08 08:25:00 +04:00
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 ) ;
ev - > addr . type = link_to_mgmt ( link_type , addr_type ) ;
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
2012-02-04 02:06:00 +04:00
return mgmt_event ( MGMT_EV_DEVICE_FOUND , hdev , ev ,
2012-03-08 08:25:00 +04:00
sizeof ( * ev ) + eir_len , NULL ) ;
2011-03-30 14:18:12 +04:00
}
2011-04-27 18:29:57 +04:00
2011-11-10 00:14:25 +04:00
int mgmt_start_discovery_failed ( struct hci_dev * hdev , u8 status )
2011-11-01 19:06:44 +04:00
{
struct pending_cmd * cmd ;
2012-02-19 14:52:07 +04:00
u8 type ;
2011-11-01 19:06:44 +04:00
int err ;
2012-02-13 22:41:01 +04:00
hci_discovery_set_state ( hdev , DISCOVERY_STOPPED ) ;
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_START_DISCOVERY , hdev ) ;
2011-11-01 19:06:44 +04:00
if ( ! cmd )
return - ENOENT ;
2012-02-19 14:52:07 +04:00
type = hdev - > discovery . type ;
err = cmd_complete ( cmd - > sk , hdev - > id , cmd - > opcode , mgmt_status ( status ) ,
2012-03-08 08:25:00 +04:00
& type , sizeof ( type ) ) ;
2011-11-01 19:06:44 +04:00
mgmt_pending_remove ( cmd ) ;
return err ;
}
2011-11-10 00:14:26 +04:00
int mgmt_stop_discovery_failed ( struct hci_dev * hdev , u8 status )
{
struct pending_cmd * cmd ;
int err ;
cmd = mgmt_pending_find ( MGMT_OP_STOP_DISCOVERY , hdev ) ;
if ( ! cmd )
return - ENOENT ;
2012-02-21 01:25:18 +04:00
err = cmd_complete ( cmd - > sk , hdev - > id , cmd - > opcode , mgmt_status ( status ) ,
2012-03-08 08:25:00 +04:00
& hdev - > discovery . type , sizeof ( hdev - > discovery . type ) ) ;
2011-11-01 19:06:44 +04:00
mgmt_pending_remove ( cmd ) ;
return err ;
}
2011-11-08 22:40:14 +04:00
int 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
struct pending_cmd * cmd ;
2011-11-23 00:14:19 +04:00
BT_DBG ( " %s discovering %u " , hdev - > name , discovering ) ;
2011-11-01 19:06:44 +04:00
if ( discovering )
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_START_DISCOVERY , hdev ) ;
2011-11-01 19:06:44 +04:00
else
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_STOP_DISCOVERY , hdev ) ;
2011-11-01 19:06:44 +04:00
if ( cmd ! = NULL ) {
2012-02-19 14:52:07 +04:00
u8 type = hdev - > discovery . type ;
2012-03-08 08:25:00 +04:00
cmd_complete ( cmd - > sk , hdev - > id , cmd - > opcode , 0 , & type ,
sizeof ( type ) ) ;
2011-11-01 19:06:44 +04:00
mgmt_pending_remove ( cmd ) ;
}
2012-02-21 01:30:44 +04:00
memset ( & ev , 0 , sizeof ( ev ) ) ;
ev . type = hdev - > discovery . type ;
ev . discovering = discovering ;
return 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
2012-02-09 17:56:11 +04:00
int mgmt_device_blocked ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 type )
2011-08-25 17:48:02 +04:00
{
struct pending_cmd * cmd ;
struct mgmt_ev_device_blocked ev ;
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_BLOCK_DEVICE , hdev ) ;
2011-08-25 17:48:02 +04:00
2012-02-09 17:56:11 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = type ;
2011-08-25 17:48:02 +04:00
2011-11-08 22:40:14 +04:00
return mgmt_event ( MGMT_EV_DEVICE_BLOCKED , hdev , & ev , sizeof ( ev ) ,
2012-03-08 08:25:00 +04:00
cmd ? cmd - > sk : NULL ) ;
2011-08-25 17:48:02 +04:00
}
2012-02-09 17:56:11 +04:00
int mgmt_device_unblocked ( struct hci_dev * hdev , bdaddr_t * bdaddr , u8 type )
2011-08-25 17:48:02 +04:00
{
struct pending_cmd * cmd ;
struct mgmt_ev_device_unblocked ev ;
2011-11-08 22:40:15 +04:00
cmd = mgmt_pending_find ( MGMT_OP_UNBLOCK_DEVICE , hdev ) ;
2011-08-25 17:48:02 +04:00
2012-02-09 17:56:11 +04:00
bacpy ( & ev . addr . bdaddr , bdaddr ) ;
ev . addr . type = type ;
2011-08-25 17:48:02 +04:00
2011-11-08 22:40:14 +04:00
return mgmt_event ( MGMT_EV_DEVICE_UNBLOCKED , hdev , & ev , sizeof ( ev ) ,
2012-03-08 08:25:00 +04:00
cmd ? cmd - > sk : NULL ) ;
2011-08-25 17:48:02 +04:00
}
2012-02-21 00:47:49 +04:00
module_param ( enable_hs , bool , 0644 ) ;
MODULE_PARM_DESC ( enable_hs , " Enable High Speed support " ) ;
module_param ( enable_le , bool , 0644 ) ;
MODULE_PARM_DESC ( enable_le , " Enable Low Energy support " ) ;