(BTStack) Commit btstack deps to repo

This commit is contained in:
twinaphex 2013-03-18 23:32:25 +01:00
parent 5790dbbc61
commit b2096c6bb0
50 changed files with 15679 additions and 26 deletions

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* bt_control.h
*
* BT Control API -- allows BT Daemon to initialize and control differnt hardware
*
* Created by Matthias Ringwald on 5/19/09.
*
*/
#pragma once
#include <stdint.h>
typedef enum {
POWER_WILL_SLEEP = 1,
POWER_WILL_WAKE_UP
} POWER_NOTIFICATION_t;
typedef struct {
int (*on) (void *config); // <-- turn BT module on and configure
int (*off) (void *config); // <-- turn BT module off
int (*sleep)(void *config); // <-- put BT module to sleep - only to be called after ON
int (*wake) (void *config); // <-- wake BT module from sleep - only to be called after SLEEP
int (*valid)(void *config); // <-- test if hardware can be supported
const char * (*name) (void *config); // <-- return hardware name
/** support for UART baud rate changes - cmd has to be stored in hci_cmd_buffer
* @return have command
*/
int (*baudrate_cmd)(void * config, uint32_t baudrate, uint8_t *hci_cmd_buffer);
/** support custom init sequences after RESET command - cmd has to be stored in hci_cmd_buffer
* @return have command
*/
int (*next_cmd)(void *config, uint8_t * hci_cmd_buffer);
void (*register_for_power_notifications)(void (*cb)(POWER_NOTIFICATION_t event));
void (*hw_error)(void);
} bt_control_t;

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* bt_control_iphone.h
*
* BT Control API implementation for the iPhone and the iPod Touch
*
* Created by Matthias Ringwald on 5/19/09.
*/
#include "bt_control.h"
extern bt_control_t bt_control_iphone;
int bt_control_iphone_power_management_enabled(void);
int bt_control_iphone_power_management_supported(void);
int iphone_system_bt_enabled(void);
void iphone_system_bt_set_enabled(int enabled);
int iphone_system_has_csr(void);

View File

@ -0,0 +1,731 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* bt_control_iphone.c
*
* control Bluetooth module using BlueTool
*
* Created by Matthias Ringwald on 5/19/09.
* PowerManagement implementation by Jens David, DG1KJD on 20110801.
*
* Bluetooth Toggle by BigBoss
*/
#include "config.h"
#include "bt_control_iphone.h"
#include "hci_transport.h"
#include "hci.h"
#include "debug.h"
#include <errno.h>
#include <fcntl.h> // open
#include <stdlib.h> // system, random, srandom
#include <stdio.h> // sscanf, printf
#include <string.h> // strcpy, strcat, strncmp
#include <sys/utsname.h> // uname
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "../SpringBoardAccess/SpringBoardAccess.h"
// minimal IOKit
#include <Availability.h>
#include <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>
// compile issue fix
#undef NSEC_PER_USEC
#undef USEC_PER_SEC
#undef NSEC_PER_SEC
// end of fix
#include <mach/mach.h>
#define IOKIT
#include <device/device_types.h>
// costants
#define sys_iokit err_system(0x38)
#define sub_iokit_common err_sub(0)
#define iokit_common_msg(message) (UInt32)(sys_iokit|sub_iokit_common|message)
#define kIOMessageCanDevicePowerOff iokit_common_msg(0x200)
#define kIOMessageDeviceWillPowerOff iokit_common_msg(0x210)
#define kIOMessageDeviceWillNotPowerOff iokit_common_msg(0x220)
#define kIOMessageDeviceHasPoweredOn iokit_common_msg(0x230)
#define kIOMessageCanSystemPowerOff iokit_common_msg(0x240)
#define kIOMessageSystemWillPowerOff iokit_common_msg(0x250)
#define kIOMessageSystemWillNotPowerOff iokit_common_msg(0x260)
#define kIOMessageCanSystemSleep iokit_common_msg(0x270)
#define kIOMessageSystemWillSleep iokit_common_msg(0x280)
#define kIOMessageSystemWillNotSleep iokit_common_msg(0x290)
#define kIOMessageSystemHasPoweredOn iokit_common_msg(0x300)
#define kIOMessageSystemWillRestart iokit_common_msg(0x310)
#define kIOMessageSystemWillPowerOn iokit_common_msg(0x320)
// types
typedef io_object_t io_connect_t;
typedef io_object_t io_service_t;
typedef kern_return_t IOReturn;
typedef struct IONotificationPort * IONotificationPortRef;
// prototypes
kern_return_t IOMasterPort( mach_port_t bootstrapPort, mach_port_t * masterPort );
CFMutableDictionaryRef IOServiceNameMatching(const char * name );
CFTypeRef IORegistryEntrySearchCFProperty(mach_port_t entry, const io_name_t plane,
CFStringRef key, CFAllocatorRef allocator, UInt32 options );
mach_port_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching );
kern_return_t IOObjectRelease(mach_port_t object);
typedef void (*IOServiceInterestCallback)(void * refcon, io_service_t service, uint32_t messageType, void * messageArgument);
io_connect_t IORegisterForSystemPower (void * refcon, IONotificationPortRef * thePortRef,
IOServiceInterestCallback callback, io_object_t * notifier );
IOReturn IODeregisterForSystemPower (io_object_t *notifier);
CFRunLoopSourceRef IONotificationPortGetRunLoopSource(IONotificationPortRef notify );
IOReturn IOAllowPowerChange ( io_connect_t kernelPort, long notificationID );
IOReturn IOCancelPowerChange ( io_connect_t kernelPort, long notificationID );
// local globals
static io_connect_t root_port = 0; // a reference to the Root Power Domain IOService
static int power_notification_pipe_fds[2];
static data_source_t power_notification_ds;
static void (*power_notification_callback)(POWER_NOTIFICATION_t event) = NULL;
#define BUFF_LEN 80
static char buffer[BUFF_LEN+1];
// local mac address and default transport speed from IORegistry
static bd_addr_t local_mac_address;
static uint32_t transport_speed;
static int power_management_active = 0;
static char *os3xBlueTool = "BlueTool";
static char *os4xBlueTool = "/usr/local/bin/BlueToolH4";
/**
* check OS version for >= 4.0
*/
static int iphone_os_at_least_40(){
return kCFCoreFoundationVersionNumber >= 550.32;
}
/**
* get machine name
*/
static struct utsname unmae_info;
static char *get_machine_name(void){
uname(&unmae_info);
return unmae_info.machine;
}
/**
* on iPhone/iPod touch
*/
static int iphone_valid(void *config){
char * machine = get_machine_name();
if (!strncmp("iPod1", machine, strlen("iPod1"))) return 0; // 1st gen touch no BT
return 1;
}
static const char * iphone_name(void *config){
return get_machine_name();
}
// Get BD_ADDR from IORegistry
static void ioregistry_get_info() {
mach_port_t mp;
IOMasterPort(MACH_PORT_NULL,&mp);
CFMutableDictionaryRef bt_matching = IOServiceNameMatching("bluetooth");
mach_port_t bt_service = IOServiceGetMatchingService(mp, bt_matching);
// local-mac-address
CFTypeRef local_mac_address_ref = IORegistryEntrySearchCFProperty(bt_service,"IODeviceTree",CFSTR("local-mac-address"), kCFAllocatorDefault, 1);
CFDataGetBytes(local_mac_address_ref,CFRangeMake(0,CFDataGetLength(local_mac_address_ref)),local_mac_address); // buffer needs to be unsigned char
// transport-speed
CFTypeRef transport_speed_ref = IORegistryEntrySearchCFProperty(bt_service,"IODeviceTree",CFSTR("transport-speed"), kCFAllocatorDefault, 1);
int transport_speed_len = CFDataGetLength(transport_speed_ref);
CFDataGetBytes(transport_speed_ref,CFRangeMake(0,transport_speed_len), (uint8_t*) &transport_speed); // buffer needs to be unsigned char
IOObjectRelease(bt_service);
// dump info
log_info("local-mac-address: %s\n", bd_addr_to_str(local_mac_address));
log_info("transport-speed: %u\n", transport_speed);
}
static int iphone_has_csr(){
// construct script path from device name
char *machine = get_machine_name();
if (strncmp(machine, "iPhone1,", strlen("iPhone1,")) == 0) {
return 1;
}
return 0;
}
static void iphone_write_string(int fd, char *string){
int len = strlen(string);
write(fd, string, len);
}
static void iphone_csr_set_pskey(int fd, int key, int value){
int len = sprintf(buffer, "csr -p 0x%04x=0x%04x\n", key, value);
write(fd, buffer, len);
}
static void iphone_csr_set_bd_addr(int fd){
int len = sprintf(buffer,"\ncsr -p 0x0001=0x00%.2x,0x%.2x%.2x,0x00%.2x,0x%.2x%.2x\n",
local_mac_address[3], local_mac_address[4], local_mac_address[5], local_mac_address[2], local_mac_address[0], local_mac_address[1]);
write(fd, buffer, len);
}
static void iphone_csr_set_baud(int fd, int baud){
// calculate baud rate (assume rate is multiply of 100)
uint32_t baud_key = (4096 * (baud/100) + 4999) / 10000;
iphone_csr_set_pskey(fd, 0x01be, baud_key);
}
static void iphone_bcm_set_bd_addr(int fd){
int len = sprintf(buffer, "bcm -a %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_address[0], local_mac_address[1],
local_mac_address[2], local_mac_address[3], local_mac_address[4], local_mac_address[5]);
write(fd, buffer, len);
}
static void iphone_bcm_set_baud(int fd, int baud){
int len = sprintf(buffer, "bcm -b %u\n", baud);
write(fd, buffer, len);
}
// OS 3.x
static int iphone_write_initscript (int output, int baudrate){
// construct script path from device name
strcpy(buffer, "/etc/bluetool/");
char *machine = get_machine_name();
strcat(buffer, machine);
if (iphone_has_csr()){
strcat(buffer, ".init.script");
} else {
strcat(buffer, ".boot.script");
}
// open script
int input = open(buffer, O_RDONLY);
int pos = 0;
int mirror = 0;
int store = 1;
while (1){
int chars = read(input, &buffer[pos], 1);
// end-of-line
if (chars == 0 || buffer[pos]=='\n' || buffer[pos]== '\r'){
if (store) {
// stored characters
write(output, buffer, pos+chars);
}
if (mirror) {
write(output, "\n", 1);
}
pos = 0;
mirror = 0;
store = 1;
if (chars) {
continue;
} else {
break;
}
}
// mirror
if (mirror){
write(output, &buffer[pos], 1);
}
// store
if (store) {
pos++;
}
// iPhone - set BD_ADDR after "csr -i"
if (store == 1 && pos == 6 && strncmp(buffer, "csr -i", 6) == 0) {
store = 0;
write(output, buffer, pos); // "csr -i"
iphone_csr_set_bd_addr(output);
}
// iPod2,1
// check for "bcm -X" cmds
if (store == 1 && pos == 6){
if (strncmp(buffer, "bcm -", 5) == 0) {
store = 0;
switch (buffer[5]){
case 'a': // BT Address
iphone_bcm_set_bd_addr(output);
mirror = 0;
break;
case 'b': // baud rate command OS 2.x
case 'B': // baud rate command OS 3.x
iphone_bcm_set_baud(output, baudrate);
mirror = 0;
break;
case 's': // sleep mode - replace with "wake" command?
iphone_write_string(output, "wake on\n");
mirror = 0;
break;
default: // other "bcm -X" command
write(output, buffer, pos);
mirror = 1;
}
}
}
// iPhone1,1 & iPhone 2,1: OS 3.x
// check for "csr -B" and "csr -T"
if (store == 1 && pos == 6){
if (strncmp(buffer, "csr -", 5) == 0) {
switch(buffer[5]){
case 'T': // Transport Mode
store = 0;
break;
case 'B': // Baud rate
iphone_csr_set_baud(output, baudrate);
store = 0;
break;
default: // wait for full command
break;
}
}
}
// iPhone1,1 & iPhone 2,1: OS 2.x
// check for "csr -p 0x1234=0x5678" (20)
if (store == 1 && pos == 20) {
int pskey, value;
store = 0;
if (sscanf(buffer, "csr -p 0x%x=0x%x", &pskey, &value) == 2){
switch (pskey) {
case 0x01f9: // UART MODE
case 0x01c1: // Configure H5 mode
mirror = 0;
break;
case 0x01be: // PSKEY_UART_BAUD_RATE
iphone_csr_set_baud(output, baudrate);
mirror = 0;
break;
default:
// anything else: dump buffer and start forwarding
write(output, buffer, pos);
mirror = 1;
break;
}
} else {
write(output, buffer, pos);
mirror = 1;
}
}
}
// close input
close(input);
#ifdef USE_POWERMANAGEMENT
if (iphone_has_csr()) {
/* CSR BT module: deactivated since it didn't work on iPhone 3G, 3.1.3
the first few packets didn't get received when iPhone is sleeping.
That's quite possible since Apple uses H5, but BTstack uses H4
H5's packet retransmission should take care of that */
// iphone_write_string(output, "msleep 50\n");
// iphone_write_string(output, "csr -p 0x01ca=0x0031\n");
// iphone_write_string(output, "msleep 50\n");
// iphone_write_string(output, "csr -p 0x01c7=0x0001,0x01f4,0x0005,0x0020\n");
} else {
/* BCM BT module, deactivated since untested for now */
// iphone_write_string(output, "bcm -s 0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x01\n");
// iphone_write_string(output, "msleep 50\n");
}
#endif
return 0;
}
// OS 4.x and higher
static void iphone_write_configscript(int fd, int baudrate){
iphone_write_string(fd, "device -D -S\n");
if (iphone_has_csr()) {
iphone_csr_set_bd_addr(fd);
if (baudrate) {
iphone_csr_set_baud(fd, baudrate);
}
iphone_write_string(fd, "csr -r\n");
#ifdef USE_POWERMANAGEMENT
/* CSR BT module: deactivated since untested, but it most likely won't work
see comments in 3.x init sequence above */
// iphone_write_string(fd, "msleep 50\n");
// iphone_write_string(fd, "csr -p 0x01ca=0x0031\n");
// iphone_write_string(fd, "msleep 50\n");
// iphone_write_string(fd, "csr -p 0x01c7=0x0001,0x01f4,0x0005,0x0020\n");
#endif
} else {
iphone_bcm_set_bd_addr(fd);
if (baudrate) {
iphone_bcm_set_baud(fd, baudrate);
iphone_write_string(fd, "msleep 200\n");
}
#ifdef USE_POWERMANAGEMENT
// power management only active on 4.x with BCM (iPhone 3GS and higher, all iPads, iPod touch 3G and higher)
iphone_write_string(fd, "bcm -s 0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x01\n");
iphone_write_string(fd, "msleep 50\n");
#endif
}
iphone_write_string(fd, "quit\n");
}
static int iphone_on (void *transport_config){
// hci_uart_config->baudrate_init == 0, if using native speed
log_info("iphone_on: entered\n");
int err = 0;
hci_uart_config_t * hci_uart_config = (hci_uart_config_t*) transport_config;
// get local-mac-addr and transport-speed from IORegistry
ioregistry_get_info();
#ifdef USE_RANDOM_BD_ADDR
// While developing an app that emulates a Bluetooth HID keyboard, we've learnt
// that in (some versions/driver combinations of) Windows XP, information about
// a device with incorrect SDP descriptions are stored forever.
//
// To continue development, this option was added that picks a random BD_ADDR
// on start to trick windows in giving us a fresh start on each try.
//
// Use with caution!
srandom(time(NULL));
bt_store_32(local_mac_address, 0, random());
bt_store_16(local_mac_address, 4, random());
#endif
if (iphone_system_bt_enabled()){
perror("iphone_on: System Bluetooth enabled!");
return 1;
}
// unload BTServer
log_info("iphone_on: unload BTServer\n");
err = system ("launchctl unload /System/Library/LaunchDaemons/com.apple.BTServer.plist");
// check for os version >= 4.0
int os4x = iphone_os_at_least_40();
// OS 4.0
char * bluetool = os3xBlueTool;
if (os4x) {
bluetool = os4xBlueTool;
}
// quick test if Bluetooth UART can be opened - and try to start cleanly
int fd = open(hci_uart_config->device_name, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd > 0) {
// everything's fine
close(fd);
} else {
// no way!
log_error( "bt_control.c:iphone_on(): Failed to open '%s', trying killall %s\n", hci_uart_config->device_name, bluetool);
system("killall -9 BlueToolH4");
system("killall -9 BlueTool");
sleep(3);
// try again
fd = open(hci_uart_config->device_name, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd > 0){
close(fd);
} else {
log_error( "bt_control.c:iphone_on(): Failed to open '%s' again, trying killall BTServer and killall %s\n", hci_uart_config->device_name, bluetool);
system("killall -9 BTServer");
system("killall -9 BlueToolH4");
system("killall -9 BlueTool");
sleep(3);
}
}
// basic config on 4.0+
if (os4x) {
sprintf(buffer, "%s -F boot", bluetool);
system(buffer);
sprintf(buffer, "%s -F init", bluetool);
system(buffer);
}
// advanced config - set bd addr, use custom baud rate, enable deep sleep
FILE * outputFile = popen(bluetool, "r+");
setvbuf(outputFile, NULL, _IONBF, 0);
int output = fileno(outputFile);
if (os4x) {
// 4.x - send custom config
iphone_write_configscript(output, hci_uart_config->baudrate_init);
} else {
// 3.x - modify original script on the fly
iphone_write_initscript(output, hci_uart_config->baudrate_init);
}
// log output
fflush(outputFile);
int singlechar = 0;
char linebuf[80];
int pos = 0;
while (singlechar != EOF) {
singlechar = fgetc(outputFile);
linebuf[pos] = singlechar;
if (singlechar != EOF && singlechar != '\n' && singlechar != '\r' && pos < 78) {
pos++;
continue;
};
linebuf[pos] = 0;
if (pos > 0) {
log_info("%s", linebuf);
}
pos = 0;
};
err = pclose(outputFile);
#ifdef USE_POWERMANAGEMENT
power_management_active = bt_control_iphone_power_management_supported();
// if baud == 0, we're using system default: set in transport config
if (hci_uart_config->baudrate_init == 0) {
hci_uart_config->baudrate_init = transport_speed;
}
#endif
// if we sleep for about 3 seconds, we miss a strage packet... but we don't care
// sleep(3);
return err;
}
static int iphone_off (void *config){
/*
char *machine = get_machine_name();
if (strncmp(machine, "iPad", strlen("iPad")) == 0) {
// put iPad Bluetooth into deep sleep
system ("echo \"wake off\n quit\" | BlueTool");
} else {
// power off for iPhone and iPod
system ("echo \"power off\n quit\" | BlueTool");
}
*/
// power off (all models)
log_info("iphone_off: turn off using BlueTool\n");
system ("echo \"power off\nquit\" | BlueTool");
// kill Apple BTServer as it gets confused and fails to start anyway
// system("killall BTServer");
// reload BTServer
log_info("iphone_off: reload BTServer\n");
system ("launchctl load /System/Library/LaunchDaemons/com.apple.BTServer.plist");
log_info("iphone_off: done\n");
return 0;
}
static int iphone_sleep(void *config){
// will sleep by itself
if (power_management_active) return 0;
// put Bluetooth into deep sleep
system ("echo \"wake off\nquit\" | BlueTool");
return 0;
}
static int iphone_wake(void *config){
// will wake by itself
if (power_management_active) return 0;
// wake up Bluetooth module
system ("echo \"wake on\nquit\" | BlueTool");
return 0;
}
static void MySleepCallBack( void * refCon, io_service_t service, natural_t messageType, void * messageArgument ) {
char data;
log_info( "messageType %08lx, arg %08lx\n", (long unsigned int)messageType, (long unsigned int)messageArgument);
switch ( messageType ) {
case kIOMessageCanSystemSleep:
/* Idle sleep is about to kick in. This message will not be sent for forced sleep.
Applications have a chance to prevent sleep by calling IOCancelPowerChange.
Most applications should not prevent idle sleep.
Power Management waits up to 30 seconds for you to either allow or deny idle sleep.
If you don't acknowledge this power change by calling either IOAllowPowerChange
or IOCancelPowerChange, the system will wait 30 seconds then go to sleep.
*/
// Uncomment to cancel idle sleep
// IOCancelPowerChange( root_port, (long)messageArgument );
// we will allow idle sleep
IOAllowPowerChange( root_port, (long)messageArgument );
break;
case kIOMessageSystemWillSleep:
/* The system WILL go to sleep. If you do not call IOAllowPowerChange or
IOCancelPowerChange to acknowledge this message, sleep will be
delayed by 30 seconds.
NOTE: If you call IOCancelPowerChange to deny sleep it returns kIOReturnSuccess,
however the system WILL still go to sleep.
*/
// notify main thread
data = POWER_WILL_SLEEP;
write(power_notification_pipe_fds[1], &data, 1);
// don't allow power change, even when power management is active
// BTstack needs to disable discovery mode during sleep to save power
// if (!power_management_active) break;
// IOAllowPowerChange( root_port, (long)messageArgument );
break;
case kIOMessageSystemWillPowerOn:
data = POWER_WILL_WAKE_UP;
write(power_notification_pipe_fds[1], &data, 1);
break;
case kIOMessageSystemHasPoweredOn:
//System has finished waking up...
break;
default:
break;
}
}
static int power_notification_process(struct data_source *ds) {
if (!power_notification_callback) return -1;
// get token
char token;
int bytes_read = read(power_notification_pipe_fds[0], &token, 1);
if (bytes_read != 1) return -1;
log_info("power_notification_process: %u\n", token);
power_notification_callback( (POWER_NOTIFICATION_t) token );
return 0;
}
/**
* assumption: called only once, cb != null
*/
void iphone_register_for_power_notifications(void (*cb)(POWER_NOTIFICATION_t event)){
// handle IOKIT power notifications - http://developer.apple.com/library/mac/#qa/qa2004/qa1340.html
IONotificationPortRef notifyPortRef = NULL; // notification port allocated by IORegisterForSystemPower
io_object_t notifierObject = 0; // notifier object, used to deregister later
root_port = IORegisterForSystemPower(NULL, &notifyPortRef, MySleepCallBack, &notifierObject);
if (!root_port) {
log_info("IORegisterForSystemPower failed\n");
return;
}
// add the notification port to this thread's runloop
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes);
//
power_notification_callback = cb;
// create pipe to deliver power notification to main run loop
pipe(power_notification_pipe_fds);
// set up data source handler
power_notification_ds.fd = power_notification_pipe_fds[0];
power_notification_ds.process = power_notification_process;
run_loop_add_data_source(&power_notification_ds);
}
int bt_control_iphone_power_management_supported(void){
// only supported on Broadcom chipsets with iOS 4.0+
if ( iphone_has_csr()) return 0;
if (!iphone_os_at_least_40()) return 0;
return 1;
}
// direct access
int bt_control_iphone_power_management_enabled(void){
return power_management_active;
}
// single instance
bt_control_t bt_control_iphone = {
.on = iphone_on,
.off = iphone_off,
.sleep = iphone_sleep,
.wake = iphone_wake,
.valid = iphone_valid,
.name = iphone_name,
.register_for_power_notifications = iphone_register_for_power_notifications
};
int iphone_system_bt_enabled(void){
return SBA_getBluetoothEnabled();
}
void iphone_system_bt_set_enabled(int enabled)
{
SBA_setBluetoothEnabled(enabled);
sleep(2); // give change a chance
}
int iphone_system_has_csr(void){
return iphone_has_csr();
}

View File

@ -0,0 +1,135 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* btstack.c
*
* Created by Matthias Ringwald on 7/1/09.
*
* BTstack client API
*/
#include <btstack/btstack.h>
#include "l2cap.h"
#include "socket_connection.h"
#include <btstack/run_loop.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>
// static uint8_t hci_cmd_buffer[3+255]; // HCI Command Header + max payload
static uint8_t hci_cmd_buffer[HCI_ACL_BUFFER_SIZE]; // BTstack command packets are not size restricted
static connection_t *btstack_connection = NULL;
/** prototypes & dummy functions */
static void dummy_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){};
static int btstack_packet_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t size);
/** local globals :) */
static void (*client_packet_handler)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) = dummy_handler;
static const char * daemon_tcp_address = NULL;
static uint16_t daemon_tcp_port = BTSTACK_PORT;
// optional: if called before bt_open, TCP socket is used instead of local unix socket
// note: address is not copied and must be valid during bt_open
void bt_use_tcp(const char * address, uint16_t port){
daemon_tcp_address = address;
daemon_tcp_port = port;
}
// init BTstack library
int bt_open(){
socket_connection_register_packet_callback(btstack_packet_handler);
// BTdaemon
if (daemon_tcp_address) {
btstack_connection = socket_connection_open_tcp(daemon_tcp_address,daemon_tcp_port);
} else {
btstack_connection = socket_connection_open_unix();
}
if (!btstack_connection) return -1;
return 0;
}
// stop using BTstack library
int bt_close(){
return socket_connection_close_tcp(btstack_connection);
}
// send hci cmd packet
int bt_send_cmd(const hci_cmd_t *cmd, ...){
va_list argptr;
va_start(argptr, cmd);
uint16_t len = hci_create_cmd_internal(hci_cmd_buffer, cmd, argptr);
va_end(argptr);
socket_connection_send_packet(btstack_connection, HCI_COMMAND_DATA_PACKET, 0, hci_cmd_buffer, len);
return 0;
}
int btstack_packet_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t size){
// printf("BTstack client handler: packet type %u, data[0] %x\n", packet_type, data[0]);
(*client_packet_handler)(packet_type, channel, data, size);
return 0;
}
// register packet handler
btstack_packet_handler_t bt_register_packet_handler(btstack_packet_handler_t handler){
btstack_packet_handler_t old_handler = client_packet_handler;
client_packet_handler = handler;
return old_handler;
}
void bt_send_l2cap(uint16_t source_cid, uint8_t *data, uint16_t len){
// send
socket_connection_send_packet(btstack_connection, L2CAP_DATA_PACKET, source_cid, data, len);
}
void bt_send_rfcomm(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){
// send
socket_connection_send_packet(btstack_connection, RFCOMM_DATA_PACKET, rfcomm_cid, data, len);
}
void bt_send_acl(uint8_t * data, uint16_t len){
// send
socket_connection_send_packet(btstack_connection, HCI_ACL_DATA_PACKET, 0, data, len);
}

View File

@ -40,9 +40,9 @@
#pragma once
#include "hci_cmds.h"
#include "run_loop.h"
#include "utils.h"
#include <btstack/hci_cmds.h>
#include <btstack/run_loop.h>
#include <btstack/utils.h>
#include <stdint.h>

View File

