Add Proof of concept Bluetooth agent

This commit is contained in:
Stefan Nygren 2015-03-09 11:54:33 +01:00
commit aa9d058747
3 changed files with 408 additions and 0 deletions

36
Makefile Normal file
View file

@ -0,0 +1,36 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=btle_alarm
PKG_VERSION:=1.0.6
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/btle_alarm-utils-$(PKG_VERSION)
PKG_SOURCE:=btle_alarm-utils-$(PKG_VERSION).tar.gz
include $(INCLUDE_DIR)/package.mk
define Package/btle_alarm
SECTION:=base
CATEGORY:=Utillities
TITLE:=Ethernet bridging configuration utility
#DESCRIPTION:=This variable is obsolete. use the Package/name/description define instead!
URL:=http://btle_alarm.sourceforge.net/
DEPENDS:=+bluez +libncurses
endef
define Package/btle_alarm/description
Ethernet bridging configuration utility
Manage ethernet bridging; a way to connect networks together to
form a larger network.
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/btle_alarm/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/btle_alarm $(1)/usr/bin/
endef
$(eval $(call BuildPackage,btle_alarm))

16
src/Makefile Normal file
View file

@ -0,0 +1,16 @@
.SUFFIXES: .tar.gz .c
override CFLAGS += -Wall -O0 -g -lbluetooth -lncurses
VERSION=0.0.1
LIBS=-lbluetooth -lm
btle_alarm:btle_alarm.c
all: btle_alarm btle_alarm.tar.gz
%.tar.gz: DIR=$(subst .tar.gz,,$@)
%.tar.gz: %.c
@mkdir -p ./$(DIR)-0.1
@cp $^ Makefile ./$(DIR)-$(VERSION)
tar -cz -f $@ ./$(DIR)-$(VERSION)
clean:
rm -f *.tar.gz
rm -f btle_alarm
rm -f *.o
rm -rf btle_alarm-*

356
src/btle_alarm.c Normal file
View file

