@@ -3,10 +3,12 @@ use anyhow::{bail, Result};
33use super :: {
44 BT_STATUS_FIXED_LEN , BT_STATUS_FLAG_CONNECTED , BT_STATUS_FLAG_SEND_REQUESTED ,
55 BT_STATUS_FLAG_STARTED , BT_STATUS_V1_FIXED_LEN , BT_STATUS_V1_VERSION , BT_STATUS_V2_FIXED_LEN ,
6- BT_STATUS_V2_VERSION , BT_STATUS_VERSION ,
6+ BT_STATUS_V2_VERSION , BT_STATUS_V3_FIXED_LEN , BT_STATUS_V3_VERSION , BT_STATUS_VERSION ,
77} ;
88#[ derive( Clone , Debug , PartialEq , Eq ) ]
99pub struct BtStatus {
10+ pub status_version : u8 ,
11+ pub decoded_status_version : u8 ,
1012 pub flags : u8 ,
1113 pub target : u8 ,
1214 pub last_status : u8 ,
@@ -57,6 +59,26 @@ pub struct BtStatus {
5759 pub last_encryption_enabled : u8 ,
5860 pub last_disconnection_reason : u8 ,
5961 pub last_hid_open_status : u8 ,
62+ pub reconnect_state : u8 ,
63+ pub reconnect_cycle_attempts : u8 ,
64+ pub last_reconnect_status : u8 ,
65+ pub last_reconnect_reason : u8 ,
66+ pub reconnect_schedule_count : u32 ,
67+ pub reconnect_attempt_count : u32 ,
68+ pub reconnect_success_count : u32 ,
69+ pub reconnect_failed_count : u32 ,
70+ pub reconnect_blocked_count : u32 ,
71+ pub last_reconnect_ms : u32 ,
72+ pub connection_complete_count : u32 ,
73+ pub last_connection_complete_status : u8 ,
74+ pub last_connection_complete_link_type : u8 ,
75+ pub last_connection_complete_ms : u32 ,
76+ pub incoming_l2cap_connection_count : u32 ,
77+ pub incoming_l2cap_hid_control_count : u32 ,
78+ pub incoming_l2cap_hid_interrupt_count : u32 ,
79+ pub last_incoming_l2cap_psm : u16 ,
80+ pub last_incoming_l2cap_local_cid : u16 ,
81+ pub last_incoming_l2cap_ms : u32 ,
6082 pub local_name : String ,
6183}
6284
@@ -85,6 +107,42 @@ impl BtStatus {
85107 || self . disconnection_complete_count > 0
86108 || self . last_security_event_ms > 0
87109 }
110+
111+ pub fn newer_status_version ( & self ) -> bool {
112+ self . status_version > BT_STATUS_VERSION
113+ }
114+
115+ pub fn reconnect_peer_known ( & self ) -> bool {
116+ self . reconnect_state & 0x01 != 0
117+ }
118+
119+ pub fn reconnect_pending ( & self ) -> bool {
120+ self . reconnect_state & 0x02 != 0
121+ }
122+
123+ pub fn reconnect_in_progress ( & self ) -> bool {
124+ self . reconnect_state & 0x04 != 0
125+ }
126+
127+ pub fn reconnect_maxed ( & self ) -> bool {
128+ self . reconnect_state & 0x08 != 0
129+ }
130+
131+ pub fn reconnect_activity_seen ( & self ) -> bool {
132+ self . reconnect_schedule_count > 0
133+ || self . reconnect_attempt_count > 0
134+ || self . reconnect_success_count > 0
135+ || self . reconnect_failed_count > 0
136+ || self . reconnect_blocked_count > 0
137+ || self . last_reconnect_ms > 0
138+ }
139+
140+ pub fn incoming_hid_l2cap_seen ( & self ) -> bool {
141+ self . incoming_l2cap_connection_count > 0
142+ || self . incoming_l2cap_hid_control_count > 0
143+ || self . incoming_l2cap_hid_interrupt_count > 0
144+ || self . last_incoming_l2cap_psm != 0
145+ }
88146}
89147
90148fn read_u16_le ( payload : & [ u8 ] , offset : usize ) -> u16 {
@@ -108,35 +166,58 @@ pub fn decode_bt_status_payload(payload: &[u8]) -> Result<BtStatus> {
108166 let fixed_len = match version {
109167 BT_STATUS_V1_VERSION => BT_STATUS_V1_FIXED_LEN ,
110168 BT_STATUS_V2_VERSION => BT_STATUS_V2_FIXED_LEN ,
169+ BT_STATUS_V3_VERSION => BT_STATUS_V3_FIXED_LEN ,
111170 BT_STATUS_VERSION => BT_STATUS_FIXED_LEN ,
171+ newer if newer > BT_STATUS_VERSION => BT_STATUS_V3_FIXED_LEN ,
112172 _ => bail ! (
113- "BT_STATUS version mismatch (got {}, want {}, {}, or {})" ,
173+ "BT_STATUS version mismatch (got {}, want {}, {}, {}, or {})" ,
114174 payload[ 0 ] ,
115175 BT_STATUS_V1_VERSION ,
116176 BT_STATUS_V2_VERSION ,
177+ BT_STATUS_V3_VERSION ,
117178 BT_STATUS_VERSION
118179 ) ,
119180 } ;
120181 if payload. len ( ) < fixed_len {
121182 bail ! ( "BT_STATUS response truncated ({} bytes)" , payload. len( ) ) ;
122183 }
184+ let decode_name = version <= BT_STATUS_VERSION ;
123185 let name_len_offset = match version {
124186 BT_STATUS_V1_VERSION => BT_STATUS_V1_FIXED_LEN - 1 ,
125187 BT_STATUS_V2_VERSION => BT_STATUS_V2_FIXED_LEN - 1 ,
126- _ => BT_STATUS_FIXED_LEN - 1 ,
188+ BT_STATUS_V3_VERSION => BT_STATUS_V3_FIXED_LEN - 1 ,
189+ BT_STATUS_VERSION => BT_STATUS_FIXED_LEN - 1 ,
190+ _ => payload. len ( ) ,
127191 } ;
128192 let name_start = fixed_len;
129- let name_len = payload[ name_len_offset] as usize ;
130- let need = name_start + name_len;
193+ let name_len = if decode_name {
194+ payload[ name_len_offset] as usize
195+ } else {
196+ 0
197+ } ;
198+ let need = if decode_name {
199+ name_start + name_len
200+ } else {
201+ name_start
202+ } ;
131203 if payload. len ( ) < need {
132204 bail ! (
133205 "BT_STATUS local name truncated (need {need} bytes, have {})" ,
134206 payload. len( )
135207 ) ;
136208 }
137- let has_v2 = version == BT_STATUS_V2_VERSION || version == BT_STATUS_VERSION ;
138- let has_v3 = version == BT_STATUS_VERSION ;
209+ let has_v2 = version == BT_STATUS_V2_VERSION
210+ || version == BT_STATUS_V3_VERSION
211+ || version >= BT_STATUS_VERSION ;
212+ let has_v3 = version == BT_STATUS_V3_VERSION || version >= BT_STATUS_VERSION ;
213+ let has_v4 = version == BT_STATUS_VERSION ;
139214 Ok ( BtStatus {
215+ status_version : version,
216+ decoded_status_version : if version > BT_STATUS_VERSION {
217+ BT_STATUS_V3_VERSION
218+ } else {
219+ version
220+ } ,
140221 flags : payload[ 1 ] ,
141222 target : payload[ 2 ] ,
142223 last_status : payload[ 3 ] ,
@@ -187,6 +268,26 @@ pub fn decode_bt_status_payload(payload: &[u8]) -> Result<BtStatus> {
187268 last_encryption_enabled : if has_v3 { payload[ 145 ] } else { 0 } ,
188269 last_disconnection_reason : if has_v3 { payload[ 146 ] } else { 0 } ,
189270 last_hid_open_status : if has_v3 { payload[ 147 ] } else { 0 } ,
271+ reconnect_state : if has_v4 { payload[ 152 ] } else { 0 } ,
272+ reconnect_cycle_attempts : if has_v4 { payload[ 153 ] } else { 0 } ,
273+ last_reconnect_status : if has_v4 { payload[ 154 ] } else { 0 } ,
274+ last_reconnect_reason : if has_v4 { payload[ 155 ] } else { 0 } ,
275+ reconnect_schedule_count : if has_v4 { read_u32_le ( payload, 156 ) } else { 0 } ,
276+ reconnect_attempt_count : if has_v4 { read_u32_le ( payload, 160 ) } else { 0 } ,
277+ reconnect_success_count : if has_v4 { read_u32_le ( payload, 164 ) } else { 0 } ,
278+ reconnect_failed_count : if has_v4 { read_u32_le ( payload, 168 ) } else { 0 } ,
279+ reconnect_blocked_count : if has_v4 { read_u32_le ( payload, 172 ) } else { 0 } ,
280+ last_reconnect_ms : if has_v4 { read_u32_le ( payload, 176 ) } else { 0 } ,
281+ connection_complete_count : if has_v4 { read_u32_le ( payload, 180 ) } else { 0 } ,
282+ last_connection_complete_status : if has_v4 { payload[ 184 ] } else { 0 } ,
283+ last_connection_complete_link_type : if has_v4 { payload[ 185 ] } else { 0 } ,
284+ last_connection_complete_ms : if has_v4 { read_u32_le ( payload, 188 ) } else { 0 } ,
285+ incoming_l2cap_connection_count : if has_v4 { read_u32_le ( payload, 192 ) } else { 0 } ,
286+ incoming_l2cap_hid_control_count : if has_v4 { read_u32_le ( payload, 196 ) } else { 0 } ,
287+ incoming_l2cap_hid_interrupt_count : if has_v4 { read_u32_le ( payload, 200 ) } else { 0 } ,
288+ last_incoming_l2cap_psm : if has_v4 { read_u16_le ( payload, 204 ) } else { 0 } ,
289+ last_incoming_l2cap_local_cid : if has_v4 { read_u16_le ( payload, 206 ) } else { 0 } ,
290+ last_incoming_l2cap_ms : if has_v4 { read_u32_le ( payload, 208 ) } else { 0 } ,
190291 local_name : String :: from_utf8_lossy ( & payload[ name_start..need] ) . into_owned ( ) ,
191292 } )
192293}
0 commit comments