1 #include "e_system.h"
2 
3 #ifdef HAVE_BLUETOOTH
4 # include <bluetooth/bluetooth.h>
5 # include <bluetooth/l2cap.h>
6 # include <sys/socket.h>
7 # include <sys/select.h>
8 #endif
9 
10 #define MAX_SZ 500
11 
12 typedef struct
13 {
14    char *dev;
15    int timeout, result;
16 } Action;
17 
18 static void
_l2ping_free(Action * a)19 _l2ping_free(Action *a)
20 {
21    free(a->dev);
22    free(a);
23 }
24 
25 #ifdef HAVE_BLUETOOTH
26 static void
_l2ping_l2addr_init(struct sockaddr_l2 * ad)27 _l2ping_l2addr_init(struct sockaddr_l2 *ad)
28 {
29    memset(ad, 0, sizeof(*ad));
30    ad->l2_family = AF_BLUETOOTH;
31 }
32 
33 # define SETUP_FDSET(rfds, wfds, exfds) \
34    FD_ZERO(&rfds); \
35    FD_ZERO(&wfds); \
36    FD_ZERO(&exfds);
37 # define SETUP_TIMEOUT(tv, a) \
38    tv.tv_sec = a->timeout / 1000; \
39    tv.tv_usec = (a->timeout % 1000) * 1000;
40 #endif
41 
42 static void
_cb_l2ping(void * data EINA_UNUSED,Ecore_Thread * th EINA_UNUSED)43 _cb_l2ping(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED)
44 {
45 #ifdef HAVE_BLUETOOTH
46    Action *a = data;
47    char buf1[L2CAP_CMD_HDR_SIZE + MAX_SZ], buf2[L2CAP_CMD_HDR_SIZE + MAX_SZ];
48    bdaddr_t ba;
49    l2cap_cmd_hdr *cmd;
50    struct sockaddr_l2 ad;
51    double start;
52    int fd, err, size, i;
53    fd_set rfds, wfds, exfds;
54    struct timeval tv;
55 
56    fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
57    if (fd < 0)
58      {
59         ERR("l2ping: Can't create socket\n");
60         return;
61      }
62 
63    // bind to local address
64    _l2ping_l2addr_init(&ad);
65    bacpy(&ba, BDADDR_ANY);
66    bacpy(&ad.l2_bdaddr, &ba);
67    if (bind(fd, (struct sockaddr *)&ad, sizeof(ad)) < 0)
68      {
69         ERR("l2ping: Can't bind socket\n");
70         goto err;
71      }
72 
73    // connect to remote device
74    _l2ping_l2addr_init(&ad);
75    str2ba(a->dev, &ad.l2_bdaddr);
76    if (connect(fd, (struct sockaddr *)&ad, sizeof(ad)) < 0)
77      {
78         ERR("l2ping: Can't connect socket to [%s]\n", a->dev);
79         goto err;
80      }
81 
82    SETUP_FDSET(rfds, wfds, exfds);
83    FD_SET(fd, &wfds);
84    SETUP_TIMEOUT(tv, a);
85    start = ecore_time_get();
86    err = select(fd + 1, &rfds, &wfds, &exfds, &tv);
87    if (err == 0)
88      {
89         ERR("l2ping: Connect timeout [%s]\n", a->dev);
90         goto err;
91      }
92 
93    // adjust timeout by how long we waited to connect
94    a->timeout -= (ecore_time_get() - start) * 1000;
95    if (a->timeout < 1) a->timeout = 1;
96 
97    size = 44; // use std 44 byte ping size, but no more than MAX_SZ
98    cmd = (l2cap_cmd_hdr *)buf1;
99    cmd->ident = 200;
100    cmd->len = htobs(size);
101    cmd->code = L2CAP_ECHO_REQ;
102    // init buffer with some content
103    // ABCDEF....WXYZABCEF... up to "size" chars
104    for (i = 0; i < size; i++) buf1[L2CAP_CMD_HDR_SIZE + i] = 'A' + (i % 26);
105 
106    // get our time just before a send
107    start = ecore_time_get();
108    // send the ping
109    if (send(fd, buf1, L2CAP_CMD_HDR_SIZE + size, 0) <= 0)
110      {
111         ERR("l2ping: Send to [%s] failed\n", a->dev);
112         goto err;
113      }
114 
115    // wait for the reply to this ping
116    for (;;)
117      {
118         SETUP_FDSET(rfds, wfds, exfds);
119         FD_SET(fd, &rfds);
120         SETUP_TIMEOUT(tv, a);
121         err = select(fd + 1, &rfds, &wfds, &exfds, &tv);
122         if (err == 0)
123           {
124              ERR("l2ping: Select timeout [%s]\n", a->dev);
125              goto err;
126           }
127         else if (err < 0)
128           {
129              ERR("l2ping: Select for [%s] failed\n", a->dev);
130              goto err;
131           }
132 
133         err = recv(fd, buf2, L2CAP_CMD_HDR_SIZE + size, 0);
134         if (err == 0)
135           {
136              ERR("l2ping: Disconnect %s\n", a->dev);
137              goto err;
138           }
139         else if (err < 0)
140           {
141              ERR("l2ping: Recv [%s] failed\n", a->dev);
142              goto err;
143           }
144 
145         cmd = (l2cap_cmd_hdr *)buf2;
146         cmd->len = btohs(cmd->len);
147         // we only want the 200 ident response packets
148         if (cmd->ident != 200) continue;
149         if (cmd->code == L2CAP_COMMAND_REJ)
150           {
151              ERR("l2ping: [%s] doesn't do echo\n", a->dev);
152              goto err;
153           }
154         if (cmd->len != size)
155           {
156              ERR("l2ping: Size %i echo for [%s] does not match %i\n",
157                  (int)cmd->len, a->dev, (int)size);
158              goto err;
159           }
160         if (memcmp(buf1 + L2CAP_CMD_HDR_SIZE, buf2 + L2CAP_CMD_HDR_SIZE,
161                    size) != 0)
162           {
163              ERR("l2ping: Echo response from [%s] does not match sent data\n",
164                  a->dev);
165              goto err;
166           }
167         break;
168      }
169    // time it took to send and get our response
170    a->result = (ecore_time_get() - start) * 1000.0;
171 err:
172    close(fd);
173 #else
174    ERR("l2ping: Bluetooth support not compiled in\n");
175 #endif
176 }
177 
178 static void
_cb_l2ping_end(void * data,Ecore_Thread * th EINA_UNUSED)179 _cb_l2ping_end(void *data, Ecore_Thread *th EINA_UNUSED)
180 {
181    Action *a = data;
182    e_system_inout_command_send("l2ping-ping", "%s %i", a->dev, a->result);
183    _l2ping_free(a);
184 }
185 
186 static void
_cb_l2ping_cancel(void * data,Ecore_Thread * th EINA_UNUSED)187 _cb_l2ping_cancel(void *data, Ecore_Thread *th EINA_UNUSED)
188 {
189    Action *a = data;
190    e_system_inout_command_send("l2ping-ping", "%s %i", a->dev, -1);
191    _l2ping_free(a);
192 }
193 
194 static void
_cb_l2ping_ping(void * data EINA_UNUSED,const char * params)195 _cb_l2ping_ping(void *data EINA_UNUSED, const char *params)
196 {
197    // ADDR TIMEOUT_MS
198    int timeout = 1;
199    char dev[1024];
200    Action *a;
201 
202    if (! params) return;
203    if (sscanf(params, "%1023s %i", dev, &timeout) != 2) return;
204    if (timeout < 1) timeout = 1;
205    else if (timeout > (1000 * 600)) timeout = 1000 * 600;
206    a = calloc(1, sizeof(Action));
207    if (!a) return;
208    a->dev = strdup(dev);
209    if (!a->dev) goto err;
210    a->timeout = timeout;
211    a->result = -1;
212    if (ecore_thread_feedback_run(_cb_l2ping, NULL,
213                                  _cb_l2ping_end, _cb_l2ping_cancel,
214                                  a, EINA_TRUE))
215      return;
216 err:
217    _l2ping_free(a);
218 }
219 
220 void
e_system_l2ping_init(void)221 e_system_l2ping_init(void)
222 {
223    e_system_inout_command_register("l2ping-ping", _cb_l2ping_ping, NULL);
224 }
225 
226 void
e_system_l2ping_shutdown(void)227 e_system_l2ping_shutdown(void)
228 {
229    // only shutdown things we really have to - no need to free mem etc.
230 }
231