• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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