diff options
author | Alok Barsode <alok.barsode@azingo.com> | 2009-08-14 21:30:20 +0530 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@nokia.com> | 2009-08-15 03:03:56 +0300 |
commit | d2a6bf00afbaeef82f9210639150e1f93cb44589 (patch) | |
tree | 264a8786243d22eeb77356badfbf6899eb389bc9 /src | |
parent | 6277974c440ae027131e62458f49f3e24ecbade3 (diff) |
Implement Class of Device cache for async behaviour.
In order to avoid race conditions for setting the Class of Device
asynchronously a queuing mechanism is needed. This patch implements that
by having the following variables in the adapter struct:
adapter->current_cod
The currently set CoD
adapter->pending_cod
CoD of a hci_write_cod command which didn't complete yet.
adapter->wanted_cod
The CoD value that is ultimately desired but might not
have been set yet.
Diffstat (limited to 'src')
-rw-r--r-- | src/adapter.c | 294 | ||||
-rw-r--r-- | src/adapter.h | 15 | ||||
-rw-r--r-- | src/dbus-hci.c | 33 | ||||
-rw-r--r-- | src/dbus-hci.h | 1 | ||||
-rw-r--r-- | src/manager.c | 11 | ||||
-rw-r--r-- | src/manager.h | 1 | ||||
-rw-r--r-- | src/security.c | 2 |
7 files changed, 170 insertions, 187 deletions
diff --git a/src/adapter.c b/src/adapter.c index bf69fcc6..6ba5e56f 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -69,6 +69,9 @@ #define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03 #define IO_CAPABILITY_INVALID 0xFF +/* Limited Discoverable bit mask in CoD */ +#define LIMITED_BIT 0x002000 + #define check_address(address) bachk(address) static DBusConnection *connection = NULL; @@ -127,7 +130,10 @@ struct btd_adapter { gboolean off_requested; /* DEVDOWN ioctl was called */ - uint8_t svc_cache; /* Service Class cache */ + uint32_t current_cod; /* Adapter's current class */ + uint32_t pending_cod; + uint32_t wanted_cod; /* CoD cache */ + gboolean cache_enable; gint ref; @@ -226,72 +232,57 @@ void clear_found_devices_list(struct btd_adapter *adapter) adapter->found_devices = NULL; } -static int set_service_classes(struct btd_adapter *adapter, uint8_t value) +static int adapter_set_service_classes(struct btd_adapter *adapter, uint8_t value) { - struct hci_dev *dev = &adapter->dev; - const uint8_t *cls = dev->class; - uint32_t dev_class; - int dd, err; - - if (cls[2] == value) - return 0; /* Already set */ - - dd = hci_open_dev(adapter->dev_id); - if (dd < 0) { - err = -errno; - error("Can't open device hci%d: %s (%d)", - adapter->dev_id, strerror(errno), errno); - return err; - } + int err; - dev_class = (value << 16) | (cls[1] << 8) | cls[0]; + /* Update only the service class, keep the limited bit, major/minor class + * bits intact */ + adapter->wanted_cod &= 0x00ffff; + adapter->wanted_cod |= (value << 16); - debug("Changing service classes to 0x%06x", dev_class); + /* If we already have the CoD we want or the cache is enabled or an + * existing CoD write is in progress just bail out */ + if (adapter->current_cod == adapter->wanted_cod || + adapter->cache_enable || adapter->pending_cod) + return 0; - if (hci_write_class_of_dev(dd, dev_class, HCI_REQ_TIMEOUT) < 0) { - err = -errno; - error("Can't write class of device: %s (%d)", - strerror(errno), errno); - hci_close_dev(dd); - return err; - } + debug("Changing service classes to 0x%06x", adapter->wanted_cod); - hci_close_dev(dd); + err = adapter_ops->set_class(adapter->dev_id, adapter->wanted_cod); + if (err < 0) + error("Adapter class update failed: %s(%d)", + strerror(err), err); + else + adapter->pending_cod = adapter->wanted_cod; - return 0; + return err; } -int set_major_and_minor_class(struct btd_adapter *adapter, uint8_t major, +int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major, uint8_t minor) { - struct hci_dev *dev = &adapter->dev; - const uint8_t *cls = dev->class; - uint32_t dev_class; - int dd, err; + int err; - dd = hci_open_dev(adapter->dev_id); - if (dd < 0) { - err = -errno; - error("Can't open device hci%d: %s (%d)", - adapter->dev_id, strerror(errno), errno); - return err; - } + /* Update only the major and minor class bits keeping remaining bits + * intact*/ + adapter->wanted_cod &= 0xffe000; + adapter->wanted_cod |= ((major & 0x1f) << 8) | minor; - dev_class = (cls[2] << 16) | ((cls[1] & 0x20) << 8) | - ((major & 0xdf) << 8) | minor; + if (adapter->wanted_cod == adapter->current_cod || + adapter->cache_enable || adapter->pending_cod) + return 0; - debug("Changing major/minor class to 0x%06x", dev_class); + debug("Changing Major/Minor class to 0x%06x", adapter->wanted_cod); - if (hci_write_class_of_dev(dd, dev_class, HCI_REQ_TIMEOUT) < 0) { - int err = -errno; - error("Can't write class of device: %s (%d)", - strerror(errno), errno); - hci_close_dev(dd); - return err; - } + err = adapter_ops->set_class(adapter->dev_id, adapter->wanted_cod); + if (err < 0) + error("Adapter class update failed: %s(%d)", + strerror(err), err); + else + adapter->pending_cod = adapter->wanted_cod; - hci_close_dev(dd); - return 0; + return err; } int pending_remote_name_cancel(struct btd_adapter *adapter) @@ -428,6 +419,25 @@ static void adapter_set_discov_timeout(struct btd_adapter *adapter, adapter); } +static void adapter_set_limited_discoverable(struct btd_adapter *adapter, + gboolean limited) +{ + /* Check if limited bit needs to be set/reset */ + if (limited) + adapter->wanted_cod |= LIMITED_BIT; + else + adapter->wanted_cod &= ~LIMITED_BIT; + + /* If we dont need the toggling, save an unnecessary CoD write */ + if (adapter->pending_cod || + adapter->wanted_cod == adapter->current_cod) + return; + + if (adapter_ops->set_limited_discoverable(adapter->dev_id, + adapter->wanted_cod, limited) == 0) + adapter->pending_cod = adapter->wanted_cod; +} + static int set_mode(struct btd_adapter *adapter, uint8_t new_mode) { int err; @@ -468,8 +478,7 @@ static int set_mode(struct btd_adapter *adapter, uint8_t new_mode) adapter->discov_timeout); if (new_mode != MODE_LIMITED && adapter->mode == MODE_LIMITED) - adapter_ops->set_limited_discoverable(adapter->dev_id, - adapter->dev.class, FALSE); + adapter_set_limited_discoverable(adapter, FALSE); } done: @@ -841,6 +850,58 @@ static void update_ext_inquiry_response(struct btd_adapter *adapter) hci_close_dev(dd); } +void adapter_set_class_complete(bdaddr_t *bdaddr, uint8_t status) +{ + uint8_t class[3]; + struct btd_adapter *adapter; + int ret; + + if (status) + return; + + adapter = manager_find_adapter(bdaddr); + if (!adapter) { + error("Unable to find matching adapter"); + return; + } + + if (adapter->pending_cod == 0) + return; + + adapter->current_cod = adapter->pending_cod; + adapter->pending_cod = 0; + + class[2] = (adapter->current_cod >> 16) & 0xff; + class[1] = (adapter->current_cod >> 8) & 0xff; + class[0] = adapter->current_cod & 0xff; + + write_local_class(&adapter->bdaddr, class); + + emit_property_changed(connection, adapter->path, + ADAPTER_INTERFACE, "Class", + DBUS_TYPE_UINT32, &adapter->current_cod); + + update_ext_inquiry_response(adapter); + + if (adapter->wanted_cod == adapter->current_cod) + return; + + if (adapter->wanted_cod & LIMITED_BIT && + !(adapter->current_cod & LIMITED_BIT)) + ret = adapter_ops->set_limited_discoverable(adapter->dev_id, + adapter->wanted_cod, TRUE); + else if (!(adapter->wanted_cod & LIMITED_BIT) && + adapter->current_cod & LIMITED_BIT) + ret = adapter_ops->set_limited_discoverable(adapter->dev_id, + adapter->wanted_cod, FALSE); + else + ret = adapter_ops->set_class(adapter->dev_id, + adapter->wanted_cod); + + if (ret == 0) + adapter->pending_cod = adapter->wanted_cod; +} + void adapter_update_local_name(bdaddr_t *bdaddr, uint8_t status, void *ptr) { read_local_name_rp rp; @@ -1139,7 +1200,6 @@ static DBusMessage *get_properties(DBusConnection *conn, DBusMessageIter iter; DBusMessageIter dict; char str[MAX_NAME_LENGTH + 1], srcaddr[18]; - uint32_t class; gboolean value; char **devices; int i; @@ -1173,10 +1233,8 @@ static DBusMessage *get_properties(DBusConnection *conn, dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &property); /* Class */ - class = adapter->dev.class[0] | - adapter->dev.class[1] << 8 | - adapter->dev.class[2] << 16; - dict_append_entry(&dict, "Class", DBUS_TYPE_UINT32, &class); + dict_append_entry(&dict, "Class", + DBUS_TYPE_UINT32, &adapter->current_cod); /* Powered */ value = (adapter->up && !adapter->off_requested) ? TRUE : FALSE; @@ -1756,6 +1814,7 @@ static int adapter_setup(struct btd_adapter *adapter, const char *mode) uint8_t inqmode; int err , dd; char name[MAX_NAME_LENGTH + 1]; + uint8_t cls[3]; dd = hci_open_dev(adapter->dev_id); if (dd < 0) { @@ -1818,6 +1877,19 @@ static int adapter_setup(struct btd_adapter *adapter, const char *mode) if (g_str_equal(mode, "off")) strncpy((char *) adapter->dev.name, name, MAX_NAME_LENGTH); + /* Set device class */ + if (adapter->wanted_cod) { + cls[1] = (adapter->wanted_cod >> 8) & 0xff; + cls[0] = adapter->wanted_cod & 0xff; + } else if (read_local_class(&adapter->bdaddr, cls) < 0) { + uint32_t class = htobl(main_opts.class); + if (class) + memcpy(cls, &class, 3); + else + goto done; + } + + btd_adapter_set_class(adapter, cls[1], cls[0]); done: hci_close_dev(dd); return 0; @@ -1971,6 +2043,31 @@ static int get_pairable_timeout(const char *src) return main_opts.pairto; } +static void adapter_disable_cod_cache(struct btd_adapter *adapter) +{ + int err; + + if (!adapter) + return; + + if (!adapter->cache_enable) + return; + + /* Disable and flush svc cache. All successive service class updates + will be written to the device */ + adapter->cache_enable = FALSE; + + if (adapter->current_cod == adapter->wanted_cod) + return; + + err = adapter_ops->set_class(adapter->dev_id, adapter->wanted_cod); + if (err < 0) + error("Adapter class update failed: %s(%d)", + strerror(err), err); + else + adapter->pending_cod = adapter->wanted_cod; +} + static int adapter_up(struct btd_adapter *adapter, const char *mode) { char srcaddr[18]; @@ -2050,7 +2147,7 @@ proceed: ADAPTER_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &powered); - adapter_disable_svc_cache(adapter); + adapter_disable_cod_cache(adapter); return 0; } @@ -2124,14 +2221,6 @@ int adapter_start(struct btd_adapter *adapter) memcpy(dev->features, features, 8); - if (hci_read_class_of_dev(dd, dev->class, HCI_REQ_TIMEOUT) < 0) { - err = -errno; - error("Can't read class of adapter on %s: %s (%d)", - adapter->path, strerror(errno), errno); - hci_close_dev(dd); - return err; - } - adapter_ops->read_name(adapter->dev_id); if (!(features[6] & LMP_SIMPLE_PAIR)) @@ -2154,6 +2243,8 @@ setup: 0, NULL); hci_close_dev(dd); + adapter->current_cod = 0; + adapter_setup(adapter, mode); if (!adapter->initialized && adapter->already_up) { @@ -2258,6 +2349,7 @@ int adapter_stop(struct btd_adapter *adapter) adapter->mode = MODE_OFF; adapter->state = DISCOVER_TYPE_NONE; adapter->cache_enable = TRUE; + adapter->pending_cod = 0; info("Adapter %s has been disabled", adapter->path); @@ -2271,60 +2363,7 @@ int adapter_update(struct btd_adapter *adapter, uint8_t new_svc) if (dev->ignore) return 0; - if (adapter->cache_enable) { - adapter->svc_cache = new_svc; - return 0; - } - - set_service_classes(adapter, new_svc); - - update_ext_inquiry_response(adapter); - - return 0; -} - -void adapter_disable_svc_cache(struct btd_adapter *adapter) -{ - if (!adapter) - return; - - if (!adapter->cache_enable) - return; - - /* Disable and flush svc cache. All successive service class updates - will be written to the device */ - adapter->cache_enable = FALSE; - - set_service_classes(adapter, adapter->svc_cache); - - update_ext_inquiry_response(adapter); -} - -int adapter_get_class(struct btd_adapter *adapter, uint8_t *cls) -{ - struct hci_dev *dev = &adapter->dev; - - memcpy(cls, dev->class, 3); - - return 0; -} - -int adapter_set_class(struct btd_adapter *adapter, uint8_t *cls) -{ - struct hci_dev *dev = &adapter->dev; - uint32_t class; - - if (memcmp(dev->class, cls, 3) == 0) - return 0; - - memcpy(dev->class, cls, 3); - - write_local_class(&adapter->bdaddr, cls); - - class = cls[0] | (cls[1] << 8) | (cls[2] << 16); - - emit_property_changed(connection, adapter->path, ADAPTER_INTERFACE, - "Class", DBUS_TYPE_UINT32, &class); + adapter_set_service_classes(adapter, new_svc); return 0; } @@ -2694,7 +2733,6 @@ void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode) { const gchar *path = adapter_get_path(adapter); gboolean discoverable, pairable; - uint8_t real_class[3]; if (adapter->scan_mode == scan_mode) return; @@ -2739,17 +2777,11 @@ void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode) ADAPTER_INTERFACE, "Pairable", DBUS_TYPE_BOOLEAN, &pairable); - memcpy(real_class, adapter->dev.class, 3); - if (adapter->svc_cache) - real_class[2] = adapter->svc_cache; - if (discoverable && adapter->pairable && adapter->discov_timeout > 0 && adapter->discov_timeout <= 60) - adapter_ops->set_limited_discoverable(adapter->dev_id, - real_class, TRUE); + adapter_set_limited_discoverable(adapter, TRUE); else if (!discoverable) - adapter_ops->set_limited_discoverable(adapter->dev_id, - real_class, FALSE); + adapter_set_limited_discoverable(adapter, FALSE); emit_property_changed(connection, path, ADAPTER_INTERFACE, "Discoverable", diff --git a/src/adapter.h b/src/adapter.h index 83f987e3..f2df1615 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -78,10 +78,6 @@ int adapter_stop(struct btd_adapter *adapter); int adapter_update(struct btd_adapter *adapter, uint8_t cls); -int adapter_get_class(struct btd_adapter *adapter, uint8_t *cls); - -int adapter_set_class(struct btd_adapter *adapter, uint8_t *cls); - int adapter_update_ssp_mode(struct btd_adapter *adapter, uint8_t mode); struct btd_device *adapter_get_device(DBusConnection *conn, @@ -91,8 +87,6 @@ struct btd_device *adapter_find_device(struct btd_adapter *adapter, const char * struct btd_device *adapter_find_connection(struct btd_adapter *adapter, uint16_t handle); -void adapter_disable_svc_cache(struct btd_adapter *adapter); - void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter, struct btd_device *device); struct btd_device *adapter_create_device(DBusConnection *conn, @@ -126,6 +120,7 @@ void adapter_update_oor_devices(struct btd_adapter *adapter); void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode); void adapter_setname_complete(bdaddr_t *local, uint8_t status); void adapter_update_local_name(bdaddr_t *bdaddr, uint8_t status, void *ptr); +void adapter_set_class_complete(bdaddr_t *bdaddr, uint8_t status); struct agent *adapter_get_agent(struct btd_adapter *adapter); void adapter_add_connection(struct btd_adapter *adapter, @@ -136,8 +131,9 @@ gboolean adapter_has_discov_sessions(struct btd_adapter *adapter); struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter); void btd_adapter_unref(struct btd_adapter *adapter); -int set_major_and_minor_class(struct btd_adapter *adapter, uint8_t major, - uint8_t minor); + +int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major, + uint8_t minor); struct btd_adapter_driver { @@ -172,7 +168,7 @@ struct btd_adapter_ops { int (*set_powered) (int index, gboolean powered); int (*set_connectable) (int index); int (*set_discoverable) (int index); - int (*set_limited_discoverable) (int index, const uint8_t *cls, + int (*set_limited_discoverable) (int index, uint32_t class, gboolean limited); int (*start_discovery) (int index, gboolean periodic); int (*stop_discovery) (int index); @@ -180,6 +176,7 @@ struct btd_adapter_ops { int (*cancel_resolve_name) (int index, bdaddr_t *bdaddr); int (*set_name) (int index, const char *name); int (*read_name) (int index); + int (*set_class) (int index, uint32_t class); }; int btd_register_adapter_ops(struct btd_adapter_ops *btd_adapter_ops); diff --git a/src/dbus-hci.c b/src/dbus-hci.c index 221c1851..891b385a 100644 --- a/src/dbus-hci.c +++ b/src/dbus-hci.c @@ -778,39 +778,6 @@ failed: hci_close_dev(dd); } -void hcid_dbus_write_class_complete(bdaddr_t *local) -{ - struct btd_adapter *adapter; - int dd; - uint8_t cls[3]; - uint16_t dev_id; - - adapter = manager_find_adapter(local); - if (!adapter) { - error("No matching adapter found"); - return; - } - - dev_id = adapter_get_dev_id(adapter); - - dd = hci_open_dev(dev_id); - if (dd < 0) { - error("HCI device open failed: hci%d", dev_id); - return; - } - - if (hci_read_class_of_dev(dd, cls, HCI_REQ_TIMEOUT) < 0) { - error("Can't read class of device on hci%d: %s (%d)", - dev_id, strerror(errno), errno); - hci_close_dev(dd); - return; - } - - hci_close_dev(dd); - - adapter_set_class(adapter, cls); -} - void hcid_dbus_write_simple_pairing_mode_complete(bdaddr_t *local) { struct btd_adapter *adapter; diff --git a/src/dbus-hci.h b/src/dbus-hci.h index 382e59ec..85767e72 100644 --- a/src/dbus-hci.h +++ b/src/dbus-hci.h @@ -31,7 +31,6 @@ void hcid_dbus_disconn_complete(bdaddr_t *local, uint8_t status, uint16_t handle void hcid_dbus_bonding_process_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t status); void hcid_dbus_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t status); void hcid_dbus_setscan_enable_complete(bdaddr_t *local); -void hcid_dbus_write_class_complete(bdaddr_t *local); void hcid_dbus_write_simple_pairing_mode_complete(bdaddr_t *local); int hcid_dbus_get_io_cap(bdaddr_t *local, bdaddr_t *remote, uint8_t *cap, uint8_t *auth); diff --git a/src/manager.c b/src/manager.c index 3cf914d1..4829bfb1 100644 --- a/src/manager.c +++ b/src/manager.c @@ -78,17 +78,6 @@ void manager_update_svc(const bdaddr_t *bdaddr, uint8_t svc) } } -int manager_get_adapter_class(uint16_t dev_id, uint8_t *cls) -{ - struct btd_adapter *adapter; - - adapter = manager_find_adapter_by_id(dev_id); - if (!adapter) - return -EINVAL; - - return adapter_get_class(adapter, cls); -} - static inline DBusMessage *invalid_args(DBusMessage *msg) { return g_dbus_create_error(msg, diff --git a/src/manager.h b/src/manager.h index 94635740..93dffa6f 100644 --- a/src/manager.h +++ b/src/manager.h @@ -44,5 +44,4 @@ void manager_add_adapter(const char *path); int manager_get_default_adapter(); void manager_set_default_adapter(int id); void manager_update_svc(const bdaddr_t *bdaddr, uint8_t svc); -int manager_get_adapter_class(uint16_t dev_id, uint8_t *cls); void btd_manager_set_offline(gboolean offline); diff --git a/src/security.c b/src/security.c index 646921be..d0560ced 100644 --- a/src/security.c +++ b/src/security.c @@ -683,7 +683,7 @@ static inline void cmd_complete(int dev, bdaddr_t *sba, void *ptr) hcid_dbus_setscan_enable_complete(sba); break; case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV): - hcid_dbus_write_class_complete(sba); + adapter_set_class_complete(sba, status); break; case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SIMPLE_PAIRING_MODE): hcid_dbus_write_simple_pairing_mode_complete(sba); |