]> www.infradead.org Git - users/dwmw2/pidgin-headset.git/commitdiff
Fix headset hotplug
authorDavid Woodhouse <dwmw@amazon.co.uk>
Tue, 26 Nov 2019 14:57:09 +0000 (14:57 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Tue, 26 Nov 2019 14:57:09 +0000 (14:57 +0000)
Kill the old GIOChannel on shutdown (otherwise events were duplicated
after a manual rescan because the original one wasn't really torn down).

Remove the need for manual rescan anyway, by automatically rescanning
whenever a call starts... and by tearing down an existing headset when
its fd returns error.

jabra.c

diff --git a/jabra.c b/jabra.c
index 1097938ba41ac74ccc1a1b394fe0c1c67be63da4..caed49d9595d729e97414096620cafb1308d7895 100644 (file)
--- a/jabra.c
+++ b/jabra.c
@@ -25,6 +25,7 @@
 
 struct jabra_headset {
        GIOChannel *ch;
+       guint watch_src;
        int fd;
        gboolean muted;
        gboolean connected;
@@ -248,18 +249,25 @@ static int doListDev(char *path) {
   if ((fd = open(path, O_RDONLY)) != -1) {
     if (ioctl(fd, HIDIOCGDEVINFO, &devinfo) == -1) {
       perror("ioctl HIDIOCGDEVINFO");
+      close(fd);
       return -1;
     }
     if (ioctl(fd, HIDIOCGNAME(sizeof(name)), name) == -1) {
       perror("ioctl HIDIOCGNAME");
+      close(fd);
       return -1;
     }
     if (ioctl(fd, HIDIOCGVERSION, &version) == -1) {
       perror("ioctl HIDIOCGVERSION");
+      close(fd);
       return -1;
     }
-    return (devinfo.vendor == JABRA_VID ||
-           devinfo.vendor == PLANTRONICS_VID);
+    if (devinfo.vendor != JABRA_VID &&
+       devinfo.vendor != PLANTRONICS_VID) {
+           close(fd);
+           return -1;
+    }
+    return fd;
   }
 
   if (errno == ENOENT) {
@@ -413,8 +421,11 @@ static gboolean jabra_in(GIOChannel *gio, GIOCondition condition, gpointer user_
        gsize rd;
 
        GIOStatus ret = g_io_channel_read_chars(gio, (void *)&ev, sizeof(ev), &rd, NULL);
-       if (ret != G_IO_STATUS_NORMAL)
+       if (ret != G_IO_STATUS_NORMAL) {
+               purple_debug(PURPLE_DEBUG_INFO, "headset", "Unplugged\n");
+               shutdown_headset();
                return FALSE;
+       }
 
        for (i = 0; i < rd / sizeof(ev[0]); i++) {
                fprintf(stdout, "Event: %x = %d\n", ev[i].hid, ev[i].value);
@@ -477,26 +488,21 @@ gboolean init_headset(void)
        pthread_t event_thread;
        __s32 val;
 
+       fd = -1;
        for (i = 0; i < 19; i++) {
                sprintf(name, "/dev/usb/hiddev%d", i);
-               if (doListDev(name) == 1) {
+               fd = doListDev(name);
+               if (fd != -1)
                        break;
-               }
        }
 
-       if (i == 19) {
+       if (fd == -1) {
                purple_debug(PURPLE_DEBUG_INFO, "headset", "No Jabra device found\n");
                return FALSE;
        }
 
-       sprintf(name, "/dev/usb/hiddev%d", i);
        purple_debug(PURPLE_DEBUG_INFO, "headset", "Using device %s\n", name);
 
-       if ((fd = open(name, O_RDONLY)) < 0) {
-               purple_debug(PURPLE_DEBUG_INFO, "headset", "Failed to open %s\n", name);
-               return FALSE;
-       }
-
        ioctl(fd, HIDIOCINITREPORT, 0);
        ioctl(fd, HIDIOCGNAME(sizeof(name)), name);
        purple_debug(PURPLE_DEBUG_INFO, "headset", "HID device name: \"%s\"\n", name);
@@ -515,18 +521,26 @@ gboolean init_headset(void)
 
        fcntl(fd, F_SETFL, O_NONBLOCK);
 
-       GIOChannel *ch = g_io_channel_unix_new(fd);
-       g_io_channel_set_encoding(ch, NULL, NULL);
-       g_io_channel_set_close_on_unref(ch, TRUE);
-       g_io_channel_set_buffered (ch, FALSE);
-       g_io_add_watch(ch, G_IO_IN | G_IO_HUP, jabra_in, NULL);
+       jabra.ch = g_io_channel_unix_new(fd);
+       g_io_channel_set_encoding(jabra.ch, NULL, NULL);
+       g_io_channel_set_close_on_unref(jabra.ch, TRUE);
+       g_io_channel_set_buffered (jabra.ch, FALSE);
+       jabra.watch_src = g_io_add_watch(jabra.ch, G_IO_IN | G_IO_HUP | G_IO_ERR, jabra_in, NULL);
+
        return TRUE;
 }
 
 void shutdown_headset(void)
 {
-       g_object_unref(jabra.ch);
-       jabra.ch = NULL;
+       if (jabra.watch_src) {
+               g_source_remove(jabra.watch_src);
+               jabra.watch_src = 0;
+       }
+       if (jabra.ch) {
+               g_io_channel_shutdown(jabra.ch, FALSE, NULL);
+               g_io_channel_unref(jabra.ch);
+               jabra.ch = NULL;
+       }
        jabra.fd = -1;
 }
 
@@ -542,6 +556,11 @@ void mute_headset(gpointer user_data, gboolean muted)
 void connect_headset(gpointer user_data, gboolean connected)
 {
        purple_debug(PURPLE_DEBUG_INFO, "headset", "connect %d\n", connected);
+
+       /* Rescan headset whenever a call starts */
+       if (connected && jabra.fd == -1)
+               init_headset();
+
        if (jabra.connected != connected) {
                jabra.connected = connected;
                if (!connected) {