diff --git a/example/rfcomm-cat.c b/example/rfcomm-cat.c index 6852b2aca..b783566e2 100644 --- a/example/rfcomm-cat.c +++ b/example/rfcomm-cat.c @@ -97,12 +97,12 @@ void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint break; case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: - // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) + // data: event(8), len(8), status (8), address (48), handle(16), server channel(8), rfcomm_cid(16), max frame size(16) if (packet[2]) { printf("RFCOMM channel open failed, status %u\n", packet[2]); } else { - rfcomm_channel_id = READ_BT_16(packet, 10); - mtu = READ_BT_16(packet, 12); + rfcomm_channel_id = READ_BT_16(packet, 12); + mtu = READ_BT_16(packet, 14); printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_channel_id, mtu); uint8_t message[] = "Hello World from BTstack!\n"; // bt_send_rfcomm(rfcomm_channel_id, message, sizeof(message)); diff --git a/example/rfcomm-echo.c b/example/rfcomm-echo.c index 16d1c3f2d..564e6cff1 100644 --- a/example/rfcomm-echo.c +++ b/example/rfcomm-echo.c @@ -125,8 +125,8 @@ void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint bd_addr_t event_addr; uint16_t mtu; uint16_t psm; + uint8_t rfcomm_channel_nr; uint16_t rfcomm_channel_id; - uint16_t rfcomm_channel_nr; uint8_t credits; static uint32_t packet_counter = 0; static char packet_info[30]; // "packets: 1234567890" @@ -151,21 +151,31 @@ void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint case BTSTACK_EVENT_STATE: // bt stack activated, get started if (packet[2] == HCI_STATE_WORKING) { - // register RFCOMM channel - bt_send_cmd(&rfcomm_register_service, 0x4711, 100); // random registration ID - } + // get persistent RFCOMM channel + printf("HCI_STATE_WORKING\n"); + bt_send_cmd(&rfcomm_persistent_channel_for_service, "ch.ringwald.btstack.rfcomm-echo2"); + } break; - case RFCOMM_EVENT_SERVICE_REGISTERED: - rfcomm_channel_nr = packet[5]; + case RFCOMM_EVENT_PERSISTENT_CHANNEL: + rfcomm_channel_nr = packet[3]; printf("RFCOMM channel %u was assigned by BTdaemon\n", rfcomm_channel_nr); + bt_send_cmd(&rfcomm_register_service, rfcomm_channel_nr, 100); // reserved channel, mtu=100 + break; + + case RFCOMM_EVENT_SERVICE_REGISTERED: + printf("RFCOMM_EVENT_SERVICE_REGISTERED\n"); + rfcomm_channel_nr = packet[3]; // register SDP for our SPP create_spp_service(service_buffer, rfcomm_channel_nr); bt_send_cmd(&sdp_register_service_record, service_buffer); + bt_send_cmd(&btstack_set_discoverable, 1); break; case SDP_SERVICE_REGISTERED: - bt_send_cmd(&btstack_set_discoverable, 1); + // event not sent yet + // printf("SDP_SERVICE_REGISTERED\n"); + // bt_send_cmd(&btstack_set_discoverable, 1); break; case HCI_EVENT_PIN_CODE_REQUEST: diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index dc0e670ff..2c4189f95 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -168,9 +168,11 @@ extern "C" { // data: event(8), len(8), local_cid(16), credits(8) #define RFCOMM_EVENT_CREDITS 0x84 -// data: event(8), len(8), status (8), registration id(16), rfcomm server channel id (8) +// data: event(8), len(8), status (8), rfcomm server channel id (8) #define RFCOMM_EVENT_SERVICE_REGISTERED 0x85 +// data: event(8), len(8), status (8), rfcomm server channel id (8) +#define RFCOMM_EVENT_PERSISTENT_CHANNEL 0x86 // data: event(8), len(8), service_record_handle(32) #define SDP_SERVICE_REGISTERED 0x90 @@ -203,6 +205,7 @@ extern "C" { #define L2CAP_CONFIG_RESPONSE_RESULT_UNKNOWN_OPTIONS 0x69 #define RFCOMM_MULTIPLEXER_STOPPED 0x70 +#define RFCOMM_CHANNEL_ALREADY_REGISTERED 0x71 /** * Default INQ Mode @@ -296,13 +299,21 @@ extern const hci_cmd_t l2cap_unregister_service; extern const hci_cmd_t sdp_register_service_record; extern const hci_cmd_t sdp_unregister_service_record; +// accept connection @param bd_addr(48), rfcomm_cid (16) extern const hci_cmd_t rfcomm_accept_connection; +// create rfcomm channel: @param bd_addr(48), channel (8) extern const hci_cmd_t rfcomm_create_channel; +// decline rfcomm disconnect,@param bd_addr(48), rfcomm cid (16), reason(8) extern const hci_cmd_t rfcomm_decline_connection; +// disconnect rfcomm disconnect, @param rfcomm_cid(8), reason(8) extern const hci_cmd_t rfcomm_disconnect; +// register rfcomm service: @param channel(8), mtu (16) extern const hci_cmd_t rfcomm_register_service; +// unregister rfcomm service, @param service_channel(16) extern const hci_cmd_t rfcomm_unregister_service; - +// request persisten rfcomm channel for service name: serive name (char*) +extern const hci_cmd_t rfcomm_persistent_channel_for_service; + #if defined __cplusplus } #endif diff --git a/src/daemon.c b/src/daemon.c index eae4591ec..f23b84cae 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -111,6 +111,9 @@ static void (*bluetooth_status_handler)(BLUETOOTH_STATE state) = dummy_bluetooth static int global_enable = 0; +static remote_device_db_t * remote_device_db = NULL; +static rfcomm_channel_generator = 1; + static void dummy_bluetooth_status_handler(BLUETOOTH_STATE state){ printf("Bluetooth status: %u\n", state); }; @@ -239,9 +242,9 @@ static int btstack_command_handler(connection_t *connection, uint8_t *packet, ui rfcomm_disconnect_internal(cid); break; case RFCOMM_REGISTER_SERVICE: - registration_id = READ_BT_16(packet, 3); - mtu = READ_BT_16(packet, 5); - rfcomm_register_service_internal(connection, registration_id, mtu); + rfcomm_channel = packet[3]; + mtu = READ_BT_16(packet, 4); + rfcomm_register_service_internal(connection, rfcomm_channel, mtu); break; case RFCOMM_UNREGISTER_SERVICE: service_channel = READ_BT_16(packet, 3); @@ -256,6 +259,24 @@ static int btstack_command_handler(connection_t *connection, uint8_t *packet, ui reason = packet[7]; rfcomm_decline_connection_internal(cid); break; + case RFCOMM_PERSISTENT_CHANNEL: { + if (remote_device_db) { + // enforce \0 + packet[3+248] = 0; + rfcomm_channel = remote_device_db->persistent_rfcomm_channel(&packet[3]); + } else { + // NOTE: hack for non-iOS platforms + rfcomm_channel = rfcomm_channel_generator++; + } + uint8_t event[4]; + event[0] = RFCOMM_EVENT_PERSISTENT_CHANNEL; + event[1] = sizeof(event) - 2; + event[2] = 0; + event[3] = rfcomm_channel; + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); + socket_connection_send_packet(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); + break; + } case SDP_REGISTER_SERVICE_RECORD: printf("SDP_REGISTER_SERVICE_RECORD size %u\n", size); @@ -493,7 +514,6 @@ int main (int argc, char * const * argv){ bt_control_t * control = NULL; - remote_device_db_t * remote_device_db = NULL; #ifdef HAVE_TRANSPORT_H4 transport = hci_transport_h4_instance(); diff --git a/src/hci.h b/src/hci.h index 11cea482e..d3446de40 100644 --- a/src/hci.h +++ b/src/hci.h @@ -129,7 +129,8 @@ extern "C" { #define RFCOMM_UNREGISTER_SERVICE 0x43 #define RFCOMM_ACCEPT_CONNECTION 0x44 #define RFCOMM_DECLINE_CONNECTION 0x45 - +#define RFCOMM_PERSISTENT_CHANNEL 0x46 + // #define IS_COMMAND(packet, command) (READ_BT_16(packet,0) == command.opcode) diff --git a/src/hci_cmds.c b/src/hci_cmds.c index 616c71c4e..008b796a3 100644 --- a/src/hci_cmds.c +++ b/src/hci_cmds.c @@ -404,21 +404,24 @@ const hci_cmd_t rfcomm_create_channel = { const hci_cmd_t rfcomm_disconnect = { OPCODE(OGF_BTSTACK, RFCOMM_DISCONNECT), "21" }; -// register rfcomm service: @param registration id(16), mtu (16) + +// register rfcomm service: @param channel(8), mtu (16) const hci_cmd_t rfcomm_register_service = { - OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE), "22" + OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE), "12" }; // unregister rfcomm service, @param service_channel(16) const hci_cmd_t rfcomm_unregister_service = { OPCODE(OGF_BTSTACK, RFCOMM_UNREGISTER_SERVICE), "2" }; -// accept connection @param bd_addr(48), rfcomm_cid (16) +// accept connection @param source cid (16) const hci_cmd_t rfcomm_accept_connection = { OPCODE(OGF_BTSTACK, RFCOMM_ACCEPT_CONNECTION), "2" - // @param source cid (16) }; -// decline rfcomm disconnect,@param bd_addr(48), rfcomm cid (16), reason(8) +// decline connection @param source cid (16) const hci_cmd_t rfcomm_decline_connection = { OPCODE(OGF_BTSTACK, RFCOMM_DECLINE_CONNECTION), "21" - // @param source cid (16), reason(8) +}; +// request persisten rfcomm channel number for named service +const hci_cmd_t rfcomm_persistent_channel_for_service = { + OPCODE(OGF_BTSTACK, RFCOMM_PERSISTENT_CHANNEL), "N" }; diff --git a/src/remote_device_db.h b/src/remote_device_db.h index b2b775a6d..14f3ee848 100644 --- a/src/remote_device_db.h +++ b/src/remote_device_db.h @@ -46,10 +46,14 @@ typedef struct { void (*put_link_key)(bd_addr_t *bd_addr, link_key_t *key); void (*delete_link_key)(bd_addr_t *bd_addr); - // remove name + // remote name int (*get_name)(bd_addr_t *bd_addr, device_name_t *device_name); void (*put_name)(bd_addr_t *bd_addr, device_name_t *device_name); void (*delete_name)(bd_addr_t *bd_addr); + + // persistent rfcomm channel + uint8_t (*persistent_rfcomm_channel)(char *servicename); + } remote_device_db_t; extern remote_device_db_t remote_device_db_iphone; diff --git a/src/remote_device_db_iphone.m b/src/remote_device_db_iphone.m index 8ce4a5c7c..2b17d2ea2 100644 --- a/src/remote_device_db_iphone.m +++ b/src/remote_device_db_iphone.m @@ -35,13 +35,21 @@ #define BTdaemonID "ch.ringwald.btdaemon" #define BTDaemonPrefsPath "Library/Preferences/ch.ringwald.btdaemon.plist" + #define DEVICES_KEY "devices" #define PREFS_REMOTE_NAME @"RemoteName" #define PREFS_LINK_KEY @"LinkKey" +#define MAX_RFCOMM_CHANNEL_NR 30 + +#define RFCOMM_SERVICES_KEY "rfcommServices" +#define PREFS_CHANNEL @"channel" +#define PREFS_LAST_USED @"lastUsed" + static void put_name(bd_addr_t *bd_addr, device_name_t *device_name); -static NSMutableDictionary *remote_devices = nil; +static NSMutableDictionary *remote_devices = nil; +static NSMutableDictionary *rfcomm_services = nil; // Device info static void db_open(){ @@ -53,7 +61,8 @@ static void db_open(){ // NSDictionary * dict = [defaults persistentDomainForName:BTdaemonID]; // NSDictionary * dict = (NSDictionary*) CFPreferencesCopyAppValue(CFSTR(DEVICES_KEY), CFSTR(BTdaemonID)); - NSDictionary * dict = (NSDictionary*) CFPreferencesCopyAppValue(CFSTR(DEVICES_KEY), CFSTR(BTdaemonID)); + NSDictionary * dict; + dict = (NSDictionary*) CFPreferencesCopyAppValue(CFSTR(DEVICES_KEY), CFSTR(BTdaemonID)); remote_devices = [[NSMutableDictionary alloc] initWithCapacity:([dict count]+5)]; // copy entries @@ -64,6 +73,17 @@ static void db_open(){ [remote_devices setObject:deviceEntry forKey:key]; } + dict = (NSDictionary*) CFPreferencesCopyAppValue(CFSTR(RFCOMM_SERVICES_KEY), CFSTR(BTdaemonID)); + rfcomm_services = [[NSMutableDictionary alloc] initWithCapacity:([dict count]+5)]; + + // copy entries + for (id key in dict) { + NSDictionary *value = [dict objectForKey:key]; + NSMutableDictionary *serviceEntry = [NSMutableDictionary dictionaryWithCapacity:[value count]]; + [serviceEntry addEntriesFromDictionary:value]; + [rfcomm_services setObject:serviceEntry forKey:key]; + } + log_dbg("read prefs for %u devices\n", [dict count]); [pool release]; @@ -76,6 +96,7 @@ static void db_synchronize(){ // Core Foundation CFPreferencesSetValue(CFSTR(DEVICES_KEY), (CFPropertyListRef) remote_devices, CFSTR(BTdaemonID), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); + CFPreferencesSetValue(CFSTR(RFCOMM_SERVICES_KEY), (CFPropertyListRef) rfcomm_services, CFSTR(BTdaemonID), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); CFPreferencesSynchronize(CFSTR(BTdaemonID), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); // NSUserDefaults didn't work @@ -174,6 +195,76 @@ static int get_name(bd_addr_t *bd_addr, device_name_t *device_name) { return (remoteName != nil); } +#pragma mark PERSISTENT RFCOMM CHANNEL ALLOCATION + +static int firstFreeChannelNr(){ + BOOL channelUsed[MAX_RFCOMM_CHANNEL_NR+1]; + int i; + for (i=0; i<=MAX_RFCOMM_CHANNEL_NR ; i++) channelUsed[i] = NO; + channelUsed[0] = YES; + channelUsed[1] = YES; // preserve channel #1 for testing + for (NSDictionary * serviceEntry in [rfcomm_services allValues]){ + int channel = [(NSNumber *) [serviceEntry objectForKey:PREFS_CHANNEL] intValue]; + channelUsed[channel] = YES; + } + for (i=0;i<=MAX_RFCOMM_CHANNEL_NR;i++) { + if (channelUsed[i] == NO) return i; + } + return -1; +} + +static void deleteLeastUsed(){ + NSString * leastUsedName = nil; + NSDate * leastUsedDate = nil; + for (NSString * serviceName in [rfcomm_services allKeys]){ + NSDictionary *service = [rfcomm_services objectForKey:serviceName]; + NSDate *serviceDate = [service objectForKey:PREFS_LAST_USED]; + if (leastUsedName == nil || [leastUsedDate compare:serviceDate] == NSOrderedDescending) { + leastUsedName = serviceName; + leastUsedDate = serviceDate; + continue; + } + } + if (leastUsedName){ + // NSLog(@"removing %@", leastUsedName); + [rfcomm_services removeObjectForKey:leastUsedName]; + } +} + +static void addService(NSString * serviceName, int channel){ + NSMutableDictionary * serviceEntry = [NSMutableDictionary dictionaryWithCapacity:2]; + [serviceEntry setObject:[NSNumber numberWithInt:channel] forKey:PREFS_CHANNEL]; + [serviceEntry setObject:[NSDate date] forKey:PREFS_LAST_USED]; + [rfcomm_services setObject:serviceEntry forKey:serviceName]; +} + +static uint8_t persistent_rfcomm_channel(char *serviceName){ + // find existing entry + NSString *serviceString = [NSString stringWithUTF8String:serviceName]; + NSMutableDictionary *serviceEntry = [rfcomm_services objectForKey:serviceString]; + if (serviceEntry){ + // update timestamp + [serviceEntry setObject:[NSDate date] forKey:PREFS_LAST_USED]; + + db_synchronize(); + + return [(NSNumber *) [serviceEntry objectForKey:PREFS_CHANNEL] intValue]; + } + // free channel exist? + int channel = firstFreeChannelNr(); + if (channel < 0){ + // free channel + deleteLeastUsed(); + channel = firstFreeChannelNr(); + } + addService(serviceString, channel); + + db_synchronize(); + + return channel; +} + + remote_device_db_t remote_device_db_iphone = { db_open, db_close, @@ -182,6 +273,7 @@ remote_device_db_t remote_device_db_iphone = { delete_link_key, get_name, put_name, - delete_name + delete_name, + persistent_rfcomm_channel }; diff --git a/src/rfcomm.c b/src/rfcomm.c index 03b7576ab..5f81cf614 100644 --- a/src/rfcomm.c +++ b/src/rfcomm.c @@ -119,7 +119,7 @@ typedef struct { linked_item_t item; // server channel - uint16_t server_channel; + uint8_t server_channel; // incoming max frame size uint16_t max_frame_size; @@ -195,7 +195,6 @@ typedef struct { // global rfcomm data static uint16_t rfcomm_client_cid_generator; // used for client channel IDs -static uint8_t rfcomm_server_cid_generator; // used for service registration // linked lists for all static linked_list_t rfcomm_multiplexers = NULL; @@ -274,7 +273,7 @@ static void rfcomm_dump_channels(){ } static void rfcomm_channel_initialize(rfcomm_channel_t *channel, rfcomm_multiplexer_t *multiplexer, - rfcomm_service_t *service, uint16_t server_channel){ + rfcomm_service_t *service, uint8_t server_channel){ // don't use 0 as channel id if (rfcomm_client_cid_generator == 0) ++rfcomm_client_cid_generator; @@ -301,7 +300,7 @@ static void rfcomm_channel_initialize(rfcomm_channel_t *channel, rfcomm_multiple // service == NULL -> outgoing channel static rfcomm_channel_t * rfcomm_channel_create(rfcomm_multiplexer_t * multiplexer, - rfcomm_service_t * service, uint16_t server_channel){ + rfcomm_service_t * service, uint8_t server_channel){ log_dbg("rfcomm_channel_create for service %p, channel %u --- begin\n", service, server_channel); rfcomm_dump_channels(); @@ -341,7 +340,7 @@ static rfcomm_channel_t * rfcomm_channel_for_multiplexer_and_dlci(rfcomm_multipl return NULL; } -static rfcomm_service_t * rfcomm_service_for_channel(uint16_t server_channel){ +static rfcomm_service_t * rfcomm_service_for_channel(uint8_t server_channel){ linked_item_t *it; for (it = (linked_item_t *) rfcomm_services; it ; it = it->next){ rfcomm_service_t * service = ((rfcomm_service_t *) it); @@ -507,7 +506,7 @@ static void rfcomm_emit_channel_opened(rfcomm_channel_t *channel, uint8_t status event[pos++] = sizeof(event) - 2; event[pos++] = status; bt_flip_addr(&event[pos], channel->multiplexer->remote_addr); pos += 6; - // bt_store_16(event, pos, channel->multiplexer->con_handle); pos += 2; + bt_store_16(event, pos, channel->multiplexer->con_handle); pos += 2; event[pos++] = channel->dlci >> 1; bt_store_16(event, pos, channel->rfcomm_cid); pos += 2; // channel ID bt_store_16(event, pos, channel->max_frame_size); pos += 2; // max frame size @@ -537,13 +536,12 @@ static void rfcomm_emit_credits(rfcomm_channel_t * channel, uint8_t credits) { (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); } -static void rfcomm_emit_service_registered(void *connection, uint8_t status, uint16_t registration_id, uint16_t rfcomm_channel_id){ - uint8_t event[6]; +static void rfcomm_emit_service_registered(void *connection, uint8_t status, uint8_t channel){ + uint8_t event[4]; event[0] = RFCOMM_EVENT_SERVICE_REGISTERED; event[1] = sizeof(event) - 2; event[2] = status; - bt_store_16(event, 3, registration_id); - event[5] = rfcomm_channel_id; + event[3] = channel; hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); (*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); } @@ -1217,7 +1215,6 @@ void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe void rfcomm_init(){ rfcomm_client_cid_generator = 0; - rfcomm_server_cid_generator = 0; rfcomm_multiplexers = NULL; rfcomm_services = NULL; rfcomm_channels = NULL; @@ -1320,12 +1317,19 @@ void rfcomm_disconnect_internal(uint16_t rfcomm_cid){ } } -void rfcomm_register_service_internal(void * connection, uint16_t registration_id, uint16_t max_frame_size){ +void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size){ + + // check if already registered + rfcomm_service_t * service = rfcomm_service_for_channel(channel); + if (service){ + rfcomm_emit_service_registered(service->connection, RFCOMM_CHANNEL_ALREADY_REGISTERED, channel); + return; + } // alloc structure - rfcomm_service_t * service = malloc(sizeof(rfcomm_service_t)); + service = malloc(sizeof(rfcomm_service_t)); if (!service) { - rfcomm_emit_service_registered(service->connection, BTSTACK_MEMORY_ALLOC_FAILED, registration_id, 0); + rfcomm_emit_service_registered(service->connection, BTSTACK_MEMORY_ALLOC_FAILED, channel); return; } @@ -1336,14 +1340,14 @@ void rfcomm_register_service_internal(void * connection, uint16_t registration_i // fill in service->connection = connection; - service->server_channel = ++rfcomm_server_cid_generator; + service->server_channel = channel; service->max_frame_size = max_frame_size; // add to services list linked_list_add(&rfcomm_services, (linked_item_t *) service); // done - rfcomm_emit_service_registered(service->connection, 0, registration_id, service->server_channel); + rfcomm_emit_service_registered(service->connection, 0, channel); } void rfcomm_unregister_service_internal(uint8_t service_channel){ diff --git a/src/rfcomm.h b/src/rfcomm.h index e79947401..c5305e0da 100644 --- a/src/rfcomm.h +++ b/src/rfcomm.h @@ -48,7 +48,7 @@ void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t p // BTstack Internal RFCOMM API void rfcomm_create_channel_internal(void * connectio, bd_addr_t *addr, uint8_t channel); void rfcomm_disconnect_internal(uint16_t rfcomm_cid); -void rfcomm_register_service_internal(void * connection, uint16_t registration_id, uint16_t max_frame_size); +void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size); void rfcomm_unregister_service_internal(uint8_t service_channel); void rfcomm_accept_connection_internal(uint16_t rfcomm_cid); void rfcomm_decline_connection_internal(uint16_t rfcomm_cid);