Skip to content

Commit

Permalink
Register MIDI application GATT server in BlueZ
Browse files Browse the repository at this point in the history
  • Loading branch information
arkq committed Sep 3, 2023
1 parent 1dbf03b commit e5f0faa
Show file tree
Hide file tree
Showing 18 changed files with 622 additions and 46 deletions.
2 changes: 2 additions & 0 deletions .github/spellcheck-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ dpsnk
dpsrc
DYN
EAGAIN
EDE
EINVAL
ENODEV
EP
Expand All @@ -172,6 +173,7 @@ ioplug
IPHONEACCEV
iter
keyp
LEAdvertisement
LF
LFE
LHDC
Expand Down
5 changes: 5 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ bluealsa_SOURCES += \
a2dp-ldac.c
endif

if ENABLE_MIDI
bluealsa_SOURCES += \
bluez-midi.c
endif

if ENABLE_MPEG
bluealsa_SOURCES += \
a2dp-mpeg.c
Expand Down
8 changes: 3 additions & 5 deletions src/ba-transport-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ static const char *transport_get_dbus_path_type(

int transport_pcm_init(
struct ba_transport_pcm *pcm,
struct ba_transport_thread *th,
enum ba_transport_pcm_mode mode) {
enum ba_transport_pcm_mode mode,
struct ba_transport_thread *th) {

struct ba_transport *t = th->t;

Expand Down Expand Up @@ -110,9 +110,7 @@ void transport_pcm_free(
pthread_cond_destroy(&pcm->cond);

g_hash_table_unref(pcm->delay_adjustments);

if (pcm->ba_dbus_path != NULL)
g_free(pcm->ba_dbus_path);
g_free(pcm->ba_dbus_path);

}

Expand Down
4 changes: 2 additions & 2 deletions src/ba-transport-pcm.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ struct ba_transport_pcm {

int transport_pcm_init(
struct ba_transport_pcm *pcm,
struct ba_transport_thread *th,
enum ba_transport_pcm_mode mode);
enum ba_transport_pcm_mode mode,
struct ba_transport_thread *th);
void transport_pcm_free(
struct ba_transport_pcm *pcm);

Expand Down
68 changes: 60 additions & 8 deletions src/ba-transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,13 +693,13 @@ struct ba_transport *ba_transport_new_a2dp(
t->a2dp.state = BLUEZ_A2DP_TRANSPORT_STATE_IDLE;

transport_pcm_init(&t->a2dp.pcm,
is_sink ? &t->thread_dec : &t->thread_enc,
is_sink ? BA_TRANSPORT_PCM_MODE_SOURCE : BA_TRANSPORT_PCM_MODE_SINK);
is_sink ? BA_TRANSPORT_PCM_MODE_SOURCE : BA_TRANSPORT_PCM_MODE_SINK,
is_sink ? &t->thread_dec : &t->thread_enc);
t->a2dp.pcm.soft_volume = !config.a2dp.volume;

transport_pcm_init(&t->a2dp.pcm_bc,
is_sink ? &t->thread_enc : &t->thread_dec,
is_sink ? BA_TRANSPORT_PCM_MODE_SINK : BA_TRANSPORT_PCM_MODE_SOURCE);
is_sink ? BA_TRANSPORT_PCM_MODE_SINK : BA_TRANSPORT_PCM_MODE_SOURCE,
is_sink ? &t->thread_enc : &t->thread_dec);
t->a2dp.pcm_bc.soft_volume = !config.a2dp.volume;

t->acquire = transport_acquire_bt_a2dp;
Expand Down Expand Up @@ -808,12 +808,12 @@ struct ba_transport *ba_transport_new_sco(
t->profile = profile;

transport_pcm_init(&t->sco.pcm_spk,
is_ag ? &t->thread_enc : &t->thread_dec,
is_ag ? BA_TRANSPORT_PCM_MODE_SINK : BA_TRANSPORT_PCM_MODE_SOURCE);
is_ag ? BA_TRANSPORT_PCM_MODE_SINK : BA_TRANSPORT_PCM_MODE_SOURCE,
is_ag ? &t->thread_enc : &t->thread_dec);

transport_pcm_init(&t->sco.pcm_mic,
is_ag ? &t->thread_dec : &t->thread_enc,
is_ag ? BA_TRANSPORT_PCM_MODE_SOURCE : BA_TRANSPORT_PCM_MODE_SINK);
is_ag ? BA_TRANSPORT_PCM_MODE_SOURCE : BA_TRANSPORT_PCM_MODE_SINK,
is_ag ? &t->thread_dec : &t->thread_enc);