@ -0,0 +1,336 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* btstsack_memory.h
*
* @brief BTstack memory management via configurable memory pools
*
* @note code semi-atuomatically generated by btstack_memory_generator.py
*
*/
#include "btstack_memory.h"
#include <btstack/memory_pool.h>
#include <stdlib.h>
#include "config.h"
#include "hci.h"
#include "l2cap.h"
#include "rfcomm.h"
// MARK: hci_connection_t
#ifdef MAX_NO_HCI_CONNECTIONS
#if MAX_NO_HCI_CONNECTIONS > 0
static hci_connection_t hci_connection_storage[MAX_NO_HCI_CONNECTIONS];
static memory_pool_t hci_connection_pool;
void * btstack_memory_hci_connection_get(void){
return memory_pool_get(&hci_connection_pool);
}
void btstack_memory_hci_connection_free(void *hci_connection){
memory_pool_free(&hci_connection_pool, hci_connection);
}
#else
void * btstack_memory_hci_connection_get(void){
return NULL;
}
void btstack_memory_hci_connection_free(void *hci_connection){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_hci_connection_get(void){
return malloc(sizeof(hci_connection_t));
}
void btstack_memory_hci_connection_free(void *hci_connection){
free(hci_connection);
}
#endif
// MARK: l2cap_service_t
#ifdef MAX_NO_L2CAP_SERVICES
#if MAX_NO_L2CAP_SERVICES > 0
static l2cap_service_t l2cap_service_storage[MAX_NO_L2CAP_SERVICES];
static memory_pool_t l2cap_service_pool;
void * btstack_memory_l2cap_service_get(void){
return memory_pool_get(&l2cap_service_pool);
}
void btstack_memory_l2cap_service_free(void *l2cap_service){
memory_pool_free(&l2cap_service_pool, l2cap_service);
}
#else
void * btstack_memory_l2cap_service_get(void){
return NULL;
}
void btstack_memory_l2cap_service_free(void *l2cap_service){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_l2cap_service_get(void){
return malloc(sizeof(l2cap_service_t));
}
void btstack_memory_l2cap_service_free(void *l2cap_service){
free(l2cap_service);
}
#endif
// MARK: l2cap_channel_t
#ifdef MAX_NO_L2CAP_CHANNELS
#if MAX_NO_L2CAP_CHANNELS > 0
static l2cap_channel_t l2cap_channel_storage[MAX_NO_L2CAP_CHANNELS];
static memory_pool_t l2cap_channel_pool;
void * btstack_memory_l2cap_channel_get(void){
return memory_pool_get(&l2cap_channel_pool);
}
void btstack_memory_l2cap_channel_free(void *l2cap_channel){
memory_pool_free(&l2cap_channel_pool, l2cap_channel);
}
#else
void * btstack_memory_l2cap_channel_get(void){
return NULL;
}
void btstack_memory_l2cap_channel_free(void *l2cap_channel){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_l2cap_channel_get(void){
return malloc(sizeof(l2cap_channel_t));
}
void btstack_memory_l2cap_channel_free(void *l2cap_channel){
free(l2cap_channel);
}
#endif
// MARK: rfcomm_multiplexer_t
#ifdef MAX_NO_RFCOMM_MULTIPLEXERS
#if MAX_NO_RFCOMM_MULTIPLEXERS > 0
static rfcomm_multiplexer_t rfcomm_multiplexer_storage[MAX_NO_RFCOMM_MULTIPLEXERS];
static memory_pool_t rfcomm_multiplexer_pool;
void * btstack_memory_rfcomm_multiplexer_get(void){
return memory_pool_get(&rfcomm_multiplexer_pool);
}
void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){
memory_pool_free(&rfcomm_multiplexer_pool, rfcomm_multiplexer);
}
#else
void * btstack_memory_rfcomm_multiplexer_get(void){
return NULL;
}
void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_rfcomm_multiplexer_get(void){
return malloc(sizeof(rfcomm_multiplexer_t));
}
void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){
free(rfcomm_multiplexer);
}
#endif
// MARK: rfcomm_service_t
#ifdef MAX_NO_RFCOMM_SERVICES
#if MAX_NO_RFCOMM_SERVICES > 0
static rfcomm_service_t rfcomm_service_storage[MAX_NO_RFCOMM_SERVICES];
static memory_pool_t rfcomm_service_pool;
void * btstack_memory_rfcomm_service_get(void){
return memory_pool_get(&rfcomm_service_pool);
}
void btstack_memory_rfcomm_service_free(void *rfcomm_service){
memory_pool_free(&rfcomm_service_pool, rfcomm_service);
}
#else
void * btstack_memory_rfcomm_service_get(void){
return NULL;
}
void btstack_memory_rfcomm_service_free(void *rfcomm_service){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_rfcomm_service_get(void){
return malloc(sizeof(rfcomm_service_t));
}
void btstack_memory_rfcomm_service_free(void *rfcomm_service){
free(rfcomm_service);
}
#endif
// MARK: rfcomm_channel_t
#ifdef MAX_NO_RFCOMM_CHANNELS
#if MAX_NO_RFCOMM_CHANNELS > 0
static rfcomm_channel_t rfcomm_channel_storage[MAX_NO_RFCOMM_CHANNELS];
static memory_pool_t rfcomm_channel_pool;
void * btstack_memory_rfcomm_channel_get(void){
return memory_pool_get(&rfcomm_channel_pool);
}
void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){
memory_pool_free(&rfcomm_channel_pool, rfcomm_channel);
}
#else
void * btstack_memory_rfcomm_channel_get(void){
return NULL;
}
void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_rfcomm_channel_get(void){
return malloc(sizeof(rfcomm_channel_t));
}
void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){
free(rfcomm_channel);
}
#endif
// MARK: db_mem_device_name_t
#ifdef MAX_NO_DB_MEM_DEVICE_NAMES
#if MAX_NO_DB_MEM_DEVICE_NAMES > 0
static db_mem_device_name_t db_mem_device_name_storage[MAX_NO_DB_MEM_DEVICE_NAMES];
static memory_pool_t db_mem_device_name_pool;
void * btstack_memory_db_mem_device_name_get(void){
return memory_pool_get(&db_mem_device_name_pool);
}
void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){
memory_pool_free(&db_mem_device_name_pool, db_mem_device_name);
}
#else
void * btstack_memory_db_mem_device_name_get(void){
return NULL;
}
void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_db_mem_device_name_get(void){
return malloc(sizeof(db_mem_device_name_t));
}
void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){
free(db_mem_device_name);
}
#endif
// MARK: db_mem_device_link_key_t
#ifdef MAX_NO_DB_MEM_DEVICE_LINK_KEYS
#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0
static db_mem_device_link_key_t db_mem_device_link_key_storage[MAX_NO_DB_MEM_DEVICE_LINK_KEYS];
static memory_pool_t db_mem_device_link_key_pool;
void * btstack_memory_db_mem_device_link_key_get(void){
return memory_pool_get(&db_mem_device_link_key_pool);
}
void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){
memory_pool_free(&db_mem_device_link_key_pool, db_mem_device_link_key);
}
#else
void * btstack_memory_db_mem_device_link_key_get(void){
return NULL;
}
void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_db_mem_device_link_key_get(void){
return malloc(sizeof(db_mem_device_link_key_t));
}
void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){
free(db_mem_device_link_key);
}
#endif
// MARK: db_mem_service_t
#ifdef MAX_NO_DB_MEM_SERVICES
#if MAX_NO_DB_MEM_SERVICES > 0
static db_mem_service_t db_mem_service_storage[MAX_NO_DB_MEM_SERVICES];
static memory_pool_t db_mem_service_pool;
void * btstack_memory_db_mem_service_get(void){
return memory_pool_get(&db_mem_service_pool);
}
void btstack_memory_db_mem_service_free(void *db_mem_service){
memory_pool_free(&db_mem_service_pool, db_mem_service);
}
#else
void * btstack_memory_db_mem_service_get(void){
return NULL;
}
void btstack_memory_db_mem_service_free(void *db_mem_service){
};
#endif
#elif defined(HAVE_MALLOC)
void * btstack_memory_db_mem_service_get(void){
return malloc(sizeof(db_mem_service_t));
}
void btstack_memory_db_mem_service_free(void *db_mem_service){
free(db_mem_service);
}
#endif
// init
void btstack_memory_init(void){
#if MAX_NO_HCI_CONNECTIONS > 0
memory_pool_create(&hci_connection_pool, hci_connection_storage, MAX_NO_HCI_CONNECTIONS, sizeof(hci_connection_t));
#endif
#if MAX_NO_L2CAP_SERVICES > 0
memory_pool_create(&l2cap_service_pool, l2cap_service_storage, MAX_NO_L2CAP_SERVICES, sizeof(l2cap_service_t));
#endif
#if MAX_NO_L2CAP_CHANNELS > 0
memory_pool_create(&l2cap_channel_pool, l2cap_channel_storage, MAX_NO_L2CAP_CHANNELS, sizeof(l2cap_channel_t));
#endif
#if MAX_NO_RFCOMM_MULTIPLEXERS > 0
memory_pool_create(&rfcomm_multiplexer_pool, rfcomm_multiplexer_storage, MAX_NO_RFCOMM_MULTIPLEXERS, sizeof(rfcomm_multiplexer_t));
#endif
#if MAX_NO_RFCOMM_SERVICES > 0
memory_pool_create(&rfcomm_service_pool, rfcomm_service_storage, MAX_NO_RFCOMM_SERVICES, sizeof(rfcomm_service_t));
#endif
#if MAX_NO_RFCOMM_CHANNELS > 0
memory_pool_create(&rfcomm_channel_pool, rfcomm_channel_storage, MAX_NO_RFCOMM_CHANNELS, sizeof(rfcomm_channel_t));
#endif
#if MAX_NO_DB_MEM_DEVICE_NAMES > 0
memory_pool_create(&db_mem_device_name_pool, db_mem_device_name_storage, MAX_NO_DB_MEM_DEVICE_NAMES, sizeof(db_mem_device_name_t));
#endif
#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0
memory_pool_create(&db_mem_device_link_key_pool, db_mem_device_link_key_storage, MAX_NO_DB_MEM_DEVICE_LINK_KEYS, sizeof(db_mem_device_link_key_t));
#endif
#if MAX_NO_DB_MEM_SERVICES > 0
memory_pool_create(&db_mem_service_pool, db_mem_service_storage, MAX_NO_DB_MEM_SERVICES, sizeof(db_mem_service_t));
#endif
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* btstsack_memory.h
*
* @brief BTstack memory management via configurable memory pools
*
*/
#pragma once
#if defined __cplusplus
extern "C" {
#endif
void btstack_memory_init(void);
void * btstack_memory_hci_connection_get(void);
void btstack_memory_hci_connection_free(void *hci_connection);
void * btstack_memory_l2cap_service_get(void);
void btstack_memory_l2cap_service_free(void *l2cap_service);
void * btstack_memory_l2cap_channel_get(void);
void btstack_memory_l2cap_channel_free(void *l2cap_channel);
void * btstack_memory_rfcomm_multiplexer_get(void);
void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer);
void * btstack_memory_rfcomm_service_get(void);
void btstack_memory_rfcomm_service_free(void *rfcomm_service);
void * btstack_memory_rfcomm_channel_get(void);
void btstack_memory_rfcomm_channel_free(void *rfcomm_channel);
void * btstack_memory_db_mem_device_name_get(void);
void btstack_memory_db_mem_device_name_free(void *db_mem_device_name);
void * btstack_memory_db_mem_device_link_key_get(void);
void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key);
void * btstack_memory_db_mem_service_get(void);
void btstack_memory_db_mem_service_free(void *db_mem_service);
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,800 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* daemon.c
*
* Created by Matthias Ringwald on 7/1/09.
*
* BTstack background daemon
*
*/
#include "config.h"
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <getopt.h>
#include <btstack/btstack.h>
#include <btstack/linked_list.h>
#include <btstack/run_loop.h>
#include "debug.h"
#include "hci.h"
#include "hci_dump.h"
#include "hci_transport.h"
#include "l2cap.h"
#include "rfcomm.h"
#include "sdp.h"
#include "socket_connection.h"
#ifdef USE_BLUETOOL
#include <CoreFoundation/CoreFoundation.h>
#include "bt_control_iphone.h"
#include <notify.h>
#endif
#ifdef USE_SPRINGBOARD
#include "platform_iphone.h"
#endif
#ifdef HAVE_TRANSPORT_USB
#include <libusb-1.0/libusb.h>
#endif
#define DAEMON_NO_ACTIVE_CLIENT_TIMEOUT 10000
typedef struct {
// linked list - assert: first field
linked_item_t item;
// connection
connection_t * connection;
// power mode
HCI_POWER_MODE power_mode;
// discoverable
uint8_t discoverable;
} client_state_t;
// MARK: prototypes
static void dummy_bluetooth_status_handler(BLUETOOTH_STATE state);
static client_state_t * client_for_connection(connection_t *connection);
static int clients_require_power_on(void);
static int clients_require_discoverable(void);
static void clients_clear_power_request(void);
static void start_power_off_timer(void);
static void stop_power_off_timer(void);
// MARK: globals
static hci_transport_t * transport;
static hci_uart_config_t config;
static timer_source_t timeout;
static uint8_t timeout_active = 0;
static int power_management_sleep = 0;
static linked_list_t clients = NULL; // list of connected clients
static void (*bluetooth_status_handler)(BLUETOOTH_STATE state) = dummy_bluetooth_status_handler;
static int global_enable = 0;
static remote_device_db_t const * remote_device_db = NULL;
static int rfcomm_channel_generator = 1;
static int loggingEnabled;
static void dummy_bluetooth_status_handler(BLUETOOTH_STATE state){
log_info("Bluetooth status: %u\n", state);
};
static void daemon_no_connections_timeout(struct timer *ts){
if (clients_require_power_on()) return; // false alarm :)
log_info("No active client connection for %u seconds -> POWER OFF\n", DAEMON_NO_ACTIVE_CLIENT_TIMEOUT/1000);
hci_power_control(HCI_POWER_OFF);
}
static int btstack_command_handler(connection_t *connection, uint8_t *packet, uint16_t size){
bd_addr_t addr;
uint16_t cid;
uint16_t psm;
uint16_t service_channel;
uint16_t mtu;
uint8_t reason;
uint8_t rfcomm_channel;
uint8_t rfcomm_credits;
uint32_t service_record_handle;
client_state_t *client;
// BTstack internal commands - 16 Bit OpCode, 8 Bit ParamLen, Params...
switch (READ_CMD_OCF(packet)){
case BTSTACK_GET_STATE:
log_info("BTSTACK_GET_STATE");
hci_emit_state();
break;
case BTSTACK_SET_POWER_MODE:
log_info("BTSTACK_SET_POWER_MODE %u", packet[3]);
// track client power requests
client = client_for_connection(connection);
if (!client) break;
client->power_mode = packet[3];
// handle merged state
if (!clients_require_power_on()){
start_power_off_timer();
} else if (!power_management_sleep) {
stop_power_off_timer();
hci_power_control(HCI_POWER_ON);
}
break;
case BTSTACK_GET_VERSION:
log_info("BTSTACK_GET_VERSION");
hci_emit_btstack_version();
break;
#ifdef USE_BLUETOOL
case BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED:
log_info("BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED %u", packet[3]);
iphone_system_bt_set_enabled(packet[3]);
hci_emit_system_bluetooth_enabled(iphone_system_bt_enabled());
break;
case BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED:
log_info("BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED");
hci_emit_system_bluetooth_enabled(iphone_system_bt_enabled());
break;
#else
case BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED:
case BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED:
hci_emit_system_bluetooth_enabled(0);
break;
#endif
case BTSTACK_SET_DISCOVERABLE:
log_info("BTSTACK_SET_DISCOVERABLE discoverable %u)", packet[3]);
// track client discoverable requests
client = client_for_connection(connection);
if (!client) break;
client->discoverable = packet[3];
// merge state
hci_discoverable_control(clients_require_discoverable());
break;
case BTSTACK_SET_BLUETOOTH_ENABLED:
log_info("BTSTACK_SET_BLUETOOTH_ENABLED: %u\n", packet[3]);
if (packet[3]) {
// global enable
global_enable = 1;
hci_power_control(HCI_POWER_ON);
} else {
global_enable = 0;
clients_clear_power_request();
hci_power_control(HCI_POWER_OFF);
}
break;
case L2CAP_CREATE_CHANNEL_MTU:
bt_flip_addr(addr, &packet[3]);
psm = READ_BT_16(packet, 9);
mtu = READ_BT_16(packet, 11);
l2cap_create_channel_internal( connection, NULL, addr, psm, mtu);
break;
case L2CAP_CREATE_CHANNEL:
bt_flip_addr(addr, &packet[3]);
psm = READ_BT_16(packet, 9);
l2cap_create_channel_internal( connection, NULL, addr, psm, 150); // until r865
break;
case L2CAP_DISCONNECT:
cid = READ_BT_16(packet, 3);
reason = packet[5];
l2cap_disconnect_internal(cid, reason);
break;
case L2CAP_REGISTER_SERVICE:
psm = READ_BT_16(packet, 3);
mtu = READ_BT_16(packet, 5);
l2cap_register_service_internal(connection, NULL, psm, mtu);
break;
case L2CAP_UNREGISTER_SERVICE:
psm = READ_BT_16(packet, 3);
l2cap_unregister_service_internal(connection, psm);
break;
case L2CAP_ACCEPT_CONNECTION:
cid = READ_BT_16(packet, 3);
l2cap_accept_connection_internal(cid);
break;
case L2CAP_DECLINE_CONNECTION:
cid = READ_BT_16(packet, 3);
reason = packet[7];
l2cap_decline_connection_internal(cid, reason);
break;
case RFCOMM_CREATE_CHANNEL:
bt_flip_addr(addr, &packet[3]);
rfcomm_channel = packet[9];
rfcomm_create_channel_internal( connection, &addr, rfcomm_channel );
break;
case RFCOMM_CREATE_CHANNEL_WITH_CREDITS:
bt_flip_addr(addr, &packet[3]);
rfcomm_channel = packet[9];
rfcomm_credits = packet[10];
rfcomm_create_channel_with_initial_credits_internal( connection, &addr, rfcomm_channel, rfcomm_credits );
break;
case RFCOMM_DISCONNECT:
cid = READ_BT_16(packet, 3);
reason = packet[5];
rfcomm_disconnect_internal(cid);
break;
case RFCOMM_REGISTER_SERVICE:
rfcomm_channel = packet[3];
mtu = READ_BT_16(packet, 4);
rfcomm_register_service_internal(connection, rfcomm_channel, mtu);
break;
case RFCOMM_REGISTER_SERVICE_WITH_CREDITS:
rfcomm_channel = packet[3];
mtu = READ_BT_16(packet, 4);
rfcomm_credits = packet[6];
rfcomm_register_service_with_initial_credits_internal(connection, rfcomm_channel, mtu, rfcomm_credits);
break;
case RFCOMM_UNREGISTER_SERVICE:
service_channel = READ_BT_16(packet, 3);
rfcomm_unregister_service_internal(service_channel);
break;
case RFCOMM_ACCEPT_CONNECTION:
cid = READ_BT_16(packet, 3);
rfcomm_accept_connection_internal(cid);
break;
case RFCOMM_DECLINE_CONNECTION:
cid = READ_BT_16(packet, 3);
reason = packet[7];
rfcomm_decline_connection_internal(cid);
break;
case RFCOMM_GRANT_CREDITS:
cid = READ_BT_16(packet, 3);
rfcomm_credits = packet[5];
rfcomm_grant_credits(cid, rfcomm_credits);
break;
case RFCOMM_PERSISTENT_CHANNEL: {
if (remote_device_db) {
// enforce \0
packet[3+248] = 0;
rfcomm_channel = remote_device_db->persistent_rfcomm_channel((char*)&packet[3]);
} else {
// NOTE: hack for non-iOS platforms
rfcomm_channel = rfcomm_channel_generator++;
}
log_info("RFCOMM_EVENT_PERSISTENT_CHANNEL %u", rfcomm_channel);
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:
log_info("SDP_REGISTER_SERVICE_RECORD size %u\n", size);
sdp_register_service_internal(connection, &packet[3]);
break;
case SDP_UNREGISTER_SERVICE_RECORD:
service_record_handle = READ_BT_32(packet, 3);
log_info("SDP_UNREGISTER_SERVICE_RECORD handle 0x%x ", service_record_handle);
sdp_unregister_service_internal(connection, service_record_handle);
break;
default:
log_error("Error: command %u not implemented\n:", READ_CMD_OCF(packet));
break;
}
// verbose log info on command before dumped command unknown to PacketLogger or Wireshark
hci_dump_packet( HCI_COMMAND_DATA_PACKET, 1, packet, size);
return 0;
}
static int daemon_client_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length){
int err = 0;
client_state_t * client;
switch (packet_type){
case HCI_COMMAND_DATA_PACKET:
if (READ_CMD_OGF(data) != OGF_BTSTACK) {
// HCI Command
hci_send_cmd_packet(data, length);
} else {
// BTstack command
btstack_command_handler(connection, data, length);
}
break;
case HCI_ACL_DATA_PACKET:
err = hci_send_acl_packet(data, length);
break;
case L2CAP_DATA_PACKET:
// process l2cap packet...
err = l2cap_send_internal(channel, data, length);
if (err == BTSTACK_ACL_BUFFERS_FULL) {
l2cap_block_new_credits(1);
}
break;
case RFCOMM_DATA_PACKET:
// process l2cap packet...
err = rfcomm_send_internal(channel, data, length);
break;
case DAEMON_EVENT_PACKET:
switch (data[0]) {
case DAEMON_EVENT_CONNECTION_OPENED:
log_info("DAEMON_EVENT_CONNECTION_OPENED %p\n",connection);
client = malloc(sizeof(client_state_t));
if (!client) break; // fail
client->connection = connection;
client->power_mode = HCI_POWER_OFF;
client->discoverable = 0;
linked_list_add(&clients, (linked_item_t *) client);
break;
case DAEMON_EVENT_CONNECTION_CLOSED:
log_info("DAEMON_EVENT_CONNECTION_CLOSED %p\n",connection);
sdp_unregister_services_for_connection(connection);
rfcomm_close_connection(connection);
l2cap_close_connection(connection);
client = client_for_connection(connection);
if (!client) break;
linked_list_remove(&clients, (linked_item_t *) client);
free(client);
// update discoverable mode
hci_discoverable_control(clients_require_discoverable());
// start power off, if last active client
if (!clients_require_power_on()){
start_power_off_timer();
}
break;
case DAEMON_NR_CONNECTIONS_CHANGED:
log_info("Nr Connections changed, new %u\n",data[1]);
break;
default:
break;
}
break;
}
if (err) {
log_info("Daemon Handler: err %d\n", err);
}
return err;
}
static void daemon_set_logging_enabled(int enabled){
if (enabled && !loggingEnabled){
// use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT
hci_dump_open("/tmp/hci_dump.pklg", HCI_DUMP_PACKETLOGGER);
}
if (!enabled && loggingEnabled){
hci_dump_close();
}
loggingEnabled = enabled;
}
// local cache used to manage UI status
static HCI_STATE hci_state = HCI_STATE_OFF;
static int num_connections = 0;
static void update_ui_status(void){
if (hci_state != HCI_STATE_WORKING) {
bluetooth_status_handler(BLUETOOTH_OFF);
} else {
if (num_connections) {
bluetooth_status_handler(BLUETOOTH_ACTIVE);
} else {
bluetooth_status_handler(BLUETOOTH_ON);
}
}
}
#ifdef USE_SPRINGBOARD
static void preferences_changed_callback(void){
int logging = platform_iphone_logging_enabled();
log_info("Logging enabled: %u\n", logging);
daemon_set_logging_enabled(logging);
}
#endif
static void deamon_status_event_handler(uint8_t *packet, uint16_t size){
uint8_t update_status = 0;
// handle state event
switch (packet[0]) {
case BTSTACK_EVENT_STATE:
hci_state = packet[2];
log_info("New state: %u\n", hci_state);
update_status = 1;
break;
case BTSTACK_EVENT_NR_CONNECTIONS_CHANGED:
num_connections = packet[2];
log_info("New nr connections: %u\n", num_connections);
update_status = 1;
break;
default:
break;
}
// choose full bluetooth state
if (update_status) {
update_ui_status();
}
}
static void daemon_retry_parked(void){
// socket_connection_retry_parked is not reentrant
static int retry_mutex = 0;
// lock mutex
if (retry_mutex) return;
retry_mutex = 1;
// ... try sending again
socket_connection_retry_parked();
if (!socket_connection_has_parked_connections()){
l2cap_block_new_credits(0);
}
// unlock mutex
retry_mutex = 0;
}
static void daemon_packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
switch (packet_type) {
case HCI_EVENT_PACKET:
deamon_status_event_handler(packet, size);
switch (packet[0]){
case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:
// ACL buffer freed...
daemon_retry_parked();
// no need to tell clients
return;
case RFCOMM_EVENT_CREDITS:
// RFCOMM CREDITS received...
daemon_retry_parked();
break;
default:
break;
}
case DAEMON_EVENT_PACKET:
switch (packet[0]){
case DAEMON_EVENT_NEW_RFCOMM_CREDITS:
daemon_retry_parked();
break;
default:
break;
}
default:
break;
}
if (connection) {
socket_connection_send_packet(connection, packet_type, channel, packet, size);
} else {
socket_connection_send_packet_all(packet_type, channel, packet, size);
}
}
static void power_notification_callback(POWER_NOTIFICATION_t notification){
switch (notification) {
case POWER_WILL_SLEEP:
// let's sleep
power_management_sleep = 1;
hci_power_control(HCI_POWER_SLEEP);
break;
case POWER_WILL_WAKE_UP:
// assume that all clients use Bluetooth -> if connection, start Bluetooth
power_management_sleep = 0;
if (clients_require_power_on()) {
hci_power_control(HCI_POWER_ON);
}
break;
default:
break;
}
}
static void daemon_sigint_handler(int param){
#ifdef USE_BLUETOOL
// notify daemons
notify_post("ch.ringwald.btstack.stopped");
#endif
log_info(" <= SIGINT received, shutting down..\n");
hci_power_control( HCI_POWER_OFF);
hci_close();
log_info("Good bye, see you.\n");
exit(0);
}
// MARK: manage power off timer
#define USE_POWER_OFF_TIMER
static void stop_power_off_timer(void){
#ifdef USE_POWER_OFF_TIMER
if (timeout_active) {
run_loop_remove_timer(&timeout);
timeout_active = 0;
}
#endif
}
static void start_power_off_timer(void){
#ifdef USE_POWER_OFF_TIMER
stop_power_off_timer();
run_loop_set_timer(&timeout, DAEMON_NO_ACTIVE_CLIENT_TIMEOUT);
run_loop_add_timer(&timeout);
timeout_active = 1;
#else
hci_power_control(HCI_POWER_OFF);
#endif
}
// MARK: manage list of clients
static client_state_t * client_for_connection(connection_t *connection) {
linked_item_t *it;
for (it = (linked_item_t *) clients; it ; it = it->next){
client_state_t * client_state = (client_state_t *) it;
if (client_state->connection == connection) {
return client_state;
}
}
return NULL;
}
static void clients_clear_power_request(void){
linked_item_t *it;
for (it = (linked_item_t *) clients; it ; it = it->next){
client_state_t * client_state = (client_state_t *) it;
client_state->power_mode = HCI_POWER_OFF;
}
}
static int clients_require_power_on(void){
if (global_enable) return 1;
linked_item_t *it;
for (it = (linked_item_t *) clients; it ; it = it->next){
client_state_t * client_state = (client_state_t *) it;
if (client_state->power_mode == HCI_POWER_ON) {
return 1;
}
}
return 0;
}
static int clients_require_discoverable(void){
linked_item_t *it;
for (it = (linked_item_t *) clients; it ; it = it->next){
client_state_t * client_state = (client_state_t *) it;
if (client_state->discoverable) {
return 1;
}
}
return 0;
}
static void usage(const char * name) {
log_info("%s, BTstack background daemon\n", name);
log_info("usage: %s [-h|--help] [--tcp]\n", name);
log_info(" -h|--help display this usage\n");
log_info(" --tcp use TCP server socket instead of local unix socket\n");
}
#ifdef USE_BLUETOOL
static void * run_loop_thread(void *context){
run_loop_execute();
return NULL;
}
#endif
int main (int argc, char * const * argv){
static int tcp_flag = 0;
while (1) {
static struct option long_options[] = {
{ "tcp", no_argument, &tcp_flag, 1 },
{ "help", no_argument, 0, 0 },
{ 0,0,0,0 } // This is a filler for -1
};
int c;
int option_index = -1;
c = getopt_long(argc, argv, "h", long_options, &option_index);
if (c == -1) break; // no more option
// treat long parameter first
if (option_index == -1) {
switch (c) {
case '?':
case 'h':
usage(argv[0]);
return 0;
break;
}
} else {
switch (option_index) {
case 1:
usage(argv[0]);
return 0;
break;
}
}
}
// make stdout unbuffered
setbuf(stdout, NULL);
log_error("BTdaemon started\n");
// handle CTRL-c
signal(SIGINT, daemon_sigint_handler);
// handle SIGTERM - suggested for launchd
signal(SIGTERM, daemon_sigint_handler);
// handle SIGPIPE
struct sigaction act;
act.sa_handler = SIG_IGN;
sigemptyset (&act.sa_mask);
act.sa_flags = 0;
sigaction (SIGPIPE, &act, NULL);
bt_control_t * control = NULL;
#ifdef HAVE_TRANSPORT_H4
config.device_name = UART_DEVICE;
config.baudrate_init = UART_SPEED;
config.baudrate_main = 0;
config.flowcontrol = 1;
#if defined(USE_BLUETOOL) && defined(USE_POWERMANAGEMENT)
if (bt_control_iphone_power_management_supported()){
// use default (max) UART baudrate over netraph interface
config.baudrate_init = 0;
transport = hci_transport_h4_iphone_instance();
} else {
transport = hci_transport_h4_instance();
}
#else
transport = hci_transport_h4_instance();
#endif
#endif
#ifdef HAVE_TRANSPORT_USB
transport = hci_transport_usb_instance();
#endif
#ifdef USE_BLUETOOL
control = &bt_control_iphone;
#endif
#if defined(USE_BLUETOOL) && defined(USE_POWERMANAGEMENT)
if (bt_control_iphone_power_management_supported()){
hci_transport_h4_iphone_set_enforce_wake_device("/dev/btwake");
}
#endif
#ifdef USE_SPRINGBOARD
bluetooth_status_handler = platform_iphone_status_handler;
platform_iphone_register_window_manager_restart(update_ui_status);
platform_iphone_register_preferences_changed(preferences_changed_callback);
#endif
#ifdef REMOTE_DEVICE_DB
remote_device_db = &REMOTE_DEVICE_DB;
#endif
run_loop_init(RUN_LOOP_POSIX);
// init power management notifications
if (control && control->register_for_power_notifications){
control->register_for_power_notifications(power_notification_callback);
}
// logging
loggingEnabled = 0;
int newLoggingEnabled = 1;
#ifdef USE_BLUETOOL
// iPhone has toggle in Preferences.app
newLoggingEnabled = platform_iphone_logging_enabled();
#endif
daemon_set_logging_enabled(newLoggingEnabled);
// init HCI
hci_init(transport, &config, control, remote_device_db);
// init L2CAP
l2cap_init();
l2cap_register_packet_handler(daemon_packet_handler);
timeout.process = daemon_no_connections_timeout;
#ifdef HAVE_RFCOMM
log_info("config.h: HAVE_RFCOMM\n");
rfcomm_init();
rfcomm_register_packet_handler(daemon_packet_handler);
#endif
#ifdef HAVE_SDP
sdp_init();
sdp_register_packet_handler(daemon_packet_handler);
#endif
#ifdef USE_LAUNCHD
socket_connection_create_launchd();
#else
// create server
if (tcp_flag) {
socket_connection_create_tcp(BTSTACK_PORT);
} else {
socket_connection_create_unix(BTSTACK_UNIX);
}
#endif
socket_connection_register_packet_callback(daemon_client_handler);
#ifdef USE_BLUETOOL
// notify daemons
notify_post("ch.ringwald.btstack.started");
// spawn thread to have BTstack run loop on new thread, while main thread is used to keep CFRunLoop
pthread_t run_loop;
pthread_create(&run_loop, NULL, &run_loop_thread, NULL);
// needed to receive notifications
CFRunLoopRun();
#endif
// go!
run_loop_execute();
return 0;
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* debug.h
*
* allow to funnel debug & error messages
*/
#include "config.h"
#include "hci_dump.h"
#include <stdio.h>
#ifdef ENABLE_LOG_DEBUG
#ifdef HAVE_HCI_DUMP
#define log_debug(format, ...) hci_dump_log(format, ## __VA_ARGS__)
#else
#define log_debug(format, ...) printf(format, ## __VA_ARGS__)
#endif
#else
#define log_debug(...)
#endif
#ifdef ENABLE_LOG_INFO
#ifdef HAVE_HCI_DUMP
#define log_info(format, ...) hci_dump_log(format, ## __VA_ARGS__)
#else
#define log_info(format, ...) printf(format, ## __VA_ARGS__)
#endif
#else
#define log_info(...)
#endif
#ifdef ENABLE_LOG_ERROR
#ifdef HAVE_HCI_DUMP
#define log_error(format, ...) hci_dump_log(format, ## __VA_ARGS__)
#else
#define log_error(format, ...) printf(format, ## __VA_ARGS__)
#endif
#else
#define log_error(...)
#endif

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2011 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* hal_cpu.h
*
* Low power mode for MCU requires that IRQs can be first blocked
* and then unblocked while entering low power mode atomically
*/
void hal_cpu_disable_irqs(void);
void hal_cpu_enable_irqs(void);
void hal_cpu_enable_irqs_and_sleep(void);

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2011 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* hal_tick.h
*
* Hardware abstraction layer for periodic ticks
*
*/
#pragma once
#include <stdint.h>
void hal_tick_init(void);
void hal_tick_set_handler(void (*tick_handler)(void));
int hal_tick_get_tick_period_in_ms(void);

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2011 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* hal_uart_dma.h
*
* Hardware abstraction layer that provides
* - blockwise IRQ-driven read/write
* - CSR IRQs
*
*/
#pragma once
#include <stdint.h>
void hal_uart_dma_init(void);
void hal_uart_dma_set_block_received( void (*block_handler)(void));
void hal_uart_dma_set_block_sent( void (*block_handler)(void));
void hal_uart_dma_set_csr_irq_handler( void (*csr_irq_handler)(void));
int hal_uart_dma_set_baud(uint32_t baud);
void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t length);
void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t len);
void hal_uart_dma_set_sleep(uint8_t sleep);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,359 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci.h
*
* Created by Matthias Ringwald on 4/29/09.
*
*/
#pragma once
#include "config.h"
#include <btstack/hci_cmds.h>
#include <btstack/utils.h>
#include "hci_transport.h"
#include "bt_control.h"
#include "remote_device_db.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#if defined __cplusplus
extern "C" {
#endif
// packet header sizes
#define HCI_CMD_HEADER_SIZE 3
#define HCI_ACL_HEADER_SIZE 4
#define HCI_SCO_HEADER_SIZE 3
#define HCI_EVENT_HEADER_SIZE 2
// packet sizes (max payload)
#define HCI_ACL_DM1_SIZE 17
#define HCI_ACL_DH1_SIZE 27
#define HCI_ACL_2DH1_SIZE 54
#define HCI_ACL_3DH1_SIZE 83
#define HCI_ACL_DM3_SIZE 121
#define HCI_ACL_DH3_SIZE 183
#define HCI_ACL_DM5_SIZE 224
#define HCI_ACL_DH5_SIZE 339
#define HCI_ACL_2DH3_SIZE 367
#define HCI_ACL_3DH3_SIZE 552
#define HCI_ACL_2DH5_SIZE 679
#define HCI_ACL_3DH5_SIZE 1021
#define HCI_EVENT_PAYLOAD_SIZE 255
#define HCI_CMD_PAYLOAD_SIZE 255
// packet buffer sizes
// HCI_ACL_PAYLOAD_SIZE is configurable and defined in config.h
#define HCI_EVENT_BUFFER_SIZE (HCI_EVENT_HEADER_SIZE + HCI_EVENT_PAYLOAD_SIZE)
#define HCI_CMD_BUFFER_SIZE (HCI_CMD_HEADER_SIZE + HCI_CMD_PAYLOAD_SIZE)
#define HCI_ACL_BUFFER_SIZE (HCI_ACL_HEADER_SIZE + HCI_ACL_PAYLOAD_SIZE)
// size of hci buffers, big enough for command, event, or acl packet without H4 packet type
// @note cmd buffer is bigger than event buffer
#if HCI_ACL_BUFFER_SIZE > HCI_CMD_BUFFER_SIZE
#define HCI_PACKET_BUFFER_SIZE HCI_ACL_BUFFER_SIZE
#else
#define HCI_PACKET_BUFFER_SIZE HCI_CMD_BUFFER_SIZE
#endif
// OGFs
#define OGF_LINK_CONTROL 0x01
#define OGF_LINK_POLICY 0x02
#define OGF_CONTROLLER_BASEBAND 0x03
#define OGF_INFORMATIONAL_PARAMETERS 0x04
#define OGF_LE_CONTROLLER 0x08
#define OGF_BTSTACK 0x3d
#define OGF_VENDOR 0x3f
// cmds for BTstack
// get state: @returns HCI_STATE
#define BTSTACK_GET_STATE 0x01
// set power mode: @param HCI_POWER_MODE
#define BTSTACK_SET_POWER_MODE 0x02
// set capture mode: @param on
#define BTSTACK_SET_ACL_CAPTURE_MODE 0x03
// get BTstack version
#define BTSTACK_GET_VERSION 0x04
// get system Bluetooth state
#define BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED 0x05
// set system Bluetooth state
#define BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED 0x06
// enable inquiry scan for this client
#define BTSTACK_SET_DISCOVERABLE 0x07
// set global Bluetooth state
#define BTSTACK_SET_BLUETOOTH_ENABLED 0x08
// create l2cap channel: @param bd_addr(48), psm (16)
#define L2CAP_CREATE_CHANNEL 0x20
// disconnect l2cap disconnect, @param channel(16), reason(8)
#define L2CAP_DISCONNECT 0x21
// register l2cap service: @param psm(16), mtu (16)
#define L2CAP_REGISTER_SERVICE 0x22
// unregister l2cap disconnect, @param psm(16)
#define L2CAP_UNREGISTER_SERVICE 0x23
// accept connection @param bd_addr(48), dest cid (16)
#define L2CAP_ACCEPT_CONNECTION 0x24
// decline l2cap disconnect,@param bd_addr(48), dest cid (16), reason(8)
#define L2CAP_DECLINE_CONNECTION 0x25
// create l2cap channel: @param bd_addr(48), psm (16), mtu (16)
#define L2CAP_CREATE_CHANNEL_MTU 0x26
// register SDP Service Record: service record (size)
#define SDP_REGISTER_SERVICE_RECORD 0x30
// unregister SDP Service Record
#define SDP_UNREGISTER_SERVICE_RECORD 0x31
// RFCOMM "HCI" Commands
#define RFCOMM_CREATE_CHANNEL 0x40
#define RFCOMM_DISCONNECT 0x41
#define RFCOMM_REGISTER_SERVICE 0x42
#define RFCOMM_UNREGISTER_SERVICE 0x43
#define RFCOMM_ACCEPT_CONNECTION 0x44
#define RFCOMM_DECLINE_CONNECTION 0x45
#define RFCOMM_PERSISTENT_CHANNEL 0x46
#define RFCOMM_CREATE_CHANNEL_WITH_CREDITS 0x47
#define RFCOMM_REGISTER_SERVICE_WITH_CREDITS 0x48
#define RFCOMM_GRANT_CREDITS 0x49
//
#define IS_COMMAND(packet, command) (READ_BT_16(packet,0) == command.opcode)
// data: event(8)
#define DAEMON_EVENT_CONNECTION_OPENED 0x50
// data: event(8)
#define DAEMON_EVENT_CONNECTION_CLOSED 0x51
// data: event(8), nr_connections(8)
#define DAEMON_NR_CONNECTIONS_CHANGED 0x52
// data: event(8)
#define DAEMON_EVENT_NEW_RFCOMM_CREDITS 0x53
// data: event()
#define DAEMON_EVENT_HCI_PACKET_SENT 0x54
/**
* Connection State
*/
typedef enum {
AUTH_FLAGS_NONE = 0x00,
RECV_LINK_KEY_REQUEST = 0x01,
HANDLE_LINK_KEY_REQUEST = 0x02,
SENT_LINK_KEY_REPLY = 0x04,
SENT_LINK_KEY_NEGATIVE_REQUEST = 0x08,
RECV_LINK_KEY_NOTIFICATION = 0x10,
RECV_PIN_CODE_REQUEST = 0x20,
SENT_PIN_CODE_REPLY = 0x40,
SENT_PIN_CODE_NEGATIVE_REPLY = 0x80
} hci_authentication_flags_t;
typedef enum {
SENT_CREATE_CONNECTION = 1,
RECEIVED_CONNECTION_REQUEST,
ACCEPTED_CONNECTION_REQUEST,
REJECTED_CONNECTION_REQUEST,
OPEN,
SENT_DISCONNECT
} CONNECTION_STATE;
typedef enum {
BLUETOOTH_OFF = 1,
BLUETOOTH_ON,
BLUETOOTH_ACTIVE
} BLUETOOTH_STATE;
typedef struct {
// linked list - assert: first field
linked_item_t item;
// remote side
bd_addr_t address;
// module handle
hci_con_handle_t con_handle;
// state
CONNECTION_STATE state;
// errands
hci_authentication_flags_t authentication_flags;
timer_source_t timeout;
#ifdef HAVE_TIME
// timer
struct timeval timestamp;
#endif
#ifdef HAVE_TICK
uint32_t timestamp; // timeout in system ticks
#endif
// ACL packet recombination - ACL Header + ACL payload
uint8_t acl_recombination_buffer[4 + HCI_ACL_BUFFER_SIZE];
uint16_t acl_recombination_pos;
uint16_t acl_recombination_length;
// number ACL packets sent to controller
uint8_t num_acl_packets_sent;
} hci_connection_t;
/**
* main data structure
*/
typedef struct {
// transport component with configuration
hci_transport_t * hci_transport;
void * config;
// hardware power controller
bt_control_t * control;
// list of existing baseband connections
linked_list_t connections;
// single buffer for HCI Command assembly
uint8_t hci_packet_buffer[HCI_PACKET_BUFFER_SIZE]; // opcode (16), len(8)
/* host to controller flow control */
uint8_t num_cmd_packets;
// uint8_t total_num_cmd_packets;
uint8_t total_num_acl_packets;
uint16_t acl_data_packet_length;
// usable packet types given acl_data_packet_length and HCI_ACL_BUFFER_SIZE
uint16_t packet_types;
/* callback to L2CAP layer */
void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size);
/* remote device db */
remote_device_db_t const*remote_device_db;
/* hci state machine */
HCI_STATE state;
uint8_t substate;
uint8_t cmds_ready;
uint8_t discoverable;
uint8_t connectable;
/* buffer for scan enable cmd - 0xff no change */
uint8_t new_scan_enable_value;
// buffer for single connection decline
uint8_t decline_reason;
bd_addr_t decline_addr;
} hci_stack_t;
// create and send hci command packets based on a template and a list of parameters
uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...);
uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr);
// set up HCI
void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db);
void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
void hci_close(void);
// power and inquriy scan control
int hci_power_control(HCI_POWER_MODE mode);
void hci_discoverable_control(uint8_t enable);
void hci_connectable_control(uint8_t enable);
/**
* run the hci control loop once
*/
void hci_run(void);
// create and send hci command packets based on a template and a list of parameters
int hci_send_cmd(const hci_cmd_t *cmd, ...);
// send complete CMD packet
int hci_send_cmd_packet(uint8_t *packet, int size);
// send ACL packet
int hci_send_acl_packet(uint8_t *packet, int size);
// non-blocking UART driver needs
int hci_can_send_packet_now(uint8_t packet_type);
hci_connection_t * connection_for_handle(hci_con_handle_t con_handle);
uint8_t hci_number_outgoing_packets(hci_con_handle_t handle);
uint8_t hci_number_free_acl_slots(void);
int hci_authentication_active_for_handle(hci_con_handle_t handle);
void hci_drop_link_key_for_bd_addr(bd_addr_t *addr);
uint16_t hci_max_acl_data_packet_length(void);
uint16_t hci_usable_acl_packet_types(void);
uint8_t* hci_get_outgoing_acl_packet_buffer(void);
//
void hci_emit_state(void);
void hci_emit_connection_complete(hci_connection_t *conn, uint8_t status);
void hci_emit_l2cap_check_timeout(hci_connection_t *conn);
void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason);
void hci_emit_nr_connections_changed(void);
void hci_emit_hci_open_failed(void);
void hci_emit_btstack_version(void);
void hci_emit_system_bluetooth_enabled(uint8_t enabled);
void hci_emit_remote_name_cached(bd_addr_t *addr, device_name_t *name);
void hci_emit_discoverable_enabled(uint8_t enabled);
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,679 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_cmds.c
*
* Created by Matthias Ringwald on 7/23/09.
*/
#include <btstack/hci_cmds.h>
#include <string.h>
#include <btstack/sdp_util.h>
#include "config.h"
#include "hci.h"
// calculate combined ogf/ocf value
#define OPCODE(ogf, ocf) (ocf | ogf << 10)
/**
* construct HCI Command based on template
*
* Format:
* 1,2,3,4: one to four byte value
* H: HCI connection handle
* B: Bluetooth Baseband Address (BD_ADDR)
* E: Extended Inquiry Result
* N: Name up to 248 chars, \0 terminated
* P: 16 byte Pairing code
* S: Service Record (Data Element Sequence)
*/
uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr){
hci_cmd_buffer[0] = cmd->opcode & 0xff;
hci_cmd_buffer[1] = cmd->opcode >> 8;
int pos = 3;
const char *format = cmd->format;
uint16_t word;
uint32_t longword;
uint8_t * ptr;
while (*format) {
switch(*format) {
case '1': // 8 bit value
case '2': // 16 bit value
case 'H': // hci_handle
word = va_arg(argptr, int); // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
hci_cmd_buffer[pos++] = word & 0xff;
if (*format == '2') {
hci_cmd_buffer[pos++] = word >> 8;
} else if (*format == 'H') {
// TODO implement opaque client connection handles
// pass module handle for now
hci_cmd_buffer[pos++] = word >> 8;
}
break;
case '3':
case '4':
longword = va_arg(argptr, uint32_t);
// longword = va_arg(argptr, int);
hci_cmd_buffer[pos++] = longword;
hci_cmd_buffer[pos++] = longword >> 8;
hci_cmd_buffer[pos++] = longword >> 16;
if (*format == '4'){
hci_cmd_buffer[pos++] = longword >> 24;
}
break;
case 'B': // bt-addr
ptr = va_arg(argptr, uint8_t *);
hci_cmd_buffer[pos++] = ptr[5];
hci_cmd_buffer[pos++] = ptr[4];
hci_cmd_buffer[pos++] = ptr[3];
hci_cmd_buffer[pos++] = ptr[2];
hci_cmd_buffer[pos++] = ptr[1];
hci_cmd_buffer[pos++] = ptr[0];
break;
case 'E': // Extended Inquiry Information 240 octets
ptr = va_arg(argptr, uint8_t *);
memcpy(&hci_cmd_buffer[pos], ptr, 240);
pos += 240;
break;
case 'N': { // UTF-8 string, null terminated
ptr = va_arg(argptr, uint8_t *);
uint16_t len = strlen((const char*) ptr);
if (len > 248) {
len = 248;
}
memcpy(&hci_cmd_buffer[pos], ptr, len);
if (len < 248) {
// fill remaining space with zeroes
memset(&hci_cmd_buffer[pos+len], 0, 248-len);
}
pos += 248;
break;
}
case 'P': // 16 byte PIN code or link key
ptr = va_arg(argptr, uint8_t *);
memcpy(&hci_cmd_buffer[pos], ptr, 16);
pos += 16;
break;
#ifdef HAVE_BLE
case 'A': // 31 bytes advertising data
ptr = va_arg(argptr, uint8_t *);
memcpy(&hci_cmd_buffer[pos], ptr, 31);
pos += 31;
break;
#endif
#ifdef HAVE_SDP
case 'S': { // Service Record (Data Element Sequence)
ptr = va_arg(argptr, uint8_t *);
uint16_t len = de_get_len(ptr);
memcpy(&hci_cmd_buffer[pos], ptr, len);
pos += len;
break;
}
#endif
default:
break;
}
format++;
};
hci_cmd_buffer[2] = pos - 3;
return pos;
}
/**
* construct HCI Command based on template
*
* mainly calls hci_create_cmd_internal
*/
uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...){
va_list argptr;
va_start(argptr, cmd);
uint16_t len = hci_create_cmd_internal(hci_cmd_buffer, cmd, argptr);
va_end(argptr);
return len;
}
/**
* Link Control Commands
*/
const hci_cmd_t hci_inquiry = {
OPCODE(OGF_LINK_CONTROL, 0x01), "311"
// LAP, Inquiry length, Num_responses
};
const hci_cmd_t hci_inquiry_cancel = {
OPCODE(OGF_LINK_CONTROL, 0x02), ""
// no params
};
const hci_cmd_t hci_create_connection = {
OPCODE(OGF_LINK_CONTROL, 0x05), "B21121"
// BD_ADDR, Packet_Type, Page_Scan_Repetition_Mode, Reserved, Clock_Offset, Allow_Role_Switch
};
const hci_cmd_t hci_disconnect = {
OPCODE(OGF_LINK_CONTROL, 0x06), "H1"
// Handle, Reason: 0x05, 0x13-0x15, 0x1a, 0x29
// see Errors Codes in BT Spec Part D
};
const hci_cmd_t hci_create_connection_cancel = {
OPCODE(OGF_LINK_CONTROL, 0x08), "B"
// BD_ADDR
};
const hci_cmd_t hci_accept_connection_request = {
OPCODE(OGF_LINK_CONTROL, 0x09), "B1"
// BD_ADDR, Role: become master, stay slave
};
const hci_cmd_t hci_reject_connection_request = {
OPCODE(OGF_LINK_CONTROL, 0x0a), "B1"
// BD_ADDR, reason e.g. CONNECTION REJECTED DUE TO LIMITED RESOURCES (0x0d)
};
const hci_cmd_t hci_link_key_request_reply = {
OPCODE(OGF_LINK_CONTROL, 0x0b), "BP"
// BD_ADDR, LINK_KEY
};
const hci_cmd_t hci_link_key_request_negative_reply = {
OPCODE(OGF_LINK_CONTROL, 0x0c), "B"
// BD_ADDR
};
const hci_cmd_t hci_pin_code_request_reply = {
OPCODE(OGF_LINK_CONTROL, 0x0d), "B1P"
// BD_ADDR, pin length, PIN: c-string
};
const hci_cmd_t hci_pin_code_request_negative_reply = {
OPCODE(OGF_LINK_CONTROL, 0x0e), "B"
// BD_ADDR
};
const hci_cmd_t hci_authentication_requested = {
OPCODE(OGF_LINK_CONTROL, 0x11), "H"
// Handle
};
const hci_cmd_t hci_set_connection_encryption = {
OPCODE(OGF_LINK_CONTROL, 0x13), "H1"
// Handle, Encryption_Enable
};
const hci_cmd_t hci_change_connection_link_key = {
OPCODE(OGF_LINK_CONTROL, 0x15), "H"
// Handle
};
const hci_cmd_t hci_remote_name_request = {
OPCODE(OGF_LINK_CONTROL, 0x19), "B112"
// BD_ADDR, Page_Scan_Repetition_Mode, Reserved, Clock_Offset
};
const hci_cmd_t hci_remote_name_request_cancel = {
OPCODE(OGF_LINK_CONTROL, 0x1A), "B"
// BD_ADDR
};
/**
* Link Policy Commands
*/
const hci_cmd_t hci_sniff_mode = {
OPCODE(OGF_LINK_POLICY, 0x03), "H2222"
// handle, Sniff_Max_Interval, Sniff_Min_Interval, Sniff_Attempt, Sniff_Timeout:
};
const hci_cmd_t hci_qos_setup = {
OPCODE(OGF_LINK_POLICY, 0x07), "H114444"
// handle, flags, service_type, token rate (bytes/s), peak bandwith (bytes/s),
// latency (us), delay_variation (us)
};
const hci_cmd_t hci_role_discovery = {
OPCODE(OGF_LINK_POLICY, 0x09), "H"
// handle
};
const hci_cmd_t hci_switch_role_command= {
OPCODE(OGF_LINK_POLICY, 0x0b), "B1"
// BD_ADDR, role: {0=master,1=slave}
};
const hci_cmd_t hci_read_link_policy_settings = {
OPCODE(OGF_LINK_POLICY, 0x0c), "H"
// handle
};
const hci_cmd_t hci_write_link_policy_settings = {
OPCODE(OGF_LINK_POLICY, 0x0d), "H2"
// handle, settings
};
/**
* Controller & Baseband Commands
*/
const hci_cmd_t hci_set_event_mask = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x01), "44"
// event_mask lower 4 octets, higher 4 bytes
};
const hci_cmd_t hci_reset = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x03), ""
// no params
};
const hci_cmd_t hci_delete_stored_link_key = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x12), "B1"
// BD_ADDR, Delete_All_Flag
};
const hci_cmd_t hci_write_local_name = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x13), "N"
// Local name (UTF-8, Null Terminated, max 248 octets)
};
const hci_cmd_t hci_write_page_timeout = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x18), "2"
// Page_Timeout * 0.625 ms
};
const hci_cmd_t hci_write_scan_enable = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x1A), "1"
// Scan_enable: no, inq, page, inq+page
};
const hci_cmd_t hci_write_authentication_enable = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x20), "1"
// Authentication_Enable
};
const hci_cmd_t hci_write_class_of_device = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x24), "3"
// Class of Device
};
const hci_cmd_t hci_read_num_broadcast_retransmissions = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x29), ""
};
const hci_cmd_t hci_write_num_broadcast_retransmissions = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x2a), "1"
// Num broadcast retransmissions (e.g. 0 for a single broadcast)
};
const hci_cmd_t hci_host_buffer_size = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x33), "2122"
// Host_ACL_Data_Packet_Length:, Host_Synchronous_Data_Packet_Length:, Host_Total_Num_ACL_Data_Packets:, Host_Total_Num_Synchronous_Data_Packets:
};
const hci_cmd_t hci_read_link_supervision_timeout = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x36), "H"
// handle
};
const hci_cmd_t hci_write_link_supervision_timeout = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x37), "H2"
// handle, Range for N: 0x0001 Ð 0xFFFF Time (Range: 0.625ms Ð 40.9 sec)
};
const hci_cmd_t hci_write_inquiry_mode = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x45), "1"
// Inquiry mode: 0x00 = standard, 0x01 = with RSSI, 0x02 = extended
};
const hci_cmd_t hci_write_extended_inquiry_response = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x52), "1E"
// FEC_Required, Exstended Inquiry Response
};
const hci_cmd_t hci_write_simple_pairing_mode = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x56), "1"
// mode: 0 = off, 1 = on
};
const hci_cmd_t hci_read_le_host_supported = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x6c), ""
// params: none
// return: status, le supported host, simultaneous le host
};
const hci_cmd_t hci_write_le_host_supported = {
OPCODE(OGF_CONTROLLER_BASEBAND, 0x6d), "11"
// param: le supported host, simultaneous le host
// return: status
};
/**
* Informational Parameters
*/
const hci_cmd_t hci_read_local_supported_features = {
OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x03), ""
// no params
};
const hci_cmd_t hci_read_buffer_size = {
OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x05), ""
// no params
};
const hci_cmd_t hci_read_bd_addr = {
OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x09), ""
// no params
};
#ifdef HAVE_BLE
/**
* Low Energy Commands
*/
const hci_cmd_t hci_le_set_event_mask = {
OPCODE(OGF_LE_CONTROLLER, 0x01), "44"
// params: event_mask lower 4 octets, higher 4 bytes
// return: status
};
const hci_cmd_t hci_le_read_buffer_size = {
OPCODE(OGF_LE_CONTROLLER, 0x02), ""
// params: none
// return: status, le acl data packet len (16), total num le acl data packets(8)
};
const hci_cmd_t hci_le_read_supported_features = {
OPCODE(OGF_LE_CONTROLLER, 0x03), ""
// params: none
// return: LE_Features See [Vol 6] Part B, Section 4.6
};
const hci_cmd_t hci_le_set_random_address = {
OPCODE(OGF_LE_CONTROLLER, 0x05), "B"
// params: random device address
// return: status
};
const hci_cmd_t hci_le_set_advertising_parameters = {
OPCODE(OGF_LE_CONTROLLER, 0x06), "22111B11"
// param: min advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec
// param: max advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec
// param: advertising type (enum from 0): ADV_IND, ADC_DIRECT_IND, ADV_SCAN_IND, ADV_NONCONN_IND
// param: own address type (enum from 0): public device address, random device address
// param: direct address type (enum from 0): public device address, random device address
// param: direct address - public or random address of device to be connecteed
// param: advertising channel map (flags): chan_37(1), chan_38(2), chan_39(4)
// param: advertising filter policy (enum from 0): scan any conn any, scan whitelist, con any, scan any conn whitelist, scan whitelist, con whitelist
// return: status
};
const hci_cmd_t hci_le_read_advertising_channel_tx_power = {
OPCODE(OGF_LE_CONTROLLER, 0x07), ""
// params: none
// return: status, level [-20,10] signed int (8), units dBm
};
const hci_cmd_t hci_le_set_advertising_data= {
OPCODE(OGF_LE_CONTROLLER, 0x08), "1A"
// param: advertising data len
// param: advertising data (31 bytes)
// return: status
};
const hci_cmd_t hci_le_set_scan_response_data= {
OPCODE(OGF_LE_CONTROLLER, 0x09), "1A"
// param: scan response data len
// param: scan response data (31 bytes)
// return: status
};
const hci_cmd_t hci_le_set_advertise_enable = {
OPCODE(OGF_LE_CONTROLLER, 0x0a), "1"
// params: avertise enable: off (0), on (1)
// return: status
};
const hci_cmd_t hci_le_set_scan_parameters = {
OPCODE(OGF_LE_CONTROLLER, 0x0b), "12211"
// param: le scan type: passive (0), active (1)
// param: le scan interval [0x0004,0x4000], unit: 0.625 msec
// param: le scan window [0x0004,0x4000], unit: 0.625 msec
// param: own address type: public (0), random (1)
// param: scanning filter policy: any (0), only whitelist (1)
// return: status
};
const hci_cmd_t hci_le_set_scan_enable = {
OPCODE(OGF_LE_CONTROLLER, 0x0c), "11"
// param: le scan enable: disabled (0), enabled (1)
// param: filter duplices: disabled (0), enabled (1)
// return: status
};
const hci_cmd_t hci_le_create_connection= {
OPCODE(OGF_LE_CONTROLLER, 0x0d), "2211B1222222"
// param: le scan interval, [0x0004, 0x4000], unit: 0.625 msec
// param: le scan window, [0x0004, 0x4000], unit: 0.625 msec
// param: initiator filter policy: peer address type + peer address (0), whitelist (1)
// param: peer address type: public (0), random (1)
// param: peer address
// param: own address type: public (0), random (1)
// param: conn interval min, [0x0006, 0x0c80], unit: 1.25 msec
// param: conn interval max, [0x0006, 0x0c80], unit: 1.25 msec
// param: conn latency, number of connection events [0x0000, 0x01f4]
// param: supervision timeout, [0x000a, 0x0c80], unit: 10 msec
// param: minimum CE length, [0x0000, 0xffff], unit: 0.625 msec
// return: none -> le create connection complete event
};
const hci_cmd_t hci_le_create_connection_cancel = {
OPCODE(OGF_LE_CONTROLLER, 0x0e), ""
// params: none
// return: status
};
const hci_cmd_t hci_le_read_white_list_size = {
OPCODE(OGF_LE_CONTROLLER, 0x0f), ""
// params: none
// return: status, number of entries in controller whitelist
};
const hci_cmd_t hci_le_clear_white_list = {
OPCODE(OGF_LE_CONTROLLER, 0x10), ""
// params: none
// return: status
};
const hci_cmd_t hci_le_add_device_to_whitelist = {
OPCODE(OGF_LE_CONTROLLER, 0x11), "1B"
// param: address type: public (0), random (1)
// param: address
// return: status
};
const hci_cmd_t hci_le_remove_device_from_whitelist = {
OPCODE(OGF_LE_CONTROLLER, 0x12), "1B"
// param: address type: public (0), random (1)
// param: address
// return: status
};
const hci_cmd_t hci_le_connection_update = {
OPCODE(OGF_LE_CONTROLLER, 0x13), "H222222"
// param: conn handle
// param: conn interval min, [0x0006,0x0c80], unit: 1.25 msec
// param: conn interval max, [0x0006,0x0c80], unit: 1.25 msec
// param: conn latency, [0x0000,0x03e8], number of connection events
// param: supervision timeout, [0x000a,0x0c80], unit: 10 msec
// param: minimum CE length, [0x0000,0xffff], unit: 0.625 msec
// param: maximum CE length, [0x0000,0xffff], unit: 0.625 msec
// return: none -> le connection update complete event
};
const hci_cmd_t hci_le_set_host_channel_classification = {
OPCODE(OGF_LE_CONTROLLER, 0x14), "41"
// param: channel map 37 bit, split into first 32 and higher 5 bits
// return: status
};
const hci_cmd_t hci_le_read_channel_map = {
OPCODE(OGF_LE_CONTROLLER, 0x15), "H"
// params: connection handle
// return: status, connection handle, channel map (5 bytes, 37 used)
};
const hci_cmd_t hci_le_read_remote_used_features = {
OPCODE(OGF_LE_CONTROLLER, 0x16), "H"
// params: connection handle
// return: none -> le read remote used features complete event
};
const hci_cmd_t hci_le_encrypt = {
OPCODE(OGF_LE_CONTROLLER, 0x17), "PP"
// param: key (128) for AES-128
// param: plain text (128)
// return: status, encrypted data (128)
};
const hci_cmd_t hci_le_rand = {
OPCODE(OGF_LE_CONTROLLER, 0x18), ""
// params: none
// return: status, random number (64)
};
const hci_cmd_t hci_le_start_encryption = {
OPCODE(OGF_LE_CONTROLLER, 0x19), "H442P"
// param: connection handle
// param: 64 bit random number lower 32 bit
// param: 64 bit random number higher 32 bit
// param: encryption diversifier (16)
// param: long term key (128)
// return: none -> encryption changed or encryption key refresh complete event
};
const hci_cmd_t hci_le_long_term_key_request_reply = {
OPCODE(OGF_LE_CONTROLLER, 0x1a), "HP"
// param: connection handle
// param: long term key (128)
// return: status, connection handle
};
const hci_cmd_t hci_le_long_term_key_negative_reply = {
OPCODE(OGF_LE_CONTROLLER, 0x1b), "H"
// param: connection handle
// return: status, connection handle
};
const hci_cmd_t hci_le_read_supported_states = {
OPCODE(OGF_LE_CONTROLLER, 0x1c), "H"
// param: none
// return: status, LE states (64)
};
const hci_cmd_t hci_le_receiver_test = {
OPCODE(OGF_LE_CONTROLLER, 0x1d), "1"
// param: rx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2
// return: status
};
const hci_cmd_t hci_le_transmitter_test = {
OPCODE(OGF_LE_CONTROLLER, 0x1e), "111"
// param: tx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2
// param: lengh of test payload [0x00,0x25]
// param: packet payload [0,7] different patterns
// return: status
};
const hci_cmd_t hci_le_test_end = {
OPCODE(OGF_LE_CONTROLLER, 0x1f), "1"
// params: none
// return: status, number of packets (8)
};
#endif
// BTstack commands
const hci_cmd_t btstack_get_state = {
OPCODE(OGF_BTSTACK, BTSTACK_GET_STATE), ""
// no params ->
};
const hci_cmd_t btstack_set_power_mode = {
OPCODE(OGF_BTSTACK, BTSTACK_SET_POWER_MODE), "1"
// mode: 0 = off, 1 = on
};
const hci_cmd_t btstack_set_acl_capture_mode = {
OPCODE(OGF_BTSTACK, BTSTACK_SET_ACL_CAPTURE_MODE), "1"
// mode: 0 = off, 1 = on
};
const hci_cmd_t btstack_get_version = {
OPCODE(OGF_BTSTACK, BTSTACK_GET_VERSION), ""
};
const hci_cmd_t btstack_get_system_bluetooth_enabled = {
OPCODE(OGF_BTSTACK, BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED), ""
};
const hci_cmd_t btstack_set_system_bluetooth_enabled = {
OPCODE(OGF_BTSTACK, BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED), "1"
};
const hci_cmd_t btstack_set_discoverable = {
OPCODE(OGF_BTSTACK, BTSTACK_SET_DISCOVERABLE), "1"
};
const hci_cmd_t btstack_set_bluetooth_enabled = {
// only used by btstack config
OPCODE(OGF_BTSTACK, BTSTACK_SET_BLUETOOTH_ENABLED), "1"
};
const hci_cmd_t l2cap_create_channel = {
OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL), "B2"
// @param bd_addr(48), psm (16)
};
const hci_cmd_t l2cap_create_channel_mtu = {
OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL_MTU), "B22"
// @param bd_addr(48), psm (16), mtu (16)
};
const hci_cmd_t l2cap_disconnect = {
OPCODE(OGF_BTSTACK, L2CAP_DISCONNECT), "21"
// @param channel(16), reason(8)
};
const hci_cmd_t l2cap_register_service = {
OPCODE(OGF_BTSTACK, L2CAP_REGISTER_SERVICE), "22"
// @param psm (16), mtu (16)
};
const hci_cmd_t l2cap_unregister_service = {
OPCODE(OGF_BTSTACK, L2CAP_UNREGISTER_SERVICE), "2"
// @param psm (16)
};
const hci_cmd_t l2cap_accept_connection = {
OPCODE(OGF_BTSTACK, L2CAP_ACCEPT_CONNECTION), "2"
// @param source cid (16)
};
const hci_cmd_t l2cap_decline_connection = {
OPCODE(OGF_BTSTACK, L2CAP_DECLINE_CONNECTION), "21"
// @param source cid (16), reason(8)
};
const hci_cmd_t sdp_register_service_record = {
OPCODE(OGF_BTSTACK, SDP_REGISTER_SERVICE_RECORD), "S"
// @param service record handle (DES)
};
const hci_cmd_t sdp_unregister_service_record = {
OPCODE(OGF_BTSTACK, SDP_UNREGISTER_SERVICE_RECORD), "4"
// @param service record handle (32)
};
// create rfcomm channel: @param bd_addr(48), channel (8)
const hci_cmd_t rfcomm_create_channel = {
OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL), "B1"
};
// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8)
const hci_cmd_t rfcomm_create_channel_with_initial_credits = {
OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL_WITH_CREDITS), "B121"
};
// grant credits: @param rfcomm_cid(16), credits (8)
const hci_cmd_t rfcomm_grants_credits= {
OPCODE(OGF_BTSTACK, RFCOMM_GRANT_CREDITS), "21"
};
// disconnect rfcomm disconnect, @param rfcomm_cid(16), reason(8)
const hci_cmd_t rfcomm_disconnect = {
OPCODE(OGF_BTSTACK, RFCOMM_DISCONNECT), "21"
};
// register rfcomm service: @param channel(8), mtu (16)
const hci_cmd_t rfcomm_register_service = {
OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE), "12"
};
// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
const hci_cmd_t rfcomm_register_service_with_initial_credits = {
OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE_WITH_CREDITS), "121"
};
// unregister rfcomm service, @param service_channel(16)
const hci_cmd_t rfcomm_unregister_service = {
OPCODE(OGF_BTSTACK, RFCOMM_UNREGISTER_SERVICE), "2"
};
// accept connection @param source cid (16)
const hci_cmd_t rfcomm_accept_connection = {
OPCODE(OGF_BTSTACK, RFCOMM_ACCEPT_CONNECTION), "2"
};
// decline connection @param source cid (16)
const hci_cmd_t rfcomm_decline_connection = {
OPCODE(OGF_BTSTACK, RFCOMM_DECLINE_CONNECTION), "21"
};
// request persisten rfcomm channel number for named service
const hci_cmd_t rfcomm_persistent_channel_for_service = {
OPCODE(OGF_BTSTACK, RFCOMM_PERSISTENT_CHANNEL), "N"
};
// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
extern const hci_cmd_t rfcomm_register_service_with_initial_credits;

