Tizen 2.0 Release
[framework/connectivity/bluez.git] / test / btiotest.c
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 static gint opt_update_sec = 0;
37
38 struct io_data {
39         guint ref;
40         GIOChannel *io;
41         BtIOType type;
42         gint reject;
43         gint disconn;
44         gint accept;
45 };
46
47 static void io_data_unref(struct io_data *data)
48 {
49         data->ref--;
50
51         if (data->ref)
52                 return;
53
54         if (data->io)
55                 g_io_channel_unref(data->io);
56
57         g_free(data);
58 }
59
60 static struct io_data *io_data_ref(struct io_data *data)
61 {
62         data->ref++;
63         return data;
64 }
65
66 static struct io_data *io_data_new(GIOChannel *io, BtIOType type, gint reject,
67                                                 gint disconn, gint accept)
68 {
69         struct io_data *data;
70
71         data = g_new0(struct io_data, 1);
72         data->io = io;
73         data->type = type;
74         data->reject = reject;
75         data->disconn = disconn;
76         data->accept = accept;
77
78         return io_data_ref(data);
79 }
80
81 static gboolean io_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
82 {
83         printf("Disconnected\n");
84         return FALSE;
85 }
86
87 static gboolean disconn_timeout(gpointer user_data)
88 {
89         struct io_data *data = user_data;
90
91         printf("Disconnecting\n");
92
93         g_io_channel_shutdown(data->io, TRUE, NULL);
94
95         return FALSE;
96 }
97
98 static void update_sec_level(struct io_data *data)
99 {
100         GError *err = NULL;
101         int sec_level;
102
103         if (!bt_io_get(data->io, data->type, &err,
104                                         BT_IO_OPT_SEC_LEVEL, &sec_level,
105                                         BT_IO_OPT_INVALID)) {
106                 printf("bt_io_get(OPT_SEC_LEVEL): %s\n", err->message);
107                 g_clear_error(&err);
108                 return;
109         }
110
111         printf("sec_level=%d\n", sec_level);
112
113         if (opt_update_sec == sec_level)
114                 return;
115
116         if (!bt_io_set(data->io, data->type, &err,
117                                         BT_IO_OPT_SEC_LEVEL, opt_update_sec,
118                                         BT_IO_OPT_INVALID)) {
119                 printf("bt_io_set(OPT_SEC_LEVEL): %s\n", err->message);
120                 g_clear_error(&err);
121         }
122 }
123
124 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
125 {
126         struct io_data *data = user_data;
127         GIOCondition cond;
128         char addr[18];
129         uint16_t handle;
130         uint8_t cls[3];
131
132         if (err) {
133                 printf("Connecting failed: %s\n", err->message);
134                 return;
135         }
136
137         if (!bt_io_get(io, data->type, &err,
138                         BT_IO_OPT_DEST, addr,
139                         BT_IO_OPT_HANDLE, &handle,
140                         BT_IO_OPT_CLASS, cls,
141                         BT_IO_OPT_INVALID)) {
142                 printf("Unable to get destination address: %s\n",
143                                                                 err->message);
144                 g_clear_error(&err);
145                 strcpy(addr, "(unknown)");
146         }
147
148         printf("Successfully connected to %s. handle=%u, class=%02x%02x%02x\n",
149                         addr, handle, cls[0], cls[1], cls[2]);
150
151         if (data->type == BT_IO_L2CAP || data->type == BT_IO_SCO) {
152                 uint16_t omtu, imtu;
153
154                 if (!bt_io_get(io, data->type, &err,
155                                         BT_IO_OPT_OMTU, &omtu,
156                                         BT_IO_OPT_IMTU, &imtu,
157                                         BT_IO_OPT_INVALID)) {
158                         printf("Unable to get L2CAP MTU sizes: %s\n",
159                                                                 err->message);
160                         g_clear_error(&err);
161                 } else
162                         printf("imtu=%u, omtu=%u\n", imtu, omtu);
163         }
164
165         if (data->type == BT_IO_L2CAP) {
166                 uint8_t key_size;
167
168                 if (!bt_io_get(io, data->type, &err,
169                                         BT_IO_OPT_KEY_SIZE, &key_size,
170                                         BT_IO_OPT_INVALID)) {
171                         printf("Unable to get L2CAP Key size: %s\n",
172                                                                 err->message);
173                         g_clear_error(&err);
174                 } else
175                         printf("key_size=%u\n", key_size);
176         }
177
178         if (data->disconn == 0) {
179                 g_io_channel_shutdown(io, TRUE, NULL);
180                 printf("Disconnected\n");
181                 return;
182         }
183
184         if (data->io == NULL)
185                 data->io = g_io_channel_ref(io);
186
187         if (data->disconn > 0) {
188                 io_data_ref(data);
189                 g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, data->disconn,
190                                         disconn_timeout, data,
191                                         (GDestroyNotify) io_data_unref);
192         }
193
194
195         io_data_ref(data);
196
197         if (opt_update_sec > 0)
198                 update_sec_level(data);
199
200         cond = G_IO_NVAL | G_IO_HUP | G_IO_ERR;
201         g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, io_watch, data,
202                                         (GDestroyNotify) io_data_unref);
203 }
204
205 static gboolean confirm_timeout(gpointer user_data)
206 {
207         struct io_data *data = user_data;
208
209         if (data->reject >= 0) {
210                 printf("Rejecting connection\n");
211                 g_io_channel_shutdown(data->io, TRUE, NULL);
212                 return FALSE;
213         }
214
215         printf("Accepting connection\n");
216
217         io_data_ref(data);
218
219         if (opt_update_sec > 0)
220                 update_sec_level(data);
221
222         if (!bt_io_accept(data->io, connect_cb, data,
223                                 (GDestroyNotify) io_data_unref, NULL)) {
224                 printf("bt_io_accept() failed\n");
225                 io_data_unref(data);
226         }
227
228         return FALSE;
229 }
230
231 static void confirm_cb(GIOChannel *io, gpointer user_data)
232 {
233         char addr[18];
234         struct io_data *data = user_data;
235         GError *err = NULL;
236
237         if (!bt_io_get(io, data->type, &err, BT_IO_OPT_DEST, addr,
238                                                         BT_IO_OPT_INVALID)) {
239                 printf("bt_io_get(OPT_DEST): %s\n", err->message);
240                 g_clear_error(&err);
241         } else
242                 printf("Got confirmation request for %s\n", addr);
243
244         if (data->accept < 0 && data->reject < 0)
245                 return;
246
247         if (data->reject == 0) {
248                 printf("Rejecting connection\n");
249                 g_io_channel_shutdown(io, TRUE, NULL);
250                 return;
251         }
252
253         data->io = g_io_channel_ref(io);
254         io_data_ref(data);
255
256         if (data->accept == 0) {
257                 if (!bt_io_accept(io, connect_cb, data,
258                                         (GDestroyNotify) io_data_unref,
259                                         &err)) {
260                         printf("bt_io_accept() failed: %s\n", err->message);
261                         g_clear_error(&err);
262                         io_data_unref(data);
263                         return;
264                 }
265         } else {
266                 gint seconds = (data->reject > 0) ?
267                                                 data->reject : data->accept;
268                 g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, seconds,
269                                         confirm_timeout, data,
270                                         (GDestroyNotify) io_data_unref);
271         }
272 }
273
274 static void l2cap_connect(const char *src, const char *dst, uint16_t psm,
275                                                 uint16_t cid, gint disconn,
276                                                 gint sec, gint prio)
277 {
278         struct io_data *data;
279         GError *err = NULL;
280
281         printf("Connecting to %s L2CAP PSM %u\n", dst, psm);
282
283         data = io_data_new(NULL, BT_IO_L2CAP, -1, disconn, -1);
284
285         if (src)
286                 data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
287                                                 (GDestroyNotify) io_data_unref,
288                                                 &err,
289                                                 BT_IO_OPT_SOURCE, src,
290                                                 BT_IO_OPT_DEST, dst,
291                                                 BT_IO_OPT_PSM, psm,
292                                                 BT_IO_OPT_CID, cid,
293                                                 BT_IO_OPT_SEC_LEVEL, sec,
294                                                 BT_IO_OPT_PRIORITY, prio,
295                                                 BT_IO_OPT_INVALID);
296         else
297                 data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
298                                                 (GDestroyNotify) io_data_unref,
299                                                 &err,
300                                                 BT_IO_OPT_DEST, dst,
301                                                 BT_IO_OPT_PSM, psm,
302                                                 BT_IO_OPT_CID, cid,
303                                                 BT_IO_OPT_SEC_LEVEL, sec,
304                                                 BT_IO_OPT_PRIORITY, prio,
305                                                 BT_IO_OPT_INVALID);
306
307         if (!data->io) {
308                 printf("Connecting to %s failed: %s\n", dst, err->message);
309                 g_error_free(err);
310                 exit(EXIT_FAILURE);
311         }
312 }
313
314 static void l2cap_listen(const char *src, uint16_t psm, gint defer,
315                                 gint reject, gint disconn, gint accept,
316                                 gint sec, gboolean master)
317 {
318         struct io_data *data;
319         BtIOConnect conn;
320         BtIOConfirm cfm;
321         GIOChannel *l2_srv;
322         GError *err = NULL;
323
324         if (defer) {
325                 conn = NULL;
326                 cfm = confirm_cb;
327         } else {
328                 conn = connect_cb;
329                 cfm = NULL;
330         }
331
332         printf("Listening on L2CAP PSM %u\n", psm);
333
334         data = io_data_new(NULL, BT_IO_L2CAP, reject, disconn, accept);
335
336         if (src)
337                 l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
338                                         data, (GDestroyNotify) io_data_unref,
339                                         &err,
340                                         BT_IO_OPT_SOURCE, src,
341                                         BT_IO_OPT_PSM, psm,
342                                         BT_IO_OPT_SEC_LEVEL, sec,
343                                         BT_IO_OPT_MASTER, master,
344                                         BT_IO_OPT_INVALID);
345         else
346                 l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
347                                         data, (GDestroyNotify) io_data_unref,
348                                         &err,
349                                         BT_IO_OPT_PSM, psm,
350                                         BT_IO_OPT_SEC_LEVEL, sec,
351                                         BT_IO_OPT_MASTER, master,
352                                         BT_IO_OPT_INVALID);
353
354         if (!l2_srv) {
355                 printf("Listening failed: %s\n", err->message);
356                 g_error_free(err);
357                 exit(EXIT_FAILURE);
358         }
359
360         g_io_channel_unref(l2_srv);
361 }
362
363 static void rfcomm_connect(const char *src, const char *dst, uint8_t ch,
364                                                 gint disconn, gint sec)
365 {
366         struct io_data *data;
367         GError *err = NULL;
368
369         printf("Connecting to %s RFCOMM channel %u\n", dst, ch);
370
371         data = io_data_new(NULL, BT_IO_RFCOMM, -1, disconn, -1);
372
373         if (src)
374                 data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
375                                                 (GDestroyNotify) io_data_unref,
376                                                 &err,
377                                                 BT_IO_OPT_SOURCE, src,
378                                                 BT_IO_OPT_DEST, dst,
379                                                 BT_IO_OPT_CHANNEL, ch,
380                                                 BT_IO_OPT_SEC_LEVEL, sec,
381                                                 BT_IO_OPT_INVALID);
382         else
383                 data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
384                                                 (GDestroyNotify) io_data_unref,
385                                                 &err,
386                                                 BT_IO_OPT_DEST, dst,
387                                                 BT_IO_OPT_CHANNEL, ch,
388                                                 BT_IO_OPT_SEC_LEVEL, sec,
389                                                 BT_IO_OPT_INVALID);
390
391         if (!data->io) {
392                 printf("Connecting to %s failed: %s\n", dst, err->message);
393                 g_error_free(err);
394                 exit(EXIT_FAILURE);
395         }
396 }
397
398 static void rfcomm_listen(const char *src, uint8_t ch, gboolean defer,
399                                 gint reject, gint disconn, gint accept,
400                                 gint sec, gboolean master)
401 {
402         struct io_data *data;
403         BtIOConnect conn;
404         BtIOConfirm cfm;
405         GIOChannel *rc_srv;
406         GError *err = NULL;
407
408         if (defer) {
409                 conn = NULL;
410                 cfm = confirm_cb;
411         } else {
412                 conn = connect_cb;
413                 cfm = NULL;
414         }
415
416         data = io_data_new(NULL, BT_IO_RFCOMM, reject, disconn, accept);
417
418         if (src)
419                 rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
420                                         data, (GDestroyNotify) io_data_unref,
421                                         &err,
422                                         BT_IO_OPT_SOURCE, src,
423                                         BT_IO_OPT_CHANNEL, ch,
424                                         BT_IO_OPT_SEC_LEVEL, sec,
425                                         BT_IO_OPT_MASTER, master,
426                                         BT_IO_OPT_INVALID);
427         else
428                 rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
429                                         data, (GDestroyNotify) io_data_unref,
430                                         &err,
431                                         BT_IO_OPT_CHANNEL, ch,
432                                         BT_IO_OPT_SEC_LEVEL, sec,
433                                         BT_IO_OPT_MASTER, master,
434                                         BT_IO_OPT_INVALID);
435
436         if (!rc_srv) {
437                 printf("Listening failed: %s\n", err->message);
438                 g_error_free(err);
439                 exit(EXIT_FAILURE);
440         }
441
442         bt_io_get(rc_srv, BT_IO_RFCOMM, &err,
443                         BT_IO_OPT_CHANNEL, &ch,
444                         BT_IO_OPT_INVALID);
445
446         printf("Listening on RFCOMM channel %u\n", ch);
447
448         g_io_channel_unref(rc_srv);
449 }
450
451 static void sco_connect(const char *src, const char *dst, gint disconn)
452 {
453         struct io_data *data;
454         GError *err = NULL;
455
456         printf("Connecting SCO to %s\n", dst);
457
458         data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
459
460         if (src)
461                 data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
462                                                 (GDestroyNotify) io_data_unref,
463                                                 &err,
464                                                 BT_IO_OPT_SOURCE, src,
465                                                 BT_IO_OPT_DEST, dst,
466                                                 BT_IO_OPT_INVALID);
467         else
468                 data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
469                                                 (GDestroyNotify) io_data_unref,
470                                                 &err,
471                                                 BT_IO_OPT_DEST, dst,
472                                                 BT_IO_OPT_INVALID);
473
474         if (!data->io) {
475                 printf("Connecting to %s failed: %s\n", dst, err->message);
476                 g_error_free(err);
477                 exit(EXIT_FAILURE);
478         }
479 }
480
481 static void sco_listen(const char *src, gint disconn)
482 {
483         struct io_data *data;
484         GIOChannel *sco_srv;
485         GError *err = NULL;
486
487         printf("Listening for SCO connections\n");
488
489         data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
490
491         if (src)
492                 sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
493                                         data, (GDestroyNotify) io_data_unref,
494                                         &err,
495                                         BT_IO_OPT_SOURCE, src,
496                                         BT_IO_OPT_INVALID);
497         else
498                 sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
499                                         data, (GDestroyNotify) io_data_unref,
500                                         &err, BT_IO_OPT_INVALID);
501
502         if (!sco_srv) {
503                 printf("Listening failed: %s\n", err->message);
504                 g_error_free(err);
505                 exit(EXIT_FAILURE);
506         }
507
508         g_io_channel_unref(sco_srv);
509 }
510
511 static gint opt_channel = -1;
512 static gint opt_psm = 0;
513 static gboolean opt_sco = FALSE;
514 static gboolean opt_defer = FALSE;
515 static char *opt_dev = NULL;
516 static gint opt_reject = -1;
517 static gint opt_disconn = -1;
518 static gint opt_accept = DEFAULT_ACCEPT_TIMEOUT;
519 static gint opt_sec = 0;
520 static gboolean opt_master = FALSE;
521 static gint opt_priority = 0;
522 static gint opt_cid = 0;
523
524 static GMainLoop *main_loop;
525
526 static GOptionEntry options[] = {
527         { "channel", 'c', 0, G_OPTION_ARG_INT, &opt_channel,
528                                 "RFCOMM channel" },
529         { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
530                                 "L2CAP PSM" },
531         { "cid", 'j', 0, G_OPTION_ARG_INT, &opt_cid,
532                                 "L2CAP CID" },
533         { "sco", 's', 0, G_OPTION_ARG_NONE, &opt_sco,
534                                 "Use SCO" },
535         { "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer,
536                                 "Use DEFER_SETUP for incoming connections" },
537         { "sec-level", 'S', 0, G_OPTION_ARG_INT, &opt_sec,
538                                 "Security level" },
539         { "update-sec-level", 'U', 0, G_OPTION_ARG_INT, &opt_update_sec,
540                                 "Update security level" },
541         { "dev", 'i', 0, G_OPTION_ARG_STRING, &opt_dev,
542                                 "Which HCI device to use" },
543         { "reject", 'r', 0, G_OPTION_ARG_INT, &opt_reject,
544                                 "Reject connection after N seconds" },
545         { "disconnect", 'D', 0, G_OPTION_ARG_INT, &opt_disconn,
546                                 "Disconnect connection after N seconds" },
547         { "accept", 'a', 0, G_OPTION_ARG_INT, &opt_accept,
548                                 "Accept connection after N seconds" },
549         { "master", 'm', 0, G_OPTION_ARG_NONE, &opt_master,
550                                 "Master role switch (incoming connections)" },
551         { "priority", 'P', 0, G_OPTION_ARG_INT, &opt_priority,
552                                 "Transmission priority: Setting a priority "
553                                 "outside the range 0 to 6 requires the"
554                                 "CAP_NET_ADMIN capability." },
555         { NULL },
556 };
557
558 static void sig_term(int sig)
559 {
560         g_main_loop_quit(main_loop);
561 }
562
563 int main(int argc, char *argv[])
564 {
565         GOptionContext *context;
566
567         context = g_option_context_new(NULL);
568         g_option_context_add_main_entries(context, options, NULL);
569
570         if (!g_option_context_parse(context, &argc, &argv, NULL))
571                 exit(EXIT_FAILURE);
572
573         g_option_context_free(context);
574
575         printf("accept=%d, reject=%d, discon=%d, defer=%d, sec=%d,"
576                 " update_sec=%d, prio=%d\n", opt_accept, opt_reject,
577                 opt_disconn, opt_defer, opt_sec, opt_update_sec, opt_priority);
578
579         if (opt_psm || opt_cid) {
580                 if (argc > 1)
581                         l2cap_connect(opt_dev, argv[1], opt_psm, opt_cid,
582                                         opt_disconn, opt_sec, opt_priority);
583                 else
584                         l2cap_listen(opt_dev, opt_psm, opt_defer, opt_reject,
585                                         opt_disconn, opt_accept, opt_sec,
586                                         opt_master);
587         }
588
589         if (opt_channel != -1) {
590                 if (argc > 1)
591                         rfcomm_connect(opt_dev, argv[1], opt_channel,
592                                                         opt_disconn, opt_sec);
593                 else
594                         rfcomm_listen(opt_dev, opt_channel, opt_defer,
595                                         opt_reject, opt_disconn, opt_accept,
596                                         opt_sec, opt_master);
597         }
598
599         if (opt_sco) {
600                 if (argc > 1)
601                         sco_connect(opt_dev, argv[1], opt_disconn);
602                 else
603                         sco_listen(opt_dev, opt_disconn);
604         }
605
606         signal(SIGTERM, sig_term);
607         signal(SIGINT, sig_term);
608
609         main_loop = g_main_loop_new(NULL, FALSE);
610
611         g_main_loop_run(main_loop);
612
613         g_main_loop_unref(main_loop);
614
615         printf("Exiting\n");
616
617         exit(EXIT_SUCCESS);
618 }