xref: /freebsd/usr.sbin/bluetooth/bthidd/server.c (revision 5bf5ca772c6de2d53344a78cf461447cc322ccea)
1 /*
2  * server.c
3  */
4 
5 /*-
6  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7  *
8  * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: server.c,v 1.9 2006/09/07 21:06:53 max Exp $
33  * $FreeBSD$
34  */
35 
36 #include <sys/queue.h>
37 #include <assert.h>
38 #define L2CAP_SOCKET_CHECKED
39 #include <bluetooth.h>
40 #include <dev/vkbd/vkbd_var.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <unistd.h>
48 #include <usbhid.h>
49 #include "bthid_config.h"
50 #include "bthidd.h"
51 #include "kbd.h"
52 
53 #undef	max
54 #define	max(x, y)	(((x) > (y))? (x) : (y))
55 
56 static int32_t	server_accept (bthid_server_p srv, int32_t fd);
57 static int32_t	server_process(bthid_server_p srv, int32_t fd);
58 
59 /*
60  * Initialize server
61  */
62 
63 int32_t
64 server_init(bthid_server_p srv)
65 {
66 	struct sockaddr_l2cap	l2addr;
67 
68 	assert(srv != NULL);
69 
70 	srv->ctrl = srv->intr = -1;
71 	FD_ZERO(&srv->rfdset);
72 	FD_ZERO(&srv->wfdset);
73 	LIST_INIT(&srv->sessions);
74 
75 	/* Open /dev/consolectl */
76 	srv->cons = open("/dev/consolectl", O_RDWR);
77 	if (srv->cons < 0) {
78 		syslog(LOG_ERR, "Could not open /dev/consolectl. %s (%d)",
79 			strerror(errno), errno);
80 		return (-1);
81 	}
82 
83 	/* Create control socket */
84 	srv->ctrl = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
85 	if (srv->ctrl < 0) {
86 		syslog(LOG_ERR, "Could not create control L2CAP socket. " \
87 			"%s (%d)", strerror(errno), errno);
88 		close(srv->cons);
89 		return (-1);
90 	}
91 
92 	l2addr.l2cap_len = sizeof(l2addr);
93 	l2addr.l2cap_family = AF_BLUETOOTH;
94 	memcpy(&l2addr.l2cap_bdaddr, &srv->bdaddr, sizeof(l2addr.l2cap_bdaddr));
95 	l2addr.l2cap_psm = htole16(0x11);
96 	l2addr.l2cap_bdaddr_type = BDADDR_BREDR;
97 	l2addr.l2cap_cid = 0;
98 
99 	if (bind(srv->ctrl, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
100 		syslog(LOG_ERR, "Could not bind control L2CAP socket. " \
101 			"%s (%d)", strerror(errno), errno);
102 		close(srv->ctrl);
103 		close(srv->cons);
104 		return (-1);
105 	}
106 
107 	if (listen(srv->ctrl, 10) < 0) {
108 		syslog(LOG_ERR, "Could not listen on control L2CAP socket. " \
109 			"%s (%d)", strerror(errno), errno);
110 		close(srv->ctrl);
111 		close(srv->cons);
112 		return (-1);
113 	}
114 
115 	/* Create intrrupt socket */
116 	srv->intr = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
117 	if (srv->intr < 0) {
118 		syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \
119 			"%s (%d)", strerror(errno), errno);
120 		close(srv->ctrl);
121 		close(srv->cons);
122 		return (-1);
123 	}
124 
125 	l2addr.l2cap_psm = htole16(0x13);
126 
127 	if (bind(srv->intr, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
128 		syslog(LOG_ERR, "Could not bind interrupt L2CAP socket. " \
129 			"%s (%d)", strerror(errno), errno);
130 		close(srv->intr);
131 		close(srv->ctrl);
132 		close(srv->cons);
133 		return (-1);
134 	}
135 
136 	if (listen(srv->intr, 10) < 0) {
137 		syslog(LOG_ERR, "Could not listen on interrupt L2CAP socket. "\
138 			"%s (%d)", strerror(errno), errno);
139 		close(srv->intr);
140 		close(srv->ctrl);
141 		close(srv->cons);
142 		return (-1);
143 	}
144 
145 	FD_SET(srv->ctrl, &srv->rfdset);
146 	FD_SET(srv->intr, &srv->rfdset);
147 	srv->maxfd = max(srv->ctrl, srv->intr);
148 
149 	return (0);
150 }
151 
152 /*
153  * Shutdown server
154  */
155 
156 void
157 server_shutdown(bthid_server_p srv)
158 {
159 	assert(srv != NULL);
160 
161 	close(srv->cons);
162 	close(srv->ctrl);
163 	close(srv->intr);
164 
165 	while (!LIST_EMPTY(&srv->sessions))
166 		session_close(LIST_FIRST(&srv->sessions));
167 
168 	memset(srv, 0, sizeof(*srv));
169 }
170 
171 /*
172  * Do one server iteration
173  */
174 
175 int32_t
176 server_do(bthid_server_p srv)
177 {
178 	struct timeval	tv;
179 	fd_set		rfdset, wfdset;
180 	int32_t		n, fd;
181 
182 	assert(srv != NULL);
183 
184 	tv.tv_sec = 1;
185 	tv.tv_usec = 0;
186 
187 	/* Copy cached version of the fd sets and call select */
188 	memcpy(&rfdset, &srv->rfdset, sizeof(rfdset));
189 	memcpy(&wfdset, &srv->wfdset, sizeof(wfdset));
190 
191 	n = select(srv->maxfd + 1, &rfdset, &wfdset, NULL, &tv);
192 	if (n < 0) {
193 		if (errno == EINTR)
194 			return (0);
195 
196 		syslog(LOG_ERR, "Could not select(%d, %p, %p). %s (%d)",
197 			srv->maxfd + 1, &rfdset, &wfdset, strerror(errno), errno);
198 
199 		return (-1);
200 	}
201 
202 	/* Process descriptors (if any) */
203 	for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) {
204 		if (FD_ISSET(fd, &rfdset)) {
205 			n --;
206 
207 			if (fd == srv->ctrl || fd == srv->intr)
208 				server_accept(srv, fd);
209 			else
210 				server_process(srv, fd);
211 		} else if (FD_ISSET(fd, &wfdset)) {
212 			n --;
213 
214 			client_connect(srv, fd);
215 		}
216 	}
217 
218 	return (0);
219 }
220 
221 /*
222  * Accept new connection
223  */
224 
225 static int32_t
226 server_accept(bthid_server_p srv, int32_t fd)
227 {
228 	bthid_session_p		s;
229 	hid_device_p		d;
230 	struct sockaddr_l2cap	l2addr;
231 	int32_t			new_fd;
232 	socklen_t		len;
233 
234 	len = sizeof(l2addr);
235 	if ((new_fd = accept(fd, (struct sockaddr *) &l2addr, &len)) < 0) {
236 		syslog(LOG_ERR, "Could not accept %s connection. %s (%d)",
237 			(fd == srv->ctrl)? "control" : "interrupt",
238 			strerror(errno), errno);
239 		return (-1);
240 	}
241 
242 	/* Is device configured? */
243 	if ((d = get_hid_device(&l2addr.l2cap_bdaddr)) == NULL) {
244 		syslog(LOG_ERR, "Rejecting %s connection from %s. " \
245 			"Device not configured",
246 			(fd == srv->ctrl)? "control" : "interrupt",
247 			bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
248 		close(new_fd);
249 		return (-1);
250 	}
251 
252 	/* Check if we have session for the device */
253 	if ((s = session_by_bdaddr(srv, &l2addr.l2cap_bdaddr)) == NULL) {
254 		d->new_device = 0; /* reset new device flag */
255 		write_hids_file();
256 
257 		/* Create new inbound session */
258 		if ((s = session_open(srv, d)) == NULL) {
259 			syslog(LOG_CRIT, "Could not open inbound session "
260 				"for %s", bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
261 			close(new_fd);
262 			return (-1);
263 		}
264 	}
265 
266 	/* Update descriptors */
267 	if (fd == srv->ctrl) {
268 		assert(s->ctrl == -1);
269 		s->ctrl = new_fd;
270 		s->state = (s->intr == -1)? W4INTR : OPEN;
271 	} else {
272 		assert(s->intr == -1);
273 		s->intr = new_fd;
274 		s->state = (s->ctrl == -1)? W4CTRL : OPEN;
275 	}
276 
277 	FD_SET(new_fd, &srv->rfdset);
278 	if (new_fd > srv->maxfd)
279 		srv->maxfd = new_fd;
280 
281 	syslog(LOG_NOTICE, "Accepted %s connection from %s",
282 		(fd == srv->ctrl)? "control" : "interrupt",
283 		bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
284 
285 	/* Register session's vkbd descriptor (if needed) for read */
286 	if (s->state == OPEN && d->keyboard) {
287 		assert(s->vkbd != -1);
288 
289 		FD_SET(s->vkbd, &srv->rfdset);
290 		if (s->vkbd > srv->maxfd)
291 			srv->maxfd = s->vkbd;
292 	}
293 
294 	/* Pass device for probing after both channels are established */
295 	if (s->state == OPEN)
296 		hid_initialise(s);
297 
298 	return (0);
299 }
300 
301 /*
302  * Process data on the connection
303  */
304 
305 static int32_t
306 server_process(bthid_server_p srv, int32_t fd)
307 {
308 	bthid_session_p		s = session_by_fd(srv, fd);
309 	int32_t			len, to_read;
310 	int32_t			(*cb)(bthid_session_p, uint8_t *, int32_t);
311 	union {
312 		uint8_t		b[1024];
313 		vkbd_status_t	s;
314 	}			data;
315 
316 	if (s == NULL)
317 		return (0); /* can happen on device disconnect */
318 
319 
320 	if (fd == s->ctrl) {
321 		cb = hid_control;
322 		to_read = sizeof(data.b);
323 	} else if (fd == s->intr) {
324 		cb = hid_interrupt;
325 		to_read = sizeof(data.b);
326 	} else {
327 		assert(fd == s->vkbd);
328 
329 		cb = kbd_status_changed;
330 		to_read = sizeof(data.s);
331 	}
332 
333 	do {
334 		len = read(fd, &data, to_read);
335 	} while (len < 0 && errno == EINTR);
336 
337 	if (len < 0) {
338 		syslog(LOG_ERR, "Could not read data from %s (%s). %s (%d)",
339 			bt_ntoa(&s->bdaddr, NULL),
340 			(fd == s->ctrl)? "control" : "interrupt",
341 			strerror(errno), errno);
342 		session_close(s);
343 		return (0);
344 	}
345 
346 	if (len == 0) {
347 		syslog(LOG_NOTICE, "Remote device %s has closed %s connection",
348 			bt_ntoa(&s->bdaddr, NULL),
349 			(fd == s->ctrl)? "control" : "interrupt");
350 		session_close(s);
351 		return (0);
352 	}
353 
354 	(*cb)(s, (uint8_t *) &data, len);
355 
356 	return (0);
357 }
358 
359