Home | History | Annotate | Line # | Download | only in bthset
bthset.c revision 1.1
      1 /*	$NetBSD: bthset.c,v 1.1 2006/06/19 15:44:56 gdamore 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.1 2006/06/19 15:44:56 gdamore 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/bthset.h>
     58 
     59 #include <netbt/rfcomm.h>
     60 
     61 #define RING_INTERVAL	5	/* seconds */
     62 #define BTHSET_DELTA	16
     63 
     64 int  main(int, char *[]);
     65 void usage(void);
     66 
     67 void do_signal(int, short, void *);
     68 void do_ring(int, short, void *);
     69 void do_mixer(int, short, void *);
     70 void do_rfcomm(int, short, void *);
     71 void do_server(int, short, void *);
     72 int send_rfcomm(const char *, ...);
     73 
     74 int init_mixer(struct bthset_info *, const char *);
     75 int init_rfcomm(struct bthset_info *);
     76 int init_server(struct bthset_info *, int);
     77 
     78 void remove_pid(void);
     79 int write_pid(void);
     80 
     81 struct event sigint_ev;		/* bye bye */
     82 struct event sigusr1_ev;	/* start ringing */
     83 struct event sigusr2_ev;	/* stop ringing */
     84 struct event mixer_ev;		/* mixer changed */
     85 struct event rfcomm_ev;		/* headset speaks */
     86 struct event server_ev;		/* headset connecting */
     87 struct event ring_ev;		/* ring timer */
     88 
     89 mixer_ctrl_t vgs;	/* speaker control */
     90 mixer_ctrl_t vgm;	/* mic control */
     91 int ringing;		/* we are ringing */
     92 int verbose;		/* copy to stdout */
     93 int mx;			/* mixer fd */
     94 int rf;			/* rfcomm connection fd */
     95 int ag;			/* rfcomm gateway fd */
     96 void *ss;		/* sdp handle */
     97 
     98 char *command;		/* answer command */
     99 char *pidfile;		/* PID file name */
    100 
    101 int
    102 main(int ac, char *av[])
    103 {
    104 	struct bthset_info	info;
    105 	const char		*mixer;
    106 	int			ch, channel;
    107 
    108 	ag = rf = -1;
    109 	verbose = 0;
    110 	channel = 0;
    111 	pidfile = getenv("BTHSET_PIDFILE");
    112 	command = getenv("BTHSET_COMMAND");
    113 	mixer = getenv("BTHSET_MIXER");
    114 	if (mixer == NULL)
    115 		mixer = "/dev/mixer";
    116 
    117 	while ((ch = getopt(ac, av, "hc:m:p:s:v")) != EOF) {
    118 		switch (ch) {
    119 		case 'c':
    120 			command = optarg;
    121 			break;
    122 
    123 		case 'm':
    124 			mixer = optarg;
    125 			break;
    126 
    127 		case 'p':
    128 			pidfile = optarg;
    129 			break;
    130 
    131 		case 's':
    132 			channel = atoi(optarg);
    133 			break;
    134 
    135 		case 'v':
    136 			verbose = 1;
    137 			break;
    138 
    139 		case 'h':
    140 		default:
    141 			usage();
    142 		}
    143 	}
    144 
    145 	if (mixer == NULL)
    146 		usage();
    147 
    148 	if ((channel < RFCOMM_CHANNEL_MIN || channel > RFCOMM_CHANNEL_MAX)
    149 	    && channel != 0)
    150 		usage();
    151 
    152 	if (write_pid() < 0)
    153 		err(EXIT_FAILURE, "%s", pidfile);
    154 
    155 	event_init();
    156 
    157 	ringing = 0;
    158 	evtimer_set(&ring_ev, do_ring, NULL);
    159 
    160 	signal_set(&sigusr1_ev, SIGUSR1, do_signal, NULL);
    161 	if (signal_add(&sigusr1_ev, NULL) < 0)
    162 		err(EXIT_FAILURE, "SIGUSR1");
    163 
    164 	signal_set(&sigusr2_ev, SIGUSR2, do_signal, NULL);
    165 	if (signal_add(&sigusr2_ev, NULL) < 0)
    166 		err(EXIT_FAILURE, "SIGUSR2");
    167 
    168 	signal_set(&sigint_ev, SIGINT, do_signal, NULL);
    169 	if (signal_add(&sigint_ev, NULL) < 0)
    170 		err(EXIT_FAILURE, "SIGINT");
    171 
    172 	if (init_mixer(&info, mixer) < 0)
    173 		err(EXIT_FAILURE, "%s", mixer);
    174 
    175 	if (channel == 0 && init_rfcomm(&info) < 0)
    176 		err(EXIT_FAILURE, "%s", bt_ntoa(&info.raddr, NULL));
    177 
    178 	if (channel && init_server(&info, channel) < 0)
    179 		err(EXIT_FAILURE, "%d", channel);
    180 
    181 	if (verbose) {
    182 		printf("Headset Info:\n");
    183 		printf("\tmixer: %s\n", mixer);
    184 		printf("\tladdr: %s\n", bt_ntoa(&info.laddr, NULL));
    185 		printf("\traddr: %s\n", bt_ntoa(&info.raddr, NULL));
    186 		printf("\tchannel: %d\n", info.channel);
    187 		printf("\tvgs.dev: %d, vgm.dev: %d\n", vgs.dev, vgm.dev);
    188 		if (channel) printf("\tserver channel: %d\n", channel);
    189 	}
    190 
    191 	event_dispatch();
    192 
    193 	err(EXIT_FAILURE, "event_dispatch");
    194 }
    195 
    196 void
    197 usage(void)
    198 {
    199 
    200 	fprintf(stderr,
    201 		"usage: %s [-hv] [-c command] [-m mixer] [-p file] [-s channel]\n"
    202 		"Where:\n"
    203 		"\t-h           display this message\n"
    204 		"\t-v           verbose output\n"
    205 		"\t-c command   command to execute on answer\n"
    206 		"\t-m mixer     mixer path\n"
    207 		"\t-p file      write PID to file\n"
    208 		"\t-s channel   register as audio gateway on channel\n"
    209 		"", getprogname());
    210 
    211 	exit(EXIT_FAILURE);
    212 }
    213 
    214 void
    215 do_signal(int s, short ev, void *arg)
    216 {
    217 
    218 	switch (s) {
    219 	case SIGUSR1:
    220 		ringing = 1;	/* start ringing */
    221 		do_ring(0, 0, NULL);
    222 		break;
    223 
    224 	case SIGUSR2:
    225 		ringing = 0;
    226 		break;
    227 
    228 	case SIGINT:
    229 	default:
    230 		exit(EXIT_SUCCESS);
    231 	}
    232 }
    233 
    234 void
    235 do_ring(int s, short ev, void *arg)
    236 {
    237 	static struct timeval tv = { RING_INTERVAL, 0 };
    238 
    239 	if (!ringing)
    240 		return;
    241 
    242 	send_rfcomm("RING");
    243 	evtimer_add(&ring_ev, &tv);
    244 }
    245 
    246 /*
    247  * The mixer device has been twiddled. We check mic and speaker
    248  * settings and send the appropriate commands to the headset,
    249  */
    250 void
    251 do_mixer(int s, short ev, void *arg)
    252 {
    253 	mixer_ctrl_t mc;
    254 	int level;
    255 
    256 	memcpy(&mc, &vgs, sizeof(mc));
    257 	if (ioctl(mx, AUDIO_MIXER_READ, &mc) < 0)
    258 		return;
    259 
    260 	if (memcmp(&vgs, &mc, sizeof(mc))) {
    261 		memcpy(&vgs, &mc, sizeof(mc));
    262 		level = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] / BTHSET_DELTA;
    263 
    264 		send_rfcomm("+VGS=%d", level);
    265 	}
    266 
    267 	memcpy(&mc, &vgm, sizeof(mc));
    268 	if (ioctl(mx, AUDIO_MIXER_READ, &mc) < 0)
    269 		return;
    270 
    271 	if (memcmp(&vgm, &mc, sizeof(mc))) {
    272 		memcpy(&vgm, &mc, sizeof(mc));
    273 		level = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] / BTHSET_DELTA;
    274 
    275 		send_rfcomm("+VGM=%d", level);
    276 	}
    277 }
    278 
    279 /*
    280  * RFCOMM socket event.
    281  */
    282 void
    283 do_rfcomm(int fd, short ev, void *arg)
    284 {
    285 	char buf[128];
    286 	int len, level;
    287 
    288 	memset(buf, 0, sizeof(buf));
    289 	len = recv(rf, buf, sizeof(buf), 0);
    290 	if (len <= 0) {
    291 		if (ag < 0)
    292 			errx(EXIT_FAILURE, "Connection Lost");
    293 
    294 		event_del(&rfcomm_ev);
    295 		close(rf);
    296 		rf = -1;
    297 		ringing = 0;
    298 		return;
    299 	}
    300 
    301 	if (verbose)
    302 		printf("> %.*s\n", len, buf);
    303 
    304 	if (len >= 7 && strncmp(buf, "AT+CKPD", 7) == 0) {
    305 		if (ringing && command != NULL) {
    306 			if (verbose)
    307 				printf("%% %s\n", command);
    308 
    309 			system(command);
    310 		}
    311 
    312 		ringing = 0;
    313 		send_rfcomm("OK");
    314 		return;
    315 	}
    316 
    317 	if (len >= 7 && strncmp(buf, "AT+VGS=", 7) == 0) {
    318 		level = atoi(buf + 7);
    319 		if (level < 0 || level > 15)
    320 			return;
    321 
    322 		vgs.un.value.level[AUDIO_MIXER_LEVEL_MONO] = level * BTHSET_DELTA;
    323 		if (ioctl(mx, AUDIO_MIXER_WRITE, &vgs) < 0)
    324 			return;
    325 
    326 		send_rfcomm("OK");
    327 		return;
    328 	}
    329 
    330 	if (len >= 7 && strncmp(buf, "AT+VGM=", 7) == 0) {
    331 		level = atoi(buf + 7);
    332 		if (level < 0 || level > 15)
    333 			return;
    334 
    335 		vgm.un.value.level[AUDIO_MIXER_LEVEL_MONO] = level * BTHSET_DELTA;
    336 		if (ioctl(mx, AUDIO_MIXER_WRITE, &vgm) < 0)
    337 			return;
    338 
    339 		send_rfcomm("OK");
    340 		return;
    341 	}
    342 
    343 	send_rfcomm("ERROR");
    344 }
    345 
    346 /*
    347  * got an incoming connection on the AG socket.
    348  */
    349 void
    350 do_server(int fd, short ev, void *arg)
    351 {
    352 	bdaddr_t *raddr = arg;
    353 	struct sockaddr_bt addr;
    354 	socklen_t len;
    355 	int s;
    356 
    357 	assert(raddr != NULL);
    358 
    359 	len = sizeof(addr);
    360 	s = accept(fd, (struct sockaddr *)&addr, &len);
    361 	if (s < 0)
    362 		return;
    363 
    364 	if (rf >= 0
    365 	    || len != sizeof(addr)
    366 	    || addr.bt_len != sizeof(addr)
    367 	    || addr.bt_family != AF_BLUETOOTH
    368 	    || !bdaddr_same(raddr, &addr.bt_bdaddr)) {
    369 		close(s);
    370 		return;
    371 	}
    372 
    373 	rf = s;
    374 	event_set(&rfcomm_ev, rf, EV_READ | EV_PERSIST, do_rfcomm, NULL);
    375 	if (event_add(&rfcomm_ev, NULL) < 0)
    376 		err(EXIT_FAILURE, "rfcomm_ev");
    377 }
    378 
    379 /*
    380  * send a message to the RFCOMM socket
    381  */
    382 int
    383 send_rfcomm(const char *msg, ...)
    384 {
    385 	char buf[128], fmt[128];
    386 	va_list ap;
    387 	int len;
    388 
    389 	va_start(ap, msg);
    390 
    391 	if (verbose) {
    392 		snprintf(fmt, sizeof(fmt), "< %s\n", msg);
    393 		vprintf(fmt, ap);
    394 	}
    395 
    396 	snprintf(fmt, sizeof(fmt), "\r\n%s\r\n", msg);
    397 	vsnprintf(buf, sizeof(buf), fmt, ap);
    398 	len = send(rf, buf, strlen(buf), 0);
    399 
    400 	va_end(ap);
    401 	return len;
    402 }
    403 
    404 /*
    405  * Initialise mixer event
    406  */
    407 int
    408 init_mixer(struct bthset_info *info, const char *mixer)
    409 {
    410 
    411 	mx = open(mixer, O_WRONLY, 0);
    412 	if (mx < 0)
    413 		return -1;
    414 
    415 	if (ioctl(mx, BTHSET_GETINFO, info) < 0)
    416 		return -1;
    417 
    418 	/* get initial vol settings */
    419 	memset(&vgs, 0, sizeof(vgs));
    420 	vgs.dev = info->vgs;
    421 	if (ioctl(mx, AUDIO_MIXER_READ, &vgs) < 0)
    422 		return -1;
    423 
    424 	memset(&vgm, 0, sizeof(vgm));
    425 	vgm.dev = info->vgm;
    426 	if (ioctl(mx, AUDIO_MIXER_READ, &vgm) < 0)
    427 		return -1;
    428 
    429 	/* set up mixer changed event */
    430 	if (fcntl(mx, F_SETFL, O_ASYNC) < 0)
    431 		return -1;
    432 
    433 	signal_set(&mixer_ev, SIGIO, do_mixer, NULL);
    434 	if (signal_add(&mixer_ev, NULL) < 0)
    435 		return -1;
    436 
    437 	return 0;
    438 }
    439 
    440 /*
    441  * Initialise RFCOMM socket
    442  */
    443 int
    444 init_rfcomm(struct bthset_info *info)
    445 {
    446 	struct sockaddr_bt addr;
    447 
    448 	rf = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    449 	if (rf < 0)
    450 		return -1;
    451 
    452 	memset(&addr, 0, sizeof(addr));
    453 	addr.bt_len = sizeof(addr);
    454 	addr.bt_family = AF_BLUETOOTH;
    455 	bdaddr_copy(&addr.bt_bdaddr, &info->laddr);
    456 
    457 	if (bind(rf, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    458 		return -1;
    459 
    460 	bdaddr_copy(&addr.bt_bdaddr, &info->raddr);
    461 	addr.bt_channel = info->channel;
    462 
    463 	if (connect(rf, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    464 		return -1;
    465 
    466 	event_set(&rfcomm_ev, rf, EV_READ | EV_PERSIST, do_rfcomm, NULL);
    467 	if (event_add(&rfcomm_ev, NULL) < 0)
    468 		return -1;
    469 
    470 	return 0;
    471 }
    472 
    473 /*
    474  * Initialise server socket
    475  */
    476 int
    477 init_server(struct bthset_info *info, int channel)
    478 {
    479 	sdp_hset_profile_t hset;
    480 	struct sockaddr_bt addr;
    481 
    482 	ag = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    483 	if (ag < 0)
    484 		return -1;
    485 
    486 	memset(&addr, 0, sizeof(addr));
    487 	addr.bt_len = sizeof(addr);
    488 	addr.bt_family = AF_BLUETOOTH;
    489 	addr.bt_channel = channel;
    490 	bdaddr_copy(&addr.bt_bdaddr, &info->laddr);
    491 
    492 	if (bind(ag, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    493 		return -1;
    494 
    495 	if (listen(ag, 1) < 0)
    496 		return -1;
    497 
    498 	event_set(&server_ev, ag, EV_READ | EV_PERSIST, do_server, &info->raddr);
    499 	if (event_add(&server_ev, NULL) < 0)
    500 		return -1;
    501 
    502 	memset(&hset, 0, sizeof(hset));
    503 	hset.server_channel = channel;
    504 
    505 	ss = sdp_open_local(NULL);
    506 	if (ss == NULL || (errno = sdp_error(ss)))
    507 		return -1;
    508 
    509 	if (sdp_register_service(ss,
    510 			SDP_SERVICE_CLASS_HEADSET_AUDIO_GATEWAY,
    511 			&info->laddr, (uint8_t *)&hset, sizeof(hset), NULL) != 0) {
    512 		errno = sdp_error(ss);
    513 		sdp_close(ss);
    514 		return -1;
    515 	}
    516 
    517 	return 0;
    518 }
    519 
    520 void
    521 remove_pid(void)
    522 {
    523 
    524 	if (pidfile == NULL)
    525 		return;
    526 
    527 	unlink(pidfile);
    528 }
    529 
    530 int
    531 write_pid(void)
    532 {
    533 	char *buf;
    534 	int fd, len;
    535 
    536 	if (pidfile == NULL)
    537 		return 0;
    538 
    539 	fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    540 	if (fd < 0)
    541 		return -1;
    542 
    543 	len = asprintf(&buf, "%d\n", getpid());
    544 	if (len > 0)
    545 		write(fd, buf, len);
    546 
    547 	if (len >= 0 && buf != NULL)
    548 		free(buf);
    549 
    550 	close (fd);
    551 
    552 	return atexit(remove_pid);
    553 }
    554