FreeBSD/Linux Kernel Cross Reference
sys/dev/hid/ietp.c
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2020, 2022 Vladimir Kondratyev <wulf@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 /*
29 * Elan I2C Touchpad driver. Based on Linux driver.
30 * https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/input/mouse/elan_i2c_core.c
31 */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/bus.h>
38 #include <sys/endian.h>
39 #include <sys/kernel.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/sysctl.h>
45 #include <sys/systm.h>
46
47 #include <dev/evdev/evdev.h>
48 #include <dev/evdev/input.h>
49
50 #include <dev/iicbus/iic.h>
51 #include <dev/iicbus/iicbus.h>
52
53 #define HID_DEBUG_VAR ietp_debug
54 #include <dev/hid/hid.h>
55 #include <dev/hid/hidbus.h>
56 #include <dev/hid/hidquirk.h>
57
58 #ifdef HID_DEBUG
59 static SYSCTL_NODE(_hw_hid, OID_AUTO, ietp, CTLFLAG_RW, 0,
60 "Elantech Touchpad");
61 static int ietp_debug = 1;
62 SYSCTL_INT(_hw_hid_ietp, OID_AUTO, debug, CTLFLAG_RWTUN,
63 &ietp_debug, 1, "Debug level");
64 #endif
65
66 #define IETP_PATTERN 0x0100
67 #define IETP_UNIQUEID 0x0101
68 #define IETP_FW_VERSION 0x0102
69 #define IETP_IC_TYPE 0x0103
70 #define IETP_OSM_VERSION 0x0103
71 #define IETP_NSM_VERSION 0x0104
72 #define IETP_TRACENUM 0x0105
73 #define IETP_MAX_X_AXIS 0x0106
74 #define IETP_MAX_Y_AXIS 0x0107
75 #define IETP_RESOLUTION 0x0108
76 #define IETP_PRESSURE 0x010A
77
78 #define IETP_CONTROL 0x0300
79 #define IETP_CTRL_ABSOLUTE 0x0001
80 #define IETP_CTRL_STANDARD 0x0000
81
82 #define IETP_REPORT_LEN_LO 32
83 #define IETP_REPORT_LEN_HI 37
84 #define IETP_MAX_FINGERS 5
85
86 #define IETP_REPORT_ID_LO 0x5D
87 #define IETP_REPORT_ID_HI 0x60
88
89 #define IETP_TOUCH_INFO 1
90 #define IETP_FINGER_DATA 2
91 #define IETP_FINGER_DATA_LEN 5
92 #define IETP_HOVER_INFO 28
93 #define IETP_WH_DATA 31
94
95 #define IETP_TOUCH_LMB (1 << 0)
96 #define IETP_TOUCH_RMB (1 << 1)
97 #define IETP_TOUCH_MMB (1 << 2)
98
99 #define IETP_MAX_PRESSURE 255
100 #define IETP_FWIDTH_REDUCE 90
101 #define IETP_FINGER_MAX_WIDTH 15
102 #define IETP_PRESSURE_BASE 25
103
104 struct ietp_softc {
105 device_t dev;
106
107 struct evdev_dev *evdev;
108 uint8_t report_id;
109 hid_size_t report_len;
110
111 uint16_t product_id;
112 uint16_t ic_type;
113
114 int32_t pressure_base;
115 uint16_t max_x;
116 uint16_t max_y;
117 uint16_t trace_x;
118 uint16_t trace_y;
119 uint16_t res_x; /* dots per mm */
120 uint16_t res_y;
121 bool hi_precission;
122 bool is_clickpad;
123 bool has_3buttons;
124 };
125
126 static evdev_open_t ietp_ev_open;
127 static evdev_close_t ietp_ev_close;
128 static hid_intr_t ietp_intr;
129
130 static int ietp_probe(struct ietp_softc *);
131 static int ietp_attach(struct ietp_softc *);
132 static int ietp_detach(struct ietp_softc *);
133 static int32_t ietp_res2dpmm(uint8_t, bool);
134
135 static device_probe_t ietp_iic_probe;
136 static device_attach_t ietp_iic_attach;
137 static device_detach_t ietp_iic_detach;
138 static device_resume_t ietp_iic_resume;
139
140 static int ietp_iic_read_reg(device_t, uint16_t, size_t, void *);
141 static int ietp_iic_write_reg(device_t, uint16_t, uint16_t);
142 static int ietp_iic_set_absolute_mode(device_t, bool);
143
144 #define IETP_IIC_DEV(pnp) \
145 { HID_TLC(HUP_GENERIC_DESKTOP, HUG_MOUSE), HID_BUS(BUS_I2C), HID_PNP(pnp) }
146
147 static const struct hid_device_id ietp_iic_devs[] = {
148 IETP_IIC_DEV("ELAN0000"),
149 IETP_IIC_DEV("ELAN0100"),
150 IETP_IIC_DEV("ELAN0600"),
151 IETP_IIC_DEV("ELAN0601"),
152 IETP_IIC_DEV("ELAN0602"),
153 IETP_IIC_DEV("ELAN0603"),
154 IETP_IIC_DEV("ELAN0604"),
155 IETP_IIC_DEV("ELAN0605"),
156 IETP_IIC_DEV("ELAN0606"),
157 IETP_IIC_DEV("ELAN0607"),
158 IETP_IIC_DEV("ELAN0608"),
159 IETP_IIC_DEV("ELAN0609"),
160 IETP_IIC_DEV("ELAN060B"),
161 IETP_IIC_DEV("ELAN060C"),
162 IETP_IIC_DEV("ELAN060F"),
163 IETP_IIC_DEV("ELAN0610"),
164 IETP_IIC_DEV("ELAN0611"),
165 IETP_IIC_DEV("ELAN0612"),
166 IETP_IIC_DEV("ELAN0615"),
167 IETP_IIC_DEV("ELAN0616"),
168 IETP_IIC_DEV("ELAN0617"),
169 IETP_IIC_DEV("ELAN0618"),
170 IETP_IIC_DEV("ELAN0619"),
171 IETP_IIC_DEV("ELAN061A"),
172 IETP_IIC_DEV("ELAN061B"),
173 IETP_IIC_DEV("ELAN061C"),
174 IETP_IIC_DEV("ELAN061D"),
175 IETP_IIC_DEV("ELAN061E"),
176 IETP_IIC_DEV("ELAN061F"),
177 IETP_IIC_DEV("ELAN0620"),
178 IETP_IIC_DEV("ELAN0621"),
179 IETP_IIC_DEV("ELAN0622"),
180 IETP_IIC_DEV("ELAN0623"),
181 IETP_IIC_DEV("ELAN0624"),
182 IETP_IIC_DEV("ELAN0625"),
183 IETP_IIC_DEV("ELAN0626"),
184 IETP_IIC_DEV("ELAN0627"),
185 IETP_IIC_DEV("ELAN0628"),
186 IETP_IIC_DEV("ELAN0629"),
187 IETP_IIC_DEV("ELAN062A"),
188 IETP_IIC_DEV("ELAN062B"),
189 IETP_IIC_DEV("ELAN062C"),
190 IETP_IIC_DEV("ELAN062D"),
191 IETP_IIC_DEV("ELAN062E"), /* Lenovo V340 Whiskey Lake U */
192 IETP_IIC_DEV("ELAN062F"), /* Lenovo V340 Comet Lake U */
193 IETP_IIC_DEV("ELAN0631"),
194 IETP_IIC_DEV("ELAN0632"),
195 IETP_IIC_DEV("ELAN0633"), /* Lenovo S145 */
196 IETP_IIC_DEV("ELAN0634"), /* Lenovo V340 Ice lake */
197 IETP_IIC_DEV("ELAN0635"), /* Lenovo V1415-IIL */
198 IETP_IIC_DEV("ELAN0636"), /* Lenovo V1415-Dali */
199 IETP_IIC_DEV("ELAN0637"), /* Lenovo V1415-IGLR */
200 IETP_IIC_DEV("ELAN1000"),
201 };
202
203 static const struct evdev_methods ietp_evdev_methods = {
204 .ev_open = &ietp_ev_open,
205 .ev_close = &ietp_ev_close,
206 };
207
208 static int
209 ietp_ev_open(struct evdev_dev *evdev)
210 {
211 return (hidbus_intr_start(evdev_get_softc(evdev)));
212 }
213
214 static int
215 ietp_ev_close(struct evdev_dev *evdev)
216 {
217 return (hidbus_intr_stop(evdev_get_softc(evdev)));
218 }
219
220 static int
221 ietp_probe(struct ietp_softc *sc)
222 {
223 if (hidbus_find_child(device_get_parent(sc->dev),
224 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD)) != NULL) {
225 DPRINTFN(5, "Ignore HID-compatible touchpad on %s\n",
226 device_get_nameunit(device_get_parent(sc->dev)));
227 return (ENXIO);
228 }
229
230 device_set_desc(sc->dev, "Elan Touchpad");
231
232 return (BUS_PROBE_DEFAULT);
233 }
234
235 static int
236 ietp_attach(struct ietp_softc *sc)
237 {
238 const struct hid_device_info *hw = hid_get_device_info(sc->dev);
239 void *d_ptr;
240 hid_size_t d_len;
241 int32_t minor, major;
242 int error;
243
244 sc->report_id = sc->hi_precission ?
245 IETP_REPORT_ID_HI : IETP_REPORT_ID_LO;
246 sc->report_len = sc->hi_precission ?
247 IETP_REPORT_LEN_HI : IETP_REPORT_LEN_LO;
248
249 /* Try to detect 3-rd button by relative mouse TLC */
250 if (!sc->is_clickpad) {
251 error = hid_get_report_descr(sc->dev, &d_ptr, &d_len);
252 if (error != 0) {
253 device_printf(sc->dev, "could not retrieve report "
254 "descriptor from device: %d\n", error);
255 return (ENXIO);
256 }
257 if (hidbus_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, 3),
258 hid_input, hidbus_get_index(sc->dev), 0, NULL, NULL, NULL,
259 NULL))
260 sc->has_3buttons = true;
261 }
262
263 sc->evdev = evdev_alloc();
264 evdev_set_name(sc->evdev, device_get_desc(sc->dev));
265 evdev_set_phys(sc->evdev, device_get_nameunit(sc->dev));
266 evdev_set_id(sc->evdev, hw->idBus, hw->idVendor, hw->idProduct,
267 hw->idVersion);
268 evdev_set_serial(sc->evdev, hw->serial);
269 evdev_set_methods(sc->evdev, sc->dev, &ietp_evdev_methods);
270 evdev_set_flag(sc->evdev, EVDEV_FLAG_MT_STCOMPAT);
271 evdev_set_flag(sc->evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */
272
273 evdev_support_event(sc->evdev, EV_SYN);
274 evdev_support_event(sc->evdev, EV_ABS);
275 evdev_support_event(sc->evdev, EV_KEY);
276 evdev_support_prop(sc->evdev, INPUT_PROP_POINTER);
277 evdev_support_key(sc->evdev, BTN_LEFT);
278 if (sc->is_clickpad) {
279 evdev_support_prop(sc->evdev, INPUT_PROP_BUTTONPAD);
280 } else {
281 evdev_support_key(sc->evdev, BTN_RIGHT);
282 if (sc->has_3buttons)
283 evdev_support_key(sc->evdev, BTN_MIDDLE);
284 }
285
286 major = IETP_FINGER_MAX_WIDTH * MAX(sc->trace_x, sc->trace_y);
287 minor = IETP_FINGER_MAX_WIDTH * MIN(sc->trace_x, sc->trace_y);
288
289 evdev_support_abs(sc->evdev, ABS_MT_SLOT,
290 0, IETP_MAX_FINGERS - 1, 0, 0, 0);
291 evdev_support_abs(sc->evdev, ABS_MT_TRACKING_ID,
292 -1, IETP_MAX_FINGERS - 1, 0, 0, 0);
293 evdev_support_abs(sc->evdev, ABS_MT_POSITION_X,
294 0, sc->max_x, 0, 0, sc->res_x);
295 evdev_support_abs(sc->evdev, ABS_MT_POSITION_Y,
296 0, sc->max_y, 0, 0, sc->res_y);
297 evdev_support_abs(sc->evdev, ABS_MT_PRESSURE,
298 0, IETP_MAX_PRESSURE, 0, 0, 0);
299 evdev_support_abs(sc->evdev, ABS_MT_ORIENTATION, 0, 1, 0, 0, 0);
300 evdev_support_abs(sc->evdev, ABS_MT_TOUCH_MAJOR, 0, major, 0, 0, 0);
301 evdev_support_abs(sc->evdev, ABS_MT_TOUCH_MINOR, 0, minor, 0, 0, 0);
302 evdev_support_abs(sc->evdev, ABS_DISTANCE, 0, 1, 0, 0, 0);
303
304 error = evdev_register(sc->evdev);
305 if (error != 0) {
306 ietp_detach(sc);
307 return (ENOMEM);
308 }
309
310 hidbus_set_intr(sc->dev, ietp_intr, sc);
311
312 device_printf(sc->dev, "[%d:%d], %s\n", sc->max_x, sc->max_y,
313 sc->is_clickpad ? "clickpad" :
314 sc->has_3buttons ? "3 buttons" : "2 buttons");
315
316 return (0);
317 }
318
319 static int
320 ietp_detach(struct ietp_softc *sc)
321 {
322 evdev_free(sc->evdev);
323
324 return (0);
325 }
326
327 static void
328 ietp_intr(void *context, void *buf, hid_size_t len)
329 {
330 struct ietp_softc *sc = context;
331 union evdev_mt_slot slot_data;
332 uint8_t *report, *fdata;
333 int32_t finger;
334 int32_t x, y, w, h, wh;
335
336 /* we seem to get 0 length reports sometimes, ignore them */
337 report = buf;
338 if (*report != sc->report_id || len < sc->report_len)
339 return;
340
341 for (finger = 0, fdata = report + IETP_FINGER_DATA;
342 finger < IETP_MAX_FINGERS;
343 finger++, fdata += IETP_FINGER_DATA_LEN) {
344 if ((report[IETP_TOUCH_INFO] & (1 << (finger + 3))) != 0) {
345 if (sc->hi_precission) {
346 x = fdata[0] << 8 | fdata[1];
347 y = fdata[2] << 8 | fdata[3];
348 wh = report[IETP_WH_DATA + finger];
349 } else {
350 x = (fdata[0] & 0xf0) << 4 | fdata[1];
351 y = (fdata[0] & 0x0f) << 8 | fdata[2];
352 wh = fdata[3];
353 }
354
355 if (x > sc->max_x || y > sc->max_y) {
356 DPRINTF("[%d] x=%d y=%d over max (%d, %d)",
357 finger, x, y, sc->max_x, sc->max_y);
358 continue;
359 }
360
361 /* Reduce trace size to not treat large finger as palm */
362 w = (wh & 0x0F) * (sc->trace_x - IETP_FWIDTH_REDUCE);
363 h = (wh >> 4) * (sc->trace_y - IETP_FWIDTH_REDUCE);
364
365 slot_data = (union evdev_mt_slot) {
366 .id = finger,
367 .x = x,
368 .y = sc->max_y - y,
369 .p = MIN((int32_t)fdata[4] + sc->pressure_base,
370 IETP_MAX_PRESSURE),
371 .ori = w > h ? 1 : 0,
372 .maj = MAX(w, h),
373 .min = MIN(w, h),
374 };
375 evdev_mt_push_slot(sc->evdev, finger, &slot_data);
376 } else {
377 evdev_push_abs(sc->evdev, ABS_MT_SLOT, finger);
378 evdev_push_abs(sc->evdev, ABS_MT_TRACKING_ID, -1);
379 }
380 }
381
382 evdev_push_key(sc->evdev, BTN_LEFT,
383 report[IETP_TOUCH_INFO] & IETP_TOUCH_LMB);
384 evdev_push_key(sc->evdev, BTN_MIDDLE,
385 report[IETP_TOUCH_INFO] & IETP_TOUCH_MMB);
386 evdev_push_key(sc->evdev, BTN_RIGHT,
387 report[IETP_TOUCH_INFO] & IETP_TOUCH_RMB);
388 evdev_push_abs(sc->evdev, ABS_DISTANCE,
389 (report[IETP_HOVER_INFO] & 0x40) >> 6);
390
391 evdev_sync(sc->evdev);
392 }
393
394 static int32_t
395 ietp_res2dpmm(uint8_t res, bool hi_precission)
396 {
397 int32_t dpi;
398
399 dpi = hi_precission ? 300 + res * 100 : 790 + res * 10;
400
401 return (dpi * 10 /254);
402 }
403
404 static int
405 ietp_iic_probe(device_t dev)
406 {
407 struct ietp_softc *sc = device_get_softc(dev);
408 device_t iichid;
409 int error;
410
411 error = HIDBUS_LOOKUP_DRIVER_INFO(dev, ietp_iic_devs);
412 if (error != 0)
413 return (error);
414
415 iichid = device_get_parent(device_get_parent(dev));
416 if (device_get_devclass(iichid) != devclass_find("iichid"))
417 return (ENXIO);
418
419 sc->dev = dev;
420
421 return (ietp_probe(sc));
422 }
423
424 static int
425 ietp_iic_attach(device_t dev)
426 {
427 struct ietp_softc *sc = device_get_softc(dev);
428 uint16_t buf, reg;
429 uint8_t *buf8;
430 uint8_t pattern;
431
432 buf8 = (uint8_t *)&buf;
433
434 if (ietp_iic_read_reg(dev, IETP_UNIQUEID, sizeof(buf), &buf) != 0) {
435 device_printf(sc->dev, "failed reading product ID\n");
436 return (EIO);
437 }
438 sc->product_id = le16toh(buf);
439
440 if (ietp_iic_read_reg(dev, IETP_PATTERN, sizeof(buf), &buf) != 0) {
441 device_printf(sc->dev, "failed reading pattern\n");
442 return (EIO);
443 }
444 pattern = buf == 0xFFFF ? 0 : buf8[1];
445 sc->hi_precission = pattern >= 0x02;
446
447 reg = pattern >= 0x01 ? IETP_IC_TYPE : IETP_OSM_VERSION;
448 if (ietp_iic_read_reg(dev, reg, sizeof(buf), &buf) != 0) {
449 device_printf(sc->dev, "failed reading IC type\n");
450 return (EIO);
451 }
452 sc->ic_type = pattern >= 0x01 ? be16toh(buf) : buf8[1];
453
454 if (ietp_iic_read_reg(dev, IETP_NSM_VERSION, sizeof(buf), &buf) != 0) {
455 device_printf(sc->dev, "failed reading SM version\n");
456 return (EIO);
457 }
458 sc->is_clickpad = (buf8[0] & 0x10) != 0;
459
460 if (ietp_iic_set_absolute_mode(dev, true) != 0) {
461 device_printf(sc->dev, "failed to set absolute mode\n");
462 return (EIO);
463 }
464
465 if (ietp_iic_read_reg(dev, IETP_MAX_X_AXIS, sizeof(buf), &buf) != 0) {
466 device_printf(sc->dev, "failed reading max x\n");
467 return (EIO);
468 }
469 sc->max_x = le16toh(buf);
470
471 if (ietp_iic_read_reg(dev, IETP_MAX_Y_AXIS, sizeof(buf), &buf) != 0) {
472 device_printf(sc->dev, "failed reading max y\n");
473 return (EIO);
474 }
475 sc->max_y = le16toh(buf);
476
477 if (ietp_iic_read_reg(dev, IETP_TRACENUM, sizeof(buf), &buf) != 0) {
478 device_printf(sc->dev, "failed reading trace info\n");
479 return (EIO);
480 }
481 sc->trace_x = sc->max_x / buf8[0];
482 sc->trace_y = sc->max_y / buf8[1];
483
484 if (ietp_iic_read_reg(dev, IETP_PRESSURE, sizeof(buf), &buf) != 0) {
485 device_printf(sc->dev, "failed reading pressure format\n");
486 return (EIO);
487 }
488 sc->pressure_base = (buf8[0] & 0x10) ? 0 : IETP_PRESSURE_BASE;
489
490 if (ietp_iic_read_reg(dev, IETP_RESOLUTION, sizeof(buf), &buf) != 0) {
491 device_printf(sc->dev, "failed reading resolution\n");
492 return (EIO);
493 }
494 /* Conversion from internal format to dot per mm */
495 sc->res_x = ietp_res2dpmm(buf8[0], sc->hi_precission);
496 sc->res_y = ietp_res2dpmm(buf8[1], sc->hi_precission);
497
498 return (ietp_attach(sc));
499 }
500
501 static int
502 ietp_iic_detach(device_t dev)
503 {
504 struct ietp_softc *sc = device_get_softc(dev);
505
506 if (ietp_iic_set_absolute_mode(dev, false) != 0)
507 device_printf(dev, "failed setting standard mode\n");
508
509 return (ietp_detach(sc));
510 }
511
512 static int
513 ietp_iic_resume(device_t dev)
514 {
515 if (ietp_iic_set_absolute_mode(dev, true) != 0) {
516 device_printf(dev, "reset when resuming failed: \n");
517 return (EIO);
518 }
519
520 return (0);
521 }
522
523 static int
524 ietp_iic_set_absolute_mode(device_t dev, bool enable)
525 {
526 struct ietp_softc *sc = device_get_softc(dev);
527 static const struct {
528 uint16_t ic_type;
529 uint16_t product_id;
530 } special_fw[] = {
531 { 0x0E, 0x05 }, { 0x0E, 0x06 }, { 0x0E, 0x07 }, { 0x0E, 0x09 },
532 { 0x0E, 0x13 }, { 0x08, 0x26 },
533 };
534 uint16_t val;
535 int i, error;
536 bool require_wakeup;
537
538 error = 0;
539
540 /*
541 * Some ASUS touchpads need to be powered on to enter absolute mode.
542 */
543 require_wakeup = false;
544 for (i = 0; i < nitems(special_fw); i++) {
545 if (sc->ic_type == special_fw[i].ic_type &&
546 sc->product_id == special_fw[i].product_id) {
547 require_wakeup = true;
548 break;
549 }
550 }
551
552 if (require_wakeup && hidbus_intr_start(dev) != 0) {
553 device_printf(dev, "failed writing poweron command\n");
554 return (EIO);
555 }
556
557 val = enable ? IETP_CTRL_ABSOLUTE : IETP_CTRL_STANDARD;
558 if (ietp_iic_write_reg(dev, IETP_CONTROL, val) != 0) {
559 device_printf(dev, "failed setting absolute mode\n");
560 error = EIO;
561 }
562
563 if (require_wakeup && hidbus_intr_stop(dev) != 0) {
564 device_printf(dev, "failed writing poweroff command\n");
565 error = EIO;
566 }
567
568 return (error);
569 }
570
571 static int
572 ietp_iic_read_reg(device_t dev, uint16_t reg, size_t len, void *val)
573 {
574 device_t iichid = device_get_parent(device_get_parent(dev));
575 uint16_t addr = iicbus_get_addr(iichid) << 1;
576 uint8_t cmd[2] = { reg & 0xff, (reg >> 8) & 0xff };
577 struct iic_msg msgs[2] = {
578 { addr, IIC_M_WR | IIC_M_NOSTOP, sizeof(cmd), cmd },
579 { addr, IIC_M_RD, len, val },
580 };
581 struct iic_rdwr_data ird = { msgs, nitems(msgs) };
582 int error;
583
584 DPRINTF("Read reg 0x%04x with size %zu\n", reg, len);
585
586 error = hid_ioctl(dev, I2CRDWR, (uintptr_t)&ird);
587 if (error != 0)
588 return (error);
589
590 DPRINTF("Response: %*D\n", (int)len, val, " ");
591
592 return (0);
593 }
594
595 static int
596 ietp_iic_write_reg(device_t dev, uint16_t reg, uint16_t val)
597 {
598 device_t iichid = device_get_parent(device_get_parent(dev));
599 uint16_t addr = iicbus_get_addr(iichid) << 1;
600 uint8_t cmd[4] = { reg & 0xff, (reg >> 8) & 0xff,
601 val & 0xff, (val >> 8) & 0xff };
602 struct iic_msg msgs[1] = {
603 { addr, IIC_M_WR, sizeof(cmd), cmd },
604 };
605 struct iic_rdwr_data ird = { msgs, nitems(msgs) };
606
607 DPRINTF("Write reg 0x%04x with value 0x%04x\n", reg, val);
608
609 return (hid_ioctl(dev, I2CRDWR, (uintptr_t)&ird));
610 }
611
612 static device_method_t ietp_methods[] = {
613 DEVMETHOD(device_probe, ietp_iic_probe),
614 DEVMETHOD(device_attach, ietp_iic_attach),
615 DEVMETHOD(device_detach, ietp_iic_detach),
616 DEVMETHOD(device_resume, ietp_iic_resume),
617 DEVMETHOD_END
618 };
619
620 static driver_t ietp_driver = {
621 .name = "ietp",
622 .methods = ietp_methods,
623 .size = sizeof(struct ietp_softc),
624 };
625
626 DRIVER_MODULE(ietp, hidbus, ietp_driver, NULL, NULL);
627 MODULE_DEPEND(ietp, hidbus, 1, 1, 1);
628 MODULE_DEPEND(ietp, hid, 1, 1, 1);
629 MODULE_DEPEND(ietp, evdev, 1, 1, 1);
630 MODULE_VERSION(ietp, 1);
631 HID_PNP_INFO(ietp_iic_devs);
Cache object: 2410fe0fe180979d6133b0bfe3f199ad
|