xref: /freebsd/usr.sbin/bluetooth/bthidd/parser.y (revision e65080696611e52ddc84103f4314122d0a336fa5)
16490c2ffSMaksim Yevmenkin %{
26490c2ffSMaksim Yevmenkin /*
36490c2ffSMaksim Yevmenkin  * parser.y
47aebfa93SMaksim Yevmenkin  */
57aebfa93SMaksim Yevmenkin 
67aebfa93SMaksim Yevmenkin /*-
71de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
81de7b4b8SPedro F. Giffuni  *
97aebfa93SMaksim Yevmenkin  * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
106490c2ffSMaksim Yevmenkin  * All rights reserved.
116490c2ffSMaksim Yevmenkin  *
126490c2ffSMaksim Yevmenkin  * Redistribution and use in source and binary forms, with or without
136490c2ffSMaksim Yevmenkin  * modification, are permitted provided that the following conditions
146490c2ffSMaksim Yevmenkin  * are met:
156490c2ffSMaksim Yevmenkin  * 1. Redistributions of source code must retain the above copyright
166490c2ffSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer.
176490c2ffSMaksim Yevmenkin  * 2. Redistributions in binary form must reproduce the above copyright
186490c2ffSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer in the
196490c2ffSMaksim Yevmenkin  *    documentation and/or other materials provided with the distribution.
206490c2ffSMaksim Yevmenkin  *
216490c2ffSMaksim Yevmenkin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
226490c2ffSMaksim Yevmenkin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
236490c2ffSMaksim Yevmenkin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
246490c2ffSMaksim Yevmenkin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
256490c2ffSMaksim Yevmenkin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
266490c2ffSMaksim Yevmenkin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
276490c2ffSMaksim Yevmenkin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
286490c2ffSMaksim Yevmenkin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
296490c2ffSMaksim Yevmenkin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
306490c2ffSMaksim Yevmenkin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
316490c2ffSMaksim Yevmenkin  * SUCH DAMAGE.
326490c2ffSMaksim Yevmenkin  *
337aebfa93SMaksim Yevmenkin  * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
346490c2ffSMaksim Yevmenkin  * $FreeBSD$
356490c2ffSMaksim Yevmenkin  */
366490c2ffSMaksim Yevmenkin 
376490c2ffSMaksim Yevmenkin #include <sys/queue.h>
388d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
396490c2ffSMaksim Yevmenkin #include <bluetooth.h>
407aebfa93SMaksim Yevmenkin #include <dev/usb/usb.h>
417aebfa93SMaksim Yevmenkin #include <dev/usb/usbhid.h>
426490c2ffSMaksim Yevmenkin #include <errno.h>
436490c2ffSMaksim Yevmenkin #include <limits.h>
446490c2ffSMaksim Yevmenkin #include <stdio.h>
455e2a209aSBaptiste Daroussin #include <stdlib.h>
466490c2ffSMaksim Yevmenkin #include <string.h>
476490c2ffSMaksim Yevmenkin #include <unistd.h>
483adfd74aSMaksim Yevmenkin #include <usbhid.h>
496490c2ffSMaksim Yevmenkin 
506490c2ffSMaksim Yevmenkin #ifndef BTHIDCONTROL
516490c2ffSMaksim Yevmenkin #include <stdarg.h>
526490c2ffSMaksim Yevmenkin #include <syslog.h>
536490c2ffSMaksim Yevmenkin #define	SYSLOG		syslog
546490c2ffSMaksim Yevmenkin #define	LOGCRIT		LOG_CRIT
556490c2ffSMaksim Yevmenkin #define	LOGERR		LOG_ERR
566490c2ffSMaksim Yevmenkin #define	LOGWARNING	LOG_WARNING
576490c2ffSMaksim Yevmenkin #define	EOL
586490c2ffSMaksim Yevmenkin #else
596490c2ffSMaksim Yevmenkin #define	SYSLOG		fprintf
606490c2ffSMaksim Yevmenkin #define	LOGCRIT		stderr
616490c2ffSMaksim Yevmenkin #define	LOGERR		stderr
626490c2ffSMaksim Yevmenkin #define	LOGWARNING	stderr
636490c2ffSMaksim Yevmenkin #define	EOL	"\n"
646490c2ffSMaksim Yevmenkin #endif /* ndef BTHIDCONTROL */
656490c2ffSMaksim Yevmenkin 
66*e6508069SVladimir Kondratyev #define	NAMELESS_DEVICE	"No Name"
67*e6508069SVladimir Kondratyev 
686490c2ffSMaksim Yevmenkin #include "bthid_config.h"
696490c2ffSMaksim Yevmenkin 
706490c2ffSMaksim Yevmenkin 	int	yylex		(void);
717aebfa93SMaksim Yevmenkin 	void	yyerror		(char const *);
727aebfa93SMaksim Yevmenkin static	int32_t	check_hid_device(hid_device_p hid_device);
736490c2ffSMaksim Yevmenkin static	void	free_hid_device	(hid_device_p hid_device);
746490c2ffSMaksim Yevmenkin 
757aebfa93SMaksim Yevmenkin extern	FILE			*yyin;
766490c2ffSMaksim Yevmenkin extern	int			 yylineno;
777aebfa93SMaksim Yevmenkin 	char const		*config_file = BTHIDD_CONFFILE;
787aebfa93SMaksim Yevmenkin 	char const		*hids_file   = BTHIDD_HIDSFILE;
796490c2ffSMaksim Yevmenkin 
806490c2ffSMaksim Yevmenkin static	char			 buffer[1024];
817aebfa93SMaksim Yevmenkin static	int32_t			 hid_descriptor_size;
826490c2ffSMaksim Yevmenkin static	hid_device_t		*hid_device = NULL;
836490c2ffSMaksim Yevmenkin static	LIST_HEAD(, hid_device)	 hid_devices;
846490c2ffSMaksim Yevmenkin 
856490c2ffSMaksim Yevmenkin %}
866490c2ffSMaksim Yevmenkin 
876490c2ffSMaksim Yevmenkin %union {
886490c2ffSMaksim Yevmenkin 	bdaddr_t	bdaddr;
897aebfa93SMaksim Yevmenkin 	int32_t		num;
90*e6508069SVladimir Kondratyev 	char		*string;
916490c2ffSMaksim Yevmenkin }
926490c2ffSMaksim Yevmenkin 
936490c2ffSMaksim Yevmenkin %token <bdaddr> T_BDADDRSTRING
946490c2ffSMaksim Yevmenkin %token <num>	T_HEXBYTE
956032284eSVladimir Kondratyev %token <num>	T_HEXWORD
96*e6508069SVladimir Kondratyev %token <string>	T_STRING
97*e6508069SVladimir Kondratyev %token T_NAME
986032284eSVladimir Kondratyev %token T_DEVICE T_BDADDR T_VENDOR_ID T_PRODUCT_ID T_VERSION T_CONTROL_PSM
996032284eSVladimir Kondratyev %token T_INTERRUPT_PSM T_RECONNECT_INITIATE T_BATTERY_POWER
1006032284eSVladimir Kondratyev %token T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
1016490c2ffSMaksim Yevmenkin %token T_TRUE T_FALSE T_ERROR
1026490c2ffSMaksim Yevmenkin 
1036490c2ffSMaksim Yevmenkin %%
1046490c2ffSMaksim Yevmenkin 
1056490c2ffSMaksim Yevmenkin config:		line
1066490c2ffSMaksim Yevmenkin 		| config line
1076490c2ffSMaksim Yevmenkin 		;
1086490c2ffSMaksim Yevmenkin 
1096490c2ffSMaksim Yevmenkin line:		T_DEVICE
1106490c2ffSMaksim Yevmenkin 			{
1116490c2ffSMaksim Yevmenkin 			hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
1126490c2ffSMaksim Yevmenkin 			if (hid_device == NULL) {
1136490c2ffSMaksim Yevmenkin 				SYSLOG(LOGCRIT, "Could not allocate new " \
1146490c2ffSMaksim Yevmenkin 						"config entry" EOL);
1156490c2ffSMaksim Yevmenkin 				YYABORT;
1166490c2ffSMaksim Yevmenkin 			}
1176490c2ffSMaksim Yevmenkin 
1186490c2ffSMaksim Yevmenkin 			hid_device->new_device = 1;
1196490c2ffSMaksim Yevmenkin 			}
1206490c2ffSMaksim Yevmenkin 		'{' options '}'
1216490c2ffSMaksim Yevmenkin 			{
1226490c2ffSMaksim Yevmenkin 			if (check_hid_device(hid_device))
1236490c2ffSMaksim Yevmenkin 				LIST_INSERT_HEAD(&hid_devices,hid_device,next);
1246490c2ffSMaksim Yevmenkin 			else
1256490c2ffSMaksim Yevmenkin 				free_hid_device(hid_device);
1266490c2ffSMaksim Yevmenkin 
1276490c2ffSMaksim Yevmenkin 			hid_device = NULL;
1286490c2ffSMaksim Yevmenkin 			}
1296490c2ffSMaksim Yevmenkin 		;
1306490c2ffSMaksim Yevmenkin 
1316490c2ffSMaksim Yevmenkin options:	option ';'
1326490c2ffSMaksim Yevmenkin 		| options option ';'
1336490c2ffSMaksim Yevmenkin 		;
1346490c2ffSMaksim Yevmenkin 
1356490c2ffSMaksim Yevmenkin option:		bdaddr
136*e6508069SVladimir Kondratyev 		| name
1376032284eSVladimir Kondratyev 		| vendor_id
1386032284eSVladimir Kondratyev 		| product_id
1396032284eSVladimir Kondratyev 		| version
1406490c2ffSMaksim Yevmenkin 		| control_psm
1416490c2ffSMaksim Yevmenkin 		| interrupt_psm
1426490c2ffSMaksim Yevmenkin 		| reconnect_initiate
1436490c2ffSMaksim Yevmenkin 		| battery_power
1446490c2ffSMaksim Yevmenkin 		| normally_connectable
1456490c2ffSMaksim Yevmenkin 		| hid_descriptor
1466490c2ffSMaksim Yevmenkin 		| parser_error
1476490c2ffSMaksim Yevmenkin 		;
1486490c2ffSMaksim Yevmenkin 
1496490c2ffSMaksim Yevmenkin bdaddr:		T_BDADDR T_BDADDRSTRING
1506490c2ffSMaksim Yevmenkin 			{
1516490c2ffSMaksim Yevmenkin 			memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
1526490c2ffSMaksim Yevmenkin 			}
1536490c2ffSMaksim Yevmenkin 		;
1546490c2ffSMaksim Yevmenkin 
155*e6508069SVladimir Kondratyev name:		T_NAME T_STRING
156*e6508069SVladimir Kondratyev 			{
157*e6508069SVladimir Kondratyev 			if (hid_device->name != NULL) {
158*e6508069SVladimir Kondratyev                                 free(hid_device->name);
159*e6508069SVladimir Kondratyev                                 hid_device->name = NULL;
160*e6508069SVladimir Kondratyev 			}
161*e6508069SVladimir Kondratyev 
162*e6508069SVladimir Kondratyev 			if (strcmp($2, NAMELESS_DEVICE)) {
163*e6508069SVladimir Kondratyev 				hid_device->name = strdup($2);
164*e6508069SVladimir Kondratyev 				if (hid_device->name == NULL) {
165*e6508069SVladimir Kondratyev 					SYSLOG(LOGCRIT, "Could not allocate new " \
166*e6508069SVladimir Kondratyev 							"device name" EOL);
167*e6508069SVladimir Kondratyev 					YYABORT;
168*e6508069SVladimir Kondratyev 				}
169*e6508069SVladimir Kondratyev 			}
170*e6508069SVladimir Kondratyev 			}
171*e6508069SVladimir Kondratyev 		;
172*e6508069SVladimir Kondratyev 
1736032284eSVladimir Kondratyev vendor_id:	T_VENDOR_ID T_HEXWORD
1746032284eSVladimir Kondratyev 			{
1756032284eSVladimir Kondratyev 			hid_device->vendor_id = $2;
1766032284eSVladimir Kondratyev 			}
1776032284eSVladimir Kondratyev 		;
1786032284eSVladimir Kondratyev 
1796032284eSVladimir Kondratyev product_id:	T_PRODUCT_ID T_HEXWORD
1806032284eSVladimir Kondratyev 			{
1816032284eSVladimir Kondratyev 			hid_device->product_id = $2;
1826032284eSVladimir Kondratyev 			}
1836032284eSVladimir Kondratyev 		;
1846032284eSVladimir Kondratyev 
1856032284eSVladimir Kondratyev version:	T_VERSION T_HEXWORD
1866032284eSVladimir Kondratyev 			{
1876032284eSVladimir Kondratyev 			hid_device->version = $2;
1886032284eSVladimir Kondratyev 			}
1896032284eSVladimir Kondratyev 		;
1906032284eSVladimir Kondratyev 
1916490c2ffSMaksim Yevmenkin control_psm:	T_CONTROL_PSM T_HEXBYTE
1926490c2ffSMaksim Yevmenkin 			{
1936490c2ffSMaksim Yevmenkin 			hid_device->control_psm = $2;
1946490c2ffSMaksim Yevmenkin 			}
1956490c2ffSMaksim Yevmenkin 		;
1966490c2ffSMaksim Yevmenkin 
1976490c2ffSMaksim Yevmenkin interrupt_psm:	T_INTERRUPT_PSM T_HEXBYTE
1986490c2ffSMaksim Yevmenkin 			{
1996490c2ffSMaksim Yevmenkin 			hid_device->interrupt_psm = $2;
2006490c2ffSMaksim Yevmenkin 			}
2016490c2ffSMaksim Yevmenkin 		;
2026490c2ffSMaksim Yevmenkin 
2036490c2ffSMaksim Yevmenkin reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
2046490c2ffSMaksim Yevmenkin 			{
2056490c2ffSMaksim Yevmenkin 			hid_device->reconnect_initiate = 1;
2066490c2ffSMaksim Yevmenkin 			}
2076490c2ffSMaksim Yevmenkin 		| T_RECONNECT_INITIATE T_FALSE
2086490c2ffSMaksim Yevmenkin 			{
2096490c2ffSMaksim Yevmenkin 			hid_device->reconnect_initiate = 0;
2106490c2ffSMaksim Yevmenkin 			}
2116490c2ffSMaksim Yevmenkin 		;
2126490c2ffSMaksim Yevmenkin 
2136490c2ffSMaksim Yevmenkin battery_power:	T_BATTERY_POWER T_TRUE
2146490c2ffSMaksim Yevmenkin 			{
2156490c2ffSMaksim Yevmenkin 			hid_device->battery_power = 1;
2166490c2ffSMaksim Yevmenkin 			}
2176490c2ffSMaksim Yevmenkin 		| T_BATTERY_POWER T_FALSE
2186490c2ffSMaksim Yevmenkin 			{
2196490c2ffSMaksim Yevmenkin 			hid_device->battery_power = 0;
2206490c2ffSMaksim Yevmenkin 			}
2216490c2ffSMaksim Yevmenkin 		;
2226490c2ffSMaksim Yevmenkin 
2236490c2ffSMaksim Yevmenkin normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
2246490c2ffSMaksim Yevmenkin 			{
2256490c2ffSMaksim Yevmenkin 			hid_device->normally_connectable = 1;
2266490c2ffSMaksim Yevmenkin 			}
2276490c2ffSMaksim Yevmenkin 		| T_NORMALLY_CONNECTABLE T_FALSE
2286490c2ffSMaksim Yevmenkin 			{
2296490c2ffSMaksim Yevmenkin 			hid_device->normally_connectable = 0;
2306490c2ffSMaksim Yevmenkin 			}
2316490c2ffSMaksim Yevmenkin 		;
2326490c2ffSMaksim Yevmenkin 
2336490c2ffSMaksim Yevmenkin hid_descriptor:	T_HID_DESCRIPTOR
2346490c2ffSMaksim Yevmenkin 			{
2356490c2ffSMaksim Yevmenkin 			hid_descriptor_size = 0;
2366490c2ffSMaksim Yevmenkin 			}
2376490c2ffSMaksim Yevmenkin 		'{' hid_descriptor_bytes '}'
2386490c2ffSMaksim Yevmenkin 			{
2396490c2ffSMaksim Yevmenkin 			if (hid_device->desc != NULL)
2406490c2ffSMaksim Yevmenkin 				hid_dispose_report_desc(hid_device->desc);
2416490c2ffSMaksim Yevmenkin 
2426e9bee64SMaksim Yevmenkin 			hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
2436490c2ffSMaksim Yevmenkin 			if (hid_device->desc == NULL) {
2446490c2ffSMaksim Yevmenkin 				SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
2456490c2ffSMaksim Yevmenkin 				YYABORT;
2466490c2ffSMaksim Yevmenkin 			}
2476490c2ffSMaksim Yevmenkin 			}
2486490c2ffSMaksim Yevmenkin 		;
2496490c2ffSMaksim Yevmenkin 
2506490c2ffSMaksim Yevmenkin hid_descriptor_bytes: hid_descriptor_byte
2516490c2ffSMaksim Yevmenkin 		| hid_descriptor_bytes hid_descriptor_byte
2526490c2ffSMaksim Yevmenkin 		;
2536490c2ffSMaksim Yevmenkin 
2546490c2ffSMaksim Yevmenkin hid_descriptor_byte: T_HEXBYTE
2556490c2ffSMaksim Yevmenkin 			{
2567aebfa93SMaksim Yevmenkin 			if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
2576490c2ffSMaksim Yevmenkin 				SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
2586490c2ffSMaksim Yevmenkin 				YYABORT;
2596490c2ffSMaksim Yevmenkin 			}
2606490c2ffSMaksim Yevmenkin 
2616490c2ffSMaksim Yevmenkin 			buffer[hid_descriptor_size ++] = $1;
2626490c2ffSMaksim Yevmenkin 			}
2636490c2ffSMaksim Yevmenkin 		;
2646490c2ffSMaksim Yevmenkin 
2656490c2ffSMaksim Yevmenkin parser_error:	T_ERROR
2666490c2ffSMaksim Yevmenkin 			{
2676490c2ffSMaksim Yevmenkin 				YYABORT;
2686490c2ffSMaksim Yevmenkin 			}
2696490c2ffSMaksim Yevmenkin 
2706490c2ffSMaksim Yevmenkin %%
2716490c2ffSMaksim Yevmenkin 
2726490c2ffSMaksim Yevmenkin /* Display parser error message */
2736490c2ffSMaksim Yevmenkin void
2746490c2ffSMaksim Yevmenkin yyerror(char const *message)
2756490c2ffSMaksim Yevmenkin {
2766490c2ffSMaksim Yevmenkin 	SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
2776490c2ffSMaksim Yevmenkin }
2786490c2ffSMaksim Yevmenkin 
2796490c2ffSMaksim Yevmenkin /* Re-read config file */
2807aebfa93SMaksim Yevmenkin int32_t
2816490c2ffSMaksim Yevmenkin read_config_file(void)
2826490c2ffSMaksim Yevmenkin {
2837aebfa93SMaksim Yevmenkin 	int32_t	e;
2846490c2ffSMaksim Yevmenkin 
2856490c2ffSMaksim Yevmenkin 	if (config_file == NULL) {
2866490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Unknown config file name!" EOL);
2876490c2ffSMaksim Yevmenkin 		return (-1);
2886490c2ffSMaksim Yevmenkin 	}
2896490c2ffSMaksim Yevmenkin 
2906490c2ffSMaksim Yevmenkin 	if ((yyin = fopen(config_file, "r")) == NULL) {
2916490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
2926490c2ffSMaksim Yevmenkin 				config_file, strerror(errno), errno);
2936490c2ffSMaksim Yevmenkin 		return (-1);
2946490c2ffSMaksim Yevmenkin 	}
2956490c2ffSMaksim Yevmenkin 
2966490c2ffSMaksim Yevmenkin 	clean_config();
2976490c2ffSMaksim Yevmenkin 	if (yyparse() < 0) {
2986490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
2996490c2ffSMaksim Yevmenkin 				config_file);
3006490c2ffSMaksim Yevmenkin 		e = -1;
3016490c2ffSMaksim Yevmenkin 	} else
3026490c2ffSMaksim Yevmenkin 		e = 0;
3036490c2ffSMaksim Yevmenkin 
3046490c2ffSMaksim Yevmenkin 	fclose(yyin);
3056490c2ffSMaksim Yevmenkin 	yyin = NULL;
3066490c2ffSMaksim Yevmenkin 
3076490c2ffSMaksim Yevmenkin 	return (e);
3086490c2ffSMaksim Yevmenkin }
3096490c2ffSMaksim Yevmenkin 
3106490c2ffSMaksim Yevmenkin /* Clean config */
3116490c2ffSMaksim Yevmenkin void
3126490c2ffSMaksim Yevmenkin clean_config(void)
3136490c2ffSMaksim Yevmenkin {
3146490c2ffSMaksim Yevmenkin 	while (!LIST_EMPTY(&hid_devices)) {
3157aebfa93SMaksim Yevmenkin 		hid_device_p	d = LIST_FIRST(&hid_devices);
3166490c2ffSMaksim Yevmenkin 
3177aebfa93SMaksim Yevmenkin 		LIST_REMOVE(d, next);
3187aebfa93SMaksim Yevmenkin 		free_hid_device(d);
3196490c2ffSMaksim Yevmenkin 	}
3206490c2ffSMaksim Yevmenkin }
3216490c2ffSMaksim Yevmenkin 
3226490c2ffSMaksim Yevmenkin /* Lookup config entry */
3236490c2ffSMaksim Yevmenkin hid_device_p
3246490c2ffSMaksim Yevmenkin get_hid_device(bdaddr_p bdaddr)
3256490c2ffSMaksim Yevmenkin {
3267aebfa93SMaksim Yevmenkin 	hid_device_p	d;
3276490c2ffSMaksim Yevmenkin 
3287aebfa93SMaksim Yevmenkin 	LIST_FOREACH(d, &hid_devices, next)
3297aebfa93SMaksim Yevmenkin 		if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
3306490c2ffSMaksim Yevmenkin 			break;
3316490c2ffSMaksim Yevmenkin 
3327aebfa93SMaksim Yevmenkin 	return (d);
3336490c2ffSMaksim Yevmenkin }
3346490c2ffSMaksim Yevmenkin 
3356490c2ffSMaksim Yevmenkin /* Get next config entry */
3366490c2ffSMaksim Yevmenkin hid_device_p
3376490c2ffSMaksim Yevmenkin get_next_hid_device(hid_device_p d)
3386490c2ffSMaksim Yevmenkin {
3396490c2ffSMaksim Yevmenkin 	return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
3406490c2ffSMaksim Yevmenkin }
3416490c2ffSMaksim Yevmenkin 
3426490c2ffSMaksim Yevmenkin /* Print config entry */
3436490c2ffSMaksim Yevmenkin void
3447aebfa93SMaksim Yevmenkin print_hid_device(hid_device_p d, FILE *f)
3456490c2ffSMaksim Yevmenkin {
3466490c2ffSMaksim Yevmenkin 	/* XXX FIXME hack! */
3476490c2ffSMaksim Yevmenkin 	struct report_desc {
3486490c2ffSMaksim Yevmenkin 		unsigned int	size;
3496490c2ffSMaksim Yevmenkin 		unsigned char	data[1];
3506490c2ffSMaksim Yevmenkin 	};
3516490c2ffSMaksim Yevmenkin 	/* XXX FIXME hack! */
3526490c2ffSMaksim Yevmenkin 
3537aebfa93SMaksim Yevmenkin 	struct report_desc	*desc = (struct report_desc *) d->desc;
3547aebfa93SMaksim Yevmenkin 	uint32_t		 i;
3556490c2ffSMaksim Yevmenkin 
3566490c2ffSMaksim Yevmenkin 	fprintf(f,
3576490c2ffSMaksim Yevmenkin "device {\n"					\
3586490c2ffSMaksim Yevmenkin "	bdaddr			%s;\n"		\
359*e6508069SVladimir Kondratyev "	name			\"%s\";\n"	\
3606032284eSVladimir Kondratyev "	vendor_id		0x%04x;\n"	\
3616032284eSVladimir Kondratyev "	product_id		0x%04x;\n"	\
3626032284eSVladimir Kondratyev "	version			0x%04x;\n"	\
3636490c2ffSMaksim Yevmenkin "	control_psm		0x%x;\n"	\
3649fbe483aSMaksim Yevmenkin "	interrupt_psm		0x%x;\n"	\
3656490c2ffSMaksim Yevmenkin "	reconnect_initiate	%s;\n"		\
3666490c2ffSMaksim Yevmenkin "	battery_power		%s;\n"		\
3676490c2ffSMaksim Yevmenkin "	normally_connectable	%s;\n"		\
3686490c2ffSMaksim Yevmenkin "	hid_descriptor		{",
3697aebfa93SMaksim Yevmenkin 		bt_ntoa(&d->bdaddr, NULL),
370*e6508069SVladimir Kondratyev 		(d->name != NULL)? d->name : NAMELESS_DEVICE,
3716032284eSVladimir Kondratyev 		d->vendor_id, d->product_id, d->version,
3727aebfa93SMaksim Yevmenkin 		d->control_psm, d->interrupt_psm,
3737aebfa93SMaksim Yevmenkin                 d->reconnect_initiate? "true" : "false",
3747aebfa93SMaksim Yevmenkin                 d->battery_power? "true" : "false",
3757aebfa93SMaksim Yevmenkin                 d->normally_connectable? "true" : "false");
3766490c2ffSMaksim Yevmenkin 
3776490c2ffSMaksim Yevmenkin 	for (i = 0; i < desc->size; i ++) {
3786490c2ffSMaksim Yevmenkin 			if ((i % 8) == 0)
379c46a9ea8SMaksim Yevmenkin 				fprintf(f, "\n		");
3806490c2ffSMaksim Yevmenkin 
3816490c2ffSMaksim Yevmenkin 			fprintf(f, "0x%2.2x ", desc->data[i]);
3826490c2ffSMaksim Yevmenkin 	}
3836490c2ffSMaksim Yevmenkin 
384c46a9ea8SMaksim Yevmenkin 	fprintf(f,
3856490c2ffSMaksim Yevmenkin "\n"		\
3866490c2ffSMaksim Yevmenkin "	};\n"	\
3876490c2ffSMaksim Yevmenkin "}\n");
3886490c2ffSMaksim Yevmenkin }
3896490c2ffSMaksim Yevmenkin 
3906490c2ffSMaksim Yevmenkin /* Check config entry */
3917aebfa93SMaksim Yevmenkin static int32_t
3927aebfa93SMaksim Yevmenkin check_hid_device(hid_device_p d)
3936490c2ffSMaksim Yevmenkin {
3947aebfa93SMaksim Yevmenkin 	hid_data_t	hd;
3957aebfa93SMaksim Yevmenkin 	hid_item_t	hi;
3967aebfa93SMaksim Yevmenkin 	int32_t		page;
3977aebfa93SMaksim Yevmenkin 
3987aebfa93SMaksim Yevmenkin 	if (get_hid_device(&d->bdaddr) != NULL) {
3996490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
4007aebfa93SMaksim Yevmenkin 				bt_ntoa(&d->bdaddr, NULL));
4016490c2ffSMaksim Yevmenkin 		return (0);
4026490c2ffSMaksim Yevmenkin 	}
4036490c2ffSMaksim Yevmenkin 
4047aebfa93SMaksim Yevmenkin 	if (d->control_psm == 0) {
4056490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
4066490c2ffSMaksim Yevmenkin 		return (0);
4076490c2ffSMaksim Yevmenkin 	}
4086490c2ffSMaksim Yevmenkin 
4097aebfa93SMaksim Yevmenkin 	if (d->interrupt_psm == 0) {
4106490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
4116490c2ffSMaksim Yevmenkin 		return (0);
4126490c2ffSMaksim Yevmenkin 	}
4136490c2ffSMaksim Yevmenkin 
4147aebfa93SMaksim Yevmenkin 	if (d->desc == NULL) {
4156490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
4166490c2ffSMaksim Yevmenkin 		return (0);
4176490c2ffSMaksim Yevmenkin 	}
4186490c2ffSMaksim Yevmenkin 
4197aebfa93SMaksim Yevmenkin 	/* XXX somehow need to make sure descriptor is valid */
4207aebfa93SMaksim Yevmenkin 	for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
4217aebfa93SMaksim Yevmenkin 		switch (hi.kind) {
4227aebfa93SMaksim Yevmenkin 		case hid_collection:
4237aebfa93SMaksim Yevmenkin 		case hid_endcollection:
4247aebfa93SMaksim Yevmenkin 		case hid_output:
4257aebfa93SMaksim Yevmenkin 		case hid_feature:
4267aebfa93SMaksim Yevmenkin 			break;
4277aebfa93SMaksim Yevmenkin 
4287aebfa93SMaksim Yevmenkin 		case hid_input:
4297aebfa93SMaksim Yevmenkin 			/* Check if the device may send keystrokes */
4307aebfa93SMaksim Yevmenkin 			page = HID_PAGE(hi.usage);
43106912ebaSMaksim Yevmenkin 			if (page == HUP_KEYBOARD)
4327aebfa93SMaksim Yevmenkin 				d->keyboard = 1;
4337aebfa93SMaksim Yevmenkin 			break;
4347aebfa93SMaksim Yevmenkin 		}
4357aebfa93SMaksim Yevmenkin 	}
4367aebfa93SMaksim Yevmenkin 	hid_end_parse(hd);
4377aebfa93SMaksim Yevmenkin 
4386490c2ffSMaksim Yevmenkin 	return (1);
4396490c2ffSMaksim Yevmenkin }
4406490c2ffSMaksim Yevmenkin 
4416490c2ffSMaksim Yevmenkin /* Free config entry */
4426490c2ffSMaksim Yevmenkin static void
4437aebfa93SMaksim Yevmenkin free_hid_device(hid_device_p d)
4446490c2ffSMaksim Yevmenkin {
4457aebfa93SMaksim Yevmenkin 	if (d->desc != NULL)
4467aebfa93SMaksim Yevmenkin 		hid_dispose_report_desc(d->desc);
4476490c2ffSMaksim Yevmenkin 
448*e6508069SVladimir Kondratyev 	free(d->name);
4497aebfa93SMaksim Yevmenkin 	memset(d, 0, sizeof(*d));
4507aebfa93SMaksim Yevmenkin 	free(d);
4516490c2ffSMaksim Yevmenkin }
4526490c2ffSMaksim Yevmenkin 
4536490c2ffSMaksim Yevmenkin /* Re-read hids file */
4547aebfa93SMaksim Yevmenkin int32_t
4556490c2ffSMaksim Yevmenkin read_hids_file(void)
4566490c2ffSMaksim Yevmenkin {
4577aebfa93SMaksim Yevmenkin 	FILE		*f;
4587aebfa93SMaksim Yevmenkin 	hid_device_t	*d;
4597aebfa93SMaksim Yevmenkin 	char		*line;
4606490c2ffSMaksim Yevmenkin 	bdaddr_t	 bdaddr;
4617aebfa93SMaksim Yevmenkin 	int32_t		 lineno;
4626490c2ffSMaksim Yevmenkin 
4636490c2ffSMaksim Yevmenkin 	if (hids_file == NULL) {
4646490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
4656490c2ffSMaksim Yevmenkin 		return (-1);
4666490c2ffSMaksim Yevmenkin 	}
4676490c2ffSMaksim Yevmenkin 
4686490c2ffSMaksim Yevmenkin 	if ((f = fopen(hids_file, "r")) == NULL) {
4696490c2ffSMaksim Yevmenkin 		if (errno == ENOENT)
4706490c2ffSMaksim Yevmenkin 			return (0);
4716490c2ffSMaksim Yevmenkin 
4726490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
4736490c2ffSMaksim Yevmenkin 			hids_file, strerror(errno), errno);
4746490c2ffSMaksim Yevmenkin 		return (-1);
4756490c2ffSMaksim Yevmenkin 	}
4766490c2ffSMaksim Yevmenkin 
4776490c2ffSMaksim Yevmenkin 	for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
4786490c2ffSMaksim Yevmenkin 		if ((line = strtok(buffer, "\r\n\t ")) == NULL)
4796490c2ffSMaksim Yevmenkin 			continue; /* ignore empty lines */
4806490c2ffSMaksim Yevmenkin 
4816490c2ffSMaksim Yevmenkin 		if (!bt_aton(line, &bdaddr)) {
4826490c2ffSMaksim Yevmenkin 			SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
4836490c2ffSMaksim Yevmenkin 				"%s:%d" EOL, hids_file, lineno);
4846490c2ffSMaksim Yevmenkin 			continue;
4856490c2ffSMaksim Yevmenkin 		}
4866490c2ffSMaksim Yevmenkin 
4877aebfa93SMaksim Yevmenkin 		if ((d = get_hid_device(&bdaddr)) != NULL)
4887aebfa93SMaksim Yevmenkin 			d->new_device = 0;
4896490c2ffSMaksim Yevmenkin 	}
4906490c2ffSMaksim Yevmenkin 
4916490c2ffSMaksim Yevmenkin 	fclose(f);
4926490c2ffSMaksim Yevmenkin 
4936490c2ffSMaksim Yevmenkin 	return (0);
4946490c2ffSMaksim Yevmenkin }
4956490c2ffSMaksim Yevmenkin 
4966490c2ffSMaksim Yevmenkin /* Write hids file */
4977aebfa93SMaksim Yevmenkin int32_t
4986490c2ffSMaksim Yevmenkin write_hids_file(void)
4996490c2ffSMaksim Yevmenkin {
5006490c2ffSMaksim Yevmenkin 	char		 path[PATH_MAX];
5017aebfa93SMaksim Yevmenkin 	FILE		*f;
5027aebfa93SMaksim Yevmenkin 	hid_device_t	*d;
5036490c2ffSMaksim Yevmenkin 
5046490c2ffSMaksim Yevmenkin 	if (hids_file == NULL) {
5056490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
5066490c2ffSMaksim Yevmenkin 		return (-1);
5076490c2ffSMaksim Yevmenkin 	}
5086490c2ffSMaksim Yevmenkin 
5096490c2ffSMaksim Yevmenkin 	snprintf(path, sizeof(path), "%s.new", hids_file);
5106490c2ffSMaksim Yevmenkin 
5116490c2ffSMaksim Yevmenkin 	if ((f = fopen(path, "w")) == NULL) {
5126490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
5136490c2ffSMaksim Yevmenkin 			path, strerror(errno), errno);
5146490c2ffSMaksim Yevmenkin 		return (-1);
5156490c2ffSMaksim Yevmenkin 	}
5166490c2ffSMaksim Yevmenkin 
5177aebfa93SMaksim Yevmenkin 	LIST_FOREACH(d, &hid_devices, next)
5187aebfa93SMaksim Yevmenkin 		if (!d->new_device)
5197aebfa93SMaksim Yevmenkin 			fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
5206490c2ffSMaksim Yevmenkin 
5216490c2ffSMaksim Yevmenkin 	fclose(f);
5226490c2ffSMaksim Yevmenkin 
5236490c2ffSMaksim Yevmenkin 	if (rename(path, hids_file) < 0) {
5246490c2ffSMaksim Yevmenkin 		SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
5256490c2ffSMaksim Yevmenkin 			"%s (%d)" EOL, path, hids_file, strerror(errno), errno);
5266490c2ffSMaksim Yevmenkin 		unlink(path);
5276490c2ffSMaksim Yevmenkin 		return (-1);
5286490c2ffSMaksim Yevmenkin 	}
5296490c2ffSMaksim Yevmenkin 
5306490c2ffSMaksim Yevmenkin 	return (0);
5316490c2ffSMaksim Yevmenkin }
5326490c2ffSMaksim Yevmenkin 
533