1 /*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
6 * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <getopt.h>
35 #include <syslog.h>
36 #include <signal.h>
37 #include <sys/time.h>
38 #include <sys/socket.h>
39
40 #include <bluetooth/bluetooth.h>
41 #include <bluetooth/sco.h>
42
43 /* Test modes */
44 enum {
45 SEND,
46 RECV,
47 RECONNECT,
48 MULTY,
49 DUMP,
50 CONNECT
51 };
52
53 static unsigned char *buf;
54
55 /* Default data size */
56 static long data_size = 672;
57
58 /* Default packet type */
59 static uint16_t pkt_type = 0;
60
61 static bdaddr_t bdaddr;
62
tv2fl(struct timeval tv)63 static float tv2fl(struct timeval tv)
64 {
65 return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
66 }
67
do_connect(char * svr)68 static int do_connect(char *svr)
69 {
70 struct sockaddr_sco addr;
71 struct sco_conninfo conn;
72 socklen_t optlen;
73 int sk;
74
75 /* Create socket */
76 sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
77 if (sk < 0) {
78 syslog(LOG_ERR, "Can't create socket: %s (%d)",
79 strerror(errno), errno);
80 return -1;
81 }
82
83 /* Bind to local address */
84 memset(&addr, 0, sizeof(addr));
85 addr.sco_family = AF_BLUETOOTH;
86 bacpy(&addr.sco_bdaddr, &bdaddr);
87
88 if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
89 syslog(LOG_ERR, "Can't bind socket: %s (%d)",
90 strerror(errno), errno);
91 goto error;
92 }
93
94 /* Connect to remote device */
95 memset(&addr, 0, sizeof(addr));
96 addr.sco_family = AF_BLUETOOTH;
97 addr.sco_pkt_type = pkt_type;
98 str2ba(svr, &addr.sco_bdaddr);
99
100 if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
101 syslog(LOG_ERR, "Can't connect: %s (%d)",
102 strerror(errno), errno);
103 goto error;
104 }
105
106 /* Get connection information */
107 memset(&conn, 0, sizeof(conn));
108 optlen = sizeof(conn);
109
110 if (getsockopt(sk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) {
111 syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)",
112 strerror(errno), errno);
113 goto error;
114 }
115
116 syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x]",
117 conn.hci_handle,
118 conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
119
120 return sk;
121
122 error:
123 close(sk);
124 return -1;
125 }
126
do_listen(void (* handler)(int sk))127 static void do_listen(void (*handler)(int sk))
128 {
129 struct sockaddr_sco addr;
130 struct sco_conninfo conn;
131 socklen_t optlen;
132 int sk, nsk;
133 char ba[18];
134
135 /* Create socket */
136 sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
137 if (sk < 0) {
138 syslog(LOG_ERR, "Can't create socket: %s (%d)",
139 strerror(errno), errno);
140 exit(1);
141 }
142
143 /* Bind to local address */
144 memset(&addr, 0, sizeof(addr));
145 addr.sco_family = AF_BLUETOOTH;
146 addr.sco_pkt_type = pkt_type;
147 bacpy(&addr.sco_bdaddr, &bdaddr);
148
149 if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
150 syslog(LOG_ERR, "Can't bind socket: %s (%d)",
151 strerror(errno), errno);
152 goto error;
153 }
154
155 /* Listen for connections */
156 if (listen(sk, 10)) {
157 syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
158 strerror(errno), errno);
159 goto error;
160 }
161
162 syslog(LOG_INFO,"Waiting for connection ...");
163
164 while (1) {
165 memset(&addr, 0, sizeof(addr));
166 optlen = sizeof(addr);
167
168 nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
169 if (nsk < 0) {
170 syslog(LOG_ERR,"Accept failed: %s (%d)",
171 strerror(errno), errno);
172 goto error;
173 }
174 if (fork()) {
175 /* Parent */
176 close(nsk);
177 continue;
178 }
179 /* Child */
180 close(sk);
181
182 /* Get connection information */
183 memset(&conn, 0, sizeof(conn));
184 optlen = sizeof(conn);
185
186 if (getsockopt(nsk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) {
187 syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)",
188 strerror(errno), errno);
189 close(nsk);
190 goto error;
191 }
192
193 ba2str(&addr.sco_bdaddr, ba);
194 syslog(LOG_INFO, "Connect from %s [handle %d, class 0x%02x%02x%02x]",
195 ba, conn.hci_handle,
196 conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
197
198 handler(nsk);
199
200 syslog(LOG_INFO, "Disconnect");
201 exit(0);
202 }
203
204 return;
205
206 error:
207 close(sk);
208 exit(1);
209 }
210
dump_mode(int sk)211 static void dump_mode(int sk)
212 {
213 int len;
214
215 syslog(LOG_INFO,"Receiving ...");
216 while ((len = read(sk, buf, data_size)) > 0)
217 syslog(LOG_INFO, "Recevied %d bytes", len);
218 }
219
recv_mode(int sk)220 static void recv_mode(int sk)
221 {
222 struct timeval tv_beg,tv_end,tv_diff;
223 long total;
224
225 syslog(LOG_INFO, "Receiving ...");
226
227 while (1) {
228 gettimeofday(&tv_beg, NULL);
229 total = 0;
230 while (total < data_size) {
231 int r;
232 if ((r = recv(sk, buf, data_size, 0)) <= 0) {
233 if (r < 0)
234 syslog(LOG_ERR, "Read failed: %s (%d)",
235 strerror(errno), errno);
236 return;
237 }
238 total += r;
239 }
240 gettimeofday(&tv_end, NULL);
241
242 timersub(&tv_end, &tv_beg, &tv_diff);
243
244 syslog(LOG_INFO,"%ld bytes in %.2fm speed %.2f kb", total,
245 tv2fl(tv_diff) / 60.0,
246 (float)( total / tv2fl(tv_diff) ) / 1024.0 );
247 }
248 }
249
send_mode(char * svr)250 static void send_mode(char *svr)
251 {
252 struct sco_options so;
253 socklen_t len;
254 uint32_t seq;
255 int i, sk;
256
257 if ((sk = do_connect(svr)) < 0) {
258 syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
259 strerror(errno), errno);
260 exit(1);
261 }
262
263 len = sizeof(so);
264 if (getsockopt(sk, SOL_SCO, SCO_OPTIONS, &so, &len) < 0) {
265 syslog(LOG_ERR, "Can't get SCO options: %s (%d)",
266 strerror(errno), errno);
267 exit(1);
268 }
269
270 syslog(LOG_INFO,"Sending ...");
271
272 for (i = 6; i < so.mtu; i++)
273 buf[i] = 0x7f;
274
275 seq = 0;
276 while (1) {
277 *(uint32_t *) buf = htobl(seq);
278 *(uint16_t *) (buf + 4) = htobs(data_size);
279 seq++;
280
281 if (send(sk, buf, so.mtu, 0) <= 0) {
282 syslog(LOG_ERR, "Send failed: %s (%d)",
283 strerror(errno), errno);
284 exit(1);
285 }
286
287 usleep(1);
288 }
289 }
290
reconnect_mode(char * svr)291 static void reconnect_mode(char *svr)
292 {
293 while (1) {
294 int sk;
295
296 if ((sk = do_connect(svr)) < 0) {
297 syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
298 strerror(errno), errno);
299 exit(1);
300 }
301
302 close(sk);
303
304 sleep(5);
305 }
306 }
307
multy_connect_mode(char * svr)308 static void multy_connect_mode(char *svr)
309 {
310 while (1) {
311 int i, sk;
312
313 for (i = 0; i < 10; i++){
314 if (fork())
315 continue;
316
317 /* Child */
318 sk = do_connect(svr);
319 if (sk < 0) {
320 syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
321 strerror(errno), errno);
322 }
323 close(sk);
324 exit(0);
325 }
326
327 sleep(19);
328 }
329 }
330
usage(void)331 static void usage(void)
332 {
333 printf("scotest - SCO testing\n"
334 "Usage:\n");
335 printf("\tscotest <mode> [-b bytes] [-p pkt_type] [bd_addr]\n");
336 printf("Modes:\n"
337 "\t-d dump (server)\n"
338 "\t-c reconnect (client)\n"
339 "\t-m multiple connects (client)\n"
340 "\t-r receive (server)\n"
341 "\t-s connect and send (client)\n"
342 "\t-n connect and be silent (client)\n");
343 }
344
main(int argc,char * argv[])345 int main(int argc ,char *argv[])
346 {
347 struct sigaction sa;
348 int opt, sk, mode = RECV;
349
350 while ((opt=getopt(argc,argv,"rdscmnb:p:")) != EOF) {
351 switch(opt) {
352 case 'r':
353 mode = RECV;
354 break;
355
356 case 's':
357 mode = SEND;
358 break;
359
360 case 'd':
361 mode = DUMP;
362 break;
363
364 case 'c':
365 mode = RECONNECT;
366 break;
367
368 case 'm':
369 mode = MULTY;
370 break;
371
372 case 'n':
373 mode = CONNECT;
374 break;
375
376 case 'b':
377 data_size = atoi(optarg);
378 break;
379
380 case 'p':
381 if (sscanf(optarg, "0x%4hx", &pkt_type) != 1) {
382 usage();
383 exit(1);
384 }
385 break;
386
387 default:
388 usage();
389 exit(1);
390 }
391 }
392
393 if (!(argc - optind) && (mode != RECV && mode != DUMP)) {
394 usage();
395 exit(1);
396 }
397
398 if (!(buf = malloc(data_size))) {
399 perror("Can't allocate data buffer");
400 exit(1);
401 }
402
403 memset(&sa, 0, sizeof(sa));
404 sa.sa_handler = SIG_IGN;
405 sa.sa_flags = SA_NOCLDSTOP;
406 sigaction(SIGCHLD, &sa, NULL);
407
408 openlog("scotest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
409
410 switch( mode ){
411 case RECV:
412 do_listen(recv_mode);
413 break;
414
415 case DUMP:
416 do_listen(dump_mode);
417 break;
418
419 case SEND:
420 send_mode(argv[optind]);
421 break;
422
423 case RECONNECT:
424 reconnect_mode(argv[optind]);
425 break;
426
427 case MULTY:
428 multy_connect_mode(argv[optind]);
429 break;
430
431 case CONNECT:
432 sk = do_connect(argv[optind]);
433 if (sk < 0)
434 exit(1);
435 dump_mode(sk);
436 break;
437 }
438
439 syslog(LOG_INFO, "Exit");
440
441 closelog();
442
443 return 0;
444 }
445