View File

@ -0,0 +1,239 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_dump.c
*
* Dump HCI trace in various formats:
*
* - BlueZ's hcidump format
* - Apple's PacketLogger
* - stdout hexdump
*
* Created by Matthias Ringwald on 5/26/09.
*/
#include "config.h"
#include "hci_dump.h"
#include "hci.h"
#include "hci_transport.h"
#include <btstack/hci_cmds.h>
#ifndef EMBEDDED
#include <fcntl.h> // open
#include <arpa/inet.h> // hton..
#include <unistd.h> // write
#include <stdio.h>
#include <time.h>
#include <sys/time.h> // for timestamps
#include <sys/stat.h> // for mode flags
#include <stdarg.h> // for va_list
#endif
// BLUEZ hcidump
typedef struct {
uint16_t len;
uint8_t in;
uint8_t pad;
uint32_t ts_sec;
uint32_t ts_usec;
uint8_t packet_type;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
hcidump_hdr;
// APPLE PacketLogger
typedef struct {
uint32_t len;
uint32_t ts_sec;
uint32_t ts_usec;
uint8_t type; // 0xfc for note
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
pktlog_hdr;
#ifndef EMBEDDED
static int dump_file = -1;
static int dump_format;
static hcidump_hdr header_bluez;
static pktlog_hdr header_packetlogger;
static char time_string[40];
static int max_nr_packets = -1;
static int nr_packets = 0;
static char log_message_buffer[256];
#endif
void hci_dump_open(char *filename, hci_dump_format_t format){
#ifndef EMBEDDED
dump_format = format;
if (dump_format == HCI_DUMP_STDOUT) {
dump_file = fileno(stdout);
} else {
dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
}
#endif
}
#ifndef EMBEDDED
void hci_dump_set_max_packets(int packets){
max_nr_packets = packets;
}
#endif
void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
#ifndef EMBEDDED
if (dump_file < 0) return; // not activated yet
// don't grow bigger than max_nr_packets
if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
if (nr_packets >= max_nr_packets){
lseek(dump_file, 0, SEEK_SET);
ftruncate(dump_file, 0);
nr_packets = 0;
}
nr_packets++;
}
// get time
struct timeval curr_time;
struct tm* ptm;
gettimeofday(&curr_time, NULL);
switch (dump_format){
case HCI_DUMP_STDOUT: {
/* Obtain the time of day, and convert it to a tm struct. */
ptm = localtime (&curr_time.tv_sec);
/* Format the date and time, down to a single second. */
strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
/* Compute milliseconds from microseconds. */
uint16_t milliseconds = curr_time.tv_usec / 1000;
/* Print the formatted time, in seconds, followed by a decimal point
and the milliseconds. */
printf ("%s.%03u] ", time_string, milliseconds);
switch (packet_type){
case HCI_COMMAND_DATA_PACKET:
printf("CMD => ");
break;
case HCI_EVENT_PACKET:
printf("EVT <= ");
break;
case HCI_ACL_DATA_PACKET:
if (in) {
printf("ACL <= ");
} else {
printf("ACL => ");
}
break;
case LOG_MESSAGE_PACKET:
// assume buffer is big enough
packet[len] = 0;
printf("LOG -- %s\n", (char*) packet);
return;
default:
return;
}
hexdump(packet, len);
break;
}
case HCI_DUMP_BLUEZ:
bt_store_16( (uint8_t *) &header_bluez.len, 0, 1 + len);
header_bluez.in = in;
header_bluez.pad = 0;
bt_store_32( (uint8_t *) &header_bluez.ts_sec, 0, curr_time.tv_sec);
bt_store_32( (uint8_t *) &header_bluez.ts_usec, 0, curr_time.tv_usec);
header_bluez.packet_type = packet_type;
write (dump_file, &header_bluez, sizeof(hcidump_hdr) );
write (dump_file, packet, len );
break;
case HCI_DUMP_PACKETLOGGER:
header_packetlogger.len = htonl( sizeof(pktlog_hdr) - 4 + len);
header_packetlogger.ts_sec = htonl(curr_time.tv_sec);
header_packetlogger.ts_usec = htonl(curr_time.tv_usec);
switch (packet_type){
case HCI_COMMAND_DATA_PACKET:
header_packetlogger.type = 0x00;
break;
case HCI_ACL_DATA_PACKET:
if (in) {
header_packetlogger.type = 0x03;
} else {
header_packetlogger.type = 0x02;
}
break;
case HCI_EVENT_PACKET:
header_packetlogger.type = 0x01;
break;
case LOG_MESSAGE_PACKET:
header_packetlogger.type = 0xfc;
break;
default:
return;
}
write (dump_file, &header_packetlogger, sizeof(pktlog_hdr) );
write (dump_file, packet, len );
break;
default:
break;
}
#endif
}
void hci_dump_log(const char * format, ...){
#ifndef EMBEDDED
va_list argptr;
va_start(argptr, format);
int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len);
va_end(argptr);
#endif
}
void hci_dump_close(){
#ifndef EMBEDDED
close(dump_file);
dump_file = -1;
#endif
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_dump.h
*
* Dump HCI trace as BlueZ's hcidump format, Apple's PacketLogger, or stdout
*
* Created by Matthias Ringwald on 5/26/09.
*/
#pragma once
#include <stdint.h>
typedef enum {
HCI_DUMP_BLUEZ = 0,
HCI_DUMP_PACKETLOGGER,
HCI_DUMP_STDOUT
} hci_dump_format_t;
void hci_dump_open(char *filename, hci_dump_format_t format);
void hci_dump_set_max_packets(int packets); // -1 for unlimited
void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len);
void hci_dump_log(const char * format, ...);
void hci_dump_close(void);

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_transport.h
*
* HCI Transport API -- allows BT Daemon to use different transport protcols
*
* Created by Matthias Ringwald on 4/29/09.
*
*/
#pragma once
#include <stdint.h>
#include <btstack/run_loop.h>
#if defined __cplusplus
extern "C" {
#endif
/* HCI packet types */
typedef struct {
int (*open)(void *transport_config);
int (*close)(void *transport_config);
int (*send_packet)(uint8_t packet_type, uint8_t *packet, int size);
void (*register_packet_handler)(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
const char * (*get_transport_name)(void);
// custom extension for UART transport implementations
int (*set_baudrate)(uint32_t baudrate);
// support async transport layers, e.g. IRQ driven without buffers
int (*can_send_packet_now)(uint8_t packet_type);
} hci_transport_t;
typedef struct {
const char *device_name;
uint32_t baudrate_init; // initial baud rate
uint32_t baudrate_main; // = 0: same as initial baudrate
int flowcontrol; //
} hci_uart_config_t;
// inline various hci_transport_X.h files
extern hci_transport_t * hci_transport_h4_instance(void);
extern hci_transport_t * hci_transport_h4_dma_instance(void);
extern hci_transport_t * hci_transport_h4_iphone_instance(void);
extern hci_transport_t * hci_transport_h5_instance(void);
extern hci_transport_t * hci_transport_usb_instance(void);
// support for "enforece wake device" in h4 - used by iOS power management
extern void hci_transport_h4_iphone_set_enforce_wake_device(char *path);
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,298 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_h4_transport.c
*
* HCI Transport API implementation for basic H4 protocol over POSIX
*
* Created by Matthias Ringwald on 4/29/09.
*/
#include "config.h"
#include <termios.h> /* POSIX terminal control definitions */
#include <fcntl.h> /* File control definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include "debug.h"
#include "hci.h"
#include "hci_transport.h"
#include "hci_dump.h"
static int h4_process(struct data_source *ds);
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
static hci_uart_config_t *hci_uart_config;
typedef enum {
H4_W4_PACKET_TYPE,
H4_W4_EVENT_HEADER,
H4_W4_ACL_HEADER,
H4_W4_PAYLOAD,
} H4_STATE;
typedef struct hci_transport_h4 {
hci_transport_t transport;
data_source_t *ds;
int uart_fd; // different from ds->fd for HCI reader thread
/* power management support, e.g. used by iOS */
timer_source_t sleep_timer;
} hci_transport_h4_t;
// single instance
static hci_transport_h4_t * hci_transport_h4 = NULL;
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
// packet reader state machine
static H4_STATE h4_state;
static int bytes_to_read;
static int read_pos;
static uint8_t hci_packet[1+HCI_PACKET_BUFFER_SIZE]; // packet type + max(acl header + acl payload, event header + event data)
static int h4_open(void *transport_config){
hci_uart_config = (hci_uart_config_t*) transport_config;
struct termios toptions;
int flags = O_RDWR | O_NOCTTY;
int fd = open(hci_uart_config->device_name, flags);
if (fd == -1) {
perror("init_serialport: Unable to open port ");
perror(hci_uart_config->device_name);
return -1;
}
if (tcgetattr(fd, &toptions) < 0) {
perror("init_serialport: Couldn't get term attributes");
return -1;
}
speed_t brate = hci_uart_config->baudrate_init; // let you override switch below if needed
switch(hci_uart_config->baudrate_init) {
case 57600: brate=B57600; break;
case 115200: brate=B115200; break;
#ifdef B230400
case 230400: brate=B230400; break;
#endif
#ifdef B460800
case 460800: brate=B460800; break;
#endif
#ifdef B921600
case 921600: brate=B921600; break;
#endif
}
cfsetspeed(&toptions, brate);
// 8N1
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
if (hci_uart_config->flowcontrol) {
// with flow control
toptions.c_cflag |= CRTSCTS;
} else {
// no flow control
toptions.c_cflag &= ~CRTSCTS;
}
toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
toptions.c_oflag &= ~OPOST; // make raw
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
toptions.c_cc[VMIN] = 1;
toptions.c_cc[VTIME] = 0;
if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
perror("init_serialport: Couldn't set term attributes");
return -1;
}
// set up data_source
hci_transport_h4->ds = malloc(sizeof(data_source_t));
if (!hci_transport_h4->ds) return -1;
hci_transport_h4->uart_fd = fd;
hci_transport_h4->ds->fd = fd;
hci_transport_h4->ds->process = h4_process;
run_loop_add_data_source(hci_transport_h4->ds);
// init state machine
bytes_to_read = 1;
h4_state = H4_W4_PACKET_TYPE;
read_pos = 0;
return 0;
}
static int h4_close(void *transport_config){
// first remove run loop handler
run_loop_remove_data_source(hci_transport_h4->ds);
// close device
close(hci_transport_h4->ds->fd);
// free struct
free(hci_transport_h4->ds);
hci_transport_h4->ds = NULL;
return 0;
}
static int h4_send_packet(uint8_t packet_type, uint8_t * packet, int size){
if (hci_transport_h4->ds == NULL) return -1;
if (hci_transport_h4->uart_fd == 0) return -1;
hci_dump_packet( (uint8_t) packet_type, 0, packet, size);
char *data = (char*) packet;
int bytes_written = write(hci_transport_h4->uart_fd, &packet_type, 1);
while (bytes_written < 1) {
usleep(5000);
bytes_written = write(hci_transport_h4->uart_fd, &packet_type, 1);
};
while (size > 0) {
int bytes_written = write(hci_transport_h4->uart_fd, data, size);
if (bytes_written < 0) {
usleep(5000);
continue;
}
data += bytes_written;
size -= bytes_written;
}
return 0;
}
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
packet_handler = handler;
}
static void h4_deliver_packet(void){
if (read_pos < 3) return; // sanity check
hci_dump_packet( hci_packet[0], 1, &hci_packet[1], read_pos-1);
packet_handler(hci_packet[0], &hci_packet[1], read_pos-1);
h4_state = H4_W4_PACKET_TYPE;
read_pos = 0;
bytes_to_read = 1;
}
static void h4_statemachine(void){
switch (h4_state) {
case H4_W4_PACKET_TYPE:
if (hci_packet[0] == HCI_EVENT_PACKET){
bytes_to_read = HCI_EVENT_HEADER_SIZE;
h4_state = H4_W4_EVENT_HEADER;
} else if (hci_packet[0] == HCI_ACL_DATA_PACKET){
bytes_to_read = HCI_ACL_HEADER_SIZE;
h4_state = H4_W4_ACL_HEADER;
} else {
log_error("h4_process: invalid packet type 0x%02x\n", hci_packet[0]);
read_pos = 0;
bytes_to_read = 1;
}
break;
case H4_W4_EVENT_HEADER:
bytes_to_read = hci_packet[2];
h4_state = H4_W4_PAYLOAD;
break;
case H4_W4_ACL_HEADER:
bytes_to_read = READ_BT_16( hci_packet, 3);
h4_state = H4_W4_PAYLOAD;
break;
case H4_W4_PAYLOAD:
h4_deliver_packet();
break;
default:
break;
}
}
static int h4_process(struct data_source *ds) {
if (hci_transport_h4->uart_fd == 0) return -1;
int read_now = bytes_to_read;
// read up to bytes_to_read data in
ssize_t bytes_read = read(hci_transport_h4->uart_fd, &hci_packet[read_pos], read_now);
// printf("h4_process: bytes read %u\n", bytes_read);
if (bytes_read < 0) {
return bytes_read;
}
// hexdump(&hci_packet[read_pos], bytes_read);
bytes_to_read -= bytes_read;
read_pos += bytes_read;
if (bytes_to_read > 0) {
return 0;
}
h4_statemachine();
return 0;
}
static const char * h4_get_transport_name(void){
return "H4";
}
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
}
// get h4 singleton
hci_transport_t * hci_transport_h4_instance() {
if (hci_transport_h4 == NULL) {
hci_transport_h4 = malloc( sizeof(hci_transport_h4_t));
hci_transport_h4->ds = NULL;
hci_transport_h4->transport.open = h4_open;
hci_transport_h4->transport.close = h4_close;
hci_transport_h4->transport.send_packet = h4_send_packet;
hci_transport_h4->transport.register_packet_handler = h4_register_packet_handler;
hci_transport_h4->transport.get_transport_name = h4_get_transport_name;
hci_transport_h4->transport.set_baudrate = NULL;
hci_transport_h4->transport.can_send_packet_now = NULL;
}
return (hci_transport_t *) hci_transport_h4;
}

