mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-25 16:43:28 +00:00
automatic HCI link disconnect
This commit is contained in:
parent
c52bf64daa
commit
ee091cf1fd
2
TODO.txt
2
TODO.txt
@ -3,8 +3,6 @@
|
||||
Last milestone reached: Restart client app without restarting BTdaemon possible
|
||||
|
||||
NEXT:
|
||||
- automatic HCI link disconnect
|
||||
- periodically (e.g., 5 seconds) close HCI connections without active L2CAP channel
|
||||
- automatic turn off Bluetooth after X minutes idle
|
||||
- turn off Bluetooth on ctrl-c
|
||||
- automatic start/stop of BTdaemon
|
||||
|
90
src/hci.c
90
src/hci.c
@ -15,9 +15,52 @@
|
||||
// temp
|
||||
#include "l2cap.h"
|
||||
|
||||
#define HCI_CONNECTION_TIMEOUT 10
|
||||
|
||||
// the STACK is here
|
||||
static hci_stack_t hci_stack;
|
||||
|
||||
/**
|
||||
* get connection for a given handle
|
||||
*
|
||||
* @return connection OR NULL, if not found
|
||||
*/
|
||||
static hci_connection_t * connection_for_handle(hci_con_handle_t con_handle){
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
|
||||
if ( ((hci_connection_t *) it)->con_handle == con_handle){
|
||||
return (hci_connection_t *) it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hci_connection_timeout_handler(timer_t *timer){
|
||||
hci_connection_t * connection = linked_item_get_user(&timer->item);
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
if (tv.tv_sec > connection->timestamp.tv_sec + HCI_CONNECTION_TIMEOUT) {
|
||||
// connections might be timed out
|
||||
hci_emit_l2cap_check_timeout(connection);
|
||||
run_loop_set_timer(timer, HCI_CONNECTION_TIMEOUT);
|
||||
} else {
|
||||
// next timeout check at
|
||||
timer->timeout.tv_sec = connection->timestamp.tv_sec + HCI_CONNECTION_TIMEOUT;
|
||||
}
|
||||
run_loop_add_timer(timer);
|
||||
}
|
||||
|
||||
static void hci_connection_timestamp(hci_connection_t *connection){
|
||||
gettimeofday(&connection->timestamp, NULL);
|
||||
}
|
||||
|
||||
static void hci_connection_update_timestamp_for_acl(uint8_t *packet) {
|
||||
// update timestamp
|
||||
hci_con_handle_t con_handle = READ_ACL_CONNECTION_HANDLE(packet);
|
||||
hci_connection_t *connection = connection_for_handle( con_handle);
|
||||
if (connection) hci_connection_timestamp(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* create connection for given address
|
||||
*
|
||||
@ -29,6 +72,9 @@ static hci_connection_t * create_connection_for_addr(bd_addr_t addr){
|
||||
BD_ADDR_COPY(conn->address, addr);
|
||||
conn->con_handle = 0xffff;
|
||||
conn->flags = 0;
|
||||
linked_item_set_user(&conn->timeout.item, conn);
|
||||
conn->timeout.process = hci_connection_timeout_handler;
|
||||
hci_connection_timestamp(conn);
|
||||
linked_list_add(&hci_stack.connections, (linked_item_t *) conn);
|
||||
return conn;
|
||||
}
|
||||
@ -48,21 +94,6 @@ static hci_connection_t * connection_for_address(bd_addr_t address){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get connection for a given handle
|
||||
*
|
||||
* @return connection OR NULL, if not found
|
||||
*/
|
||||
static hci_connection_t * connection_for_handle(hci_con_handle_t con_handle){
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
|
||||
if ( ((hci_connection_t *) it)->con_handle == con_handle){
|
||||
return (hci_connection_t *) it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummy handler called by HCI
|
||||
@ -87,7 +118,13 @@ static bt_control_t null_control = {
|
||||
};
|
||||
|
||||
|
||||
int hci_send_acl_packet(uint8_t *packet, int size){
|
||||
hci_connection_update_timestamp_for_acl(packet);
|
||||
return hci_stack.hci_transport->send_acl_packet(packet, size);
|
||||
}
|
||||
|
||||
static void acl_handler(uint8_t *packet, int size){
|
||||
hci_connection_update_timestamp_for_acl(packet);
|
||||
hci_stack.acl_packet_handler(packet, size);
|
||||
|
||||
// execute main loop
|
||||
@ -114,6 +151,11 @@ static void event_handler(uint8_t *packet, int size){
|
||||
conn->state = OPEN;
|
||||
conn->con_handle = READ_BT_16(packet, 3);
|
||||
conn->flags = 0;
|
||||
|
||||
gettimeofday(&conn->timestamp, NULL);
|
||||
run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT);
|
||||
run_loop_add_timer(&conn->timeout);
|
||||
|
||||
printf("New connection: handle %u, ", conn->con_handle);
|
||||
print_bd_addr( conn->address );
|
||||
printf("\n");
|
||||
@ -129,6 +171,7 @@ static void event_handler(uint8_t *packet, int size){
|
||||
printf("Connection closed: handle %u, ", conn->con_handle);
|
||||
print_bd_addr( conn->address );
|
||||
printf("\n");
|
||||
run_loop_remove_timer(&conn->timeout);
|
||||
linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
|
||||
free( conn );
|
||||
}
|
||||
@ -282,11 +325,6 @@ void hci_run(){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int hci_send_acl_packet(uint8_t *packet, int size){
|
||||
return hci_stack.hci_transport->send_acl_packet(packet, size);
|
||||
}
|
||||
|
||||
int hci_send_cmd_packet(uint8_t *packet, int size){
|
||||
bd_addr_t addr;
|
||||
hci_connection_t * conn;
|
||||
@ -338,6 +376,9 @@ int hci_send_cmd(hci_cmd_t *cmd, ...){
|
||||
return hci_send_cmd_packet(hci_cmd_buffer, size);
|
||||
}
|
||||
|
||||
// Create various non-HCI events.
|
||||
// TODO: generalize, use table similar to hci_create_command
|
||||
|
||||
void hci_emit_state(){
|
||||
uint8_t len = 3;
|
||||
uint8_t event[len];
|
||||
@ -361,3 +402,12 @@ void hci_emit_connection_complete(hci_connection_t *conn){
|
||||
hci_stack.event_packet_handler(event, len);
|
||||
}
|
||||
|
||||
void hci_emit_l2cap_check_timeout(hci_connection_t *conn){
|
||||
uint8_t len = 4;
|
||||
uint8_t event[len];
|
||||
event[0] = HCI_EVENT_L2CAP_TIMEOUT_CHECK;
|
||||
event[1] = 2;
|
||||
bt_store_16(event, 2, conn->con_handle);
|
||||
hci_dump_packet( HCI_EVENT_PACKET, 0, event, len);
|
||||
hci_stack.event_packet_handler(event, len);
|
||||
}
|
||||
|
@ -49,6 +49,10 @@ typedef struct {
|
||||
// errands
|
||||
hci_connection_flags_t flags;
|
||||
|
||||
// timer
|
||||
timer_t timeout;
|
||||
struct timeval timestamp;
|
||||
|
||||
} hci_connection_t;
|
||||
|
||||
/**
|
||||
@ -110,3 +114,4 @@ int hci_send_acl_packet(uint8_t *packet, int size);
|
||||
//
|
||||
void hci_emit_state();
|
||||
void hci_emit_connection_complete(hci_connection_t *conn);
|
||||
void hci_emit_l2cap_check_timeout(hci_connection_t *conn);
|
||||
|
@ -82,9 +82,14 @@
|
||||
|
||||
// data: event (8), len(8), address(48), handle (16), psm (16), source_cid(16), dest_cid (16)
|
||||
#define HCI_EVENT_L2CAP_CHANNEL_OPENED 0x82
|
||||
|
||||
// data: event (8), len(8), channel (16)
|
||||
#define HCI_EVENT_L2CAP_CHANNEL_CLOSED 0x83
|
||||
|
||||
// data: event(8), len(8), handle(16)
|
||||
#define HCI_EVENT_L2CAP_TIMEOUT_CHECK 0x84
|
||||
|
||||
|
||||
#define COMMAND_COMPLETE_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_COMPLETE && READ_BT_16(event,3) == cmd.opcode)
|
||||
|
||||
#define IS_COMMAND(packet, command) (READ_BT_16(packet,0) == command.opcode)
|
||||
|
17
src/l2cap.c
17
src/l2cap.c
@ -139,6 +139,23 @@ void l2cap_event_handler( uint8_t *packet, uint16_t size ){
|
||||
}
|
||||
}
|
||||
|
||||
// HCI Connection Timeouts
|
||||
if (packet[0] == HCI_EVENT_L2CAP_TIMEOUT_CHECK){
|
||||
hci_con_handle_t handle = READ_BT_16(packet, 2);
|
||||
linked_item_t *it;
|
||||
l2cap_channel_t * channel;
|
||||
int used = 0;
|
||||
for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){
|
||||
channel = (l2cap_channel_t *) it;
|
||||
if (channel->handle == handle) {
|
||||
used = 1;
|
||||
}
|
||||
}
|
||||
if (!used) {
|
||||
hci_send_cmd(&hci_disconnect, handle, 0x13); // remote closd connection
|
||||
}
|
||||
}
|
||||
|
||||
// forward to higher layers
|
||||
(*event_packet_handler)(packet, size);
|
||||
|
||||
|
@ -23,27 +23,36 @@ void run_loop_set_timer(timer_t *a, int timeout_in_seconds){
|
||||
|
||||
// compare timers - NULL is assumed to be before the Big Bang
|
||||
// pre: 0 <= tv_usec < 1000000
|
||||
int run_loop_timer_compare(timer_t *a, timer_t *b){
|
||||
|
||||
int run_loop_timeval_compare(struct timeval *a, struct timeval *b){
|
||||
if (!a || !b) return 0;
|
||||
if (!a) return -1;
|
||||
if (!b) return 1;
|
||||
|
||||
if (a->timeout.tv_sec < b->timeout.tv_sec) {
|
||||
if (a->tv_sec < b->tv_sec) {
|
||||
return -1;
|
||||
}
|
||||
if (a->timeout.tv_sec > b->timeout.tv_sec) {
|
||||
if (a->tv_sec > b->tv_sec) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (a->timeout.tv_usec < b->timeout.tv_usec) {
|
||||
|
||||
if (a->tv_usec < b->tv_usec) {
|
||||
return -1;
|
||||
}
|
||||
if (a->timeout.tv_usec > b->timeout.tv_usec) {
|
||||
if (a->tv_usec > b->tv_usec) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
// compare timers - NULL is assumed to be before the Big Bang
|
||||
// pre: 0 <= tv_usec < 1000000
|
||||
int run_loop_timer_compare(timer_t *a, timer_t *b){
|
||||
if (!a || !b) return 0;
|
||||
if (!a) return -1;
|
||||
if (!b) return 1;
|
||||
return run_loop_timeval_compare(&a->timeout, &b->timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,8 @@ typedef struct timer {
|
||||
// set timer based on current time
|
||||
void run_loop_set_timer(timer_t *a, int timeout_in_seconds);
|
||||
|
||||
// compare timers - NULL is assumed to be before the Big Bang
|
||||
// compare timeval or timers - NULL is assumed to be before the Big Bang
|
||||
int run_loop_timeval_compare(struct timeval *a, struct timeval *b);
|
||||
int run_loop_timer_compare(timer_t *a, timer_t *b);
|
||||
|
||||
void run_loop_add_data_source(data_source_t *dataSource); // <-- add DataSource to RunLoop
|
||||
|
Loading…
x
Reference in New Issue
Block a user