summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am16
-rw-r--r--common/error.c126
-rw-r--r--common/error.h49
-rw-r--r--common/glib-helper.c1283
-rw-r--r--common/glib-helper.h96
-rw-r--r--common/logging.c106
-rw-r--r--common/logging.h38
-rw-r--r--common/oui.c109
-rw-r--r--common/oui.h25
-rw-r--r--common/ppoll.h16
-rw-r--r--common/sdp-glib.c239
-rw-r--r--common/sdp-xml.c793
-rw-r--r--common/sdp-xml.h61
-rw-r--r--common/test_textfile.c187
-rw-r--r--common/textfile.c463
-rw-r--r--common/textfile.h42
-rw-r--r--common/uinput.h586
17 files changed, 4235 insertions, 0 deletions
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644
index 00000000..6ae5ed7a
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,16 @@
+
+noinst_LIBRARIES = libhelper.a
+
+libhelper_a_SOURCES = oui.h oui.c textfile.h textfile.c \
+ logging.h logging.c error.h error.c \
+ glib-helper.h glib-helper.c sdp-xml.h sdp-xml.c sdp-glib.c
+
+noinst_PROGRAMS = test_textfile
+
+test_textfile_LDADD = libhelper.a
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @GDBUS_CFLAGS@
+
+EXTRA_DIST = ppoll.h uinput.h
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/common/error.c b/common/error.c
new file mode 100644
index 00000000..bc813d74
--- /dev/null
+++ b/common/error.c
@@ -0,0 +1,126 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2007-2008 Fabien Chevalier <fabchevalier@free.fr>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <gdbus.h>
+
+#include "error.h"
+
+DBusMessage *create_errno_message(DBusMessage *msg, int err)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ strerror(err));
+}
+
+/**
+ org.bluez.Error.ConnectionAttemptFailed:
+
+ An unexpected error (other than DeviceUnreachable) error has occured while
+ attempting a connection to a device
+*/
+DBusHandlerResult error_connection_attempt_failed(DBusConnection *conn, DBusMessage *msg, int err)
+{
+ return error_common_reply(conn, msg,
+ ERROR_INTERFACE ".ConnectionAttemptFailed",
+ err > 0 ? strerror(err) : "Connection attempt failed");
+}
+
+/**
+ org.bluez.Error.NotSupported:
+
+ The remote device does not support the expected feature.
+ Examples of use: trying to connect to audio device while audio is not
+ declared in device sdp record.
+*/
+DBusHandlerResult error_not_supported(DBusConnection *conn, DBusMessage *msg)
+{
+ return error_common_reply(conn, msg, ERROR_INTERFACE ".NotSupported",
+ "Not supported");
+}
+
+/**
+ org.bluez.Error.Canceled:
+
+ The operation was canceled.
+ Examples of use : autorization process canceled, connection canceled
+*/
+DBusHandlerResult error_canceled(DBusConnection *conn, DBusMessage *msg,
+ const char *str)
+{
+ return error_common_reply(conn, msg, ERROR_INTERFACE ".Canceled", str);
+}
+
+/**
+ org.bluez.Error.Failed:
+
+ This is a the most generic error.
+ desc filed is MANDATORY
+*/
+DBusHandlerResult error_failed(DBusConnection *conn, DBusMessage *msg,
+ const char * desc)
+{
+ return error_common_reply(conn, msg, ERROR_INTERFACE ".Failed", desc);
+}
+
+/**
+ org.bluez.Error.Failed:
+
+ This is a the most generic error, instantiated form a UNIX errno number.
+*/
+DBusHandlerResult error_failed_errno(DBusConnection *conn, DBusMessage *msg,
+ int err)
+{
+ const char *desc = strerror(err);
+
+ return error_failed(conn, msg, desc);
+}
+
+/* Helper function - internal use only */
+DBusHandlerResult error_common_reply(DBusConnection *conn, DBusMessage *msg,
+ const char *name, const char *descr)
+{
+ DBusMessage *derr;
+ dbus_bool_t ret;
+
+ if (!conn || !msg)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ derr = dbus_message_new_error(msg, name, descr);
+ if (!derr)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ ret = dbus_connection_send(conn, derr, NULL);
+
+ dbus_message_unref(derr);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
diff --git a/common/error.h b/common/error.h
new file mode 100644
index 00000000..68ce105a
--- /dev/null
+++ b/common/error.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2007-2008 Fabien Chevalier <fabchevalier@free.fr>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <dbus/dbus.h>
+
+#define ERROR_INTERFACE "org.bluez.Error"
+
+DBusMessage *create_errno_message(DBusMessage *msg, int err);
+
+DBusHandlerResult error_connection_attempt_failed(DBusConnection *conn,
+ DBusMessage *msg,
+ int err);
+
+DBusHandlerResult error_not_supported(DBusConnection *conn,
+ DBusMessage *msg);
+
+DBusHandlerResult error_canceled(DBusConnection *conn, DBusMessage *msg,
+ const char *str);
+
+DBusHandlerResult error_failed(DBusConnection *conn, DBusMessage *msg,
+ const char *desc);
+
+DBusHandlerResult error_failed_errno(DBusConnection *conn, DBusMessage *msg,
+ int err);
+
+DBusHandlerResult error_common_reply(DBusConnection *conn, DBusMessage *msg,
+ const char *name, const char *descr);
diff --git a/common/glib-helper.c b/common/glib-helper.c
new file mode 100644
index 00000000..f0435aca
--- /dev/null
+++ b/common/glib-helper.c
@@ -0,0 +1,1283 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "glib-helper.h"
+
+typedef int (*resolver_t) (int fd, char *src, char *dst);
+typedef BtIOError (*connect_t) (BtIO *io, BtIOFunc func);
+typedef BtIOError (*listen_t) (BtIO *io, BtIOFunc func);
+
+int set_nonblocking(int fd)
+{
+ long arg;
+
+ arg = fcntl(fd, F_GETFL);
+ if (arg < 0)
+ return -errno;
+
+ /* Return if already nonblocking */
+ if (arg & O_NONBLOCK)
+ return 0;
+
+ arg |= O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, arg) < 0)
+ return -errno;
+
+ return 0;
+}
+
+struct io_context {
+ int fd;
+ GIOChannel *io;
+ BtIOFunc func;
+ bt_io_callback_t cb;
+ resolver_t resolver;
+ gpointer user_data;
+};
+
+struct bt_io {
+ char src[18];
+ char dst[18];
+ guint32 flags;
+ guint8 channel;
+ guint16 psm;
+ guint16 mtu;
+ BtIOTransport type;
+ connect_t connect;
+ listen_t listen;
+ struct io_context *io_ctxt;
+ int refcount;
+};
+
+struct search_context {
+ bdaddr_t src;
+ bdaddr_t dst;
+ sdp_session_t *session;
+ bt_callback_t cb;
+ bt_destroy_t destroy;
+ gpointer user_data;
+ uuid_t uuid;
+};
+
+static GSList *context_list = NULL;
+
+static void search_context_cleanup(struct search_context *ctxt)
+{
+ context_list = g_slist_remove(context_list, ctxt);
+
+ if (ctxt->destroy)
+ ctxt->destroy(ctxt->user_data);
+
+ g_free(ctxt);
+}
+
+static void search_completed_cb(uint8_t type, uint16_t status,
+ uint8_t *rsp, size_t size, void *user_data)
+{
+ struct search_context *ctxt = user_data;
+ sdp_list_t *recs = NULL;
+ int scanned, seqlen = 0, bytesleft = size;
+ uint8_t dataType;
+ int err = 0;
+
+ if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ scanned = sdp_extract_seqtype_safe(rsp, bytesleft, &dataType, &seqlen);
+ if (!scanned || !seqlen)
+ goto done;
+
+ rsp += scanned;
+ bytesleft -= scanned;
+ do {
+ sdp_record_t *rec;
+ int recsize;
+
+ recsize = 0;
+ rec = sdp_extract_pdu_safe(rsp, bytesleft, &recsize);
+ if (!rec)
+ break;
+
+ if (!recsize) {
+ sdp_record_free(rec);
+ break;
+ }
+
+ scanned += recsize;
+ rsp += recsize;
+ bytesleft -= recsize;
+
+ recs = sdp_list_append(recs, rec);
+ } while (scanned < size && bytesleft > 0);
+
+done:
+ sdp_close(ctxt->session);
+
+ if (ctxt->cb)
+ ctxt->cb(recs, err, ctxt->user_data);
+
+ search_context_cleanup(ctxt);
+}
+
+static gboolean search_process_cb(GIOChannel *chan,
+ GIOCondition cond, void *user_data)
+{
+ struct search_context *ctxt = user_data;
+ int err = 0;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ err = EIO;
+ goto failed;
+ }
+
+ if (sdp_process(ctxt->session) < 0)
+ goto failed;
+
+ return TRUE;
+
+failed:
+ if (err) {
+ sdp_close(ctxt->session);
+
+ if (ctxt->cb)
+ ctxt->cb(NULL, err, ctxt->user_data);
+
+ search_context_cleanup(ctxt);
+ }
+
+ return FALSE;
+}
+
+static gboolean connect_watch(GIOChannel *chan, GIOCondition cond, gpointer user_data)
+{
+ struct search_context *ctxt = user_data;
+ sdp_list_t *search, *attrids;
+ uint32_t range = 0x0000ffff;
+ socklen_t len;
+ int sk, err = 0;
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ len = sizeof(err);
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ if (err != 0)
+ goto failed;
+
+ if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) {
+ err = EIO;
+ goto failed;
+ }
+
+ search = sdp_list_append(NULL, &ctxt->uuid);
+ attrids = sdp_list_append(NULL, &range);
+ if (sdp_service_search_attr_async(ctxt->session,
+ search, SDP_ATTR_REQ_RANGE, attrids) < 0) {
+ sdp_list_free(attrids, NULL);
+ sdp_list_free(search, NULL);
+ err = EIO;
+ goto failed;
+ }
+
+ sdp_list_free(attrids, NULL);
+ sdp_list_free(search, NULL);
+
+ /* Set callback responsible for update the internal SDP transaction */
+ g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ search_process_cb, ctxt);
+ return FALSE;
+
+failed:
+ sdp_close(ctxt->session);
+
+ if (ctxt->cb)
+ ctxt->cb(NULL, -err, ctxt->user_data);
+
+ search_context_cleanup(ctxt);
+
+ return FALSE;
+}
+
+static int create_search_context(struct search_context **ctxt,
+ const bdaddr_t *src, const bdaddr_t *dst,
+ uuid_t *uuid)
+{
+ sdp_session_t *s;
+ GIOChannel *chan;
+
+ if (!ctxt)
+ return -EINVAL;
+
+ s = sdp_connect(src, dst, SDP_NON_BLOCKING);
+ if (!s)
+ return -errno;
+
+ *ctxt = g_try_malloc0(sizeof(struct search_context));
+ if (!*ctxt) {
+ sdp_close(s);
+ return -ENOMEM;
+ }
+
+ bacpy(&(*ctxt)->src, src);
+ bacpy(&(*ctxt)->dst, dst);
+ (*ctxt)->session = s;
+ (*ctxt)->uuid = *uuid;
+
+ chan = g_io_channel_unix_new(sdp_get_socket(s));
+ g_io_add_watch(chan, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ connect_watch, *ctxt);
+ g_io_channel_unref(chan);
+
+ return 0;
+}
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+ uuid_t *uuid, bt_callback_t cb, void *user_data,
+ bt_destroy_t destroy)
+{
+ struct search_context *ctxt = NULL;
+ int err;
+
+ if (!cb)
+ return -EINVAL;
+
+ err = create_search_context(&ctxt, src, dst, uuid);
+ if (err < 0)
+ return err;
+
+ ctxt->cb = cb;
+ ctxt->destroy = destroy;
+ ctxt->user_data = user_data;
+
+ context_list = g_slist_append(context_list, ctxt);
+
+ return 0;
+}
+
+int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst,
+ bt_callback_t cb, void *user_data, bt_destroy_t destroy)
+{
+ uuid_t uuid;
+
+ sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+
+ return bt_search_service(src, dst, &uuid, cb, user_data, destroy);
+}
+
+static int find_by_bdaddr(const void *data, const void *user_data)
+{
+ const struct search_context *ctxt = data, *search = user_data;
+
+ return (bacmp(&ctxt->dst, &search->dst) &&
+ bacmp(&ctxt->src, &search->src));
+}
+
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct search_context search, *ctxt;
+ GSList *match;
+
+ memset(&search, 0, sizeof(search));
+ bacpy(&search.src, src);
+ bacpy(&search.dst, dst);
+
+ /* Ongoing SDP Discovery */
+ match = g_slist_find_custom(context_list, &search, find_by_bdaddr);
+ if (!match)
+ return -ENODATA;
+
+ ctxt = match->data;
+ if (!ctxt->session)
+ return -ENOTCONN;
+
+ close(ctxt->session->sock);
+ return 0;
+}
+
+char *bt_uuid2string(uuid_t *uuid)
+{
+ gchar *str;
+ uuid_t uuid128;
+ unsigned int data0;
+ unsigned short data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned int data4;
+ unsigned short data5;
+
+ if (!uuid)
+ return NULL;
+
+ switch (uuid->type) {
+ case SDP_UUID16:
+ sdp_uuid16_to_uuid128(&uuid128, uuid);
+ break;
+ case SDP_UUID32:
+ sdp_uuid32_to_uuid128(&uuid128, uuid);
+ break;
+ case SDP_UUID128:
+ memcpy(&uuid128, uuid, sizeof(uuid_t));
+ break;
+ default:
+ /* Type of UUID unknown */
+ return NULL;
+ }
+
+ memcpy(&data0, &uuid128.value.uuid128.data[0], 4);
+ memcpy(&data1, &uuid128.value.uuid128.data[4], 2);
+ memcpy(&data2, &uuid128.value.uuid128.data[6], 2);
+ memcpy(&data3, &uuid128.value.uuid128.data[8], 2);
+ memcpy(&data4, &uuid128.value.uuid128.data[10], 4);
+ memcpy(&data5, &uuid128.value.uuid128.data[14], 2);
+
+ str = g_try_malloc0(MAX_LEN_UUID_STR);
+ if (!str)
+ return NULL;
+
+ sprintf(str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+ g_ntohl(data0), g_ntohs(data1),
+ g_ntohs(data2), g_ntohs(data3),
+ g_ntohl(data4), g_ntohs(data5));
+
+ return str;
+}
+
+static struct {
+ const char *name;
+ uint16_t class;
+} bt_services[] = {
+ { "vcp", VIDEO_CONF_SVCLASS_ID },
+ { "pbap", PBAP_SVCLASS_ID },
+ { "sap", SAP_SVCLASS_ID },
+ { "ftp", OBEX_FILETRANS_SVCLASS_ID },
+ { "bpp", BASIC_PRINTING_SVCLASS_ID },
+ { "bip", IMAGING_SVCLASS_ID },
+ { "synch", IRMC_SYNC_SVCLASS_ID },
+ { "dun", DIALUP_NET_SVCLASS_ID },
+ { "opp", OBEX_OBJPUSH_SVCLASS_ID },
+ { "fax", FAX_SVCLASS_ID },
+ { "spp", SERIAL_PORT_SVCLASS_ID },
+ { "hsp", HEADSET_SVCLASS_ID },
+ { "hfp", HANDSFREE_SVCLASS_ID },
+ { }
+};
+
+uint16_t bt_string2class(const char *pattern)
+{
+ int i;
+
+ for (i = 0; bt_services[i].name; i++) {
+ if (strcasecmp(bt_services[i].name, pattern) == 0)
+ return bt_services[i].class;
+ }
+
+ return 0;
+}
+
+int bt_string2uuid(uuid_t *uuid, const char *string)
+{
+ uint32_t data0, data4;
+ uint16_t data1, data2, data3, data5;
+
+ if (strlen(string) == 36 &&
+ string[8] == '-' &&
+ string[13] == '-' &&
+ string[18] == '-' &&
+ string[23] == '-' &&
+ sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+ &data0, &data1, &data2, &data3, &data4, &data5) == 6) {
+ uint8_t val[16];
+
+ data0 = g_htonl(data0);
+ data1 = g_htons(data1);
+ data2 = g_htons(data2);
+ data3 = g_htons(data3);
+ data4 = g_htonl(data4);
+ data5 = g_htons(data5);
+
+ memcpy(&val[0], &data0, 4);
+ memcpy(&val[4], &data1, 2);
+ memcpy(&val[6], &data2, 2);
+ memcpy(&val[8], &data3, 2);
+ memcpy(&val[10], &data4, 4);
+ memcpy(&val[14], &data5, 2);
+
+ sdp_uuid128_create(uuid, val);
+
+ return 0;
+ } else {
+ uint16_t class = bt_string2class(string);
+ if (class) {
+ sdp_uuid16_create(uuid, class);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+gchar *bt_list2string(GSList *list)
+{
+ GSList *l;
+ gchar *str, *tmp;
+
+ if (!list)
+ return NULL;
+
+ str = g_strdup((const gchar *) list->data);
+
+ for (l = list->next; l; l = l->next) {
+ tmp = g_strconcat(str, " " , (const gchar *) l->data, NULL);
+ g_free(str);
+ str = tmp;
+ }
+
+ return str;
+}
+
+GSList *bt_string2list(const gchar *str)
+{
+ GSList *l = NULL;
+ gchar **uuids;
+ int i = 0;
+
+ if (!str)
+ return NULL;
+
+ /* FIXME: eglib doesn't support g_strsplit */
+ uuids = g_strsplit(str, " ", 0);
+ if (!uuids)
+ return NULL;
+
+ while (uuids[i]) {
+ l = g_slist_append(l, uuids[i]);
+ i++;
+ }
+
+ g_free(uuids);
+
+ return l;
+}
+
+static inline int resolve_names(int fd, struct sockaddr *host,
+ struct sockaddr *peer, socklen_t len)
+{
+ int err;
+ socklen_t namelen;
+
+ namelen = len;
+ memset(host, 0, len);
+ err = getsockname(fd, host, &namelen);
+ if (err < 0)
+ return err;
+
+ namelen = len;
+ memset(peer, 0, len);
+ err = getpeername(fd, peer, &namelen);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rfcomm_resolver(int fd, char *src, char *dst)
+{
+ struct sockaddr_rc host, peer;
+ socklen_t len;
+ int err;
+
+ len = sizeof(host);
+ err = resolve_names(fd, (struct sockaddr *) &host,
+ (struct sockaddr *) &peer, len);
+ if (err < 0)
+ return err;
+
+ ba2str(&host.rc_bdaddr, src);
+ ba2str(&peer.rc_bdaddr, dst);
+
+ return 0;
+}
+
+static int l2cap_resolver(int fd, char *src, char *dst)
+{
+ struct sockaddr_l2 host, peer;
+ socklen_t len;
+ int err;
+
+ len = sizeof(host);
+ err = resolve_names(fd, (struct sockaddr *) &host,
+ (struct sockaddr *) &peer, len);
+ if (err < 0)
+ return err;
+
+ ba2str(&host.l2_bdaddr, src);
+ ba2str(&peer.l2_bdaddr, dst);
+
+ return 0;
+}
+
+static int sco_resolver(int fd, char *src, char *dst)
+{
+ struct sockaddr_sco host, peer;
+ socklen_t len;
+ int err;
+
+ len = sizeof(host);
+ err = resolve_names(fd, (struct sockaddr *) &host,
+ (struct sockaddr *) &peer, len);
+ if (err < 0)
+ return err;
+
+ ba2str(&host.sco_bdaddr, src);
+ ba2str(&peer.sco_bdaddr, dst);
+
+ return 0;
+}
+
+static gboolean listen_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ BtIO *io = user_data;
+ struct io_context *io_ctxt = io->io_ctxt;
+ int fd, err = 0;
+ GIOChannel *gio;
+ struct sockaddr addr;
+ socklen_t len;
+ bdaddr_t src, dst;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ g_io_channel_close(chan);
+ g_io_channel_unref(chan);
+ g_free(io_ctxt);
+ return FALSE;
+ }
+
+ len = sizeof(addr);
+ memset(&addr, 0, len);
+ fd = accept(io_ctxt->fd, &addr, &len);
+ if (fd < 0)
+ goto drop;
+
+ if (io_ctxt->resolver) {
+ err = io_ctxt->resolver(fd, io->src, io->dst);
+ if (err < 0) {
+ close(fd);
+ goto drop;
+ }
+ }
+
+ gio = g_io_channel_unix_new(fd);
+ if (!gio)
+ err = -ENOMEM;
+
+ if (io_ctxt->func)
+ io_ctxt->func(io, err, gio, io_ctxt->user_data);
+ if (io_ctxt->cb) {
+ str2ba(io->src, &src);
+ str2ba(io->dst, &dst);
+ io_ctxt->cb(gio, err, &src, &dst, io_ctxt->user_data);
+ }
+
+ return TRUE;
+
+drop:
+ if (io_ctxt->func)
+ io_ctxt->func(io, -errno, NULL, io_ctxt->user_data);
+ if (io_ctxt->cb)
+ io_ctxt->cb(NULL, err, NULL, NULL, io_ctxt->user_data);
+
+ return TRUE;
+}
+
+static int transport_listen(BtIO *io)
+{
+ struct io_context *io_ctxt = io->io_ctxt;
+ int err;
+
+ err = listen(io_ctxt->fd, 5);
+ if (err < 0)
+ return -errno;
+
+ io_ctxt->io = g_io_channel_unix_new(io_ctxt->fd);
+ if (!io_ctxt->io)
+ return -ENOMEM;
+
+ g_io_add_watch(io_ctxt->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) listen_cb, io);
+
+ return 0;
+}
+
+static gboolean connect_cb(GIOChannel *gio, GIOCondition cond,
+ gpointer user_data)
+{
+ BtIO *io = user_data;
+ struct io_context *io_ctxt = io->io_ctxt;
+ int err = 0, ret;
+ socklen_t len;
+ bdaddr_t src, dst;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ len = sizeof(ret);
+ if (getsockopt(io_ctxt->fd, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {
+ err = -errno;
+ goto done;
+ }
+
+ if (ret != 0) {
+ err = -ret;
+ goto done;
+ }
+
+ if (io_ctxt->resolver) {
+ err = io_ctxt->resolver(io_ctxt->fd, io->src, io->dst);
+ if (err < 0)
+ goto done;
+ }
+
+ io_ctxt->io = NULL;
+
+done:
+ if (io_ctxt->func)
+ io_ctxt->func(io, err, gio, io_ctxt->user_data);
+ if (io_ctxt->cb) {
+ str2ba(io->src, &src);
+ str2ba(io->dst, &dst);
+ io_ctxt->cb(gio, err, &src, &dst, io_ctxt->user_data);
+ }
+ if (io_ctxt->io) {
+ g_io_channel_close(io_ctxt->io);
+ g_io_channel_unref(io_ctxt->io);
+ }
+ g_free(io_ctxt);
+
+ return FALSE;
+}
+
+static int transport_connect(BtIO *io, struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct io_context *io_ctxt = io->io_ctxt;
+ int err;
+
+ io_ctxt->io = g_io_channel_unix_new(io_ctxt->fd);
+ if (!io_ctxt->io)
+ return -ENOMEM;
+
+ err = g_io_channel_set_flags(io_ctxt->io, G_IO_FLAG_NONBLOCK, NULL);
+ if (err != G_IO_STATUS_NORMAL)
+ return -EPERM;
+
+ err = connect(io_ctxt->fd, addr, addrlen);
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+ return -errno;
+
+ g_io_add_watch(io_ctxt->io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) connect_cb, io);
+
+ return 0;
+}
+
+static BtIOError sco_connect(BtIO *io, BtIOFunc func)
+{
+ struct io_context *io_ctxt = io->io_ctxt;
+ struct sockaddr_sco addr;
+ int sk, err;
+
+ io_ctxt->func = func;
+
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ if (sk < 0)
+ return -errno;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ str2ba(io->src, &addr.sco_bdaddr);
+
+ err = bind(sk, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0) {
+ close(sk);
+ return BT_IO_FAILED;
+ }
+
+ io_ctxt->fd = sk;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ str2ba(io->dst, &addr.sco_bdaddr);
+
+ err = transport_connect(io, (struct sockaddr *) &addr,
+ sizeof(addr));
+ if (err < 0) {
+ close(sk);
+ return BT_IO_FAILED;
+ }
+
+ return BT_IO_SUCCESS;
+}
+
+static int l2cap_bind(struct io_context *io_ctxt, const char *address,
+ uint16_t psm, uint16_t mtu, uint32_t flags,
+ struct sockaddr_l2 *addr)
+{
+ int err;
+ struct l2cap_options l2o;
+ struct sockaddr_l2 l2a;
+
+ io_ctxt->fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (io_ctxt->fd < 0)
+ return -errno;
+
+ if (mtu) {
+ socklen_t olen = sizeof(l2o);
+ memset(&l2o, 0, olen);
+ getsockopt(io_ctxt->fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen);
+ l2o.imtu = l2o.omtu = mtu;
+ setsockopt(io_ctxt->fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o));
+ }
+
+ if (flags) {
+ int opt = flags;
+ err = setsockopt(io_ctxt->fd, SOL_L2CAP, L2CAP_LM, &opt,
+ sizeof(opt));
+ if (err < 0) {
+ close(io_ctxt->fd);
+ return -errno;
+ }
+ }
+
+ memset(addr, 0, sizeof(*addr));
+ addr->l2_family = AF_BLUETOOTH;
+ str2ba(address, &l2a.l2_bdaddr);
+ addr->l2_psm = htobs(psm);
+
+ err = bind(io_ctxt->fd, (struct sockaddr *) addr, sizeof(*addr));
+ if (err < 0) {
+ close(io_ctxt->fd);
+ return -errno;
+ }
+
+ return 0;
+}
+
+static BtIOError l2cap_listen(BtIO *io, BtIOFunc func)
+{
+ struct io_context *io_ctxt = io->io_ctxt;
+ struct sockaddr_l2 addr;
+ BtIOError err;
+
+ io_ctxt->func = func;
+
+ err = l2cap_bind(io_ctxt, io->src, io->psm, io->mtu, io->flags, &addr);
+ if (err < 0)
+ return err;
+
+ err = transport_listen(io);
+ if (err < 0) {
+ close(io_ctxt->fd);
+ return err;
+ }
+
+ return BT_IO_SUCCESS;
+}
+
+static BtIOError l2cap_connect(BtIO *io, BtIOFunc func)
+{
+ struct io_context *io_ctxt = io->io_ctxt;
+ struct sockaddr_l2 l2a;
+ BtIOError err;
+
+ io_ctxt->func = func;
+
+ err = l2cap_bind(io_ctxt, io->src, 0, io->mtu, 0, &l2a);
+ if (err < 0)
+ return err;
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ str2ba(io->dst, &l2a.l2_bdaddr);
+ l2a.l2_psm = htobs(io->psm);
+
+ err = transport_connect(io, (struct sockaddr *) &l2a,
+ sizeof(l2a));
+ if (err < 0) {
+ close(io_ctxt->fd);
+ return err;
+ }
+
+ return BT_IO_SUCCESS;
+}
+
+static BtIOError rfcomm_bind(struct io_context *io_ctxt, const char *address,
+ uint8_t channel, uint32_t flags,
+ struct sockaddr_rc *addr)
+{
+ BtIOError err;
+
+ io_ctxt->fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (io_ctxt->fd < 0)
+ return BT_IO_FAILED;
+
+ if (flags) {
+ int opt = flags;
+ err = setsockopt(io_ctxt->fd, SOL_RFCOMM, RFCOMM_LM, &opt,
+ sizeof(opt));
+ if (err < 0) {
+ close(io_ctxt->fd);
+ return BT_IO_FAILED;
+ }
+ }
+
+ memset(addr, 0, sizeof(*addr));
+ addr->rc_family = AF_BLUETOOTH;
+ str2ba(address, &addr->rc_bdaddr);
+ addr->rc_channel = channel;
+
+ err = bind(io_ctxt->fd, (struct sockaddr *) addr, sizeof(*addr));
+ if (err < 0) {
+ close(io_ctxt->fd);
+ return BT_IO_FAILED;
+ }
+
+ return BT_IO_SUCCESS;
+}
+
+static BtIOError rfcomm_listen(BtIO *io, BtIOFunc func)
+{
+ struct io_context *io_ctxt = io->io_ctxt;
+ struct sockaddr_rc addr;
+ socklen_t sa_len;
+ BtIOError err;
+
+ io_ctxt->func = func;
+
+ err = rfcomm_bind(io_ctxt, io->src, io->channel, io->flags, &addr);
+ if (err < 0)
+ return err;
+
+ err = transport_listen(io);
+ if (err < 0) {
+ close(io_ctxt->fd);
+ return err;
+ }
+
+ sa_len = sizeof(struct sockaddr_rc);
+ memset(&addr, 0, sizeof(addr));
+ if (getsockname(io_ctxt->fd, (struct sockaddr *) &addr, &sa_len) < 0) {
+ err = -errno;
+ close(io_ctxt->fd);
+ return err;
+ }
+
+ io->channel = addr.rc_channel;
+
+ return BT_IO_SUCCESS;
+}
+
+static BtIOError rfcomm_connect(BtIO *io, BtIOFunc func)
+{
+ struct io_context *io_ctxt = io->io_ctxt;
+ struct sockaddr_rc addr;
+ BtIOError err;
+
+ io_ctxt->func = func;
+
+ err = rfcomm_bind(io_ctxt, io->src, 0, 0, &addr);
+ if (err < 0)
+ return err;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ str2ba(io->dst, &addr.rc_bdaddr);
+ addr.rc_channel = io->channel;
+
+ err = transport_connect(io, (struct sockaddr *) &addr,
+ sizeof(addr));
+ if (err < 0) {
+ close(io_ctxt->fd);
+ return err;
+ }
+
+ return BT_IO_SUCCESS;
+}
+
+static int create_io_context(struct io_context **io_ctxt, BtIOFunc func,
+ gpointer cb, gpointer resolver, gpointer user_data)
+{
+ *io_ctxt = g_try_malloc0(sizeof(struct search_context));
+ if (!*io_ctxt)
+ return -ENOMEM;
+
+ (*io_ctxt)->cb = cb;
+ (*io_ctxt)->func = func;
+ (*io_ctxt)->resolver = resolver;
+ (*io_ctxt)->user_data = user_data;
+
+ return 0;
+}
+
+static void io_context_cleanup(struct io_context *io_ctxt)
+{
+ if (io_ctxt->io) {
+ g_io_channel_close(io_ctxt->io);
+ g_io_channel_unref(io_ctxt->io);
+ }
+ g_free(io_ctxt);
+}
+
+GIOChannel *rfcomm_listen_internal(const bdaddr_t *src, uint8_t *channel,
+ uint32_t flags, bt_io_callback_t cb, void *user_data)
+{
+ BtIO *io;
+ BtIOError err;
+
+ io = bt_io_create(BT_IO_RFCOMM, user_data, NULL);
+ if (!io)
+ return NULL;
+
+ ba2str(src, io->src);
+ io->channel = *channel;
+ io->flags = flags;
+ io->io_ctxt->cb = cb;
+ err = bt_io_listen(io, NULL, NULL);
+ if (err != BT_IO_SUCCESS) {
+ bt_io_unref(io);
+ return NULL;
+ }
+
+ *channel = io->channel;
+
+ return io->io_ctxt->io;
+}
+
+GIOChannel *bt_rfcomm_listen_allocate(const bdaddr_t *src, uint8_t *channel,
+ uint32_t flags, bt_io_callback_t cb, void *user_data)
+{
+ if (!channel)
+ return NULL;
+
+ *channel = 0;
+
+ return rfcomm_listen_internal(src, channel, flags, cb, user_data);
+}
+
+GIOChannel *bt_rfcomm_listen(const bdaddr_t *src, uint8_t channel,
+ uint32_t flags, bt_io_callback_t cb, void *user_data)
+{
+ if (channel < 1 || channel > 30)
+ return NULL;
+
+ return rfcomm_listen_internal(src, &channel, flags, cb, user_data);
+
+}
+
+int bt_rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst,
+ uint8_t channel, bt_io_callback_t cb, void *user_data)
+{
+ BtIO *io;
+ BtIOError err;
+
+ io = bt_io_create(BT_IO_RFCOMM, user_data, NULL);
+ if (!io)
+ return -1;
+
+ ba2str(src, io->src);
+ ba2str(dst, io->dst);
+ io->channel = channel;
+ io->io_ctxt->cb = cb;
+ err = bt_io_connect(io, NULL, NULL);
+ if (err != BT_IO_SUCCESS) {
+ bt_io_unref(io);
+ return -1;
+ }
+
+ return 0;
+}
+
+GIOChannel *bt_l2cap_listen(const bdaddr_t *src, uint16_t psm, uint16_t mtu,
+ uint32_t flags, bt_io_callback_t cb, void *user_data)
+{
+ BtIO *io;
+ BtIOError err;
+
+ io = bt_io_create(BT_IO_L2CAP, user_data, NULL);
+ if (!io)
+ return NULL;
+
+ ba2str(src, io->src);
+ io->psm = psm;
+ io->mtu = mtu;
+ io->flags = flags;
+ io->io_ctxt->cb = cb;
+ err = bt_io_listen(io, NULL, NULL);
+ if (err != BT_IO_SUCCESS) {
+ bt_io_unref(io);
+ return NULL;
+ }
+
+ return io->io_ctxt->io;
+}
+
+int bt_l2cap_connect(const bdaddr_t *src, const bdaddr_t *dst,
+ uint16_t psm, uint16_t mtu, bt_io_callback_t cb,
+ void *user_data)
+{
+ BtIO *io;
+ BtIOError err;
+
+ io = bt_io_create(BT_IO_L2CAP, user_data, NULL);
+ if (!io)
+ return -1;
+
+ ba2str(src, io->src);
+ ba2str(dst, io->dst);
+ io->psm = psm;
+ io->mtu = mtu;
+ io->io_ctxt->cb = cb;
+ err = bt_io_connect(io, NULL, NULL);
+ if (err != BT_IO_SUCCESS) {
+ bt_io_unref(io);
+ return -1;
+ }
+
+ return 0;
+}
+
+int bt_sco_connect(const bdaddr_t *src, const bdaddr_t *dst,
+ bt_io_callback_t cb, void *user_data)
+{
+ BtIO *io;
+ BtIOError err;
+
+ io = bt_io_create(BT_IO_SCO, user_data, NULL);
+ if (!io)
+ return -1;
+
+ ba2str(src, io->src);
+ ba2str(dst, io->dst);
+ io->io_ctxt->cb = cb;
+ err = bt_io_connect(io, NULL, NULL);
+ if (err != BT_IO_SUCCESS) {
+ bt_io_unref(io);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Experiemental bt_io API */
+
+BtIO *bt_io_create(BtIOTransport type, gpointer user_data, GDestroyNotify notify)
+{
+ BtIO *io;
+ int err;
+
+ io = g_new0(BtIO, 1);
+ if (!io)
+ return NULL;
+
+ io->refcount = 1;
+
+ switch (type) {
+ case BT_IO_L2CAP:
+ err = create_io_context(&io->io_ctxt, NULL, NULL,
+ l2cap_resolver, user_data);
+ io->connect = l2cap_connect;
+ io->listen = l2cap_listen;
+ break;
+ case BT_IO_RFCOMM:
+ err = create_io_context(&io->io_ctxt, NULL, NULL,
+ rfcomm_resolver, user_data);
+ io->connect = rfcomm_connect;
+ io->listen = rfcomm_listen;
+ break;
+ case BT_IO_SCO:
+ err = create_io_context(&io->io_ctxt, NULL, NULL,
+ sco_resolver, user_data);
+ io->connect = sco_connect;
+ break;
+ default:
+ return NULL;
+ }
+
+ if (err < 0) {
+ bt_io_unref(io);
+ return NULL;
+ }
+
+ return io;
+}
+
+BtIO *bt_io_ref(BtIO *io)
+{
+ io->refcount++;
+
+ return io;
+}
+
+void bt_io_unref(BtIO *io)
+{
+ io->refcount--;
+
+ if (io->refcount)
+ return;
+
+ io_context_cleanup(io->io_ctxt);
+ g_free(io);
+}
+
+gboolean bt_io_set_source(BtIO *io, const char *address)
+{
+ if (strlen(address) != sizeof(io->src))
+ return FALSE;
+
+ memcpy(io->src, address, sizeof(io->src));
+
+ return TRUE;
+}
+
+const char *bt_io_get_source(BtIO *io)
+{
+ return io->src;
+}
+
+gboolean bt_io_set_destination(BtIO *io, const char *address)
+{
+ if (strlen(address) != sizeof(io->dst))
+ return FALSE;
+
+ memcpy(io->dst, address, sizeof(io->dst));
+
+ return TRUE;
+}
+
+const char *bt_io_get_destination(BtIO *io)
+{
+ return io->dst;
+}
+
+gboolean bt_io_set_flags(BtIO *io, guint32 flags)
+{
+ io->flags = flags;
+
+ return TRUE;
+}
+
+guint32 bt_io_get_flags(BtIO *io)
+{
+ return io->flags;
+}
+
+gboolean bt_io_set_channel(BtIO *io, guint8 channel)
+{
+ if (io->type != BT_IO_RFCOMM)
+ return FALSE;
+
+ io->channel = channel;
+
+ return TRUE;
+}
+
+guint8 bt_io_get_channel(BtIO *io)
+{
+ return io->channel;
+}
+
+gboolean bt_io_set_psm(BtIO *io, guint16 psm)
+{
+ if (io->type != BT_IO_L2CAP)
+ return FALSE;
+
+ io->psm = psm;
+
+ return TRUE;
+}
+
+guint16 bt_io_get_psm(BtIO *io)
+{
+ return io->psm;
+}
+
+gboolean bt_io_set_mtu(BtIO *io, guint16 mtu)
+{
+ io->mtu = mtu;
+
+ return TRUE;
+}
+
+guint16 bt_io_get_mtu(BtIO *io)
+{
+ return io->mtu;
+}
+
+BtIOError bt_io_connect(BtIO *io, const char *uuid, BtIOFunc func)
+{
+ if (!io->connect)
+ return BT_IO_FAILED;
+
+ return io->connect(io, func);
+}
+
+BtIOError bt_io_listen(BtIO *io, const char *uuid, BtIOFunc func)
+{
+ if (!io->listen)
+ return BT_IO_FAILED;
+
+ return io->listen(io, func);
+}
+
+BtIOError bt_io_shutdown(BtIO *io)
+{
+ io_context_cleanup(io->io_ctxt);
+
+ return BT_IO_SUCCESS;
+}
diff --git a/common/glib-helper.h b/common/glib-helper.h
new file mode 100644
index 00000000..6b188f7f
--- /dev/null
+++ b/common/glib-helper.h
@@ -0,0 +1,96 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int set_nonblocking(int fd);
+
+typedef void (*bt_io_callback_t) (GIOChannel *io, int err, const bdaddr_t *src,
+ const bdaddr_t *dst, gpointer user_data);
+typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
+typedef void (*bt_destroy_t) (gpointer user_data);
+
+int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst,
+ bt_callback_t cb, void *user_data, bt_destroy_t destroy);
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+ uuid_t *uuid, bt_callback_t cb, void *user_data,
+ bt_destroy_t destroy);
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst);
+
+gchar *bt_uuid2string(uuid_t *uuid);
+uint16_t bt_string2class(const char *string);
+int bt_string2uuid(uuid_t *uuid, const char *string);
+gchar *bt_list2string(GSList *list);
+GSList *bt_string2list(const gchar *str);
+
+GIOChannel *bt_rfcomm_listen(const bdaddr_t *src, uint8_t channel,
+ uint32_t flags, bt_io_callback_t cb, void *user_data);
+GIOChannel *bt_rfcomm_listen_allocate(const bdaddr_t *src, uint8_t *channel,
+ uint32_t flags, bt_io_callback_t cb, void *user_data);
+int bt_rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst,
+ uint8_t channel, bt_io_callback_t cb, void *user_data);
+
+GIOChannel *bt_l2cap_listen(const bdaddr_t *src, uint16_t psm, uint16_t mtu,
+ uint32_t flags, bt_io_callback_t cb, void *user_data);
+int bt_l2cap_connect(const bdaddr_t *src, const bdaddr_t *dst,
+ uint16_t psm, uint16_t mtu, bt_io_callback_t cb,
+ void *user_data);
+int bt_sco_connect(const bdaddr_t *src, const bdaddr_t *dst,
+ bt_io_callback_t cb, void *user_data);
+
+/* Experiemental bt_io API */
+
+typedef struct bt_io BtIO;
+
+typedef enum {
+ BT_IO_AUTO,
+ BT_IO_L2CAP,
+ BT_IO_RFCOMM,
+ BT_IO_SCO,
+} BtIOTransport;
+
+typedef enum {
+ BT_IO_SUCCESS,
+ BT_IO_FAILED,
+} BtIOError;
+
+typedef void (*BtIOFunc) (BtIO *io, BtIOError err, GIOChannel *chan,
+ gpointer user_data);
+
+BtIO *bt_io_create(BtIOTransport type, gpointer user_data, GDestroyNotify notify);
+BtIO *bt_io_ref(BtIO *io);
+void bt_io_unref(BtIO *io);
+gboolean bt_io_set_source(BtIO *io, const char *address);
+const char *bt_io_get_source(BtIO *io);
+gboolean bt_io_set_destination(BtIO *io, const char *address);
+const char *bt_io_get_destination(BtIO *io);
+gboolean bt_io_set_flags(BtIO *io, guint32 flags);
+guint32 bt_io_get_flags(BtIO *io);
+gboolean bt_io_set_channel(BtIO *io, guint8 channel);
+guint8 bt_io_get_channel(BtIO *io);
+gboolean bt_io_set_psm(BtIO *io, guint16 psm);
+guint16 bt_io_get_psm(BtIO *io);
+gboolean bt_io_set_mtu(BtIO *io, guint16 mtu);
+guint16 bt_io_get_mtu(BtIO *io);
+BtIOError bt_io_connect(BtIO *io, const char *uuid, BtIOFunc func);
+BtIOError bt_io_listen(BtIO *io, const char *uuid, BtIOFunc func);
+BtIOError bt_io_shutdown(BtIO *io);
diff --git a/common/logging.c b/common/logging.c
new file mode 100644
index 00000000..28fc9af5
--- /dev/null
+++ b/common/logging.c
@@ -0,0 +1,106 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+static volatile int debug_enabled = 0;
+
+static inline void vinfo(const char *format, va_list ap)
+{
+ vsyslog(LOG_INFO, format, ap);
+}
+
+void info(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vinfo(format, ap);
+
+ va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_ERR, format, ap);
+
+ va_end(ap);
+}
+
+void debug(const char *format, ...)
+{
+ va_list ap;
+
+ if (!debug_enabled)
+ return;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_DEBUG, format, ap);
+
+ va_end(ap);
+}
+
+void toggle_debug(void)
+{
+ debug_enabled = (debug_enabled + 1) % 2;
+}
+
+void enable_debug(void)
+{
+ debug_enabled = 1;
+}
+
+void disable_debug(void)
+{
+ debug_enabled = 0;
+}
+
+void start_logging(const char *ident, const char *message, ...)
+{
+ va_list ap;
+
+ openlog(ident, LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+
+ va_start(ap, message);
+
+ vinfo(message, ap);
+
+ va_end(ap);
+}
+
+void stop_logging(void)
+{
+ closelog();
+}
diff --git a/common/logging.h b/common/logging.h
new file mode 100644
index 00000000..822eb15a
--- /dev/null
+++ b/common/logging.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __LOGGING_H
+#define __LOGGING_H
+
+void info(const char *format, ...);
+void error(const char *format, ...);
+void debug(const char *format, ...);
+void toggle_debug(void);
+void enable_debug(void);
+void disable_debug(void);
+void start_logging(const char *ident, const char *message, ...);
+void stop_logging(void);
+
+#define DBG(fmt, arg...) debug("%s: " fmt "\n" , __FUNCTION__ , ## arg)
+
+#endif /* __LOGGING_H */
diff --git a/common/oui.c b/common/oui.c
new file mode 100644
index 00000000..0915afdf
--- /dev/null
+++ b/common/oui.c
@@ -0,0 +1,109 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "oui.h"
+
+/* http://standards.ieee.org/regauth/oui/oui.txt */
+
+#define OUIFILE "/var/lib/misc/oui.txt"
+
+char *ouitocomp(const char *oui)
+{
+ struct stat st;
+ char *str, *map, *off, *end;
+ int fd;
+
+ fd = open("oui.txt", O_RDONLY);
+ if (fd < 0) {
+ fd = open(OUIFILE, O_RDONLY);
+ if (fd < 0) {
+ fd = open("/usr/share/misc/oui.txt", O_RDONLY);
+ if (fd < 0)
+ return NULL;
+ }
+ }
+
+ if (fstat(fd, &st) < 0) {
+ close(fd);
+ return NULL;
+ }
+
+ str = malloc(128);
+ if (!str) {
+ close(fd);
+ return NULL;
+ }
+
+ memset(str, 0, 128);
+
+ map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ free(str);
+ close(fd);
+ return NULL;
+ }
+
+ off = strstr(map, oui);
+ if (off) {
+ off += 18;
+ end = strpbrk(off, "\r\n");
+ strncpy(str, off, end - off);
+ } else {
+ free(str);
+ str = NULL;
+ }
+
+ munmap(map, st.st_size);
+
+ close(fd);
+
+ return str;
+}
+
+int oui2comp(const char *oui, char *comp, size_t size)
+{
+ char *tmp;
+
+ tmp = ouitocomp(oui);
+ if (!tmp)
+ return -1;
+
+ snprintf(comp, size, "%s", tmp);
+
+ free(tmp);
+
+ return 0;
+}
diff --git a/common/oui.h b/common/oui.h
new file mode 100644
index 00000000..991e145c
--- /dev/null
+++ b/common/oui.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+char *ouitocomp(const char *oui);
+int oui2comp(const char *oui, char *comp, size_t size);
diff --git a/common/ppoll.h b/common/ppoll.h
new file mode 100644
index 00000000..7d09d44d
--- /dev/null
+++ b/common/ppoll.h
@@ -0,0 +1,16 @@
+#ifdef ppoll
+#undef ppoll
+#endif
+
+#define ppoll compat_ppoll
+
+static inline int compat_ppoll(struct pollfd *fds, nfds_t nfds,
+ const struct timespec *timeout, const sigset_t *sigmask)
+{
+ if (timeout == NULL)
+ return poll(fds, nfds, -1);
+ else if (timeout->tv_sec == 0)
+ return poll(fds, nfds, 500);
+ else
+ return poll(fds, nfds, timeout->tv_sec * 1000);
+}
diff --git a/common/sdp-glib.c b/common/sdp-glib.c
new file mode 100644
index 00000000..bd27d822
--- /dev/null
+++ b/common/sdp-glib.c
@@ -0,0 +1,239 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "logging.h"
+#include "sdp-xml.h"
+
+static int compute_seq_size(sdp_data_t *data)
+{
+ int unit_size = data->unitSize;
+ sdp_data_t *seq = data->val.dataseq;
+
+ for (; seq; seq = seq->next)
+ unit_size += seq->unitSize;
+
+ return unit_size;
+}
+
+struct context_data {
+ sdp_record_t *record;
+ sdp_data_t attr_data;
+ struct sdp_xml_data *stack_head;
+ uint16_t attr_id;
+};
+
+static void element_start(GMarkupParseContext *context,
+ const gchar *element_name, const gchar **attribute_names,
+ const gchar **attribute_values, gpointer user_data, GError **err)
+{
+ struct context_data *ctx_data = user_data;
+
+ if (!strcmp(element_name, "record"))
+ return;
+
+ if (!strcmp(element_name, "attribute")) {
+ int i;
+ for (i = 0; attribute_names[i]; i++) {
+ if (!strcmp(attribute_names[i], "id")) {
+ ctx_data->attr_id = strtol(attribute_values[i], 0, 0);
+ break;
+ }
+ }
+ debug("New attribute 0x%04x", ctx_data->attr_id);
+ return;
+ }
+
+ if (ctx_data->stack_head) {
+ struct sdp_xml_data *newelem = sdp_xml_data_alloc();
+ newelem->next = ctx_data->stack_head;
+ ctx_data->stack_head = newelem;
+ } else {
+ ctx_data->stack_head = sdp_xml_data_alloc();
+ ctx_data->stack_head->next = NULL;
+ }
+
+ if (!strcmp(element_name, "sequence"))
+ ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL);
+ else if (!strcmp(element_name, "alternate"))
+ ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL);
+ else {
+ int i;
+ /* Parse value, name, encoding */
+ for (i = 0; attribute_names[i]; i++) {
+ if (!strcmp(attribute_names[i], "value")) {
+ int curlen = strlen(ctx_data->stack_head->text);
+ int attrlen = strlen(attribute_values[i]);
+
+ /* Ensure we're big enough */
+ while ((curlen + 1 + attrlen) > ctx_data->stack_head->size) {
+ sdp_xml_data_expand(ctx_data->stack_head);
+ }
+
+ memcpy(ctx_data->stack_head->text + curlen,
+ attribute_values[i], attrlen);
+ ctx_data->stack_head->text[curlen + attrlen] = '\0';
+ }
+
+ if (!strcmp(attribute_names[i], "encoding")) {
+ if (!strcmp(attribute_values[i], "hex"))
+ ctx_data->stack_head->type = 1;
+ }
+
+ if (!strcmp(attribute_names[i], "name")) {
+ ctx_data->stack_head->name = strdup(attribute_values[i]);
+ }
+ }
+
+ ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name,
+ ctx_data->stack_head, ctx_data->record);
+
+ if (ctx_data->stack_head->data == NULL)
+ error("Can't parse element %s", element_name);
+ }
+}
+
+static void element_end(GMarkupParseContext *context,
+ const gchar *element_name, gpointer user_data, GError **err)
+{
+ struct context_data *ctx_data = user_data;
+ struct sdp_xml_data *elem;
+
+ if (!strcmp(element_name, "record"))
+ return;
+
+ if (!strcmp(element_name, "attribute")) {
+ if (ctx_data->stack_head && ctx_data->stack_head->data) {
+ int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id,
+ ctx_data->stack_head->data);
+ if (ret == -1)
+ debug("Trouble adding attribute\n");
+
+ ctx_data->stack_head->data = NULL;
+ sdp_xml_data_free(ctx_data->stack_head);
+ ctx_data->stack_head = NULL;
+ } else {
+ debug("No data for attribute 0x%04x\n", ctx_data->attr_id);
+ }
+ return;
+ }
+
+ if (!strcmp(element_name, "sequence")) {
+ ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
+
+ if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
+ ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
+ ctx_data->stack_head->data->dtd = SDP_SEQ32;
+ } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
+ ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
+ ctx_data->stack_head->data->dtd = SDP_SEQ16;
+ } else {
+ ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
+ }
+ } else if (!strcmp(element_name, "alternate")) {
+ ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
+
+ if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
+ ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
+ ctx_data->stack_head->data->dtd = SDP_ALT32;
+ } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
+ ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
+ ctx_data->stack_head->data->dtd = SDP_ALT16;
+ } else {
+ ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
+ }
+ }
+
+ if (ctx_data->stack_head->next && ctx_data->stack_head->data &&
+ ctx_data->stack_head->next->data) {
+ switch (ctx_data->stack_head->next->data->dtd) {
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ ctx_data->stack_head->next->data->val.dataseq =
+ sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq,
+ ctx_data->stack_head->data);
+ ctx_data->stack_head->data = NULL;
+ break;
+ }
+
+ elem = ctx_data->stack_head;
+ ctx_data->stack_head = ctx_data->stack_head->next;
+
+ sdp_xml_data_free(elem);
+ }
+}
+
+static GMarkupParser parser = {
+ element_start, element_end, NULL, NULL, NULL
+};
+
+sdp_record_t *sdp_xml_parse_record(const char *data, int size)
+{
+ GMarkupParseContext *ctx;
+ struct context_data *ctx_data;
+ sdp_record_t *record;
+
+ ctx_data = malloc(sizeof(*ctx_data));
+ if (!ctx_data)
+ return NULL;
+
+ record = sdp_record_alloc();
+ if (!record) {
+ free(ctx_data);
+ return NULL;
+ }
+
+ memset(ctx_data, 0, sizeof(*ctx_data));
+ ctx_data->record = record;
+
+ ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL);
+
+ if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
+ error("XML parsing error");
+ g_markup_parse_context_free(ctx);
+ sdp_record_free(record);
+ free(ctx_data);
+ return NULL;
+ }
+
+ g_markup_parse_context_free(ctx);
+
+ free(ctx_data);
+
+ return record;
+}
diff --git a/common/sdp-xml.c b/common/sdp-xml.c
new file mode 100644
index 00000000..1e1e07c3
--- /dev/null
+++ b/common/sdp-xml.c
@@ -0,0 +1,793 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "logging.h"
+#include "sdp-xml.h"
+
+#define STRBUFSIZE 1024
+#define MAXINDENT 64
+
+static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,
+ void *data, void (*appender)(void *, const char *))
+{
+ int i, hex;
+ char buf[STRBUFSIZE];
+ char indent[MAXINDENT];
+ char next_indent[MAXINDENT];
+
+ if (!value)
+ return;
+
+ if (indent_level >= MAXINDENT)
+ indent_level = MAXINDENT - 2;
+
+ for (i = 0; i < indent_level; i++) {
+ indent[i] = '\t';
+ next_indent[i] = '\t';
+ }
+
+ indent[i] = '\0';
+ next_indent[i] = '\t';
+ next_indent[i + 1] = '\0';
+
+ buf[STRBUFSIZE - 1] = '\0';
+
+ switch (value->dtd) {
+ case SDP_DATA_NIL:
+ appender(data, indent);
+ appender(data, "<nil/>\n");
+ break;
+
+ case SDP_BOOL:
+ appender(data, indent);
+ appender(data, "<boolean value=\"");
+ appender(data, value->val.uint8 ? "true" : "false");
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT8:
+ appender(data, indent);
+ appender(data, "<uint8 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%02x", value->val.uint8);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT16:
+ appender(data, indent);
+ appender(data, "<uint16 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uint16);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT32:
+ appender(data, indent);
+ appender(data, "<uint32 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uint32);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT64:
+ appender(data, indent);
+ appender(data, "<uint64 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%016jx", value->val.uint64);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT128:
+ appender(data, indent);
+ appender(data, "<uint128 value=\"");
+
+ for (i = 0; i < 16; i++) {
+ sprintf(&buf[i * 2], "%02x",
+ (unsigned char) value->val.uint128.data[i]);
+ }
+
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT8:
+ appender(data, indent);
+ appender(data, "<int8 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int8);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT16:
+ appender(data, indent);
+ appender(data, "<int16 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int16);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT32:
+ appender(data, indent);
+ appender(data, "<int32 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int32);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT64:
+ appender(data, indent);
+ appender(data, "<int64 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "%jd", value->val.int64);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT128:
+ appender(data, indent);
+ appender(data, "<int128 value=\"");
+
+ for (i = 0; i < 16; i++) {
+ sprintf(&buf[i * 2], "%02x",
+ (unsigned char) value->val.int128.data[i]);
+ }
+ appender(data, buf);
+
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UUID16:
+ appender(data, indent);
+ appender(data, "<uuid value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uuid.value.uuid16);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UUID32:
+ appender(data, indent);
+ appender(data, "<uuid value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uuid.value.uuid32);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UUID128:
+ appender(data, indent);
+ appender(data, "<uuid value=\"");
+
+ snprintf(buf, STRBUFSIZE - 1,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[0],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[1],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[2],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[3],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[4],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[5],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[6],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[7],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[8],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[9],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[10],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[11],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[12],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[13],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[14],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[15]);
+
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ {
+ hex = 0;
+
+ int num_chars_to_escape = 0;
+
+ for (i = 0; i < value->unitSize; i++) {
+ if (i == (value->unitSize - 1)
+ && value->val.str[i] == '\0')
+ break;
+ if (!isprint(value->val.str[i])) {
+ hex = 1;
+ break;
+ }
+
+ /* XML is evil, must do this... */
+ if ((value->val.str[i] == '<') ||
+ (value->val.str[i] == '>') ||
+ (value->val.str[i] == '"') ||
+ (value->val.str[i] == '&'))
+ num_chars_to_escape++;
+ }
+
+ appender(data, indent);
+
+ appender(data, "<text ");
+
+ char *strBuf = 0;
+
+ if (hex) {
+ appender(data, "encoding=\"hex\" ");
+ strBuf = (char *) malloc(sizeof(char)
+ * ((value->unitSize-1) * 2 + 1));
+
+ /* Unit Size seems to include the size for dtd
+ It is thus off by 1
+ This is safe for Normal strings, but not
+ hex encoded data */
+ for (i = 0; i < (value->unitSize-1); i++)
+ sprintf(&strBuf[i*sizeof(char)*2],
+ "%02x",
+ (unsigned char) value->val.str[i]);
+
+ strBuf[(value->unitSize-1) * 2] = '\0';
+ }
+ else {
+ int j;
+ /* escape the XML disallowed chars */
+ strBuf = (char *)
+ malloc(sizeof(char) *
+ (value->unitSize + 1 + num_chars_to_escape * 4));
+ for (i = 0, j = 0; i < value->unitSize; i++) {
+ if (value->val.str[i] == '&') {
+ strBuf[j++] = '&';
+ strBuf[j++] = 'a';
+ strBuf[j++] = 'm';
+ strBuf[j++] = 'p';
+ }
+ else if (value->val.str[i] == '<') {
+ strBuf[j++] = '&';
+ strBuf[j++] = 'l';
+ strBuf[j++] = 't';
+ }
+ else if (value->val.str[i] == '>') {
+ strBuf[j++] = '&';
+ strBuf[j++] = 'g';
+ strBuf[j++] = 't';
+ }
+ else if (value->val.str[i] == '"') {
+ strBuf[j++] = '&';
+ strBuf[j++] = 'q';
+ strBuf[j++] = 'u';
+ strBuf[j++] = 'o';
+ strBuf[j++] = 't';
+ }
+ else {
+ strBuf[j++] = value->val.str[i];
+ }
+ }
+
+ strBuf[j] = '\0';
+ }
+
+ appender(data, "value=\"");
+ appender(data, strBuf);
+ appender(data, "\" />\n");
+ free(strBuf);
+ break;
+ }
+
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ appender(data, indent);
+ appender(data, "<url value=\"");
+ appender(data, value->val.str);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ appender(data, indent);
+ appender(data, "<sequence>\n");
+
+ convert_raw_data_to_xml(value->val.dataseq,
+ indent_level + 1, data, appender);
+
+ appender(data, indent);
+ appender(data, "</sequence>\n");
+
+ break;
+
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ appender(data, indent);
+
+ appender(data, "<alternate>\n");
+
+ convert_raw_data_to_xml(value->val.dataseq,
+ indent_level + 1, data, appender);
+ appender(data, indent);
+
+ appender(data, "</alternate>\n");
+
+ break;
+ }
+
+ convert_raw_data_to_xml(value->next, indent_level, data, appender);
+}
+
+struct conversion_data {
+ void *data;
+ void (*appender)(void *data, const char *);
+};
+
+static void convert_raw_attr_to_xml_func(void *val, void *data)
+{
+ struct conversion_data *cd = (struct conversion_data *) data;
+ sdp_data_t *value = (sdp_data_t *) val;
+ char buf[STRBUFSIZE];
+
+ buf[STRBUFSIZE - 1] = '\0';
+ snprintf(buf, STRBUFSIZE - 1, "\t<attribute id=\"0x%04x\">\n",
+ value->attrId);
+ cd->appender(cd->data, buf);
+
+ if (data)
+ convert_raw_data_to_xml(value, 2, cd->data, cd->appender);
+ else
+ cd->appender(cd->data, "\t\tNULL\n");
+
+ cd->appender(cd->data, "\t</attribute>\n");
+}
+
+/*
+ * Will convert the sdp record to XML. The appender and data can be used
+ * to control where to output the record (e.g. file or a data buffer). The
+ * appender will be called repeatedly with data and the character buffer
+ * (containing parts of the generated XML) to append.
+ */
+void convert_sdp_record_to_xml(sdp_record_t *rec,
+ void *data, void (*appender)(void *, const char *))
+{
+ struct conversion_data cd;
+
+ cd.data = data;
+ cd.appender = appender;
+
+ if (rec && rec->attrlist) {
+ appender(data, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n");
+ appender(data, "<record>\n");
+ sdp_list_foreach(rec->attrlist,
+ convert_raw_attr_to_xml_func, &cd);
+ appender(data, "</record>\n");
+ }
+}
+
+static sdp_data_t *sdp_xml_parse_uuid128(const char *data)
+{
+ uint128_t val;
+ int i;
+ int j;
+
+ char buf[3];
+
+ memset(&val, 0, sizeof(val));
+
+ buf[2] = '\0';
+
+ for (j = 0, i = 0; i < strlen(data);) {
+ if (data[i] == '-') {
+ i++;
+ continue;
+ }
+
+ buf[0] = data[i];
+ buf[1] = data[i + 1];
+
+ val.data[j++] = strtoul(buf, 0, 16);
+ i += 2;
+ }
+
+ return sdp_data_alloc(SDP_UUID128, &val);
+}
+
+sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record)
+{
+ sdp_data_t *ret;
+ char *endptr;
+ uint32_t val;
+ uint16_t val2;
+ int len;
+
+ len = strlen(data);
+
+ if (len == 36) {
+ ret = sdp_xml_parse_uuid128(data);
+ goto result;
+ }
+
+ val = strtoll(data, &endptr, 16);
+
+ /* Couldn't parse */
+ if (*endptr != '\0')
+ return NULL;
+
+ if (val > USHRT_MAX) {
+ ret = sdp_data_alloc(SDP_UUID32, &val);
+ goto result;
+ }
+
+ val2 = val;
+
+ ret = sdp_data_alloc(SDP_UUID16, &val2);
+
+result:
+ if (record && ret)
+ sdp_pattern_add_uuid(record, &ret->val.uuid);
+
+ return ret;
+}
+
+sdp_data_t *sdp_xml_parse_int(const char * data, uint8_t dtd)
+{
+ char *endptr;
+ sdp_data_t *ret = NULL;
+
+ switch (dtd) {
+ case SDP_BOOL:
+ {
+ uint8_t val = 0;
+
+ if (!strcmp("true", data)) {
+ val = 1;
+ }
+
+ else if (!strcmp("false", data)) {
+ val = 0;
+ }
+ else {
+ return NULL;
+ }
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT8:
+ {
+ int8_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_UINT8:
+ {
+ uint8_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT16:
+ {
+ int16_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_UINT16:
+ {
+ uint16_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT32:
+ {
+ int32_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_UINT32:
+ {
+ uint32_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT64:
+ {
+ int64_t val = strtoull(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_UINT64:
+ {
+ uint64_t val = strtoull(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT128:
+ case SDP_UINT128:
+ {
+ uint128_t val;
+ int i = 0;
+ char buf[3];
+
+ buf[2] = '\0';
+
+ for (; i < 32; i += 2) {
+ buf[0] = data[i];
+ buf[1] = data[i + 1];
+
+ val.data[i >> 1] = strtoul(buf, 0, 16);
+ }
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ };
+
+ return ret;
+}
+
+static char *sdp_xml_parse_string_decode(const char *data, char encoding, uint32_t *length)
+{
+ int len = strlen(data);
+ char *text;
+
+ if (encoding == SDP_XML_ENCODING_NORMAL) {
+ text = strdup(data);
+ *length = len;
+ } else {
+ char buf[3], *decoded;
+ int i;
+
+ decoded = malloc((len >> 1) + 1);
+
+ /* Ensure the string is a power of 2 */
+ len = (len >> 1) << 1;
+
+ buf[2] = '\0';
+
+ for (i = 0; i < len; i += 2) {
+ buf[0] = data[i];
+ buf[1] = data[i + 1];
+
+ decoded[i >> 1] = strtoul(buf, 0, 16);
+ }
+
+ decoded[len >> 1] = '\0';
+ text = decoded;
+ *length = len >> 1;
+ }
+
+ return text;
+}
+
+sdp_data_t *sdp_xml_parse_url(const char *data)
+{
+ uint8_t dtd = SDP_URL_STR8;
+ char *url;
+ uint32_t length;
+ sdp_data_t *ret;
+
+ url = sdp_xml_parse_string_decode(data,
+ SDP_XML_ENCODING_NORMAL, &length);
+
+ if (length > UCHAR_MAX)
+ dtd = SDP_URL_STR16;
+
+ ret = sdp_data_alloc_with_length(dtd, url, length);
+
+ debug("URL size %d length %d: -->%s<--", ret->unitSize, length, url);
+
+ free(url);
+
+ return ret;
+}
+
+sdp_data_t *sdp_xml_parse_text(const char *data, char encoding)
+{
+ uint8_t dtd = SDP_TEXT_STR8;
+ char *text;
+ uint32_t length;
+ sdp_data_t *ret;
+
+ text = sdp_xml_parse_string_decode(data, encoding, &length);
+
+ if (length > UCHAR_MAX)
+ dtd = SDP_TEXT_STR16;
+
+ ret = sdp_data_alloc_with_length(dtd, text, length);
+
+ debug("Text size %d length %d: -->%s<--", ret->unitSize, length, text);
+
+ free(text);
+
+ return ret;
+}
+
+sdp_data_t *sdp_xml_parse_nil(const char *data)
+{
+ return sdp_data_alloc(SDP_DATA_NIL, 0);
+}
+
+#define DEFAULT_XML_DATA_SIZE 1024
+
+struct sdp_xml_data *sdp_xml_data_alloc()
+{
+ struct sdp_xml_data *elem;
+
+ elem = malloc(sizeof(struct sdp_xml_data));
+ if (!elem)
+ return NULL;
+
+ memset(elem, 0, sizeof(struct sdp_xml_data));
+
+ /* Null terminate the text */
+ elem->size = DEFAULT_XML_DATA_SIZE;
+ elem->text = malloc(DEFAULT_XML_DATA_SIZE);
+ elem->text[0] = '\0';
+
+ return elem;
+}
+
+void sdp_xml_data_free(struct sdp_xml_data *elem)
+{
+ if (elem->data)
+ sdp_data_free(elem->data);
+
+ if (elem->name)
+ free(elem->name);
+
+ if (elem->text)
+
+ free(elem->text);
+ free(elem);
+}
+
+struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem)
+{
+ char *newbuf;
+
+ newbuf = malloc(elem->size * 2);
+ if (!newbuf)
+ return NULL;
+
+ memcpy(newbuf, elem->text, elem->size);
+ elem->size *= 2;
+ free(elem->text);
+
+ elem->text = newbuf;
+
+ return elem;
+}
+
+sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem,
+ sdp_record_t *record)
+{
+ const char *data = elem->text;
+
+ if (!strcmp(el, "boolean"))
+ return sdp_xml_parse_int(data, SDP_BOOL);
+ else if (!strcmp(el, "uint8"))
+ return sdp_xml_parse_int(data, SDP_UINT8);
+ else if (!strcmp(el, "uint16"))
+ return sdp_xml_parse_int(data, SDP_UINT16);
+ else if (!strcmp(el, "uint32"))
+ return sdp_xml_parse_int(data, SDP_UINT32);
+ else if (!strcmp(el, "uint64"))
+ return sdp_xml_parse_int(data, SDP_UINT64);
+ else if (!strcmp(el, "uint128"))
+ return sdp_xml_parse_int(data, SDP_UINT128);
+ else if (!strcmp(el, "int8"))
+ return sdp_xml_parse_int(data, SDP_INT8);
+ else if (!strcmp(el, "int16"))
+ return sdp_xml_parse_int(data, SDP_INT16);
+ else if (!strcmp(el, "int32"))
+ return sdp_xml_parse_int(data, SDP_INT32);
+ else if (!strcmp(el, "int64"))
+ return sdp_xml_parse_int(data, SDP_INT64);
+ else if (!strcmp(el, "int128"))
+ return sdp_xml_parse_int(data, SDP_INT128);
+ else if (!strcmp(el, "uuid"))
+ return sdp_xml_parse_uuid(data, record);
+ else if (!strcmp(el, "url"))
+ return sdp_xml_parse_url(data);
+ else if (!strcmp(el, "text"))
+ return sdp_xml_parse_text(data, elem->type);
+ else if (!strcmp(el, "nil"))
+ return sdp_xml_parse_nil(data);
+
+ return NULL;
+}
diff --git a/common/sdp-xml.h b/common/sdp-xml.h
new file mode 100644
index 00000000..adb173e2
--- /dev/null
+++ b/common/sdp-xml.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+
+#ifndef __SDP_XML_H
+#define __SDP_XML_H
+
+#include <bluetooth/sdp.h>
+
+#define SDP_XML_ENCODING_NORMAL 0
+#define SDP_XML_ENCODING_HEX 1
+
+void convert_sdp_record_to_xml(sdp_record_t *rec,
+ void *user_data, void (*append_func) (void *, const char *));
+
+sdp_data_t *sdp_xml_parse_nil(const char *data);
+sdp_data_t *sdp_xml_parse_text(const char *data, char encoding);
+sdp_data_t *sdp_xml_parse_url(const char *data);
+sdp_data_t *sdp_xml_parse_int(const char *data, uint8_t dtd);
+sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record);
+
+struct sdp_xml_data {
+ char *text; /* Pointer to the current buffer */
+ int size; /* Size of the current buffer */
+ sdp_data_t *data; /* The current item being built */
+ struct sdp_xml_data *next; /* Next item on the stack */
+ char type; /* 0 = Text or Hexadecimal */
+ char *name; /* Name, optional in the dtd */
+ /* TODO: What is it used for? */
+};
+
+struct sdp_xml_data *sdp_xml_data_alloc();
+void sdp_xml_data_free(struct sdp_xml_data *elem);
+struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem);
+
+sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem,
+ sdp_record_t *record);
+
+sdp_record_t *sdp_xml_parse_record(const char *data, int size);
+
+#endif /* __SDP_XML_H */
diff --git a/common/test_textfile.c b/common/test_textfile.c
new file mode 100644
index 00000000..1bb024eb
--- /dev/null
+++ b/common/test_textfile.c
@@ -0,0 +1,187 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "textfile.h"
+
+static void print_entry(char *key, char *value, void *data)
+{
+ printf("%s %s\n", key, value);
+}
+
+int main(int argc, char *argv[])
+{
+ char filename[] = "/tmp/textfile";
+ char key[18], value[512], *str;
+ int i, j, fd, err, size, max = 10;
+
+ size = getpagesize();
+ printf("System uses a page size of %d bytes\n\n", size);
+
+ fd = creat(filename, 0644);
+ err = ftruncate(fd, 0);
+
+ memset(value, 0, sizeof(value));
+ for (i = 0; i < (size / sizeof(value)); i++)
+ err = write(fd, value, sizeof(value));
+
+ close(fd);
+
+ sprintf(key, "11:11:11:11:11:11");
+ str = textfile_get(filename, key);
+
+ err = truncate(filename, 0);
+
+
+ sprintf(key, "00:00:00:00:00:00");
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ memset(value, 0, sizeof(value));
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ str = textfile_get(filename, key);
+ if (!str)
+ fprintf(stderr, "No value for %s\n", key);
+ else
+ free(str);
+
+ snprintf(value, sizeof(value), "Test");
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ str = textfile_get(filename, key);
+ if (str) {
+ fprintf(stderr, "Found value for %s\n", key);
+ free(str);
+ }
+
+ for (i = 1; i < max + 1; i++) {
+ sprintf(key, "00:00:00:00:00:%02X", i);
+
+ memset(value, 0, sizeof(value));
+ for (j = 0; j < i; j++)
+ value[j] = 'x';
+
+ printf("%s %s\n", key, value);
+
+ if (textfile_put(filename, key, value) < 0) {
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+ break;
+ }
+
+ str = textfile_get(filename, key);
+ if (!str)
+ fprintf(stderr, "No value for %s\n", key);
+ else
+ free(str);
+ }
+
+
+ sprintf(key, "00:00:00:00:00:%02X", max);
+
+ memset(value, 0, sizeof(value));
+ for (j = 0; j < max; j++)
+ value[j] = 'y';
+
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ sprintf(key, "00:00:00:00:00:%02X", 1);
+
+ memset(value, 0, sizeof(value));
+ for (j = 0; j < max; j++)
+ value[j] = 'z';
+
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ printf("\n");
+
+ for (i = 1; i < max + 1; i++) {
+ sprintf(key, "00:00:00:00:00:%02X", i);
+
+ str = textfile_get(filename, key);
+ if (str) {
+ printf("%s %s\n", key, str);
+ free(str);
+ }
+ }
+
+
+ sprintf(key, "00:00:00:00:00:%02X", 2);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ sprintf(key, "00:00:00:00:00:%02X", max - 3);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ printf("\n");
+
+ textfile_foreach(filename, print_entry, NULL);
+
+
+ sprintf(key, "00:00:00:00:00:%02X", 1);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ sprintf(key, "00:00:00:00:00:%02X", max);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ sprintf(key, "00:00:00:00:00:%02X", max + 1);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ printf("\n");
+
+ textfile_foreach(filename, print_entry, NULL);
+
+ return 0;
+}
diff --git a/common/textfile.c b/common/textfile.c
new file mode 100644
index 00000000..eef0f96a
--- /dev/null
+++ b/common/textfile.c
@@ -0,0 +1,463 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include "textfile.h"
+
+int create_dirs(const char *filename, const mode_t mode)
+{
+ struct stat st;
+ char dir[PATH_MAX + 1], *prev, *next;
+ int err;
+
+ err = stat(filename, &st);
+ if (!err && S_ISREG(st.st_mode))
+ return 0;
+
+ memset(dir, 0, PATH_MAX + 1);
+ strcat(dir, "/");
+
+ prev = strchr(filename, '/');
+
+ while (prev) {
+ next = strchr(prev + 1, '/');
+ if (!next)
+ break;
+
+ if (next - prev == 1) {
+ prev = next;
+ continue;
+ }
+
+ strncat(dir, prev + 1, next - prev);
+ mkdir(dir, mode);
+
+ prev = next;
+ }
+
+ return 0;
+}
+
+int create_file(const char *filename, const mode_t mode)
+{
+ int fd;
+
+ umask(S_IWGRP | S_IWOTH);
+ create_dirs(filename, S_IRUSR | S_IWUSR | S_IXUSR |
+ S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+
+ fd = open(filename, O_RDWR | O_CREAT, mode);
+ if (fd < 0)
+ return fd;
+
+ close(fd);
+
+ return 0;
+}
+
+int create_name(char *buf, size_t size, const char *path, const char *address, const char *name)
+{
+ return snprintf(buf, size, "%s/%s/%s", path, address, name);
+}
+
+static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase)
+{
+ char *ptr = map;
+ size_t ptrlen = size;
+
+ while (ptrlen > len + 1) {
+ int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len);
+ if (cmp == 0) {
+ if (ptr == map)
+ return ptr;
+
+ if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') &&
+ *(ptr + len) == ' ')
+ return ptr;
+ }
+
+ if (icase) {
+ char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1);
+ char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1);
+
+ if (!p1)
+ ptr = p2;
+ else if (!p2)
+ ptr = p1;
+ else
+ ptr = (p1 < p2) ? p1 : p2;
+ } else
+ ptr = memchr(ptr + 1, *key, ptrlen - 1);
+
+ if (!ptr)
+ return NULL;
+
+ ptrlen = size - (ptr - map);
+ }
+
+ return NULL;
+}
+
+static inline int write_key_value(int fd, const char *key, const char *value)
+{
+ char *str;
+ size_t size;
+ int err = 0;
+
+ size = strlen(key) + strlen(value) + 2;
+
+ str = malloc(size + 1);
+ if (!str)
+ return ENOMEM;
+
+ sprintf(str, "%s %s\n", key, value);
+
+ if (write(fd, str, size) < 0)
+ err = errno;
+
+ free(str);
+
+ return err;
+}
+
+static int write_key(const char *pathname, const char *key, const char *value, int icase)
+{
+ struct stat st;
+ char *map, *off, *end, *str;
+ off_t size, pos; size_t base, len;
+ int fd, err = 0;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ return -errno;
+
+ if (flock(fd, LOCK_EX) < 0) {
+ err = errno;
+ goto close;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ err = errno;
+ goto unlock;
+ }
+
+ size = st.st_size;
+
+ if (!size) {
+ if (value) {
+ pos = lseek(fd, size, SEEK_SET);
+ err = write_key_value(fd, key, value);
+ }
+ goto unlock;
+ }
+
+ map = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_LOCKED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ err = errno;
+ goto unlock;
+ }
+
+ len = strlen(key);
+ off = find_key(map, size, key, len, icase);
+ if (!off) {
+ if (value) {
+ munmap(map, size);
+ pos = lseek(fd, size, SEEK_SET);
+ err = write_key_value(fd, key, value);
+ }
+ goto unlock;
+ }
+
+ base = off - map;
+
+ end = strpbrk(off, "\r\n");
+ if (!end) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ if (value && (strlen(value) == end - off - len - 1) &&
+ !strncmp(off + len + 1, value, end - off - len - 1))
+ goto unmap;
+
+ len = strspn(end, "\r\n");
+ end += len;
+
+ len = size - (end - map);
+ if (!len) {
+ munmap(map, size);
+ if (ftruncate(fd, base) < 0) {
+ err = errno;
+ goto unlock;
+ }
+ pos = lseek(fd, base, SEEK_SET);
+ if (value)
+ err = write_key_value(fd, key, value);
+
+ goto unlock;
+ }
+
+ if (len < 0 || len > size) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ str = malloc(len);
+ if (!str) {
+ err = errno;
+ goto unmap;
+ }
+
+ memcpy(str, end, len);
+
+ munmap(map, size);
+ if (ftruncate(fd, base) < 0) {
+ err = errno;
+ free(str);
+ goto unlock;
+ }
+ pos = lseek(fd, base, SEEK_SET);
+ if (value)
+ err = write_key_value(fd, key, value);
+
+ if (write(fd, str, len) < 0)
+ err = errno;
+
+ free(str);
+
+ goto unlock;
+
+unmap:
+ munmap(map, size);
+
+unlock:
+ flock(fd, LOCK_UN);
+
+close:
+ close(fd);
+ errno = err;
+
+ return -err;
+}
+
+static char *read_key(const char *pathname, const char *key, int icase)
+{
+ struct stat st;
+ char *map, *off, *end, *str = NULL;
+ off_t size; size_t len;
+ int fd, err = 0;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ if (flock(fd, LOCK_SH) < 0) {
+ err = errno;
+ goto close;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ err = errno;
+ goto unlock;
+ }
+
+ size = st.st_size;
+
+ map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ err = errno;
+ goto unlock;
+ }
+
+ len = strlen(key);
+ off = find_key(map, size, key, len, icase);
+ if (!off) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ end = strpbrk(off, "\r\n");
+ if (!end) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ str = malloc(end - off - len);
+ if (!str) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ memset(str, 0, end - off - len);
+ strncpy(str, off + len + 1, end - off - len - 1);
+
+unmap:
+ munmap(map, size);
+
+unlock:
+ flock(fd, LOCK_UN);
+
+close:
+ close(fd);
+ errno = err;
+
+ return str;
+}
+
+int textfile_put(const char *pathname, const char *key, const char *value)
+{
+ return write_key(pathname, key, value, 0);
+}
+
+int textfile_caseput(const char *pathname, const char *key, const char *value)
+{
+ return write_key(pathname, key, value, 1);
+}
+
+int textfile_del(const char *pathname, const char *key)
+{
+ return write_key(pathname, key, NULL, 0);
+}
+
+int textfile_casedel(const char *pathname, const char *key)
+{
+ return write_key(pathname, key, NULL, 1);
+}
+
+char *textfile_get(const char *pathname, const char *key)
+{
+ return read_key(pathname, key, 0);
+}
+
+char *textfile_caseget(const char *pathname, const char *key)
+{
+ return read_key(pathname, key, 1);
+}
+
+int textfile_foreach(const char *pathname,
+ void (*func)(char *key, char *value, void *data), void *data)
+{
+ struct stat st;
+ char *map, *off, *end, *key, *value;
+ off_t size; size_t len;
+ int fd, err = 0;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ if (flock(fd, LOCK_SH) < 0) {
+ err = errno;
+ goto close;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ err = errno;
+ goto unlock;
+ }
+
+ size = st.st_size;
+
+ map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ err = errno;
+ goto unlock;
+ }
+
+ off = map;
+
+ while (1) {
+ end = strpbrk(off, " ");
+ if (!end) {
+ err = EILSEQ;
+ break;
+ }
+
+ len = end - off;
+
+ key = malloc(len + 1);
+ if (!key) {
+ err = errno;
+ break;
+ }
+
+ memset(key, 0, len + 1);
+ memcpy(key, off, len);
+
+ off = end + 1;
+
+ end = strpbrk(off, "\r\n");
+ if (!end) {
+ err = EILSEQ;
+ free(key);
+ break;
+ }
+
+ len = end - off;
+
+ value = malloc(len + 1);
+ if (!value) {
+ err = errno;
+ free(key);
+ break;
+ }
+
+ memset(value, 0, len + 1);
+ memcpy(value, off, len);
+
+ func(key, value, data);
+
+ free(key);
+ free(value);
+
+ off = end + 1;
+ }
+
+ munmap(map, size);
+
+unlock:
+ flock(fd, LOCK_UN);
+
+close:
+ close(fd);
+ errno = err;
+
+ return 0;
+}
diff --git a/common/textfile.h b/common/textfile.h
new file mode 100644
index 00000000..b2276e3a
--- /dev/null
+++ b/common/textfile.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __TEXTFILE_H
+#define __TEXTFILE_H
+
+int create_dirs(const char *filename, const mode_t mode);
+int create_file(const char *filename, const mode_t mode);
+int create_name(char *buf, size_t size, const char *path,
+ const char *address, const char *name);
+
+int textfile_put(const char *pathname, const char *key, const char *value);
+int textfile_caseput(const char *pathname, const char *key, const char *value);
+int textfile_del(const char *pathname, const char *key);
+int textfile_casedel(const char *pathname, const char *key);
+char *textfile_get(const char *pathname, const char *key);
+char *textfile_caseget(const char *pathname, const char *key);
+
+int textfile_foreach(const char *pathname,
+ void (*func)(char *key, char *value, void *data), void *data);
+
+#endif /* __TEXTFILE_H */
diff --git a/common/uinput.h b/common/uinput.h
new file mode 100644
index 00000000..dca87829
--- /dev/null
+++ b/common/uinput.h
@@ -0,0 +1,586 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __UINPUT_H
+#define __UINPUT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+/* Events */
+
+#define EV_SYN 0x00
+#define EV_KEY 0x01
+#define EV_REL 0x02
+#define EV_ABS 0x03
+#define EV_MSC 0x04
+#define EV_LED 0x11
+#define EV_SND 0x12
+#define EV_REP 0x14
+#define EV_FF 0x15
+#define EV_PWR 0x16
+#define EV_FF_STATUS 0x17
+#define EV_MAX 0x1f
+
+/* Synchronization events */
+
+#define SYN_REPORT 0
+#define SYN_CONFIG 1
+
+/* Keys and buttons */
+
+#define KEY_RESERVED 0
+#define KEY_ESC 1
+#define KEY_1 2
+#define KEY_2 3
+#define KEY_3 4
+#define KEY_4 5
+#define KEY_5 6
+#define KEY_6 7
+#define KEY_7 8
+#define KEY_8 9
+#define KEY_9 10
+#define KEY_0 11
+#define KEY_MINUS 12
+#define KEY_EQUAL 13
+#define KEY_BACKSPACE 14
+#define KEY_TAB 15
+#define KEY_Q 16
+#define KEY_W 17
+#define KEY_E 18
+#define KEY_R 19
+#define KEY_T 20
+#define KEY_Y 21
+#define KEY_U 22
+#define KEY_I 23
+#define KEY_O 24
+#define KEY_P 25
+#define KEY_LEFTBRACE 26
+#define KEY_RIGHTBRACE 27
+#define KEY_ENTER 28
+#define KEY_LEFTCTRL 29
+#define KEY_A 30
+#define KEY_S 31
+#define KEY_D 32
+#define KEY_F 33
+#define KEY_G 34
+#define KEY_H 35
+#define KEY_J 36
+#define KEY_K 37
+#define KEY_L 38
+#define KEY_SEMICOLON 39
+#define KEY_APOSTROPHE 40
+#define KEY_GRAVE 41
+#define KEY_LEFTSHIFT 42
+#define KEY_BACKSLASH 43
+#define KEY_Z 44
+#define KEY_X 45
+#define KEY_C 46
+#define KEY_V 47
+#define KEY_B 48
+#define KEY_N 49
+#define KEY_M 50
+#define KEY_COMMA 51
+#define KEY_DOT 52
+#define KEY_SLASH 53
+#define KEY_RIGHTSHIFT 54
+#define KEY_KPASTERISK 55
+#define KEY_LEFTALT 56
+#define KEY_SPACE 57
+#define KEY_CAPSLOCK 58
+#define KEY_F1 59
+#define KEY_F2 60
+#define KEY_F3 61
+#define KEY_F4 62
+#define KEY_F5 63
+#define KEY_F6 64
+#define KEY_F7 65
+#define KEY_F8 66
+#define KEY_F9 67
+#define KEY_F10 68
+#define KEY_NUMLOCK 69
+#define KEY_SCROLLLOCK 70
+#define KEY_KP7 71
+#define KEY_KP8 72
+#define KEY_KP9 73
+#define KEY_KPMINUS 74
+#define KEY_KP4 75
+#define KEY_KP5 76
+#define KEY_KP6 77
+#define KEY_KPPLUS 78
+#define KEY_KP1 79
+#define KEY_KP2 80
+#define KEY_KP3 81
+#define KEY_KP0 82
+#define KEY_KPDOT 83
+#define KEY_103RD 84
+#define KEY_F13 85
+#define KEY_102ND 86
+#define KEY_F11 87
+#define KEY_F12 88
+#define KEY_F14 89
+#define KEY_F15 90
+#define KEY_F16 91
+#define KEY_F17 92
+#define KEY_F18 93
+#define KEY_F19 94
+#define KEY_F20 95
+#define KEY_KPENTER 96
+#define KEY_RIGHTCTRL 97
+#define KEY_KPSLASH 98
+#define KEY_SYSRQ 99
+#define KEY_RIGHTALT 100
+#define KEY_LINEFEED 101
+#define KEY_HOME 102
+#define KEY_UP 103
+#define KEY_PAGEUP 104
+#define KEY_LEFT 105
+#define KEY_RIGHT 106
+#define KEY_END 107
+#define KEY_DOWN 108
+#define KEY_PAGEDOWN 109
+#define KEY_INSERT 110
+#define KEY_DELETE 111
+#define KEY_MACRO 112
+#define KEY_MUTE 113
+#define KEY_VOLUMEDOWN 114
+#define KEY_VOLUMEUP 115
+#define KEY_POWER 116
+#define KEY_KPEQUAL 117
+#define KEY_KPPLUSMINUS 118
+#define KEY_PAUSE 119
+#define KEY_F21 120
+#define KEY_F22 121
+#define KEY_F23 122
+#define KEY_F24 123
+#define KEY_KPCOMMA 124
+#define KEY_LEFTMETA 125
+#define KEY_RIGHTMETA 126
+#define KEY_COMPOSE 127
+
+#define KEY_STOP 128
+#define KEY_AGAIN 129
+#define KEY_PROPS 130
+#define KEY_UNDO 131
+#define KEY_FRONT 132
+#define KEY_COPY 133
+#define KEY_OPEN 134
+#define KEY_PASTE 135
+#define KEY_FIND 136
+#define KEY_CUT 137
+#define KEY_HELP 138
+#define KEY_MENU 139
+#define KEY_CALC 140
+#define KEY_SETUP 141
+#define KEY_SLEEP 142
+#define KEY_WAKEUP 143
+#define KEY_FILE 144
+#define KEY_SENDFILE 145
+#define KEY_DELETEFILE 146
+#define KEY_XFER 147
+#define KEY_PROG1 148
+#define KEY_PROG2 149
+#define KEY_WWW 150
+#define KEY_MSDOS 151
+#define KEY_COFFEE 152
+#define KEY_DIRECTION 153
+#define KEY_CYCLEWINDOWS 154
+#define KEY_MAIL 155
+#define KEY_BOOKMARKS 156
+#define KEY_COMPUTER 157
+#define KEY_BACK 158
+#define KEY_FORWARD 159
+#define KEY_CLOSECD 160
+#define KEY_EJECTCD 161
+#define KEY_EJECTCLOSECD 162
+#define KEY_NEXTSONG 163
+#define KEY_PLAYPAUSE 164
+#define KEY_PREVIOUSSONG 165
+#define KEY_STOPCD 166
+#define KEY_RECORD 167
+#define KEY_REWIND 168
+#define KEY_PHONE 169
+#define KEY_ISO 170
+#define KEY_CONFIG 171
+#define KEY_HOMEPAGE 172
+#define KEY_REFRESH 173
+#define KEY_EXIT 174
+#define KEY_MOVE 175
+#define KEY_EDIT 176
+#define KEY_SCROLLUP 177
+#define KEY_SCROLLDOWN 178
+#define KEY_KPLEFTPAREN 179
+#define KEY_KPRIGHTPAREN 180
+
+#define KEY_INTL1 181
+#define KEY_INTL2 182
+#define KEY_INTL3 183
+#define KEY_INTL4 184
+#define KEY_INTL5 185
+#define KEY_INTL6 186
+#define KEY_INTL7 187
+#define KEY_INTL8 188
+#define KEY_INTL9 189
+#define KEY_LANG1 190
+#define KEY_LANG2 191
+#define KEY_LANG3 192
+#define KEY_LANG4 193
+#define KEY_LANG5 194
+#define KEY_LANG6 195
+#define KEY_LANG7 196
+#define KEY_LANG8 197
+#define KEY_LANG9 198
+
+#define KEY_PLAYCD 200
+#define KEY_PAUSECD 201
+#define KEY_PROG3 202
+#define KEY_PROG4 203
+#define KEY_SUSPEND 205
+#define KEY_CLOSE 206
+#define KEY_PLAY 207
+
+#define KEY_UNKNOWN 220
+
+#define KEY_BRIGHTNESSDOWN 224
+#define KEY_BRIGHTNESSUP 225
+
+#define BTN_MISC 0x100
+#define BTN_0 0x100
+#define BTN_1 0x101
+#define BTN_2 0x102
+#define BTN_3 0x103
+#define BTN_4 0x104
+#define BTN_5 0x105
+#define BTN_6 0x106
+#define BTN_7 0x107
+#define BTN_8 0x108
+#define BTN_9 0x109
+
+#define BTN_MOUSE 0x110
+#define BTN_LEFT 0x110
+#define BTN_RIGHT 0x111
+#define BTN_MIDDLE 0x112
+#define BTN_SIDE 0x113
+#define BTN_EXTRA 0x114
+#define BTN_FORWARD 0x115
+#define BTN_BACK 0x116
+#define BTN_TASK 0x117
+
+#define BTN_JOYSTICK 0x120
+#define BTN_TRIGGER 0x120
+#define BTN_THUMB 0x121
+#define BTN_THUMB2 0x122
+#define BTN_TOP 0x123
+#define BTN_TOP2 0x124
+#define BTN_PINKIE 0x125
+#define BTN_BASE 0x126
+#define BTN_BASE2 0x127
+#define BTN_BASE3 0x128
+#define BTN_BASE4 0x129
+#define BTN_BASE5 0x12a
+#define BTN_BASE6 0x12b
+#define BTN_DEAD 0x12f
+
+#define BTN_GAMEPAD 0x130
+#define BTN_A 0x130
+#define BTN_B 0x131
+#define BTN_C 0x132
+#define BTN_X 0x133
+#define BTN_Y 0x134
+#define BTN_Z 0x135
+#define BTN_TL 0x136
+#define BTN_TR 0x137
+#define BTN_TL2 0x138
+#define BTN_TR2 0x139
+#define BTN_SELECT 0x13a
+#define BTN_START 0x13b
+#define BTN_MODE 0x13c
+#define BTN_THUMBL 0x13d
+#define BTN_THUMBR 0x13e
+
+#define BTN_DIGI 0x140
+#define BTN_TOOL_PEN 0x140
+#define BTN_TOOL_RUBBER 0x141
+#define BTN_TOOL_BRUSH 0x142
+#define BTN_TOOL_PENCIL 0x143
+#define BTN_TOOL_AIRBRUSH 0x144
+#define BTN_TOOL_FINGER 0x145
+#define BTN_TOOL_MOUSE 0x146
+#define BTN_TOOL_LENS 0x147
+#define BTN_TOUCH 0x14a
+#define BTN_STYLUS 0x14b
+#define BTN_STYLUS2 0x14c
+#define BTN_TOOL_DOUBLETAP 0x14d
+#define BTN_TOOL_TRIPLETAP 0x14e
+
+#define BTN_WHEEL 0x150
+#define BTN_GEAR_DOWN 0x150
+#define BTN_GEAR_UP 0x151
+
+#define KEY_OK 0x160
+#define KEY_SELECT 0x161
+#define KEY_GOTO 0x162
+#define KEY_CLEAR 0x163
+#define KEY_POWER2 0x164
+#define KEY_OPTION 0x165
+#define KEY_INFO 0x166
+#define KEY_TIME 0x167
+#define KEY_VENDOR 0x168
+#define KEY_ARCHIVE 0x169
+#define KEY_PROGRAM 0x16a
+#define KEY_CHANNEL 0x16b
+#define KEY_FAVORITES 0x16c
+#define KEY_EPG 0x16d
+#define KEY_PVR 0x16e
+#define KEY_MHP 0x16f
+#define KEY_LANGUAGE 0x170
+#define KEY_TITLE 0x171
+#define KEY_SUBTITLE 0x172
+#define KEY_ANGLE 0x173
+#define KEY_ZOOM 0x174
+#define KEY_MODE 0x175
+#define KEY_KEYBOARD 0x176
+#define KEY_SCREEN 0x177
+#define KEY_PC 0x178
+#define KEY_TV 0x179
+#define KEY_TV2 0x17a
+#define KEY_VCR 0x17b
+#define KEY_VCR2 0x17c
+#define KEY_SAT 0x17d
+#define KEY_SAT2 0x17e
+#define KEY_CD 0x17f
+#define KEY_TAPE 0x180
+#define KEY_RADIO 0x181
+#define KEY_TUNER 0x182
+#define KEY_PLAYER 0x183
+#define KEY_TEXT 0x184
+#define KEY_DVD 0x185
+#define KEY_AUX 0x186
+#define KEY_MP3 0x187
+#define KEY_AUDIO 0x188
+#define KEY_VIDEO 0x189
+#define KEY_DIRECTORY 0x18a
+#define KEY_LIST 0x18b
+#define KEY_MEMO 0x18c
+#define KEY_CALENDAR 0x18d
+#define KEY_RED 0x18e
+#define KEY_GREEN 0x18f
+#define KEY_YELLOW 0x190
+#define KEY_BLUE 0x191
+#define KEY_CHANNELUP 0x192
+#define KEY_CHANNELDOWN 0x193
+#define KEY_FIRST 0x194
+#define KEY_LAST 0x195
+#define KEY_AB 0x196
+#define KEY_NEXT 0x197
+#define KEY_RESTART 0x198
+#define KEY_SLOW 0x199
+#define KEY_SHUFFLE 0x19a
+#define KEY_BREAK 0x19b
+#define KEY_PREVIOUS 0x19c
+#define KEY_DIGITS 0x19d
+#define KEY_TEEN 0x19e
+#define KEY_TWEN 0x19f
+
+#define KEY_FRAMEBACK 0x1b2
+#define KEY_FRAMEFORWARD 0x1b3
+#define KEY_CONTEXT_MENU 0x1fb
+
+#define KEY_MAX 0x1ff
+
+/* Relative axes */
+
+#define REL_X 0x00
+#define REL_Y 0x01
+#define REL_Z 0x02
+#define REL_RX 0x03
+#define REL_RY 0x04
+#define REL_RZ 0x05
+#define REL_HWHEEL 0x06
+#define REL_DIAL 0x07
+#define REL_WHEEL 0x08
+#define REL_MISC 0x09
+#define REL_MAX 0x0f
+
+/* Absolute axes */
+
+#define ABS_X 0x00
+#define ABS_Y 0x01
+#define ABS_Z 0x02
+#define ABS_RX 0x03
+#define ABS_RY 0x04
+#define ABS_RZ 0x05
+#define ABS_THROTTLE 0x06
+#define ABS_RUDDER 0x07
+#define ABS_WHEEL 0x08
+#define ABS_GAS 0x09
+#define ABS_BRAKE 0x0a
+#define ABS_HAT0X 0x10
+#define ABS_HAT0Y 0x11
+#define ABS_HAT1X 0x12
+#define ABS_HAT1Y 0x13
+#define ABS_HAT2X 0x14
+#define ABS_HAT2Y 0x15
+#define ABS_HAT3X 0x16
+#define ABS_HAT3Y 0x17
+#define ABS_PRESSURE 0x18
+#define ABS_DISTANCE 0x19
+#define ABS_TILT_X 0x1a
+#define ABS_TILT_Y 0x1b
+#define ABS_TOOL_WIDTH 0x1c
+#define ABS_VOLUME 0x20
+#define ABS_MISC 0x28
+#define ABS_MAX 0x3f
+
+/* Switch events */
+
+#define SW_0 0x00
+#define SW_1 0x01
+#define SW_2 0x02
+#define SW_3 0x03
+#define SW_4 0x04
+#define SW_5 0x05
+#define SW_6 0x06
+#define SW_7 0x07
+#define SW_MAX 0x0f
+
+/* Misc events */
+
+#define MSC_SERIAL 0x00
+#define MSC_PULSELED 0x01
+#define MSC_GESTURE 0x02
+#define MSC_RAW 0x03
+#define MSC_SCAN 0x04
+#define MSC_MAX 0x07
+
+/* LEDs */
+
+#define LED_NUML 0x00
+#define LED_CAPSL 0x01
+#define LED_SCROLLL 0x02
+#define LED_COMPOSE 0x03
+#define LED_KANA 0x04
+#define LED_SLEEP 0x05
+#define LED_SUSPEND 0x06
+#define LED_MUTE 0x07
+#define LED_MISC 0x08
+#define LED_MAIL 0x09
+#define LED_CHARGING 0x0a
+#define LED_MAX 0x0f
+
+/* Autorepeat values */
+
+#define REP_DELAY 0x00
+#define REP_PERIOD 0x01
+#define REP_MAX 0x01
+
+/* Sounds */
+
+#define SND_CLICK 0x00
+#define SND_BELL 0x01
+#define SND_TONE 0x02
+#define SND_MAX 0x07
+
+/* Identifiers */
+
+#define ID_BUS 0
+#define ID_VENDOR 1
+#define ID_PRODUCT 2
+#define ID_VERSION 3
+
+#define BUS_PCI 0x01
+#define BUS_ISAPNP 0x02
+#define BUS_USB 0x03
+#define BUS_HIL 0x04
+#define BUS_BLUETOOTH 0x05
+
+#define BUS_ISA 0x10
+#define BUS_I8042 0x11
+#define BUS_XTKBD 0x12
+#define BUS_RS232 0x13
+#define BUS_GAMEPORT 0x14
+#define BUS_PARPORT 0x15
+#define BUS_AMIGA 0x16
+#define BUS_ADB 0x17
+#define BUS_I2C 0x18
+#define BUS_HOST 0x19
+#define BUS_GSC 0x1A
+
+/* User input interface */
+
+#define UINPUT_IOCTL_BASE 'U'
+
+#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
+#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
+
+#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
+#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
+#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
+#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int)
+#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int)
+#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int)
+#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int)
+#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
+#define UI_SET_PHYS _IOW(UINPUT_IOCTL_BASE, 108, char*)
+#define UI_SET_SWBIT _IOW(UINPUT_IOCTL_BASE, 109, int)
+
+#ifndef NBITS
+#define NBITS(x) ((((x) - 1) / (sizeof(long) * 8)) + 1)
+#endif
+
+#define UINPUT_MAX_NAME_SIZE 80
+
+struct uinput_id {
+ uint16_t bustype;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+};
+
+struct uinput_dev {
+ char name[UINPUT_MAX_NAME_SIZE];
+ struct uinput_id id;
+ int ff_effects_max;
+ int absmax[ABS_MAX + 1];
+ int absmin[ABS_MAX + 1];
+ int absfuzz[ABS_MAX + 1];
+ int absflat[ABS_MAX + 1];
+};
+
+struct uinput_event {
+ struct timeval time;
+ uint16_t type;
+ uint16_t code;
+ int32_t value;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UINPUT_H */