View File

@ -0,0 +1,313 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_h4_transport_dma.c
*
* HCI Transport implementation for basic H4 protocol for blocking UART write and IRQ-driven blockwise RX
*
* Created by Matthias Ringwald on 4/29/09.
*/
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "hci.h"
#include "hci_dump.h"
#include "hci_transport.h"
#include <btstack/run_loop.h>
#include <btstack/hal_uart_dma.h>
typedef enum {
H4_W4_PACKET_TYPE = 1,
H4_W4_EVENT_HEADER,
H4_W4_ACL_HEADER,
H4_W4_PAYLOAD,
H4_PACKET_RECEIVED
} H4_STATE;
typedef enum {
TX_IDLE = 1,
TX_W4_HEADER_SENT,
TX_W4_PACKET_SENT,
TX_DONE
} TX_STATE;
typedef struct hci_transport_h4 {
hci_transport_t transport;
data_source_t *ds;
} hci_transport_h4_t;
// prototypes
static int h4_process(struct data_source *ds);
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
static void h4_block_received(void);
static void h4_block_sent(void);
static int h4_open(void *transport_config);
static int h4_close();
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
static int h4_send_packet(uint8_t packet_type, uint8_t *packet, int size);
static const char * h4_get_transport_name();
static int h4_set_baudrate(uint32_t baudrate);
static int h4_can_send_packet_now(uint8_t packet_type);
// packet reader state machine
static H4_STATE h4_state;
static int read_pos;
static int bytes_to_read;
static uint8_t hci_packet[HCI_PACKET_BUFFER_SIZE]; // bigger than largest packet
// tx state
static TX_STATE tx_state;
static uint8_t *tx_data;
static uint16_t tx_len;
// static hci_transport_h4_t * hci_transport_h4 = NULL;
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
static data_source_t hci_transport_h4_dma_ds = {
.process = h4_process
};
static hci_transport_h4_t hci_transport_h4_dma = {
.ds = &hci_transport_h4_dma_ds,
.transport.open = h4_open,
.transport.close = h4_close,
.transport.send_packet = h4_send_packet,
.transport.register_packet_handler = h4_register_packet_handler,
.transport.get_transport_name = h4_get_transport_name,
.transport.set_baudrate = h4_set_baudrate,
.transport.can_send_packet_now = h4_can_send_packet_now
};
static void h4_init_sm(void){
h4_state = H4_W4_PACKET_TYPE;
read_pos = 0;
bytes_to_read = 1;
hal_uart_dma_receive_block(hci_packet, bytes_to_read);
}
static int h4_open(void *transport_config){
// open uart
hal_uart_dma_init();
hal_uart_dma_set_block_received(h4_block_received);
hal_uart_dma_set_block_sent(h4_block_sent);
// set up data_source
run_loop_add_data_source(&hci_transport_h4_dma_ds);
//
h4_init_sm();
tx_state = TX_IDLE;
return 0;
}
static int h4_close(){
// first remove run loop handler
run_loop_remove_data_source(&hci_transport_h4_dma_ds);
// close device
// ...
return 0;
}
static void h4_block_received(void){
read_pos += bytes_to_read;
// act
switch (h4_state) {
case H4_W4_PACKET_TYPE:
switch (hci_packet[0]) {
case HCI_ACL_DATA_PACKET:
h4_state = H4_W4_ACL_HEADER;
bytes_to_read = HCI_ACL_HEADER_SIZE;
break;
case HCI_EVENT_PACKET:
h4_state = H4_W4_EVENT_HEADER;
bytes_to_read = HCI_EVENT_HEADER_SIZE;
break;
default:
log_error("h4_process: invalid packet type 0x%02x\r\n", hci_packet[0]);
read_pos = 0;
h4_state = H4_W4_PACKET_TYPE;
bytes_to_read = 1;
break;
}
break;
case H4_W4_EVENT_HEADER:
bytes_to_read = hci_packet[2];
if (bytes_to_read == 0) {
h4_state = H4_PACKET_RECEIVED;
break;
}
h4_state = H4_W4_PAYLOAD;
break;
case H4_W4_ACL_HEADER:
bytes_to_read = READ_BT_16( hci_packet, 3);
if (bytes_to_read == 0) {
h4_state = H4_PACKET_RECEIVED;
break;
}
h4_state = H4_W4_PAYLOAD;
break;
case H4_W4_PAYLOAD:
h4_state = H4_PACKET_RECEIVED;
bytes_to_read = 0;
// trigger run loop
embedded_trigger();
break;
default:
bytes_to_read = 0;
break;
}
// read next block
if (bytes_to_read) {
hal_uart_dma_receive_block(&hci_packet[read_pos], bytes_to_read);
}
}
static void h4_block_sent(void){
switch (tx_state){
case TX_W4_HEADER_SENT:
tx_state = TX_W4_PACKET_SENT;
// h4 packet type + actual packet
hal_uart_dma_send_block(tx_data, tx_len);
break;
case TX_W4_PACKET_SENT:
tx_state = TX_DONE;
// trigger run loop
embedded_trigger();
break;
default:
break;
}
}
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
packet_handler = handler;
}
// #define DUMP
#ifdef DUMP
static void dump(uint8_t *data, uint16_t len){
int i;
for (i=0; i<len;i++){
printf("%02X ", ((uint8_t *)data)[i]);
}
printf("\n\r");
}
#endif
static int h4_process(struct data_source *ds) {
// notify about packet sent
if (tx_state == TX_DONE){
// reset state
tx_state = TX_IDLE;
uint8_t event = DAEMON_EVENT_HCI_PACKET_SENT;
packet_handler(HCI_EVENT_PACKET, &event, 1);
}
if (h4_state != H4_PACKET_RECEIVED) return 0;
// log packet
#ifdef DUMP
printf("RX: ");
dump(hci_packet, read_pos);
#endif
packet_handler(hci_packet[0], &hci_packet[1], read_pos-1);
h4_init_sm();
return 0;
}
static int h4_send_packet(uint8_t packet_type, uint8_t *packet, int size){
// write in progress
if (tx_state != TX_IDLE) {
log_error("h4_send_packet with tx_state = %u, type %u, data %02x %02x %02x", tx_state, packet_type, packet[0], packet[1], packet[2]);
return -1;
}
#ifdef DUMP
printf("TX: %02x ", packet_type);
dump(packet, size);
#endif
tx_data = packet;
tx_len = size;
tx_state = TX_W4_HEADER_SENT;
hal_uart_dma_send_block(&packet_type, 1);
return 0;
}
static int h4_set_baudrate(uint32_t baudrate){
printf("h4_set_baudrate - set baud %lu\n\r", baudrate);
return hal_uart_dma_set_baud(baudrate);
}
static int h4_can_send_packet_now(uint8_t packet_type){
return tx_state == TX_IDLE;
}
static const char * h4_get_transport_name(){
return "H4_DMA";
}
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
}
// get h4 singleton
hci_transport_t * hci_transport_h4_dma_instance() {
return (hci_transport_t *) &hci_transport_h4_dma;
}

View File

@ -0,0 +1,508 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_h4_transport_dma.c
*
* HCI Transport implementation of H4 protocol with eHCILL support
* for IRQ-driven blockwise RX and TX, and IRQ callback on CTS toggle
*
* Based on information found at http://e2e.ti.com/support/low_power_rf/f/660/t/134855.aspx
*
* Created by Matthias Ringwald on 9/16/11.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "hci.h"
#include "hci_dump.h"
#include "hci_transport.h"
#include <btstack/run_loop.h>
#include <btstack/hal_uart_dma.h>
// #define DUMP
// eHCILL commands (+interal CTS signal)
#define EHCILL_GO_TO_SLEEP_IND 0x030
#define EHCILL_GO_TO_SLEEP_ACK 0x031
#define EHCILL_WAKE_UP_IND 0x032
#define EHCILL_WAKE_UP_ACK 0x033
#define EHCILL_CTS_SIGNAL 0x034
typedef enum {
H4_W4_PACKET_TYPE = 1,
H4_W4_EVENT_HEADER,
H4_W4_ACL_HEADER,
H4_W4_PAYLOAD,
H4_PACKET_RECEIVED
} H4_STATE;
typedef enum {
TX_IDLE = 1,
TX_W4_WAKEUP, // eHCILL only
TX_W4_HEADER_SENT,
TX_W4_PACKET_SENT,
TX_W4_EHCILL_SENT,
TX_DONE
} TX_STATE;
typedef enum {
EHCILL_STATE_SLEEP = 1,
EHCILL_STATE_W4_ACK,
EHCILL_STATE_AWAKE
} EHCILL_STATE;
typedef struct hci_transport_h4 {
hci_transport_t transport;
data_source_t *ds;
} hci_transport_h4_t;
// prototypes
static int h4_process(struct data_source *ds);
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
static void h4_block_received(void);
static void h4_block_sent(void);
static int h4_open(void *transport_config);
static int h4_close();
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
static const char * h4_get_transport_name();
static int h4_set_baudrate(uint32_t baudrate);
static int h4_can_send_packet_now(uint8_t packet_type);
static int ehcill_send_packet(uint8_t packet_type, uint8_t *packet, int size);
static void ehcill_uart_dma_receive_block(uint8_t *buffer, uint16_t size);
static int ehcill_sleep_mode_active(void);
static void ehcill_handle(uint8_t action);
static void ehcill_cts_irq_handler();
// eCHILL: state machine
static EHCILL_STATE ehcill_state = EHCILL_STATE_AWAKE;
static uint8_t * ehcill_defer_rx_buffer;
static uint16_t ehcill_defer_rx_size = 0;
static uint8_t ehcill_command_to_send;
// H4: packet reader state machine
static H4_STATE h4_state;
static int read_pos;
static int bytes_to_read;
static uint8_t hci_packet[HCI_PACKET_BUFFER_SIZE]; // bigger than largest packet
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
// H4: tx state
static TX_STATE tx_state;
static uint8_t * tx_data;
static uint16_t tx_len;
static uint8_t tx_packet_type;
// data source used in run_loop
static data_source_t hci_transport_h4_dma_ds = {
.process = h4_process
};
// hci_transport for use by hci
static const hci_transport_h4_t hci_transport_h4_ehcill_dma = {
.ds = &hci_transport_h4_dma_ds,
.transport.open = h4_open,
.transport.close = h4_close,
.transport.send_packet = ehcill_send_packet,
.transport.register_packet_handler = h4_register_packet_handler,
.transport.get_transport_name = h4_get_transport_name,
.transport.set_baudrate = h4_set_baudrate,
.transport.can_send_packet_now = h4_can_send_packet_now
};
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
}
static const char * h4_get_transport_name(){
return "H4_EHCILL_DMA";
}
// get h4 singleton
hci_transport_t * hci_transport_h4_dma_instance() {
return (hci_transport_t *) &hci_transport_h4_ehcill_dma;
}
static void h4_rx_init_sm(void){
h4_state = H4_W4_PACKET_TYPE;
read_pos = 0;
bytes_to_read = 1;
ehcill_uart_dma_receive_block(hci_packet, bytes_to_read);
}
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
packet_handler = handler;
}
static int h4_open(void *transport_config){
// open uart
hal_uart_dma_init();
hal_uart_dma_set_block_received(h4_block_received);
hal_uart_dma_set_block_sent(h4_block_sent);
hal_uart_dma_set_csr_irq_handler(ehcill_cts_irq_handler);
// set up data_source
run_loop_add_data_source(&hci_transport_h4_dma_ds);
// init state machiens
h4_rx_init_sm();
tx_state = TX_IDLE;
return 0;
}
static int h4_set_baudrate(uint32_t baudrate){
log_info("h4_set_baudrate - set baud %lu\n", baudrate);
return hal_uart_dma_set_baud(baudrate);
}
static int h4_close(){
// first remove run loop handler
run_loop_remove_data_source(&hci_transport_h4_dma_ds);
// stop IRQ
hal_uart_dma_set_csr_irq_handler(NULL);
// close device
// ...
return 0;
}
static void h4_block_received(void){
read_pos += bytes_to_read;
// act
switch (h4_state) {
case H4_W4_PACKET_TYPE:
switch (hci_packet[0]) {
case HCI_ACL_DATA_PACKET:
h4_state = H4_W4_ACL_HEADER;
bytes_to_read = HCI_ACL_HEADER_SIZE;
break;
case HCI_EVENT_PACKET:
h4_state = H4_W4_EVENT_HEADER;
bytes_to_read = HCI_EVENT_HEADER_SIZE;
break;
case EHCILL_GO_TO_SLEEP_IND:
case EHCILL_GO_TO_SLEEP_ACK:
case EHCILL_WAKE_UP_IND:
case EHCILL_WAKE_UP_ACK:
ehcill_handle(hci_packet[0]);
read_pos = 0;
bytes_to_read = 1;
break;
default:
log_error("h4_process: invalid packet type 0x%02x\n", hci_packet[0]);
read_pos = 0;
bytes_to_read = 1;
break;
}
break;
case H4_W4_EVENT_HEADER:
bytes_to_read = hci_packet[2];
if (bytes_to_read) {
h4_state = H4_W4_PAYLOAD;
break;
}
h4_state = H4_PACKET_RECEIVED;
break;
case H4_W4_ACL_HEADER:
bytes_to_read = READ_BT_16( hci_packet, 3);
if (bytes_to_read) {
h4_state = H4_W4_PAYLOAD;
break;
}
h4_state = H4_PACKET_RECEIVED;
break;
case H4_W4_PAYLOAD:
h4_state = H4_PACKET_RECEIVED;
bytes_to_read = 0;
// trigger run loop - necessary for use in low power modes
embedded_trigger();
break;
default:
bytes_to_read = 0;
break;
}
// read next block
if (bytes_to_read) {
ehcill_uart_dma_receive_block(&hci_packet[read_pos], bytes_to_read);
}
}
static int h4_can_send_packet_now(uint8_t packet_type){
return tx_state == TX_IDLE;
}
static void h4_block_sent(void){
switch (tx_state){
case TX_W4_HEADER_SENT:
tx_state = TX_W4_PACKET_SENT;
// h4 packet type + actual packet
hal_uart_dma_send_block(tx_data, tx_len);
break;
case TX_W4_PACKET_SENT:
// send pending ehcill command
if (ehcill_command_to_send){
tx_state = TX_W4_EHCILL_SENT;
hal_uart_dma_send_block(&ehcill_command_to_send, 1);
break;
}
tx_state = TX_DONE;
// trigger run loop
embedded_trigger();
break;
case TX_W4_EHCILL_SENT:
if (ehcill_command_to_send == EHCILL_GO_TO_SLEEP_ACK) {
// UART not needed after EHCILL_GO_TO_SLEEP_ACK was sent
hal_uart_dma_set_sleep(1);
}
ehcill_command_to_send = 0;
tx_state = TX_DONE;
// trigger run loop
embedded_trigger();
break;
default:
break;
}
}
#ifdef DUMP
static void dump(uint8_t *data, uint16_t len){
int i;
if (len > 5) len = 8;
for (i=0; i<len;i++){
printf("%02X ", ((uint8_t *)data)[i]);
}
printf("\n\r");
}
#endif
static int h4_process(struct data_source *ds) {
// notify about packet sent
if (tx_state == TX_DONE){
// reset state
tx_state = TX_IDLE;
uint8_t event = DAEMON_EVENT_HCI_PACKET_SENT;
packet_handler(HCI_EVENT_PACKET, &event, 1);
}
if (h4_state != H4_PACKET_RECEIVED) return 0;
// log packet
#ifdef DUMP
printf("RX: ");
dump(hci_packet, read_pos);
#endif
packet_handler(hci_packet[0], &hci_packet[1], read_pos-1);
h4_rx_init_sm();
return 0;
}
//////////////////////////
int ehcill_sleep_mode_active(void){
return ehcill_state == EHCILL_STATE_SLEEP;
}
static void ehcill_cts_irq_handler(){
ehcill_handle(EHCILL_CTS_SIGNAL);
}
static void ehcill_schedule_ecill_command(uint8_t command){
// reply with HCILL_SLEEP_ACK
ehcill_command_to_send = command;
switch (tx_state){
case TX_IDLE:
case TX_DONE:
tx_state = TX_W4_EHCILL_SENT;
hal_uart_dma_send_block(&ehcill_command_to_send, 1);
break;
default:
break;
}
}
static void ehcill_handle(uint8_t action){
int size;
// printf("ehcill_handle: %x, state %u, defer_rx %u \n\r", action, ehcill_state, ehcill_defer_rx_size);
switch(ehcill_state){
case EHCILL_STATE_AWAKE:
switch(action){
case EHCILL_GO_TO_SLEEP_IND:
// 1. set RTS high - already done by BT RX ISR
// 2. enable CTS - CTS always enabled
ehcill_state = EHCILL_STATE_SLEEP;
log_info("RX: EHCILL_GO_TO_SLEEP_IND\n");
ehcill_schedule_ecill_command(EHCILL_GO_TO_SLEEP_ACK);
break;
default:
break;
}
break;
case EHCILL_STATE_SLEEP:
switch(action){
case EHCILL_CTS_SIGNAL:
// re-activate rx (also clears RTS)
if (!ehcill_defer_rx_size) break;
// UART needed again
hal_uart_dma_set_sleep(0);
log_info ("Re-activate rx\n");
size = ehcill_defer_rx_size;
ehcill_defer_rx_size = 0;
hal_uart_dma_receive_block(ehcill_defer_rx_buffer, size);
break;
case EHCILL_WAKE_UP_IND:
ehcill_state = EHCILL_STATE_AWAKE;
log_info("RX: EHCILL_GO_TO_SLEEP_IND\n");
ehcill_schedule_ecill_command(EHCILL_WAKE_UP_ACK);
break;
default:
break;
}
break;
case EHCILL_STATE_W4_ACK:
switch(action){
case EHCILL_WAKE_UP_IND:
case EHCILL_WAKE_UP_ACK:
log_info("RX: EHCILL_WAKE_UP_IND or ACK\n");
tx_state = TX_W4_HEADER_SENT;
hal_uart_dma_send_block(&tx_packet_type, 1);
ehcill_state = EHCILL_STATE_AWAKE;
break;
default:
break;
}
break;
}
}
static int ehcill_send_packet(uint8_t packet_type, uint8_t *packet, int size){
// write in progress
if (tx_state != TX_IDLE) {
log_error("h4_send_packet with tx_state = %u, type %u, data %02x %02x %02x\n", tx_state, packet_type, packet[0], packet[1], packet[2]);
return -1;
}
#ifdef DUMP
printf("TX: %02x ", packet_type);
dump(packet, size);
#endif
tx_packet_type = packet_type;
tx_data = packet;
tx_len = size;
if (!ehcill_sleep_mode_active()){
tx_state = TX_W4_HEADER_SENT;
hal_uart_dma_send_block(&packet_type, 1);
return 0;
}
// UART needed again
hal_uart_dma_set_sleep(0);
// update state
tx_state = TX_W4_WAKEUP;
ehcill_state = EHCILL_STATE_W4_ACK;
// wake up
log_info("RX: SLEEP\n");
log_info("TX: EHCILL_WAKE_UP_IND\n");
ehcill_command_to_send = EHCILL_WAKE_UP_IND;
hal_uart_dma_send_block(&ehcill_command_to_send, 1);
if (!ehcill_defer_rx_size){
log_error("ERROR: NO RX REQUEST PENDING\n");
return 0;
}
// receive request, clears RTS
int rx_size = ehcill_defer_rx_size;
ehcill_defer_rx_size = 0;
hal_uart_dma_receive_block(ehcill_defer_rx_buffer, rx_size);
return 0;
}
void ehcill_uart_dma_receive_block(uint8_t *buffer, uint16_t size){
if (!ehcill_sleep_mode_active()){
ehcill_defer_rx_size = 0;
hal_uart_dma_receive_block(buffer, size);
return;
}
// store receive request for later
ehcill_defer_rx_buffer = buffer;
ehcill_defer_rx_size = size;
}

View File

@ -0,0 +1,379 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_h4_transport_iphone.c
*
* HCI Transport API implementation for basic H4 protocol
* with extension for "enforced wake device" using the netgraph styele
* interface to the Bluetooth UART on iOS
*
* This is only tested on newer devices with Broadcom chipsets
*
*/
#include "config.h"
#define SOCKET_DEVICE "com.apple.uart.bluetooth"
#include <sys/socket.h>
#include <sys/ioctl.h>
#ifndef PF_NETGRAPH
#define PF_NETGRAPH 32 /* normally in sys/socket.h */
#endif
#define NG_CONTROL 2 /* from freeBSD sys/ng_socket.h */
#define SOCK_IOCTL_VAL 0xC0644E03 /* from reversing BTServer */
#define GETSOCKOPT_VAL 0x402C7413 /* from reversing BTServer */
#define SETSOCKOPT_VAL 0x802c7414 /* from reversing BTServer */
// don't enforce wake after 3s idle
#define HCI_WAKE_TIMER_MS 3000
#define HCI_WAKE_DURATION 10000
#include <termios.h> /* POSIX terminal control definitions */
#include <fcntl.h> /* File control definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include "debug.h"
#include "hci.h"
#include "hci_transport.h"
#include "hci_dump.h"
static int h4_process(struct data_source *ds);
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
static hci_uart_config_t *hci_uart_config;
static void h4_enforce_wake_on(void);
static void h4_enforce_wake_off(void);
static void h4_enforce_wake_timeout(struct timer *ts);
typedef enum {
H4_W4_PACKET_TYPE,
H4_W4_EVENT_HEADER,
H4_W4_ACL_HEADER,
H4_W4_PAYLOAD,
} H4_STATE;
typedef struct hci_transport_h4 {
hci_transport_t transport;
data_source_t *ds;
int uart_fd; // different from ds->fd for HCI reader thread
/* power management support, e.g. used by iOS */
timer_source_t sleep_timer;
} hci_transport_h4_t;
/* NG sockaddr definition */
struct sockaddr_ng {
uint8_t sg_len; /* total length */
uint8_t sg_family; /* address family */
uint16_t sg_subtype;
uint32_t sg_node;
uint32_t sg_null;
uint8_t sg_dummy[20];
};
// single instance
static hci_transport_h4_t * hci_transport_h4 = NULL;
// enforced wake support
static char * enforce_wake_device = NULL;
static int enforce_wake_fd = 0;
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
// packet reader state machine
static H4_STATE h4_state;
static int bytes_to_read;
static int read_pos;
static uint8_t hci_packet[1+HCI_PACKET_BUFFER_SIZE]; // packet type + max(acl header + acl payload, event header + event data)
static int h4_open(void *transport_config)
{
hci_uart_config = (hci_uart_config_t *) transport_config;
int fd = socket(PF_NETGRAPH, SOCK_STREAM, NG_CONTROL);
if (fd < 0) {
perror("socket(HCI_IF)");
goto err_out0;
}
// get node address
struct ioctl_arg_t {
uint32_t result;
char socket_name[96];
} ioctl_arg;
memset((void *) &ioctl_arg, 0x00, sizeof(struct ioctl_arg_t));
strcpy((char *) &ioctl_arg.socket_name, SOCKET_DEVICE);
if (ioctl(fd, SOCK_IOCTL_VAL, &ioctl_arg) != 0) {
perror("ioctl(fd_sock, SOCK_IOCTL_VAL)");
goto err_out1;
}
// setup sock addr struct
struct sockaddr_ng sock_addr;
sock_addr.sg_len = sizeof(struct sockaddr_ng);
sock_addr.sg_family = PF_NETGRAPH;
sock_addr.sg_subtype = 0x02;
sock_addr.sg_node = ioctl_arg.result;
sock_addr.sg_null = 0;
// connect
if (connect(fd, (const struct sockaddr *) &sock_addr, sizeof(struct sockaddr_ng)) != 0) {
perror("connect(fd_sock)");
goto err_out2;
}
// configure UART
struct termios toptions;
socklen_t toptions_len = sizeof(struct termios);
if (getsockopt(fd, SO_ACCEPTCONN, GETSOCKOPT_VAL, &toptions, &toptions_len) != 0) {
perror("getsockopt(fd_sock)");
goto err_out3;
}
cfmakeraw(&toptions);
speed_t brate = (speed_t) hci_uart_config->baudrate_init;
cfsetspeed(&toptions, brate);
toptions.c_iflag |= IGNPAR;
toptions.c_cflag = 0x00038b00;
if (setsockopt(fd, SO_ACCEPTCONN, SETSOCKOPT_VAL, &toptions, toptions_len) != 0) {
perror("setsockopt(fd_sock)");
goto err_out4;
}
// set up data_source
hci_transport_h4->ds = malloc(sizeof(data_source_t));
if (!hci_transport_h4->ds) return -1;
hci_transport_h4->uart_fd = fd;
hci_transport_h4->ds->fd = fd;
hci_transport_h4->ds->process = h4_process;
run_loop_add_data_source(hci_transport_h4->ds);
// init state machine
bytes_to_read = 1;
h4_state = H4_W4_PACKET_TYPE;
read_pos = 0;
return 0;
err_out4:
err_out3:
err_out2:
err_out1:
close(fd);
err_out0:
fprintf(stderr, "h4_open error\n");
return -1;
}
static int h4_close(){
// first remove run loop handler
run_loop_remove_data_source(hci_transport_h4->ds);
// close device
close(hci_transport_h4->ds->fd);
// let module sleep
h4_enforce_wake_off();
// free struct
free(hci_transport_h4->ds);
hci_transport_h4->ds = NULL;
return 0;
}
static int h4_send_packet(uint8_t packet_type, uint8_t * packet, int size){
if (hci_transport_h4->ds == NULL) return -1;
if (hci_transport_h4->uart_fd == 0) return -1;
// wake Bluetooth module
h4_enforce_wake_on();
hci_dump_packet( (uint8_t) packet_type, 0, packet, size);
char *data = (char*) packet;
int bytes_written = write(hci_transport_h4->uart_fd, &packet_type, 1);
while (bytes_written < 1) {
usleep(5000);
bytes_written = write(hci_transport_h4->uart_fd, &packet_type, 1);
};
while (size > 0) {
int bytes_written = write(hci_transport_h4->uart_fd, data, size);
if (bytes_written < 0) {
usleep(5000);
continue;
}
data += bytes_written;
size -= bytes_written;
}
return 0;
}
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
packet_handler = handler;
}
static void h4_deliver_packet(void){
if (read_pos < 3) return; // sanity check
hci_dump_packet( hci_packet[0], 1, &hci_packet[1], read_pos-1);
packet_handler(hci_packet[0], &hci_packet[1], read_pos-1);
h4_state = H4_W4_PACKET_TYPE;
read_pos = 0;
bytes_to_read = 1;
}
static void h4_statemachine(void){
switch (h4_state) {
case H4_W4_PACKET_TYPE:
if (hci_packet[0] == HCI_EVENT_PACKET){
bytes_to_read = HCI_EVENT_HEADER_SIZE;
h4_state = H4_W4_EVENT_HEADER;
} else if (hci_packet[0] == HCI_ACL_DATA_PACKET){
bytes_to_read = HCI_ACL_HEADER_SIZE;
h4_state = H4_W4_ACL_HEADER;
} else {
log_error("h4_process: invalid packet type 0x%02x\n", hci_packet[0]);
read_pos = 0;
bytes_to_read = 1;
}
break;
case H4_W4_EVENT_HEADER:
bytes_to_read = hci_packet[2];
h4_state = H4_W4_PAYLOAD;
break;
case H4_W4_ACL_HEADER:
bytes_to_read = READ_BT_16( hci_packet, 3);
h4_state = H4_W4_PAYLOAD;
break;
case H4_W4_PAYLOAD:
h4_deliver_packet();
break;
default:
break;
}
}
static int h4_process(struct data_source *ds) {
if (hci_transport_h4->uart_fd == 0) return -1;
int read_now = bytes_to_read;
// if (read_now > 100) {
// read_now = 100;
// }
// read up to bytes_to_read data in
ssize_t bytes_read = read(hci_transport_h4->uart_fd, &hci_packet[read_pos], read_now);
// printf("h4_process: bytes read %u\n", bytes_read);
if (bytes_read < 0) {
return bytes_read;
}
// hexdump(&hci_packet[read_pos], bytes_read);
bytes_to_read -= bytes_read;
read_pos += bytes_read;
if (bytes_to_read > 0) {
return 0;
}
h4_statemachine();
return 0;
}
static void h4_enforce_wake_on(void)
{
if (!enforce_wake_device) return;
if (!enforce_wake_fd) {
enforce_wake_fd = open(enforce_wake_device, O_RDWR);
usleep(HCI_WAKE_DURATION); // wait until device is ready
}
run_loop_remove_timer(&hci_transport_h4->sleep_timer);
run_loop_set_timer(&hci_transport_h4->sleep_timer, HCI_WAKE_TIMER_MS);
hci_transport_h4->sleep_timer.process = h4_enforce_wake_timeout;
run_loop_add_timer(&hci_transport_h4->sleep_timer);
}
static void h4_enforce_wake_off(void)
{
run_loop_remove_timer(&hci_transport_h4->sleep_timer);
if (enforce_wake_fd) {
close(enforce_wake_fd);
enforce_wake_fd = 0;
}
}
static void h4_enforce_wake_timeout(struct timer *ts)
{
h4_enforce_wake_off();
}
static const char * h4_get_transport_name(void){
return "H4_IPHONE";
}
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
}
// get h4 singleton
hci_transport_t * hci_transport_h4_iphone_instance() {
if (hci_transport_h4 == NULL) {
hci_transport_h4 = malloc( sizeof(hci_transport_h4_t));
hci_transport_h4->ds = NULL;
hci_transport_h4->transport.open = h4_open;
hci_transport_h4->transport.close = h4_close;
hci_transport_h4->transport.send_packet = h4_send_packet;
hci_transport_h4->transport.register_packet_handler = h4_register_packet_handler;
hci_transport_h4->transport.get_transport_name = h4_get_transport_name;
hci_transport_h4->transport.set_baudrate = NULL;
hci_transport_h4->transport.can_send_packet_now = NULL;
}
return (hci_transport_t *) hci_transport_h4;
}
void hci_transport_h4_iphone_set_enforce_wake_device(char *path){
enforce_wake_device = path;
}

