@@ -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
@@ -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;
@@ -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);
@@ -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);
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@samsung.com> --- lib/bluetooth.h | 9 ++ src/adapter.c | 8 ++ src/adapter.h | 1 + src/device.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+)