Date: Sat, 24 Dec 2022 08:36:58 GMT From: Vladimir Kondratyev <wulf@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Subject: git: 4430a5d185ed - stable/13 - bcm5974: wsp(4) driver version with HID attachment. Message-ID: <202212240836.2BO8aw6t083399@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by wulf: URL: https://cgit.FreeBSD.org/src/commit/?id=4430a5d185edcdf567a47b5976e3af1d857faade commit 4430a5d185edcdf567a47b5976e3af1d857faade Author: Vladimir Kondratyev <wulf@FreeBSD.org> AuthorDate: 2022-03-02 23:35:23 +0000 Commit: Vladimir Kondratyev <wulf@FreeBSD.org> CommitDate: 2022-12-24 08:33:26 +0000 bcm5974: wsp(4) driver version with HID attachment. (cherry picked from commit 5aa839c9e2c373275091b8bf529c1311d0b84d76) --- 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 0d0b016ac4e3..45182387cd4e 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -70,6 +70,7 @@ MAN= aac.4 \ axe.4 \ axge.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 a9bb372cec2a..5bdd43d2b4b3 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1826,6 +1826,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 68da9778a09b..72368487bc6c 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>
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202212240836.2BO8aw6t083399>