xref: /openbsd-src/sys/dev/usb/ums.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: ums.c,v 1.35 2011/07/03 15:47:17 matthew Exp $ */
2 /*	$NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $	*/
3 
4 /*
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (lennart@augustsson.net) at
10  * Carlstedt Research & Technology.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/device.h>
42 #include <sys/ioctl.h>
43 
44 #include <dev/usb/usb.h>
45 #include <dev/usb/usbhid.h>
46 
47 #include <dev/usb/usbdi.h>
48 #include <dev/usb/usbdi_util.h>
49 #include <dev/usb/usbdevs.h>
50 #include <dev/usb/usb_quirks.h>
51 #include <dev/usb/uhidev.h>
52 #include <dev/usb/hid.h>
53 
54 #include <dev/wscons/wsconsio.h>
55 #include <dev/wscons/wsmousevar.h>
56 
57 #include <dev/usb/hidmsvar.h>
58 
59 struct ums_softc {
60 	struct uhidev	sc_hdev;
61 	struct hidms	sc_ms;
62 	char		sc_dying;
63 };
64 
65 void ums_intr(struct uhidev *addr, void *ibuf, u_int len);
66 
67 int	ums_enable(void *);
68 void	ums_disable(void *);
69 int	ums_ioctl(void *, u_long, caddr_t, int, struct proc *);
70 
71 const struct wsmouse_accessops ums_accessops = {
72 	ums_enable,
73 	ums_ioctl,
74 	ums_disable,
75 };
76 
77 int ums_match(struct device *, void *, void *);
78 void ums_attach(struct device *, struct device *, void *);
79 int ums_detach(struct device *, int);
80 int ums_activate(struct device *, int);
81 
82 struct cfdriver ums_cd = {
83 	NULL, "ums", DV_DULL
84 };
85 
86 const struct cfattach ums_ca = {
87 	sizeof(struct ums_softc),
88 	ums_match,
89 	ums_attach,
90 	ums_detach,
91 	ums_activate,
92 };
93 
94 int
95 ums_match(struct device *parent, void *match, void *aux)
96 {
97 	struct usb_attach_arg *uaa = aux;
98 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
99 	int size;
100 	void *desc;
101 
102 	uhidev_get_report_desc(uha->parent, &desc, &size);
103 	if (!hid_is_collection(desc, size, uha->reportid,
104 			       HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
105 		return (UMATCH_NONE);
106 
107 	return (UMATCH_IFACECLASS);
108 }
109 
110 void
111 ums_attach(struct device *parent, struct device *self, void *aux)
112 {
113 	struct ums_softc *sc = (struct ums_softc *)self;
114 	struct hidms *ms = &sc->sc_ms;
115 	struct usb_attach_arg *uaa = aux;
116 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
117 	int size, repid;
118 	void *desc;
119 	u_int32_t quirks;
120 
121 	sc->sc_hdev.sc_intr = ums_intr;
122 	sc->sc_hdev.sc_parent = uha->parent;
123 	sc->sc_hdev.sc_report_id = uha->reportid;
124 
125 	quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags;
126 	uhidev_get_report_desc(uha->parent, &desc, &size);
127 	repid = uha->reportid;
128 	sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
129 	sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
130 	sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
131 
132 	if (hidms_setup(self, ms, quirks, uha->reportid, desc, size) != 0)
133 		return;
134 
135 	/*
136 	 * The Microsoft Wireless Notebook Optical Mouse 3000 Model 1049 has
137 	 * five Report IDs: 19, 23, 24, 17, 18 (in the order they appear in
138 	 * report descriptor), it seems that report 17 contains the necessary
139 	 * mouse information (3-buttons, X, Y, wheel) so we specify it
140 	 * manually.
141 	 */
142 	if (uaa->vendor == USB_VENDOR_MICROSOFT &&
143 	    uaa->product == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3) {
144 		ms->sc_flags = HIDMS_Z;
145 		ms->sc_num_buttons = 3;
146 		/* XXX change sc_hdev isize to 5? */
147 		ms->sc_loc_x.pos = 8;
148 		ms->sc_loc_y.pos = 16;
149 		ms->sc_loc_z.pos = 24;
150 		ms->sc_loc_btn[0].pos = 0;
151 		ms->sc_loc_btn[1].pos = 1;
152 		ms->sc_loc_btn[2].pos = 2;
153 	}
154 
155 	hidms_attach(ms, &ums_accessops);
156 }
157 
158 int
159 ums_activate(struct device *self, int act)
160 {
161 	struct ums_softc *sc = (struct ums_softc *)self;
162 	struct hidms *ms = &sc->sc_ms;
163 	int rv = 0;
164 
165 	switch (act) {
166 	case DVACT_DEACTIVATE:
167 		if (ms->sc_wsmousedev != NULL)
168 			rv = config_deactivate(ms->sc_wsmousedev);
169 		sc->sc_dying = 1;
170 		break;
171 	}
172 	return (rv);
173 }
174 
175 int
176 ums_detach(struct device *self, int flags)
177 {
178 	struct ums_softc *sc = (struct ums_softc *)self;
179 	struct hidms *ms = &sc->sc_ms;
180 
181 	return hidms_detach(ms, flags);
182 }
183 
184 void
185 ums_intr(struct uhidev *addr, void *buf, u_int len)
186 {
187 	struct ums_softc *sc = (struct ums_softc *)addr;
188 	struct hidms *ms = &sc->sc_ms;
189 
190 	if (ms->sc_enabled != 0)
191 		hidms_input(ms, (uint8_t *)buf, len);
192 }
193 
194 int
195 ums_enable(void *v)
196 {
197 	struct ums_softc *sc = v;
198 	struct hidms *ms = &sc->sc_ms;
199 	int rv;
200 
201 	if (sc->sc_dying)
202 		return EIO;
203 
204 	if ((rv = hidms_enable(ms)) != 0)
205 		return rv;
206 
207 	return uhidev_open(&sc->sc_hdev);
208 }
209 
210 void
211 ums_disable(void *v)
212 {
213 	struct ums_softc *sc = v;
214 	struct hidms *ms = &sc->sc_ms;
215 
216 	hidms_disable(ms);
217 	uhidev_close(&sc->sc_hdev);
218 }
219 
220 int
221 ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
222 {
223 	struct ums_softc *sc = v;
224 	struct hidms *ms = &sc->sc_ms;
225 	int rc;
226 
227 	rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p);
228 	if (rc != -1)
229 		return rc;
230 	rc = hidms_ioctl(ms, cmd, data, flag, p);
231 	if (rc != -1)
232 		return rc;
233 
234 	switch (cmd) {
235 	case WSMOUSEIO_GTYPE:
236 		*(u_int *)data = WSMOUSE_TYPE_USB;
237 		return 0;
238 	default:
239 		return -1;
240 	}
241 }
242