summaryrefslogtreecommitdiff
path: root/src/hid_hidapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hid_hidapi.c')
-rw-r--r--src/hid_hidapi.c226
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
21struct hid_hidapi {
22 void *handle;
23 size_t report_in_len;
24 size_t report_out_len;
25};
26
15static size_t 27static size_t
16fido_wcslen(const wchar_t *wcs) 28fido_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__
100static int
101get_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
117static int
118get_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
140static int
141get_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
175static int
176get_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;
201fail:
202 if (fd != -1)
203 close(fd);
204
205 return ok;
206}
207
208static bool
209is_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__)
225static bool
226is_fido(const struct hid_device_info *hdi)
227{
228 return hdi->usage_page == 0xf1d0;
229}
230#else
231static bool
232is_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
86void * 240void *
87fido_hid_open(const char *path) 241fido_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
92void 259void
93fido_hid_close(void *hid_dev_handle) 260fido_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
98int 268int
99fido_hid_read(void *hid_dev_handle, unsigned char *buf, size_t len, int ms) 269fido_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
104int 281int
105fido_hid_write(void *hid_dev_handle, const unsigned char *buf, size_t len) 282fido_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
110int 294int
@@ -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
322size_t
323fido_hid_report_in_len(void *handle)
324{
325 struct hid_hidapi *ctx = handle;
326
327 return (ctx->report_in_len);
328}
329
330size_t
331fido_hid_report_out_len(void *handle)
332{
333 struct hid_hidapi *ctx = handle;
334
335 return (ctx->report_out_len);
336}