module-udev-detect: support hdmi hotplug using extcon 66/286166/17 accepted/tizen/unified/20230117.140515
authorSeungbae Shin <seungbae.shin@samsung.com>
Thu, 29 Dec 2022 11:41:12 +0000 (20:41 +0900)
committerSeungbae Shin <seungbae.shin@samsung.com>
Fri, 13 Jan 2023 09:38:57 +0000 (18:38 +0900)
+ check DEVPATH instead of DRIVERS for HDMI detection on udev rules

[Version] 15.0-13
[Issue Type] Update

Change-Id: I131adee21e5dbc1add8ca6d046d74d3d60e475a1

packaging/pulseaudio.spec
src/modules/alsa/90-pulseaudio.rules
src/modules/module-udev-detect.c

index dc88318..07dc916 100644 (file)
@@ -4,7 +4,7 @@
 Name:             pulseaudio
 Summary:          Improved Linux sound server
 Version:          15.0
-Release:          12
+Release:          13
 Group:            Multimedia/Audio
 License:          LGPL-2.1
 URL:              http://pulseaudio.org
index 4811ba9..73b6746 100644 (file)
@@ -18,7 +18,7 @@
 SUBSYSTEM!="sound", GOTO="pulseaudio_end"
 ACTION!="change", GOTO="pulseaudio_end"
 KERNEL!="card*", GOTO="pulseaudio_end"
-DRIVERS=="*hdmi*", ENV{PULSE_PROFILE_SET}="default.conf" GOTO="pulseaudio_end"
+DEVPATH=="*hdmi*", ENV{PULSE_PROFILE_SET}="default.conf" GOTO="pulseaudio_end"
 SUBSYSTEMS=="usb", GOTO="pulseaudio_check_usb"
 # For tizen platform, we only allow usb devices from udev
 SUBSYSTEMS!="usb", ENV{PULSE_IGNORE}="1", GOTO="pulseaudio_end"
index a2885ce..f91ab5b 100644 (file)
@@ -470,6 +470,139 @@ static void remove_card(struct userdata *u, struct udev_device *dev) {
     device_free(d);
 }
 
