• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
6  *  Copyright (C) 2009-2010  Nokia Corporation
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <signal.h>
30 
31 #include <glib.h>
32 
33 #include "btio.h"
34 
35 #define DEFAULT_ACCEPT_TIMEOUT 2
36 
37 struct io_data {
38 	guint ref;
39 	GIOChannel *io;
40 	BtIOType type;
41 	gint reject;
42 	gint disconn;
43 	gint accept;
44 };
45 
io_data_unref(struct io_data * data)46 static void io_data_unref(struct io_data *data)
47 {
48 	data->ref--;
49 
50 	if (data->ref)
51 		return;
52 
53 	if (data->io)
54 		g_io_channel_unref(data->io);
55 
56 	g_free(data);
57 }
58 
io_data_ref(struct io_data * data)59 static struct io_data *io_data_ref(struct io_data *data)
60 {
61 	data->ref++;
62 	return data;
63 }
64 
io_data_new(GIOChannel * io,BtIOType type,gint reject,gint disconn,gint accept)65 static struct io_data *io_data_new(GIOChannel *io, BtIOType type, gint reject,
66 						gint disconn, gint accept)
67 {
68 	struct io_data *data;
69 
70 	data = g_new0(struct io_data, 1);
71 	data->io = io;
72 	data->type = type;
73 	data->reject = reject;
74 	data->disconn = disconn;
75 	data->accept = accept;
76 
77 	return io_data_ref(data);
78 }
79 
io_watch(GIOChannel * io,GIOCondition cond,gpointer user_data)80 static gboolean io_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
81 {
82 	printf("Disconnected\n");
83 	return FALSE;
84 }
85 
disconn_timeout(gpointer user_data)86 static gboolean disconn_timeout(gpointer user_data)
87 {
88 	struct io_data *data = user_data;
89 
90 	printf("Disconnecting\n");
91 
92 	g_io_channel_shutdown(data->io, TRUE, NULL);
93 
94 	return FALSE;
95 }
96 
connect_cb(GIOChannel * io,GError * err,gpointer user_data)97 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
98 {
99 	struct io_data *data = user_data;
100 	GIOCondition cond;
101 	char addr[18];
102 	uint16_t handle;
103 	uint8_t cls[3];
104 
105 	if (err) {
106 		printf("Connecting failed: %s\n", err->message);
107 		return;
108 	}
109 
110 	if (!bt_io_get(io, data->type, &err,
111 			BT_IO_OPT_DEST, addr,
112 			BT_IO_OPT_HANDLE, &handle,
113 			BT_IO_OPT_CLASS, cls,
114 			BT_IO_OPT_INVALID)) {
115 		printf("Unable to get destination address: %s\n",
116 								err->message);
117 		g_clear_error(&err);
118 		strcpy(addr, "(unknown)");
119 	}
120 
121 	printf("Successfully connected to %s. handle=%u, class=%02x%02x%02x\n",
122 			addr, handle, cls[0], cls[1], cls[2]);
123 
124 	if (data->type == BT_IO_L2CAP || data->type == BT_IO_SCO) {
125 		uint16_t omtu, imtu;
126 
127 		if (!bt_io_get(io, data->type, &err,
128 					BT_IO_OPT_OMTU, &omtu,
129 					BT_IO_OPT_IMTU, &imtu,
130 					BT_IO_OPT_INVALID)) {
131 			printf("Unable to get L2CAP MTU sizes: %s\n",
132 								err->message);
133 			g_clear_error(&err);
134 		} else
135 			printf("imtu=%u, omtu=%u\n", imtu, omtu);
136 	}
137 
138 	if (data->disconn == 0) {
139 		g_io_channel_shutdown(io, TRUE, NULL);
140 		printf("Disconnected\n");
141 		return;
142 	}
143 
144 	if (data->io == NULL)
145 		data->io = g_io_channel_ref(io);
146 
147 	if (data->disconn > 0) {
148 		io_data_ref(data);
149 		g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, data->disconn,
150 					disconn_timeout, data,
151 					(GDestroyNotify) io_data_unref);
152 	}
153 
154 
155 	io_data_ref(data);
156 	cond = G_IO_NVAL | G_IO_HUP | G_IO_ERR;
157 	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, io_watch, data,
158 					(GDestroyNotify) io_data_unref);
159 }
160 
confirm_timeout(gpointer user_data)161 static gboolean confirm_timeout(gpointer user_data)
162 {
163 	struct io_data *data = user_data;
164 
165 	if (data->reject >= 0) {
166 		printf("Rejecting connection\n");
167 		g_io_channel_shutdown(data->io, TRUE, NULL);
168 		return FALSE;
169 	}
170 
171 	printf("Accepting connection\n");
172 
173 	io_data_ref(data);
174 
175 	if (!bt_io_accept(data->io, connect_cb, data,
176 				(GDestroyNotify) io_data_unref, NULL)) {
177 		printf("bt_io_accept() failed\n");
178 		io_data_unref(data);
179 	}
180 
181 	return FALSE;
182 }
183 
confirm_cb(GIOChannel * io,gpointer user_data)184 static void confirm_cb(GIOChannel *io, gpointer user_data)
185 {
186 	char addr[18];
187 	struct io_data *data = user_data;
188 	GError *err = NULL;
189 
190 	if (!bt_io_get(io, data->type, &err, BT_IO_OPT_DEST, addr,
191 							BT_IO_OPT_INVALID)) {
192 		printf("bt_io_get(OPT_DEST): %s\n", err->message);
193 		g_clear_error(&err);
194 	} else
195 		printf("Got confirmation request for %s\n", addr);
196 
197 	if (data->accept < 0 && data->reject < 0)
198 		return;
199 
200 	if (data->reject == 0) {
201 		printf("Rejecting connection\n");
202 		g_io_channel_shutdown(io, TRUE, NULL);
203 		return;
204 	}
205 
206 	data->io = g_io_channel_ref(io);
207 	io_data_ref(data);
208 
209 	if (data->accept == 0) {
210 		if (!bt_io_accept(io, connect_cb, data,
211 					(GDestroyNotify) io_data_unref,
212 					&err)) {
213 			printf("bt_io_accept() failed: %s\n", err->message);
214 			g_clear_error(&err);
215 			io_data_unref(data);
216 			return;
217 		}
218 	} else {
219 		gint seconds = (data->reject > 0) ?
220 						data->reject : data->accept;
221 		g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, seconds,
222 					confirm_timeout, data,
223 					(GDestroyNotify) io_data_unref);
224 	}
225 }
226 
l2cap_connect(const char * src,const char * dst,uint16_t psm,gint disconn,gint sec)227 static void l2cap_connect(const char *src, const char *dst, uint16_t psm,
228 						gint disconn, gint sec)
229 {
230 	struct io_data *data;
231 	GError *err = NULL;
232 
233 	printf("Connecting to %s L2CAP PSM %u\n", dst, psm);
234 
235 	data = io_data_new(NULL, BT_IO_L2CAP, -1, disconn, -1);
236 
237 	if (src)
238 		data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
239 						(GDestroyNotify) io_data_unref,
240 						&err,
241 						BT_IO_OPT_SOURCE, src,
242 						BT_IO_OPT_DEST, dst,
243 						BT_IO_OPT_PSM, psm,
244 						BT_IO_OPT_SEC_LEVEL, sec,
245 						BT_IO_OPT_INVALID);
246 	else
247 		data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
248 						(GDestroyNotify) io_data_unref,
249 						&err,
250 						BT_IO_OPT_DEST, dst,
251 						BT_IO_OPT_PSM, psm,
252 						BT_IO_OPT_SEC_LEVEL, sec,
253 						BT_IO_OPT_INVALID);
254 
255 	if (!data->io) {
256 		printf("Connecting to %s failed: %s\n", dst, err->message);
257 		g_error_free(err);
258 		exit(EXIT_FAILURE);
259 	}
260 }
261 
l2cap_listen(const char * src,uint16_t psm,gint defer,gint reject,gint disconn,gint accept,gint sec,gboolean master)262 static void l2cap_listen(const char *src, uint16_t psm, gint defer,
263 				gint reject, gint disconn, gint accept,
264 				gint sec, gboolean master)
265 {
266 	struct io_data *data;
267 	BtIOConnect conn;
268 	BtIOConfirm cfm;
269 	GIOChannel *l2_srv;
270 	GError *err = NULL;
271 
272 	if (defer) {
273 		conn = NULL;
274 		cfm = confirm_cb;
275 	} else {
276 		conn = connect_cb;
277 		cfm = NULL;
278 	}
279 
280 	printf("Listening on L2CAP PSM %u\n", psm);
281 
282 	data = io_data_new(NULL, BT_IO_L2CAP, reject, disconn, accept);
283 
284 	if (src)
285 		l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
286 					data, (GDestroyNotify) io_data_unref,
287 					&err,
288 					BT_IO_OPT_SOURCE, src,
289 					BT_IO_OPT_PSM, psm,
290 					BT_IO_OPT_SEC_LEVEL, sec,
291 					BT_IO_OPT_MASTER, master,
292 					BT_IO_OPT_INVALID);
293 	else
294 		l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
295 					data, (GDestroyNotify) io_data_unref,
296 					&err,
297 					BT_IO_OPT_PSM, psm,
298 					BT_IO_OPT_SEC_LEVEL, sec,
299 					BT_IO_OPT_MASTER, master,
300 					BT_IO_OPT_INVALID);
301 
302 	if (!l2_srv) {
303 		printf("Listening failed: %s\n", err->message);
304 		g_error_free(err);
305 		exit(EXIT_FAILURE);
306 	}
307 
308 	g_io_channel_unref(l2_srv);
309 }
310 
rfcomm_connect(const char * src,const char * dst,uint8_t ch,gint disconn,gint sec)311 static void rfcomm_connect(const char *src, const char *dst, uint8_t ch,
312 						gint disconn, gint sec)
313 {
314 	struct io_data *data;
315 	GError *err = NULL;
316 
317 	printf("Connecting to %s RFCOMM channel %u\n", dst, ch);
318 
319 	data = io_data_new(NULL, BT_IO_RFCOMM, -1, disconn, -1);
320 
321 	if (src)
322 		data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
323 						(GDestroyNotify) io_data_unref,
324 						&err,
325 						BT_IO_OPT_SOURCE, src,
326 						BT_IO_OPT_DEST, dst,
327 						BT_IO_OPT_CHANNEL, ch,
328 						BT_IO_OPT_SEC_LEVEL, sec,
329 						BT_IO_OPT_INVALID);
330 	else
331 		data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
332 						(GDestroyNotify) io_data_unref,
333 						&err,
334 						BT_IO_OPT_DEST, dst,
335 						BT_IO_OPT_CHANNEL, ch,
336 						BT_IO_OPT_SEC_LEVEL, sec,
337 						BT_IO_OPT_INVALID);
338 
339 	if (!data->io) {
340 		printf("Connecting to %s failed: %s\n", dst, err->message);
341 		g_error_free(err);
342 		exit(EXIT_FAILURE);
343 	}
344 }
345 
rfcomm_listen(const char * src,uint8_t ch,gboolean defer,gint reject,gint disconn,gint accept,gint sec,gboolean master)346 static void rfcomm_listen(const char *src, uint8_t ch, gboolean defer,
347 				gint reject, gint disconn, gint accept,
348 				gint sec, gboolean master)
349 {
350 	struct io_data *data;
351 	BtIOConnect conn;
352 	BtIOConfirm cfm;
353 	GIOChannel *rc_srv;
354 	GError *err = NULL;
355 
356 	if (defer) {
357 		conn = NULL;
358 		cfm = confirm_cb;
359 	} else {
360 		conn = connect_cb;
361 		cfm = NULL;
362 	}
363 
364 	data = io_data_new(NULL, BT_IO_RFCOMM, reject, disconn, accept);
365 
366 	if (src)
367 		rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
368 					data, (GDestroyNotify) io_data_unref,
369 					&err,
370 					BT_IO_OPT_SOURCE, src,
371 					BT_IO_OPT_CHANNEL, ch,
372 					BT_IO_OPT_SEC_LEVEL, sec,
373 					BT_IO_OPT_MASTER, master,
374 					BT_IO_OPT_INVALID);
375 	else
376 		rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
377 					data, (GDestroyNotify) io_data_unref,
378 					&err,
379 					BT_IO_OPT_CHANNEL, ch,
380 					BT_IO_OPT_SEC_LEVEL, sec,
381 					BT_IO_OPT_MASTER, master,
382 					BT_IO_OPT_INVALID);
383 
384 	if (!rc_srv) {
385 		printf("Listening failed: %s\n", err->message);
386 		g_error_free(err);
387 		exit(EXIT_FAILURE);
388 	}
389 
390 	bt_io_get(rc_srv, BT_IO_RFCOMM, &err,
391 			BT_IO_OPT_CHANNEL, &ch,
392 			BT_IO_OPT_INVALID);
393 
394 	printf("Listening on RFCOMM channel %u\n", ch);
395 
396 	g_io_channel_unref(rc_srv);
397 }
398 
sco_connect(const char * src,const char * dst,gint disconn)399 static void sco_connect(const char *src, const char *dst, gint disconn)
400 {
401 	struct io_data *data;
402 	GError *err = NULL;
403 
404 	printf("Connecting SCO to %s\n", dst);
405 
406 	data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
407 
408 	if (src)
409 		data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
410 						(GDestroyNotify) io_data_unref,
411 						&err,
412 						BT_IO_OPT_SOURCE, src,
413 						BT_IO_OPT_DEST, dst,
414 						BT_IO_OPT_INVALID);
415 	else
416 		data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
417 						(GDestroyNotify) io_data_unref,
418 						&err,
419 						BT_IO_OPT_DEST, dst,
420 						BT_IO_OPT_INVALID);
421 
422 	if (!data->io) {
423 		printf("Connecting to %s failed: %s\n", dst, err->message);
424 		g_error_free(err);
425 		exit(EXIT_FAILURE);
426 	}
427 }
428 
sco_listen(const char * src,gint disconn)429 static void sco_listen(const char *src, gint disconn)
430 {
431 	struct io_data *data;
432 	GIOChannel *sco_srv;
433 	GError *err = NULL;
434 
435 	printf("Listening for SCO connections\n");
436 
437 	data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
438 
439 	if (src)
440 		sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
441 					data, (GDestroyNotify) io_data_unref,
442 					&err,
443 					BT_IO_OPT_SOURCE, src,
444 					BT_IO_OPT_INVALID);
445 	else
446 		sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
447 					data, (GDestroyNotify) io_data_unref,
448 					&err, BT_IO_OPT_INVALID);
449 
450 	if (!sco_srv) {
451 		printf("Listening failed: %s\n", err->message);
452 		g_error_free(err);
453 		exit(EXIT_FAILURE);
454 	}
455 
456 	g_io_channel_unref(sco_srv);
457 }
458 
459 static gint opt_channel = -1;
460 static gint opt_psm = 0;
461 static gboolean opt_sco = FALSE;
462 static gboolean opt_defer = FALSE;
463 static char *opt_dev = NULL;
464 static gint opt_reject = -1;
465 static gint opt_disconn = -1;
466 static gint opt_accept = DEFAULT_ACCEPT_TIMEOUT;
467 static gint opt_sec = 0;
468 static gboolean opt_master = FALSE;
469 
470 static GMainLoop *main_loop;
471 
472 static GOptionEntry options[] = {
473 	{ "channel", 'c', 0, G_OPTION_ARG_INT, &opt_channel,
474 				"RFCOMM channel" },
475 	{ "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
476 				"L2CAP PSM" },
477 	{ "sco", 's', 0, G_OPTION_ARG_NONE, &opt_sco,
478 				"Use SCO" },
479 	{ "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer,
480 				"Use DEFER_SETUP for incoming connections" },
481 	{ "sec-level", 'S', 0, G_OPTION_ARG_INT, &opt_sec,
482 				"Security level" },
483 	{ "dev", 'i', 0, G_OPTION_ARG_STRING, &opt_dev,
484 				"Which HCI device to use" },
485 	{ "reject", 'r', 0, G_OPTION_ARG_INT, &opt_reject,
486 				"Reject connection after N seconds" },
487 	{ "disconnect", 'D', 0, G_OPTION_ARG_INT, &opt_disconn,
488 				"Disconnect connection after N seconds" },
489 	{ "accept", 'a', 0, G_OPTION_ARG_INT, &opt_accept,
490 				"Accept connection after N seconds" },
491 	{ "master", 'm', 0, G_OPTION_ARG_NONE, &opt_master,
492 				"Master role switch (incoming connections)" },
493 	{ NULL },
494 };
495 
sig_term(int sig)496 static void sig_term(int sig)
497 {
498 	g_main_loop_quit(main_loop);
499 }
500 
main(int argc,char * argv[])501 int main(int argc, char *argv[])
502 {
503 	GOptionContext *context;
504 
505 	context = g_option_context_new(NULL);
506 	g_option_context_add_main_entries(context, options, NULL);
507 
508 	if (!g_option_context_parse(context, &argc, &argv, NULL))
509 		exit(EXIT_FAILURE);
510 
511 	g_option_context_free(context);
512 
513 	printf("accept=%d, reject=%d, discon=%d, defer=%d, sec=%d\n",
514 		opt_accept, opt_reject, opt_disconn, opt_defer, opt_sec);
515 
516 	if (opt_psm) {
517 		if (argc > 1)
518 			l2cap_connect(opt_dev, argv[1], opt_psm,
519 							opt_disconn, opt_sec);
520 		else
521 			l2cap_listen(opt_dev, opt_psm, opt_defer, opt_reject,
522 					opt_disconn, opt_accept, opt_sec,
523 					opt_master);
524 	}
525 
526 	if (opt_channel != -1) {
527 		if (argc > 1)
528 			rfcomm_connect(opt_dev, argv[1], opt_channel,
529 							opt_disconn, opt_sec);
530 		else
531 			rfcomm_listen(opt_dev, opt_channel, opt_defer,
532 					opt_reject, opt_disconn, opt_accept,
533 					opt_sec, opt_master);
534 	}
535 
536 	if (opt_sco) {
537 		if (argc > 1)
538 			sco_connect(opt_dev, argv[1], opt_disconn);
539 		else
540 			sco_listen(opt_dev, opt_disconn);
541 	}
542 
543 	signal(SIGTERM, sig_term);
544 	signal(SIGINT, sig_term);
545 
546 	main_loop = g_main_loop_new(NULL, FALSE);
547 
548 	g_main_loop_run(main_loop);
549 
550 	g_main_loop_unref(main_loop);
551 
552 	printf("Exiting\n");
553 
554 	exit(EXIT_SUCCESS);
555 }
556