]> code.delx.au - pulseaudio/blob - src/modules/bt-proximity-helper.c
really create glitch-free branch
[pulseaudio] / src / modules / bt-proximity-helper.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2007 Lennart Poettering
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA.
22 ***/
23
24 /*
25 * Small SUID helper that allows us to ping a BT device. Borrows
26 * heavily from bluez-utils' l2ping, which is licensed as GPL2+, too
27 * and comes with a copyright like this:
28 *
29 * Copyright (C) 2000-2001 Qualcomm Incorporated
30 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
31 * Copyright (C) 2002-2007 Marcel Holtmann <marcel@holtmann.org>
32 *
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #undef NDEBUG
40
41 #include <assert.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <stdlib.h>
46 #include <sys/time.h>
47 #include <sys/select.h>
48
49 #include <bluetooth/bluetooth.h>
50 #include <bluetooth/hci.h>
51 #include <bluetooth/hci_lib.h>
52 #include <bluetooth/l2cap.h>
53
54 #define PING_STRING "PulseAudio"
55 #define IDENT 200
56 #define TIMEOUT 4
57 #define INTERVAL 2
58
59 static void update_status(int found) {
60 static int status = -1;
61
62 if (!found && status != 0)
63 printf("-");
64 if (found && status <= 0)
65 printf("+");
66
67 fflush(stdout);
68 status = !!found;
69 }
70
71 int main(int argc, char *argv[]) {
72 struct sockaddr_l2 addr;
73 union {
74 l2cap_cmd_hdr hdr;
75 uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
76 } packet;
77 int fd = -1;
78 uint8_t id = IDENT;
79 int connected = 0;
80
81 assert(argc == 2);
82
83 for (;;) {
84 fd_set fds;
85 struct timeval end;
86 ssize_t r;
87
88 if (!connected) {
89
90 if (fd >= 0)
91 close(fd);
92
93 if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
94 fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
95 goto finish;
96 }
97
98 memset(&addr, 0, sizeof(addr));
99 addr.l2_family = AF_BLUETOOTH;
100 bacpy(&addr.l2_bdaddr, BDADDR_ANY);
101
102 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
103 fprintf(stderr, "bind() failed: %s", strerror(errno));
104 goto finish;
105 }
106
107 memset(&addr, 0, sizeof(addr));
108 addr.l2_family = AF_BLUETOOTH;
109 str2ba(argv[1], &addr.l2_bdaddr);
110
111 if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
112
113 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
114 update_status(0);
115 sleep(INTERVAL);
116 continue;
117 }
118
119 fprintf(stderr, "connect() failed: %s", strerror(errno));
120 goto finish;
121 }
122
123 connected = 1;
124 }
125
126 assert(connected);
127
128 memset(&packet, 0, sizeof(packet));
129 strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
130 packet.hdr.ident = id;
131 packet.hdr.len = htobs(sizeof(PING_STRING));
132 packet.hdr.code = L2CAP_ECHO_REQ;
133
134 if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
135
136 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
137 update_status(0);
138 connected = 0;
139 sleep(INTERVAL);
140 continue;
141 }
142
143 fprintf(stderr, "send() failed: %s", strerror(errno));
144 goto finish;
145 }
146
147 assert(r == sizeof(packet));
148
149 gettimeofday(&end, NULL);
150 end.tv_sec += TIMEOUT;
151
152 for (;;) {
153 struct timeval now, delta;
154
155 gettimeofday(&now, NULL);
156
157 if (timercmp(&end, &now, <=)) {
158 update_status(0);
159 connected = 0;
160 sleep(INTERVAL);
161 break;
162 }
163
164 timersub(&end, &now, &delta);
165
166 FD_ZERO(&fds);
167 FD_SET(fd, &fds);
168
169 if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
170 fprintf(stderr, "select() failed: %s", strerror(errno));
171 goto finish;
172 }
173
174 if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
175
176 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
177 update_status(0);
178 connected = 0;
179 sleep(INTERVAL);
180 break;
181 }
182
183 fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
184 goto finish;
185 }
186
187 assert(r >= L2CAP_CMD_HDR_SIZE);
188
189 if (packet.hdr.ident != id)
190 continue;
191
192 if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
193
194 if (++id >= 0xFF)
195 id = IDENT;
196
197 update_status(1);
198 sleep(INTERVAL);
199 break;
200 }
201 }
202 }
203
204 finish:
205
206 if (fd >= 0)
207 close(fd);
208
209 return 1;
210 }