View File

@ -0,0 +1,313 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_transport_h5.c
*
* HCI Transport API implementation for basic H5 protocol
*
* Created by Matthias Ringwald on 4/29/09.
*/
#include <termios.h> /* POSIX terminal control definitions */
#include <fcntl.h> /* File control definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <stdio.h>
#include <string.h>
#include "hci.h"
#include "hci_transport.h"
#include "hci_dump.h"
typedef struct hci_transport_h5 {
hci_transport_t transport;
data_source_t *ds;
} hci_transport_h5_t;
// h5 slip state machine
typedef enum {
unknown = 1,
decoding,
x_c0,
x_db
} H5_STATE;
typedef struct h5_slip {
state_t state;
uint16_t length;
uint8_t data[HCI_ACL_BUFFER_SIZE];
} h5_slip_t;
// Global State
static h5_slip_t read_sm;
static h5_slip_t write_sm;
static int bt_filedesc = 0;
// single instance
static hci_transport_h5_t * hci_transport_h5 = NULL;
static int h5_process(struct data_source *ds);
static void dummy_handler(uint8_t packet_type, uint8_t *packet, int size);
static hci_uart_config_t *hci_uart_config;
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, int size) = dummy_handler;
// prototypes
static int h5_open(void *transport_config){
hci_uart_config = (hci_uart_config_t*) transport_config;
struct termios toptions;
int fd = open(hci_uart_config->device_name, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("init_serialport: Unable to open port ");
perror(hci_uart_config->device_name);
return -1;
}
if (tcgetattr(fd, &toptions) < 0) {
perror("init_serialport: Couldn't get term attributes");
return -1;
}
speed_t brate = hci_uart_config->baudrate; // let you override switch below if needed
switch(hci_uart_config->baudrate) {
case 57600: brate=B57600; break;
case 115200: brate=B115200; break;
#ifdef B230400
case 230400: brate=B230400; break;
#endif
#ifdef B460800
case 460800: brate=B460800; break;
#endif
#ifdef B921600
case 921600: brate=B921600; break;
#endif
}
cfsetispeed(&toptions, brate);
cfsetospeed(&toptions, brate);
// 8N1
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
if (hci_uart_config->flowcontrol) {
// with flow control
toptions.c_cflag |= CRTSCTS;
} else {
// no flow control
toptions.c_cflag &= ~CRTSCTS;
}
toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
toptions.c_oflag &= ~OPOST; // make raw
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
toptions.c_cc[VMIN] = 1;
toptions.c_cc[VTIME] = 0;
if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
perror("init_serialport: Couldn't set term attributes");
return -1;
}
// set up data_source
hci_transport_h5->ds = malloc(sizeof(data_source_t));
if (!hci_transport_h5) return -1;
hci_transport_h5->ds->fd = fd;
hci_transport_h5->ds->process = h5_process;
run_loop_add_data_source(hci_transport_h5->ds);
// init state machine
h5_slip_init( &read_sm);
h5_slip_init( &write_sm);
return 0;
}
static int h5_close(){
// first remove run loop handler
run_loop_remove_data_source(hci_transport_h5->ds);
// close device
close(hci_transport_h5->ds->fd);
free(hci_transport_h5->ds);
// free struct
hci_transport_h5->ds = NULL;
return 0;
}
static int h5_send_packet(uint8_t packet_type, uint8_t *packet, int size){
if (hci_transport_h5->ds == NULL) return -1;
if (hci_transport_h5->ds->fd == 0) return -1;
char *data = (char*) packet;
hci_dump_packet( (uint8_t) packet_type, 0, packet, size);
write(hci_transport_h5->ds->fd, &packet_type, 1);
while (size > 0) {
int bytes_written = write(hci_transport_h5->ds->fd, data, size);
if (bytes_written < 0) {
usleep(5000);
continue;
}
data += bytes_written;
size -= bytes_written;
}
return 0;
}
static void h5_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, int size)){
packet_handler = handler;
}
static void h5_slip_init( h5_slip_t * sm){
sm->state = unknown;
}
static void h5_slip_process( h5_slip_t * sm, uint8_t input, uint8_t in){
uint8_t type;
switch (sm->state) {
case unknown:
if (input == 0xc0){
sm->length = 0;
sm->state = x_c0;
}
break;
case x_c0:
switch (input ){
case 0xc0:
break;
case 0xdb:
sm->state = x_db;
break;
default:
sm->data[sm->length] = input;
++sm->length;
sm->state = decoding;
break;
}
break;
case decoding:
switch (input ){
case 0xc0:
sm->state = unknown;
// packet done - check if valid HCI packet
if (sm->length < 6) break;
type = sm->data[1] & 0x0f;
if (type < 1 || type > 4) break;
hci_dump_packet( type, in, &sm->data[4], sm->length-4-2); // -4 header, -2 crc for reliable
switch (type) {
case HCI_ACL_DATA_PACKET:
acl_packet_handler( &sm->data[4], sm->length-4-2);
break;
case HCI_EVENT_PACKET:
event_packet_handler( &sm->data[4], sm->length-4-2);
break;
default:
break;
}
break;
case 0xdb:
sm->state = x_db;
break;
default:
sm->data[sm->length] = input;
++sm->length;
break;
}
break;
case x_db:
switch (input) {
case 0xdc:
sm->data[sm->length] = 0xc0;
++sm->length;
sm->state = decoding;
break;
case 0xdd:
sm->data[sm->length] = 0xdb;
++sm->length;
sm->state = decoding;
break;
default:
sm->state = unknown;
break;
}
break;
default:
break;
}
}
static int h5_process(struct data_source *ds) {
if (hci_transport_h5->ds->fd == 0) return -1;
// read up to bytes_to_read data in
uint8_t data;
while (1) {
int bytes_read = read(hci_transport_h5->ds->fd, &data, 1);
if (bytes_read < 1) break;
h5_slip_process( &read_sm, data, 1);
};
return 0;
}
static const char * h5_get_transport_name(){
return "H5";
}
static void dummy_handler(uint8_t *packet, int size){
}
// get h5 singleton
hci_transport_t * hci_transport_h5_instance() {
if (hci_transport_h5 == NULL) {
hci_transport_h5 = malloc( sizeof(hci_transport_h5_t));
hci_transport_h5->ds = NULL;
hci_transport_h5->transport.open = h5_open;
hci_transport_h5->transport.close = h5_close;
hci_transport_h5->transport.send_packet = h5_send_packet;
hci_transport_h5->transport.register_packet_handler = h5_register_event_packet_handler;
hci_transport_h5->transport.get_transport_name = h5_get_transport_name;
hci_transport_h5->transport.set_baudrate = NULL;
hci_transport_h5->transport.can_send_packet_now = NULL;
}
return (hci_transport_t *) hci_transport_h5;
}

View File

@ -0,0 +1,573 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* hci_transport_usb.c
*
* HCI Transport API implementation for USB
*
* Created by Matthias Ringwald on 7/5/09.
*/
// delock bt class 2 - csr
// 0a12:0001 (bus 27, device 2)
// Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size
// HCI Commands 0 0 0x00 Control 8/16/32/64
// HCI Events 0 0 0x81 Interrupt (IN) 16
// ACL Data 0 0 0x82 Bulk (IN) 32/64
// ACL Data 0 0 0x02 Bulk (OUT) 32/64
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <unistd.h> /* UNIX standard function definitions */
#include <sys/types.h>
#include <libusb-1.0/libusb.h>
#include "config.h"
#include "debug.h"
#include "hci.h"
#include "hci_transport.h"
#include "hci_dump.h"
// prototypes
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
static int usb_close(void *transport_config);
enum {
LIB_USB_CLOSED = 0,
LIB_USB_OPENED,
LIB_USB_DEVICE_OPENDED,
LIB_USB_KERNEL_DETACHED,
LIB_USB_INTERFACE_CLAIMED,
LIB_USB_TRANSFERS_ALLOCATED
} libusb_state = LIB_USB_CLOSED;
// single instance
static hci_transport_t * hci_transport_usb = NULL;
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
// libusb
#if !USB_VENDOR_ID || !USB_PRODUCT_ID
static struct libusb_device_descriptor desc;
static libusb_device * dev;
#endif
static libusb_device_handle * handle;
#define ASYNC_BUFFERS 4
static struct libusb_transfer *event_in_transfer[ASYNC_BUFFERS];
static struct libusb_transfer *bulk_in_transfer[ASYNC_BUFFERS];
static uint8_t hci_event_in_buffer[ASYNC_BUFFERS][HCI_ACL_BUFFER_SIZE]; // bigger than largest packet
static uint8_t hci_bulk_in_buffer[ASYNC_BUFFERS][HCI_ACL_BUFFER_SIZE]; // bigger than largest packet
// For (ab)use as a linked list of received packets
static struct libusb_transfer *handle_packet;
static int doing_pollfds;
static timer_source_t usb_timer;
static int usb_timer_active;
// endpoint addresses
static int event_in_addr;
static int acl_in_addr;
static int acl_out_addr;
#if !USB_VENDOR_ID || !USB_PRODUCT_ID
void scan_for_bt_endpoints(void) {
int r;
// get endpoints from interface descriptor
struct libusb_config_descriptor *config_descriptor;
r = libusb_get_active_config_descriptor(dev, &config_descriptor);
log_info("configuration: %u interfaces\n", config_descriptor->bNumInterfaces);
const struct libusb_interface *interface = config_descriptor->interface;
const struct libusb_interface_descriptor * interface0descriptor = interface->altsetting;
log_info("interface 0: %u endpoints\n", interface0descriptor->bNumEndpoints);
const struct libusb_endpoint_descriptor *endpoint = interface0descriptor->endpoint;
for (r=0;r<interface0descriptor->bNumEndpoints;r++,endpoint++){
log_info("endpoint %x, attributes %x\n", endpoint->bEndpointAddress, endpoint->bmAttributes);
if ((endpoint->bmAttributes & 0x3) == LIBUSB_TRANSFER_TYPE_INTERRUPT){
event_in_addr = endpoint->bEndpointAddress;
log_info("Using 0x%2.2X for HCI Events\n", event_in_addr);
}
if ((endpoint->bmAttributes & 0x3) == LIBUSB_TRANSFER_TYPE_BULK){
if (endpoint->bEndpointAddress & 0x80) {
acl_in_addr = endpoint->bEndpointAddress;
log_info("Using 0x%2.2X for ACL Data In\n", acl_in_addr);
} else {
acl_out_addr = endpoint->bEndpointAddress;
log_info("Using 0x%2.2X for ACL Data Out\n", acl_out_addr);
}
}
}
}
static libusb_device * scan_for_bt_device(libusb_device **devs) {
int i = 0;
while ((dev = devs[i++]) != NULL) {
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
log_error("failed to get device descriptor");
return 0;
}
log_info("%04x:%04x (bus %d, device %d) - class %x subclass %x protocol %x \n",
desc.idVendor, desc.idProduct,
libusb_get_bus_number(dev), libusb_get_device_address(dev),
desc.bDeviceClass, desc.bDeviceSubClass, desc.bDeviceProtocol);
// @TODO: detect BT USB Dongle based on character and not by id
// The class code (bDeviceClass) is 0xE0 Wireless Controller.
// The SubClass code (bDeviceSubClass) is 0x01 RF Controller.
// The Protocol code (bDeviceProtocol) is 0x01 Bluetooth programming.
// if (desc.bDeviceClass == 0xe0 && desc.bDeviceSubClass == 0x01 && desc.bDeviceProtocol == 0x01){
if (desc.bDeviceClass == 0xE0 && desc.bDeviceSubClass == 0x01
&& desc.bDeviceProtocol == 0x01) {
log_info("BT Dongle found.\n");
return dev;
}
}
return NULL;
}
#endif
static void LIBUSB_CALL async_callback(struct libusb_transfer *transfer)
{
int r;
//log_info("in async_callback %d\n", transfer->endpoint);
if (transfer->status == LIBUSB_TRANSFER_COMPLETED ||
(transfer->status == LIBUSB_TRANSFER_TIMED_OUT && transfer->actual_length > 0)) {
if (handle_packet == NULL) {
handle_packet = transfer;
} else {
// Walk to end of list and add current packet there
struct libusb_transfer *temp = handle_packet;
while (temp->user_data) {
temp = temp->user_data;
}
temp->user_data = transfer;
}
} else {
// No usable data, just resubmit packet
if (libusb_state == LIB_USB_TRANSFERS_ALLOCATED) {
r = libusb_submit_transfer(transfer);
if (r) {
log_error("Error re-submitting transfer %d\n", r);
}
}
}
}
static int usb_process_ds(struct data_source *ds) {
struct timeval tv;
int r;
//log_info("in usb_process_ds\n");
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1;
// always handling an event as we're called when data is ready
memset(&tv, 0, sizeof(struct timeval));
libusb_handle_events_timeout(NULL, &tv);
// Handle any packet in the order that they were received
while (handle_packet) {
void * next = handle_packet->user_data;
//log_info("handle packet %x, endpoint", handle_packet, handle_packet->endpoint);
if (handle_packet->endpoint == event_in_addr) {
hci_dump_packet( HCI_EVENT_PACKET, 1, handle_packet-> buffer,
handle_packet->actual_length);
packet_handler(HCI_EVENT_PACKET, handle_packet-> buffer,
handle_packet->actual_length);
}
if (handle_packet->endpoint == acl_in_addr) {
hci_dump_packet( HCI_ACL_DATA_PACKET, 1, handle_packet-> buffer,
handle_packet->actual_length);
packet_handler(HCI_ACL_DATA_PACKET, handle_packet-> buffer,
handle_packet->actual_length);
}
// Re-submit transfer
if (libusb_state == LIB_USB_TRANSFERS_ALLOCATED) {
handle_packet->user_data = NULL;
r = libusb_submit_transfer(handle_packet);
if (r) {
log_error("Error re-submitting transfer %d\n", r);
}
}
// Move to next in the list of packets to handle
if (next) {
handle_packet = next;
} else {
handle_packet = NULL;
}
}
return 0;
}
void usb_process_ts(timer_source_t *timer) {
struct timeval tv, now;
long msec;
//log_info("in usb_process_ts\n");
// Deactivate timer
run_loop_remove_timer(&usb_timer);
usb_timer_active = 0;
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return;
// actuall handled the packet in the pollfds function
usb_process_ds((struct data_source *) NULL);
// Compute the amount of time until next event is due
gettimeofday(&now, NULL);
msec = (now.tv_sec - tv.tv_sec) * 1000;
msec = (now.tv_usec - tv.tv_usec) / 1000;
// Maximum wait time, async packet can come in earlier than timeout
if (msec > 10) msec = 10;
// Activate timer
run_loop_set_timer(&usb_timer, msec);
run_loop_add_timer(&usb_timer);
usb_timer_active = 1;
return;
}
static int usb_open(void *transport_config){
int r,c;
#if !USB_VENDOR_ID || !USB_PRODUCT_ID
libusb_device * aDev;
libusb_device **devs;
ssize_t cnt;
#endif
handle_packet = NULL;
// default endpoint addresses
event_in_addr = 0x81; // EP1, IN interrupt
acl_in_addr = 0x82; // EP2, IN bulk
acl_out_addr = 0x02; // EP2, OUT bulk
// USB init
r = libusb_init(NULL);
if (r < 0) return -1;
libusb_state = LIB_USB_OPENED;
// configure debug level
libusb_set_debug(0,3);
#if USB_VENDOR_ID && USB_PRODUCT_ID
// Use a specified device
log_info("Want vend: %04x, prod: %04x\n", USB_VENDOR_ID, USB_PRODUCT_ID);
handle = libusb_open_device_with_vid_pid(NULL, USB_VENDOR_ID, USB_PRODUCT_ID);
if (!handle){
log_error("libusb_open_device_with_vid_pid failed!\n");
usb_close(handle);
return -1;
}
#else
// Scan system for an appropriate device
log_info("Scanning for a device");
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0) {
usb_close(handle);
return -1;
}
// Find BT modul
aDev = scan_for_bt_device(devs);
if (!aDev){
libusb_free_device_list(devs, 1);
usb_close(handle);
return -1;
}
dev = aDev;
r = libusb_open(dev, &handle);
libusb_free_device_list(devs, 1);
if (r < 0) {
usb_close(handle);
return r;
}
#endif
log_info("libusb open %d, handle %p\n", r, handle);
libusb_state = LIB_USB_OPENED;
// Detach OS driver (not possible for OS X)
#ifndef __APPLE__
r = libusb_kernel_driver_active(handle, 0);
if (r < 0) {
log_error("libusb_kernel_driver_active error %d\n", r);
usb_close(handle);
return r;
}
if (r == 1) {
r = libusb_detach_kernel_driver(handle, 0);
if (r < 0) {
log_error("libusb_detach_kernel_driver error %d\n", r);
usb_close(handle);
return r;
}
}
log_info("libusb_detach_kernel_driver\n");
#endif
libusb_state = LIB_USB_KERNEL_DETACHED;
// reserve access to device
log_info("claiming interface 0...\n");
r = libusb_claim_interface(handle, 0);
if (r < 0) {
log_error("Error claiming interface %d\n", r);
usb_close(handle);
return r;
}
libusb_state = LIB_USB_INTERFACE_CLAIMED;
log_info("claimed interface 0\n");
#if !USB_VENDOR_ID || !USB_PRODUCT_ID
scan_for_bt_endpoints();
#endif
// allocate transfer handlers
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
event_in_transfer[c] = libusb_alloc_transfer(0); // 0 isochronous transfers Events
bulk_in_transfer[c] = libusb_alloc_transfer(0); // 0 isochronous transfers ACL in
if ( !event_in_transfer[c] || !bulk_in_transfer[c] ) {
usb_close(handle);
return LIBUSB_ERROR_NO_MEM;
}
}
libusb_state = LIB_USB_TRANSFERS_ALLOCATED;
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
// configure event_in handlers
libusb_fill_interrupt_transfer(event_in_transfer[c], handle, event_in_addr,
hci_event_in_buffer[c], HCI_ACL_BUFFER_SIZE, async_callback, NULL, 2000) ;
r = libusb_submit_transfer(event_in_transfer[c]);
if (r) {
log_error("Error submitting interrupt transfer %d\n", r);
usb_close(handle);
return r;
}
// configure bulk_in handlers
libusb_fill_bulk_transfer(bulk_in_transfer[c], handle, acl_in_addr,
hci_bulk_in_buffer[c], HCI_ACL_BUFFER_SIZE, async_callback, NULL, 2000) ;
r = libusb_submit_transfer(bulk_in_transfer[c]);
if (r) {
log_error("Error submitting bulk in transfer %d\n", r);
usb_close(handle);
return r;
}
}
// Check for pollfds functionality
doing_pollfds = libusb_pollfds_handle_timeouts(NULL);
if (doing_pollfds) {
log_info("Async using pollfds:\n");
const struct libusb_pollfd ** pollfd = libusb_get_pollfds(NULL);
for (r = 0 ; pollfd[r] ; r++) {
data_source_t *ds = malloc(sizeof(data_source_t));
ds->fd = pollfd[r]->fd;
ds->process = usb_process_ds;
run_loop_add_data_source(ds);
log_info("%u: %p fd: %u, events %x\n", r, pollfd[r], pollfd[r]->fd, pollfd[r]->events);
}
} else {
log_info("Async using timers:\n");
usb_timer.process = usb_process_ts;
run_loop_set_timer(&usb_timer, 100);
run_loop_add_timer(&usb_timer);
usb_timer_active = 1;
}
return 0;
}
static int usb_close(void *transport_config){
int c;
// @TODO: remove all run loops!
switch (libusb_state){
case LIB_USB_CLOSED:
break;
case LIB_USB_TRANSFERS_ALLOCATED:
libusb_state = LIB_USB_INTERFACE_CLAIMED;
if(usb_timer_active) {
run_loop_remove_timer(&usb_timer);
usb_timer_active = 0;
}
// Cancel any asynchronous transfers
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
libusb_cancel_transfer(event_in_transfer[c]);
libusb_cancel_transfer(bulk_in_transfer[c]);
}
/* TODO - find a better way to ensure that all transfers have completed */
struct timeval tv;
memset(&tv, 0, sizeof(struct timeval));
libusb_handle_events_timeout(NULL, &tv);
case LIB_USB_INTERFACE_CLAIMED:
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
if (event_in_transfer[c]) libusb_free_transfer(event_in_transfer[c]);
if (bulk_in_transfer[c]) libusb_free_transfer(bulk_in_transfer[c]);
}
libusb_release_interface(handle, 0);
case LIB_USB_KERNEL_DETACHED:
#ifndef __APPLE__
libusb_attach_kernel_driver (handle, 0);
#endif
case LIB_USB_DEVICE_OPENDED:
libusb_close(handle);
case LIB_USB_OPENED:
libusb_exit(NULL);
}
return 0;
}
static int usb_send_cmd_packet(uint8_t *packet, int size){
int r;
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1;
hci_dump_packet( HCI_COMMAND_DATA_PACKET, 0, packet, size);
// Use synchronous call to sent out command
r = libusb_control_transfer(handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
0, 0, 0, packet, size, 2000);
if (r < 0 || r !=size ) {
log_error("Error submitting control transfer %d\n", r);
return r;
}
return 0;
}
static int usb_send_acl_packet(uint8_t *packet, int size){
int r, t;
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1;
hci_dump_packet( HCI_ACL_DATA_PACKET, 0, packet, size);
// Use synchronous call to sent out data
r = libusb_bulk_transfer(handle, acl_out_addr, packet, size, &t, 1000);
if(r < 0){
log_error("Error submitting data transfer");
}
return 0;
}
static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
switch (packet_type){
case HCI_COMMAND_DATA_PACKET:
return usb_send_cmd_packet(packet, size);
case HCI_ACL_DATA_PACKET:
return usb_send_acl_packet(packet, size);
default:
return -1;
}
}
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
log_info("registering packet handler\n");
packet_handler = handler;
}
static const char * usb_get_transport_name(void){
return "USB";
}
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
}
// get usb singleton
hci_transport_t * hci_transport_usb_instance() {
if (!hci_transport_usb) {
hci_transport_usb = malloc( sizeof(hci_transport_t));
hci_transport_usb->open = usb_open;
hci_transport_usb->close = usb_close;
hci_transport_usb->send_packet = usb_send_packet;
hci_transport_usb->register_packet_handler = usb_register_packet_handler;
hci_transport_usb->get_transport_name = usb_get_transport_name;
hci_transport_usb->set_baudrate = NULL;
hci_transport_usb->can_send_packet_now = NULL;
}
return hci_transport_usb;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,201 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* l2cap.h
*
* Logical Link Control and Adaption Protocl (L2CAP)
*
* Created by Matthias Ringwald on 5/16/09.
*/
#pragma once
#include "hci.h"
#include "l2cap_signaling.h"
#include <btstack/utils.h>
#include <btstack/btstack.h>
#if defined __cplusplus
extern "C" {
#endif
#define L2CAP_SIG_ID_INVALID 0
#define L2CAP_HEADER_SIZE 4
// size of HCI ACL + L2CAP Header for regular data packets (8)
#define COMPLETE_L2CAP_HEADER (HCI_ACL_HEADER_SIZE + L2CAP_HEADER_SIZE)
// minimum signaling MTU
#define L2CAP_MINIMAL_MTU 48
#define L2CAP_DEFAULT_MTU 672
// check L2CAP MTU
#if (L2CAP_MINIMAL_MTU + L2CAP_HEADER_SIZE) > HCI_ACL_PAYLOAD_SIZE
#error "HCI_ACL_PAYLOAD_SIZE too small for minimal L2CAP MTU of 48 bytes"
#endif
// L2CAP Fixed Channel IDs
#define L2CAP_CID_SIGNALING 0x0001
#define L2CAP_CID_CONNECTIONLESS_CHANNEL 0x0002
#define L2CAP_CID_ATTRIBUTE_PROTOCOL 0x0004
#define L2CAP_CID_SIGNALING_LE 0x0005
#define L2CAP_CID_SECURITY_MANAGER_PROTOCOL 0x0006
void l2cap_init(void);
void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size));
void l2cap_create_channel_internal(void * connection, btstack_packet_handler_t packet_handler, bd_addr_t address, uint16_t psm, uint16_t mtu);
void l2cap_disconnect_internal(uint16_t local_cid, uint8_t reason);
uint16_t l2cap_get_remote_mtu_for_local_cid(uint16_t local_cid);
uint16_t l2cap_max_mtu(void);
void l2cap_block_new_credits(uint8_t blocked);
int l2cap_can_send_packet_now(uint16_t local_cid); // non-blocking UART write
// get outgoing buffer and prepare data
uint8_t *l2cap_get_outgoing_buffer(void);
int l2cap_send_prepared(uint16_t local_cid, uint16_t len);
int l2cap_send_internal(uint16_t local_cid, uint8_t *data, uint16_t len);
int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len);
int l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len);
void l2cap_close_connection(void *connection);
void l2cap_register_service_internal(void *connection, btstack_packet_handler_t packet_handler, uint16_t psm, uint16_t mtu);
void l2cap_unregister_service_internal(void *connection, uint16_t psm);
void l2cap_accept_connection_internal(uint16_t local_cid);
void l2cap_decline_connection_internal(uint16_t local_cid, uint8_t reason);
// Bluetooth 4.0 - allows to register handler for Attribute Protocol and Security Manager Protocol
void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id);
// private structs
typedef enum {
L2CAP_STATE_CLOSED = 1, // no baseband
L2CAP_STATE_WILL_SEND_CREATE_CONNECTION,
L2CAP_STATE_WAIT_CONNECTION_COMPLETE,
L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT,
L2CAP_STATE_WAIT_CONNECT_RSP, // from peer
L2CAP_STATE_CONFIG,
L2CAP_STATE_OPEN,
L2CAP_STATE_WAIT_DISCONNECT, // from application
L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST,
L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE,
L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT,
L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST,
L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE,
} L2CAP_STATE;
typedef enum {
L2CAP_CHANNEL_STATE_VAR_NONE = 0,
L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ = 1 << 0,
L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP = 1 << 1,
L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ = 1 << 2,
L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP = 1 << 3,
L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ = 1 << 4,
L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP = 1 << 5,
} L2CAP_CHANNEL_STATE_VAR;
// info regarding an actual coneection
typedef struct {
// linked list - assert: first field
linked_item_t item;
L2CAP_STATE state;
L2CAP_CHANNEL_STATE_VAR state_var;
bd_addr_t address;
hci_con_handle_t handle;
uint8_t remote_sig_id; // used by other side, needed for delayed response
uint8_t local_sig_id; // own signaling identifier
uint16_t local_cid;
uint16_t remote_cid;
uint16_t local_mtu;
uint16_t remote_mtu;
uint16_t psm;
uint8_t packets_granted; // number of L2CAP/ACL packets client is allowed to send
uint8_t reason; // used in decline internal
// client connection
void * connection;
// internal connection
btstack_packet_handler_t packet_handler;
} l2cap_channel_t;
// info regarding potential connections
typedef struct {
// linked list - assert: first field
linked_item_t item;
// service id
uint16_t psm;
// incoming MTU
uint16_t mtu;
// client connection
void *connection;
// internal connection
btstack_packet_handler_t packet_handler;
} l2cap_service_t;
typedef struct l2cap_signaling_response {
hci_con_handle_t handle;
uint8_t sig_id;
uint8_t code;
uint16_t data; // infoType for INFORMATION REQUEST, result for CONNECTION request
} l2cap_signaling_response_t;
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* l2cap_signaling.h
*
* Created by Matthias Ringwald on 7/23/09.
*/
#include "l2cap_signaling.h"
#include <string.h>
static char *l2cap_signaling_commands_format[] = {
"D", // 0x01 command reject: reason {cmd not understood (0), sig MTU exceeded (2:max sig MTU), invalid CID (4:req CID)}, data len, data
"22", // 0x02 connection request: PSM, Source CID
"2222", // 0x03 connection response: Dest CID, Source CID, Result, Status
"22D", // 0x04 config request: Dest CID, Flags, Configuration options
"222D", // 0x05 config response: Source CID, Flags, Result, Configuration options
"22", // 0x06 disconection request: Dest CID, Source CID
"22", // 0x07 disconection response: Dest CID, Source CID
"D", // 0x08 echo request: Data
"D", // 0x09 echo response: Data
"2", // 0x0a information request: InfoType {1=Connectionless MTU, 2=Extended features supported}
"22D", // 0x0b information response: InfoType, Result, Data
};
uint8_t sig_seq_nr = 0xff;
uint16_t source_cid = 0x40;
uint8_t l2cap_next_sig_id(void){
if (sig_seq_nr == 0xff) {
sig_seq_nr = 1;
} else {
sig_seq_nr++;
}
return sig_seq_nr;
}
uint16_t l2cap_next_local_cid(void){
return source_cid++;
}
uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer, hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr){
// 0 - Connection handle : PB=10 : BC=00
bt_store_16(acl_buffer, 0, handle | (2 << 12) | (0 << 14));
// 6 - L2CAP channel = 1
bt_store_16(acl_buffer, 6, 1);
// 8 - Code
acl_buffer[8] = cmd;
// 9 - id (!= 0 sequentially)
acl_buffer[9] = identifier;
// 12 - L2CAP signaling parameters
uint16_t pos = 12;
const char *format = l2cap_signaling_commands_format[cmd-1];
uint16_t word;
uint8_t * ptr;
while (*format) {
switch(*format) {
case '1': // 8 bit value
case '2': // 16 bit value
word = va_arg(argptr, int);
// minimal va_arg is int: 2 bytes on 8+16 bit CPUs
acl_buffer[pos++] = word & 0xff;
if (*format == '2') {
acl_buffer[pos++] = word >> 8;
}
break;
case 'D': // variable data. passed: len, ptr
word = va_arg(argptr, int);
ptr = va_arg(argptr, uint8_t *);
memcpy(&acl_buffer[pos], ptr, word);
pos += word;
break;
default:
break;
}
format++;
};
va_end(argptr);
// Fill in various length fields: it's the number of bytes following for ACL lenght and l2cap parameter length
// - the l2cap payload length is counted after the following channel id (only payload)
// 2 - ACL length
bt_store_16(acl_buffer, 2, pos - 4);
// 4 - L2CAP packet length
bt_store_16(acl_buffer, 4, pos - 6 - 2);
// 10 - L2CAP signaling parameter length
bt_store_16(acl_buffer, 10, pos - 12);
return pos;
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* l2cap_signaling.h
*
* Created by Matthias Ringwald on 7/23/09.
*/
#pragma once
#include <stdint.h>
#include <stdarg.h>
#include <btstack/utils.h>
#include <btstack/hci_cmds.h>
typedef enum {
COMMAND_REJECT = 1,
CONNECTION_REQUEST,
CONNECTION_RESPONSE,
CONFIGURE_REQUEST,
CONFIGURE_RESPONSE,
DISCONNECTION_REQUEST,
DISCONNECTION_RESPONSE,
ECHO_REQUEST,
ECHO_RESPONSE,
INFORMATION_REQUEST,
INFORMATION_RESPONSE
} L2CAP_SIGNALING_COMMANDS;
uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer,hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr);
uint8_t l2cap_next_sig_id(void);
uint16_t l2cap_next_local_cid(void);

View File

