/*
|
* BlueALSA - transport.h
|
* Copyright (c) 2016-2018 Arkadiusz Bokowy
|
*
|
* This file is a part of bluez-alsa.
|
*
|
* This project is licensed under the terms of the MIT license.
|
*
|
*/
|
|
#ifndef BLUEALSA_TRANSPORT_H_
|
#define BLUEALSA_TRANSPORT_H_
|
|
#include <pthread.h>
|
#include <stdbool.h>
|
#include <stddef.h>
|
#include <stdint.h>
|
|
#include <bluetooth/bluetooth.h>
|
#include <bluetooth/hci.h>
|
#include <glib.h>
|
|
#include "bluez.h"
|
#include "hfp.h"
|
|
enum ba_transport_type {
|
TRANSPORT_TYPE_A2DP,
|
TRANSPORT_TYPE_RFCOMM,
|
TRANSPORT_TYPE_SCO,
|
};
|
|
enum ba_transport_state {
|
TRANSPORT_IDLE,
|
TRANSPORT_PENDING,
|
TRANSPORT_ACTIVE,
|
TRANSPORT_PAUSED,
|
/* transport is in the eviction state */
|
TRANSPORT_LIMBO,
|
};
|
|
enum ba_transport_signal {
|
TRANSPORT_PCM_OPEN,
|
TRANSPORT_PCM_CLOSE,
|
TRANSPORT_PCM_PAUSE,
|
TRANSPORT_PCM_RESUME,
|
TRANSPORT_PCM_SYNC,
|
TRANSPORT_SET_VOLUME,
|
TRANSPORT_SEND_RFCOMM,
|
};
|
|
struct ba_device {
|
|
/* ID of the underlying HCI device */
|
int hci_dev_id;
|
/* address of the Bluetooth device */
|
bdaddr_t addr;
|
/* human-readable Bluetooth device name */
|
char name[HCI_MAX_NAME_LENGTH];
|
|
/* adjusted (in the range 0-100) battery level */
|
struct {
|
bool enabled;
|
uint8_t level;
|
} battery;
|
|
/* Apple's extension used with HFP profile */
|
struct {
|
|
uint16_t vendor_id;
|
uint16_t product_id;
|
uint16_t version;
|
uint8_t features;
|
|
/* determine whether headset is docked */
|
uint8_t accev_docked;
|
|
} xapl;
|
|
/* hash-map with connected transports */
|
GHashTable *transports;
|
|
};
|
|
struct ba_pcm {
|
|
int fd;
|
|
/* client identifier (most likely client socket file descriptor) used
|
* by the PCM client lookup function - transport_lookup_pcm_client() */
|
int client;
|
|
/* variables used for PCM synchronization */
|
pthread_cond_t drained;
|
pthread_mutex_t drained_mn;
|
|
};
|
|
struct ba_transport {
|
|
/* backward reference to the owner */
|
struct ba_device *device;
|
|
/* Transport structure covers all transports supported by BlueALSA. However,
|
* every transport requires specific handling - link acquisition, transport
|
* specific configuration, freeing resources, etc. */
|
enum ba_transport_type type;
|
|
/* data required for D-Bus management */
|
char *dbus_owner;
|
char *dbus_path;
|
|
/* Selected profile and audio codec. For A2DP vendor codecs the upper byte
|
* of the codec field contains the lowest byte of the vendor ID. */
|
enum bluetooth_profile profile;
|
uint16_t codec;
|
|
/* This mutex shall guard modifications of the critical sections in this
|
* transport structure, e.g. thread creation/termination. */
|
pthread_mutex_t mutex;
|
|
/* IO thread - actual transport layer */
|
enum ba_transport_state state;
|
pthread_t thread;
|
|
/* This field stores a file descriptor (socket) associated with the BlueZ
|
* side of the transport. The role of this socket depends on the transport
|
* type - it can be either A2DP, RFCOMM or SCO link. */
|
int bt_fd;
|
|
/* max transfer unit values for bt_fd */
|
size_t mtu_read;
|
size_t mtu_write;
|
|
/* PIPE used to notify thread about changes. If thread is based on loop with
|
* an event wait syscall (e.g. poll), this file descriptor is used to send a
|
* control event. */
|
int sig_fd[2];
|
|
/* Overall delay in 1/10 of millisecond, caused by the data transfer and
|
* the audio encoder or decoder. */
|
unsigned int delay;
|
|
union {
|
|
struct {
|
|
/* if non-zero, equivalent of volume = 0 */
|
uint8_t ch1_muted;
|
uint8_t ch2_muted;
|
/* software audio volume in range [0, 127] */
|
uint8_t ch1_volume;
|
uint8_t ch2_volume;
|
|
/* delay reported by the AVDTP */
|
uint16_t delay;
|
|
struct ba_pcm pcm;
|
|
/* selected audio codec configuration */
|
uint8_t *cconfig;
|
size_t cconfig_size;
|
|
/* Value reported by the ioctl(TIOCOUTQ) when the output buffer is
|
* empty. Somehow this ioctl call reports "available" buffer space.
|
* So, in order to get the number of bytes in the queue buffer, we
|
* have to subtract the initial value from values returned by
|
* subsequent ioctl() calls. */
|
int bt_fd_coutq_init;
|
|
} a2dp;
|
|
struct {
|
|
/* associated SCO transport */
|
struct ba_transport *sco;
|
|
/* AG/HF supported features bitmask */
|
uint32_t hfp_features;
|
/* received AG indicator values */
|
unsigned char hfp_inds[__HFP_IND_MAX];
|
|
} rfcomm;
|
|
struct {
|
|
/* parent RFCOMM transport */
|
struct ba_transport *rfcomm;
|
|
/* if true, equivalent of gain = 0 */
|
bool spk_muted;
|
bool mic_muted;
|
/* software audio gain in range [0, 15] */
|
uint8_t spk_gain;
|
uint8_t mic_gain;
|
|
/* Speaker and microphone signals should to be exposed as
|
* a separate PCM devices. Hence, there is a requirement
|
* for separate configurations. */
|
struct ba_pcm spk_pcm;
|
struct ba_pcm mic_pcm;
|
|
int listen_fd;
|
|
} sco;
|
|
};
|
|
/* indicates cleanup lock */
|
bool cleanup_lock;
|
|
/* callback function for self-management */
|
int (*release)(struct ba_transport *);
|
|
};
|
|
struct ba_device *device_new(int hci_dev_id, const bdaddr_t *addr, const char *name);
|
void device_free(struct ba_device *d);
|
|
struct ba_device *device_get(GHashTable *devices, const char *key);
|
struct ba_device *device_lookup(GHashTable *devices, const char *key);
|
bool device_remove(GHashTable *devices, const char *key);
|
|
void device_set_battery_level(struct ba_device *d, uint8_t value);
|
|
struct ba_transport *transport_new(
|
struct ba_device *device,
|
enum ba_transport_type type,
|
const char *dbus_owner,
|
const char *dbus_path,
|
enum bluetooth_profile profile,
|
uint16_t codec);
|
struct ba_transport *transport_new_a2dp(
|
struct ba_device *device,
|
const char *dbus_owner,
|
const char *dbus_path,
|
enum bluetooth_profile profile,
|
uint16_t codec,
|
const uint8_t *config,
|
size_t config_size);
|
struct ba_transport *transport_new_rfcomm(
|
struct ba_device *device,
|
const char *dbus_owner,
|
const char *dbus_path,
|
enum bluetooth_profile profile);
|
void transport_free(struct ba_transport *t);
|
|
struct ba_transport *transport_lookup(GHashTable *devices, const char *dbus_path);
|
struct ba_transport *transport_lookup_pcm_client(GHashTable *devices, int client);
|
bool transport_remove(GHashTable *devices, const char *dbus_path);
|
|
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]);
|
|
unsigned int transport_get_channels(const struct ba_transport *t);
|
unsigned int transport_get_sampling(const struct ba_transport *t);
|
|
int transport_set_volume(struct ba_transport *t, uint8_t ch1_muted, uint8_t ch2_muted,
|
uint8_t ch1_volume, uint8_t ch2_volume);
|
|
int transport_set_state(struct ba_transport *t, enum ba_transport_state state);
|
int transport_set_state_from_string(struct ba_transport *t, const char *state);
|
|
int transport_drain_pcm(struct ba_transport *t);
|
|
int transport_acquire_bt_a2dp(struct ba_transport *t);
|
int transport_release_bt_a2dp(struct ba_transport *t);
|
|
int transport_release_bt_rfcomm(struct ba_transport *t);
|
|
int transport_acquire_bt_sco(struct ba_transport *t);
|
int transport_acquire_bt_sco2(struct ba_transport *t, int asock);
|
int transport_release_bt_sco(struct ba_transport *t);
|
|
int transport_release_pcm(struct ba_pcm *pcm);
|
|
void transport_pthread_cancel(pthread_t thread);
|
void transport_pthread_cleanup(struct ba_transport *t);
|
int transport_pthread_cleanup_lock(struct ba_transport *t);
|
int transport_pthread_cleanup_unlock(struct ba_transport *t);
|
|
#endif
|