Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/bluetooth offset processing of input reports to a ke...



details:   https://anonhg.NetBSD.org/src/rev/c6e3eb2f7f63
branches:  trunk
changeset: 772680:c6e3eb2f7f63
user:      plunky <plunky%NetBSD.org@localhost>
date:      Wed Jan 11 17:27:45 2012 +0000

description:
offset processing of input reports to a kernel thread, to avoid
locking issues when a child device needs to call back into the
Bluetooth stack (eg when caps-lock is pressed, and wskbd wants
to change a LED)

(as discussed with Radoslaw Kujawa)

diffstat:

 sys/dev/bluetooth/bthidev.c |  237 ++++++++++++++++++++++++++++++-------------
 sys/dev/bluetooth/btkbd.c   |    6 +-
 2 files changed, 165 insertions(+), 78 deletions(-)

diffs (truncated from 366 to 300 lines):

diff -r bd3c923115e1 -r c6e3eb2f7f63 sys/dev/bluetooth/bthidev.c
--- a/sys/dev/bluetooth/bthidev.c       Wed Jan 11 17:25:19 2012 +0000
+++ b/sys/dev/bluetooth/bthidev.c       Wed Jan 11 17:27:45 2012 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: bthidev.c,v 1.20 2011/12/31 01:16:09 rkujawa Exp $     */
+/*     $NetBSD: bthidev.c,v 1.21 2012/01/11 17:27:45 plunky Exp $      */
 
 /*-
  * Copyright (c) 2006 Itronix Inc.
@@ -32,16 +32,19 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bthidev.c,v 1.20 2011/12/31 01:16:09 rkujawa Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bthidev.c,v 1.21 2012/01/11 17:27:45 plunky Exp $");
 
 #include <sys/param.h>
+#include <sys/condvar.h>
 #include <sys/conf.h>
 #include <sys/device.h>
 #include <sys/fcntl.h>
 #include <sys/kernel.h>
+#include <sys/kthread.h>
 #include <sys/queue.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
+#include <sys/mutex.h>
 #include <sys/proc.h>
 #include <sys/socketvar.h>
 #include <sys/systm.h>
@@ -83,6 +86,12 @@
        struct l2cap_channel    *sc_int;        /* interrupt channel */
        struct l2cap_channel    *sc_int_l;      /* interrupt listen */
 
+       MBUFQ_HEAD()            sc_inq;         /* input queue */
+       kmutex_t                sc_lock;        /* input queue lock */
+       kcondvar_t              sc_cv;          /* input queue trigger */
+       lwp_t                   *sc_lwp;        /* input queue processor */
+       int                     sc_detach;
+
        LIST_HEAD(,bthidev)     sc_list;        /* child list */
 
        callout_t               sc_reconnect;
@@ -107,6 +116,8 @@
 static int  bthidev_connect(struct bthidev_softc *);
 static int  bthidev_output(struct bthidev *, uint8_t *, int);
 static void bthidev_null(struct bthidev *, uint8_t *, int);
+static void bthidev_process(void *);
+static void bthidev_process_one(struct bthidev_softc *, struct mbuf *);
 
 /* autoconf(9) glue */
 static int  bthidev_match(device_t, cfdata_t, void *);
@@ -188,6 +199,7 @@
         */
        sc->sc_dev = self;
        LIST_INIT(&sc->sc_list);
+       MBUFQ_INIT(&sc->sc_inq);
        callout_init(&sc->sc_reconnect, 0);
        callout_setfunc(&sc->sc_reconnect, bthidev_timeout, sc);
        sc->sc_state = BTHID_CLOSED;
@@ -196,6 +208,8 @@
        sc->sc_intpsm = L2CAP_PSM_HID_INTR;
 
        sockopt_init(&sc->sc_mode, BTPROTO_L2CAP, SO_L2CAP_LM, 0);
+       mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
+       cv_init(&sc->sc_cv, device_xname(self));
 
        /*
         * extract config from proplist
@@ -280,6 +294,12 @@
 
        aprint_normal("\n");
 
+       if (kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL, bthidev_process,
+           sc, &sc->sc_lwp, "%s", device_xname(self)) != 0) {
+               aprint_error_dev(self, "failed to create input thread\n");
+               return;
+       }
+
        for (rep = 0 ; rep <= maxid ; rep++) {
                if (hid_report_size(desc, dlen, hid_feature, rep) == 0
                    && hid_report_size(desc, dlen, hid_input, rep) == 0
@@ -362,12 +382,25 @@
 
        mutex_exit(bt_lock);
 
+       /* kill off the input processor */
+       if (sc->sc_lwp != NULL) {
+               mutex_enter(&sc->sc_lock);
+               sc->sc_detach = 1;
+               cv_signal(&sc->sc_cv);
+               mutex_exit(&sc->sc_lock);
+               kthread_join(sc->sc_lwp);
+               sc->sc_lwp = NULL;
+       }
+
        /* detach children */
        while ((hidev = LIST_FIRST(&sc->sc_list)) != NULL) {
                LIST_REMOVE(hidev, sc_next);
                config_detach(hidev->sc_dev, flags);
        }
 
+       MBUFQ_DRAIN(&sc->sc_inq);
+       cv_destroy(&sc->sc_cv);
+       mutex_destroy(&sc->sc_lock);
        sockopt_destroy(&sc->sc_mode);
 
        return 0;
@@ -545,6 +578,123 @@
        return 0;
 }
 
