Home | History | Annotate | Download | only in test
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk (at) qualcomm.com>
      6  *  Copyright (C) 2002-2010  Marcel Holtmann <marcel (at) 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 
     63 static float tv2fl(struct timeval tv)
     64 {
     65 	return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
     66 }
     67 
     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 
    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 
    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 
    220 static void recv_mode(int sk)
    221 {
    222 	struct timeval tv_beg,tv_end,tv_diff;
    223 	long total;
    224 	uint32_t seq;
    225 
    226 	syslog(LOG_INFO, "Receiving ...");
    227 
    228 	seq = 0;
    229 	while (1) {
    230 		gettimeofday(&tv_beg, NULL);
    231 		total = 0;
    232 		while (total < data_size) {
    233 			int r;
    234 			if ((r = recv(sk, buf, data_size, 0)) <= 0) {
    235 				if (r < 0)
    236 					syslog(LOG_ERR, "Read failed: %s (%d)",
    237 							strerror(errno), errno);
    238 				return;
    239 			}
    240 			total += r;
    241 		}
    242 		gettimeofday(&tv_end, NULL);
    243 
    244 		timersub(&tv_end, &tv_beg, &tv_diff);
    245 
    246 		syslog(LOG_INFO,"%ld bytes in %.2fm speed %.2f kb", total,
    247 			tv2fl(tv_diff) / 60.0,
    248 			(float)( total / tv2fl(tv_diff) ) / 1024.0 );
    249 	}
    250 }
    251 
    252 static void send_mode(char *svr)
    253 {
    254 	struct sco_options so;
    255 	socklen_t len;
    256 	uint32_t seq;
    257 	int i, sk;
    258 
    259 	if ((sk = do_connect(svr)) < 0) {
    260 		syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
    261 							strerror(errno), errno);
    262 		exit(1);
    263 	}
    264 
    265 	len = sizeof(so);
    266 	if (getsockopt(sk, SOL_SCO, SCO_OPTIONS, &so, &len) < 0) {
    267 		syslog(LOG_ERR, "Can't get SCO options: %s (%d)",
    268 							strerror(errno), errno);
    269 		exit(1);
    270 	}
    271 
    272 	syslog(LOG_INFO,"Sending ...");
    273 
    274 	for (i = 6; i < so.mtu; i++)
    275 		buf[i] = 0x7f;
    276 
    277 	seq = 0;
    278 	while (1) {
    279 		*(uint32_t *) buf = htobl(seq);
    280 		*(uint16_t *) (buf + 4) = htobs(data_size);
    281 		seq++;
    282 
    283 		if (send(sk, buf, so.mtu, 0) <= 0) {
    284 			syslog(LOG_ERR, "Send failed: %s (%d)",
    285 							strerror(errno), errno);
    286 			exit(1);
    287 		}
    288 
    289 		usleep(1);
    290 	}
    291 }
    292 
    293 static void reconnect_mode(char *svr)
    294 {
    295 	while (1) {
    296 		int sk;
    297 
    298 		if ((sk = do_connect(svr)) < 0) {
    299 			syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
    300 							strerror(errno), errno);
    301 			exit(1);
    302 		}
    303 
    304 		close(sk);
    305 
    306 		sleep(5);
    307 	}
    308 }
    309 
    310 static void multy_connect_mode(char *svr)
    311 {
    312 	while (1) {
    313 		int i, sk;
    314 
    315 		for (i = 0; i < 10; i++){
    316 			if (fork())
    317 				continue;
    318 
    319 			/* Child */
    320 			sk = do_connect(svr);
    321 			if (sk < 0) {
    322 				syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
    323 							strerror(errno), errno);
    324 			}
    325 			close(sk);
    326 			exit(0);
    327 		}
    328 
    329 		sleep(19);
    330 	}
    331 }
    332 
    333 static void usage(void)
    334 {
    335 	printf("scotest - SCO testing\n"
    336 		"Usage:\n");
    337 	printf("\tscotest <mode> [-b bytes] [-p pkt_type] [bd_addr]\n");
    338 	printf("Modes:\n"
    339 		"\t-d dump (server)\n"
    340 		"\t-c reconnect (client)\n"
    341 		"\t-m multiple connects (client)\n"
    342 		"\t-r receive (server)\n"
    343 		"\t-s connect and send (client)\n"
    344 		"\t-n connect and be silent (client)\n");
    345 }
    346 
    347 int main(int argc ,char *argv[])
    348 {
    349 	struct sigaction sa;
    350 	int opt, sk, mode = RECV;
    351 
    352 	while ((opt=getopt(argc,argv,"rdscmnb:p:")) != EOF) {
    353 		switch(opt) {
    354 		case 'r':
    355 			mode = RECV;
    356 			break;
    357 
    358 		case 's':
    359 			mode = SEND;
    360 			break;
    361 
    362 		case 'd':
    363 			mode = DUMP;
    364 			break;
    365 
    366 		case 'c':
    367 			mode = RECONNECT;
    368 			break;
    369 
    370 		case 'm':
    371 			mode = MULTY;
    372 			break;
    373 
    374 		case 'n':
    375 			mode = CONNECT;
    376 			break;
    377 
    378 		case 'b':
    379 			data_size = atoi(optarg);
    380 			break;
    381 
    382 		case 'p':
    383 			if (sscanf(optarg, "0x%4hx", &pkt_type) != 1) {
    384 				usage();
    385 				exit(1);
    386 			}
    387 			break;
    388 
    389 		default:
    390 			usage();
    391 			exit(1);
    392 		}
    393 	}
    394 
    395 	if (!(argc - optind) && (mode != RECV && mode != DUMP)) {
    396 		usage();
    397 		exit(1);
    398 	}
    399 
    400 	if (!(buf = malloc(data_size))) {
    401 		perror("Can't allocate data buffer");
    402 		exit(1);
    403 	}
    404 
    405 	memset(&sa, 0, sizeof(sa));
    406 	sa.sa_handler = SIG_IGN;
    407 	sa.sa_flags   = SA_NOCLDSTOP;
    408 	sigaction(SIGCHLD, &sa, NULL);
    409 
    410 	openlog("scotest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
    411 
    412 	switch( mode ){
    413 		case RECV:
    414 			do_listen(recv_mode);
    415 			break;
    416 
    417 		case DUMP:
    418 			do_listen(dump_mode);
    419 			break;
    420 
    421 		case SEND:
    422 			send_mode(argv[optind]);
    423 			break;
    424 
    425 		case RECONNECT:
    426 			reconnect_mode(argv[optind]);
    427 			break;
    428 
    429 		case MULTY:
    430 			multy_connect_mode(argv[optind]);
    431 			break;
    432 
    433 		case CONNECT:
    434 			sk = do_connect(argv[optind]);
    435 			if (sk < 0)
    436 				exit(1);
    437 			dump_mode(sk);
    438 			break;
    439 	}
    440 
    441 	syslog(LOG_INFO, "Exit");
    442 
    443 	closelog();
    444 
    445 	return 0;
    446 }
    447