diff --git a/TODO.txt b/TODO.txt index 43c8140d4..6e0781199 100644 --- a/TODO.txt +++ b/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 diff --git a/src/hci.c b/src/hci.c index 856ac4761..bf2a1da40 100644 --- a/src/hci.c +++ b/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); +} diff --git a/src/hci.h b/src/hci.h index 7c71de85d..ec31c0d3c 100644 --- a/src/hci.h +++ b/src/hci.h @@ -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); diff --git a/src/hci_cmds.h b/src/hci_cmds.h index 9f68dd888..cf356f08c 100644 --- a/src/hci_cmds.h +++ b/src/hci_cmds.h @@ -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) diff --git a/src/l2cap.c b/src/l2cap.c index 3bb294d5b..1319d4dd6 100644 --- a/src/l2cap.c +++ b/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); diff --git a/src/run_loop.c b/src/run_loop.c index a5aba922f..b2a6d16d6 100644 --- a/src/run_loop.c +++ b/src/run_loop.c @@ -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); } /** diff --git a/src/run_loop.h b/src/run_loop.h index 82d1b8022..41474252f 100644 --- a/src/run_loop.h +++ b/src/run_loop.h @@ -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