hidpp: refactor to introduce structure in protocol

Before this patch, there was no structure at all in the messages that
were passed around. Some issues:

 - (debug) Every message of length 7 was considered a request (and
   length 20 were seen as responses). This is not the case for HID++
   1.0.
 - The length of the message payload (ignoring the header) is fixed to 7
   or 20, this was not considered in the previous code.
 - The hidpp_device_cmd function contained special-case code for a HID++
   1.0 response on a HID++ 2.0 ping request.

After this patch, the protocol message structure should be more
explicit. To be done:

 - Test for HID++ 1.0 ping response (removed/broken by this patch).
 - Validate responses, retry read if needed.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: validate messages, retry if invalid

This prevents matching the wrong response packet, for example when
a mouse is moved while a packet is read. As a result, the reads are
more reliable and log spam is reduced.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: clear message queue before initiating new requests (v2)

This prevents the use of responses from other HID++ applications (such
as Solaar and ltunify).

 - v2: do not hang in a loop when read() fails, e.g. when it returns EIO
   because the device was unplugged.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: recognise HID++ 1.0 ping response again

Removed while refactoring hidpp_device_cmd, now that the message
validation is in place, introduce the 1.0 check again.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: do not print warnings if device is offline

This reduces spam in stderr (which is logged to the systemd journal).

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: support battery register 0x07

The HID++ 1.0 Illuminated Keyboard K800 does not support the d7
register, instead is uses 07. Its observed behaviour is documented in
the ltunify repository[1].

 [1]: https://git.lekensteyn.nl/ltunify/tree/registers.txt

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: test only once for d7 register

The d7 battery register does not magically re-appear, therefore skip
this register once it is detected that the register is unusable.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: assume HID++ 1.0 for unreachable devices

When the USB receiver is just plugged in, devices may not be active
("unreachable") and therefore return RESOURCE_ERROR on requests. This
causes upower to fail on picking up new devices. As a workaround, assume
that all discovered devices will eventually become reachable (even if
they don't, for example if the device was paired to another computer
while the computer carrying the receiver was powered off).

Actual removal of unpaired devices is done by the Logitech HID driver,
that should remove sysfs entries which can be detected by upower.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: pick up devices that just got paired

When sending a ping request right after a device got paired, the ping
message gets lost (there is no response). Work around that by delaying
the initial packet exchange. 10 milliseconds seems to work, but let's
choose 30 ms to be fully safe.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: improve HID++ version detection, fix uninit var

Do not assume HID++ 1.0 when device is unreachable. This allows
up_device_unifying_refresh() to be optimized to stop sending a ping
message at every refresh for HID++ 1.0 devices.

priv->version will now always contain 0 when the real HID++ version of
a device is not (yet) known, comments are updated to reflect this.

Also fix an uninitialised msg variable that might confuse the error
handler in hidpp_device_refresh.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: fix properties for unreachable devices (v2)

This includes "is-present" and "state" (which will be marked "unknown").
"percentage" is not touched since it is still an indication of the
battery level, changing it to zero is not helpful.

Previously, properties were never updated because the refresh would
fail when the battery refresh request failed.

 - v2: fix invalid return value when an invalid parameter is passed to
   hidpp_device_is_reachable.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: remove unnecessary HID++ 2.0 code

The device name and type can be queried from the receiver which does
not mind if a paired device is using HID++ 2.0 or 1.0. Therefore remove
the hidpp20-specific code which also removes indirection of an
uninitialised "map" variable.

The following code was buggy:

    msg.feature_idx = map->idx;
    ...
    map = ...;
    if (map != NULL) {
        ...
    }
    ...
    priv->model = ...;

It should have become:

    map = ...;
    if (map != NULL) {
        ...
        priv->model = ...;
    }

... but since the HID++ 2.0 code is not necessary, just remove it.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: retrieve serial number for devices (v2)

Before this, history files were saved as history-rate-M525.dat. Since
multiple devices of the same model might be connected, this name is not
unique enough.

The serial number gives a more reliable path for the history files and
allows clients to distinguish their devices (an object path like
/org/freedesktop/UPower/devices/mouse_0003o046DoC52Bx0018 does not stay
the same).

Note: if a serial number cannot be read, instead of reverting this
patch, consider splitting the serial refresh from the version, kind and
model refresh.

 - v2: Fix invalid return value when an invalid parameter is passed to
   hidpp_device_get_serial.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: fix fd leak

Upon unplugging the USB receiver, the file descriptor of /dev/hidrawX is
not closed.

With commit "HID: hidraw: correctly deallocate memory on device
disconnect"[1] (queued for 3.12), this missing close() causes a
/dev/hidrawX devuce to persist until UPower is restarted.

On earlier kernels, never remove your USB receiver or you will
experience random errors resulting from data structures corruption[2]
(which occurs because UPower tries to poll() or read() an hidraw device
while the kernel has already released the memory for it). Not UPowers
fault, but UPower makes this bug more likely since it runs as daemon.

 [1]: http://lkml.org/lkml/2013/7/22/248
 [2]: http://lkml.org/lkml/2013/8/6/457

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: fix memleak while coldplugging

The device returned by g_udev_device_get_parent must be freed by the
caller.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: more useful error messages

If read, wait or g_poll failed, it would return -1. This information is
not really useful, so print the error string instead where applicable.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: retry when read or g_poll is interrupted

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: split request read/write functions

In preparation for reading notifications; the device index is now
also validated before using a message, this avoid matching the wrong
report.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: move timeout checking before reading

When no report matches the report type or device, the previous code
did not terminate the loop early enough because the check was missed.
Now the timeout is always checked before attempting to poll.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>
hidpp: fix K750 battery and luminosity properties

Since commit b1f12feb1fd4535255f04c91bef90ae11ce57311 ("Factor out the
Logitech Unifying support to support other devices"), the battery status
would always be reported as 1% for the K750 keyboard. Besides that, the
luminosity level was removed. This patch fixes the battery level and
re-adds the luminosity level.

To avoid negativity issues when reading light level into a double type,
make the HidppMessage params unsigned.

Signed-off-by: Peter Wu <lekensteyn@gmail.com>