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