xref: /titanic_41/usr/src/uts/common/io/usb/clients/usbinput/usbwcm/usbwcm.c (revision 692d834dec3070f47507bc0f44d3d92620b49cfa)
1 /*
2  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2007, 2008 Bartosz Fabianowski <freebsd@chillt.de>
8  * All rights reserved.
9  *
10  * Financed by the "Irish Research Council for Science, Engineering and
11  * Technology: funded by the National Development Plan"
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions, and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
26  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*
36  * Copyright (c) 1998 The NetBSD Foundation, Inc.
37  * All rights reserved.
38  *
39  * This code is derived from software contributed to The NetBSD Foundation
40  * by Lennart Augustsson (lennart@augustsson.net) at
41  * Carlstedt Research & Technology.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *        This product includes software developed by the NetBSD
54  *        Foundation, Inc. and its contributors.
55  * 4. Neither the name of The NetBSD Foundation nor the names of its
56  *    contributors may be used to endorse or promote products derived
57  *    from this software without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
60  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
61  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
62  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
63  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
64  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
65  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
66  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
67  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
68  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
69  * POSSIBILITY OF SUCH DAMAGE.
70  */
71 
72 #include <sys/stropts.h>
73 #include <sys/strsun.h>
74 #include <sys/termios.h>
75 #include <sys/termio.h>
76 #include <sys/strtty.h>
77 #include <sys/systm.h>
78 
79 #include <sys/usb/usba/usbai_version.h>
80 #include <sys/usb/usba.h>
81 #include <sys/usb/usba/usbai_private.h>
82 #include <sys/usb/clients/hid/hid.h>
83 #include <sys/usb/clients/usbinput/usbwcm/usbwcm.h>
84 
85 /* debugging information */
86 uint_t	usbwcm_errmask = (uint_t)PRINT_MASK_ALL;
87 uint_t	usbwcm_errlevel = USB_LOG_L2;
88 static usb_log_handle_t usbwcm_log_handle;
89 
90 static void
uwacom_event(usbwcm_state_t * usbwcmp,uint_t type,uint_t idx,int val)91 uwacom_event(usbwcm_state_t *usbwcmp, uint_t type, uint_t idx, int val)
92 {
93 	struct uwacom_softc	*sc = &usbwcmp->usbwcm_softc;
94 	mblk_t			*mp;
95 
96 	switch (type) {
97 	case EVT_SYN:
98 		if (sc->sc_sync)
99 			return;
100 		break;
101 
102 	case EVT_BTN:
103 		if (sc->sc_btn[idx] == val)
104 			return;
105 
106 		sc->sc_btn[idx] = val;
107 		break;
108 
109 	case EVT_ABS:
110 		if (sc->sc_abs[idx].fuzz) {
111 			int dist = abs(val - sc->sc_abs[idx].value);
112 
113 			if (dist < sc->sc_abs[idx].fuzz >> 1) {
114 				return;
115 			} else if (dist < sc->sc_abs[idx].fuzz) {
116 				val = (7 * sc->sc_abs[idx].value + val) >> 3;
117 			} else if (dist < sc->sc_abs[idx].fuzz << 1) {
118 				val = (sc->sc_abs[idx].value + val) >> 1;
119 			}
120 		}
121 		if (sc->sc_abs[idx].value == val) {
122 			return;
123 		}
124 
125 		sc->sc_abs[idx].value = val;
126 		break;
127 
128 	case EVT_REL:
129 		if (!val)
130 			return;
131 		break;
132 
133 	case EVT_MSC:
134 		break;
135 
136 	default:
137 		return;
138 	}
139 
140 	if ((mp = allocb(sizeof (struct event_input), BPRI_HI)) != NULL) {
141 		struct event_input *ev = (struct event_input *)mp->b_wptr;
142 
143 		ev->type = (uint16_t)type;
144 		ev->code = (uint16_t)idx;
145 		ev->value = (int32_t)val;
146 		uniqtime32(&ev->time);
147 
148 		mp->b_wptr += sizeof (struct event_input);
149 		putnext(usbwcmp->usbwcm_rq, mp);
150 	} else {
151 		return;
152 	}
153 
154 	sc->sc_sync = (type == EVT_SYN);
155 }
156 
157 static void
uwacom_pos_events_graphire(usbwcm_state_t * usbwcmp,int x,int y)158 uwacom_pos_events_graphire(usbwcm_state_t *usbwcmp, int x, int y)
159 {
160 	uwacom_event(usbwcmp, EVT_ABS, ABS_X, x);
161 	uwacom_event(usbwcmp, EVT_ABS, ABS_Y, y);
162 }
163 
164 static void
uwacom_pen_events_graphire(usbwcm_state_t * usbwcmp,int prs,int stl1,int stl2)165 uwacom_pen_events_graphire(usbwcm_state_t *usbwcmp, int prs, int stl1, int stl2)
166 {
167 	uwacom_event(usbwcmp, EVT_ABS, ABS_PRESSURE, prs);
168 	uwacom_event(usbwcmp, EVT_BTN, BTN_TIP, prs);
169 	uwacom_event(usbwcmp, EVT_BTN, BTN_STYLUS_1, stl1);
170 	uwacom_event(usbwcmp, EVT_BTN, BTN_STYLUS_2, stl2);
171 }
172 
173 static void
uwacom_mouse_events_graphire(usbwcm_state_t * usbwcmp,int left,int middle,int right,int wheel,int distance)174 uwacom_mouse_events_graphire(usbwcm_state_t *usbwcmp, int left, int middle,
175     int right, int wheel, int distance)
176 {
177 	uwacom_event(usbwcmp, EVT_BTN, BTN_LEFT, left);
178 	uwacom_event(usbwcmp, EVT_BTN, BTN_MIDDLE, middle);
179 	uwacom_event(usbwcmp, EVT_BTN, BTN_RIGHT, right);
180 	uwacom_event(usbwcmp, EVT_REL, REL_WHEEL, wheel);
181 	uwacom_event(usbwcmp, EVT_ABS, ABS_DISTANCE, distance);
182 }
183 
184 static void
uwacom_tool_events_graphire(usbwcm_state_t * usbwcmp,int idx,int proximity)185 uwacom_tool_events_graphire(usbwcm_state_t *usbwcmp, int idx, int proximity)
186 {
187 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
188 
189 	uwacom_event(usbwcmp, EVT_BTN, sc->sc_tool[idx], proximity);
190 	uwacom_event(usbwcmp, EVT_ABS, ABS_MISC, sc->sc_tool_id[idx]);
191 	if (sc->sc_serial[idx]) {
192 		uwacom_event(usbwcmp, EVT_MSC, MSC_SERIAL, sc->sc_serial[idx]);
193 	}
194 
195 	uwacom_event(usbwcmp, EVT_SYN, SYN_REPORT, 0);
196 }
197 
198 static void
uwacom_pad_events_graphire4(usbwcm_state_t * usbwcmp,int b0,int b1,int b4,int b5,int rel,int abs)199 uwacom_pad_events_graphire4(usbwcm_state_t *usbwcmp, int b0, int b1, int b4,
200     int b5, int rel, int abs)
201 {
202 	uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_0, b0);
203 	uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_1, b1);
204 	uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_4, b4);
205 	uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_5, b5);
206 	uwacom_event(usbwcmp, EVT_REL, REL_WHEEL, rel);
207 	uwacom_event(usbwcmp, EVT_ABS, ABS_WHEEL, abs);
208 	uwacom_tool_events_graphire(usbwcmp, 1, b0 | b1 | b4 | b5 | rel | abs);
209 }
210 
211 static void
usbwcm_input_graphire(usbwcm_state_t * usbwcmp,mblk_t * mp)212 usbwcm_input_graphire(usbwcm_state_t *usbwcmp, mblk_t *mp)
213 {
214 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
215 	uint8_t *packet = mp->b_rptr;
216 
217 	if (PACKET_BITS(0, 0, 8) != 0x02) {
218 		USB_DPRINTF_L1(PRINT_MASK_ALL, usbwcm_log_handle,
219 		    "unknown report type %02x received\n",
220 		    PACKET_BITS(0, 0, 8));
221 		return;
222 	}
223 
224 	/* Tool in proximity */
225 	if (PACKET_BIT(1, 7)) {
226 		uwacom_pos_events_graphire(usbwcmp,
227 		    (PACKET_BITS(3, 0, 8) << 8) | PACKET_BITS(2, 0, 8),
228 		    (PACKET_BITS(5, 0, 8) << 8) | PACKET_BITS(4, 0, 8));
229 
230 		if (!PACKET_BIT(1, 6)) {
231 			if (!PACKET_BIT(1, 5)) {
232 				sc->sc_tool[0] = BTN_TOOL_PEN;
233 				sc->sc_tool_id[0] = TOOL_ID_PEN;
234 			} else {
235 				sc->sc_tool[0] = BTN_TOOL_ERASER;
236 				sc->sc_tool_id[0] = TOOL_ID_ERASER;
237 			}
238 
239 			uwacom_pen_events_graphire(usbwcmp,
240 			    (PACKET_BIT(7, 0) << 8) | PACKET_BITS(6, 0, 8),
241 			    PACKET_BIT(1, 1), PACKET_BIT(1, 2));
242 		} else {
243 			int wheel, distance;
244 
245 			if (sc->sc_type->protocol == GRAPHIRE) {
246 				wheel = (PACKET_BIT(1, 5) ?
247 				    0 : -(int8_t)PACKET_BITS(6, 0, 8));
248 				distance = PACKET_BITS(7, 0, 6);
249 			} else {
250 				wheel = (PACKET_BIT(7, 2) << 2) -
251 				    PACKET_BITS(7, 0, 2);
252 				distance = PACKET_BITS(6, 0, 6);
253 			}
254 
255 			sc->sc_tool[0] = BTN_TOOL_MOUSE;
256 			sc->sc_tool_id[0] = TOOL_ID_MOUSE;
257 
258 			uwacom_mouse_events_graphire(usbwcmp, PACKET_BIT(1, 0),
259 			    PACKET_BIT(1, 2), PACKET_BIT(1, 1), wheel,
260 			    distance);
261 		}
262 
263 		uwacom_tool_events_graphire(usbwcmp, 0, 1);
264 
265 		/* Tool leaving proximity */
266 	} else if (sc->sc_tool_id[0]) {
267 		uwacom_pos_events_graphire(usbwcmp, 0, 0);
268 
269 		if (sc->sc_tool[0] == BTN_TOOL_MOUSE)
270 			uwacom_mouse_events_graphire(usbwcmp, 0, 0, 0, 0, 0);
271 		else
272 			uwacom_pen_events_graphire(usbwcmp, 0, 0, 0);
273 
274 		sc->sc_tool_id[0] = 0;
275 		uwacom_tool_events_graphire(usbwcmp, 0, 0);
276 	}
277 
278 	/* Finger on pad: Graphire4 */
279 	if ((sc->sc_type->protocol == GRAPHIRE4) && PACKET_BITS(7, 3, 5)) {
280 		sc->sc_tool_id[1] = TOOL_ID_PAD;
281 		uwacom_pad_events_graphire4(usbwcmp, PACKET_BIT(7, 6), 0,
282 		    PACKET_BIT(7, 7), 0,
283 		    PACKET_BITS(7, 3, 2) - (PACKET_BIT(7, 5) << 2), 0);
284 
285 	/* Finger on pad: MyOffice */
286 	} else if ((sc->sc_type->protocol == MYOFFICE) &&
287 	    (PACKET_BITS(7, 3, 4) || PACKET_BITS(8, 0, 8))) {
288 		sc->sc_tool_id[1] = TOOL_ID_PAD;
289 		uwacom_pad_events_graphire4(usbwcmp, PACKET_BIT(7, 3),
290 		    PACKET_BIT(7, 4), PACKET_BIT(7, 5), PACKET_BIT(7, 6), 0,
291 		    PACKET_BITS(8, 0, 7));
292 
293 	/* Finger leaving pad */
294 	} else if (sc->sc_tool_id[1]) {
295 		sc->sc_tool_id[1] = 0;
296 		uwacom_pad_events_graphire4(usbwcmp, 0, 0, 0, 0, 0, 0);
297 	}
298 }
299 
300 static void
uwacom_pos_events_intuos(usbwcm_state_t * usbwcmp,int x,int y,int distance)301 uwacom_pos_events_intuos(usbwcm_state_t *usbwcmp, int x, int y, int distance)
302 {
303 	uwacom_event(usbwcmp, EVT_ABS, ABS_X, x);
304 	uwacom_event(usbwcmp, EVT_ABS, ABS_Y, y);
305 	uwacom_event(usbwcmp, EVT_ABS, ABS_DISTANCE, distance);
306 }
307 
308 static void
uwacom_pen_events_intuos(usbwcm_state_t * usbwcmp,uint8_t * packet)309 uwacom_pen_events_intuos(usbwcm_state_t *usbwcmp, uint8_t *packet)
310 {
311 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
312 	int press, tilt_x, tilt_y, stl1, stl2;
313 
314 	switch (sc->sc_type->protocol) {
315 	case INTUOS4S:
316 	case INTUOS4L:
317 		press = PACKET_BITS(7, 6, 10) << 1 | PACKET_BIT(1, 0);
318 		break;
319 	default:
320 		press = PACKET_BITS(7, 6, 10);
321 		break;
322 	}
323 
324 	tilt_x = PACKET_BITS(8, 7, 7);
325 	tilt_y = PACKET_BITS(8, 0, 7);
326 	stl1 = PACKET_BIT(1, 1);
327 	stl2 = PACKET_BIT(1, 2);
328 
329 	uwacom_event(usbwcmp, EVT_ABS, ABS_PRESSURE, press);
330 	uwacom_event(usbwcmp, EVT_ABS, ABS_TILT_X, tilt_x);
331 	uwacom_event(usbwcmp, EVT_ABS, ABS_TILT_Y, tilt_y);
332 	uwacom_event(usbwcmp, EVT_BTN, BTN_TIP, press);
333 	uwacom_event(usbwcmp, EVT_BTN, BTN_STYLUS_1, stl1);
334 	uwacom_event(usbwcmp, EVT_BTN, BTN_STYLUS_2, stl2);
335 }
336 
337 static void
uwacom_mouse_events_intuos(usbwcm_state_t * usbwcmp,uint8_t * packet)338 uwacom_mouse_events_intuos(usbwcm_state_t *usbwcmp, uint8_t *packet)
339 {
340 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
341 	int left, middle, right, extra, side, wheel;
342 
343 	switch (sc->sc_type->protocol) {
344 	case INTUOS4S:
345 	case INTUOS4L:
346 		left = PACKET_BIT(6, 0);
347 		middle = PACKET_BIT(6, 1);
348 		right = PACKET_BIT(6, 2);
349 		side = PACKET_BIT(6, 3);
350 		extra = PACKET_BIT(6, 4);
351 		wheel = PACKET_BIT(7, 7) - PACKET_BIT(7, 6);
352 		break;
353 
354 	default:
355 		left = PACKET_BIT(8, 2);
356 		middle = PACKET_BIT(8, 3);
357 		right = PACKET_BIT(8, 4);
358 		extra = PACKET_BIT(8, 5);
359 		side = PACKET_BIT(8, 6);
360 		wheel = PACKET_BIT(8, 0) - PACKET_BIT(8, 1);
361 		break;
362 	}
363 
364 	uwacom_event(usbwcmp, EVT_BTN, BTN_LEFT, left);
365 	uwacom_event(usbwcmp, EVT_BTN, BTN_MIDDLE, middle);
366 	uwacom_event(usbwcmp, EVT_BTN, BTN_RIGHT, right);
367 	uwacom_event(usbwcmp, EVT_BTN, BTN_EXTRA, extra);
368 	uwacom_event(usbwcmp, EVT_BTN, BTN_SIDE, side);
369 	uwacom_event(usbwcmp, EVT_REL, REL_WHEEL, wheel);
370 }
371 
372 static void
uwacom_tool_events_intuos(usbwcm_state_t * usbwcmp,int idx,int proximity)373 uwacom_tool_events_intuos(usbwcm_state_t *usbwcmp, int idx, int proximity)
374 {
375 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
376 
377 	uwacom_event(usbwcmp, EVT_BTN, sc->sc_tool[idx], proximity);
378 	uwacom_event(usbwcmp, EVT_ABS, ABS_MISC, sc->sc_tool_id[idx]);
379 	uwacom_event(usbwcmp, EVT_MSC, MSC_SERIAL, sc->sc_serial[idx]);
380 	uwacom_event(usbwcmp, EVT_SYN, SYN_REPORT, 0);
381 }
382 
383 static void
uwacom_pad_events_intuos(usbwcm_state_t * usbwcmp,uint8_t * packet)384 uwacom_pad_events_intuos(usbwcm_state_t *usbwcmp, uint8_t *packet)
385 {
386 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
387 	int b0, b1, b2, b3, b4, b5, b6, b7;
388 	int rx, ry, prox;
389 	int b8, whl, rot;
390 
391 	switch (sc->sc_type->protocol) {
392 	case INTUOS4L:
393 		b7 = PACKET_BIT(3, 6);
394 		b8 = PACKET_BIT(3, 7);
395 
396 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_7, b7);
397 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_8, b8);
398 	/*FALLTHRU*/
399 	case INTUOS4S:
400 		b0 = PACKET_BIT(2, 0);
401 		b1 = PACKET_BIT(3, 0);
402 		b2 = PACKET_BIT(3, 1);
403 		b3 = PACKET_BIT(3, 2);
404 		b4 = PACKET_BIT(3, 3);
405 		b5 = PACKET_BIT(3, 4);
406 		b6 = PACKET_BIT(3, 5);
407 
408 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_0, b0);
409 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_1, b1);
410 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_2, b2);
411 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_3, b3);
412 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_4, b4);
413 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_5, b5);
414 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_6, b6);
415 
416 		whl = PACKET_BIT(1, 7);
417 		if (whl) {
418 			rot = PACKET_BITS(1, 0, 7);
419 			uwacom_event(usbwcmp, EVT_ABS, ABS_WHEEL, rot);
420 		}
421 
422 		prox = b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8 | whl;
423 		uwacom_tool_events_intuos(usbwcmp, 1, prox);
424 
425 		break;
426 
427 	default:
428 		b0 = PACKET_BIT(5, 0);
429 		b1 = PACKET_BIT(5, 1);
430 		b2 = PACKET_BIT(5, 2);
431 		b3 = PACKET_BIT(5, 3);
432 		b4 = PACKET_BIT(6, 0);
433 		b5 = PACKET_BIT(6, 1);
434 		b6 = PACKET_BIT(6, 2);
435 		b7 = PACKET_BIT(6, 3);
436 		rx = PACKET_BITS(2, 0, 13);
437 		ry = PACKET_BITS(4, 0, 13);
438 
439 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_0, b0);
440 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_1, b1);
441 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_2, b2);
442 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_3, b3);
443 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_4, b4);
444 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_5, b5);
445 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_6, b6);
446 		uwacom_event(usbwcmp, EVT_BTN, BTN_MISC_7, b7);
447 		uwacom_event(usbwcmp, EVT_ABS, ABS_RX, rx);
448 		uwacom_event(usbwcmp, EVT_ABS, ABS_RY, ry);
449 
450 		prox = b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 | rx | ry;
451 		uwacom_tool_events_intuos(usbwcmp, 1, prox);
452 
453 		break;
454 	}
455 }
456 
457 static void
usbwcm_input_intuos(usbwcm_state_t * usbwcmp,mblk_t * mp)458 usbwcm_input_intuos(usbwcm_state_t *usbwcmp, mblk_t *mp)
459 {
460 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
461 	uint8_t *packet = mp->b_rptr;
462 
463 	switch (PACKET_BITS(0, 0, 8)) {
464 	case 0x02:
465 		switch (PACKET_BITS(1, 5, 2)) {
466 		/* Tool entering proximity */
467 		case 0x2:
468 			sc->sc_tool_id[0] = PACKET_BITS(3, 4, 12);
469 			sc->sc_serial[0] =
470 			    (PACKET_BIT(1, 1) ? PACKET_BITS(7, 4, 32) : 0);
471 
472 			switch (sc->sc_tool_id[0]) {
473 			case 0x802: /* Intuos4 Grip Pen */
474 			case 0x804: /* Intuos4 Art Marker */
475 			case 0x823: /* Intuos3 Grip Pen */
476 			case 0x885: /* Intuos3 Art Marker */
477 				sc->sc_tool[0] = BTN_TOOL_PEN;
478 				break;
479 			case 0x80a: /* Intuos4 Grip Pen eraser */
480 			case 0x82b: /* Intuos3 Grip Pen eraser */
481 				sc->sc_tool[0] = BTN_TOOL_ERASER;
482 				break;
483 			case 0x017: /* Intuos3 2D mouse */
484 			case 0x806: /* Intuos4 2D mouse */
485 				sc->sc_tool[0] = BTN_TOOL_MOUSE;
486 				break;
487 			default:
488 				USB_DPRINTF_L1(PRINT_MASK_ALL,
489 				    usbwcm_log_handle,
490 				    "unknown tool ID %03x seen\n",
491 				    sc->sc_tool_id[0]);
492 				sc->sc_tool[0] = BTN_TOOL_PEN;
493 			}
494 			break;
495 
496 		/* Tool leaving proximity */
497 		case 0x0:
498 			uwacom_pos_events_intuos(usbwcmp, 0, 0, 0);
499 
500 			if (sc->sc_tool[0] == BTN_TOOL_MOUSE)
501 				uwacom_mouse_events_intuos(usbwcmp, packet);
502 			else
503 				uwacom_pen_events_intuos(usbwcmp, packet);
504 
505 			sc->sc_tool_id[0] = 0;
506 			uwacom_tool_events_intuos(usbwcmp, 0, 0);
507 			break;
508 
509 		/* Tool motion, outbound */
510 		case 0x1:
511 		/* Outbound tracking is unreliable on the Cintiq */
512 			if (sc->sc_type->protocol == CINTIQ)
513 			break;
514 
515 		/* Tool motion */
516 		/*FALLTHRU*/
517 		case 0x3:
518 			uwacom_pos_events_intuos(usbwcmp,
519 			    (PACKET_BITS(3, 0, 16) << 1) | PACKET_BIT(9, 1),
520 			    (PACKET_BITS(5, 0, 16) << 1) | PACKET_BIT(9, 0),
521 			    PACKET_BITS(9, 2, 6));
522 
523 			if (PACKET_BITS(1, 3, 2) == 0) {
524 				uwacom_pen_events_intuos(usbwcmp, packet);
525 
526 			} else if (PACKET_BITS(1, 1, 4) == 0x5) {
527 				int angle = 450 - PACKET_BITS(7, 6, 10);
528 
529 				if (PACKET_BIT(7, 5)) {
530 					angle = (angle > 0 ? 900 : -900) -
531 					    angle;
532 				}
533 
534 				uwacom_event(usbwcmp, EVT_ABS, ABS_Z, angle);
535 				break;
536 			} else if (PACKET_BITS(1, 1, 4) == 0x8) {
537 				uwacom_mouse_events_intuos(usbwcmp, packet);
538 			} else {
539 				USB_DPRINTF_L1(PRINT_MASK_ALL,
540 				    usbwcm_log_handle,
541 				    "unsupported motion packet type %x "
542 				    "received\n", PACKET_BITS(1, 1, 4));
543 			}
544 
545 			uwacom_tool_events_intuos(usbwcmp, 0, 1);
546 			break;
547 		}
548 
549 		break;
550 
551 	case 0x0c:
552 		uwacom_pad_events_intuos(usbwcmp, packet);
553 		break;
554 
555 	default:
556 		USB_DPRINTF_L1(PRINT_MASK_ALL, usbwcm_log_handle,
557 		    "unknown report type %02x received\n",
558 		    PACKET_BITS(0, 0, 8));
559 	}
560 }
561 
562 static void
uwacom_init_abs(usbwcm_state_t * usbwcmp,int axis,int32_t min,int32_t max,int32_t fuzz,int32_t flat)563 uwacom_init_abs(usbwcm_state_t *usbwcmp, int axis, int32_t min, int32_t max,
564     int32_t fuzz, int32_t flat)
565 {
566 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
567 
568 	sc->sc_abs[axis].min = min;
569 	sc->sc_abs[axis].max = max;
570 	sc->sc_abs[axis].fuzz = fuzz;
571 	sc->sc_abs[axis].flat = flat;
572 }
573 
574 static void
uwacom_init_graphire4(usbwcm_state_t * usbwcmp)575 uwacom_init_graphire4(usbwcm_state_t *usbwcmp)
576 {
577 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
578 
579 	BM_SET_BIT(sc->sc_bm[0], EVT_MSC);
580 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_0);
581 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_4);
582 	BM_SET_BIT(sc->sc_bm[1], BTN_TOOL_PAD);
583 	BM_SET_BIT(sc->sc_bm[4], MSC_SERIAL);
584 
585 	sc->sc_tool[1] = BTN_TOOL_PAD;
586 	sc->sc_serial[1] = SERIAL_PAD_GRAPHIRE4;
587 }
588 
589 static void
uwacom_init_myoffice(usbwcm_state_t * usbwcmp)590 uwacom_init_myoffice(usbwcm_state_t *usbwcmp)
591 {
592 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
593 
594 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_1);
595 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_5);
596 	BM_SET_BIT(sc->sc_bm[3], ABS_WHEEL);
597 
598 	uwacom_init_abs(usbwcmp, ABS_WHEEL, 0, 71, 0, 0);
599 }
600 
601 static void
uwacom_init_intuos(usbwcm_state_t * usbwcmp)602 uwacom_init_intuos(usbwcm_state_t *usbwcmp)
603 {
604 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
605 
606 	BM_SET_BIT(sc->sc_bm[0], EVT_MSC);
607 
608 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_0);
609 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_1);
610 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_2);
611 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_3);
612 	BM_SET_BIT(sc->sc_bm[1], BTN_SIDE);
613 	BM_SET_BIT(sc->sc_bm[1], BTN_EXTRA);
614 	BM_SET_BIT(sc->sc_bm[1], BTN_TOOL_PAD);
615 
616 	BM_SET_BIT(sc->sc_bm[3], ABS_TILT_X);
617 	BM_SET_BIT(sc->sc_bm[3], ABS_TILT_Y);
618 
619 	BM_SET_BIT(sc->sc_bm[4], MSC_SERIAL);
620 
621 	sc->sc_tool[1] = BTN_TOOL_PAD;
622 	sc->sc_tool_id[1] = TOOL_ID_PAD;
623 	sc->sc_serial[1] = SERIAL_PAD_INTUOS;
624 }
625 
626 static void
uwacom_init_intuos3(usbwcm_state_t * usbwcmp)627 uwacom_init_intuos3(usbwcm_state_t *usbwcmp)
628 {
629 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
630 
631 	BM_SET_BIT(sc->sc_bm[3], ABS_Z);
632 	BM_SET_BIT(sc->sc_bm[3], ABS_RX);
633 
634 	uwacom_init_abs(usbwcmp, ABS_Z, -900,  899, 0, 0);
635 	uwacom_init_abs(usbwcmp, ABS_RX, 0, 4096, 0, 0);
636 }
637 
638 static void
uwacom_init_intuos3_large(usbwcm_state_t * usbwcmp)639 uwacom_init_intuos3_large(usbwcm_state_t *usbwcmp)
640 {
641 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
642 
643 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_4);
644 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_5);
645 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_6);
646 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_7);
647 
648 	BM_SET_BIT(sc->sc_bm[3], ABS_RY);
649 
650 	uwacom_init_abs(usbwcmp, ABS_RY, 0, 4096, 0, 0);
651 }
652 
653 static void
uwacom_init_intuos4(usbwcm_state_t * usbwcmp)654 uwacom_init_intuos4(usbwcm_state_t *usbwcmp)
655 {
656 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
657 
658 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_4);
659 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_5);
660 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_6);
661 
662 	BM_SET_BIT(sc->sc_bm[3], ABS_Z);
663 
664 	uwacom_init_abs(usbwcmp, ABS_Z, -900,  899, 0, 0);
665 }
666 static void
uwacom_init_intuos4_large(usbwcm_state_t * usbwcmp)667 uwacom_init_intuos4_large(usbwcm_state_t *usbwcmp)
668 {
669 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
670 
671 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_7);
672 	BM_SET_BIT(sc->sc_bm[1], BTN_MISC_8);
673 }
674 
675 static int
uwacom_init(usbwcm_state_t * usbwcmp)676 uwacom_init(usbwcm_state_t *usbwcmp)
677 {
678 	struct uwacom_softc *sc = &usbwcmp->usbwcm_softc;
679 
680 	sc->sc_id.bus = ID_BUS_USB;
681 	sc->sc_id.vendor = usbwcmp->usbwcm_devid.VendorId;
682 	sc->sc_id.product = usbwcmp->usbwcm_devid.ProductId;
683 
684 	sc->sc_id.version = 0;
685 
686 	for (int i = 0; i < EVT_USED; ++i)
687 		sc->sc_bm[i] = kmem_zalloc(bm_size[i], KM_SLEEP);
688 
689 	sc->sc_btn = kmem_zalloc(BTN_USED * sizeof (int), KM_SLEEP);
690 	sc->sc_abs = kmem_zalloc(ABS_USED * sizeof (struct event_abs_axis),
691 	    KM_SLEEP);
692 
693 	BM_SET_BIT(sc->sc_bm[0], EVT_SYN);
694 	BM_SET_BIT(sc->sc_bm[0], EVT_BTN);
695 	BM_SET_BIT(sc->sc_bm[0], EVT_REL);
696 	BM_SET_BIT(sc->sc_bm[0], EVT_ABS);
697 
698 	BM_SET_BIT(sc->sc_bm[1], BTN_LEFT);
699 	BM_SET_BIT(sc->sc_bm[1], BTN_RIGHT);
700 	BM_SET_BIT(sc->sc_bm[1], BTN_MIDDLE);
701 	BM_SET_BIT(sc->sc_bm[1], BTN_TOOL_PEN);
702 	BM_SET_BIT(sc->sc_bm[1], BTN_TOOL_ERASER);
703 	BM_SET_BIT(sc->sc_bm[1], BTN_TOOL_MOUSE);
704 	BM_SET_BIT(sc->sc_bm[1], BTN_TIP);
705 	BM_SET_BIT(sc->sc_bm[1], BTN_STYLUS_1);
706 	BM_SET_BIT(sc->sc_bm[1], BTN_STYLUS_2);
707 
708 	BM_SET_BIT(sc->sc_bm[2], REL_WHEEL);
709 
710 	BM_SET_BIT(sc->sc_bm[3], ABS_X);
711 	BM_SET_BIT(sc->sc_bm[3], ABS_Y);
712 	BM_SET_BIT(sc->sc_bm[3], ABS_PRESSURE);
713 	BM_SET_BIT(sc->sc_bm[3], ABS_DISTANCE);
714 	BM_SET_BIT(sc->sc_bm[3], ABS_MISC);
715 
716 	uwacom_init_abs(usbwcmp, ABS_X, 0, sc->sc_type->x_max, 4, 0);
717 	uwacom_init_abs(usbwcmp, ABS_Y, 0, sc->sc_type->y_max, 4, 0);
718 	uwacom_init_abs(usbwcmp, ABS_PRESSURE, 0, sc->sc_type->pressure_max,
719 	    0, 0);
720 	uwacom_init_abs(usbwcmp, ABS_DISTANCE, 0,
721 	    uwacom_protocols[sc->sc_type->protocol].distance_max, 0, 0);
722 
723 	switch (sc->sc_type->protocol) {
724 		case CINTIQ:
725 		case INTUOS3L:
726 			uwacom_init_intuos3_large(usbwcmp);
727 		/*FALLTHRU*/
728 		case INTUOS3S:
729 			uwacom_init_intuos3(usbwcmp);
730 			uwacom_init_intuos(usbwcmp);
731 			break;
732 
733 		case INTUOS4L:
734 			uwacom_init_intuos4_large(usbwcmp);
735 		/*FALLTHRU*/
736 		case INTUOS4S:
737 			uwacom_init_intuos4(usbwcmp);
738 			uwacom_init_intuos(usbwcmp);
739 			break;
740 		case MYOFFICE:
741 			uwacom_init_myoffice(usbwcmp);
742 		/*FALLTHRU*/
743 		case GRAPHIRE4:
744 			uwacom_init_graphire4(usbwcmp);
745 		/*FALLTHRU*/
746 		case GRAPHIRE:
747 			break;
748 	}
749 
750 	return (0);
751 }
752 
753 /*
754  * usbwcm_match() :
755  *	Match device with it's parameters.
756  */
757 static const struct uwacom_type *
usbwcm_match(uint16_t vid,uint16_t pid)758 usbwcm_match(uint16_t vid, uint16_t pid)
759 {
760 	const struct uwacom_type *dev;
761 
762 	dev = uwacom_devs;
763 	while (dev->devno.vid != 0 && dev->devno.pid != 0) {
764 		if (dev->devno.vid == vid && dev->devno.pid == pid) {
765 			return (dev);
766 		}
767 		dev++;
768 	}
769 
770 	return (NULL);
771 }
772 
773 /*
774  * usbwcm_probe() :
775  *	Check the device type and protocol.
776  */
777 static int
usbwcm_probe(usbwcm_state_t * usbwcmp)778 usbwcm_probe(usbwcm_state_t *usbwcmp)
779 {
780 	queue_t		*q = usbwcmp->usbwcm_rq;
781 	mblk_t		*mctl_ptr;
782 	struct iocblk	mctlmsg;
783 	hid_req_t	*featr;
784 
785 	/* check device IDs */
786 	mctlmsg.ioc_cmd = HID_GET_VID_PID;
787 	mctlmsg.ioc_count = 0;
788 
789 	mctl_ptr = usba_mk_mctl(mctlmsg, NULL, 0);
790 	if (mctl_ptr == NULL) {
791 		return (ENOMEM);
792 	}
793 
794 	putnext(usbwcmp->usbwcm_wq, mctl_ptr);
795 	usbwcmp->usbwcm_flags |= USBWCM_QWAIT;
796 
797 	while (usbwcmp->usbwcm_flags & USBWCM_QWAIT) {
798 		if (qwait_sig(q) == 0) {
799 			usbwcmp->usbwcm_flags = 0;
800 			return (EINTR);
801 		}
802 	}
803 
804 	usbwcmp->usbwcm_softc.sc_type =
805 	    usbwcm_match(usbwcmp->usbwcm_devid.VendorId,
806 	    usbwcmp->usbwcm_devid.ProductId);
807 	if (!usbwcmp->usbwcm_softc.sc_type) {
808 		USB_DPRINTF_L1(PRINT_MASK_ALL, usbwcm_log_handle,
809 		    "unsupported tablet model\n");
810 		return (ENXIO);
811 	}
812 
813 	if (uwacom_init(usbwcmp) != 0) {
814 		return (ENXIO);
815 	}
816 
817 	/* set feature: tablet mode */
818 	featr = kmem_zalloc(sizeof (hid_req_t), KM_SLEEP);
819 	featr->hid_req_version_no = HID_VERSION_V_0;
820 	featr->hid_req_wValue = REPORT_TYPE_FEATURE | 2;
821 	featr->hid_req_wLength = sizeof (uint8_t) * 2;
822 	featr->hid_req_data[0] = 2;
823 	featr->hid_req_data[1] = 2;
824 
825 	mctlmsg.ioc_cmd = HID_SET_REPORT;
826 	mctlmsg.ioc_count = sizeof (featr);
827 	mctl_ptr = usba_mk_mctl(mctlmsg, featr, sizeof (hid_req_t));
828 	if (mctl_ptr != NULL) {
829 		putnext(usbwcmp->usbwcm_wq, mctl_ptr);
830 
831 		/*
832 		 * Waiting for response of HID_SET_REPORT
833 		 * mctl for setting the feature.
834 		 */
835 		usbwcmp->usbwcm_flags |= USBWCM_QWAIT;
836 		while (usbwcmp->usbwcm_flags & USBWCM_QWAIT) {
837 			qwait(q);
838 		}
839 	} else {
840 		USB_DPRINTF_L1(PRINT_MASK_ALL, usbwcm_log_handle,
841 		    "enable tablet mode failed\n");
842 	}
843 
844 	kmem_free(featr, sizeof (hid_req_t));
845 
846 	return (0);
847 }
848 
849 /*
850  * usbwcm_copyreq() :
851  *	helper function for usbwcm ioctls
852  */
853 static int
usbwcm_copyreq(mblk_t * mp,uint_t pvtsize,uint_t state,uint_t reqsize,uint_t contsize,uint_t copytype)854 usbwcm_copyreq(mblk_t *mp, uint_t pvtsize, uint_t state, uint_t reqsize,
855     uint_t contsize, uint_t copytype)
856 {
857 	usbwcm_copyin_t	*copystat;
858 	mblk_t		*iocmp, *contmp;
859 	struct copyreq	*cq;
860 	struct copyresp	*cr;
861 
862 	if ((pvtsize == 0) && (state != 0)) {
863 		cr = (struct copyresp *)mp->b_rptr;
864 		iocmp = cr->cp_private;
865 	}
866 
867 	cq = (struct copyreq *)mp->b_rptr;
868 	if (mp->b_cont == NULL) {
869 
870 		return (EINVAL);
871 	}
872 
873 	cq->cq_addr = *((caddr_t *)mp->b_cont->b_rptr);
874 	cq->cq_size = reqsize;
875 	cq->cq_flag = 0;
876 
877 	if (pvtsize) {
878 		iocmp = (mblk_t *)allocb(pvtsize, BPRI_MED);
879 		if (iocmp == NULL) {
880 
881 			return (EAGAIN);
882 		}
883 		cq->cq_private = iocmp;
884 		iocmp = cq->cq_private;
885 	} else {
886 		/*
887 		 * Here we need to set cq_private even if there's
888 		 * no private data, otherwise its value will be
889 		 * TRANSPARENT (-1) on 64bit systems because it
890 		 * overlaps iocp->ioc_count. If user address (cq_addr)
891 		 * is invalid, it would cause panic later in
892 		 * usbwcm_copyin:
893 		 * 	freemsg((mblk_t *)copyresp->cp_private);
894 		 */
895 		cq->cq_private = NULL;
896 	}
897 
898 	if (state) {
899 		copystat = (usbwcm_copyin_t *)iocmp->b_rptr;
900 		copystat->state = state;
901 		if (pvtsize) {  /* M_COPYIN */
902 			copystat->addr = cq->cq_addr;
903 		} else {
904 			cq->cq_addr = copystat->addr;
905 			cq->cq_private = iocmp;
906 		}
907 		iocmp->b_wptr = iocmp->b_rptr + sizeof (usbwcm_copyin_t);
908 	}
909 
910 	if (contsize) {
911 		contmp = (mblk_t *)allocb(contsize, BPRI_MED);
912 		if (contmp == NULL) {
913 
914 			return (EAGAIN);
915 		}
916 		if (mp->b_cont) {
917 			freemsg(mp->b_cont);
918 			mp->b_cont = contmp;
919 		}
920 	}
921 
922 	mp->b_datap->db_type = (unsigned char)copytype;
923 	mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
924 
925 	return (0);
926 }
927 
928 static void
usbwcm_miocack(queue_t * q,mblk_t * mp,int rval)929 usbwcm_miocack(queue_t *q, mblk_t *mp, int rval)
930 {
931 	struct iocblk	*iocbp = (struct iocblk *)mp->b_rptr;
932 
933 	mp->b_datap->db_type = M_IOCACK;
934 	mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
935 
936 	iocbp->ioc_error = 0;
937 	iocbp->ioc_count = 0;
938 	iocbp->ioc_rval = rval;
939 
940 	if (mp->b_cont != NULL) {
941 		freemsg(mp->b_cont);
942 		mp->b_cont = NULL;
943 	}
944 
945 	qreply(q, mp);
946 }
947 
948 /*
949  * usbwcm_iocpy() :
950  * M_IOCDATA processing for IOCTL's
951  */
952 static void
usbwcm_iocpy(queue_t * q,mblk_t * mp)953 usbwcm_iocpy(queue_t *q, mblk_t *mp)
954 {
955 	usbwcm_state_t		*usbwcmp = (usbwcm_state_t *)q->q_ptr;
956 	struct uwacom_softc	*sc = &usbwcmp->usbwcm_softc;
957 	struct copyresp		*copyresp;
958 	usbwcm_copyin_t 	*copystat;
959 	mblk_t			*datap, *ioctmp;
960 	struct iocblk		*iocbp;
961 	int			err = 0;
962 
963 	copyresp = (struct copyresp *)mp->b_rptr;
964 	iocbp = (struct iocblk *)mp->b_rptr;
965 	if (copyresp->cp_rval) {
966 		err = EAGAIN;
967 
968 		goto out;
969 	}
970 
971 	switch (copyresp->cp_cmd) {
972 	default: {
973 		int num = copyresp->cp_cmd & 0xff;
974 		int len = IOCPARM_MASK & (copyresp->cp_cmd >> 16);
975 
976 		if (((copyresp->cp_cmd >> 8) & 0xFF) != 'E') {
977 			putnext(q, mp); /* pass it down the line */
978 			return;
979 
980 		} else if ((copyresp->cp_cmd & IOC_INOUT) != IOC_OUT) {
981 			err = EINVAL;
982 			break;
983 		}
984 
985 		switch (num) {
986 		case EUWACOMGETVERSION:
987 			ioctmp = copyresp->cp_private;
988 			copystat = (usbwcm_copyin_t *)ioctmp->b_rptr;
989 			if (copystat->state == USBWCM_GETSTRUCT) {
990 				if (mp->b_cont == NULL) {
991 					err = EINVAL;
992 
993 					break;
994 				}
995 				datap = (mblk_t *)mp->b_cont;
996 
997 				*(int *)datap->b_rptr = 0x00010000;
998 
999 				if (err = usbwcm_copyreq(mp, 0,
1000 				    USBWCM_GETRESULT, sizeof (int), 0,
1001 				    M_COPYOUT)) {
1002 
1003 					goto out;
1004 				}
1005 			} else if (copystat->state == USBWCM_GETRESULT) {
1006 				freemsg(ioctmp);
1007 				usbwcm_miocack(q, mp, 0);
1008 				return;
1009 			}
1010 			break;
1011 
1012 		case EUWACOMGETID:
1013 			ioctmp = copyresp->cp_private;
1014 			copystat = (usbwcm_copyin_t *)ioctmp->b_rptr;
1015 			if (copystat->state == USBWCM_GETSTRUCT) {
1016 				if (mp->b_cont == NULL) {
1017 					err = EINVAL;
1018 
1019 					break;
1020 				}
1021 				datap = (mblk_t *)mp->b_cont;
1022 
1023 				bcopy(&sc->sc_id, datap->b_rptr,
1024 				    sizeof (struct event_dev_id));
1025 
1026 				if (err = usbwcm_copyreq(mp, 0,
1027 				    USBWCM_GETRESULT,
1028 				    sizeof (struct event_dev_id), 0,
1029 				    M_COPYOUT)) {
1030 
1031 					goto out;
1032 				}
1033 			} else if (copystat->state == USBWCM_GETRESULT) {
1034 				freemsg(ioctmp);
1035 				usbwcm_miocack(q, mp, 0);
1036 				return;
1037 			}
1038 			break;
1039 
1040 		default:
1041 			if (num >= EUWACOMGETBM &&
1042 			    num < EUWACOMGETBM + EVT_USED) {
1043 				int idx = num - EUWACOMGETBM;
1044 				size_t length = min(bm_size[idx], len);
1045 
1046 				ioctmp = copyresp->cp_private;
1047 				copystat = (usbwcm_copyin_t *)ioctmp->b_rptr;
1048 				if (copystat->state == USBWCM_GETSTRUCT) {
1049 					if (mp->b_cont == NULL) {
1050 						err = EINVAL;
1051 
1052 						break;
1053 					}
1054 					datap = (mblk_t *)mp->b_cont;
1055 
1056 					bcopy(sc->sc_bm[idx], datap->b_rptr,
1057 					    length);
1058 
1059 					if (err = usbwcm_copyreq(mp, 0,
1060 					    USBWCM_GETRESULT, length, 0,
1061 					    M_COPYOUT)) {
1062 
1063 						goto out;
1064 					}
1065 
1066 				} else if (copystat->state ==
1067 				    USBWCM_GETRESULT) {
1068 					freemsg(ioctmp);
1069 					usbwcm_miocack(q, mp, length);
1070 					return;
1071 				}
1072 				break;
1073 
1074 			} else if (num >= EUWACOMGETABS &&
1075 			    num < EUWACOMGETABS + ABS_USED) {
1076 				int idx = num - EUWACOMGETABS;
1077 
1078 				ioctmp = copyresp->cp_private;
1079 				copystat = (usbwcm_copyin_t *)ioctmp->b_rptr;
1080 				if (copystat->state == USBWCM_GETSTRUCT) {
1081 					if (mp->b_cont == NULL) {
1082 						err = EINVAL;
1083 
1084 						break;
1085 					}
1086 					datap = (mblk_t *)mp->b_cont;
1087 
1088 					bcopy(&sc->sc_abs[idx], datap->b_rptr,
1089 					    sizeof (struct event_abs_axis));
1090 
1091 					if (err = usbwcm_copyreq(mp, 0,
1092 					    USBWCM_GETRESULT,
1093 					    sizeof (struct event_abs_axis), 0,
1094 					    M_COPYOUT)) {
1095 
1096 						goto out;
1097 					}
1098 
1099 				} else if (copystat->state ==
1100 				    USBWCM_GETRESULT) {
1101 					freemsg(ioctmp);
1102 					usbwcm_miocack(q, mp, 0);
1103 					return;
1104 				}
1105 				break;
1106 
1107 			} else {
1108 				err = EINVAL;
1109 				break;
1110 			}
1111 		}
1112 	}
1113 	}
1114 
1115 out:
1116 	if (err) {
1117 		mp->b_datap->db_type = M_IOCNAK;
1118 		if (mp->b_cont) {
1119 			freemsg(mp->b_cont);
1120 			mp->b_cont = (mblk_t *)NULL;
1121 		}
1122 		if (copyresp->cp_private) {
1123 			freemsg((mblk_t *)copyresp->cp_private);
1124 			copyresp->cp_private = (mblk_t *)NULL;
1125 		}
1126 		iocbp->ioc_count = 0;
1127 		iocbp->ioc_error = err;
1128 	}
1129 
1130 	qreply(q, mp);
1131 }
1132 
1133 /*
1134  * usbwcm_ioctl() :
1135  *	Process ioctls we recognize and own.  Otherwise, NAK.
1136  */
1137 static void
usbwcm_ioctl(queue_t * q,mblk_t * mp)1138 usbwcm_ioctl(queue_t *q, mblk_t *mp)
1139 {
1140 	usbwcm_state_t		*usbwcmp = (usbwcm_state_t *)q->q_ptr;
1141 	struct uwacom_softc	*sc;
1142 	mblk_t			*datap;
1143 	struct iocblk		*iocp;
1144 	int			err = 0;
1145 
1146 	if (usbwcmp == NULL) {
1147 		miocnak(q, mp, 0, EINVAL);
1148 		return;
1149 	}
1150 
1151 	sc = &usbwcmp->usbwcm_softc;
1152 	iocp = (struct iocblk *)mp->b_rptr;
1153 
1154 	switch (iocp->ioc_cmd) {
1155 	default: {
1156 		int num = iocp->ioc_cmd & 0xff;
1157 		int len = IOCPARM_MASK & (iocp->ioc_cmd >> 16);
1158 
1159 		if (((iocp->ioc_cmd >> 8) & 0xFF) != 'E') {
1160 			putnext(q, mp); /* pass it down the line */
1161 			return;
1162 
1163 		} else if ((iocp->ioc_cmd & IOC_INOUT) != IOC_OUT) {
1164 			err = EINVAL;
1165 			break;
1166 		}
1167 
1168 		switch (num) {
1169 		case EUWACOMGETVERSION:
1170 			if (iocp->ioc_count == TRANSPARENT) {
1171 				if (err = usbwcm_copyreq(mp,
1172 				    sizeof (usbwcm_copyin_t), USBWCM_GETSTRUCT,
1173 				    sizeof (int), 0, M_COPYIN)) {
1174 					break;
1175 				}
1176 				freemsg(mp->b_cont);
1177 				mp->b_cont = (mblk_t *)NULL;
1178 
1179 				qreply(q, mp);
1180 				return;
1181 			}
1182 
1183 			if (mp->b_cont == NULL ||
1184 			    iocp->ioc_count != sizeof (int)) {
1185 				err = EINVAL;
1186 				break;
1187 			}
1188 			datap = mp->b_cont;
1189 
1190 			*(int *)datap->b_rptr = 0x00010000;
1191 
1192 			break;
1193 
1194 		case EUWACOMGETID:
1195 			if (iocp->ioc_count == TRANSPARENT) {
1196 				if (err = usbwcm_copyreq(mp,
1197 				    sizeof (usbwcm_copyin_t), USBWCM_GETSTRUCT,
1198 				    sizeof (struct event_dev_id), 0,
1199 				    M_COPYIN)) {
1200 					break;
1201 				}
1202 				freemsg(mp->b_cont);
1203 				mp->b_cont = (mblk_t *)NULL;
1204 
1205 				qreply(q, mp);
1206 				return;
1207 			}
1208 
1209 			if (mp->b_cont == NULL ||
1210 			    iocp->ioc_count != sizeof (struct event_dev_id)) {
1211 				err = EINVAL;
1212 				break;
1213 			}
1214 			datap = mp->b_cont;
1215 
1216 			bcopy(&sc->sc_id, datap->b_rptr,
1217 			    sizeof (struct event_dev_id));
1218 
1219 			break;
1220 
1221 		default:
1222 			if (num >= EUWACOMGETBM &&
1223 			    num < EUWACOMGETBM + EVT_USED) {
1224 				int idx = num - EUWACOMGETBM;
1225 				size_t length = min(bm_size[idx], len);
1226 
1227 				if (iocp->ioc_count == TRANSPARENT) {
1228 					if (err = usbwcm_copyreq(mp,
1229 					    sizeof (usbwcm_copyin_t),
1230 					    USBWCM_GETSTRUCT, length, 0,
1231 					    M_COPYIN)) {
1232 						break;
1233 					}
1234 					freemsg(mp->b_cont);
1235 					mp->b_cont = (mblk_t *)NULL;
1236 
1237 					qreply(q, mp);
1238 					return;
1239 				}
1240 
1241 				if (mp->b_cont == NULL ||
1242 				    iocp->ioc_count != length) {
1243 					err = EINVAL;
1244 					break;
1245 				}
1246 				datap = mp->b_cont;
1247 
1248 				bcopy(sc->sc_bm[idx], datap->b_rptr, length);
1249 
1250 				break;
1251 
1252 			} else if (num >= EUWACOMGETABS &&
1253 			    num < EUWACOMGETABS + ABS_USED) {
1254 				int idx = num - EUWACOMGETABS;
1255 
1256 				if (iocp->ioc_count == TRANSPARENT) {
1257 					if (err = usbwcm_copyreq(mp,
1258 					    sizeof (usbwcm_copyin_t),
1259 					    USBWCM_GETSTRUCT,
1260 					    sizeof (struct event_abs_axis), 0,
1261 					    M_COPYIN)) {
1262 						break;
1263 					}
1264 					freemsg(mp->b_cont);
1265 					mp->b_cont = (mblk_t *)NULL;
1266 
1267 					qreply(q, mp);
1268 					return;
1269 				}
1270 
1271 				if (mp->b_cont == NULL ||
1272 				    iocp->ioc_count !=
1273 				    sizeof (struct event_abs_axis)) {
1274 					err = EINVAL;
1275 					break;
1276 				}
1277 				datap = mp->b_cont;
1278 
1279 				bcopy(&sc->sc_abs[idx], datap->b_rptr,
1280 				    sizeof (struct event_abs_axis));
1281 
1282 				break;
1283 
1284 			} else {
1285 				err = EINVAL;
1286 				break;
1287 			}
1288 		}
1289 	}
1290 	}
1291 
1292 	if (err != 0)
1293 		miocnak(q, mp, 0, err);
1294 	else {
1295 		iocp->ioc_rval = 0;
1296 		iocp->ioc_error = 0;
1297 		mp->b_datap->db_type = M_IOCACK;
1298 		qreply(q, mp);
1299 		/* REMOVE */
1300 	}
1301 
1302 	return;
1303 
1304 }
1305 
1306 /*
1307  * usbwcm_input() :
1308  *
1309  *	Wacom input routine; process data received from a device and
1310  *	assemble into a input event for the window system.
1311  *
1312  *	Watch out for overflow!
1313  */
1314 static void
usbwcm_input(usbwcm_state_t * usbwcmp,mblk_t * mp)1315 usbwcm_input(usbwcm_state_t *usbwcmp, mblk_t *mp)
1316 {
1317 	struct uwacom_softc	*sc = &usbwcmp->usbwcm_softc;
1318 
1319 	switch (sc->sc_type->protocol) {
1320 	case GRAPHIRE:
1321 	case GRAPHIRE4:
1322 	case MYOFFICE:
1323 		usbwcm_input_graphire(usbwcmp, mp);
1324 		break;
1325 
1326 	case INTUOS3S:
1327 	case INTUOS3L:
1328 	case INTUOS4S:
1329 	case INTUOS4L:
1330 	case CINTIQ:
1331 		usbwcm_input_intuos(usbwcmp, mp);
1332 		break;
1333 	}
1334 }
1335 
1336 /*
1337  * usbwcm_flush() :
1338  *	Resets the soft state to default values
1339  *	and sends M_FLUSH above.
1340  */
1341 static void
usbwcm_flush(usbwcm_state_t * usbwcmp)1342 usbwcm_flush(usbwcm_state_t *usbwcmp)
1343 {
1344 	queue_t			*q;
1345 
1346 	if ((q = usbwcmp->usbwcm_rq) != NULL && q->q_next != NULL) {
1347 		(void) putnextctl1(q, M_FLUSH, FLUSHR);
1348 	}
1349 }
1350 
1351 /*
1352  * usbwcm_mctl() :
1353  *	Handle M_CTL messages from hid.  If
1354  *	we don't understand the command, free message.
1355  */
1356 static void
usbwcm_mctl(queue_t * q,mblk_t * mp)1357 usbwcm_mctl(queue_t *q, mblk_t *mp)
1358 {
1359 	usbwcm_state_t	*usbwcmp = (usbwcm_state_t *)q->q_ptr;
1360 	struct iocblk	*iocp;
1361 	caddr_t		data = NULL;
1362 	struct iocblk	mctlmsg;
1363 	mblk_t		*mctl_ptr;
1364 	hid_req_t	*featr;
1365 
1366 	iocp = (struct iocblk *)mp->b_rptr;
1367 	if (mp->b_cont != NULL)
1368 		data = (caddr_t)mp->b_cont->b_rptr;
1369 
1370 	switch (iocp->ioc_cmd) {
1371 	case HID_GET_VID_PID:
1372 		if ((data != NULL) &&
1373 		    (iocp->ioc_count == sizeof (hid_vid_pid_t)) &&
1374 		    (MBLKL(mp->b_cont) == iocp->ioc_count)) {
1375 			bcopy(data, &usbwcmp->usbwcm_devid, iocp->ioc_count);
1376 		}
1377 
1378 		freemsg(mp);
1379 		usbwcmp->usbwcm_flags &= ~USBWCM_QWAIT;
1380 		break;
1381 
1382 	case HID_CONNECT_EVENT:
1383 		/* set feature: tablet mode */
1384 		featr = kmem_zalloc(sizeof (hid_req_t), KM_SLEEP);
1385 		featr->hid_req_version_no = HID_VERSION_V_0;
1386 		featr->hid_req_wValue = REPORT_TYPE_FEATURE | 2;
1387 		featr->hid_req_wLength = sizeof (uint8_t) * 2;
1388 		featr->hid_req_data[0] = 2;
1389 		featr->hid_req_data[1] = 2;
1390 
1391 		mctlmsg.ioc_cmd = HID_SET_REPORT;
1392 		mctlmsg.ioc_count = sizeof (featr);
1393 		mctl_ptr = usba_mk_mctl(mctlmsg, featr, sizeof (hid_req_t));
1394 		if (mctl_ptr != NULL) {
1395 			putnext(usbwcmp->usbwcm_wq, mctl_ptr);
1396 		} else {
1397 		USB_DPRINTF_L1(PRINT_MASK_ALL, usbwcm_log_handle,
1398 		    "enable tablet mode failed\n");
1399 		}
1400 
1401 		kmem_free(featr, sizeof (hid_req_t));
1402 		freemsg(mp);
1403 		break;
1404 
1405 	case HID_SET_REPORT:
1406 		/* FALLTHRU */
1407 
1408 	case HID_SET_PROTOCOL:
1409 		usbwcmp->usbwcm_flags &= ~USBWCM_QWAIT;
1410 		/* FALLTHRU */
1411 
1412 	default:
1413 		freemsg(mp);
1414 	}
1415 }
1416 
1417 /*
1418  * usbwcm_open() :
1419  *	open() entry point for the USB wacom module.
1420  */
1421 /*ARGSUSED*/
1422 static int
usbwcm_open(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp)1423 usbwcm_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
1424 {
1425 	usbwcm_state_t	*usbwcmp;
1426 
1427 	/* Clone opens are not allowed */
1428 	if (sflag != MODOPEN)
1429 		return (EINVAL);
1430 
1431 	/* If the module is already open, just return */
1432 	if (q->q_ptr) {
1433 		return (0);
1434 	}
1435 
1436 	/* allocate usbwcm state structure */
1437 	usbwcmp = kmem_zalloc(sizeof (usbwcm_state_t), KM_SLEEP);
1438 
1439 	q->q_ptr = usbwcmp;
1440 	WR(q)->q_ptr = usbwcmp;
1441 
1442 	usbwcmp->usbwcm_rq = q;
1443 	usbwcmp->usbwcm_wq = WR(q);
1444 
1445 	qprocson(q);
1446 
1447 	if (usbwcm_probe(usbwcmp) != 0) {
1448 
1449 		qprocsoff(q);
1450 		kmem_free(usbwcmp, sizeof (usbwcm_state_t));
1451 
1452 		return (EINVAL);
1453 	}
1454 
1455 	usbwcm_flush(usbwcmp);
1456 
1457 	usbwcmp->usbwcm_flags |= USBWCM_OPEN;
1458 	return (0);
1459 }
1460 
1461 
1462 /*
1463  * usbwcm_close() :
1464  *	close() entry point for the USB wacom module.
1465  */
1466 /*ARGSUSED*/
1467 static int
usbwcm_close(queue_t * q,int flag,cred_t * credp)1468 usbwcm_close(queue_t *q, int flag, cred_t *credp)
1469 {
1470 	usbwcm_state_t		*usbwcmp = q->q_ptr;
1471 	struct uwacom_softc	*sc = &usbwcmp->usbwcm_softc;
1472 
1473 	qprocsoff(q);
1474 
1475 	if (usbwcmp->usbwcm_bufcall) {
1476 		qunbufcall(q, (bufcall_id_t)(long)usbwcmp->usbwcm_bufcall);
1477 		usbwcmp->usbwcm_bufcall = 0;
1478 	}
1479 
1480 	if (usbwcmp->usbwcm_mioctl != NULL) {
1481 		/*
1482 		 * We were holding an "ioctl" response pending the
1483 		 * availability of an "mblk" to hold data to be passed up;
1484 		 * another "ioctl" came through, which means that "ioctl"
1485 		 * must have timed out or been aborted.
1486 		 */
1487 		freemsg(usbwcmp->usbwcm_mioctl);
1488 		usbwcmp->usbwcm_mioctl = NULL;
1489 	}
1490 
1491 	for (int i = 0; i < EVT_USED; i++)
1492 		kmem_free(sc->sc_bm[i], bm_size[i]);
1493 
1494 	kmem_free(sc->sc_btn, BTN_USED * sizeof (int));
1495 	kmem_free(sc->sc_abs, ABS_USED * sizeof (struct event_abs_axis));
1496 	kmem_free(usbwcmp, sizeof (usbwcm_state_t));
1497 
1498 	q->q_ptr = WR(q)->q_ptr = NULL;
1499 	return (0);
1500 }
1501 
1502 /*
1503  * usbwcm_wput() :
1504  *	wput() routine for the wacom module.
1505  *	Module below : hid, module above : consms
1506  */
1507 static int
usbwcm_wput(queue_t * q,mblk_t * mp)1508 usbwcm_wput(queue_t *q, mblk_t *mp)
1509 {
1510 	switch (mp->b_datap->db_type) {
1511 
1512 	case M_FLUSH:  /* Canonical flush handling */
1513 		if (*mp->b_rptr & FLUSHW) {
1514 			flushq(q, FLUSHDATA);
1515 		}
1516 		if (*mp->b_rptr & FLUSHR) {
1517 			flushq(RD(q), FLUSHDATA);
1518 		}
1519 		putnext(q, mp); /* pass it down the line. */
1520 		break;
1521 
1522 	case M_IOCTL:
1523 		usbwcm_ioctl(q, mp);
1524 		break;
1525 
1526 	case M_IOCDATA:
1527 		usbwcm_iocpy(q, mp);
1528 		break;
1529 
1530 	default:
1531 		putnext(q, mp); /* pass it down the line. */
1532 	}
1533 
1534 	return (0);
1535 }
1536 
1537 /*
1538  * usbwcm_rput() :
1539  *	Put procedure for input from driver end of stream (read queue).
1540  */
1541 static void
usbwcm_rput(queue_t * q,mblk_t * mp)1542 usbwcm_rput(queue_t *q, mblk_t *mp)
1543 {
1544 	usbwcm_state_t		*usbwcmp = q->q_ptr;
1545 	struct uwacom_softc	*sc = &usbwcmp->usbwcm_softc;
1546 	mblk_t			*mp0 = mp;
1547 	int			limit;
1548 
1549 	if (usbwcmp == 0) {
1550 		freemsg(mp);	/* nobody's listening */
1551 		return;
1552 	}
1553 
1554 	switch (mp->b_datap->db_type) {
1555 	case M_FLUSH:
1556 		if (*mp->b_rptr & FLUSHW)
1557 			flushq(WR(q), FLUSHDATA);
1558 
1559 		if (*mp->b_rptr & FLUSHR)
1560 			flushq(q, FLUSHDATA);
1561 
1562 		freemsg(mp);
1563 		return;
1564 
1565 	case M_BREAK:
1566 		/*
1567 		 * We don't have to handle this
1568 		 * because nothing is sent from the downstream
1569 		 */
1570 		freemsg(mp);
1571 		return;
1572 
1573 	case M_DATA:
1574 		if (!(usbwcmp->usbwcm_flags & USBWCM_OPEN)) {
1575 			freemsg(mp);	/* not ready to listen */
1576 
1577 			return;
1578 		}
1579 
1580 		/*
1581 		 * Make sure there are at least "limit" number of bytes.
1582 		 */
1583 		limit = uwacom_protocols[sc->sc_type->protocol].packet_size;
1584 		if (MBLKL(mp0) == limit) {	/* REMOVE */
1585 			do {
1586 				/* REMOVE */
1587 				usbwcm_input(usbwcmp, mp0);
1588 				mp0 = mp0->b_cont;
1589 			} while (mp0 != NULL);   /* next block, if any */
1590 		}
1591 
1592 		freemsg(mp);
1593 		break;
1594 
1595 	case M_CTL:
1596 		usbwcm_mctl(q, mp);
1597 		return;
1598 
1599 	case M_ERROR:
1600 		/* REMOVE */
1601 		usbwcmp->usbwcm_flags &= ~USBWCM_QWAIT;
1602 
1603 		freemsg(mp);
1604 		return;
1605 	default:
1606 		putnext(q, mp);
1607 		return;
1608 	}
1609 }
1610 
1611 
1612 static struct module_info modinfo;
1613 
1614 /* STREAMS entry points */
1615 
1616 /* read side queue */
1617 static struct qinit rinit = {
1618 	(int (*)())usbwcm_rput,	/* put procedure not needed */
1619 	NULL, 			/* service procedure */
1620 	usbwcm_open,		/* called on startup */
1621 	usbwcm_close,		/* called on finish */
1622 	NULL,			/* for future use */
1623 	&modinfo,		/* module information structure */
1624 	NULL			/* module statistics structure */
1625 };
1626 
1627 /* write side queue */
1628 static struct qinit winit = {
1629 	usbwcm_wput,		/* put procedure */
1630 	NULL,			/* no service proecedure needed */
1631 	NULL,			/* open not used on write side */
1632 	NULL,			/* close not used on write side */
1633 	NULL,			/* for future use */
1634 	&modinfo,		/* module information structure */
1635 	NULL			/* module statistics structure */
1636 };
1637 
1638 /* STREAMS table */
1639 static struct streamtab strtab = {
1640 	&rinit,
1641 	&winit,
1642 	NULL,			/* not a MUX */
1643 	NULL			/* not a MUX */
1644 };
1645 
1646 /* Module linkage information */
1647 
1648 static struct fmodsw modsw = {
1649 	"usbwcm",
1650 	&strtab,
1651 	D_MP | D_MTPERMOD
1652 };
1653 
1654 
1655 static struct modlstrmod modlstr = {
1656 	&mod_strmodops,
1657 	"USB Wacom STRMOD",
1658 	&modsw
1659 };
1660 
1661 static struct modlinkage modlink = {
1662 	MODREV_1,
1663 	(void *)&modlstr,
1664 	NULL
1665 };
1666 
1667 static struct module_info modinfo = {
1668 	0x0ffff,		/* module id number */
1669 	"usbwcm",		/* module name */
1670 	0,			/* min packet size accepted */
1671 	INFPSZ,			/* max packet size accepted */
1672 	512,			/* hi-water mark */
1673 	128			/* lo-water mark */
1674 };
1675 
1676 
1677 /* Module entry points */
1678 
1679 int
_init(void)1680 _init(void)
1681 {
1682 	int rval = mod_install(&modlink);
1683 
1684 	if (rval == 0) {
1685 		usbwcm_log_handle = usb_alloc_log_hdl(NULL, "usbwcm",
1686 		    &usbwcm_errlevel, &usbwcm_errmask, NULL, 0);
1687 	}
1688 
1689 	return (rval);
1690 }
1691 
1692 int
_fini(void)1693 _fini(void)
1694 {
1695 	int rval = mod_remove(&modlink);
1696 
1697 	if (rval == 0) {
1698 		usb_free_log_hdl(usbwcm_log_handle);
1699 	}
1700 
1701 	return (rval);
1702 }
1703 
1704 int
_info(struct modinfo * modinfop)1705 _info(struct modinfo *modinfop)
1706 {
1707 
1708 	return (mod_info(&modlink, modinfop));
1709 }
1710