xref: /netbsd-src/sys/dev/usb/ums.c (revision 010a79541ef5a5a184c8d4c11ce40dbe33afbb19)
1*010a7954Sjakllsch /*	$NetBSD: ums.c,v 1.106 2024/03/18 15:15:27 jakllsch Exp $	*/
2deb00272Saugustss 
3deb00272Saugustss /*
4404d04cdSbouyer  * Copyright (c) 1998, 2017 The NetBSD Foundation, Inc.
5deb00272Saugustss  * All rights reserved.
6deb00272Saugustss  *
7ca707fcdSaugustss  * This code is derived from software contributed to The NetBSD Foundation
830c20f93Saugustss  * by Lennart Augustsson (lennart@augustsson.net) at
9ca707fcdSaugustss  * Carlstedt Research & Technology.
10deb00272Saugustss  *
11deb00272Saugustss  * Redistribution and use in source and binary forms, with or without
12deb00272Saugustss  * modification, are permitted provided that the following conditions
13deb00272Saugustss  * are met:
14deb00272Saugustss  * 1. Redistributions of source code must retain the above copyright
15deb00272Saugustss  *    notice, this list of conditions and the following disclaimer.
16deb00272Saugustss  * 2. Redistributions in binary form must reproduce the above copyright
17deb00272Saugustss  *    notice, this list of conditions and the following disclaimer in the
18deb00272Saugustss  *    documentation and/or other materials provided with the distribution.
19deb00272Saugustss  *
20deb00272Saugustss  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21deb00272Saugustss  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22deb00272Saugustss  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23deb00272Saugustss  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24deb00272Saugustss  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25deb00272Saugustss  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26deb00272Saugustss  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27deb00272Saugustss  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28deb00272Saugustss  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29deb00272Saugustss  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30deb00272Saugustss  * POSSIBILITY OF SUCH DAMAGE.
31deb00272Saugustss  */
32deb00272Saugustss 
3307e0165bSaugustss /*
34287c3adcSaugustss  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
3507e0165bSaugustss  */
3607e0165bSaugustss 
37667807e4Slukem #include <sys/cdefs.h>
38*010a7954Sjakllsch __KERNEL_RCSID(0, "$NetBSD: ums.c,v 1.106 2024/03/18 15:15:27 jakllsch Exp $");
39f734221aSjakllsch 
40f734221aSjakllsch #ifdef _KERNEL_OPT
41f734221aSjakllsch #include "opt_usb.h"
42f734221aSjakllsch #endif
43667807e4Slukem 
44deb00272Saugustss #include <sys/param.h>
45deb00272Saugustss #include <sys/systm.h>
46deb00272Saugustss #include <sys/kernel.h>
47deb00272Saugustss #include <sys/device.h>
48deb00272Saugustss #include <sys/ioctl.h>
49deb00272Saugustss #include <sys/file.h>
50deb00272Saugustss #include <sys/select.h>
51d6a34bb1Smrg #include <sys/sysctl.h>
52deb00272Saugustss #include <sys/proc.h>
53deb00272Saugustss #include <sys/vnode.h>
54deb00272Saugustss #include <sys/poll.h>
55deb00272Saugustss 
56deb00272Saugustss #include <dev/usb/usb.h>
57deb00272Saugustss #include <dev/usb/usbhid.h>
58deb00272Saugustss 
59deb00272Saugustss #include <dev/usb/usbdi.h>
60deb00272Saugustss #include <dev/usb/usbdi_util.h>
61deb00272Saugustss #include <dev/usb/usbdevs.h>
62deb00272Saugustss #include <dev/usb/usb_quirks.h>
63935b0481Saugustss #include <dev/usb/uhidev.h>
64d6a34bb1Smrg #include <dev/usb/usbhist.h>
65404d04cdSbouyer #include <dev/hid/hid.h>
66404d04cdSbouyer #include <dev/hid/hidms.h>
6767ec8587Saugustss 
68d6a34bb1Smrg #ifdef USB_DEBUG
69d6a34bb1Smrg #ifndef UMS_DEBUG
70d6a34bb1Smrg #define umsdebug 0
71deb00272Saugustss #else
72d6a34bb1Smrg 
73d6a34bb1Smrg #ifndef UMS_DEBUG_DEFAULT
74d6a34bb1Smrg #define UMS_DEBUG_DEFAULT 0
75deb00272Saugustss #endif
76deb00272Saugustss 
77d6a34bb1Smrg static int umsdebug = UMS_DEBUG_DEFAULT;
78d6a34bb1Smrg 
79d6a34bb1Smrg SYSCTL_SETUP(sysctl_hw_ums_setup, "sysctl hw.ums setup")
80d6a34bb1Smrg {
81d6a34bb1Smrg 	int err;
82d6a34bb1Smrg 	const struct sysctlnode *rnode;
83d6a34bb1Smrg 	const struct sysctlnode *cnode;
84d6a34bb1Smrg 
85d6a34bb1Smrg 	err = sysctl_createv(clog, 0, NULL, &rnode,
86d6a34bb1Smrg 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "ums",
87d6a34bb1Smrg 	    SYSCTL_DESCR("ums global controls"),
88d6a34bb1Smrg 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
89d6a34bb1Smrg 
90d6a34bb1Smrg 	if (err)
91d6a34bb1Smrg 		goto fail;
92d6a34bb1Smrg 
93d6a34bb1Smrg 	/* control debugging printfs */
94d6a34bb1Smrg 	err = sysctl_createv(clog, 0, &rnode, &cnode,
95d6a34bb1Smrg 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
96d6a34bb1Smrg 	    "debug", SYSCTL_DESCR("Enable debugging output"),
97d6a34bb1Smrg 	    NULL, 0, &umsdebug, sizeof(umsdebug), CTL_CREATE, CTL_EOL);
98d6a34bb1Smrg 	if (err)
99d6a34bb1Smrg 		goto fail;
100d6a34bb1Smrg 
101d6a34bb1Smrg 	return;
102d6a34bb1Smrg fail:
103d6a34bb1Smrg 	aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
104d6a34bb1Smrg }
105d6a34bb1Smrg 
106d6a34bb1Smrg #endif /* UMS_DEBUG */
107d6a34bb1Smrg #endif /* USB_DEBUG */
108d6a34bb1Smrg 
109d6a34bb1Smrg #define DPRINTF(FMT,A,B,C,D)	USBHIST_LOGN(umsdebug,1,FMT,A,B,C,D)
110d6a34bb1Smrg #define DPRINTFN(N,FMT,A,B,C,D)	USBHIST_LOGN(umsdebug,N,FMT,A,B,C,D)
111d6a34bb1Smrg #define UMSHIST_FUNC()		USBHIST_FUNC()
112d6a34bb1Smrg #define UMSHIST_CALLED(name)	USBHIST_CALLED(umsdebug)
113d6a34bb1Smrg #define UMSHIST_CALLARGS(FMT,A,B,C,D) \
114d6a34bb1Smrg 				USBHIST_CALLARGS(umsdebug,FMT,A,B,C,D)
115d6a34bb1Smrg #define UMSHIST_CALLARGSN(N,FMT,A,B,C,D) \
116d6a34bb1Smrg 				USBHIST_CALLARGSN(umsdebug,N,FMT,A,B,C,D)
117d6a34bb1Smrg 
11824f66ce1Saugustss #define UMSUNIT(s)	(minor(s))
119e0c228aeSaugustss 
120935b0481Saugustss struct ums_softc {
1210b6c2425Sriastradh 	struct uhidev *sc_hdev;
122f82ea250Sriastradh 	struct usbd_device *sc_udev;
123404d04cdSbouyer 	struct hidms sc_ms;
12467ec8587Saugustss 
125c47914cbSjmcneill 	bool	sc_alwayson;
126c47914cbSjmcneill 
12767ec8587Saugustss 	int	sc_enabled;
1281f266179Saugustss 	char	sc_dying;
129deb00272Saugustss };
130deb00272Saugustss 
1310b6c2425Sriastradh Static void ums_intr(void *, void *, u_int);
132deb00272Saugustss 
133ed9486e1Saugustss Static int	ums_enable(void *);
134ed9486e1Saugustss Static void	ums_disable(void *);
13553524e44Schristos Static int	ums_ioctl(void *, u_long, void *, int, struct lwp *);
13667ec8587Saugustss 
13797b908dcSmaxv static const struct wsmouse_accessops ums_accessops = {
13867ec8587Saugustss 	ums_enable,
13967ec8587Saugustss 	ums_ioctl,
14067ec8587Saugustss 	ums_disable,
14167ec8587Saugustss };
14267ec8587Saugustss 
14397b908dcSmaxv static int ums_match(device_t, cfdata_t, void *);
14497b908dcSmaxv static void ums_attach(device_t, device_t, void *);
14597b908dcSmaxv static void ums_childdet(device_t, device_t);
14697b908dcSmaxv static int ums_detach(device_t, int);
14797b908dcSmaxv static int ums_activate(device_t, enum devact);
1483b24a134Smrg 
1493624455eScube CFATTACH_DECL2_NEW(ums, sizeof(struct ums_softc), ums_match, ums_attach,
1500b1dce29Sdyoung     ums_detach, ums_activate, NULL, ums_childdet);
151e0c228aeSaugustss 
15297b908dcSmaxv static int
ums_match(device_t parent,cfdata_t match,void * aux)1533624455eScube ums_match(device_t parent, cfdata_t match, void *aux)
154deb00272Saugustss {
155935b0481Saugustss 	struct uhidev_attach_arg *uha = aux;
156935b0481Saugustss 	int size;
157deb00272Saugustss 	void *desc;
158deb00272Saugustss 
159108bad03Sjakllsch 	/*
160108bad03Sjakllsch 	 * Some (older) Griffin PowerMate knobs may masquerade as a
161108bad03Sjakllsch 	 * mouse, avoid treating them as such, they have only one axis.
162108bad03Sjakllsch 	 */
1634e8e6643Sskrll 	if (uha->uiaa->uiaa_vendor == USB_VENDOR_GRIFFIN &&
1644e8e6643Sskrll 	    uha->uiaa->uiaa_product == USB_PRODUCT_GRIFFIN_POWERMATE)
1654e8e6643Sskrll 		return UMATCH_NONE;
166108bad03Sjakllsch 
167935b0481Saugustss 	uhidev_get_report_desc(uha->parent, &desc, &size);
168935b0481Saugustss 	if (!hid_is_collection(desc, size, uha->reportid,
169d1721822Sjakllsch 			       HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) &&
170d1721822Sjakllsch 	    !hid_is_collection(desc, size, uha->reportid,
171748ee23bSchristos 			       HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER)) &&
172748ee23bSchristos 	    !hid_is_collection(desc, size, uha->reportid,
173*010a7954Sjakllsch 			       HID_USAGE2(HUP_DIGITIZERS, HUD_PEN)))
1744e8e6643Sskrll 		return UMATCH_NONE;
175deb00272Saugustss 
1764e8e6643Sskrll 	return UMATCH_IFACECLASS;
177deb00272Saugustss }
178deb00272Saugustss 
17997b908dcSmaxv static void
ums_attach(device_t parent,device_t self,void * aux)1803624455eScube ums_attach(device_t parent, device_t self, void *aux)
181deb00272Saugustss {
1823624455eScube 	struct ums_softc *sc = device_private(self);
183935b0481Saugustss 	struct uhidev_attach_arg *uha = aux;
184348742fbSjmcneill 	struct hid_data *d;
185348742fbSjmcneill 	struct hid_item item;
186c47914cbSjmcneill 	int size, error;
187deb00272Saugustss 	void *desc;
188404d04cdSbouyer 	uint32_t quirks;
189deb00272Saugustss 
190490542edSjmcneill 	aprint_naive("\n");
191490542edSjmcneill 
1920b6c2425Sriastradh 	sc->sc_hdev = uha->parent;
193f82ea250Sriastradh 	sc->sc_udev = uha->uiaa->uiaa_device;
194deb00272Saugustss 
195f82ea250Sriastradh 	quirks = usbd_get_quirks(sc->sc_udev)->uq_flags;
196aef39844Saugustss 	if (quirks & UQ_MS_REVZ)
197404d04cdSbouyer 		sc->sc_ms.flags |= HIDMS_REVZ;
198aef39844Saugustss 	if (quirks & UQ_SPUR_BUT_UP)
199404d04cdSbouyer 		sc->sc_ms.flags |= HIDMS_SPUR_BUT_UP;
2006e16e2a4Smacallan 	if (quirks & UQ_ALWAYS_ON)
2016e16e2a4Smacallan 		sc->sc_alwayson = true;
202748ee23bSchristos 
2038eeb638bSjmcneill 	if (!pmf_device_register(self, NULL, NULL))
2048eeb638bSjmcneill 		aprint_error_dev(self, "couldn't establish power handler\n");
2058eeb638bSjmcneill 
206404d04cdSbouyer 	uhidev_get_report_desc(uha->parent, &desc, &size);
207404d04cdSbouyer 
208404d04cdSbouyer 	if (!hidms_setup(self, &sc->sc_ms, uha->reportid, desc, size))
20949337c88Sdyoung 		return;
21048164bddSaugustss 
2114e8e6643Sskrll 	if (uha->uiaa->uiaa_vendor == USB_VENDOR_MICROSOFT) {
21276cb47a5Schristos 		int fixpos;
213432235c8Sjdolecek 		int woffset = 8;
2144e042dc0Srafal 		/*
21576cb47a5Schristos 		 * The Microsoft Wireless Laser Mouse 6000 v2.0 and the
21676cb47a5Schristos 		 * Microsoft Comfort Mouse 2.0 report a bad position for
21776cb47a5Schristos 		 * the wheel and wheel tilt controls -- should be in bytes
21876cb47a5Schristos 		 * 3 & 4 of the report. Fix this if necessary.
2194e042dc0Srafal 		 */
2204e8e6643Sskrll 		switch (uha->uiaa->uiaa_product) {
22176cb47a5Schristos 		case USB_PRODUCT_MICROSOFT_24GHZ_XCVR10:
22276cb47a5Schristos 		case USB_PRODUCT_MICROSOFT_24GHZ_XCVR20:
2230d202a45Smaya 		case USB_PRODUCT_MICROSOFT_NATURAL_6000:
22476cb47a5Schristos 			fixpos = 24;
22576cb47a5Schristos 			break;
226432235c8Sjdolecek 		case USB_PRODUCT_MICROSOFT_24GHZ_XCVR80:
227b8a50098Shgutch 		case USB_PRODUCT_MICROSOFT_24GHZ_XCVR90:
228432235c8Sjdolecek 			fixpos = 40;
229432235c8Sjdolecek 			woffset = sc->sc_ms.hidms_loc_z.size;
230432235c8Sjdolecek 			break;
23176cb47a5Schristos 		case USB_PRODUCT_MICROSOFT_CM6000:
23276cb47a5Schristos 			fixpos = 40;
23376cb47a5Schristos 			break;
23476cb47a5Schristos 		default:
23576cb47a5Schristos 			fixpos = 0;
23676cb47a5Schristos 			break;
23776cb47a5Schristos 		}
23876cb47a5Schristos 		if (fixpos) {
239404d04cdSbouyer 			if ((sc->sc_ms.flags & HIDMS_Z) &&
240404d04cdSbouyer 			    sc->sc_ms.hidms_loc_z.pos == 0)
241404d04cdSbouyer 				sc->sc_ms.hidms_loc_z.pos = fixpos;
242404d04cdSbouyer 			if ((sc->sc_ms.flags & HIDMS_W) &&
243404d04cdSbouyer 			    sc->sc_ms.hidms_loc_w.pos == 0)
244404d04cdSbouyer 				sc->sc_ms.hidms_loc_w.pos =
245432235c8Sjdolecek 				    sc->sc_ms.hidms_loc_z.pos + woffset;
2464e042dc0Srafal 		}
24776cb47a5Schristos 	}
248ae97a4cbSaugustss 
2492873e725Syhardy 	tpcalib_init(&sc->sc_ms.sc_tpcalib);
250348742fbSjmcneill 
251348742fbSjmcneill 	/* calibrate the pointer if it reports absolute events */
252348742fbSjmcneill 	if (sc->sc_ms.flags & HIDMS_ABS) {
253348742fbSjmcneill 		memset(&sc->sc_ms.sc_calibcoords, 0, sizeof(sc->sc_ms.sc_calibcoords));
254348742fbSjmcneill 		sc->sc_ms.sc_calibcoords.maxx = 0;
255348742fbSjmcneill 		sc->sc_ms.sc_calibcoords.maxy = 0;
256348742fbSjmcneill 		sc->sc_ms.sc_calibcoords.samplelen = WSMOUSE_CALIBCOORDS_RESET;
257348742fbSjmcneill 		d = hid_start_parse(desc, size, hid_input);
258348742fbSjmcneill 		if (d != NULL) {
259348742fbSjmcneill 			while (hid_get_item(d, &item)) {
260348742fbSjmcneill 				if (item.kind != hid_input
261348742fbSjmcneill 				    || HID_GET_USAGE_PAGE(item.usage) != HUP_GENERIC_DESKTOP
2620b6c2425Sriastradh 				    || item.report_ID != uha->reportid)
263348742fbSjmcneill 					continue;
264348742fbSjmcneill 				if (HID_GET_USAGE(item.usage) == HUG_X) {
265348742fbSjmcneill 					sc->sc_ms.sc_calibcoords.minx = item.logical_minimum;
266348742fbSjmcneill 					sc->sc_ms.sc_calibcoords.maxx = item.logical_maximum;
267348742fbSjmcneill 				}
268348742fbSjmcneill 				if (HID_GET_USAGE(item.usage) == HUG_Y) {
269348742fbSjmcneill 					sc->sc_ms.sc_calibcoords.miny = item.logical_minimum;
270348742fbSjmcneill 					sc->sc_ms.sc_calibcoords.maxy = item.logical_maximum;
271348742fbSjmcneill 				}
272348742fbSjmcneill 			}
273348742fbSjmcneill 			hid_end_parse(d);
274348742fbSjmcneill 		}
275348742fbSjmcneill         	tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
276348742fbSjmcneill         	    (void *)&sc->sc_ms.sc_calibcoords, 0, 0);
277348742fbSjmcneill 	}
278348742fbSjmcneill 
279404d04cdSbouyer 	hidms_attach(self, &sc->sc_ms, &ums_accessops);
280c47914cbSjmcneill 
281c47914cbSjmcneill 	if (sc->sc_alwayson) {
2820b6c2425Sriastradh 		error = uhidev_open(sc->sc_hdev, &ums_intr, sc);
283c47914cbSjmcneill 		if (error != 0) {
284c47914cbSjmcneill 			aprint_error_dev(self,
285c47914cbSjmcneill 			    "WARNING: couldn't open always-on device\n");
286c47914cbSjmcneill 			sc->sc_alwayson = false;
287c47914cbSjmcneill 		}
288c47914cbSjmcneill 	}
289e0c228aeSaugustss }
290e0c228aeSaugustss 
29197b908dcSmaxv static int
ums_activate(device_t self,enum devact act)29249337c88Sdyoung ums_activate(device_t self, enum devact act)
293e0c228aeSaugustss {
2943624455eScube 	struct ums_softc *sc = device_private(self);
2951f266179Saugustss 
2961f266179Saugustss 	switch (act) {
2971f266179Saugustss 	case DVACT_DEACTIVATE:
2981f266179Saugustss 		sc->sc_dying = 1;
299f611b6f9Sdyoung 		return 0;
300f611b6f9Sdyoung 	default:
301f611b6f9Sdyoung 		return EOPNOTSUPP;
3021f266179Saugustss 	}
303e0c228aeSaugustss }
304e0c228aeSaugustss 
30597b908dcSmaxv static void
ums_childdet(device_t self,device_t child)3060b1dce29Sdyoung ums_childdet(device_t self, device_t child)
307deb00272Saugustss {
3080b1dce29Sdyoung 	struct ums_softc *sc = device_private(self);
3090b1dce29Sdyoung 
310404d04cdSbouyer 	KASSERT(sc->sc_ms.hidms_wsmousedev == child);
311404d04cdSbouyer 	sc->sc_ms.hidms_wsmousedev = NULL;
3120b1dce29Sdyoung }
3130b1dce29Sdyoung 
31497b908dcSmaxv static int
ums_detach(device_t self,int flags)3150b1dce29Sdyoung ums_detach(device_t self, int flags)
3160b1dce29Sdyoung {
3170b1dce29Sdyoung 	struct ums_softc *sc = device_private(self);
318b177c7a3Saugustss 	int rv = 0;
3196aff95b0Saugustss 
320d6a34bb1Smrg 	UMSHIST_FUNC();
321d6a34bb1Smrg 	UMSHIST_CALLARGS("ums_detach: sc=%qd flags=%qd\n",
322d6a34bb1Smrg 	    (uintptr_t)sc, flags, 0, 0);
32303a522d3Saugustss 
324c47914cbSjmcneill 	if (sc->sc_alwayson)
3250b6c2425Sriastradh 		uhidev_close(sc->sc_hdev);
326c47914cbSjmcneill 
327a7fd78b8Saugustss 	/* No need to do reference counting of ums, wsmouse has all the goo. */
328404d04cdSbouyer 	if (sc->sc_ms.hidms_wsmousedev != NULL)
329404d04cdSbouyer 		rv = config_detach(sc->sc_ms.hidms_wsmousedev, flags);
3304b93911bSaugustss 
3318eeb638bSjmcneill 	pmf_device_deregister(self);
3328eeb638bSjmcneill 
3334e8e6643Sskrll 	return rv;
334deb00272Saugustss }
335deb00272Saugustss 
33697b908dcSmaxv Static void
ums_intr(void * cookie,void * ibuf,u_int len)3370b6c2425Sriastradh ums_intr(void *cookie, void *ibuf, u_int len)
338deb00272Saugustss {
3390b6c2425Sriastradh 	struct ums_softc *sc = cookie;
340c47914cbSjmcneill 
341c47914cbSjmcneill 	if (sc->sc_enabled)
342404d04cdSbouyer 		hidms_intr(&sc->sc_ms, ibuf, len);
343deb00272Saugustss }
34467ec8587Saugustss 
345fc72e72dSaugustss Static int
ums_enable(void * v)346ed9486e1Saugustss ums_enable(void *v)
34767ec8587Saugustss {
34867ec8587Saugustss 	struct ums_softc *sc = v;
349c47914cbSjmcneill 	int error = 0;
350e0c228aeSaugustss 
351d6a34bb1Smrg 	UMSHIST_FUNC(); UMSHIST_CALLARGS("sc=%jx\n", (uintptr_t)sc, 0, 0, 0);
3521f266179Saugustss 
3531f266179Saugustss 	if (sc->sc_dying)
3544e8e6643Sskrll 		return EIO;
3551f266179Saugustss 
35667ec8587Saugustss 	if (sc->sc_enabled)
3574e8e6643Sskrll 		return EBUSY;
35867ec8587Saugustss 
35967ec8587Saugustss 	sc->sc_enabled = 1;
360404d04cdSbouyer 	sc->sc_ms.hidms_buttons = 0;
36167ec8587Saugustss 
362c47914cbSjmcneill 	if (!sc->sc_alwayson) {
3630b6c2425Sriastradh 		error = uhidev_open(sc->sc_hdev, &ums_intr, sc);
36480628d62Smlelstv 		if (error)
36580628d62Smlelstv 			sc->sc_enabled = 0;
366c47914cbSjmcneill 	}
36780628d62Smlelstv 
36880628d62Smlelstv 	return error;
36967ec8587Saugustss }
37067ec8587Saugustss 
371fc72e72dSaugustss Static void
ums_disable(void * v)372ed9486e1Saugustss ums_disable(void *v)
37367ec8587Saugustss {
37467ec8587Saugustss 	struct ums_softc *sc = v;
37567ec8587Saugustss 
376d6a34bb1Smrg 	UMSHIST_FUNC(); UMSHIST_CALLARGS("sc=%jx\n", (uintptr_t)sc, 0, 0, 0);
377d6a34bb1Smrg 
378b177c7a3Saugustss #ifdef DIAGNOSTIC
379b177c7a3Saugustss 	if (!sc->sc_enabled) {
380b177c7a3Saugustss 		printf("ums_disable: not enabled\n");
381b177c7a3Saugustss 		return;
382b177c7a3Saugustss 	}
383b177c7a3Saugustss #endif
384b177c7a3Saugustss 
38580628d62Smlelstv 	if (sc->sc_enabled) {
38667ec8587Saugustss 		sc->sc_enabled = 0;
387c47914cbSjmcneill 		if (!sc->sc_alwayson)
3880b6c2425Sriastradh 			uhidev_close(sc->sc_hdev);
38967ec8587Saugustss 	}
39080628d62Smlelstv }
39167ec8587Saugustss 
392fc72e72dSaugustss Static int
ums_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)39353524e44Schristos ums_ioctl(void *v, u_long cmd, void *data, int flag,
394348742fbSjmcneill     struct lwp *l)
39567ec8587Saugustss 
396e0c228aeSaugustss {
3978a44b5e0Smbalmer 	struct ums_softc *sc = v;
398348742fbSjmcneill 	int error;
399348742fbSjmcneill 
400348742fbSjmcneill 	if (sc->sc_ms.flags & HIDMS_ABS) {
401348742fbSjmcneill 		error = tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, cmd, data,
402348742fbSjmcneill 		    flag, l);
403348742fbSjmcneill 		if (error != EPASSTHROUGH)
404348742fbSjmcneill 			return error;
405348742fbSjmcneill 	}
4068a44b5e0Smbalmer 
40767ec8587Saugustss 	switch (cmd) {
40867ec8587Saugustss 	case WSMOUSEIO_GTYPE:
409404d04cdSbouyer 		if (sc->sc_ms.flags & HIDMS_ABS)
4108a44b5e0Smbalmer 			*(u_int *)data = WSMOUSE_TYPE_TPANEL;
4118a44b5e0Smbalmer 		else
4120ae03102Saugustss 			*(u_int *)data = WSMOUSE_TYPE_USB;
4134e8e6643Sskrll 		return 0;
41467ec8587Saugustss 	}
415e0c228aeSaugustss 
4164e8e6643Sskrll 	return EPASSTHROUGH;
41767ec8587Saugustss }
418