summaryrefslogtreecommitdiffstats
path: root/tests/btclient/btclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/btclient/btclient.c')
-rw-r--r--tests/btclient/btclient.c581
1 files changed, 581 insertions, 0 deletions
diff --git a/tests/btclient/btclient.c b/tests/btclient/btclient.c
new file mode 100644
index 00000000..2d52718f
--- /dev/null
+++ b/tests/btclient/btclient.c
@@ -0,0 +1,581 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <poll.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/l2cap.h>
+
+#include <dbus/dbus.h>
+
+#include "btclient.h"
+
+#define UNUSED(x) do { (void)x; } while(0)
+
+const char *xmldefn =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+ "\n"
+ "<record>\n"
+ " <attribute id=\"0x0000\">\n"
+ " <uint32 value=\"0x00010010\" />\n"
+ " </attribute>\n"
+ " <attribute id=\"0x0001\">\n"
+ " <sequence>\n"
+ " <uuid value=\"" ECHO_SERVICE_UUID "\" />\n"
+ " </sequence>\n"
+ " </attribute>\n"
+ " <attribute id=\"0x0003\">\n"
+ " <uuid value=\"" ECHO_SERVICE_UUID "\" />\n"
+ " </attribute>\n"
+ " <attribute id=\"0x0004\">\n"
+ " <sequence>\n"
+ " <sequence>\n"
+ " <uuid value=\"0x0100\" />\n"
+ " </sequence>\n"
+ " <sequence>\n"
+ " <uuid value=\"0x0003\" />\n"
+ " <uint8 value=\"0x0a\" />\n"
+ " </sequence>\n"
+ " </sequence>\n"
+ " </attribute>\n"
+ " <attribute id=\"0x0005\">\n"
+ " <sequence>\n"
+ " <uuid value=\"0x1002\" />\n"
+ " </sequence>\n"
+ " </attribute>\n"
+ " <attribute id=\"0x0100\">\n"
+ " <text value=\"QtConnectivity Test Echo Server\" />\n"
+ " </attribute>\n"
+ " <attribute id=\"0x0101\">\n"
+ " <text value=\"QtConnectivity test echo server\" />\n"
+ " </attribute>\n"
+ " <attribute id=\"0x0102\">\n"
+ " <text value=\"Nokia, QtDF\" />\n"
+ " </attribute>\n"
+ "</record>\n";
+
+typedef void (*actionhandler_t)(int, short, void *);
+
+struct fdlist {
+ struct fdlist *next;
+ int fd;
+ short events;
+ actionhandler_t hanlder;
+ void *ptr;
+};
+
+struct fdlist *head = 0;
+
+void removefd(struct fdlist *fdl, int fd){
+ struct fdlist *prev = fdl;
+ while(fdl && fdl->fd != fd) {
+ prev = fdl;
+ fdl = fdl->next;
+ }
+ assert(fdl);
+
+ prev->next = fdl->next;
+ free(fdl);
+}
+
+struct fdlist *addfd(struct fdlist *fdl, int fd, short events, actionhandler_t hanlder, void *data){
+ struct fdlist *n = malloc(sizeof(struct fdlist));
+ if(fdl){
+ while(fdl->next)
+ fdl = fdl->next;
+ fdl->next = n;
+ }
+
+ n->next = 0;
+ n->fd = fd;
+ n->events = events;
+ n->hanlder = hanlder;
+ n->ptr = data;
+
+ return n;
+
+}
+
+int mkpoll(struct fdlist *fdl, struct pollfd *fds, int max){
+ int num = 0;
+ while(fdl && num < max){
+ fds[num].events = fdl->events;
+ fds[num].fd = fdl->fd;
+ fdl = fdl->next;
+ num++;
+ }
+ assert(num <= max);
+ return num;
+}
+
+int createListening(int channel){
+ struct sockaddr_rc addr;
+ int flags;
+ socklen_t addrLength = sizeof(addr);
+ int sk, opt = 1;
+
+ /* Create echo socket on bt channel 10 */
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+
+ addr.rc_family = AF_BLUETOOTH;
+ addr.rc_channel = channel;
+ memset(&addr.rc_bdaddr, 0, sizeof(bdaddr_t));
+
+ if (bind(sk, (struct sockaddr *)&addr, addrLength) < 0) {
+ perror("Failed to bind to Bluetooth socket");
+ return -1;
+ }
+
+ if(setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0){
+ perror("Warning: Failed to set SO_REUSEADDR options");
+ }
+
+ // ensure that O_NONBLOCK is set on new connections.
+ flags = fcntl(sk, F_GETFL, 0);
+ if (!(flags & O_NONBLOCK))
+ fcntl(sk, F_SETFL, flags | O_NONBLOCK);
+
+ if (listen(sk, 10) < 0){
+ perror("Can't start listening on bluetooth socket");
+ return -1;
+ }
+ printf("Got socket: %d\n", sk);
+ return sk;
+}
+
+int createListeningL2cap(int psm){
+ struct sockaddr_l2 addr;
+ int flags;
+ socklen_t addrLength = sizeof(struct sockaddr_l2);
+ int sk, opt = 1;
+
+ sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+
+ memset(&addr,0, addrLength);
+ addr.l2_family = AF_BLUETOOTH;
+ addr.l2_psm = htobs(psm);
+ memset(&addr.l2_bdaddr, 0, sizeof(bdaddr_t));
+
+ if (bind(sk, (struct sockaddr *)&addr, addrLength) < 0) {
+ perror("Failed to bind to Bluetooth socket");
+ return -1;
+ }
+
+ if(setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0){
+ perror("Warning: Failed to set SO_REUSEADDR options");
+ }
+
+ // ensure that O_NONBLOCK is set on new connections.
+ flags = fcntl(sk, F_GETFL, 0);
+ if (!(flags & O_NONBLOCK))
+ fcntl(sk, F_SETFL, flags | O_NONBLOCK);
+
+ if (listen(sk, 10) < 0){
+ perror("Can't start listening on bluetooth socket");
+ return -1;
+ }
+ printf("Got L2cap socket: %d\n", sk);
+ return sk;
+}
+
+void echo_process(int fd, short revents, void *data){
+ char buff[1024];
+
+ UNUSED(data);
+
+ if(revents&POLLHUP || revents&POLLNVAL){
+ close(fd);
+ removefd(head, fd);
+ return;
+ }
+ int size = read(fd, buff, 1024);
+ printf("%d: Read: %d bytes\n", fd, size);
+ size = write(fd, buff, size);
+ printf("%d: Wrote: %d bytes\n", fd, size);
+}
+
+void echo_connect(int fd, short revents, void *data){
+
+ struct sockaddr_rc addr;
+ unsigned char *a;
+ socklen_t length = sizeof(struct sockaddr_rc);
+
+ UNUSED(data);
+ UNUSED(revents);
+
+ printf("Echo connect started\n");
+
+ int sk = accept(fd, (struct sockaddr *)&addr, &length);
+
+ a = addr.rc_bdaddr.b;
+ printf("Connect from: %02x:%02x:%02x:%02x:%02x:%02x port %d\n", a[5], a[4], a[3], a[2], a[1], a[0], addr.rc_channel);
+ addfd(head, sk, POLLIN|POLLHUP|POLLNVAL, echo_process, 0);
+
+}
+
+struct connectioninfo {
+ int socket;
+ FILE *pipe;
+ struct sockaddr_rc addr_them;
+};
+
+void id_greeting(int fd, short revents, void *data){
+ int err;
+ unsigned char *a, *b;
+ struct sockaddr_rc addr;
+ char buffer[1024];
+ char name[100];
+ socklen_t addrLength = sizeof(addr);
+ struct connectioninfo *ci;
+
+ if(revents&POLLHUP || revents&POLLNVAL){
+ close(fd);
+ removefd(head, fd);
+ return;
+ }
+
+ ci = (struct connectioninfo *)data;
+
+ err = getsockname(ci->socket, (struct sockaddr *)&addr, &addrLength);
+ if(err < 0){
+ perror("failed to get socket name");
+ exit(-1);
+ }
+ a = addr.rc_bdaddr.b;
+
+ b = ci->addr_them.rc_bdaddr.b;
+
+ err = fscanf(ci->pipe, "%s", name);
+ removefd(head, fd);
+ pclose(ci->pipe);
+
+ sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x %d %s %02x:%02x:%02x:%02x:%02x:%02x %d\n",
+ a[5], a[4], a[3], a[2], a[1], a[0], addr.rc_channel, name,
+ b[5], b[4], b[3], b[2], b[1], b[0], ci->addr_them.rc_channel);
+ if(write(ci->socket, buffer, strlen(buffer)) < 0){
+ perror("Failed to write greeting");
+ }
+ printf("%s", buffer);
+ free(ci);
+
+}
+
+void id_process(int fd, short revents, void *data){
+ int err;
+ unsigned char *b;
+ struct sockaddr_rc addr_them;
+ char cmd[100];
+ socklen_t addrLength = sizeof(addr_them);
+ struct connectioninfo *ci;
+
+ UNUSED(data);
+
+ if(revents&POLLHUP || revents&POLLNVAL){
+ close(fd);
+ removefd(head, fd);
+ return;
+ }
+
+ err = getpeername(fd, (struct sockaddr *)&addr_them, &addrLength);
+ if(err < 0){
+ perror("failed to get socket name");
+ exit(-1);
+ }
+ b = addr_them.rc_bdaddr.b;
+
+ sprintf(cmd, "hcitool name %02x:%02x:%02x:%02x:%02x:%02x", b[5], b[4], b[3], b[2], b[1], b[0]);
+
+ FILE *f = popen(cmd, "r");
+ ci = malloc(sizeof(struct connectioninfo));
+ ci->socket = fd;
+ ci->pipe = f;
+ memcpy(&ci->addr_them, &addr_them, sizeof(struct sockaddr_rc));
+
+ addfd(head, fileno(f), POLLIN, id_greeting, ci);
+
+}
+
+void id_connect(int fd, short revents, void *data){
+
+ struct sockaddr_rc addr;
+ unsigned char *a;
+ socklen_t length = sizeof(struct sockaddr_rc);
+
+ UNUSED(revents);
+ UNUSED(data);
+
+ printf("ID connect started\n");
+
+ int sk = accept(fd, (struct sockaddr *)&addr, &length);
+
+ a = addr.rc_bdaddr.b;
+ printf("Connect from: %02x:%02x:%02x:%02x:%02x:%02x port %d\n", a[5], a[4], a[3], a[2], a[1], a[0], addr.rc_channel);
+ addfd(head, sk, POLLIN|POLLHUP|POLLNVAL, id_process, 0);
+ id_process(sk, POLLIN, 0x0);
+}
+
+void id_print(int socket){
+
+ int err;
+ unsigned char *a;
+ struct sockaddr_rc addr;
+ socklen_t addrLength = sizeof(addr);
+
+ err = getsockname(socket, (struct sockaddr *)&addr, &addrLength);
+ if(err < 0){
+ perror("failed to get socket name");
+ exit(-1);
+ }
+ a = addr.rc_bdaddr.b;
+ printf("We are: %d:%d:%d:%d:%d:%d port %d\n", a[0], a[1], a[2], a[3], a[4], a[5], addr.rc_channel);
+}
+
+void l2_process(int fd, short revents, void *data){
+ char buff[1024];
+
+ UNUSED(data);
+
+ if(revents&POLLHUP || revents&POLLNVAL){
+ close(fd);
+ removefd(head, fd);
+ return;
+ }
+ int size = read(fd, buff, 1024);
+ printf("%d: Read: %d bytes\n", fd, size);
+ size = write(fd, buff, size);
+ printf("%d: Wrote: %d bytes\n", fd, size);
+}
+
+void l2_connect(int fd, short revents, void *data){
+
+ struct sockaddr_l2 addr;
+ unsigned char *a;
+ socklen_t length = sizeof(struct sockaddr_l2);
+
+ UNUSED(revents);
+ UNUSED(data);
+
+ printf("L2 connect started\n");
+
+ int sk = accept(fd, (struct sockaddr *)&addr, &length);
+
+ a = addr.l2_bdaddr.b;
+ printf("Connect from: %02x:%02x:%02x:%02x:%02x:%02x port %d\n", a[5], a[4], a[3], a[2], a[1], a[0], addr.l2_psm);
+ addfd(head, sk, POLLIN|POLLHUP|POLLNVAL, l2_process, 0);
+ l2_process(sk, POLLIN, 0x0);
+}
+
+void dbus_error(int fd, short revents, void *data){
+ UNUSED(fd);
+ UNUSED(data);
+ if(revents&POLLHUP || revents&POLLNVAL){
+ printf("dBus connection failed, shutting down\n");
+ exit(-1);
+ }
+}
+
+void registerService()
+{
+ DBusMessage* msg;
+ DBusMessageIter args;
+ DBusConnection* conn;
+ DBusError err;
+ DBusPendingCall* pending;
+ dbus_uint32_t level;
+ int fd;
+
+ char path[PATH_MAX];
+ const char *ret_string = 0;
+
+ // initialiset the errors
+ dbus_error_init(&err);
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "Connection Error (%s)\n", err.message);
+ dbus_error_free(&err);
+ return;
+ }
+
+ // create a new method call and check for errors
+ msg = dbus_message_new_method_call("org.bluez", // target for the method call
+ "/", // object to call on
+ "org.bluez.Manager", // interface to call on
+ "DefaultAdapter"); // method name
+
+ if (!dbus_connection_send_with_reply (conn, msg, &pending, -1)) {
+ fprintf(stderr, "dbus-send failed: Out Of Memory!\n");
+ return;
+ }
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(msg);
+ dbus_pending_call_block(pending);
+ msg = dbus_pending_call_steal_reply(pending);
+ dbus_pending_call_unref(pending);
+
+ if (!dbus_message_iter_init(msg, &args))
+ fprintf(stderr, "Message has no arguments!\n");
+ else if (DBUS_TYPE_OBJECT_PATH != dbus_message_iter_get_arg_type(&args))
+ fprintf(stderr, "Argument is not an object path!\n");
+ else
+ dbus_message_iter_get_basic(&args, &ret_string);
+
+ if(!ret_string){
+ fprintf(stderr, "Failed to get bluez path\n");
+ return;
+ }
+
+ strcpy(path, ret_string);
+ // change path to use any
+ strcpy(rindex(path, '/'), "/any");
+
+ printf("Using path: %s\n", path);
+
+ dbus_message_unref(msg);
+
+ // create a new method call and check for errors
+ msg = dbus_message_new_method_call("org.bluez", // target for the method call
+ path, // object to call on
+ "org.bluez.Service", // interface to call on
+ "AddRecord"); // method name
+
+ if(!dbus_message_append_args(msg, DBUS_TYPE_STRING, &xmldefn, DBUS_TYPE_INVALID)){
+ fprintf(stderr, "Failed to append args\n");
+ return;
+ }
+
+ // send message and get a handle for a reply
+ if (!dbus_connection_send_with_reply (conn, msg, &pending, -1)) { // -1 is default timeout
+ fprintf(stderr, "Out Of Memory!\n");
+ exit(1);
+ }
+ dbus_connection_flush(conn);
+
+ // free message
+ dbus_message_unref(msg);
+
+ // block until we receive a reply
+ dbus_pending_call_block(pending);
+
+ // get the reply message
+ msg = dbus_pending_call_steal_reply(pending);
+ if (NULL == msg) {
+ fprintf(stderr, "Reply Null\n");
+ return;
+ }
+ // free the pending message handle
+ dbus_pending_call_unref(pending);
+
+ // read the parameters
+ if (!dbus_message_iter_init(msg, &args))
+ fprintf(stderr, "Message has no arguments!\n");
+ else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args))
+ fprintf(stderr, "Argument is not int!\n");
+ else
+ dbus_message_iter_get_basic(&args, &level);
+
+ printf("Got handle: 0x%x\n", level);
+
+ // free reply
+ dbus_message_unref(msg);
+
+ dbus_connection_get_socket(conn, &fd);
+ addfd(head, fd, POLLHUP|POLLNVAL, dbus_error, 0x0);
+}
+
+int main(int argc, char **argv)
+{
+ int socket;
+#define MAX_POLL 256
+ struct pollfd fds[MAX_POLL];
+
+ UNUSED(argc);
+ UNUSED(argv);
+
+ registerService();
+
+ socket = createListening(10);
+ head = addfd(head, socket, POLLIN, echo_connect, 0); // first creation
+
+ socket = createListening(11);
+ addfd(head, socket, POLLIN, id_connect, 0);
+
+ socket = createListeningL2cap(0x1011); // must be > 0x1001 and odd
+ addfd(head, socket, POLLIN, l2_connect, 0);
+
+ while(1){
+ int n = mkpoll(head, fds, MAX_POLL);
+ if(poll(fds, n, -1)){
+ struct fdlist *fdl = head;
+ int i;
+
+ for(i = 0; i < n; i++){
+ if(fds[i].revents){
+ while(fdl && fdl->fd != fds[i].fd)
+ fdl = fdl->next;
+ assert(fdl);
+ fdl->hanlder(fds[i].fd, fds[i].revents, fdl->ptr);
+ }
+ }
+ }
+ else {
+ perror("Poll failed");
+ exit(-1);
+ }
+ }
+
+ return 0;
+}