This change introduces a new device property which will enable user to get PHYs of the current LE Connection. It will also allow to set preferred PHYs for a particular LE connection. Reviewed-by: Anupam Roy <anupam.r@xxxxxxxxxxx> --- lib/bluetooth.h | 9 ++ src/adapter.c | 8 ++ src/adapter.h | 1 + src/device.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+) diff --git a/lib/bluetooth.h b/lib/bluetooth.h index 9ab033ec4..a6afaa753 100644 --- a/lib/bluetooth.h +++ b/lib/bluetooth.h @@ -127,6 +127,15 @@ struct bt_voice { #define BT_PHY_LE_2M_RX 0x00001000 #define BT_PHY_LE_CODED_TX 0x00002000 #define BT_PHY_LE_CODED_RX 0x00004000 +#define BT_PHY_LE_CODED_S2 0x00008000 +#define BT_PHY_LE_CODED_S8 0x00010000 + +#define BT_PHY_LE_MASK (BT_PHY_LE_1M_TX | BT_PHY_LE_1M_RX | \ + BT_PHY_LE_2M_TX | BT_PHY_LE_2M_RX | \ + BT_PHY_LE_CODED_TX | BT_PHY_LE_CODED_RX) + +#define BT_PHY_LE_CODED_MASK (BT_PHY_LE_CODED_TX | \ + BT_PHY_LE_CODED_RX) #define BT_MODE 15 diff --git a/src/adapter.c b/src/adapter.c index 3861ade8a..5de92a570 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -6661,6 +6661,14 @@ const char *adapter_get_path(struct btd_adapter *adapter) return adapter->path; } +uint32_t adapter_get_supported_phys(struct btd_adapter *adapter) +{ + if (!adapter) + return 0; + + return adapter->supported_phys; +} + const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter) { return &adapter->bdaddr; diff --git a/src/adapter.h b/src/adapter.h index 60b5e3bcc..21046ceaa 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -87,6 +87,7 @@ struct btd_device *btd_adapter_find_device_by_path(struct btd_adapter *adapter, const char *path); const char *adapter_get_path(struct btd_adapter *adapter); +uint32_t adapter_get_supported_phys(struct btd_adapter *adapter); const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter); uint8_t btd_adapter_get_address_type(struct btd_adapter *adapter); const char *btd_adapter_get_storage_dir(struct btd_adapter *adapter); diff --git a/src/device.c b/src/device.c index b29aa195d..6f74989c7 100644 --- a/src/device.c +++ b/src/device.c @@ -272,6 +272,9 @@ struct btd_device { GIOChannel *att_io; guint store_id; + + uint32_t phys; + bool pending_phys; }; static const uint16_t uuid_list[] = { @@ -1470,6 +1473,212 @@ static gboolean dev_property_wake_allowed_exist( return device_get_wake_support(device); } +static struct phys_config { + uint32_t flag; + const char *name; +} phys_str[] = { + { BT_PHY_LE_1M_TX, "LE1MTX" }, + { BT_PHY_LE_1M_RX, "LE1MRX" }, + { BT_PHY_LE_2M_TX, "LE2MTX" }, + { BT_PHY_LE_2M_RX, "LE2MRX" }, + { BT_PHY_LE_CODED_TX, "LECODEDTX" }, + { BT_PHY_LE_CODED_RX, "LECODEDRX" }, + { BT_PHY_LE_CODED_S2, "LECODEDS2" }, + { BT_PHY_LE_CODED_S8, "LECODEDS8" } +}; + +static void append_phys_str(DBusMessageIter *array, uint32_t phys) +{ + unsigned int i; + + for (i = 0; i < NELEM(phys_str); i++) { + if (phys & phys_str[i].flag) + dbus_message_iter_append_basic(array, DBUS_TYPE_STRING, + &phys_str[i].name); + } +} + +static bool parse_phys_str(DBusMessageIter *array, uint32_t *phys) +{ + const char *str; + unsigned int i; + + *phys = 0; + + do { + if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_STRING) + return false; + + dbus_message_iter_get_basic(array, &str); + + for (i = 0; i < NELEM(phys_str); i++) { + if (!strcmp(str, phys_str[i].name)) { + *phys |= phys_str[i].flag; + break; + } + } + + if (i == NELEM(phys_str)) + return false; + } while (dbus_message_iter_next(array)); + + return true; +} + +static void device_set_phy(struct btd_device *device, uint32_t phys) +{ + if (!device) + return; + + DBG("Device PHYs %u", phys); + + device->phys = phys; + + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "Phy"); + +} + +static int set_preferred_phy(struct btd_device *device, uint32_t phys) +{ + GIOChannel *io; + GError *gerr = NULL; + uint32_t supported_phys = 0; + int ret; + + if (!device || !phys) { + error("Invalid arguments in method call"); + return -EINVAL; + } + + if (!device->le) { + error("Operation is not supported"); + return -ENOTSUP; + } + + if (!device->le_state.connected || !device->attrib) { + error("Device Not Connected"); + return -ENOTCONN; + } + + io = g_attrib_get_channel(device->attrib); + + if (!io) { + error("Device Not Connected"); + return -ENOTCONN; + } + + supported_phys = adapter_get_supported_phys(device->adapter) & + BT_PHY_LE_MASK; + + if (supported_phys & BT_PHY_LE_CODED_MASK) { + supported_phys |= BT_PHY_LE_CODED_S2; + supported_phys |= BT_PHY_LE_CODED_S8; + } + + if (phys & ~supported_phys) { + error("Supplied phy(s) are not supported"); + return -EINVAL; + } + + if (device->pending_phys) { + error("Operation already in progress"); + return -EINPROGRESS; + } + + device->pending_phys = true; + + if (device->phys == phys) { + device->pending_phys = false; + return 0; + } + + bt_io_set(io, &gerr, BT_IO_OPT_PHY, phys, BT_IO_OPT_INVALID); + + if (gerr) { + error("bt_io_set: %s", gerr->message); + device->pending_phys = false; + ret = gerr->code; + g_error_free(gerr); + return ret; + } + + return 0; +} + +static gboolean dev_property_get_phy(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *device = data; + DBusMessageIter array; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array); + + append_phys_str(&array, device->phys); + + dbus_message_iter_close_container(iter, &array); + + return TRUE; +} + +static void dev_property_set_phy(const GDBusPropertyTable *property, + DBusMessageIter *value, + GDBusPendingPropertySet id, void *data) +{ + struct btd_device *device = data; + DBusMessageIter array; + uint32_t phys; + int ret; + + if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY) { + ret = -EINVAL; + goto failed; + } + + dbus_message_iter_recurse(value, &array); + + if (!parse_phys_str(&array, &phys)) { + ret = -EINVAL; + goto failed; + } + + ret = set_preferred_phy(device, phys); + + if (ret >= 0) { + g_dbus_pending_property_success(id); + return; + } + +failed: + switch (-ret) { + case EINVAL: + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + break; + case ENOTSUP: + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".NotSupported", + "Operation is not supported"); + break; + case ENOTCONN: + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".NotConnected", + "Device Not Connected"); + break; + case EINPROGRESS: + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InProgress", + "Operation already in progress"); + break; + default: + g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", + strerror(-ret)); + break; + } + +} + static bool disconnect_all(gpointer user_data) { struct btd_device *device = user_data; @@ -2950,6 +3159,7 @@ static const GDBusPropertyTable device_properties[] = { { "WakeAllowed", "b", dev_property_get_wake_allowed, dev_property_set_wake_allowed, dev_property_wake_allowed_exist }, + { "Phy", "as", dev_property_get_phy, dev_property_set_phy }, { } }; @@ -3017,6 +3227,12 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type) state->connected = false; device->general_connect = FALSE; + /* Reset PHYs if LE connection is disconnected */ + if (bdaddr_type != BDADDR_BREDR) { + device->pending_phys = false; + device_set_phy(device, 0); + } + device_set_svc_refreshed(device, false); if (device->disconn_timer > 0) { @@ -5305,10 +5521,12 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) struct btd_gatt_database *database; const bdaddr_t *dst; char dstaddr[18]; + uint32_t phys; bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid, + BT_IO_OPT_PHY, &phys, BT_IO_OPT_INVALID); if (gerr) { @@ -5358,6 +5576,8 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) dev->attrib = attrib; dev->att = g_attrib_get_att(attrib); + device_set_phy(dev, phys); + bt_att_ref(dev->att); bt_att_set_debug(dev->att, BT_ATT_DEBUG, gatt_debug, NULL, NULL); -- 2.17.1