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