Skip to content

Commit

Permalink
Adapter abstraction layer for HCI device
Browse files Browse the repository at this point in the history
Add adapter abstraction layer to the internal transport management
stack. Now, it will be possible to handle multiple number of HCI
with single BlueALSA instance. The layer hierarchy looks like this:
adapter (HCI) -> device (BT device) -> transport (audio)
  • Loading branch information
arkq committed Mar 4, 2019
1 parent 6b19b7b commit b3078b2
Show file tree
Hide file tree
Showing 18 changed files with 509 additions and 288 deletions.
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ bluealsa_SOURCES = \
shared/log.c \
shared/rt.c \
at.c \
ba-adapter.c \
ba-device.c \
ba-transport.c \
bluealsa.c \
Expand Down
80 changes: 80 additions & 0 deletions src/ba-adapter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* BlueALSA - ba-adapter.c
* Copyright (c) 2016-2019 Arkadiusz Bokowy
*
* This file is a part of bluez-alsa.
*
* This project is licensed under the terms of the MIT license.
*
*/

#include "ba-adapter.h"

#include <errno.h>
#include <stdio.h>

#include "bluealsa.h"

static guint g_bdaddr_hash(gconstpointer v) {
const bdaddr_t *ba = (const bdaddr_t *)v;
return ((uint32_t *)ba->b)[0] * ((uint16_t *)ba->b)[2];
}

static gboolean g_bdaddr_equal(gconstpointer v1, gconstpointer v2) {
return bacmp(v1, v2) == 0;
}

struct ba_adapter *ba_adapter_new(int dev_id, const char *name) {

struct ba_adapter *a;

/* make sure we are within array boundaries */
if (dev_id < 0 || dev_id >= HCI_MAX_DEV) {
errno = EINVAL;
return NULL;
}

if ((a = calloc(1, sizeof(*a))) == NULL)
return NULL;

a->hci_dev_id = dev_id;

if (name != NULL)
strncpy(a->hci_name, name, sizeof(a->hci_name) - 1);
else
sprintf(a->hci_name, "hci%d", dev_id);

pthread_mutex_init(&a->devices_mutex, NULL);
a->devices = g_hash_table_new_full(g_bdaddr_hash, g_bdaddr_equal, NULL, NULL);

config.adapters[a->hci_dev_id] = a;
return a;
}

struct ba_adapter *ba_adapter_lookup(int dev_id) {
if (dev_id >= 0 && dev_id < HCI_MAX_DEV)
return config.adapters[dev_id];
return NULL;
}

void ba_adapter_free(struct ba_adapter *a) {

/* detach adapter from global configuration */
config.adapters[a->hci_dev_id] = NULL;

if (a->devices != NULL) {

GHashTableIter iter;
struct ba_device *d;

g_hash_table_iter_init(&iter, a->devices);
while (g_hash_table_iter_next(&iter, NULL, (gpointer)&d))
ba_device_free(d);

g_hash_table_unref(a->devices);
}

pthread_mutex_destroy(&a->devices_mutex);

free(a);
}
40 changes: 40 additions & 0 deletions src/ba-adapter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* BlueALSA - ba-adapter.h
* Copyright (c) 2016-2019 Arkadiusz Bokowy
*
* This file is a part of bluez-alsa.
*
* This project is licensed under the terms of the MIT license.
*
*/

#ifndef BLUEALSA_BAADAPTER_H
#define BLUEALSA_BAADAPTER_H

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <pthread.h>

#include <glib.h>

#include "ba-device.h"

/* Data associated with BT adapter. */
struct ba_adapter {

int hci_dev_id;
char hci_name[8];

/* collection of connected devices */
pthread_mutex_t devices_mutex;
GHashTable *devices;

};

struct ba_adapter *ba_adapter_new(int dev_id, const char *name);
struct ba_adapter *ba_adapter_lookup(int dev_id);
void ba_adapter_free(struct ba_adapter *a);

#endif
24 changes: 19 additions & 5 deletions src/ba-device.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@

#include "ba-device.h"

#include <errno.h>
#include <stdlib.h>

#include "ba-transport.h"
#include "bluealsa.h"
#include "ctl.h"

