doc: update README, libble is unmaintained, has moved to libsigrok
[libble/gsi.git] / main-rfcomm.c
blobd3eb1cf0e47aa4368d78448983aa807c5e2b3634
1 /*
2 * This file is part of the libble project.
4 * Copyright (C) 2018-2019 Gerhard Sittig <gerhard.sittig@gmx.net>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * Simple RFCOMM server implementation, modelled after
22 * http://people.csail.mit.edu/albert/bluez-intro/x502.html
24 * Creates a server socket, accepts up to one client at a time, reflects
25 * received data to the server (echo mode), terminates upon reception of
26 * CTRL-C / SIGTERM and friends.
28 * Was specifically written as a standalone application not using libble
29 * to demonstrate interoperability.
32 /* {{{ includes, defines, globals */
34 #include <bluetooth/bluetooth.h>
35 #include <bluetooth/rfcomm.h>
36 #include <poll.h>
37 #include <signal.h>
38 #include <stdarg.h>
39 #include <stdint.h>
40 #include <stdio.h>
41 #include <sys/socket.h>
42 #include <unistd.h>
44 #ifndef ARRAY_SIZE
45 # define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
46 #endif
48 static bdaddr_t local_addr = *BDADDR_ANY;
49 static int channel = 1;
51 static int log_level = 1;
52 static int done = 0;
54 /* }}} includes, defines, globals */
55 /* {{{ logging, progress messages */
57 static void log_msg(int level, const char *fmt, ...)
59 va_list arg;
60 FILE *log_stream;
62 if (level > log_level)
63 return;
65 log_stream = stdout;
66 va_start(arg, fmt);
67 vfprintf(log_stream, fmt, arg);
68 va_end(arg);
69 fflush(log_stream);
72 /* }}} logging, progress messages */
73 /* {{{ helper routines */
75 static int server_socket(void)
77 int fd;
78 struct sockaddr_rc addr;
79 int rc;
81 log_msg(2, "socket()\n");
82 fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
83 if (fd < 0) {
84 perror("socket()");
85 return -1;
88 log_msg(2, "bind()\n");
89 memset(&addr, 0, sizeof(addr));
90 addr.rc_family = AF_BLUETOOTH;
91 addr.rc_channel = channel;
92 memcpy(&addr.rc_bdaddr, &local_addr, sizeof(addr.rc_bdaddr));
93 rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
94 if (rc < 0) {
95 perror("bind()");
96 close(fd);
97 return -1;
100 log_msg(2, "listen()\n");
101 rc = listen(fd, -1);
102 if (rc < 0) {
103 perror("listen()");
104 close(fd);
105 return -1;
108 log_msg(1, "listening ...\n");
109 return fd;
112 static void close_server(int fd)
115 log_msg(2, "close() server\n");
116 close(fd);
117 log_msg(1, "server closed\n");
120 static int accept_client(int srv_fd)
122 int cli_fd;
123 socklen_t len;
124 struct sockaddr_rc addr;
125 char peer[20];
127 if (srv_fd < 0)
128 return -1;
130 log_msg(2, "accept()\n");
131 len = sizeof(addr);
132 cli_fd = accept(srv_fd, (struct sockaddr *)&addr, &len);
133 if (cli_fd < 0) {
134 perror("accept()");
135 return -1;
137 ba2str(&addr.rc_bdaddr, peer);
138 log_msg(1, "accept: fd %d, client addr %s\n", cli_fd, peer);
139 return cli_fd;
142 static int process_client(int fd, const char *msg)
144 ssize_t rdlen, wrlen;
146 rdlen = strlen(msg);
147 log_msg(2, "write()\n");
148 wrlen = write(fd, msg, rdlen);
149 if (wrlen < 0) {
150 perror("write()");
151 return -1;
153 log_msg(1, "sent: fd %d, data: %s\n", fd, msg);
154 if (wrlen != rdlen) {
155 log_msg(0, "write mismatch: fd %d, rcvd %zd, sent %zd\n", fd, rdlen, wrlen);
157 return 0;
160 static int receive_client(int fd)
162 char buff[256];
163 ssize_t rdlen;
164 int rc;
166 if (fd < 0)
167 return -1;
169 log_msg(2, "read()\n");
170 rdlen = read(fd, buff, sizeof(buff));
171 if (rdlen < 0) {
172 perror("read()");
173 return -1;
175 buff[rdlen] = '\0';
176 log_msg(1, "rcvd: fd %d, data: %s\n", fd, buff);
177 rc = process_client(fd, buff);
178 if (rc < 0)
179 return -1;
180 return rdlen;
183 static void close_client(int fd)
186 log_msg(2, "close() client\n");
187 close(fd);
188 log_msg(1, "close: fd %d, client gone\n", fd);
191 /* }}} helper routines */
192 /* {{{ main program */
194 static void signal_handler(int sig)
197 done++;
200 static int is_readable(int fd)
202 struct pollfd fds[1];
203 int rc;
205 memset(fds, 0, sizeof(fds));
206 fds[0].fd = fd;
207 fds[0].events = POLLIN;
208 rc = poll(fds, ARRAY_SIZE(fds), 0);
209 if (rc < 1)
210 return 0;
212 * Implementor's note: Silently accept "error" conditions here
213 * as well, to have the socket closed on failed read. Or else
214 * we'd need another has_error() test routine, too.
216 return 1;
219 int main(int argc, char *argv[])
221 int server_fd, client_fd;
222 int rc;
224 signal(SIGHUP, signal_handler);
225 signal(SIGINT, signal_handler);
226 signal(SIGTERM, signal_handler);
228 server_fd = server_socket();
229 client_fd = -1;
230 while (!done) {
231 if (is_readable(server_fd)) {
232 client_fd = accept_client(server_fd);
233 if (client_fd < 0)
234 done++;
236 if (client_fd >= 0 && is_readable(client_fd)) {
237 rc = receive_client(client_fd);
238 if (rc <= 0) {
239 close_client(client_fd);
240 client_fd = -1;
243 usleep(10000);
245 close_server(server_fd);
246 return 0;
249 /* }}} main program */
251 * vim:foldmethod=marker: