Linux listener prototype
[pulsecounter.git] / linux / pulsecounter.c
1 #include <errno.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <stdbool.h>
5 #include <time.h>
6
7 #include <glib.h>
8
9 #include <lib/bluetooth.h>
10 #include <lib/hci.h>
11 #include <lib/hci_lib.h>
12 #include <lib/sdp.h>
13 #include "lib/uuid.h"
14
15 #include "src/shared/util.h"
16 #include "attrib/att.h"
17 #include "btio/btio.h"
18 #include "attrib/gattrib.h"
19 #include "attrib/gatt.h"
20
21 GIOChannel *gatt_connect(const char *src, const char *dst,
22                         const char *dst_type, const char *sec_level,
23                         int psm, int mtu, BtIOConnect connect_cb,
24                         GError **gerr);
25
26 static char *opt_src = NULL;
27 static char *opt_dst = NULL;
28 static char *opt_dst_type = NULL;
29 static int opt_mtu = 0;
30 static int opt_psm = 0;
31 static char *opt_sec_level = NULL;
32
33 static GMainLoop *event_loop;
34
35 static GOptionEntry options[] = {
36         { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
37                 "Specify local adapter interface", "hciX" },
38         { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
39                 "Specify remote Bluetooth address", "MAC" },
40         { "addr-type", 't', 0, G_OPTION_ARG_STRING, &opt_dst_type,
41                 "Set LE address type. Default: public", "[public | random]"},
42         { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
43                 "Specify the MTU size", "MTU" },
44         { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
45                 "Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
46         { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
47                 "Set security level. Default: low", "[low | medium | high]"},
48         { NULL },
49 };
50
51 static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
52                                 gpointer user_data)
53 {
54         g_io_channel_shutdown(chan, FALSE, NULL);
55         g_io_channel_unref(chan);
56         g_main_loop_quit(event_loop);
57         g_print("channel_watcher cleared channel and exiting\n");
58         return FALSE;
59 }
60
61 static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
62 {
63         GAttrib *attrib = user_data;
64         uint8_t *opdu;
65         uint16_t handle, i, olen = 0;
66         size_t plen;
67         time_t t;
68         struct tm tm;
69         char tstr[64];
70
71         t = time(NULL);
72         (void)gmtime_r(&t, &tm);
73         (void)strftime(tstr, sizeof(tstr), "%Y-%m-%d %H:%M:%S", &tm);
74         handle = bt_get_le16(&pdu[1]);
75         g_print("%s ev %02x hd 0x%04x value: ", tstr, pdu[0], handle);
76         for (i = 3; i < len; i++)
77                 g_print("%02x ", pdu[i]);
78         g_print("\n");
79 }
80
81 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
82 {
83         GAttrib *attrib;
84         uint16_t mtu;
85         uint16_t cid;
86         GError *gerr = NULL;
87
88         if (err) {
89                 g_printerr("%s\n", err->message);
90                 g_main_loop_quit(event_loop);
91         }
92         bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu,
93                                 BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
94         if (gerr) {
95                 g_printerr("Can't detect MTU, using default: %s",
96                                                         gerr->message);
97                 g_error_free(gerr);
98                 mtu = ATT_DEFAULT_LE_MTU;
99         }
100         if (cid == ATT_CID)
101                 mtu = ATT_DEFAULT_LE_MTU;
102         attrib = g_attrib_new(io, mtu, false);
103         g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
104                                                 events_handler, attrib, NULL);
105         g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
106                                                 events_handler, attrib, NULL);
107         g_print("connect_cb registered events_handler and exiting\n");
108 }
109
110 int main(int argc, char *argv[])
111 {
112         GOptionContext *context;
113         GError *gerr = NULL;
114         GIOChannel *chan;
115         gboolean got_error = FALSE;
116
117         opt_dst_type = g_strdup("public");
118         opt_sec_level = g_strdup("low");
119         context = g_option_context_new(NULL);
120         g_option_context_add_main_entries(context, options, NULL);
121         if (!g_option_context_parse(context, &argc, &argv, &gerr)) {
122                 g_printerr("%s\n", gerr->message);
123                 g_clear_error(&gerr);
124                 got_error = TRUE;
125                 goto done;
126         }
127         chan = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
128                                 opt_psm, opt_mtu, connect_cb, &gerr);
129         if (chan == NULL) {
130                 g_printerr("%s\n", gerr->message);
131                 g_clear_error(&gerr);
132                 got_error = TRUE;
133                 goto done;
134         } else {
135                 g_io_add_watch(chan, G_IO_HUP, channel_watcher, NULL);
136         }
137
138         event_loop = g_main_loop_new(NULL, FALSE);
139         g_main_loop_run(event_loop);
140         g_main_loop_unref(event_loop);
141
142 done:
143         g_option_context_free(context);
144         g_free(opt_src);
145         g_free(opt_dst);
146         g_free(opt_sec_level);
147         if (got_error)
148                 exit(EXIT_FAILURE);
149         else
150                 exit(EXIT_SUCCESS);
151 }