2005-04-17 02:20:36 +04:00
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright ( C ) 2000 - 2001 Qualcomm Incorporated
Written 2000 , 2001 by Maxim Krasnyansky < maxk @ qualcomm . com >
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 event handling. */
# include <linux/module.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/fcntl.h>
# include <linux/init.h>
# include <linux/skbuff.h>
# include <linux/interrupt.h>
# include <linux/notifier.h>
# include <net/sock.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <asm/unaligned.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
# ifndef CONFIG_BT_HCI_CORE_DEBUG
# undef BT_DBG
# define BT_DBG(D...)
# endif
/* Handle HCI Event packets */
/* Command Complete OGF LINK_CTL */
static void hci_cc_link_ctl ( struct hci_dev * hdev , __u16 ocf , struct sk_buff * skb )
{
__u8 status ;
BT_DBG ( " %s ocf 0x%x " , hdev - > name , ocf ) ;
switch ( ocf ) {
case OCF_INQUIRY_CANCEL :
status = * ( ( __u8 * ) skb - > data ) ;
if ( status ) {
BT_DBG ( " %s Inquiry cancel error: status 0x%x " , hdev - > name , status ) ;
} else {
clear_bit ( HCI_INQUIRY , & hdev - > flags ) ;
hci_req_complete ( hdev , status ) ;
}
break ;
default :
BT_DBG ( " %s Command complete: ogf LINK_CTL ocf %x " , hdev - > name , ocf ) ;
break ;
}
}
/* Command Complete OGF LINK_POLICY */
static void hci_cc_link_policy ( struct hci_dev * hdev , __u16 ocf , struct sk_buff * skb )
{
struct hci_conn * conn ;
struct hci_rp_role_discovery * rd ;
BT_DBG ( " %s ocf 0x%x " , hdev - > name , ocf ) ;
switch ( ocf ) {
case OCF_ROLE_DISCOVERY :
rd = ( void * ) skb - > data ;
if ( rd - > status )
break ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_handle ( hdev , __le16_to_cpu ( rd - > handle ) ) ;
if ( conn ) {
if ( rd - > role )
conn - > link_mode & = ~ HCI_LM_MASTER ;
else
conn - > link_mode | = HCI_LM_MASTER ;
}
hci_dev_unlock ( hdev ) ;
break ;
default :
BT_DBG ( " %s: Command complete: ogf LINK_POLICY ocf %x " ,
hdev - > name , ocf ) ;
break ;
}
}
/* Command Complete OGF HOST_CTL */
static void hci_cc_host_ctl ( struct hci_dev * hdev , __u16 ocf , struct sk_buff * skb )
{
__u8 status , param ;
__u16 setting ;
struct hci_rp_read_voice_setting * vs ;
void * sent ;
BT_DBG ( " %s ocf 0x%x " , hdev - > name , ocf ) ;
switch ( ocf ) {
case OCF_RESET :
status = * ( ( __u8 * ) skb - > data ) ;
hci_req_complete ( hdev , status ) ;
break ;
case OCF_SET_EVENT_FLT :
status = * ( ( __u8 * ) skb - > data ) ;
if ( status ) {
BT_DBG ( " %s SET_EVENT_FLT failed %d " , hdev - > name , status ) ;
} else {
BT_DBG ( " %s SET_EVENT_FLT succeseful " , hdev - > name ) ;
}
break ;
case OCF_WRITE_AUTH_ENABLE :
sent = hci_sent_cmd_data ( hdev , OGF_HOST_CTL , OCF_WRITE_AUTH_ENABLE ) ;
if ( ! sent )
break ;
status = * ( ( __u8 * ) skb - > data ) ;
param = * ( ( __u8 * ) sent ) ;
if ( ! status ) {
if ( param = = AUTH_ENABLED )
set_bit ( HCI_AUTH , & hdev - > flags ) ;
else
clear_bit ( HCI_AUTH , & hdev - > flags ) ;
}
hci_req_complete ( hdev , status ) ;
break ;
case OCF_WRITE_ENCRYPT_MODE :
sent = hci_sent_cmd_data ( hdev , OGF_HOST_CTL , OCF_WRITE_ENCRYPT_MODE ) ;
if ( ! sent )
break ;
status = * ( ( __u8 * ) skb - > data ) ;
param = * ( ( __u8 * ) sent ) ;
if ( ! status ) {
if ( param )
set_bit ( HCI_ENCRYPT , & hdev - > flags ) ;
else
clear_bit ( HCI_ENCRYPT , & hdev - > flags ) ;
}
hci_req_complete ( hdev , status ) ;
break ;
case OCF_WRITE_CA_TIMEOUT :
status = * ( ( __u8 * ) skb - > data ) ;
if ( status ) {
BT_DBG ( " %s OCF_WRITE_CA_TIMEOUT failed %d " , hdev - > name , status ) ;
} else {
BT_DBG ( " %s OCF_WRITE_CA_TIMEOUT succeseful " , hdev - > name ) ;
}
break ;
case OCF_WRITE_PG_TIMEOUT :
status = * ( ( __u8 * ) skb - > data ) ;
if ( status ) {
BT_DBG ( " %s OCF_WRITE_PG_TIMEOUT failed %d " , hdev - > name , status ) ;
} else {
BT_DBG ( " %s: OCF_WRITE_PG_TIMEOUT succeseful " , hdev - > name ) ;
}
break ;
case OCF_WRITE_SCAN_ENABLE :
sent = hci_sent_cmd_data ( hdev , OGF_HOST_CTL , OCF_WRITE_SCAN_ENABLE ) ;
if ( ! sent )
break ;
status = * ( ( __u8 * ) skb - > data ) ;
param = * ( ( __u8 * ) sent ) ;
BT_DBG ( " param 0x%x " , param ) ;
if ( ! status ) {
clear_bit ( HCI_PSCAN , & hdev - > flags ) ;
clear_bit ( HCI_ISCAN , & hdev - > flags ) ;
if ( param & SCAN_INQUIRY )
set_bit ( HCI_ISCAN , & hdev - > flags ) ;
if ( param & SCAN_PAGE )
set_bit ( HCI_PSCAN , & hdev - > flags ) ;
}
hci_req_complete ( hdev , status ) ;
break ;
case OCF_READ_VOICE_SETTING :
vs = ( struct hci_rp_read_voice_setting * ) skb - > data ;
if ( vs - > status ) {
BT_DBG ( " %s READ_VOICE_SETTING failed %d " , hdev - > name , vs - > status ) ;
break ;
}
setting = __le16_to_cpu ( vs - > voice_setting ) ;
if ( hdev - > voice_setting ! = setting ) {
hdev - > voice_setting = setting ;
BT_DBG ( " %s: voice setting 0x%04x " , hdev - > name , setting ) ;
if ( hdev - > notify ) {
tasklet_disable ( & hdev - > tx_task ) ;
hdev - > notify ( hdev , HCI_NOTIFY_VOICE_SETTING ) ;
tasklet_enable ( & hdev - > tx_task ) ;
}
}
break ;
case OCF_WRITE_VOICE_SETTING :
sent = hci_sent_cmd_data ( hdev , OGF_HOST_CTL , OCF_WRITE_VOICE_SETTING ) ;
if ( ! sent )
break ;
status = * ( ( __u8 * ) skb - > data ) ;
2005-11-08 20:57:21 +03:00
setting = __le16_to_cpu ( get_unaligned ( ( __le16 * ) sent ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! status & & hdev - > voice_setting ! = setting ) {
hdev - > voice_setting = setting ;
BT_DBG ( " %s: voice setting 0x%04x " , hdev - > name , setting ) ;
if ( hdev - > notify ) {
tasklet_disable ( & hdev - > tx_task ) ;
hdev - > notify ( hdev , HCI_NOTIFY_VOICE_SETTING ) ;
tasklet_enable ( & hdev - > tx_task ) ;
}
}
hci_req_complete ( hdev , status ) ;
break ;
case OCF_HOST_BUFFER_SIZE :
status = * ( ( __u8 * ) skb - > data ) ;
if ( status ) {
BT_DBG ( " %s OCF_BUFFER_SIZE failed %d " , hdev - > name , status ) ;
hci_req_complete ( hdev , status ) ;
}
break ;
default :
BT_DBG ( " %s Command complete: ogf HOST_CTL ocf %x " , hdev - > name , ocf ) ;
break ;
}
}
/* Command Complete OGF INFO_PARAM */
static void hci_cc_info_param ( struct hci_dev * hdev , __u16 ocf , struct sk_buff * skb )
{
struct hci_rp_read_loc_features * lf ;
struct hci_rp_read_buffer_size * bs ;
struct hci_rp_read_bd_addr * ba ;
BT_DBG ( " %s ocf 0x%x " , hdev - > name , ocf ) ;
switch ( ocf ) {
case OCF_READ_LOCAL_FEATURES :
lf = ( struct hci_rp_read_loc_features * ) skb - > data ;
if ( lf - > status ) {
BT_DBG ( " %s READ_LOCAL_FEATURES failed %d " , hdev - > name , lf - > status ) ;
break ;
}
memcpy ( hdev - > features , lf - > features , sizeof ( hdev - > features ) ) ;
/* Adjust default settings according to features
* supported by device . */
if ( hdev - > features [ 0 ] & LMP_3SLOT )
hdev - > pkt_type | = ( HCI_DM3 | HCI_DH3 ) ;
if ( hdev - > features [ 0 ] & LMP_5SLOT )
hdev - > pkt_type | = ( HCI_DM5 | HCI_DH5 ) ;
if ( hdev - > features [ 1 ] & LMP_HV2 )
hdev - > pkt_type | = ( HCI_HV2 ) ;
if ( hdev - > features [ 1 ] & LMP_HV3 )
hdev - > pkt_type | = ( HCI_HV3 ) ;
BT_DBG ( " %s: features 0x%x 0x%x 0x%x " , hdev - > name , lf - > features [ 0 ] , lf - > features [ 1 ] , lf - > features [ 2 ] ) ;
break ;
case OCF_READ_BUFFER_SIZE :
bs = ( struct hci_rp_read_buffer_size * ) skb - > data ;
if ( bs - > status ) {
BT_DBG ( " %s READ_BUFFER_SIZE failed %d " , hdev - > name , bs - > status ) ;
hci_req_complete ( hdev , bs - > status ) ;
break ;
}
hdev - > acl_mtu = __le16_to_cpu ( bs - > acl_mtu ) ;
2006-07-03 12:02:29 +04:00
hdev - > sco_mtu = bs - > sco_mtu ;
hdev - > acl_pkts = __le16_to_cpu ( bs - > acl_max_pkt ) ;
hdev - > sco_pkts = __le16_to_cpu ( bs - > sco_max_pkt ) ;
if ( test_bit ( HCI_QUIRK_FIXUP_BUFFER_SIZE , & hdev - > quirks ) ) {
hdev - > sco_mtu = 64 ;
hdev - > sco_pkts = 8 ;
}
hdev - > acl_cnt = hdev - > acl_pkts ;
hdev - > sco_cnt = hdev - > sco_pkts ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " %s mtu: acl %d, sco %d max_pkt: acl %d, sco %d " , hdev - > name ,
hdev - > acl_mtu , hdev - > sco_mtu , hdev - > acl_pkts , hdev - > sco_pkts ) ;
break ;
case OCF_READ_BD_ADDR :
ba = ( struct hci_rp_read_bd_addr * ) skb - > data ;
if ( ! ba - > status ) {
bacpy ( & hdev - > bdaddr , & ba - > bdaddr ) ;
} else {
BT_DBG ( " %s: READ_BD_ADDR failed %d " , hdev - > name , ba - > status ) ;
}
hci_req_complete ( hdev , ba - > status ) ;
break ;
default :
BT_DBG ( " %s Command complete: ogf INFO_PARAM ocf %x " , hdev - > name , ocf ) ;
break ;
}
}
/* Command Status OGF LINK_CTL */
static inline void hci_cs_create_conn ( struct hci_dev * hdev , __u8 status )
{
struct hci_conn * conn ;
struct hci_cp_create_conn * cp = hci_sent_cmd_data ( hdev , OGF_LINK_CTL , OCF_CREATE_CONN ) ;
if ( ! cp )
return ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , & cp - > bdaddr ) ;
BT_DBG ( " %s status 0x%x bdaddr %s conn %p " , hdev - > name ,
status , batostr ( & cp - > bdaddr ) , conn ) ;
if ( status ) {
if ( conn & & conn - > state = = BT_CONNECT ) {
conn - > state = BT_CLOSED ;
hci_proto_connect_cfm ( conn , status ) ;
hci_conn_del ( conn ) ;
}
} else {
if ( ! conn ) {
conn = hci_conn_add ( hdev , ACL_LINK , & cp - > bdaddr ) ;
if ( conn ) {
conn - > out = 1 ;
conn - > link_mode | = HCI_LM_MASTER ;
} else
BT_ERR ( " No memmory for new connection " ) ;
}
}
hci_dev_unlock ( hdev ) ;
}
static void hci_cs_link_ctl ( struct hci_dev * hdev , __u16 ocf , __u8 status )
{
BT_DBG ( " %s ocf 0x%x " , hdev - > name , ocf ) ;
switch ( ocf ) {
case OCF_CREATE_CONN :
hci_cs_create_conn ( hdev , status ) ;
break ;
case OCF_ADD_SCO :
if ( status ) {
struct hci_conn * acl , * sco ;
struct hci_cp_add_sco * cp = hci_sent_cmd_data ( hdev , OGF_LINK_CTL , OCF_ADD_SCO ) ;
__u16 handle ;
if ( ! cp )
break ;
handle = __le16_to_cpu ( cp - > handle ) ;
BT_DBG ( " %s Add SCO error: handle %d status 0x%x " , hdev - > name , handle , status ) ;
hci_dev_lock ( hdev ) ;
acl = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( acl & & ( sco = acl - > link ) ) {
sco - > state = BT_CLOSED ;
hci_proto_connect_cfm ( sco , status ) ;
hci_conn_del ( sco ) ;
}
hci_dev_unlock ( hdev ) ;
}
break ;
case OCF_INQUIRY :
if ( status ) {
BT_DBG ( " %s Inquiry error: status 0x%x " , hdev - > name , status ) ;
hci_req_complete ( hdev , status ) ;
} else {
set_bit ( HCI_INQUIRY , & hdev - > flags ) ;
}
break ;
default :
BT_DBG ( " %s Command status: ogf LINK_CTL ocf %x status %d " ,
hdev - > name , ocf , status ) ;
break ;
}
}
/* Command Status OGF LINK_POLICY */
static void hci_cs_link_policy ( struct hci_dev * hdev , __u16 ocf , __u8 status )
{
BT_DBG ( " %s ocf 0x%x " , hdev - > name , ocf ) ;
switch ( ocf ) {
default :
BT_DBG ( " %s Command status: ogf HOST_POLICY ocf %x " , hdev - > name , ocf ) ;
break ;
}
}
/* Command Status OGF HOST_CTL */
static void hci_cs_host_ctl ( struct hci_dev * hdev , __u16 ocf , __u8 status )
{
BT_DBG ( " %s ocf 0x%x " , hdev - > name , ocf ) ;
switch ( ocf ) {
default :
BT_DBG ( " %s Command status: ogf HOST_CTL ocf %x " , hdev - > name , ocf ) ;
break ;
}
}
/* Command Status OGF INFO_PARAM */
static void hci_cs_info_param ( struct hci_dev * hdev , __u16 ocf , __u8 status )
{
BT_DBG ( " %s: hci_cs_info_param: ocf 0x%x " , hdev - > name , ocf ) ;
switch ( ocf ) {
default :
BT_DBG ( " %s Command status: ogf INFO_PARAM ocf %x " , hdev - > name , ocf ) ;
break ;
}
}
/* Inquiry Complete */
static inline void hci_inquiry_complete_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
__u8 status = * ( ( __u8 * ) skb - > data ) ;
BT_DBG ( " %s status %d " , hdev - > name , status ) ;
clear_bit ( HCI_INQUIRY , & hdev - > flags ) ;
hci_req_complete ( hdev , status ) ;
}
/* Inquiry Result */
static inline void hci_inquiry_result_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
2005-08-10 07:27:49 +04:00
struct inquiry_data data ;
2005-04-17 02:20:36 +04:00
struct inquiry_info * info = ( struct inquiry_info * ) ( skb - > data + 1 ) ;
int num_rsp = * ( ( __u8 * ) skb - > data ) ;
BT_DBG ( " %s num_rsp %d " , hdev - > name , num_rsp ) ;
2005-08-10 07:27:49 +04:00
if ( ! num_rsp )
return ;
2005-04-17 02:20:36 +04:00
hci_dev_lock ( hdev ) ;
2005-08-10 07:27:49 +04:00
2005-04-17 02:20:36 +04:00
for ( ; num_rsp ; num_rsp - - ) {
bacpy ( & data . bdaddr , & info - > bdaddr ) ;
data . pscan_rep_mode = info - > pscan_rep_mode ;
data . pscan_period_mode = info - > pscan_period_mode ;
data . pscan_mode = info - > pscan_mode ;
memcpy ( data . dev_class , info - > dev_class , 3 ) ;
data . clock_offset = info - > clock_offset ;
data . rssi = 0x00 ;
info + + ;
hci_inquiry_cache_update ( hdev , & data ) ;
}
2005-08-10 07:27:49 +04:00
2005-04-17 02:20:36 +04:00
hci_dev_unlock ( hdev ) ;
}
/* Inquiry Result With RSSI */
static inline void hci_inquiry_result_with_rssi_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
2005-08-10 07:27:49 +04:00
struct inquiry_data data ;
2005-04-17 02:20:36 +04:00
int num_rsp = * ( ( __u8 * ) skb - > data ) ;
BT_DBG ( " %s num_rsp %d " , hdev - > name , num_rsp ) ;
2005-08-10 07:27:49 +04:00
if ( ! num_rsp )
return ;
2005-04-17 02:20:36 +04:00
hci_dev_lock ( hdev ) ;
2005-08-10 07:27:49 +04:00
if ( ( skb - > len - 1 ) / num_rsp ! = sizeof ( struct inquiry_info_with_rssi ) ) {
struct inquiry_info_with_rssi_and_pscan_mode * info =
( struct inquiry_info_with_rssi_and_pscan_mode * ) ( skb - > data + 1 ) ;
for ( ; num_rsp ; num_rsp - - ) {
bacpy ( & data . bdaddr , & info - > bdaddr ) ;
data . pscan_rep_mode = info - > pscan_rep_mode ;
data . pscan_period_mode = info - > pscan_period_mode ;
data . pscan_mode = info - > pscan_mode ;
memcpy ( data . dev_class , info - > dev_class , 3 ) ;
data . clock_offset = info - > clock_offset ;
data . rssi = info - > rssi ;
info + + ;
hci_inquiry_cache_update ( hdev , & data ) ;
}
} else {
struct inquiry_info_with_rssi * info =
( struct inquiry_info_with_rssi * ) ( skb - > data + 1 ) ;
for ( ; num_rsp ; num_rsp - - ) {
bacpy ( & data . bdaddr , & info - > bdaddr ) ;
data . pscan_rep_mode = info - > pscan_rep_mode ;
data . pscan_period_mode = info - > pscan_period_mode ;
data . pscan_mode = 0x00 ;
memcpy ( data . dev_class , info - > dev_class , 3 ) ;
data . clock_offset = info - > clock_offset ;
data . rssi = info - > rssi ;
info + + ;
hci_inquiry_cache_update ( hdev , & data ) ;
}
2005-04-17 02:20:36 +04:00
}
2005-08-10 07:27:49 +04:00
2005-04-17 02:20:36 +04:00
hci_dev_unlock ( hdev ) ;
}
2005-09-13 03:32:25 +04:00
/* Extended Inquiry Result */
static inline void hci_extended_inquiry_result_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct inquiry_data data ;
struct extended_inquiry_info * info = ( struct extended_inquiry_info * ) ( skb - > data + 1 ) ;
int num_rsp = * ( ( __u8 * ) skb - > data ) ;
BT_DBG ( " %s num_rsp %d " , hdev - > name , num_rsp ) ;
if ( ! num_rsp )
return ;
hci_dev_lock ( hdev ) ;
for ( ; num_rsp ; num_rsp - - ) {
bacpy ( & data . bdaddr , & info - > bdaddr ) ;
data . pscan_rep_mode = info - > pscan_rep_mode ;
data . pscan_period_mode = info - > pscan_period_mode ;
data . pscan_mode = 0x00 ;
memcpy ( data . dev_class , info - > dev_class , 3 ) ;
data . clock_offset = info - > clock_offset ;
data . rssi = info - > rssi ;
info + + ;
hci_inquiry_cache_update ( hdev , & data ) ;
}
hci_dev_unlock ( hdev ) ;
}
2005-04-17 02:20:36 +04:00
/* Connect Request */
static inline void hci_conn_request_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_conn_request * ev = ( struct hci_ev_conn_request * ) skb - > data ;
int mask = hdev - > link_mode ;
BT_DBG ( " %s Connection request: %s type 0x%x " , hdev - > name ,
batostr ( & ev - > bdaddr ) , ev - > link_type ) ;
mask | = hci_proto_connect_ind ( hdev , & ev - > bdaddr , ev - > link_type ) ;
if ( mask & HCI_LM_ACCEPT ) {
/* Connection accepted */
struct hci_conn * conn ;
struct hci_cp_accept_conn_req cp ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_ba ( hdev , ev - > link_type , & ev - > bdaddr ) ;
if ( ! conn ) {
if ( ! ( conn = hci_conn_add ( hdev , ev - > link_type , & ev - > bdaddr ) ) ) {
BT_ERR ( " No memmory for new connection " ) ;
hci_dev_unlock ( hdev ) ;
return ;
}
}
memcpy ( conn - > dev_class , ev - > dev_class , 3 ) ;
conn - > state = BT_CONNECT ;
hci_dev_unlock ( hdev ) ;
bacpy ( & cp . bdaddr , & ev - > bdaddr ) ;
if ( lmp_rswitch_capable ( hdev ) & & ( mask & HCI_LM_MASTER ) )
cp . role = 0x00 ; /* Become master */
else
cp . role = 0x01 ; /* Remain slave */
hci_send_cmd ( hdev , OGF_LINK_CTL , OCF_ACCEPT_CONN_REQ , sizeof ( cp ) , & cp ) ;
} else {
/* Connection rejected */
struct hci_cp_reject_conn_req cp ;
bacpy ( & cp . bdaddr , & ev - > bdaddr ) ;
cp . reason = 0x0f ;
hci_send_cmd ( hdev , OGF_LINK_CTL , OCF_REJECT_CONN_REQ , sizeof ( cp ) , & cp ) ;
}
}
/* Connect Complete */
static inline void hci_conn_complete_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_conn_complete * ev = ( struct hci_ev_conn_complete * ) skb - > data ;
struct hci_conn * conn = NULL ;
BT_DBG ( " %s " , hdev - > name ) ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_ba ( hdev , ev - > link_type , & ev - > bdaddr ) ;
if ( ! conn ) {
hci_dev_unlock ( hdev ) ;
return ;
}
if ( ! ev - > status ) {
conn - > handle = __le16_to_cpu ( ev - > handle ) ;
conn - > state = BT_CONNECTED ;
if ( test_bit ( HCI_AUTH , & hdev - > flags ) )
conn - > link_mode | = HCI_LM_AUTH ;
if ( test_bit ( HCI_ENCRYPT , & hdev - > flags ) )
conn - > link_mode | = HCI_LM_ENCRYPT ;
/* Set link policy */
if ( conn - > type = = ACL_LINK & & hdev - > link_policy ) {
struct hci_cp_write_link_policy cp ;
cp . handle = ev - > handle ;
cp . policy = __cpu_to_le16 ( hdev - > link_policy ) ;
hci_send_cmd ( hdev , OGF_LINK_POLICY , OCF_WRITE_LINK_POLICY , sizeof ( cp ) , & cp ) ;
}
/* Set packet type for incoming connection */
if ( ! conn - > out ) {
struct hci_cp_change_conn_ptype cp ;
cp . handle = ev - > handle ;
cp . pkt_type = ( conn - > type = = ACL_LINK ) ?
__cpu_to_le16 ( hdev - > pkt_type & ACL_PTYPE_MASK ) :
__cpu_to_le16 ( hdev - > pkt_type & SCO_PTYPE_MASK ) ;
hci_send_cmd ( hdev , OGF_LINK_CTL , OCF_CHANGE_CONN_PTYPE , sizeof ( cp ) , & cp ) ;
}
} else
conn - > state = BT_CLOSED ;
if ( conn - > type = = ACL_LINK ) {
struct hci_conn * sco = conn - > link ;
if ( sco ) {
if ( ! ev - > status )
hci_add_sco ( sco , conn - > handle ) ;
else {
hci_proto_connect_cfm ( sco , ev - > status ) ;
hci_conn_del ( sco ) ;
}
}
}
hci_proto_connect_cfm ( conn , ev - > status ) ;
if ( ev - > status )
hci_conn_del ( conn ) ;
hci_dev_unlock ( hdev ) ;
}
/* Disconnect Complete */
static inline void hci_disconn_complete_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_disconn_complete * ev = ( struct hci_ev_disconn_complete * ) skb - > data ;
struct hci_conn * conn = NULL ;
__u16 handle = __le16_to_cpu ( ev - > handle ) ;
BT_DBG ( " %s status %d " , hdev - > name , ev - > status ) ;
if ( ev - > status )
return ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( conn ) {
conn - > state = BT_CLOSED ;
hci_proto_disconn_ind ( conn , ev - > reason ) ;
hci_conn_del ( conn ) ;
}
hci_dev_unlock ( hdev ) ;
}
/* Number of completed packets */
static inline void hci_num_comp_pkts_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_num_comp_pkts * ev = ( struct hci_ev_num_comp_pkts * ) skb - > data ;
2005-11-08 20:57:21 +03:00
__le16 * ptr ;
2005-04-17 02:20:36 +04:00
int i ;
skb_pull ( skb , sizeof ( * ev ) ) ;
BT_DBG ( " %s num_hndl %d " , hdev - > name , ev - > num_hndl ) ;
if ( skb - > len < ev - > num_hndl * 4 ) {
BT_DBG ( " %s bad parameters " , hdev - > name ) ;
return ;
}
tasklet_disable ( & hdev - > tx_task ) ;
2005-11-08 20:57:21 +03:00
for ( i = 0 , ptr = ( __le16 * ) skb - > data ; i < ev - > num_hndl ; i + + ) {
2005-04-17 02:20:36 +04:00
struct hci_conn * conn ;
__u16 handle , count ;
handle = __le16_to_cpu ( get_unaligned ( ptr + + ) ) ;
count = __le16_to_cpu ( get_unaligned ( ptr + + ) ) ;
conn = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( conn ) {
conn - > sent - = count ;
if ( conn - > type = = SCO_LINK ) {
if ( ( hdev - > sco_cnt + = count ) > hdev - > sco_pkts )
hdev - > sco_cnt = hdev - > sco_pkts ;
} else {
if ( ( hdev - > acl_cnt + = count ) > hdev - > acl_pkts )
hdev - > acl_cnt = hdev - > acl_pkts ;
}
}
}
hci_sched_tx ( hdev ) ;
tasklet_enable ( & hdev - > tx_task ) ;
}
/* Role Change */
static inline void hci_role_change_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_role_change * ev = ( struct hci_ev_role_change * ) skb - > data ;
struct hci_conn * conn = NULL ;
BT_DBG ( " %s status %d " , hdev - > name , ev - > status ) ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , & ev - > bdaddr ) ;
if ( conn ) {
if ( ! ev - > status ) {
if ( ev - > role )
conn - > link_mode & = ~ HCI_LM_MASTER ;
else
conn - > link_mode | = HCI_LM_MASTER ;
}
clear_bit ( HCI_CONN_RSWITCH_PEND , & conn - > pend ) ;
hci_role_switch_cfm ( conn , ev - > status , ev - > role ) ;
}
hci_dev_unlock ( hdev ) ;
}
/* Authentication Complete */
static inline void hci_auth_complete_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_auth_complete * ev = ( struct hci_ev_auth_complete * ) skb - > data ;
struct hci_conn * conn = NULL ;
__u16 handle = __le16_to_cpu ( ev - > handle ) ;
BT_DBG ( " %s status %d " , hdev - > name , ev - > status ) ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( conn ) {
if ( ! ev - > status )
conn - > link_mode | = HCI_LM_AUTH ;
clear_bit ( HCI_CONN_AUTH_PEND , & conn - > pend ) ;
hci_auth_cfm ( conn , ev - > status ) ;
if ( test_bit ( HCI_CONN_ENCRYPT_PEND , & conn - > pend ) ) {
if ( ! ev - > status ) {
struct hci_cp_set_conn_encrypt cp ;
cp . handle = __cpu_to_le16 ( conn - > handle ) ;
cp . encrypt = 1 ;
hci_send_cmd ( conn - > hdev , OGF_LINK_CTL ,
OCF_SET_CONN_ENCRYPT ,
sizeof ( cp ) , & cp ) ;
} else {
clear_bit ( HCI_CONN_ENCRYPT_PEND , & conn - > pend ) ;
hci_encrypt_cfm ( conn , ev - > status , 0x00 ) ;
}
}
}
hci_dev_unlock ( hdev ) ;
}
/* Encryption Change */
static inline void hci_encrypt_change_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_encrypt_change * ev = ( struct hci_ev_encrypt_change * ) skb - > data ;
struct hci_conn * conn = NULL ;
__u16 handle = __le16_to_cpu ( ev - > handle ) ;
BT_DBG ( " %s status %d " , hdev - > name , ev - > status ) ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( conn ) {
if ( ! ev - > status ) {
if ( ev - > encrypt )
conn - > link_mode | = HCI_LM_ENCRYPT ;
else
conn - > link_mode & = ~ HCI_LM_ENCRYPT ;
}
clear_bit ( HCI_CONN_ENCRYPT_PEND , & conn - > pend ) ;
hci_encrypt_cfm ( conn , ev - > status , ev - > encrypt ) ;
}
hci_dev_unlock ( hdev ) ;
}
/* Change Connection Link Key Complete */
static inline void hci_change_conn_link_key_complete_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_change_conn_link_key_complete * ev = ( struct hci_ev_change_conn_link_key_complete * ) skb - > data ;
struct hci_conn * conn = NULL ;
__u16 handle = __le16_to_cpu ( ev - > handle ) ;
BT_DBG ( " %s status %d " , hdev - > name , ev - > status ) ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( conn ) {
if ( ! ev - > status )
conn - > link_mode | = HCI_LM_SECURE ;
clear_bit ( HCI_CONN_AUTH_PEND , & conn - > pend ) ;
hci_key_change_cfm ( conn , ev - > status ) ;
}
hci_dev_unlock ( hdev ) ;
}
/* Pin Code Request*/
static inline void hci_pin_code_request_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
}
/* Link Key Request */
static inline void hci_link_key_request_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
}
/* Link Key Notification */
static inline void hci_link_key_notify_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
}
/* Clock Offset */
static inline void hci_clock_offset_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_clock_offset * ev = ( struct hci_ev_clock_offset * ) skb - > data ;
struct hci_conn * conn = NULL ;
__u16 handle = __le16_to_cpu ( ev - > handle ) ;
BT_DBG ( " %s status %d " , hdev - > name , ev - > status ) ;
hci_dev_lock ( hdev ) ;
conn = hci_conn_hash_lookup_handle ( hdev , handle ) ;
if ( conn & & ! ev - > status ) {
struct inquiry_entry * ie ;
if ( ( ie = hci_inquiry_cache_lookup ( hdev , & conn - > dst ) ) ) {
ie - > data . clock_offset = ev - > clock_offset ;
ie - > timestamp = jiffies ;
}
}
hci_dev_unlock ( hdev ) ;
}
2005-08-10 07:28:02 +04:00
/* Page Scan Repetition Mode */
static inline void hci_pscan_rep_mode_evt ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_ev_pscan_rep_mode * ev = ( struct hci_ev_pscan_rep_mode * ) skb - > data ;
struct inquiry_entry * ie ;
BT_DBG ( " %s " , hdev - > name ) ;
hci_dev_lock ( hdev ) ;
if ( ( ie = hci_inquiry_cache_lookup ( hdev , & ev - > bdaddr ) ) ) {
ie - > data . pscan_rep_mode = ev - > pscan_rep_mode ;
ie - > timestamp = jiffies ;
}
hci_dev_unlock ( hdev ) ;
}
2005-04-17 02:20:36 +04:00
void hci_event_packet ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct hci_event_hdr * hdr = ( struct hci_event_hdr * ) skb - > data ;
struct hci_ev_cmd_complete * ec ;
struct hci_ev_cmd_status * cs ;
u16 opcode , ocf , ogf ;
skb_pull ( skb , HCI_EVENT_HDR_SIZE ) ;
BT_DBG ( " %s evt 0x%x " , hdev - > name , hdr - > evt ) ;
switch ( hdr - > evt ) {
case HCI_EV_NUM_COMP_PKTS :
hci_num_comp_pkts_evt ( hdev , skb ) ;
break ;
case HCI_EV_INQUIRY_COMPLETE :
hci_inquiry_complete_evt ( hdev , skb ) ;
break ;
case HCI_EV_INQUIRY_RESULT :
hci_inquiry_result_evt ( hdev , skb ) ;
break ;
case HCI_EV_INQUIRY_RESULT_WITH_RSSI :
hci_inquiry_result_with_rssi_evt ( hdev , skb ) ;
break ;
2005-09-13 03:32:25 +04:00
case HCI_EV_EXTENDED_INQUIRY_RESULT :
hci_extended_inquiry_result_evt ( hdev , skb ) ;
break ;
2005-04-17 02:20:36 +04:00
case HCI_EV_CONN_REQUEST :
hci_conn_request_evt ( hdev , skb ) ;
break ;
case HCI_EV_CONN_COMPLETE :
hci_conn_complete_evt ( hdev , skb ) ;
break ;
case HCI_EV_DISCONN_COMPLETE :
hci_disconn_complete_evt ( hdev , skb ) ;
break ;
case HCI_EV_ROLE_CHANGE :
hci_role_change_evt ( hdev , skb ) ;
break ;
case HCI_EV_AUTH_COMPLETE :
hci_auth_complete_evt ( hdev , skb ) ;
break ;
case HCI_EV_ENCRYPT_CHANGE :
hci_encrypt_change_evt ( hdev , skb ) ;
break ;
case HCI_EV_CHANGE_CONN_LINK_KEY_COMPLETE :
hci_change_conn_link_key_complete_evt ( hdev , skb ) ;
break ;
case HCI_EV_PIN_CODE_REQ :
hci_pin_code_request_evt ( hdev , skb ) ;
break ;
case HCI_EV_LINK_KEY_REQ :
hci_link_key_request_evt ( hdev , skb ) ;
break ;
case HCI_EV_LINK_KEY_NOTIFY :
hci_link_key_notify_evt ( hdev , skb ) ;
break ;
case HCI_EV_CLOCK_OFFSET :
hci_clock_offset_evt ( hdev , skb ) ;
break ;
2005-08-10 07:28:02 +04:00
case HCI_EV_PSCAN_REP_MODE :
hci_pscan_rep_mode_evt ( hdev , skb ) ;
break ;
2005-04-17 02:20:36 +04:00
case HCI_EV_CMD_STATUS :
cs = ( struct hci_ev_cmd_status * ) skb - > data ;
skb_pull ( skb , sizeof ( cs ) ) ;
opcode = __le16_to_cpu ( cs - > opcode ) ;
ogf = hci_opcode_ogf ( opcode ) ;
ocf = hci_opcode_ocf ( opcode ) ;
switch ( ogf ) {
case OGF_INFO_PARAM :
hci_cs_info_param ( hdev , ocf , cs - > status ) ;
break ;
case OGF_HOST_CTL :
hci_cs_host_ctl ( hdev , ocf , cs - > status ) ;
break ;
case OGF_LINK_CTL :
hci_cs_link_ctl ( hdev , ocf , cs - > status ) ;
break ;
case OGF_LINK_POLICY :
hci_cs_link_policy ( hdev , ocf , cs - > status ) ;
break ;
default :
BT_DBG ( " %s Command Status OGF %x " , hdev - > name , ogf ) ;
break ;
}
if ( cs - > ncmd ) {
atomic_set ( & hdev - > cmd_cnt , 1 ) ;
if ( ! skb_queue_empty ( & hdev - > cmd_q ) )
hci_sched_cmd ( hdev ) ;
}
break ;
case HCI_EV_CMD_COMPLETE :
ec = ( struct hci_ev_cmd_complete * ) skb - > data ;
skb_pull ( skb , sizeof ( * ec ) ) ;
opcode = __le16_to_cpu ( ec - > opcode ) ;
ogf = hci_opcode_ogf ( opcode ) ;
ocf = hci_opcode_ocf ( opcode ) ;
switch ( ogf ) {
case OGF_INFO_PARAM :
hci_cc_info_param ( hdev , ocf , skb ) ;
break ;
case OGF_HOST_CTL :
hci_cc_host_ctl ( hdev , ocf , skb ) ;
break ;
case OGF_LINK_CTL :
hci_cc_link_ctl ( hdev , ocf , skb ) ;
break ;
case OGF_LINK_POLICY :
hci_cc_link_policy ( hdev , ocf , skb ) ;
break ;
default :
BT_DBG ( " %s Command Completed OGF %x " , hdev - > name , ogf ) ;
break ;
}
if ( ec - > ncmd ) {
atomic_set ( & hdev - > cmd_cnt , 1 ) ;
if ( ! skb_queue_empty ( & hdev - > cmd_q ) )
hci_sched_cmd ( hdev ) ;
}
break ;
}
kfree_skb ( skb ) ;
hdev - > stat . evt_rx + + ;
}
/* Generate internal stack event */
void hci_si_event ( struct hci_dev * hdev , int type , int dlen , void * data )
{
struct hci_event_hdr * hdr ;
struct hci_ev_stack_internal * ev ;
struct sk_buff * skb ;
skb = bt_skb_alloc ( HCI_EVENT_HDR_SIZE + sizeof ( * ev ) + dlen , GFP_ATOMIC ) ;
if ( ! skb )
return ;
hdr = ( void * ) skb_put ( skb , HCI_EVENT_HDR_SIZE ) ;
hdr - > evt = HCI_EV_STACK_INTERNAL ;
hdr - > plen = sizeof ( * ev ) + dlen ;
ev = ( void * ) skb_put ( skb , sizeof ( * ev ) + dlen ) ;
ev - > type = type ;
memcpy ( ev - > data , data , dlen ) ;
2005-08-06 14:36:54 +04:00
bt_cb ( skb ) - > incoming = 1 ;
2005-08-15 04:24:31 +04:00
__net_timestamp ( skb ) ;
2005-08-06 14:36:54 +04:00
2005-08-10 07:30:28 +04:00
bt_cb ( skb ) - > pkt_type = HCI_EVENT_PKT ;
2005-04-17 02:20:36 +04:00
skb - > dev = ( void * ) hdev ;
hci_send_to_sock ( hdev , skb ) ;
kfree_skb ( skb ) ;
}