+/*
+ * The LWP which processes input reports, forwarding to child devices.
+ * We are always either processing input reports, holding the lock, or
+ * waiting for a signal on condvar.
+ */
+static void
+bthidev_process(void *arg)
+{
+       struct bthidev_softc *sc = arg;
+       struct mbuf *m;
+
+       mutex_enter(&sc->sc_lock);
+       while (sc->sc_detach == 0) {
+               MBUFQ_DEQUEUE(&sc->sc_inq, m);
+               if (m == NULL) {
+                       cv_wait(&sc->sc_cv, &sc->sc_lock);
+                       continue;
+               }
+
+               mutex_exit(&sc->sc_lock);
+               bthidev_process_one(sc, m);
+               m_freem(m);
+               mutex_enter(&sc->sc_lock);
+       }
+       mutex_exit(&sc->sc_lock);
+       kthread_exit(0);
+}
+
+static void
+bthidev_process_one(struct bthidev_softc *sc, struct mbuf *m)
+{
+       struct bthidev *hidev;
+       uint8_t *data;
+       int len;
+
+       if (sc->sc_state != BTHID_OPEN)
+               return;
+
+       if (m->m_pkthdr.len > m->m_len)
+               aprint_error_dev(sc->sc_dev, "truncating HID report\n");
+
+       len = m->m_len;
+       data = mtod(m, uint8_t *);
+
+       switch (BTHID_TYPE(data[0])) {
+       case BTHID_DATA:
+               /*
+                * data[0] == type / parameter
+                * data[1] == id
+                * data[2..len] == report
+                */
+               if (len < 3)
+                       break;
+
+               LIST_FOREACH(hidev, &sc->sc_list, sc_next)
+                       if (data[1] == hidev->sc_id)
+                               break;
+
+               if (hidev == NULL) {
+                       aprint_error_dev(sc->sc_dev,
+                           "report id %d, len = %d ignored\n", data[1], len - 2);
+
+                       break;
+               }
+
+               switch (BTHID_DATA_PARAM(data[0])) {
+               case BTHID_DATA_INPUT:
+                       (*hidev->sc_input)(hidev, data + 2, len - 2);
+                       break;
+
+               case BTHID_DATA_FEATURE:
+                       (*hidev->sc_feature)(hidev, data + 2, len - 2);
+                       break;
+
+               default:
+                       break;
+               }
+
+               break;
+
+       case BTHID_CONTROL:
+               if (len < 1)
+                       break;
+
+               switch (BTHID_DATA_PARAM(data[0])) {
+               case BTHID_CONTROL_UNPLUG:
+                       aprint_normal_dev(sc->sc_dev, "unplugged\n");
+
+                       mutex_enter(bt_lock);
+                       /* close interrupt channel */
+                       if (sc->sc_int != NULL) {
+                               l2cap_disconnect(sc->sc_int, 0);
+                               l2cap_detach(&sc->sc_int);
+                               sc->sc_int = NULL;
+                       }
+
+                       /* close control channel */
+                       if (sc->sc_ctl != NULL) {
+                               l2cap_disconnect(sc->sc_ctl, 0);
+                               l2cap_detach(&sc->sc_ctl);
+                               sc->sc_ctl = NULL;
+                       }
+                       mutex_exit(bt_lock);
+
+                       break;
+
+               default:
+                       break;
+               }
+
+               break;
+
+       default:
+               break;
+       }
+}
+
 /*****************************************************************************
  *
  *     bluetooth(9) callback methods for L2CAP
@@ -787,85 +937,23 @@
 }
 
 /*
- * Receive reports from the protocol stack.
+ * Receive reports from the protocol stack. Because this will be called
+ * with bt_lock held, we queue the mbuf and process it with a kernel thread
  */
 static void
 bthidev_input(void *arg, struct mbuf *m)
 {
        struct bthidev_softc *sc = arg;
-       struct bthidev *hidev;
-       uint8_t *data;
-       int len;
 
-       if (sc->sc_state != BTHID_OPEN)
-               goto release;
-
-       if (m->m_pkthdr.len > m->m_len)
-               aprint_error_dev(sc->sc_dev, "truncating HID report\n");
-
-       len = m->m_len;
-       data = mtod(m, uint8_t *);
-
-       if (BTHID_TYPE(data[0]) == BTHID_DATA) {
-               /*
-                * data[0] == type / parameter
-                * data[1] == id
-                * data[2..len] == report
-                */
-               if (len < 3)
-                       goto release;
-
-               LIST_FOREACH(hidev, &sc->sc_list, sc_next) {
-                       if (data[1] == hidev->sc_id) {
-                               switch (BTHID_DATA_PARAM(data[0])) {
-                               case BTHID_DATA_INPUT:
-                                       (*hidev->sc_input)(hidev, data + 2, len - 2);
-                                       break;
-
-                               case BTHID_DATA_FEATURE:
-                                       (*hidev->sc_feature)(hidev, data + 2, len - 2);
-                                       break;
-
-                               default:
-                                       break;
-                               }
-
-                               goto release;
-                       }
-               }
-               aprint_error_dev(sc->sc_dev, "report id %d, len = %d ignored\n",
-                   data[1], len - 2);
-
-               goto release;
+       if (sc->sc_state != BTHID_OPEN) {
+               m_freem(m);
+               return;
        }
 
-       if (BTHID_TYPE(data[0]) == BTHID_CONTROL) {
-               if (len < 1)
-                       goto release;
-
-               if (BTHID_DATA_PARAM(data[0]) == BTHID_CONTROL_UNPLUG) {
-                       aprint_normal_dev(sc->sc_dev, "unplugged\n");
-



Home | Main Index | Thread Index | Old Index