@ -0,0 +1,118 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* linked_list.c
*
* Created by Matthias Ringwald on 7/13/09.
*/
#include <btstack/linked_list.h>
#include <stdlib.h>
/**
* tests if list is empty
*/
int linked_list_empty(linked_list_t * list){
return *list == (void *) 0;
}
/**
* linked_list_get_last_item
*/
linked_item_t * linked_list_get_last_item(linked_list_t * list){ // <-- find the last item in the list
linked_item_t *lastItem = NULL;
linked_item_t *it;
for (it = *list; it ; it = it->next){
if (it) {
lastItem = it;
}
}
return lastItem;
}
/**
* linked_list_add
*/
void linked_list_add(linked_list_t * list, linked_item_t *item){ // <-- add item to list
// check if already in list
linked_item_t *it;
for (it = *list; it ; it = it->next){
if (it == item) {
return;
}
}
// add first
item->next = *list;
*list = item;
}
void linked_list_add_tail(linked_list_t * list, linked_item_t *item){ // <-- add item to list as last element
// check if already in list
linked_item_t *it;
for (it = (linked_item_t *) list; it->next ; it = it->next){
if (it->next == item) {
return;
}
}
item->next = (linked_item_t*) 0;
it->next = item;
}
/**
* Remove data_source from run loop
*
* @note: assumes that data_source_t.next is first element in data_source
*/
int linked_list_remove(linked_list_t * list, linked_item_t *item){ // <-- remove item from list
linked_item_t *it;
for (it = (linked_item_t *) list; it ; it = it->next){
if (it->next == item){
it->next = item->next;
return 0;
}
}
return -1;
}
void linked_item_set_user(linked_item_t *item, void *user_data){
item->next = (linked_item_t *) 0;
item->user_data = user_data;
}
void * linked_item_get_user(linked_item_t *item) {
return item->user_data;
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* memory_pool.c
*
* Fixed-size block allocation
*
* Free blocks are kept in singly linked list
*
*/
#include <btstack/memory_pool.h>
#include <stddef.h>
typedef struct node {
struct node * next;
} node_t;
void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size){
node_t *free_blocks = (node_t*) pool;
char *mem_ptr = (char *) storage;
int i;
// create singly linked list of all available blocks
free_blocks->next = NULL;
for (i = 0 ; i < count ; i++){
memory_pool_free(pool, mem_ptr);
mem_ptr += block_size;
}
}
void * memory_pool_get(memory_pool_t *pool){
node_t *free_blocks = (node_t*) pool;
if (!free_blocks->next) return NULL;
// remove first
node_t *node = free_blocks->next;
free_blocks->next = node->next;
return (void*) node;
}
void memory_pool_free(memory_pool_t *pool, void * block){
node_t *free_blocks = (node_t*) pool;
node_t *node = (node_t*) block;
// add block as node to list
node->next = free_blocks->next;
free_blocks->next = node;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2011 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* memory_pool.h
*
* @Brief Fixed-size block allocation
*
* @Assumption block_size >= sizeof(void *)
* @Assumption size of storage >= count * block_size
*
* @Note minimal implementation, no error checking/handling
*/
#pragma once
typedef void * memory_pool_t;
// initialize memory pool with with given storage, block size and count
void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size);
// get free block from pool, @returns NULL or pointer to block
void * memory_pool_get(memory_pool_t *pool);
// return previously reserved block to memory pool
void memory_pool_free(memory_pool_t *pool, void * block);

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
//
// platform_iphone.h
//
// support for the iPhone platform
//
// Created by Matthias Ringwald on 8/15/09.
//
#pragma once
#include "hci.h"
void platform_iphone_status_handler(BLUETOOTH_STATE state);
void platform_iphone_register_window_manager_restart(void (*callback)());
void platform_iphone_register_preferences_changed(void (*callback)());
int platform_iphone_logging_enabled(void);

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
//
// platform_iphone.c
//
// Created by Matthias Ringwald on 8/15/09.
//
#include "platform_iphone.h"
#include "config.h"
#include "../SpringBoardAccess/SpringBoardAccess.h"
#include <stdio.h>
#include <unistd.h>
#ifdef USE_SPRINGBOARD
#include <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>
// update SpringBoard icons
void platform_iphone_status_handler(BLUETOOTH_STATE state){
switch (state) {
case BLUETOOTH_OFF:
SBA_removeStatusBarImage("BTstack");
SBA_removeStatusBarImage("BTstackActive");
NSLog(@"Bluetooth status: OFF");
break;
case BLUETOOTH_ON:
SBA_removeStatusBarImage("BTstackActive");
SBA_addStatusBarImage("BTstack");
NSLog(@"Bluetooth status: ON");
break;
case BLUETOOTH_ACTIVE:
SBA_removeStatusBarImage("BTstack");
SBA_addStatusBarImage("BTstackActive");
NSLog(@"Bluetooth status: ACTIVE");
break;
default:
break;
}
}
static void (*window_manager_restart_callback)() = NULL;
static void springBoardDidLaunch(){
NSLog(@"springBoardDidLaunch!\n");
if (window_manager_restart_callback) {
int timer;
for (timer = 0 ; timer < 10 ; timer++){
NSLog(@"ping SBA %u", timer);
if (SBA_available()){
NSLog(@"pong from SBA!");
break;
}
sleep(1);
}
(*window_manager_restart_callback)();
}
}
void platform_iphone_register_window_manager_restart(void (*callback)() ){
static int registered = 0;
if (!registered) {
// register for launch notification
CFNotificationCenterRef darwin = CFNotificationCenterGetDarwinNotifyCenter();
CFNotificationCenterAddObserver(darwin, NULL, (CFNotificationCallback) springBoardDidLaunch,
(CFStringRef) @"SBSpringBoardDidLaunchNotification", NULL, 0);
}
window_manager_restart_callback = callback;
}
static void (*preferences_changed_callback)() = NULL;
static void preferencesDidChange(){
NSLog(@"ch.ringwald.btstack.preferences!\n");
if (preferences_changed_callback) {
(*preferences_changed_callback)();
}
}
void platform_iphone_register_preferences_changed(void (*callback)() ){
static int registered = 0;
if (!registered) {
// register for launch notification
CFNotificationCenterRef darwin = CFNotificationCenterGetDarwinNotifyCenter();
CFNotificationCenterAddObserver(darwin, NULL, (CFNotificationCallback) preferencesDidChange,
(CFStringRef) @"ch.ringwald.btstack.preferences", NULL, 0);
}
preferences_changed_callback = callback;
}
int platform_iphone_logging_enabled(void){
int result = 0;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/ch.ringwald.btstack.plist"];
NSNumber *loggingEnabled = [dict objectForKey:@"Logging"];
if (loggingEnabled){
result = [loggingEnabled boolValue];
}
[pool release];
return result;
}
#endif

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/**
* interface to provide link key and remote name storage
*/
#pragma once
#include <btstack/utils.h>
typedef struct {
// management
void (*open)(void);
void (*close)(void);
// link key
int (*get_link_key)(bd_addr_t *bd_addr, link_key_t *link_key);
void (*put_link_key)(bd_addr_t *bd_addr, link_key_t *key);
void (*delete_link_key)(bd_addr_t *bd_addr);
// 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;
extern const remote_device_db_t remote_device_db_memory;
// MARK: non-persisten implementation
#include <btstack/linked_list.h>
#define MAX_NAME_LEN 32
typedef struct {
// linked list - assert: first field
linked_item_t item;
bd_addr_t bd_addr;
} db_mem_device_t;
typedef struct {
db_mem_device_t device;
link_key_t link_key;
} db_mem_device_link_key_t;
typedef struct {
db_mem_device_t device;
char device_name[MAX_NAME_LEN];
} db_mem_device_name_t;
typedef struct {
// linked list - assert: first field
linked_item_t item;
char service_name[MAX_NAME_LEN];
uint8_t channel;
} db_mem_service_t;

View File

@ -0,0 +1,298 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
#include "remote_device_db.h"
#include "debug.h"
#import <Foundation/Foundation.h>
#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 *rfcomm_services = nil;
// Device info
static void db_open(void){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// NSUserDefaults didn't work
//
// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// NSDictionary * dict = [defaults persistentDomainForName: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
for (id key in dict) {
NSDictionary *value = [dict objectForKey:key];
NSMutableDictionary *deviceEntry = [NSMutableDictionary dictionaryWithCapacity:[value count]];
[deviceEntry addEntriesFromDictionary:value];
[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_info("read prefs for %u devices\n", (unsigned int) [dict count]);
[pool release];
}
static void db_synchronize(void){
log_info("stored prefs for %u devices\n", (unsigned int) [remote_devices count]);
// 3 different ways
// 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
//
// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// [defaults setPersistentDomain:remote_devices forName:BTdaemonID];
// [defaults synchronize];
}
static void db_close(void){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// don't call db_synchronize();
// a) we're calling db_synchronize() after each change already
// b) db_close is called during the SIGINT handler which causes a corrupt prefs file
[remote_devices release];
remote_devices = nil;
[pool release];
}
static NSString * stringForAddress(bd_addr_t* address) {
uint8_t *addr = (uint8_t*) *address;
return [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2],
addr[3], addr[4], addr[5]];
}
static void set_value(bd_addr_t *bd_addr, NSString *key, id value){
NSString *devAddress = stringForAddress(bd_addr);
NSMutableDictionary * deviceDict = [remote_devices objectForKey:devAddress];
if (!deviceDict){
deviceDict = [NSMutableDictionary dictionaryWithCapacity:3];
[remote_devices setObject:deviceDict forKey:devAddress];
}
[deviceDict setObject:value forKey:key];
db_synchronize();
}
static void delete_value(bd_addr_t *bd_addr, NSString *key){
NSString *devAddress = stringForAddress(bd_addr);
NSMutableDictionary * deviceDict = [remote_devices objectForKey:devAddress];
[deviceDict removeObjectForKey:key];
db_synchronize();
}
static id get_value(bd_addr_t *bd_addr, NSString *key){
NSString *devAddress = stringForAddress(bd_addr);
NSMutableDictionary * deviceDict = [remote_devices objectForKey:devAddress];
if (!deviceDict) return nil;
return [deviceDict objectForKey:key];
}
static int get_link_key(bd_addr_t *bd_addr, link_key_t *link_key) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSData *linkKey = get_value(bd_addr, PREFS_LINK_KEY);
if ([linkKey length] == LINK_KEY_LEN){
memcpy(link_key, [linkKey bytes], LINK_KEY_LEN);
}
[pool release];
return (linkKey != nil);
}
static void put_link_key(bd_addr_t *bd_addr, link_key_t *link_key){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSData *linkKey = [NSData dataWithBytes:link_key length:16];
set_value(bd_addr, PREFS_LINK_KEY, linkKey);
[pool release];
}
static void delete_link_key(bd_addr_t *bd_addr){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
delete_value(bd_addr, PREFS_LINK_KEY);
[pool release];
}
static void put_name(bd_addr_t *bd_addr, device_name_t *device_name){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *remoteName = [NSString stringWithUTF8String:(char*)device_name];
if (!remoteName){
remoteName = [NSString stringWithCString:(char*)device_name encoding:NSISOLatin1StringEncoding];
}
if (remoteName) {
set_value(bd_addr, PREFS_REMOTE_NAME, remoteName);
}
[pool release];
}
static void delete_name(bd_addr_t *bd_addr){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
delete_value(bd_addr, PREFS_REMOTE_NAME);
[pool release];
}
static int get_name(bd_addr_t *bd_addr, device_name_t *device_name) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *remoteName = get_value(bd_addr, PREFS_REMOTE_NAME);
if (remoteName){
memset(device_name, 0, sizeof(device_name_t));
strncpy((char*) device_name, [remoteName UTF8String], sizeof(device_name_t)-1);
}
[pool release];
return (remoteName != nil);
}
// MARK: PERSISTENT RFCOMM CHANNEL ALLOCATION
static int firstFreeChannelNr(void){
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(void){
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){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(@"persistent_rfcomm_channel for %s", 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();
[pool release];
return channel;
}
remote_device_db_t remote_device_db_iphone = {
db_open,
db_close,
get_link_key,
put_link_key,
delete_link_key,
get_name,
put_name,
delete_name,
persistent_rfcomm_channel
};

View File

@ -0,0 +1,198 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
#include <string.h>
#include <stdlib.h>
#include "remote_device_db.h"
#include "btstack_memory.h"
#include "debug.h"
#include <btstack/utils.h>
#include <btstack/linked_list.h>
// This lists should be only accessed by tests.
linked_list_t db_mem_link_keys = NULL;
linked_list_t db_mem_names = NULL;
static linked_list_t db_mem_services = NULL;
// Device info
static void db_open(void){
}
static void db_close(void){
}
static db_mem_device_t * get_item(linked_list_t list, bd_addr_t *bd_addr) {
linked_item_t *it;
for (it = (linked_item_t *) list; it ; it = it->next){
db_mem_device_t * item = (db_mem_device_t *) it;
if (BD_ADDR_CMP(item->bd_addr, *bd_addr) == 0) {
return item;
}
}
return NULL;
}
static int get_name(bd_addr_t *bd_addr, device_name_t *device_name) {
db_mem_device_name_t * item = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr);
if (!item) return 0;
strncpy((char*)device_name, item->device_name, MAX_NAME_LEN);
linked_list_remove(&db_mem_names, (linked_item_t *) item);
linked_list_add(&db_mem_names, (linked_item_t *) item);
return 1;
}
static int get_link_key(bd_addr_t *bd_addr, link_key_t *link_key) {
db_mem_device_link_key_t * item = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr);
if (!item) return 0;
memcpy(link_key, item->link_key, LINK_KEY_LEN);
linked_list_remove(&db_mem_link_keys, (linked_item_t *) item);
linked_list_add(&db_mem_link_keys, (linked_item_t *) item);
return 1;
}
static void delete_link_key(bd_addr_t *bd_addr){
db_mem_device_t * item = get_item(db_mem_link_keys, bd_addr);
if (!item) return;
linked_list_remove(&db_mem_link_keys, (linked_item_t *) item);
btstack_memory_db_mem_device_link_key_free(item);
}
static void put_link_key(bd_addr_t *bd_addr, link_key_t *link_key){
db_mem_device_link_key_t * existingRecord = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr);
if (existingRecord){
memcpy(existingRecord->link_key, link_key, LINK_KEY_LEN);
return;
}
// Record not found, create new one for this device
db_mem_device_link_key_t * newItem = (db_mem_device_link_key_t*) btstack_memory_db_mem_device_link_key_get();
if (!newItem){
newItem = (db_mem_device_link_key_t*)linked_list_get_last_item(&db_mem_link_keys);
}
if (!newItem) return;
memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t));
memcpy(newItem->link_key, link_key, LINK_KEY_LEN);
linked_list_add(&db_mem_link_keys, (linked_item_t *) newItem);
}
static void delete_name(bd_addr_t *bd_addr){
db_mem_device_t * item = get_item(db_mem_names, bd_addr);
if (!item) return;
linked_list_remove(&db_mem_names, (linked_item_t *) item);
btstack_memory_db_mem_device_name_free(item);
}
static void put_name(bd_addr_t *bd_addr, device_name_t *device_name){
db_mem_device_name_t * existingRecord = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr);
if (existingRecord){
strncpy(existingRecord->device_name, (const char*) device_name, MAX_NAME_LEN);
return;
}
// Record not found, create a new one for this device
db_mem_device_name_t * newItem = (db_mem_device_name_t *) btstack_memory_db_mem_device_name_get();
if (!newItem) {
newItem = (db_mem_device_name_t*)linked_list_get_last_item(&db_mem_names);
};
if (!newItem) return;
memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t));
strncpy(newItem->device_name, (const char*) device_name, MAX_NAME_LEN);
linked_list_add(&db_mem_names, (linked_item_t *) newItem);
}
// MARK: PERSISTENT RFCOMM CHANNEL ALLOCATION
static uint8_t persistent_rfcomm_channel(char *serviceName){
linked_item_t *it;
db_mem_service_t * item;
uint8_t max_channel = 1;
for (it = (linked_item_t *) db_mem_services; it ; it = it->next){
item = (db_mem_service_t *) it;
if (strncmp(item->service_name, serviceName, MAX_NAME_LEN) == 0) {
// Match found
return item->channel;
}
// TODO prevent overflow
if (item->channel >= max_channel) max_channel = item->channel + 1;
}
// Allocate new persistant channel
db_mem_service_t * newItem = (db_mem_service_t *) btstack_memory_db_mem_service_get();
if (!newItem) return 0;
strncpy(newItem->service_name, serviceName, MAX_NAME_LEN);
newItem->channel = max_channel;
linked_list_add(&db_mem_services, (linked_item_t *) newItem);
return max_channel;
}
const remote_device_db_t remote_device_db_memory = {
db_open,
db_close,
get_link_key,
put_link_key,
delete_link_key,
get_name,
put_name,
delete_name,
persistent_rfcomm_channel
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,278 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* RFCOMM.h
*/
#include <btstack/btstack.h>
#include <btstack/utils.h>
#include <stdint.h>
#if defined __cplusplus
extern "C" {
#endif
void rfcomm_init(void);
// register packet handler
void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
uint16_t channel, uint8_t *packet, uint16_t size));
// BTstack Internal RFCOMM API
void rfcomm_create_channel_internal(void * connection, bd_addr_t *addr, uint8_t channel);
void rfcomm_create_channel_with_initial_credits_internal(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t initial_credits);
void rfcomm_disconnect_internal(uint16_t rfcomm_cid);
void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size);
void rfcomm_register_service_with_initial_credits_internal(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t initial_credits);
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);
void rfcomm_grant_credits(uint16_t rfcomm_cid, uint8_t credits);
int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len);
void rfcomm_close_connection(void *connection);
#define UNLIMITED_INCOMING_CREDITS 0xff
// private structs
typedef enum {
RFCOMM_MULTIPLEXER_CLOSED = 1,
RFCOMM_MULTIPLEXER_W4_CONNECT, // outgoing
RFCOMM_MULTIPLEXER_SEND_SABM_0, // "
RFCOMM_MULTIPLEXER_W4_UA_0, // "
RFCOMM_MULTIPLEXER_W4_SABM_0, // incoming
RFCOMM_MULTIPLEXER_SEND_UA_0,
RFCOMM_MULTIPLEXER_OPEN,
RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC
} RFCOMM_MULTIPLEXER_STATE;
typedef enum {
MULT_EV_READY_TO_SEND = 1,
} RFCOMM_MULTIPLEXER_EVENT;
typedef enum {
RFCOMM_CHANNEL_CLOSED = 1,
RFCOMM_CHANNEL_W4_MULTIPLEXER,
RFCOMM_CHANNEL_SEND_UIH_PN,
RFCOMM_CHANNEL_W4_PN_RSP,
RFCOMM_CHANNEL_SEND_SABM_W4_UA,
RFCOMM_CHANNEL_W4_UA,
RFCOMM_CHANNEL_INCOMING_SETUP,
RFCOMM_CHANNEL_DLC_SETUP,
RFCOMM_CHANNEL_OPEN,
RFCOMM_CHANNEL_SEND_UA_AFTER_DISC,
RFCOMM_CHANNEL_SEND_DISC,
RFCOMM_CHANNEL_SEND_DM,
} RFCOMM_CHANNEL_STATE;
typedef enum {
RFCOMM_CHANNEL_STATE_VAR_NONE = 0,
RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED = 1 << 0,
RFCOMM_CHANNEL_STATE_VAR_RCVD_PN = 1 << 1,
RFCOMM_CHANNEL_STATE_VAR_RCVD_RPN = 1 << 2,
RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM = 1 << 3,
RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD = 1 << 4,
RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP = 1 << 5,
RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP = 1 << 6,
RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_INFO = 1 << 7,
RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP = 1 << 8,
RFCOMM_CHANNEL_STATE_VAR_SEND_UA = 1 << 9,
RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD = 1 << 10,
RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP = 1 << 11,
RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS = 1 << 12,
RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD = 1 << 13,
RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP = 1 << 14,
RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS = 1 << 15,
} RFCOMM_CHANNEL_STATE_VAR;
typedef enum {
CH_EVT_RCVD_SABM = 1,
CH_EVT_RCVD_UA,
CH_EVT_RCVD_PN,
CH_EVT_RCVD_PN_RSP,
CH_EVT_RCVD_DISC,
CH_EVT_RCVD_DM,
CH_EVT_RCVD_MSC_CMD,
CH_EVT_RCVD_MSC_RSP,
CH_EVT_RCVD_RPN_CMD,
CH_EVT_RCVD_RPN_REQ,
CH_EVT_RCVD_CREDITS,
CH_EVT_MULTIPLEXER_READY,
CH_EVT_READY_TO_SEND,
} RFCOMM_CHANNEL_EVENT;
typedef struct rfcomm_channel_event {
RFCOMM_CHANNEL_EVENT type;
} rfcomm_channel_event_t;
typedef struct rfcomm_channel_event_pn {
rfcomm_channel_event_t super;
uint16_t max_frame_size;
uint8_t priority;
uint8_t credits_outgoing;
} rfcomm_channel_event_pn_t;
typedef struct rfcomm_rpn_data {
uint8_t baud_rate;
uint8_t flags;
uint8_t flow_control;
uint8_t xon;
uint8_t xoff;
uint8_t parameter_mask_0; // first byte
uint8_t parameter_mask_1; // second byte
} rfcomm_rpn_data_t;
typedef struct rfcomm_channel_event_rpn {
rfcomm_channel_event_t super;
rfcomm_rpn_data_t data;
} rfcomm_channel_event_rpn_t;
// info regarding potential connections
typedef struct {
// linked list - assert: first field
linked_item_t item;
// server channel
uint8_t server_channel;
// incoming max frame size
uint16_t max_frame_size;
// use incoming flow control
uint8_t incoming_flow_control;
// initial incoming credits
uint8_t incoming_initial_credits;
// client connection
void *connection;
// internal connection
btstack_packet_handler_t packet_handler;
} rfcomm_service_t;
// info regarding multiplexer
// note: spec mandates single multplexer per device combination
typedef struct {
// linked list - assert: first field
linked_item_t item;
timer_source_t timer;
int timer_active;
RFCOMM_MULTIPLEXER_STATE state;
uint16_t l2cap_cid;
uint8_t l2cap_credits;
bd_addr_t remote_addr;
hci_con_handle_t con_handle;
uint8_t outgoing;
// hack to deal with authentication failure only observed by remote side
uint8_t at_least_one_connection;
uint16_t max_frame_size;
// send DM for DLCI != 0
uint8_t send_dm_for_dlci;
} rfcomm_multiplexer_t;
// info regarding an actual coneection
typedef struct {
// linked list - assert: first field
linked_item_t item;
rfcomm_multiplexer_t *multiplexer;
uint16_t rfcomm_cid;
uint8_t outgoing;
uint8_t dlci;
// number of packets granted to client
uint8_t packets_granted;
// credits for outgoing traffic
uint8_t credits_outgoing;
// number of packets remote will be granted
uint8_t new_credits_incoming;
// credits for incoming traffic
uint8_t credits_incoming;
// use incoming flow control
uint8_t incoming_flow_control;
// channel state
RFCOMM_CHANNEL_STATE state;
// state variables used in RFCOMM_CHANNEL_INCOMING
RFCOMM_CHANNEL_STATE_VAR state_var;
// priority set by incoming side in PN
uint8_t pn_priority;
// negotiated frame size
uint16_t max_frame_size;
// rpn data
rfcomm_rpn_data_t rpn_data;
// server channel (see rfcomm_service_t) - NULL => outgoing channel
rfcomm_service_t * service;
// internal connection
btstack_packet_handler_t packet_handler;
// client connection
void * connection;
} rfcomm_channel_t;
#if defined __cplusplus
}
#endif

View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* run_loop.c
*
* Created by Matthias Ringwald on 6/6/09.
*/
#include <btstack/run_loop.h>
#include <stdio.h>
#include <stdlib.h> // exit()
#include "run_loop_private.h"
#include "debug.h"
#include "config.h"
static run_loop_t * the_run_loop = NULL;
extern const run_loop_t run_loop_embedded;
#ifdef USE_POSIX_RUN_LOOP
extern run_loop_t run_loop_posix;
#endif
#ifdef USE_COCOA_RUN_LOOP
extern run_loop_t run_loop_cocoa;
#endif
// assert run loop initialized
void run_loop_assert(void){
#ifndef EMBEDDED
if (!the_run_loop){
log_error("ERROR: run_loop function called before run_loop_init!\n");
exit(10);
}
#endif
}
/**
* Add data_source to run_loop
*/
void run_loop_add_data_source(data_source_t *ds){
run_loop_assert();
the_run_loop->add_data_source(ds);
}
/**
* Remove data_source from run loop
*/
int run_loop_remove_data_source(data_source_t *ds){
run_loop_assert();
return the_run_loop->remove_data_source(ds);
}
/**
* Add timer to run_loop (keep list sorted)
*/
void run_loop_add_timer(timer_source_t *ts){
run_loop_assert();
the_run_loop->add_timer(ts);
}
/**
* Remove timer from run loop
*/
int run_loop_remove_timer(timer_source_t *ts){
run_loop_assert();
return the_run_loop->remove_timer(ts);
}
void run_loop_timer_dump(){
run_loop_assert();
the_run_loop->dump_timer();
}
/**
* Execute run_loop
*/
void run_loop_execute() {
run_loop_assert();
the_run_loop->execute();
}
// init must be called before any other run_loop call
void run_loop_init(RUN_LOOP_TYPE type){
#ifndef EMBEDDED
if (the_run_loop){
log_error("ERROR: run loop initialized twice!\n");
exit(10);
}
#endif
switch (type) {
#ifdef EMBEDDED
case RUN_LOOP_EMBEDDED:
the_run_loop = (run_loop_t*) &run_loop_embedded;
break;
#endif
#ifdef USE_POSIX_RUN_LOOP
case RUN_LOOP_POSIX:
the_run_loop = &run_loop_posix;
break;
#endif
#ifdef USE_COCOA_RUN_LOOP
case RUN_LOOP_COCOA:
the_run_loop = &run_loop_cocoa;
break;
#endif
default:
#ifndef EMBEDDED
log_error("ERROR: invalid run loop type %u selected!\n", type);
exit(10);
#endif
break;
}
the_run_loop->init();
}

View File

@ -37,10 +37,9 @@
#pragma once
//#include "config.h"
#define HAVE_TIME
#include "config.h"
#include "linked_list.h"
#include <btstack/linked_list.h>
#include <stdint.h>
@ -76,44 +75,30 @@ typedef struct timer {
} timer_source_t;
// Set timer based on current time in milliseconds.
// set timer based on current time
void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms);
// Set callback that will be executed when timer expires.
void run_loop_set_timer_handler(timer_source_t *ts, void (*process)(timer_source_t *_ts));
// Add/Remove timer source.
// add/remove timer_source
void run_loop_add_timer(timer_source_t *timer);
int run_loop_remove_timer(timer_source_t *timer);
// Init must be called before any other run_loop call.
// Use RUN_LOOP_EMBEDDED for embedded devices.
// init must be called before any other run_loop call
void run_loop_init(RUN_LOOP_TYPE type);
// Set data source callback.
void run_loop_set_data_source_handler(data_source_t *ds, int (*process)(data_source_t *_ds));
// Add/Remove data source.
// add/remove data_source
void run_loop_add_data_source(data_source_t *dataSource);
int run_loop_remove_data_source(data_source_t *dataSource);
// Execute configured run loop. This function does not return.
// execute configured run_loop
void run_loop_execute(void);
// hack to fix HCI timer handling
#ifdef HAVE_TICK
// Sets how many miliseconds has one tick.
uint32_t embedded_ticks_for_ms(uint32_t time_in_ms);
// Queries the current time in ticks.
uint32_t embedded_get_ticks(void);
uint32_t embedded_ticks_for_ms(uint32_t time_in_ms);
#endif
#ifdef EMBEDDED
// Sets an internal flag that is checked in the critical section
// just before entering sleep mode. Has to be called by the interupt
// handler of a data source to signal the run loop that a new data
// is available.
void embedded_trigger(void);
#endif
#if defined __cplusplus

View File

@ -0,0 +1,163 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* run_loop_cocoa.c
*
* Created by Matthias Ringwald on 8/2/09.
*/
#include <btstack/btstack.h>
#include <btstack/run_loop.h>
#include "run_loop_private.h"
#include "debug.h"
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#include <stdio.h>
#include <stdlib.h>
static void theCFRunLoopTimerCallBack (CFRunLoopTimerRef timer,void *info){
timer_source_t * ts = (timer_source_t*)info;
ts->process(ts);
}
static void socketDataCallback (
CFSocketRef s,
CFSocketCallBackType callbackType,
CFDataRef address,
const void *data,
void *info) {
if (callbackType == kCFSocketReadCallBack && info){
data_source_t *dataSource = (data_source_t *) info;
// printf("cocoa_data_source %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->fd, (int) s, (int) dataSource->item.next);
dataSource->process(dataSource);
}
}
void cocoa_add_data_source(data_source_t *dataSource){
// add fd as CFSocket
// store our dataSource in socket context
CFSocketContext socketContext;
memset(&socketContext, 0, sizeof(CFSocketContext));
socketContext.info = dataSource;
// create CFSocket from file descriptor
CFSocketRef socket = CFSocketCreateWithNative (
kCFAllocatorDefault,
dataSource->fd,
kCFSocketReadCallBack,
socketDataCallback,
&socketContext
);
// don't close native fd on CFSocketInvalidate
CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate);
// create run loop source
CFRunLoopSourceRef socketRunLoop = CFSocketCreateRunLoopSource ( kCFAllocatorDefault, socket, 0);
// hack: store CFSocketRef in "next" and CFRunLoopSourceRef in "user_data" of linked_item_t
dataSource->item.next = (void *) socket;
dataSource->item.user_data = (void *) socketRunLoop;
// add to run loop
CFRunLoopAddSource( CFRunLoopGetCurrent(), socketRunLoop, kCFRunLoopCommonModes);
// printf("cocoa_add_data_source %x - fd %u - CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->fd, (int) socket, (int) socketRunLoop);
}
int cocoa_remove_data_source(data_source_t *dataSource){
// printf("cocoa_remove_data_source %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->fd, (int) dataSource->item.next, (int) dataSource->item.user_data);
CFRunLoopRemoveSource( CFRunLoopGetCurrent(), (CFRunLoopSourceRef) dataSource->item.user_data, kCFRunLoopCommonModes);
CFRelease(dataSource->item.user_data);
CFSocketInvalidate((CFSocketRef) dataSource->item.next);
CFRelease(dataSource->item.next);
return 0;
}
void cocoa_add_timer(timer_source_t * ts)
{
// note: ts uses unix time: seconds since Jan 1st 1970, CF uses Jan 1st 2001 as reference date
// printf("kCFAbsoluteTimeIntervalSince1970 = %f\n", kCFAbsoluteTimeIntervalSince1970);
CFAbsoluteTime fireDate = ((double)ts->timeout.tv_sec) + (((double)ts->timeout.tv_usec)/1000000.0) - kCFAbsoluteTimeIntervalSince1970; // unix time - since Jan 1st 1970
CFRunLoopTimerContext timerContext = {0, ts, NULL, NULL, NULL};
CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate (kCFAllocatorDefault,fireDate,0,0,0,theCFRunLoopTimerCallBack,&timerContext);
CFRetain(timerRef);
// hack: store CFRunLoopTimerRef in next pointer of linked_item
ts->item.next = (void *)timerRef;
// printf("cocoa_add_timer %x -> %x now %f, then %f\n", (int) ts, (int) ts->item.next, CFAbsoluteTimeGetCurrent(),fireDate);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerRef, kCFRunLoopCommonModes);
}
int cocoa_remove_timer(timer_source_t * ts){
// printf("cocoa_remove_timer %x -> %x\n", (int) ts, (int) ts->item.next);
if (ts->item.next != NULL) {
CFRunLoopTimerInvalidate((CFRunLoopTimerRef) ts->item.next); // also removes timer from run loops + releases it
CFRelease((CFRunLoopTimerRef) ts->item.next);
}
return 0;
}
void cocoa_init(void){
}
void cocoa_execute(void)
{
CFRunLoopRun();
}
void cocoa_dump_timer(void){
log_error("WARNING: run_loop_dump_timer not implemented!");
return;
}
run_loop_t run_loop_cocoa = {
&cocoa_init,
&cocoa_add_data_source,
&cocoa_remove_data_source,
&cocoa_add_timer,
&cocoa_remove_timer,
&cocoa_execute,
&cocoa_dump_timer
};

View File

