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