summaryrefslogtreecommitdiff
path: root/sys/dev/bluetooth/btms.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/bluetooth/btms.c')
-rw-r--r--sys/dev/bluetooth/btms.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/sys/dev/bluetooth/btms.c b/sys/dev/bluetooth/btms.c
new file mode 100644
index 00000000000..fa49dcc570e
--- /dev/null
+++ b/sys/dev/bluetooth/btms.c
@@ -0,0 +1,317 @@
+/* $OpenBSD: btms.c,v 1.1 2007/07/27 16:52:24 gwk Exp $ */
+/* $NetBSD: btms.c,v 1.6 2007/03/04 06:01:45 christos Exp $ */
+
+/*-
+ * Copyright (c) 2006 Itronix Inc.
+ * All rights reserved.
+ *
+ * Written by Iain Hibbert for Itronix Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * based on dev/usb/ums.c
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+
+#include <netbt/bluetooth.h>
+
+#include <dev/bluetooth/bthid.h>
+#include <dev/bluetooth/bthidev.h>
+
+#include <dev/usb/hid.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+
+#define MAX_BUTTONS 31
+#define BUTTON(n) (1 << (((n) == 1 || (n) == 2) ? 3 - (n) : (n)))
+#define NOTMOUSE(f) (((f) & (HIO_CONST | HIO_RELATIVE)) != HIO_RELATIVE)
+
+struct btms_softc {
+ struct bthidev sc_hidev; /* device+ */
+
+ struct device *sc_wsmouse; /* child */
+ int sc_enabled;
+ uint16_t sc_flags;
+
+ /* locators */
+ struct hid_location sc_loc_x;
+ struct hid_location sc_loc_y;
+ struct hid_location sc_loc_z;
+ struct hid_location sc_loc_w;
+ struct hid_location sc_loc_button[MAX_BUTTONS];
+
+ int sc_num_buttons;
+ uint32_t sc_buttons;
+};
+
+/* sc_flags */
+#define BTMS_REVZ (1 << 0) /* reverse Z direction */
+#define BTMS_HASZ (1 << 1) /* has Z direction */
+#define BTMS_HASW (1 << 2) /* has W direction */
+
+int btms_match(struct device *, struct cfdata *, void *);
+void btms_attach(struct device *, struct device *, void *);
+int btms_detach(struct device *, int);
+
+struct cfdriver btms_cd = {
+ NULL, "btms", DV_DULL
+};
+
+const struct cfattach btms_ca = {
+ sizeof(struct btms_softc),
+ btms_match,
+ btms_attach,
+ btms_detach,
+};
+
+/* wsmouse(4) accessops */
+int btms_enable(void *);
+int btms_ioctl(void *, unsigned long, void *, int, struct lwp *);
+void btms_disable(void *);
+
+const struct wsmouse_accessops btms_accessops = {
+ btms_enable,
+ btms_ioctl,
+ btms_disable,
+};
+
+/* bthid methods */
+void btms_input(struct bthidev *, uint8_t *, int);
+
+
+int
+btms_match(struct device *parent, struct cfdata *match, void *aux)
+{
+ struct bthidev_attach_args *ba = aux;
+
+ if (hid_is_collection(ba->ba_desc, ba->ba_dlen, ba->ba_id,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
+ return 1;
+
+ return 0;
+}
+
+void
+btms_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+ struct bthidev_attach_args *ba = aux;
+ struct wsmousedev_attach_args wsma;
+ struct hid_location *zloc;
+ uint32_t flags;
+ int i, hl;
+
+ ba->ba_input = btms_input;
+
+ /* control the horizontal */
+ hl = hid_locate(ba->ba_desc, ba->ba_dlen,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), ba->ba_id, hid_input,
+ &sc->sc_loc_x, &flags);
+
+ if (hl == 0 || NOTMOUSE(flags)) {
+ printf("\n%s: X report 0x%04x not supported\n",
+ sc->sc_hidev.sc_dev.dv_xname, flags);
+
+ return;
+ }
+
+ /* control the vertical */
+ hl = hid_locate(ba->ba_desc, ba->ba_dlen,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), ba->ba_id, hid_input,
+ &sc->sc_loc_y, &flags);
+
+ if (hl == 0 || NOTMOUSE(flags)) {
+ printf("\n%s: Y report 0x%04x not supported\n",
+ sc->sc_hidev.sc_dev.dv_xname, flags);
+
+ return;
+ }
+
+ /* Try the wheel first as the Z activator since it's tradition. */
+ hl = hid_locate(ba->ba_desc, ba->ba_dlen,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), ba->ba_id, hid_input,
+ &sc->sc_loc_z, &flags);
+
+ zloc = &sc->sc_loc_z;
+ if (hl) {
+ if (NOTMOUSE(flags)) {
+ printf("\n%s: Wheel report 0x%04x not supported\n",
+ sc->sc_hidev.sc_dev.dv_xname, flags);
+
+ /* ignore Bad Z coord */
+ sc->sc_loc_z.size = 0;
+ } else {
+ sc->sc_flags |= BTMS_HASZ;
+ /* Wheels need the Z axis reversed. */
+ sc->sc_flags ^= BTMS_REVZ;
+ /* Put Z on the W coordinate */
+ zloc = &sc->sc_loc_w;
+ }
+ }
+
+ hl = hid_locate(ba->ba_desc, ba->ba_dlen,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), ba->ba_id, hid_input,
+ zloc, &flags);
+
+ /*
+ * The horizontal component of the scrollball can also be given by
+ * Application Control Pan in the Consumer page, so if we didnt see
+ * any Z then check that.
+ */
+ if (!hl) {
+ hl = hid_locate(ba->ba_desc, ba->ba_dlen,
+ HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), ba->ba_id, hid_input,
+ zloc, &flags);
+ }
+
+ if (hl) {
+ if (NOTMOUSE(flags))
+ zloc->size = 0; /* ignore Z */
+ else {
+ if (sc->sc_flags & BTMS_HASZ)
+ sc->sc_flags |= BTMS_HASW;
+ else
+ sc->sc_flags |= BTMS_HASZ;
+ }
+ }
+
+ for (i = 1 ; i <= MAX_BUTTONS ; i++) {
+ hl = hid_locate(ba->ba_desc, ba->ba_dlen,
+ HID_USAGE2(HUP_BUTTON, i), ba->ba_id, hid_input,
+ &sc->sc_loc_button[i - 1], NULL);
+
+ if (hl == 0)
+ break;
+ }
+ sc->sc_num_buttons = i - 1;
+
+ printf(": %d button%s%s%s%s.\n", sc->sc_num_buttons,
+ sc->sc_num_buttons == 1 ? "" : "s",
+ sc->sc_flags & BTMS_HASW ? ", W" : "",
+ sc->sc_flags & BTMS_HASZ ? " and Z dir" : "",
+ sc->sc_flags & BTMS_HASW ? "s" : "");
+
+ wsma.accessops = &btms_accessops;
+ wsma.accesscookie = sc;
+
+ sc->sc_wsmouse = config_found((struct device *)sc,
+ &wsma, wsmousedevprint);
+}
+
+int
+btms_detach(struct device *self, int flags)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+ int err = 0;
+
+ if (sc->sc_wsmouse != NULL) {
+ err = config_detach(sc->sc_wsmouse, flags);
+ sc->sc_wsmouse = NULL;
+ }
+
+ return err;
+}
+
+int
+btms_enable(void *self)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+
+ if (sc->sc_enabled)
+ return EBUSY;
+
+ sc->sc_enabled = 1;
+ return 0;
+}
+
+int
+btms_ioctl(void *self, unsigned long cmd, void *data, int flag, struct lwp *l)
+{
+ /* struct btms_softc *sc = (struct btms_softc *)self; */
+
+ switch (cmd) {
+ case WSMOUSEIO_GTYPE:
+ *(uint *)data = WSMOUSE_TYPE_BLUETOOTH;
+ break;
+
+ default:
+ return EPASSTHROUGH;
+ }
+
+ return 0;
+}
+
+void
+btms_disable(void *self)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+
+ sc->sc_enabled = 0;
+}
+
+void
+btms_input(struct bthidev *self, uint8_t *data, int len)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+ int dx, dy, dz, dw;
+ uint32_t buttons;
+ int i, s;
+
+ if (sc->sc_wsmouse == NULL || sc->sc_enabled == 0)
+ return;
+
+ dx = hid_get_data(data, &sc->sc_loc_x);
+ dy = -hid_get_data(data, &sc->sc_loc_y);
+ dz = hid_get_data(data, &sc->sc_loc_z);
+ dw = hid_get_data(data, &sc->sc_loc_w);
+
+ if (sc->sc_flags & BTMS_REVZ)
+ dz = -dz;
+
+ buttons = 0;
+ for (i = 0 ; i < sc->sc_num_buttons ; i++)
+ if (hid_get_data(data, &sc->sc_loc_button[i]))
+ buttons |= BUTTON(i);
+
+ if (dx != 0 || dy != 0 || dz != 0 || dw != 0 ||
+ buttons != sc->sc_buttons) {
+ sc->sc_buttons = buttons;
+
+ s = spltty();
+ wsmouse_input(sc->sc_wsmouse, buttons, dx, dy, dz, dw,
+ WSMOUSE_INPUT_DELTA);
+ splx(s);
+ }
+}