@ -0,0 +1,217 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* run_loop_embedded.c
*
* For this run loop, we assume that there's no global way to wait for a list
* of data sources to get ready. Instead, each data source has to queried
* individually. Calling ds->isReady() before calling ds->process() doesn't
* make sense, so we just poll each data source round robin.
*
* To support an idle state, where an MCU could go to sleep, the process function
* has to return if it has to called again as soon as possible
*
* After calling process() on every data source and evaluating the pending timers,
* the idle hook gets called if no data source did indicate that it needs to be
* called right away.
*
*/
#include <btstack/run_loop.h>
#include <btstack/linked_list.h>
#include <btstack/hal_tick.h>
#include <btstack/hal_cpu.h>
#include "run_loop_private.h"
#include "debug.h"
#include <stddef.h> // NULL
// the run loop
static linked_list_t data_sources;
static linked_list_t timers;
#ifdef HAVE_TICK
static uint32_t system_ticks;
#endif
static int trigger_event_received = 0;
/**
* trigger run loop iteration
*/
void embedded_trigger(void){
trigger_event_received = 1;
}
/**
* Add data_source to run_loop
*/
void embedded_add_data_source(data_source_t *ds){
linked_list_add(&data_sources, (linked_item_t *) ds);
}
/**
* Remove data_source from run loop
*/
int embedded_remove_data_source(data_source_t *ds){
return linked_list_remove(&data_sources, (linked_item_t *) ds);
}
/**
* Add timer to run_loop (keep list sorted)
*/
void embedded_add_timer(timer_source_t *ts){
#ifdef HAVE_TICK
linked_item_t *it;
for (it = (linked_item_t *) &timers; it->next ; it = it->next){
if (ts->timeout < ((timer_source_t *) it->next)->timeout) {
break;
}
}
ts->item.next = it->next;
it->next = (linked_item_t *) ts;
// log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
// embedded_dump_timer();
#endif
}
/**
* Remove timer from run loop
*/
int embedded_remove_timer(timer_source_t *ts){
#ifdef HAVE_TICK
// log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
return linked_list_remove(&timers, (linked_item_t *) ts);
#else
return 0;
#endif
}
void embedded_dump_timer(void){
#ifdef HAVE_TICK
#ifdef ENABLE_LOG_INFO
linked_item_t *it;
int i = 0;
for (it = (linked_item_t *) timers; it ; it = it->next){
timer_source_t *ts = (timer_source_t*) it;
log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout);
}
#endif
#endif
}
/**
* Execute run_loop
*/
void embedded_execute(void) {
data_source_t *ds;
while (1) {
// process data sources
data_source_t *next;
for (ds = (data_source_t *) data_sources; ds != NULL ; ds = next){
next = (data_source_t *) ds->item.next; // cache pointer to next data_source to allow data source to remove itself
ds->process(ds);
}
#ifdef HAVE_TICK
// process timers
while (timers) {
timer_source_t *ts = (timer_source_t *) timers;
if (ts->timeout > system_ticks) break;
run_loop_remove_timer(ts);
ts->process(ts);
}
#endif
// disable IRQs and check if run loop iteration has been requested. if not, go to sleep
hal_cpu_disable_irqs();
if (trigger_event_received){
hal_cpu_enable_irqs_and_sleep();
continue;
}
hal_cpu_enable_irqs();
}
}
#ifdef HAVE_TICK
static void embedded_tick_handler(void){
system_ticks++;
trigger_event_received = 1;
}
uint32_t embedded_get_ticks(void){
return system_ticks;
}
uint32_t embedded_ticks_for_ms(uint32_t time_in_ms){
return time_in_ms / hal_tick_get_tick_period_in_ms();
}
// set timer
void run_loop_set_timer(timer_source_t *ts, uint32_t timeout_in_ms){
uint32_t ticks = embedded_ticks_for_ms(timeout_in_ms);
if (ticks == 0) ticks++;
ts->timeout = system_ticks + ticks;
}
#endif
void embedded_init(void){
data_sources = NULL;
#ifdef HAVE_TICK
timers = NULL;
system_ticks = 0;
hal_tick_init();
hal_tick_set_handler(&embedded_tick_handler);
#endif
}
const run_loop_t run_loop_embedded = {
&embedded_init,
&embedded_add_data_source,
&embedded_remove_data_source,
&embedded_add_timer,
&embedded_remove_timer,
&embedded_execute,
&embedded_dump_timer
};

View File

@ -0,0 +1,250 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* run_loop.c
*
* Created by Matthias Ringwald on 6/6/09.
*/
#include <btstack/run_loop.h>
#include <btstack/linked_list.h>
#include "debug.h"
#include "run_loop_private.h"
#include <sys/select.h>
#include <stdlib.h>
#include <stdio.h>
void posix_dump_timer(void);
// the run loop
static linked_list_t data_sources;
static int data_sources_modified;
static linked_list_t timers;
/**
* Add data_source to run_loop
*/
void posix_add_data_source(data_source_t *ds){
data_sources_modified = 1;
// log_info("posix_add_data_source %x with fd %u\n", (int) ds, ds->fd);
linked_list_add(&data_sources, (linked_item_t *) ds);
}
/**
* Remove data_source from run loop
*/
int posix_remove_data_source(data_source_t *ds){
data_sources_modified = 1;
// log_info("posix_remove_data_source %x\n", (int) ds);
return linked_list_remove(&data_sources, (linked_item_t *) ds);
}
/**
* Add timer to run_loop (keep list sorted)
*/
void posix_add_timer(timer_source_t *ts){
linked_item_t *it;
for (it = (linked_item_t *) &timers; it->next ; it = it->next){
if ((timer_source_t *) it->next == ts){
log_error( "run_loop_timer_add error: timer to add already in list!\n");
return;
}
if (run_loop_timer_compare( (timer_source_t *) it->next, ts) > 0) {
break;
}
}
ts->item.next = it->next;
it->next = (linked_item_t *) ts;
// log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
// posix_dump_timer();
}
/**
* Remove timer from run loop
*/
int posix_remove_timer(timer_source_t *ts){
// log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
// posix_dump_timer();
return linked_list_remove(&timers, (linked_item_t *) ts);
}
void posix_dump_timer(void){
linked_item_t *it;
int i = 0;
for (it = (linked_item_t *) timers; it ; it = it->next){
timer_source_t *ts = (timer_source_t*) it;
log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout.tv_sec);
}
}
/**
* Execute run_loop
*/
void posix_execute(void) {
fd_set descriptors;
data_source_t *ds;
timer_source_t *ts;
struct timeval current_tv;
struct timeval next_tv;
struct timeval *timeout;
while (1) {
// collect FDs
FD_ZERO(&descriptors);
int highest_fd = 0;
for (ds = (data_source_t *) data_sources; ds ; ds = (data_source_t *) ds->item.next){
if (ds->fd >= 0) {
FD_SET(ds->fd, &descriptors);
if (ds->fd > highest_fd) {
highest_fd = ds->fd;
}
}
}
// get next timeout
// pre: 0 <= tv_usec < 1000000
timeout = NULL;
if (timers) {
gettimeofday(&current_tv, NULL);
ts = (timer_source_t *) timers;
next_tv.tv_usec = ts->timeout.tv_usec - current_tv.tv_usec;
next_tv.tv_sec = ts->timeout.tv_sec - current_tv.tv_sec;
while (next_tv.tv_usec < 0){
next_tv.tv_usec += 1000000;
next_tv.tv_sec--;
}
if (next_tv.tv_sec < 0 || (next_tv.tv_sec == 0 && next_tv.tv_usec < 0)){
next_tv.tv_sec = 0;
next_tv.tv_usec = 0;
}
timeout = &next_tv;
}
// wait for ready FDs
select( highest_fd+1 , &descriptors, NULL, NULL, timeout);
// process data sources very carefully
// bt_control.close() triggered from a client can remove a different data source
// log_info("posix_execute: before ds check\n");
data_sources_modified = 0;
for (ds = (data_source_t *) data_sources; !data_sources_modified && ds != NULL; ds = (data_source_t *) ds->item.next){
// log_info("posix_execute: check %x with fd %u\n", (int) ds, ds->fd);
if (FD_ISSET(ds->fd, &descriptors)) {
// log_info("posix_execute: process %x with fd %u\n", (int) ds, ds->fd);
ds->process(ds);
}
}
// log_info("posix_execute: after ds check\n");
// process timers
// pre: 0 <= tv_usec < 1000000
while (timers) {
gettimeofday(&current_tv, NULL);
ts = (timer_source_t *) timers;
if (ts->timeout.tv_sec > current_tv.tv_sec) break;
if (ts->timeout.tv_sec == current_tv.tv_sec && ts->timeout.tv_usec > current_tv.tv_usec) break;
// log_info("posix_execute: process times %x\n", (int) ts);
// remove timer before processing it to allow handler to re-register with run loop
run_loop_remove_timer(ts);
ts->process(ts);
}
}
}
// set timer
void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms){
gettimeofday(&a->timeout, NULL);
a->timeout.tv_sec += timeout_in_ms / 1000;
a->timeout.tv_usec += (timeout_in_ms % 1000) * 1000;
if (a->timeout.tv_usec > 1000000) {
a->timeout.tv_usec -= 1000000;
a->timeout.tv_sec++;
}
}
// compare timers - NULL is assumed to be before the Big Bang
// pre: 0 <= tv_usec < 1000000
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->tv_sec < b->tv_sec) {
return -1;
}
if (a->tv_sec > b->tv_sec) {
return 1;
}
if (a->tv_usec < b->tv_usec) {
return -1;
}
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_source_t *a, timer_source_t *b){
if (!a && !b) return 0;
if (!a) return -1;
if (!b) return 1;
return run_loop_timeval_compare(&a->timeout, &b->timeout);
}
void posix_init(void){
data_sources = NULL;
timers = NULL;
}
run_loop_t run_loop_posix = {
&posix_init,
&posix_add_data_source,
&posix_remove_data_source,
&posix_add_timer,
&posix_remove_timer,
&posix_execute,
&posix_dump_timer
};

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* run_loop_private.h
*
* Created by Matthias Ringwald on 6/6/09.
*/
#pragma once
#include <btstack/run_loop.h>
#ifdef HAVE_TIME
#include <sys/time.h>
// 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_source_t *a, timer_source_t *b);
#endif
//
void run_loop_timer_dump(void);
// internal use only
typedef struct {
void (*init)(void);
void (*add_data_source)(data_source_t *dataSource);
int (*remove_data_source)(data_source_t *dataSource);
void (*add_timer)(timer_source_t *timer);
int (*remove_timer)(timer_source_t *timer);
void (*execute)(void);
void (*dump_timer)(void);
} run_loop_t;

View File

@ -0,0 +1,662 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* Implementation of the Service Discovery Protocol Server
*/
#include "sdp.h"
#include <stdio.h>
#include <string.h>
#include <btstack/sdp_util.h>
#include "hci_dump.h"
#include "l2cap.h"
#include "debug.h"
// max reserved ServiceRecordHandle
#define maxReservedServiceRecordHandle 0xffff
// max SDP response
#define SDP_RESPONSE_BUFFER_SIZE (HCI_ACL_BUFFER_SIZE-HCI_ACL_HEADER_SIZE)
static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
// registered service records
static linked_list_t sdp_service_records = NULL;
// our handles start after the reserved range
static uint32_t sdp_next_service_record_handle = maxReservedServiceRecordHandle + 2;
static uint8_t sdp_response_buffer[SDP_RESPONSE_BUFFER_SIZE];
static void (*app_packet_handler)(void * connection, uint8_t packet_type,
uint16_t channel, uint8_t *packet, uint16_t size) = NULL;
static uint16_t l2cap_cid = 0;
static uint16_t sdp_response_size = 0;
void sdp_init(){
// register with l2cap psm sevices - max MTU
l2cap_register_service_internal(NULL, sdp_packet_handler, PSM_SDP, 0xffff);
}
// register packet handler
void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
uint16_t channel, uint8_t *packet, uint16_t size)){
app_packet_handler = handler;
l2cap_cid = 0;
}
uint32_t sdp_get_service_record_handle(uint8_t * record){
uint8_t * serviceRecordHandleAttribute = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle);
if (!serviceRecordHandleAttribute) return 0;
if (de_get_element_type(serviceRecordHandleAttribute) != DE_UINT) return 0;
if (de_get_size_type(serviceRecordHandleAttribute) != DE_SIZE_32) return 0;
return READ_NET_32(serviceRecordHandleAttribute, 1);
}
// data: event(8), len(8), status(8), service_record_handle(32)
static void sdp_emit_service_registered(void *connection, uint32_t handle, uint8_t status) {
if (!app_packet_handler) return;
uint8_t event[7];
event[0] = SDP_SERVICE_REGISTERED;
event[1] = sizeof(event) - 2;
event[2] = status;
bt_store_32(event, 3, handle);
hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event));
(*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event));
}
service_record_item_t * sdp_get_record_for_handle(uint32_t handle){
linked_item_t *it;
for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
service_record_item_t * item = (service_record_item_t *) it;
if (item->service_record_handle == handle){
return item;
}
}
return NULL;
}
// get next free, unregistered service record handle
uint32_t sdp_create_service_record_handle(void){
uint32_t handle = 0;
do {
handle = sdp_next_service_record_handle++;
if (sdp_get_record_for_handle(handle)) handle = 0;
} while (handle == 0);
return handle;
}
#ifdef EMBEDDED
// register service record internally - this special version doesn't copy the record, it should not be freeed
// pre: AttributeIDs are in ascending order
// pre: ServiceRecordHandle is first attribute and valid
// pre: record
// @returns ServiceRecordHandle or 0 if registration failed
uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item){
// get user record handle
uint32_t record_handle = record_item->service_record_handle;
// get actual record
uint8_t *record = record_item->service_record;
// check for ServiceRecordHandle attribute, returns pointer or null
uint8_t * req_record_handle = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle);
if (!req_record_handle) {
log_error("SDP Error - record does not contain ServiceRecordHandle attribute\n");
return 0;
}
// validate service record handle is not in reserved range
if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0;
// check if already in use
if (record_handle) {
if (sdp_get_record_for_handle(record_handle)) {
record_handle = 0;
}
}
// create new handle if needed
if (!record_handle){
record_handle = sdp_create_service_record_handle();
// Write the handle back into the record too
record_item->service_record_handle = record_handle;
sdp_set_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle, record_handle);
}
// add to linked list
linked_list_add(&sdp_service_records, (linked_item_t *) record_item);
sdp_emit_service_registered(connection, 0, record_item->service_record_handle);
return record_handle;
}
#else
// AttributeIDList used to remove ServiceRecordHandle
static const uint8_t removeServiceRecordHandleAttributeIDList[] = { 0x36, 0x00, 0x05, 0x0A, 0x00, 0x01, 0xFF, 0xFF };
// register service record internally - the normal version creates a copy of the record
// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present
// @returns ServiceRecordHandle or 0 if registration failed
uint32_t sdp_register_service_internal(void *connection, uint8_t * record){
// dump for now
// printf("Register service record\n");
// de_dump_data_element(record);
// get user record handle
uint32_t record_handle = sdp_get_service_record_handle(record);
// validate service record handle is not in reserved range
if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0;
// check if already in use
if (record_handle) {
if (sdp_get_record_for_handle(record_handle)) {
record_handle = 0;
}
}
// create new handle if needed
if (!record_handle){
record_handle = sdp_create_service_record_handle();
}
// calculate size of new service record: DES (2 byte len)
// + ServiceRecordHandle attribute (UINT16 UINT32) + size of existing attributes
uint16_t recordSize = 3 + (3 + 5) + de_get_data_size(record);
// alloc memory for new service_record_item
service_record_item_t * newRecordItem = (service_record_item_t *) malloc(recordSize + sizeof(service_record_item_t));
if (!newRecordItem) {
sdp_emit_service_registered(connection, 0, BTSTACK_MEMORY_ALLOC_FAILED);
return 0;
}
// link new service item to client connection
newRecordItem->connection = connection;
// set new handle
newRecordItem->service_record_handle = record_handle;
// create updated service record
uint8_t * newRecord = (uint8_t *) &(newRecordItem->service_record);
// create DES for new record
de_create_sequence(newRecord);
// set service record handle
de_add_number(newRecord, DE_UINT, DE_SIZE_16, 0);
de_add_number(newRecord, DE_UINT, DE_SIZE_32, record_handle);
// add other attributes
sdp_append_attributes_in_attributeIDList(record, (uint8_t *) removeServiceRecordHandleAttributeIDList, 0, recordSize, newRecord);
// dump for now
// de_dump_data_element(newRecord);
// printf("reserved size %u, actual size %u\n", recordSize, de_get_len(newRecord));
// add to linked list
linked_list_add(&sdp_service_records, (linked_item_t *) newRecordItem);
sdp_emit_service_registered(connection, 0, newRecordItem->service_record_handle);
return record_handle;
}
#endif
// unregister service record internally
//
// makes sure one client cannot remove service records of other clients
//
void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle){
service_record_item_t * record_item = sdp_get_record_for_handle(service_record_handle);
if (record_item && record_item->connection == connection) {
linked_list_remove(&sdp_service_records, (linked_item_t *) record_item);
}
}
// remove all service record for a client connection
void sdp_unregister_services_for_connection(void *connection){
linked_item_t *it = (linked_item_t *) &sdp_service_records;
while (it->next){
service_record_item_t *record_item = (service_record_item_t *) it->next;
if (record_item->connection == connection){
it->next = it->next->next;
#ifndef EMBEDDED
free(record_item);
#endif
} else {
it = it->next;
}
}
}
// PDU
// PDU ID (1), Transaction ID (2), Param Length (2), Param 1, Param 2, ..
int sdp_create_error_response(uint16_t transaction_id, uint16_t error_code){
sdp_response_buffer[0] = SDP_ErrorResponse;
net_store_16(sdp_response_buffer, 1, transaction_id);
net_store_16(sdp_response_buffer, 3, 2);
net_store_16(sdp_response_buffer, 5, error_code); // invalid syntax
return 7;
}
int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu){
// get request details
uint16_t transaction_id = READ_NET_16(packet, 1);
// not used yet - uint16_t param_len = READ_NET_16(packet, 3);
uint8_t * serviceSearchPattern = &packet[5];
uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
uint16_t maximumServiceRecordCount = READ_NET_16(packet, 5 + serviceSearchPatternLen);
uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2];
// calc maxumumServiceRecordCount based on remote MTU
uint16_t maxNrServiceRecordsPerResponse = (remote_mtu - (9+3))/4;
// continuation state contains index of next service record to examine
int continuation = 0;
uint16_t continuation_index = 0;
if (continuationState[0] == 2){
continuation_index = READ_NET_16(continuationState, 1);
}
// get and limit total count
linked_item_t *it;
uint16_t total_service_count = 0;
for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
service_record_item_t * item = (service_record_item_t *) it;
if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
total_service_count++;
}
if (total_service_count > maximumServiceRecordCount){
total_service_count = maximumServiceRecordCount;
}
// ServiceRecordHandleList at 9
uint16_t pos = 9;
uint16_t current_service_count = 0;
uint16_t current_service_index = 0;
uint16_t matching_service_count = 0;
for (it = (linked_item_t *) sdp_service_records; it ; it = it->next, ++current_service_index){
service_record_item_t * item = (service_record_item_t *) it;
if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
matching_service_count++;
if (current_service_index < continuation_index) continue;
net_store_32(sdp_response_buffer, pos, item->service_record_handle);
pos += 4;
current_service_count++;
if (matching_service_count >= total_service_count) break;
if (current_service_count >= maxNrServiceRecordsPerResponse){
continuation = 1;
continuation_index = current_service_index + 1;
break;
}
}
// Store continuation state
if (continuation) {
sdp_response_buffer[pos++] = 2;
net_store_16(sdp_response_buffer, pos, continuation_index);
pos += 2;
} else {
sdp_response_buffer[pos++] = 0;
}
// header
sdp_response_buffer[0] = SDP_ServiceSearchResponse;
net_store_16(sdp_response_buffer, 1, transaction_id);
net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
net_store_16(sdp_response_buffer, 5, total_service_count);
net_store_16(sdp_response_buffer, 7, current_service_count);
return pos;
}
int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu){
// get request details
uint16_t transaction_id = READ_NET_16(packet, 1);
// not used yet - uint16_t param_len = READ_NET_16(packet, 3);
uint32_t serviceRecordHandle = READ_NET_32(packet, 5);
uint16_t maximumAttributeByteCount = READ_NET_16(packet, 9);
uint8_t * attributeIDList = &packet[11];
uint16_t attributeIDListLen = de_get_len(attributeIDList);
uint8_t * continuationState = &packet[11+attributeIDListLen];
// calc maximumAttributeByteCount based on remote MTU
uint16_t maximumAttributeByteCount2 = remote_mtu - (7+3);
if (maximumAttributeByteCount2 < maximumAttributeByteCount) {
maximumAttributeByteCount = maximumAttributeByteCount2;
}
// continuation state contains the offset into the complete response
uint16_t continuation_offset = 0;
if (continuationState[0] == 2){
continuation_offset = READ_NET_16(continuationState, 1);
}
// get service record
service_record_item_t * item = sdp_get_record_for_handle(serviceRecordHandle);
if (!item){
// service record handle doesn't exist
return sdp_create_error_response(transaction_id, 0x0002); /// invalid Service Record Handle
}
// AttributeList - starts at offset 7
uint16_t pos = 7;
if (continuation_offset == 0){
// get size of this record
uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList);
// store DES
de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size);
maximumAttributeByteCount -= 3;
pos += 3;
}
// copy maximumAttributeByteCount from record
uint16_t bytes_used;
int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]);
pos += bytes_used;
uint16_t attributeListByteCount = pos - 7;
if (complete) {
sdp_response_buffer[pos++] = 0;
} else {
continuation_offset += bytes_used;
sdp_response_buffer[pos++] = 2;
net_store_16(sdp_response_buffer, pos, continuation_offset);
pos += 2;
}
// header
sdp_response_buffer[0] = SDP_ServiceAttributeResponse;
net_store_16(sdp_response_buffer, 1, transaction_id);
net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
net_store_16(sdp_response_buffer, 5, attributeListByteCount);
return pos;
}
static uint16_t sdp_get_size_for_service_search_attribute_response(uint8_t * serviceSearchPattern, uint8_t * attributeIDList){
uint16_t total_response_size = 0;
linked_item_t *it;
for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
service_record_item_t * item = (service_record_item_t *) it;
if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
// for all service records that match
total_response_size += 3 + spd_get_filtered_size(item->service_record, attributeIDList);
}
return total_response_size;
}
int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu){
// SDP header before attribute sevice list: 7
// Continuation, worst case: 5
// get request details
uint16_t transaction_id = READ_NET_16(packet, 1);
// not used yet - uint16_t param_len = READ_NET_16(packet, 3);
uint8_t * serviceSearchPattern = &packet[5];
uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
uint16_t maximumAttributeByteCount = READ_NET_16(packet, 5 + serviceSearchPatternLen);
uint8_t * attributeIDList = &packet[5+serviceSearchPatternLen+2];
uint16_t attributeIDListLen = de_get_len(attributeIDList);
uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2+attributeIDListLen];
// calc maximumAttributeByteCount based on remote MTU, SDP header and reserved Continuation block
uint16_t maximumAttributeByteCount2 = remote_mtu - 12;
if (maximumAttributeByteCount2 < maximumAttributeByteCount) {
maximumAttributeByteCount = maximumAttributeByteCount2;
}
// continuation state contains: index of next service record to examine
// continuation state contains: byte offset into this service record
uint16_t continuation_service_index = 0;
uint16_t continuation_offset = 0;
if (continuationState[0] == 4){
continuation_service_index = READ_NET_16(continuationState, 1);
continuation_offset = READ_NET_16(continuationState, 3);
}
// printf("--> sdp_handle_service_search_attribute_request, cont %u/%u, max %u\n", continuation_service_index, continuation_offset, maximumAttributeByteCount);
// AttributeLists - starts at offset 7
uint16_t pos = 7;
// add DES with total size for first request
if (continuation_service_index == 0 && continuation_offset == 0){
uint16_t total_response_size = sdp_get_size_for_service_search_attribute_response(serviceSearchPattern, attributeIDList);
de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, total_response_size);
// log_info("total response size %u\n", total_response_size);
pos += 3;
maximumAttributeByteCount -= 3;
}
// create attribute list
int first_answer = 1;
int continuation = 0;
uint16_t current_service_index = 0;
linked_item_t *it = (linked_item_t *) sdp_service_records;
for ( ; it ; it = it->next, ++current_service_index){
service_record_item_t * item = (service_record_item_t *) it;
if (current_service_index < continuation_service_index ) continue;
if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
if (continuation_offset == 0){
// get size of this record
uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList);
// stop if complete record doesn't fits into response but we already have a partial response
if ((filtered_attributes_size + 3 > maximumAttributeByteCount) && !first_answer) {
continuation = 1;
break;
}
// store DES
de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size);
pos += 3;
maximumAttributeByteCount -= 3;
}
first_answer = 0;
// copy maximumAttributeByteCount from record
uint16_t bytes_used;
int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]);
pos += bytes_used;
maximumAttributeByteCount -= bytes_used;
if (complete) {
continuation_offset = 0;
continue;
}
continuation = 1;
continuation_offset += bytes_used;
break;
}
uint16_t attributeListsByteCount = pos - 7;
// Continuation State
if (continuation){
sdp_response_buffer[pos++] = 4;
net_store_16(sdp_response_buffer, pos, (uint16_t) current_service_index);
pos += 2;
net_store_16(sdp_response_buffer, pos, continuation_offset);
pos += 2;
} else {
// complete
sdp_response_buffer[pos++] = 0;
}
// create SDP header
sdp_response_buffer[0] = SDP_ServiceSearchAttributeResponse;
net_store_16(sdp_response_buffer, 1, transaction_id);
net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
net_store_16(sdp_response_buffer, 5, attributeListsByteCount);
return pos;
}
static void sdp_try_respond(void){
if (!sdp_response_size ) return;
if (!l2cap_cid) return;
if (!l2cap_can_send_packet_now(l2cap_cid)) return;
// update state before sending packet (avoid getting called when new l2cap credit gets emitted)
uint16_t size = sdp_response_size;
sdp_response_size = 0;
l2cap_send_internal(l2cap_cid, sdp_response_buffer, size);
}
// we assume that we don't get two requests in a row
static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
uint16_t transaction_id;
SDP_PDU_ID_t pdu_id;
uint16_t remote_mtu;
// uint16_t param_len;
switch (packet_type) {
case L2CAP_DATA_PACKET:
pdu_id = (SDP_PDU_ID_t) packet[0];
transaction_id = READ_NET_16(packet, 1);
// param_len = READ_NET_16(packet, 3);
remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel);
// account for our buffer
if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE){
remote_mtu = SDP_RESPONSE_BUFFER_SIZE;
}
// printf("SDP Request: type %u, transaction id %u, len %u, mtu %u\n", pdu_id, transaction_id, param_len, remote_mtu);
switch (pdu_id){
case SDP_ServiceSearchRequest:
sdp_response_size = sdp_handle_service_search_request(packet, remote_mtu);
break;
case SDP_ServiceAttributeRequest:
sdp_response_size = sdp_handle_service_attribute_request(packet, remote_mtu);
break;
case SDP_ServiceSearchAttributeRequest:
sdp_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu);
break;
default:
sdp_response_size = sdp_create_error_response(transaction_id, 0x0003); // invalid syntax
break;
}
sdp_try_respond();
break;
case HCI_EVENT_PACKET:
switch (packet[0]) {
case L2CAP_EVENT_INCOMING_CONNECTION:
if (l2cap_cid) {
// CONNECTION REJECTED DUE TO LIMITED RESOURCES
l2cap_decline_connection_internal(channel, 0x0d);
break;
}
// accept
l2cap_cid = channel;
sdp_response_size = 0;
l2cap_accept_connection_internal(channel);
break;
case L2CAP_EVENT_CHANNEL_OPENED:
if (packet[2]) {
// open failed -> reset
l2cap_cid = 0;
}
break;
case L2CAP_EVENT_CREDITS:
case DAEMON_EVENT_HCI_PACKET_SENT:
sdp_try_respond();
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
if (channel == l2cap_cid){
// reset
l2cap_cid = 0;
}
break;
default:
// other event
break;
}
break;
default:
// other packet type
break;
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
#pragma once
#include <stdint.h>
#include <btstack/linked_list.h>
#include "config.h"
typedef enum {
SDP_ErrorResponse = 1,
SDP_ServiceSearchRequest,
SDP_ServiceSearchResponse,
SDP_ServiceAttributeRequest,
SDP_ServiceAttributeResponse,
SDP_ServiceSearchAttributeRequest,
SDP_ServiceSearchAttributeResponse
} SDP_PDU_ID_t;
// service record
// -- uses user_data field for actual
typedef struct {
// linked list - assert: first field
linked_item_t item;
// client connection
void * connection;
// data is contained in same memory
uint32_t service_record_handle;
uint8_t service_record[0];
} service_record_item_t;
void sdp_init(void);
void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
uint16_t channel, uint8_t *packet, uint16_t size));
#ifdef EMBEDDED
// register service record internally - the normal version creates a copy of the record
// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present
// @returns ServiceRecordHandle or 0 if registration failed
uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item);
#else
// register service record internally - this special version doesn't copy the record, it cannot be freeed
// pre: AttributeIDs are in ascending order
// pre: ServiceRecordHandle is first attribute and valid
// pre: record
// @returns ServiceRecordHandle or 0 if registration failed
uint32_t sdp_register_service_internal(void *connection, uint8_t * service_record);
#endif
// unregister service record internally
void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle);
//
void sdp_unregister_services_for_connection(void *connection);
//
int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu);
int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu);
int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu);

View File

