xref: /openbsd/sys/dev/usb/uhidpp.c (revision 55cc5ba3)
1 /*	$OpenBSD: uhidpp.c,v 1.1 2021/02/04 16:25:39 anton 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/kernel.h>
22 #include <sys/device.h>
23 #include <sys/mutex.h>
24 #include <sys/sensors.h>
25 
26 #include <dev/usb/usb.h>
27 #include <dev/usb/usbhid.h>
28 #include <dev/usb/usbdi.h>
29 #include <dev/usb/usbdevs.h>
30 #include <dev/usb/uhidev.h>
31 
32 /* #define UHIDPP_DEBUG */
33 #ifdef UHIDPP_DEBUG
34 
35 #define DPRINTF(x...) do {						\
36 	if (uhidpp_debug)						\
37 		printf(x);						\
38 } while (0)
39 
40 #define DREPORT(prefix, repid, buf, len) do {				\
41 	if (uhidpp_debug)						\
42 		uhidd_dump_report((prefix), (repid), (buf), (len));	\
43 } while (0)
44 
45 void uhidd_dump_report(const char *, uint8_t, const unsigned char *, u_int);
46 
47 int uhidpp_debug = 1;
48 
49 #else
50 
51 #define DPRINTF(x...)
52 #define DREPORT(prefix, repid, buf, len)
53 
54 #endif
55 
56 #define HIDPP_LINK_STATUS(x)	((x) & (1 << 7))
57 
58 #define HIDPP_REPORT_ID_SHORT			0x10
59 #define HIDPP_REPORT_ID_LONG			0x11
60 
61 /*
62  * Length of reports. Note that the effective length is always +1 as
63  * uhidev_set_report() prepends the report ID.
64  */
65 #define HIDPP_REPORT_SHORT_LENGTH		(7 - 1)
66 #define HIDPP_REPORT_LONG_LENGTH		(20 - 1)
67 
68 /*
69  * Maximum number of allowed parameters for reports. Note, the parameters always
70  * starts at offset 3 for both RAP and FAP reports.
71  */
72 #define HIDPP_REPORT_SHORT_PARAMS_MAX		(HIDPP_REPORT_SHORT_LENGTH - 3)
73 #define HIDPP_REPORT_LONG_PARAMS_MAX		(HIDPP_REPORT_LONG_LENGTH - 3)
74 
75 #define HIDPP_DEVICE_ID_RECEIVER		0xff
76 
77 #define HIDPP_FEAT_ROOT_IDX			0x00
78 #define HIDPP_FEAT_ROOT_PING_FUNC		0x01
79 #define HIDPP_FEAT_ROOT_PING_DATA		0x5a
80 
81 #define HIDPP_SET_REGISTER			0x80
82 #define HIDPP_GET_REGISTER			0x81
83 #define HIDPP_SET_LONG_REGISTER			0x82
84 #define HIDPP_GET_LONG_REGISTER			0x83
85 
86 #define HIDPP_REG_ENABLE_REPORTS		0x00
87 #define HIDPP_REG_PAIRING_INFORMATION		0xb5
88 
89 #define HIDPP_NOTIF_DEVICE_BATTERY_STATUS	(1 << 4)
90 #define HIDPP_NOTIF_RECEIVER_WIRELESS		(1 << 0)
91 #define HIDPP_NOTIF_RECEIVER_SOFTWARE_PRESENT	(1 << 3)
92 
93 /* HID++ 1.0 error codes. */
94 #define HIDPP_ERROR				0x8f
95 #define HIDPP_ERROR_SUCCESS			0x00
96 #define HIDPP_ERROR_INVALID_SUBID		0x01
97 #define HIDPP_ERROR_INVALID_ADRESS		0x02
98 #define HIDPP_ERROR_INVALID_VALUE		0x03
99 #define HIDPP_ERROR_CONNECT_FAIL		0x04
100 #define HIDPP_ERROR_TOO_MANY_DEVICES		0x05
101 #define HIDPP_ERROR_ALREADY_EXISTS		0x06
102 #define HIDPP_ERROR_BUSY			0x07
103 #define HIDPP_ERROR_UNKNOWN_DEVICE		0x08
104 #define HIDPP_ERROR_RESOURCE_ERROR		0x09
105 #define HIDPP_ERROR_REQUEST_UNAVAILABLE		0x0a
106 #define HIDPP_ERROR_INVALID_PARAM_VALUE		0x0b
107 #define HIDPP_ERROR_WRONG_PIN_CODE		0x0c
108 
109 /*
110  * The software ID is added to feature access reports (FAP) and used to
111  * distinguish responses from notifications. Note, the software ID must be
112  * greater than zero which is reserved for notifications.
113  */
114 #define HIDPP_SOFTWARE_ID			0x01
115 #define HIDPP_SOFTWARE_ID_MASK			0x0f
116 #define HIDPP_SOFTWARE_ID_LEN			4
117 
118 #define HIDPP20_FEAT_ROOT_IDX			0x00
119 #define HIDPP20_FEAT_ROOT_GET_FEATURE_FUNC	0x00
120 
121 #define HIDPP20_FEAT_BATTERY_IDX		0x1000
122 #define HIDPP20_FEAT_BATTERY_LEVEL_FUNC		0x0000
123 #define HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC	0x0001
124 
125 /* HID++ 2.0 error codes. */
126 #define HIDPP20_ERROR				0xff
127 #define HIDPP20_ERROR_NO_ERROR			0x00
128 #define HIDPP20_ERROR_UNKNOWN			0x01
129 #define HIDPP20_ERROR_INVALID_ARGUMENT		0x02
130 #define HIDPP20_ERROR_OUT_OF_RANGE		0x03
131 #define HIDPP20_ERROR_HARDWARE_ERROR		0x04
132 #define HIDPP20_ERROR_LOGITECH_INTERNAL		0x05
133 #define HIDPP20_ERROR_INVALID_FEATURE_INDEX	0x06
134 #define HIDPP20_ERROR_INVALID_FUNCTION_ID	0x07
135 #define HIDPP20_ERROR_BUSY			0x08
136 #define HIDPP20_ERROR_UNSUPPORTED		0x09
137 
138 /*
139  * Sentinels used for interrupt response synchronization. The values must be
140  * disjoint from existing report IDs.
141  */
142 #define UHIDPP_RESP_NONE			0
143 #define UHIDPP_RESP_WAIT			1
144 #define UHIDPP_RESP_ERROR			2
145 
146 /* Maximum number of devices associated with a single receiver. */
147 #define UHIDPP_NDEVICES				6
148 
149 /* Maximum number of pending notifications. */
150 #define UHIDPP_NNOTIFICATIONS			4
151 
152 /* Number of sensors per paired device. */
153 #define UHIDPP_NSENSORS				2
154 
155 /* Feature access report used by the HID++ 2.0 (and greater) protocol. */
156 struct fap {
157 	uint8_t feature_index;
158 	uint8_t funcindex_clientid;
159 	uint8_t params[HIDPP_REPORT_LONG_PARAMS_MAX];
160 };
161 
162 /*
163  * Register access report used by the HID++ 1.0 protocol. Receivers always uses
164  * this type of report.
165  */
166 struct rap {
167 	uint8_t sub_id;
168 	uint8_t reg_address;
169 	uint8_t params[HIDPP_REPORT_LONG_PARAMS_MAX];
170 };
171 
172 struct uhidpp_report {
173 	uint8_t device_id;
174 	union {
175 		struct fap fap;
176 		struct rap rap;
177 	};
178 } __packed;
179 
180 struct uhidpp_notification {
181 	struct uhidpp_report n_rep;
182 	unsigned int n_id;
183 };
184 
185 struct uhidpp_device {
186 	uint8_t d_id;
187 	uint8_t d_connected;
188 	struct {
189 		struct ksensor b_sens[UHIDPP_NSENSORS];
190 		uint8_t b_feature_idx;
191 		uint8_t b_level;
192 		uint8_t b_next_level;
193 		uint8_t b_status;
194 		uint8_t b_nlevels;
195 	} d_battery;
196 };
197 
198 /*
199  * Locking:
200  *	[m]	sc_mtx
201  */
202 struct uhidpp_softc {
203 	struct uhidev sc_hdev;
204 	struct usbd_device *sc_udev;
205 
206 	struct mutex sc_mtx;
207 
208 	struct uhidpp_device sc_devices[UHIDPP_NDEVICES];
209 					/* [m] connected devices */
210 
211 	struct uhidpp_notification sc_notifications[UHIDPP_NNOTIFICATIONS];
212 					/* [m] pending notifications */
213 
214 	struct usb_task sc_task;	/* [m] notification task */
215 
216 	struct ksensordev sc_sensdev;	/* [m] */
217 	struct sensor_task *sc_senstsk;	/* [m] */
218 
219 	struct uhidpp_report *sc_req;	/* [m] synchronous request buffer */
220 	struct uhidpp_report *sc_resp;	/* [m] synchronous response buffer */
221 	u_int sc_resp_state;		/* [m] synchronous response state */
222 
223 };
224 
225 int uhidpp_match(struct device *, void *, void *);
226 void uhidpp_attach(struct device *, struct device *, void *);
227 int uhidpp_detach(struct device *, int flags);
228 void uhidpp_intr(struct uhidev *addr, void *ibuf, u_int len);
229 void uhidpp_refresh(void *);
230 void uhidpp_task(void *);
231 int uhidpp_sleep(struct uhidpp_softc *, uint64_t);
232 
233 void uhidpp_device_connect(struct uhidpp_softc *, struct uhidpp_device *);
234 void uhidpp_device_refresh(struct uhidpp_softc *, struct uhidpp_device *);
235 
236 struct uhidpp_notification *uhidpp_claim_notification(struct uhidpp_softc *);
237 int uhidpp_consume_notification(struct uhidpp_softc *, struct uhidpp_report *);
238 int uhidpp_is_notification(struct uhidpp_softc *, struct uhidpp_report *);
239 
240 int hidpp_get_protocol_version(struct uhidpp_softc  *, uint8_t, int *, int *);
241 
242 int hidpp10_get_name(struct uhidpp_softc *, uint8_t, char *, size_t);
243 int hidpp10_get_serial(struct uhidpp_softc *, uint8_t, uint8_t *, size_t);
244 int hidpp10_get_type(struct uhidpp_softc *, uint8_t, const char **);
245 int hidpp10_enable_notifications(struct uhidpp_softc *, uint8_t);
246 
247 int hidpp20_root_get_feature(struct uhidpp_softc *, uint8_t, uint16_t,
248     uint8_t *, uint8_t *);
249 int hidpp20_battery_get_level_status(struct uhidpp_softc *, uint8_t, uint8_t,
250     uint8_t *, uint8_t *, uint8_t *);
251 int hidpp20_battery_get_capability(struct uhidpp_softc *, uint8_t, uint8_t,
252     uint8_t *);
253 
254 int hidpp_send_validate(uint8_t, int);
255 int hidpp_send_rap_report(struct uhidpp_softc *, uint8_t, uint8_t,
256     uint8_t, uint8_t, uint8_t *, int, struct uhidpp_report *);
257 int hidpp_send_fap_report(struct uhidpp_softc *, uint8_t, uint8_t, uint8_t,
258     uint8_t, uint8_t *, int, struct uhidpp_report *);
259 int hidpp_send_report(struct uhidpp_softc *, uint8_t, struct uhidpp_report *,
260     struct uhidpp_report *);
261 
262 struct cfdriver uhidpp_cd = {
263 	NULL, "uhidpp", DV_DULL
264 };
265 
266 const struct cfattach uhidpp_ca = {
267 	sizeof(struct uhidpp_softc),
268 	uhidpp_match,
269 	uhidpp_attach,
270 	uhidpp_detach,
271 };
272 
273 static const struct usb_devno uhidpp_devs[] = {
274 	{ USB_VENDOR_LOGITECH,	USB_PRODUCT_ANY },
275 };
276 
277 int
278 uhidpp_match(struct device *parent, void *match, void *aux)
279 {
280 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
281 	void *desc;
282 	int descsiz, siz;
283 
284 	if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
285 		return UMATCH_NONE;
286 
287 	if (usb_lookup(uhidpp_devs,
288 		    uha->uaa->vendor, uha->uaa->product) == NULL)
289 		return UMATCH_NONE;
290 
291 	uhidev_get_report_desc(uha->parent, &desc, &descsiz);
292 	siz = hid_report_size(desc, descsiz, hid_output, HIDPP_REPORT_ID_SHORT);
293 	if (siz != HIDPP_REPORT_SHORT_LENGTH)
294 		return UMATCH_NONE;
295 	siz = hid_report_size(desc, descsiz, hid_output, HIDPP_REPORT_ID_LONG);
296 	if (siz != HIDPP_REPORT_LONG_LENGTH)
297 		return UMATCH_NONE;
298 
299 	return UMATCH_VENDOR_PRODUCT;
300 }
301 
302 void
303 uhidpp_attach(struct device *parent, struct device *self, void *aux)
304 {
305 	struct uhidpp_softc *sc = (struct uhidpp_softc *)self;
306 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
307 	struct usb_attach_arg *uaa = uha->uaa;
308 	int error, i;
309 	int npaired = 0;
310 
311 	sc->sc_hdev.sc_intr = uhidpp_intr;
312 	sc->sc_hdev.sc_udev = uaa->device;
313 	sc->sc_hdev.sc_parent = uha->parent;
314 	sc->sc_hdev.sc_report_id = uha->reportid;
315 	/* The largest supported report dictates the sizes. */
316 	sc->sc_hdev.sc_isize = HIDPP_REPORT_LONG_LENGTH;
317 	sc->sc_hdev.sc_osize = HIDPP_REPORT_LONG_LENGTH;
318 
319 	sc->sc_udev = uaa->device;
320 
321 	mtx_init(&sc->sc_mtx, IPL_USB);
322 
323 	sc->sc_resp = NULL;
324 	sc->sc_resp_state = UHIDPP_RESP_NONE;
325 
326 	error = uhidev_open(&sc->sc_hdev);
327 	if (error) {
328 		printf(" error %d\n", error);
329 		return;
330 	}
331 
332 	usb_init_task(&sc->sc_task, uhidpp_task, sc, USB_TASK_TYPE_GENERIC);
333 
334 	mtx_enter(&sc->sc_mtx);
335 
336 	/*
337 	 * Wire up report device handlers before issuing commands to the device
338 	 * in order to receive responses. Necessary as uhidev by default
339 	 * performs the wiring after the attach routine has returned.
340 	 */
341 	uhidev_set_report_dev(sc->sc_hdev.sc_parent, &sc->sc_hdev,
342 	    HIDPP_REPORT_ID_SHORT);
343 	uhidev_set_report_dev(sc->sc_hdev.sc_parent, &sc->sc_hdev,
344 	    HIDPP_REPORT_ID_LONG);
345 
346 	/* Probe paired devices. */
347 	for (i = 0; i < UHIDPP_NDEVICES; i++) {
348 		char name[16];
349 		uint8_t serial[4];
350 		struct uhidpp_device *dev = &sc->sc_devices[i];
351 		const char *type;
352 		uint8_t device_id = device_id + 1;
353 
354 		dev->d_id = device_id;
355 
356 		if (hidpp10_get_serial(sc, device_id, serial, sizeof(serial)) ||
357 		    hidpp10_get_type(sc, device_id, &type) ||
358 		    hidpp10_get_name(sc, device_id, name, sizeof(name)))
359 			continue;
360 
361 		if (npaired > 0)
362 			printf(",");
363 		printf(" device %d", device_id);
364 		printf(" %s", type);
365 		printf(" \"%s\"", name);
366 		printf(" serial %02x-%02x-%02x-%02x",
367 		    serial[0], serial[1], serial[2], serial[3]);
368 		npaired++;
369 	}
370 
371 	/* Enable notifications for the receiver. */
372 	error = hidpp10_enable_notifications(sc, HIDPP_DEVICE_ID_RECEIVER);
373 	if (error)
374 		printf(" error %d", error);
375 
376 	printf("\n");
377 
378 	strlcpy(sc->sc_sensdev.xname, sc->sc_hdev.sc_dev.dv_xname,
379 	    sizeof(sc->sc_sensdev.xname));
380 	sensordev_install(&sc->sc_sensdev);
381 	sc->sc_senstsk = sensor_task_register(sc, uhidpp_refresh, 6);
382 
383 	mtx_leave(&sc->sc_mtx);
384 }
385 
386 int
387 uhidpp_detach(struct device *self, int flags)
388 {
389 	struct uhidpp_softc *sc = (struct uhidpp_softc *)self;
390 	int i, j;
391 
392 	usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
393 
394 	if (sc->sc_senstsk != NULL)
395 		sensor_task_unregister(sc->sc_senstsk);
396 
397 	KASSERT(sc->sc_resp_state == UHIDPP_RESP_NONE);
398 
399 	sensordev_deinstall(&sc->sc_sensdev);
400 
401 	for (i = 0; i < UHIDPP_NDEVICES; i++) {
402 		struct uhidpp_device *dev = &sc->sc_devices[i];
403 
404 		if (!dev->d_connected)
405 			continue;
406 
407 		for (j = 0; j < UHIDPP_NSENSORS; j++)
408 			sensor_detach(&sc->sc_sensdev, &dev->d_battery.b_sens[j]);
409 	}
410 
411 	uhidev_close(&sc->sc_hdev);
412 
413 	return 0;
414 }
415 
416 void
417 uhidpp_intr(struct uhidev *addr, void *buf, u_int len)
418 {
419 	struct uhidpp_softc *sc = (struct uhidpp_softc *)addr;
420 	struct uhidpp_report *rep = buf;
421 	int dowake = 0;
422 	uint8_t repid;
423 
424 	/*
425 	 * Ugliness ahead as the report ID is stripped of by uhidev_intr() but
426 	 * needed to determine if an error occurred.
427 	 * Note that an error response is always a short report even if the
428 	 * command that caused the error is a long report.
429 	 */
430 	repid = ((uint8_t *)buf)[-1];
431 
432 	DREPORT(__func__, repid, buf, len);
433 
434 	mtx_enter(&sc->sc_mtx);
435 	if (uhidpp_is_notification(sc, rep)) {
436 		struct uhidpp_notification *ntf;
437 
438 		ntf = uhidpp_claim_notification(sc);
439 		if (ntf != NULL) {
440 			memcpy(&ntf->n_rep, buf, len);
441 			usb_add_task(sc->sc_udev, &sc->sc_task);
442 		} else {
443 			DPRINTF("%s: too many notifications", __func__);
444 		}
445 	} else {
446 		KASSERT(sc->sc_resp_state == UHIDPP_RESP_WAIT);
447 		dowake = 1;
448 		sc->sc_resp_state = repid;
449 		memcpy(sc->sc_resp, buf, len);
450 	}
451 	mtx_leave(&sc->sc_mtx);
452 	if (dowake)
453 		wakeup(sc);
454 }
455 
456 void
457 uhidpp_refresh(void *arg)
458 {
459 	struct uhidpp_softc *sc = arg;
460 	int i;
461 
462 	mtx_enter(&sc->sc_mtx);
463 	for (i = 0; i < UHIDPP_NDEVICES; i++) {
464 		struct uhidpp_device *dev = &sc->sc_devices[i];
465 
466 		if (dev->d_connected)
467 			uhidpp_device_refresh(sc, dev);
468 	}
469 	mtx_leave(&sc->sc_mtx);
470 }
471 
472 void
473 uhidpp_task(void *arg)
474 {
475 	struct uhidpp_softc *sc = arg;
476 
477 	mtx_enter(&sc->sc_mtx);
478 	for (;;) {
479 		struct uhidpp_report rep;
480 		struct uhidpp_device *dev;
481 
482 		if (uhidpp_consume_notification(sc, &rep))
483 			break;
484 
485 		DPRINTF("%s: device_id=%d, sub_id=%02x\n",
486 		    __func__, rep.device_id, rep.rap.sub_id);
487 
488 		if (rep.device_id == 0 || rep.device_id > UHIDPP_NDEVICES) {
489 			DPRINTF("%s: invalid device\n", __func__);
490 			continue;
491 		}
492 		dev = &sc->sc_devices[rep.device_id - 1];
493 
494 		switch (rep.rap.sub_id) {
495 		case 0x0e:	/* leds */
496 		case 0x40:	/* disconnect */
497 		case 0x4b:	/* pairing accepted */
498 			break;
499 		case 0x41:	/* connect */
500 			/*
501 			 * Do nothing if the link is reported to be out of
502 			 * range. This happens when a device has been idle for a
503 			 * while.
504 			 */
505 			if (HIDPP_LINK_STATUS(rep.rap.params[0]))
506 				uhidpp_device_connect(sc, dev);
507 			break;
508 		}
509 	}
510 	mtx_leave(&sc->sc_mtx);
511 }
512 
513 int
514 uhidpp_sleep(struct uhidpp_softc *sc, uint64_t nsecs)
515 {
516 	return msleep_nsec(sc, &sc->sc_mtx, PZERO, "uhidpp", nsecs);
517 }
518 
519 void
520 uhidpp_device_connect(struct uhidpp_softc *sc, struct uhidpp_device *dev)
521 {
522 	struct ksensor *sens;
523 	int error, major, minor;
524 	uint8_t feature_type;
525 
526 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
527 
528 	/* A connected device will continously send connect events. */
529 	if (dev->d_connected)
530 		return;
531 
532 	error = hidpp_get_protocol_version(sc, dev->d_id, &major, &minor);
533 	if (error) {
534 		DPRINTF("%s: protocol version failure: device_id=%d, error=%d\n",
535 		    __func__, dev->d_id, error);
536 		return;
537 	}
538 
539 	DPRINTF("%s: device_id=%d, version=%d.%d\n",
540 	    __func__, dev->d_id, major, minor);
541 
542 	error = hidpp20_root_get_feature(sc, dev->d_id,
543 	    HIDPP20_FEAT_BATTERY_IDX,
544 	    &dev->d_battery.b_feature_idx, &feature_type);
545 	if (error) {
546 		DPRINTF("%s: battery feature index failure: device_id=%d, "
547 		    "error=%d\n", __func__, dev->d_id, error);
548 		return;
549 	}
550 
551 	error = hidpp20_battery_get_capability(sc, dev->d_id,
552 	    dev->d_battery.b_feature_idx, &dev->d_battery.b_nlevels);
553 	if (error) {
554 		DPRINTF("%s: battery capability failure: device_id=%d, "
555 		    "error=%d\n", __func__, dev->d_id, error);
556 		return;
557 	}
558 
559 	sens = &dev->d_battery.b_sens[0];
560 	strlcpy(sens->desc, "battery level", sizeof(sens->desc));
561 	sens->type = SENSOR_PERCENT;
562 	sens->flags = SENSOR_FUNKNOWN;
563 	sensor_attach(&sc->sc_sensdev, sens);
564 
565 	sens = &dev->d_battery.b_sens[1];
566 	strlcpy(sens->desc, "battery levels", sizeof(sens->desc));
567 	sens->type = SENSOR_INTEGER;
568 	sens->value = dev->d_battery.b_nlevels;
569 	sensor_attach(&sc->sc_sensdev, sens);
570 
571 	dev->d_connected = 1;
572 	uhidpp_device_refresh(sc, dev);
573 }
574 
575 void
576 uhidpp_device_refresh(struct uhidpp_softc *sc, struct uhidpp_device *dev)
577 {
578 	int error;
579 
580 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
581 
582 	error = hidpp20_battery_get_level_status(sc, dev->d_id,
583 	    dev->d_battery.b_feature_idx,
584 	    &dev->d_battery.b_level, &dev->d_battery.b_next_level,
585 	    &dev->d_battery.b_status);
586 	if (error) {
587 		DPRINTF("%s: battery level status failure: device_id=%d, "
588 		    "error=%d\n", __func__, dev->d_id, error);
589 		return;
590 	}
591 
592 	dev->d_battery.b_sens[0].value = dev->d_battery.b_level * 1000;
593 	dev->d_battery.b_sens[0].flags &= ~SENSOR_FUNKNOWN;
594 	if (dev->d_battery.b_nlevels < 10) {
595 		/*
596 		 * According to the HID++ 2.0 specification, less than 10 levels
597 		 * should be mapped to the following 4 levels:
598 		 *
599 		 * [0, 10]   critical
600 		 * [11, 30]  low
601 		 * [31, 80]  good
602 		 * [81, 100] full
603 		 *
604 		 * Since sensors are limited to 3 valid statuses, clamp it even
605 		 * further.
606 		 */
607 		if (dev->d_battery.b_level <= 10)
608 			dev->d_battery.b_sens[0].status = SENSOR_S_CRIT;
609 		else if (dev->d_battery.b_level <= 30)
610 			dev->d_battery.b_sens[0].status = SENSOR_S_WARN;
611 		else
612 			dev->d_battery.b_sens[0].status = SENSOR_S_OK;
613 	} else {
614 		/*
615 		 * XXX the device supports battery mileage. The current level
616 		 * must be checked against resp.fap.params[3] given by
617 		 * hidpp20_battery_get_capability().
618 		 */
619 		dev->d_battery.b_sens[0].status = SENSOR_S_UNKNOWN;
620 	}
621 }
622 
623 /*
624  * Returns the next available notification slot, if available.
625  */
626 struct uhidpp_notification *
627 uhidpp_claim_notification(struct uhidpp_softc *sc)
628 {
629 	struct uhidpp_notification *ntf = NULL;
630 	int nclaimed = 0;
631 	int i;
632 
633 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
634 
635 	for (i = 0; i < UHIDPP_NNOTIFICATIONS; i++) {
636 		struct uhidpp_notification *tmp = &sc->sc_notifications[i];
637 
638 		if (tmp->n_id > 0)
639 			nclaimed++;
640 		else if (ntf == NULL)
641 			ntf = tmp;
642 	}
643 
644 	if (ntf == NULL)
645 		return NULL;
646 	ntf->n_id = nclaimed + 1;
647 	return ntf;
648 }
649 
650 /*
651  * Consume the first unhandled notification, if present.
652  */
653 int
654 uhidpp_consume_notification(struct uhidpp_softc *sc, struct uhidpp_report *rep)
655 {
656 	struct uhidpp_notification *ntf = NULL;
657 	int i;
658 
659 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
660 
661 	for (i = 0; i < UHIDPP_NNOTIFICATIONS; i++) {
662 		struct uhidpp_notification *tmp = &sc->sc_notifications[i];
663 
664 		if (tmp->n_id > 0 && (ntf == NULL || tmp->n_id < ntf->n_id))
665 			ntf = tmp;
666 	}
667 	if (ntf == NULL)
668 		return 1;
669 
670 	memcpy(rep, &ntf->n_rep, sizeof(*rep));
671 	ntf->n_id = 0;
672 	return 0;
673 }
674 
675 
676 /*
677  * Returns non-zero if the given report is a notification. Otherwise, it must be
678  * a response.
679  */
680 int
681 uhidpp_is_notification(struct uhidpp_softc *sc, struct uhidpp_report *rep)
682 {
683 	/* Not waiting for a response. */
684 	if (sc->sc_req == NULL)
685 		return 1;
686 
687 	/* Everything except the parameters must be repeated in a response. */
688 	if (sc->sc_req->device_id == rep->device_id &&
689 	    sc->sc_req->rap.sub_id == rep->rap.sub_id &&
690 	    sc->sc_req->rap.reg_address == rep->rap.reg_address)
691 		return 0;
692 
693 	/* An error must always be a response. */
694 	if ((rep->rap.sub_id == HIDPP_ERROR ||
695 		    rep->fap.feature_index == HIDPP20_ERROR) &&
696 	    rep->fap.funcindex_clientid == sc->sc_req->fap.feature_index &&
697 	    rep->fap.params[0] == sc->sc_req->fap.funcindex_clientid)
698 		return 0;
699 
700 	return 1;
701 }
702 
703 int
704 hidpp_get_protocol_version(struct uhidpp_softc  *sc, uint8_t device_id,
705     int *major, int *minor)
706 {
707 	struct uhidpp_report resp;
708 	uint8_t params[3] = { 0, 0, HIDPP_FEAT_ROOT_PING_DATA };
709 	int error;
710 
711 	error = hidpp_send_fap_report(sc,
712 	    HIDPP_REPORT_ID_SHORT,
713 	    device_id,
714 	    HIDPP_FEAT_ROOT_IDX,
715 	    HIDPP_FEAT_ROOT_PING_FUNC,
716 	    params, sizeof(params), &resp);
717 	if (error == HIDPP_ERROR_INVALID_SUBID) {
718 		*major = 1;
719 		*minor = 0;
720 		return 0;
721 	}
722 	if (error)
723 		return error;
724 	if (resp.rap.params[2] != HIDPP_FEAT_ROOT_PING_DATA)
725 		return -EPROTO;
726 
727 	*major = resp.fap.params[0];
728 	*minor = resp.fap.params[1];
729 	return 0;
730 }
731 
732 int
733 hidpp10_get_name(struct uhidpp_softc *sc, uint8_t device_id,
734     char *buf, size_t bufsiz)
735 {
736 	struct uhidpp_report resp;
737 	int error;
738 	uint8_t params[1] = { 0x40 + (device_id - 1) };
739 	uint8_t len;
740 
741 	error = hidpp_send_rap_report(sc,
742 	    HIDPP_REPORT_ID_SHORT,
743 	    HIDPP_DEVICE_ID_RECEIVER,
744 	    HIDPP_GET_LONG_REGISTER,
745 	    HIDPP_REG_PAIRING_INFORMATION,
746 	    params, sizeof(params), &resp);
747 	if (error)
748 		return error;
749 
750 	len = resp.rap.params[1];
751 	if (len + 2 > sizeof(resp.rap.params))
752 		return -ENAMETOOLONG;
753 	if (len > bufsiz - 1)
754 		len = bufsiz - 1;
755 	memcpy(buf, &resp.rap.params[2], len);
756 	buf[len] = '\0';
757 	return 0;
758 }
759 
760 int
761 hidpp10_get_serial(struct uhidpp_softc *sc, uint8_t device_id,
762     uint8_t *buf, size_t bufsiz)
763 {
764 	struct uhidpp_report resp;
765 	int error;
766 	uint8_t params[1] = { 0x30 + (device_id - 1) };
767 	uint8_t len;
768 
769 	error = hidpp_send_rap_report(sc,
770 	    HIDPP_REPORT_ID_SHORT,
771 	    HIDPP_DEVICE_ID_RECEIVER,
772 	    HIDPP_GET_LONG_REGISTER,
773 	    HIDPP_REG_PAIRING_INFORMATION,
774 	    params, sizeof(params), &resp);
775 	if (error)
776 		return error;
777 
778 	len = 4;
779 	if (bufsiz < len)
780 		len = bufsiz;
781 	memcpy(buf, &resp.rap.params[1], len);
782 	return 0;
783 }
784 
785 int
786 hidpp10_get_type(struct uhidpp_softc *sc, uint8_t device_id, const char **type)
787 {
788 	struct uhidpp_report resp;
789 	int error;
790 	uint8_t params[1] = { 0x20 + (device_id - 1) };
791 
792 	error = hidpp_send_rap_report(sc,
793 	    HIDPP_REPORT_ID_SHORT,
794 	    HIDPP_DEVICE_ID_RECEIVER,
795 	    HIDPP_GET_LONG_REGISTER,
796 	    HIDPP_REG_PAIRING_INFORMATION,
797 	    params, sizeof(params), &resp);
798 	if (error)
799 		return error;
800 
801 	switch (resp.rap.params[7]) {
802 	case 0x00:
803 		*type = "unknown";
804 		return 0;
805 	case 0x01:
806 		*type = "keyboard";
807 		return 0;
808 	case 0x02:
809 		*type = "mouse";
810 		return 0;
811 	case 0x03:
812 		*type = "numpad";
813 		return 0;
814 	case 0x04:
815 		*type = "presenter";
816 		return 0;
817 	case 0x08:
818 		*type = "trackball";
819 		return 0;
820 	case 0x09:
821 		*type = "touchpad";
822 		return 0;
823 	}
824 	return -ENOENT;
825 }
826 
827 int
828 hidpp10_enable_notifications(struct uhidpp_softc *sc, uint8_t device_id)
829 {
830 	struct uhidpp_report resp;
831 	uint8_t params[3];
832 
833 	/* Device reporting flags. */
834 	params[0] = HIDPP_NOTIF_DEVICE_BATTERY_STATUS;
835 	/* Receiver reporting flags. */
836 	params[1] = HIDPP_NOTIF_RECEIVER_WIRELESS |
837 	    HIDPP_NOTIF_RECEIVER_SOFTWARE_PRESENT;
838 	/* Device reporting flags (continued). */
839 	params[2] = 0;
840 
841 	return hidpp_send_rap_report(sc,
842 	    HIDPP_REPORT_ID_SHORT,
843 	    device_id,
844 	    HIDPP_SET_REGISTER,
845 	    HIDPP_REG_ENABLE_REPORTS,
846 	    params, sizeof(params), &resp);
847 }
848 
849 int
850 hidpp20_root_get_feature(struct uhidpp_softc *sc, uint8_t device_id,
851     uint16_t feature, uint8_t *feature_index, uint8_t *feature_type)
852 {
853 	struct uhidpp_report resp;
854 	uint8_t params[2] = { feature >> 8, feature & 0xff };
855 	int error;
856 
857 	error = hidpp_send_fap_report(sc,
858 	    HIDPP_REPORT_ID_LONG,
859 	    device_id,
860 	    HIDPP20_FEAT_ROOT_IDX,
861 	    HIDPP20_FEAT_ROOT_GET_FEATURE_FUNC,
862 	    params, sizeof(params), &resp);
863 	if (error)
864 		return error;
865 
866 	if (resp.fap.params[0] == 0)
867 		return -ENOENT;
868 
869 	*feature_index = resp.fap.params[0];
870 	*feature_type = resp.fap.params[1];
871 	return 0;
872 }
873 
874 int
875 hidpp20_battery_get_level_status(struct uhidpp_softc *sc, uint8_t device_id,
876     uint8_t feature_index, uint8_t *level, uint8_t *next_level, uint8_t *status)
877 {
878 	struct uhidpp_report resp;
879 	int error;
880 
881 	error = hidpp_send_fap_report(sc,
882 	    HIDPP_REPORT_ID_LONG,
883 	    device_id,
884 	    feature_index,
885 	    HIDPP20_FEAT_BATTERY_LEVEL_FUNC,
886 	    NULL, 0, &resp);
887 	if (error)
888 		return error;
889 
890 	*level = resp.fap.params[0];
891 	*next_level = resp.fap.params[1];
892 	*status = resp.fap.params[2];
893 	return 0;
894 }
895 
896 int
897 hidpp20_battery_get_capability(struct uhidpp_softc *sc, uint8_t device_id,
898     uint8_t feature_index, uint8_t *nlevels)
899 {
900 	struct uhidpp_report resp;
901 	int error;
902 
903 	error = hidpp_send_fap_report(sc,
904 	    HIDPP_REPORT_ID_LONG,
905 	    device_id,
906 	    feature_index,
907 	    HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC,
908 	    NULL, 0, &resp);
909 	if (error)
910 		return error;
911 	*nlevels = resp.fap.params[0];
912 	return 0;
913 }
914 
915 int
916 hidpp_send_validate(uint8_t report_id, int nparams)
917 {
918 	if (report_id == HIDPP_REPORT_ID_SHORT) {
919 		if (nparams > HIDPP_REPORT_SHORT_PARAMS_MAX)
920 			return -EMSGSIZE;
921 	} else if (report_id == HIDPP_REPORT_ID_LONG) {
922 		if (nparams > HIDPP_REPORT_LONG_PARAMS_MAX)
923 			return -EMSGSIZE;
924 	} else {
925 		return -EINVAL;
926 	}
927 	return 0;
928 }
929 
930 int
931 hidpp_send_fap_report(struct uhidpp_softc *sc, uint8_t report_id,
932     uint8_t device_id, uint8_t feature_index, uint8_t funcindex_clientid,
933     uint8_t *params, int nparams, struct uhidpp_report *resp)
934 {
935 	struct uhidpp_report req;
936 	int error;
937 
938 	error = hidpp_send_validate(report_id, nparams);
939 	if (error)
940 		return error;
941 
942 	memset(&req, 0, sizeof(req));
943 	req.device_id = device_id;
944 	req.fap.feature_index = feature_index;
945 	req.fap.funcindex_clientid =
946 	    (funcindex_clientid << HIDPP_SOFTWARE_ID_LEN) | HIDPP_SOFTWARE_ID;
947 	memcpy(req.fap.params, params, nparams);
948 	return hidpp_send_report(sc, report_id, &req, resp);
949 }
950 
951 int
952 hidpp_send_rap_report(struct uhidpp_softc *sc, uint8_t report_id,
953     uint8_t device_id, uint8_t sub_id, uint8_t reg_address,
954     uint8_t *params, int nparams, struct uhidpp_report *resp)
955 {
956 	struct uhidpp_report req;
957 	int error;
958 
959 	error = hidpp_send_validate(report_id, nparams);
960 	if (error)
961 		return error;
962 
963 	memset(&req, 0, sizeof(req));
964 	req.device_id = device_id;
965 	req.rap.sub_id = sub_id;
966 	req.rap.reg_address = reg_address;
967 	memcpy(req.rap.params, params, nparams);
968 	return hidpp_send_report(sc, report_id, &req, resp);
969 }
970 
971 int
972 hidpp_send_report(struct uhidpp_softc *sc, uint8_t report_id,
973     struct uhidpp_report *req, struct uhidpp_report *resp)
974 {
975 	int error, len, n;
976 
977 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
978 
979 	if (report_id == HIDPP_REPORT_ID_SHORT)
980 		len = HIDPP_REPORT_SHORT_LENGTH;
981 	else if (report_id == HIDPP_REPORT_ID_LONG)
982 		len = HIDPP_REPORT_LONG_LENGTH;
983 	else
984 		return -EINVAL;
985 
986 	DREPORT(__func__, report_id, (const unsigned char *)req, len);
987 
988 	/* Wait until any ongoing command has completed. */
989 	while (sc->sc_resp_state != UHIDPP_RESP_NONE)
990 		uhidpp_sleep(sc, INFSLP);
991 	sc->sc_req = req;
992 	sc->sc_resp = resp;
993 	sc->sc_resp_state = UHIDPP_RESP_WAIT;
994 	/*
995 	 * The mutex must be temporarily released while calling
996 	 * uhidev_set_report() as it might end up sleeping.
997 	 */
998 	mtx_leave(&sc->sc_mtx);
999 
1000 	n = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
1001 	    report_id, req, len);
1002 
1003 	mtx_enter(&sc->sc_mtx);
1004 	if (len != n) {
1005 		error = -EBUSY;
1006 		goto out;
1007 	}
1008 	/*
1009 	 * The interrupt could already have been received while the mutex was
1010 	 * released. Otherwise, wait for it.
1011 	 */
1012 	if (sc->sc_resp_state == UHIDPP_RESP_WAIT) {
1013 		/* Timeout taken from the hid-logitech-hidpp Linux driver. */
1014 		error = uhidpp_sleep(sc, SEC_TO_NSEC(5));
1015 		if (error) {
1016 			error = -error;
1017 			goto out;
1018 		}
1019 	}
1020 
1021 	if (sc->sc_resp_state == UHIDPP_RESP_ERROR)
1022 		error = -EIO;
1023 	else if (sc->sc_resp_state == HIDPP_REPORT_ID_SHORT &&
1024 	    resp->rap.sub_id == HIDPP_ERROR)
1025 		error = resp->rap.params[1];
1026 	else if (sc->sc_resp_state == HIDPP_REPORT_ID_LONG &&
1027 	    resp->fap.feature_index == HIDPP20_ERROR)
1028 		error = resp->fap.params[1];
1029 
1030 out:
1031 	sc->sc_req = NULL;
1032 	sc->sc_resp = NULL;
1033 	sc->sc_resp_state = UHIDPP_RESP_NONE;
1034 	wakeup(sc);
1035 	return error;
1036 }
1037 
1038 #ifdef UHIDPP_DEBUG
1039 
1040 void
1041 uhidd_dump_report(const char *prefix, uint8_t repid, const unsigned char *buf,
1042     u_int buflen)
1043 {
1044 	u_int i;
1045 
1046 	printf("%s: %02x ", prefix, repid);
1047 	for (i = 0; i < buflen; i++) {
1048 		printf("%02x%s", buf[i],
1049 		    i == 2 ? " [" : (i + 1 < buflen ? " " : ""));
1050 	}
1051 	printf("]\n");
1052 }
1053 
1054 #endif
1055