mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
bluez5: midi: put node nick in GATT Characteristic User Description
As server, put node nick in GATT Characteristic User Description, so that clients have some information which endpoint is which in case we have multiple. This is not in BLE MIDI standard, but cannot hurt. As client, make use of that information, if present.
This commit is contained in:
parent
2ef126885a
commit
e13fed1a80
4 changed files with 319 additions and 8 deletions
|
|
@ -111,18 +111,29 @@ struct chr
|
|||
{
|
||||
struct spa_dbus_object object;
|
||||
char *service_path;
|
||||
char *description;
|
||||
uint32_t id;
|
||||
DBusPendingCall *read_call;
|
||||
DBusPendingCall *dsc_call;
|
||||
unsigned int node_emitted:1;
|
||||
unsigned int valid_uuid:1;
|
||||
unsigned int read_probed:1;
|
||||
unsigned int read_done:1;
|
||||
unsigned int dsc_probed:1;
|
||||
unsigned int dsc_done:1;
|
||||
};
|
||||
|
||||
struct dsc
|
||||
{
|
||||
struct spa_dbus_object object;
|
||||
char *chr_path;
|
||||
unsigned int valid_uuid:1;
|
||||
};
|
||||
|
||||
static void emit_chr_node(struct impl *impl, struct chr *chr, struct device *device)
|
||||
{
|
||||
struct spa_device_object_info info;
|
||||
char class[16];
|
||||
char nick[512], class[16];
|
||||
struct spa_dict_item items[23];
|
||||
uint32_t n_items = 0;
|
||||
|
||||
|
|
@ -140,6 +151,10 @@ static void emit_chr_node(struct impl *impl, struct chr *chr, struct device *dev
|
|||
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Midi/Bridge");
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT("node.description",
|
||||
device->alias ? device->alias : device->name);
|
||||
if (chr->description && chr->description[0] != '\0') {
|
||||
spa_scnprintf(nick, sizeof(nick), "%s (%s)", device->alias, chr->description);
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT("node.nick", nick);
|
||||
}
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_ICON, device->icon);
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_PATH, chr->object.path);
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_ADDRESS, device->address);
|
||||
|
|
@ -214,11 +229,109 @@ static int read_probe(struct impl *impl, struct chr *chr)
|
|||
read_probe_reply);
|
||||
}
|
||||
|
||||
struct dsc *find_dsc(struct impl *impl, struct chr *chr)
|
||||
{
|
||||
struct dsc *dsc;
|
||||
const struct spa_list *dscs = spa_dbus_monitor_object_list(
|
||||
impl->dbus_monitor, BLUEZ_GATT_DSC_INTERFACE);
|
||||
|
||||
spa_assert(dscs);
|
||||
spa_list_for_each(dsc, dscs, object.link) {
|
||||
if (dsc->valid_uuid && spa_streq(dsc->chr_path, chr->object.path))
|
||||
return dsc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void read_dsc_reply(DBusPendingCall **call_ptr, DBusMessage *r)
|
||||
{
|
||||
struct chr *chr = SPA_CONTAINER_OF(call_ptr, struct chr, dsc_call);
|
||||
struct impl *impl = chr->object.user_data;
|
||||
DBusError err;
|
||||
DBusMessageIter args, arr;
|
||||
void *value = NULL;
|
||||
int n = 0;
|
||||
|
||||
chr->dsc_done = true;
|
||||
|
||||
dbus_error_init(&err);
|
||||
if (dbus_set_error_from_message(&err, r)) {
|
||||
spa_log_error(impl->log, "%s.ReadValue() failed: %s (%s)",
|
||||
BLUEZ_GATT_DSC_INTERFACE,
|
||||
err.name ? err.name : "unknown",
|
||||
err.message ? err.message : "");
|
||||
dbus_error_free(&err);
|
||||
return;
|
||||
}
|
||||
dbus_error_free(&err);
|
||||
if (!dbus_message_has_signature(r, "ay")) {
|
||||
spa_log_warn(impl->log, "invalid ReadValue() signature");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_iter_init(r, &args);
|
||||
dbus_message_iter_recurse(&args, &arr);
|
||||
dbus_message_iter_get_fixed_array(&arr, &value, &n);
|
||||
|
||||
free(chr->description);
|
||||
if (n > 0)
|
||||
chr->description = strndup(value, n);
|
||||
else
|
||||
chr->description = NULL;
|
||||
|
||||
spa_log_debug(impl->log, "MIDI GATT user descriptor value: '%s'",
|
||||
chr->description);
|
||||
|
||||
check_chr_node(impl, chr);
|
||||
}
|
||||
|
||||
static int read_dsc(struct impl *impl, struct chr *chr)
|
||||
{
|
||||
DBusMessageIter i, d;
|
||||
DBusMessage *m;
|
||||
struct dsc *dsc;
|
||||
|
||||
if (chr->dsc_probed)
|
||||
return 0;
|
||||
if (chr->dsc_call)
|
||||
return -EBUSY;
|
||||
|
||||
chr->dsc_probed = true;
|
||||
|
||||
dsc = find_dsc(impl, chr);
|
||||
if (dsc == NULL) {
|
||||
chr->dsc_done = true;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
spa_log_debug(impl->log, "MIDI GATT user descriptor read, path=%s",
|
||||
dsc->object.path);
|
||||
|
||||
m = dbus_message_new_method_call(BLUEZ_SERVICE,
|
||||
dsc->object.path,
|
||||
BLUEZ_GATT_DSC_INTERFACE,
|
||||
"ReadValue");
|
||||
if (m == NULL) {
|
||||
chr->dsc_done = true;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dbus_message_iter_init_append(m, &i);
|
||||
dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "{sv}", &d);
|
||||
dbus_message_iter_close_container(&i, &d);
|
||||
|
||||
return spa_dbus_async_call(impl->conn, m, &chr->dsc_call,
|
||||
read_dsc_reply);
|
||||
}
|
||||
|
||||
static int read_probe_reset(struct impl *impl, struct chr *chr)
|
||||
{
|
||||
spa_dbus_async_call_cancel(&chr->read_call);
|
||||
spa_dbus_async_call_cancel(&chr->dsc_call);
|
||||
chr->read_probed = false;
|
||||
chr->read_done = false;
|
||||
chr->dsc_probed = false;
|
||||
chr->dsc_done = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -255,6 +368,11 @@ static void check_chr_node(struct impl *impl, struct chr *chr)
|
|||
available = false;
|
||||
}
|
||||
|
||||
if (available && !chr->dsc_done) {
|
||||
read_dsc(impl, chr);
|
||||
available = chr->dsc_done;
|
||||
}
|
||||
|
||||
if (chr->node_emitted && !available) {
|
||||
remove_chr_node(impl, chr);
|
||||
chr->node_emitted = false;
|
||||
|
|
@ -477,6 +595,7 @@ static void chr_remove(struct spa_dbus_object *object)
|
|||
remove_chr_node(impl, chr);
|
||||
|
||||
free(chr->service_path);
|
||||
free(chr->description);
|
||||
}
|
||||
|
||||
static void chr_property(struct spa_dbus_object *object, const char *key, DBusMessageIter *value)
|
||||
|
|
@ -489,6 +608,35 @@ static void chr_property(struct spa_dbus_object *object, const char *key, DBusMe
|
|||
dup_dbus_string(value, &chr->service_path);
|
||||
}
|
||||
|
||||
static void dsc_update(struct spa_dbus_object *object)
|
||||
{
|
||||
struct impl *impl = object->user_data;
|
||||
struct dsc *dsc = SPA_CONTAINER_OF(object, struct dsc, object);
|
||||
|
||||
if (!dsc->valid_uuid) {
|
||||
spa_dbus_monitor_ignore_object(impl->dbus_monitor, object);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void dsc_property(struct spa_dbus_object *object, const char *key, DBusMessageIter *value)
|
||||
{
|
||||
struct dsc *dsc = SPA_CONTAINER_OF(object, struct dsc, object);
|
||||
|
||||
if (spa_streq(key, "UUID"))
|
||||
dsc->valid_uuid = spa_streq(get_dbus_string(value),
|
||||
BT_GATT_CHARACTERISTIC_USER_DESCRIPTION_UUID);
|
||||
else if (spa_streq(key, "Characteristic"))
|
||||
dup_dbus_string(value, &dsc->chr_path);
|
||||
}
|
||||
|
||||
static void dsc_remove(struct spa_dbus_object *object)
|
||||
{
|
||||
struct dsc *dsc = SPA_CONTAINER_OF(object, struct dsc, object);
|
||||
|
||||
free(dsc->chr_path);
|
||||
}
|
||||
|
||||
static const struct spa_dbus_interface monitor_interfaces[] = {
|
||||
{
|
||||
.name = BLUEZ_ADAPTER_INTERFACE,
|
||||
|
|
@ -517,6 +665,13 @@ static const struct spa_dbus_interface monitor_interfaces[] = {
|
|||
.property = chr_property,
|
||||
.object_size = sizeof(struct chr),
|
||||
},
|
||||
{
|
||||
.name = BLUEZ_GATT_DSC_INTERFACE,
|
||||
.update = dsc_update,
|
||||
.remove = dsc_remove,
|
||||
.property = dsc_property,
|
||||
.object_size = sizeof(struct dsc),
|
||||
},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ enum node_role {
|
|||
|
||||
struct props {
|
||||
char clock_name[64];
|
||||
char device_name[512];
|
||||
int64_t latency_offset;
|
||||
};
|
||||
|
||||
|
|
@ -311,6 +312,7 @@ static void reset_props(struct props *props)
|
|||
{
|
||||
props->latency_offset = DEFAULT_LATENCY_OFFSET;
|
||||
strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name));
|
||||
props->device_name[0] = '\0';
|
||||
}
|
||||
|
||||
static bool is_following(struct impl *this)
|
||||
|
|
@ -1053,6 +1055,12 @@ static int server_release(void *user_data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char *server_description(void *user_data)
|
||||
{
|
||||
struct impl *this = user_data;
|
||||
return this->props.device_name;
|
||||
}
|
||||
|
||||
static int do_remove_port_source(struct spa_loop *loop,
|
||||
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
||||
{
|
||||
|
|
@ -1254,6 +1262,8 @@ next:
|
|||
switch (id) {
|
||||
case SPA_PARAM_PropInfo:
|
||||
{
|
||||
struct props *p = &this->props;
|
||||
|
||||
switch (result.index) {
|
||||
case 0:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
|
|
@ -1262,6 +1272,13 @@ next:
|
|||
SPA_PROP_INFO_description, SPA_POD_String("Latency offset (ns)"),
|
||||
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, INT64_MIN, INT64_MAX));
|
||||
break;
|
||||
case 1:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_deviceName),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("Device name"),
|
||||
SPA_PROP_INFO_type, SPA_POD_String(p->device_name));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1275,7 +1292,8 @@ next:
|
|||
case 0:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Props, id,
|
||||
SPA_PROP_latencyOffsetNsec, SPA_POD_Long(p->latency_offset));
|
||||
SPA_PROP_latencyOffsetNsec, SPA_POD_Long(p->latency_offset),
|
||||
SPA_PROP_deviceName, SPA_POD_String(p->device_name));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -1322,7 +1340,9 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
|
|||
} else {
|
||||
spa_pod_parse_object(param,
|
||||
SPA_TYPE_OBJECT_Props, NULL,
|
||||
SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&new_props.latency_offset));
|
||||
SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&new_props.latency_offset),
|
||||
SPA_PROP_deviceName, SPA_POD_OPT_Stringn(new_props.device_name,
|
||||
sizeof(new_props.device_name)));
|
||||
}
|
||||
|
||||
changed = (memcmp(&new_props, &this->props, sizeof(struct props)) != 0);
|
||||
|
|
@ -1814,6 +1834,7 @@ static const struct spa_bt_midi_server_cb impl_server = {
|
|||
.acquire_write = server_acquire_write,
|
||||
.acquire_notify = server_acquire_notify,
|
||||
.release = server_release,
|
||||
.get_description = server_description,
|
||||
};
|
||||
|
||||
static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
|
||||
|
|
@ -1870,6 +1891,7 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
uint32_t n_support)
|
||||
{
|
||||
struct impl *this;
|
||||
const char *device_name = "";
|
||||
int res = 0;
|
||||
size_t i;
|
||||
|
||||
|
|
@ -1917,6 +1939,11 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
if (spa_streq(str, "server"))
|
||||
this->role = NODE_SERVER;
|
||||
}
|
||||
|
||||
if ((str = spa_dict_lookup(info, "node.nick")) != NULL)
|
||||
device_name = str;
|
||||
else if ((str = spa_dict_lookup(info, "node.description")) != NULL)
|
||||
device_name = str;
|
||||
}
|
||||
|
||||
if (this->role == NODE_CLIENT && this->chr_path == NULL) {
|
||||
|
|
@ -1953,6 +1980,9 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
|
||||
reset_props(&this->props);
|
||||
|
||||
spa_scnprintf(this->props.device_name, sizeof(this->props.device_name),
|
||||
"%s", device_name);
|
||||
|
||||
/* set the node info */
|
||||
this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
|
||||
SPA_NODE_CHANGE_MASK_PROPS |
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#define MIDI_SERVER_PATH "/midiserver"
|
||||
#define MIDI_SERVICE_PATH "/midiserver/service"
|
||||
#define MIDI_CHR_PATH "/midiserver/service/chr"
|
||||
#define MIDI_DSC_PATH "/midiserver/service/chr/dsc"
|
||||
|
||||
#define BLE_DEFAULT_MTU 23
|
||||
|
||||
|
|
@ -56,6 +57,7 @@ struct impl
|
|||
|
||||
struct spa_dbus_local_object *service;
|
||||
struct spa_dbus_local_object *chr;
|
||||
struct spa_dbus_local_object *dsc;
|
||||
|
||||
struct spa_dbus_monitor *dbus_monitor;
|
||||
struct spa_dbus_object_manager *objects;
|
||||
|
|
@ -76,6 +78,118 @@ struct chr {
|
|||
unsigned int notify_acquired:1;
|
||||
};
|
||||
|
||||
struct dsc {
|
||||
struct spa_dbus_local_object object;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Characteristic user descriptor: not in BLE MIDI standard, but we
|
||||
* put a device name here in case we have multiple MIDI endpoints.
|
||||
*/
|
||||
|
||||
static DBusMessage *parse_options(struct impl *impl, DBusMessage *m, const char *out_key, uint16_t *out_value);
|
||||
|
||||
static DBusMessage *dsc_read_value(struct spa_dbus_local_object *object, DBusMessage *m)
|
||||
{
|
||||
struct dsc *dsc = SPA_CONTAINER_OF(object, struct dsc, object);
|
||||
struct impl *impl = dsc->object.user_data;
|
||||
DBusMessage *r;
|
||||
DBusMessageIter i, a;
|
||||
const char *description = NULL;
|
||||
const uint8_t *ptr;
|
||||
uint16_t offset = 0;
|
||||
int len;
|
||||
|
||||
r = parse_options(impl, m, "offset", &offset);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (impl->cb->get_description)
|
||||
description = impl->cb->get_description(impl->user_data);
|
||||
if (!description)
|
||||
description = "";
|
||||
|
||||
len = strlen(description);
|
||||
if (offset > len)
|
||||
return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid arguments");
|
||||
|
||||
r = dbus_message_new_method_return(m);
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
ptr = SPA_PTROFF(description, offset, const uint8_t);
|
||||
len -= offset;
|
||||
|
||||
dbus_message_iter_init_append(r, &i);
|
||||
dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "y", &a);
|
||||
dbus_message_iter_append_fixed_array(&a, DBUS_TYPE_BYTE, &ptr, len);
|
||||
dbus_message_iter_close_container(&i, &a);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int dsc_prop_uuid_get(struct spa_dbus_local_object *object, DBusMessageIter *value)
|
||||
{
|
||||
const char *uuid = BT_GATT_CHARACTERISTIC_USER_DESCRIPTION_UUID;
|
||||
dbus_message_iter_append_basic(value, DBUS_TYPE_STRING, &uuid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsc_prop_characteristic_get(struct spa_dbus_local_object *object, DBusMessageIter *value)
|
||||
{
|
||||
struct dsc *dsc = SPA_CONTAINER_OF(object, struct dsc, object);
|
||||
struct impl *impl = dsc->object.user_data;
|
||||
dbus_message_iter_append_basic(value, DBUS_TYPE_OBJECT_PATH, &impl->chr->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsc_prop_flags_get(struct spa_dbus_local_object *object, DBusMessageIter *value)
|
||||
{
|
||||
DBusMessageIter a;
|
||||
const char *flags[] = {"encrypt-read", NULL };
|
||||
const char **p;
|
||||
|
||||
dbus_message_iter_open_container(value, DBUS_TYPE_ARRAY, "s", &a);
|
||||
for (p = &flags[0]; *p; ++p)
|
||||
dbus_message_iter_append_basic(&a, DBUS_TYPE_STRING, p);
|
||||
dbus_message_iter_close_container(value, &a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spa_dbus_local_interface midi_dsc_interfaces[] = {
|
||||
{
|
||||
.name = BLUEZ_GATT_DSC_INTERFACE,
|
||||
.methods = (struct spa_dbus_method[]) {
|
||||
{
|
||||
.name = "ReadValue",
|
||||
.call = dsc_read_value,
|
||||
},
|
||||
{NULL}
|
||||
},
|
||||
.properties = (struct spa_dbus_property[]) {
|
||||
{
|
||||
.name = "UUID",
|
||||
.signature = "s",
|
||||
.get = dsc_prop_uuid_get,
|
||||
},
|
||||
{
|
||||
.name = "Characteristic",
|
||||
.signature = "o",
|
||||
.get = dsc_prop_characteristic_get,
|
||||
},
|
||||
{
|
||||
.name = "Flags",
|
||||
.signature = "as",
|
||||
.get = dsc_prop_flags_get,
|
||||
},
|
||||
{NULL}
|
||||
},
|
||||
},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* MIDI characteristic
|
||||
|
|
@ -122,7 +236,7 @@ static void chr_change_acquired(struct impl *impl, struct chr *chr, bool write,
|
|||
spa_dbus_object_manager_properties_changed(impl->objects, impl->chr, iface, changed);
|
||||
}
|
||||
|
||||
static DBusMessage *parse_options(struct impl *impl, DBusMessage *m, uint16_t *mtu)
|
||||
static DBusMessage *parse_options(struct impl *impl, DBusMessage *m, const char *out_key, uint16_t *out_value)
|
||||
{
|
||||
DBusMessageIter args, options;
|
||||
|
||||
|
|
@ -146,8 +260,8 @@ static DBusMessage *parse_options(struct impl *impl, DBusMessage *m, uint16_t *m
|
|||
dbus_message_iter_recurse(&entry, &value);
|
||||
type = dbus_message_iter_get_arg_type(&value);
|
||||
|
||||
if (spa_streq(key, "mtu") && type == DBUS_TYPE_UINT16)
|
||||
dbus_message_iter_get_basic(&value, mtu);
|
||||
if (spa_streq(key, out_key) && type == DBUS_TYPE_UINT16)
|
||||
dbus_message_iter_get_basic(&value, out_value);
|
||||
|
||||
dbus_message_iter_next(&options);
|
||||
}
|
||||
|
|
@ -184,7 +298,7 @@ static DBusMessage *chr_acquire(struct spa_dbus_local_object *object, DBusMessag
|
|||
goto fail;
|
||||
}
|
||||
|
||||
r = parse_options(impl, m, &mtu);
|
||||
r = parse_options(impl, m, "mtu", &mtu);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
|
|
@ -270,7 +384,8 @@ static int chr_prop_write_acquired_get(struct spa_dbus_local_object *object, DBu
|
|||
static int chr_prop_flags_get(struct spa_dbus_local_object *object, DBusMessageIter *value)
|
||||
{
|
||||
DBusMessageIter a;
|
||||
const char *flags[] = {"read", "write-without-response", "notify", NULL };
|
||||
const char *flags[] = {"encrypt-read", "write-without-response",
|
||||
"encrypt-write", "encrypt-notify", NULL };
|
||||
const char **p;
|
||||
|
||||
dbus_message_iter_open_container(value, DBUS_TYPE_ARRAY, "s", &a);
|
||||
|
|
@ -481,6 +596,14 @@ static int register_objects(struct impl *impl)
|
|||
if (impl->chr == NULL)
|
||||
goto fail;
|
||||
|
||||
impl->dsc = spa_dbus_object_manager_register(impl->objects,
|
||||
MIDI_DSC_PATH,
|
||||
midi_dsc_interfaces,
|
||||
sizeof(struct dsc),
|
||||
impl);
|
||||
if (impl->dsc == NULL)
|
||||
goto fail;
|
||||
|
||||
impl->dbus_monitor = spa_dbus_monitor_new(impl->conn,
|
||||
BLUEZ_SERVICE, "/", monitor_interfaces,
|
||||
impl->log, impl);
|
||||
|
|
|
|||
|
|
@ -40,9 +40,11 @@
|
|||
#define BLUEZ_GATT_PROFILE_INTERFACE BLUEZ_SERVICE ".GattProfile1"
|
||||
#define BLUEZ_GATT_SERVICE_INTERFACE BLUEZ_SERVICE ".GattService1"
|
||||
#define BLUEZ_GATT_CHR_INTERFACE BLUEZ_SERVICE ".GattCharacteristic1"
|
||||
#define BLUEZ_GATT_DSC_INTERFACE BLUEZ_SERVICE ".GattDescriptor1"
|
||||
|
||||
#define BT_MIDI_SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700"
|
||||
#define BT_MIDI_CHR_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3"
|
||||
#define BT_GATT_CHARACTERISTIC_USER_DESCRIPTION_UUID "00002901-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define MIDI_BUF_SIZE 8192
|
||||
#define MIDI_MAX_MTU 8192
|
||||
|
|
@ -76,6 +78,7 @@ struct spa_bt_midi_server_cb
|
|||
int (*acquire_notify)(void *user_data, int fd, uint16_t mtu);
|
||||
int (*acquire_write)(void *user_data, int fd, uint16_t mtu);
|
||||
int (*release)(void *user_data);
|
||||
const char *(*get_description)(void *user_data);
|
||||
};
|
||||
|
||||
static inline void spa_bt_midi_parser_init(struct spa_bt_midi_parser *parser)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue