automatic HCI link disconnect

This commit is contained in:
matthias.ringwald 2009-08-09 18:39:53 +00:00
parent c52bf64daa
commit ee091cf1fd
7 changed files with 115 additions and 30 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);
}
/**

View File

@ -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