git: 5aa839c9e2c3 - main - bcm5974: wsp(4) driver version with HID attachment.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 02 Mar 2022 23:36:39 UTC
The branch main has been updated by wulf:
URL: https://cgit.FreeBSD.org/src/commit/?id=5aa839c9e2c373275091b8bf529c1311d0b84d76
commit 5aa839c9e2c373275091b8bf529c1311d0b84d76
Author: Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2022-03-02 23:35:23 +0000
Commit: Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2022-03-02 23:35:23 +0000
bcm5974: wsp(4) driver version with HID attachment.
MFC after: 2 month
Tested by: Greg V (Type 4 touchpads)
---
share/man/man4/Makefile | 1 +
share/man/man4/bcm5974.4 | 85 +++++
sys/conf/files | 1 +
sys/dev/hid/bcm5974.c | 794 +++++++++++++++++++++++++++++++++++++++
sys/modules/hid/Makefile | 1 +
sys/modules/hid/bcm5974/Makefile | 10 +
6 files changed, 892 insertions(+)
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index c79fc1de0c76..b24e393a17f9 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -70,6 +70,7 @@ MAN= aac.4 \
axge.4 \
axp.4 \
bce.4 \
+ bcm5974.4 \
bcma.4 \
bfe.4 \
bge.4 \
diff --git a/share/man/man4/bcm5974.4 b/share/man/man4/bcm5974.4
new file mode 100644
index 000000000000..b5f875c8d934
--- /dev/null
+++ b/share/man/man4/bcm5974.4
@@ -0,0 +1,85 @@
+.\" Copyright (c) 2022 Vladimir Kondratyev <wulf@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 27, 2022
+.Dt BCM5974 4
+.Os
+.Sh NAME
+.Nm bcm5974
+.Nd Wellspring touchpad driver
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following lines into
+your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device bcm5974"
+.Cd "device hidbus"
+.Cd "device hid"
+.Cd "device usbhid"
+.Cd "device usb"
+.Cd "device evdev"
+
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+bcm5974_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the Wellspring touchpads found in many Apple
+laptops.
+.Pp
+To get multi-touch device working in
+.Xr X 7 ,
+install
+.Pa ports/x11-drivers/xf86-input-libinput .
+.Sh FILES
+.Nm
+creates a pseudo-device file,
+.Pa /dev/input/eventX
+which presents the multi-touch device as an input event device.
+.Sh SEE ALSO
+.Xr hid 4 ,
+.Xr loader.conf 5 ,
+.Xr xorg.conf 5 Pq Pa ports/x11/xorg ,
+.Xr libinput 4 Pq Pa ports/x11-drivers/xf86-input-libinput .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org .
+It is based on
+.Xr wsp 4
+driver written by
+.An Huang Wen Hui Aq Mt huanghwh@gmail.com .
+.Sh BUGS
+.Nm
+cannot act like
+.Xr sysmouse 4
diff --git a/sys/conf/files b/sys/conf/files
index 405d9d5f3bcc..dfd6476be7a1 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1791,6 +1791,7 @@ dev/gpio/gpio_if.m optional gpio
dev/gpio/gpiobus_if.m optional gpio
dev/gpio/gpiopps.c optional gpiopps fdt
dev/gpio/ofw_gpiobus.c optional fdt gpio
+dev/hid/bcm5974.c optional bcm5974
dev/hid/hconf.c optional hconf
dev/hid/hcons.c optional hcons
dev/hid/hgame.c optional hgame
diff --git a/sys/dev/hid/bcm5974.c b/sys/dev/hid/bcm5974.c
new file mode 100644
index 000000000000..d92c1c5c43b5
--- /dev/null
+++ b/sys/dev/hid/bcm5974.c
@@ -0,0 +1,794 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Huang Wen Hui
+ * Copyright (c) 2021 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#define HID_DEBUG_VAR bcm5974_debug
+#include <dev/hid/hid.h>
+#include <dev/hid/hidbus.h>
+#include <dev/hid/hidquirk.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usb_ioctl.h>
+
+#include "usbdevs.h"
+
+#define BCM5974_BUFFER_MAX (248 * 4) /* 4 Type4 SPI frames */
+#define BCM5974_TLC_PAGE HUP_GENERIC_DESKTOP
+#define BCM5974_TLC_USAGE HUG_MOUSE
+
+/* magic to switch device from HID (default) mode into raw */
+/* Type1 & Type2 trackpads */
+#define BCM5974_USB_IFACE_INDEX 0
+#define BCM5974_USB_REPORT_LEN 8
+#define BCM5974_USB_REPORT_ID 0
+#define BCM5974_USB_MODE_RAW 0x01
+#define BCM5974_USB_MODE_HID 0x08
+/* Type4 trackpads */
+#define BCM5974_HID_REPORT_LEN 2
+#define BCM5974_HID_REPORT_ID 2
+#define BCM5974_HID_MODE_RAW 0x01
+#define BCM5974_HID_MODE_HID 0x00
+
+/* Tunables */
+static SYSCTL_NODE(_hw_hid, OID_AUTO, bcm5974, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+ "HID wellspring touchpad");
+
+#ifdef HID_DEBUG
+enum wsp_log_level {
+ BCM5974_LLEVEL_DISABLED = 0,
+ BCM5974_LLEVEL_ERROR,
+ BCM5974_LLEVEL_DEBUG, /* for troubleshooting */
+ BCM5974_LLEVEL_INFO, /* for diagnostics */
+};
+/* the default is to only log errors */
+static int bcm5974_debug = BCM5974_LLEVEL_ERROR;
+
+SYSCTL_INT(_hw_hid_bcm5974, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &bcm5974_debug, BCM5974_LLEVEL_ERROR, "BCM5974 debug level");
+#endif /* HID_DEBUG */
+
+/*
+ * Some tables, structures, definitions and constant values for the
+ * touchpad protocol has been copied from Linux's
+ * "drivers/input/mouse/bcm5974.c" which has the following copyright
+ * holders under GPLv2. All device specific code in this driver has
+ * been written from scratch. The decoding algorithm is based on
+ * output from FreeBSD's usbdump.
+ *
+ * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
+ * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com)
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
+ */
+
+/* trackpad header types */
+enum tp_type {
+ TYPE1, /* plain trackpad */
+ TYPE2, /* button integrated in trackpad */
+ TYPE3, /* additional header fields since June 2013 */
+ TYPE4, /* additional header field for pressure data */
+ TYPE_CNT
+};
+
+/* list of device capability bits */
+#define HAS_INTEGRATED_BUTTON 1
+
+struct tp_type_params {
+ uint8_t caps; /* device capability bitmask */
+ uint8_t button; /* offset to button data */
+ uint8_t offset; /* offset to trackpad finger data */
+ uint8_t delta; /* offset from header to finger struct */
+} const static tp[TYPE_CNT] = {
+ [TYPE1] = {
+ .caps = 0,
+ .button = 0,
+ .offset = 13 * 2,
+ .delta = 0,
+ },
+ [TYPE2] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .button = 15,
+ .offset = 15 * 2,
+ .delta = 0,
+ },
+ [TYPE3] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .button = 23,
+ .offset = 19 * 2,
+ .delta = 0,
+ },
+ [TYPE4] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .button = 31,
+ .offset = 23 * 2,
+ .delta = 2,
+ },
+};
+
+/* trackpad finger structure - little endian */
+struct tp_finger {
+ int16_t origin; /* zero when switching track finger */
+ int16_t abs_x; /* absolute x coodinate */
+ int16_t abs_y; /* absolute y coodinate */
+ int16_t rel_x; /* relative x coodinate */
+ int16_t rel_y; /* relative y coodinate */
+ int16_t tool_major; /* tool area, major axis */
+ int16_t tool_minor; /* tool area, minor axis */
+ int16_t orientation; /* 16384 when point, else 15 bit angle */
+ int16_t touch_major; /* touch area, major axis */
+ int16_t touch_minor; /* touch area, minor axis */
+ int16_t unused[2]; /* zeros */
+ int16_t pressure; /* pressure on forcetouch touchpad */
+ int16_t multi; /* one finger: varies, more fingers:
+ * constant */
+} __packed;
+
+/* trackpad finger data size, empirically at least ten fingers */
+#define MAX_FINGERS MAX_MT_SLOTS
+
+#define MAX_FINGER_ORIENTATION 16384
+
+enum {
+ BCM5974_FLAG_WELLSPRING1,
+ BCM5974_FLAG_WELLSPRING2,
+ BCM5974_FLAG_WELLSPRING3,
+ BCM5974_FLAG_WELLSPRING4,
+ BCM5974_FLAG_WELLSPRING4A,
+ BCM5974_FLAG_WELLSPRING5,
+ BCM5974_FLAG_WELLSPRING6A,
+ BCM5974_FLAG_WELLSPRING6,
+ BCM5974_FLAG_WELLSPRING5A,
+ BCM5974_FLAG_WELLSPRING7,
+ BCM5974_FLAG_WELLSPRING7A,
+ BCM5974_FLAG_WELLSPRING8,
+ BCM5974_FLAG_WELLSPRING9,
+ BCM5974_FLAG_MAX,
+};
+
+/* device-specific parameters */
+struct bcm5974_axis {
+ int snratio; /* signal-to-noise ratio */
+ int min; /* device minimum reading */
+ int max; /* device maximum reading */
+ int size; /* physical size, mm */
+};
+
+/* device-specific configuration */
+struct bcm5974_dev_params {
+ const struct tp_type_params* tp;
+ struct bcm5974_axis p; /* finger pressure limits */
+ struct bcm5974_axis w; /* finger width limits */
+ struct bcm5974_axis x; /* horizontal limits */
+ struct bcm5974_axis y; /* vertical limits */
+ struct bcm5974_axis o; /* orientation limits */
+};
+
+/* logical signal quality */
+#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
+#define SN_WIDTH 25 /* width signal-to-noise ratio */
+#define SN_COORD 250 /* coordinate signal-to-noise ratio */
+#define SN_ORIENT 10 /* orientation signal-to-noise ratio */
+
+static const struct bcm5974_dev_params bcm5974_dev_params[BCM5974_FLAG_MAX] = {
+ [BCM5974_FLAG_WELLSPRING1] = {
+ .tp = tp + TYPE1,
+ .p = { SN_PRESSURE, 0, 256, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4824, 5342, 105 },
+ .y = { SN_COORD, -172, 5820, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING2] = {
+ .tp = tp + TYPE1,
+ .p = { SN_PRESSURE, 0, 256, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4824, 4824, 105 },
+ .y = { SN_COORD, -172, 4290, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING3] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4460, 5166, 105 },
+ .y = { SN_COORD, -75, 6700, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING4] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4620, 5140, 105 },
+ .y = { SN_COORD, -150, 6600, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING4A] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4616, 5112, 105 },
+ .y = { SN_COORD, -142, 5234, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING5] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4415, 5050, 105 },
+ .y = { SN_COORD, -55, 6680, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING6] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4620, 5140, 105 },
+ .y = { SN_COORD, -150, 6600, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING5A] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4750, 5280, 105 },
+ .y = { SN_COORD, -150, 6730, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING6A] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4620, 5140, 105 },
+ .y = { SN_COORD, -150, 6600, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING7] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4750, 5280, 105 },
+ .y = { SN_COORD, -150, 6730, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING7A] = {
+ .tp = tp + TYPE2,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4750, 5280, 105 },
+ .y = { SN_COORD, -150, 6730, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING8] = {
+ .tp = tp + TYPE3,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4620, 5140, 105 },
+ .y = { SN_COORD, -150, 6600, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+ [BCM5974_FLAG_WELLSPRING9] = {
+ .tp = tp + TYPE4,
+ .p = { SN_PRESSURE, 0, 300, 0 },
+ .w = { SN_WIDTH, 0, 2048, 0 },
+ .x = { SN_COORD, -4828, 5345, 105 },
+ .y = { SN_COORD, -203, 6803, 75 },
+ .o = { SN_ORIENT,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+ },
+};
+
+#define BCM5974_DEV(v,p,i) { \
+ HID_BVPI(BUS_USB, USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i), \
+ HID_TLC(BCM5974_TLC_PAGE, BCM5974_TLC_USAGE), \
+}
+
+static const struct hid_device_id bcm5974_devs[] = {
+ /* MacbookAir1.1 */
+ BCM5974_DEV(APPLE, WELLSPRING_ANSI, BCM5974_FLAG_WELLSPRING1),
+ BCM5974_DEV(APPLE, WELLSPRING_ISO, BCM5974_FLAG_WELLSPRING1),
+ BCM5974_DEV(APPLE, WELLSPRING_JIS, BCM5974_FLAG_WELLSPRING1),
+
+ /* MacbookProPenryn, aka wellspring2 */
+ BCM5974_DEV(APPLE, WELLSPRING2_ANSI, BCM5974_FLAG_WELLSPRING2),
+ BCM5974_DEV(APPLE, WELLSPRING2_ISO, BCM5974_FLAG_WELLSPRING2),
+ BCM5974_DEV(APPLE, WELLSPRING2_JIS, BCM5974_FLAG_WELLSPRING2),
+
+ /* Macbook5,1 (unibody), aka wellspring3 */
+ BCM5974_DEV(APPLE, WELLSPRING3_ANSI, BCM5974_FLAG_WELLSPRING3),
+ BCM5974_DEV(APPLE, WELLSPRING3_ISO, BCM5974_FLAG_WELLSPRING3),
+ BCM5974_DEV(APPLE, WELLSPRING3_JIS, BCM5974_FLAG_WELLSPRING3),
+
+ /* MacbookAir3,2 (unibody), aka wellspring4 */
+ BCM5974_DEV(APPLE, WELLSPRING4_ANSI, BCM5974_FLAG_WELLSPRING4),
+ BCM5974_DEV(APPLE, WELLSPRING4_ISO, BCM5974_FLAG_WELLSPRING4),
+ BCM5974_DEV(APPLE, WELLSPRING4_JIS, BCM5974_FLAG_WELLSPRING4),
+
+ /* MacbookAir3,1 (unibody), aka wellspring4 */
+ BCM5974_DEV(APPLE, WELLSPRING4A_ANSI, BCM5974_FLAG_WELLSPRING4A),
+ BCM5974_DEV(APPLE, WELLSPRING4A_ISO, BCM5974_FLAG_WELLSPRING4A),
+ BCM5974_DEV(APPLE, WELLSPRING4A_JIS, BCM5974_FLAG_WELLSPRING4A),
+
+ /* Macbook8 (unibody, March 2011) */
+ BCM5974_DEV(APPLE, WELLSPRING5_ANSI, BCM5974_FLAG_WELLSPRING5),
+ BCM5974_DEV(APPLE, WELLSPRING5_ISO, BCM5974_FLAG_WELLSPRING5),
+ BCM5974_DEV(APPLE, WELLSPRING5_JIS, BCM5974_FLAG_WELLSPRING5),
+
+ /* MacbookAir4,1 (unibody, July 2011) */
+ BCM5974_DEV(APPLE, WELLSPRING6A_ANSI, BCM5974_FLAG_WELLSPRING6A),
+ BCM5974_DEV(APPLE, WELLSPRING6A_ISO, BCM5974_FLAG_WELLSPRING6A),
+ BCM5974_DEV(APPLE, WELLSPRING6A_JIS, BCM5974_FLAG_WELLSPRING6A),
+
+ /* MacbookAir4,2 (unibody, July 2011) */
+ BCM5974_DEV(APPLE, WELLSPRING6_ANSI, BCM5974_FLAG_WELLSPRING6),
+ BCM5974_DEV(APPLE, WELLSPRING6_ISO, BCM5974_FLAG_WELLSPRING6),
+ BCM5974_DEV(APPLE, WELLSPRING6_JIS, BCM5974_FLAG_WELLSPRING6),
+
+ /* Macbook8,2 (unibody) */
+ BCM5974_DEV(APPLE, WELLSPRING5A_ANSI, BCM5974_FLAG_WELLSPRING5A),
+ BCM5974_DEV(APPLE, WELLSPRING5A_ISO, BCM5974_FLAG_WELLSPRING5A),
+ BCM5974_DEV(APPLE, WELLSPRING5A_JIS, BCM5974_FLAG_WELLSPRING5A),
+
+ /* MacbookPro10,1 (unibody, June 2012) */
+ /* MacbookPro11,1-3 (unibody, June 2013) */
+ BCM5974_DEV(APPLE, WELLSPRING7_ANSI, BCM5974_FLAG_WELLSPRING7),
+ BCM5974_DEV(APPLE, WELLSPRING7_ISO, BCM5974_FLAG_WELLSPRING7),
+ BCM5974_DEV(APPLE, WELLSPRING7_JIS, BCM5974_FLAG_WELLSPRING7),
+
+ /* MacbookPro10,2 (unibody, October 2012) */
+ BCM5974_DEV(APPLE, WELLSPRING7A_ANSI, BCM5974_FLAG_WELLSPRING7A),
+ BCM5974_DEV(APPLE, WELLSPRING7A_ISO, BCM5974_FLAG_WELLSPRING7A),
+ BCM5974_DEV(APPLE, WELLSPRING7A_JIS, BCM5974_FLAG_WELLSPRING7A),
+
+ /* MacbookAir6,2 (unibody, June 2013) */
+ BCM5974_DEV(APPLE, WELLSPRING8_ANSI, BCM5974_FLAG_WELLSPRING8),
+ BCM5974_DEV(APPLE, WELLSPRING8_ISO, BCM5974_FLAG_WELLSPRING8),
+ BCM5974_DEV(APPLE, WELLSPRING8_JIS, BCM5974_FLAG_WELLSPRING8),
+
+ /* MacbookPro12,1 MacbookPro11,4 */
+ BCM5974_DEV(APPLE, WELLSPRING9_ANSI, BCM5974_FLAG_WELLSPRING9),
+ BCM5974_DEV(APPLE, WELLSPRING9_ISO, BCM5974_FLAG_WELLSPRING9),
+ BCM5974_DEV(APPLE, WELLSPRING9_JIS, BCM5974_FLAG_WELLSPRING9),
+};
+
+struct bcm5974_softc {
+ device_t sc_dev;
+ struct evdev_dev *sc_evdev;
+ /* device configuration */
+ const struct bcm5974_dev_params *sc_params;
+};
+
+static const uint8_t bcm5974_rdesc[] = {
+ 0x05, BCM5974_TLC_PAGE, /* Usage Page (BCM5974_TLC_PAGE) */
+ 0x09, BCM5974_TLC_USAGE,/* Usage (BCM5974_TLC_USAGE) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
+ 0x09, 0x01, /* Usage (0x01) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x96, /* Report Count (BCM5974_BUFFER_MAX) */
+ BCM5974_BUFFER_MAX & 0xFF,
+ BCM5974_BUFFER_MAX >> 8 & 0xFF,
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0xC0, /* End Collection */
+};
+
+/*
+ * function prototypes
+ */
+static evdev_open_t bcm5974_ev_open;
+static evdev_close_t bcm5974_ev_close;
+static const struct evdev_methods bcm5974_evdev_methods = {
+ .ev_open = &bcm5974_ev_open,
+ .ev_close = &bcm5974_ev_close,
+};
+static hid_intr_t bcm5974_intr;
+
+/* Device methods. */
+static device_identify_t bcm5974_identify;
+static device_probe_t bcm5974_probe;
+static device_attach_t bcm5974_attach;
+static device_detach_t bcm5974_detach;
+
+/*
+ * Type1 and Type2 touchpads use keyboard USB interface to switch from HID to
+ * RAW mode. Although it is possible to extend hkbd driver to support such a
+ * mode change requests, it's not wanted due to cross device tree dependencies.
+ * So, find lowest common denominator (struct usb_device of grandparent usbhid
+ * driver) of touchpad and keyboard drivers and issue direct USB requests.
+ */
+static int
+bcm5974_set_device_mode_usb(struct bcm5974_softc *sc, bool on)
+{
+ uint8_t mode_bytes[BCM5974_USB_REPORT_LEN];
+ struct usb_ctl_request ucr;
+ int err;
+
+ ucr.ucr_request.bmRequestType = UT_READ_CLASS_INTERFACE;
+ ucr.ucr_request.bRequest = UR_GET_REPORT;
+ USETW2(ucr.ucr_request.wValue,
+ UHID_FEATURE_REPORT, BCM5974_USB_REPORT_ID);
+ ucr.ucr_request.wIndex[0] = BCM5974_USB_IFACE_INDEX;
+ ucr.ucr_request.wIndex[1] = 0;
+ USETW(ucr.ucr_request.wLength, BCM5974_USB_REPORT_LEN);
+ ucr.ucr_data = mode_bytes;
+
+ err = hid_ioctl(sc->sc_dev, USB_REQUEST, (uintptr_t)&ucr);
+ if (err != 0) {
+ DPRINTF("Failed to read device mode (%d)\n", err);
+ return (EIO);
+ }
+#if 0
+ /*
+ * XXX Need to wait at least 250ms for hardware to get
+ * ready. The device mode handling appears to be handled
+ * asynchronously and we should not issue these commands too
+ * quickly.
+ */
+ pause("WHW", hz / 4);
+#endif
+ mode_bytes[0] = on ? BCM5974_USB_MODE_RAW : BCM5974_USB_MODE_HID;
+ ucr.ucr_request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ ucr.ucr_request.bRequest = UR_SET_REPORT;
+
+ err = hid_ioctl(sc->sc_dev, USB_REQUEST, (uintptr_t)&ucr);
+ if (err != 0) {
+ DPRINTF("Failed to write device mode (%d)\n", err);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+static int
+bcm5974_set_device_mode_hid(struct bcm5974_softc *sc, bool on)
+{
+ uint8_t mode_bytes[BCM5974_HID_REPORT_LEN] = {
+ BCM5974_HID_REPORT_ID,
+ on ? BCM5974_HID_MODE_RAW : BCM5974_HID_MODE_HID,
+ };
+#if 0
+ int err;
+
+ err = hid_get_report(sc->sc_dev, mode_bytes, BCM5974_HID_REPORT_LEN,
+ NULL, HID_FEATURE_REPORT, BCM5974_HID_REPORT_ID);
+ if (err != 0) {
+ DPRINTF("Failed to read device mode (%d)\n", err);
+ return (err);
+ }
+ /*
+ * XXX Need to wait at least 250ms for hardware to get
+ * ready. The device mode handling appears to be handled
+ * asynchronously and we should not issue these commands too
+ * quickly.
+ */
+ pause("WHW", hz / 4);
+ mode_bytes[1] = on ? BCM5974_HID_MODE_RAW : BCM5974_HID_MODE_HID;
+#endif
+ return (hid_set_report(sc->sc_dev, mode_bytes, BCM5974_HID_REPORT_LEN,
+ HID_FEATURE_REPORT, BCM5974_HID_REPORT_ID));
+}
+
+static int
+bcm5974_set_device_mode(struct bcm5974_softc *sc, bool on)
+{
+ int err = 0;
+
+ switch (sc->sc_params->tp - tp) {
+ case TYPE1:
+ case TYPE2:
+ err = bcm5974_set_device_mode_usb(sc, on);
+ break;
+ case TYPE3: /* Type 3 does not require a mode switch */
+ break;
+ case TYPE4:
+ err = bcm5974_set_device_mode_hid(sc, on);
+ break;
+ default:
+ KASSERT(0 == 1, ("Unknown trackpad type"));
+ }
+
+ return (err);
+}
+
+static void
+bcm5974_identify(driver_t *driver, device_t parent)
+{
+ void *d_ptr;
+ hid_size_t d_len;
+
+ /*
+ * The bcm5974 touchpad has no stable RAW mode TLC in its report
+ * descriptor. So replace existing HID mode mouse TLC with dummy one
+ * to set proper transport layer buffer sizes, make driver probe
+ * simpler and prevent unwanted hms driver attachment.
+ */
+ if (HIDBUS_LOOKUP_ID(parent, bcm5974_devs) != NULL &&
+ hid_get_report_descr(parent, &d_ptr, &d_len) == 0 &&
+ hid_is_mouse(d_ptr, d_len))
+ hid_set_report_descr(parent, bcm5974_rdesc,
+ sizeof(bcm5974_rdesc));
+}
+
+static int
+bcm5974_probe(device_t dev)
+{
+ int err;
+
+ err = HIDBUS_LOOKUP_DRIVER_INFO(dev, bcm5974_devs);
+ if (err != 0)
+ return (err);
+
+ hidbus_set_desc(dev, "Touchpad");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+bcm5974_attach(device_t dev)
+{
+ struct bcm5974_softc *sc = device_get_softc(dev);
+ const struct hid_device_info *hw = hid_get_device_info(dev);
+ int err;
+
+ DPRINTFN(BCM5974_LLEVEL_INFO, "sc=%p\n", sc);
+
+ sc->sc_dev = dev;
+
+ /* get device specific configuration */
+ sc->sc_params = bcm5974_dev_params + hidbus_get_driver_info(dev);
+
+ sc->sc_evdev = evdev_alloc();
+ evdev_set_name(sc->sc_evdev, device_get_desc(dev));
+ evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
+ evdev_set_id(sc->sc_evdev, hw->idBus, hw->idVendor, hw->idProduct,
+ hw->idVersion);
+ evdev_set_serial(sc->sc_evdev, hw->serial);
+ evdev_set_methods(sc->sc_evdev, sc, &bcm5974_evdev_methods);
+ evdev_support_prop(sc->sc_evdev, INPUT_PROP_POINTER);
+ evdev_support_event(sc->sc_evdev, EV_SYN);
+ evdev_support_event(sc->sc_evdev, EV_ABS);
+ evdev_support_event(sc->sc_evdev, EV_KEY);
+ evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */
+
+#define BCM5974_ABS(evdev, code, param) \
+ evdev_support_abs((evdev), (code), (param).min, (param).max, \
+ ((param).max - (param).min) / (param).snratio, 0, \
+ (param).size != 0 ? ((param).max - (param).min) / (param).size : 0);
+
+ /* finger position */
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_X, sc->sc_params->x);
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_Y, sc->sc_params->y);
+ /* finger pressure */
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p);
+ /* finger touch area */
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w);
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w);
+ /* finger approach area */
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MAJOR, sc->sc_params->w);
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MINOR, sc->sc_params->w);
+ /* finger orientation */
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_ORIENTATION, sc->sc_params->o);
+ /* button properties */
+ evdev_support_key(sc->sc_evdev, BTN_LEFT);
+ if ((sc->sc_params->tp->caps & HAS_INTEGRATED_BUTTON) != 0)
+ evdev_support_prop(sc->sc_evdev, INPUT_PROP_BUTTONPAD);
+ /* Enable automatic touch assignment for type B MT protocol */
+ evdev_support_abs(sc->sc_evdev, ABS_MT_SLOT,
+ 0, MAX_FINGERS - 1, 0, 0, 0);
+ evdev_support_abs(sc->sc_evdev, ABS_MT_TRACKING_ID,
+ -1, MAX_FINGERS - 1, 0, 0, 0);
+ evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_TRACK);
+ evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_AUTOREL);
+ /* Synaptics compatibility events */
+ evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_STCOMPAT);
+
+ err = evdev_register(sc->sc_evdev);
+ if (err)
+ goto detach;
+
+ hidbus_set_intr(dev, bcm5974_intr, sc);
+
+ return (0);
+
+detach:
+ bcm5974_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+bcm5974_detach(device_t dev)
+{
+ struct bcm5974_softc *sc = device_get_softc(dev);
+
+ evdev_free(sc->sc_evdev);
+
+ return (0);
+}
+
+static void
+bcm5974_intr(void *context, void *data, hid_size_t len)
+{
+ struct bcm5974_softc *sc = context;
+ const struct bcm5974_dev_params *params = sc->sc_params;
+ union evdev_mt_slot slot_data;
+ struct tp_finger *f;
+ int ntouch; /* the finger number in touch */
+ int ibt; /* button status */
+ int i;
+ int slot;
+ uint8_t fsize = sizeof(struct tp_finger) + params->tp->delta;
+
+ if ((len < params->tp->offset + fsize) ||
+ ((len - params->tp->offset) % fsize) != 0) {
+ DPRINTFN(BCM5974_LLEVEL_INFO, "Invalid length: %d, %x, %x\n",
+ len, sc->tp_data[0], sc->tp_data[1]);
+ return;
+ }
+
+ ibt = ((uint8_t *)data)[params->tp->button];
+ ntouch = (len - params->tp->offset) / fsize;
+
+ for (i = 0, slot = 0; i != ntouch; i++) {
+ f = (struct tp_finger *)(((uint8_t *)data) +
+ params->tp->offset + params->tp->delta + i * fsize);
+ DPRINTFN(BCM5974_LLEVEL_INFO,
+ "[%d]ibt=%d, taps=%d, o=%4d, ax=%5d, ay=%5d, "
+ "rx=%5d, ry=%5d, tlmaj=%4d, tlmin=%4d, ot=%4x, "
+ "tchmaj=%4d, tchmin=%4d, presure=%4d, m=%4x\n",
+ i, ibt, ntouch, le16toh(f->origin), le16toh(f->abs_x),
+ le16toh(f->abs_y), le16toh(f->rel_x), le16toh(f->rel_y),
+ le16toh(f->tool_major), le16toh(f->tool_minor),
+ le16toh(f->orientation), le16toh(f->touch_major),
+ le16toh(f->touch_minor), le16toh(f->pressure),
+ le16toh(f->multi));
+
+ if (f->touch_major == 0)
+ continue;
+ slot_data = (union evdev_mt_slot) {
+ .id = slot,
+ .x = le16toh(f->abs_x),
+ .y = params->y.min + params->y.max - le16toh(f->abs_y),
+ .p = le16toh(f->pressure),
+ .maj = le16toh(f->touch_major) << 1,
+ .min = le16toh(f->touch_minor) << 1,
+ .w_maj = le16toh(f->tool_major) << 1,
+ .w_min = le16toh(f->tool_minor) << 1,
+ .ori = params->o.max - le16toh(f->orientation),
+ };
+ evdev_mt_push_slot(sc->sc_evdev, slot, &slot_data);
+ slot++;
+ }
+
+ evdev_push_key(sc->sc_evdev, BTN_LEFT, ibt);
+ evdev_sync(sc->sc_evdev);
+}
+
+static int
+bcm5974_ev_open(struct evdev_dev *evdev)
+{
+ struct bcm5974_softc *sc = evdev_get_softc(evdev);
+ int err;
+
+ /*
+ * By default the touchpad behaves like a HID device, sending
+ * packets with reportID = 8. Such reports contain only
+ * limited information. They encode movement deltas and button
+ * events, but do not include data from the pressure
+ * sensors. The device input mode can be switched from HID
+ * reports to raw sensor data using vendor-specific USB
+ * control commands:
+ */
+ err = bcm5974_set_device_mode(sc, true);
+ if (err != 0) {
+ DPRINTF("failed to set mode to RAW MODE (%d)\n", err);
+ return (err);
+ }
+
+ return (hidbus_intr_start(sc->sc_dev));
+}
+
+static int
+bcm5974_ev_close(struct evdev_dev *evdev)
+{
+ struct bcm5974_softc *sc = evdev_get_softc(evdev);
+ int err;
+
+ err = hidbus_intr_stop(sc->sc_dev);
+ if (err != 0)
+ return (err);
+
+ /*
+ * During re-enumeration of the device we need to force the
+ * device back into HID mode before switching it to RAW
+ * mode. Else the device does not work like expected.
+ */
+ err = bcm5974_set_device_mode(sc, false);
+ if (err != 0)
+ DPRINTF("Failed to set mode to HID MODE (%d)\n", err);
+
+ return (err);
+}
+
+static device_method_t bcm5974_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, bcm5974_identify),
+ DEVMETHOD(device_probe, bcm5974_probe),
+ DEVMETHOD(device_attach, bcm5974_attach),
+ DEVMETHOD(device_detach, bcm5974_detach),
+ DEVMETHOD_END
+};
+
+static driver_t bcm5974_driver = {
+ .name = "bcm5974",
+ .methods = bcm5974_methods,
+ .size = sizeof(struct bcm5974_softc)
+};
+
+static devclass_t bcm5974_devclass;
+
+DRIVER_MODULE(bcm5974, hidbus, bcm5974_driver, bcm5974_devclass, NULL, 0);
+MODULE_DEPEND(bcm5974, hidbus, 1, 1, 1);
+MODULE_DEPEND(bcm5974, hid, 1, 1, 1);
+MODULE_DEPEND(bcm5974, evdev, 1, 1, 1);
+MODULE_VERSION(bcm5974, 1);
+HID_PNP_INFO(bcm5974_devs);
diff --git a/sys/modules/hid/Makefile b/sys/modules/hid/Makefile
index 7d5515480bc0..21ec488095a5 100644
--- a/sys/modules/hid/Makefile
+++ b/sys/modules/hid/Makefile
@@ -8,6 +8,7 @@ SUBDIR = \
hidraw
SUBDIR += \
+ bcm5974 \
hconf \
hcons \
hgame \
diff --git a/sys/modules/hid/bcm5974/Makefile b/sys/modules/hid/bcm5974/Makefile
new file mode 100644
index 000000000000..13a17ec200df
--- /dev/null
+++ b/sys/modules/hid/bcm5974/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/hid
+
+KMOD= bcm5974
+SRCS= bcm5974.c
+SRCS+= opt_hid.h
+SRCS+= bus_if.h device_if.h usbdevs.h
+
+.include <bsd.kmod.mk>