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