]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/bthidd/server.c
Import the Linaro Cortex Strings library into contrib.
[FreeBSD/FreeBSD.git] / usr.sbin / bluetooth / bthidd / server.c
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