2014-12-20 18:05:13 +03:00
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright ( C ) 2014 Intel 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 .
*/
# include <linux/debugfs.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
# include "hci_debugfs.h"
2015-03-20 21:14:50 +03:00
# define DEFINE_QUIRK_ATTRIBUTE(__name, __quirk) \
static ssize_t __name # # _read ( struct file * file , \
char __user * user_buf , \
size_t count , loff_t * ppos ) \
{ \
struct hci_dev * hdev = file - > private_data ; \
char buf [ 3 ] ; \
\
buf [ 0 ] = test_bit ( __quirk , & hdev - > quirks ) ? ' Y ' : ' N ' ; \
buf [ 1 ] = ' \n ' ; \
buf [ 2 ] = ' \0 ' ; \
return simple_read_from_buffer ( user_buf , count , ppos , buf , 2 ) ; \
} \
\
static ssize_t __name # # _write ( struct file * file , \
const char __user * user_buf , \
size_t count , loff_t * ppos ) \
{ \
struct hci_dev * hdev = file - > private_data ; \
char buf [ 32 ] ; \
size_t buf_size = min ( count , ( sizeof ( buf ) - 1 ) ) ; \
bool enable ; \
\
if ( test_bit ( HCI_UP , & hdev - > flags ) ) \
return - EBUSY ; \
\
if ( copy_from_user ( buf , user_buf , buf_size ) ) \
return - EFAULT ; \
\
buf [ buf_size ] = ' \0 ' ; \
if ( strtobool ( buf , & enable ) ) \
return - EINVAL ; \
\
if ( enable = = test_bit ( __quirk , & hdev - > quirks ) ) \
return - EALREADY ; \
\
change_bit ( __quirk , & hdev - > quirks ) ; \
\
return count ; \
} \
\
static const struct file_operations __name # # _fops = { \
. open = simple_open , \
. read = __name # # _read , \
. write = __name # # _write , \
. llseek = default_llseek , \
} \
2014-12-20 18:05:14 +03:00
static int features_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
u8 p ;
hci_dev_lock ( hdev ) ;
for ( p = 0 ; p < HCI_MAX_PAGES & & p < = hdev - > max_page ; p + + ) {
seq_printf ( f , " %2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
" 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x \n " , p ,
hdev - > features [ p ] [ 0 ] , hdev - > features [ p ] [ 1 ] ,
hdev - > features [ p ] [ 2 ] , hdev - > features [ p ] [ 3 ] ,
hdev - > features [ p ] [ 4 ] , hdev - > features [ p ] [ 5 ] ,
hdev - > features [ p ] [ 6 ] , hdev - > features [ p ] [ 7 ] ) ;
}
if ( lmp_le_capable ( hdev ) )
seq_printf ( f , " LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
" 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x \n " ,
hdev - > le_features [ 0 ] , hdev - > le_features [ 1 ] ,
hdev - > le_features [ 2 ] , hdev - > le_features [ 3 ] ,
hdev - > le_features [ 4 ] , hdev - > le_features [ 5 ] ,
hdev - > le_features [ 6 ] , hdev - > le_features [ 7 ] ) ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int features_open ( struct inode * inode , struct file * file )
{
return single_open ( file , features_show , inode - > i_private ) ;
}
static const struct file_operations features_fops = {
. open = features_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2015-04-01 23:21:37 +03:00
static int device_id_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
hci_dev_lock ( hdev ) ;
seq_printf ( f , " %4.4x:%4.4x:%4.4x:%4.4x \n " , hdev - > devid_source ,
hdev - > devid_vendor , hdev - > devid_product , hdev - > devid_version ) ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int device_id_open ( struct inode * inode , struct file * file )
{
return single_open ( file , device_id_show , inode - > i_private ) ;
}
static const struct file_operations device_id_fops = {
. open = device_id_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2014-12-20 18:05:14 +03:00
static int device_list_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
struct hci_conn_params * p ;
struct bdaddr_list * b ;
hci_dev_lock ( hdev ) ;
list_for_each_entry ( b , & hdev - > whitelist , list )
seq_printf ( f , " %pMR (type %u) \n " , & b - > bdaddr , b - > bdaddr_type ) ;
list_for_each_entry ( p , & hdev - > le_conn_params , list ) {
seq_printf ( f , " %pMR (type %u) %u \n " , & p - > addr , p - > addr_type ,
p - > auto_connect ) ;
}
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int device_list_open ( struct inode * inode , struct file * file )
{
return single_open ( file , device_list_show , inode - > i_private ) ;
}
static const struct file_operations device_list_fops = {
. open = device_list_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int blacklist_show ( struct seq_file * f , void * p )
{
struct hci_dev * hdev = f - > private ;
struct bdaddr_list * b ;
hci_dev_lock ( hdev ) ;
list_for_each_entry ( b , & hdev - > blacklist , list )
seq_printf ( f , " %pMR (type %u) \n " , & b - > bdaddr , b - > bdaddr_type ) ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int blacklist_open ( struct inode * inode , struct file * file )
{
return single_open ( file , blacklist_show , inode - > i_private ) ;
}
static const struct file_operations blacklist_fops = {
. open = blacklist_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int uuids_show ( struct seq_file * f , void * p )
{
struct hci_dev * hdev = f - > private ;
struct bt_uuid * uuid ;
hci_dev_lock ( hdev ) ;
list_for_each_entry ( uuid , & hdev - > uuids , list ) {
u8 i , val [ 16 ] ;
/* The Bluetooth UUID values are stored in big endian,
* but with reversed byte order . So convert them into
* the right order for the % pUb modifier .
*/
for ( i = 0 ; i < 16 ; i + + )
val [ i ] = uuid - > uuid [ 15 - i ] ;
seq_printf ( f , " %pUb \n " , val ) ;
}
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int uuids_open ( struct inode * inode , struct file * file )
{
return single_open ( file , uuids_show , inode - > i_private ) ;
}
static const struct file_operations uuids_fops = {
. open = uuids_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2015-02-01 08:01:07 +03:00
static int remote_oob_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
struct oob_data * data ;
hci_dev_lock ( hdev ) ;
list_for_each_entry ( data , & hdev - > remote_oob_data , list ) {
seq_printf ( f , " %pMR (type %u) %u %*phN %*phN %*phN %*phN \n " ,
& data - > bdaddr , data - > bdaddr_type , data - > present ,
16 , data - > hash192 , 16 , data - > rand192 ,
2015-03-16 22:34:58 +03:00
16 , data - > hash256 , 16 , data - > rand256 ) ;
2015-02-01 08:01:07 +03:00
}
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int remote_oob_open ( struct inode * inode , struct file * file )
{
return single_open ( file , remote_oob_show , inode - > i_private ) ;
}
static const struct file_operations remote_oob_fops = {
. open = remote_oob_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2014-12-20 18:05:14 +03:00
static int conn_info_min_age_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val = = 0 | | val > hdev - > conn_info_max_age )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > conn_info_min_age = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int conn_info_min_age_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > conn_info_min_age ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( conn_info_min_age_fops , conn_info_min_age_get ,
conn_info_min_age_set , " %llu \n " ) ;
static int conn_info_max_age_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val = = 0 | | val < hdev - > conn_info_min_age )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > conn_info_max_age = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int conn_info_max_age_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > conn_info_max_age ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( conn_info_max_age_fops , conn_info_max_age_get ,
conn_info_max_age_set , " %llu \n " ) ;
2015-02-01 02:12:06 +03:00
static ssize_t use_debug_keys_read ( struct file * file , char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct hci_dev * hdev = file - > private_data ;
char buf [ 3 ] ;
2015-03-13 12:11:00 +03:00
buf [ 0 ] = hci_dev_test_flag ( hdev , HCI_USE_DEBUG_KEYS ) ? ' Y ' : ' N ' ;
2015-02-01 02:12:06 +03:00
buf [ 1 ] = ' \n ' ;
buf [ 2 ] = ' \0 ' ;
return simple_read_from_buffer ( user_buf , count , ppos , buf , 2 ) ;
}
static const struct file_operations use_debug_keys_fops = {
. open = simple_open ,
. read = use_debug_keys_read ,
. llseek = default_llseek ,
} ;
2015-01-01 01:43:19 +03:00
static ssize_t sc_only_mode_read ( struct file * file , char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct hci_dev * hdev = file - > private_data ;
char buf [ 3 ] ;
2015-03-13 12:11:00 +03:00
buf [ 0 ] = hci_dev_test_flag ( hdev , HCI_SC_ONLY ) ? ' Y ' : ' N ' ;
2015-01-01 01:43:19 +03:00
buf [ 1 ] = ' \n ' ;
buf [ 2 ] = ' \0 ' ;
return simple_read_from_buffer ( user_buf , count , ppos , buf , 2 ) ;
}
static const struct file_operations sc_only_mode_fops = {
. open = simple_open ,
. read = sc_only_mode_read ,
. llseek = default_llseek ,
} ;
2014-12-20 18:05:13 +03:00
void hci_debugfs_create_common ( struct hci_dev * hdev )
{
2014-12-20 18:05:14 +03:00
debugfs_create_file ( " features " , 0444 , hdev - > debugfs , hdev ,
& features_fops ) ;
debugfs_create_u16 ( " manufacturer " , 0444 , hdev - > debugfs ,
& hdev - > manufacturer ) ;
debugfs_create_u8 ( " hci_version " , 0444 , hdev - > debugfs , & hdev - > hci_ver ) ;
debugfs_create_u16 ( " hci_revision " , 0444 , hdev - > debugfs , & hdev - > hci_rev ) ;
2015-02-01 06:54:39 +03:00
debugfs_create_u8 ( " hardware_error " , 0444 , hdev - > debugfs ,
& hdev - > hw_error_code ) ;
2015-04-01 23:21:37 +03:00
debugfs_create_file ( " device_id " , 0444 , hdev - > debugfs , hdev ,
& device_id_fops ) ;
2015-02-01 06:54:39 +03:00
2014-12-20 18:05:14 +03:00
debugfs_create_file ( " device_list " , 0444 , hdev - > debugfs , hdev ,
& device_list_fops ) ;
debugfs_create_file ( " blacklist " , 0444 , hdev - > debugfs , hdev ,
& blacklist_fops ) ;
debugfs_create_file ( " uuids " , 0444 , hdev - > debugfs , hdev , & uuids_fops ) ;
2015-02-01 08:01:07 +03:00
debugfs_create_file ( " remote_oob " , 0400 , hdev - > debugfs , hdev ,
& remote_oob_fops ) ;
2014-12-20 18:05:14 +03:00
debugfs_create_file ( " conn_info_min_age " , 0644 , hdev - > debugfs , hdev ,
& conn_info_min_age_fops ) ;
debugfs_create_file ( " conn_info_max_age " , 0644 , hdev - > debugfs , hdev ,
& conn_info_max_age_fops ) ;
2015-01-01 01:43:19 +03:00
2015-02-01 02:12:06 +03:00
if ( lmp_ssp_capable ( hdev ) | | lmp_le_capable ( hdev ) )
debugfs_create_file ( " use_debug_keys " , 0444 , hdev - > debugfs ,
hdev , & use_debug_keys_fops ) ;
2015-01-01 01:43:19 +03:00
if ( lmp_sc_capable ( hdev ) | | lmp_le_capable ( hdev ) )
debugfs_create_file ( " sc_only_mode " , 0444 , hdev - > debugfs ,
hdev , & sc_only_mode_fops ) ;
2014-12-20 18:05:13 +03:00
}
2014-12-20 18:05:15 +03:00
static int inquiry_cache_show ( struct seq_file * f , void * p )
{
struct hci_dev * hdev = f - > private ;
struct discovery_state * cache = & hdev - > discovery ;
struct inquiry_entry * e ;
hci_dev_lock ( hdev ) ;
list_for_each_entry ( e , & cache - > all , all ) {
struct inquiry_data * data = & e - > data ;
seq_printf ( f , " %pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u \n " ,
& data - > bdaddr ,
data - > pscan_rep_mode , data - > pscan_period_mode ,
data - > pscan_mode , data - > dev_class [ 2 ] ,
data - > dev_class [ 1 ] , data - > dev_class [ 0 ] ,
__le16_to_cpu ( data - > clock_offset ) ,
data - > rssi , data - > ssp_mode , e - > timestamp ) ;
}
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int inquiry_cache_open ( struct inode * inode , struct file * file )
{
return single_open ( file , inquiry_cache_show , inode - > i_private ) ;
}
static const struct file_operations inquiry_cache_fops = {
. open = inquiry_cache_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int link_keys_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
struct link_key * key ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( key , & hdev - > link_keys , list )
seq_printf ( f , " %pMR %u %*phN %u \n " , & key - > bdaddr , key - > type ,
HCI_LINK_KEY_SIZE , key - > val , key - > pin_len ) ;
rcu_read_unlock ( ) ;
return 0 ;
}
static int link_keys_open ( struct inode * inode , struct file * file )
{
return single_open ( file , link_keys_show , inode - > i_private ) ;
}
static const struct file_operations link_keys_fops = {
. open = link_keys_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int dev_class_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
hci_dev_lock ( hdev ) ;
seq_printf ( f , " 0x%.2x%.2x%.2x \n " , hdev - > dev_class [ 2 ] ,
hdev - > dev_class [ 1 ] , hdev - > dev_class [ 0 ] ) ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int dev_class_open ( struct inode * inode , struct file * file )
{
return single_open ( file , dev_class_show , inode - > i_private ) ;
}
static const struct file_operations dev_class_fops = {
. open = dev_class_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int voice_setting_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > voice_setting ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( voice_setting_fops , voice_setting_get ,
NULL , " 0x%4.4llx \n " ) ;
2015-02-01 02:07:51 +03:00
static ssize_t ssp_debug_mode_read ( struct file * file , char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct hci_dev * hdev = file - > private_data ;
char buf [ 3 ] ;
buf [ 0 ] = hdev - > ssp_debug_mode ? ' Y ' : ' N ' ;
buf [ 1 ] = ' \n ' ;
buf [ 2 ] = ' \0 ' ;
return simple_read_from_buffer ( user_buf , count , ppos , buf , 2 ) ;
}
static const struct file_operations ssp_debug_mode_fops = {
. open = simple_open ,
. read = ssp_debug_mode_read ,
. llseek = default_llseek ,
} ;
2014-12-20 18:05:15 +03:00
static int auto_accept_delay_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
hdev - > auto_accept_delay = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int auto_accept_delay_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > auto_accept_delay ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( auto_accept_delay_fops , auto_accept_delay_get ,
auto_accept_delay_set , " %llu \n " ) ;
static int idle_timeout_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val ! = 0 & & ( val < 500 | | val > 3600000 ) )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > idle_timeout = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int idle_timeout_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > idle_timeout ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( idle_timeout_fops , idle_timeout_get ,
idle_timeout_set , " %llu \n " ) ;
static int sniff_min_interval_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val = = 0 | | val % 2 | | val > hdev - > sniff_max_interval )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > sniff_min_interval = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int sniff_min_interval_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > sniff_min_interval ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( sniff_min_interval_fops , sniff_min_interval_get ,
sniff_min_interval_set , " %llu \n " ) ;
static int sniff_max_interval_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val = = 0 | | val % 2 | | val < hdev - > sniff_min_interval )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > sniff_max_interval = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int sniff_max_interval_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > sniff_max_interval ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( sniff_max_interval_fops , sniff_max_interval_get ,
sniff_max_interval_set , " %llu \n " ) ;
2014-12-20 18:05:13 +03:00
void hci_debugfs_create_bredr ( struct hci_dev * hdev )
{
2014-12-20 18:05:15 +03:00
debugfs_create_file ( " inquiry_cache " , 0444 , hdev - > debugfs , hdev ,
& inquiry_cache_fops ) ;
debugfs_create_file ( " link_keys " , 0400 , hdev - > debugfs , hdev ,
& link_keys_fops ) ;
debugfs_create_file ( " dev_class " , 0444 , hdev - > debugfs , hdev ,
& dev_class_fops ) ;
debugfs_create_file ( " voice_setting " , 0444 , hdev - > debugfs , hdev ,
& voice_setting_fops ) ;
2015-02-01 02:07:51 +03:00
if ( lmp_ssp_capable ( hdev ) ) {
debugfs_create_file ( " ssp_debug_mode " , 0444 , hdev - > debugfs ,
hdev , & ssp_debug_mode_fops ) ;
2014-12-20 18:05:15 +03:00
debugfs_create_file ( " auto_accept_delay " , 0644 , hdev - > debugfs ,
hdev , & auto_accept_delay_fops ) ;
2015-02-01 02:07:51 +03:00
}
2014-12-20 18:05:15 +03:00
if ( lmp_sniff_capable ( hdev ) ) {
debugfs_create_file ( " idle_timeout " , 0644 , hdev - > debugfs ,
hdev , & idle_timeout_fops ) ;
debugfs_create_file ( " sniff_min_interval " , 0644 , hdev - > debugfs ,
hdev , & sniff_min_interval_fops ) ;
debugfs_create_file ( " sniff_max_interval " , 0644 , hdev - > debugfs ,
hdev , & sniff_max_interval_fops ) ;
}
2014-12-20 18:05:13 +03:00
}
2014-12-20 18:05:16 +03:00
static int identity_show ( struct seq_file * f , void * p )
{
struct hci_dev * hdev = f - > private ;
bdaddr_t addr ;
u8 addr_type ;
hci_dev_lock ( hdev ) ;
hci_copy_identity_address ( hdev , & addr , & addr_type ) ;
seq_printf ( f , " %pMR (type %u) %*phN %pMR \n " , & addr , addr_type ,
16 , hdev - > irk , & hdev - > rpa ) ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int identity_open ( struct inode * inode , struct file * file )
{
return single_open ( file , identity_show , inode - > i_private ) ;
}
static const struct file_operations identity_fops = {
. open = identity_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int rpa_timeout_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
/* Require the RPA timeout to be at least 30 seconds and at most
* 24 hours .
*/
if ( val < 30 | | val > ( 60 * 60 * 24 ) )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > rpa_timeout = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int rpa_timeout_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > rpa_timeout ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( rpa_timeout_fops , rpa_timeout_get ,
rpa_timeout_set , " %llu \n " ) ;
static int random_address_show ( struct seq_file * f , void * p )
{
struct hci_dev * hdev = f - > private ;
hci_dev_lock ( hdev ) ;
seq_printf ( f , " %pMR \n " , & hdev - > random_addr ) ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int random_address_open ( struct inode * inode , struct file * file )
{
return single_open ( file , random_address_show , inode - > i_private ) ;
}
static const struct file_operations random_address_fops = {
. open = random_address_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int static_address_show ( struct seq_file * f , void * p )
{
struct hci_dev * hdev = f - > private ;
hci_dev_lock ( hdev ) ;
seq_printf ( f , " %pMR \n " , & hdev - > static_addr ) ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int static_address_open ( struct inode * inode , struct file * file )
{
return single_open ( file , static_address_show , inode - > i_private ) ;
}
static const struct file_operations static_address_fops = {
. open = static_address_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static ssize_t force_static_address_read ( struct file * file ,
char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct hci_dev * hdev = file - > private_data ;
char buf [ 3 ] ;
2015-03-13 20:20:35 +03:00
buf [ 0 ] = hci_dev_test_flag ( hdev , HCI_FORCE_STATIC_ADDR ) ? ' Y ' : ' N ' ;
2014-12-20 18:05:16 +03:00
buf [ 1 ] = ' \n ' ;
buf [ 2 ] = ' \0 ' ;
return simple_read_from_buffer ( user_buf , count , ppos , buf , 2 ) ;
}
static ssize_t force_static_address_write ( struct file * file ,
const char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct hci_dev * hdev = file - > private_data ;
char buf [ 32 ] ;
size_t buf_size = min ( count , ( sizeof ( buf ) - 1 ) ) ;
bool enable ;
if ( test_bit ( HCI_UP , & hdev - > flags ) )
return - EBUSY ;
if ( copy_from_user ( buf , user_buf , buf_size ) )
return - EFAULT ;
buf [ buf_size ] = ' \0 ' ;
if ( strtobool ( buf , & enable ) )
return - EINVAL ;
2015-03-13 20:20:35 +03:00
if ( enable = = hci_dev_test_flag ( hdev , HCI_FORCE_STATIC_ADDR ) )
2014-12-20 18:05:16 +03:00
return - EALREADY ;
2015-03-13 20:20:35 +03:00
hci_dev_change_flag ( hdev , HCI_FORCE_STATIC_ADDR ) ;
2014-12-20 18:05:16 +03:00
return count ;
}
static const struct file_operations force_static_address_fops = {
. open = simple_open ,
. read = force_static_address_read ,
. write = force_static_address_write ,
. llseek = default_llseek ,
} ;
static int white_list_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
struct bdaddr_list * b ;
hci_dev_lock ( hdev ) ;
list_for_each_entry ( b , & hdev - > le_white_list , list )
seq_printf ( f , " %pMR (type %u) \n " , & b - > bdaddr , b - > bdaddr_type ) ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int white_list_open ( struct inode * inode , struct file * file )
{
return single_open ( file , white_list_show , inode - > i_private ) ;
}
static const struct file_operations white_list_fops = {
. open = white_list_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int identity_resolving_keys_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
struct smp_irk * irk ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( irk , & hdev - > identity_resolving_keys , list ) {
seq_printf ( f , " %pMR (type %u) %*phN %pMR \n " ,
& irk - > bdaddr , irk - > addr_type ,
16 , irk - > val , & irk - > rpa ) ;
}
rcu_read_unlock ( ) ;
return 0 ;
}
static int identity_resolving_keys_open ( struct inode * inode , struct file * file )
{
return single_open ( file , identity_resolving_keys_show ,
inode - > i_private ) ;
}
static const struct file_operations identity_resolving_keys_fops = {
. open = identity_resolving_keys_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int long_term_keys_show ( struct seq_file * f , void * ptr )
{
struct hci_dev * hdev = f - > private ;
struct smp_ltk * ltk ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( ltk , & hdev - > long_term_keys , list )
seq_printf ( f , " %pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN \n " ,
& ltk - > bdaddr , ltk - > bdaddr_type , ltk - > authenticated ,
ltk - > type , ltk - > enc_size , __le16_to_cpu ( ltk - > ediv ) ,
__le64_to_cpu ( ltk - > rand ) , 16 , ltk - > val ) ;
rcu_read_unlock ( ) ;
return 0 ;
}
static int long_term_keys_open ( struct inode * inode , struct file * file )
{
return single_open ( file , long_term_keys_show , inode - > i_private ) ;
}
static const struct file_operations long_term_keys_fops = {
. open = long_term_keys_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int conn_min_interval_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val < 0x0006 | | val > 0x0c80 | | val > hdev - > le_conn_max_interval )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > le_conn_min_interval = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int conn_min_interval_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > le_conn_min_interval ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( conn_min_interval_fops , conn_min_interval_get ,
conn_min_interval_set , " %llu \n " ) ;
static int conn_max_interval_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val < 0x0006 | | val > 0x0c80 | | val < hdev - > le_conn_min_interval )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > le_conn_max_interval = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int conn_max_interval_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > le_conn_max_interval ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( conn_max_interval_fops , conn_max_interval_get ,
conn_max_interval_set , " %llu \n " ) ;
static int conn_latency_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val > 0x01f3 )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > le_conn_latency = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int conn_latency_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > le_conn_latency ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( conn_latency_fops , conn_latency_get ,
conn_latency_set , " %llu \n " ) ;
static int supervision_timeout_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val < 0x000a | | val > 0x0c80 )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > le_supv_timeout = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int supervision_timeout_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > le_supv_timeout ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( supervision_timeout_fops , supervision_timeout_get ,
supervision_timeout_set , " %llu \n " ) ;
static int adv_channel_map_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val < 0x01 | | val > 0x07 )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > le_adv_channel_map = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int adv_channel_map_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > le_adv_channel_map ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( adv_channel_map_fops , adv_channel_map_get ,
adv_channel_map_set , " %llu \n " ) ;
static int adv_min_interval_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val < 0x0020 | | val > 0x4000 | | val > hdev - > le_adv_max_interval )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > le_adv_min_interval = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int adv_min_interval_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > le_adv_min_interval ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( adv_min_interval_fops , adv_min_interval_get ,
adv_min_interval_set , " %llu \n " ) ;
static int adv_max_interval_set ( void * data , u64 val )
{
struct hci_dev * hdev = data ;
if ( val < 0x0020 | | val > 0x4000 | | val < hdev - > le_adv_min_interval )
return - EINVAL ;
hci_dev_lock ( hdev ) ;
hdev - > le_adv_max_interval = val ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
static int adv_max_interval_get ( void * data , u64 * val )
{
struct hci_dev * hdev = data ;
hci_dev_lock ( hdev ) ;
* val = hdev - > le_adv_max_interval ;
hci_dev_unlock ( hdev ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( adv_max_interval_fops , adv_max_interval_get ,
adv_max_interval_set , " %llu \n " ) ;
2015-03-20 21:14:50 +03:00
DEFINE_QUIRK_ATTRIBUTE ( quirk_strict_duplicate_filter ,
HCI_QUIRK_STRICT_DUPLICATE_FILTER ) ;
DEFINE_QUIRK_ATTRIBUTE ( quirk_simultaneous_discovery ,
HCI_QUIRK_SIMULTANEOUS_DISCOVERY ) ;
2014-12-20 18:05:13 +03:00
void hci_debugfs_create_le ( struct hci_dev * hdev )
{
2014-12-20 18:05:16 +03:00
debugfs_create_file ( " identity " , 0400 , hdev - > debugfs , hdev ,
& identity_fops ) ;
debugfs_create_file ( " rpa_timeout " , 0644 , hdev - > debugfs , hdev ,
& rpa_timeout_fops ) ;
debugfs_create_file ( " random_address " , 0444 , hdev - > debugfs , hdev ,
& random_address_fops ) ;
debugfs_create_file ( " static_address " , 0444 , hdev - > debugfs , hdev ,
& static_address_fops ) ;
/* For controllers with a public address, provide a debug
* option to force the usage of the configured static
* address . By default the public address is used .
*/
if ( bacmp ( & hdev - > bdaddr , BDADDR_ANY ) )
debugfs_create_file ( " force_static_address " , 0644 ,
hdev - > debugfs , hdev ,
& force_static_address_fops ) ;
debugfs_create_u8 ( " white_list_size " , 0444 , hdev - > debugfs ,
& hdev - > le_white_list_size ) ;
debugfs_create_file ( " white_list " , 0444 , hdev - > debugfs , hdev ,
& white_list_fops ) ;
debugfs_create_file ( " identity_resolving_keys " , 0400 , hdev - > debugfs ,
hdev , & identity_resolving_keys_fops ) ;
debugfs_create_file ( " long_term_keys " , 0400 , hdev - > debugfs , hdev ,
& long_term_keys_fops ) ;
debugfs_create_file ( " conn_min_interval " , 0644 , hdev - > debugfs , hdev ,
& conn_min_interval_fops ) ;
debugfs_create_file ( " conn_max_interval " , 0644 , hdev - > debugfs , hdev ,
& conn_max_interval_fops ) ;
debugfs_create_file ( " conn_latency " , 0644 , hdev - > debugfs , hdev ,
& conn_latency_fops ) ;
debugfs_create_file ( " supervision_timeout " , 0644 , hdev - > debugfs , hdev ,
& supervision_timeout_fops ) ;
debugfs_create_file ( " adv_channel_map " , 0644 , hdev - > debugfs , hdev ,
& adv_channel_map_fops ) ;
debugfs_create_file ( " adv_min_interval " , 0644 , hdev - > debugfs , hdev ,
& adv_min_interval_fops ) ;
debugfs_create_file ( " adv_max_interval " , 0644 , hdev - > debugfs , hdev ,
& adv_max_interval_fops ) ;
debugfs_create_u16 ( " discov_interleaved_timeout " , 0644 , hdev - > debugfs ,
& hdev - > discov_interleaved_timeout ) ;
2015-03-20 21:14:50 +03:00
debugfs_create_file ( " quirk_strict_duplicate_filter " , 0644 ,
hdev - > debugfs , hdev ,
& quirk_strict_duplicate_filter_fops ) ;
debugfs_create_file ( " quirk_simultaneous_discovery " , 0644 ,
hdev - > debugfs , hdev ,
& quirk_simultaneous_discovery_fops ) ;
2014-12-20 18:05:13 +03:00
}
2014-12-20 19:13:41 +03:00
void hci_debugfs_create_conn ( struct hci_conn * conn )
{
struct hci_dev * hdev = conn - > hdev ;
char name [ 6 ] ;
if ( IS_ERR_OR_NULL ( hdev - > debugfs ) )
return ;
snprintf ( name , sizeof ( name ) , " %u " , conn - > handle ) ;
conn - > debugfs = debugfs_create_dir ( name , hdev - > debugfs ) ;
}