Hi, I have a bit oldish Logitech M705 mouse, bought around 2010-2011. Regarding the dmesg (on below) I can see it gets attached correctly to uhiddp0 but doesn't report battery levels. Here's the line from dmesg: uhidpp0 at uhidev2 device 1 mouse "M705" serial xx-xx-x-xx, device 2 keyboard "K750" serial xx-xx-xx-xx. And corresponding sysctl values: hw.sensors.uhidpp0.raw0=unknown (battery levels) hw.sensors.uhidpp0.raw1=unknown (battery levels) hw.sensors.uhidpp0.percent0=unknown (battery level) hw.sensors.uhidpp0.percent1=unknown (battery level)
Not sure if censoring of serial has any value, though. On Ubuntu battery levels are detected correctly so I could probably take a USB dump with Wireshark and compare the differences. And here's sysctl.sensors & dmesg differences (before <-> after the patch): <sysctl.sensors> --- sysctl_before.txt Fri Jan 29 21:31:44 2021 +++ sysctl_after.txt Fri Jan 29 21:34:50 2021 @@ -5,39 +5,43 @@ hw.byteorder=1234 hw.pagesize=4096 hw.disknames=sd0:c01774372c2b714a hw.diskcount=1 -hw.sensors.cpu0.temp0=54.00 degC +hw.sensors.cpu0.temp0=50.00 degC hw.sensors.acpibtn0.indicator0=On (lid open) hw.sensors.acpibat0.volt0=11.10 VDC (voltage) hw.sensors.acpibat0.volt1=12.38 VDC (current voltage) hw.sensors.acpibat0.power0=0.00 W (rate) -hw.sensors.acpibat0.watthour0=15.64 Wh (last full capacity) +hw.sensors.acpibat0.watthour0=15.62 Wh (last full capacity) hw.sensors.acpibat0.watthour1=0.78 Wh (warning capacity) hw.sensors.acpibat0.watthour2=0.20 Wh (low capacity) hw.sensors.acpibat0.watthour3=15.62 Wh (remaining capacity), OK hw.sensors.acpibat0.watthour4=23.20 Wh (design capacity) -hw.sensors.acpibat0.raw0=0 (battery idle), OK +hw.sensors.acpibat0.raw0=0 (battery full), OK hw.sensors.acpibat1.volt0=11.10 VDC (voltage) hw.sensors.acpibat1.volt1=12.32 VDC (current voltage) hw.sensors.acpibat1.power0=0.00 W (rate) -hw.sensors.acpibat1.watthour0=16.53 Wh (last full capacity) +hw.sensors.acpibat1.watthour0=16.52 Wh (last full capacity) hw.sensors.acpibat1.watthour1=0.83 Wh (warning capacity) hw.sensors.acpibat1.watthour2=0.20 Wh (low capacity) hw.sensors.acpibat1.watthour3=16.52 Wh (remaining capacity), OK hw.sensors.acpibat1.watthour4=23.20 Wh (design capacity) -hw.sensors.acpibat1.raw0=0 (battery idle), OK +hw.sensors.acpibat1.raw0=0 (battery full), OK hw.sensors.acpiac0.indicator0=On (power supply) -hw.sensors.acpithinkpad0.temp0=55.00 degC -hw.sensors.acpithinkpad0.temp1=55.00 degC -hw.sensors.acpithinkpad0.temp2=55.00 degC -hw.sensors.acpithinkpad0.temp3=55.00 degC -hw.sensors.acpithinkpad0.temp4=55.00 degC -hw.sensors.acpithinkpad0.temp5=55.00 degC -hw.sensors.acpithinkpad0.temp6=55.00 degC -hw.sensors.acpithinkpad0.temp7=55.00 degC -hw.sensors.acpithinkpad0.fan0=4112 RPM +hw.sensors.acpithinkpad0.temp0=51.00 degC +hw.sensors.acpithinkpad0.temp1=51.00 degC +hw.sensors.acpithinkpad0.temp2=51.00 degC +hw.sensors.acpithinkpad0.temp3=51.00 degC +hw.sensors.acpithinkpad0.temp4=51.00 degC +hw.sensors.acpithinkpad0.temp5=51.00 degC +hw.sensors.acpithinkpad0.temp6=51.00 degC +hw.sensors.acpithinkpad0.temp7=51.00 degC +hw.sensors.acpithinkpad0.fan0=3659 RPM hw.sensors.acpithinkpad0.indicator0=Off (port replicator), UNKNOWN -hw.sensors.acpitz0.temp0=55.00 degC (zone temperature) -hw.sensors.pchtemp0.temp0=62.50 degC +hw.sensors.acpitz0.temp0=51.00 degC (zone temperature) +hw.sensors.pchtemp0.temp0=58.00 degC +hw.sensors.uhidpp0.raw0=unknown (battery levels) +hw.sensors.uhidpp0.raw1=unknown (battery levels) +hw.sensors.uhidpp0.percent0=unknown (battery level) +hw.sensors.uhidpp0.percent1=unknown (battery level) hw.cpuspeed=500 hw.setperf=0 hw.vendor=LENOVO @@ -46,7 +50,7 @@ hw.version=ThinkPad X250 hw.serialno=PC031KCD hw.uuid=816b2e2f-d253-cb11-9791-8821c15b5a68 hw.physmem=8277995520 -hw.usermem=8233095168 +hw.usermem=8221360128 hw.ncpufound=2 hw.allowpowerdown=1 hw.perfpolicy=auto </sysctl.sensors> And: <dmesg> --- dmesg_before.txt 2021-01-29 21:38:10.000000000 +0200 +++ dmesg_after.txt 2021-01-29 22:03:30.341216215 +0200 @@ -1,7 +1,7 @@ -OpenBSD 6.8-current (GENERIC.MP) #301: Fri Jan 29 02:04:27 MST 2021 - dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP +OpenBSD 6.8-current (GENERIC.MP) #0: Fri Jan 29 21:30:07 EET 2021 + wee...@weezel.lan:/obsd_src/sys/arch/amd64/compile/GENERIC.MP real mem = 8277995520 (7894MB) -avail mem = 8011780096 (7640MB) +avail mem = 8011751424 (7640MB) random: good seed from bootblocks mpath0 at root scsibus0 at mpath0: 256 targets @@ -18,7 +18,7 @@ acpihpet0 at acpi0: 14318179 Hz acpiec0 at acpi0 acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat cpu0 at mainbus0: apid 0 (boot processor) -cpu0: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz, 2494.55 MHz, 06-3d-04 +cpu0: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz, 2494.63 MHz, 06-3d-04 cpu0: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36 ,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAI T,DS- CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x 2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSC P,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,TSC_ADJUST,BMI1,HLE,AVX2,SMEP ,BMI2,ERMS,INVPCID,RTM,RDSEED,ADX,SMAP,PT,SRBDS_CTRL,MD_CLEAR,IBRS,IBPB ,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN cpu0: 256KB 64b/line 8-way L2 cache cpu0: smt 0, core 0, package 0 @@ -26,7 +26,7 @@ mtrr: Pentium Pro MTRR support, 10 var r cpu0: apic clock running at 99MHz cpu0: mwait min=64, max=64, C-substates=0.2.1.2.4.1.1.1, IBE cpu1 at mainbus0: apid 2 (application processor) -cpu1: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz, 2494.24 MHz, 06-3d-04 +cpu1: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz, 2494.23 MHz, 06-3d-04 cpu1: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36 ,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAI T,DS- CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x 2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSC P,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,TSC_ADJUST,BMI1,HLE,AVX2,SMEP ,BMI2,ERMS,INVPCID,RTM,RDSEED,ADX,SMAP,PT,SRBDS_CTRL,MD_CLEAR,IBRS,IBPB ,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN cpu1: 256KB 64b/line 8-way L2 cache cpu1: smt 0, core 1, package 0 @@ -107,32 +107,29 @@ pms0: Synaptics clickpad, firmware 8.1, pcppi0 at isa0 port 0x61 spkr0 at pcppi0 vmm0 at mainbus0: VMX/EPT -ugen0 at uhub0 port 7 "Intel Bluetooth" rev 2.01/0.01 addr 2 -uhub2 at uhub1 port 1 configuration 1 interface 0 "Intel Rate Matching Hub" rev 2.00/0.03 addr 2 -vscsi0 at root -scsibus2 at vscsi0: 256 targets -softraid0 at root -scsibus3 at softraid0: 256 targets -root on sd0a (c01774372c2b714a.a) swap on sd0b dump on sd0b -inteldrm0: 1920x1080, 32bpp -wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation), using wskbd0 -wsdisplay0: screen 1-5 added (std, vt100 emulation) -iwm0: hw rev 0x210, fw ver 17.3216344376.0, address 60:57:18:6a:df:8d -uhidev0 at uhub0 port 1 configuration 1 interface 0 "Logitech USB Receiver" rev 2.00/12.10 addr 3 +uhidev0 at uhub0 port 1 configuration 1 interface 0 "Logitech USB Receiver" rev 2.00/12.10 addr 2 uhidev0: iclass 3/1 ukbd0 at uhidev0: 8 variable keys, 6 key codes wskbd1 at ukbd0 mux 1 -wskbd1: connecting to wsdisplay0 -uhidev1 at uhub0 port 1 configuration 1 interface 1 "Logitech USB Receiver" rev 2.00/12.10 addr 3 +uhidev1 at uhub0 port 1 configuration 1 interface 1 "Logitech USB Receiver" rev 2.00/12.10 addr 2 uhidev1: iclass 3/1, 8 report ids ums0 at uhidev1 reportid 2: 16 buttons, Z and W dir wsmouse2 at ums0 mux 0 uhid0 at uhidev1 reportid 3: input=4, output=0, feature=0 uhid1 at uhidev1 reportid 4: input=1, output=0, feature=0 uhid2 at uhidev1 reportid 8: input=1, output=0, feature=0 -uhidev2 at uhub0 port 1 configuration 1 interface 2 "Logitech USB Receiver" rev 2.00/12.10 addr 3 +uhidev2 at uhub0 port 1 configuration 1 interface 2 "Logitech USB Receiver" rev 2.00/12.10 addr 2 uhidev2: iclass 3/0, 33 report ids -uhid3 at uhidev2 reportid 16: input=6, output=6, feature=0 -uhid4 at uhidev2 reportid 17: input=19, output=19, feature=0 -uhid5 at uhidev2 reportid 32: input=14, output=14, feature=0 -uhid6 at uhidev2 reportid 33: input=31, output=31, feature=0 +uhidpp0 at uhidev2 device 1 mouse "M705" serial xx-xx-xx-xx, device 2 keyboard "K750" serial xx-xx-xx-xx +ugen0 at uhub0 port 7 "Intel Bluetooth" rev 2.01/0.01 addr 3 +uhub2 at uhub1 port 1 configuration 1 interface 0 "Intel Rate Matching Hub" rev 2.00/0.03 addr 2 +vscsi0 at root +scsibus2 at vscsi0: 256 targets +softraid0 at root +scsibus3 at softraid0: 256 targets +root on sd0a (c01774372c2b714a.a) swap on sd0b dump on sd0b +inteldrm0: 1920x1080, 32bpp +wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation), using wskbd0 +wskbd1: connecting to wsdisplay0 +wsdisplay0: screen 1-5 added (std, vt100 emulation) +iwm0: hw rev 0x210, fw ver 17.3216344376.0, address 60:57:18:6a:df:8d </dmesg> -- Kind regards, Ville On Fri, 2021-01-29 at 19:23 +0200, Ville Valkonen wrote: Hello Anton, either I failed to use git or then files have changed since the first patch: $ git apply -p0 --check hid_plusplus.patch error: patch failed: share/man/man4/Makefile:83 error: share/man/man4/Makefile: patch does not apply error: patch failed: share/man/man4/uhidev.4:40 error: share/man/man4/uhidev.4: patch does not apply error: patch failed: share/man/man4/usb.4:255 error: share/man/man4/usb.4: patch does not apply error: patch failed: sys/arch/amd64/conf/GENERIC:288 error: sys/arch/amd64/conf/GENERIC: patch does not apply error: patch failed: sys/dev/usb/uhidev.c:950 error: sys/dev/usb/uhidev.c: patch does not apply error: sys/dev/usb/uhidpp.c: already exists in working directory Running that on root of src. A quick peek on sys/dev/usb/uhidev.c file shows that it has been modified on 25th of Jan so I'd guess the patch needs to be updated. Thanks in advance! Been thinking to have a look on that protocol but since I am no HW hacker I've postponed that for years :) -- Kind regards, Ville On Fri, 29 Jan 2021 at 09:21, Anton Lindqvist <an...@openbsd.org> wrote: > > Ping > > On Fri, Jan 22, 2021 at 08:18:51AM +0100, Anton Lindqvist wrote: > > Hi, > > Here's a new driver for Logitech HID++ devices, currently limited > > to > > exposing battery sensors. Here's an example using a Logitech M330 > > mouse: > > > > $ dmesg | grep uhidpp > > uhidpp0 at uhidev1 device 1 mouse "B330/M330/M3" serial c7- > > 2f-a8-33 > > $ sysctl hw.sensors.uhidpp0 > > hw.sensors.uhidpp0.raw0=2 (battery levels) > > hw.sensors.uhidpp0.percent0=70.00% (battery level), OK > > > > The raw0 sensor reflects number of available battery levels, the > > resolution on this device is not great... > > > > Most of the code is derived from the hid-logitech-hidpp Linux > > driver. > > Some assorted notes: > > > > * In order to communicate with the device inside the attach > > routine, I > > had to wire up the interrupt handler as this by default is done > > first > > once the same attach routine has returned. Hence the introduction > > of > > uhidev_set_intr(). If this is an acceptable approach, this can go > > in > > as a separate commit. > > > > * I kept using the `return -errno' convention from the Linux driver > > in > > order to distingush errors from the hardware, which are always > > positive. > > > > I you happen to have a Logitech HID++ device and run into any > > problem(s), please enable UHIDPP_DEBUG and send me the output. > > > > Comments? OK? > > > > diff --git share/man/man4/Makefile share/man/man4/Makefile > > index 02af7a47a44..74a4e17d7dc 100644 > > --- share/man/man4/Makefile > > +++ share/man/man4/Makefile > > @@ -83,8 +83,8 @@ MAN= aac.4 abcrtc.4 abl.4 ac97.4 acphy.4 > > acrtc.4 \ > > txp.4 txphy.4 uaudio.4 uark.4 uath.4 ubcmtp.4 uberry.4 ubsa.4 > > \ > > ubsec.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 ukspan.4 uslhcom.4 > > \ > > udav.4 udcf.4 udl.4 udp.4 udsbr.4 \ > > - uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 > > uipaq.4 \ > > - uk.4 ukbd.4 \ > > + uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 > > uhidpp.4 \ > > + uipaq.4 uk.4 ukbd.4 \ > > ukphy.4 ulpt.4 umass.4 umb.4 umbg.4 umcs.4 umct.4 umidi.4 > > umodem.4 \ > > ums.4 umsm.4 umstc.4 umt.4 unix.4 uonerng.4 uow.4 uoaklux.4 > > uoakrh.4 \ > > uoakv.4 upd.4 upgt.4 upl.4 uplcom.4 ural.4 ure.4 url.4 > > urlphy.4 \ > > diff --git share/man/man4/uhidev.4 share/man/man4/uhidev.4 > > index f0a6776a27b..d264935a65c 100644 > > --- share/man/man4/uhidev.4 > > +++ share/man/man4/uhidev.4 > > @@ -40,6 +40,7 @@ > > .Cd "ucycom* at uhidev?" > > .Cd "ugold* at uhidev?" > > .Cd "uhid* at uhidev?" > > +.Cd "uhidpp* at uhidev?" > > .Cd "ukbd* at uhidev?" > > .Cd "ums* at uhidev?" > > .Cd "umstc* at uhidev?" > > @@ -73,6 +74,7 @@ only dispatches data to them based on the report > > id. > > .Xr ucycom 4 , > > .Xr ugold 4 , > > .Xr uhid 4 , > > +.Xr uhidpp 4 , > > .Xr ukbd 4 , > > .Xr ums 4 , > > .Xr umstc 4 , > > diff --git share/man/man4/uhidpp.4 share/man/man4/uhidpp.4 > > new file mode 100644 > > index 00000000000..4c78380c35b > > --- /dev/null > > +++ share/man/man4/uhidpp.4 > > @@ -0,0 +1,48 @@ > > +.\" $OpenBSD$ > > +.\" > > +.\" Copyright (c) 2021 Anton Lindqvsit <an...@openbsd.org> > > +.\" > > +.\" Permission to use, copy, modify, and distribute this software > > for any > > +.\" purpose with or without fee is hereby granted, provided that > > the above > > +.\" copyright notice and this permission notice appear in all > > copies. > > +.\" > > +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL > > WARRANTIES > > +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES > > OF > > +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE > > LIABLE FOR > > +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY > > DAMAGES > > +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, > > WHETHER IN AN > > +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, > > ARISING OUT OF > > +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > > +.\" > > +.Dd $Mdocdate$ > > +.Dt UHIDPP 4 > > +.Os > > +.Sh NAME > > +.Nm uhidpp > > +.Nd Logitech HID++ devices > > +.Sh SYNOPSIS > > +.Cd "uhidpp* at uhidev?" > > +.Sh DESCRIPTION > > +The > > +.Nm > > +driver provides support for Logitech HID++ devices. > > +It exposes a collection of battery sensor values which are made > > available > > +through the > > +.Xr sysctl 8 > > +interface. > > +.Sh SEE ALSO > > +.Xr intro 4 , > > +.Xr uhidev 4 , > > +.Xr usb 4 , > > +.Xr sensorsd 8 , > > +.Xr sysctl 8 > > +.Sh HISTORY > > +The > > +.Nm > > +driver first appeared in > > +.Ox 6.9 . > > +.Sh AUTHORS > > +The > > +.Nm > > +driver was written by > > +.An Anton Lindqvist Aq Mt an...@opensd.org . > > diff --git share/man/man4/usb.4 share/man/man4/usb.4 > > index 520f46265e0..b58190539e3 100644 > > --- share/man/man4/usb.4 > > +++ share/man/man4/usb.4 > > @@ -255,6 +255,8 @@ TEMPer gold HID thermometer and hygrometer > > Generic driver for Human Interface Devices > > .It Xr uhidev 4 > > Base driver for all Human Interface Devices > > +.It Xr uhidpp 4 > > +Logitech HID++ devices > > .It Xr ukbd 4 > > USB keyboards that follow the boot protocol > > .It Xr ums 4 > > diff --git sys/arch/amd64/conf/GENERIC sys/arch/amd64/conf/GENERIC > > index 45b3a9b6e66..00ac52adcd6 100644 > > --- sys/arch/amd64/conf/GENERIC > > +++ sys/arch/amd64/conf/GENERIC > > @@ -288,6 +288,7 @@ uhid* at uhidev? # USB generic > > HID support > > fido* at uhidev? # FIDO/U2F security key > > support > > upd* at uhidev? # USB Power Devices sensors > > umstc* at uhidev? # Microsoft Surface Type > > Cover > > +uhidpp* at uhidev? # Logitech HID++ Devices > > aue* at uhub? # ADMtek AN986 Pegasus Ethernet > > atu* at uhub? # Atmel AT76c50x based 802.11b > > axe* at uhub? # ASIX Electronics AX88172 USB > > Ethernet > > diff --git sys/dev/usb/files.usb sys/dev/usb/files.usb > > index 1d673cf635d..5c95d1c0ac5 100644 > > --- sys/dev/usb/files.usb > > +++ sys/dev/usb/files.usb > > @@ -478,3 +478,8 @@ file dev/usb/if_bwfm_usb.c > > bwfm_usb > > device umstc: hid > > attach umstc at uhidbus > > file dev/usb/umstc.c umstc > > + > > +# Logitech HID++ Devices > > +device uhidpp: hid > > +attach uhidpp at uhidbus > > +file dev/usb/uhidpp.c uhidpp > > diff --git sys/dev/usb/uhidev.c sys/dev/usb/uhidev.c > > index 5f2bc07cc96..68212d50471 100644 > > --- sys/dev/usb/uhidev.c > > +++ sys/dev/usb/uhidev.c > > @@ -255,8 +255,11 @@ uhidev_attach(struct device *parent, struct > > device *self, void *aux) > > /* Look for a driver claiming all report IDs first. */ > > dev = config_found_sm(self, &uha, NULL, uhidevsubmatch); > > if (dev != NULL) { > > - for (repid = 0; repid < nrepid; repid++) > > - sc->sc_subdevs[repid] = (struct uhidev *)dev; > > + for (repid = 0; repid < nrepid; repid++) { > > + /* Could already be assigned by > > uhidev_set_intr(). */ > > + if (sc->sc_subdevs[repid] == NULL) > > + sc->sc_subdevs[repid] = (struct > > uhidev *)dev; > > + } > > return; > > } > > > > @@ -269,7 +272,9 @@ uhidev_attach(struct device *parent, struct > > device *self, void *aux) > > > > uha.reportid = repid; > > dev = config_found_sm(self, &uha, uhidevprint, > > uhidevsubmatch); > > - sc->sc_subdevs[repid] = (struct uhidev *)dev; > > + /* Could already be assigned by uhidev_set_intr(). */ > > + if (sc->sc_subdevs[repid] == NULL) > > + sc->sc_subdevs[repid] = (struct uhidev *)dev; > > } > > } > > > > @@ -950,3 +955,16 @@ uhidev_ioctl(struct uhidev *sc, u_long cmd, > > caddr_t addr, int flag, > > } > > return 0; > > } > > + > > +int > > +uhidev_set_intr(struct uhidev_softc *sc, struct uhidev *dev, int > > repid) > > +{ > > + > > + if ((dev->sc_state & UHIDEV_OPEN) == 0) > > + return ENODEV; > > + if (repid >= sc->sc_nrepid) > > + return EINVAL; > > + > > + sc->sc_subdevs[repid] = dev; > > + return 0; > > +} > > diff --git sys/dev/usb/uhidev.h sys/dev/usb/uhidev.h > > index 16657f1e712..791c8882769 100644 > > --- sys/dev/usb/uhidev.h > > +++ sys/dev/usb/uhidev.h > > @@ -95,3 +95,4 @@ int uhidev_get_report(struct uhidev_softc *, int, > > int, void *, int); > > int uhidev_get_report_async(struct uhidev_softc *, int, int, void > > *, int, > > void *, void (*)(void *, int, void *, int)); > > usbd_status uhidev_write(struct uhidev_softc *, void *, int); > > +int uhidev_set_intr(struct uhidev_softc *, struct uhidev *, int); > > diff --git sys/dev/usb/uhidpp.c sys/dev/usb/uhidpp.c > > new file mode 100644 > > index 00000000000..aee989e2de5 > > --- /dev/null > > +++ sys/dev/usb/uhidpp.c > > @@ -0,0 +1,1055 @@ > > +/* $OpenBSD$ */ > > + > > +/* > > + * Copyright (c) 2021 Anton Lindqvist <an...@openbsd.org> > > + * > > + * Permission to use, copy, modify, and distribute this software > > for any > > + * purpose with or without fee is hereby granted, provided that > > the above > > + * copyright notice and this permission notice appear in all > > copies. > > + * > > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL > > WARRANTIES > > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES > > OF > > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE > > LIABLE FOR > > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY > > DAMAGES > > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER > > IN AN > > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, > > ARISING OUT OF > > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > > + */ > > + > > +#include <sys/param.h> > > +#include <sys/systm.h> > > +#include <sys/kernel.h> > > +#include <sys/device.h> > > +#include <sys/mutex.h> > > +#include <sys/sensors.h> > > + > > +#include <dev/usb/usb.h> > > +#include <dev/usb/usbhid.h> > > +#include <dev/usb/usbdi.h> > > +#include <dev/usb/usbdevs.h> > > +#include <dev/usb/uhidev.h> > > + > > +/* #define UHIDPP_DEBUG */ > > +#ifdef UHIDPP_DEBUG > > + > > +#define DPRINTF(x...) do > > { \ > > + if > > (uhidpp_debug) \ > > + > > printf(x); \ > > +} while (0) > > + > > +#define DREPORT(prefix, repid, buf, len) do > > { \ > > + if > > (uhidpp_debug) \ > > + uhidd_dump_report((prefix), (repid), (buf), > > (len)); \ > > +} while (0) > > + > > +void uhidd_dump_report(const char *, uint8_t, const unsigned char > > *, u_int); > > + > > +int uhidpp_debug = 1; > > + > > +#else > > + > > +#define DPRINTF(x...) > > +#define DREPORT(prefix, repid, buf, len) > > + > > +#endif > > + > > +#define HIDPP_LINK_STATUS(x) ((x) & (1 << 7)) > > + > > +#define HIDPP_REPORT_ID_SHORT 0x10 > > +#define HIDPP_REPORT_ID_LONG 0x11 > > + > > +/* > > + * Length of reports. Note that the effective length is always +1 > > as > > + * uhidev_set_report() prepends the report ID. > > + */ > > +#define HIDPP_REPORT_SHORT_LENGTH (7 - 1) > > +#define HIDPP_REPORT_LONG_LENGTH (20 - 1) > > + > > +/* > > + * Maximum number of allowed parameters for reports. Note, the > > parameters always > > + * starts at offset 3 for both RAP and FAP reports. > > + */ > > +#define HIDPP_REPORT_SHORT_PARAMS_MAX > > (HIDPP_REPORT_SHORT_LENGTH - 3) > > +#define HIDPP_REPORT_LONG_PARAMS_MAX > > (HIDPP_REPORT_LONG_LENGTH - 3) > > + > > +#define HIDPP_DEVICE_ID_RECEIVER 0xff > > + > > +#define HIDPP_FEAT_ROOT_IDX 0x00 > > +#define HIDPP_FEAT_ROOT_PING_FUNC 0x01 > > +#define HIDPP_FEAT_ROOT_PING_DATA 0x5a > > + > > +#define HIDPP_SET_REGISTER 0x80 > > +#define HIDPP_GET_REGISTER 0x81 > > +#define HIDPP_SET_LONG_REGISTER 0x82 > > +#define HIDPP_GET_LONG_REGISTER 0x83 > > + > > +#define HIDPP_REG_ENABLE_REPORTS 0x00 > > +#define HIDPP_REG_PAIRING_INFORMATION 0xb5 > > + > > +#define HIDPP_NOTIF_DEVICE_BATTERY_STATUS (1 << 4) > > +#define HIDPP_NOTIF_RECEIVER_WIRELESS (1 << 0) > > +#define HIDPP_NOTIF_RECEIVER_SOFTWARE_PRESENT (1 << 3) > > + > > +/* HID++ 1.0 error codes. */ > > +#define HIDPP_ERROR 0x8f > > +#define HIDPP_ERROR_SUCCESS 0x00 > > +#define HIDPP_ERROR_INVALID_SUBID 0x01 > > +#define HIDPP_ERROR_INVALID_ADRESS 0x02 > > +#define HIDPP_ERROR_INVALID_VALUE 0x03 > > +#define HIDPP_ERROR_CONNECT_FAIL 0x04 > > +#define HIDPP_ERROR_TOO_MANY_DEVICES 0x05 > > +#define HIDPP_ERROR_ALREADY_EXISTS 0x06 > > +#define HIDPP_ERROR_BUSY 0x07 > > +#define HIDPP_ERROR_UNKNOWN_DEVICE 0x08 > > +#define HIDPP_ERROR_RESOURCE_ERROR 0x09 > > +#define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a > > +#define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b > > +#define HIDPP_ERROR_WRONG_PIN_CODE 0x0c > > + > > +/* > > + * The software ID is added to feature access reports (FAP) and > > used to > > + * distinguish responses from notifications. Note, the software ID > > must be > > + * greater than zero which is reserved for notifications. > > + */ > > +#define HIDPP_SOFTWARE_ID 0x01 > > +#define HIDPP_SOFTWARE_ID_MASK 0x0f > > +#define HIDPP_SOFTWARE_ID_LEN 4 > > + > > +#define HIDPP20_FEAT_ROOT_IDX 0x00 > > +#define HIDPP20_FEAT_ROOT_GET_FEATURE_FUNC 0x00 > > + > > +#define HIDPP20_FEAT_BATTERY_IDX 0x1000 > > +#define HIDPP20_FEAT_BATTERY_LEVEL_FUNC 0x0000 > > +#define HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC 0x0001 > > + > > +/* HID++ 2.0 error codes. */ > > +#define HIDPP20_ERROR 0xff > > +#define HIDPP20_ERROR_NO_ERROR 0x00 > > +#define HIDPP20_ERROR_UNKNOWN 0x01 > > +#define HIDPP20_ERROR_INVALID_ARGUMENT 0x02 > > +#define HIDPP20_ERROR_OUT_OF_RANGE 0x03 > > +#define HIDPP20_ERROR_HARDWARE_ERROR 0x04 > > +#define HIDPP20_ERROR_LOGITECH_INTERNAL 0x05 > > +#define HIDPP20_ERROR_INVALID_FEATURE_INDEX 0x06 > > +#define HIDPP20_ERROR_INVALID_FUNCTION_ID 0x07 > > +#define HIDPP20_ERROR_BUSY 0x08 > > +#define HIDPP20_ERROR_UNSUPPORTED 0x09 > > + > > +/* > > + * Sentinels used for interrupt response synchronization. The > > values must be > > + * disjoint from existing report IDs. > > + */ > > +#define UHIDPP_RESP_NONE 0 > > +#define UHIDPP_RESP_WAIT 1 > > +#define UHIDPP_RESP_ERROR 2 > > + > > +/* Maximum number of devices associated with a single receiver. */ > > +#define UHIDPP_NDEVICES 6 > > + > > +/* Maximum number of pending notifications. */ > > +#define UHIDPP_NNOTIFICATIONS 4 > > + > > +/* Number of sensors per paired device. */ > > +#define UHIDPP_NSENSORS 2 > > + > > +/* Feature access report used by the HID++ 2.0 (and greater) > > protocol. */ > > +struct fap { > > + uint8_t feature_index; > > + uint8_t funcindex_clientid; > > + uint8_t params[HIDPP_REPORT_LONG_PARAMS_MAX]; > > +}; > > + > > +/* > > + * Register access report used by the HID++ 1.0 protocol. > > Receivers always uses > > + * this type of report. > > + */ > > +struct rap { > > + uint8_t sub_id; > > + uint8_t reg_address; > > + uint8_t params[HIDPP_REPORT_LONG_PARAMS_MAX]; > > +}; > > + > > +struct uhidpp_report { > > + uint8_t device_id; > > + union { > > + struct fap fap; > > + struct rap rap; > > + }; > > +} __packed; > > + > > +struct uhidpp_notification { > > + struct uhidpp_report n_rep; > > + unsigned int n_id; > > +}; > > + > > +struct uhidpp_device { > > + uint8_t d_id; > > + uint8_t d_paired; > > + uint8_t d_connected; > > + struct { > > + struct ksensor b_sens[UHIDPP_NSENSORS]; > > + uint8_t b_feature_idx; > > + uint8_t b_level; > > + uint8_t b_next_level; > > + uint8_t b_status; > > + uint8_t b_nlevels; > > + } d_battery; > > +}; > > + > > +/* > > + * Locking: > > + * [m] sc_mtx > > + */ > > +struct uhidpp_softc { > > + struct uhidev sc_hdev; > > + struct usbd_device *sc_udev; > > + > > + struct mutex sc_mtx; > > + > > + struct uhidpp_device sc_devices[UHIDPP_NDEVICES]; > > + /* [m] connected devices */ > > + > > + struct uhidpp_notification > > sc_notifications[UHIDPP_NNOTIFICATIONS]; > > + /* [m] pending notifications > > */ > > + > > + struct usb_task sc_task; /* [m] notification task */ > > + > > + struct ksensordev sc_sensdev; /* [m] */ > > + struct sensor_task *sc_senstsk; /* [m] */ > > + > > + struct uhidpp_report *sc_resp; /* [m] synchronous response > > buffer */ > > + u_int sc_resp_state; /* [m] synchronous response > > state */ > > + > > +}; > > + > > +int uhidpp_match(struct device *, void *, void *); > > +void uhidpp_attach(struct device *, struct device *, void *); > > +int uhidpp_detach(struct device *, int flags); > > +void uhidpp_intr(struct uhidev *addr, void *ibuf, u_int len); > > +void uhidpp_refresh(void *); > > +void uhidpp_task(void *); > > +int uhidpp_sleep(struct uhidpp_softc *, uint64_t); > > + > > +void uhidpp_device_pair(struct uhidpp_softc *, struct > > uhidpp_device *); > > +void uhidpp_device_connect(struct uhidpp_softc *, struct > > uhidpp_device *); > > +void uhidpp_device_refresh(struct uhidpp_softc *, struct > > uhidpp_device *); > > + > > +struct uhidpp_notification *uhidpp_claim_notification(struct > > uhidpp_softc *); > > +int uhidpp_consume_notification(struct uhidpp_softc *, struct > > uhidpp_report *); > > +int uhidpp_is_notification(struct uhidpp_report *); > > + > > +int hidpp_get_protocol_version(struct uhidpp_softc *, uint8_t, > > int *, int *); > > + > > +int hidpp10_get_name(struct uhidpp_softc *, uint8_t, char *, > > size_t); > > +int hidpp10_get_serial(struct uhidpp_softc *, uint8_t, uint8_t *, > > size_t); > > +int hidpp10_get_type(struct uhidpp_softc *, uint8_t, const char > > **); > > +int hidpp10_enable_notifications(struct uhidpp_softc *, uint8_t); > > + > > +int hidpp20_root_get_feature(struct uhidpp_softc *, uint8_t, > > uint16_t, > > + uint8_t *, uint8_t *); > > +int hidpp20_battery_get_level_status(struct uhidpp_softc *, > > uint8_t, uint8_t, > > + uint8_t *, uint8_t *, uint8_t *); > > +int hidpp20_battery_get_capability(struct uhidpp_softc *, uint8_t, > > uint8_t, > > + uint8_t *); > > + > > +int hidpp_send_validate(uint8_t, int); > > +int hidpp_send_rap_report(struct uhidpp_softc *, uint8_t, uint8_t, > > + uint8_t, uint8_t, uint8_t *, int, struct uhidpp_report *); > > +int hidpp_send_fap_report(struct uhidpp_softc *, uint8_t, uint8_t, > > uint8_t, > > + uint8_t, uint8_t *, int, struct uhidpp_report *); > > +int hidpp_send_report(struct uhidpp_softc *, uint8_t, void *, > > + struct uhidpp_report *); > > + > > +struct cfdriver uhidpp_cd = { > > + NULL, "uhidpp", DV_DULL > > +}; > > + > > +const struct cfattach uhidpp_ca = { > > + sizeof(struct uhidpp_softc), > > + uhidpp_match, > > + uhidpp_attach, > > + uhidpp_detach, > > +}; > > + > > +static const struct usb_devno uhidpp_devs[] = { > > + { USB_VENDOR_LOGITECH, USB_PRODUCT_ANY }, > > +}; > > + > > +int > > +uhidpp_match(struct device *parent, void *match, void *aux) > > +{ > > + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg > > *)aux; > > + void *desc; > > + int descsiz, siz; > > + > > + if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID) > > + return UMATCH_NONE; > > + > > + if (usb_lookup(uhidpp_devs, > > + uha->uaa->vendor, uha->uaa->product) == NULL) > > + return UMATCH_NONE; > > + > > + uhidev_get_report_desc(uha->parent, &desc, &descsiz); > > + siz = hid_report_size(desc, descsiz, hid_output, > > HIDPP_REPORT_ID_SHORT); > > + if (siz != HIDPP_REPORT_SHORT_LENGTH) > > + return UMATCH_NONE; > > + siz = hid_report_size(desc, descsiz, hid_output, > > HIDPP_REPORT_ID_LONG); > > + if (siz != HIDPP_REPORT_LONG_LENGTH) > > + return UMATCH_NONE; > > + > > + return UMATCH_VENDOR_PRODUCT; > > +} > > + > > +void > > +uhidpp_attach(struct device *parent, struct device *self, void > > *aux) > > +{ > > + struct uhidpp_softc *sc = (struct uhidpp_softc *)self; > > + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg > > *)aux; > > + struct usb_attach_arg *uaa = uha->uaa; > > + int error, i; > > + int npaired = 0; > > + > > + sc->sc_hdev.sc_intr = uhidpp_intr; > > + sc->sc_hdev.sc_udev = uaa->device; > > + sc->sc_hdev.sc_parent = uha->parent; > > + sc->sc_hdev.sc_report_id = uha->reportid; > > + /* The largest supported report dictates the sizes. */ > > + sc->sc_hdev.sc_isize = HIDPP_REPORT_LONG_LENGTH; > > + sc->sc_hdev.sc_osize = HIDPP_REPORT_LONG_LENGTH; > > + > > + sc->sc_udev = uaa->device; > > + > > + mtx_init(&sc->sc_mtx, IPL_USB); > > + > > + sc->sc_resp = NULL; > > + sc->sc_resp_state = UHIDPP_RESP_NONE; > > + > > + error = uhidev_open(&sc->sc_hdev); > > + if (error) { > > + printf(" error %d\n", error); > > + return; > > + } > > + > > + usb_init_task(&sc->sc_task, uhidpp_task, sc, > > USB_TASK_TYPE_GENERIC); > > + > > + mtx_enter(&sc->sc_mtx); > > + > > + /* > > + * Wire up interrupt handlers before issuing commands to the > > device in > > + * order to receive responses. Necessary as uhidev by default > > performs > > + * the wiring after the attach routine has returned. > > + */ > > + uhidev_set_intr(sc->sc_hdev.sc_parent, &sc->sc_hdev, > > + HIDPP_REPORT_ID_SHORT); > > + uhidev_set_intr(sc->sc_hdev.sc_parent, &sc->sc_hdev, > > + HIDPP_REPORT_ID_LONG); > > + > > + /* Probe paired devices. */ > > + for (i = 0; i < UHIDPP_NDEVICES; i++) { > > + char name[16]; > > + uint8_t serial[4]; > > + struct uhidpp_device *dev = &sc->sc_devices[i]; > > + const char *type; > > + uint8_t device_id = device_id + 1; > > + > > + dev->d_id = device_id; > > + > > + if (hidpp10_get_serial(sc, device_id, serial, > > sizeof(serial)) || > > + hidpp10_get_type(sc, device_id, &type) || > > + hidpp10_get_name(sc, device_id, name, > > sizeof(name))) > > + continue; > > + > > + uhidpp_device_pair(sc, dev); > > + > > + if (npaired > 0) > > + printf(","); > > + printf(" device %d", device_id); > > + printf(" %s", type); > > + printf(" \"%s\"", name); > > + printf(" serial %02x-%02x-%02x-%02x", > > + serial[0], serial[1], serial[2], serial[3]); > > + npaired++; > > + } > > + > > + /* Enable notifications for the receiver. */ > > + error = hidpp10_enable_notifications(sc, > > HIDPP_DEVICE_ID_RECEIVER); > > + if (error) > > + printf(" error %d", error); > > + > > + printf("\n"); > > + > > + strlcpy(sc->sc_sensdev.xname, sc->sc_hdev.sc_dev.dv_xname, > > + sizeof(sc->sc_sensdev.xname)); > > + sensordev_install(&sc->sc_sensdev); > > + sc->sc_senstsk = sensor_task_register(sc, uhidpp_refresh, 6); > > + > > + mtx_leave(&sc->sc_mtx); > > +} > > + > > +int > > +uhidpp_detach(struct device *self, int flags) > > +{ > > + struct uhidpp_softc *sc = (struct uhidpp_softc *)self; > > + int i, j; > > + > > + usb_rem_wait_task(sc->sc_udev, &sc->sc_task); > > + > > + if (sc->sc_senstsk != NULL) > > + sensor_task_unregister(sc->sc_senstsk); > > + > > + KASSERT(sc->sc_resp_state == UHIDPP_RESP_NONE); > > + > > + sensordev_deinstall(&sc->sc_sensdev); > > + > > + for (i = 0; i < UHIDPP_NDEVICES; i++) { > > + struct uhidpp_device *dev = &sc->sc_devices[i]; > > + > > + if (!dev->d_paired) > > + continue; > > + > > + for (j = 0; j < UHIDPP_NSENSORS; j++) > > + sensor_detach(&sc->sc_sensdev, &dev- > > >d_battery.b_sens[j]); > > + } > > + > > + uhidev_close(&sc->sc_hdev); > > + > > + return 0; > > +} > > + > > +void > > +uhidpp_intr(struct uhidev *addr, void *buf, u_int len) > > +{ > > + struct uhidpp_softc *sc = (struct uhidpp_softc *)addr; > > + struct uhidpp_report *rep = buf; > > + int dowake = 0; > > + uint8_t repid; > > + > > + /* > > + * Ugliness ahead as the report ID is stripped of by > > uhidev_intr() but > > + * needed to determine if an error occurred. > > + * Note that an error response is always a short report even > > if the > > + * command that caused the error is a long report. > > + */ > > + repid = ((uint8_t *)buf)[-1]; > > + > > + DREPORT(__func__, repid, buf, len); > > + > > + mtx_enter(&sc->sc_mtx); > > + if (uhidpp_is_notification(rep)) { > > + struct uhidpp_notification *ntf; > > + > > + ntf = uhidpp_claim_notification(sc); > > + if (ntf != NULL) { > > + memcpy(&ntf->n_rep, buf, len); > > + usb_add_task(sc->sc_udev, &sc->sc_task); > > + } else { > > + DPRINTF("%s: too many notifications", > > __func__); > > + } > > + } else { > > + KASSERT(sc->sc_resp_state == UHIDPP_RESP_WAIT); > > + dowake = 1; > > + sc->sc_resp_state = repid; > > + memcpy(sc->sc_resp, buf, len); > > + } > > + mtx_leave(&sc->sc_mtx); > > + if (dowake) > > + wakeup(sc); > > +} > > + > > +void > > +uhidpp_refresh(void *arg) > > +{ > > + struct uhidpp_softc *sc = arg; > > + int i; > > + > > + mtx_enter(&sc->sc_mtx); > > + for (i = 0; i < UHIDPP_NDEVICES; i++) { > > + struct uhidpp_device *dev = &sc->sc_devices[i]; > > + > > + if (dev->d_connected) > > + uhidpp_device_refresh(sc, dev); > > + } > > + mtx_leave(&sc->sc_mtx); > > +} > > + > > +void > > +uhidpp_task(void *arg) > > +{ > > + struct uhidpp_softc *sc = arg; > > + > > + mtx_enter(&sc->sc_mtx); > > + for (;;) { > > + struct uhidpp_report rep; > > + struct uhidpp_device *dev; > > + > > + if (uhidpp_consume_notification(sc, &rep)) > > + break; > > + > > + DPRINTF("%s: device_id=%d, sub_id=%02x\n", > > + __func__, rep.device_id, rep.rap.sub_id); > > + > > + if (rep.device_id == 0 || rep.device_id > > > UHIDPP_NDEVICES) { > > + DPRINTF("%s: invalid device\n", __func__); > > + continue; > > + } > > + dev = &sc->sc_devices[rep.device_id - 1]; > > + > > + switch (rep.rap.sub_id) { > > + case 0x0e: /* leds */ > > + case 0x40: /* disconnect */ > > + break; > > + case 0x41: /* connect */ > > + /* > > + * Do nothing if the link is reported to be > > out of > > + * range. This happens when a device has been > > idle for a > > + * while. > > + */ > > + if (HIDPP_LINK_STATUS(rep.rap.params[0])) > > + uhidpp_device_connect(sc, dev); > > + break; > > + } > > + } > > + mtx_leave(&sc->sc_mtx); > > +} > > + > > +int > > +uhidpp_sleep(struct uhidpp_softc *sc, uint64_t nsecs) > > +{ > > + return msleep_nsec(sc, &sc->sc_mtx, PZERO, "uhidpp", nsecs); > > +} > > + > > +void > > +uhidpp_device_pair(struct uhidpp_softc *sc, struct uhidpp_device > > *dev) > > +{ > > + struct ksensor *sens; > > + > > + MUTEX_ASSERT_LOCKED(&sc->sc_mtx); > > + > > + sens = &dev->d_battery.b_sens[0]; > > + strlcpy(sens->desc, "battery level", sizeof(sens->desc)); > > + sens->type = SENSOR_PERCENT; > > + sens->flags = SENSOR_FUNKNOWN; > > + sensor_attach(&sc->sc_sensdev, sens); > > + > > + sens = &dev->d_battery.b_sens[1]; > > + strlcpy(sens->desc, "battery levels", sizeof(sens->desc)); > > + sens->type = SENSOR_INTEGER; > > + sens->flags = SENSOR_FUNKNOWN; > > + sensor_attach(&sc->sc_sensdev, sens); > > + > > + dev->d_paired = 1; > > +} > > + > > +void > > +uhidpp_device_connect(struct uhidpp_softc *sc, struct > > uhidpp_device *dev) > > +{ > > + int error, major, minor; > > + uint8_t feature_type; > > + > > + MUTEX_ASSERT_LOCKED(&sc->sc_mtx); > > + > > + /* A connected device will continously send connect events. > > */ > > + if (dev->d_connected) > > + return; > > + > > + error = hidpp_get_protocol_version(sc, dev->d_id, &major, > > &minor); > > + if (error) { > > + DPRINTF("%s: protocol version failure: device_id=%d, > > error=%d\n", > > + __func__, dev->d_id, error); > > + return; > > + } > > + > > + DPRINTF("%s: device_id=%d, version=%d.%d\n", > > + __func__, dev->d_id, major, minor); > > + > > + error = hidpp20_root_get_feature(sc, dev->d_id, > > + HIDPP20_FEAT_BATTERY_IDX, > > + &dev->d_battery.b_feature_idx, &feature_type); > > + if (error) { > > + DPRINTF("%s: battery feature index failure: > > device_id=%d, " > > + "error=%d\n", __func__, dev->d_id, error); > > + return; > > + } > > + > > + error = hidpp20_battery_get_capability(sc, dev->d_id, > > + dev->d_battery.b_feature_idx, &dev->d_battery.b_nlevels); > > + if (error) { > > + DPRINTF("%s: battery capability failure: > > device_id=%d, " > > + "error=%d\n", __func__, dev->d_id, error); > > + return; > > + } > > + dev->d_battery.b_sens[1].value = dev->d_battery.b_nlevels; > > + dev->d_battery.b_sens[1].flags &= ~SENSOR_FUNKNOWN; > > + > > + dev->d_connected = 1; > > + uhidpp_device_refresh(sc, dev); > > +} > > + > > +void > > +uhidpp_device_refresh(struct uhidpp_softc *sc, struct > > uhidpp_device *dev) > > +{ > > + int error; > > + > > + MUTEX_ASSERT_LOCKED(&sc->sc_mtx); > > + > > + error = hidpp20_battery_get_level_status(sc, dev->d_id, > > + dev->d_battery.b_feature_idx, > > + &dev->d_battery.b_level, &dev->d_battery.b_next_level, > > + &dev->d_battery.b_status); > > + if (error) { > > + DPRINTF("%s: battery level status failure: > > device_id=%d, " > > + "error=%d\n", __func__, dev->d_id, error); > > + return; > > + } > > + > > + dev->d_battery.b_sens[0].value = dev->d_battery.b_level * > > 1000; > > + dev->d_battery.b_sens[0].flags &= ~SENSOR_FUNKNOWN; > > + if (dev->d_battery.b_nlevels < 10) { > > + /* > > + * According to the HID++ 2.0 specification, less > > than 10 levels > > + * should be mapped to the following 4 levels: > > + * > > + * [0, 10] critical > > + * [11, 30] low > > + * [31, 80] good > > + * [81, 100] full > > + * > > + * Since sensors are limited to 3 valid statuses, > > clamp it even > > + * further. > > + */ > > + if (dev->d_battery.b_level <= 10) > > + dev->d_battery.b_sens[0].status = > > SENSOR_S_CRIT; > > + else if (dev->d_battery.b_level <= 30) > > + dev->d_battery.b_sens[0].status = > > SENSOR_S_WARN; > > + else > > + dev->d_battery.b_sens[0].status = > > SENSOR_S_OK; > > + } else { > > + /* > > + * XXX the device supports battery mileage. The > > current level > > + * must be checked against resp.fap.params[3] given > > by > > + * hidpp20_battery_get_capability(). > > + */ > > + dev->d_battery.b_sens[0].status = SENSOR_S_UNKNOWN; > > + } > > +} > > + > > +/* > > + * Returns the next available notification slot, if available. > > + */ > > +struct uhidpp_notification * > > +uhidpp_claim_notification(struct uhidpp_softc *sc) > > +{ > > + struct uhidpp_notification *ntf = NULL; > > + int nclaimed = 0; > > + int i; > > + > > + MUTEX_ASSERT_LOCKED(&sc->sc_mtx); > > + > > + for (i = 0; i < UHIDPP_NNOTIFICATIONS; i++) { > > + struct uhidpp_notification *tmp = &sc- > > >sc_notifications[i]; > > + > > + if (tmp->n_id > 0) > > + nclaimed++; > > + else if (ntf == NULL) > > + ntf = tmp; > > + } > > + > > + if (ntf == NULL) > > + return NULL; > > + ntf->n_id = nclaimed + 1; > > + return ntf; > > +} > > + > > +/* > > + * Consume the first unhandled notification, if present. > > + */ > > +int > > +uhidpp_consume_notification(struct uhidpp_softc *sc, struct > > uhidpp_report *rep) > > +{ > > + struct uhidpp_notification *ntf = NULL; > > + int i; > > + > > + MUTEX_ASSERT_LOCKED(&sc->sc_mtx); > > + > > + for (i = 0; i < UHIDPP_NNOTIFICATIONS; i++) { > > + struct uhidpp_notification *tmp = &sc- > > >sc_notifications[i]; > > + > > + if (tmp->n_id > 0 && (ntf == NULL || tmp->n_id < ntf- > > >n_id)) > > + ntf = tmp; > > + } > > + if (ntf == NULL) > > + return 1; > > + > > + memcpy(rep, &ntf->n_rep, sizeof(*rep)); > > + ntf->n_id = 0; > > + return 0; > > +} > > + > > + > > +int > > +uhidpp_is_notification(struct uhidpp_report *rep) > > +{ > > + uint8_t swid; > > + > > + /* HID++ 1.0 response. */ > > + if (rep->rap.sub_id > 0x7f) > > + return 0; > > + > > + /* HID++ 2.0 response if the software ID is ours. */ > > + swid = rep->fap.funcindex_clientid & HIDPP_SOFTWARE_ID_MASK; > > + if (swid == HIDPP_SOFTWARE_ID) > > + return 0; > > + > > + return 1; > > +} > > + > > +int > > +hidpp_get_protocol_version(struct uhidpp_softc *sc, uint8_t > > device_id, > > + int *major, int *minor) > > +{ > > + struct uhidpp_report resp; > > + uint8_t params[3] = { 0, 0, HIDPP_FEAT_ROOT_PING_DATA }; > > + int error; > > + > > + error = hidpp_send_fap_report(sc, > > + HIDPP_REPORT_ID_SHORT, > > + device_id, > > + HIDPP_FEAT_ROOT_IDX, > > + HIDPP_FEAT_ROOT_PING_FUNC, > > + params, sizeof(params), &resp); > > + if (error == HIDPP_ERROR_INVALID_SUBID) { > > + *major = 1; > > + *minor = 0; > > + return 0; > > + } > > + if (error) > > + return error; > > + if (resp.rap.params[2] != HIDPP_FEAT_ROOT_PING_DATA) > > + return -EPROTO; > > + > > + *major = resp.fap.params[0]; > > + *minor = resp.fap.params[1]; > > + return 0; > > +} > > + > > +int > > +hidpp10_get_name(struct uhidpp_softc *sc, uint8_t device_id, > > + char *buf, size_t bufsiz) > > +{ > > + struct uhidpp_report resp; > > + int error; > > + uint8_t params[1] = { 0x40 + (device_id - 1) }; > > + uint8_t len; > > + > > + error = hidpp_send_rap_report(sc, > > + HIDPP_REPORT_ID_SHORT, > > + HIDPP_DEVICE_ID_RECEIVER, > > + HIDPP_GET_LONG_REGISTER, > > + HIDPP_REG_PAIRING_INFORMATION, > > + params, sizeof(params), &resp); > > + if (error) > > + return error; > > + > > + len = resp.rap.params[1]; > > + if (len + 2 > sizeof(resp.rap.params)) > > + return -ENAMETOOLONG; > > + if (len > bufsiz - 1) > > + len = bufsiz - 1; > > + memcpy(buf, &resp.rap.params[2], len); > > + buf[len] = '\0'; > > + return 0; > > +} > > + > > +int > > +hidpp10_get_serial(struct uhidpp_softc *sc, uint8_t device_id, > > + uint8_t *buf, size_t bufsiz) > > +{ > > + struct uhidpp_report resp; > > + int error; > > + uint8_t params[1] = { 0x30 + (device_id - 1) }; > > + uint8_t len; > > + > > + error = hidpp_send_rap_report(sc, > > + HIDPP_REPORT_ID_SHORT, > > + HIDPP_DEVICE_ID_RECEIVER, > > + HIDPP_GET_LONG_REGISTER, > > + HIDPP_REG_PAIRING_INFORMATION, > > + params, sizeof(params), &resp); > > + if (error) > > + return error; > > + > > + len = 4; > > + if (bufsiz < len) > > + len = bufsiz; > > + memcpy(buf, &resp.rap.params[1], len); > > + return 0; > > +} > > + > > +int > > +hidpp10_get_type(struct uhidpp_softc *sc, uint8_t device_id, const > > char **type) > > +{ > > + struct uhidpp_report resp; > > + int error; > > + uint8_t params[1] = { 0x20 + (device_id - 1) }; > > + > > + error = hidpp_send_rap_report(sc, > > + HIDPP_REPORT_ID_SHORT, > > + HIDPP_DEVICE_ID_RECEIVER, > > + HIDPP_GET_LONG_REGISTER, > > + HIDPP_REG_PAIRING_INFORMATION, > > + params, sizeof(params), &resp); > > + if (error) > > + return error; > > + > > + switch (resp.rap.params[7]) { > > + case 0x00: > > + *type = "unknown"; > > + return 0; > > + case 0x01: > > + *type = "keyboard"; > > + return 0; > > + case 0x02: > > + *type = "mouse"; > > + return 0; > > + case 0x03: > > + *type = "numpad"; > > + return 0; > > + case 0x04: > > + *type = "presenter"; > > + return 0; > > + case 0x08: > > + *type = "trackball"; > > + return 0; > > + case 0x09: > > + *type = "touchpad"; > > + return 0; > > + } > > + return -ENOENT; > > +} > > + > > +int > > +hidpp10_enable_notifications(struct uhidpp_softc *sc, uint8_t > > device_id) > > +{ > > + struct uhidpp_report resp; > > + uint8_t params[3]; > > + > > + /* Device reporting flags. */ > > + params[0] = HIDPP_NOTIF_DEVICE_BATTERY_STATUS; > > + /* Receiver reporting flags. */ > > + params[1] = HIDPP_NOTIF_RECEIVER_WIRELESS | > > + HIDPP_NOTIF_RECEIVER_SOFTWARE_PRESENT; > > + /* Device reporting flags (continued). */ > > + params[2] = 0; > > + > > + return hidpp_send_rap_report(sc, > > + HIDPP_REPORT_ID_SHORT, > > + device_id, > > + HIDPP_SET_REGISTER, > > + HIDPP_REG_ENABLE_REPORTS, > > + params, sizeof(params), &resp); > > +} > > + > > +int > > +hidpp20_root_get_feature(struct uhidpp_softc *sc, uint8_t > > device_id, > > + uint16_t feature, uint8_t *feature_index, uint8_t > > *feature_type) > > +{ > > + struct uhidpp_report resp; > > + uint8_t params[2] = { feature >> 8, feature & 0xff }; > > + int error; > > + > > + error = hidpp_send_fap_report(sc, > > + HIDPP_REPORT_ID_LONG, > > + device_id, > > + HIDPP20_FEAT_ROOT_IDX, > > + HIDPP20_FEAT_ROOT_GET_FEATURE_FUNC, > > + params, sizeof(params), &resp); > > + if (error) > > + return error; > > + > > + if (resp.fap.params[0] == 0) > > + return -ENOENT; > > + > > + *feature_index = resp.fap.params[0]; > > + *feature_type = resp.fap.params[1]; > > + return 0; > > +} > > + > > +int > > +hidpp20_battery_get_level_status(struct uhidpp_softc *sc, uint8_t > > device_id, > > + uint8_t feature_index, uint8_t *level, uint8_t *next_level, > > uint8_t *status) > > +{ > > + struct uhidpp_report resp; > > + int error; > > + > > + error = hidpp_send_fap_report(sc, > > + HIDPP_REPORT_ID_LONG, > > + device_id, > > + feature_index, > > + HIDPP20_FEAT_BATTERY_LEVEL_FUNC, > > + NULL, 0, &resp); > > + if (error) > > + return error; > > + > > + *level = resp.fap.params[0]; > > + *next_level = resp.fap.params[1]; > > + *status = resp.fap.params[2]; > > + return 0; > > +} > > + > > +int > > +hidpp20_battery_get_capability(struct uhidpp_softc *sc, uint8_t > > device_id, > > + uint8_t feature_index, uint8_t *nlevels) > > +{ > > + struct uhidpp_report resp; > > + int error; > > + > > + error = hidpp_send_fap_report(sc, > > + HIDPP_REPORT_ID_LONG, > > + device_id, > > + feature_index, > > + HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC, > > + NULL, 0, &resp); > > + if (error) > > + return error; > > + *nlevels = resp.fap.params[0]; > > + return 0; > > +} > > + > > +int > > +hidpp_send_validate(uint8_t report_id, int nparams) > > +{ > > + if (report_id == HIDPP_REPORT_ID_SHORT) { > > + if (nparams > HIDPP_REPORT_SHORT_PARAMS_MAX) > > + return -EMSGSIZE; > > + } else if (report_id == HIDPP_REPORT_ID_LONG) { > > + if (nparams > HIDPP_REPORT_LONG_PARAMS_MAX) > > + return -EMSGSIZE; > > + } else { > > + return -EINVAL; > > + } > > + return 0; > > +} > > + > > +int > > +hidpp_send_fap_report(struct uhidpp_softc *sc, uint8_t report_id, > > + uint8_t device_id, uint8_t feature_index, uint8_t > > funcindex_clientid, > > + uint8_t *params, int nparams, struct uhidpp_report *resp) > > +{ > > + struct uhidpp_report req; > > + int error; > > + > > + error = hidpp_send_validate(report_id, nparams); > > + if (error) > > + return error; > > + > > + memset(&req, 0, sizeof(req)); > > + req.device_id = device_id; > > + req.fap.feature_index = feature_index; > > + req.fap.funcindex_clientid = > > + (funcindex_clientid << HIDPP_SOFTWARE_ID_LEN) | > > HIDPP_SOFTWARE_ID; > > + memcpy(req.fap.params, params, nparams); > > + return hidpp_send_report(sc, report_id, &req, resp); > > +} > > + > > +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) > > +{ > > + struct uhidpp_report req; > > + int error; > > + > > + error = hidpp_send_validate(report_id, nparams); > > + if (error) > > + return error; > > + > > + memset(&req, 0, sizeof(req)); > > + req.device_id = device_id; > > + req.rap.sub_id = sub_id; > > + req.rap.reg_address = reg_address; > > + memcpy(req.rap.params, params, nparams); > > + return hidpp_send_report(sc, report_id, &req, resp); > > +} > > + > > +int > > +hidpp_send_report(struct uhidpp_softc *sc, uint8_t report_id, void > > *data, > > + struct uhidpp_report *resp) > > +{ > > + int error, len, n; > > + > > + MUTEX_ASSERT_LOCKED(&sc->sc_mtx); > > + > > + if (report_id == HIDPP_REPORT_ID_SHORT) > > + len = HIDPP_REPORT_SHORT_LENGTH; > > + else if (report_id == HIDPP_REPORT_ID_LONG) > > + len = HIDPP_REPORT_LONG_LENGTH; > > + else > > + return -EINVAL; > > + > > + DREPORT(__func__, report_id, data, len); > > + > > + /* Wait until any ongoing command has completed. */ > > + while (sc->sc_resp_state != UHIDPP_RESP_NONE) > > + uhidpp_sleep(sc, INFSLP); > > + sc->sc_resp = resp; > > + sc->sc_resp_state = UHIDPP_RESP_WAIT; > > + /* > > + * The mutex must be temporarily released while calling > > + * uhidev_set_report() as it might end up sleeping. > > + */ > > + mtx_leave(&sc->sc_mtx); > > + > > + n = uhidev_set_report(sc->sc_hdev.sc_parent, > > UHID_OUTPUT_REPORT, > > + report_id, data, len); > > + > > + mtx_enter(&sc->sc_mtx); > > + if (len != n) { > > + error = -EBUSY; > > + goto out; > > + } > > + /* > > + * The interrupt could already have been received while the > > mutex was > > + * released. Otherwise, wait for it. > > + */ > > + if (sc->sc_resp_state == UHIDPP_RESP_WAIT) { > > + /* Timeout taken from the hid-logitech-hidpp Linux > > driver. */ > > + error = uhidpp_sleep(sc, SEC_TO_NSEC(5)); > > + if (error) { > > + error = -error; > > + goto out; > > + } > > + } > > + > > + if (sc->sc_resp_state == UHIDPP_RESP_ERROR) > > + error = -EIO; > > + else if (sc->sc_resp_state == HIDPP_REPORT_ID_SHORT && > > + resp->rap.sub_id == HIDPP_ERROR) > > + error = resp->rap.params[1]; > > + else if (sc->sc_resp_state == HIDPP_REPORT_ID_LONG && > > + resp->fap.feature_index == HIDPP20_ERROR) > > + error = resp->fap.params[1]; > > + > > +out: > > + sc->sc_resp = NULL; > > + sc->sc_resp_state = UHIDPP_RESP_NONE; > > + wakeup(sc); > > + return error; > > +} > > + > > +#ifdef UHIDPP_DEBUG > > + > > +void > > +uhidd_dump_report(const char *prefix, uint8_t repid, const > > unsigned char *buf, > > + u_int buflen) > > +{ > > + u_int i; > > + > > + printf("%s: %02x ", prefix, repid); > > + for (i = 0; i < buflen; i++) { > > + printf("%02x%s", buf[i], > > + i == 2 ? " [" : (i + 1 < buflen ? " " : "")); > > + } > > + printf("]\n"); > > +} > > + > > +#endif >