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