@ -0,0 +1,698 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* sdp_util.c
*/
#include <btstack/sdp_util.h>
#include <btstack/utils.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h> // PRIx32
// workaround for missing PRIx32 on mspgcc (16-bit MCU)
#ifndef PRIx32
#warning Using own: #define PRIx32 "lx"
#define PRIx32 "lx"
#endif
// date element type names
const char * const type_names[] = { "NIL", "UINT", "INT", "UUID", "STRING", "BOOL", "DES", "DEA", "URL"};
// Bluetooth Base UUID: 00000000-0000-1000-8000- 00805F9B34FB
const uint8_t sdp_bluetooth_base_uuid[] = { 0x00, 0x00, 0x00, 0x00, /* - */ 0x00, 0x00, /* - */ 0x10, 0x00, /* - */
0x80, 0x00, /* - */ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
void sdp_normalize_uuid(uint8_t *uuid, uint32_t shortUUID){
memcpy(uuid, sdp_bluetooth_base_uuid, 16);
net_store_32(uuid, 0, shortUUID);
}
// MARK: DataElement getter
de_size_t de_get_size_type(uint8_t *header){
return (de_size_t) (header[0] & 7);
}
de_type_t de_get_element_type(uint8_t *header){
return (de_type_t) (header[0] >> 3);
}
int de_get_header_size(uint8_t * header){
de_size_t de_size = de_get_size_type(header);
if (de_size <= DE_SIZE_128) {
return 1;
}
return 1 + (1 << (de_size-DE_SIZE_VAR_8));
}
int de_get_data_size(uint8_t * header){
uint32_t result = 0;
de_type_t de_type = de_get_element_type(header);
de_size_t de_size = de_get_size_type(header);
switch (de_size){
case DE_SIZE_VAR_8:
result = header[1];
break;
case DE_SIZE_VAR_16:
result = READ_NET_16(header,1);
break;
case DE_SIZE_VAR_32:
result = READ_NET_32(header,1);
break;
default:
// case DE_SIZE_8:
// case DE_SIZE_16:
// case DE_SIZE_32:
// case DE_SIZE_64:
// case DE_SIZE_128:
if (de_type == DE_NIL) return 0;
return 1 << de_size;
}
return result;
}
int de_get_len(uint8_t *header){
return de_get_header_size(header) + de_get_data_size(header);
}
// @returns: element is valid UUID
int de_get_normalized_uuid(uint8_t *uuid128, uint8_t *element){
de_type_t uuidType = de_get_element_type(element);
de_size_t uuidSize = de_get_size_type(element);
if (uuidType != DE_UUID) return 0;
uint32_t shortUUID;
switch (uuidSize){
case DE_SIZE_16:
shortUUID = READ_NET_16(element, 1);
break;
case DE_SIZE_32:
shortUUID = READ_NET_32(element, 1);
break;
case DE_SIZE_128:
memcpy(uuid128, element+1, 16);
return 1;
default:
return 0;
}
sdp_normalize_uuid(uuid128, shortUUID);
return 1;
}
// functions to create record
static void de_store_descriptor(uint8_t * header, de_type_t type, de_size_t size){
header[0] = (type << 3) | size;
}
void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len){
header[0] = (type << 3) | size;
switch (size){
case DE_SIZE_VAR_8:
header[1] = len;
break;
case DE_SIZE_VAR_16:
net_store_16(header, 1, len);
break;
case DE_SIZE_VAR_32:
net_store_32(header, 1, len);
break;
default:
break;
}
}
// MARK: DataElement creation
/* starts a new sequence in empty buffer - first call */
void de_create_sequence(uint8_t *header){
de_store_descriptor_with_len( header, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length
};
/* starts a sub-sequence, @returns handle for sub-sequence */
uint8_t * de_push_sequence(uint8_t *header){
int element_len = de_get_len(header);
de_store_descriptor_with_len(header+element_len, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length
return header + element_len;
}
/* closes the current sequence and updates the parent sequence */
void de_pop_sequence(uint8_t * parent, uint8_t * child){
int child_len = de_get_len(child);
int data_size_parent = READ_NET_16(parent,1);
net_store_16(parent, 1, data_size_parent + child_len);
}
/* adds a single number value and 16+32 bit UUID to the sequence */
void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value){
int data_size = READ_NET_16(seq,1);
int element_size = 1; // e.g. for DE_TYPE_NIL
de_store_descriptor(seq+3+data_size, type, size);
switch (size){
case DE_SIZE_8:
if (type != DE_NIL){
seq[4+data_size] = value;
element_size = 2;
}
break;
case DE_SIZE_16:
net_store_16(seq, 4+data_size, value);
element_size = 3;
break;
case DE_SIZE_32:
net_store_32(seq, 4+data_size, value);
element_size = 5;
break;
default:
break;
}
net_store_16(seq, 1, data_size+element_size);
}
/* add a single block of data, e.g. as DE_STRING, DE_URL */
void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data){
int data_size = READ_NET_16(seq,1);
if (size > 0xff) {
// use 16-bit lengh information (3 byte header)
de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_16, size);
data_size += 3;
} else {
// use 8-bit lengh information (2 byte header)
de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_8, size);
data_size += 2;
}
memcpy( seq + 3 + data_size, data, size);
data_size += size;
net_store_16(seq, 1, data_size);
}
void de_add_uuid128(uint8_t * seq, uint8_t * uuid){
int data_size = READ_NET_16(seq,1);
de_store_descriptor(seq+3+data_size, DE_UUID, DE_SIZE_128);
memcpy( seq + 4 + data_size, uuid, 16);
net_store_16(seq, 1, data_size+1+16);
}
void sdp_add_attribute(uint8_t *seq, uint16_t attributeID, uint8_t attributeValue){
}
// MARK: DataElementSequence traversal
typedef int (*de_traversal_callback_t)(uint8_t * element, de_type_t type, de_size_t size, void *context);
static void de_traverse_sequence(uint8_t * element, de_traversal_callback_t handler, void *context){
de_type_t type = de_get_element_type(element);
if (type != DE_DES) return;
int pos = de_get_header_size(element);
int end_pos = de_get_len(element);
while (pos < end_pos){
de_type_t elemType = de_get_element_type(element + pos);
de_size_t elemSize = de_get_size_type(element + pos);
uint8_t done = (*handler)(element + pos, elemType, elemSize, context);
if (done) break;
pos += de_get_len(element + pos);
}
}
// MARK: AttributeList traversal
typedef int (*sdp_attribute_list_traversal_callback_t)(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *context);
static void sdp_attribute_list_traverse_sequence(uint8_t * element, sdp_attribute_list_traversal_callback_t handler, void *context){
de_type_t type = de_get_element_type(element);
if (type != DE_DES) return;
int pos = de_get_header_size(element);
int end_pos = de_get_len(element);
while (pos < end_pos){
de_type_t idType = de_get_element_type(element + pos);
de_size_t idSize = de_get_size_type(element + pos);
if (idType != DE_UINT || idSize != DE_SIZE_16) break; // wrong type
uint16_t attribute_id = READ_NET_16(element, pos + 1);
pos += 3;
if (pos >= end_pos) break; // array out of bounds
de_type_t valueType = de_get_element_type(element + pos);
de_size_t valueSize = de_get_size_type(element + pos);
uint8_t done = (*handler)(attribute_id, element + pos, valueType, valueSize, context);
if (done) break;
pos += de_get_len(element + pos);
}
}
// MARK: AttributeID in AttributeIDList
// attribute ID in AttributeIDList
// context { result, attributeID }
struct sdp_context_attributeID_search {
int result;
uint16_t attributeID;
};
static int sdp_traversal_attributeID_search(uint8_t * element, de_type_t type, de_size_t size, void *my_context){
struct sdp_context_attributeID_search * context = (struct sdp_context_attributeID_search *) my_context;
if (type != DE_UINT) return 0;
switch (size) {
case DE_SIZE_16:
if (READ_NET_16(element, 1) == context->attributeID) {
context->result = 1;
return 1;
}
break;
case DE_SIZE_32:
if (READ_NET_16(element, 1) <= context->attributeID
&& context->attributeID <= READ_NET_16(element, 3)) {
context->result = 1;
return 1;
}
break;
default:
break;
}
return 0;
}
int sdp_attribute_list_constains_id(uint8_t *attributeIDList, uint16_t attributeID){
struct sdp_context_attributeID_search attributeID_search;
attributeID_search.result = 0;
attributeID_search.attributeID = attributeID;
de_traverse_sequence(attributeIDList, sdp_traversal_attributeID_search, &attributeID_search);
return attributeID_search.result;
}
// MARK: Append Attributes for AttributeIDList
// pre: buffer contains DES with 2 byte length field
struct sdp_context_append_attributes {
uint8_t * buffer;
uint16_t startOffset; // offset of when to start copying
uint16_t maxBytes;
uint16_t usedBytes;
uint8_t *attributeIDList;
};
static int sdp_traversal_append_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
struct sdp_context_append_attributes * context = (struct sdp_context_append_attributes *) my_context;
if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) {
// DES_HEADER(3) + DES_DATA + (UINT16(3) + attribute)
uint16_t data_size = READ_NET_16(context->buffer, 1);
int attribute_len = de_get_len(attributeValue);
if (3 + data_size + (3 + attribute_len) <= context->maxBytes) {
// copy Attribute
de_add_number(context->buffer, DE_UINT, DE_SIZE_16, attributeID);
data_size += 3; // 3 bytes
memcpy(context->buffer + 3 + data_size, attributeValue, attribute_len);
net_store_16(context->buffer,1,data_size+attribute_len);
} else {
// not enought space left -> continue with previous element
return 1;
}
}
return 0;
}
// maxBytes: maximal size of data element sequence
uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer){
struct sdp_context_append_attributes context;
context.buffer = buffer;
context.maxBytes = maxBytes;
context.usedBytes = 0;
context.startOffset = startOffset;
context.attributeIDList = attributeIDList;
sdp_attribute_list_traverse_sequence(record, sdp_traversal_append_attributes, &context);
return context.usedBytes;
}
// MARK: Filter attributes that match attribute list from startOffset and a max nr bytes
struct sdp_context_filter_attributes {
uint8_t * buffer;
uint16_t startOffset; // offset of when to start copying
uint16_t maxBytes;
uint16_t usedBytes;
uint8_t *attributeIDList;
int complete;
};
// copy data with given start offset and max bytes, returns OK if all data has been copied
static int spd_append_range(struct sdp_context_filter_attributes* context, uint16_t len, uint8_t *data){
int ok = 1;
uint16_t remainder_len = len - context->startOffset;
if (context->maxBytes < remainder_len){
remainder_len = context->maxBytes;
ok = 0;
}
memcpy(context->buffer, &data[context->startOffset], remainder_len);
context->usedBytes += remainder_len;
context->buffer += remainder_len;
context->maxBytes -= remainder_len;
context->startOffset = 0;
return ok;
}
static int sdp_traversal_filter_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
struct sdp_context_filter_attributes * context = (struct sdp_context_filter_attributes *) my_context;
if (!sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) return 0;
// { Attribute ID (Descriptor, big endian 16-bit ID), AttributeValue (data)}
// handle Attribute ID
if (context->startOffset >= 3){
context->startOffset -= 3;
} else {
uint8_t idBuffer[3];
de_store_descriptor(idBuffer, DE_UINT, DE_SIZE_16);
net_store_16(idBuffer,1,attributeID);
int ok = spd_append_range(context, 3, idBuffer);
if (!ok) {
context->complete = 0;
return 1;
}
}
// handle Attribute Value
int attribute_len = de_get_len(attributeValue);
if (context->startOffset >= attribute_len) {
context->startOffset -= attribute_len;
return 0;
}
int ok = spd_append_range(context, attribute_len, attributeValue);
if (!ok) {
context->complete = 0;
return 1;
}
return 0;
}
int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer){
struct sdp_context_filter_attributes context;
context.buffer = buffer;
context.maxBytes = maxBytes;
context.usedBytes = 0;
context.startOffset = startOffset;
context.attributeIDList = attributeIDList;
context.complete = 1;
sdp_attribute_list_traverse_sequence(record, sdp_traversal_filter_attributes, &context);
*usedBytes = context.usedBytes;
return context.complete;
}
// MARK: Get sum of attributes matching attribute list
struct sdp_context_get_filtered_size {
uint8_t *attributeIDList;
uint16_t size;
};
static int sdp_traversal_get_filtered_size(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
struct sdp_context_get_filtered_size * context = (struct sdp_context_get_filtered_size *) my_context;
if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) {
context->size += 3 + de_get_len(attributeValue);
}
return 0;
}
int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList){
struct sdp_context_get_filtered_size context;
context.size = 0;
context.attributeIDList = attributeIDList;
sdp_attribute_list_traverse_sequence(record, sdp_traversal_get_filtered_size, &context);
return context.size;
}
// MARK: Get AttributeValue for AttributeID
// find attribute (ELEMENT) by ID
struct sdp_context_attribute_by_id {
uint16_t attributeID;
uint8_t * attributeValue;
};
static int sdp_traversal_attribute_by_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){
struct sdp_context_attribute_by_id * context = (struct sdp_context_attribute_by_id *) my_context;
if (attributeID == context->attributeID) {
context->attributeValue = attributeValue;
return 1;
}
return 0;
}
uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID){
struct sdp_context_attribute_by_id context;
context.attributeValue = NULL;
context.attributeID = attributeID;
sdp_attribute_list_traverse_sequence(record, sdp_traversal_attribute_by_id, &context);
return context.attributeValue;
}
// MARK: Set AttributeValue for AttributeID
struct sdp_context_set_attribute_for_id {
uint16_t attributeID;
uint32_t attributeValue;
uint8_t attributeFound;
};
static int sdp_traversal_set_attribute_for_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){
struct sdp_context_set_attribute_for_id * context = (struct sdp_context_set_attribute_for_id *) my_context;
if (attributeID == context->attributeID) {
context->attributeFound = 1;
switch (size){
case DE_SIZE_8:
if (attributeType != DE_NIL){
attributeValue[1] = context->attributeValue;
}
break;
case DE_SIZE_16:
net_store_16(attributeValue, 1, context->attributeValue);
break;
case DE_SIZE_32:
net_store_32(attributeValue, 1, context->attributeValue);
break;
// Might want to support STRINGS to, copy upto original length
default:
break;
}
return 1;
}
return 0;
}
uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value){
struct sdp_context_set_attribute_for_id context;
context.attributeID = attributeID;
context.attributeValue = value;
context.attributeFound = 0;
sdp_attribute_list_traverse_sequence(record, sdp_traversal_set_attribute_for_id, &context);
return context.attributeFound;
}
// MARK: ServiceRecord contains UUID
// service record contains UUID
// context { normalizedUUID }
struct sdp_context_contains_uuid128 {
uint8_t * uuid128;
int result;
};
int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128);
static int sdp_traversal_contains_UUID128(uint8_t * element, de_type_t type, de_size_t size, void *my_context){
struct sdp_context_contains_uuid128 * context = (struct sdp_context_contains_uuid128 *) my_context;
uint8_t normalizedUUID[16];
if (type == DE_UUID){
uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element);
context->result = uuidOK && memcmp(context->uuid128, normalizedUUID, 16) == 0;
}
if (type == DE_DES){
context->result = sdp_record_contains_UUID128(element, context->uuid128);
}
return context->result;
}
int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128){
struct sdp_context_contains_uuid128 context;
context.uuid128 = uuid128;
context.result = 0;
de_traverse_sequence(record, sdp_traversal_contains_UUID128, &context);
return context.result;
}
// MARK: ServiceRecord matches SearchServicePattern
// if UUID in searchServicePattern is not found in record => false
// context { result, record }
struct sdp_context_match_pattern {
uint8_t * record;
int result;
};
int sdp_traversal_match_pattern(uint8_t * element, de_type_t attributeType, de_size_t size, void *my_context){
struct sdp_context_match_pattern * context = (struct sdp_context_match_pattern *) my_context;
uint8_t normalizedUUID[16];
uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element);
if (!uuidOK || !sdp_record_contains_UUID128(context->record, normalizedUUID)){
context->result = 0;
return 1;
}
return 0;
}
int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern){
struct sdp_context_match_pattern context;
context.record = record;
context.result = 1;
de_traverse_sequence(serviceSearchPattern, sdp_traversal_match_pattern, &context);
return context.result;
}
// MARK: Dump DataElement
// context { indent }
static int de_traversal_dump_data(uint8_t * element, de_type_t de_type, de_size_t de_size, void *my_context){
int indent = *(int*) my_context;
int i;
for (i=0; i<indent;i++) printf(" ");
int pos = de_get_header_size(element);
int end_pos = de_get_len(element);
printf("type %5s (%u), element len %2u ", type_names[de_type], de_type, end_pos);
if (de_type == DE_DES) {
printf("\n");
indent++;
de_traverse_sequence(element, de_traversal_dump_data, (void *)&indent);
} else if (de_type == DE_UUID && de_size == DE_SIZE_128) {
printf(", value: ");
printUUID(element+1);
printf("\n");
} else if (de_type == DE_STRING) {
int len = 0;
switch (de_size){
case DE_SIZE_VAR_8:
len = element[1];
break;
case DE_SIZE_VAR_16:
len = READ_NET_16(element, 1);
break;
default:
break;
}
printf("len %u (0x%02x)\n", len, len);
hexdump(&element[pos], len);
} else {
uint32_t value = 0;
switch (de_size) {
case DE_SIZE_8:
if (de_type != DE_NIL){
value = element[pos];
}
break;
case DE_SIZE_16:
value = READ_NET_16(element,pos);
break;
case DE_SIZE_32:
value = READ_NET_32(element,pos);
break;
default:
break;
}
printf(", value: 0x%08" PRIx32 "\n", value);
}
return 0;
}
void de_dump_data_element(uint8_t * record){
int indent = 0;
// hack to get root DES, too.
de_type_t type = de_get_element_type(record);
de_size_t size = de_get_size_type(record);
de_traversal_dump_data(record, type, size, (void*) &indent);
}
void sdp_create_spp_service(uint8_t *service, int service_id, const char *name){
uint8_t* attribute;
de_create_sequence(service);
// 0x0000 "Service Record Handle"
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle);
de_add_number(service, DE_UINT, DE_SIZE_32, 0x10001);
// 0x0001 "Service Class ID List"
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList);
attribute = de_push_sequence(service);
{
de_add_number(attribute, DE_UUID, DE_SIZE_16, 0x1101 );
}
de_pop_sequence(service, attribute);
// 0x0004 "Protocol Descriptor List"
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList);
attribute = de_push_sequence(service);
{
uint8_t* l2cpProtocol = de_push_sequence(attribute);
{
de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, 0x0100);
}
de_pop_sequence(attribute, l2cpProtocol);
uint8_t* rfcomm = de_push_sequence(attribute);
{
de_add_number(rfcomm, DE_UUID, DE_SIZE_16, 0x0003); // rfcomm_service
de_add_number(rfcomm, DE_UINT, DE_SIZE_8, service_id); // rfcomm channel
}
de_pop_sequence(attribute, rfcomm);
}
de_pop_sequence(service, attribute);
// 0x0005 "Public Browse Group"
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BrowseGroupList); // public browse group
attribute = de_push_sequence(service);
{
de_add_number(attribute, DE_UUID, DE_SIZE_16, 0x1002 );
}
de_pop_sequence(service, attribute);
// 0x0006
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_LanguageBaseAttributeIDList);
attribute = de_push_sequence(service);
{
de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e);
de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a);
de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100);
}
de_pop_sequence(service, attribute);
// 0x0009 "Bluetooth Profile Descriptor List"
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList);
attribute = de_push_sequence(service);
{
uint8_t *sppProfile = de_push_sequence(attribute);
{
de_add_number(sppProfile, DE_UUID, DE_SIZE_16, 0x1101);
de_add_number(sppProfile, DE_UINT, DE_SIZE_16, 0x0100);
}
de_pop_sequence(attribute, sppProfile);
}
de_pop_sequence(service, attribute);
// 0x0100 "ServiceName"
de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100);
de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name);
}

View File

@ -0,0 +1,614 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* SocketServer.c
*
* Handles multiple connections to a single socket without blocking
*
* Created by Matthias Ringwald on 6/6/09.
*
*/
#include "socket_connection.h"
#include "hci.h"
#include "debug.h"
#include "config.h"
#include <btstack/btstack.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <unistd.h>
#ifdef USE_LAUNCHD
#include "../3rdparty/launch.h"
#endif
#define MAX_PENDING_CONNECTIONS 10
/** prototypes */
static int socket_connection_hci_process(struct data_source *ds);
static int socket_connection_dummy_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length);
/** globals */
/** packet header used over socket connections, in front of the HCI packet */
typedef struct packet_header {
uint16_t type;
uint16_t channel;
uint16_t length;
uint8_t data[0];
} packet_header_t; // 6
typedef enum {
SOCKET_W4_HEADER,
SOCKET_W4_DATA
} SOCKET_STATE;
struct connection {
data_source_t ds; // used for run loop
linked_item_t item; // used for connection list, user_data points to connection_t base
SOCKET_STATE state;
uint16_t bytes_read;
uint16_t bytes_to_read;
uint8_t buffer[6+HCI_ACL_BUFFER_SIZE]; // packet_header(6) + max packet: 3-DH5 = header(6) + payload (1021)
};
/** list of socket connections */
static linked_list_t connections = NULL;
static linked_list_t parked = NULL;
/** client packet handler */
static int (*socket_connection_packet_callback)(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length) = socket_connection_dummy_handler;
static int socket_connection_dummy_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length){
return 0;
}
int socket_connection_set_non_blocking(int fd) {
int err;
int flags;
// According to the man page, F_GETFL can't error!
flags = fcntl(fd, F_GETFL, NULL);
err = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
return err;
}
void socket_connection_set_no_sigpipe(int fd){
#ifdef HAVE_SO_NOSIGPIPE
int set = 1;
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
#endif
}
void socket_connection_free_connection(connection_t *conn){
// remove from run_loop
run_loop_remove_data_source(&conn->ds);
// and from connection list
linked_list_remove(&connections, &conn->item);
// destroy
free(conn);
}
void socket_connection_init_statemachine(connection_t *connection){
// wait for next packet
connection->state = SOCKET_W4_HEADER;
connection->bytes_read = 0;
connection->bytes_to_read = sizeof(packet_header_t);
}
connection_t * socket_connection_register_new_connection(int fd){
// create connection objec
connection_t * conn = malloc( sizeof(connection_t));
if (conn == NULL) return 0;
linked_item_set_user( &conn->item, conn);
conn->ds.fd = fd;
conn->ds.process = socket_connection_hci_process;
// prepare state machine and
socket_connection_init_statemachine(conn);
// add this socket to the run_loop
run_loop_add_data_source( &conn->ds );
// and the connection list
linked_list_add( &connections, &conn->item);
return conn;
}
void static socket_connection_emit_connection_opened(connection_t *connection){
uint8_t event[1];
event[0] = DAEMON_EVENT_CONNECTION_OPENED;
(*socket_connection_packet_callback)(connection, DAEMON_EVENT_PACKET, 0, (uint8_t *) &event, 1);
}
void static socket_connection_emit_connection_closed(connection_t *connection){
uint8_t event[1];
event[0] = DAEMON_EVENT_CONNECTION_CLOSED;
(*socket_connection_packet_callback)(connection, DAEMON_EVENT_PACKET, 0, (uint8_t *) &event, 1);
}
void static socket_connection_emit_nr_connections(void){
linked_item_t *it;
uint8_t nr_connections = 0;
for (it = (linked_item_t *) connections; it != NULL ; it = it->next, nr_connections++);
uint8_t event[2];
event[0] = DAEMON_NR_CONNECTIONS_CHANGED;
event[1] = nr_connections;
(*socket_connection_packet_callback)(NULL, DAEMON_EVENT_PACKET, 0, (uint8_t *) &event, 2);
// log_info("Nr connections changed,.. new %u\n", nr_connections);
}
int socket_connection_hci_process(struct data_source *ds) {
connection_t *conn = (connection_t *) ds;
int maxToRead = 0;
ioctl(ds->fd, FIONREAD, (char*)&maxToRead);
int bytesToRead = maxToRead;
do
{
int bytes_read = read(ds->fd, &conn->buffer[conn->bytes_read], conn->bytes_to_read);
if (bytes_read <= 0){
// connection broken (no particular channel, no date yet)
socket_connection_emit_connection_closed(conn);
// free connection
socket_connection_free_connection(linked_item_get_user(&conn->item));
socket_connection_emit_nr_connections();
return 0;
}
conn->bytes_read += bytes_read;
conn->bytes_to_read -= bytes_read;
// hexdump( conn->buffer, conn->bytes_read);
if (conn->bytes_to_read > 0) {
return 0;
}
int dispatch = 0;
switch (conn->state){
case SOCKET_W4_HEADER:
conn->state = SOCKET_W4_DATA;
conn->bytes_to_read = READ_BT_16( conn->buffer, 4);
if (conn->bytes_to_read == 0){
dispatch = 1;
}
break;
case SOCKET_W4_DATA:
dispatch = 1;
break;
default:
break;
}
if (dispatch){
// dispatch packet !!! connection, type, channel, data, size
int dispatch_err = (*socket_connection_packet_callback)(conn, READ_BT_16( conn->buffer, 0), READ_BT_16( conn->buffer, 2),
&conn->buffer[sizeof(packet_header_t)], READ_BT_16( conn->buffer, 4));
// reset state machine
socket_connection_init_statemachine(conn);
// "park" if dispatch failed
if (dispatch_err) {
log_info("socket_connection_hci_process dispatch failed -> park connection\n");
run_loop_remove_data_source(ds);
linked_list_add_tail(&parked, (linked_item_t *) ds);
}
}
// update bytes read from socket
bytesToRead -= bytes_read;
} while(bytesToRead > 0);
return 0;
}
/**
* try to dispatch packet for all "parked" connections.
* if dispatch is successful, a connection is added again to run loop
* pre: connections get parked iff packet was dispatched but could not be sent
*/
void socket_connection_retry_parked(){
// log_info("socket_connection_hci_process retry parked\n");
linked_item_t *it = (linked_item_t *) &parked;
while (it->next) {
connection_t * conn = (connection_t *) it->next;
// dispatch packet !!! connection, type, channel, data, size
log_info("socket_connection_hci_process retry parked %p\n", conn);
int dispatch_err = (*socket_connection_packet_callback)(conn, READ_BT_16( conn->buffer, 0), READ_BT_16( conn->buffer, 2),
&conn->buffer[sizeof(packet_header_t)], READ_BT_16( conn->buffer, 4));
// "un-park" if successful
if (!dispatch_err) {
log_info("socket_connection_hci_process dispatch succeeded -> un-park connection %p\n", conn);
it->next = it->next->next;
run_loop_add_data_source( (data_source_t *) conn);
} else {
it = it->next;
}
}
}
int socket_connection_has_parked_connections(void){
return parked != NULL;
}
static int socket_connection_accept(struct data_source *socket_ds) {
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
/* New connection coming in! */
int fd = accept(socket_ds->fd, (struct sockaddr *)&ss, &slen);
if (fd < 0) {
perror("accept");
return 0;
}
// non-blocking ?
// socket_connection_set_non_blocking(ds->fd);
// no sigpipe
socket_connection_set_no_sigpipe(fd);
log_info("socket_connection_accept new connection %u\n", fd);
connection_t * connection = socket_connection_register_new_connection(fd);
socket_connection_emit_connection_opened(connection);
socket_connection_emit_nr_connections();
return 0;
}
/**
* create socket data_source for tcp socket
*
* @return data_source object. If null, check errno
*/
int socket_connection_create_tcp(int port){
// create data_source_t
data_source_t *ds = malloc( sizeof(data_source_t));
if (ds == NULL) return -1;
ds->fd = 0;
ds->process = socket_connection_accept;
// create tcp socket
if ((ds->fd = socket (PF_INET, SOCK_STREAM, 0)) < 0) {
log_error("Error creating socket ...(%s)\n", strerror(errno));
free(ds);
return -1;
}
log_info ("Socket created for port %u\n", port);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
memset (&addr.sin_addr, 0, sizeof (addr.sin_addr));
const int y = 1;
setsockopt(ds->fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
if (bind ( ds->fd, (struct sockaddr *) &addr, sizeof (addr) ) ) {
log_error("Error on bind() ...(%s)\n", strerror(errno));
free(ds);
return -1;
}
if (listen (ds->fd, MAX_PENDING_CONNECTIONS)) {
log_error("Error on listen() ...(%s)\n", strerror(errno));
free(ds);
return -1;
}
run_loop_add_data_source(ds);
log_info ("Server up and running ...\n");
return 0;
}
#ifdef USE_LAUNCHD
/*
* Register listening sockets with our run loop
*/
void socket_connection_launchd_register_fd_array(launch_data_t listening_fd_array){
int i;
for (i = 0; i < launch_data_array_get_count(listening_fd_array); i++) {
// get fd
launch_data_t tempi = launch_data_array_get_index (listening_fd_array, i);
int listening_fd = launch_data_get_fd(tempi);
launch_data_free (tempi);
log_info("file descriptor = %u\n", listening_fd);
// create data_source_t for fd
data_source_t *ds = malloc( sizeof(data_source_t));
if (ds == NULL) return;
ds->process = socket_connection_accept;
ds->fd = listening_fd;
run_loop_add_data_source(ds);
}
}
/**
* create socket data_source for socket specified by launchd configuration
*/
int socket_connection_create_launchd(){
launch_data_t sockets_dict, checkin_response;
launch_data_t checkin_request;
launch_data_t listening_fd_array;
/*
* Register ourselves with launchd.
*
*/
if ((checkin_request = launch_data_new_string(LAUNCH_KEY_CHECKIN)) == NULL) {
log_error( "launch_data_new_string(\"" LAUNCH_KEY_CHECKIN "\") Unable to create string.");
return -1;
}
if ((checkin_response = launch_msg(checkin_request)) == NULL) {
log_error( "launch_msg(\"" LAUNCH_KEY_CHECKIN "\") IPC failure: %u", errno);
return -1;
}
if (LAUNCH_DATA_ERRNO == launch_data_get_type(checkin_response)) {
errno = launch_data_get_errno(checkin_response);
log_error( "Check-in failed: %u", errno);
return -1;
}
launch_data_t the_label = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_LABEL);
if (NULL == the_label) {
log_error( "No label found");
return -1;
}
/*
* Retrieve the dictionary of Socket entries in the config file
*/
sockets_dict = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_SOCKETS);
if (NULL == sockets_dict) {
log_error("No sockets found to answer requests on!");
return -1;
}
// if (launch_data_dict_get_count(sockets_dict) > 1) {
// log_error("Some sockets will be ignored!");
// }
/*
* Get the dictionary value from the key "Listeners"
*/
listening_fd_array = launch_data_dict_lookup(sockets_dict, "Listeners");
if (listening_fd_array) {
// log_error("Listeners...\n");
socket_connection_launchd_register_fd_array( listening_fd_array );
}
/*
* Get the dictionary value from the key "Listeners"
*/
listening_fd_array = launch_data_dict_lookup(sockets_dict, "Listeners2");
if (listening_fd_array) {
// log_error("Listeners2...\n");
socket_connection_launchd_register_fd_array( listening_fd_array );
}
// although used in Apple examples, it creates a malloc warning
// launch_data_free(checkin_response);
return 0;
}
#endif
/**
* create socket data_source for unix domain socket
*/
int socket_connection_create_unix(char *path){
// create data_source_t
data_source_t *ds = malloc( sizeof(data_source_t));
if (ds == NULL) return -1;
ds->fd = 0;
ds->process = socket_connection_accept;
// create unix socket
if ((ds->fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
log_error( "Error creating socket ...(%s)\n", strerror(errno));
free(ds);
return -1;
}
log_info ("Socket created at %s\n", path);
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, path);
unlink(path);
const int y = 1;
setsockopt(ds->fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
if (bind ( ds->fd, (struct sockaddr *) &addr, sizeof (addr) ) ) {
log_error( "Error on bind() ...(%s)\n", strerror(errno));
free(ds);
return -1;
}
if (listen (ds->fd, MAX_PENDING_CONNECTIONS)) {
log_error( "Error on listen() ...(%s)\n", strerror(errno));
free(ds);
return -1;
}
run_loop_add_data_source(ds);
log_info ("Server up and running ...\n");
return 0;
}
/**
* set packet handler for all auto-accepted connections
*/
void socket_connection_register_packet_callback( int (*packet_callback)(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length) ){
socket_connection_packet_callback = packet_callback;
}
/**
* send HCI packet to single connection
*/
void socket_connection_send_packet(connection_t *conn, uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size){
uint8_t header[sizeof(packet_header_t)];
bt_store_16(header, 0, type);
bt_store_16(header, 2, channel);
bt_store_16(header, 4, size);
#ifdef HAVE_SO_NOSIGPIPE
// BSD Variants like Darwin and iOS
write(conn->ds.fd, header, 6);
write(conn->ds.fd, packet, size);
#else
// Linux
send(conn->ds.fd, header, 6, MSG_NOSIGNAL);
send(conn->ds.fd, packet, size, MSG_NOSIGNAL);
#endif
}
/**
* send HCI packet to all connections
*/
void socket_connection_send_packet_all(uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size){
linked_item_t *next;
linked_item_t *it;
for (it = (linked_item_t *) connections; it ; it = next){
next = it->next; // cache pointer to next connection_t to allow for removal
socket_connection_send_packet( (connection_t *) linked_item_get_user(it), type, channel, packet, size);
}
}
/**
* create socket connection to BTdaemon
*/
connection_t * socket_connection_open_tcp(const char *address, uint16_t port){
// TCP
struct protoent* tcp = getprotobyname("tcp");
int btsocket = socket(PF_INET, SOCK_STREAM, tcp->p_proto);
if(btsocket == -1){
return NULL;
}
// localhost
struct sockaddr_in btdaemon_address;
btdaemon_address.sin_family = AF_INET;
btdaemon_address.sin_port = htons(port);
struct hostent* localhost = gethostbyname(address);
if(!localhost){
return NULL;
}
// connect
char* addr = localhost->h_addr_list[0];
memcpy(&btdaemon_address.sin_addr.s_addr, addr, sizeof addr);
if(connect(btsocket, (struct sockaddr*)&btdaemon_address, sizeof btdaemon_address) == -1){
return NULL;
}
return socket_connection_register_new_connection(btsocket);
}
/**
* close socket connection to BTdaemon
*/
int socket_connection_close_tcp(connection_t * connection){
if (!connection) return -1;
shutdown(connection->ds.fd, SHUT_RDWR);
socket_connection_free_connection(connection);
return 0;
}
/**
* create socket connection to BTdaemon
*/
connection_t * socket_connection_open_unix(){
int btsocket = socket(AF_UNIX, SOCK_STREAM, 0);
if(btsocket == -1){
return NULL;
}
struct sockaddr_un server;
memset(&server, 0, sizeof(server));
server.sun_family = AF_UNIX;
strcpy(server.sun_path, BTSTACK_UNIX);
if (connect(btsocket, (struct sockaddr *)&server, sizeof (server)) == -1){
return NULL;
};
return socket_connection_register_new_connection(btsocket);
}
/**
* close socket connection to BTdaemon
*/
int socket_connection_close_unix(connection_t * connection){
if (!connection) return -1;
shutdown(connection->ds.fd, SHUT_RDWR);
socket_connection_free_connection(connection);
return 0;
}

View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* socket_connection.h
*
* Created by Matthias Ringwald on 6/6/09.
*/
#pragma once
#include <btstack/run_loop.h>
#include <stdint.h>
/** opaque connection type */
typedef struct connection connection_t;
/**
* create socket data_source for socket specified by launchd configuration
*/
int socket_connection_create_launchd(void);
/**
* create socket for incoming tcp connections
*/
int socket_connection_create_tcp(int port);
/**
* create socket for incoming unix domain connections
*/
int socket_connection_create_unix(char *path);
/**
* close socket connection to BTdaemon
*/
int socket_connection_close_tcp(connection_t *connection);
/**
* create TCP socket connection to BTdaemon
*/
connection_t * socket_connection_open_tcp(const char *address, uint16_t port);
/**
* close TCP socket connection to BTdaemon
*/
int socket_connection_close_tcp(connection_t *connection);
/**
* create unix socket connection to BTdaemon
*/
connection_t * socket_connection_open_unix(void);
/**
* close unix connection to BTdaemon
*/
int socket_connection_close_unix(connection_t *connection);
/**
* set packet handler for all auto-accepted connections
* -- packet_callback @return: 0 == OK/NO ERROR
*/
void socket_connection_register_packet_callback( int (*packet_callback)(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length) );
/**
* send HCI packet to single connection
*/
void socket_connection_send_packet(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t size);
/**
* send event data to all clients
*/
void socket_connection_send_packet_all(uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size);
/**
* try to dispatch packet for all "parked" connections.
* if dispatch is successful, a connection is added again to run loop
*/
void socket_connection_retry_parked(void);
/**
* query if at least one connection had to be parked
*/
int socket_connection_has_parked_connections(void);

View File

@ -0,0 +1,184 @@
/*
* Copyright (C) 2009-2012 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* utils.c
*
* General utility functions
*
* Created by Matthias Ringwald on 7/23/09.
*/
#include "config.h"
#include <btstack/utils.h>
#include <stdio.h>
#include "debug.h"
void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
buffer[pos++] = value;
buffer[pos++] = value >> 8;
}
void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){
buffer[pos++] = value;
buffer[pos++] = value >> 8;
buffer[pos++] = value >> 16;
buffer[pos++] = value >> 24;
}
void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
buffer[pos++] = value >> 8;
buffer[pos++] = value;
}
void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){
buffer[pos++] = value >> 24;
buffer[pos++] = value >> 16;
buffer[pos++] = value >> 8;
buffer[pos++] = value;
}
void bt_flip_addr(bd_addr_t dest, bd_addr_t src){
dest[0] = src[5];
dest[1] = src[4];
dest[2] = src[3];
dest[3] = src[2];
dest[4] = src[1];
dest[5] = src[0];
}
void hexdump(void *data, int size){
int i;
for (i=0; i<size;i++){
log_info("%02X ", ((uint8_t *)data)[i]);
}
log_info("\n");
}
void printUUID(uint8_t *uuid) {
log_info("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
}
static char bd_addr_to_str_buffer[6*3]; // 12:45:78:01:34:67\0
char * bd_addr_to_str(bd_addr_t addr){
sprintf(bd_addr_to_str_buffer, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
return (char *) bd_addr_to_str_buffer;
}
void print_bd_addr( bd_addr_t addr){
log_info("%s", bd_addr_to_str(addr));
}
#ifndef EMBEDDED
int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr){
unsigned int bd_addr_buffer[BD_ADDR_LEN]; //for sscanf, integer needed
// reset result buffer
int i;
for (i = 0; i < BD_ADDR_LEN; i++) {
bd_addr_buffer[i] = 0;
}
// parse
int result = sscanf( (char *) addr_string, "%2x:%2x:%2x:%2x:%2x:%2x", &bd_addr_buffer[0], &bd_addr_buffer[1], &bd_addr_buffer[2],
&bd_addr_buffer[3], &bd_addr_buffer[4], &bd_addr_buffer[5]);
// store
if (result == 6){
for (i = 0; i < BD_ADDR_LEN; i++) {
addr[i] = (uint8_t) bd_addr_buffer[i];
}
}
return (result == 6);
}
#endif
/*
* CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
*/
static const uint8_t crc8table[256] = { /* reversed, 8-bit, poly=0x07 */
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
};
#define CRC8_INIT 0xFF // Initial FCS value
#define CRC8_OK 0xCF // Good final FCS value
/*-----------------------------------------------------------------------------------*/
uint8_t crc8(uint8_t *data, uint16_t len)
{
uint16_t count;
uint8_t crc = CRC8_INIT;
for (count = 0; count < len; count++)
crc = crc8table[crc ^ data[count]];
return crc;
}
/*-----------------------------------------------------------------------------------*/
uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum)
{
uint8_t crc;
crc = crc8(data, len);
crc = crc8table[crc ^ check_sum];
if (crc == CRC8_OK)
return 0; /* Valid */
else
return 1; /* Failed */
}
/*-----------------------------------------------------------------------------------*/
uint8_t crc8_calc(uint8_t *data, uint16_t len)
{
/* Ones complement */
return 0xFF - crc8(data, len);
}