]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/bluetooth/bthidd/parser.y
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / bluetooth / bthidd / parser.y
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 #include <bluetooth.h>
37 #include <dev/usb/usb.h>
38 #include <dev/usb/usbhid.h>
39 #include <errno.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <usbhid.h>
45
46 #ifndef BTHIDCONTROL
47 #include <stdarg.h>
48 #include <syslog.h>
49 #define SYSLOG          syslog
50 #define LOGCRIT         LOG_CRIT
51 #define LOGERR          LOG_ERR
52 #define LOGWARNING      LOG_WARNING
53 #define EOL
54 #else
55 #define SYSLOG          fprintf
56 #define LOGCRIT         stderr
57 #define LOGERR          stderr
58 #define LOGWARNING      stderr
59 #define EOL     "\n"
60 #endif /* ndef BTHIDCONTROL */
61
62 #include "bthid_config.h"
63
64         int     yyparse         (void);
65         int     yylex           (void);
66         void    yyerror         (char const *);
67 static  int32_t check_hid_device(hid_device_p hid_device);
68 static  void    free_hid_device (hid_device_p hid_device);
69
70 extern  FILE                    *yyin;
71 extern  int                      yylineno;
72         char const              *config_file = BTHIDD_CONFFILE;
73         char const              *hids_file   = BTHIDD_HIDSFILE;
74
75 static  char                     buffer[1024];
76 static  int32_t                  hid_descriptor_size;
77 static  hid_device_t            *hid_device = NULL;
78 static  LIST_HEAD(, hid_device)  hid_devices;
79
80 %}
81
82 %union {
83         bdaddr_t        bdaddr;
84         int32_t         num;
85 }
86
87 %token <bdaddr> T_BDADDRSTRING
88 %token <num>    T_HEXBYTE
89 %token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
90 %token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
91 %token T_TRUE T_FALSE T_ERROR
92
93 %%
94
95 config:         line
96                 | config line
97                 ;
98
99 line:           T_DEVICE
100                         {
101                         hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
102                         if (hid_device == NULL) {
103                                 SYSLOG(LOGCRIT, "Could not allocate new " \
104                                                 "config entry" EOL);
105                                 YYABORT;
106                         }
107
108                         hid_device->new_device = 1;
109                         }
110                 '{' options '}'
111                         {
112                         if (check_hid_device(hid_device))
113                                 LIST_INSERT_HEAD(&hid_devices,hid_device,next);
114                         else
115                                 free_hid_device(hid_device);
116
117                         hid_device = NULL;
118                         }
119                 ;
120
121 options:        option ';'
122                 | options option ';'
123                 ;
124
125 option:         bdaddr
126                 | control_psm
127                 | interrupt_psm
128                 | reconnect_initiate
129                 | battery_power
130                 | normally_connectable
131                 | hid_descriptor
132                 | parser_error
133                 ;
134
135 bdaddr:         T_BDADDR T_BDADDRSTRING
136                         {
137                         memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
138                         }
139                 ;
140
141 control_psm:    T_CONTROL_PSM T_HEXBYTE
142                         {
143                         hid_device->control_psm = $2;
144                         }
145                 ;
146
147 interrupt_psm:  T_INTERRUPT_PSM T_HEXBYTE
148                         {
149                         hid_device->interrupt_psm = $2;
150                         }
151                 ;
152
153 reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
154                         {
155                         hid_device->reconnect_initiate = 1;
156                         }
157                 | T_RECONNECT_INITIATE T_FALSE
158                         {
159                         hid_device->reconnect_initiate = 0;
160                         }
161                 ;
162
163 battery_power:  T_BATTERY_POWER T_TRUE
164                         {
165                         hid_device->battery_power = 1;
166                         }
167                 | T_BATTERY_POWER T_FALSE
168                         {
169                         hid_device->battery_power = 0;
170                         }
171                 ;
172
173 normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
174                         {
175                         hid_device->normally_connectable = 1;
176                         }
177                 | T_NORMALLY_CONNECTABLE T_FALSE
178                         {
179                         hid_device->normally_connectable = 0;
180                         }
181                 ;
182
183 hid_descriptor: T_HID_DESCRIPTOR        
184                         {
185                         hid_descriptor_size = 0;
186                         }
187                 '{' hid_descriptor_bytes '}'
188                         {
189                         if (hid_device->desc != NULL)
190                                 hid_dispose_report_desc(hid_device->desc);
191
192                         hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
193                         if (hid_device->desc == NULL) {
194                                 SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
195                                 YYABORT;
196                         }
197                         }
198                 ;
199
200 hid_descriptor_bytes: hid_descriptor_byte
201                 | hid_descriptor_bytes hid_descriptor_byte
202                 ;
203
204 hid_descriptor_byte: T_HEXBYTE
205                         {
206                         if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
207                                 SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
208                                 YYABORT;
209                         }
210
211                         buffer[hid_descriptor_size ++] = $1;
212                         }
213                 ;
214
215 parser_error:   T_ERROR
216                         {
217                                 YYABORT;
218                         }
219
220 %%
221
222 /* Display parser error message */
223 void
224 yyerror(char const *message)
225 {
226         SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); 
227 }
228
229 /* Re-read config file */
230 int32_t
231 read_config_file(void)
232 {
233         int32_t e;
234
235         if (config_file == NULL) {
236                 SYSLOG(LOGERR, "Unknown config file name!" EOL);
237                 return (-1);
238         }
239
240         if ((yyin = fopen(config_file, "r")) == NULL) {
241                 SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
242                                 config_file, strerror(errno), errno);
243                 return (-1);
244         }
245
246         clean_config();
247         if (yyparse() < 0) {
248                 SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
249                                 config_file);
250                 e = -1;
251         } else
252                 e = 0;
253
254         fclose(yyin);
255         yyin = NULL;
256
257         return (e);
258 }
259
260 /* Clean config */
261 void
262 clean_config(void)
263 {
264         while (!LIST_EMPTY(&hid_devices)) {
265                 hid_device_p    d = LIST_FIRST(&hid_devices);
266
267                 LIST_REMOVE(d, next);
268                 free_hid_device(d);
269         }
270 }
271
272 /* Lookup config entry */
273 hid_device_p
274 get_hid_device(bdaddr_p bdaddr)
275 {
276         hid_device_p    d;
277
278         LIST_FOREACH(d, &hid_devices, next)
279                 if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
280                         break;
281
282         return (d);
283 }
284
285 /* Get next config entry */
286 hid_device_p
287 get_next_hid_device(hid_device_p d)
288 {
289         return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
290 }
291
292 /* Print config entry */
293 void
294 print_hid_device(hid_device_p d, FILE *f)
295 {
296         /* XXX FIXME hack! */
297         struct report_desc {
298                 unsigned int    size;
299                 unsigned char   data[1];
300         };
301         /* XXX FIXME hack! */
302
303         struct report_desc      *desc = (struct report_desc *) d->desc;
304         uint32_t                 i;
305
306         fprintf(f,
307 "device {\n"                                    \
308 "       bdaddr                  %s;\n"          \
309 "       control_psm             0x%x;\n"        \
310 "       interrupt_psm           0x%x;\n"        \
311 "       reconnect_initiate      %s;\n"          \
312 "       battery_power           %s;\n"          \
313 "       normally_connectable    %s;\n"          \
314 "       hid_descriptor          {",
315                 bt_ntoa(&d->bdaddr, NULL),
316                 d->control_psm, d->interrupt_psm,
317                 d->reconnect_initiate? "true" : "false",
318                 d->battery_power? "true" : "false",
319                 d->normally_connectable? "true" : "false");
320  
321         for (i = 0; i < desc->size; i ++) {
322                         if ((i % 8) == 0)
323                                 fprintf(f, "\n          ");
324  
325                         fprintf(f, "0x%2.2x ", desc->data[i]);
326         }
327                 
328         fprintf(f,
329 "\n"            \
330 "       };\n"   \
331 "}\n");
332 }
333
334 /* Check config entry */
335 static int32_t
336 check_hid_device(hid_device_p d)
337 {
338         hid_data_t      hd;
339         hid_item_t      hi;
340         int32_t         page;
341
342         if (get_hid_device(&d->bdaddr) != NULL) {
343                 SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
344                                 bt_ntoa(&d->bdaddr, NULL));
345                 return (0);
346         }
347
348         if (d->control_psm == 0) {
349                 SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
350                 return (0);
351         }
352
353         if (d->interrupt_psm == 0) {
354                 SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
355                 return (0);
356         }
357
358         if (d->desc == NULL) {
359                 SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
360                 return (0);
361         }
362
363         /* XXX somehow need to make sure descriptor is valid */
364         for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
365                 switch (hi.kind) {
366                 case hid_collection:
367                 case hid_endcollection:
368                 case hid_output:
369                 case hid_feature:
370                         break;
371
372                 case hid_input:
373                         /* Check if the device may send keystrokes */
374                         page = HID_PAGE(hi.usage);
375                         if (page == HUP_KEYBOARD)
376                                 d->keyboard = 1;
377                         break;
378                 }
379         }
380         hid_end_parse(hd);
381
382         return (1);
383 }
384
385 /* Free config entry */
386 static void
387 free_hid_device(hid_device_p d)
388 {
389         if (d->desc != NULL)
390                 hid_dispose_report_desc(d->desc);
391
392         memset(d, 0, sizeof(*d));
393         free(d);
394 }
395
396 /* Re-read hids file */
397 int32_t
398 read_hids_file(void)
399 {
400         FILE            *f;
401         hid_device_t    *d;
402         char            *line;
403         bdaddr_t         bdaddr;
404         int32_t          lineno;
405
406         if (hids_file == NULL) {
407                 SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
408                 return (-1);
409         }
410
411         if ((f = fopen(hids_file, "r")) == NULL) {
412                 if (errno == ENOENT)
413                         return (0);
414
415                 SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
416                         hids_file, strerror(errno), errno);
417                 return (-1);
418         }
419
420         for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
421                 if ((line = strtok(buffer, "\r\n\t ")) == NULL)
422                         continue; /* ignore empty lines */
423
424                 if (!bt_aton(line, &bdaddr)) {
425                         SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
426                                 "%s:%d" EOL, hids_file, lineno);
427                         continue;
428                 }
429
430                 if ((d = get_hid_device(&bdaddr)) != NULL)
431                         d->new_device = 0;
432         }
433
434         fclose(f);
435
436         return (0);
437 }
438
439 /* Write hids file */
440 int32_t
441 write_hids_file(void)
442 {
443         char             path[PATH_MAX];
444         FILE            *f;
445         hid_device_t    *d;
446
447         if (hids_file == NULL) {
448                 SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
449                 return (-1);
450         }
451
452         snprintf(path, sizeof(path), "%s.new", hids_file);
453
454         if ((f = fopen(path, "w")) == NULL) {
455                 SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
456                         path, strerror(errno), errno);
457                 return (-1);
458         }
459
460         LIST_FOREACH(d, &hid_devices, next)
461                 if (!d->new_device)
462                         fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
463
464         fclose(f);
465
466         if (rename(path, hids_file) < 0) {
467                 SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
468                         "%s (%d)" EOL, path, hids_file, strerror(errno), errno);
469                 unlink(path);
470                 return (-1);
471         }
472
473         return (0);
474 }
475