t->acquire = transport_acquire_bt_sco;
t->release = transport_release_bt_sco;
Expand Down Expand Up @@ -857,6 +857,54 @@ struct ba_transport *ba_transport_new_sco(
return NULL;
}

#if ENABLE_MIDI

static int transport_acquire_bt_midi(struct ba_transport *t) {

int fds[2];
if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, fds) == -1)
return -1;

debug("New BLE-MIDI link: %d", fds[0]);

t->bt_fd = fds[0];
t->midi.fd = fds[1];

return 0;
}

static int transport_release_bt_midi(struct ba_transport *t) {

debug("Releasing BLE-MIDI link: %d", t->bt_fd);

close(t->bt_fd);
t->bt_fd = -1;
close(t->midi.fd);
t->midi.fd = -1;

return 0;
}

struct ba_transport *ba_transport_new_midi(
struct ba_device *device,
enum ba_transport_profile profile,
const char *dbus_owner,
const char *dbus_path) {

struct ba_transport *t;
if ((t = transport_new(device, dbus_owner, dbus_path)) == NULL)
return NULL;

t->profile = profile;

t->acquire = transport_acquire_bt_midi;
t->release = transport_release_bt_midi;

return t;
}

#endif

#if DEBUG
/**
* Get BlueALSA transport type debug name.
Expand Down Expand Up @@ -958,6 +1006,10 @@ const char *ba_transport_debug_name(
return "HSP Headset";
case BA_TRANSPORT_PROFILE_HSP_AG:
return "HSP Audio Gateway";
#if ENABLE_MIDI
case BA_TRANSPORT_PROFILE_MIDI:
return "MIDI";
#endif
}
debug("Unknown transport: profile:%#x codec:%#x", profile, codec_id);
return "N/A";
Expand Down
22 changes: 21 additions & 1 deletion src/ba-transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ enum ba_transport_profile {
BA_TRANSPORT_PROFILE_HFP_AG = (2 << 2),
BA_TRANSPORT_PROFILE_HSP_HS = (1 << 4),
BA_TRANSPORT_PROFILE_HSP_AG = (2 << 4),
#if ENABLE_MIDI
BA_TRANSPORT_PROFILE_MIDI = (1 << 6),
#endif
};

#define BA_TRANSPORT_PROFILE_MASK_A2DP \
Expand Down Expand Up @@ -178,7 +181,7 @@ struct ba_transport {

/* 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 or SCO link. */
* type - it can be either A2DP, MIDI or SCO link. */
int bt_fd;

/* max transfer unit values for bt_fd */
Expand Down Expand Up @@ -258,6 +261,16 @@ struct ba_transport {

} sco;

#if ENABLE_MIDI
struct {

/* The rare end of the BLE-MIDI link. This FD is passed to the BlueZ
* GATT subsystem which is responsible for BT data transport. */
int fd;

} midi;
#endif

};

/* callback functions for self-management */
Expand All @@ -282,6 +295,13 @@ struct ba_transport *ba_transport_new_sco(
const char *dbus_owner,
const char *dbus_path,
int rfcomm_fd);
#if ENABLE_MIDI
struct ba_transport *ba_transport_new_midi(
struct ba_device *device,
enum ba_transport_profile profile,
const char *dbus_owner,
const char *dbus_path);
#endif

