Module Name:    src
Committed By:   martin
Date:           Fri Aug 23 15:34:47 UTC 2024

Modified Files:
        src/sys/dev/hdaudio [netbsd-9]: hdafg.c

Log Message:
Pull up following revision(s) (requested by riastradh in ticket #1868):

        sys/dev/hdaudio/hdafg.c: revision 1.30

hdafg(4): Do hotplug detection in kthread, not callout.

This can sometimes take a while (~1ms), and the logic to suspend the
callout on device suspend/resume was racy (PR kern/57322).


To generate a diff of this commit:
cvs rdiff -u -r1.18.2.2 -r1.18.2.3 src/sys/dev/hdaudio/hdafg.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/hdaudio/hdafg.c
diff -u src/sys/dev/hdaudio/hdafg.c:1.18.2.2 src/sys/dev/hdaudio/hdafg.c:1.18.2.3
--- src/sys/dev/hdaudio/hdafg.c:1.18.2.2	Sat Feb  3 14:24:38 2024
+++ src/sys/dev/hdaudio/hdafg.c	Fri Aug 23 15:34:47 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: hdafg.c,v 1.18.2.2 2024/02/03 14:24:38 martin Exp $ */
+/* $NetBSD: hdafg.c,v 1.18.2.3 2024/08/23 15:34:47 martin Exp $ */
 
 /*
  * Copyright (c) 2009 Precedence Technologies Ltd <supp...@precedence.co.uk>
@@ -60,7 +60,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hdafg.c,v 1.18.2.2 2024/02/03 14:24:38 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hdafg.c,v 1.18.2.3 2024/08/23 15:34:47 martin Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -71,6 +71,9 @@ __KERNEL_RCSID(0, "$NetBSD: hdafg.c,v 1.
 #include <sys/bus.h>
 #include <sys/kmem.h>
 #include <sys/module.h>
+#include <sys/condvar.h>
+#include <sys/kthread.h>
+#include <sys/mutex.h>
 
 #include <sys/audioio.h>
 #include <dev/audio/audio_if.h>
@@ -311,8 +314,12 @@ struct hdafg_softc {
 	int				sc_pchan, sc_rchan;
 	audio_params_t			sc_pparam, sc_rparam;
 
-	struct callout			sc_jack_callout;
+	kmutex_t			sc_jack_lock;
+	kcondvar_t			sc_jack_cv;
+	struct lwp			*sc_jack_thread;
 	bool				sc_jack_polling;
+	bool				sc_jack_suspended;
+	bool				sc_jack_dying;
 
 	struct {
 		uint32_t		afg_cap;
@@ -3505,16 +3512,18 @@ hdafg_configure_encodings(struct hdafg_s
 }
 
 static void
-hdafg_hp_switch_handler(void *opaque)
+hdafg_hp_switch_handler(struct hdafg_softc *sc)
 {
-	struct hdafg_softc *sc = opaque;
 	struct hdaudio_assoc *as = sc->sc_assocs;
 	struct hdaudio_widget *w;
 	uint32_t res = 0;
 	int i, j;
 
+	KASSERT(sc->sc_jack_polling);
+	KASSERT(mutex_owned(&sc->sc_jack_lock));
+
 	if (!device_is_active(sc->sc_dev))
-		goto resched;
+		return;
 
 	for (i = 0; i < sc->sc_nassocs; i++) {
 		if (as[i].as_digital != HDAFG_AS_ANALOG &&
@@ -3573,9 +3582,28 @@ hdafg_hp_switch_handler(void *opaque)
 			}
 		}
 	}
+}
+
+static void
+hdafg_hp_switch_thread(void *opaque)
+{
+	struct hdafg_softc *sc = opaque;
+
+	KASSERT(sc->sc_jack_polling);
+
+	mutex_enter(&sc->sc_jack_lock);
+	while (!sc->sc_jack_dying) {
+		if (sc->sc_jack_suspended) {
+			cv_wait(&sc->sc_jack_cv, &sc->sc_jack_lock);
+			continue;
+		}
+		hdafg_hp_switch_handler(sc);
+		(void)cv_timedwait(&sc->sc_jack_cv, &sc->sc_jack_lock,
+		    HDAUDIO_HP_SENSE_PERIOD);
+	}
+	mutex_exit(&sc->sc_jack_lock);
 
-resched:
-	callout_schedule(&sc->sc_jack_callout, HDAUDIO_HP_SENSE_PERIOD);
+	kthread_exit(0);
 }
 
 static void
@@ -3585,6 +3613,7 @@ hdafg_hp_switch_init(struct hdafg_softc 
 	struct hdaudio_widget *w;
 	bool enable = false;
 	int i, j;
+	int error;
 
 	for (i = 0; i < sc->sc_nassocs; i++) {
 		if (as[i].as_hpredir < 0 && as[i].as_displaydev == false)
@@ -3643,11 +3672,24 @@ hdafg_hp_switch_init(struct hdafg_softc 
 			hda_trace1(sc, ",displayport");
 		hda_trace1(sc, "]\n");
 	}
-	if (enable) {
-		sc->sc_jack_polling = true;
-		hdafg_hp_switch_handler(sc);
-	} else
+	if (!enable) {
 		hda_trace(sc, "jack detect not enabled\n");
+		return;
+	}
+
+	mutex_init(&sc->sc_jack_lock, MUTEX_DEFAULT, IPL_NONE);
+	cv_init(&sc->sc_jack_cv, "hdafghp");
+	sc->sc_jack_polling = true;
+	error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, /*ci*/NULL,
+	    hdafg_hp_switch_thread, sc, &sc->sc_jack_thread,
+	    "%s hotplug detect", device_xname(sc->sc_dev));
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "failed to create hotplug thread:"
+		    " %d", error);
+		sc->sc_jack_polling = false;
+		cv_destroy(&sc->sc_jack_cv);
+		mutex_destroy(&sc->sc_jack_lock);
+	}
 }
 
 static void
