separate dbstore module
[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 #include "dbstore.h"
22
23 GIOChannel *gatt_connect(const char *src, const char *dst,
24                         const char *dst_type, const char *sec_level,
25                         int psm, int mtu, BtIOConnect connect_cb,
26                         GError **gerr);
27
28 static char *opt_src = NULL;
29 static char *opt_dst = NULL;
30 static char *opt_dst_type = NULL;
31 static int opt_mtu = 0;
32 static int opt_psm = 0;
33 static char *opt_sec_level = NULL;
34
35 static GMainLoop *event_loop;
36
37 static GOptionEntry options[] = {
38         { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
39                 "Specify local adapter interface", "hciX" },
40         { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
41                 "Specify remote Bluetooth address", "MAC" },
42         { "addr-type", 't', 0, G_OPTION_ARG_STRING, &opt_dst_type,
43                 "Set LE address type. Default: public", "[public | random]"},
44         { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
45                 "Specify the MTU size", "MTU" },
46         { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
47                 "Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
48         { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
49                 "Set security level. Default: low", "[low | medium | high]"},
50         { NULL },
51 };
52
53 static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
54                                 gpointer user_data)
55 {
56         g_io_channel_shutdown(chan, FALSE, NULL);
57         g_io_channel_unref(chan);
58         g_main_loop_quit(event_loop);
59         g_info("channel_watcher cleared channel and exiting");
60         return FALSE;
61 }
62
63 static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
64 {
65         GAttrib *attrib = user_data;
66         uint8_t *opdu;
67         uint8_t which;
68         uint16_t handle;
69
70         handle = bt_get_le16(&pdu[1]);
71         which = pdu[3];
72         if ((pdu[0] == 0x1b) && (handle == 0x0012) && (len == 9) &&
73             ((which == 1) || (which == 2))) {
74                 uint32_t val = bt_get_le32(&pdu[5]);
75                 g_debug("store: \"%hhu,%u\"\n", which, val);
76                 if (dbstore(which, val))
77                         g_warning("error storing \"%hhu,%u\"\n", which, val);
78         } else {
79                 time_t t;
80                 int i;
81                 struct tm tm;
82                 char buf[64];
83                 char tstr[64];
84
85                 t = time(NULL);
86                 (void)gmtime_r(&t, &tm);
87                 (void)strftime(tstr, sizeof(tstr), "%Y-%m-%d %H:%M:%S", &tm);
88                 for (i = 3; (i < len) && ((i-3) < (sizeof(buf)/3)); i++)
89                         sprintf(buf+strlen(buf), " %02x ", pdu[i]);
90                 g_warning("%s ev %02x hd 0x%04x value: %s",
91                         tstr, pdu[0], handle, buf);
92         }
93 }
94
95 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
96 {
97         GAttrib *attrib;
98         uint16_t mtu;
99         uint16_t cid;
100         GError *gerr = NULL;
101
102         if (err) {
103                 g_warning("%s", err->message);
104                 g_main_loop_quit(event_loop);
105         }
106         bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu,
107                                 BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
108         if (gerr) {
109                 g_warning("Can't detect MTU, using default: %s",
110                                                         gerr->message);
111                 g_error_free(gerr);
112                 mtu = ATT_DEFAULT_LE_MTU;
113         }
114         if (cid == ATT_CID)
115                 mtu = ATT_DEFAULT_LE_MTU;
116         attrib = g_attrib_new(io, mtu, false);
117         g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
118                                                 events_handler, attrib, NULL);
119         g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
120                                                 events_handler, attrib, NULL);
121         g_info("connect_cb registered events_handler and exiting\n");
122 }
123
124 int main(int argc, char *argv[])
125 {
126         GOptionContext *context;
127         GError *gerr = NULL;
128         GIOChannel *chan;
129         gboolean got_error = FALSE;
130
131         g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
132                           | G_LOG_FLAG_RECURSION, g_log_default_handler, NULL);
133         opt_dst_type = g_strdup("public");
134         opt_sec_level = g_strdup("low");
135         context = g_option_context_new(NULL);
136         g_option_context_add_main_entries(context, options, NULL);
137         if (!g_option_context_parse(context, &argc, &argv, &gerr)) {
138                 g_error("%s", gerr->message);
139                 g_clear_error(&gerr);
140                 got_error = TRUE;
141                 goto done;
142         }
143         while (1) {
144                 chan = gatt_connect(opt_src, opt_dst, opt_dst_type,
145                         opt_sec_level, opt_psm, opt_mtu, connect_cb, &gerr);
146                 if (chan) {
147                         g_io_add_watch(chan, G_IO_HUP, channel_watcher, NULL);
148                         event_loop = g_main_loop_new(NULL, FALSE);
149                         g_main_loop_run(event_loop);
150                         g_main_loop_unref(event_loop);
151                 } else {
152                         g_warning("%s", gerr->message);
153                         g_clear_error(&gerr);
154                         got_error = TRUE;
155                 }
156                 sleep(10);
157         }
158
159 done:
160         g_option_context_free(context);
161         g_free(opt_src);
162         g_free(opt_dst);
163         g_free(opt_sec_level);
164         if (got_error)
165                 exit(EXIT_FAILURE);
166         else
167                 exit(EXIT_SUCCESS);
168 }