Home | History | Annotate | Line # | Download | only in bthset
bthset.c revision 1.2
      1 /*	$NetBSD: bthset.c,v 1.2 2006/07/26 10:43:02 tron Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006 Itronix Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Iain Hibbert for Itronix Inc.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. The name of Itronix Inc. may not be used to endorse
     18  *    or promote products derived from this software without specific
     19  *    prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
     25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     28  * ON ANY THEORY OF LIABILITY, WHETHER IN
     29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     31  * POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 
     34 #include <sys/cdefs.h>
     35 __COPYRIGHT("@(#) Copyright (c) 2006 Itronix, Inc\n"
     36 	    "All rights reserved.\n");
     37 __RCSID("$NetBSD: bthset.c,v 1.2 2006/07/26 10:43:02 tron Exp $");
     38 
     39 #include <sys/types.h>
     40 #include <sys/audioio.h>
     41 #include <sys/ioctl.h>
     42 #include <sys/time.h>
     43 #include <assert.h>
     44 #include <bluetooth.h>
     45 #include <err.h>
     46 #include <event.h>
     47 #include <fcntl.h>
     48 #include <signal.h>
     49 #include <sdp.h>
     50 #include <stdarg.h>
     51 #include <stdio.h>
     52 #include <stdlib.h>
     53 #include <string.h>
     54 #include <unistd.h>
     55 
     56 #include <dev/bluetooth/btdev.h>
     57 #include <dev/bluetooth/btsco.h>
     58 
     59 #include <netbt/rfcomm.h>
     60 
     61 #define RING_INTERVAL	5	/* seconds */
     62 
     63 int  main(int, char *[]);
     64 void usage(void);
     65 
     66 void do_signal(int, short, void *);
     67 void do_ring(int, short, void *);
     68 void do_mixer(int, short, void *);
     69 void do_rfcomm(int, short, void *);
     70 void do_server(int, short, void *);
     71 int send_rfcomm(const char *, ...);
     72 
     73 int init_mixer(struct btsco_info *, const char *);
     74 int init_rfcomm(struct btsco_info *);
     75 int init_server(struct btsco_info *, int);
     76 
     77 void remove_pid(void);
     78 int write_pid(void);
     79 
     80 struct event sigint_ev;		/* bye bye */
     81 struct event sigusr1_ev;	/* start ringing */
     82 struct event sigusr2_ev;	/* stop ringing */
     83 struct event mixer_ev;		/* mixer changed */
     84 struct event rfcomm_ev;		/* headset speaks */
     85 struct event server_ev;		/* headset connecting */
     86 struct event ring_ev;		/* ring timer */
     87 
     88 mixer_ctrl_t vgs;	/* speaker control */
     89 mixer_ctrl_t vgm;	/* mic control */
     90 int ringing;		/* we are ringing */
     91 int verbose;		/* copy to stdout */
     92 int mx;			/* mixer fd */
     93 int rf;			/* rfcomm connection fd */
     94 int ag;			/* rfcomm gateway fd */
     95 void *ss;		/* sdp handle */
     96 
     97 char *command;		/* answer command */
     98 char *pidfile;		/* PID file name */
     99 
    100 int
    101 main(int ac, char *av[])
    102 {
    103 	struct btsco_info	info;
    104 	const char		*mixer;
    105 	int			ch, channel;
    106 
    107 	ag = rf = -1;
    108 	verbose = 0;
    109 	channel = 0;
    110 	pidfile = getenv("BTHSET_PIDFILE");
    111 	command = getenv("BTHSET_COMMAND");
    112 	mixer = getenv("BTHSET_MIXER");
    113 	if (mixer == NULL)
    114 		mixer = "/dev/mixer";
    115 
    116 	while ((ch = getopt(ac, av, "hc:m:p:s:v")) != EOF) {
    117 		switch (ch) {
    118 		case 'c':
    119 			command = optarg;
    120 			break;
    121 
    122 		case 'm':
    123 			mixer = optarg;
    124 			break;
    125 
    126 		case 'p':
    127 			pidfile = optarg;
    128 			break;
    129 
    130 		case 's':
    131 			channel = atoi(optarg);
    132 			break;
    133 
    134 		case 'v':
    135 			verbose = 1;
    136 			break;
    137 
    138 		case 'h':
    139 		default:
    140 			usage();
    141 		}
    142 	}
    143 
    144 	if (mixer == NULL)
    145 		usage();
    146 
    147 	if ((channel < RFCOMM_CHANNEL_MIN || channel > RFCOMM_CHANNEL_MAX)
    148 	    && channel != 0)
    149 		usage();
    150 
    151 	if (write_pid() < 0)
    152 		err(EXIT_FAILURE, "%s", pidfile);
    153 
    154 	event_init();
    155 
    156 	ringing = 0;
    157 	evtimer_set(&ring_ev, do_ring, NULL);
    158 
    159 	signal_set(&sigusr1_ev, SIGUSR1, do_signal, NULL);
    160 	if (signal_add(&sigusr1_ev, NULL) < 0)
    161 		err(EXIT_FAILURE, "SIGUSR1");
    162 
    163 	signal_set(&sigusr2_ev, SIGUSR2, do_signal, NULL);
    164 	if (signal_add(&sigusr2_ev, NULL) < 0)
    165 		err(EXIT_FAILURE, "SIGUSR2");
    166 
    167 	signal_set(&sigint_ev, SIGINT, do_signal, NULL);
    168 	if (signal_add(&sigint_ev, NULL) < 0)
    169 		err(EXIT_FAILURE, "SIGINT");
    170 
    171 	if (init_mixer(&info, mixer) < 0)
    172 		err(EXIT_FAILURE, "%s", mixer);
    173 
    174 	if (channel == 0 && init_rfcomm(&info) < 0)
    175 		err(EXIT_FAILURE, "%s", bt_ntoa(&info.raddr, NULL));
    176 
    177 	if (channel && init_server(&info, channel) < 0)
    178 		err(EXIT_FAILURE, "%d", channel);
    179 
    180 	if (verbose) {
    181 		printf("Headset Info:\n");
    182 		printf("\tmixer: %s\n", mixer);
    183 		printf("\tladdr: %s\n", bt_ntoa(&info.laddr, NULL));
    184 		printf("\traddr: %s\n", bt_ntoa(&info.raddr, NULL));
    185 		printf("\tchannel: %d\n", info.channel);
    186 		printf("\tvgs.dev: %d, vgm.dev: %d\n", vgs.dev, vgm.dev);
    187 		if (channel) printf("\tserver channel: %d\n", channel);
    188 	}
    189 
    190 	event_dispatch();
    191 
    192 	err(EXIT_FAILURE, "event_dispatch");
    193 }
    194 
    195 void
    196 usage(void)
    197 {
    198 
    199 	fprintf(stderr,
    200 		"usage: %s [-hv] [-c command] [-m mixer] [-p file] [-s channel]\n"
    201 		"Where:\n"
    202 		"\t-h           display this message\n"
    203 		"\t-v           verbose output\n"
    204 		"\t-c command   command to execute on answer\n"
    205 		"\t-m mixer     mixer path\n"
    206 		"\t-p file      write PID to file\n"
    207 		"\t-s channel   register as audio gateway on channel\n"
    208 		"", getprogname());
    209 
    210 	exit(EXIT_FAILURE);
    211 }
    212 
    213 void
    214 do_signal(int s, short ev, void *arg)
    215 {
    216 
    217 	switch (s) {
    218 	case SIGUSR1:
    219 		ringing = 1;	/* start ringing */
    220 		do_ring(0, 0, NULL);
    221 		break;
    222 
    223 	case SIGUSR2:
    224 		ringing = 0;
    225 		break;
    226 
    227 	case SIGINT:
    228 	default:
    229 		exit(EXIT_SUCCESS);
    230 	}
    231 }
    232 
    233 void
    234 do_ring(int s, short ev, void *arg)
    235 {
    236 	static struct timeval tv = { RING_INTERVAL, 0 };
    237 
    238 	if (!ringing)
    239 		return;
    240 
    241 	send_rfcomm("RING");
    242 	evtimer_add(&ring_ev, &tv);
    243 }
    244 
    245 /*
    246  * The mixer device has been twiddled. We check mic and speaker
    247  * settings and send the appropriate commands to the headset,
    248  */
    249 void
    250 do_mixer(int s, short ev, void *arg)
    251 {
    252 	mixer_ctrl_t mc;
    253 	int level;
    254 
    255 	memcpy(&mc, &vgs, sizeof(mc));
    256 	if (ioctl(mx, AUDIO_MIXER_READ, &mc) < 0)
    257 		return;
    258 
    259 	if (memcmp(&vgs, &mc, sizeof(mc))) {
    260 		memcpy(&vgs, &mc, sizeof(mc));
    261 		level = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] / BTSCO_DELTA;
    262 
    263 		send_rfcomm("+VGS=%d", level);
    264 	}
    265 
    266 	memcpy(&mc, &vgm, sizeof(mc));
    267 	if (ioctl(mx, AUDIO_MIXER_READ, &mc) < 0)
    268 		return;
    269 
    270 	if (memcmp(&vgm, &mc, sizeof(mc))) {
    271 		memcpy(&vgm, &mc, sizeof(mc));
    272 		level = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] / BTSCO_DELTA;
    273 
    274 		send_rfcomm("+VGM=%d", level);
    275 	}
    276 }
    277 
    278 /*
    279  * RFCOMM socket event.
    280  */
    281 void
    282 do_rfcomm(int fd, short ev, void *arg)
    283 {
    284 	char buf[128];
    285 	int len, level;
    286 
    287 	memset(buf, 0, sizeof(buf));
    288 	len = recv(rf, buf, sizeof(buf), 0);
    289 	if (len <= 0) {
    290 		if (ag < 0)
    291 			errx(EXIT_FAILURE, "Connection Lost");
    292 
    293 		event_del(&rfcomm_ev);
    294 		close(rf);
    295 		rf = -1;
    296 		ringing = 0;
    297 		return;
    298 	}
    299 
    300 	if (verbose)
    301 		printf("> %.*s\n", len, buf);
    302 
    303 	if (len >= 7 && strncmp(buf, "AT+CKPD", 7) == 0) {
    304 		if (ringing && command != NULL) {
    305 			if (verbose)
    306 				printf("%% %s\n", command);
    307 
    308 			system(command);
    309 		}
    310 
    311 		ringing = 0;
    312 		send_rfcomm("OK");
    313 		return;
    314 	}
    315 
    316 	if (len >= 7 && strncmp(buf, "AT+VGS=", 7) == 0) {
    317 		level = atoi(buf + 7);
    318 		if (level < 0 || level > 15)
    319 			return;
    320 
    321 		vgs.un.value.level[AUDIO_MIXER_LEVEL_MONO] = level * BTSCO_DELTA;
    322 		if (ioctl(mx, AUDIO_MIXER_WRITE, &vgs) < 0)
    323 			return;
    324 
    325 		send_rfcomm("OK");
    326 		return;
    327 	}
    328 
    329 	if (len >= 7 && strncmp(buf, "AT+VGM=", 7) == 0) {
    330 		level = atoi(buf + 7);
    331 		if (level < 0 || level > 15)
    332 			return;
    333 
    334 		vgm.un.value.level[AUDIO_MIXER_LEVEL_MONO] = level * BTSCO_DELTA;
    335 		if (ioctl(mx, AUDIO_MIXER_WRITE, &vgm) < 0)
    336 			return;
    337 
    338 		send_rfcomm("OK");
    339 		return;
    340 	}
    341 
    342 	send_rfcomm("ERROR");
    343 }
    344 
    345 /*
    346  * got an incoming connection on the AG socket.
    347  */
    348 void
    349 do_server(int fd, short ev, void *arg)
    350 {
    351 	bdaddr_t *raddr = arg;
    352 	struct sockaddr_bt addr;
    353 	socklen_t len;
    354 	int s;
    355 
    356 	assert(raddr != NULL);
    357 
    358 	len = sizeof(addr);
    359 	s = accept(fd, (struct sockaddr *)&addr, &len);
    360 	if (s < 0)
    361 		return;
    362 
    363 	if (rf >= 0
    364 	    || len != sizeof(addr)
    365 	    || addr.bt_len != sizeof(addr)
    366 	    || addr.bt_family != AF_BLUETOOTH
    367 	    || !bdaddr_same(raddr, &addr.bt_bdaddr)) {
    368 		close(s);
    369 		return;
    370 	}
    371 
    372 	rf = s;
    373 	event_set(&rfcomm_ev, rf, EV_READ | EV_PERSIST, do_rfcomm, NULL);
    374 	if (event_add(&rfcomm_ev, NULL) < 0)
    375 		err(EXIT_FAILURE, "rfcomm_ev");
    376 }
    377 
    378 /*
    379  * send a message to the RFCOMM socket
    380  */
    381 int
    382 send_rfcomm(const char *msg, ...)
    383 {
    384 	char buf[128], fmt[128];
    385 	va_list ap;
    386 	int len;
    387 
    388 	va_start(ap, msg);
    389 
    390 	if (verbose) {
    391 		snprintf(fmt, sizeof(fmt), "< %s\n", msg);
    392 		vprintf(fmt, ap);
    393 	}
    394 
    395 	snprintf(fmt, sizeof(fmt), "\r\n%s\r\n", msg);
    396 	vsnprintf(buf, sizeof(buf), fmt, ap);
    397 	len = send(rf, buf, strlen(buf), 0);
    398 
    399 	va_end(ap);
    400 	return len;
    401 }
    402 
    403 /*
    404  * Initialise mixer event
    405  */
    406 int
    407 init_mixer(struct btsco_info *info, const char *mixer)
    408 {
    409 
    410 	mx = open(mixer, O_WRONLY, 0);
    411 	if (mx < 0)
    412 		return -1;
    413 
    414 	if (ioctl(mx, BTSCO_GETINFO, info) < 0)
    415 		return -1;
    416 
    417 	/* get initial vol settings */
    418 	memset(&vgs, 0, sizeof(vgs));
    419 	vgs.dev = info->vgs;
    420 	if (ioctl(mx, AUDIO_MIXER_READ, &vgs) < 0)
    421 		return -1;
    422 
    423 	memset(&vgm, 0, sizeof(vgm));
    424 	vgm.dev = info->vgm;
    425 	if (ioctl(mx, AUDIO_MIXER_READ, &vgm) < 0)
    426 		return -1;
    427 
    428 	/* set up mixer changed event */
    429 	if (fcntl(mx, F_SETFL, O_ASYNC) < 0)
    430 		return -1;
    431 
    432 	signal_set(&mixer_ev, SIGIO, do_mixer, NULL);
    433 	if (signal_add(&mixer_ev, NULL) < 0)
    434 		return -1;
    435 
    436 	return 0;
    437 }
    438 
    439 /*
    440  * Initialise RFCOMM socket
    441  */
    442 int
    443 init_rfcomm(struct btsco_info *info)
    444 {
    445 	struct sockaddr_bt addr;
    446 
    447 	rf = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    448 	if (rf < 0)
    449 		return -1;
    450 
    451 	memset(&addr, 0, sizeof(addr));
    452 	addr.bt_len = sizeof(addr);
    453 	addr.bt_family = AF_BLUETOOTH;
    454 	bdaddr_copy(&addr.bt_bdaddr, &info->laddr);
    455 
    456 	if (bind(rf, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    457 		return -1;
    458 
    459 	bdaddr_copy(&addr.bt_bdaddr, &info->raddr);
    460 	addr.bt_channel = info->channel;
    461 
    462 	if (connect(rf, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    463 		return -1;
    464 
    465 	event_set(&rfcomm_ev, rf, EV_READ | EV_PERSIST, do_rfcomm, NULL);
    466 	if (event_add(&rfcomm_ev, NULL) < 0)
    467 		return -1;
    468 
    469 	return 0;
    470 }
    471 
    472 /*
    473  * Initialise server socket
    474  */
    475 int
    476 init_server(struct btsco_info *info, int channel)
    477 {
    478 	sdp_hset_profile_t hset;
    479 	struct sockaddr_bt addr;
    480 
    481 	ag = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    482 	if (ag < 0)
    483 		return -1;
    484 
    485 	memset(&addr, 0, sizeof(addr));
    486 	addr.bt_len = sizeof(addr);
    487 	addr.bt_family = AF_BLUETOOTH;
    488 	addr.bt_channel = channel;
    489 	bdaddr_copy(&addr.bt_bdaddr, &info->laddr);
    490 
    491 	if (bind(ag, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    492 		return -1;
    493 
    494 	if (listen(ag, 1) < 0)
    495 		return -1;
    496 
    497 	event_set(&server_ev, ag, EV_READ | EV_PERSIST, do_server, &info->raddr);
    498 	if (event_add(&server_ev, NULL) < 0)
    499 		return -1;
    500 
    501 	memset(&hset, 0, sizeof(hset));
    502 	hset.server_channel = channel;
    503 
    504 	ss = sdp_open_local(NULL);
    505 	if (ss == NULL || (errno = sdp_error(ss)))
    506 		return -1;
    507 
    508 	if (sdp_register_service(ss,
    509 			SDP_SERVICE_CLASS_HEADSET_AUDIO_GATEWAY,
    510 			&info->laddr, (uint8_t *)&hset, sizeof(hset), NULL) != 0) {
    511 		errno = sdp_error(ss);
    512 		sdp_close(ss);
    513 		return -1;
    514 	}
    515 
    516 	return 0;
    517 }
    518 
    519 void
    520 remove_pid(void)
    521 {
    522 
    523 	if (pidfile == NULL)
    524 		return;
    525 
    526 	unlink(pidfile);
    527 }
    528 
    529 int
    530 write_pid(void)
    531 {
    532 	char *buf;
    533 	int fd, len;
    534 
    535 	if (pidfile == NULL)
    536 		return 0;
    537 
    538 	fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    539 	if (fd < 0)
    540 		return -1;
    541 
    542 	len = asprintf(&buf, "%d\n", getpid());
    543 	if (len > 0)
    544 		write(fd, buf, len);
    545 
    546 	if (len >= 0 && buf != NULL)
    547 		free(buf);
    548 
    549 	close (fd);
    550 
    551 	return atexit(remove_pid);
    552 }
    553