@@ -3669,10 +3711,6 @@ hdafg_attach(device_t parent, device_t s
 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
 
-	callout_init(&sc->sc_jack_callout, 0);
-	callout_setfunc(&sc->sc_jack_callout,
-	    hdafg_hp_switch_handler, sc);
-
 	if (!pmf_device_register(self, hdafg_suspend, hdafg_resume))
 		aprint_error_dev(self, "couldn't establish power handler\n");
 
@@ -3820,8 +3858,16 @@ hdafg_detach(device_t self, int flags)
 	struct hdaudio_mixer *mx = sc->sc_mixers;
 	int nid;
 
-	callout_halt(&sc->sc_jack_callout, NULL);
-	callout_destroy(&sc->sc_jack_callout);
+	if (sc->sc_jack_polling) {
+		int error __diagused;
+
+		mutex_enter(&sc->sc_jack_lock);
+		sc->sc_jack_dying = true;
+		cv_broadcast(&sc->sc_jack_cv);
+		mutex_exit(&sc->sc_jack_lock);
+		error = kthread_join(sc->sc_jack_thread);
+		KASSERTMSG(error == 0, "error=%d", error);
+	}
 
 	if (sc->sc_config)
 		prop_object_release(sc->sc_config);
@@ -3871,7 +3917,12 @@ hdafg_suspend(device_t self, const pmf_q
 {
 	struct hdafg_softc *sc = device_private(self);
 
-	callout_halt(&sc->sc_jack_callout, NULL);
+	if (sc->sc_jack_polling) {
+		mutex_enter(&sc->sc_jack_lock);
+		KASSERT(!sc->sc_jack_suspended);
+		sc->sc_jack_suspended = true;
+		mutex_exit(&sc->sc_jack_lock);
+	}
 
 	return true;
 }
@@ -3902,8 +3953,13 @@ hdafg_resume(device_t self, const pmf_qu
 	hdafg_stream_connect(sc, AUMODE_PLAY);
 	hdafg_stream_connect(sc, AUMODE_RECORD);
 
-	if (sc->sc_jack_polling)
-		hdafg_hp_switch_handler(sc);
+	if (sc->sc_jack_polling) {
+		mutex_enter(&sc->sc_jack_lock);
+		KASSERT(sc->sc_jack_suspended);
+		sc->sc_jack_suspended = false;
+		cv_broadcast(&sc->sc_jack_cv);
+		mutex_exit(&sc->sc_jack_lock);
+	}
 
 	return true;
 }

Reply via email to