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