diff --git a/src/service.c b/src/service.c
index 378dd2d..298d600 100644
--- a/src/service.c
+++ b/src/service.c
gint ref;
struct btd_device *device;
struct btd_profile *profile;
+ btd_service_state_t state;
};
+static const char *state2str(btd_service_state_t state)
+{
+ switch (state) {
+ case BTD_SERVICE_STATE_UNAVAILABLE:
+ return "unavailable";
+ case BTD_SERVICE_STATE_DISCONNECTED:
+ return "disconnected";
+ case BTD_SERVICE_STATE_CONNECTING:
+ return "connecting";
+ case BTD_SERVICE_STATE_CONNECTED:
+ return "connected";
+ case BTD_SERVICE_STATE_DISCONNECTING:
+ return "disconnecting";
+ }
+
+ return NULL;
+}
+
+static void change_state(struct btd_service *service, btd_service_state_t state,
+ int err)
+{
+ btd_service_state_t old = service->state;
+ char addr[18];
+
+ if (state == old)
+ return;
+
+ assert(service->device != NULL);
+ assert(service->profile != NULL);
+
+ service->state = state;
+
+ ba2str(device_get_address(service->device), addr);
+ DBG("%p: device %s profile %s state changed: %s -> %s (%d)", service,
+ addr, service->profile->name,
+ state2str(old), state2str(state), err);
+}
+
struct btd_service *btd_service_ref(struct btd_service *service)
{
service->ref++;
service->ref = 1;
service->device = device; /* Weak ref */
service->profile = profile;
+ service->state = BTD_SERVICE_STATE_UNAVAILABLE;
return service;
}
char addr[18];
int err;
+ assert(service->state == BTD_SERVICE_STATE_UNAVAILABLE);
+
err = service->profile->device_probe(service->profile, service->device);
- if (err == 0)
+ if (err == 0) {
+ change_state(service, BTD_SERVICE_STATE_DISCONNECTED, 0);
return 0;
+ }
ba2str(device_get_address(service->device), addr);
error("%s profile probe failed for %s", service->profile->name, addr);
void service_shutdown(struct btd_service *service)
{
+ change_state(service, BTD_SERVICE_STATE_UNAVAILABLE, 0);
service->profile->device_remove(service->profile, service->device);
service->device = NULL;
service->profile = NULL;
if (!profile->connect)
return -ENOTSUP;
+ switch (service->state) {
+ case BTD_SERVICE_STATE_UNAVAILABLE:
+ return -EINVAL;
+ case BTD_SERVICE_STATE_DISCONNECTED:
+ break;
+ case BTD_SERVICE_STATE_CONNECTING:
+ case BTD_SERVICE_STATE_CONNECTED:
+ return -EALREADY;
+ case BTD_SERVICE_STATE_DISCONNECTING:
+ return -EBUSY;
+ }
+
+ change_state(service, BTD_SERVICE_STATE_CONNECTING, 0);
+
err = profile->connect(service->device, service->profile);
if (err == 0)
return 0;
error("%s profile connect failed for %s: %s", profile->name, addr,
strerror(-err));
+ btd_service_connecting_complete(service, err);
+
return err;
}
if (!profile->disconnect)
return -ENOTSUP;
+ switch (service->state) {
+ case BTD_SERVICE_STATE_UNAVAILABLE:
+ return -EINVAL;
+ case BTD_SERVICE_STATE_DISCONNECTED:
+ case BTD_SERVICE_STATE_DISCONNECTING:
+ return -EALREADY;
+ case BTD_SERVICE_STATE_CONNECTING:
+ case BTD_SERVICE_STATE_CONNECTED:
+ break;
+ }
+
+ change_state(service, BTD_SERVICE_STATE_DISCONNECTING, 0);
+
err = profile->disconnect(service->device, service->profile);
if (err == 0)
return 0;
error("%s profile disconnect failed for %s: %s", profile->name, addr,
strerror(-err));
+ btd_service_disconnecting_complete(service, err);
+
return err;
}
{
return service->profile;
}
+
+btd_service_state_t btd_service_get_state(const struct btd_service *service)
+{
+ return service->state;
+}
+
+void btd_service_connecting_complete(struct btd_service *service, int err)
+{
+ if (service->state != BTD_SERVICE_STATE_DISCONNECTED &&
+ service->state != BTD_SERVICE_STATE_CONNECTING)
+ return;
+
+ if (err == 0)
+ change_state(service, BTD_SERVICE_STATE_CONNECTED, 0);
+ else
+ change_state(service, BTD_SERVICE_STATE_DISCONNECTED, err);
+}
+
+void btd_service_disconnecting_complete(struct btd_service *service, int err)
+{
+ if (service->state != BTD_SERVICE_STATE_CONNECTED &&
+ service->state != BTD_SERVICE_STATE_DISCONNECTING)
+ return;
+
+ if (err == 0)
+ change_state(service, BTD_SERVICE_STATE_DISCONNECTED, 0);
+ else /* If disconnect fails, we assume it remains connected */
+ change_state(service, BTD_SERVICE_STATE_CONNECTED, err);
+}
diff --git a/src/service.h b/src/service.h
index f242f2f..8fb1ed5 100644
--- a/src/service.h
+++ b/src/service.h
*
*/
+typedef enum {
+ BTD_SERVICE_STATE_UNAVAILABLE, /* Not probed */
+ BTD_SERVICE_STATE_DISCONNECTED,
+ BTD_SERVICE_STATE_CONNECTING,
+ BTD_SERVICE_STATE_CONNECTED,
+ BTD_SERVICE_STATE_DISCONNECTING,
+} btd_service_state_t;
+
struct btd_service;
struct btd_device;
struct btd_profile;
/* Public member access */
struct btd_device *btd_service_get_device(const struct btd_service *service);
struct btd_profile *btd_service_get_profile(const struct btd_service *service);
+btd_service_state_t btd_service_get_state(const struct btd_service *service);
+
+/* Functions used by profile implementation */
+void btd_service_connecting_complete(struct btd_service *service, int err);
+void btd_service_disconnecting_complete(struct btd_service *service, int err);