xref: /freebsd/sys/dev/hid/bcm5974.c (revision 9097284b98be57e2e2bf12942844fa3c920dd1a7)
15aa839c9SVladimir Kondratyev /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35aa839c9SVladimir Kondratyev  *
45aa839c9SVladimir Kondratyev  * Copyright (c) 2012 Huang Wen Hui
55aa839c9SVladimir Kondratyev  * Copyright (c) 2021 Vladimir Kondratyev <wulf@FreeBSD.org>
65aa839c9SVladimir Kondratyev  * All rights reserved.
75aa839c9SVladimir Kondratyev  *
85aa839c9SVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
95aa839c9SVladimir Kondratyev  * modification, are permitted provided that the following conditions
105aa839c9SVladimir Kondratyev  * are met:
115aa839c9SVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
125aa839c9SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
135aa839c9SVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
145aa839c9SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
155aa839c9SVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
165aa839c9SVladimir Kondratyev  *
175aa839c9SVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
185aa839c9SVladimir Kondratyev  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
195aa839c9SVladimir Kondratyev  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
205aa839c9SVladimir Kondratyev  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
215aa839c9SVladimir Kondratyev  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
225aa839c9SVladimir Kondratyev  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
235aa839c9SVladimir Kondratyev  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
245aa839c9SVladimir Kondratyev  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
255aa839c9SVladimir Kondratyev  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
265aa839c9SVladimir Kondratyev  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
275aa839c9SVladimir Kondratyev  * SUCH DAMAGE.
285aa839c9SVladimir Kondratyev  */
295aa839c9SVladimir Kondratyev 
304f345989SVladimir Kondratyev #include "opt_hid.h"
314f345989SVladimir Kondratyev 
325aa839c9SVladimir Kondratyev #include <sys/param.h>
335aa839c9SVladimir Kondratyev #include <sys/bus.h>
345aa839c9SVladimir Kondratyev #include <sys/endian.h>
355aa839c9SVladimir Kondratyev #include <sys/kernel.h>
365aa839c9SVladimir Kondratyev #include <sys/malloc.h>
375aa839c9SVladimir Kondratyev #include <sys/module.h>
385aa839c9SVladimir Kondratyev #include <sys/sysctl.h>
395aa839c9SVladimir Kondratyev #include <sys/systm.h>
405aa839c9SVladimir Kondratyev 
415aa839c9SVladimir Kondratyev #include <dev/evdev/input.h>
425aa839c9SVladimir Kondratyev #include <dev/evdev/evdev.h>
435aa839c9SVladimir Kondratyev 
445aa839c9SVladimir Kondratyev #define HID_DEBUG_VAR   bcm5974_debug
455aa839c9SVladimir Kondratyev #include <dev/hid/hid.h>
465aa839c9SVladimir Kondratyev #include <dev/hid/hidbus.h>
475aa839c9SVladimir Kondratyev #include <dev/hid/hidquirk.h>
485aa839c9SVladimir Kondratyev 
495aa839c9SVladimir Kondratyev #include <dev/usb/usb.h>
505aa839c9SVladimir Kondratyev #include <dev/usb/usbdi.h>
515aa839c9SVladimir Kondratyev #include <dev/usb/usbhid.h>
525aa839c9SVladimir Kondratyev #include <dev/usb/usb_ioctl.h>
535aa839c9SVladimir Kondratyev 
545aa839c9SVladimir Kondratyev #include "usbdevs.h"
555aa839c9SVladimir Kondratyev 
561c4edee3SVal Packett #define	BCM5974_BUFFER_MAX	(246 * 2)	/* 2 Type4 SPI frames */
575aa839c9SVladimir Kondratyev #define	BCM5974_TLC_PAGE	HUP_GENERIC_DESKTOP
585aa839c9SVladimir Kondratyev #define	BCM5974_TLC_USAGE	HUG_MOUSE
595aa839c9SVladimir Kondratyev 
605aa839c9SVladimir Kondratyev /* magic to switch device from HID (default) mode into raw */
615aa839c9SVladimir Kondratyev /* Type1 & Type2 trackpads */
625aa839c9SVladimir Kondratyev #define	BCM5974_USB_IFACE_INDEX	0
635aa839c9SVladimir Kondratyev #define	BCM5974_USB_REPORT_LEN	8
645aa839c9SVladimir Kondratyev #define	BCM5974_USB_REPORT_ID	0
655aa839c9SVladimir Kondratyev #define	BCM5974_USB_MODE_RAW	0x01
665aa839c9SVladimir Kondratyev #define	BCM5974_USB_MODE_HID	0x08
675aa839c9SVladimir Kondratyev /* Type4 trackpads */
685aa839c9SVladimir Kondratyev #define	BCM5974_HID_REPORT_LEN	2
695aa839c9SVladimir Kondratyev #define	BCM5974_HID_REPORT_ID	2
705aa839c9SVladimir Kondratyev #define	BCM5974_HID_MODE_RAW	0x01
715aa839c9SVladimir Kondratyev #define	BCM5974_HID_MODE_HID	0x00
725aa839c9SVladimir Kondratyev 
735aa839c9SVladimir Kondratyev /* Tunables */
745aa839c9SVladimir Kondratyev static	SYSCTL_NODE(_hw_hid, OID_AUTO, bcm5974, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
755aa839c9SVladimir Kondratyev     "HID wellspring touchpad");
765aa839c9SVladimir Kondratyev 
775aa839c9SVladimir Kondratyev #ifdef HID_DEBUG
785aa839c9SVladimir Kondratyev enum wsp_log_level {
795aa839c9SVladimir Kondratyev 	BCM5974_LLEVEL_DISABLED = 0,
805aa839c9SVladimir Kondratyev 	BCM5974_LLEVEL_ERROR,
815aa839c9SVladimir Kondratyev 	BCM5974_LLEVEL_DEBUG,		/* for troubleshooting */
825aa839c9SVladimir Kondratyev 	BCM5974_LLEVEL_INFO,		/* for diagnostics */
835aa839c9SVladimir Kondratyev };
845aa839c9SVladimir Kondratyev /* the default is to only log errors */
855aa839c9SVladimir Kondratyev static int bcm5974_debug = BCM5974_LLEVEL_ERROR;
865aa839c9SVladimir Kondratyev 
875aa839c9SVladimir Kondratyev SYSCTL_INT(_hw_hid_bcm5974, OID_AUTO, debug, CTLFLAG_RWTUN,
885aa839c9SVladimir Kondratyev     &bcm5974_debug, BCM5974_LLEVEL_ERROR, "BCM5974 debug level");
895aa839c9SVladimir Kondratyev #endif					/* HID_DEBUG */
905aa839c9SVladimir Kondratyev 
915aa839c9SVladimir Kondratyev /*
925aa839c9SVladimir Kondratyev  * Some tables, structures, definitions and constant values for the
935aa839c9SVladimir Kondratyev  * touchpad protocol has been copied from Linux's
945aa839c9SVladimir Kondratyev  * "drivers/input/mouse/bcm5974.c" which has the following copyright
955aa839c9SVladimir Kondratyev  * holders under GPLv2. All device specific code in this driver has
965aa839c9SVladimir Kondratyev  * been written from scratch. The decoding algorithm is based on
975aa839c9SVladimir Kondratyev  * output from FreeBSD's usbdump.
985aa839c9SVladimir Kondratyev  *
995aa839c9SVladimir Kondratyev  * Copyright (C) 2008      Henrik Rydberg (rydberg@euromail.se)
1005aa839c9SVladimir Kondratyev  * Copyright (C) 2008      Scott Shawcroft (scott.shawcroft@gmail.com)
1015aa839c9SVladimir Kondratyev  * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
1025aa839c9SVladimir Kondratyev  * Copyright (C) 2005      Johannes Berg (johannes@sipsolutions.net)
1035aa839c9SVladimir Kondratyev  * Copyright (C) 2005      Stelian Pop (stelian@popies.net)
1045aa839c9SVladimir Kondratyev  * Copyright (C) 2005      Frank Arnold (frank@scirocco-5v-turbo.de)
1055aa839c9SVladimir Kondratyev  * Copyright (C) 2005      Peter Osterlund (petero2@telia.com)
1065aa839c9SVladimir Kondratyev  * Copyright (C) 2005      Michael Hanselmann (linux-kernel@hansmi.ch)
1075aa839c9SVladimir Kondratyev  * Copyright (C) 2006      Nicolas Boichat (nicolas@boichat.ch)
1085aa839c9SVladimir Kondratyev  */
1095aa839c9SVladimir Kondratyev 
1105aa839c9SVladimir Kondratyev /* trackpad header types */
1115aa839c9SVladimir Kondratyev enum tp_type {
1125aa839c9SVladimir Kondratyev 	TYPE1,			/* plain trackpad */
1135aa839c9SVladimir Kondratyev 	TYPE2,			/* button integrated in trackpad */
1145aa839c9SVladimir Kondratyev 	TYPE3,			/* additional header fields since June 2013 */
1155aa839c9SVladimir Kondratyev 	TYPE4,                  /* additional header field for pressure data */
116ef8397c2SVal Packett 	TYPE_MT2U,			/* Magic Trackpad 2 USB */
1175aa839c9SVladimir Kondratyev 	TYPE_CNT
1185aa839c9SVladimir Kondratyev };
1195aa839c9SVladimir Kondratyev 
1205aa839c9SVladimir Kondratyev /* list of device capability bits */
1215aa839c9SVladimir Kondratyev #define	HAS_INTEGRATED_BUTTON	1
122ef8397c2SVal Packett #define	USES_COMPACT_REPORT	2
1235aa839c9SVladimir Kondratyev 
1245aa839c9SVladimir Kondratyev struct tp_type_params {
1255aa839c9SVladimir Kondratyev 	uint8_t	caps;		/* device capability bitmask */
1265aa839c9SVladimir Kondratyev 	uint8_t	button;		/* offset to button data */
1275aa839c9SVladimir Kondratyev 	uint8_t	offset;		/* offset to trackpad finger data */
1285aa839c9SVladimir Kondratyev 	uint8_t delta;		/* offset from header to finger struct */
1295aa839c9SVladimir Kondratyev } const static tp[TYPE_CNT] = {
1305aa839c9SVladimir Kondratyev 	[TYPE1] = {
1315aa839c9SVladimir Kondratyev 		.caps = 0,
1325aa839c9SVladimir Kondratyev 		.button = 0,
1335aa839c9SVladimir Kondratyev 		.offset = 13 * 2,
1345aa839c9SVladimir Kondratyev 		.delta = 0,
1355aa839c9SVladimir Kondratyev 	},
1365aa839c9SVladimir Kondratyev 	[TYPE2] = {
1375aa839c9SVladimir Kondratyev 		.caps = HAS_INTEGRATED_BUTTON,
1385aa839c9SVladimir Kondratyev 		.button = 15,
1395aa839c9SVladimir Kondratyev 		.offset = 15 * 2,
1405aa839c9SVladimir Kondratyev 		.delta = 0,
1415aa839c9SVladimir Kondratyev 	},
1425aa839c9SVladimir Kondratyev 	[TYPE3] = {
1435aa839c9SVladimir Kondratyev 		.caps = HAS_INTEGRATED_BUTTON,
1445aa839c9SVladimir Kondratyev 		.button = 23,
1455aa839c9SVladimir Kondratyev 		.offset = 19 * 2,
1465aa839c9SVladimir Kondratyev 		.delta = 0,
1475aa839c9SVladimir Kondratyev 	},
1485aa839c9SVladimir Kondratyev 	[TYPE4] = {
1495aa839c9SVladimir Kondratyev 		.caps = HAS_INTEGRATED_BUTTON,
1505aa839c9SVladimir Kondratyev 		.button = 31,
1515aa839c9SVladimir Kondratyev 		.offset = 23 * 2,
1525aa839c9SVladimir Kondratyev 		.delta = 2,
1535aa839c9SVladimir Kondratyev 	},
154ef8397c2SVal Packett 	[TYPE_MT2U] = {
155ef8397c2SVal Packett 		.caps = HAS_INTEGRATED_BUTTON | USES_COMPACT_REPORT,
156ef8397c2SVal Packett 		.button = 1,
157ef8397c2SVal Packett 		.offset = 12,
158ef8397c2SVal Packett 		.delta = 0,
159ef8397c2SVal Packett 	},
160ef8397c2SVal Packett };
161ef8397c2SVal Packett 
162ef8397c2SVal Packett /* trackpad finger structure - compact version for external "Magic" devices */
163ef8397c2SVal Packett struct tp_finger_compact {
164ef8397c2SVal Packett 	uint32_t coords; /* not struct directly due to endian conversion */
165ef8397c2SVal Packett 	uint8_t touch_major;
166ef8397c2SVal Packett 	uint8_t touch_minor;
167ef8397c2SVal Packett 	uint8_t size;
168ef8397c2SVal Packett 	uint8_t pressure;
16919c804b7SVladimir Kondratyev 	uint8_t id_ori;
170ef8397c2SVal Packett } __packed;
171ef8397c2SVal Packett 
172ef8397c2SVal Packett _Static_assert((sizeof(struct tp_finger_compact) == 9), "tp_finger struct size must be 9");
173ef8397c2SVal Packett 
1745aa839c9SVladimir Kondratyev /* trackpad finger structure - little endian */
1755aa839c9SVladimir Kondratyev struct tp_finger {
176fda9ac06SGreg V 	uint16_t	origin;		/* zero when switching track finger */
177fda9ac06SGreg V 	uint16_t	abs_x;		/* absolute x coodinate */
178fda9ac06SGreg V 	uint16_t	abs_y;		/* absolute y coodinate */
179fda9ac06SGreg V 	uint16_t	rel_x;		/* relative x coodinate */
180fda9ac06SGreg V 	uint16_t	rel_y;		/* relative y coodinate */
181fda9ac06SGreg V 	uint16_t	tool_major;	/* tool area, major axis */
182fda9ac06SGreg V 	uint16_t	tool_minor;	/* tool area, minor axis */
183fda9ac06SGreg V 	uint16_t	orientation;	/* 16384 when point, else 15 bit angle */
184fda9ac06SGreg V 	uint16_t	touch_major;	/* touch area, major axis */
185fda9ac06SGreg V 	uint16_t	touch_minor;	/* touch area, minor axis */
186fda9ac06SGreg V 	uint16_t	unused[2];	/* zeros */
187fda9ac06SGreg V 	uint16_t	pressure;	/* pressure on forcetouch touchpad */
188fda9ac06SGreg V 	uint16_t	multi;		/* one finger: varies, more fingers:
1895aa839c9SVladimir Kondratyev 					 * constant */
1905aa839c9SVladimir Kondratyev } __packed;
1915aa839c9SVladimir Kondratyev 
192fda9ac06SGreg V #define BCM5974_LE2H(x) ((int32_t)(int16_t)le16toh(x))
193fda9ac06SGreg V 
1945aa839c9SVladimir Kondratyev /* trackpad finger data size, empirically at least ten fingers */
1955aa839c9SVladimir Kondratyev #define	MAX_FINGERS		MAX_MT_SLOTS
1965aa839c9SVladimir Kondratyev 
1975aa839c9SVladimir Kondratyev #define	MAX_FINGER_ORIENTATION	16384
1985aa839c9SVladimir Kondratyev 
1995aa839c9SVladimir Kondratyev enum {
2005aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING1,
2015aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING2,
2025aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING3,
2035aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING4,
2045aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING4A,
2055aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING5,
2065aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING6A,
2075aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING6,
2085aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING5A,
2095aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING7,
2105aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING7A,
2115aa839c9SVladimir Kondratyev 	BCM5974_FLAG_WELLSPRING8,
2121c4edee3SVal Packett 	BCM5974_FLAG_WELLSPRING9_MODEL3,
2131c4edee3SVal Packett 	BCM5974_FLAG_WELLSPRING9_MODEL4,
2141c4edee3SVal Packett #define	BCM5974_FLAG_WELLSPRING9_MODEL_SPI	BCM5974_FLAG_WELLSPRING9_MODEL4
2151c4edee3SVal Packett 	BCM5974_FLAG_WELLSPRING9_MODEL5,
2161c4edee3SVal Packett 	BCM5974_FLAG_WELLSPRING9_MODEL6,
217ef8397c2SVal Packett 	BCM5974_FLAG_MAGIC_TRACKPAD2_USB,
2185aa839c9SVladimir Kondratyev 	BCM5974_FLAG_MAX,
2195aa839c9SVladimir Kondratyev };
2205aa839c9SVladimir Kondratyev 
2215aa839c9SVladimir Kondratyev /* device-specific parameters */
2225aa839c9SVladimir Kondratyev struct bcm5974_axis {
2235aa839c9SVladimir Kondratyev 	int snratio;			/* signal-to-noise ratio */
2245aa839c9SVladimir Kondratyev 	int min;			/* device minimum reading */
2255aa839c9SVladimir Kondratyev 	int max;			/* device maximum reading */
2265aa839c9SVladimir Kondratyev 	int size;			/* physical size, mm */
2275aa839c9SVladimir Kondratyev };
2285aa839c9SVladimir Kondratyev 
2295aa839c9SVladimir Kondratyev /* device-specific configuration */
2305aa839c9SVladimir Kondratyev struct bcm5974_dev_params {
2315aa839c9SVladimir Kondratyev 	const struct tp_type_params* tp;
2325aa839c9SVladimir Kondratyev 	struct bcm5974_axis p;		/* finger pressure limits */
2335aa839c9SVladimir Kondratyev 	struct bcm5974_axis w;		/* finger width limits */
2345aa839c9SVladimir Kondratyev 	struct bcm5974_axis x;		/* horizontal limits */
2355aa839c9SVladimir Kondratyev 	struct bcm5974_axis y;		/* vertical limits */
2365aa839c9SVladimir Kondratyev 	struct bcm5974_axis o;		/* orientation limits */
2375aa839c9SVladimir Kondratyev };
2385aa839c9SVladimir Kondratyev 
2395aa839c9SVladimir Kondratyev /* logical signal quality */
2405aa839c9SVladimir Kondratyev #define	SN_PRESSURE	45		/* pressure signal-to-noise ratio */
2415aa839c9SVladimir Kondratyev #define	SN_WIDTH	25		/* width signal-to-noise ratio */
2425aa839c9SVladimir Kondratyev #define	SN_COORD	250		/* coordinate signal-to-noise ratio */
2435aa839c9SVladimir Kondratyev #define	SN_ORIENT	10		/* orientation signal-to-noise ratio */
2445aa839c9SVladimir Kondratyev 
2455aa839c9SVladimir Kondratyev static const struct bcm5974_dev_params bcm5974_dev_params[BCM5974_FLAG_MAX] = {
2465aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING1] = {
2475aa839c9SVladimir Kondratyev 		.tp = tp + TYPE1,
2485aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 256, 0 },
2495aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
2505aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4824, 5342, 105 },
2515aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -172, 5820, 75 },
2525aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
2535aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
2545aa839c9SVladimir Kondratyev 	},
2555aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING2] = {
2565aa839c9SVladimir Kondratyev 		.tp = tp + TYPE1,
2575aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 256, 0 },
2585aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
2595aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4824, 4824, 105 },
2605aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -172, 4290, 75 },
2615aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
2625aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
2635aa839c9SVladimir Kondratyev 	},
2645aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING3] = {
2655aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
2665aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
2675aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
2685aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4460, 5166, 105 },
2695aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -75, 6700, 75 },
2705aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
2715aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
2725aa839c9SVladimir Kondratyev 	},
2735aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING4] = {
2745aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
2755aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
2765aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
2775aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4620, 5140, 105 },
2785aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -150, 6600, 75 },
2795aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
2805aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
2815aa839c9SVladimir Kondratyev 	},
2825aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING4A] = {
2835aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
2845aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
2855aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
2865aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4616, 5112, 105 },
2875aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -142, 5234, 75 },
2885aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
2895aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
2905aa839c9SVladimir Kondratyev 	},
2915aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING5] = {
2925aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
2935aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
2945aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
2955aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4415, 5050, 105 },
2965aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -55, 6680, 75 },
2975aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
2985aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
2995aa839c9SVladimir Kondratyev 	},
3005aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING6] = {
3015aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
3025aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
3035aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
3045aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4620, 5140, 105 },
3055aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -150, 6600, 75 },
3065aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
3075aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3085aa839c9SVladimir Kondratyev 	},
3095aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING5A] = {
3105aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
3115aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
3125aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
3135aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4750, 5280, 105 },
3145aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -150, 6730, 75 },
3155aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
3165aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3175aa839c9SVladimir Kondratyev 	},
3185aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING6A] = {
3195aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
3205aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
3215aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
3225aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4620, 5140, 105 },
3235aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -150, 6600, 75 },
3245aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
3255aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3265aa839c9SVladimir Kondratyev 	},
3275aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING7] = {
3285aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
3295aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
3305aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
3315aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4750, 5280, 105 },
3325aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -150, 6730, 75 },
3335aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
3345aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3355aa839c9SVladimir Kondratyev 	},
3365aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING7A] = {
3375aa839c9SVladimir Kondratyev 		.tp = tp + TYPE2,
3385aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
3395aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
3405aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4750, 5280, 105 },
3415aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -150, 6730, 75 },
3425aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
3435aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3445aa839c9SVladimir Kondratyev 	},
3455aa839c9SVladimir Kondratyev 	[BCM5974_FLAG_WELLSPRING8] = {
3465aa839c9SVladimir Kondratyev 		.tp = tp + TYPE3,
3475aa839c9SVladimir Kondratyev 		.p = { SN_PRESSURE, 0, 300, 0 },
3485aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
3495aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4620, 5140, 105 },
3505aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -150, 6600, 75 },
3515aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
3525aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3535aa839c9SVladimir Kondratyev 	},
354176939bdSVal Packett 	/*
355176939bdSVal Packett 	 * NOTE: Actually force-sensitive. Pressure has a "size" equal to the max
356176939bdSVal Packett 	 * so that the "resolution" is 1 (i.e. values will be interpreted as grams).
357176939bdSVal Packett 	 * No scientific measurements have been done :) but a really hard press
358176939bdSVal Packett 	 * results in a value around 3500 on model 4.
359176939bdSVal Packett 	 */
3601c4edee3SVal Packett 	[BCM5974_FLAG_WELLSPRING9_MODEL3] = {
3615aa839c9SVladimir Kondratyev 		.tp = tp + TYPE4,
362176939bdSVal Packett 		.p = { SN_PRESSURE, 0, 4096, 4096 },
3635aa839c9SVladimir Kondratyev 		.w = { SN_WIDTH, 0, 2048, 0 },
3645aa839c9SVladimir Kondratyev 		.x = { SN_COORD, -4828, 5345, 105 },
3655aa839c9SVladimir Kondratyev 		.y = { SN_COORD, -203, 6803, 75 },
3665aa839c9SVladimir Kondratyev 		.o = { SN_ORIENT,
3675aa839c9SVladimir Kondratyev 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3685aa839c9SVladimir Kondratyev 	},
3691c4edee3SVal Packett 	[BCM5974_FLAG_WELLSPRING9_MODEL4] = {
3701c4edee3SVal Packett 		.tp = tp + TYPE4,
3711c4edee3SVal Packett 		.p = { SN_PRESSURE, 0, 4096, 4096 },
3721c4edee3SVal Packett 		.w = { SN_WIDTH, 0, 2048, 0 },
3731c4edee3SVal Packett 		.x = { SN_COORD, -5087, 5579, 105 },
3741c4edee3SVal Packett 		.y = { SN_COORD, -182, 6089, 75 },
3751c4edee3SVal Packett 		.o = { SN_ORIENT,
3761c4edee3SVal Packett 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3771c4edee3SVal Packett 	},
3781c4edee3SVal Packett 	[BCM5974_FLAG_WELLSPRING9_MODEL5] = {
3791c4edee3SVal Packett 		.tp = tp + TYPE4,
3801c4edee3SVal Packett 		.p = { SN_PRESSURE, 0, 4096, 4096 },
3811c4edee3SVal Packett 		.w = { SN_WIDTH, 0, 2048, 0 },
3821c4edee3SVal Packett 		.x = { SN_COORD, -6243, 6749, 105 },
3831c4edee3SVal Packett 		.y = { SN_COORD, -170, 7685, 75 },
3841c4edee3SVal Packett 		.o = { SN_ORIENT,
3851c4edee3SVal Packett 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3861c4edee3SVal Packett 	},
3871c4edee3SVal Packett 	[BCM5974_FLAG_WELLSPRING9_MODEL6] = {
3881c4edee3SVal Packett 		.tp = tp + TYPE4,
3891c4edee3SVal Packett 		.p = { SN_PRESSURE, 0, 4096, 4096 },
3901c4edee3SVal Packett 		.w = { SN_WIDTH, 0, 2048, 0 },
3911c4edee3SVal Packett 		.x = { SN_COORD, -7456, 7976, 105 },
3921c4edee3SVal Packett 		.y = { SN_COORD, -163, 9283, 75 },
3931c4edee3SVal Packett 		.o = { SN_ORIENT,
3941c4edee3SVal Packett 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
3951c4edee3SVal Packett 	},
396ef8397c2SVal Packett 	[BCM5974_FLAG_MAGIC_TRACKPAD2_USB] = {
397ef8397c2SVal Packett 		.tp = tp + TYPE_MT2U,
398ef8397c2SVal Packett 		.p = { SN_PRESSURE, 0, 256, 256 },
399ef8397c2SVal Packett 		.w = { SN_WIDTH, 0, 2048, 0 },
400c85e6a5cSVladimir Kondratyev 		.x = { SN_COORD, -3678, 3934, 157 },
401c85e6a5cSVladimir Kondratyev 		.y = { SN_COORD, -2478, 2587, 107 },
40219c804b7SVladimir Kondratyev 		.o = { SN_ORIENT, -3, 4, 0 },
403ef8397c2SVal Packett 	},
4045aa839c9SVladimir Kondratyev };
4055aa839c9SVladimir Kondratyev 
4065aa839c9SVladimir Kondratyev #define	BCM5974_DEV(v,p,i)	{					\
4075aa839c9SVladimir Kondratyev 	HID_BVPI(BUS_USB, USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i),	\
4085aa839c9SVladimir Kondratyev 	HID_TLC(BCM5974_TLC_PAGE, BCM5974_TLC_USAGE),			\
4095aa839c9SVladimir Kondratyev }
4105aa839c9SVladimir Kondratyev 
4111c4edee3SVal Packett #define	APPLE_HID	"APP000D"
4121c4edee3SVal Packett #define	BCM5974_DEV_SPI(hid, i)	{					\
4131c4edee3SVal Packett 	HID_BUS(BUS_SPI), HID_PNP(hid), HID_DRIVER_INFO(i),		\
4141c4edee3SVal Packett 	HID_TLC(BCM5974_TLC_PAGE, BCM5974_TLC_USAGE),			\
4151c4edee3SVal Packett }
4161c4edee3SVal Packett 
4175aa839c9SVladimir Kondratyev static const struct hid_device_id bcm5974_devs[] = {
4185aa839c9SVladimir Kondratyev 	/* MacbookAir1.1 */
4195aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING_ANSI, BCM5974_FLAG_WELLSPRING1),
4205aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING_ISO, BCM5974_FLAG_WELLSPRING1),
4215aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING_JIS, BCM5974_FLAG_WELLSPRING1),
4225aa839c9SVladimir Kondratyev 
4235aa839c9SVladimir Kondratyev 	/* MacbookProPenryn, aka wellspring2 */
4245aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING2_ANSI, BCM5974_FLAG_WELLSPRING2),
4255aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING2_ISO, BCM5974_FLAG_WELLSPRING2),
4265aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING2_JIS, BCM5974_FLAG_WELLSPRING2),
4275aa839c9SVladimir Kondratyev 
4285aa839c9SVladimir Kondratyev 	/* Macbook5,1 (unibody), aka wellspring3 */
4295aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING3_ANSI, BCM5974_FLAG_WELLSPRING3),
4305aa839c9SVladimir Kondratyev         BCM5974_DEV(APPLE, WELLSPRING3_ISO, BCM5974_FLAG_WELLSPRING3),
4315aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING3_JIS, BCM5974_FLAG_WELLSPRING3),
4325aa839c9SVladimir Kondratyev 
4335aa839c9SVladimir Kondratyev 	/* MacbookAir3,2 (unibody), aka wellspring4 */
4345aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING4_ANSI, BCM5974_FLAG_WELLSPRING4),
4355aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING4_ISO, BCM5974_FLAG_WELLSPRING4),
4365aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING4_JIS, BCM5974_FLAG_WELLSPRING4),
4375aa839c9SVladimir Kondratyev 
4385aa839c9SVladimir Kondratyev 	/* MacbookAir3,1 (unibody), aka wellspring4 */
4395aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING4A_ANSI, BCM5974_FLAG_WELLSPRING4A),
4405aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING4A_ISO, BCM5974_FLAG_WELLSPRING4A),
4415aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING4A_JIS, BCM5974_FLAG_WELLSPRING4A),
4425aa839c9SVladimir Kondratyev 
4435aa839c9SVladimir Kondratyev 	/* Macbook8 (unibody, March 2011) */
4445aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING5_ANSI, BCM5974_FLAG_WELLSPRING5),
4455aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING5_ISO, BCM5974_FLAG_WELLSPRING5),
4465aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING5_JIS, BCM5974_FLAG_WELLSPRING5),
4475aa839c9SVladimir Kondratyev 
4485aa839c9SVladimir Kondratyev 	/* MacbookAir4,1 (unibody, July 2011) */
4495aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING6A_ANSI, BCM5974_FLAG_WELLSPRING6A),
4505aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING6A_ISO, BCM5974_FLAG_WELLSPRING6A),
4515aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING6A_JIS, BCM5974_FLAG_WELLSPRING6A),
4525aa839c9SVladimir Kondratyev 
4535aa839c9SVladimir Kondratyev 	/* MacbookAir4,2 (unibody, July 2011) */
4545aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING6_ANSI, BCM5974_FLAG_WELLSPRING6),
4555aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING6_ISO, BCM5974_FLAG_WELLSPRING6),
4565aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING6_JIS, BCM5974_FLAG_WELLSPRING6),
4575aa839c9SVladimir Kondratyev 
4585aa839c9SVladimir Kondratyev 	/* Macbook8,2 (unibody) */
4595aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING5A_ANSI, BCM5974_FLAG_WELLSPRING5A),
4605aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING5A_ISO, BCM5974_FLAG_WELLSPRING5A),
4615aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING5A_JIS, BCM5974_FLAG_WELLSPRING5A),
4625aa839c9SVladimir Kondratyev 
4635aa839c9SVladimir Kondratyev 	/* MacbookPro10,1 (unibody, June 2012) */
4645aa839c9SVladimir Kondratyev 	/* MacbookPro11,1-3 (unibody, June 2013) */
4655aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING7_ANSI, BCM5974_FLAG_WELLSPRING7),
4665aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING7_ISO, BCM5974_FLAG_WELLSPRING7),
4675aa839c9SVladimir Kondratyev         BCM5974_DEV(APPLE, WELLSPRING7_JIS, BCM5974_FLAG_WELLSPRING7),
4685aa839c9SVladimir Kondratyev 
4695aa839c9SVladimir Kondratyev         /* MacbookPro10,2 (unibody, October 2012) */
4705aa839c9SVladimir Kondratyev         BCM5974_DEV(APPLE, WELLSPRING7A_ANSI, BCM5974_FLAG_WELLSPRING7A),
4715aa839c9SVladimir Kondratyev         BCM5974_DEV(APPLE, WELLSPRING7A_ISO, BCM5974_FLAG_WELLSPRING7A),
4725aa839c9SVladimir Kondratyev         BCM5974_DEV(APPLE, WELLSPRING7A_JIS, BCM5974_FLAG_WELLSPRING7A),
4735aa839c9SVladimir Kondratyev 
4745aa839c9SVladimir Kondratyev 	/* MacbookAir6,2 (unibody, June 2013) */
4755aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING8_ANSI, BCM5974_FLAG_WELLSPRING8),
4765aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING8_ISO, BCM5974_FLAG_WELLSPRING8),
4775aa839c9SVladimir Kondratyev 	BCM5974_DEV(APPLE, WELLSPRING8_JIS, BCM5974_FLAG_WELLSPRING8),
4785aa839c9SVladimir Kondratyev 
4795aa839c9SVladimir Kondratyev 	/* MacbookPro12,1 MacbookPro11,4 */
4801c4edee3SVal Packett 	BCM5974_DEV(APPLE, WELLSPRING9_ANSI, BCM5974_FLAG_WELLSPRING9_MODEL3),
4811c4edee3SVal Packett 	BCM5974_DEV(APPLE, WELLSPRING9_ISO, BCM5974_FLAG_WELLSPRING9_MODEL3),
4821c4edee3SVal Packett 	BCM5974_DEV(APPLE, WELLSPRING9_JIS, BCM5974_FLAG_WELLSPRING9_MODEL3),
4831c4edee3SVal Packett 
4841c4edee3SVal Packett 	/* Generic SPI device */
4851c4edee3SVal Packett 	BCM5974_DEV_SPI(APPLE_HID, BCM5974_FLAG_WELLSPRING9_MODEL_SPI),
486ef8397c2SVal Packett 
487ef8397c2SVal Packett 	/* External "Magic" devices */
488ef8397c2SVal Packett 	BCM5974_DEV(APPLE, MAGIC_TRACKPAD2, BCM5974_FLAG_MAGIC_TRACKPAD2_USB),
4895aa839c9SVladimir Kondratyev };
4905aa839c9SVladimir Kondratyev 
4911c4edee3SVal Packett #define	BCM5974_WELLSPRING9_RDESC_SIZE		110
4921c4edee3SVal Packett #define	BCM5974_WELLSPRING9_MODEL_OFFSET	106
4931c4edee3SVal Packett 
4945aa839c9SVladimir Kondratyev struct bcm5974_softc {
4955aa839c9SVladimir Kondratyev 	device_t sc_dev;
4965aa839c9SVladimir Kondratyev 	struct evdev_dev *sc_evdev;
4975aa839c9SVladimir Kondratyev 	/* device configuration */
4985aa839c9SVladimir Kondratyev 	const struct bcm5974_dev_params *sc_params;
49945b6c31aSGreg V 	bool sc_saved_mode;
5005aa839c9SVladimir Kondratyev };
5015aa839c9SVladimir Kondratyev 
5025aa839c9SVladimir Kondratyev static const uint8_t bcm5974_rdesc[] = {
5035aa839c9SVladimir Kondratyev 	0x05, BCM5974_TLC_PAGE,	/* Usage Page (BCM5974_TLC_PAGE)	*/
5045aa839c9SVladimir Kondratyev 	0x09, BCM5974_TLC_USAGE,/* Usage (BCM5974_TLC_USAGE)		*/
5055aa839c9SVladimir Kondratyev 	0xA1, 0x01,		/* Collection (Application)		*/
5065aa839c9SVladimir Kondratyev 	0x06, 0x00, 0xFF,	/*   Usage Page (Vendor Defined 0xFF00)	*/
5075aa839c9SVladimir Kondratyev 	0x09, 0x01,		/*   Usage (0x01)			*/
5085aa839c9SVladimir Kondratyev 	0x15, 0x00,		/*   Logical Minimum (0)		*/
5095aa839c9SVladimir Kondratyev 	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
5105aa839c9SVladimir Kondratyev 	0x75, 0x08,		/*   Report Size (8)			*/
5115aa839c9SVladimir Kondratyev 	0x96,			/*   Report Count (BCM5974_BUFFER_MAX)	*/
5125aa839c9SVladimir Kondratyev 	BCM5974_BUFFER_MAX & 0xFF,
5135aa839c9SVladimir Kondratyev 	BCM5974_BUFFER_MAX >> 8 & 0xFF,
5145aa839c9SVladimir Kondratyev 	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
5155aa839c9SVladimir Kondratyev 	0xC0,			/* End Collection			*/
5165aa839c9SVladimir Kondratyev };
5175aa839c9SVladimir Kondratyev 
5185aa839c9SVladimir Kondratyev /*
5195aa839c9SVladimir Kondratyev  * function prototypes
5205aa839c9SVladimir Kondratyev  */
5215aa839c9SVladimir Kondratyev static evdev_open_t	bcm5974_ev_open;
5225aa839c9SVladimir Kondratyev static evdev_close_t	bcm5974_ev_close;
5235aa839c9SVladimir Kondratyev static const struct evdev_methods bcm5974_evdev_methods = {
5245aa839c9SVladimir Kondratyev 	.ev_open =	&bcm5974_ev_open,
5255aa839c9SVladimir Kondratyev 	.ev_close =	&bcm5974_ev_close,
5265aa839c9SVladimir Kondratyev };
5275aa839c9SVladimir Kondratyev static hid_intr_t	bcm5974_intr;
5285aa839c9SVladimir Kondratyev 
5295aa839c9SVladimir Kondratyev /* Device methods. */
5305aa839c9SVladimir Kondratyev static device_identify_t bcm5974_identify;
5315aa839c9SVladimir Kondratyev static device_probe_t	bcm5974_probe;
5325aa839c9SVladimir Kondratyev static device_attach_t	bcm5974_attach;
5335aa839c9SVladimir Kondratyev static device_detach_t	bcm5974_detach;
5345aa839c9SVladimir Kondratyev 
5355aa839c9SVladimir Kondratyev /*
5365aa839c9SVladimir Kondratyev  * Type1 and Type2 touchpads use keyboard USB interface to switch from HID to
5375aa839c9SVladimir Kondratyev  * RAW mode. Although it is possible to extend hkbd driver to support such a
5385aa839c9SVladimir Kondratyev  * mode change requests, it's not wanted due to cross device tree dependencies.
5395aa839c9SVladimir Kondratyev  * So, find lowest common denominator (struct usb_device of grandparent usbhid
5405aa839c9SVladimir Kondratyev  * driver) of touchpad and keyboard drivers and issue direct USB requests.
5415aa839c9SVladimir Kondratyev  */
5425aa839c9SVladimir Kondratyev static int
5435aa839c9SVladimir Kondratyev bcm5974_set_device_mode_usb(struct bcm5974_softc *sc, bool on)
5445aa839c9SVladimir Kondratyev {
5455aa839c9SVladimir Kondratyev 	uint8_t mode_bytes[BCM5974_USB_REPORT_LEN];
5465aa839c9SVladimir Kondratyev 	struct usb_ctl_request ucr;
5475aa839c9SVladimir Kondratyev 	int err;
5485aa839c9SVladimir Kondratyev 
5495aa839c9SVladimir Kondratyev 	ucr.ucr_request.bmRequestType = UT_READ_CLASS_INTERFACE;
5505aa839c9SVladimir Kondratyev 	ucr.ucr_request.bRequest = UR_GET_REPORT;
5515aa839c9SVladimir Kondratyev 	USETW2(ucr.ucr_request.wValue,
5525aa839c9SVladimir Kondratyev 	    UHID_FEATURE_REPORT, BCM5974_USB_REPORT_ID);
5535aa839c9SVladimir Kondratyev 	ucr.ucr_request.wIndex[0] = BCM5974_USB_IFACE_INDEX;
5545aa839c9SVladimir Kondratyev 	ucr.ucr_request.wIndex[1] = 0;
5555aa839c9SVladimir Kondratyev 	USETW(ucr.ucr_request.wLength, BCM5974_USB_REPORT_LEN);
5565aa839c9SVladimir Kondratyev 	ucr.ucr_data = mode_bytes;
5575aa839c9SVladimir Kondratyev 
5585aa839c9SVladimir Kondratyev 	err = hid_ioctl(sc->sc_dev, USB_REQUEST, (uintptr_t)&ucr);
5595aa839c9SVladimir Kondratyev 	if (err != 0) {
5605aa839c9SVladimir Kondratyev 		DPRINTF("Failed to read device mode (%d)\n", err);
5615aa839c9SVladimir Kondratyev 		return (EIO);
5625aa839c9SVladimir Kondratyev 	}
5635aa839c9SVladimir Kondratyev #if 0
5645aa839c9SVladimir Kondratyev 	/*
5655aa839c9SVladimir Kondratyev 	 * XXX Need to wait at least 250ms for hardware to get
5665aa839c9SVladimir Kondratyev 	 * ready. The device mode handling appears to be handled
5675aa839c9SVladimir Kondratyev 	 * asynchronously and we should not issue these commands too
5685aa839c9SVladimir Kondratyev 	 * quickly.
5695aa839c9SVladimir Kondratyev 	 */
5705aa839c9SVladimir Kondratyev 	pause("WHW", hz / 4);
5715aa839c9SVladimir Kondratyev #endif
5725aa839c9SVladimir Kondratyev 	mode_bytes[0] = on ? BCM5974_USB_MODE_RAW : BCM5974_USB_MODE_HID;
5735aa839c9SVladimir Kondratyev 	ucr.ucr_request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
5745aa839c9SVladimir Kondratyev 	ucr.ucr_request.bRequest = UR_SET_REPORT;
5755aa839c9SVladimir Kondratyev 
5765aa839c9SVladimir Kondratyev 	err = hid_ioctl(sc->sc_dev, USB_REQUEST, (uintptr_t)&ucr);
5775aa839c9SVladimir Kondratyev 	if (err != 0) {
5785aa839c9SVladimir Kondratyev 		DPRINTF("Failed to write device mode (%d)\n", err);
5795aa839c9SVladimir Kondratyev 		return (EIO);
5805aa839c9SVladimir Kondratyev 	}
5815aa839c9SVladimir Kondratyev 
5825aa839c9SVladimir Kondratyev 	return (0);
5835aa839c9SVladimir Kondratyev }
5845aa839c9SVladimir Kondratyev 
5855aa839c9SVladimir Kondratyev static int
5865aa839c9SVladimir Kondratyev bcm5974_set_device_mode_hid(struct bcm5974_softc *sc, bool on)
5875aa839c9SVladimir Kondratyev {
5885aa839c9SVladimir Kondratyev 	uint8_t	mode_bytes[BCM5974_HID_REPORT_LEN] = {
5895aa839c9SVladimir Kondratyev 		BCM5974_HID_REPORT_ID,
5905aa839c9SVladimir Kondratyev 		on ? BCM5974_HID_MODE_RAW : BCM5974_HID_MODE_HID,
5915aa839c9SVladimir Kondratyev 	};
5925aa839c9SVladimir Kondratyev #if 0
5935aa839c9SVladimir Kondratyev 	int err;
5945aa839c9SVladimir Kondratyev 
5955aa839c9SVladimir Kondratyev 	err = hid_get_report(sc->sc_dev, mode_bytes, BCM5974_HID_REPORT_LEN,
5965aa839c9SVladimir Kondratyev 	    NULL, HID_FEATURE_REPORT, BCM5974_HID_REPORT_ID);
5975aa839c9SVladimir Kondratyev 	if (err != 0) {
5985aa839c9SVladimir Kondratyev 		DPRINTF("Failed to read device mode (%d)\n", err);
5995aa839c9SVladimir Kondratyev 		return (err);
6005aa839c9SVladimir Kondratyev 	}
6015aa839c9SVladimir Kondratyev 	/*
6025aa839c9SVladimir Kondratyev 	 * XXX Need to wait at least 250ms for hardware to get
6035aa839c9SVladimir Kondratyev 	 * ready. The device mode handling appears to be handled
6045aa839c9SVladimir Kondratyev 	 * asynchronously and we should not issue these commands too
6055aa839c9SVladimir Kondratyev 	 * quickly.
6065aa839c9SVladimir Kondratyev 	 */
6075aa839c9SVladimir Kondratyev 	pause("WHW", hz / 4);
6085aa839c9SVladimir Kondratyev 	mode_bytes[1] = on ? BCM5974_HID_MODE_RAW : BCM5974_HID_MODE_HID;
6095aa839c9SVladimir Kondratyev #endif
6105aa839c9SVladimir Kondratyev 	return (hid_set_report(sc->sc_dev, mode_bytes, BCM5974_HID_REPORT_LEN,
6115aa839c9SVladimir Kondratyev 	    HID_FEATURE_REPORT, BCM5974_HID_REPORT_ID));
6125aa839c9SVladimir Kondratyev }
6135aa839c9SVladimir Kondratyev 
6145aa839c9SVladimir Kondratyev static int
6155aa839c9SVladimir Kondratyev bcm5974_set_device_mode(struct bcm5974_softc *sc, bool on)
6165aa839c9SVladimir Kondratyev {
6175aa839c9SVladimir Kondratyev 	int err = 0;
6185aa839c9SVladimir Kondratyev 
6195aa839c9SVladimir Kondratyev 	switch (sc->sc_params->tp - tp) {
6205aa839c9SVladimir Kondratyev 	case TYPE1:
6215aa839c9SVladimir Kondratyev 	case TYPE2:
6225aa839c9SVladimir Kondratyev 		err = bcm5974_set_device_mode_usb(sc, on);
6235aa839c9SVladimir Kondratyev 		break;
6245aa839c9SVladimir Kondratyev 	case TYPE3:	/* Type 3 does not require a mode switch */
6255aa839c9SVladimir Kondratyev 		break;
6265aa839c9SVladimir Kondratyev 	case TYPE4:
627ef8397c2SVal Packett 	case TYPE_MT2U:
6285aa839c9SVladimir Kondratyev 		err = bcm5974_set_device_mode_hid(sc, on);
6295aa839c9SVladimir Kondratyev 		break;
6305aa839c9SVladimir Kondratyev 	default:
6315aa839c9SVladimir Kondratyev 		KASSERT(0 == 1, ("Unknown trackpad type"));
6325aa839c9SVladimir Kondratyev 	}
6335aa839c9SVladimir Kondratyev 
63445b6c31aSGreg V 	if (!err)
63545b6c31aSGreg V 		sc->sc_saved_mode = on;
63645b6c31aSGreg V 
6375aa839c9SVladimir Kondratyev 	return (err);
6385aa839c9SVladimir Kondratyev }
6395aa839c9SVladimir Kondratyev 
6401c4edee3SVal Packett static uintptr_t
6411c4edee3SVal Packett bcm5974_get_wsp9_model(device_t dev)
6421c4edee3SVal Packett {
6431c4edee3SVal Packett 	const struct hid_device_info *hw = hid_get_device_info(dev);
6441c4edee3SVal Packett 	static uint8_t rdesc[BCM5974_WELLSPRING9_RDESC_SIZE];
6451c4edee3SVal Packett 	uint8_t model_byte = 0;
6461c4edee3SVal Packett 
6471c4edee3SVal Packett 	bus_topo_assert();
6481c4edee3SVal Packett 
6491c4edee3SVal Packett 	if (hw->rdescsize == sizeof(rdesc) &&
6501c4edee3SVal Packett 	    hid_get_rdesc(dev, rdesc, sizeof(rdesc)) == 0) {
6511c4edee3SVal Packett 		model_byte = rdesc[BCM5974_WELLSPRING9_MODEL_OFFSET];
6521c4edee3SVal Packett 		switch (model_byte) {
6531c4edee3SVal Packett 		case 3:
6541c4edee3SVal Packett 			/* MacbookPro12,1 MacbookPro11,4 */
6551c4edee3SVal Packett 			return (BCM5974_FLAG_WELLSPRING9_MODEL3);
6561c4edee3SVal Packett 		case 4:
6571c4edee3SVal Packett 			/* Macbook8,1 Macbook9,1 Macbook10,1 */
6581c4edee3SVal Packett 			return (BCM5974_FLAG_WELLSPRING9_MODEL4);
6591c4edee3SVal Packett 		case 5:
6601c4edee3SVal Packett 			/*
6611c4edee3SVal Packett 			 * MacbookPro13,1 MacbookPro13,2
6621c4edee3SVal Packett 			 * MacbookPro14,1 MacbookPro14,2
6631c4edee3SVal Packett 			 */
6641c4edee3SVal Packett 			return (BCM5974_FLAG_WELLSPRING9_MODEL5);
6651c4edee3SVal Packett 		case 6:
6661c4edee3SVal Packett 			/* MacbookPro13,3 MacbookPro14,3 */
6671c4edee3SVal Packett 			return (BCM5974_FLAG_WELLSPRING9_MODEL6);
6681c4edee3SVal Packett 		}
6691c4edee3SVal Packett 	}
6701c4edee3SVal Packett 
6711c4edee3SVal Packett 	device_printf(dev, "Unexpected trackpad descriptor len=%u model_byte="
6721c4edee3SVal Packett 	    "%u, not extracting model\n", hw->rdescsize, model_byte);
6731c4edee3SVal Packett 
6741c4edee3SVal Packett 	/* Fallback for unknown SPI versions */
6751c4edee3SVal Packett 	return (BCM5974_FLAG_WELLSPRING9_MODEL_SPI);
6761c4edee3SVal Packett }
6771c4edee3SVal Packett 
6785aa839c9SVladimir Kondratyev static void
6795aa839c9SVladimir Kondratyev bcm5974_identify(driver_t *driver, device_t parent)
6805aa839c9SVladimir Kondratyev {
6815aa839c9SVladimir Kondratyev 	void *d_ptr;
6825aa839c9SVladimir Kondratyev 	hid_size_t d_len;
6835aa839c9SVladimir Kondratyev 
6845aa839c9SVladimir Kondratyev 	/*
6855aa839c9SVladimir Kondratyev 	 * The bcm5974 touchpad has no stable RAW mode TLC in its report
6865aa839c9SVladimir Kondratyev 	 * descriptor.  So replace existing HID mode mouse TLC with dummy one
6875aa839c9SVladimir Kondratyev 	 * to set proper transport layer buffer sizes, make driver probe
6885aa839c9SVladimir Kondratyev 	 * simpler and prevent unwanted hms driver attachment.
6895aa839c9SVladimir Kondratyev 	 */
6905aa839c9SVladimir Kondratyev 	if (HIDBUS_LOOKUP_ID(parent, bcm5974_devs) != NULL &&
6915aa839c9SVladimir Kondratyev 	    hid_get_report_descr(parent, &d_ptr, &d_len) == 0 &&
6925aa839c9SVladimir Kondratyev 	    hid_is_mouse(d_ptr, d_len))
6935aa839c9SVladimir Kondratyev 		hid_set_report_descr(parent, bcm5974_rdesc,
6945aa839c9SVladimir Kondratyev 		    sizeof(bcm5974_rdesc));
6955aa839c9SVladimir Kondratyev }
6965aa839c9SVladimir Kondratyev 
6975aa839c9SVladimir Kondratyev static int
6985aa839c9SVladimir Kondratyev bcm5974_probe(device_t dev)
6995aa839c9SVladimir Kondratyev {
7005aa839c9SVladimir Kondratyev 	int err;
7015aa839c9SVladimir Kondratyev 
7025aa839c9SVladimir Kondratyev 	err = HIDBUS_LOOKUP_DRIVER_INFO(dev, bcm5974_devs);
7035aa839c9SVladimir Kondratyev 	if (err != 0)
7045aa839c9SVladimir Kondratyev 		return (err);
7055aa839c9SVladimir Kondratyev 
7065aa839c9SVladimir Kondratyev 	hidbus_set_desc(dev, "Touchpad");
7075aa839c9SVladimir Kondratyev 
7085aa839c9SVladimir Kondratyev 	return (BUS_PROBE_DEFAULT);
7095aa839c9SVladimir Kondratyev }
7105aa839c9SVladimir Kondratyev 
7115aa839c9SVladimir Kondratyev static int
7125aa839c9SVladimir Kondratyev bcm5974_attach(device_t dev)
7135aa839c9SVladimir Kondratyev {
7145aa839c9SVladimir Kondratyev 	struct bcm5974_softc *sc = device_get_softc(dev);
7155aa839c9SVladimir Kondratyev 	const struct hid_device_info *hw = hid_get_device_info(dev);
7161c4edee3SVal Packett 	uintptr_t drv_info;
7175aa839c9SVladimir Kondratyev 	int err;
7185aa839c9SVladimir Kondratyev 
7195aa839c9SVladimir Kondratyev 	DPRINTFN(BCM5974_LLEVEL_INFO, "sc=%p\n", sc);
7205aa839c9SVladimir Kondratyev 
7215aa839c9SVladimir Kondratyev 	sc->sc_dev = dev;
7225aa839c9SVladimir Kondratyev 
7235aa839c9SVladimir Kondratyev 	/* get device specific configuration */
7241c4edee3SVal Packett 	drv_info = hidbus_get_driver_info(dev);
7251c4edee3SVal Packett 	if (drv_info == BCM5974_FLAG_WELLSPRING9_MODEL_SPI)
7261c4edee3SVal Packett 		drv_info = bcm5974_get_wsp9_model(dev);
7271c4edee3SVal Packett 	sc->sc_params = bcm5974_dev_params + drv_info;
7285aa839c9SVladimir Kondratyev 
7295aa839c9SVladimir Kondratyev 	sc->sc_evdev = evdev_alloc();
7305aa839c9SVladimir Kondratyev 	evdev_set_name(sc->sc_evdev, device_get_desc(dev));
7315aa839c9SVladimir Kondratyev 	evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
7325aa839c9SVladimir Kondratyev 	evdev_set_id(sc->sc_evdev, hw->idBus, hw->idVendor, hw->idProduct,
7335aa839c9SVladimir Kondratyev 	    hw->idVersion);
7345aa839c9SVladimir Kondratyev 	evdev_set_serial(sc->sc_evdev, hw->serial);
7355aa839c9SVladimir Kondratyev 	evdev_set_methods(sc->sc_evdev, sc, &bcm5974_evdev_methods);
7365aa839c9SVladimir Kondratyev 	evdev_support_prop(sc->sc_evdev, INPUT_PROP_POINTER);
7375aa839c9SVladimir Kondratyev 	evdev_support_event(sc->sc_evdev, EV_SYN);
7385aa839c9SVladimir Kondratyev 	evdev_support_event(sc->sc_evdev, EV_ABS);
7395aa839c9SVladimir Kondratyev 	evdev_support_event(sc->sc_evdev, EV_KEY);
7405aa839c9SVladimir Kondratyev 	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */
7415aa839c9SVladimir Kondratyev 
7425aa839c9SVladimir Kondratyev #define BCM5974_ABS(evdev, code, param)					\
7435aa839c9SVladimir Kondratyev 	evdev_support_abs((evdev), (code), (param).min, (param).max,	\
7445aa839c9SVladimir Kondratyev 	((param).max - (param).min) / (param).snratio, 0,		\
7455aa839c9SVladimir Kondratyev 	(param).size != 0 ? ((param).max - (param).min) / (param).size : 0);
7465aa839c9SVladimir Kondratyev 
7475aa839c9SVladimir Kondratyev 	/* finger position */
7485aa839c9SVladimir Kondratyev 	BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_X, sc->sc_params->x);
7495aa839c9SVladimir Kondratyev 	BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_Y, sc->sc_params->y);
7505aa839c9SVladimir Kondratyev 	/* finger pressure */
7515aa839c9SVladimir Kondratyev 	BCM5974_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p);
7525aa839c9SVladimir Kondratyev 	/* finger touch area */
7535aa839c9SVladimir Kondratyev 	BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w);
7545aa839c9SVladimir Kondratyev 	BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w);
7555aa839c9SVladimir Kondratyev 	/* finger approach area */
756ef8397c2SVal Packett 	if ((sc->sc_params->tp->caps & USES_COMPACT_REPORT) == 0) {
7575aa839c9SVladimir Kondratyev 		BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MAJOR, sc->sc_params->w);
7585aa839c9SVladimir Kondratyev 		BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MINOR, sc->sc_params->w);
759ef8397c2SVal Packett 	}
7605aa839c9SVladimir Kondratyev 	/* finger orientation */
7615aa839c9SVladimir Kondratyev 	BCM5974_ABS(sc->sc_evdev, ABS_MT_ORIENTATION, sc->sc_params->o);
7625aa839c9SVladimir Kondratyev 	/* button properties */
7635aa839c9SVladimir Kondratyev 	evdev_support_key(sc->sc_evdev, BTN_LEFT);
7645aa839c9SVladimir Kondratyev 	if ((sc->sc_params->tp->caps & HAS_INTEGRATED_BUTTON) != 0)
7655aa839c9SVladimir Kondratyev 		evdev_support_prop(sc->sc_evdev, INPUT_PROP_BUTTONPAD);
7665aa839c9SVladimir Kondratyev 	/* Enable automatic touch assignment for type B MT protocol */
7675aa839c9SVladimir Kondratyev 	evdev_support_abs(sc->sc_evdev, ABS_MT_SLOT,
7685aa839c9SVladimir Kondratyev 	    0, MAX_FINGERS - 1, 0, 0, 0);
7695aa839c9SVladimir Kondratyev 	evdev_support_abs(sc->sc_evdev, ABS_MT_TRACKING_ID,
7705aa839c9SVladimir Kondratyev 	    -1, MAX_FINGERS - 1, 0, 0, 0);
771ef8397c2SVal Packett 	if ((sc->sc_params->tp->caps & USES_COMPACT_REPORT) == 0)
7725aa839c9SVladimir Kondratyev 		evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_TRACK);
7735aa839c9SVladimir Kondratyev 	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_AUTOREL);
7745aa839c9SVladimir Kondratyev 	/* Synaptics compatibility events */
7755aa839c9SVladimir Kondratyev 	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_STCOMPAT);
7765aa839c9SVladimir Kondratyev 
7775aa839c9SVladimir Kondratyev 	err = evdev_register(sc->sc_evdev);
7785aa839c9SVladimir Kondratyev 	if (err)
7795aa839c9SVladimir Kondratyev 		goto detach;
7805aa839c9SVladimir Kondratyev 
7815aa839c9SVladimir Kondratyev 	hidbus_set_intr(dev, bcm5974_intr, sc);
7825aa839c9SVladimir Kondratyev 
7835aa839c9SVladimir Kondratyev 	return (0);
7845aa839c9SVladimir Kondratyev 
7855aa839c9SVladimir Kondratyev detach:
7865aa839c9SVladimir Kondratyev 	bcm5974_detach(dev);
7875aa839c9SVladimir Kondratyev 	return (ENOMEM);
7885aa839c9SVladimir Kondratyev }
7895aa839c9SVladimir Kondratyev 
7905aa839c9SVladimir Kondratyev static int
7915aa839c9SVladimir Kondratyev bcm5974_detach(device_t dev)
7925aa839c9SVladimir Kondratyev {
7935aa839c9SVladimir Kondratyev 	struct bcm5974_softc *sc = device_get_softc(dev);
7945aa839c9SVladimir Kondratyev 
7955aa839c9SVladimir Kondratyev 	evdev_free(sc->sc_evdev);
7965aa839c9SVladimir Kondratyev 
7975aa839c9SVladimir Kondratyev 	return (0);
7985aa839c9SVladimir Kondratyev }
7995aa839c9SVladimir Kondratyev 
80045b6c31aSGreg V static int
80145b6c31aSGreg V bcm5974_resume(device_t dev)
80245b6c31aSGreg V {
80345b6c31aSGreg V 	struct bcm5974_softc *sc = device_get_softc(dev);
80445b6c31aSGreg V 
80545b6c31aSGreg V 	bcm5974_set_device_mode(sc, sc->sc_saved_mode);
80645b6c31aSGreg V 
80745b6c31aSGreg V 	return (0);
80845b6c31aSGreg V }
80945b6c31aSGreg V 
8105aa839c9SVladimir Kondratyev static void
8115aa839c9SVladimir Kondratyev bcm5974_intr(void *context, void *data, hid_size_t len)
8125aa839c9SVladimir Kondratyev {
8135aa839c9SVladimir Kondratyev 	struct bcm5974_softc *sc = context;
8145aa839c9SVladimir Kondratyev 	const struct bcm5974_dev_params *params = sc->sc_params;
8155aa839c9SVladimir Kondratyev 	union evdev_mt_slot slot_data;
8165aa839c9SVladimir Kondratyev 	struct tp_finger *f;
817ef8397c2SVal Packett 	struct tp_finger_compact *fc;
81819c804b7SVladimir Kondratyev 	int coords;
8195aa839c9SVladimir Kondratyev 	int ntouch;			/* the finger number in touch */
8205aa839c9SVladimir Kondratyev 	int ibt;			/* button status */
8215aa839c9SVladimir Kondratyev 	int i;
8225aa839c9SVladimir Kondratyev 	int slot;
823*9097284bSVladimir Kondratyev 	uint8_t id;
8245aa839c9SVladimir Kondratyev 	uint8_t fsize = sizeof(struct tp_finger) + params->tp->delta;
8255aa839c9SVladimir Kondratyev 
826ef8397c2SVal Packett 	if ((params->tp->caps & USES_COMPACT_REPORT) != 0)
827ef8397c2SVal Packett 		fsize = sizeof(struct tp_finger_compact) + params->tp->delta;
828ef8397c2SVal Packett 
8295aa839c9SVladimir Kondratyev 	if ((len < params->tp->offset + fsize) ||
8305aa839c9SVladimir Kondratyev 	    ((len - params->tp->offset) % fsize) != 0) {
8315aa839c9SVladimir Kondratyev 		DPRINTFN(BCM5974_LLEVEL_INFO, "Invalid length: %d, %x, %x\n",
8324f345989SVladimir Kondratyev 		    len, params->tp->offset, fsize);
8335aa839c9SVladimir Kondratyev 		return;
8345aa839c9SVladimir Kondratyev 	}
8355aa839c9SVladimir Kondratyev 
8365aa839c9SVladimir Kondratyev 	ibt = ((uint8_t *)data)[params->tp->button];
8375aa839c9SVladimir Kondratyev 	ntouch = (len - params->tp->offset) / fsize;
8385aa839c9SVladimir Kondratyev 
8395aa839c9SVladimir Kondratyev 	for (i = 0, slot = 0; i != ntouch; i++) {
840ef8397c2SVal Packett 		if ((params->tp->caps & USES_COMPACT_REPORT) != 0) {
841ef8397c2SVal Packett 			fc = (struct tp_finger_compact *)(((uint8_t *)data) +
842ef8397c2SVal Packett 			     params->tp->offset + params->tp->delta + i * fsize);
84319c804b7SVladimir Kondratyev 			coords = (int)le32toh(fc->coords);
844*9097284bSVladimir Kondratyev 			id = fc->id_ori & 0x0f;
845*9097284bSVladimir Kondratyev 			slot = evdev_mt_id_to_slot(sc->sc_evdev, id);
846ef8397c2SVal Packett 			DPRINTFN(BCM5974_LLEVEL_INFO,
847ef8397c2SVal Packett 			    "[%d]ibt=%d, taps=%d, x=%5d, y=%5d, state=%4d, "
848ef8397c2SVal Packett 			    "tchmaj=%4d, tchmin=%4d, size=%4d, pressure=%4d, "
849*9097284bSVladimir Kondratyev 			    "ot=%4x, id=%4x, slot=%d\n",
85019c804b7SVladimir Kondratyev 			    i, ibt, ntouch, coords << 19 >> 19,
85119c804b7SVladimir Kondratyev 			    coords << 6 >> 19, (u_int)coords >> 30,
85219c804b7SVladimir Kondratyev 			    fc->touch_major, fc->touch_minor, fc->size,
853*9097284bSVladimir Kondratyev 			    fc->pressure, fc->id_ori >> 5, id, slot);
854*9097284bSVladimir Kondratyev 			if (fc->touch_major == 0 || slot == -1)
855ef8397c2SVal Packett 				continue;
856ef8397c2SVal Packett 			slot_data = (union evdev_mt_slot) {
857*9097284bSVladimir Kondratyev 				.id = id,
85819c804b7SVladimir Kondratyev 				.x = coords << 19 >> 19,
85919c804b7SVladimir Kondratyev 				.y = params->y.min + params->y.max -
86019c804b7SVladimir Kondratyev 				    ((coords << 6) >> 19),
861ef8397c2SVal Packett 				.p = fc->pressure,
862ef8397c2SVal Packett 				.maj = fc->touch_major << 2,
863ef8397c2SVal Packett 				.min = fc->touch_minor << 2,
86419c804b7SVladimir Kondratyev 				.ori = (int)(fc->id_ori >> 5) - 4,
865ef8397c2SVal Packett 			};
866ef8397c2SVal Packett 			evdev_mt_push_slot(sc->sc_evdev, slot, &slot_data);
867ef8397c2SVal Packett 			continue;
868ef8397c2SVal Packett 		}
8695aa839c9SVladimir Kondratyev 		f = (struct tp_finger *)(((uint8_t *)data) +
8705aa839c9SVladimir Kondratyev 		    params->tp->offset + params->tp->delta + i * fsize);
8715aa839c9SVladimir Kondratyev 		DPRINTFN(BCM5974_LLEVEL_INFO,
8725aa839c9SVladimir Kondratyev 		    "[%d]ibt=%d, taps=%d, o=%4d, ax=%5d, ay=%5d, "
8735aa839c9SVladimir Kondratyev 		    "rx=%5d, ry=%5d, tlmaj=%4d, tlmin=%4d, ot=%4x, "
874fda9ac06SGreg V 		    "tchmaj=%4d, tchmin=%4d, pressure=%4d, m=%4x\n",
875fda9ac06SGreg V 		    i, ibt, ntouch, BCM5974_LE2H(f->origin),
876fda9ac06SGreg V 		    BCM5974_LE2H(f->abs_x), BCM5974_LE2H(f->abs_y),
877fda9ac06SGreg V 		    BCM5974_LE2H(f->rel_x), BCM5974_LE2H(f->rel_y),
878fda9ac06SGreg V 		    BCM5974_LE2H(f->tool_major), BCM5974_LE2H(f->tool_minor),
879fda9ac06SGreg V 		    BCM5974_LE2H(f->orientation), BCM5974_LE2H(f->touch_major),
880fda9ac06SGreg V 		    BCM5974_LE2H(f->touch_minor), BCM5974_LE2H(f->pressure),
881fda9ac06SGreg V 		    BCM5974_LE2H(f->multi));
8825aa839c9SVladimir Kondratyev 
883fda9ac06SGreg V 		if (BCM5974_LE2H(f->touch_major) == 0)
8845aa839c9SVladimir Kondratyev 			continue;
8855aa839c9SVladimir Kondratyev 		slot_data = (union evdev_mt_slot) {
8865aa839c9SVladimir Kondratyev 			.id = slot,
887fda9ac06SGreg V 			.x = BCM5974_LE2H(f->abs_x),
888fda9ac06SGreg V 			.y = params->y.min + params->y.max -
889fda9ac06SGreg V 			     BCM5974_LE2H(f->abs_y),
890fda9ac06SGreg V 			.p = BCM5974_LE2H(f->pressure),
891fda9ac06SGreg V 			.maj = BCM5974_LE2H(f->touch_major) << 1,
892fda9ac06SGreg V 			.min = BCM5974_LE2H(f->touch_minor) << 1,
893fda9ac06SGreg V 			.w_maj = BCM5974_LE2H(f->tool_major) << 1,
894fda9ac06SGreg V 			.w_min = BCM5974_LE2H(f->tool_minor) << 1,
895fda9ac06SGreg V 			.ori = params->o.max - BCM5974_LE2H(f->orientation),
8965aa839c9SVladimir Kondratyev 		};
8975aa839c9SVladimir Kondratyev 		evdev_mt_push_slot(sc->sc_evdev, slot, &slot_data);
8985aa839c9SVladimir Kondratyev 		slot++;
8995aa839c9SVladimir Kondratyev 	}
9005aa839c9SVladimir Kondratyev 
9015aa839c9SVladimir Kondratyev 	evdev_push_key(sc->sc_evdev, BTN_LEFT, ibt);
9025aa839c9SVladimir Kondratyev 	evdev_sync(sc->sc_evdev);
9035aa839c9SVladimir Kondratyev }
9045aa839c9SVladimir Kondratyev 
9055aa839c9SVladimir Kondratyev static int
9065aa839c9SVladimir Kondratyev bcm5974_ev_open(struct evdev_dev *evdev)
9075aa839c9SVladimir Kondratyev {
9085aa839c9SVladimir Kondratyev 	struct bcm5974_softc *sc = evdev_get_softc(evdev);
9095aa839c9SVladimir Kondratyev 	int err;
9105aa839c9SVladimir Kondratyev 
9115aa839c9SVladimir Kondratyev 	/*
9125aa839c9SVladimir Kondratyev 	 * By default the touchpad behaves like a HID device, sending
9135aa839c9SVladimir Kondratyev 	 * packets with reportID = 8. Such reports contain only
9145aa839c9SVladimir Kondratyev 	 * limited information. They encode movement deltas and button
9155aa839c9SVladimir Kondratyev 	 * events, but do not include data from the pressure
9165aa839c9SVladimir Kondratyev 	 * sensors. The device input mode can be switched from HID
9175aa839c9SVladimir Kondratyev 	 * reports to raw sensor data using vendor-specific USB
9185aa839c9SVladimir Kondratyev 	 * control commands:
9195aa839c9SVladimir Kondratyev 	 */
9205aa839c9SVladimir Kondratyev 	err = bcm5974_set_device_mode(sc, true);
9215aa839c9SVladimir Kondratyev 	if (err != 0) {
9225aa839c9SVladimir Kondratyev 		DPRINTF("failed to set mode to RAW MODE (%d)\n", err);
9235aa839c9SVladimir Kondratyev 		return (err);
9245aa839c9SVladimir Kondratyev 	}
9255aa839c9SVladimir Kondratyev 
9264151ac9fSVladimir Kondratyev 	return (hid_intr_start(sc->sc_dev));
9275aa839c9SVladimir Kondratyev }
9285aa839c9SVladimir Kondratyev 
9295aa839c9SVladimir Kondratyev static int
9305aa839c9SVladimir Kondratyev bcm5974_ev_close(struct evdev_dev *evdev)
9315aa839c9SVladimir Kondratyev {
9325aa839c9SVladimir Kondratyev 	struct bcm5974_softc *sc = evdev_get_softc(evdev);
9335aa839c9SVladimir Kondratyev 	int err;
9345aa839c9SVladimir Kondratyev 
9354151ac9fSVladimir Kondratyev 	err = hid_intr_stop(sc->sc_dev);
9365aa839c9SVladimir Kondratyev 	if (err != 0)
9375aa839c9SVladimir Kondratyev 		return (err);
9385aa839c9SVladimir Kondratyev 
9395aa839c9SVladimir Kondratyev 	/*
9405aa839c9SVladimir Kondratyev 	 * During re-enumeration of the device we need to force the
9415aa839c9SVladimir Kondratyev 	 * device back into HID mode before switching it to RAW
9425aa839c9SVladimir Kondratyev 	 * mode. Else the device does not work like expected.
9435aa839c9SVladimir Kondratyev 	 */
9445aa839c9SVladimir Kondratyev 	err = bcm5974_set_device_mode(sc, false);
9455aa839c9SVladimir Kondratyev 	if (err != 0)
9465aa839c9SVladimir Kondratyev 		DPRINTF("Failed to set mode to HID MODE (%d)\n", err);
9475aa839c9SVladimir Kondratyev 
9485aa839c9SVladimir Kondratyev 	return (err);
9495aa839c9SVladimir Kondratyev }
9505aa839c9SVladimir Kondratyev 
9515aa839c9SVladimir Kondratyev static device_method_t bcm5974_methods[] = {
9525aa839c9SVladimir Kondratyev 	/* Device interface */
9535aa839c9SVladimir Kondratyev 	DEVMETHOD(device_identify,	bcm5974_identify),
9545aa839c9SVladimir Kondratyev 	DEVMETHOD(device_probe,		bcm5974_probe),
9555aa839c9SVladimir Kondratyev 	DEVMETHOD(device_attach,	bcm5974_attach),
9565aa839c9SVladimir Kondratyev 	DEVMETHOD(device_detach,	bcm5974_detach),
95745b6c31aSGreg V 	DEVMETHOD(device_resume,	bcm5974_resume),
9585aa839c9SVladimir Kondratyev 	DEVMETHOD_END
9595aa839c9SVladimir Kondratyev };
9605aa839c9SVladimir Kondratyev 
9615aa839c9SVladimir Kondratyev static driver_t bcm5974_driver = {
9625aa839c9SVladimir Kondratyev 	.name = "bcm5974",
9635aa839c9SVladimir Kondratyev 	.methods = bcm5974_methods,
9645aa839c9SVladimir Kondratyev 	.size = sizeof(struct bcm5974_softc)
9655aa839c9SVladimir Kondratyev };
9665aa839c9SVladimir Kondratyev 
9677eeede15SJohn Baldwin DRIVER_MODULE(bcm5974, hidbus, bcm5974_driver, NULL, NULL);
9685aa839c9SVladimir Kondratyev MODULE_DEPEND(bcm5974, hidbus, 1, 1, 1);
9695aa839c9SVladimir Kondratyev MODULE_DEPEND(bcm5974, hid, 1, 1, 1);
9705aa839c9SVladimir Kondratyev MODULE_DEPEND(bcm5974, evdev, 1, 1, 1);
9715aa839c9SVladimir Kondratyev MODULE_VERSION(bcm5974, 1);
9725aa839c9SVladimir Kondratyev HID_PNP_INFO(bcm5974_devs);
973