2010-12-08 01:21:06 +03:00
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright ( C ) 2010 Nokia Corporation
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-02-17 16:16:32 +03:00
# include <linux/uaccess.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>
2010-12-13 22:07:04 +03:00
# define MGMT_VERSION 0
# define MGMT_REVISION 1
2010-12-16 11:17:38 +03:00
struct pending_cmd {
struct list_head list ;
__u16 opcode ;
int index ;
void * cmd ;
struct sock * sk ;
2011-02-19 18:05:56 +03:00
void * user_data ;
2010-12-16 11:17:38 +03:00
} ;
LIST_HEAD ( cmd_list ) ;
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-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 ;
put_unaligned_le16 ( cmd , & ev - > opcode ) ;
if ( sock_queue_rcv_skb ( sk , skb ) < 0 )
kfree_skb ( skb ) ;
return 0 ;
}
2011-02-25 21:05:48 +03:00
static int cmd_complete ( struct sock * sk , u16 index , u16 cmd , 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 ;
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 ) ;
put_unaligned_le16 ( cmd , & ev - > opcode ) ;
2011-02-28 16:09:50 +03:00
if ( rp )
memcpy ( ev - > data , rp , rp_len ) ;
2010-12-13 22:07:04 +03:00
if ( sock_queue_rcv_skb ( sk , skb ) < 0 )
kfree_skb ( skb ) ;
return 0 ;
}
2011-01-22 07:46:43 +03:00
static int read_version ( struct sock * sk )
{
struct mgmt_rp_read_version rp ;
BT_DBG ( " sock %p " , sk ) ;
rp . version = MGMT_VERSION ;
put_unaligned_le16 ( MGMT_REVISION , & rp . revision ) ;
2011-02-25 21:05:48 +03:00
return cmd_complete ( sk , MGMT_INDEX_NONE , MGMT_OP_READ_VERSION , & rp ,
sizeof ( rp ) ) ;
2011-01-22 07:46:43 +03:00
}
2010-12-13 22:07:05 +03:00
static int read_index_list ( struct sock * sk )
{
struct mgmt_rp_read_index_list * rp ;
struct list_head * p ;
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
put_unaligned_le16 ( count , & rp - > num_controllers ) ;
i = 0 ;
list_for_each ( p , & hci_dev_list ) {
struct hci_dev * d = list_entry ( p , struct hci_dev , list ) ;
2010-12-15 14:53:18 +03:00
hci_del_off_timer ( d ) ;
2011-01-04 12:54:26 +03:00
set_bit ( HCI_MGMT , & d - > flags ) ;
2010-12-15 14:53:18 +03:00
if ( test_bit ( HCI_SETUP , & d - > flags ) )
continue ;
2010-12-13 22:07:05 +03:00
put_unaligned_le16 ( d - > id , & rp - > index [ i + + ] ) ;
BT_DBG ( " Added hci%u " , d - > id ) ;
}
read_unlock ( & hci_dev_list_lock ) ;
2011-02-25 21:05:48 +03:00
err = cmd_complete ( sk , MGMT_INDEX_NONE , MGMT_OP_READ_INDEX_LIST , rp ,
rp_len ) ;
2010-12-13 22:07:05 +03:00
2011-01-22 07:46:43 +03:00
kfree ( rp ) ;
return err ;
2010-12-13 22:07:05 +03:00
}
2011-02-25 21:05:48 +03:00
static int read_controller_info ( struct sock * sk , u16 index )
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
struct hci_dev * hdev ;
2010-12-08 01:21:06 +03:00
2011-02-25 21:05:48 +03:00
BT_DBG ( " sock %p hci%u " , sk , index ) ;
2010-12-13 22:07:06 +03:00
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-22 07:46:43 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_READ_INFO , ENODEV ) ;
2010-12-13 22:07:06 +03:00
2010-12-15 14:53:18 +03:00
hci_del_off_timer ( hdev ) ;
2010-12-13 22:07:06 +03:00
hci_dev_lock_bh ( hdev ) ;
2011-01-04 12:54:26 +03:00
set_bit ( HCI_MGMT , & hdev - > flags ) ;
2011-01-22 07:46:43 +03:00
rp . type = hdev - > dev_type ;
2010-12-13 22:07:06 +03:00
2011-01-22 07:46:43 +03:00
rp . powered = test_bit ( HCI_UP , & hdev - > flags ) ;
rp . connectable = test_bit ( HCI_PSCAN , & hdev - > flags ) ;
rp . discoverable = test_bit ( HCI_ISCAN , & hdev - > flags ) ;
rp . pairable = test_bit ( HCI_PSCAN , & hdev - > flags ) ;
2010-12-13 22:07:06 +03:00
if ( test_bit ( HCI_AUTH , & hdev - > flags ) )
2011-01-22 07:46:43 +03:00
rp . sec_mode = 3 ;
2010-12-13 22:07:06 +03:00
else if ( hdev - > ssp_mode > 0 )
2011-01-22 07:46:43 +03:00
rp . sec_mode = 4 ;
2010-12-13 22:07:06 +03:00
else
2011-01-22 07:46:43 +03:00
rp . sec_mode = 2 ;
2010-12-13 22:07:06 +03:00
2011-01-22 07:46:43 +03:00
bacpy ( & rp . bdaddr , & hdev - > bdaddr ) ;
memcpy ( rp . features , hdev - > features , 8 ) ;
memcpy ( rp . dev_class , hdev - > dev_class , 3 ) ;
put_unaligned_le16 ( hdev - > manufacturer , & rp . manufacturer ) ;
rp . hci_ver = hdev - > hci_ver ;
put_unaligned_le16 ( hdev - > hci_rev , & rp . hci_rev ) ;
2010-12-13 22:07:06 +03:00
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
2010-12-08 01:21:06 +03:00
2011-02-25 21:05:48 +03:00
return cmd_complete ( sk , index , MGMT_OP_READ_INFO , & rp , 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 ) ;
kfree ( cmd - > cmd ) ;
kfree ( cmd ) ;
}
2011-02-19 18:05:55 +03:00
static struct pending_cmd * mgmt_pending_add ( struct sock * sk , u16 opcode ,
u16 index , 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 ;
cmd - > index = index ;
cmd - > cmd = kmalloc ( len , GFP_ATOMIC ) ;
if ( ! cmd - > cmd ) {
kfree ( cmd ) ;
2011-02-19 18:05:55 +03:00
return NULL ;
2010-12-16 11:17:38 +03:00
}
memcpy ( cmd - > cmd , data , len ) ;
cmd - > sk = sk ;
sock_hold ( sk ) ;
list_add ( & cmd - > list , & cmd_list ) ;
2011-02-19 18:05:55 +03:00
return cmd ;
2010-12-16 11:17:38 +03:00
}
static void mgmt_pending_foreach ( u16 opcode , int index ,
void ( * cb ) ( struct pending_cmd * cmd , void * data ) ,
void * data )
{
struct list_head * p , * n ;
list_for_each_safe ( p , n , & cmd_list ) {
struct pending_cmd * cmd ;
cmd = list_entry ( p , struct pending_cmd , list ) ;
if ( cmd - > opcode ! = opcode )
continue ;
if ( index > = 0 & & cmd - > index ! = index )
continue ;
cb ( cmd , data ) ;
}
}
static struct pending_cmd * mgmt_pending_find ( u16 opcode , int index )
{
struct list_head * p ;
list_for_each ( p , & cmd_list ) {
struct pending_cmd * cmd ;
cmd = list_entry ( p , struct pending_cmd , list ) ;
if ( cmd - > opcode ! = opcode )
continue ;
if ( index > = 0 & & cmd - > index ! = index )
continue ;
return cmd ;
}
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-02-25 21:05:48 +03:00
static int set_powered ( struct sock * sk , u16 index , unsigned char * data , u16 len )
2010-12-16 11:17:38 +03:00
{
2010-12-30 01:38:22 +03:00
struct mgmt_mode * cp ;
2010-12-16 11:17:38 +03:00
struct hci_dev * hdev ;
2011-02-19 18:05:55 +03:00
struct pending_cmd * cmd ;
int err , up ;
2010-12-16 11:17:38 +03:00
cp = ( void * ) data ;
2011-02-25 21:05:48 +03:00
BT_DBG ( " request for hci%u " , index ) ;
2010-12-16 11:17:38 +03:00
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_SET_POWERED , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2010-12-16 11:17:38 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_POWERED , ENODEV ) ;
2010-12-16 11:17:38 +03:00
hci_dev_lock_bh ( hdev ) ;
up = test_bit ( HCI_UP , & hdev - > flags ) ;
2010-12-30 01:38:22 +03:00
if ( ( cp - > val & & up ) | | ( ! cp - > val & & ! up ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_SET_POWERED , EALREADY ) ;
2010-12-16 11:17:38 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
if ( mgmt_pending_find ( MGMT_OP_SET_POWERED , index ) ) {
err = cmd_status ( sk , index , MGMT_OP_SET_POWERED , EBUSY ) ;
2010-12-16 11:17:38 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_POWERED , index , 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 )
2010-12-16 11:17:38 +03:00
queue_work ( hdev - > workqueue , & hdev - > power_on ) ;
else
queue_work ( hdev - > workqueue , & hdev - > power_off ) ;
2011-02-19 18:05:55 +03:00
err = 0 ;
2010-12-16 11:17:38 +03:00
failed :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
2011-02-19 18:05:55 +03:00
return err ;
2010-12-16 11:17:38 +03:00
}
2011-02-25 21:05:48 +03:00
static int set_discoverable ( struct sock * sk , u16 index , unsigned char * data ,
u16 len )
2010-12-29 17:00:25 +03:00
{
2010-12-30 01:38:22 +03:00
struct mgmt_mode * cp ;
2010-12-29 17:00:25 +03:00
struct hci_dev * hdev ;
2011-02-19 18:05:55 +03:00
struct pending_cmd * cmd ;
2010-12-29 17:00:25 +03:00
u8 scan ;
int err ;
cp = ( void * ) data ;
2011-02-25 21:05:48 +03:00
BT_DBG ( " request for hci%u " , index ) ;
2010-12-29 17:00:25 +03:00
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_SET_DISCOVERABLE , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2010-12-29 17:00:25 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_DISCOVERABLE , ENODEV ) ;
2010-12-29 17:00:25 +03:00
hci_dev_lock_bh ( hdev ) ;
if ( ! test_bit ( HCI_UP , & hdev - > flags ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_SET_DISCOVERABLE , ENETDOWN ) ;
2010-12-29 17:00:25 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
if ( mgmt_pending_find ( MGMT_OP_SET_DISCOVERABLE , index ) | |
mgmt_pending_find ( MGMT_OP_SET_CONNECTABLE , index ) ) {
err = cmd_status ( sk , index , MGMT_OP_SET_DISCOVERABLE , EBUSY ) ;
2010-12-29 17:00:25 +03:00
goto failed ;
}
2010-12-30 01:38:22 +03:00
if ( cp - > val = = test_bit ( HCI_ISCAN , & hdev - > flags ) & &
2010-12-29 17:00:25 +03:00
test_bit ( HCI_PSCAN , & hdev - > flags ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_SET_DISCOVERABLE , EALREADY ) ;
2010-12-29 17:00:25 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_DISCOVERABLE , index , 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 ;
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
failed :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int set_connectable ( struct sock * sk , u16 index , unsigned char * data ,
u16 len )
2010-12-30 01:18:33 +03:00
{
2010-12-30 01:38:22 +03:00
struct mgmt_mode * cp ;
2010-12-30 01:18:33 +03:00
struct hci_dev * hdev ;
2011-02-19 18:05:55 +03:00
struct pending_cmd * cmd ;
2010-12-30 01:18:33 +03:00
u8 scan ;
int err ;
cp = ( void * ) data ;
2011-02-25 21:05:48 +03:00
BT_DBG ( " request for hci%u " , index ) ;
2010-12-30 01:18:33 +03:00
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_SET_CONNECTABLE , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2010-12-30 01:18:33 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_CONNECTABLE , ENODEV ) ;
2010-12-30 01:18:33 +03:00
hci_dev_lock_bh ( hdev ) ;
if ( ! test_bit ( HCI_UP , & hdev - > flags ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_SET_CONNECTABLE , ENETDOWN ) ;
2010-12-30 01:18:33 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
if ( mgmt_pending_find ( MGMT_OP_SET_DISCOVERABLE , index ) | |
mgmt_pending_find ( MGMT_OP_SET_CONNECTABLE , index ) ) {
err = cmd_status ( sk , index , MGMT_OP_SET_CONNECTABLE , EBUSY ) ;
2010-12-30 01:18:33 +03:00
goto failed ;
}
2010-12-30 01:38:22 +03:00
if ( cp - > val = = test_bit ( HCI_PSCAN , & hdev - > flags ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_SET_CONNECTABLE , EALREADY ) ;
2010-12-30 01:18:33 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_SET_CONNECTABLE , index , 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
2010-12-30 01:38:22 +03:00
if ( cp - > val )
2010-12-30 01:18:33 +03:00
scan = SCAN_PAGE ;
else
scan = 0 ;
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 :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int mgmt_event ( u16 event , u16 index , void * data , u16 data_len ,
struct sock * skip_sk )
2011-01-26 14:11:03 +03:00
{
struct sk_buff * skb ;
struct mgmt_hdr * hdr ;
skb = alloc_skb ( sizeof ( * hdr ) + data_len , GFP_ATOMIC ) ;
if ( ! skb )
return - ENOMEM ;
bt_cb ( skb ) - > channel = HCI_CHANNEL_CONTROL ;
hdr = ( void * ) skb_put ( skb , sizeof ( * hdr ) ) ;
hdr - > opcode = cpu_to_le16 ( event ) ;
2011-02-25 21:05:48 +03:00
hdr - > index = cpu_to_le16 ( index ) ;
2011-01-26 14:11:03 +03:00
hdr - > len = cpu_to_le16 ( data_len ) ;
2011-02-25 21:05:48 +03:00
if ( data )
memcpy ( skb_put ( skb , data_len ) , data , data_len ) ;
2011-01-26 14:11:03 +03:00
hci_send_to_sock ( NULL , skb , skip_sk ) ;
kfree_skb ( skb ) ;
return 0 ;
}
2011-01-26 14:07:10 +03:00
static int send_mode_rsp ( struct sock * sk , u16 opcode , u16 index , u8 val )
{
2011-01-22 07:46:43 +03:00
struct mgmt_mode rp ;
2011-01-26 14:07:10 +03:00
2011-01-22 07:46:43 +03:00
rp . val = val ;
2011-01-26 14:07:10 +03:00
2011-02-25 21:05:48 +03:00
return cmd_complete ( sk , index , opcode , & rp , sizeof ( rp ) ) ;
2011-01-26 14:07:10 +03:00
}
2011-02-25 21:05:48 +03:00
static int set_pairable ( struct sock * sk , u16 index , unsigned char * data ,
u16 len )
2011-01-26 14:11:03 +03:00
{
struct mgmt_mode * cp , ev ;
struct hci_dev * hdev ;
int err ;
cp = ( void * ) data ;
2011-02-25 21:05:48 +03:00
BT_DBG ( " request for hci%u " , index ) ;
2011-01-26 14:11:03 +03:00
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_SET_PAIRABLE , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-26 14:11:03 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_PAIRABLE , ENODEV ) ;
2011-01-26 14:11:03 +03:00
hci_dev_lock_bh ( hdev ) ;
if ( cp - > val )
set_bit ( HCI_PAIRABLE , & hdev - > flags ) ;
else
clear_bit ( HCI_PAIRABLE , & hdev - > flags ) ;
2011-02-25 21:05:48 +03:00
err = send_mode_rsp ( sk , MGMT_OP_SET_PAIRABLE , index , cp - > val ) ;
2011-01-26 14:11:03 +03:00
if ( err < 0 )
goto failed ;
ev . val = cp - > val ;
2011-02-25 21:05:48 +03:00
err = mgmt_event ( MGMT_EV_PAIRABLE , index , & ev , sizeof ( ev ) , sk ) ;
2011-01-26 14:11:03 +03:00
failed :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-01-13 22:56:52 +03:00
static u8 get_service_classes ( struct hci_dev * hdev )
{
struct list_head * p ;
u8 val = 0 ;
list_for_each ( p , & hdev - > uuids ) {
struct bt_uuid * uuid = list_entry ( p , struct bt_uuid , list ) ;
val | = uuid - > svc_hint ;
}
return val ;
}
static int update_class ( struct hci_dev * hdev )
{
u8 cod [ 3 ] ;
BT_DBG ( " %s " , hdev - > name ) ;
if ( test_bit ( HCI_SERVICE_CACHE , & hdev - > flags ) )
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 ;
return hci_send_cmd ( hdev , HCI_OP_WRITE_CLASS_OF_DEV , sizeof ( cod ) , cod ) ;
}
2011-02-25 21:05:48 +03:00
static int add_uuid ( struct sock * sk , u16 index , unsigned char * data , u16 len )
2011-01-04 13:08:51 +03:00
{
struct mgmt_cp_add_uuid * cp ;
struct hci_dev * hdev ;
struct bt_uuid * uuid ;
int err ;
cp = ( void * ) data ;
2011-02-25 21:05:48 +03:00
BT_DBG ( " request for hci%u " , index ) ;
2011-01-04 13:08:51 +03:00
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_ADD_UUID , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-04 13:08:51 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_ADD_UUID , ENODEV ) ;
2011-01-04 13:08:51 +03:00
hci_dev_lock_bh ( hdev ) ;
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-02-25 21:05:48 +03:00
err = cmd_complete ( sk , index , MGMT_OP_ADD_UUID , NULL , 0 ) ;
2011-01-04 13:08:51 +03:00
failed :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int remove_uuid ( struct sock * sk , u16 index , unsigned char * data , u16 len )
2011-01-04 13:08:51 +03:00
{
struct list_head * p , * n ;
2011-02-25 21:05:47 +03:00
struct mgmt_cp_remove_uuid * cp ;
2011-01-04 13:08:51 +03:00
struct hci_dev * hdev ;
u8 bt_uuid_any [ ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
int err , found ;
cp = ( void * ) data ;
2011-02-25 21:05:48 +03:00
BT_DBG ( " request for hci%u " , index ) ;
2011-01-04 13:08:51 +03:00
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_REMOVE_UUID , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-04 13:08:51 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_REMOVE_UUID , ENODEV ) ;
2011-01-04 13:08:51 +03:00
hci_dev_lock_bh ( hdev ) ;
if ( memcmp ( cp - > uuid , bt_uuid_any , 16 ) = = 0 ) {
err = hci_uuids_clear ( hdev ) ;
goto unlock ;
}
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 ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_REMOVE_UUID , ENOENT ) ;
2011-01-04 13:08:51 +03:00
goto unlock ;
}
2011-01-13 22:56:52 +03:00
err = update_class ( hdev ) ;
if ( err < 0 )
goto unlock ;
2011-02-25 21:05:48 +03:00
err = cmd_complete ( sk , index , MGMT_OP_REMOVE_UUID , NULL , 0 ) ;
2011-01-04 13:08:51 +03:00
unlock :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int set_dev_class ( struct sock * sk , u16 index , unsigned char * data ,
u16 len )
2011-01-13 22:56:52 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_set_dev_class * cp ;
int err ;
cp = ( void * ) data ;
2011-02-25 21:05:48 +03:00
BT_DBG ( " request for hci%u " , index ) ;
2011-01-13 22:56:52 +03:00
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_SET_DEV_CLASS , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-13 22:56:52 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_DEV_CLASS , ENODEV ) ;
2011-01-13 22:56:52 +03:00
hci_dev_lock_bh ( hdev ) ;
hdev - > major_class = cp - > major ;
hdev - > minor_class = cp - > minor ;
err = update_class ( hdev ) ;
if ( err = = 0 )
2011-02-25 21:05:48 +03:00
err = cmd_complete ( sk , index , MGMT_OP_SET_DEV_CLASS , NULL , 0 ) ;
2011-01-13 22:56:52 +03:00
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int set_service_cache ( struct sock * sk , u16 index , unsigned char * data ,
u16 len )
2011-01-13 22:56:52 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_set_service_cache * cp ;
int err ;
cp = ( void * ) data ;
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
2011-03-01 18:55:34 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_SERVICE_CACHE , EINVAL ) ;
2011-02-25 21:05:49 +03:00
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-13 22:56:52 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_SERVICE_CACHE , ENODEV ) ;
2011-01-13 22:56:52 +03:00
hci_dev_lock_bh ( hdev ) ;
2011-02-25 21:05:48 +03:00
BT_DBG ( " hci%u enable %d " , index , cp - > enable ) ;
2011-01-13 22:56:52 +03:00
if ( cp - > enable ) {
set_bit ( HCI_SERVICE_CACHE , & hdev - > flags ) ;
err = 0 ;
} else {
clear_bit ( HCI_SERVICE_CACHE , & hdev - > flags ) ;
err = update_class ( hdev ) ;
}
if ( err = = 0 )
2011-02-25 21:05:48 +03:00
err = cmd_complete ( sk , index , MGMT_OP_SET_SERVICE_CACHE , NULL ,
0 ) ;
2011-01-13 22:56:52 +03:00
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int load_keys ( struct sock * sk , u16 index , unsigned char * data , u16 len )
2011-01-17 15:41:05 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_load_keys * cp ;
2011-02-25 21:05:48 +03:00
u16 key_count , expected_len ;
2011-01-17 15:41:05 +03:00
int i ;
cp = ( void * ) data ;
2011-02-25 21:05:49 +03:00
if ( len < sizeof ( * cp ) )
return - EINVAL ;
2011-01-17 15:41:05 +03:00
key_count = get_unaligned_le16 ( & cp - > key_count ) ;
expected_len = sizeof ( * cp ) + key_count * sizeof ( struct mgmt_key_info ) ;
if ( expected_len ! = len ) {
BT_ERR ( " load_keys: expected %u bytes, got %u bytes " ,
len , expected_len ) ;
return - EINVAL ;
}
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-17 15:41:05 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_LOAD_KEYS , ENODEV ) ;
2011-01-17 15:41:05 +03:00
2011-02-25 21:05:48 +03:00
BT_DBG ( " hci%u debug_keys %u key_count %u " , index , cp - > debug_keys ,
2011-01-17 15:41:05 +03:00
key_count ) ;
hci_dev_lock_bh ( hdev ) ;
hci_link_keys_clear ( hdev ) ;
set_bit ( HCI_LINK_KEYS , & hdev - > flags ) ;
if ( cp - > debug_keys )
set_bit ( HCI_DEBUG_KEYS , & hdev - > flags ) ;
else
clear_bit ( HCI_DEBUG_KEYS , & hdev - > flags ) ;
for ( i = 0 ; i < key_count ; i + + ) {
struct mgmt_key_info * key = & cp - > keys [ i ] ;
hci_add_link_key ( hdev , 0 , & key - > bdaddr , key - > val , key - > type ,
key - > pin_len ) ;
}
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return 0 ;
}
2011-02-25 21:05:48 +03:00
static int remove_key ( struct sock * sk , u16 index , unsigned char * data , u16 len )
2011-01-17 15:41:05 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_remove_key * cp ;
struct hci_conn * conn ;
int err ;
cp = ( void * ) data ;
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_REMOVE_KEY , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-17 15:41:05 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_REMOVE_KEY , ENODEV ) ;
2011-01-17 15:41:05 +03:00
hci_dev_lock_bh ( hdev ) ;
err = hci_remove_link_key ( hdev , & cp - > bdaddr ) ;
if ( err < 0 ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_REMOVE_KEY , - err ) ;
2011-01-17 15:41:05 +03:00
goto unlock ;
}
err = 0 ;
if ( ! test_bit ( HCI_UP , & hdev - > flags ) | | ! cp - > disconnect )
goto unlock ;
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , & cp - > bdaddr ) ;
if ( conn ) {
struct hci_cp_disconnect dc ;
put_unaligned_le16 ( conn - > handle , & dc . handle ) ;
dc . reason = 0x13 ; /* Remote User Terminated Connection */
err = hci_send_cmd ( hdev , HCI_OP_DISCONNECT , 0 , NULL ) ;
}
unlock :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int disconnect ( struct sock * sk , u16 index , unsigned char * data , u16 len )
2011-01-20 13:40:27 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_disconnect * cp ;
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 ( " " ) ;
cp = ( void * ) data ;
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_DISCONNECT , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-20 13:40:27 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_DISCONNECT , ENODEV ) ;
2011-01-20 13:40:27 +03:00
hci_dev_lock_bh ( hdev ) ;
if ( ! test_bit ( HCI_UP , & hdev - > flags ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_DISCONNECT , ENETDOWN ) ;
2011-01-20 13:40:27 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
if ( mgmt_pending_find ( MGMT_OP_DISCONNECT , index ) ) {
err = cmd_status ( sk , index , MGMT_OP_DISCONNECT , EBUSY ) ;
2011-01-20 13:40:27 +03:00
goto failed ;
}
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , & cp - > bdaddr ) ;
if ( ! conn ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_DISCONNECT , ENOTCONN ) ;
2011-01-20 13:40:27 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_DISCONNECT , index , 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
put_unaligned_le16 ( conn - > handle , & dc . handle ) ;
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 :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-03-01 18:55:32 +03:00
static int get_connections ( struct sock * sk , u16 index )
2011-01-21 14:56:35 +03:00
{
struct mgmt_rp_get_connections * rp ;
struct hci_dev * hdev ;
struct list_head * p ;
2011-01-22 07:46:43 +03:00
size_t rp_len ;
2011-02-25 21:05:48 +03:00
u16 count ;
2011-01-21 14:56:35 +03:00
int i , err ;
BT_DBG ( " " ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-21 14:56:35 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_GET_CONNECTIONS , ENODEV ) ;
2011-01-21 14:56:35 +03:00
hci_dev_lock_bh ( hdev ) ;
count = 0 ;
list_for_each ( p , & hdev - > conn_hash . list ) {
count + + ;
}
2011-01-22 07:46:43 +03:00
rp_len = sizeof ( * rp ) + ( count * sizeof ( bdaddr_t ) ) ;
rp = kmalloc ( rp_len , GFP_ATOMIC ) ;
if ( ! rp ) {
2011-01-21 14:56:35 +03:00
err = - ENOMEM ;
goto unlock ;
}
put_unaligned_le16 ( count , & rp - > conn_count ) ;
read_lock ( & hci_dev_list_lock ) ;
i = 0 ;
list_for_each ( p , & hdev - > conn_hash . list ) {
struct hci_conn * c = list_entry ( p , struct hci_conn , list ) ;
bacpy ( & rp - > conn [ i + + ] , & c - > dst ) ;
}
read_unlock ( & hci_dev_list_lock ) ;
2011-02-25 21:05:48 +03:00
err = cmd_complete ( sk , index , MGMT_OP_GET_CONNECTIONS , rp , rp_len ) ;
2011-01-21 14:56:35 +03:00
unlock :
2011-01-22 07:46:43 +03:00
kfree ( rp ) ;
2011-01-21 14:56:35 +03:00
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int pin_code_reply ( struct sock * sk , u16 index , unsigned char * data ,
u16 len )
2011-01-22 07:10:07 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_pin_code_reply * cp ;
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 ( " " ) ;
cp = ( void * ) data ;
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_PIN_CODE_REPLY , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-22 07:10:07 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_PIN_CODE_REPLY , ENODEV ) ;
2011-01-22 07:10:07 +03:00
hci_dev_lock_bh ( hdev ) ;
if ( ! test_bit ( HCI_UP , & hdev - > flags ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_PIN_CODE_REPLY , ENETDOWN ) ;
2011-01-22 07:10:07 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_PIN_CODE_REPLY , index , 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
bacpy ( & reply . bdaddr , & cp - > bdaddr ) ;
reply . pin_len = cp - > pin_len ;
memcpy ( reply . pin_code , cp - > pin_code , 16 ) ;
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 :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int pin_code_neg_reply ( struct sock * sk , u16 index , unsigned char * data ,
u16 len )
2011-01-22 07:10:07 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_pin_code_neg_reply * cp ;
2011-02-19 18:05:55 +03:00
struct pending_cmd * cmd ;
2011-01-22 07:10:07 +03:00
int err ;
BT_DBG ( " " ) ;
cp = ( void * ) data ;
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_PIN_CODE_NEG_REPLY ,
EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-22 07:10:07 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_PIN_CODE_NEG_REPLY ,
ENODEV ) ;
2011-01-22 07:10:07 +03:00
hci_dev_lock_bh ( hdev ) ;
if ( ! test_bit ( HCI_UP , & hdev - > flags ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_PIN_CODE_NEG_REPLY ,
ENETDOWN ) ;
2011-01-22 07:10:07 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_PIN_CODE_NEG_REPLY , index ,
2011-01-22 07:10:07 +03:00
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
2011-03-01 18:55:33 +03:00
err = hci_send_cmd ( hdev , HCI_OP_PIN_CODE_NEG_REPLY , sizeof ( cp - > bdaddr ) ,
2011-01-22 07:10:07 +03:00
& cp - > bdaddr ) ;
if ( err < 0 )
2011-02-19 18:06:02 +03:00
mgmt_pending_remove ( cmd ) ;
2011-01-22 07:10:07 +03:00
failed :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int set_io_capability ( struct sock * sk , u16 index , unsigned char * data ,
u16 len )
2011-01-25 14:28:33 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_set_io_capability * cp ;
BT_DBG ( " " ) ;
cp = ( void * ) data ;
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
2011-03-01 18:55:34 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_IO_CAPABILITY , EINVAL ) ;
2011-02-25 21:05:49 +03:00
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-01-25 14:28:33 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_SET_IO_CAPABILITY , ENODEV ) ;
2011-01-25 14:28:33 +03:00
hci_dev_lock_bh ( hdev ) ;
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
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
2011-02-25 21:05:48 +03:00
return cmd_complete ( sk , index , MGMT_OP_SET_IO_CAPABILITY , 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 ;
struct list_head * p ;
list_for_each ( p , & cmd_list ) {
struct pending_cmd * cmd ;
cmd = list_entry ( p , struct pending_cmd , list ) ;
if ( cmd - > opcode ! = MGMT_OP_PAIR_DEVICE )
continue ;
if ( cmd - > index ! = hdev - > id )
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 ;
bacpy ( & rp . bdaddr , & conn - > dst ) ;
rp . status = status ;
2011-02-25 21:05:48 +03:00
cmd_complete ( cmd - > sk , cmd - > index , MGMT_OP_PAIR_DEVICE , & 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 ) ;
if ( ! cmd ) {
BT_DBG ( " Unable to find a pending command " ) ;
return ;
}
pairing_complete ( cmd , status ) ;
}
2011-02-25 21:05:48 +03:00
static int pair_device ( struct sock * sk , u16 index , unsigned char * data , u16 len )
2011-02-19 18:05:56 +03:00
{
struct hci_dev * hdev ;
struct mgmt_cp_pair_device * cp ;
struct pending_cmd * cmd ;
u8 sec_level , auth_type ;
struct hci_conn * conn ;
int err ;
BT_DBG ( " " ) ;
cp = ( void * ) data ;
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , MGMT_OP_PAIR_DEVICE , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-02-19 18:05:56 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , MGMT_OP_PAIR_DEVICE , ENODEV ) ;
2011-02-19 18:05:56 +03:00
hci_dev_lock_bh ( hdev ) ;
if ( cp - > io_cap = = 0x03 ) {
sec_level = BT_SECURITY_MEDIUM ;
auth_type = HCI_AT_DEDICATED_BONDING ;
} else {
sec_level = BT_SECURITY_HIGH ;
auth_type = HCI_AT_DEDICATED_BONDING_MITM ;
}
conn = hci_connect ( hdev , ACL_LINK , & cp - > bdaddr , sec_level , auth_type ) ;
2011-02-22 22:10:53 +03:00
if ( IS_ERR ( conn ) ) {
err = PTR_ERR ( conn ) ;
2011-02-19 18:05:56 +03:00
goto unlock ;
}
if ( conn - > connect_cfm_cb ) {
hci_conn_put ( conn ) ;
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , MGMT_OP_PAIR_DEVICE , EBUSY ) ;
2011-02-19 18:05:56 +03:00
goto unlock ;
}
2011-02-25 21:05:48 +03:00
cmd = mgmt_pending_add ( sk , MGMT_OP_PAIR_DEVICE , index , data , len ) ;
2011-02-19 18:05:56 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
hci_conn_put ( conn ) ;
goto unlock ;
}
conn - > connect_cfm_cb = pairing_complete_cb ;
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 :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2011-02-25 21:05:48 +03:00
static int user_confirm_reply ( struct sock * sk , u16 index , unsigned char * data ,
u16 len , int success )
2011-02-19 18:05:57 +03:00
{
struct mgmt_cp_user_confirm_reply * cp = ( void * ) data ;
2011-02-25 21:05:48 +03:00
u16 mgmt_op , hci_op ;
2011-02-19 18:05:57 +03:00
struct pending_cmd * cmd ;
struct hci_dev * hdev ;
int err ;
BT_DBG ( " " ) ;
if ( success ) {
mgmt_op = MGMT_OP_USER_CONFIRM_REPLY ;
hci_op = HCI_OP_USER_CONFIRM_REPLY ;
} else {
mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY ;
hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY ;
}
2011-02-25 21:05:49 +03:00
if ( len ! = sizeof ( * cp ) )
return cmd_status ( sk , index , mgmt_op , EINVAL ) ;
2011-02-25 21:05:48 +03:00
hdev = hci_dev_get ( index ) ;
2011-02-19 18:05:57 +03:00
if ( ! hdev )
2011-02-25 21:05:48 +03:00
return cmd_status ( sk , index , mgmt_op , ENODEV ) ;
2011-02-19 18:05:57 +03:00
2011-03-16 15:29:34 +03:00
hci_dev_lock_bh ( hdev ) ;
2011-02-19 18:05:57 +03:00
if ( ! test_bit ( HCI_UP , & hdev - > flags ) ) {
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , mgmt_op , ENETDOWN ) ;
2011-02-19 18:05:57 +03:00
goto failed ;
}
2011-02-25 21:05:48 +03:00
cmd = mgmt_pending_add ( sk , mgmt_op , index , data , len ) ;
2011-02-19 18:05:57 +03:00
if ( ! cmd ) {
err = - ENOMEM ;
goto failed ;
}
err = hci_send_cmd ( hdev , hci_op , sizeof ( cp - > bdaddr ) , & cp - > bdaddr ) ;
2011-02-19 18:06:02 +03:00
if ( err < 0 )
mgmt_pending_remove ( cmd ) ;
2011-02-19 18:05:57 +03:00
failed :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
2010-12-08 01:21:06 +03:00
int mgmt_control ( struct sock * sk , struct msghdr * msg , size_t msglen )
{
unsigned char * buf ;
struct mgmt_hdr * hdr ;
2011-02-25 21:05:48 +03:00
u16 opcode , index , len ;
2010-12-08 01:21:06 +03:00
int err ;
BT_DBG ( " got %zu bytes " , msglen ) ;
if ( msglen < sizeof ( * hdr ) )
return - EINVAL ;
buf = kmalloc ( msglen , GFP_ATOMIC ) ;
if ( ! buf )
return - ENOMEM ;
if ( memcpy_fromiovec ( buf , msg - > msg_iov , msglen ) ) {
err = - EFAULT ;
goto done ;
}
hdr = ( struct mgmt_hdr * ) buf ;
opcode = get_unaligned_le16 ( & hdr - > opcode ) ;
2011-02-25 21:05:48 +03:00
index = get_unaligned_le16 ( & hdr - > index ) ;
2010-12-08 01:21:06 +03:00
len = get_unaligned_le16 ( & hdr - > len ) ;
if ( len ! = msglen - sizeof ( * hdr ) ) {
err = - EINVAL ;
goto done ;
}
switch ( opcode ) {
2010-12-13 22:07:04 +03:00
case MGMT_OP_READ_VERSION :
err = read_version ( sk ) ;
break ;
2010-12-13 22:07:05 +03:00
case MGMT_OP_READ_INDEX_LIST :
err = read_index_list ( sk ) ;
break ;
2010-12-13 22:07:06 +03:00
case MGMT_OP_READ_INFO :
2011-02-25 21:05:48 +03:00
err = read_controller_info ( sk , index ) ;
2010-12-13 22:07:06 +03:00
break ;
2010-12-16 11:17:38 +03:00
case MGMT_OP_SET_POWERED :
2011-02-25 21:05:48 +03:00
err = set_powered ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2010-12-16 11:17:38 +03:00
break ;
2010-12-29 17:00:25 +03:00
case MGMT_OP_SET_DISCOVERABLE :
2011-02-25 21:05:48 +03:00
err = set_discoverable ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2010-12-29 17:00:25 +03:00
break ;
2010-12-30 01:18:33 +03:00
case MGMT_OP_SET_CONNECTABLE :
2011-02-25 21:05:48 +03:00
err = set_connectable ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2010-12-30 01:18:33 +03:00
break ;
2011-01-26 14:11:03 +03:00
case MGMT_OP_SET_PAIRABLE :
2011-02-25 21:05:48 +03:00
err = set_pairable ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-26 14:11:03 +03:00
break ;
2011-01-04 13:08:51 +03:00
case MGMT_OP_ADD_UUID :
2011-02-25 21:05:48 +03:00
err = add_uuid ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-04 13:08:51 +03:00
break ;
case MGMT_OP_REMOVE_UUID :
2011-02-25 21:05:48 +03:00
err = remove_uuid ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-04 13:08:51 +03:00
break ;
2011-01-13 22:56:52 +03:00
case MGMT_OP_SET_DEV_CLASS :
2011-02-25 21:05:48 +03:00
err = set_dev_class ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-13 22:56:52 +03:00
break ;
case MGMT_OP_SET_SERVICE_CACHE :
2011-02-25 21:05:48 +03:00
err = set_service_cache ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-13 22:56:52 +03:00
break ;
2011-01-17 15:41:05 +03:00
case MGMT_OP_LOAD_KEYS :
2011-02-25 21:05:48 +03:00
err = load_keys ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-17 15:41:05 +03:00
break ;
case MGMT_OP_REMOVE_KEY :
2011-02-25 21:05:48 +03:00
err = remove_key ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-17 15:41:05 +03:00
break ;
2011-01-20 13:40:27 +03:00
case MGMT_OP_DISCONNECT :
2011-02-25 21:05:48 +03:00
err = disconnect ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-20 13:40:27 +03:00
break ;
2011-01-21 14:56:35 +03:00
case MGMT_OP_GET_CONNECTIONS :
2011-03-01 18:55:32 +03:00
err = get_connections ( sk , index ) ;
2011-01-21 14:56:35 +03:00
break ;
2011-01-22 07:10:07 +03:00
case MGMT_OP_PIN_CODE_REPLY :
2011-02-25 21:05:48 +03:00
err = pin_code_reply ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-22 07:10:07 +03:00
break ;
case MGMT_OP_PIN_CODE_NEG_REPLY :
2011-02-25 21:05:48 +03:00
err = pin_code_neg_reply ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-22 07:10:07 +03:00
break ;
2011-01-25 14:28:33 +03:00
case MGMT_OP_SET_IO_CAPABILITY :
2011-02-25 21:05:48 +03:00
err = set_io_capability ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-01-25 14:28:33 +03:00
break ;
2011-02-19 18:05:56 +03:00
case MGMT_OP_PAIR_DEVICE :
2011-02-25 21:05:48 +03:00
err = pair_device ( sk , index , buf + sizeof ( * hdr ) , len ) ;
2011-02-19 18:05:56 +03:00
break ;
2011-02-19 18:05:57 +03:00
case MGMT_OP_USER_CONFIRM_REPLY :
2011-02-25 21:05:48 +03:00
err = user_confirm_reply ( sk , index , buf + sizeof ( * hdr ) , len , 1 ) ;
2011-02-19 18:05:57 +03:00
break ;
case MGMT_OP_USER_CONFIRM_NEG_REPLY :
2011-02-25 21:05:48 +03:00
err = user_confirm_reply ( sk , index , buf + sizeof ( * hdr ) , len , 0 ) ;
2011-02-19 18:05:57 +03:00
break ;
2010-12-08 01:21:06 +03:00
default :
BT_DBG ( " Unknown op %u " , opcode ) ;
2011-02-25 21:05:48 +03:00
err = cmd_status ( sk , index , opcode , 0x01 ) ;
2010-12-08 01:21:06 +03:00
break ;
}
2010-12-13 22:07:03 +03:00
if ( err < 0 )
goto done ;
2010-12-08 01:21:06 +03:00
err = msglen ;
done :
kfree ( buf ) ;
return err ;
}
2010-12-13 22:07:07 +03:00
int mgmt_index_added ( u16 index )
{
2011-02-25 21:05:48 +03:00
return mgmt_event ( MGMT_EV_INDEX_ADDED , index , NULL , 0 , NULL ) ;
2010-12-13 22:07:07 +03:00
}
int mgmt_index_removed ( u16 index )
{
2011-02-25 21:05:48 +03:00
return mgmt_event ( MGMT_EV_INDEX_REMOVED , index , NULL , 0 , NULL ) ;
2010-12-16 11:17:38 +03:00
}
2010-12-29 17:00:25 +03:00
struct cmd_lookup {
2010-12-30 01:38:22 +03:00
u8 val ;
2010-12-16 11:17:38 +03:00
struct sock * sk ;
} ;
2010-12-30 01:38:22 +03:00
static void mode_rsp ( struct pending_cmd * cmd , void * data )
2010-12-16 11:17:38 +03:00
{
2010-12-30 01:38:22 +03:00
struct mgmt_mode * cp = cmd - > cmd ;
2010-12-29 17:00:25 +03:00
struct cmd_lookup * match = data ;
2010-12-16 11:17:38 +03:00
2010-12-30 01:38:22 +03:00
if ( cp - > val ! = match - > val )
2010-12-16 11:17:38 +03:00
return ;
2011-01-26 14:07:10 +03:00
send_mode_rsp ( cmd - > sk , cmd - > opcode , cmd - > index , cp - > val ) ;
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
int mgmt_powered ( u16 index , u8 powered )
{
2010-12-30 01:38:22 +03:00
struct mgmt_mode ev ;
2010-12-29 17:00:25 +03:00
struct cmd_lookup match = { powered , NULL } ;
2010-12-16 11:17:38 +03:00
int ret ;
2010-12-16 11:00:37 +03:00
2010-12-30 01:38:22 +03:00
mgmt_pending_foreach ( MGMT_OP_SET_POWERED , index , mode_rsp , & match ) ;
2010-12-16 11:00:37 +03:00
2010-12-30 01:38:22 +03:00
ev . val = powered ;
2010-12-16 11:17:38 +03:00
2011-02-25 21:05:48 +03:00
ret = mgmt_event ( MGMT_EV_POWERED , index , & ev , sizeof ( ev ) , match . sk ) ;
2010-12-16 11:17:38 +03:00
if ( match . sk )
sock_put ( match . sk ) ;
return ret ;
2010-12-16 11:00:37 +03:00
}
2010-12-29 17:00:25 +03:00
int mgmt_discoverable ( u16 index , u8 discoverable )
{
2010-12-30 01:38:22 +03:00
struct mgmt_mode ev ;
2010-12-29 17:00:25 +03:00
struct cmd_lookup match = { discoverable , NULL } ;
int ret ;
2011-03-01 18:55:34 +03:00
mgmt_pending_foreach ( MGMT_OP_SET_DISCOVERABLE , index , mode_rsp , & match ) ;
2010-12-30 01:38:22 +03:00
ev . val = discoverable ;
2010-12-29 17:00:25 +03:00
2011-02-25 21:05:48 +03:00
ret = mgmt_event ( MGMT_EV_DISCOVERABLE , index , & ev , sizeof ( ev ) ,
match . sk ) ;
2010-12-29 17:00:25 +03:00
if ( match . sk )
sock_put ( match . sk ) ;
return ret ;
}
2010-12-30 01:18:33 +03:00
int mgmt_connectable ( u16 index , u8 connectable )
{
2010-12-30 01:38:22 +03:00
struct mgmt_mode ev ;
2010-12-30 01:18:33 +03:00
struct cmd_lookup match = { connectable , NULL } ;
int ret ;
2010-12-30 01:38:22 +03:00
mgmt_pending_foreach ( MGMT_OP_SET_CONNECTABLE , index , mode_rsp , & match ) ;
2010-12-30 01:18:33 +03:00
2010-12-30 01:38:22 +03:00
ev . val = connectable ;
2010-12-30 01:18:33 +03:00
2011-02-25 21:05:48 +03:00
ret = mgmt_event ( MGMT_EV_CONNECTABLE , index , & ev , sizeof ( ev ) , match . sk ) ;
2010-12-30 01:18:33 +03:00
if ( match . sk )
sock_put ( match . sk ) ;
return ret ;
}
2011-01-17 15:41:05 +03:00
int mgmt_new_key ( u16 index , struct link_key * key , u8 old_key_type )
{
struct mgmt_ev_new_key ev ;
memset ( & ev , 0 , sizeof ( ev ) ) ;
bacpy ( & ev . key . bdaddr , & key - > bdaddr ) ;
ev . key . type = key - > type ;
memcpy ( ev . key . val , key - > val , 16 ) ;
ev . key . pin_len = key - > pin_len ;
ev . old_key_type = old_key_type ;
2011-02-25 21:05:48 +03:00
return mgmt_event ( MGMT_EV_NEW_KEY , index , & ev , sizeof ( ev ) , NULL ) ;
2011-01-17 15:41:05 +03:00
}
2011-01-20 13:34:39 +03:00
int mgmt_connected ( u16 index , bdaddr_t * bdaddr )
{
struct mgmt_ev_connected ev ;
bacpy ( & ev . bdaddr , bdaddr ) ;
2011-02-25 21:05:48 +03:00
return mgmt_event ( MGMT_EV_CONNECTED , index , & ev , sizeof ( ev ) , 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 )
{
struct mgmt_cp_disconnect * cp = cmd - > cmd ;
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
2011-01-22 07:46:43 +03:00
bacpy ( & rp . bdaddr , & cp - > bdaddr ) ;
2011-01-20 13:40:27 +03:00
2011-02-25 21:05:48 +03:00
cmd_complete ( cmd - > sk , cmd - > index , MGMT_OP_DISCONNECT , & rp , 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
}
2011-01-20 13:34:39 +03:00
int mgmt_disconnected ( u16 index , bdaddr_t * bdaddr )
{
struct mgmt_ev_disconnected ev ;
2011-01-20 13:40:27 +03:00
struct sock * sk = NULL ;
int err ;
mgmt_pending_foreach ( MGMT_OP_DISCONNECT , index , disconnect_rsp , & sk ) ;
2011-01-20 13:34:39 +03:00
bacpy ( & ev . bdaddr , bdaddr ) ;
2011-02-25 21:05:48 +03:00
err = mgmt_event ( MGMT_EV_DISCONNECTED , index , & ev , sizeof ( ev ) , sk ) ;
2011-01-20 13:40:27 +03:00
if ( sk )
sock_put ( sk ) ;
return err ;
}
int mgmt_disconnect_failed ( u16 index )
{
struct pending_cmd * cmd ;
int err ;
cmd = mgmt_pending_find ( MGMT_OP_DISCONNECT , index ) ;
if ( ! cmd )
return - ENOENT ;
2011-02-25 21:05:48 +03:00
err = cmd_status ( cmd - > sk , index , MGMT_OP_DISCONNECT , EIO ) ;
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
return err ;
2011-01-20 13:34:39 +03:00
}
2011-01-22 07:09:08 +03:00
int mgmt_connect_failed ( u16 index , bdaddr_t * bdaddr , u8 status )
{
struct mgmt_ev_connect_failed ev ;
bacpy ( & ev . bdaddr , bdaddr ) ;
ev . status = status ;
2011-02-25 21:05:48 +03:00
return mgmt_event ( MGMT_EV_CONNECT_FAILED , index , & ev , sizeof ( ev ) , NULL ) ;
2011-01-22 07:09:08 +03:00
}
2011-01-22 07:10:07 +03:00
int mgmt_pin_code_request ( u16 index , bdaddr_t * bdaddr )
{
struct mgmt_ev_pin_code_request ev ;
bacpy ( & ev . bdaddr , bdaddr ) ;
2011-02-25 21:05:48 +03:00
return mgmt_event ( MGMT_EV_PIN_CODE_REQUEST , index , & ev , sizeof ( ev ) ,
NULL ) ;
2011-01-22 07:10:07 +03:00
}
int mgmt_pin_code_reply_complete ( u16 index , bdaddr_t * bdaddr , u8 status )
{
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 ;
cmd = mgmt_pending_find ( MGMT_OP_PIN_CODE_REPLY , index ) ;
if ( ! cmd )
return - ENOENT ;
2011-02-19 18:05:59 +03:00
bacpy ( & rp . bdaddr , bdaddr ) ;
rp . status = status ;
2011-02-25 21:05:48 +03:00
err = cmd_complete ( cmd - > sk , index , MGMT_OP_PIN_CODE_REPLY , & 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 ;
}
int mgmt_pin_code_neg_reply_complete ( u16 index , bdaddr_t * bdaddr , u8 status )
{
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 ;
cmd = mgmt_pending_find ( MGMT_OP_PIN_CODE_NEG_REPLY , index ) ;
if ( ! cmd )
return - ENOENT ;
2011-02-19 18:05:59 +03:00
bacpy ( & rp . bdaddr , bdaddr ) ;
rp . status = status ;
2011-02-25 21:05:48 +03:00
err = cmd_complete ( cmd - > sk , index , MGMT_OP_PIN_CODE_NEG_REPLY , & 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
int mgmt_user_confirm_request ( u16 index , bdaddr_t * bdaddr , __le32 value )
{
struct mgmt_ev_user_confirm_request ev ;
BT_DBG ( " hci%u " , index ) ;
bacpy ( & ev . bdaddr , bdaddr ) ;
put_unaligned_le32 ( value , & ev . value ) ;
2011-02-25 21:05:48 +03:00
return mgmt_event ( MGMT_EV_USER_CONFIRM_REQUEST , index , & ev , sizeof ( ev ) ,
NULL ) ;
2011-02-19 18:05:57 +03:00
}
static int confirm_reply_complete ( u16 index , bdaddr_t * bdaddr , u8 status ,
u8 opcode )
{
struct pending_cmd * cmd ;
struct mgmt_rp_user_confirm_reply rp ;
int err ;
cmd = mgmt_pending_find ( opcode , index ) ;
if ( ! cmd )
return - ENOENT ;
bacpy ( & rp . bdaddr , bdaddr ) ;
rp . status = status ;
2011-02-25 21:05:48 +03:00
err = cmd_complete ( cmd - > sk , index , opcode , & 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 ;
}
int mgmt_user_confirm_reply_complete ( u16 index , bdaddr_t * bdaddr , u8 status )
{
return confirm_reply_complete ( index , bdaddr , status ,
MGMT_OP_USER_CONFIRM_REPLY ) ;
}
2011-03-01 18:55:34 +03:00
int mgmt_user_confirm_neg_reply_complete ( u16 index , bdaddr_t * bdaddr , u8 status )
2011-02-19 18:05:57 +03:00
{
return confirm_reply_complete ( index , bdaddr , status ,
MGMT_OP_USER_CONFIRM_NEG_REPLY ) ;
}
2011-02-19 18:06:00 +03:00
int mgmt_auth_failed ( u16 index , bdaddr_t * bdaddr , u8 status )
{
struct mgmt_ev_auth_failed ev ;
bacpy ( & ev . bdaddr , bdaddr ) ;
ev . status = status ;
2011-02-25 21:05:48 +03:00
return mgmt_event ( MGMT_EV_AUTH_FAILED , index , & ev , sizeof ( ev ) , NULL ) ;
2011-02-19 18:06:00 +03:00
}