10 #include <lib/bluetooth.h>
12 #include <lib/hci_lib.h>
16 #include "src/shared/util.h"
17 #include "attrib/att.h"
18 #include "btio/btio.h"
19 #include "attrib/gattrib.h"
20 #include "attrib/gatt.h"
24 GIOChannel *gatt_connect(const char *src, const char *dst,
25 const char *dst_type, const char *sec_level,
26 int psm, int mtu, BtIOConnect connect_cb,
29 static char *opt_src = NULL;
30 static char *opt_dst = NULL;
31 static char *opt_dst_type = NULL;
32 static int opt_mtu = 0;
33 static int opt_psm = 0;
34 static char *opt_sec_level = NULL;
35 static char *opt_dbconffile = NULL;
36 static gboolean opt_daemon = FALSE;
38 static GMainLoop *event_loop;
40 static GOptionEntry options[] = {
41 { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
42 "Specify local adapter interface", "hciX" },
43 { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
44 "Specify remote Bluetooth address", "MAC" },
45 { "addr-type", 't', 0, G_OPTION_ARG_STRING, &opt_dst_type,
46 "Set LE address type. Default: public", "[public | random]"},
47 { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
48 "Specify the MTU size", "MTU" },
49 { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
50 "Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
51 { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
52 "Set security level. Default: low", "[low | medium | high]"},
53 { "dbconfig", 'c', 0, G_OPTION_ARG_FILENAME, &opt_dbconffile,
54 "Specify file name with database configuration", "cfile"},
55 { "daemon", 'd', 0, G_OPTION_ARG_NONE, &opt_daemon,
56 "Specify file name with database configuration", "cfile"},
60 void local_log_handler(const gchar *log_domain, GLogLevelFlags log_level,
61 const gchar *message, gpointer log_context)
66 case G_LOG_LEVEL_CRITICAL: syslog_level = LOG_CRIT; break;
67 case G_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break;
68 case G_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
69 case G_LOG_LEVEL_MESSAGE: syslog_level = LOG_NOTICE; break;
70 case G_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break;
71 case G_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
72 default: syslog_level = LOG_INFO;
74 if (!log_domain || (log_domain[0] == '\0'))
75 syslog(syslog_level, "%s", message);
77 syslog(syslog_level, "%s: %s", log_domain, message);
80 static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
83 g_io_channel_shutdown(chan, FALSE, NULL);
84 g_io_channel_unref(chan);
85 g_main_loop_quit(event_loop);
86 g_info("channel_watcher cleared channel and exiting");
90 static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
92 GAttrib *attrib = user_data;
97 handle = bt_get_le16(&pdu[1]);
99 if ((pdu[0] == 0x1b) && (handle == 0x0012) && (len == 9)) {
100 uint32_t val = bt_get_le32(&pdu[5]);
102 if ((which == 1) || (which == 2)) {
103 g_debug("store: \"%hhu,%u\"\n", which, val);
104 if (dbstore(which, val))
105 g_warning("error storing \"%hhu,%u\"\n",
108 g_debug("jitter: \"%hhu,%u\"\n", which, val);
118 (void)gmtime_r(&t, &tm);
119 (void)strftime(tstr, sizeof(tstr), "%Y-%m-%d %H:%M:%S", &tm);
120 for (i = 3; (i < len) && ((i-3) < (sizeof(buf)/3)); i++)
121 sprintf(buf+strlen(buf), " %02x ", pdu[i]);
122 g_warning("%s ev %02x hd 0x%04x value: %s",
123 tstr, pdu[0], handle, buf);
127 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
135 g_warning("%s", err->message);
136 g_main_loop_quit(event_loop);
138 bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu,
139 BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
141 g_warning("Can't detect MTU, using default: %s",
144 mtu = ATT_DEFAULT_LE_MTU;
147 mtu = ATT_DEFAULT_LE_MTU;
148 attrib = g_attrib_new(io, mtu, false);
149 g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
150 events_handler, attrib, NULL);
151 g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
152 events_handler, attrib, NULL);
153 g_info("connect_cb registered events_handler and exiting\n");
156 int main(int argc, char *argv[])
158 GOptionContext *context;
161 gboolean got_error = FALSE;
163 g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
164 | G_LOG_FLAG_RECURSION, local_log_handler, NULL);
165 opt_dst_type = g_strdup("public");
166 opt_sec_level = g_strdup("low");
167 opt_dbconffile = g_strdup("/etc/pulsecounter.db");
168 context = g_option_context_new(NULL);
169 g_option_context_add_main_entries(context, options, NULL);
170 if (!g_option_context_parse(context, &argc, &argv, &gerr)) {
171 g_error("%s", gerr->message);
172 g_clear_error(&gerr);
177 g_error("Destination MAC address must be specified");
181 if (dbconfig(opt_dbconffile)) {
182 g_error("Could not parse database configuration file");
186 if (opt_daemon) daemon(0, 0);
188 chan = gatt_connect(opt_src, opt_dst, opt_dst_type,
189 opt_sec_level, opt_psm, opt_mtu, connect_cb, &gerr);
191 g_io_add_watch(chan, G_IO_HUP, channel_watcher, NULL);
192 event_loop = g_main_loop_new(NULL, FALSE);
193 g_main_loop_run(event_loop);
194 g_main_loop_unref(event_loop);
196 g_warning("%s", gerr->message);
197 g_clear_error(&gerr);
204 g_option_context_free(context);
207 g_free(opt_sec_level);
208 g_free(opt_dbconffile);