summaryrefslogtreecommitdiff
path: root/hidpp20.c
blob: ac42a774a9a71292c7ab7913fb598f8ab4ee9c4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#define SHORT_MESSAGE           0x10
#define LONG_MESSAGE            0x11

struct hidpp2_message {
	u8 report_id;
	u8 device_index;
	u8 feature_index;
#define HIDPP_SET_FUNC(msg, func) ((msg)->func_swId |= ((func) << 4))
	u8 func_swId;
	u8 params[16]; // 3 or 16 params
} __attribute__((__packed__));

struct feature {
	uint16_t featureId;
#define FEAT_TYPE_MASK          0xe0
#define FEAT_TYPE_OBSOLETE      0x80
#define FEAT_TYPE_SWHIDDEN      0x40
#define FEAT_TYPE_RSVD_INTERNAL 0x20
	u8 featureType;
};

#define SOFTWARE_ID 0x04 /* getRandom() -- guaranteed to be random. */

#define FEATURE_INDEX_IROOT 0x00

#define FID_IFEATURESET 0x0001
static
const char *
get_feature_name(uint16_t featureId) {
	switch (featureId) {
	case 0x0000: return "Root";
	case 0x0001: return "FeatureSet";
	case 0x0002: return "FeatureInfo";
	case 0x0003: return "DeviceFwVersion";
	case 0x0005: return "DeviceName";
	case 0x0006: return "DeviceGroups";
	case 0x00C0: return "Dfucontrol";
	case 0x1000: return "BatteryStatus";
	case 0x1981: return "Backlight";
	case 0x1B00: return "ReprogControls";
	case 0x1B01: return "ReprogControlsV2";
	case 0x1B03: return "ReprogControlsV3";
	case 0x1D4B: return "WirelessDeviceStatus";
	case 0x2001: return "LeftRightSwap";
	case 0x2100: return "VerticalScrolling";
	case 0x2120: return "HiResScrolling";
	case 0x2200: return "MousePointer";
	case 0x40A0: return "FnInversion";
	case 0x40A2: return "NewFnInversion";
	case 0x4100: return "Encryption";
	case 0x4301: return "SolarDashboard";
	case 0x4520: return "KeyboardLayout";
	case 0x6010: return "TouchpadFwItems";
	case 0x6011: return "TouchpadSwItems";
	case 0x6012: return "TouchpadWin8FwItems";
	case 0x6100: return "TouchpadRawXy";
	case 0x6110: return "TouchmouseRawPoints";
	case 0x6120: return "Touchmouse6120";
	default: return "unknown";
	}
}

/**
 * Initialize common values of a HID++ 2.0 message: report_id, device_index and
 * software ID. Remaining fields: feature_index, func, params.
 */
#define INIT_HIDPP_SHORT(msg, dev_index) \
	memset((msg), 0, sizeof *(msg)); \
	(msg)->report_id = SHORT_MESSAGE; \
	(msg)->device_index = dev_index; \
	(msg)->func_swId = SOFTWARE_ID

static
bool
do_hidpp2_request(int fd, struct hidpp2_message *msg) {
	struct hidpp_message *hid10_message = (struct hidpp_message *) msg;

        if (!do_write(fd, hid10_message)) {
                return false;
        }

	// use do_read_skippy in case there are interleaving notifications
	// sub id is feature index in HID++ 2.0
	if (!do_read_skippy(fd, hid10_message, 0x11, hid10_message->sub_id)) {
		puts("WTF");
		return false;
	}

	return true;
}

// Returns feature index for featureId
static
u8
get_feature(int fd, u8 device_index, uint16_t featureId) {
	struct hidpp2_message msg;
	INIT_HIDPP_SHORT(&msg, device_index);
	msg.feature_index = FEATURE_INDEX_IROOT;
	HIDPP_SET_FUNC(&msg, 0); // GetFeature(featureId)
	msg.params[0] = featureId >> 8;
	msg.params[1] = featureId & 0xFF;
	if (!do_hidpp2_request(fd, &msg)) {
		return 0;
	}
	return msg.params[0];
}

// Returns number of features or 0 on error.
static
u8
get_feature_count(int fd, u8 device_index, u8 ifeatIndex) {
	struct hidpp2_message msg;
	INIT_HIDPP_SHORT(&msg, device_index);
	// XXX: is this variable? Can't it be hard-coded to 0x01?
	msg.feature_index = ifeatIndex;
	HIDPP_SET_FUNC(&msg, 0); // GetCount()
	if (!do_hidpp2_request(fd, &msg)) {
		fprintf(stderr, "Failed to request features count\n");
		return 0;
	}
	return msg.params[0];
}

// Get featureId and type for a given featureIndex.
static
bool
get_featureId(int fd, u8 device_index, u8 ifeatIndex, u8 featureIndex, struct feature *feat) {
	struct hidpp2_message msg;
	INIT_HIDPP_SHORT(&msg, device_index);
	// XXX: is this variable? Can't it be hard-coded to 0x01?
	msg.feature_index = ifeatIndex;
	HIDPP_SET_FUNC(&msg, 1); // GetFeatureId(featureIndex)
	msg.params[0] = featureIndex;
	if (!do_hidpp2_request(fd, &msg)) {
		return false;
	}
	feat->featureId = (msg.params[0] << 8) | msg.params[1];
	feat->featureType = msg.params[2];
	return true;
}

void
hidpp20_print_features(int fd, u8 device_index) {
	u8 i, count, ifeatIndex;

	ifeatIndex = get_feature(fd, device_index, FID_IFEATURESET);
	if (!ifeatIndex) {
		fprintf(stderr, "Failed to get feature information\n");
		return;
	}
	count = get_feature_count(fd, device_index, ifeatIndex);

	printf("Total number of HID++ 2.0 features: %i\n", count);
	for (i = 0; i <= count; i++) {
		struct feature feat;
		if (get_featureId(fd, device_index, ifeatIndex, i, &feat)) {
			printf(" %2i: [%04X] %c%c%c %s\n", i, feat.featureId,
				feat.featureType & FEAT_TYPE_OBSOLETE ? 'O' : ' ',
				feat.featureType & FEAT_TYPE_SWHIDDEN ? 'H' : ' ',
				feat.featureType & FEAT_TYPE_RSVD_INTERNAL ? 'I' : ' ',
				get_feature_name(feat.featureId));
			if (feat.featureType & ~FEAT_TYPE_MASK) {
				printf("Warning: unrecognized feature flags: %#04x\n",
					feat.featureType & ~FEAT_TYPE_MASK);
			}
		} else {
			fprintf(stderr, "Failed to get feature, is device connected?\n");
		}
	}
	puts("(O = obsolete feature; H = SW hidden feature;\n"
		" I = reserved for internal use)");
}