1 /* $OpenBSD: ietp.c,v 1.3 2024/08/18 03:25:04 deraadt Exp $ */
2 /*
3 * Elan I2C Touchpad driver
4 *
5 * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org>
6 * Copyright (c) 2020, 2022 Vladimir Kondratyev <wulf@FreeBSD.org>
7 * Copyright (c) 2023 vladimir serbinenko <phcoder@gmail.com>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22 /* Protocol documentation:
23 * https://lkml.indiana.edu/hypermail/linux/kernel/1205.0/02551.html
24 * Based on FreeBSD ietp driver.
25 */
26
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/device.h>
30 #include <sys/malloc.h>
31 #include <sys/stdint.h>
32
33 #include <dev/i2c/i2cvar.h>
34 #include <dev/i2c/ietp.h>
35
36 #include <dev/wscons/wsconsio.h>
37 #include <dev/wscons/wsmousevar.h>
38
39 /* #define IETP_DEBUG */
40
41 #ifdef IETP_DEBUG
42 #define DPRINTF(x) printf x
43 #else
44 #define DPRINTF(x)
45 #endif
46
47 enum {
48 I2C_HID_CMD_DESCR = 0x0,
49 I2C_HID_CMD_RESET = 0x1,
50 I2C_HID_CMD_SET_POWER = 0x8,
51 };
52
53 #define I2C_HID_POWER_ON 0x0
54 #define I2C_HID_POWER_OFF 0x1
55
56 #define IETP_PATTERN 0x0100
57 #define IETP_UNIQUEID 0x0101
58 #define IETP_IC_TYPE 0x0103
59 #define IETP_OSM_VERSION 0x0103
60 #define IETP_NSM_VERSION 0x0104
61 #define IETP_TRACENUM 0x0105
62 #define IETP_MAX_X_AXIS 0x0106
63 #define IETP_MAX_Y_AXIS 0x0107
64 #define IETP_RESOLUTION 0x0108
65 #define IETP_PRESSURE 0x010A
66
67 #define IETP_CONTROL 0x0300
68 #define IETP_CTRL_ABSOLUTE 0x0001
69 #define IETP_CTRL_STANDARD 0x0000
70
71 #define IETP_REPORT_LEN_LO 31
72 #define IETP_REPORT_LEN_HI 36
73 #define IETP_MAX_FINGERS 5
74
75 #define IETP_REPORT_ID_LO 0x5D
76 #define IETP_REPORT_ID_HI 0x60
77
78 #define IETP_TOUCH_INFO 0
79 #define IETP_FINGER_DATA 1
80 #define IETP_FINGER_DATA_LEN 5
81 #define IETP_WH_DATA 31
82
83 #define IETP_TOUCH_LMB (1 << 0)
84 #define IETP_TOUCH_RMB (1 << 1)
85 #define IETP_TOUCH_MMB (1 << 2)
86
87 #define IETP_MAX_PRESSURE 255
88 #define IETP_FWIDTH_REDUCE 90
89 #define IETP_PRESSURE_BASE 25
90
91 int ietp_match(struct device *, void *, void *);
92 void ietp_attach(struct device *, struct device *, void *);
93 int ietp_detach(struct device *, int);
94 int ietp_activate(struct device *, int);
95
96 int ietp_intr(void *);
97 int ietp_reset(struct ietp_softc *);
98
99 int ietp_fetch_descriptor(struct ietp_softc *sc);
100 int ietp_set_power(struct ietp_softc *sc, int power);
101 int ietp_reset_cmd(struct ietp_softc *sc);
102
103 int32_t ietp_res2dpmm(uint8_t, bool);
104
105 int ietp_iic_read_reg(struct ietp_softc *, uint16_t, size_t, void *);
106 int ietp_iic_write_reg(struct ietp_softc *, uint16_t, uint16_t);
107 int ietp_iic_set_absolute_mode(struct ietp_softc *, bool);
108
109 const struct cfattach ietp_ca = {
110 sizeof(struct ietp_softc),
111 ietp_match,
112 ietp_attach,
113 ietp_detach,
114 ietp_activate,
115 };
116
117 const struct wsmouse_accessops ietp_mouse_access = {
118 ietp_enable,
119 ietp_ioctl,
120 ietp_disable
121 };
122
123 struct cfdriver ietp_cd = {
124 NULL, "ietp", DV_DULL
125 };
126
127 int
ietp_match(struct device * parent,void * match,void * aux)128 ietp_match(struct device *parent, void *match, void *aux)
129 {
130 struct i2c_attach_args *ia = aux;
131
132 if (strcmp(ia->ia_name, "ietp") == 0)
133 return (1);
134
135 return (0);
136 }
137
138 int32_t
ietp_res2dpmm(uint8_t res,bool hi_precision)139 ietp_res2dpmm(uint8_t res, bool hi_precision)
140 {
141 int32_t dpi;
142
143 dpi = hi_precision ? 300 + res * 100 : 790 + res * 10;
144
145 return (dpi * 10 /254);
146 }
147
148 void
ietp_attach(struct device * parent,struct device * self,void * aux)149 ietp_attach(struct device *parent, struct device *self, void *aux)
150 {
151 struct ietp_softc *sc = (struct ietp_softc *)self;
152 struct i2c_attach_args *ia = aux;
153 uint16_t buf, reg;
154 uint8_t *buf8;
155 uint8_t pattern;
156 struct wsmousedev_attach_args a;
157 struct wsmousehw *hw;
158
159 sc->sc_tag = ia->ia_tag;
160 sc->sc_addr = ia->ia_addr;
161
162 ietp_fetch_descriptor(sc);
163
164 if (ia->ia_intr) {
165 printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr));
166
167 sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr,
168 IPL_TTY, ietp_intr, sc, sc->sc_dev.dv_xname);
169 if (sc->sc_ih == NULL) {
170 printf(", can't establish interrupt\n");
171 return;
172 }
173 }
174
175 sc->sc_buttons = 0;
176 sc->sc_refcnt = 0;
177
178 buf8 = (uint8_t *)&buf;
179
180 if (ietp_iic_read_reg(sc, IETP_UNIQUEID, sizeof(buf), &buf) != 0) {
181 printf(": failed reading product ID\n");
182 return;
183 }
184 sc->product_id = le16toh(buf);
185
186 if (ietp_iic_read_reg(sc, IETP_PATTERN, sizeof(buf), &buf) != 0) {
187 printf(": failed reading pattern\n");
188 return;
189 }
190 pattern = buf == 0xFFFF ? 0 : buf8[1];
191 sc->hi_precision = pattern >= 0x02;
192
193 reg = pattern >= 0x01 ? IETP_IC_TYPE : IETP_OSM_VERSION;
194 if (ietp_iic_read_reg(sc, reg, sizeof(buf), &buf) != 0) {
195 printf(": failed reading IC type\n");
196 return;
197 }
198 sc->ic_type = pattern >= 0x01 ? be16toh(buf) : buf8[1];
199
200 if (ietp_iic_read_reg(sc, IETP_NSM_VERSION, sizeof(buf), &buf) != 0) {
201 printf(": failed reading SM version\n");
202 return;
203 }
204 sc->is_clickpad = (buf8[0] & 0x10) != 0;
205
206 if (ietp_iic_set_absolute_mode(sc, true) != 0) {
207 printf(": failed to set absolute mode\n");
208 return;
209 }
210
211 if (ietp_iic_read_reg(sc, IETP_MAX_X_AXIS, sizeof(buf), &buf) != 0) {
212 printf(": failed reading max x\n");
213 return;
214 }
215 sc->max_x = le16toh(buf);
216
217 if (ietp_iic_read_reg(sc, IETP_MAX_Y_AXIS, sizeof(buf), &buf) != 0) {
218 printf(": failed reading max y\n");
219 return;
220 }
221 sc->max_y = le16toh(buf);
222
223 if (ietp_iic_read_reg(sc, IETP_TRACENUM, sizeof(buf), &buf) != 0) {
224 printf(": failed reading trace info\n");
225 return;
226 }
227 sc->trace_x = sc->max_x / buf8[0];
228 sc->trace_y = sc->max_y / buf8[1];
229
230 if (ietp_iic_read_reg(sc, IETP_PRESSURE, sizeof(buf), &buf) != 0) {
231 printf(": failed reading pressure format\n");
232 return;
233 }
234 sc->pressure_base = (buf8[0] & 0x10) ? 0 : IETP_PRESSURE_BASE;
235
236 if (ietp_iic_read_reg(sc, IETP_RESOLUTION, sizeof(buf), &buf) != 0) {
237 printf(": failed reading resolution\n");
238 return;
239 }
240 /* Conversion from internal format to dot per mm */
241 sc->res_x = ietp_res2dpmm(buf8[0], sc->hi_precision);
242 sc->res_y = ietp_res2dpmm(buf8[1], sc->hi_precision);
243
244 sc->report_id = sc->hi_precision ?
245 IETP_REPORT_ID_HI : IETP_REPORT_ID_LO;
246 sc->report_len = sc->hi_precision ?
247 IETP_REPORT_LEN_HI : IETP_REPORT_LEN_LO;
248
249 sc->sc_ibuf = malloc(IETP_REPORT_LEN_HI + 12, M_DEVBUF,
250 M_NOWAIT | M_ZERO);
251 sc->sc_isize = sc->report_len + 3;
252
253 a.accessops = &ietp_mouse_access;
254 a.accesscookie = sc;
255 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
256
257 hw = wsmouse_get_hw(sc->sc_wsmousedev);
258 hw->type = WSMOUSE_TYPE_TOUCHPAD;
259 hw->hw_type = sc->is_clickpad ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD;
260 hw->x_min = 0;
261 hw->x_max = sc->max_x;
262 hw->y_min = 0;
263 hw->y_max = sc->max_y;
264 hw->h_res = sc->res_x;
265 hw->v_res = sc->res_y;
266 hw->mt_slots = IETP_MAX_FINGERS;
267
268 wsmouse_configure(sc->sc_wsmousedev, NULL, 0);
269
270 /* power down until we're opened */
271 if (ietp_set_power(sc, I2C_HID_POWER_OFF)) {
272 printf(": failed to power down\n");
273 return;
274 }
275
276 printf("\n");
277
278 DPRINTF(("%s: max_x=%d, max_y=%d, %s\n", sc->sc_dev.dv_xname,
279 sc->max_x, sc->max_y,
280 sc->is_clickpad ? "clickpad" : "touchpad"));
281
282 return;
283 }
284
285 int
ietp_detach(struct device * self,int flags)286 ietp_detach(struct device *self, int flags)
287 {
288 struct ietp_softc *sc = (struct ietp_softc *)self;
289
290 if (sc->sc_ih != NULL) {
291 iic_intr_disestablish(sc->sc_tag, sc->sc_ih);
292 sc->sc_ih = NULL;
293 }
294
295 if (sc->sc_ibuf != NULL) {
296 free(sc->sc_ibuf, M_DEVBUF, sc->sc_isize);
297 sc->sc_ibuf = NULL;
298 }
299
300 return (0);
301 }
302
303 int
ietp_activate(struct device * self,int act)304 ietp_activate(struct device *self, int act)
305 {
306 struct ietp_softc *sc = (struct ietp_softc *)self;
307 int rv;
308
309 DPRINTF(("%s(%d)\n", __func__, act));
310
311 switch (act) {
312 case DVACT_QUIESCE:
313 rv = config_activate_children(self, act);
314 sc->sc_dying = 1;
315 if (ietp_set_power(sc, I2C_HID_POWER_OFF))
316 printf("%s: failed to power down\n",
317 sc->sc_dev.dv_xname);
318 break;
319 case DVACT_WAKEUP:
320 ietp_reset(sc);
321 sc->sc_dying = 0;
322 rv = config_activate_children(self, act);
323 break;
324 default:
325 rv = config_activate_children(self, act);
326 break;
327 }
328 return rv;
329 }
330
331 void
ietp_sleep(struct ietp_softc * sc,int ms)332 ietp_sleep(struct ietp_softc *sc, int ms)
333 {
334 if (cold)
335 delay(ms * 1000);
336 else
337 tsleep_nsec(&sc, PWAIT, "ietp", MSEC_TO_NSEC(ms));
338 }
339
340 int
ietp_iic_set_absolute_mode(struct ietp_softc * sc,bool enable)341 ietp_iic_set_absolute_mode(struct ietp_softc *sc, bool enable)
342 {
343 static const struct {
344 uint16_t ic_type;
345 uint16_t product_id;
346 } special_fw[] = {
347 { 0x0E, 0x05 }, { 0x0E, 0x06 }, { 0x0E, 0x07 }, { 0x0E, 0x09 },
348 { 0x0E, 0x13 }, { 0x08, 0x26 },
349 };
350 uint16_t val;
351 int i, error;
352 bool require_wakeup;
353
354 error = 0;
355
356 /*
357 * Some ASUS touchpads need to be powered on to enter absolute mode.
358 */
359 require_wakeup = false;
360 for (i = 0; i < nitems(special_fw); i++) {
361 if (sc->ic_type == special_fw[i].ic_type &&
362 sc->product_id == special_fw[i].product_id) {
363 require_wakeup = true;
364 break;
365 }
366 }
367
368 if (require_wakeup && ietp_set_power(sc, I2C_HID_POWER_ON) != 0) {
369 printf("%s: failed writing poweron command\n",
370 sc->sc_dev.dv_xname);
371 return (EIO);
372 }
373
374 val = enable ? IETP_CTRL_ABSOLUTE : IETP_CTRL_STANDARD;
375 if (ietp_iic_write_reg(sc, IETP_CONTROL, val) != 0) {
376 printf("%s: failed setting absolute mode\n",
377 sc->sc_dev.dv_xname);
378 error = EIO;
379 }
380
381 if (require_wakeup && ietp_set_power(sc, I2C_HID_POWER_OFF) != 0) {
382 printf("%s: failed writing poweroff command\n",
383 sc->sc_dev.dv_xname);
384 error = EIO;
385 }
386
387 return (error);
388 }
389
390 int
ietp_iic_read_reg(struct ietp_softc * sc,uint16_t reg,size_t len,void * val)391 ietp_iic_read_reg(struct ietp_softc *sc, uint16_t reg, size_t len, void *val)
392 {
393 uint8_t cmd[] = {
394 reg & 0xff,
395 reg >> 8,
396 };
397
398 return iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
399 &cmd, 2, val, len, 0);
400 }
401
402 int
ietp_iic_write_reg(struct ietp_softc * sc,uint16_t reg,uint16_t val)403 ietp_iic_write_reg(struct ietp_softc *sc, uint16_t reg, uint16_t val)
404 {
405 uint8_t cmd[] = {
406 reg & 0xff,
407 reg >> 8,
408 val & 0xff,
409 val >> 8,
410 };
411
412 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
413 &cmd, 4, NULL, 0, 0);
414 }
415
416 int
ietp_set_power(struct ietp_softc * sc,int power)417 ietp_set_power(struct ietp_softc *sc, int power)
418 {
419 int res = 1;
420 uint8_t cmd[] = {
421 htole16(sc->hid_desc.wCommandRegister) & 0xff,
422 htole16(sc->hid_desc.wCommandRegister) >> 8,
423 power,
424 I2C_HID_CMD_SET_POWER,
425 };
426
427 iic_acquire_bus(sc->sc_tag, 0);
428
429 DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n",
430 sc->sc_dev.dv_xname, power));
431
432 /* 22 00 00 08 */
433 res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
434 &cmd, sizeof(cmd), NULL, 0, 0);
435
436 iic_release_bus(sc->sc_tag, 0);
437
438 return (res);
439 }
440
441 int
ietp_reset_cmd(struct ietp_softc * sc)442 ietp_reset_cmd(struct ietp_softc *sc)
443 {
444 int res = 1;
445 uint8_t cmd[] = {
446 htole16(sc->hid_desc.wCommandRegister) & 0xff,
447 htole16(sc->hid_desc.wCommandRegister) >> 8,
448 0,
449 I2C_HID_CMD_RESET,
450 };
451
452 iic_acquire_bus(sc->sc_tag, 0);
453
454 DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n",
455 sc->sc_dev.dv_xname));
456
457 /* 22 00 00 01 */
458 res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
459 &cmd, sizeof(cmd), NULL, 0, 0);
460
461 iic_release_bus(sc->sc_tag, 0);
462
463 return (res);
464 }
465
466 int
ietp_fetch_descriptor(struct ietp_softc * sc)467 ietp_fetch_descriptor(struct ietp_softc *sc)
468 {
469 int i, res = 1;
470 /*
471 * 5.2.2 - HID Descriptor Retrieval
472 * register is passed from the controller
473 */
474 uint8_t cmd[] = {
475 1,
476 0,
477 };
478
479 iic_acquire_bus(sc->sc_tag, 0);
480
481 DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x1\n",
482 sc->sc_dev.dv_xname));
483
484 /* 20 00 */
485 res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
486 &cmd, sizeof(cmd), &sc->hid_desc_buf,
487 sizeof(struct i2c_hid_desc), 0);
488
489 DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname));
490 for (i = 0; i < sizeof(struct i2c_hid_desc); i++)
491 DPRINTF((" %.2x", sc->hid_desc_buf[i]));
492 DPRINTF(("\n"));
493
494 iic_release_bus(sc->sc_tag, 0);
495
496 return (res);
497 }
498
499 int
ietp_reset(struct ietp_softc * sc)500 ietp_reset(struct ietp_softc *sc)
501 {
502 DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname));
503
504 if (ietp_set_power(sc, I2C_HID_POWER_ON)) {
505 printf("%s: failed to power on\n", sc->sc_dev.dv_xname);
506 return (1);
507 }
508
509 ietp_sleep(sc, 100);
510
511 if (ietp_reset_cmd(sc)) {
512 printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname);
513
514 ietp_set_power(sc, I2C_HID_POWER_OFF);
515
516 return (1);
517 }
518
519 ietp_sleep(sc, 100);
520
521 return (0);
522 }
523
524 void
parse_input(struct ietp_softc * sc,u_char * report,int len)525 parse_input(struct ietp_softc *sc, u_char *report, int len)
526 {
527 uint8_t *fdata;
528 int32_t finger;
529 int32_t x, y, p;
530 int buttons = 0;
531 int s;
532
533 /* we seem to get 0 length reports sometimes, ignore them */
534 if (len == 0)
535 return;
536 if (len != sc->report_len) {
537 printf("%s: wrong report length (%d vs %d expected)",
538 sc->sc_dev.dv_xname, len, (int) sc->report_len);
539 return;
540 }
541
542 s = spltty();
543
544 buttons = report[IETP_TOUCH_INFO] & 7;
545
546 if (sc->sc_buttons != buttons) {
547 wsmouse_buttons(sc->sc_wsmousedev, buttons);
548 sc->sc_buttons = buttons;
549 }
550
551 for (finger = 0, fdata = report + IETP_FINGER_DATA;
552 finger < IETP_MAX_FINGERS;
553 finger++, fdata += IETP_FINGER_DATA_LEN) {
554 if ((report[IETP_TOUCH_INFO] & (1 << (finger + 3))) != 0) {
555 if (sc->hi_precision) {
556 x = fdata[0] << 8 | fdata[1];
557 y = fdata[2] << 8 | fdata[3];
558 } else {
559 x = (fdata[0] & 0xf0) << 4 | fdata[1];
560 y = (fdata[0] & 0x0f) << 8 | fdata[2];
561 }
562
563 if (x > sc->max_x || y > sc->max_y) {
564 printf("%s: [%d] x=%d y=%d over max (%d, %d)\n",
565 sc->sc_dev.dv_xname, finger, x, y,
566 sc->max_x, sc->max_y);
567 continue;
568 }
569
570
571 p = MIN((int32_t)fdata[4] + sc->pressure_base,
572 IETP_MAX_PRESSURE);
573
574 } else {
575 x = 0;
576 y = 0;
577 p = 0;
578 }
579
580 DPRINTF(("position: [finger=%d, x=%d, y=%d, p=%d]\n", finger,
581 x, y, p));
582 wsmouse_mtstate(sc->sc_wsmousedev, finger, x, y, p);
583 }
584
585 wsmouse_input_sync(sc->sc_wsmousedev);
586
587 splx(s);
588 }
589
590 int
ietp_intr(void * arg)591 ietp_intr(void *arg)
592 {
593 struct ietp_softc *sc = arg;
594 int psize, i;
595 u_char *p;
596 u_int rep = 0;
597
598 if (sc->sc_dying)
599 return 1;
600
601 /*
602 * XXX: force I2C_F_POLL for now to avoid dwiic interrupting
603 * while we are interrupting
604 */
605
606 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
607 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0,
608 sc->sc_ibuf, letoh16(sc->hid_desc.wMaxInputLength), I2C_F_POLL);
609 iic_release_bus(sc->sc_tag, I2C_F_POLL);
610
611 /*
612 * 6.1.1 - First two bytes are the packet length, which must be less
613 * than or equal to wMaxInputLength
614 */
615 psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8;
616 if (psize <= 2 || psize > sc->sc_isize) {
617 DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n",
618 sc->sc_dev.dv_xname, __func__, psize,
619 sc->sc_isize));
620 return (1);
621 }
622
623 /* 3rd byte is the report id */
624 p = sc->sc_ibuf + 2;
625 psize -= 2;
626 rep = *p++;
627 psize--;
628
629 DPRINTF(("%s: %s: hid input (rep 0x%x):", sc->sc_dev.dv_xname, __func__,
630 rep));
631 for (i = 0; i < psize; i++) {
632 DPRINTF((" %.2x", p[i]));
633 }
634 DPRINTF(("\n"));
635
636 if (sc->sc_refcnt && rep == sc->report_id) {
637 parse_input(sc, p, psize);
638 }
639
640 return (1);
641 }
642
643 int
ietp_enable(void * dev)644 ietp_enable(void *dev)
645 {
646 struct ietp_softc *sc = dev;
647
648 DPRINTF(("%s: %s: refcnt=%d\n", sc->sc_dev.dv_xname,
649 __func__, sc->sc_refcnt));
650
651 if (sc->sc_refcnt++ || sc->sc_isize == 0)
652 return (0);
653
654 /* power on */
655 ietp_reset(sc);
656
657 return (0);
658 }
659
660 void
ietp_disable(void * dev)661 ietp_disable(void *dev)
662 {
663 struct ietp_softc *sc = dev;
664 DPRINTF(("%s: %s: refcnt=%d\n", sc->sc_dev.dv_xname,
665 __func__, sc->sc_refcnt));
666
667 if (--sc->sc_refcnt)
668 return;
669
670 /* no sub-devices open, conserve power */
671
672 if (ietp_set_power(sc, I2C_HID_POWER_OFF))
673 printf("%s: failed to power down\n", sc->sc_dev.dv_xname);
674 }
675
676 int
ietp_ioctl(void * dev,u_long cmd,caddr_t data,int flag,struct proc * p)677 ietp_ioctl(void *dev, u_long cmd, caddr_t data, int flag,
678 struct proc *p)
679 {
680 struct ietp_softc *sc = dev;
681 struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
682
683 switch (cmd) {
684 case WSMOUSEIO_GTYPE:
685 *(u_int *)data = WSMOUSE_TYPE_TOUCHPAD;
686 return 0;
687
688 case WSMOUSEIO_GCALIBCOORDS:
689 wsmc->minx = 0;
690 wsmc->maxx = sc->max_x;
691 wsmc->miny = 0;
692 wsmc->maxy = sc->max_y;
693 wsmc->swapxy = 0;
694 wsmc->resx = sc->res_x;
695 wsmc->resy = sc->res_y;
696 return 0;
697 }
698 return -1;
699 }
700