]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/bthidd/server.c
sysctl(9): Fix a few mandoc related issues
[FreeBSD/FreeBSD.git] / usr.sbin / bluetooth / bthidd / server.c
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/evdev/input.h>
41 #include <dev/vkbd/vkbd_var.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include <unistd.h>
49 #include <usbhid.h>
50 #include "bthid_config.h"
51 #include "bthidd.h"
52 #include "btuinput.h"
53 #include "kbd.h"
54
55 #undef  max
56 #define max(x, y)       (((x) > (y))? (x) : (y))
57
58 static int32_t  server_accept (bthid_server_p srv, int32_t fd);
59 static int32_t  server_process(bthid_server_p srv, int32_t fd);
60
61 /*
62  * Initialize server
63  */
64
65 int32_t
66 server_init(bthid_server_p srv)
67 {
68         struct sockaddr_l2cap   l2addr;
69
70         assert(srv != NULL);
71
72         srv->ctrl = srv->intr = -1;
73         FD_ZERO(&srv->rfdset);
74         FD_ZERO(&srv->wfdset);
75         LIST_INIT(&srv->sessions);
76
77         /* Open /dev/consolectl */
78         srv->cons = open("/dev/consolectl", O_RDWR);
79         if (srv->cons < 0) {
80                 syslog(LOG_ERR, "Could not open /dev/consolectl. %s (%d)",
81                         strerror(errno), errno);
82                 return (-1);
83         }
84
85         /* Create control socket */
86         srv->ctrl = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
87         if (srv->ctrl < 0) {
88                 syslog(LOG_ERR, "Could not create control L2CAP socket. " \
89                         "%s (%d)", strerror(errno), errno);
90                 close(srv->cons);
91                 return (-1);
92         }
93
94         l2addr.l2cap_len = sizeof(l2addr);
95         l2addr.l2cap_family = AF_BLUETOOTH;
96         memcpy(&l2addr.l2cap_bdaddr, &srv->bdaddr, sizeof(l2addr.l2cap_bdaddr));
97         l2addr.l2cap_psm = htole16(0x11);
98         l2addr.l2cap_bdaddr_type = BDADDR_BREDR;
99         l2addr.l2cap_cid = 0;
100         
101         if (bind(srv->ctrl, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
102                 syslog(LOG_ERR, "Could not bind control L2CAP socket. " \
103                         "%s (%d)", strerror(errno), errno);
104                 close(srv->ctrl);
105                 close(srv->cons);
106                 return (-1);
107         }
108
109         if (listen(srv->ctrl, 10) < 0) {
110                 syslog(LOG_ERR, "Could not listen on control L2CAP socket. " \
111                         "%s (%d)", strerror(errno), errno);
112                 close(srv->ctrl);
113                 close(srv->cons);
114                 return (-1);
115         }
116
117         /* Create intrrupt socket */
118         srv->intr = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
119         if (srv->intr < 0) {
120                 syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \
121                         "%s (%d)", strerror(errno), errno);
122                 close(srv->ctrl);
123                 close(srv->cons);
124                 return (-1);
125         }
126
127         l2addr.l2cap_psm = htole16(0x13);
128
129         if (bind(srv->intr, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
130                 syslog(LOG_ERR, "Could not bind interrupt L2CAP socket. " \
131                         "%s (%d)", strerror(errno), errno);
132                 close(srv->intr);
133                 close(srv->ctrl);
134                 close(srv->cons);
135                 return (-1);
136         }
137
138         if (listen(srv->intr, 10) < 0) {
139                 syslog(LOG_ERR, "Could not listen on interrupt L2CAP socket. "\
140                         "%s (%d)", strerror(errno), errno);
141                 close(srv->intr);
142                 close(srv->ctrl);
143                 close(srv->cons);
144                 return (-1);
145         }
146
147         FD_SET(srv->ctrl, &srv->rfdset);
148         FD_SET(srv->intr, &srv->rfdset);
149         srv->maxfd = max(srv->ctrl, srv->intr);
150
151         return (0);
152 }
153
154 /*
155  * Shutdown server
156  */
157
158 void
159 server_shutdown(bthid_server_p srv)
160 {
161         assert(srv != NULL);
162
163         close(srv->cons);
164         close(srv->ctrl);
165         close(srv->intr);
166
167         while (!LIST_EMPTY(&srv->sessions))
168                 session_close(LIST_FIRST(&srv->sessions));
169
170         memset(srv, 0, sizeof(*srv));
171 }
172
173 /*
174  * Do one server iteration
175  */
176
177 int32_t
178 server_do(bthid_server_p srv)
179 {
180         struct timeval  tv;
181         fd_set          rfdset, wfdset;
182         int32_t         n, fd;
183
184         assert(srv != NULL);
185
186         tv.tv_sec = 1;
187         tv.tv_usec = 0;
188
189         /* Copy cached version of the fd sets and call select */
190         memcpy(&rfdset, &srv->rfdset, sizeof(rfdset));
191         memcpy(&wfdset, &srv->wfdset, sizeof(wfdset));
192
193         n = select(srv->maxfd + 1, &rfdset, &wfdset, NULL, &tv);
194         if (n < 0) {
195                 if (errno == EINTR)  
196                         return (0);
197
198                 syslog(LOG_ERR, "Could not select(%d, %p, %p). %s (%d)",
199                         srv->maxfd + 1, &rfdset, &wfdset, strerror(errno), errno);
200
201                 return (-1);
202         }
203
204         /* Process descriptors (if any) */
205         for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) {
206                 if (FD_ISSET(fd, &rfdset)) {
207                         n --;
208
209                         if (fd == srv->ctrl || fd == srv->intr)
210                                 server_accept(srv, fd);
211                         else
212                                 server_process(srv, fd);
213                 } else if (FD_ISSET(fd, &wfdset)) {
214                         n --;
215
216                         client_connect(srv, fd);
217                 }
218         }
219
220         return (0);
221 }
222
223 /*
224  * Accept new connection 
225  */
226
227 static int32_t
228 server_accept(bthid_server_p srv, int32_t fd)
229 {
230         bthid_session_p         s;
231         hid_device_p            d;
232         struct sockaddr_l2cap   l2addr;
233         int32_t                 new_fd;
234         socklen_t               len;
235
236         len = sizeof(l2addr);
237         if ((new_fd = accept(fd, (struct sockaddr *) &l2addr, &len)) < 0) {
238                 syslog(LOG_ERR, "Could not accept %s connection. %s (%d)",
239                         (fd == srv->ctrl)? "control" : "interrupt",
240                         strerror(errno), errno);
241                 return (-1);
242         }
243
244         /* Is device configured? */
245         if ((d = get_hid_device(&l2addr.l2cap_bdaddr)) == NULL) {
246                 syslog(LOG_ERR, "Rejecting %s connection from %s. " \
247                         "Device not configured",
248                         (fd == srv->ctrl)? "control" : "interrupt",
249                         bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
250                 close(new_fd);
251                 return (-1);
252         }
253
254         /* Check if we have session for the device */
255         if ((s = session_by_bdaddr(srv, &l2addr.l2cap_bdaddr)) == NULL) {
256                 d->new_device = 0; /* reset new device flag */
257                 write_hids_file();
258
259                 /* Create new inbound session */
260                 if ((s = session_open(srv, d)) == NULL) {
261                         syslog(LOG_CRIT, "Could not open inbound session "
262                                 "for %s", bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
263                         close(new_fd);
264                         return (-1);
265                 }
266         }
267
268         /* Update descriptors */
269         if (fd == srv->ctrl) {
270                 assert(s->ctrl == -1);
271                 s->ctrl = new_fd;
272                 s->state = (s->intr == -1)? W4INTR : OPEN;
273         } else {
274                 assert(s->intr == -1);
275                 s->intr = new_fd;
276                 s->state = (s->ctrl == -1)? W4CTRL : OPEN;
277         }
278
279         FD_SET(new_fd, &srv->rfdset);
280         if (new_fd > srv->maxfd)
281                 srv->maxfd = new_fd;
282
283         syslog(LOG_NOTICE, "Accepted %s connection from %s",
284                 (fd == srv->ctrl)? "control" : "interrupt",
285                 bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
286
287         /* Create virtual kbd/mouse after both channels are established */
288         if (s->state == OPEN && session_run(s) < 0) {
289                 session_close(s);
290                 return (-1);
291         }
292
293         return (0);
294 }
295
296 /*
297  * Process data on the connection
298  */
299
300 static int32_t
301 server_process(bthid_server_p srv, int32_t fd)
302 {
303         bthid_session_p         s = session_by_fd(srv, fd);
304         int32_t                 len, to_read;
305         int32_t                 (*cb)(bthid_session_p, uint8_t *, int32_t);
306         union {
307                 uint8_t                 b[1024];
308                 vkbd_status_t           s;
309                 struct input_event      ie;
310         }                               data;
311
312         if (s == NULL)
313                 return (0); /* can happen on device disconnect */
314
315
316         if (fd == s->ctrl) {
317                 cb = hid_control;
318                 to_read = sizeof(data.b);
319         } else if (fd == s->intr) {
320                 cb = hid_interrupt;
321                 to_read = sizeof(data.b);
322         } else if (fd == s->ukbd) {
323                 cb = uinput_kbd_status_changed;
324                 to_read = sizeof(data.ie);
325         } else {
326                 assert(fd == s->vkbd);
327
328                 cb = kbd_status_changed;
329                 to_read = sizeof(data.s);
330         }
331
332         do {
333                 len = read(fd, &data, to_read);
334         } while (len < 0 && errno == EINTR);
335
336         if (len < 0) {
337                 syslog(LOG_ERR, "Could not read data from %s (%s). %s (%d)",
338                         bt_ntoa(&s->bdaddr, NULL),
339                         (fd == s->ctrl)? "control" : "interrupt",
340                         strerror(errno), errno);
341                 session_close(s);
342                 return (0);
343         }
344
345         if (len == 0) {
346                 syslog(LOG_NOTICE, "Remote device %s has closed %s connection",
347                         bt_ntoa(&s->bdaddr, NULL),
348                         (fd == s->ctrl)? "control" : "interrupt");
349                 session_close(s);
350                 return (0);
351         }
352
353         (*cb)(s, (uint8_t *) &data, len);
354
355         return (0);
356 }
357