hidpp

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2022 License: MIT Imports: 3 Imported by: 0

README

hidpp

Go Reference

Library to interact with devices that are using Logitech HID++ protocol.

NOTE: Not guaranteed to work correctly for HID++ 1.0 devices right now. Compatibility with older devices is kept in mind during development, but due to the major changes from 2.0 onward and currently the library not being tested against any 1.0 device no promises on it working as expected. (Contributions are welcome though!)

What is HID++

HID++ is a proprietary protocol written on top of HID specification that was created, and is being used by, Logitech. The protocol is being used to interact with Logitech devices, including, but not limited to, controlling things like RGB LED lights and mouse DPI settings.

Protocol

The protocol splits functionality into arbitrary amount of "features". Each feature exposes some number of functions.

Features are identified by uint16 identifiers, but when using them you must know the index the device uses to expose that feature. Only feature that is always guaranteed to be available is in index 0, which provides Root (0x0000) feature. In order to know if a device supports specific HID++ feature one can use functions in Root to check if device exposes a feature and which index it is located in. To simplify this process Device has method IndexOf(<identifier>) that does exactly this.

Usage

package main

import (
    "log"
    "git.sr.ht/~errnoh/hidpp"
)

// Use from any platform as long as your device implements the provided interface
func PrintDeviceName(device hidpp.Interface) {
    var (
        dev *hidpp.Device

		err error
		b   []byte
	)

    // 0xFF here means that we're talking to the device directly, instead of middleware 
    // that controls multiple HID++ devices.
    if dev, err = hidpp.New(device, 0xFF); err != nil {
        log.Fatal(err)
    }
    defer dev.Close()

    // Check if device supports feature with ID 0x0005 (deviceTypeName)
    feature, exists := dev.IndexOf(0x0005)
    if !exists {
        log.Fatal("deviceTypeName feature not supported by this device")
    }

    // Third argument (`0`) here is the function index, in this case the
    // function `GetCount()`, which return how long the device name is.
    if b, err = dev.Call(hidpp.ReportIDLong, feature.Index, 0); err != nil {
        log.Fatal(err)
    }

    // Data starts from index 4
    // Since the function has one return value of single byte,
    // this is how to read the function output.
    length := b[4]

    // Since Long report has maximum length of 20 bytes and only the last 16 bytes
    // are used for actual data, we need to loop until we've read enough bytes to
    // contain the full device name.
    var name []byte
    for i := byte(0); i < length; i += byte(len(b[4:])) {
        // `1` here is the function `getDeviceName(charIndex)``
        if b, err = dev.Call(hidpp.ReportIDLong, feature.Index, 1, i); err != nil {
            log.Fatal(err)
        }
        name = append(name, b[4:]...)
    }
    log.Println(string(name))
}

Issues & Contributing

(You can participate easily by sending an email to address listed on either page)

NOTES

HID++ protocol is copyright of Logitech (c)

Documentation

Index

Constants