@ -0,0 +1,356 @@
#include <stdlib.h>
#include <errno.h>
#include <curses.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#define HCI_STATE_NONE 0
#define HCI_STATE_OPEN 2
#define HCI_STATE_SCANNING 3
#define HCI_STATE_FILTERING 4
struct hci_state {
int device_id;
int device_handle;
struct hci_filter original_filter;
int state;
int has_error;
char error_message[1024];
} hci_state;
struct
{
unsigned int battery : 2;
unsigned int panic :1;
unsigned int fall :1;
unsigned int clear :1;
unsigned int reserved :3;
} activePersAlarm;
#define EIR_FLAGS 0X01
#define EIR_NAME_SHORT 0x08
#define EIR_NAME_COMPLETE 0x09
#define EIR_MANUFACTURE_SPECIFIC 0xFF
static void write_call_file(int id);
struct hci_state open_default_hci_device()
{
struct hci_state current_hci_state = {0};
current_hci_state.device_id = hci_get_route(NULL);
if((current_hci_state.device_handle = hci_open_dev(current_hci_state.device_id)) < 0)
{
current_hci_state.has_error = TRUE;
snprintf(current_hci_state.error_message, sizeof(current_hci_state.error_message), "Could not open device: %s", strerror(errno));
return current_hci_state;
}
// Set fd non-blocking
int on = 1;
if(ioctl(current_hci_state.device_handle, FIONBIO, (char *)&on) < 0)
{
current_hci_state.has_error = TRUE;
snprintf(current_hci_state.error_message, sizeof(current_hci_state.error_message), "Could set device to non-blocking: %s", strerror(errno));
return current_hci_state;
}
current_hci_state.state = HCI_STATE_OPEN;
return current_hci_state;
}
void start_hci_scan(struct hci_state current_hci_state)
{
if(hci_le_set_scan_parameters(current_hci_state.device_handle, 0x00, htobs(0x0010), htobs(0x0010), 0x00, 0x01, 1000) < 0)
{
current_hci_state.has_error = TRUE;
snprintf(current_hci_state.error_message, sizeof(current_hci_state.error_message), "Failed to set scan parameters: %s", strerror(errno));
return;
}
if(hci_le_set_scan_enable(current_hci_state.device_handle, 0x01, 1, 1000) < 0)
{
current_hci_state.has_error = TRUE;
snprintf(current_hci_state.error_message, sizeof(current_hci_state.error_message), "Failed to enable scan: %s", strerror(errno));
return;
}
current_hci_state.state = HCI_STATE_SCANNING;
// Save the current HCI filter
socklen_t olen = sizeof(current_hci_state.original_filter);
if(getsockopt(current_hci_state.device_handle, SOL_HCI, HCI_FILTER, &current_hci_state.original_filter, &olen) < 0)
{
current_hci_state.has_error = TRUE;
snprintf(current_hci_state.error_message, sizeof(current_hci_state.error_message), "Could not get socket options: %s", strerror(errno));
return;
}
// Create and set the new filter
struct hci_filter new_filter;
hci_filter_clear(&new_filter);
hci_filter_set_ptype(HCI_EVENT_PKT, &new_filter);
hci_filter_set_event(EVT_LE_META_EVENT, &new_filter);
if(setsockopt(current_hci_state.device_handle, SOL_HCI, HCI_FILTER, &new_filter, sizeof(new_filter)) < 0)
{
current_hci_state.has_error = TRUE;
snprintf(current_hci_state.error_message, sizeof(current_hci_state.error_message), "Could not set socket options: %s", strerror(errno));
return;
}
current_hci_state.state = HCI_STATE_FILTERING;
}
void stop_hci_scan(struct hci_state current_hci_state)
{
if(current_hci_state.state == HCI_STATE_FILTERING)
{
current_hci_state.state = HCI_STATE_SCANNING;
setsockopt(current_hci_state.device_handle, SOL_HCI, HCI_FILTER, &current_hci_state.original_filter, sizeof(current_hci_state.original_filter));
}
if(hci_le_set_scan_enable(current_hci_state.device_handle, 0x00, 1, 1000) < 0)
{
current_hci_state.has_error = TRUE;
snprintf(current_hci_state.error_message, sizeof(current_hci_state.error_message), "Disable scan failed: %s", strerror(errno));
}
current_hci_state.state = HCI_STATE_OPEN;
}
void close_hci_device(struct hci_state current_hci_state)
{
if(current_hci_state.state == HCI_STATE_OPEN)
{
hci_close_dev(current_hci_state.device_handle);
}
}
void error_check_and_exit(struct hci_state current_hci_state)
{
if(current_hci_state.has_error)
{
printf("ERROR: %s\n", current_hci_state.error_message);
endwin();
exit(1);
}
}
void process_data(uint8_t *data, size_t data_len, le_advertising_info *info)
{
int cec=0;
printf("Test: %p and %d\n", data, data_len);
if(data[0] == EIR_NAME_SHORT || data[0] == EIR_NAME_COMPLETE)
{
size_t name_len = data_len - 1;
char *name = malloc(name_len + 1);
memset(name, 0, name_len + 1);
memcpy(name, &data[2], name_len);
char addr[18];
ba2str(&info->bdaddr, addr);
printf("addr=%s name=%s\n", addr, name);
free(name);
}
else if(data[0] == EIR_FLAGS)
{
printf("Flag type: len=%d\n", data_len);
int i;
for(i=1; i<data_len; i++)
{
printf("\tFlag data: 0x%0X\n", data[i]);
}
}
else if(data[0] == EIR_MANUFACTURE_SPECIFIC)
{
printf("Manufacture specific type: len=%d\n", data_len);
// TODO int company_id = data[current_index + 2]
int i;
printf("data : ");
for(i=1; i<data_len; i++)
{
printf("0x%0X ", data[i]);
}
printf("\n\n");
}
else
{
printf("Unknown type: type=%X\n", data[0]);
}
unsigned char d=data[3];
activePersAlarm.battery=d&0x03;
activePersAlarm.panic=activePersAlarm.fall=activePersAlarm.clear=0;
if(0x04 & data[3])
activePersAlarm.panic=1;
if(0x08 & data[3])
activePersAlarm.fall=1;
if(0x10 & data[3])
activePersAlarm.clear=1;
printf("Test: 0x%0X \n Fall: %d\n Alarm: %d\n Battery: %d\n Clear: %d\n",data[3],activePersAlarm.fall,activePersAlarm.panic,activePersAlarm.battery,activePersAlarm.clear);
if((activePersAlarm.panic || activePersAlarm.fall) && !(activePersAlarm.clear))
{
for(cec=0;cec<6;cec++)
write_call_file(cec);
(void)system("mv /tmp/btle_* /var/spool/asterisk/outgoing/.");
}
}
static void write_call_file(int id)
{
char str[94];
char fname[22];
FILE *fp;
sprintf(str,"Channel: BRCM/%d\nMaxRetries: 2\nRetryTime: 60\nWaitTime: 30\nContext: call-file-test\nExtension: %d\0",id,10);
sprintf(fname,"/tmp/btle_alarm_%d.call",id);
fp=fopen(fname,"w");
fwrite(str , 1 , sizeof(str) , fp );
fclose(fp);
}
int get_rssi(bdaddr_t *bdaddr, struct hci_state current_hci_state)
{
struct hci_dev_info di;
if (hci_devinfo(current_hci_state.device_id, &di) < 0) {
perror("Can't get device info");
return(-1);
}
uint16_t handle;
// int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to);
// HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5
if (hci_create_connection(current_hci_state.device_handle, bdaddr, htobs(di.pkt_type & ACL_PTYPE_MASK), 0, 0x01, &handle, 25000) < 0) {
perror("Can't create connection");
// TODO close(dd);
return(-1);
}
sleep(1);
struct hci_conn_info_req *cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
bacpy(&cr->bdaddr, bdaddr);
cr->type = ACL_LINK;
if(ioctl(current_hci_state.device_handle, HCIGETCONNINFO, (unsigned long) cr) < 0) {
perror("Get connection info failed");
return(-1);
}
int8_t rssi;
if(hci_read_rssi(current_hci_state.device_handle, htobs(cr->conn_info->handle), &rssi, 1000) < 0) {
perror("Read RSSI failed");
return(-1);
}
printf("RSSI return value: %d\n", rssi);
free(cr);
usleep(10000);
hci_disconnect(current_hci_state.device_handle, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
}
void main(void)
{
timeout(0);
struct hci_state current_hci_state = open_default_hci_device();
error_check_and_exit(current_hci_state);
start_hci_scan(current_hci_state);
error_check_and_exit(current_hci_state);
printf("Scanning...\n");
int done = FALSE;
int error = FALSE;
while(!done && !error)
{
int len = 0;
unsigned char buf[HCI_MAX_EVENT_SIZE];
while((len = read(current_hci_state.device_handle, buf, sizeof(buf))) < 0)
{
if (errno == EINTR)
{
done = TRUE;
break;
}
if (errno == EAGAIN || errno == EINTR)
{
if(getch() == 'q')
{
done = TRUE;
break;
}
usleep(100);
continue;
}
error = TRUE;
}
if(!done && !error)
{
evt_le_meta_event *meta = (void *)(buf + (1 + HCI_EVENT_HDR_SIZE));
len -= (1 + HCI_EVENT_HDR_SIZE);
if (meta->subevent != EVT_LE_ADVERTISING_REPORT)
{
continue;
}
le_advertising_info *info = (le_advertising_info *) (meta->data + 1);
printf("Event: %d\n", info->evt_type);
printf("Length: %d\n", info->length);
if(info->length == 0)
{
continue;
}
int current_index = 0;
int data_error = 0;
while(!data_error && current_index < info->length)
{
size_t data_len = info->data[current_index];
if(data_len + 1 > info->length)
{
data_error = 1;
}
else
{
process_data(info->data + current_index + 1, data_len, info);
current_index += data_len + 1;
}
}
}
}
if(error)
{
printf("Error scanning.");
}
stop_hci_scan(current_hci_state);
error_check_and_exit(current_hci_state);
close_hci_device(current_hci_state);
}