diff options
Diffstat (limited to 'src/hid_hidapi.c')
-rw-r--r-- | src/hid_hidapi.c | 226 |
1 files changed, 212 insertions, 14 deletions
diff --git a/src/hid_hidapi.c b/src/hid_hidapi.c index 915621f..898fd9e 100644 --- a/src/hid_hidapi.c +++ b/src/hid_hidapi.c | |||
@@ -4,14 +4,26 @@ | |||
4 | * license that can be found in the LICENSE file. | 4 | * license that can be found in the LICENSE file. |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <hidapi/hidapi.h> | 7 | #ifdef __linux__ |
8 | #include <sys/ioctl.h> | ||
9 | #include <linux/hidraw.h> | ||
10 | #include <linux/input.h> | ||
11 | #include <fcntl.h> | ||
12 | #endif | ||
8 | 13 | ||
14 | #include <hidapi.h> | ||
9 | #include <stdlib.h> | 15 | #include <stdlib.h> |
10 | #include <string.h> | 16 | #include <string.h> |
11 | #include <wchar.h> | 17 | #include <wchar.h> |
12 | 18 | ||
13 | #include "fido.h" | 19 | #include "fido.h" |
14 | 20 | ||
21 | struct hid_hidapi { | ||
22 | void *handle; | ||
23 | size_t report_in_len; | ||
24 | size_t report_out_len; | ||
25 | }; | ||
26 | |||
15 | static size_t | 27 | static size_t |
16 | fido_wcslen(const wchar_t *wcs) | 28 | fido_wcslen(const wchar_t *wcs) |
17 | { | 29 | { |
@@ -27,7 +39,7 @@ wcs_to_cs(const wchar_t *wcs) | |||
27 | char *cs; | 39 | char *cs; |
28 | size_t i; | 40 | size_t i; |
29 | 41 | ||
30 | if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL) | 42 | if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL) |
31 | return NULL; | 43 | return NULL; |
32 | 44 | ||
33 | for (i = 0; i < fido_wcslen(wcs); i++) { | 45 | for (i = 0; i < fido_wcslen(wcs); i++) { |
@@ -68,11 +80,12 @@ copy_info(fido_dev_info_t *di, const struct hid_device_info *d) | |||
68 | free(di->path); | 80 | free(di->path); |
69 | free(di->manufacturer); | 81 | free(di->manufacturer); |
70 | free(di->product); | 82 | free(di->product); |
83 | explicit_bzero(di, sizeof(*di)); | ||
71 | return -1; | 84 | return -1; |
72 | } | 85 | } |
73 | 86 | ||
74 | di->product_id = d->product_id; | 87 | di->product_id = (int16_t)d->product_id; |
75 | di->vendor_id = d->vendor_id; | 88 | di->vendor_id = (int16_t)d->vendor_id; |
76 | di->io = (fido_dev_io_t) { | 89 | di->io = (fido_dev_io_t) { |
77 | &fido_hid_open, | 90 | &fido_hid_open, |
78 | &fido_hid_close, | 91 | &fido_hid_close, |
@@ -83,28 +96,199 @@ copy_info(fido_dev_info_t *di, const struct hid_device_info *d) | |||
83 | return 0; | 96 | return 0; |
84 | } | 97 | } |
85 | 98 | ||
99 | #ifdef __linux__ | ||
100 | static int | ||
101 | get_key_len(uint8_t tag, uint8_t *key, size_t *key_len) | ||
102 | { | ||
103 | *key = tag & 0xfc; | ||
104 | if ((*key & 0xf0) == 0xf0) { | ||
105 | fido_log_debug("%s: *key=0x%02x", __func__, *key); | ||
106 | return -1; | ||
107 | } | ||
108 | |||
109 | *key_len = tag & 0x3; | ||
110 | if (*key_len == 3) { | ||
111 | *key_len = 4; | ||
112 | } | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int | ||
118 | get_key_val(const void *body, size_t key_len, uint32_t *val) | ||
119 | { | ||
120 | const uint8_t *ptr = body; | ||
121 | |||
122 | switch (key_len) { | ||
123 | case 0: | ||
124 | *val = 0; | ||
125 | break; | ||
126 | case 1: | ||
127 | *val = ptr[0]; | ||
128 | break; | ||
129 | case 2: | ||
130 | *val = (uint32_t)((ptr[1] << 8) | ptr[0]); | ||
131 | break; | ||
132 | default: | ||
133 | fido_log_debug("%s: key_len=%zu", __func__, key_len); | ||
134 | return -1; | ||
135 | } | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int | ||
141 | get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page, | ||
142 | uint32_t *usage) | ||
143 | { | ||
144 | const uint8_t *ptr = hrd->value; | ||
145 | size_t len = hrd->size; | ||
146 | |||
147 | while (len > 0) { | ||
148 | const uint8_t tag = ptr[0]; | ||
149 | |||
150 | ptr++; | ||
151 | len--; | ||
152 | |||
153 | uint8_t key; | ||
154 | size_t key_len; | ||
155 | uint32_t key_val; | ||
156 | |||
157 | if (get_key_len(tag, &key, &key_len) < 0 || key_len > len || | ||
158 | get_key_val(ptr, key_len, &key_val) < 0) { | ||
159 | return -1; | ||
160 | } | ||
161 | |||
162 | if (key == 0x4) { | ||
163 | *usage_page = key_val; | ||
164 | } else if (key == 0x8) { | ||
165 | *usage = key_val; | ||
166 | } | ||
167 | |||
168 | ptr += key_len; | ||
169 | len -= key_len; | ||
170 | } | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static int | ||
176 | get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) | ||
177 | { | ||
178 | int fd; | ||
179 | int s = -1; | ||
180 | int ok = -1; | ||
181 | |||
182 | if ((fd = open(path, O_RDONLY)) < 0) { | ||
183 | fido_log_debug("%s: open", __func__); | ||
184 | return -1; | ||
185 | } | ||
186 | |||
187 | if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 || | ||
188 | (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { | ||
189 | fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__); | ||
190 | goto fail; | ||
191 | } | ||
192 | |||
193 | hrd->size = (unsigned)s; | ||
194 | |||
195 | if (ioctl(fd, HIDIOCGRDESC, hrd) < 0) { | ||
196 | fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__); | ||
197 | goto fail; | ||
198 | } | ||
199 | |||
200 | ok = 0; | ||
201 | fail: | ||
202 | if (fd != -1) | ||
203 | close(fd); | ||
204 | |||
205 | return ok; | ||
206 | } | ||
207 | |||
208 | static bool | ||
209 | is_fido(const struct hid_device_info *hdi) | ||
210 | { | ||
211 | uint32_t usage = 0; | ||
212 | uint32_t usage_page = 0; | ||
213 | struct hidraw_report_descriptor hrd; | ||
214 | |||
215 | memset(&hrd, 0, sizeof(hrd)); | ||
216 | |||
217 | if (get_report_descriptor(hdi->path, &hrd) < 0 || | ||
218 | get_usage_info(&hrd, &usage_page, &usage) < 0) { | ||
219 | return false; | ||
220 | } | ||
221 | |||
222 | return usage_page == 0xf1d0; | ||
223 | } | ||
224 | #elif defined(_WIN32) || defined(__APPLE__) | ||
225 | static bool | ||
226 | is_fido(const struct hid_device_info *hdi) | ||
227 | { | ||
228 | return hdi->usage_page == 0xf1d0; | ||
229 | } | ||
230 | #else | ||
231 | static bool | ||
232 | is_fido(const struct hid_device_info *hdi) | ||
233 | { | ||
234 | (void)hdi; | ||
235 | fido_log_debug("%s: assuming FIDO HID", __func__); | ||
236 | return true; | ||
237 | } | ||
238 | #endif | ||
239 | |||
86 | void * | 240 | void * |
87 | fido_hid_open(const char *path) | 241 | fido_hid_open(const char *path) |
88 | { | 242 | { |
89 | return hid_open_path(path); | 243 | struct hid_hidapi *ctx; |
244 | |||
245 | if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { | ||
246 | return (NULL); | ||
247 | } | ||
248 | |||
249 | if ((ctx->handle = hid_open_path(path)) == NULL) { | ||
250 | free(ctx); | ||
251 | return (NULL); | ||
252 | } | ||
253 | |||
254 | ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN; | ||
255 | |||
256 | return ctx; | ||
90 | } | 257 | } |
91 | 258 | ||
92 | void | 259 | void |
93 | fido_hid_close(void *hid_dev_handle) | 260 | fido_hid_close(void *handle) |
94 | { | 261 | { |
95 | hid_close(hid_dev_handle); | 262 | struct hid_hidapi *ctx = handle; |
263 | |||
264 | hid_close(ctx->handle); | ||
265 | free(ctx); | ||
96 | } | 266 | } |
97 | 267 | ||
98 | int | 268 | int |
99 | fido_hid_read(void *hid_dev_handle, unsigned char *buf, size_t len, int ms) | 269 | fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) |
100 | { | 270 | { |
101 | return hid_read_timeout(hid_dev_handle, buf, len, ms); | 271 | struct hid_hidapi *ctx = handle; |
272 | |||
273 | if (len != ctx->report_in_len) { | ||
274 | fido_log_debug("%s: len %zu", __func__, len); | ||
275 | return -1; | ||
276 | } | ||
277 | |||
278 | return hid_read_timeout(ctx->handle, buf, len, ms); | ||
102 | } | 279 | } |
103 | 280 | ||
104 | int | 281 | int |
105 | fido_hid_write(void *hid_dev_handle, const unsigned char *buf, size_t len) | 282 | fido_hid_write(void *handle, const unsigned char *buf, size_t len) |
106 | { | 283 | { |
107 | return hid_write(hid_dev_handle, buf, len); | 284 | struct hid_hidapi *ctx = handle; |
285 | |||
286 | if (len != ctx->report_out_len + 1) { | ||
287 | fido_log_debug("%s: len %zu", __func__, len); | ||
288 | return -1; | ||
289 | } | ||
290 | |||
291 | return hid_write(ctx->handle, buf, len); | ||
108 | } | 292 | } |
109 | 293 | ||
110 | int | 294 | int |
@@ -122,10 +306,8 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) | |||
122 | return FIDO_OK; /* nothing to do */ | 306 | return FIDO_OK; /* nothing to do */ |
123 | 307 | ||
124 | for (struct hid_device_info *d = hdi; d != NULL; d = d->next) { | 308 | for (struct hid_device_info *d = hdi; d != NULL; d = d->next) { |
125 | #if defined(_WIN32) || defined(__APPLE__) | 309 | if (is_fido(d) == false) |
126 | if (d->usage_page != 0xf1d0) | ||
127 | continue; | 310 | continue; |
128 | #endif | ||
129 | if (copy_info(&devlist[*olen], d) == 0) { | 311 | if (copy_info(&devlist[*olen], d) == 0) { |
130 | if (++(*olen) == ilen) | 312 | if (++(*olen) == ilen) |
131 | break; | 313 | break; |
@@ -136,3 +318,19 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) | |||
136 | 318 | ||
137 | return FIDO_OK; | 319 | return FIDO_OK; |
138 | } | 320 | } |
321 | |||
322 | size_t | ||
323 | fido_hid_report_in_len(void *handle) | ||
324 | { | ||
325 | struct hid_hidapi *ctx = handle; | ||
326 | |||
327 | return (ctx->report_in_len); | ||
328 | } | ||
329 | |||
330 | size_t | ||
331 | fido_hid_report_out_len(void *handle) | ||
332 | { | ||
333 | struct hid_hidapi *ctx = handle; | ||
334 | |||
335 | return (ctx->report_out_len); | ||
336 | } | ||