View Source
const (
	HIDpp2_Success                = Hidpp2Err(0) // no error
	HIDpp2_ErrUnknown             = Hidpp2Err(1) // unknown
	HIDpp2_ErrInvalidArgument     = Hidpp2Err(2) // invalid argument
	HIDpp2_ErrOutOfRange          = Hidpp2Err(3) // out of range
	HIDpp2_ErrHWError             = Hidpp2Err(4) // hardware error
	HIDpp2_ErrLogitechInternal    = Hidpp2Err(5) // logitech internal
	HIDpp2_ErrInvalidFeatureIndex = Hidpp2Err(6) // invalid feature index
	HIDpp2_ErrInvalidFunctionID   = Hidpp2Err(7) // invalid function id
	HIDpp2_ErrBusy                = Hidpp2Err(8) // busy
	HIDpp2_ErrUnsupported         = Hidpp2Err(9) // unsupported
)
View Source
const (
	HIDpp1_Success               = Hidpp1Err(0x0) // No error / undefined
	HIDpp1_ErrInvalidSubID       = Hidpp1Err(0x1) // Invalid SubID / command
	HIDpp1_ErrInvalidAddress     = Hidpp1Err(0x2) // Invalid address
	HIDpp1_ErrInvalidValue       = Hidpp1Err(0x3) // Invalid value
	HIDpp1_ErrConnectFail        = Hidpp1Err(0x4) // Connection request failed (Receiver)
	HIDpp1_ErrTooManyDevices     = Hidpp1Err(0x5) // Too many devices connected (Receiver)
	HIDpp1_ErrAlreadyExists      = Hidpp1Err(0x6) // Already exists (Receiver)
	HIDpp1_ErrBusy               = Hidpp1Err(0x7) // Busy (Receiver)
	HIDpp1_ErrUnknownDevice      = Hidpp1Err(0x8) // Unknown device (Receiver)
	HIDpp1_ErrResourceError      = Hidpp1Err(0x9) // Resource error (Receiver)
	HIDpp1_ErrRequestUnavailable = Hidpp1Err(0xA) // "Request not valid in current context" error
	HIDpp1_ErrInvalidParamValue  = Hidpp1Err(0xB) // Request parameter has unsupported value
	HIDpp1_ErrWrongPINCode       = Hidpp1Err(0xC) // the PIN code entered on the device was wrong
)
View Source
const (
	ReportIDShort    = 0x10
	ReportIDLong     = 0x11
	ReportIDVeryLong = 0x12
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Device

type Device struct {
	// contains filtered or unexported fields
}

func New

func New(d Interface, deviceIndex byte) (dev *Device, err error)

New returns Device that provides HID++ protocol

Underlying device in `d` must be open before calling, and the returned *Device should be closed after use.

func (*Device) Call

func (d *Device) Call(reportId, featureIndex, functionIndex byte, data ...byte) ([]byte, error)

Call does a single HID++ function call into the using the input report defined by `reportId` and returns the response. The call is made to function in `functionIndex` of feature in `featureIndex`.

HID++ protocol exposes arbitrary number of "features" that each provide set of functions. Feature indexes can change between devices, one can use IndexOf() to check if feature is available on a decice and which index it's located in.

func (*Device) Close

func (d *Device) Close()

func (*Device) Features

func (d *Device) Features() (m map[uint16]Feature, err error)

func (*Device) IndexOf

func (d *Device) IndexOf(featureId uint16) (feature Feature, found bool)

func (*Device) NotificationChan

func (d *Device) NotificationChan(index byte, c chan []byte)

NotificationChan allows setting a channel where all notifications coming from feature at specific index will be directed to.

If an index that already has an notification channel, the old channel will be closed unless it is also used as a notification channel for some other index. (i.e. it's safe to use notification channels that handle multiple indexes)

Passing nil disables notification forwarding for that index.

func (*Device) Open

func (d *Device) Open() error

func (*Device) Version

func (d *Device) Version() (version byte, err error)

Version checks which HID++ version the device uses

It also does additional checks to try to verify the device is hid++ compatible and returns error if that's not the case.

type Feature

type Feature struct {
	Id             uint16
	Index, Version byte
	Type           TypeBitfield
}

func (Feature) String

func (f Feature) String() string

type Hidpp1Err

type Hidpp1Err byte

func (Hidpp1Err) Error

func (e Hidpp1Err) Error() string

type Hidpp2Err

type Hidpp2Err byte

func (Hidpp2Err) Error

func (e Hidpp2Err) Error() string

type HidppErr

type HidppErr interface {
	Hidpp1Err | Hidpp2Err
}

type Interface

type Interface interface {
	Write(p []byte) (n int, err error)

	Events() <-chan []byte

	Open() error
	Opened() bool
	Close()
}

type TypeBitfield

type TypeBitfield byte

TpeBitfield contents for each bit:

0: -
1: -
2: -
3: compliance_deactivatable: A compliance feature that can be permanently deactivated. It is usually also hidden and engineering.
4: manufacturing_deactivatable: A manufacturing feature that can be permanently deactivated. It is usually also hidden and engineering.
5: engineering: A hidden feature that has been disabled for user software. Used for internal testing and manufacturing.
6: hidden: A SW hidden feature is a feature that should not be known/managed/used by end user configuration SW. The host should ignore this type of features.
7: obsolete: An obsolete feature is a feature that has been replaced by a newer one, but is advertised in order for older SWs to still be able to support the feature (in case the old SW does not knowyet the newer one).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL