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