struct ba_device *ba_device_new(
int hci_dev_id,
struct ba_adapter *adapter,
const bdaddr_t *addr,
const char *name) {

Expand All @@ -26,21 +27,34 @@ struct ba_device *ba_device_new(
if ((d = calloc(1, sizeof(*d))) == NULL)
return NULL;

d->hci_dev_id = hci_dev_id;
d->a = adapter;
bacpy(&d->addr, addr);
strncpy(d->name, name, sizeof(d->name) - 1);

d->transports = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, (GDestroyNotify)transport_free);
d->transports = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);

g_hash_table_insert(adapter->devices, &d->addr, d);
return d;
}

struct ba_device *ba_device_lookup(
struct ba_adapter *adapter,
const bdaddr_t *addr) {
#if DEBUG
/* make sure that the device mutex is acquired */
g_assert(pthread_mutex_trylock(&adapter->devices_mutex) == EBUSY);
#endif
return g_hash_table_lookup(adapter->devices, addr);
}

void ba_device_free(struct ba_device *d) {

if (d == NULL)
return;

/* detach device from the adapter */
g_hash_table_steal(d->a->devices, &d->addr);

/* XXX: Modification-safe remove-all loop.
*
* By the usage of a standard g_hash_table_remove_all() function, one
Expand All @@ -64,7 +78,7 @@ void ba_device_free(struct ba_device *d) {
if (!g_hash_table_iter_next(&iter, NULL, (gpointer)&t))
break;

transport_free(t);
ba_transport_free(t);
}

g_hash_table_unref(d->transports);
Expand Down
14 changes: 11 additions & 3 deletions src/ba-device.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
#include <bluetooth/hci.h>
#include <glib.h>

#include "ba-adapter.h"

struct ba_device {

/* ID of the underlying HCI device */
int hci_dev_id;
/* backward reference to adapter */
struct ba_adapter *a;

/* address of the Bluetooth device */
bdaddr_t addr;
/* human-readable Bluetooth device name */
Expand Down Expand Up @@ -56,9 +59,14 @@ struct ba_device {
};

struct ba_device *ba_device_new(
int hci_dev_id,
struct ba_adapter *adapter,
const bdaddr_t *addr,
const char *name);

struct ba_device *ba_device_lookup(
struct ba_adapter *adapter,
const bdaddr_t *addr);

void ba_device_free(struct ba_device *d);

void ba_device_set_battery_level(struct ba_device *d, uint8_t value);
Expand Down
75 changes: 17 additions & 58 deletions src/ba-transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ struct ba_transport *transport_new(

fail:
err = errno;
transport_free(t);
ba_transport_free(t);
errno = err;
return NULL;
}
Expand Down Expand Up @@ -240,7 +240,7 @@ struct ba_transport *transport_new_rfcomm(
fail:
if (dbus_path_sco != NULL)
g_free(dbus_path_sco);
transport_free(t);
ba_transport_free(t);
return NULL;
}

Expand Down Expand Up @@ -280,7 +280,17 @@ struct ba_transport *transport_new_sco(
return t;
}

void transport_free(struct ba_transport *t) {
struct ba_transport *ba_transport_lookup(
struct ba_device *device,
const char *dbus_path) {
#if DEBUG
/* make sure that the device mutex is acquired */
g_assert(pthread_mutex_trylock(&device->a->devices_mutex) == EBUSY);
#endif
return g_hash_table_lookup(device->transports, dbus_path);
}

void ba_transport_free(struct ba_transport *t) {

if (t == NULL || t->state == TRANSPORT_LIMBO)
return;
Expand Down Expand Up @@ -323,7 +333,7 @@ void transport_free(struct ba_transport *t) {
case BA_TRANSPORT_PROFILE_RFCOMM:
memset(&t->d->battery, 0, sizeof(t->d->battery));
memset(&t->d->xapl, 0, sizeof(t->d->xapl));
transport_free(t->rfcomm.sco);
ba_transport_free(t->rfcomm.sco);
break;
case BA_TRANSPORT_PROFILE_HFP_HF:
case BA_TRANSPORT_PROFILE_HFP_AG:
Expand All @@ -339,9 +349,7 @@ void transport_free(struct ba_transport *t) {
break;
}

/* If the free action was called on the behalf of the destroy notification,
* removing a value from the hash-table shouldn't hurt - it would have been
* removed anyway. */
/* detach transport from the device */
g_hash_table_steal(t->d->transports, t->dbus_path);

if (pcm_type != BA_PCM_TYPE_NULL)
Expand All @@ -352,55 +360,6 @@ void transport_free(struct ba_transport *t) {
free(t);
}

struct ba_transport *transport_lookup(GHashTable *devices, const char *dbus_path) {

#if DEBUG
/* make sure that the device mutex is acquired */
g_assert(pthread_mutex_trylock(&config.devices_mutex) == EBUSY);
#endif

GHashTableIter iter;
struct ba_device *d;
struct ba_transport *t;

g_hash_table_iter_init(&iter, devices);
while (g_hash_table_iter_next(&iter, NULL, (gpointer)&d)) {
if ((t = g_hash_table_lookup(d->transports, dbus_path)) != NULL)
return t;
}

return NULL;
}

bool transport_remove(GHashTable *devices, const char *dbus_path) {

#if DEBUG
/* make sure that the device mutex is acquired */
g_assert(pthread_mutex_trylock(&config.devices_mutex) == EBUSY);
#endif

GHashTableIter iter;
struct ba_device *d;
struct ba_transport *t;

g_hash_table_iter_init(&iter, devices);
while (g_hash_table_iter_next(&iter, NULL, (gpointer)&d)) {
/* Disassociate D-Bus owner before further actions. This will ensure,
* that we will not generate errors by using non-existent interface. */
if ((t = g_hash_table_lookup(d->transports, dbus_path)) != NULL) {
free(t->dbus_owner);
t->dbus_owner = NULL;
}
if (g_hash_table_remove(d->transports, dbus_path)) {
if (g_hash_table_size(d->transports) == 0)
g_hash_table_iter_remove(&iter);
return true;
}
}

return false;
}

int transport_send_signal(struct ba_transport *t, enum ba_transport_signal sig) {
return write(t->sig_fd[1], &sig, sizeof(sig));
}
Expand Down Expand Up @@ -807,7 +766,7 @@ static int transport_release_bt_rfcomm(struct ba_transport *t) {
/* BlueZ does not trigger profile disconnection signal when the Bluetooth
* link has been lost (e.g. device power down). However, it is required to
* remove transport from the transport pool before reconnecting. */
transport_free(t);
ba_transport_free(t);

return 0;
}
Expand All @@ -819,7 +778,7 @@ static int transport_acquire_bt_sco(struct ba_transport *t) {
if (t->bt_fd != -1)
return t->bt_fd;

if (hci_devinfo(t->d->hci_dev_id, &di) == -1) {
if (hci_devinfo(t->d->a->hci_dev_id, &di) == -1) {
error("Couldn't get HCI device info: %s", strerror(errno));
return -1;
}
Expand Down
8 changes: 5 additions & 3 deletions src/ba-transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,12 @@ struct ba_transport *transport_new_sco(
struct ba_transport_type type,
const char *dbus_owner,
const char *dbus_path);
void transport_free(struct ba_transport *t);

struct ba_transport *transport_lookup(GHashTable *devices, const char *dbus_path);
bool transport_remove(GHashTable *devices, const char *dbus_path);
struct ba_transport *ba_transport_lookup(
struct ba_device *device,
const char *dbus_path);

void ba_transport_free(struct ba_transport *t);

int transport_send_signal(struct ba_transport *t, enum ba_transport_signal sig);
int transport_send_rfcomm(struct ba_transport *t, const char command[32]);
Expand Down
4 changes: 0 additions & 4 deletions src/bluealsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ int bluealsa_config_init(void) {

config.main_thread = pthread_self();

pthread_mutex_init(&config.devices_mutex, NULL);
config.devices = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, (GDestroyNotify)ba_device_free);

config.dbus_objects = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, g_free);

Expand Down
Loading

0 comments on commit b3078b2

Please sign in to comment.