xref: /netbsd-src/usr.bin/bthset/bthset.c (revision e01e6533713e59a1f8119befe33a47779b70521e)
1 /*	$NetBSD: bthset.c,v 1.9 2016/03/06 21:12:07 plunky 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.  All rights reserved.");
36 __RCSID("$NetBSD: bthset.c,v 1.9 2016/03/06 21:12:07 plunky Exp $");
37 
38 #include <sys/types.h>
39 #include <sys/audioio.h>
40 #include <sys/ioctl.h>
41 #include <sys/time.h>
42 #include <sys/uio.h>
43 
44 #include <assert.h>
45 #include <bluetooth.h>
46 #include <err.h>
47 #include <event.h>
48 #include <fcntl.h>
49 #include <sdp.h>
50 #include <signal.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <errno.h>
57 
58 #include <dev/bluetooth/btdev.h>
59 #include <dev/bluetooth/btsco.h>
60 
61 #include <netbt/rfcomm.h>
62 
63 #define RING_INTERVAL	5	/* seconds */
64 
65 __dead static void usage(void);
66 
67 static void do_signal(int, short, void *);
68 static void do_ring(int, short, void *);
69 static void do_mixer(int, short, void *);
70 static void do_rfcomm(int, short, void *);
71 static void do_server(int, short, void *);
72 static int send_rfcomm(const char *, ...) __printflike(1, 2);
73 
74 static int init_mixer(struct btsco_info *, const char *);
75 static int init_rfcomm(struct btsco_info *);
76 static int init_server(struct btsco_info *, int);
77 
78 static void remove_pid(void);
79 static int write_pid(void);
80 
81 static struct event sigint_ev;		/* bye bye */
82 static struct event sigusr1_ev;	/* start ringing */
83 static struct event sigusr2_ev;	/* stop ringing */
84 static struct event mixer_ev;		/* mixer changed */
85 static struct event rfcomm_ev;		/* headset speaks */
86 static struct event server_ev;		/* headset connecting */
87 static struct event ring_ev;		/* ring timer */
88 
89 static mixer_ctrl_t vgs;	/* speaker control */
90 static mixer_ctrl_t vgm;	/* mic control */
91 static int ringing;		/* we are ringing */
92 static int verbose;		/* copy to stdout */
93 static int mx;			/* mixer fd */
94 static int rf;			/* rfcomm connection fd */
95 static int ag;			/* rfcomm gateway fd */
96 static sdp_session_t ss;	/* SDP server session */
97 
98 static char *command;		/* answer command */
99 static char *pidfile;		/* PID file name */
100 
101 /* Headset Audio Gateway service record */
102 static uint8_t hset_data[] = {
103 	0x09, 0x00, 0x00,	//  uint16	ServiceRecordHandle
104 	0x0a, 0x00, 0x00, 0x00,	//  uint32	0x00000000
105 	0x00,
106 
107 	0x09, 0x00, 0x01,	//  uint16	ServiceClassIDList
108 	0x35, 0x06,		//  seq8(6)
109 	0x19, 0x11, 0x12,	//   uuid16	HeadsetAudioGateway
110 	0x19, 0x12, 0x03,	//   uuid16	GenericAudio
111 
112 	0x09, 0x00, 0x04,	//  uint16	ProtocolDescriptorList
113 	0x35, 0x0c,		//  seq8(12)
114 	0x35, 0x03,		//   seq8(3)
115 	0x19, 0x01, 0x00,	//    uuid16	L2CAP
116 	0x35, 0x05,		//   seq8(5)
117 	0x19, 0x00, 0x03,	//    uuid16	RFCOMM
118 	0x08, 0x00,		//    uint8	%hset_channel%
119 
120 	0x09, 0x00, 0x05,	//  uint16	BrowseGroupList
121 	0x35, 0x03,		//  seq8(3)
122 	0x19, 0x10, 0x02,	//   uuid16	PublicBrowseGroup
123 
124 	0x09, 0x00, 0x06,	//  uint16	LanguageBaseAttributeIDList
125 	0x35, 0x09,		//  seq8(9)
126 	0x09, 0x65, 0x6e,	//   uint16	0x656e	("en")
127 	0x09, 0x00, 0x6a,	//   uint16	106	(UTF-8)
128 	0x09, 0x01, 0x00,	//   uint16	PrimaryLanguageBaseID
129 
130 	0x09, 0x00, 0x09,	//  uint16	BluetoothProfileDescriptorList
131 	0x35, 0x08,		//  seq8(8)
132 	0x35, 0x06,		//   seq8(6)
133 	0x19, 0x11, 0x08,	//    uuid16	Headset
134 	0x09, 0x01, 0x00,	//    uint16	v1.0
135 
136 	0x09, 0x01, 0x00,	//  uint16	PrimaryLanguageBaseID + ServiceNameOffset
137 	0x25, 0x0d, 0x56, 0x6f,	//  str8(13)	"Voice Gateway"
138 	0x69, 0x63, 0x65, 0x20,
139 	0x47, 0x61, 0x74, 0x65,
140 	0x77, 0x61, 0x79
141 };
142 
143 static sdp_data_t hset_record = { hset_data + 0, hset_data + 89 };
144 static sdp_data_t hset_channel = { hset_data + 34, hset_data + 36 };
145 
146 int
main(int ac,char * av[])147 main(int ac, char *av[])
148 {
149 	struct btsco_info	info;
150 	const char		*mixer;
151 	int			ch, channel;
152 
153 	ag = rf = -1;
154 	verbose = 0;
155 	channel = 0;
156 	pidfile = getenv("BTHSET_PIDFILE");
157 	command = getenv("BTHSET_COMMAND");
158 	mixer = getenv("BTHSET_MIXER");
159 	if (mixer == NULL)
160 		mixer = "/dev/mixer";
161 
162 	while ((ch = getopt(ac, av, "hc:m:p:s:v")) != EOF) {
163 		switch (ch) {
164 		case 'c':
165 			command = optarg;
166 			break;
167 
168 		case 'm':
169 			mixer = optarg;
170 			break;
171 
172 		case 'p':
173 			pidfile = optarg;
174 			break;
175 
176 		case 's':
177 			channel = atoi(optarg);
178 			break;
179 
180 		case 'v':
181 			verbose = 1;
182 			break;
183 
184 		case 'h':
185 		default:
186 			usage();
187 		}
188 	}
189 
190 	if (mixer == NULL)
191 		usage();
192 
193 	if ((channel < RFCOMM_CHANNEL_MIN || channel > RFCOMM_CHANNEL_MAX)
194 	    && channel != 0)
195 		usage();
196 
197 	if (write_pid() < 0)
198 		err(EXIT_FAILURE, "%s", pidfile);
199 
200 	event_init();
201 
202 	ringing = 0;
203 	evtimer_set(&ring_ev, do_ring, NULL);
204 
205 	signal_set(&sigusr1_ev, SIGUSR1, do_signal, NULL);
206 	if (signal_add(&sigusr1_ev, NULL) < 0)
207 		err(EXIT_FAILURE, "SIGUSR1");
208 
209 	signal_set(&sigusr2_ev, SIGUSR2, do_signal, NULL);
210 	if (signal_add(&sigusr2_ev, NULL) < 0)
211 		err(EXIT_FAILURE, "SIGUSR2");
212 
213 	signal_set(&sigint_ev, SIGINT, do_signal, NULL);
214 	if (signal_add(&sigint_ev, NULL) < 0)
215 		err(EXIT_FAILURE, "SIGINT");
216 
217 	if (init_mixer(&info, mixer) < 0)
218 		err(EXIT_FAILURE, "%s", mixer);
219 
220 	if (channel == 0 && init_rfcomm(&info) < 0)
221 		err(EXIT_FAILURE, "%s", bt_ntoa(&info.raddr, NULL));
222 
223 	if (channel && init_server(&info, channel) < 0)
224 		err(EXIT_FAILURE, "%d", channel);
225 
226 	if (verbose) {
227 		printf("Headset Info:\n");
228 		printf("\tmixer: %s\n", mixer);
229 		printf("\tladdr: %s\n", bt_ntoa(&info.laddr, NULL));
230 		printf("\traddr: %s\n", bt_ntoa(&info.raddr, NULL));
231 		printf("\tchannel: %d\n", info.channel);
232 		printf("\tvgs.dev: %d, vgm.dev: %d\n", vgs.dev, vgm.dev);
233 		if (channel) printf("\tserver channel: %d\n", channel);
234 	}
235 
236 	event_dispatch();
237 
238 	err(EXIT_FAILURE, "event_dispatch");
239 }
240 
241 static void
usage(void)242 usage(void)
243 {
244 
245 	fprintf(stderr,
246 		"usage: %s [-hv] [-c command] [-m mixer] [-p file] [-s channel]\n"
247 		"Where:\n"
248 		"\t-h           display this message\n"
249 		"\t-v           verbose output\n"
250 		"\t-c command   command to execute on answer\n"
251 		"\t-m mixer     mixer path\n"
252 		"\t-p file      write PID to file\n"
253 		"\t-s channel   register as audio gateway on channel\n"
254 		"", getprogname());
255 
256 	exit(EXIT_FAILURE);
257 }
258 
259 static void
do_signal(int s,short ev,void * arg)260 do_signal(int s, short ev, void *arg)
261 {
262 
263 	switch (s) {
264 	case SIGUSR1:
265 		ringing = 1;	/* start ringing */
266 		do_ring(0, 0, NULL);
267 		break;
268 
269 	case SIGUSR2:
270 		ringing = 0;
271 		break;
272 
273 	case SIGINT:
274 	default:
275 		exit(EXIT_SUCCESS);
276 	}
277 }
278 
279 static void
do_ring(int s,short ev,void * arg)280 do_ring(int s, short ev, void *arg)
281 {
282 	static struct timeval tv = { RING_INTERVAL, 0 };
283 
284 	if (!ringing)
285 		return;
286 
287 	send_rfcomm("RING");
288 	evtimer_add(&ring_ev, &tv);
289 }
290 
291 /*
292  * The mixer device has been twiddled. We check mic and speaker
293  * settings and send the appropriate commands to the headset,
294  */
295 static void
do_mixer(int s,short ev,void * arg)296 do_mixer(int s, short ev, void *arg)
297 {
298 	mixer_ctrl_t mc;
299 	int level;
300 
301 	memcpy(&mc, &vgs, sizeof(mc));
302 	if (ioctl(mx, AUDIO_MIXER_READ, &mc) < 0)
303 		return;
304 
305 	if (memcmp(&vgs, &mc, sizeof(mc))) {
306 		memcpy(&vgs, &mc, sizeof(mc));
307 		level = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] / BTSCO_DELTA;
308 
309 		send_rfcomm("+VGS=%d", level);
310 	}
311 
312 	memcpy(&mc, &vgm, sizeof(mc));
313 	if (ioctl(mx, AUDIO_MIXER_READ, &mc) < 0)
314 		return;
315 
316 	if (memcmp(&vgm, &mc, sizeof(mc))) {
317 		memcpy(&vgm, &mc, sizeof(mc));
318 		level = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] / BTSCO_DELTA;
319 
320 		send_rfcomm("+VGM=%d", level);
321 	}
322 }
323 
324 /*
325  * RFCOMM socket event.
326  */
327 static void
do_rfcomm(int fd,short ev,void * arg)328 do_rfcomm(int fd, short ev, void *arg)
329 {
330 	char buf[128];
331 	int len, level;
332 
333 	memset(buf, 0, sizeof(buf));
334 	len = recv(rf, buf, sizeof(buf), 0);
335 	if (len <= 0) {
336 		if (ag < 0)
337 			errx(EXIT_FAILURE, "Connection Lost");
338 
339 		event_del(&rfcomm_ev);
340 		close(rf);
341 		rf = -1;
342 		ringing = 0;
343 		return;
344 	}
345 
346 	if (verbose)
347 		printf("> %.*s\n", len, buf);
348 
349 	if (len >= 7 && strncmp(buf, "AT+CKPD", 7) == 0) {
350 		if (ringing && command != NULL) {
351 			if (verbose)
352 				printf("%% %s\n", command);
353 
354 			system(command);
355 		}
356 
357 		ringing = 0;
358 		send_rfcomm("OK");
359 		return;
360 	}
361 
362 	if (len >= 7 && strncmp(buf, "AT+VGS=", 7) == 0) {
363 		level = atoi(buf + 7);
364 		if (level < 0 || level > 15)
365 			return;
366 
367 		vgs.un.value.level[AUDIO_MIXER_LEVEL_MONO] = level * BTSCO_DELTA;
368 		if (ioctl(mx, AUDIO_MIXER_WRITE, &vgs) < 0)
369 			return;
370 
371 		send_rfcomm("OK");
372 		return;
373 	}
374 
375 	if (len >= 7 && strncmp(buf, "AT+VGM=", 7) == 0) {
376 		level = atoi(buf + 7);
377 		if (level < 0 || level > 15)
378 			return;
379 
380 		vgm.un.value.level[AUDIO_MIXER_LEVEL_MONO] = level * BTSCO_DELTA;
381 		if (ioctl(mx, AUDIO_MIXER_WRITE, &vgm) < 0)
382 			return;
383 
384 		send_rfcomm("OK");
385 		return;
386 	}
387 
388 	send_rfcomm("ERROR");
389 }
390 
391 /*
392  * got an incoming connection on the AG socket.
393  */
394 static void
do_server(int fd,short ev,void * arg)395 do_server(int fd, short ev, void *arg)
396 {
397 	bdaddr_t *raddr = arg;
398 	struct sockaddr_bt addr;
399 	socklen_t len;
400 	int s;
401 
402 	assert(raddr != NULL);
403 
404 	len = sizeof(addr);
405 	s = accept(fd, (struct sockaddr *)&addr, &len);
406 	if (s < 0)
407 		return;
408 
409 	if (rf >= 0
410 	    || len != sizeof(addr)
411 	    || addr.bt_len != sizeof(addr)
412 	    || addr.bt_family != AF_BLUETOOTH
413 	    || !bdaddr_same(raddr, &addr.bt_bdaddr)) {
414 		close(s);
415 		return;
416 	}
417 
418 	rf = s;
419 	event_set(&rfcomm_ev, rf, EV_READ | EV_PERSIST, do_rfcomm, NULL);
420 	if (event_add(&rfcomm_ev, NULL) < 0)
421 		err(EXIT_FAILURE, "rfcomm_ev");
422 }
423 
424 /*
425  * send a message to the RFCOMM socket
426  */
427 static int
send_rfcomm(const char * msg,...)428 send_rfcomm(const char *msg, ...)
429 {
430 	struct iovec iov[3];
431 	char buf[128];
432 	va_list ap;
433 
434 	if (verbose) {
435 		fputs("< ", stdout);
436 		va_start(ap, msg);
437 		vprintf(msg, ap);
438 		va_end(ap);
439 		putchar('\n');
440 	}
441 
442 	iov[0].iov_base = iov[2].iov_base = __UNCONST("\r\n");
443 	iov[0].iov_len = iov[2].iov_len = 2;
444 	va_start(ap, msg);
445 	iov[1].iov_base = buf;
446 	iov[1].iov_len = vsnprintf(buf, sizeof(buf), msg, ap);
447 	va_end(ap);
448 
449 	return writev(rf, iov, __arraycount(iov));
450 }
451 
452 /*
453  * Initialise mixer event
454  */
455 static int
init_mixer(struct btsco_info * info,const char * mixer)456 init_mixer(struct btsco_info *info, const char *mixer)
457 {
458 
459 	mx = open(mixer, O_WRONLY, 0);
460 	if (mx < 0)
461 		return -1;
462 
463 	if (ioctl(mx, BTSCO_GETINFO, info) < 0)
464 		return -1;
465 
466 	/* get initial vol settings */
467 	memset(&vgs, 0, sizeof(vgs));
468 	vgs.dev = info->vgs;
469 	if (ioctl(mx, AUDIO_MIXER_READ, &vgs) < 0)
470 		return -1;
471 
472 	memset(&vgm, 0, sizeof(vgm));
473 	vgm.dev = info->vgm;
474 	if (ioctl(mx, AUDIO_MIXER_READ, &vgm) < 0)
475 		return -1;
476 
477 	/* set up mixer changed event */
478 	if (fcntl(mx, F_SETFL, O_ASYNC) < 0)
479 		return -1;
480 
481 	signal_set(&mixer_ev, SIGIO, do_mixer, NULL);
482 	if (signal_add(&mixer_ev, NULL) < 0)
483 		return -1;
484 
485 	return 0;
486 }
487 
488 /*
489  * Initialise RFCOMM socket
490  */
491 static int
init_rfcomm(struct btsco_info * info)492 init_rfcomm(struct btsco_info *info)
493 {
494 	struct sockaddr_bt addr;
495 
496 	rf = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
497 	if (rf < 0)
498 		return -1;
499 
500 	memset(&addr, 0, sizeof(addr));
501 	addr.bt_len = sizeof(addr);
502 	addr.bt_family = AF_BLUETOOTH;
503 	bdaddr_copy(&addr.bt_bdaddr, &info->laddr);
504 
505 	if (bind(rf, (struct sockaddr *)&addr, sizeof(addr)) < 0)
506 		return -1;
507 
508 	bdaddr_copy(&addr.bt_bdaddr, &info->raddr);
509 	addr.bt_channel = info->channel;
510 
511 	if (connect(rf, (struct sockaddr *)&addr, sizeof(addr)) < 0)
512 		return -1;
513 
514 	event_set(&rfcomm_ev, rf, EV_READ | EV_PERSIST, do_rfcomm, NULL);
515 	if (event_add(&rfcomm_ev, NULL) < 0)
516 		return -1;
517 
518 	return 0;
519 }
520 
521 /*
522  * Initialise server socket
523  */
524 static int
init_server(struct btsco_info * info,int channel)525 init_server(struct btsco_info *info, int channel)
526 {
527 	struct sockaddr_bt addr;
528 
529 	ag = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
530 	if (ag < 0)
531 		return -1;
532 
533 	memset(&addr, 0, sizeof(addr));
534 	addr.bt_len = sizeof(addr);
535 	addr.bt_family = AF_BLUETOOTH;
536 	addr.bt_channel = channel;
537 	bdaddr_copy(&addr.bt_bdaddr, &info->laddr);
538 
539 	if (bind(ag, (struct sockaddr *)&addr, sizeof(addr)) < 0)
540 		return -1;
541 
542 	if (listen(ag, 1) < 0)
543 		return -1;
544 
545 	event_set(&server_ev, ag, EV_READ | EV_PERSIST, do_server, &info->raddr);
546 	if (event_add(&server_ev, NULL) < 0)
547 		return -1;
548 
549 	sdp_set_uint(&hset_channel, channel);
550 
551 	ss = sdp_open_local(NULL);
552 	if (ss == NULL)
553 		return -1;
554 
555 	if (!sdp_record_insert(ss, &info->laddr, NULL, &hset_record)) {
556 		sdp_close(ss);
557 		return -1;
558 	}
559 
560 	return 0;
561 }
562 
563 static void
remove_pid(void)564 remove_pid(void)
565 {
566 
567 	if (pidfile == NULL)
568 		return;
569 
570 	unlink(pidfile);
571 }
572 
573 static int
write_pid(void)574 write_pid(void)
575 {
576 	char *buf;
577 	int fd, len;
578 
579 	if (pidfile == NULL)
580 		return 0;
581 
582 	fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
583 	if (fd < 0)
584 		return -1;
585 
586 	len = asprintf(&buf, "%d\n", getpid());
587 	if (len > 0)
588 		write(fd, buf, len);
589 
590 	if (len >= 0 && buf != NULL)
591 		free(buf);
592 
593 	close (fd);
594 
595 	return atexit(remove_pid);
596 }
597