mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-28 19:20:54 +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
|
Last milestone reached: Restart client app without restarting BTdaemon possible
|
||||||
|
|
||||||
NEXT:
|
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
|
- automatic turn off Bluetooth after X minutes idle
|
||||||
- turn off Bluetooth on ctrl-c
|
- turn off Bluetooth on ctrl-c
|
||||||
- automatic start/stop of BTdaemon
|
- automatic start/stop of BTdaemon
|
||||||
|
90
src/hci.c
90
src/hci.c
@ -15,9 +15,52 @@
|
|||||||
// temp
|
// temp
|
||||||
#include "l2cap.h"
|
#include "l2cap.h"
|
||||||
|
|
||||||
|
#define HCI_CONNECTION_TIMEOUT 10
|
||||||
|
|
||||||
// the STACK is here
|
// the STACK is here
|
||||||
static hci_stack_t hci_stack;
|
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
|
* 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);
|
BD_ADDR_COPY(conn->address, addr);
|
||||||
conn->con_handle = 0xffff;
|
conn->con_handle = 0xffff;
|
||||||
conn->flags = 0;
|
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);
|
linked_list_add(&hci_stack.connections, (linked_item_t *) conn);
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
@ -48,21 +94,6 @@ static hci_connection_t * connection_for_address(bd_addr_t address){
|
|||||||
return NULL;
|
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
|
* 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){
|
static void acl_handler(uint8_t *packet, int size){
|
||||||
|
hci_connection_update_timestamp_for_acl(packet);
|
||||||
hci_stack.acl_packet_handler(packet, size);
|
hci_stack.acl_packet_handler(packet, size);
|
||||||
|
|
||||||
// execute main loop
|
// execute main loop
|
||||||
@ -114,6 +151,11 @@ static void event_handler(uint8_t *packet, int size){
|
|||||||
conn->state = OPEN;
|
conn->state = OPEN;
|
||||||
conn->con_handle = READ_BT_16(packet, 3);
|
conn->con_handle = READ_BT_16(packet, 3);
|
||||||
conn->flags = 0;
|
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);
|
printf("New connection: handle %u, ", conn->con_handle);
|
||||||
print_bd_addr( conn->address );
|
print_bd_addr( conn->address );
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -129,6 +171,7 @@ static void event_handler(uint8_t *packet, int size){
|
|||||||
printf("Connection closed: handle %u, ", conn->con_handle);
|
printf("Connection closed: handle %u, ", conn->con_handle);
|
||||||
print_bd_addr( conn->address );
|
print_bd_addr( conn->address );
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
run_loop_remove_timer(&conn->timeout);
|
||||||
linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
|
linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
|
||||||
free( 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){
|
int hci_send_cmd_packet(uint8_t *packet, int size){
|
||||||
bd_addr_t addr;
|
bd_addr_t addr;
|
||||||
hci_connection_t * conn;
|
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);
|
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(){
|
void hci_emit_state(){
|
||||||
uint8_t len = 3;
|
uint8_t len = 3;
|
||||||
uint8_t event[len];
|
uint8_t event[len];
|
||||||
@ -361,3 +402,12 @@ void hci_emit_connection_complete(hci_connection_t *conn){
|
|||||||
hci_stack.event_packet_handler(event, len);
|
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
|
// errands
|
||||||
hci_connection_flags_t flags;
|
hci_connection_flags_t flags;
|
||||||
|
|
||||||
|
// timer
|
||||||
|
timer_t timeout;
|
||||||
|
struct timeval timestamp;
|
||||||
|
|
||||||
} hci_connection_t;
|
} hci_connection_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,3 +114,4 @@ int hci_send_acl_packet(uint8_t *packet, int size);
|
|||||||
//
|
//
|
||||||
void hci_emit_state();
|
void hci_emit_state();
|
||||||
void hci_emit_connection_complete(hci_connection_t *conn);
|
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)
|
// data: event (8), len(8), address(48), handle (16), psm (16), source_cid(16), dest_cid (16)
|
||||||
#define HCI_EVENT_L2CAP_CHANNEL_OPENED 0x82
|
#define HCI_EVENT_L2CAP_CHANNEL_OPENED 0x82
|
||||||
|
|
||||||
// data: event (8), len(8), channel (16)
|
// data: event (8), len(8), channel (16)
|
||||||
#define HCI_EVENT_L2CAP_CHANNEL_CLOSED 0x83
|
#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 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)
|
#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
|
// forward to higher layers
|
||||||
(*event_packet_handler)(packet, size);
|
(*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
|
// compare timers - NULL is assumed to be before the Big Bang
|
||||||
// pre: 0 <= tv_usec < 1000000
|
// 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 || !b) return 0;
|
||||||
if (!a) return -1;
|
if (!a) return -1;
|
||||||
if (!b) return 1;
|
if (!b) return 1;
|
||||||
|
|
||||||
if (a->timeout.tv_sec < b->timeout.tv_sec) {
|
if (a->tv_sec < b->tv_sec) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (a->timeout.tv_sec > b->timeout.tv_sec) {
|
if (a->tv_sec > b->tv_sec) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a->timeout.tv_usec < b->timeout.tv_usec) {
|
if (a->tv_usec < b->tv_usec) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (a->timeout.tv_usec > b->timeout.tv_usec) {
|
if (a->tv_usec > b->tv_usec) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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
|
// set timer based on current time
|
||||||
void run_loop_set_timer(timer_t *a, int timeout_in_seconds);
|
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);
|
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
|
void run_loop_add_data_source(data_source_t *dataSource); // <-- add DataSource to RunLoop
|
||||||
|
Loading…
x
Reference in New Issue
Block a user