+#ifdef __TIZEN__
+static void dump_device(struct udev_device *dev) {
+    pa_log_debug("[ devpath = %s", udev_device_get_devpath(dev));
+    pa_log_debug("  subsystem = %s", udev_device_get_subsystem(dev));
+    pa_log_debug("  devtype = %s", udev_device_get_devtype(dev));
+    pa_log_debug("  syspath = %s", udev_device_get_syspath(dev));
+    pa_log_debug("  sysname = %s", udev_device_get_sysname(dev));
+    pa_log_debug("  sysnum = %s", udev_device_get_sysnum(dev));
+    pa_log_debug("  devnode = %s", udev_device_get_devnode(dev));
+    pa_log_debug("  parent subsystem = %s ]", udev_device_get_subsystem(udev_device_get_parent(dev)));
+}
+
+static bool is_devpath_hdmi_extcon(struct udev_device *dev) {
+    return (bool)strstr(udev_device_get_devpath(dev), "hdmi");
+}
+
+static void add_hdmi_card(struct userdata *u, const char *card) {
+    struct udev *udev;
+    struct udev_device *card_dev;
+    char *syspath;
+
+    if (!(udev = udev_new())) {
+        pa_log_error("Failed to allocate udev context.");
+        return;
+    }
+
+    syspath = pa_sprintf_malloc("/sys/class/sound/%s", card);
+
+    if ((card_dev = udev_device_new_from_syspath(udev, syspath))) {
+        card_changed(u, card_dev);
+        udev_device_unref(card_dev);
+    } else {
+        pa_log_error("Failed to get card object.");
+    }
+
+    pa_xfree(syspath);
+    udev_unref(udev);
+}
+
+/* NOTE : based from pa_card_choose_initial_profile() */
+pa_card_profile *find_best_card_profile(pa_card *card) {
+    pa_card_profile *profile;
+    void *state;
+    pa_card_profile *best = NULL;
+
+    pa_assert(card);
+
+    pa_log_info("Looking for best profile for card %s", card->name);
+    PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+        pa_log_debug(" name(%s), avail(%s)", profile->name, pa_available_to_string(profile->available));
+        if (profile->available == PA_AVAILABLE_NO)
+            continue;
+
+        if (!best || profile->priority > best->priority)
+            best = profile;
+    }
+
+    if (!best) {
+        PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+            if (!best || profile->priority > best->priority)
+                best = profile;
+        }
+    }
+
+    if (best)
+        pa_log_info("Found best profile %s", best->name);
+    else
+        pa_log_error("No best profile!");
+
+    return best;
+}
+
+static void process_hdmi_device(struct userdata *u, struct udev_device *dev) {
+    pa_card *card;
+    uint32_t idx;
+    bool is_removed;
+    const char *devtype;
+    const char *event_str[] = { "Added", "Removed" };
+    const char *sysfs_path;
+    char *card_to_find;
+    pa_card_profile *profile_to_set;
+
+    pa_assert(u);
+    pa_assert(dev);
+
+    devtype = udev_device_get_devtype(dev);
+    if (!devtype) {
+        pa_log_error("no parent devtype exist!");
+        return;
+    }
+
+    dump_device(dev);
+    is_removed = pa_safe_streq(udev_device_get_property_value(dev, "STATE"), "HDMI=0");
+
+    if (!pa_startswith(devtype, "extcon")) {
+        pa_log_error("invalid devtype %s", devtype);
+        return;
+    }
+
+    card_to_find = pa_replace(devtype, "extcon", "card");
+
+    pa_log_info("%s %s, devtype : %s", card_to_find, event_str[is_removed], devtype);
+
+    PA_IDXSET_FOREACH(card, u->core->cards, idx) {
+        sysfs_path = pa_proplist_gets(card->proplist, "sysfs.path");
+        if (!sysfs_path) {
+            pa_log_warn("no sysfs.path property...");
+            continue;
+        }
+        pa_log_info("sysfs.path : %s, finding : %s", sysfs_path, card_to_find);
+
+        if (pa_endswith(sysfs_path, card_to_find)) {
+            pa_log_info(" card exists, update proper profile for event (%s)", event_str[is_removed]);
+
+            profile_to_set = is_removed ? pa_hashmap_get(card->profiles, "off") : find_best_card_profile(card);
+
+            if (profile_to_set)
+                pa_card_set_profile(card, profile_to_set, false);
+            else
+                pa_log_error("no profile to set!");
+
+            goto profile_done;
+        }
+    }
+
+    pa_log_info("No card found, add new card (%s)", card_to_find);
+    add_hdmi_card(u, card_to_find);
+
+profile_done:
+    pa_xfree(card_to_find);
+}
+#endif /* __TIZEN__ */
+
 static void process_device(struct userdata *u, struct udev_device *dev) {
     const char *action, *ff;
 
@@ -488,14 +621,7 @@ static void process_device(struct userdata *u, struct udev_device *dev) {
     }
 
 #ifdef __TIZEN__
-    pa_log_debug ("devpath = %s", udev_device_get_devpath(dev));
-    pa_log_debug ("subsystem = %s", udev_device_get_subsystem(dev));
-    pa_log_debug ("devtype = %s", udev_device_get_devtype(dev));
-    pa_log_debug ("syspath = %s", udev_device_get_syspath(dev));
-    pa_log_debug ("sysname = %s", udev_device_get_sysname(dev));
-    pa_log_debug ("sysnum = %s", udev_device_get_sysnum(dev));
-    pa_log_debug ("devnode = %s", udev_device_get_devnode(dev));
-    pa_log_debug ("parent subsystem = %s", udev_device_get_subsystem(udev_device_get_parent(dev)));
+    dump_device(dev);
 #endif
     action = udev_device_get_action(dev);
 
@@ -540,6 +666,14 @@ static void monitor_cb(
         goto fail;
     }
 
+#ifdef __TIZEN__
+    if (is_devpath_hdmi_extcon(dev)) {
+        pa_log_info("HDMI, devpath : %s", udev_device_get_devpath(dev));
+        process_hdmi_device(u, dev);
+        udev_device_unref(dev);
+        return;
+    }
+#endif
     if (!path_get_card_id(udev_device_get_devpath(dev))) {
         udev_device_unref(dev);
         return;
@@ -807,6 +941,12 @@ int pa__init(pa_module *m) {
         pa_log("Failed to subscribe to sound devices.");
         goto fail;
     }
+#ifdef __TIZEN__
+    if (udev_monitor_filter_add_match_subsystem_devtype(u->monitor, "extcon", NULL) < 0) {
+        pa_log("Failed to subscribe to extcon devices.");
+        goto fail;
+    }
+#endif
 
     errno = 0;
     if (udev_monitor_enable_receiving(u->monitor) < 0) {