1 %{
2 /*
3 * parser.y
4 */
5
6 /*-
7 * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
32 * $FreeBSD$
33 */
34
35 #include <sys/queue.h>
36 #define L2CAP_SOCKET_CHECKED
37 #include <bluetooth.h>
38 #include <dev/usb/usb.h>
39 #include <dev/usb/usbhid.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <usbhid.h>
47
48 #ifndef BTHIDCONTROL
49 #include <stdarg.h>
50 #include <syslog.h>
51 #define SYSLOG syslog
52 #define LOGCRIT LOG_CRIT
53 #define LOGERR LOG_ERR
54 #define LOGWARNING LOG_WARNING
55 #define EOL
56 #else
57 #define SYSLOG fprintf
58 #define LOGCRIT stderr
59 #define LOGERR stderr
60 #define LOGWARNING stderr
61 #define EOL "\n"
62 #endif /* ndef BTHIDCONTROL */
63
64 #include "bthid_config.h"
65
66 int yylex (void);
67 void yyerror (char const *);
68 static int32_t check_hid_device(hid_device_p hid_device);
69 static void free_hid_device (hid_device_p hid_device);
70
71 extern FILE *yyin;
72 extern int yylineno;
73 char const *config_file = BTHIDD_CONFFILE;
74 char const *hids_file = BTHIDD_HIDSFILE;
75
76 static char buffer[1024];
77 static int32_t hid_descriptor_size;
78 static hid_device_t *hid_device = NULL;
79 static LIST_HEAD(, hid_device) hid_devices;
80
81 %}
82
83 %union {
84 bdaddr_t bdaddr;
85 int32_t num;
86 }
87
88 %token <bdaddr> T_BDADDRSTRING
89 %token <num> T_HEXBYTE
90 %token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
91 %token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
92 %token T_TRUE T_FALSE T_ERROR
93
94 %%
95
96 config: line
97 | config line
98 ;
99
100 line: T_DEVICE
101 {
102 hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
103 if (hid_device == NULL) {
104 SYSLOG(LOGCRIT, "Could not allocate new " \
105 "config entry" EOL);
106 YYABORT;
107 }
108
109 hid_device->new_device = 1;
110 }
111 '{' options '}'
112 {
113 if (check_hid_device(hid_device))
114 LIST_INSERT_HEAD(&hid_devices,hid_device,next);
115 else
116 free_hid_device(hid_device);
117
118 hid_device = NULL;
119 }
120 ;
121
122 options: option ';'
123 | options option ';'
124 ;
125
126 option: bdaddr
127 | control_psm
128 | interrupt_psm
129 | reconnect_initiate
130 | battery_power
131 | normally_connectable
132 | hid_descriptor
133 | parser_error
134 ;
135
136 bdaddr: T_BDADDR T_BDADDRSTRING
137 {
138 memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
139 }
140 ;
141
142 control_psm: T_CONTROL_PSM T_HEXBYTE
143 {
144 hid_device->control_psm = $2;
145 }
146 ;
147
148 interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE
149 {
150 hid_device->interrupt_psm = $2;
151 }
152 ;
153
154 reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
155 {
156 hid_device->reconnect_initiate = 1;
157 }
158 | T_RECONNECT_INITIATE T_FALSE
159 {
160 hid_device->reconnect_initiate = 0;
161 }
162 ;
163
164 battery_power: T_BATTERY_POWER T_TRUE
165 {
166 hid_device->battery_power = 1;
167 }
168 | T_BATTERY_POWER T_FALSE
169 {
170 hid_device->battery_power = 0;
171 }
172 ;
173
174 normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
175 {
176 hid_device->normally_connectable = 1;
177 }
178 | T_NORMALLY_CONNECTABLE T_FALSE
179 {
180 hid_device->normally_connectable = 0;
181 }
182 ;
183
184 hid_descriptor: T_HID_DESCRIPTOR
185 {
186 hid_descriptor_size = 0;
187 }
188 '{' hid_descriptor_bytes '}'
189 {
190 if (hid_device->desc != NULL)
191 hid_dispose_report_desc(hid_device->desc);
192
193 hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
194 if (hid_device->desc == NULL) {
195 SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
196 YYABORT;
197 }
198 }
199 ;
200
201 hid_descriptor_bytes: hid_descriptor_byte
202 | hid_descriptor_bytes hid_descriptor_byte
203 ;
204
205 hid_descriptor_byte: T_HEXBYTE
206 {
207 if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
208 SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
209 YYABORT;
210 }
211
212 buffer[hid_descriptor_size ++] = $1;
213 }
214 ;
215
216 parser_error: T_ERROR
217 {
218 YYABORT;
219 }
220
221 %%
222
223 /* Display parser error message */
224 void
225 yyerror(char const *message)
226 {
227 SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
228 }
229
230 /* Re-read config file */
231 int32_t
read_config_file(void)232 read_config_file(void)
233 {
234 int32_t e;
235
236 if (config_file == NULL) {
237 SYSLOG(LOGERR, "Unknown config file name!" EOL);
238 return (-1);
239 }
240
241 if ((yyin = fopen(config_file, "r")) == NULL) {
242 SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
243 config_file, strerror(errno), errno);
244 return (-1);
245 }
246
247 clean_config();
248 if (yyparse() < 0) {
249 SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
250 config_file);
251 e = -1;
252 } else
253 e = 0;
254
255 fclose(yyin);
256 yyin = NULL;
257
258 return (e);
259 }
260
261 /* Clean config */
262 void
clean_config(void)263 clean_config(void)
264 {
265 while (!LIST_EMPTY(&hid_devices)) {
266 hid_device_p d = LIST_FIRST(&hid_devices);
267
268 LIST_REMOVE(d, next);
269 free_hid_device(d);
270 }
271 }
272
273 /* Lookup config entry */
274 hid_device_p
get_hid_device(bdaddr_p bdaddr)275 get_hid_device(bdaddr_p bdaddr)
276 {
277 hid_device_p d;
278
279 LIST_FOREACH(d, &hid_devices, next)
280 if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
281 break;
282
283 return (d);
284 }
285
286 /* Get next config entry */
287 hid_device_p
get_next_hid_device(hid_device_p d)288 get_next_hid_device(hid_device_p d)
289 {
290 return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
291 }
292
293 /* Print config entry */
294 void
print_hid_device(hid_device_p d,FILE * f)295 print_hid_device(hid_device_p d, FILE *f)
296 {
297 /* XXX FIXME hack! */
298 struct report_desc {
299 unsigned int size;
300 unsigned char data[1];
301 };
302 /* XXX FIXME hack! */
303
304 struct report_desc *desc = (struct report_desc *) d->desc;
305 uint32_t i;
306
307 fprintf(f,
308 "device {\n" \
309 " bdaddr %s;\n" \
310 " control_psm 0x%x;\n" \
311 " interrupt_psm 0x%x;\n" \
312 " reconnect_initiate %s;\n" \
313 " battery_power %s;\n" \
314 " normally_connectable %s;\n" \
315 " hid_descriptor {",
316 bt_ntoa(&d->bdaddr, NULL),
317 d->control_psm, d->interrupt_psm,
318 d->reconnect_initiate? "true" : "false",
319 d->battery_power? "true" : "false",
320 d->normally_connectable? "true" : "false");
321
322 for (i = 0; i < desc->size; i ++) {
323 if ((i % 8) == 0)
324 fprintf(f, "\n ");
325
326 fprintf(f, "0x%2.2x ", desc->data[i]);
327 }
328
329 fprintf(f,
330 "\n" \
331 " };\n" \
332 "}\n");
333 }
334
335 /* Check config entry */
336 static int32_t
check_hid_device(hid_device_p d)337 check_hid_device(hid_device_p d)
338 {
339 hid_data_t hd;
340 hid_item_t hi;
341 int32_t page;
342
343 if (get_hid_device(&d->bdaddr) != NULL) {
344 SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
345 bt_ntoa(&d->bdaddr, NULL));
346 return (0);
347 }
348
349 if (d->control_psm == 0) {
350 SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
351 return (0);
352 }
353
354 if (d->interrupt_psm == 0) {
355 SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
356 return (0);
357 }
358
359 if (d->desc == NULL) {
360 SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
361 return (0);
362 }
363
364 /* XXX somehow need to make sure descriptor is valid */
365 for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
366 switch (hi.kind) {
367 case hid_collection:
368 case hid_endcollection:
369 case hid_output:
370 case hid_feature:
371 break;
372
373 case hid_input:
374 /* Check if the device may send keystrokes */
375 page = HID_PAGE(hi.usage);
376 if (page == HUP_KEYBOARD)
377 d->keyboard = 1;
378 break;
379 }
380 }
381 hid_end_parse(hd);
382
383 return (1);
384 }
385
386 /* Free config entry */
387 static void
free_hid_device(hid_device_p d)388 free_hid_device(hid_device_p d)
389 {
390 if (d->desc != NULL)
391 hid_dispose_report_desc(d->desc);
392
393 memset(d, 0, sizeof(*d));
394 free(d);
395 }
396
397 /* Re-read hids file */
398 int32_t
read_hids_file(void)399 read_hids_file(void)
400 {
401 FILE *f;
402 hid_device_t *d;
403 char *line;
404 bdaddr_t bdaddr;
405 int32_t lineno;
406
407 if (hids_file == NULL) {
408 SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
409 return (-1);
410 }
411
412 if ((f = fopen(hids_file, "r")) == NULL) {
413 if (errno == ENOENT)
414 return (0);
415
416 SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
417 hids_file, strerror(errno), errno);
418 return (-1);
419 }
420
421 for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
422 if ((line = strtok(buffer, "\r\n\t ")) == NULL)
423 continue; /* ignore empty lines */
424
425 if (!bt_aton(line, &bdaddr)) {
426 SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
427 "%s:%d" EOL, hids_file, lineno);
428 continue;
429 }
430
431 if ((d = get_hid_device(&bdaddr)) != NULL)
432 d->new_device = 0;
433 }
434
435 fclose(f);
436
437 return (0);
438 }
439
440 /* Write hids file */
441 int32_t
write_hids_file(void)442 write_hids_file(void)
443 {
444 char path[PATH_MAX];
445 FILE *f;
446 hid_device_t *d;
447
448 if (hids_file == NULL) {
449 SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
450 return (-1);
451 }
452
453 snprintf(path, sizeof(path), "%s.new", hids_file);
454
455 if ((f = fopen(path, "w")) == NULL) {
456 SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
457 path, strerror(errno), errno);
458 return (-1);
459 }
460
461 LIST_FOREACH(d, &hid_devices, next)
462 if (!d->new_device)
463 fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
464
465 fclose(f);
466
467 if (rename(path, hids_file) < 0) {
468 SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
469 "%s (%d)" EOL, path, hids_file, strerror(errno), errno);
470 unlink(path);
471 return (-1);
472 }
473
474 return (0);
475 }
476
477