#if DEBUG
const char *ba_transport_debug_name(
Expand Down
1 change: 1 addition & 0 deletions src/bluealsa-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct ba_config {
bool hfp_ag;
bool hsp_hs;
bool hsp_ag;
bool midi;
} profile;

/* established D-Bus connection */
Expand Down
7 changes: 7 additions & 0 deletions src/bluealsa-dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ static GVariant *ba_variant_new_bluealsa_profiles(void) {
{ BLUEALSA_TRANSPORT_TYPE_HFP_HF, config.profile.hfp_hf },
{ BLUEALSA_TRANSPORT_TYPE_HSP_AG, config.profile.hsp_ag },
{ BLUEALSA_TRANSPORT_TYPE_HSP_HS, config.profile.hsp_hs },
#if ENABLE_MIDI
{ BLUEALSA_TRANSPORT_TYPE_MIDI, config.profile.midi },
#endif
};

const char *strv[ARRAYSIZE(profiles)];
Expand Down Expand Up @@ -195,6 +198,10 @@ static GVariant *ba_variant_new_transport_type(const struct ba_transport *t) {
return g_variant_new_string(BLUEALSA_TRANSPORT_TYPE_HSP_AG);
case BA_TRANSPORT_PROFILE_HSP_HS:
return g_variant_new_string(BLUEALSA_TRANSPORT_TYPE_HSP_HS);
#if ENABLE_MIDI
case BA_TRANSPORT_PROFILE_MIDI:
return g_variant_new_string(BLUEALSA_TRANSPORT_TYPE_MIDI);
#endif
case BA_TRANSPORT_PROFILE_NONE:
break;
}
Expand Down
5 changes: 1 addition & 4 deletions src/bluealsa-iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#define BLUEALSA_TRANSPORT_TYPE_HSP "HSP"
#define BLUEALSA_TRANSPORT_TYPE_HSP_AG BLUEALSA_TRANSPORT_TYPE_HSP "-AG"
#define BLUEALSA_TRANSPORT_TYPE_HSP_HS BLUEALSA_TRANSPORT_TYPE_HSP "-HS"
#define BLUEALSA_TRANSPORT_TYPE_MIDI "MIDI"

#define BLUEALSA_PCM_CTRL_DRAIN "Drain"
#define BLUEALSA_PCM_CTRL_DROP "Drop"
Expand Down Expand Up @@ -66,8 +67,4 @@ OrgBluealsaRfcomm1Skeleton *org_bluealsa_rfcomm1_skeleton_new(
const GDBusInterfaceSkeletonVTable *vtable, void *userdata,
GDestroyNotify userdata_free_func);

extern const GDBusInterfaceInfo org_bluealsa_manager1_interface;
extern const GDBusInterfaceInfo org_bluealsa_pcm1_interface;
extern const GDBusInterfaceInfo org_bluealsa_rfcomm1_interface;

#endif
34 changes: 30 additions & 4 deletions src/bluez-iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
#ifndef BLUEALSA_BLUEZIFACE_H_
#define BLUEALSA_BLUEZIFACE_H_

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

#include <gio/gio.h>
#include <glib.h>

Expand All @@ -23,6 +27,12 @@
#define BLUEZ_IFACE_BATTERY_PROVIDER BLUEZ_SERVICE ".BatteryProvider1"
#define BLUEZ_IFACE_BATTERY_PROVIDER_MANAGER BLUEZ_SERVICE ".BatteryProviderManager1"
#define BLUEZ_IFACE_DEVICE BLUEZ_SERVICE ".Device1"
#define BLUEZ_IFACE_GATT_CHARACTERISTIC BLUEZ_SERVICE ".GattCharacteristic1"
#define BLUEZ_IFACE_GATT_MANAGER BLUEZ_SERVICE ".GattManager1"
#define BLUEZ_IFACE_GATT_PROFILE BLUEZ_SERVICE ".GattProfile1"
#define BLUEZ_IFACE_GATT_SERVICE BLUEZ_SERVICE ".GattService1"
#define BLUEZ_IFACE_LE_ADVERTISEMENT BLUEZ_SERVICE ".LEAdvertisement1"
#define BLUEZ_IFACE_LE_ADVERTISING_MANAGER BLUEZ_SERVICE ".LEAdvertisingManager1"
#define BLUEZ_IFACE_MEDIA BLUEZ_SERVICE ".Media1"
#define BLUEZ_IFACE_MEDIA_ENDPOINT BLUEZ_SERVICE ".MediaEndpoint1"
#define BLUEZ_IFACE_MEDIA_TRANSPORT BLUEZ_SERVICE ".MediaTransport1"
Expand All @@ -41,6 +51,26 @@ OrgBluezBatteryProvider1Skeleton *org_bluez_battery_provider1_skeleton_new(
const GDBusInterfaceSkeletonVTable *vtable, void *userdata,
GDestroyNotify userdata_free_func);

#if ENABLE_MIDI

typedef struct {
GDBusInterfaceSkeletonEx parent;
} OrgBluezGattCharacteristic1Skeleton;

OrgBluezGattCharacteristic1Skeleton *org_bluez_gatt_characteristic1_skeleton_new(
const GDBusInterfaceSkeletonVTable *vtable, void *userdata,
GDestroyNotify userdata_free_func);

typedef struct {
GDBusInterfaceSkeletonEx parent;
} OrgBluezGattService1Skeleton;

OrgBluezGattService1Skeleton *org_bluez_gatt_service1_skeleton_new(
const GDBusInterfaceSkeletonVTable *vtable, void *userdata,
GDestroyNotify userdata_free_func);

#endif

typedef struct {
GDBusInterfaceSkeletonEx parent;
} OrgBluezMediaEndpoint1Skeleton;
Expand All @@ -57,8 +87,4 @@ OrgBluezProfile1Skeleton *org_bluez_profile1_skeleton_new(
const GDBusInterfaceSkeletonVTable *vtable, void *userdata,
GDestroyNotify userdata_free_func);

extern const GDBusInterfaceInfo org_bluez_battery_provider1_interface;
extern const GDBusInterfaceInfo org_bluez_media_endpoint1_interface;
extern const GDBusInterfaceInfo org_bluez_profile1_interface;

#endif
27 changes: 27 additions & 0 deletions src/bluez-iface.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,33 @@
the first interface must have at least one method.
-->

<interface name="org.bluez.GattCharacteristic1">
<method name="ReadValue">
<arg direction="in" type="a{sv}" name="options"/>
<arg direction="out" type="ay" name="value"/>
</method>
<method name="AcquireWrite">
<arg direction="in" type="a{sv}" name="options"/>
<arg direction="out" type="h" name="fd"/>
<arg direction="out" type="q" name="mtu"/>
</method>
<method name="AcquireNotify">
<arg direction="in" type="a{sv}" name="options"/>
<arg direction="out" type="h" name="fd"/>
<arg direction="out" type="q" name="mtu"/>
</method>
<property name="UUID" type="s" access="read"/>
<property name="Service" type="o" access="read"/>
<property name="WriteAcquired" type="b" access="read"/>
<property name="NotifyAcquired" type="b" access="read"/>
<property name="Flags" type="as" access="read"/>
</interface>

<interface name="org.bluez.GattService1">
<property name="UUID" type="s" access="read"/>
<property name="Primary" type="b" access="read"/>
</interface>

<interface name="org.bluez.MediaEndpoint1">
<method name="SelectConfiguration">
<arg direction="in" type="ay" name="capabilities"/>
Expand Down
Loading

0 comments on commit e5f0faa

Please sign in to comment.