/* * Logitech Unifying receiver emulation (HID++ protocol). * * Copyright (c) 2014 Peter Wu * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef HID_LOGITECH_DJ_H #define HID_LOGITECH_DJ_H /* HID requests ((bmRequestType << 8) | bRequest) */ #define HID_GET_REPORT 0xa101 #define HID_GET_IDLE 0xa102 #define HID_GET_PROTOCOL 0xa103 /* 0x04-0x08 Reserved */ #define HID_SET_REPORT 0x2109 #define HID_SET_IDLE 0x210a #define HID_SET_PROTOCOL 0x210b /* interface numbers (also used for array indices) */ enum { IFACE_KBD, /* keyboard */ IFACE_MSE, /* mouse; multimedia, power, media center buttons */ IFACE_HIDPP, /* HID++, DJ */ }; #define MAX_DEVICES 6 /* Report IDs */ enum { HIDPP_SHORT = 0x10, /* 7 bytes */ HIDPP_LONG = 0x11, /* 20 bytes */ DJ_SHORT = 0x20, /* 15 bytes */ DJ_LONG = 0x21, /* 32 bytes */ }; /* report formats */ typedef struct { uint8_t report_id; uint8_t device_index; uint8_t sub_id; union { uint8_t params[4]; struct { uint8_t address; uint8_t value[3]; }; /* for register queries */ }; } HidppMsgShort; typedef struct { uint8_t report_id; uint8_t device_index; uint8_t sub_id; union { uint8_t params[17]; struct { uint8_t address; uint8_t value[16]; }; /* for register queries */ }; } HidppMsgLong; typedef struct { uint8_t report_id; const uint8_t device_index; const uint8_t feat_index; const uint8_t func; /* function/ASE ID | sw ID */ union { uint8_t params[16]; uint8_t params_s[3]; }; } Hidpp20Msg; typedef struct { uint8_t report_id; uint8_t device_index; uint8_t report_type; uint8_t payload[12]; } DjMsgShort; typedef struct { uint8_t report_id; uint8_t device_index; uint8_t report_type; uint8_t payload[29]; } DjMsgLong; /* generic HID++ or DJ report */ typedef struct { union { struct { uint8_t report_id; uint8_t device_index; }; HidppMsgShort hidpp_s; HidppMsgLong hidpp_l; DjMsgShort dj_s; DjMsgLong dj_l; }; } HidppMsg; /* HID++ 1.0 error codes */ #define HIDPP_ERR_SUCCESS 0x00 #define HIDPP_ERR_INVALID_SUBID 0x01 #define HIDPP_ERR_INVALID_ADDRESS 0x02 #define HIDPP_ERR_INVALID_VALUE 0x03 #define HIDPP_ERR_CONNECT_FAIL 0x04 #define HIDPP_ERR_TOO_MANY_DEVICES 0x05 #define HIDPP_ERR_ALREADY_EXISTS 0x06 #define HIDPP_ERR_BUSY 0x07 #define HIDPP_ERR_UNKNOWN_DEVICE 0x08 #define HIDPP_ERR_RESOURCE_ERROR 0x09 #define HIDPP_ERR_REQUEST_UNAVAILABLE 0x0A #define HIDPP_ERR_INVALID_PARAM_VALUE 0x0B #define HIDPP_ERR_WRONG_PIN_CODE 0x0C /* HID++ 2.0 error codes */ #define HIDPP20_ERR_CODE_NOERROR 0x00 #define HIDPP20_ERR_CODE_UNKNOWN 0x01 #define HIDPP20_ERR_CODE_INVALIDARGUMENT 0x02 #define HIDPP20_ERR_CODE_OUTOFRANGE 0x03 #define HIDPP20_ERR_CODE_HWERROR 0x04 #define HIDPP20_ERR_CODE_LOGITECH_INTERNAL 0x05 #define HIDPP20_ERR_CODE_INVALID_FEATURE_INDEX 0x06 #define HIDPP20_ERR_CODE_INVALID_FUNCTION_ID 0x07 #define HIDPP20_ERR_CODE_BUSY 0x08 #define HIDPP20_ERR_CODE_UNSUPPORTED 0x09 /* device and receiver info */ struct firmware_version { uint8_t fw_major; uint8_t fw_minor; uint16_t fw_build; /* boot loader */ uint8_t bl_major; uint8_t bl_minor; }; typedef struct { struct { uint32_t serial; struct firmware_version version; } info; /* static information */ #define REPORTING_FLAG_DEV_BATTERY_STATUS (1 << 4) #define REPORTING_FLAG_DEV_MASK REPORTING_FLAG_DEV_BATTERY_STATUS int reporting_flags; uint8_t activity_counter[MAX_DEVICES]; /* TODO: pairing lock open or closed (+ timeout) */ /* TODO: connected devices */ /* TODO: device firmware upgrade things? */ } LHidReceiver; /* defined in hid-logitech-hidpp20.c */ typedef struct HidppFeature HidppFeature; typedef struct { struct { enum { DEVTYPE_KEYBOARD = 1, DEVTYPE_MOUSE, DEVTYPE_NUMPAD, DEVTYPE_PRESENTER, /* 0x05..0x07 Reserved for future */ DEVTYPE_TRACKBALL = 8, DEVTYPE_TOUCHPAD, /* 0x0A..0x0F Reserved */ } device_type; enum { PROTO_UNIFYING = 4, } protocol_type; struct firmware_version version; uint16_t protocol_version; /* HID++ protocol version */ uint16_t wireless_pid; uint32_t serial; char name[15]; /* short name for receiver */ const char *device_name; /* long device name for HID++ 2.0 */ uint8_t usability_info; /* bits 0..3 power switch location */ uint32_t report_types; /* supported report types (e.g. mouse) */ const HidppFeature *features; /* Note: feature_index is index - 1 */ unsigned features_count; /* features count not including root */ /* TODO: special mouse and key button mappings */ } info; /* static information */ HIDState *hid; enum { LTUNIFY_MODE_HID = 1, LTUNIFY_MODE_DJ = 2 } mode; bool powered_on; /* "link established, in range" */ bool link_encrypted; uint8_t report_interval; #define REPORTING_FLAG_RCV_WIRELESS_NOTIFS 1 #define REPORTING_FLAG_RCV_SOFTWARE_PRESENT (1 << 3) #define REPORTING_FLAG_RCV_MASK (REPORTING_FLAG_RCV_WIRELESS_NOTIFS | \ REPORTING_FLAG_RCV_SOFTWARE_PRESENT) int reporting_flags; /* TODO: status (device seen or not, encrypted link) */ struct { uint8_t nlevels; uint8_t level; /* in range (1, nlevels) */ #define BAT_FLAG_DISABLE_OSD (1 << 0) #define BAT_FLAG_ENABLE_MILEAGE (1 << 1) #define BAT_FLAG_RECHARGEABLE (1 << 2) uint8_t flags; uint8_t critical_perc; #define BAT_STS_DISCHARGING 0 #define BAT_STS_RECHARGING 1 #define BAT_STS_CHARGE_FINAL 2 #define BAT_STS_CHARGE_COMPLETE 3 #define BAT_STS_RECHARGING_SLOW 4 #define BAT_STS_INVALID_BATTERY 5 #define BAT_STS_THERMAL_ERROR 6 #define BAT_STS_CHARGING_ERROR 7 uint8_t status; } battery; uint8_t activity_counter; } LHidDevice; /* helper macros for handling the report and error queue */ #define LQUEUE_SIZE(q) ARRAY_SIZE((q).reports) #define LQUEUE_WRAP(q, val) ((val) & (LQUEUE_SIZE((q)) - 1u)) #define LQUEUE_INCR(q, var) ((var) = LQUEUE_WRAP((q), (var) + 1)) typedef struct USBLtunifyState { USBDevice dev; USBEndpoint *intr[3]; /* interfaces (keyboard, mouse, DJ) */ char *usbdump_filename; UsbDumpState *usb_dump_state; HIDState hid[2]; /* HID devices (keyboard, mouse) */ /* queue for HID++ requests and responses */ struct { HidppMsg reports[16]; unsigned head; unsigned n; } input_queue; struct { HidppMsg reports[16]; unsigned head; unsigned n; } output_queue; /* receiver error queue (to be send): drop if full */ struct { HidppMsgShort reports[2]; unsigned head; unsigned n; } error_queue; LHidReceiver receiver; LHidDevice devices[MAX_DEVICES]; /* paired devices */ } USBLtunifyState; /* handle control packets for interface 3 (HID++ / DJ) */ void usb_ltunify_handle_control_hidpp(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data); /* handle control packets for interface 3 (HID++ / DJ) */ void usb_ltunify_handle_datain_hidpp(USBDevice *dev, USBPacket *p); void hidpp_reset(USBLtunifyState *s); void hidpp_init(USBLtunifyState *s); /* hid-logitech-hidpp20.c */ void hidpp20_init_features(LHidDevice *hd); /* return codes: negative: send error; 0: send nothing; positive: send output */ int hidpp20_feature_call(LHidDevice *hd, Hidpp20Msg *func); #endif