]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/bthidd/parser.y
Update ELF Tool Chain to upstream rev 3400
[FreeBSD/FreeBSD.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 #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
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
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
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
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
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
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
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
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
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