Return-Path: From: Alex Deymo To: linux-bluetooth@vger.kernel.org Cc: marcel@holtmann.org, keybuk@chromium.org, Alex Deymo Subject: [PATCH v7 5/5] input: Automatically attempt a reconnect when required. Date: Tue, 16 Apr 2013 14:58:41 -0700 Message-Id: <1366149521-26908-6-git-send-email-deymo@chromium.org> In-Reply-To: <1366149521-26908-1-git-send-email-deymo@chromium.org> References: <1366149521-26908-1-git-send-email-deymo@chromium.org> List-ID: The HID 1.1 spec requires a host to attempt a reconnection when a HID device goes out of range and comes back. There is test (see TP/HCE/BV-04-I [Host Initiated Re-connection]) to ensure that the host initiates the connection when that happens. This patch adds a reconnection timer for HID devices trying to reconnect every 30s for a maximum of 3 minutes after the connection to a HID device with a ReconnectMode of "host" or "any" is closed. --- profiles/input/device.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/profiles/input/device.c b/profiles/input/device.c index 6ffc199..4af95d8 100644 --- a/profiles/input/device.c +++ b/profiles/input/device.c @@ -81,10 +81,15 @@ struct input_device { bool disable_sdp; char *name; enum reconnect_mode_t reconnect_mode; + guint reconnect_timer; + uint32_t reconnect_attempt; }; static GSList *devices = NULL; +static void input_device_enter_reconnect_mode(struct input_device *idev); +static const char *reconnect_mode_to_string(const enum reconnect_mode_t mode); + static struct input_device *find_device_by_path(GSList *list, const char *path) { for (; list; list = list->next) { @@ -126,6 +131,9 @@ static void input_device_free(struct input_device *idev) g_free(idev->req); } + if (idev->reconnect_timer > 0) + g_source_remove(idev->reconnect_timer); + g_free(idev->uuid); g_free(idev); @@ -160,6 +168,9 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data if (idev->ctrl_io && !(cond & G_IO_NVAL)) g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL); + /* Enter the auto-reconnect mode if needed */ + input_device_enter_reconnect_mode(idev); + return FALSE; } @@ -654,6 +665,62 @@ static int dev_connect(struct input_device *idev) return -EIO; } +static gboolean input_device_auto_reconnect(gpointer user_data) +{ + struct input_device *idev = user_data; + + DBG("path=%s, attempt=%d", idev->path, idev->reconnect_attempt); + + /* Stop the recurrent reconnection attempts if the device is reconnected + * or is marked for removal. */ + if (device_is_temporary(idev->device) || + device_is_connected(idev->device)) + return FALSE; + + /* Only attempt an auto-reconnect for at most 3 minutes (6 * 30s). */ + if (idev->reconnect_attempt >= 6) + return FALSE; + + /* Check if the profile is already connected. */ + if (idev->ctrl_io) + return FALSE; + + if (is_connected(idev)) + return FALSE; + + idev->reconnect_attempt++; + dev_connect(idev); + + return TRUE; +} + +static void input_device_enter_reconnect_mode(struct input_device *idev) +{ + DBG("path=%s reconnect_mode=%s", idev->path, + reconnect_mode_to_string(idev->reconnect_mode)); + + /* Only attempt an auto-reconnect when the device is required to accept + * reconnections from the host. */ + if (idev->reconnect_mode != RECONNECT_ANY && + idev->reconnect_mode != RECONNECT_HOST) + return; + + /* If the device is temporary we are not required to reconnect with the + * device. This is likely the case of a removing device. */ + if (device_is_temporary(idev->device) || + device_is_connected(idev->device)) + return; + + if (idev->reconnect_timer > 0) + g_source_remove(idev->reconnect_timer); + + DBG("registering auto-reconnect"); + idev->reconnect_attempt = 0; + idev->reconnect_timer = g_timeout_add_seconds(30, + input_device_auto_reconnect, idev); + +} + int input_device_connect(struct btd_device *dev, struct btd_profile *profile) { struct input_device *idev; -- 1.8.1.3