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