1 /* $OpenBSD: uhidpp.c,v 1.44 2024/05/23 03:21:09 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2021 Anton Lindqvist <anton@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/mutex.h>
23 #include <sys/sensors.h>
24
25 #include <dev/usb/usb.h>
26 #include <dev/usb/usbhid.h>
27 #include <dev/usb/usbdi.h>
28 #include <dev/usb/usbdevs.h>
29 #include <dev/usb/uhidev.h>
30
31 /* #define UHIDPP_DEBUG */
32 #ifdef UHIDPP_DEBUG
33
34 #define DPRINTF(x...) do { \
35 if (uhidpp_debug) \
36 printf(x); \
37 } while (0)
38
39 #define DREPORT(prefix, repid, buf, len) do { \
40 if (uhidpp_debug) \
41 uhidd_dump_report((prefix), (repid), (buf), (len)); \
42 } while (0)
43
44 void uhidd_dump_report(const char *, uint8_t, const unsigned char *, u_int);
45
46 int uhidpp_debug = 1;
47
48 #else
49
50 #define DPRINTF(x...)
51 #define DREPORT(prefix, repid, buf, len)
52
53 #endif
54
55 #define HIDPP_REPORT_ID_SHORT 0x10
56 #define HIDPP_REPORT_ID_LONG 0x11
57
58 /*
59 * Length of reports. Note that the effective length is always +1 as
60 * uhidev_set_report() prepends the report ID.
61 */
62 #define HIDPP_REPORT_SHORT_LENGTH (7 - 1)
63 #define HIDPP_REPORT_LONG_LENGTH (20 - 1)
64
65 /*
66 * Maximum number of allowed parameters for reports. Note, the parameters always
67 * starts at offset 3 for both RAP and FAP reports.
68 */
69 #define HIDPP_REPORT_SHORT_PARAMS_MAX (HIDPP_REPORT_SHORT_LENGTH - 3)
70 #define HIDPP_REPORT_LONG_PARAMS_MAX (HIDPP_REPORT_LONG_LENGTH - 3)
71
72 #define HIDPP_DEVICE_ID_RECEIVER 0xff
73
74 #define HIDPP_FEAT_ROOT_ID 0x00
75 #define HIDPP_FEAT_ROOT_PING_FUNC 0x01
76 #define HIDPP_PING_DATA 0x5a
77
78 #define HIDPP_SET_REGISTER 0x80
79 #define HIDPP_GET_REGISTER 0x81
80 #define HIDPP_SET_LONG_REGISTER 0x82
81 #define HIDPP_GET_LONG_REGISTER 0x83
82
83 #define HIDPP_REG_ENABLE_REPORTS 0x00
84 #define HIDPP_ENABLE_REPORTS_DEVICE_BATTERY_STATUS 0x10
85 #define HIDPP_ENABLE_REPORTS_RECEIVER_WIRELESS 0x01
86 #define HIDPP_ENABLE_REPORTS_RECEIVER_SOFTWARE_PRESENT 0x08
87
88 #define HIDPP_REG_PAIRING_INFORMATION 0xb5
89
90 /* HID++ 1.0 error codes. */
91 #define HIDPP_ERROR 0x8f
92 #define HIDPP_ERROR_SUCCESS 0x00
93 #define HIDPP_ERROR_INVALID_SUBID 0x01
94 #define HIDPP_ERROR_INVALID_ADRESS 0x02
95 #define HIDPP_ERROR_INVALID_VALUE 0x03
96 #define HIDPP_ERROR_CONNECT_FAIL 0x04
97 #define HIDPP_ERROR_TOO_MANY_DEVICES 0x05
98 #define HIDPP_ERROR_ALREADY_EXISTS 0x06
99 #define HIDPP_ERROR_BUSY 0x07
100 #define HIDPP_ERROR_UNKNOWN_DEVICE 0x08
101 #define HIDPP_ERROR_RESOURCE_ERROR 0x09
102 #define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a
103 #define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b
104 #define HIDPP_ERROR_WRONG_PIN_CODE 0x0c
105
106 /*
107 * The software ID is added to feature access reports (FAP) and used to
108 * distinguish responses from notifications. Note, the software ID must be
109 * greater than zero which is reserved for notifications.
110 * The effective software ID round robins within its allowed interval [1, 15]
111 * making it easier to correlate requests and responses.
112 */
113 #define HIDPP_SOFTWARE_ID_MIN 1
114 #define HIDPP_SOFTWARE_ID_MAX 15
115 #define HIDPP_SOFTWARE_ID_LEN 4
116
117 #define HIDPP20_FEAT_ROOT_ID 0x0000
118 #define HIDPP20_FEAT_ROOT_GET_FEATURE_FUNC 0x0000
119
120 #define HIDPP20_FEAT_FEATURE_ID 0x0001
121 #define HIDPP20_FEAT_FEATURE_COUNT_FUNC 0x0000
122 #define HIDPP20_FEAT_FEATURE_ID_FUNC 0x0001
123
124 #define HIDPP20_FEAT_BATTERY_ID 0x1000
125 #define HIDPP20_FEAT_BATTERY_LEVEL_STATUS_FUNC 0x0000
126 #define HIDPP20_LEVEL_STATUS_CHARGING_DONE 0x0003
127 #define HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC 0x0001
128 #define HIDPP20_CAPABILITY_RECHARGEABLE 0x0004
129
130 #define HIDPP20_FEAT_UNIFIED_BATTERY_ID 0x1004
131 #define HIDPP20_FEAT_UNIFIED_BATTERY_CAPABILITIES_FUNC 0x0000
132 #define HIDPP20_CAPABILITES_RECHARGEABLE 0x0002
133 #define HIDPP20_FEAT_UNIFIED_BATTERY_STATUS_FUNC 0x0001
134 #define HIDPP20_BATTERY_STATUS_CRITICAL 0x0001
135 #define HIDPP20_BATTERY_STATUS_LOW 0x0002
136 #define HIDPP20_BATTERY_STATUS_GOOD 0x0004
137 #define HIDPP20_BATTERY_STATUS_FULL 0x0008
138
139 /* HID++ 2.0 error codes. */
140 #define HIDPP20_ERROR 0xff
141 #define HIDPP20_ERROR_NO_ERROR 0x00
142 #define HIDPP20_ERROR_UNKNOWN 0x01
143 #define HIDPP20_ERROR_INVALID_ARGUMENT 0x02
144 #define HIDPP20_ERROR_OUT_OF_RANGE 0x03
145 #define HIDPP20_ERROR_HARDWARE_ERROR 0x04
146 #define HIDPP20_ERROR_LOGITECH_INTERNAL 0x05
147 #define HIDPP20_ERROR_INVALID_FEATURE_INDEX 0x06
148 #define HIDPP20_ERROR_INVALID_FUNCTION_ID 0x07
149 #define HIDPP20_ERROR_BUSY 0x08
150 #define HIDPP20_ERROR_UNSUPPORTED 0x09
151
152 /*
153 * Sentinels used for interrupt response synchronization. The values must be
154 * disjoint from existing report IDs.
155 */
156 #define UHIDPP_RESP_NONE 0
157 #define UHIDPP_RESP_WAIT 1
158 #define UHIDPP_RESP_ERROR 2
159
160 /* Maximum number of devices associated with a single receiver. */
161 #define UHIDPP_NDEVICES 6
162
163 /* Maximum number of pending notifications. */
164 #define UHIDPP_NNOTIFICATIONS 4
165
166 /* Number of sensors per paired device. */
167 #define UHIDPP_NSENSORS 3
168
169 /* Feature access report used by the HID++ 2.0 (and greater) protocol. */
170 struct fap {
171 uint8_t feature_idx;
172 uint8_t funcidx_swid;
173 uint8_t params[HIDPP_REPORT_LONG_PARAMS_MAX];
174 };
175
176 /*
177 * Register access report used by the HID++ 1.0 protocol. Receivers always uses
178 * this type of report.
179 */
180 struct rap {
181 uint8_t sub_id;
182 uint8_t reg_address;
183 uint8_t params[HIDPP_REPORT_LONG_PARAMS_MAX];
184 };
185
186 struct uhidpp_report {
187 uint8_t device_id;
188 union {
189 struct fap fap;
190 struct rap rap;
191 };
192 } __packed;
193
194 struct uhidpp_notification {
195 struct uhidpp_report n_rep;
196 unsigned int n_id;
197 };
198
199 struct uhidpp_device {
200 uint8_t d_id;
201 uint8_t d_connected;
202 uint8_t d_major;
203 uint8_t d_minor;
204 uint8_t d_features;
205 #define UHIDPP_DEVICE_FEATURE_ROOT 0x01
206 #define UHIDPP_DEVICE_FEATURE_BATTERY 0x02
207 #define UHIDPP_DEVICE_FEATURE_UNIFIED_BATTERY 0x04
208
209 struct {
210 struct ksensor sens[UHIDPP_NSENSORS];
211 uint8_t feature_idx;
212 uint8_t nlevels;
213 uint8_t unified_level_mask;
214 uint8_t rechargeable;
215 } d_battery;
216 };
217
218 /*
219 * Locking:
220 * [I] immutable
221 * [m] sc_mtx
222 */
223 struct uhidpp_softc {
224 struct uhidev sc_hdev;
225 struct usbd_device *sc_udev;
226
227 struct mutex sc_mtx;
228
229 struct uhidpp_device sc_devices[UHIDPP_NDEVICES];
230 /* [m] connected devices */
231
232 struct uhidpp_notification sc_notifications[UHIDPP_NNOTIFICATIONS];
233 /* [m] pending notifications */
234
235 struct usb_task sc_task; /* [m] notification task */
236
237 struct ksensordev sc_sensdev; /* [m] */
238 struct sensor_task *sc_senstsk; /* [m] */
239
240 struct uhidpp_report *sc_req; /* [m] synchronous request buffer */
241 struct uhidpp_report *sc_resp; /* [m] synchronous response buffer */
242 u_int sc_resp_state; /* [m] synchronous response state */
243 u_int sc_swid; /* [m] request software id */
244
245 enum {
246 UHIDPP_RECEIVER_UNIFYING,
247 UHIDPP_RECEIVER_BOLT,
248 } sc_receiver; /* [I] */
249 };
250
251 int uhidpp_match(struct device *, void *, void *);
252 void uhidpp_attach(struct device *, struct device *, void *);
253 int uhidpp_detach(struct device *, int flags);
254 void uhidpp_intr(struct uhidev *addr, void *ibuf, u_int len);
255 void uhidpp_refresh(void *);
256 void uhidpp_task(void *);
257 int uhidpp_sleep(struct uhidpp_softc *, uint64_t);
258
259 void uhidpp_device_connect(struct uhidpp_softc *, struct uhidpp_device *);
260 void uhidpp_device_refresh(struct uhidpp_softc *, struct uhidpp_device *);
261 int uhidpp_device_features(struct uhidpp_softc *, struct uhidpp_device *);
262
263 struct uhidpp_notification *uhidpp_claim_notification(struct uhidpp_softc *);
264 int uhidpp_consume_notification(struct uhidpp_softc *, struct uhidpp_report *);
265 int uhidpp_is_notification(struct uhidpp_softc *, struct uhidpp_report *);
266
267 static int uhidpp_has_sensors(struct uhidpp_softc *);
268
269 int hidpp_get_protocol_version(struct uhidpp_softc *, uint8_t, uint8_t *,
270 uint8_t *);
271
272 int hidpp10_get_name(struct uhidpp_softc *, uint8_t, char *, size_t);
273 int hidpp10_get_type(struct uhidpp_softc *, uint8_t, const char **);
274 int hidpp10_enable_notifications(struct uhidpp_softc *, uint8_t);
275
276 int hidpp20_root_get_feature(struct uhidpp_softc *, uint8_t, uint16_t,
277 uint8_t *, uint8_t *);
278 int hidpp20_feature_get_count(struct uhidpp_softc *, uint8_t, uint8_t,
279 uint8_t *);
280 int hidpp20_feature_get_id(struct uhidpp_softc *, uint8_t, uint8_t, uint8_t,
281 uint16_t *, uint8_t *);
282 int hidpp20_battery_get_level_status(struct uhidpp_softc *,
283 struct uhidpp_device *);
284 int hidpp20_battery_get_capability(struct uhidpp_softc *,
285 struct uhidpp_device *);
286 int hidpp20_battery_status_is_charging(uint8_t);
287 int hidpp20_unified_battery_get_capabilities(struct uhidpp_softc *,
288 struct uhidpp_device *);
289 int hidpp20_unified_battery_get_status(struct uhidpp_softc *,
290 struct uhidpp_device *);
291 int hidpp20_unified_battery_status_is_charging(uint8_t);
292
293 int hidpp_send_validate(uint8_t, int);
294 int hidpp_send_rap_report(struct uhidpp_softc *, uint8_t, uint8_t, uint8_t,
295 uint8_t, uint8_t *, int, struct uhidpp_report *);
296 int hidpp_send_fap_report(struct uhidpp_softc *, uint8_t, uint8_t, uint8_t,
297 uint8_t, uint8_t *, int, struct uhidpp_report *);
298 int hidpp_send_report(struct uhidpp_softc *, uint8_t, struct uhidpp_report *,
299 struct uhidpp_report *);
300
301 static uint8_t
nlevels(uint8_t mask)302 nlevels(uint8_t mask)
303 {
304 uint8_t nbits = 0;
305
306 for (; mask > 0; mask >>= 1) {
307 if (mask & 1)
308 nbits++;
309 }
310 return nbits;
311 }
312
313 struct cfdriver uhidpp_cd = {
314 NULL, "uhidpp", DV_DULL
315 };
316
317 const struct cfattach uhidpp_ca = {
318 sizeof(struct uhidpp_softc),
319 uhidpp_match,
320 uhidpp_attach,
321 uhidpp_detach,
322 };
323
324 static const struct usb_devno uhidpp_devs[] = {
325 { USB_VENDOR_LOGITECH, USB_PRODUCT_ANY },
326 };
327
328 int
uhidpp_match(struct device * parent,void * match,void * aux)329 uhidpp_match(struct device *parent, void *match, void *aux)
330 {
331 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
332 void *desc;
333 int descsiz, siz;
334
335 if (!UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
336 return UMATCH_NONE;
337
338 if (usb_lookup(uhidpp_devs,
339 uha->uaa->vendor, uha->uaa->product) == NULL)
340 return UMATCH_NONE;
341
342 uhidev_get_report_desc(uha->parent, &desc, &descsiz);
343 siz = hid_report_size(desc, descsiz, hid_output, HIDPP_REPORT_ID_SHORT);
344 if (siz != HIDPP_REPORT_SHORT_LENGTH)
345 return UMATCH_NONE;
346 siz = hid_report_size(desc, descsiz, hid_output, HIDPP_REPORT_ID_LONG);
347 if (siz != HIDPP_REPORT_LONG_LENGTH)
348 return UMATCH_NONE;
349
350 uha->claimed[HIDPP_REPORT_ID_SHORT] = 1;
351 uha->claimed[HIDPP_REPORT_ID_LONG] = 1;
352 return UMATCH_VENDOR_PRODUCT;
353 }
354
355 void
uhidpp_attach(struct device * parent,struct device * self,void * aux)356 uhidpp_attach(struct device *parent, struct device *self, void *aux)
357 {
358 struct uhidpp_softc *sc = (struct uhidpp_softc *)self;
359 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
360 struct usb_attach_arg *uaa = uha->uaa;
361 int npaired = 0;
362 int error, i;
363
364 sc->sc_hdev.sc_intr = uhidpp_intr;
365 sc->sc_hdev.sc_udev = uaa->device;
366 sc->sc_hdev.sc_parent = uha->parent;
367 sc->sc_hdev.sc_report_id = uha->reportid;
368 /* The largest supported report dictates the sizes. */
369 sc->sc_hdev.sc_isize = HIDPP_REPORT_LONG_LENGTH;
370 sc->sc_hdev.sc_osize = HIDPP_REPORT_LONG_LENGTH;
371
372 sc->sc_udev = uaa->device;
373
374 mtx_init(&sc->sc_mtx, IPL_USB);
375
376 sc->sc_resp = NULL;
377 sc->sc_resp_state = UHIDPP_RESP_NONE;
378 sc->sc_swid = HIDPP_SOFTWARE_ID_MIN;
379
380 error = uhidev_open(&sc->sc_hdev);
381 if (error) {
382 printf(" open error %d\n", error);
383 return;
384 }
385
386 usb_init_task(&sc->sc_task, uhidpp_task, sc, USB_TASK_TYPE_GENERIC);
387
388 mtx_enter(&sc->sc_mtx);
389
390 /*
391 * Wire up report device handlers before issuing commands to the device
392 * in order to receive responses. Necessary as uhidev by default
393 * performs the wiring after the attach routine has returned.
394 */
395 error = uhidev_set_report_dev(sc->sc_hdev.sc_parent, &sc->sc_hdev,
396 HIDPP_REPORT_ID_SHORT);
397 if (error) {
398 mtx_leave(&sc->sc_mtx);
399 printf(" short report error %d\n", error);
400 return;
401 }
402 error = uhidev_set_report_dev(sc->sc_hdev.sc_parent, &sc->sc_hdev,
403 HIDPP_REPORT_ID_LONG);
404 if (error) {
405 mtx_leave(&sc->sc_mtx);
406 printf(" long report error %d\n", error);
407 return;
408 }
409
410 if (uaa->product == 0xc548)
411 sc->sc_receiver = UHIDPP_RECEIVER_BOLT;
412 else
413 sc->sc_receiver = UHIDPP_RECEIVER_UNIFYING;
414
415 /* Probe paired devices. */
416 for (i = 0; i < UHIDPP_NDEVICES; i++) {
417 char name[16];
418 struct uhidpp_device *dev = &sc->sc_devices[i];
419 const char *type;
420 uint8_t device_id = i + 1;
421
422 dev->d_id = device_id;
423
424 if (hidpp10_get_type(sc, device_id, &type) ||
425 hidpp10_get_name(sc, device_id, name, sizeof(name)))
426 continue;
427
428 if (npaired > 0)
429 printf(",");
430 printf(" device %d", device_id);
431 printf(" %s", type);
432 printf(" \"%s\"", name);
433 npaired++;
434 }
435 if (npaired == 0)
436 goto out;
437
438 /* Enable notifications for the receiver. */
439 error = hidpp10_enable_notifications(sc, HIDPP_DEVICE_ID_RECEIVER);
440 if (error)
441 printf(" error %d", error);
442
443 out:
444 mtx_leave(&sc->sc_mtx);
445 printf("\n");
446 }
447
448 int
uhidpp_detach(struct device * self,int flags)449 uhidpp_detach(struct device *self, int flags)
450 {
451 struct uhidpp_softc *sc = (struct uhidpp_softc *)self;
452 int i;
453
454 usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
455
456 if (uhidpp_has_sensors(sc))
457 sensor_task_unregister(sc->sc_senstsk);
458
459 KASSERT(sc->sc_resp_state == UHIDPP_RESP_NONE);
460
461 if (uhidpp_has_sensors(sc))
462 sensordev_deinstall(&sc->sc_sensdev);
463
464 for (i = 0; i < UHIDPP_NDEVICES; i++) {
465 struct uhidpp_device *dev = &sc->sc_devices[i];
466 struct ksensor *sens = dev->d_battery.sens;
467
468 if (!dev->d_connected)
469 continue;
470
471 sensor_detach(&sc->sc_sensdev, &sens[0]);
472 sensor_detach(&sc->sc_sensdev, &sens[1]);
473 if (dev->d_battery.rechargeable)
474 sensor_detach(&sc->sc_sensdev, &sens[2]);
475 }
476
477 uhidev_close(&sc->sc_hdev);
478
479 return 0;
480 }
481
482 void
uhidpp_intr(struct uhidev * addr,void * buf,u_int len)483 uhidpp_intr(struct uhidev *addr, void *buf, u_int len)
484 {
485 struct uhidpp_softc *sc = (struct uhidpp_softc *)addr;
486 struct uhidpp_report *rep = buf;
487 int dowake = 0;
488 uint8_t repid;
489
490 /*
491 * Ugliness ahead as the report ID is stripped of by uhidev_intr() but
492 * needed to determine if an error occurred.
493 * Note that an error response is always a short report even if the
494 * command that caused the error is a long report.
495 */
496 repid = ((uint8_t *)buf)[-1];
497
498 DREPORT(__func__, repid, buf, len);
499
500 mtx_enter(&sc->sc_mtx);
501 if (uhidpp_is_notification(sc, rep)) {
502 struct uhidpp_notification *ntf;
503
504 ntf = uhidpp_claim_notification(sc);
505 if (ntf != NULL) {
506 memcpy(&ntf->n_rep, buf, len);
507 usb_add_task(sc->sc_udev, &sc->sc_task);
508 } else {
509 DPRINTF("%s: too many notifications", __func__);
510 }
511 } else {
512 KASSERT(sc->sc_resp_state == UHIDPP_RESP_WAIT);
513 dowake = 1;
514 sc->sc_resp_state = repid;
515 memcpy(sc->sc_resp, buf, len);
516 }
517 mtx_leave(&sc->sc_mtx);
518 if (dowake)
519 wakeup(sc);
520 }
521
522 void
uhidpp_refresh(void * arg)523 uhidpp_refresh(void *arg)
524 {
525 struct uhidpp_softc *sc = arg;
526 int i;
527
528 mtx_enter(&sc->sc_mtx);
529 for (i = 0; i < UHIDPP_NDEVICES; i++) {
530 struct uhidpp_device *dev = &sc->sc_devices[i];
531
532 if (dev->d_connected)
533 uhidpp_device_refresh(sc, dev);
534 }
535 mtx_leave(&sc->sc_mtx);
536 }
537
538 void
uhidpp_task(void * arg)539 uhidpp_task(void *arg)
540 {
541 struct uhidpp_softc *sc = arg;
542
543 mtx_enter(&sc->sc_mtx);
544 for (;;) {
545 struct uhidpp_report rep;
546 struct uhidpp_device *dev;
547
548 if (uhidpp_consume_notification(sc, &rep))
549 break;
550
551 DPRINTF("%s: device_id=%d, sub_id=%02x\n",
552 __func__, rep.device_id, rep.rap.sub_id);
553
554 if (rep.device_id == 0 || rep.device_id > UHIDPP_NDEVICES) {
555 DPRINTF("%s: invalid device\n", __func__);
556 continue;
557 }
558 dev = &sc->sc_devices[rep.device_id - 1];
559
560 switch (rep.rap.sub_id) {
561 case 0x0e: /* leds */
562 case 0x40: /* disconnect */
563 case 0x4b: /* pairing accepted */
564 break;
565 case 0x41: /* connect */
566 uhidpp_device_connect(sc, dev);
567 break;
568 }
569 }
570 mtx_leave(&sc->sc_mtx);
571 }
572
573 int
uhidpp_sleep(struct uhidpp_softc * sc,uint64_t nsecs)574 uhidpp_sleep(struct uhidpp_softc *sc, uint64_t nsecs)
575 {
576 return msleep_nsec(sc, &sc->sc_mtx, PZERO, "uhidpp", nsecs);
577 }
578
579 void
uhidpp_device_connect(struct uhidpp_softc * sc,struct uhidpp_device * dev)580 uhidpp_device_connect(struct uhidpp_softc *sc, struct uhidpp_device *dev)
581 {
582 struct ksensor *sens;
583 int error;
584
585 MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
586
587 /* A connected device will continuously send connect events. */
588 if (dev->d_connected)
589 return;
590
591 /*
592 * If features are already present, it must be a device lacking battery
593 * support.
594 */
595 if (dev->d_features)
596 return;
597
598 error = hidpp_get_protocol_version(sc, dev->d_id,
599 &dev->d_major, &dev->d_minor);
600 if (error) {
601 DPRINTF("%s: protocol version failure: device_id=%d, "
602 "error=%d\n",
603 __func__, dev->d_id, error);
604 return;
605 }
606 DPRINTF("%s: device_id=%d, version=%d.%d\n",
607 __func__, dev->d_id, dev->d_major, dev->d_minor);
608 if (dev->d_major <= 1)
609 return;
610
611 error = uhidpp_device_features(sc, dev);
612 if (error) {
613 DPRINTF("%s: features failure: device_id=%d, "
614 "error=%d\n",
615 __func__, dev->d_id, error);
616 return;
617 }
618
619 if (dev->d_features & UHIDPP_DEVICE_FEATURE_BATTERY)
620 error = hidpp20_battery_get_capability(sc, dev);
621 else if (dev->d_features & UHIDPP_DEVICE_FEATURE_UNIFIED_BATTERY)
622 error = hidpp20_unified_battery_get_capabilities(sc, dev);
623 if (error) {
624 DPRINTF("%s: battery capability failure: device_id=%d, "
625 "error=%d\n", __func__, dev->d_id, error);
626 return;
627 }
628
629 dev->d_connected = 1;
630
631 sens = &dev->d_battery.sens[0];
632 strlcpy(sens->desc, "battery level", sizeof(sens->desc));
633 sens->type = SENSOR_PERCENT;
634 sens->flags = SENSOR_FUNKNOWN;
635 sensor_attach(&sc->sc_sensdev, sens);
636
637 sens = &dev->d_battery.sens[1];
638 strlcpy(sens->desc, "number of battery levels", sizeof(sens->desc));
639 sens->type = SENSOR_INTEGER;
640 sens->value = dev->d_battery.nlevels;
641 sensor_attach(&sc->sc_sensdev, sens);
642
643 if (dev->d_battery.rechargeable) {
644 sens = &dev->d_battery.sens[2];
645 strlcpy(sens->desc, "charger", sizeof(sens->desc));
646 sens->type = SENSOR_INDICATOR;
647 sens->value = 0;
648 sensor_attach(&sc->sc_sensdev, sens);
649 }
650
651 uhidpp_device_refresh(sc, dev);
652
653 /*
654 * There could be many devices connected to the same receiver, therefore
655 * only install the sensors once.
656 */
657 if (uhidpp_has_sensors(sc))
658 return;
659
660 strlcpy(sc->sc_sensdev.xname, sc->sc_hdev.sc_dev.dv_xname,
661 sizeof(sc->sc_sensdev.xname));
662 sensordev_install(&sc->sc_sensdev);
663
664 /*
665 * The mutex must be temporarily released while calling
666 * sensor_task_register() as it might end up sleeping.
667 */
668 KASSERT(sc->sc_senstsk == NULL);
669 mtx_leave(&sc->sc_mtx);
670 sc->sc_senstsk = sensor_task_register(sc, uhidpp_refresh, 60);
671 mtx_enter(&sc->sc_mtx);
672 }
673
674 void
uhidpp_device_refresh(struct uhidpp_softc * sc,struct uhidpp_device * dev)675 uhidpp_device_refresh(struct uhidpp_softc *sc, struct uhidpp_device *dev)
676 {
677 int error;
678
679 MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
680
681 if (dev->d_major <= 1)
682 return;
683
684 if (dev->d_features & UHIDPP_DEVICE_FEATURE_BATTERY)
685 error = hidpp20_battery_get_level_status(sc, dev);
686 else if (dev->d_features & UHIDPP_DEVICE_FEATURE_UNIFIED_BATTERY)
687 error = hidpp20_unified_battery_get_status(sc, dev);
688 else
689 error = -ENOTSUP;
690 if (error) {
691 DPRINTF("%s: battery status failure: device_id=%d, error=%d\n",
692 __func__, dev->d_id, error);
693 }
694 }
695
696 /*
697 * Enumerate all supported HID++ 2.0 features for the given device.
698 */
699 int
uhidpp_device_features(struct uhidpp_softc * sc,struct uhidpp_device * dev)700 uhidpp_device_features(struct uhidpp_softc *sc, struct uhidpp_device *dev)
701 {
702 int error;
703 uint8_t count, feature_idx, feature_type, i;
704
705 /* All devices support the root feature. */
706 dev->d_features |= UHIDPP_DEVICE_FEATURE_ROOT;
707
708 error = hidpp20_root_get_feature(sc, dev->d_id,
709 HIDPP20_FEAT_FEATURE_ID,
710 &feature_idx, &feature_type);
711 if (error) {
712 DPRINTF("%s: feature index failure: device_id=%d, error=%d\n",
713 __func__, dev->d_id, error);
714 return error;
715 }
716
717 error = hidpp20_feature_get_count(sc, dev->d_id, feature_idx, &count);
718 if (error) {
719 DPRINTF("%s: feature count failure: device_id=%d, error=%d\n",
720 __func__, dev->d_id, error);
721 return error;
722 }
723
724 for (i = 1; i <= count; i++) {
725 uint16_t id;
726 uint8_t type;
727
728 error = hidpp20_feature_get_id(sc, dev->d_id, feature_idx, i,
729 &id, &type);
730 if (error)
731 continue;
732
733 if (id == HIDPP20_FEAT_BATTERY_ID) {
734 dev->d_features |= UHIDPP_DEVICE_FEATURE_BATTERY;
735 dev->d_battery.feature_idx = i;
736 } else if (id == HIDPP20_FEAT_UNIFIED_BATTERY_ID) {
737 dev->d_features |= UHIDPP_DEVICE_FEATURE_UNIFIED_BATTERY;
738 dev->d_battery.feature_idx = i;
739 }
740
741 DPRINTF("%s: idx=%d, id=%x, type=%x device_id=%d\n",
742 __func__, i, id, type, dev->d_id);
743 }
744 DPRINTF("%s: device_id=%d, count=%d, features=%x\n",
745 __func__, dev->d_id, count, dev->d_features);
746
747 if ((dev->d_features & ~UHIDPP_DEVICE_FEATURE_ROOT) == 0)
748 return -ENODEV;
749 return 0;
750 }
751
752 /*
753 * Returns the next available notification slot, if available.
754 */
755 struct uhidpp_notification *
uhidpp_claim_notification(struct uhidpp_softc * sc)756 uhidpp_claim_notification(struct uhidpp_softc *sc)
757 {
758 struct uhidpp_notification *ntf = NULL;
759 int nclaimed = 0;
760 int i;
761
762 MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
763
764 for (i = 0; i < UHIDPP_NNOTIFICATIONS; i++) {
765 struct uhidpp_notification *tmp = &sc->sc_notifications[i];
766
767 if (tmp->n_id > 0)
768 nclaimed++;
769 else if (ntf == NULL)
770 ntf = tmp;
771 }
772
773 if (ntf == NULL)
774 return NULL;
775 ntf->n_id = nclaimed + 1;
776 return ntf;
777 }
778
779 /*
780 * Consume the first unhandled notification, if present.
781 */
782 int
uhidpp_consume_notification(struct uhidpp_softc * sc,struct uhidpp_report * rep)783 uhidpp_consume_notification(struct uhidpp_softc *sc, struct uhidpp_report *rep)
784 {
785 struct uhidpp_notification *ntf = NULL;
786 int i;
787
788 MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
789
790 for (i = 0; i < UHIDPP_NNOTIFICATIONS; i++) {
791 struct uhidpp_notification *tmp = &sc->sc_notifications[i];
792
793 if (tmp->n_id > 0 && (ntf == NULL || tmp->n_id < ntf->n_id))
794 ntf = tmp;
795 }
796 if (ntf == NULL)
797 return 1;
798
799 memcpy(rep, &ntf->n_rep, sizeof(*rep));
800 ntf->n_id = 0;
801 return 0;
802 }
803
804 /*
805 * Returns non-zero if the given report is a notification. Otherwise, it must be
806 * a response.
807 */
808 int
uhidpp_is_notification(struct uhidpp_softc * sc,struct uhidpp_report * rep)809 uhidpp_is_notification(struct uhidpp_softc *sc, struct uhidpp_report *rep)
810 {
811 /* Not waiting for a response. */
812 if (sc->sc_req == NULL)
813 return 1;
814
815 /* Everything except the parameters must be repeated in a response. */
816 if (sc->sc_req->device_id == rep->device_id &&
817 sc->sc_req->rap.sub_id == rep->rap.sub_id &&
818 sc->sc_req->rap.reg_address == rep->rap.reg_address)
819 return 0;
820
821 /* An error must always be a response. */
822 if ((rep->rap.sub_id == HIDPP_ERROR ||
823 rep->fap.feature_idx == HIDPP20_ERROR) &&
824 rep->fap.funcidx_swid == sc->sc_req->fap.feature_idx &&
825 rep->fap.params[0] == sc->sc_req->fap.funcidx_swid)
826 return 0;
827
828 return 1;
829 }
830
831 static int
uhidpp_has_sensors(struct uhidpp_softc * sc)832 uhidpp_has_sensors(struct uhidpp_softc *sc)
833 {
834 return sc->sc_sensdev.xname[0] != '\0';
835 }
836
837 int
hidpp_get_protocol_version(struct uhidpp_softc * sc,uint8_t device_id,uint8_t * major,uint8_t * minor)838 hidpp_get_protocol_version(struct uhidpp_softc *sc, uint8_t device_id,
839 uint8_t *major, uint8_t *minor)
840 {
841 struct uhidpp_report resp;
842 uint8_t params[3] = { 0, 0, HIDPP_PING_DATA };
843 int error;
844
845 error = hidpp_send_fap_report(sc,
846 HIDPP_REPORT_ID_SHORT,
847 device_id,
848 HIDPP_FEAT_ROOT_ID,
849 HIDPP_FEAT_ROOT_PING_FUNC,
850 params, sizeof(params), &resp);
851 if (error == HIDPP_ERROR_INVALID_SUBID) {
852 *major = 1;
853 *minor = 0;
854 return 0;
855 }
856 if (error)
857 return error;
858 if (resp.rap.params[2] != HIDPP_PING_DATA)
859 return -EPROTO;
860
861 *major = resp.fap.params[0];
862 *minor = resp.fap.params[1];
863 return 0;
864 }
865
866 int
hidpp10_get_name(struct uhidpp_softc * sc,uint8_t device_id,char * buf,size_t bufsiz)867 hidpp10_get_name(struct uhidpp_softc *sc, uint8_t device_id,
868 char *buf, size_t bufsiz)
869 {
870 struct uhidpp_report resp;
871 int error;
872 const uint8_t *name;
873 uint8_t len;
874
875 if (sc->sc_receiver == UHIDPP_RECEIVER_BOLT) {
876 uint8_t params[2] = { 0x60 + device_id, 0x01 };
877
878 error = hidpp_send_rap_report(sc,
879 HIDPP_REPORT_ID_SHORT,
880 HIDPP_DEVICE_ID_RECEIVER,
881 HIDPP_GET_LONG_REGISTER,
882 HIDPP_REG_PAIRING_INFORMATION,
883 params, sizeof(params), &resp);
884 if (error)
885 return error;
886 len = resp.rap.params[2];
887 name = &resp.rap.params[3];
888 } else {
889 uint8_t params[1] = { 0x40 + (device_id - 1) };
890
891 error = hidpp_send_rap_report(sc,
892 HIDPP_REPORT_ID_SHORT,
893 HIDPP_DEVICE_ID_RECEIVER,
894 HIDPP_GET_LONG_REGISTER,
895 HIDPP_REG_PAIRING_INFORMATION,
896 params, sizeof(params), &resp);
897 if (error)
898 return error;
899 len = resp.rap.params[1];
900 name = &resp.rap.params[2];
901 }
902
903 if (len > bufsiz - 1)
904 len = bufsiz - 1;
905 memcpy(buf, name, len);
906 buf[len] = '\0';
907 return 0;
908 }
909
910 int
hidpp10_get_type(struct uhidpp_softc * sc,uint8_t device_id,const char ** buf)911 hidpp10_get_type(struct uhidpp_softc *sc, uint8_t device_id, const char **buf)
912 {
913 struct uhidpp_report resp;
914 int error;
915 uint8_t type;
916
917 if (sc->sc_receiver == UHIDPP_RECEIVER_BOLT) {
918 uint8_t params[1] = { 0x50 + device_id };
919
920 error = hidpp_send_rap_report(sc,
921 HIDPP_REPORT_ID_SHORT,
922 HIDPP_DEVICE_ID_RECEIVER,
923 HIDPP_GET_LONG_REGISTER,
924 HIDPP_REG_PAIRING_INFORMATION,
925 params, sizeof(params), &resp);
926 if (error)
927 return error;
928 type = resp.rap.params[1] & 0xf;
929 } else {
930 uint8_t params[1] = { 0x20 + (device_id - 1) };
931
932 error = hidpp_send_rap_report(sc,
933 HIDPP_REPORT_ID_SHORT,
934 HIDPP_DEVICE_ID_RECEIVER,
935 HIDPP_GET_LONG_REGISTER,
936 HIDPP_REG_PAIRING_INFORMATION,
937 params, sizeof(params), &resp);
938 if (error)
939 return error;
940 type = resp.rap.params[7];
941 }
942
943 switch (type) {
944 case 0x00:
945 *buf = "unknown";
946 return 0;
947 case 0x01:
948 *buf = "keyboard";
949 return 0;
950 case 0x02:
951 *buf = "mouse";
952 return 0;
953 case 0x03:
954 *buf = "numpad";
955 return 0;
956 case 0x04:
957 *buf = "presenter";
958 return 0;
959 case 0x08:
960 *buf = "trackball";
961 return 0;
962 case 0x09:
963 *buf = "touchpad";
964 return 0;
965 }
966 return -ENOENT;
967 }
968
969 int
hidpp10_enable_notifications(struct uhidpp_softc * sc,uint8_t device_id)970 hidpp10_enable_notifications(struct uhidpp_softc *sc, uint8_t device_id)
971 {
972 struct uhidpp_report resp;
973 uint8_t params[3];
974
975 /* Device reporting flags. */
976 params[0] = HIDPP_ENABLE_REPORTS_DEVICE_BATTERY_STATUS;
977 /* Receiver reporting flags. */
978 params[1] = HIDPP_ENABLE_REPORTS_RECEIVER_WIRELESS |
979 HIDPP_ENABLE_REPORTS_RECEIVER_SOFTWARE_PRESENT;
980 /* Device reporting flags (continued). */
981 params[2] = 0;
982
983 return hidpp_send_rap_report(sc,
984 HIDPP_REPORT_ID_SHORT,
985 device_id,
986 HIDPP_SET_REGISTER,
987 HIDPP_REG_ENABLE_REPORTS,
988 params, sizeof(params), &resp);
989 }
990
991 int
hidpp20_root_get_feature(struct uhidpp_softc * sc,uint8_t device_id,uint16_t feature,uint8_t * feature_idx,uint8_t * feature_type)992 hidpp20_root_get_feature(struct uhidpp_softc *sc, uint8_t device_id,
993 uint16_t feature, uint8_t *feature_idx, uint8_t *feature_type)
994 {
995 struct uhidpp_report resp;
996 uint8_t params[2] = { feature >> 8, feature & 0xff };
997 int error;
998
999 error = hidpp_send_fap_report(sc,
1000 HIDPP_REPORT_ID_LONG,
1001 device_id,
1002 HIDPP20_FEAT_ROOT_ID,
1003 HIDPP20_FEAT_ROOT_GET_FEATURE_FUNC,
1004 params, sizeof(params), &resp);
1005 if (error)
1006 return error;
1007
1008 if (resp.fap.params[0] == 0)
1009 return -ENOENT;
1010
1011 *feature_idx = resp.fap.params[0];
1012 *feature_type = resp.fap.params[1];
1013 return 0;
1014 }
1015
1016 int
hidpp20_feature_get_count(struct uhidpp_softc * sc,uint8_t device_id,uint8_t feature_idx,uint8_t * count)1017 hidpp20_feature_get_count(struct uhidpp_softc *sc, uint8_t device_id,
1018 uint8_t feature_idx, uint8_t *count)
1019 {
1020 struct uhidpp_report resp;
1021 int error;
1022
1023 error = hidpp_send_fap_report(sc,
1024 HIDPP_REPORT_ID_LONG,
1025 device_id,
1026 feature_idx,
1027 HIDPP20_FEAT_FEATURE_COUNT_FUNC,
1028 NULL, 0, &resp);
1029 if (error)
1030 return error;
1031
1032 *count = resp.fap.params[0];
1033 return 0;
1034 }
1035
1036 int
hidpp20_feature_get_id(struct uhidpp_softc * sc,uint8_t device_id,uint8_t feature_idx,uint8_t idx,uint16_t * id,uint8_t * type)1037 hidpp20_feature_get_id(struct uhidpp_softc *sc, uint8_t device_id,
1038 uint8_t feature_idx, uint8_t idx, uint16_t *id, uint8_t *type)
1039 {
1040 struct uhidpp_report resp;
1041 uint8_t params[1] = { idx };
1042 int error;
1043
1044 error = hidpp_send_fap_report(sc,
1045 HIDPP_REPORT_ID_LONG,
1046 device_id,
1047 feature_idx,
1048 HIDPP20_FEAT_FEATURE_ID_FUNC,
1049 params, sizeof(params), &resp);
1050 if (error)
1051 return error;
1052
1053 *id = bemtoh16(resp.fap.params);
1054 *type = resp.fap.params[2];
1055 return 0;
1056 }
1057
1058 int
hidpp20_battery_get_level_status(struct uhidpp_softc * sc,struct uhidpp_device * dev)1059 hidpp20_battery_get_level_status(struct uhidpp_softc *sc,
1060 struct uhidpp_device *dev)
1061 {
1062 struct uhidpp_report resp;
1063 int charging, error;
1064 uint8_t level, status;
1065
1066 error = hidpp_send_fap_report(sc,
1067 HIDPP_REPORT_ID_LONG,
1068 dev->d_id,
1069 dev->d_battery.feature_idx,
1070 HIDPP20_FEAT_BATTERY_LEVEL_STATUS_FUNC,
1071 NULL, 0, &resp);
1072 if (error)
1073 return error;
1074
1075 level = resp.fap.params[0];
1076 /* next_level = resp.fap.params[1]; */
1077 status = resp.fap.params[2];
1078 /*
1079 * While charging, the reported level cannot be trusted. However, fake
1080 * the battery state once the charging is done.
1081 */
1082 if (status == HIDPP20_LEVEL_STATUS_CHARGING_DONE) {
1083 level = 100;
1084 status = 0;
1085 }
1086
1087 charging = hidpp20_battery_status_is_charging(status);
1088
1089 dev->d_battery.sens[0].value = level * 1000;
1090 dev->d_battery.sens[0].flags &= ~SENSOR_FUNKNOWN;
1091 if (dev->d_battery.nlevels < 10) {
1092 /*
1093 * According to the HID++ 2.0 specification, less than
1094 * 10 levels should be mapped to the following 4 levels:
1095 *
1096 * [0, 10] critical
1097 * [11, 30] low
1098 * [31, 80] good
1099 * [81, 100] full
1100 *
1101 * Since sensors are limited to 3 valid statuses, clamp
1102 * it even further. Unless the battery is charging in
1103 * which the level cannot be trusted.
1104 */
1105 if (charging)
1106 dev->d_battery.sens[0].status = SENSOR_S_UNKNOWN;
1107 else if (level <= 10)
1108 dev->d_battery.sens[0].status = SENSOR_S_CRIT;
1109 else if (level <= 30)
1110 dev->d_battery.sens[0].status = SENSOR_S_WARN;
1111 else
1112 dev->d_battery.sens[0].status = SENSOR_S_OK;
1113 } else {
1114 /*
1115 * XXX the device supports battery mileage. The current
1116 * level must be checked against resp.fap.params[3]
1117 * given by hidpp20_battery_get_capability().
1118 */
1119 dev->d_battery.sens[0].status = SENSOR_S_UNKNOWN;
1120 }
1121
1122 if (dev->d_battery.rechargeable)
1123 dev->d_battery.sens[2].value = charging;
1124
1125 return 0;
1126 }
1127
1128 int
hidpp20_battery_get_capability(struct uhidpp_softc * sc,struct uhidpp_device * dev)1129 hidpp20_battery_get_capability(struct uhidpp_softc *sc,
1130 struct uhidpp_device *dev)
1131 {
1132 struct uhidpp_report resp;
1133 int error;
1134
1135 error = hidpp_send_fap_report(sc,
1136 HIDPP_REPORT_ID_LONG,
1137 dev->d_id,
1138 dev->d_battery.feature_idx,
1139 HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC,
1140 NULL, 0, &resp);
1141 if (error)
1142 return error;
1143 dev->d_battery.nlevels = resp.fap.params[0];
1144 dev->d_battery.rechargeable = resp.fap.params[1] &
1145 HIDPP20_CAPABILITY_RECHARGEABLE;
1146 return 0;
1147 }
1148
1149 int
hidpp20_unified_battery_get_capabilities(struct uhidpp_softc * sc,struct uhidpp_device * dev)1150 hidpp20_unified_battery_get_capabilities(struct uhidpp_softc *sc,
1151 struct uhidpp_device *dev)
1152 {
1153 struct uhidpp_report resp;
1154 int error;
1155
1156 error = hidpp_send_fap_report(sc,
1157 HIDPP_REPORT_ID_LONG,
1158 dev->d_id,
1159 dev->d_battery.feature_idx,
1160 HIDPP20_FEAT_UNIFIED_BATTERY_CAPABILITIES_FUNC,
1161 NULL, 0, &resp);
1162 if (error)
1163 return error;
1164 dev->d_battery.nlevels = nlevels(resp.fap.params[0]);
1165 dev->d_battery.unified_level_mask = resp.fap.params[0];
1166 dev->d_battery.rechargeable = resp.fap.params[1] &
1167 HIDPP20_CAPABILITES_RECHARGEABLE;
1168 return 0;
1169 }
1170
1171 int
hidpp20_unified_battery_get_status(struct uhidpp_softc * sc,struct uhidpp_device * dev)1172 hidpp20_unified_battery_get_status(struct uhidpp_softc *sc,
1173 struct uhidpp_device *dev)
1174 {
1175 struct uhidpp_report resp;
1176 int charging, error;
1177 uint8_t level, percentage, status;
1178
1179 error = hidpp_send_fap_report(sc,
1180 HIDPP_REPORT_ID_LONG,
1181 dev->d_id,
1182 dev->d_battery.feature_idx,
1183 HIDPP20_FEAT_UNIFIED_BATTERY_STATUS_FUNC,
1184 NULL, 0, &resp);
1185 if (error)
1186 return error;
1187 percentage = resp.fap.params[0];
1188 level = resp.fap.params[1] & dev->d_battery.unified_level_mask;
1189 status = resp.fap.params[2];
1190 /* external_power_status = resp.fap.params[3]; */
1191
1192 charging = hidpp20_unified_battery_status_is_charging(status);
1193 dev->d_battery.sens[0].value = percentage * 1000;
1194 dev->d_battery.sens[0].flags &= ~SENSOR_FUNKNOWN;
1195 dev->d_battery.sens[0].status = SENSOR_S_UNKNOWN;
1196 /* Do not trust the level while charging. */
1197 if (!charging) {
1198 if (level & HIDPP20_BATTERY_STATUS_CRITICAL)
1199 dev->d_battery.sens[0].status = SENSOR_S_CRIT;
1200 else if (level & HIDPP20_BATTERY_STATUS_LOW)
1201 dev->d_battery.sens[0].status = SENSOR_S_WARN;
1202 else if (level & HIDPP20_BATTERY_STATUS_GOOD)
1203 dev->d_battery.sens[0].status = SENSOR_S_OK;
1204 else if (level & HIDPP20_BATTERY_STATUS_FULL)
1205 dev->d_battery.sens[0].status = SENSOR_S_OK;
1206 }
1207 if (dev->d_battery.rechargeable)
1208 dev->d_battery.sens[2].value = charging;
1209 return 0;
1210 }
1211
1212 int
hidpp20_unified_battery_status_is_charging(uint8_t status)1213 hidpp20_unified_battery_status_is_charging(uint8_t status)
1214 {
1215 switch (status) {
1216 case 1: /* charging */
1217 case 2: /* charging slow */
1218 return 1;
1219 default:
1220 return 0;
1221 }
1222 }
1223
1224 int
hidpp20_battery_status_is_charging(uint8_t status)1225 hidpp20_battery_status_is_charging(uint8_t status)
1226 {
1227 switch (status) {
1228 case 1: /* recharging */
1229 case 2: /* charge in final stage */
1230 case 4: /* recharging below optimal speed */
1231 return 1;
1232
1233 case 3: /* charge complete */
1234 return 1;
1235
1236 case 0: /* discharging */
1237 case 5: /* invalid battery type */
1238 case 6: /* thermal error */
1239 case 7: /* other charging error */
1240 default:
1241 return 0;
1242 }
1243 }
1244
1245 int
hidpp_send_validate(uint8_t report_id,int nparams)1246 hidpp_send_validate(uint8_t report_id, int nparams)
1247 {
1248 if (report_id == HIDPP_REPORT_ID_SHORT) {
1249 if (nparams > HIDPP_REPORT_SHORT_PARAMS_MAX)
1250 return -EMSGSIZE;
1251 } else if (report_id == HIDPP_REPORT_ID_LONG) {
1252 if (nparams > HIDPP_REPORT_LONG_PARAMS_MAX)
1253 return -EMSGSIZE;
1254 } else {
1255 return -EINVAL;
1256 }
1257 return 0;
1258 }
1259
1260 int
hidpp_send_fap_report(struct uhidpp_softc * sc,uint8_t report_id,uint8_t device_id,uint8_t feature_idx,uint8_t func_idx,uint8_t * params,int nparams,struct uhidpp_report * resp)1261 hidpp_send_fap_report(struct uhidpp_softc *sc, uint8_t report_id,
1262 uint8_t device_id, uint8_t feature_idx, uint8_t func_idx,
1263 uint8_t *params, int nparams, struct uhidpp_report *resp)
1264 {
1265 struct uhidpp_report req;
1266 int error;
1267
1268 error = hidpp_send_validate(report_id, nparams);
1269 if (error)
1270 return error;
1271
1272 memset(&req, 0, sizeof(req));
1273 req.device_id = device_id;
1274 req.fap.feature_idx = feature_idx;
1275 sc->sc_swid = sc->sc_swid == HIDPP_SOFTWARE_ID_MAX ?
1276 HIDPP_SOFTWARE_ID_MIN : sc->sc_swid + 1;
1277 req.fap.funcidx_swid =
1278 (func_idx << HIDPP_SOFTWARE_ID_LEN) | sc->sc_swid;
1279 memcpy(req.fap.params, params, nparams);
1280 return hidpp_send_report(sc, report_id, &req, resp);
1281 }
1282
1283 int
hidpp_send_rap_report(struct uhidpp_softc * sc,uint8_t report_id,uint8_t device_id,uint8_t sub_id,uint8_t reg_address,uint8_t * params,int nparams,struct uhidpp_report * resp)1284 hidpp_send_rap_report(struct uhidpp_softc *sc, uint8_t report_id,
1285 uint8_t device_id, uint8_t sub_id, uint8_t reg_address,
1286 uint8_t *params, int nparams, struct uhidpp_report *resp)
1287 {
1288 struct uhidpp_report req;
1289 int error;
1290
1291 error = hidpp_send_validate(report_id, nparams);
1292 if (error)
1293 return error;
1294
1295 memset(&req, 0, sizeof(req));
1296 req.device_id = device_id;
1297 req.rap.sub_id = sub_id;
1298 req.rap.reg_address = reg_address;
1299 memcpy(req.rap.params, params, nparams);
1300 return hidpp_send_report(sc, report_id, &req, resp);
1301 }
1302
1303 int
hidpp_send_report(struct uhidpp_softc * sc,uint8_t report_id,struct uhidpp_report * req,struct uhidpp_report * resp)1304 hidpp_send_report(struct uhidpp_softc *sc, uint8_t report_id,
1305 struct uhidpp_report *req, struct uhidpp_report *resp)
1306 {
1307 int error = 0;
1308 int len, n;
1309
1310 MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
1311
1312 if (report_id == HIDPP_REPORT_ID_SHORT)
1313 len = HIDPP_REPORT_SHORT_LENGTH;
1314 else if (report_id == HIDPP_REPORT_ID_LONG)
1315 len = HIDPP_REPORT_LONG_LENGTH;
1316 else
1317 return -EINVAL;
1318
1319 DREPORT(__func__, report_id, (const unsigned char *)req, len);
1320
1321 /* Wait until any ongoing command has completed. */
1322 while (sc->sc_resp_state != UHIDPP_RESP_NONE)
1323 uhidpp_sleep(sc, INFSLP);
1324 sc->sc_req = req;
1325 sc->sc_resp = resp;
1326 sc->sc_resp_state = UHIDPP_RESP_WAIT;
1327 /*
1328 * The mutex must be temporarily released while calling
1329 * uhidev_set_report() as it might end up sleeping.
1330 */
1331 mtx_leave(&sc->sc_mtx);
1332
1333 n = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
1334 report_id, req, len);
1335
1336 mtx_enter(&sc->sc_mtx);
1337 if (len != n) {
1338 error = -EBUSY;
1339 goto out;
1340 }
1341 /*
1342 * The interrupt could already have been received while the mutex was
1343 * released. Otherwise, wait for it.
1344 */
1345 if (sc->sc_resp_state == UHIDPP_RESP_WAIT) {
1346 /* Timeout taken from the hid-logitech-hidpp Linux driver. */
1347 error = uhidpp_sleep(sc, SEC_TO_NSEC(5));
1348 if (error) {
1349 error = -error;
1350 goto out;
1351 }
1352 }
1353
1354 if (sc->sc_resp_state == UHIDPP_RESP_ERROR)
1355 error = -EIO;
1356 else if (sc->sc_resp_state == HIDPP_REPORT_ID_SHORT &&
1357 resp->rap.sub_id == HIDPP_ERROR)
1358 error = resp->rap.params[1];
1359 else if (sc->sc_resp_state == HIDPP_REPORT_ID_LONG &&
1360 resp->fap.feature_idx == HIDPP20_ERROR)
1361 error = resp->fap.params[1];
1362
1363 out:
1364 sc->sc_req = NULL;
1365 sc->sc_resp = NULL;
1366 sc->sc_resp_state = UHIDPP_RESP_NONE;
1367 wakeup(sc);
1368 return error;
1369 }
1370
1371 #ifdef UHIDPP_DEBUG
1372
1373 void
uhidd_dump_report(const char * prefix,uint8_t repid,const unsigned char * buf,u_int buflen)1374 uhidd_dump_report(const char *prefix, uint8_t repid, const unsigned char *buf,
1375 u_int buflen)
1376 {
1377 u_int i;
1378
1379 printf("%s: %02x ", prefix, repid);
1380 for (i = 0; i < buflen; i++) {
1381 printf("%02x%s", buf[i],
1382 i == 2 ? " [" : (i + 1 < buflen ? " " : ""));
1383 }
1384 printf("]\n");
1385 }
1386
1387 #endif
1388