]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/bthidd/server.c
MFV r324198: 8081 Compiler warnings in zdb
[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/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