summaryrefslogtreecommitdiffstats
path: root/compat
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2008-08-04 20:58:34 +0200
committerMarcel Holtmann <marcel@holtmann.org>2008-08-04 20:58:34 +0200
commit40372f9dc3ab115870a10343124216a041e55d17 (patch)
treeef5bfa80e3f560cf6856fd7a0d5c27c75c2cefe0 /compat
parent295eb0c879802eacff46cf3c962646363d81f6d3 (diff)
Move hidd pand and dund into compat directory
Diffstat (limited to 'compat')
-rw-r--r--compat/Makefile.am47
-rw-r--r--compat/bnep.c311
-rw-r--r--compat/dun.c331
-rw-r--r--compat/dund.172
-rw-r--r--compat/dund.c637
-rw-r--r--compat/dund.h40
-rw-r--r--compat/fakehid.c669
-rw-r--r--compat/fakehid.txt134
-rw-r--r--compat/hidd.141
-rw-r--r--compat/hidd.c861
-rw-r--r--compat/hidd.h30
-rw-r--r--compat/lib.h86
-rw-r--r--compat/msdun.c151
-rw-r--r--compat/pand.177
-rw-r--r--compat/pand.c800
-rw-r--r--compat/pand.h36
-rw-r--r--compat/sdp.c718
-rw-r--r--compat/sdp.h39
18 files changed, 5080 insertions, 0 deletions
diff --git a/compat/Makefile.am b/compat/Makefile.am
new file mode 100644
index 00000000..4054b3e7
--- /dev/null
+++ b/compat/Makefile.am
@@ -0,0 +1,47 @@
+
+bin_PROGRAMS =
+man_MANS =
+
+if HIDD
+bin_PROGRAMS += hidd
+
+hidd_SOURCES = hidd.c hidd.h sdp.c fakehid.c
+
+hidd_LDADD = $(top_builddir)/common/libhelper.a @BLUEZ_LIBS@ -lm
+
+if MANPAGES
+man_MANS += hidd.1
+endif
+endif
+
+if PAND
+bin_PROGRAMS += pand
+
+pand_SOURCES = pand.c pand.h bnep.c sdp.c
+
+pand_LDADD = $(top_builddir)/common/libhelper.a @BLUEZ_LIBS@
+endif
+
+if MANPAGES
+man_MANS += pand.1
+endif
+
+if DUND
+bin_PROGRAMS += dund
+
+dund_SOURCES = dund.c dund.h lib.h sdp.c dun.c msdun.c
+
+dund_LDADD = $(top_builddir)/common/libhelper.a @BLUEZ_LIBS@
+endif
+
+if MANPAGES
+man_MANS += dund.1
+endif
+
+AM_CFLAGS = @BLUEZ_CFLAGS@
+
+INCLUDES = -I$(top_srcdir)/common
+
+EXTRA_DIST = fakehid.txt hidd.1 pand.1 dund.1
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/compat/bnep.c b/compat/bnep.c
new file mode 100644
index 00000000..604ed552
--- /dev/null
+++ b/compat/bnep.c
@@ -0,0 +1,311 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-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 <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+
+#include <netinet/in.h>
+
+#include "pand.h"
+
+static int ctl;
+
+/* Compatibility with old ioctls */
+#define OLD_BNEPCONADD 1
+#define OLD_BNEPCONDEL 2
+#define OLD_BNEPGETCONLIST 3
+#define OLD_BNEPGETCONINFO 4
+
+static unsigned long bnepconnadd;
+static unsigned long bnepconndel;
+static unsigned long bnepgetconnlist;
+static unsigned long bnepgetconninfo;
+
+static struct {
+ char *str;
+ uint16_t uuid;
+} __svc[] = {
+ { "PANU", BNEP_SVC_PANU },
+ { "NAP", BNEP_SVC_NAP },
+ { "GN", BNEP_SVC_GN },
+ { NULL }
+};
+
+int bnep_str2svc(char *svc, uint16_t *uuid)
+{
+ int i;
+ for (i = 0; __svc[i].str; i++)
+ if (!strcasecmp(svc, __svc[i].str)) {
+ *uuid = __svc[i].uuid;
+ return 0;
+ }
+ return -1;
+}
+
+char *bnep_svc2str(uint16_t uuid)
+{
+ int i;
+ for (i = 0; __svc[i].str; i++)
+ if (__svc[i].uuid == uuid)
+ return __svc[i].str;
+ return NULL;
+}
+
+int bnep_init(void)
+{
+ ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+ if (ctl < 0) {
+ perror("Failed to open control socket");
+ return 1;
+ }
+
+ /* Temporary ioctl compatibility hack */
+ {
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[1];
+
+ req.cnum = 1;
+ req.ci = ci;
+
+ if (!ioctl(ctl, BNEPGETCONNLIST, &req)) {
+ /* New ioctls */
+ bnepconnadd = BNEPCONNADD;
+ bnepconndel = BNEPCONNDEL;
+ bnepgetconnlist = BNEPGETCONNLIST;
+ bnepgetconninfo = BNEPGETCONNINFO;
+ } else {
+ /* Old ioctls */
+ bnepconnadd = OLD_BNEPCONADD;
+ bnepconndel = OLD_BNEPCONDEL;
+ bnepgetconnlist = OLD_BNEPGETCONLIST;
+ bnepgetconninfo = OLD_BNEPGETCONINFO;
+ }
+ }
+
+ return 0;
+}
+
+int bnep_cleanup(void)
+{
+ close(ctl);
+ return 0;
+}
+
+int bnep_show_connections(void)
+{
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[48];
+ int i;
+
+ req.cnum = 48;
+ req.ci = ci;
+ if (ioctl(ctl, bnepgetconnlist, &req)) {
+ perror("Failed to get connection list");
+ return -1;
+ }
+
+ for (i=0; i < req.cnum; i++) {
+ printf("%s %s %s\n", ci[i].device,
+ batostr((bdaddr_t *) ci[i].dst),
+ bnep_svc2str(ci[i].role));
+ }
+ return 0;
+}
+
+int bnep_kill_connection(uint8_t *dst)
+{
+ struct bnep_conndel_req req;
+
+ memcpy(req.dst, dst, ETH_ALEN);
+ req.flags = 0;
+ if (ioctl(ctl, bnepconndel, &req)) {
+ perror("Failed to kill connection");
+ return -1;
+ }
+ return 0;
+}
+
+int bnep_kill_all_connections(void)
+{
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[48];
+ int i;
+
+ req.cnum = 48;
+ req.ci = ci;
+ if (ioctl(ctl, bnepgetconnlist, &req)) {
+ perror("Failed to get connection list");
+ return -1;
+ }
+
+ for (i=0; i < req.cnum; i++) {
+ struct bnep_conndel_req req;
+ memcpy(req.dst, ci[i].dst, ETH_ALEN);
+ req.flags = 0;
+ ioctl(ctl, bnepconndel, &req);
+ }
+ return 0;
+}
+
+static int bnep_connadd(int sk, uint16_t role, char *dev)
+{
+ struct bnep_connadd_req req;
+
+ strncpy(req.device, dev, 16);
+ req.device[15] = '\0';
+ req.sock = sk;
+ req.role = role;
+ if (ioctl(ctl, bnepconnadd, &req))
+ return -1;
+ strncpy(dev, req.device, 16);
+ return 0;
+}
+
+struct __service_16 {
+ uint16_t dst;
+ uint16_t src;
+} __attribute__ ((packed));
+
+struct __service_32 {
+ uint16_t unused1;
+ uint16_t dst;
+ uint16_t unused2;
+ uint16_t src;
+} __attribute__ ((packed));
+
+struct __service_128 {
+ uint16_t unused1;
+ uint16_t dst;
+ uint16_t unused2[8];
+ uint16_t src;
+ uint16_t unused3[7];
+} __attribute__ ((packed));
+
+int bnep_accept_connection(int sk, uint16_t role, char *dev)
+{
+ struct bnep_setup_conn_req *req;
+ struct bnep_control_rsp *rsp;
+ unsigned char pkt[BNEP_MTU];
+ int r;
+
+ r = recv(sk, pkt, BNEP_MTU, 0);
+ if (r <= 0)
+ return -1;
+
+ errno = EPROTO;
+
+ if (r < sizeof(*req))
+ return -1;
+
+ req = (void *) pkt;
+ if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+ return -1;
+
+ /* FIXME: Check role UUIDs */
+
+ rsp = (void *) pkt;
+ rsp->type = BNEP_CONTROL;
+ rsp->ctrl = BNEP_SETUP_CONN_RSP;
+ rsp->resp = htons(BNEP_SUCCESS);
+ if (send(sk, rsp, sizeof(*rsp), 0) < 0)
+ return -1;
+
+ return bnep_connadd(sk, role, dev);
+}
+
+/* Create BNEP connection
+ * sk - Connect L2CAP socket
+ * role - Local role
+ * service - Remote service
+ * dev - Network device (contains actual dev name on return)
+ */
+int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev)
+{
+ struct bnep_setup_conn_req *req;
+ struct bnep_control_rsp *rsp;
+ struct __service_16 *s;
+ unsigned char pkt[BNEP_MTU];
+ int r;
+
+ /* Send request */
+ req = (void *) pkt;
+ req->type = BNEP_CONTROL;
+ req->ctrl = BNEP_SETUP_CONN_REQ;
+ req->uuid_size = 2; /* 16bit UUID */
+ s = (void *) req->service;
+ s->dst = htons(svc);
+ s->src = htons(role);
+
+ if (send(sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0)
+ return -1;
+
+receive:
+ /* Get response */
+ r = recv(sk, pkt, BNEP_MTU, 0);
+ if (r <= 0)
+ return -1;
+
+ errno = EPROTO;
+
+ if (r < sizeof(*rsp))
+ return -1;
+
+ rsp = (void *) pkt;
+ if (rsp->type != BNEP_CONTROL)
+ return -1;
+
+ if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
+ goto receive;
+
+ r = ntohs(rsp->resp);
+
+ switch (r) {
+ case BNEP_SUCCESS:
+ break;
+
+ case BNEP_CONN_INVALID_DST:
+ case BNEP_CONN_INVALID_SRC:
+ case BNEP_CONN_INVALID_SVC:
+ errno = EPROTO;
+ return -1;
+
+ case BNEP_CONN_NOT_ALLOWED:
+ errno = EACCES;
+ return -1;
+ }
+
+ return bnep_connadd(sk, role, dev);
+}
diff --git a/compat/dun.c b/compat/dun.c
new file mode 100644
index 00000000..de437cf4
--- /dev/null
+++ b/compat/dun.c
@@ -0,0 +1,331 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <dirent.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "dund.h"
+#include "lib.h"
+
+#define PROC_BASE "/proc"
+
+static int for_each_port(int (*func)(struct rfcomm_dev_info *, unsigned long), unsigned long arg)
+{
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ long r = 0;
+ int sk, i;
+
+ sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+ if (sk < 0 ) {
+ perror("Can't open RFCOMM control socket");
+ exit(1);
+ }
+
+ dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+ if (!dl) {
+ perror("Can't allocate request memory");
+ close(sk);
+ exit(1);
+ }
+
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if (ioctl(sk, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ perror("Can't get device list");
+ exit(1);
+ }
+
+ for (i = 0; i < dl->dev_num; i++) {
+ r = func(di + i, arg);
+ if (r) break;
+ }
+
+ close(sk);
+ return r;
+}
+
+static int uses_rfcomm(char *path, char *dev)
+{
+ struct dirent *de;
+ DIR *dir;
+
+ dir = opendir(path);
+ if (!dir)
+ return 0;
+
+ if (chdir(path) < 0)
+ return 0;
+
+ while ((de = readdir(dir)) != NULL) {
+ char link[PATH_MAX + 1];
+ int len = readlink(de->d_name, link, sizeof(link));
+ if (len > 0) {
+ link[len] = 0;
+ if (strstr(link, dev))
+ return 1;
+ }
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+static int find_pppd(int id, pid_t *pid)
+{
+ struct dirent *de;
+ char path[PATH_MAX + 1];
+ char dev[10];
+ int empty = 1;
+ DIR *dir;
+
+ dir = opendir(PROC_BASE);
+ if (!dir) {
+ perror(PROC_BASE);
+ return -1;
+ }
+
+ sprintf(dev, "rfcomm%d", id);
+
+ *pid = 0;
+ while ((de = readdir(dir)) != NULL) {
+ empty = 0;
+ if (isdigit(de->d_name[0])) {
+ sprintf(path, "%s/%s/fd", PROC_BASE, de->d_name);
+ if (uses_rfcomm(path, dev)) {
+ *pid = atoi(de->d_name);
+ break;
+ }
+ }
+ }
+ closedir(dir);
+
+ if (empty)
+ fprintf(stderr, "%s is empty (not mounted ?)\n", PROC_BASE);
+
+ return *pid != 0;
+}
+
+static int dun_exec(char *tty, char *prog, char **args)
+{
+ int pid = fork();
+ int fd;
+
+ switch (pid) {
+ case -1:
+ return -1;
+
+ case 0:
+ break;
+
+ default:
+ return pid;
+ }
+
+ setsid();
+
+ /* Close all FDs */
+ for (fd = 3; fd < 20; fd++)
+ close(fd);
+
+ execvp(prog, args);
+
+ syslog(LOG_ERR, "Error while executing %s", prog);
+
+ exit(1);
+}
+
+static int dun_create_tty(int sk, char *tty, int size)
+{
+ struct sockaddr_rc sa;
+ struct stat st;
+ socklen_t alen;
+ int id, try = 30;
+
+ struct rfcomm_dev_req req = {
+ flags: (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
+ dev_id: -1
+ };
+
+ alen = sizeof(sa);
+ if (getpeername(sk, (struct sockaddr *) &sa, &alen) < 0)
+ return -1;
+ bacpy(&req.dst, &sa.rc_bdaddr);
+
+ alen = sizeof(sa);
+ if (getsockname(sk, (struct sockaddr *) &sa, &alen) < 0)
+ return -1;
+ bacpy(&req.src, &sa.rc_bdaddr);
+ req.channel = sa.rc_channel;
+
+ id = ioctl(sk, RFCOMMCREATEDEV, &req);
+ if (id < 0)
+ return id;
+
+ snprintf(tty, size, "/dev/rfcomm%d", id);
+ while (stat(tty, &st) < 0) {
+ snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id);
+ if (stat(tty, &st) < 0) {
+ snprintf(tty, size, "/dev/rfcomm%d", id);
+ if (try--) {
+ usleep(100 * 1000);
+ continue;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = id;
+ ioctl(sk, RFCOMMRELEASEDEV, &req);
+
+ return -1;
+ }
+ }
+
+ return id;
+}
+
+int dun_init(void)
+{
+ return 0;
+}
+
+int dun_cleanup(void)
+{
+ return 0;
+}
+
+static int show_conn(struct rfcomm_dev_info *di, unsigned long arg)
+{
+ pid_t pid;
+
+ if (di->state == BT_CONNECTED &&
+ (di->flags & (1<<RFCOMM_REUSE_DLC)) &&
+ (di->flags & (1<<RFCOMM_TTY_ATTACHED)) &&
+ (di->flags & (1<<RFCOMM_RELEASE_ONHUP))) {
+
+ if (find_pppd(di->id, &pid)) {
+ char dst[18];
+ ba2str(&di->dst, dst);
+
+ printf("rfcomm%d: %s channel %d pppd pid %d\n",
+ di->id, dst, di->channel, pid);
+ }
+ }
+ return 0;
+}
+
+static int kill_conn(struct rfcomm_dev_info *di, unsigned long arg)
+{
+ bdaddr_t *dst = (bdaddr_t *) arg;
+ pid_t pid;
+
+ if (di->state == BT_CONNECTED &&
+ (di->flags & (1<<RFCOMM_REUSE_DLC)) &&
+ (di->flags & (1<<RFCOMM_TTY_ATTACHED)) &&
+ (di->flags & (1<<RFCOMM_RELEASE_ONHUP))) {
+
+ if (dst && bacmp(&di->dst, dst))
+ return 0;
+
+ if (find_pppd(di->id, &pid)) {
+ if (kill(pid, SIGINT) < 0)
+ perror("Kill");
+
+ if (!dst)
+ return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int dun_show_connections(void)
+{
+ for_each_port(show_conn, 0);
+ return 0;
+}
+
+int dun_kill_connection(uint8_t *dst)
+{
+ for_each_port(kill_conn, (unsigned long) dst);
+ return 0;
+}
+
+int dun_kill_all_connections(void)
+{
+ for_each_port(kill_conn, 0);
+ return 0;
+}
+
+int dun_open_connection(int sk, char *pppd, char **args, int wait)
+{
+ char tty[100];
+ int pid;
+
+ if (dun_create_tty(sk, tty, sizeof(tty) - 1) < 0) {
+ syslog(LOG_ERR, "RFCOMM TTY creation failed. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ args[0] = "pppd";
+ args[1] = tty;
+ args[2] = "nodetach";
+
+ pid = dun_exec(tty, pppd, args);
+ if (pid < 0) {
+ syslog(LOG_ERR, "Exec failed. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ if (wait) {
+ int status;
+ waitpid(pid, &status, 0);
+ /* FIXME: Check for waitpid errors */
+ }
+
+ return 0;
+}
diff --git a/compat/dund.1 b/compat/dund.1
new file mode 100644
index 00000000..09fb7f75
--- /dev/null
+++ b/compat/dund.1
@@ -0,0 +1,72 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29.
+.TH BlueZ "1" "February 2003" "DUN daemon" "User Commands"
+.SH NAME
+dund \- BlueZ Bluetooth dial-up networking daemon
+.SH DESCRIPTION
+DUN daemon
+.SH SYNOPSIS
+dund <options> [pppd options]
+.SH OPTIONS
+.TP
+\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR
+Show active DUN connections
+.TP
+\fB\-\-listen\fR \fB\-s\fR
+Listen for DUN connections
+.TP
+\fB\-\-dialup\fR \fB\-u\fR
+Listen for dialup/telephone connections
+.TP
+\fB\-\-connect\fR \fB\-c\fR <bdaddr>
+Create DUN connection
+.TP
+\fB\-\-mrouter\fR \fB\-m\fR <bdaddr>
+Create mRouter connection
+.TP
+\fB\-\-search\fR \fB\-Q[duration]\fR
+Search and connect
+.TP
+\fB\-\-kill\fR \fB\-k\fR <bdaddr>
+Kill DUN connection
+.TP
+\fB\-\-killall\fR \fB\-K\fR
+Kill all DUN connections
+.TP
+\fB\-\-channel\fR \fB\-C\fR <channel>
+RFCOMM channel
+.TP
+\fB\-\-device\fR \fB\-i\fR <bdaddr>
+Source bdaddr
+.TP
+\fB\-\-nosdp\fR \fB\-D\fR
+Disable SDP
+.TP
+\fB\-\-auth\fR \fB\-A\fR
+Enable authentification
+.TP
+\fB\-\-encrypt\fR \fB\-E\fR
+Enable encryption
+.TP
+\fB\-\-secure\fR \fB\-S\fR
+Secure connection
+.TP
+\fB\-\-master\fR \fB\-M\fR
+Become the master of a piconet
+.TP
+\fB\-\-nodetach\fR \fB\-n\fR
+Do not become a daemon
+.TP
+\fB\-\-persist\fR \fB\-p[interval]\fR
+Persist mode
+.TP
+\fB\-\-pppd\fR \fB\-d\fR <pppd>
+Location of the PPP daemon (pppd)
+.TP
+\fB\-\-msdun\fR \fB\-X\fR [timeo]
+Enable Microsoft dialup networking support
+.TP
+\fB\-\-activesync\fR \fB\-a\fR
+Enable Microsoft ActiveSync networking
+.TP
+\fB\-\-cache\fR \fB\-C\fR [valid]
+Enable address cache
diff --git a/compat/dund.c b/compat/dund.c
new file mode 100644
index 00000000..dea945d2
--- /dev/null
+++ b/compat/dund.c
@@ -0,0 +1,637 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-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 <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "dund.h"
+#include "lib.h"
+
+volatile sig_atomic_t __io_canceled;
+
+/* MS dialup networking support (i.e. CLIENT / CLIENTSERVER thing) */
+static int msdun = 0;
+
+static char *pppd = "/usr/sbin/pppd";
+static char *pppd_opts[DUN_MAX_PPP_OPTS] =
+ {
+ /* First 3 are reserved */
+ "", "", "",
+ "noauth",
+ "noipdefault",
+ NULL
+ };
+
+static int detach = 1;
+static int persist;
+static int use_sdp = 1;
+static int auth;
+static int encrypt;
+static int secure;
+static int master;
+static int type = LANACCESS;
+static int search_duration = 10;
+static uint use_cache;
+
+static int channel;
+
+static struct {
+ uint valid;
+ char dst[40];
+ bdaddr_t bdaddr;
+ int channel;
+} cache;
+
+static bdaddr_t src_addr = *BDADDR_ANY;
+static int src_dev = -1;
+
+volatile int terminate;
+
+enum {
+ NONE,
+ SHOW,
+ LISTEN,
+ CONNECT,
+ KILL
+} modes;
+
+static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter);
+
+static int do_listen(void)
+{
+ struct sockaddr_rc sa;
+ int sk, lm;
+
+ if (type == MROUTER) {
+ if (!cache.valid)
+ return -1;
+
+ if (create_connection(cache.dst, &cache.bdaddr, type) < 0) {
+ syslog(LOG_ERR, "Cannot connect to mRouter device. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+ }
+
+ if (!channel)
+ channel = DUN_DEFAULT_CHANNEL;
+
+ if (use_sdp)
+ dun_sdp_register(&src_addr, channel, type);
+
+ if (type == MROUTER)
+ syslog(LOG_INFO, "Waiting for mRouter callback on channel %d", channel);
+
+ /* Create RFCOMM socket */
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = channel;
+ sa.rc_bdaddr = src_addr;
+
+ if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) {
+ syslog(LOG_ERR, "Bind failed. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ /* Set link mode */
+ lm = 0;
+ if (master)
+ lm |= RFCOMM_LM_MASTER;
+ if (auth)
+ lm |= RFCOMM_LM_AUTH;
+ if (encrypt)
+ lm |= RFCOMM_LM_ENCRYPT;
+ if (secure)
+ lm |= RFCOMM_LM_SECURE;
+
+ if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+ syslog(LOG_ERR, "Failed to set link mode. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ listen(sk, 10);
+
+ while (!terminate) {
+ socklen_t alen = sizeof(sa);
+ int nsk;
+ char ba[40];
+ char ch[10];
+
+ nsk = accept(sk, (struct sockaddr *) &sa, &alen);
+ if (nsk < 0) {
+ syslog(LOG_ERR, "Accept failed. %s(%d)", strerror(errno), errno);
+ continue;
+ }
+
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ syslog(LOG_ERR, "Fork failed. %s(%d)", strerror(errno), errno);
+ default:
+ close(nsk);
+ if (type == MROUTER) {
+ close(sk);
+ terminate = 1;
+ }
+ continue;
+ }
+
+ close(sk);
+
+ if (msdun && ms_dun(nsk, 1, msdun) < 0) {
+ syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno);
+ exit(0);
+ }
+
+ ba2str(&sa.rc_bdaddr, ba);
+ sprintf(ch, "%d", channel);
+
+ /* Setup environment */
+ setenv("DUN_BDADDR", ba, 1);
+ setenv("DUN_CHANNEL", ch, 1);
+
+ if (!dun_open_connection(nsk, pppd, pppd_opts, 0))
+ syslog(LOG_INFO, "New connection from %s", ba);
+
+ close(nsk);
+ exit(0);
+ }
+
+ if (use_sdp)
+ dun_sdp_unregister();
+ return 0;
+}
+
+/* Connect and initiate RFCOMM session
+ * Returns:
+ * -1 - critical error (exit persist mode)
+ * 1 - non critical error
+ * 0 - success
+ */
+static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter)
+{
+ struct sockaddr_rc sa;
+ int sk, err = 0, ch;
+
+ if (use_cache && cache.valid && cache.channel) {
+ /* Use cached channel */
+ ch = cache.channel;
+
+ } else if (!channel) {
+ syslog(LOG_INFO, "Searching for %s on %s", mrouter ? "SP" : "LAP", dst);
+
+ if (dun_sdp_search(&src_addr, bdaddr, &ch, mrouter) <= 0)
+ return 0;
+ } else
+ ch = channel;
+
+ syslog(LOG_INFO, "Connecting to %s channel %d", dst, ch);
+
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = 0;
+ sa.rc_bdaddr = src_addr;
+
+ if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)))
+ syslog(LOG_ERR, "Bind failed. %s(%d)",
+ strerror(errno), errno);
+
+ sa.rc_channel = ch;
+ sa.rc_bdaddr = *bdaddr;
+
+ if (!connect(sk, (struct sockaddr *) &sa, sizeof(sa)) ) {
+ if (mrouter) {
+ sleep(1);
+ close(sk);
+ return 0;
+ }
+
+ syslog(LOG_INFO, "Connection established");
+
+ if (msdun && ms_dun(sk, 0, msdun) < 0) {
+ syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno);
+ err = 1;
+ goto out;
+ }
+
+ if (!dun_open_connection(sk, pppd, pppd_opts, (persist > 0)))
+ err = 0;
+ else
+ err = 1;
+ } else {
+ syslog(LOG_ERR, "Connect to %s failed. %s(%d)",
+ dst, strerror(errno), errno);
+ err = 1;
+ }
+
+out:
+ if (use_cache) {
+ if (!err) {
+ /* Succesesful connection, validate cache */
+ strcpy(cache.dst, dst);
+ bacpy(&cache.bdaddr, bdaddr);
+ cache.channel = ch;
+ cache.valid = use_cache;
+ } else {
+ cache.channel = 0;
+ cache.valid--;
+ }
+ }
+
+ close(sk);
+ return err;
+}
+
+/* Search and connect
+ * Returns:
+ * -1 - critical error (exit persist mode)
+ * 1 - non critical error
+ * 0 - success
+ */
+static int do_connect(void)
+{
+ inquiry_info *ii;
+ int reconnect = 0;
+ int i, n, r = 0;
+
+ do {
+ if (reconnect)
+ sleep(persist);
+ reconnect = 1;
+
+ if (cache.valid) {
+ /* Use cached bdaddr */
+ r = create_connection(cache.dst, &cache.bdaddr, 0);
+ if (r < 0) {
+ terminate = 1;
+ break;
+ }
+ continue;
+ }
+
+ syslog(LOG_INFO, "Inquiring");
+
+ /* FIXME: Should we use non general LAP here ? */
+
+ ii = NULL;
+ n = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0);
+ if (n < 0) {
+ syslog(LOG_ERR, "Inquiry failed. %s(%d)", strerror(errno), errno);
+ continue;
+ }
+
+ for (i = 0; i < n; i++) {
+ char dst[40];
+ ba2str(&ii[i].bdaddr, dst);
+
+ r = create_connection(dst, &ii[i].bdaddr, 0);
+ if (r < 0) {
+ terminate = 1;
+ break;
+ }
+ }
+ bt_free(ii);
+ } while (!terminate && persist);
+
+ return r;
+}
+
+static void do_show(void)
+{
+ dun_show_connections();
+}
+
+static void do_kill(char *dst)
+{
+ if (dst) {
+ bdaddr_t ba;
+ str2ba(dst, &ba);
+ dun_kill_connection((void *) &ba);
+ } else
+ dun_kill_all_connections();
+}
+
+void sig_hup(int sig)
+{
+ return;
+}
+
+void sig_term(int sig)
+{
+ io_cancel();
+ terminate = 1;
+}
+
+static struct option main_lopts[] = {
+ { "help", 0, 0, 'h' },
+ { "listen", 0, 0, 's' },
+ { "connect", 1, 0, 'c' },
+ { "search", 2, 0, 'Q' },
+ { "kill", 1, 0, 'k' },
+ { "killall", 0, 0, 'K' },
+ { "channel", 1, 0, 'P' },
+ { "device", 1, 0, 'i' },
+ { "nosdp", 0, 0, 'D' },
+ { "list", 0, 0, 'l' },
+ { "show", 0, 0, 'l' },
+ { "nodetach", 0, 0, 'n' },
+ { "persist", 2, 0, 'p' },
+ { "auth", 0, 0, 'A' },
+ { "encrypt", 0, 0, 'E' },
+ { "secure", 0, 0, 'S' },
+ { "master", 0, 0, 'M' },
+ { "cache", 0, 0, 'C' },
+ { "pppd", 1, 0, 'd' },
+ { "msdun", 2, 0, 'X' },
+ { "activesync", 0, 0, 'a' },
+ { "mrouter", 1, 0, 'm' },
+ { "dialup", 0, 0, 'u' },
+ { 0, 0, 0, 0 }
+};
+
+static char main_sopts[] = "hsc:k:Kr:i:lnp::DQ::AESMP:C::P:Xam:u";
+
+static char main_help[] =
+ "Bluetooth LAP (LAN Access over PPP) daemon version " VERSION " \n"
+ "Usage:\n"
+ "\tdund <options> [pppd options]\n"
+ "Options:\n"
+ "\t--show --list -l Show active LAP connections\n"
+ "\t--listen -s Listen for LAP connections\n"
+ "\t--dialup -u Pretend to be a dialup/telephone\n"
+ "\t--connect -c <bdaddr> Create LAP connection\n"
+ "\t--mrouter -m <bdaddr> Create mRouter connection\n"
+ "\t--search -Q[duration] Search and connect\n"
+ "\t--kill -k <bdaddr> Kill LAP connection\n"
+ "\t--killall -K Kill all LAP connections\n"
+ "\t--channel -P <channel> RFCOMM channel\n"
+ "\t--device -i <bdaddr> Source bdaddr\n"
+ "\t--nosdp -D Disable SDP\n"
+ "\t--auth -A Enable authentication\n"
+ "\t--encrypt -E Enable encryption\n"
+ "\t--secure -S Secure connection\n"
+ "\t--master -M Become the master of a piconet\n"
+ "\t--nodetach -n Do not become a daemon\n"
+ "\t--persist -p[interval] Persist mode\n"
+ "\t--pppd -d <pppd> Location of the PPP daemon (pppd)\n"
+ "\t--msdun -X[timeo] Enable Microsoft dialup networking support\n"
+ "\t--activesync -a Enable Microsoft ActiveSync networking\n"
+ "\t--cache -C[valid] Enable address cache\n";
+
+int main(int argc, char *argv[])
+{
+ char *dst = NULL, *src = NULL;
+ struct sigaction sa;
+ int mode = NONE;
+ int opt;
+
+ while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) {
+ switch(opt) {
+ case 'l':
+ mode = SHOW;
+ detach = 0;
+ break;
+
+ case 's':
+ mode = LISTEN;
+ type = LANACCESS;
+ break;
+
+ case 'c':
+ mode = CONNECT;
+ dst = strdup(optarg);
+ break;
+
+ case 'Q':
+ mode = CONNECT;
+ dst = NULL;
+ if (optarg)
+ search_duration = atoi(optarg);
+ break;
+
+ case 'k':
+ mode = KILL;
+ detach = 0;
+ dst = strdup(optarg);
+ break;
+
+ case 'K':
+ mode = KILL;
+ detach = 0;
+ dst = NULL;
+ break;
+
+ case 'P':
+ channel = atoi(optarg);
+ break;
+
+ case 'i':
+ src = strdup(optarg);
+ break;
+
+ case 'D':
+ use_sdp = 0;
+ break;
+
+ case 'A':
+ auth = 1;
+ break;
+
+ case 'E':
+ encrypt = 1;
+ break;
+
+ case 'S':
+ secure = 1;
+ break;
+
+ case 'M':
+ master = 1;
+ break;
+
+ case 'n':
+ detach = 0;
+ break;
+
+ case 'p':
+ if (optarg)
+ persist = atoi(optarg);
+ else
+ persist = 5;
+ break;
+
+ case 'C':
+ if (optarg)
+ use_cache = atoi(optarg);
+ else
+ use_cache = 2;
+ break;
+
+ case 'd':
+ pppd = strdup(optarg);
+ break;
+
+ case 'X':
+ if (optarg)
+ msdun = atoi(optarg);
+ else
+ msdun = 10;
+ break;
+
+ case 'a':
+ msdun = 10;
+ type = ACTIVESYNC;
+ break;
+
+ case 'm':
+ mode = LISTEN;
+ dst = strdup(optarg);
+ type = MROUTER;
+ break;
+
+ case 'u':
+ mode = LISTEN;
+ type = DIALUP;
+ break;
+
+ case 'h':
+ default:
+ printf(main_help);
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* The rest is pppd options */
+ if (argc > 0) {
+ for (opt = 3; argc && opt < DUN_MAX_PPP_OPTS; argc--, opt++)
+ pppd_opts[opt] = *argv++;
+ pppd_opts[opt] = NULL;
+ }
+
+ io_init();
+
+ if (dun_init())
+ return -1;
+
+ /* Check non daemon modes first */
+ switch (mode) {
+ case SHOW:
+ do_show();
+ return 0;
+
+ case KILL:
+ do_kill(dst);
+ return 0;
+
+ case NONE:
+ printf(main_help);
+ return 0;
+ }
+
+ /* Initialize signals */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ if (detach && daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+
+ openlog("dund", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+ syslog(LOG_INFO, "Bluetooth DUN daemon version %s", VERSION);
+
+ if (src) {
+ src_dev = hci_devid(src);
+ if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) {
+ syslog(LOG_ERR, "Invalid source. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+ }
+
+ if (dst) {
+ strncpy(cache.dst, dst, sizeof(cache.dst) - 1);
+ str2ba(dst, &cache.bdaddr);
+
+ /* Disable cache invalidation */
+ use_cache = cache.valid = ~0;
+ }
+
+ switch (mode) {
+ case CONNECT:
+ do_connect();
+ break;
+
+ case LISTEN:
+ do_listen();
+ break;
+ }
+
+ return 0;
+}
diff --git a/compat/dund.h b/compat/dund.h
new file mode 100644
index 00000000..c3308e1e
--- /dev/null
+++ b/compat/dund.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-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
+ *
+ */
+
+#define DUN_CONFIG_DIR "/etc/bluetooth/dun"
+
+#define DUN_DEFAULT_CHANNEL 1
+
+#define DUN_MAX_PPP_OPTS 40
+
+int dun_init(void);
+int dun_cleanup(void);
+
+int dun_show_connections(void);
+int dun_kill_connection(uint8_t *dst);
+int dun_kill_all_connections(void);
+
+int dun_open_connection(int sk, char *pppd, char **pppd_opts, int wait);
+
+int ms_dun(int fd, int server, int timeo);
diff --git a/compat/fakehid.c b/compat/fakehid.c
new file mode 100644
index 00000000..feefe2a6
--- /dev/null
+++ b/compat/fakehid.c
@@ -0,0 +1,669 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/hidp.h>
+
+#include "hidd.h"
+#include "uinput.h"
+
+#include <math.h>
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static void send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+ struct uinput_event event;
+ int len;
+
+ if (fd <= fileno(stderr))
+ return;
+
+ memset(&event, 0, sizeof(event));
+ event.type = type;
+ event.code = code;
+ event.value = value;
+
+ len = write(fd, &event, sizeof(event));
+}
+
+static int uinput_create(char *name, int keyboard, int mouse)
+{
+ struct uinput_dev dev;
+ int fd, aux;
+
+ fd = open("/dev/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/input/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/misc/uinput", O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open input device: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+ }
+ }
+
+ memset(&dev, 0, sizeof(dev));
+
+ if (name)
+ strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE);
+
+ dev.id.bustype = BUS_BLUETOOTH;
+ dev.id.vendor = 0x0000;
+ dev.id.product = 0x0000;
+ dev.id.version = 0x0000;
+
+ if (write(fd, &dev, sizeof(dev)) < 0) {
+ fprintf(stderr, "Can't write device information: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ if (mouse) {
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+
+ for (aux = REL_X; aux <= REL_MISC; aux++)
+ ioctl(fd, UI_SET_RELBIT, aux);
+ }
+
+ if (keyboard) {
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_LED);
+ ioctl(fd, UI_SET_EVBIT, EV_REP);
+
+ for (aux = KEY_RESERVED; aux <= KEY_UNKNOWN; aux++)
+ ioctl(fd, UI_SET_KEYBIT, aux);
+
+ //for (aux = LED_NUML; aux <= LED_MISC; aux++)
+ // ioctl(fd, UI_SET_LEDBIT, aux);
+ }
+
+ if (mouse) {
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+
+ for (aux = BTN_LEFT; aux <= BTN_BACK; aux++)
+ ioctl(fd, UI_SET_KEYBIT, aux);
+ }
+
+ ioctl(fd, UI_DEV_CREATE);
+
+ return fd;
+}
+
+static int rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ struct sockaddr_rc addr;
+ int sk;
+
+ sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ fprintf(stderr, "Can't create socket: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, src);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "Can't bind socket: %s (%d)\n",
+ strerror(errno), errno);
+ close(sk);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, dst);
+ addr.rc_channel = channel;
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "Can't connect: %s (%d)\n",
+ strerror(errno), errno);
+ close(sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+static void func(int fd)
+{
+}
+
+static void back(int fd)
+{
+}
+
+static void next(int fd)
+{
+}
+
+static void button(int fd, unsigned int button, int is_press)
+{
+ switch (button) {
+ case 1:
+ send_event(fd, EV_KEY, BTN_LEFT, is_press);
+ break;
+ case 3:
+ send_event(fd, EV_KEY, BTN_RIGHT, is_press);
+ break;
+ }
+
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static void move(int fd, unsigned int direction)
+{
+ double angle;
+ int32_t x, y;
+
+ angle = (direction * 22.5) * 3.1415926 / 180;
+ x = (int) (sin(angle) * 8);
+ y = (int) (cos(angle) * -8);
+
+ send_event(fd, EV_REL, REL_X, x);
+ send_event(fd, EV_REL, REL_Y, y);
+
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static inline void epox_decode(int fd, unsigned char event)
+{
+ switch (event) {
+ case 48:
+ func(fd); break;
+ case 55:
+ back(fd); break;
+ case 56:
+ next(fd); break;
+ case 53:
+ button(fd, 1, 1); break;
+ case 121:
+ button(fd, 1, 0); break;
+ case 113:
+ break;
+ case 54:
+ button(fd, 3, 1); break;
+ case 120:
+ button(fd, 3, 0); break;
+ case 112:
+ break;
+ case 51:
+ move(fd, 0); break;
+ case 97:
+ move(fd, 1); break;
+ case 65:
+ move(fd, 2); break;
+ case 98:
+ move(fd, 3); break;
+ case 50:
+ move(fd, 4); break;
+ case 99:
+ move(fd, 5); break;
+ case 67:
+ move(fd, 6); break;
+ case 101:
+ move(fd, 7); break;
+ case 52:
+ move(fd, 8); break;
+ case 100:
+ move(fd, 9); break;
+ case 66:
+ move(fd, 10); break;
+ case 102:
+ move(fd, 11); break;
+ case 49:
+ move(fd, 12); break;
+ case 103:
+ move(fd, 13); break;
+ case 57:
+ move(fd, 14); break;
+ case 104:
+ move(fd, 15); break;
+ case 69:
+ break;
+ default:
+ printf("Unknown event code %d\n", event);
+ break;
+ }
+}
+
+int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ unsigned char buf[16];
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ char addr[18];
+ int i, fd, sk, len;
+
+ sk = rfcomm_connect(src, dst, channel);
+ if (sk < 0)
+ return -1;
+
+ fd = uinput_create("Bluetooth Presenter", 0, 1);
+ if (fd < 0) {
+ close(sk);
+ return -1;
+ }
+
+ ba2str(dst, addr);
+
+ printf("Connected to %s on channel %d\n", addr, channel);
+ printf("Press CTRL-C for hangup\n");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = sk;
+ p.events = POLLIN | POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) < 1)
+ continue;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ break;
+
+ for (i = 0; i < len; i++)
+ epox_decode(fd, buf[i]);
+ }
+
+ printf("Disconnected\n");
+
+ ioctl(fd, UI_DEV_DESTROY);
+
+ close(fd);
+ close(sk);
+
+ return 0;
+}
+
+int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ printf("Not implemented\n");
+ return -1;
+}
+
+/* The strange meta key close to Ctrl has been assigned to Esc,
+ Fn key to CtrlR and the left space to Alt*/
+
+static unsigned char jthree_keycodes[63] = {
+ KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6,
+ KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T,
+ KEY_A, KEY_S, KEY_D, KEY_F, KEY_G,
+ KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B,
+ KEY_LEFTALT, KEY_TAB, KEY_CAPSLOCK, KEY_ESC,
+ KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE,
+ KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE,
+ KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER,
+ KEY_N, KEY_M, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_UP,
+ KEY_SPACE, KEY_COMPOSE, KEY_LEFT, KEY_DOWN, KEY_RIGHT,
+ KEY_LEFTCTRL, KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_DELETE, KEY_RIGHTCTRL, KEY_RIGHTALT,
+};
+
+static inline void jthree_decode(int fd, unsigned char event)
+{
+ if (event > 63)
+ send_event(fd, EV_KEY, jthree_keycodes[event & 0x3f], 0);
+ else
+ send_event(fd, EV_KEY, jthree_keycodes[event - 1], 1);
+}
+
+int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ unsigned char buf[16];
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ char addr[18];
+ int i, fd, sk, len;
+
+ sk = rfcomm_connect(src, dst, channel);
+ if (sk < 0)
+ return -1;
+
+ fd = uinput_create("J-Three Keyboard", 1, 0);
+ if (fd < 0) {
+ close(sk);
+ return -1;
+ }
+
+ ba2str(dst, addr);
+
+ printf("Connected to %s on channel %d\n", addr, channel);
+ printf("Press CTRL-C for hangup\n");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = sk;
+ p.events = POLLIN | POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) < 1)
+ continue;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ break;
+
+ for (i = 0; i < len; i++)
+ jthree_decode(fd, buf[i]);
+ }
+
+ printf("Disconnected\n");
+
+ ioctl(fd, UI_DEV_DESTROY);
+
+ close(fd);
+ close(sk);
+
+ return 0;
+}
+
+static const int celluon_xlate_num[10] = {
+ KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9
+};
+
+static const int celluon_xlate_char[26] = {
+ KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
+ KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
+ KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z
+};
+
+static int celluon_xlate(int c)
+{
+ if (c >= '0' && c <= '9')
+ return celluon_xlate_num[c - '0'];
+
+ if (c >= 'A' && c <= 'Z')
+ return celluon_xlate_char[c - 'A'];
+
+ switch (c) {
+ case 0x08:
+ return KEY_BACKSPACE;
+ case 0x09:
+ return KEY_TAB;
+ case 0x0d:
+ return KEY_ENTER;
+ case 0x11:
+ return KEY_LEFTCTRL;
+ case 0x14:
+ return KEY_CAPSLOCK;
+ case 0x20:
+ return KEY_SPACE;
+ case 0x25:
+ return KEY_LEFT;
+ case 0x26:
+ return KEY_UP;
+ case 0x27:
+ return KEY_RIGHT;
+ case 0x28:
+ return KEY_DOWN;
+ case 0x2e:
+ return KEY_DELETE;
+ case 0x5b:
+ return KEY_MENU;
+ case 0xa1:
+ return KEY_RIGHTSHIFT;
+ case 0xa0:
+ return KEY_LEFTSHIFT;
+ case 0xba:
+ return KEY_SEMICOLON;
+ case 0xbd:
+ return KEY_MINUS;
+ case 0xbc:
+ return KEY_COMMA;
+ case 0xbb:
+ return KEY_EQUAL;
+ case 0xbe:
+ return KEY_DOT;
+ case 0xbf:
+ return KEY_SLASH;
+ case 0xc0:
+ return KEY_GRAVE;
+ case 0xdb:
+ return KEY_LEFTBRACE;
+ case 0xdc:
+ return KEY_BACKSLASH;
+ case 0xdd:
+ return KEY_RIGHTBRACE;
+ case 0xde:
+ return KEY_APOSTROPHE;
+ case 0xff03:
+ return KEY_HOMEPAGE;
+ case 0xff04:
+ return KEY_TIME;
+ case 0xff06:
+ return KEY_OPEN;
+ case 0xff07:
+ return KEY_LIST;
+ case 0xff08:
+ return KEY_MAIL;
+ case 0xff30:
+ return KEY_CALC;
+ case 0xff1a: /* Map FN to ALT */
+ return KEY_LEFTALT;
+ case 0xff2f:
+ return KEY_INFO;
+ default:
+ printf("Unknown key %x\n", c);
+ return c;
+ }
+}
+
+struct celluon_state {
+ int len; /* Expected length of current packet */
+ int count; /* Number of bytes received */
+ int action;
+ int key;
+};
+
+static void celluon_decode(int fd, struct celluon_state *s, uint8_t c)
+{
+ if (s->count < 2 && c != 0xa5) {
+ /* Lost Sync */
+ s->count = 0;
+ return;
+ }
+
+ switch (s->count) {
+ case 0:
+ /* New packet - Reset state */
+ s->len = 30;
+ s->key = 0;
+ break;
+ case 1:
+ break;
+ case 6:
+ s->action = c;
+ break;
+ case 28:
+ s->key = c;
+ if (c == 0xff)
+ s->len = 31;
+ break;
+ case 29:
+ case 30:
+ if (s->count == s->len - 1) {
+ /* TODO: Verify checksum */
+ if (s->action < 2) {
+ send_event(fd, EV_KEY, celluon_xlate(s->key),
+ s->action);
+ }
+ s->count = -1;
+ } else {
+ s->key = (s->key << 8) | c;
+ }
+ break;
+ }
+
+ s->count++;
+
+ return;
+}
+
+int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ unsigned char buf[16];
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ char addr[18];
+ int i, fd, sk, len;
+ struct celluon_state s;
+
+ sk = rfcomm_connect(src, dst, channel);
+ if (sk < 0)
+ return -1;
+
+ fd = uinput_create("Celluon Keyboard", 1, 0);
+ if (fd < 0) {
+ close(sk);
+ return -1;
+ }
+
+ ba2str(dst, addr);
+
+ printf("Connected to %s on channel %d\n", addr, channel);
+ printf("Press CTRL-C for hangup\n");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = sk;
+ p.events = POLLIN | POLLERR | POLLHUP;
+
+ memset(&s, 0, sizeof(s));
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) < 1)
+ continue;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ break;
+
+ for (i = 0; i < len; i++)
+ celluon_decode(fd, &s, buf[i]);
+ }
+
+ printf("Disconnected\n");
+
+ ioctl(fd, UI_DEV_DESTROY);
+
+ close(fd);
+ close(sk);
+
+ return 0;
+}
diff --git a/compat/fakehid.txt b/compat/fakehid.txt
new file mode 100644
index 00000000..000d0ee2
--- /dev/null
+++ b/compat/fakehid.txt
@@ -0,0 +1,134 @@
+EPox Presenter
+==============
+
+# hcitool inq
+Inquiring ...
+ 00:04:61:aa:bb:cc clock offset: 0x1ded class: 0x004000
+
+# hcitool info 00:04:61:aa:bb:cc
+Requesting information ...
+ BD Address: 00:04:61:aa:bb:cc
+ OUI Company: EPOX Computer Co., Ltd. (00-04-61)
+ Device Name: EPox BT-PM01B aabbcc
+ LMP Version: 1.1 (0x1) LMP Subversion: 0xf78
+ Manufacturer: Cambridge Silicon Radio (10)
+ Features: 0xff 0xff 0x0f 0x00 0x00 0x00 0x00 0x00
+ <3-slot packets> <5-slot packets> <encryption> <slot offset>
+ <timing accuracy> <role switch> <hold mode> <sniff mode>
+ <park state> <RSSI> <channel quality> <SCO link> <HV2 packets>
+ <HV3 packets> <u-law log> <A-law log> <CVSD> <paging scheme>
+ <power control> <transparent SCO>
+
+# sdptool records --raw 00:04:61:aa:bb:cc
+Sequence
+ Attribute 0x0000 - ServiceRecordHandle
+ UINT32 0x00010000
+ Attribute 0x0001 - ServiceClassIDList
+ Sequence
+ UUID16 0x1101 - SerialPort
+ Attribute 0x0004 - ProtocolDescriptorList
+ Sequence
+ Sequence
+ UUID16 0x0100 - L2CAP
+ Sequence
+ UUID16 0x0003 - RFCOMM
+ UINT8 0x01
+ Attribute 0x0100
+ String Cable Replacement
+
+
+J-Three Keyboard
+================
+
+# hcitool inq
+Inquiring ...
+ 00:0A:3A:aa:bb:cc clock offset: 0x3039 class: 0x001f00
+
+# hcitool info 00:0A:3A:aa:bb:cc
+Password:
+Requesting information ...
+ BD Address: 00:0A:3A:aa:bb:cc
+ OUI Company: J-THREE INTERNATIONAL Holding Co., Ltd. (00-0A-3A)
+ Device Name: KEYBOARD
+ LMP Version: 1.1 (0x1) LMP Subversion: 0x2c2
+ Manufacturer: Cambridge Silicon Radio (10)
+ Features: 0xbc 0x06 0x07 0x00 0x00 0x00 0x00 0x00
+ <encryption> <slot offset> <timing accuracy> <role switch>
+ <sniff mode> <RSSI> <channel quality> <CVSD> <paging scheme>
+ <power control>
+
+# sdptool records --raw 00:0A:3A:aa:bb:cc
+Sequence
+ Attribute 0x0000 - ServiceRecordHandle
+ UINT32 0x00010000
+ Attribute 0x0001 - ServiceClassIDList
+ Sequence
+ UUID16 0x1101 - SerialPort
+ Attribute 0x0004 - ProtocolDescriptorList
+ Sequence
+ Sequence
+ UUID16 0x0100 - L2CAP
+ Sequence
+ UUID16 0x0003 - RFCOMM
+ UINT8 0x01
+ Attribute 0x0006 - LanguageBaseAttributeIDList
+ Sequence
+ UINT16 0x656e
+ UINT16 0x006a
+ UINT16 0x0100
+ Attribute 0x0100
+ String SPP slave
+
+
+Celluon Laserkey Keyboard
+=========================
+
+# hcitool inq
+Inquiring ...
+ 00:0B:24:aa:bb:cc clock offset: 0x3ab6 class: 0x400210
+
+# hcitool info 00:0B:24:aa:bb:cc
+Requesting information ...
+ BD Address: 00:0B:24:aa:bb:cc
+ OUI Company: AirLogic (00-0B-24)
+ Device Name: CL800BT
+ LMP Version: 1.1 (0x1) LMP Subversion: 0x291
+ Manufacturer: Cambridge Silicon Radio (10)
+ Features: 0xff 0xff 0x0f 0x00 0x00 0x00 0x00 0x00
+ <3-slot packets> <5-slot packets> <encryption> <slot offset>
+ <timing accuracy> <role switch> <hold mode> <sniff mode>
+ <park state> <RSSI> <channel quality> <SCO link> <HV2 packets>
+ <HV3 packets> <u-law log> <A-law log> <CVSD> <paging scheme>
+ <power control> <transparent SCO>
+
+# sdptool records --raw 00:0B:24:aa:bb:cc
+Sequence
+ Attribute 0x0000 - ServiceRecordHandle
+ UINT32 0x00010000
+ Attribute 0x0001 - ServiceClassIDList
+ Sequence
+ UUID16 0x1101 - SerialPort
+ Attribute 0x0004 - ProtocolDescriptorList
+ Sequence
+ Sequence
+ UUID16 0x0100 - L2CAP
+ Sequence
+ UUID16 0x0003 - RFCOMM
+ UINT8 0x01
+ Attribute 0x0100
+ String Serial Port
+
+Packet format is as follows (all fields little-endian):
+ 0 uint16 magic # 0x5a5a
+ 2 uint32 unknown # ???
+ 6 uint8 action # 0 = keyup, 1 = keydown, 2 = repeat
+ # 3, 4, 5, 6 = ??? (Mouse mode)
+ 7 uint8 unknown[9] # ???
+ 16 uint8 action2 # ??? same as action
+ 17 uint16 x # Horizontal coordinate
+ 19 uint16 y # Vertical coordinate
+ 21 uint16 time # Some sort of timestamp
+ 23 uint8 unknown[5] # ???
+ 28 uint8 key[] # single byte keycode or 0xff byte
+ # follwed by special keycode byte.
+ Each packet followed by a checksum byte.
diff --git a/compat/hidd.1 b/compat/hidd.1
new file mode 100644
index 00000000..b186ac24
--- /dev/null
+++ b/compat/hidd.1
@@ -0,0 +1,41 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.33.
+.TH HIDD "1" "May 2004" "hidd - Bluetooth HID daemon" "User Commands"
+.SH NAME
+hidd \- Bluetooth HID daemon
+.SH DESCRIPTION
+hidd - Bluetooth HID daemon
+.SS "Usage:"
+.IP
+hidd [options] [commands]
+.SH OPTIONS
+.TP
+\fB\-i\fR <hciX|bdaddr>
+Local HCI device or BD Address
+.TP
+\fB\-t\fR <timeout>
+Set idle timeout (in minutes)
+.TP
+\fB\-n\fR, \fB\-\-nodaemon\fR
+Don't fork daemon to background
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help
+.SS "Commands:"
+.TP
+\fB\-\-server\fR
+Start HID server
+.TP
+\fB\-\-search\fR
+Search for HID devices
+.TP
+\fB\-\-connect\fR <bdaddr>
+Connect remote HID device
+.TP
+\fB\-\-kill\fR <bdaddr>
+Terminate HID connection
+.TP
+\fB\-\-killall\fR
+Terminate all connections
+.TP
+\fB\-\-show\fR
+List current HID connections
diff --git a/compat/hidd.c b/compat/hidd.c
new file mode 100644
index 00000000..6a0058c4
--- /dev/null
+++ b/compat/hidd.c
@@ -0,0 +1,861 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "hidd.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+enum {
+ NONE,
+ SHOW,
+ SERVER,
+ SEARCH,
+ CONNECT,
+ KILL
+};
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm)
+{
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ int sk;
+
+ if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ memset(&opts, 0, sizeof(opts));
+ opts.imtu = HIDP_DEFAULT_MTU;
+ opts.omtu = HIDP_DEFAULT_MTU;
+ opts.flush_to = 0xffff;
+
+ setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(psm);
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+static int l2cap_listen(const bdaddr_t *bdaddr, unsigned short psm, int lm, int backlog)
+{
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ int sk;
+
+ if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, bdaddr);
+ addr.l2_psm = htobs(psm);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm));
+
+ memset(&opts, 0, sizeof(opts));
+ opts.imtu = HIDP_DEFAULT_MTU;
+ opts.omtu = HIDP_DEFAULT_MTU;
+ opts.flush_to = 0xffff;
+
+ setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts));
+
+ if (listen(sk, backlog) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+static int l2cap_accept(int sk, bdaddr_t *bdaddr)
+{
+ struct sockaddr_l2 addr;
+ socklen_t addrlen;
+ int nsk;
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if ((nsk = accept(sk, (struct sockaddr *) &addr, &addrlen)) < 0)
+ return -1;
+
+ if (bdaddr)
+ bacpy(bdaddr, &addr.l2_bdaddr);
+
+ return nsk;
+}
+
+static int request_authentication(bdaddr_t *src, bdaddr_t *dst)
+{
+ struct hci_conn_info_req *cr;
+ char addr[18];
+ int err, dd, dev_id;
+
+ ba2str(src, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0)
+ return dev_id;
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0)
+ return dd;
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr)
+ return -ENOMEM;
+
+ bacpy(&cr->bdaddr, dst);
+ cr->type = ACL_LINK;
+ err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr);
+ if (err < 0) {
+ free(cr);
+ hci_close_dev(dd);
+ return err;
+ }
+
+ err = hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000);
+
+ free(cr);
+ hci_close_dev(dd);
+
+ return err;
+}
+
+static int request_encryption(bdaddr_t *src, bdaddr_t *dst)
+{
+ struct hci_conn_info_req *cr;
+ char addr[18];
+ int err, dd, dev_id;
+
+ ba2str(src, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0)
+ return dev_id;
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0)
+ return dd;
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr)
+ return -ENOMEM;
+
+ bacpy(&cr->bdaddr, dst);
+ cr->type = ACL_LINK;
+ err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr);
+ if (err < 0) {
+ free(cr);
+ hci_close_dev(dd);
+ return err;
+ }
+
+ err = hci_encrypt_link(dd, htobs(cr->conn_info->handle), 1, 25000);
+
+ free(cr);
+ hci_close_dev(dd);
+
+ return err;
+}
+
+static void enable_sixaxis(int csk)
+{
+ const unsigned char buf[] = {
+ 0x53 /*HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE*/,
+ 0xf4, 0x42, 0x03, 0x00, 0x00 };
+ int err;
+
+ err = write(csk, buf, sizeof(buf));
+}
+
+static int create_device(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout)
+{
+ struct hidp_connadd_req req;
+ struct sockaddr_l2 addr;
+ socklen_t addrlen;
+ bdaddr_t src, dst;
+ char bda[18];
+ int err;
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if (getsockname(csk, (struct sockaddr *) &addr, &addrlen) < 0)
+ return -1;
+
+ bacpy(&src, &addr.l2_bdaddr);
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if (getpeername(csk, (struct sockaddr *) &addr, &addrlen) < 0)
+ return -1;
+
+ bacpy(&dst, &addr.l2_bdaddr);
+
+ memset(&req, 0, sizeof(req));
+ req.ctrl_sock = csk;
+ req.intr_sock = isk;
+ req.flags = 0;
+ req.idle_to = timeout * 60;
+
+ err = get_stored_device_info(&src, &dst, &req);
+ if (!err)
+ goto create;
+
+ if (!nocheck) {
+ ba2str(&dst, bda);
+ syslog(LOG_ERR, "Rejected connection from unknown device %s", bda);
+ /* Return no error to avoid run_server() complaining too */
+ return 0;
+ }
+
+ if (!nosdp) {
+ err = get_sdp_device_info(&src, &dst, &req);
+ if (err < 0)
+ goto error;
+ } else {
+ struct l2cap_conninfo conn;
+ socklen_t size;
+ uint8_t class[3];
+
+ memset(&conn, 0, sizeof(conn));
+ size = sizeof(conn);
+ if (getsockopt(csk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &size) < 0)
+ memset(class, 0, 3);
+ else
+ memcpy(class, conn.dev_class, 3);
+
+ if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01))
+ req.subclass = class[0];
+ else
+ req.subclass = 0xc0;
+ }
+
+create:
+ if (subclass != 0x00)
+ req.subclass = subclass;
+
+ ba2str(&dst, bda);
+ syslog(LOG_INFO, "New HID device %s (%s)", bda, req.name);
+
+ if (encrypt && (req.subclass & 0x40)) {
+ err = request_authentication(&src, &dst);
+ if (err < 0) {
+ syslog(LOG_ERR, "Authentication for %s failed", bda);
+ goto error;
+ }
+
+ err = request_encryption(&src, &dst);
+ if (err < 0)
+ syslog(LOG_ERR, "Encryption for %s failed", bda);
+ }
+
+ if (bootonly) {
+ req.rd_size = 0;
+ req.flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
+ }
+
+ if (req.vendor == 0x054c && req.product == 0x0268)
+ enable_sixaxis(csk);
+
+ err = ioctl(ctl, HIDPCONNADD, &req);
+
+error:
+ if (req.rd_data)
+ free(req.rd_data);
+
+ return err;
+}
+
+static void run_server(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout)
+{
+ struct pollfd p[2];
+ sigset_t sigs;
+ short events;
+ int err, ncsk, nisk;
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p[0].fd = csk;
+ p[0].events = POLLIN | POLLERR | POLLHUP;
+
+ p[1].fd = isk;
+ p[1].events = POLLIN | POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p[0].revents = 0;
+ p[1].revents = 0;
+
+ if (ppoll(p, 2, NULL, &sigs) < 1)
+ continue;
+
+ events = p[0].revents | p[1].revents;
+
+ if (events & POLLIN) {
+ ncsk = l2cap_accept(csk, NULL);
+ nisk = l2cap_accept(isk, NULL);
+
+ err = create_device(ctl, ncsk, nisk, subclass, nosdp, nocheck, bootonly, encrypt, timeout);
+ if (err < 0)
+ syslog(LOG_ERR, "HID create error %d (%s)",
+ errno, strerror(errno));
+
+ close(nisk);
+ sleep(1);
+ close(ncsk);
+ }
+ }
+}
+
+static char *hidp_state[] = {
+ "unknown",
+ "connected",
+ "open",
+ "bound",
+ "listening",
+ "connecting",
+ "connecting",
+ "config",
+ "disconnecting",
+ "closed"
+};
+
+static char *hidp_flagstostr(uint32_t flags)
+{
+ static char str[100];
+ str[0] = 0;
+
+ strcat(str, "[");
+
+ if (flags & (1 << HIDP_BOOT_PROTOCOL_MODE))
+ strcat(str, "boot-protocol");
+
+ strcat(str, "]");
+
+ return str;
+}
+
+static void do_show(int ctl)
+{
+ struct hidp_connlist_req req;
+ struct hidp_conninfo ci[16];
+ char addr[18];
+ int i;
+
+ req.cnum = 16;
+ req.ci = ci;
+
+ if (ioctl(ctl, HIDPGETCONNLIST, &req) < 0) {
+ perror("Can't get connection list");
+ close(ctl);
+ exit(1);
+ }
+
+ for (i = 0; i < req.cnum; i++) {
+ ba2str(&ci[i].bdaddr, addr);
+ printf("%s %s [%04x:%04x] %s %s\n", addr, ci[i].name,
+ ci[i].vendor, ci[i].product, hidp_state[ci[i].state],
+ ci[i].flags ? hidp_flagstostr(ci[i].flags) : "");
+ }
+}
+
+static void do_connect(int ctl, bdaddr_t *src, bdaddr_t *dst, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout)
+{
+ struct hidp_connadd_req req;
+ uint16_t uuid = HID_SVCLASS_ID;
+ uint8_t channel = 0;
+ char name[256];
+ int csk, isk, err;
+
+ memset(&req, 0, sizeof(req));
+
+ err = get_sdp_device_info(src, dst, &req);
+ if (err < 0 && fakehid)
+ err = get_alternate_device_info(src, dst,
+ &uuid, &channel, name, sizeof(name) - 1);
+
+ if (err < 0) {
+ perror("Can't get device information");
+ close(ctl);
+ exit(1);
+ }
+
+ switch (uuid) {
+ case HID_SVCLASS_ID:
+ goto connect;
+
+ case SERIAL_PORT_SVCLASS_ID:
+ if (subclass == 0x40 || !strcmp(name, "Cable Replacement")) {
+ if (epox_presenter(src, dst, channel) < 0) {
+ close(ctl);
+ exit(1);
+ }
+ break;
+ }
+ if (subclass == 0x1f || !strcmp(name, "SPP slave")) {
+ if (jthree_keyboard(src, dst, channel) < 0) {
+ close(ctl);
+ exit(1);
+ }
+ break;
+ }
+ if (subclass == 0x02 || !strcmp(name, "Serial Port")) {
+ if (celluon_keyboard(src, dst, channel) < 0) {
+ close(ctl);
+ exit(1);
+ }
+ break;
+ }
+ break;
+
+ case HEADSET_SVCLASS_ID:
+ case HANDSFREE_SVCLASS_ID:
+ if (headset_presenter(src, dst, channel) < 0) {
+ close(ctl);
+ exit(1);
+ }
+ break;
+ }
+
+ return;
+
+connect:
+ csk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_CTRL);
+ if (csk < 0) {
+ perror("Can't create HID control channel");
+ close(ctl);
+ exit(1);
+ }
+
+ isk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_INTR);
+ if (isk < 0) {
+ perror("Can't create HID interrupt channel");
+ close(csk);
+ close(ctl);
+ exit(1);
+ }
+
+ err = create_device(ctl, csk, isk, subclass, 1, 1, bootonly, encrypt, timeout);
+ if (err < 0) {
+ fprintf(stderr, "HID create error %d (%s)\n",
+ errno, strerror(errno));
+ close(isk);
+ sleep(1);
+ close(csk);
+ close(ctl);
+ exit(1);
+ }
+}
+
+static void do_search(int ctl, bdaddr_t *bdaddr, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout)
+{
+ inquiry_info *info = NULL;
+ bdaddr_t src, dst;
+ int i, dev_id, num_rsp, length, flags;
+ char addr[18];
+ uint8_t class[3];
+
+ ba2str(bdaddr, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ hci_devba(dev_id, &src);
+ } else
+ bacpy(&src, bdaddr);
+
+ length = 8; /* ~10 seconds */
+ num_rsp = 0;
+ flags = IREQ_CACHE_FLUSH;
+
+ printf("Searching ...\n");
+
+ num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
+
+ for (i = 0; i < num_rsp; i++) {
+ memcpy(class, (info+i)->dev_class, 3);
+ if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) {
+ bacpy(&dst, &(info+i)->bdaddr);
+ ba2str(&dst, addr);
+
+ printf("\tConnecting to device %s\n", addr);
+ do_connect(ctl, &src, &dst, subclass, fakehid, bootonly, encrypt, timeout);
+ }
+ }
+
+ if (!fakehid)
+ goto done;
+
+ for (i = 0; i < num_rsp; i++) {
+ memcpy(class, (info+i)->dev_class, 3);
+ if ((class[0] == 0x00 && class[2] == 0x00 &&
+ (class[1] == 0x40 || class[1] == 0x1f)) ||
+ (class[0] == 0x10 && class[1] == 0x02 && class[2] == 0x40)) {
+ bacpy(&dst, &(info+i)->bdaddr);
+ ba2str(&dst, addr);
+
+ printf("\tConnecting to device %s\n", addr);
+ do_connect(ctl, &src, &dst, subclass, 1, bootonly, 0, timeout);
+ }
+ }
+
+done:
+ bt_free(info);
+
+ if (!num_rsp) {
+ fprintf(stderr, "\tNo devices in range or visible\n");
+ close(ctl);
+ exit(1);
+ }
+}
+
+static void do_kill(int ctl, bdaddr_t *bdaddr, uint32_t flags)
+{
+ struct hidp_conndel_req req;
+ struct hidp_connlist_req cl;
+ struct hidp_conninfo ci[16];
+ int i;
+
+ if (!bacmp(bdaddr, BDADDR_ALL)) {
+ cl.cnum = 16;
+ cl.ci = ci;
+
+ if (ioctl(ctl, HIDPGETCONNLIST, &cl) < 0) {
+ perror("Can't get connection list");
+ close(ctl);
+ exit(1);
+ }
+
+ for (i = 0; i < cl.cnum; i++) {
+ bacpy(&req.bdaddr, &ci[i].bdaddr);
+ req.flags = flags;
+
+ if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+ perror("Can't release connection");
+ close(ctl);
+ exit(1);
+ }
+ }
+
+ } else {
+ bacpy(&req.bdaddr, bdaddr);
+ req.flags = flags;
+
+ if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+ perror("Can't release connection");
+ close(ctl);
+ exit(1);
+ }
+ }
+}
+
+static void usage(void)
+{
+ printf("hidd - Bluetooth HID daemon version %s\n\n", VERSION);
+
+ printf("Usage:\n"
+ "\thidd [options] [commands]\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-i <hciX|bdaddr> Local HCI device or BD Address\n"
+ "\t-t <timeout> Set idle timeout (in minutes)\n"
+ "\t-b <subclass> Overwrite the boot mode subclass\n"
+ "\t-n, --nodaemon Don't fork daemon to background\n"
+ "\t-h, --help Display help\n"
+ "\n");
+
+ printf("Commands:\n"
+ "\t--server Start HID server\n"
+ "\t--search Search for HID devices\n"
+ "\t--connect <bdaddr> Connect remote HID device\n"
+ "\t--unplug <bdaddr> Unplug the HID connection\n"
+ "\t--kill <bdaddr> Terminate HID connection\n"
+ "\t--killall Terminate all connections\n"
+ "\t--show List current HID connections\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "nodaemon", 0, 0, 'n' },
+ { "subclass", 1, 0, 'b' },
+ { "timeout", 1, 0, 't' },
+ { "device", 1, 0, 'i' },
+ { "master", 0, 0, 'M' },
+ { "encrypt", 0, 0, 'E' },
+ { "nosdp", 0, 0, 'D' },
+ { "nocheck", 0, 0, 'Z' },
+ { "bootonly", 0, 0, 'B' },
+ { "hidonly", 0, 0, 'H' },
+ { "show", 0, 0, 'l' },
+ { "list", 0, 0, 'l' },
+ { "server", 0, 0, 'd' },
+ { "listen", 0, 0, 'd' },
+ { "search", 0, 0, 's' },
+ { "create", 1, 0, 'c' },
+ { "connect", 1, 0, 'c' },
+ { "disconnect", 1, 0, 'k' },
+ { "terminate", 1, 0, 'k' },
+ { "release", 1, 0, 'k' },
+ { "kill", 1, 0, 'k' },
+ { "killall", 0, 0, 'K' },
+ { "unplug", 1, 0, 'u' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ bdaddr_t bdaddr, dev;
+ uint32_t flags = 0;
+ uint8_t subclass = 0x00;
+ char addr[18];
+ int log_option = LOG_NDELAY | LOG_PID;
+ int opt, ctl, csk, isk;
+ int mode = SHOW, detach = 1, nosdp = 0, nocheck = 0, bootonly = 0;
+ int fakehid = 1, encrypt = 0, timeout = 30, lm = 0;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt = getopt_long(argc, argv, "+i:nt:b:MEDZBHldsc:k:Ku:h", main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'i':
+ if (!strncasecmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+ case 'n':
+ detach = 0;
+ break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
+ case 'b':
+ if (!strncasecmp(optarg, "0x", 2))
+ subclass = (uint8_t) strtol(optarg, NULL, 16);
+ else
+ subclass = atoi(optarg);
+ break;
+ case 'M':
+ lm |= L2CAP_LM_MASTER;
+ break;
+ case 'E':
+ encrypt = 1;
+ break;
+ case 'D':
+ nosdp = 1;
+ break;
+ case 'Z':
+ nocheck = 1;
+ break;
+ case 'B':
+ bootonly = 1;
+ break;
+ case 'H':
+ fakehid = 0;
+ break;
+ case 'l':
+ mode = SHOW;
+ break;
+ case 'd':
+ mode = SERVER;
+ break;
+ case 's':
+ mode = SEARCH;
+ break;
+ case 'c':
+ str2ba(optarg, &dev);
+ mode = CONNECT;
+ break;
+ case 'k':
+ str2ba(optarg, &dev);
+ mode = KILL;
+ break;
+ case 'K':
+ bacpy(&dev, BDADDR_ALL);
+ mode = KILL;
+ break;
+ case 'u':
+ str2ba(optarg, &dev);
+ flags = (1 << HIDP_VIRTUAL_CABLE_UNPLUG);
+ mode = KILL;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(0);
+ }
+ }
+
+ ba2str(&bdaddr, addr);
+
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+ if (ctl < 0) {
+ perror("Can't open HIDP control socket");
+ exit(1);
+ }
+
+ switch (mode) {
+ case SERVER:
+ csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, lm, 10);
+ if (csk < 0) {
+ perror("Can't listen on HID control channel");
+ close(ctl);
+ exit(1);
+ }
+
+ isk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_INTR, lm, 10);
+ if (isk < 0) {
+ perror("Can't listen on HID interrupt channel");
+ close(ctl);
+ close(csk);
+ exit(1);
+ }
+ break;
+
+ case SEARCH:
+ do_search(ctl, &bdaddr, subclass, fakehid, bootonly, encrypt, timeout);
+ close(ctl);
+ exit(0);
+
+ case CONNECT:
+ do_connect(ctl, &bdaddr, &dev, subclass, fakehid, bootonly, encrypt, timeout);
+ close(ctl);
+ exit(0);
+
+ case KILL:
+ do_kill(ctl, &dev, flags);
+ close(ctl);
+ exit(0);
+
+ default:
+ do_show(ctl);
+ close(ctl);
+ exit(0);
+ }
+
+ if (detach) {
+ if (daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+ } else
+ log_option |= LOG_PERROR;
+
+ openlog("hidd", log_option, LOG_DAEMON);
+
+ if (bacmp(&bdaddr, BDADDR_ANY))
+ syslog(LOG_INFO, "Bluetooth HID daemon (%s)", addr);
+ else
+ syslog(LOG_INFO, "Bluetooth HID daemon");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ run_server(ctl, csk, isk, subclass, nosdp, nocheck, bootonly, encrypt, timeout);
+
+ syslog(LOG_INFO, "Exit");
+
+ close(csk);
+ close(isk);
+ close(ctl);
+
+ return 0;
+}
diff --git a/compat/hidd.h b/compat/hidd.h
new file mode 100644
index 00000000..ba94bcf4
--- /dev/null
+++ b/compat/hidd.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#define L2CAP_PSM_HIDP_CTRL 0x11
+#define L2CAP_PSM_HIDP_INTR 0x13
+
+int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
diff --git a/compat/lib.h b/compat/lib.h
new file mode 100644
index 00000000..b070ee40
--- /dev/null
+++ b/compat/lib.h
@@ -0,0 +1,86 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-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
+ *
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifndef min
+#define min(a,b) ( (a)<(b) ? (a):(b) )
+#endif
+
+/* IO cancelation */
+extern volatile sig_atomic_t __io_canceled;
+
+static inline void io_init(void)
+{
+ __io_canceled = 0;
+}
+
+static inline void io_cancel(void)
+{
+ __io_canceled = 1;
+}
+
+/* Read exactly len bytes (Signal safe)*/
+static inline int read_n(int fd, char *buf, int len)
+{
+ register int t = 0, w;
+
+ while (!__io_canceled && len > 0) {
+ if ((w = read(fd, buf, len)) < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ if (!w)
+ return 0;
+ len -= w;
+ buf += w;
+ t += w;
+ }
+
+ return t;
+}
+
+/* Write exactly len bytes (Signal safe)*/
+static inline int write_n(int fd, char *buf, int len)
+{
+ register int t = 0, w;
+
+ while (!__io_canceled && len > 0) {
+ if ((w = write(fd, buf, len)) < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ if (!w)
+ return 0;
+ len -= w;
+ buf += w;
+ t += w;
+ }
+
+ return t;
+}
diff --git a/compat/msdun.c b/compat/msdun.c
new file mode 100644
index 00000000..1ef559f1
--- /dev/null
+++ b/compat/msdun.c
@@ -0,0 +1,151 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-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 <stdint.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include "lib.h"
+
+#define MS_PPP 2
+#define MS_SUCCESS 1
+#define MS_FAILED -1
+#define MS_TIMEOUT -2
+
+static sigjmp_buf jmp;
+static int retry;
+static int timeout;
+
+static void sig_alarm(int sig)
+{
+ siglongjmp(jmp, MS_TIMEOUT);
+}
+
+static int w4_str(int fd, char *str)
+{
+ char buf[40];
+ int r, len = 0;
+
+ while (1) {
+ r = read(fd, buf + len, sizeof(buf) - len - 1);
+ if (r < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ break;
+ }
+ if (!r)
+ break;
+
+ len += r;
+
+ if (len < strlen(str))
+ continue;
+ buf[len] = 0;
+
+ if (strstr(buf, str))
+ return MS_SUCCESS;
+
+ /* Detect PPP */
+ if (strchr(buf, '~'))
+ return MS_PPP;
+ }
+ return MS_FAILED;
+}
+
+static int ms_server(int fd)
+{
+ switch (w4_str(fd, "CLIENT")) {
+ case MS_SUCCESS:
+ write_n(fd, "CLIENTSERVER", 12);
+ case MS_PPP:
+ return MS_SUCCESS;
+ default:
+ return MS_FAILED;
+ }
+}
+
+static int ms_client(int fd)
+{
+ write_n(fd, "CLIENT", 6);
+ return w4_str(fd, "CLIENTSERVER");
+}
+
+int ms_dun(int fd, int server, int timeo)
+{
+ sig_t osig;
+
+ retry = 4;
+ timeout = timeo;
+
+ if (!server)
+ timeout /= retry;
+
+ osig = signal(SIGALRM, sig_alarm);
+
+ while (1) {
+ int r = sigsetjmp(jmp, 1);
+ if (r) {
+ if (r == MS_TIMEOUT && !server && --retry)
+ continue;
+
+ alarm(0);
+ signal(SIGALRM, osig);
+
+ switch (r) {
+ case MS_SUCCESS:
+ case MS_PPP:
+ errno = 0;
+ return 0;
+
+ case MS_FAILED:
+ errno = EPROTO;
+ break;
+
+ case MS_TIMEOUT:
+ errno = ETIMEDOUT;
+ break;
+ }
+ return -1;
+ }
+
+ alarm(timeout);
+
+ if (server)
+ r = ms_server(fd);
+ else
+ r = ms_client(fd);
+
+ siglongjmp(jmp, r);
+ }
+}
diff --git a/compat/pand.1 b/compat/pand.1
new file mode 100644
index 00000000..4603b8bf
--- /dev/null
+++ b/compat/pand.1
@@ -0,0 +1,77 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29.
+.TH BlueZ "1" "February 2003" "PAN daemon" "User Commands"
+.SH NAME
+pand \- BlueZ Bluetooth PAN daemon
+.SH DESCRIPTION
+The pand PAN daemon allows your computer to connect to ethernet
+networks using Bluetooth.
+.SH SYNPOSIS
+pand <options>
+.SH OPTIONS
+.TP
+\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR
+Show active PAN connections
+.TP
+\fB\-\-listen\fR \fB\-s\fR
+Listen for PAN connections
+.TP
+\fB\-\-connect\fR \fB\-c\fR <bdaddr>
+Create PAN connection
+.TP
+\fB\-\-search\fR \fB\-Q[duration]\fR
+Search and connect
+.TP
+\fB\-\-kill\fR \fB\-k\fR <bdaddr>
+Kill PAN connection
+.TP
+\fB\-\-killall\fR \fB\-K\fR
+Kill all PAN connections
+.TP
+\fB\-\-role\fR \fB\-r\fR <role>
+Local PAN role (PANU, NAP, GN)
+.TP
+\fB\-\-service\fR \fB\-d\fR <role>
+Remote PAN service (PANU, NAP, GN)
+.TP
+\fB\-\-ethernet\fR \fB\-e\fR <name>
+Network interface name
+.TP
+\fB\-\-device\fR \fB\-i\fR <bdaddr>
+Source bdaddr
+.TP
+\fB\-\-nosdp\fR \fB\-D\fR
+Disable SDP
+.TP
+\fB\-\-encrypt\fR \fB\-E\fR
+Enable encryption
+.TP
+\fB\-\-secure\fR \fB\-S\fR
+Secure connection
+.TP
+\fB\-\-master\fR \fB\-M\fR
+Become the master of a piconet
+.TP
+\fB\-\-nodetach\fR \fB\-n\fR
+Do not become a daemon
+.TP
+\fB\-\-persist\fR \fB\-p[interval]\fR
+Persist mode
+.TP
+\fB\-\-cache\fR \fB\-C[valid]\fR
+Cache addresses
+.TP
+\fB\-\-pidfile\fR \fB\-P <pidfile>\fR
+Create PID file
+.TP
+\fB\-\-devup\fR \fB\-u <script>\fR
+Script to run when interface comes up
+.TP
+\fB\-\-devdown\fR \fB\-o <script>\fR
+Script to run when interface comes down
+.TP
+\fB\-\-autozap\fR \fB\-z\fR
+Disconnect automatically on exit
+
+.SH SCRIPTS
+The devup/devdown script will be called with bluetooth device as first argument
+and bluetooth destination address as second argument.
diff --git a/compat/pand.c b/compat/pand.c
new file mode 100644
index 00000000..ed80c7a5
--- /dev/null
+++ b/compat/pand.c
@@ -0,0 +1,800 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "pand.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static uint16_t role = BNEP_SVC_PANU; /* Local role (ie service) */
+static uint16_t service = BNEP_SVC_NAP; /* Remote service */
+
+static int detach = 1;
+static int persist;
+static int use_sdp = 1;
+static int use_cache;
+static int link_mode = 0;
+static int cleanup;
+static int search_duration = 10;
+
+static struct {
+ int valid;
+ char dst[40];
+ bdaddr_t bdaddr;
+} cache;
+
+static char netdev[16] = "bnep%d";
+static char *pidfile = NULL;
+static char *devupcmd = NULL;
+static char *devdowncmd = NULL;
+
+static bdaddr_t src_addr = *BDADDR_ANY;
+static int src_dev = -1;
+
+static volatile int terminate;
+
+static void do_kill(char *dst);
+
+enum {
+ NONE,
+ SHOW,
+ LISTEN,
+ CONNECT,
+ KILL
+} modes;
+
+struct script_arg {
+ char dev[20];
+ char dst[20];
+ int sk;
+ int nsk;
+};
+
+static void run_script(char *script, char *dev, char *dst, int sk, int nsk)
+{
+ char *argv[4];
+ struct sigaction sa;
+
+ if (!script)
+ return;
+
+ if (access(script, R_OK | X_OK))
+ return;
+
+ if (fork())
+ return;
+
+ if (sk >= 0)
+ close(sk);
+
+ if (nsk >= 0)
+ close(nsk);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ argv[0] = script;
+ argv[1] = dev;
+ argv[2] = dst;
+ argv[3] = NULL;
+
+ execv(script, argv);
+
+ exit(1);
+}
+
+/* Wait for disconnect or error condition on the socket */
+static int w4_hup(int sk, struct script_arg *down_cmd)
+{
+ struct pollfd pf;
+ sigset_t sigs;
+ int n;
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ while (!terminate) {
+ pf.fd = sk;
+ pf.events = POLLERR | POLLHUP;
+
+ n = ppoll(&pf, 1, NULL, &sigs);
+
+ if (n < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ syslog(LOG_ERR, "Poll failed. %s(%d)",
+ strerror(errno), errno);
+
+ return 1;
+ }
+
+ if (n) {
+ int err = 0;
+ socklen_t olen = sizeof(err);
+
+ getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &olen);
+
+ syslog(LOG_INFO, "%s disconnected%s%s", netdev,
+ err ? " : " : "", err ? strerror(err) : "");
+
+ if (down_cmd)
+ run_script(devdowncmd,
+ down_cmd->dev, down_cmd->dst,
+ down_cmd->sk, down_cmd->nsk);
+
+ close(sk);
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int do_listen(void)
+{
+ struct l2cap_options l2o;
+ struct sockaddr_l2 l2a;
+ socklen_t olen;
+ int sk, lm;
+
+ if (use_sdp)
+ bnep_sdp_register(&src_addr, role);
+
+ /* Create L2CAP socket and bind it to PSM BNEP */
+ sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ bacpy(&l2a.l2_bdaddr, &src_addr);
+ l2a.l2_psm = htobs(BNEP_PSM);
+
+ if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) {
+ syslog(LOG_ERR, "Bind failed. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Setup L2CAP options according to BNEP spec */
+ memset(&l2o, 0, sizeof(l2o));
+ olen = sizeof(l2o);
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen) < 0) {
+ syslog(LOG_ERR, "Failed to get L2CAP options. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ l2o.imtu = l2o.omtu = BNEP_MTU;
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+ syslog(LOG_ERR, "Failed to set L2CAP options. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Set link mode */
+ lm = link_mode;
+ if (lm && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) {
+ syslog(LOG_ERR, "Failed to set link mode. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ listen(sk, 10);
+
+ while (!terminate) {
+ socklen_t alen = sizeof(l2a);
+ char devname[16];
+ int nsk;
+
+ nsk = accept(sk, (struct sockaddr *) &l2a, &alen);
+ if (nsk < 0) {
+ syslog(LOG_ERR, "Accept failed. %s(%d)",
+ strerror(errno), errno);
+ continue;
+ }
+
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ syslog(LOG_ERR, "Fork failed. %s(%d)",
+ strerror(errno), errno);
+ default:
+ close(nsk);
+ continue;
+ }
+
+ strncpy(devname, netdev, 16);
+ devname[15] = '\0';
+
+ if (!bnep_accept_connection(nsk, role, devname)) {
+ char str[40];
+ struct script_arg down_cmd;
+
+ ba2str(&l2a.l2_bdaddr, str);
+
+ syslog(LOG_INFO, "New connection from %s at %s",
+ str, devname);
+
+ run_script(devupcmd, devname, str, sk, nsk);
+
+ memset(&down_cmd, 0, sizeof(struct script_arg));
+ strncpy(down_cmd.dev, devname, strlen(devname) + 1);
+ strncpy(down_cmd.dst, str, strlen(str) + 1);
+ down_cmd.sk = sk;
+ down_cmd.nsk = nsk;
+ w4_hup(nsk, &down_cmd);
+ } else {
+ syslog(LOG_ERR, "Connection failed. %s(%d)",
+ strerror(errno), errno);
+ }
+
+ close(nsk);
+ exit(0);
+ }
+
+ if (use_sdp)
+ bnep_sdp_unregister();
+
+ return 0;
+}
+
+/* Connect and initiate BNEP session
+ * Returns:
+ * -1 - critical error (exit persist mode)
+ * 1 - non critical error
+ * 0 - success
+ */
+static int create_connection(char *dst, bdaddr_t *bdaddr)
+{
+ struct l2cap_options l2o;
+ struct sockaddr_l2 l2a;
+ socklen_t olen;
+ int sk, r = 0;
+ struct script_arg down_cmd;
+
+ syslog(LOG_INFO, "Connecting to %s", dst);
+
+ sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Setup L2CAP options according to BNEP spec */
+ memset(&l2o, 0, sizeof(l2o));
+ olen = sizeof(l2o);
+ getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen);
+ l2o.imtu = l2o.omtu = BNEP_MTU;
+ setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o));
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ bacpy(&l2a.l2_bdaddr, &src_addr);
+
+ if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)))
+ syslog(LOG_ERR, "Bind failed. %s(%d)",
+ strerror(errno), errno);
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ bacpy(&l2a.l2_bdaddr, bdaddr);
+ l2a.l2_psm = htobs(BNEP_PSM);
+
+ if (!connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) &&
+ !bnep_create_connection(sk, role, service, netdev)) {
+
+ syslog(LOG_INFO, "%s connected", netdev);
+
+ run_script(devupcmd, netdev, dst, sk, -1);
+
+ if (persist || devdowncmd) {
+ memset(&down_cmd, 0, sizeof(struct script_arg));
+ strncpy(down_cmd.dev, netdev, strlen(netdev) + 1);
+ strncpy(down_cmd.dst, dst, strlen(dst) + 1);
+ down_cmd.sk = sk;
+ down_cmd.nsk = -1;
+ w4_hup(sk, &down_cmd);
+
+ if (terminate && cleanup) {
+ syslog(LOG_INFO, "Disconnecting from %s.", dst);
+ do_kill(dst);
+ }
+ }
+
+ r = 0;
+ } else {
+ syslog(LOG_ERR, "Connect to %s failed. %s(%d)",
+ dst, strerror(errno), errno);
+ r = 1;
+ }
+
+ close(sk);
+
+ if (use_cache) {
+ if (!r) {
+ /* Succesesful connection, validate cache */
+ strcpy(cache.dst, dst);
+ bacpy(&cache.bdaddr, bdaddr);
+ cache.valid = use_cache;
+ } else
+ cache.valid--;
+ }
+
+ return r;
+}
+
+/* Search and connect
+ * Returns:
+ * -1 - critical error (exit persist mode)
+ * 1 - non critical error
+ * 0 - success
+ */
+static int do_connect(void)
+{
+ inquiry_info *ii;
+ int reconnect = 0;
+ int i, n, r = 0;
+
+ do {
+ if (reconnect)
+ sleep(persist);
+ reconnect = 1;
+
+ if (cache.valid > 0) {
+ /* Use cached bdaddr */
+ r = create_connection(cache.dst, &cache.bdaddr);
+ if (r < 0) {
+ terminate = 1;
+ break;
+ }
+ continue;
+ }
+
+ syslog(LOG_INFO, "Inquiring");
+
+ /* FIXME: Should we use non general LAP here ? */
+
+ ii = NULL;
+ n = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0);
+ if (n < 0) {
+ syslog(LOG_ERR, "Inquiry failed. %s(%d)",
+ strerror(errno), errno);
+ continue;
+ }
+
+ for (i = 0; i < n; i++) {
+ char dst[40];
+ ba2str(&ii[i].bdaddr, dst);
+
+ if (use_sdp) {
+ syslog(LOG_INFO, "Searching for %s on %s",
+ bnep_svc2str(service), dst);
+
+ if (bnep_sdp_search(&src_addr, &ii[i].bdaddr, service) <= 0)
+ continue;
+ }
+
+ r = create_connection(dst, &ii[i].bdaddr);
+ if (r < 0) {
+ terminate = 1;
+ break;
+ }
+ }
+ bt_free(ii);
+ } while (!terminate && persist);
+
+ return r;
+}
+
+static void do_show(void)
+{
+ bnep_show_connections();
+}
+
+static void do_kill(char *dst)
+{
+ if (dst)
+ bnep_kill_connection((void *) strtoba(dst));
+ else
+ bnep_kill_all_connections();
+}
+
+static void sig_hup(int sig)
+{
+ return;
+}
+
+static void sig_term(int sig)
+{
+ terminate = 1;
+}
+
+static int write_pidfile(void)
+{
+ int fd;
+ FILE *f;
+ pid_t pid;
+
+ do {
+ fd = open(pidfile, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL, 0644);
+ if (fd == -1) {
+ /* Try to open the file for read. */
+ fd = open(pidfile, O_RDONLY);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Could not read old pidfile: %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* We're already running; send a SIGHUP (we presume that they
+ * are calling ifup for a reason, so they probably want to
+ * rescan) and then exit cleanly and let things go on in the
+ * background. Muck with the filename so that we don't go
+ * deleting the pid file for the already-running instance.
+ */
+ f = fdopen(fd, "r");
+ if (!f) {
+ syslog(LOG_ERR, "Could not fdopen old pidfile: %s(%d)",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ pid = 0;
+ if (fscanf(f, "%d", &pid) != 1)
+ pid = 0;
+ fclose(f);
+
+ if (pid) {
+ /* Try to kill it. */
+ if (kill(pid, SIGHUP) == -1) {
+ /* No such pid; remove the bogus pid file. */
+ syslog(LOG_INFO, "Removing stale pidfile");
+ unlink(pidfile);
+ fd = -1;
+ } else {
+ /* Got it. Don't mess with the pid file on
+ * our way out. */
+ syslog(LOG_INFO, "Signalling existing process %d and exiting\n", pid);
+ pidfile = NULL;
+ return -1;
+ }
+ }
+ }
+ } while(fd == -1);
+
+ f = fdopen(fd, "w");
+ if (!f) {
+ syslog(LOG_ERR, "Could not fdopen new pidfile: %s(%d)",
+ strerror(errno), errno);
+ close(fd);
+ unlink(pidfile);
+ return -1;
+ }
+
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+
+ return 0;
+}
+
+static struct option main_lopts[] = {
+ { "help", 0, 0, 'h' },
+ { "listen", 0, 0, 's' },
+ { "connect", 1, 0, 'c' },
+ { "search", 2, 0, 'Q' },
+ { "kill", 1, 0, 'k' },
+ { "killall", 0, 0, 'K' },
+ { "role", 1, 0, 'r' },
+ { "service", 1, 0, 'd' },
+ { "ethernet", 1, 0, 'e' },
+ { "device", 1, 0, 'i' },
+ { "nosdp", 0, 0, 'D' },
+ { "list", 0, 0, 'l' },
+ { "show", 0, 0, 'l' },
+ { "nodetach", 0, 0, 'n' },
+ { "persist", 2, 0, 'p' },
+ { "auth", 0, 0, 'A' },
+ { "encrypt", 0, 0, 'E' },
+ { "secure", 0, 0, 'S' },
+ { "master", 0, 0, 'M' },
+ { "cache", 0, 0, 'C' },
+ { "pidfile", 1, 0, 'P' },
+ { "devup", 1, 0, 'u' },
+ { "devdown", 1, 0, 'o' },
+ { "autozap", 0, 0, 'z' },
+ { 0, 0, 0, 0 }
+};
+
+static char main_sopts[] = "hsc:k:Kr:d:e:i:lnp::DQ::AESMC::P:u:o:z";
+
+static char main_help[] =
+ "Bluetooth PAN daemon version " VERSION " \n"
+ "Usage:\n"
+ "\tpand <options>\n"
+ "Options:\n"
+ "\t--show --list -l Show active PAN connections\n"
+ "\t--listen -s Listen for PAN connections\n"
+ "\t--connect -c <bdaddr> Create PAN connection\n"
+ "\t--autozap -z Disconnect automatically on exit\n"
+ "\t--search -Q[duration] Search and connect\n"
+ "\t--kill -k <bdaddr> Kill PAN connection\n"
+ "\t--killall -K Kill all PAN connections\n"
+ "\t--role -r <role> Local PAN role (PANU, NAP, GN)\n"
+ "\t--service -d <role> Remote PAN service (PANU, NAP, GN)\n"
+ "\t--ethernet -e <name> Network interface name\n"
+ "\t--device -i <bdaddr> Source bdaddr\n"
+ "\t--nosdp -D Disable SDP\n"
+ "\t--auth -A Enable authentication\n"
+ "\t--encrypt -E Enable encryption\n"
+ "\t--secure -S Secure connection\n"
+ "\t--master -M Become the master of a piconet\n"
+ "\t--nodetach -n Do not become a daemon\n"
+ "\t--persist -p[interval] Persist mode\n"
+ "\t--cache -C[valid] Cache addresses\n"
+ "\t--pidfile -P <pidfile> Create PID file\n"
+ "\t--devup -u <script> Script to run when interface comes up\n"
+ "\t--devdown -o <script> Script to run when interface comes down\n";
+
+int main(int argc, char *argv[])
+{
+ char *dst = NULL, *src = NULL;
+ struct sigaction sa;
+ int mode = NONE;
+ int opt;
+
+ while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) {
+ switch(opt) {
+ case 'l':
+ mode = SHOW;
+ detach = 0;
+ break;
+
+ case 's':
+ mode = LISTEN;
+ break;
+
+ case 'c':
+ mode = CONNECT;
+ dst = strdup(optarg);
+ break;
+
+ case 'Q':
+ mode = CONNECT;
+ if (optarg)
+ search_duration = atoi(optarg);
+ break;
+
+ case 'k':
+ mode = KILL;
+ detach = 0;
+ dst = strdup(optarg);
+ break;
+
+ case 'K':
+ mode = KILL;
+ detach = 0;
+ break;
+
+ case 'i':
+ src = strdup(optarg);
+ break;
+
+ case 'r':
+ bnep_str2svc(optarg, &role);
+ break;
+
+ case 'd':
+ bnep_str2svc(optarg, &service);
+ break;
+
+ case 'D':
+ use_sdp = 0;
+ break;
+
+ case 'A':
+ link_mode |= L2CAP_LM_AUTH;
+ break;
+
+ case 'E':
+ link_mode |= L2CAP_LM_ENCRYPT;
+ break;
+
+ case 'S':
+ link_mode |= L2CAP_LM_SECURE;
+ break;
+
+ case 'M':
+ link_mode |= L2CAP_LM_MASTER;
+ break;
+
+ case 'e':
+ strncpy(netdev, optarg, 16);
+ netdev[15] = '\0';
+ break;
+
+ case 'n':
+ detach = 0;
+ break;
+
+ case 'p':
+ if (optarg)
+ persist = atoi(optarg);
+ else
+ persist = 5;
+ break;
+
+ case 'C':
+ if (optarg)
+ use_cache = atoi(optarg);
+ else
+ use_cache = 2;
+ break;
+
+ case 'P':
+ pidfile = strdup(optarg);
+ break;
+
+ case 'u':
+ devupcmd = strdup(optarg);
+ break;
+
+ case 'o':
+ devdowncmd = strdup(optarg);
+ break;
+
+ case 'z':
+ cleanup = 1;
+ break;
+
+ case 'h':
+ default:
+ printf(main_help);
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (bnep_init())
+ return -1;
+
+ /* Check non daemon modes first */
+ switch (mode) {
+ case SHOW:
+ do_show();
+ return 0;
+
+ case KILL:
+ do_kill(dst);
+ return 0;
+
+ case NONE:
+ printf(main_help);
+ return 0;
+ }
+
+ /* Initialize signals */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ if (detach && daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+
+ openlog("pand", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+ syslog(LOG_INFO, "Bluetooth PAN daemon version %s", VERSION);
+
+ if (src) {
+ src_dev = hci_devid(src);
+ if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) {
+ syslog(LOG_ERR, "Invalid source. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+ }
+
+ if (pidfile && write_pidfile())
+ return -1;
+
+ if (dst) {
+ /* Disable cache invalidation */
+ use_cache = 0;
+
+ strncpy(cache.dst, dst, sizeof(cache.dst) - 1);
+ str2ba(dst, &cache.bdaddr);
+ cache.valid = 1;
+ free(dst);
+ }
+
+ switch (mode) {
+ case CONNECT:
+ do_connect();
+ break;
+
+ case LISTEN:
+ do_listen();
+ break;
+ }
+
+ if (pidfile)
+ unlink(pidfile);
+
+ return 0;
+}
diff --git a/compat/pand.h b/compat/pand.h
new file mode 100644
index 00000000..90f26ed6
--- /dev/null
+++ b/compat/pand.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-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 bnep_init(void);
+int bnep_cleanup(void);
+
+int bnep_str2svc(char *svc, uint16_t *uuid);
+char *bnep_svc2str(uint16_t uuid);
+
+int bnep_show_connections(void);
+int bnep_kill_connection(uint8_t *dst);
+int bnep_kill_all_connections(void);
+
+int bnep_accept_connection(int sk, uint16_t role, char *dev);
+int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev);
diff --git a/compat/sdp.c b/compat/sdp.c
new file mode 100644
index 00000000..a08ace90
--- /dev/null
+++ b/compat/sdp.c
@@ -0,0 +1,718 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/hidp.h>
+#include <bluetooth/bnep.h>
+
+#include "textfile.h"
+#include "sdp.h"
+
+static sdp_record_t *record = NULL;
+static sdp_session_t *session = NULL;
+
+static void add_lang_attr(sdp_record_t *r)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs = 0;
+
+ /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(r, langs);
+ sdp_list_free(langs, 0);
+}
+
+static void epox_endian_quirk(unsigned char *data, int size)
+{
+ /* USAGE_PAGE (Keyboard) 05 07
+ * USAGE_MINIMUM (0) 19 00
+ * USAGE_MAXIMUM (65280) 2A 00 FF <= must be FF 00
+ * LOGICAL_MINIMUM (0) 15 00
+ * LOGICAL_MAXIMUM (65280) 26 00 FF <= must be FF 00
+ */
+ unsigned char pattern[] = { 0x05, 0x07, 0x19, 0x00, 0x2a, 0x00, 0xff,
+ 0x15, 0x00, 0x26, 0x00, 0xff };
+ int i;
+
+ if (!data)
+ return;
+
+ for (i = 0; i < size - sizeof(pattern); i++) {
+ if (!memcmp(data + i, pattern, sizeof(pattern))) {
+ data[i + 5] = 0xff;
+ data[i + 6] = 0x00;
+ data[i + 10] = 0xff;
+ data[i + 11] = 0x00;
+ }
+ }
+}
+
+static int store_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+ char filename[PATH_MAX + 1], addr[18], *str, *desc;
+ int i, err, size;
+
+ ba2str(src, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "hidd");
+
+ size = 15 + 3 + 3 + 5 + (req->rd_size * 2) + 1 + 9 + strlen(req->name) + 2;
+ str = malloc(size);
+ if (!str)
+ return -ENOMEM;
+
+ desc = malloc((req->rd_size * 2) + 1);
+ if (!desc) {
+ free(str);
+ return -ENOMEM;
+ }
+
+ memset(desc, 0, (req->rd_size * 2) + 1);
+ for (i = 0; i < req->rd_size; i++)
+ sprintf(desc + (i * 2), "%2.2X", req->rd_data[i]);
+
+ snprintf(str, size - 1, "%04X:%04X:%04X %02X %02X %04X %s %08X %s",
+ req->vendor, req->product, req->version,
+ req->subclass, req->country, req->parser, desc,
+ req->flags, req->name);
+
+ free(desc);
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dst, addr);
+ err = textfile_put(filename, addr, str);
+
+ free(str);
+
+ return err;
+}
+
+int get_stored_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+ char filename[PATH_MAX + 1], addr[18], tmp[3], *str, *desc;
+ unsigned int vendor, product, version, subclass, country, parser, pos;
+ int i;
+
+ desc = malloc(4096);
+ if (!desc)
+ return -ENOMEM;
+
+ memset(desc, 0, 4096);
+
+ ba2str(src, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "hidd");
+
+ ba2str(dst, addr);
+ str = textfile_get(filename, addr);
+ if (!str) {
+ free(desc);
+ return -EIO;
+ }
+
+ sscanf(str, "%04X:%04X:%04X %02X %02X %04X %4095s %08X %n",
+ &vendor, &product, &version, &subclass, &country,
+ &parser, desc, &req->flags, &pos);
+
+ free(str);
+
+ req->vendor = vendor;
+ req->product = product;
+ req->version = version;
+ req->subclass = subclass;
+ req->country = country;
+ req->parser = parser;
+
+ snprintf(req->name, 128, str + pos);
+
+ req->rd_size = strlen(desc) / 2;
+ req->rd_data = malloc(req->rd_size);
+ if (!req->rd_data) {
+ free(desc);
+ return -ENOMEM;
+ }
+
+ memset(tmp, 0, sizeof(tmp));
+ for (i = 0; i < req->rd_size; i++) {
+ memcpy(tmp, desc + (i * 2), 2);
+ req->rd_data[i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ free(desc);
+
+ return 0;
+}
+
+int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+ struct sockaddr_l2 addr;
+ socklen_t addrlen;
+ bdaddr_t bdaddr;
+ uint32_t range = 0x0000ffff;
+ sdp_session_t *s;
+ sdp_list_t *search, *attrid, *pnp_rsp, *hid_rsp;
+ sdp_record_t *rec;
+ sdp_data_t *pdlist, *pdlist2;
+ uuid_t svclass;
+ int err;
+
+ s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE);
+ if (!s)
+ return -1;
+
+ sdp_uuid16_create(&svclass, PNP_INFO_SVCLASS_ID);
+ search = sdp_list_append(NULL, &svclass);
+ attrid = sdp_list_append(NULL, &range);
+
+ err = sdp_service_search_attr_req(s, search,
+ SDP_ATTR_REQ_RANGE, attrid, &pnp_rsp);
+
+ sdp_list_free(search, NULL);
+ sdp_list_free(attrid, NULL);
+
+ sdp_uuid16_create(&svclass, HID_SVCLASS_ID);
+ search = sdp_list_append(NULL, &svclass);
+ attrid = sdp_list_append(NULL, &range);
+
+ err = sdp_service_search_attr_req(s, search,
+ SDP_ATTR_REQ_RANGE, attrid, &hid_rsp);
+
+ sdp_list_free(search, NULL);
+ sdp_list_free(attrid, NULL);
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if (getsockname(s->sock, (struct sockaddr *) &addr, &addrlen) < 0)
+ bacpy(&bdaddr, src);
+ else
+ bacpy(&bdaddr, &addr.l2_bdaddr);
+
+ sdp_close(s);
+
+ if (err || !hid_rsp)
+ return -1;
+
+ if (pnp_rsp) {
+ rec = (sdp_record_t *) pnp_rsp->data;
+
+ pdlist = sdp_data_get(rec, 0x0201);
+ req->vendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, 0x0202);
+ req->product = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, 0x0203);
+ req->version = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ sdp_record_free(rec);
+ }
+
+ rec = (sdp_record_t *) hid_rsp->data;
+
+ pdlist = sdp_data_get(rec, 0x0101);
+ pdlist2 = sdp_data_get(rec, 0x0102);
+ if (pdlist) {
+ if (pdlist2) {
+ if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) {
+ strncpy(req->name, pdlist2->val.str, sizeof(req->name) - 1);
+ strcat(req->name, " ");
+ }
+ strncat(req->name, pdlist->val.str,
+ sizeof(req->name) - strlen(req->name));
+ } else
+ strncpy(req->name, pdlist->val.str, sizeof(req->name));
+ } else {
+ pdlist2 = sdp_data_get(rec, 0x0100);
+ if (pdlist2)
+ strncpy(req->name, pdlist2->val.str, sizeof(req->name));
+ }
+
+ pdlist = sdp_data_get(rec, 0x0201);
+ req->parser = pdlist ? pdlist->val.uint16 : 0x0100;
+
+ pdlist = sdp_data_get(rec, 0x0202);
+ req->subclass = pdlist ? pdlist->val.uint8 : 0;
+
+ pdlist = sdp_data_get(rec, 0x0203);
+ req->country = pdlist ? pdlist->val.uint8 : 0;
+
+ pdlist = sdp_data_get(rec, 0x0206);
+ if (pdlist) {
+ pdlist = pdlist->val.dataseq;
+ pdlist = pdlist->val.dataseq;
+ pdlist = pdlist->next;
+
+ req->rd_data = malloc(pdlist->unitSize);
+ if (req->rd_data) {
+ memcpy(req->rd_data, (unsigned char *) pdlist->val.str, pdlist->unitSize);
+ req->rd_size = pdlist->unitSize;
+ epox_endian_quirk(req->rd_data, req->rd_size);
+ }
+ }
+
+ sdp_record_free(rec);
+
+ if (bacmp(&bdaddr, BDADDR_ANY))
+ store_device_info(&bdaddr, dst, req);
+
+ return 0;
+}
+
+int get_alternate_device_info(const bdaddr_t *src, const bdaddr_t *dst, uint16_t *uuid, uint8_t *channel, char *name, size_t len)
+{
+ uint16_t attr1 = SDP_ATTR_PROTO_DESC_LIST;
+ uint16_t attr2 = SDP_ATTR_SVCNAME_PRIMARY;
+ sdp_session_t *s;
+ sdp_list_t *search, *attrid, *rsp;
+ uuid_t svclass;
+ int err;
+
+ s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE);
+ if (!s)
+ return -1;
+
+ sdp_uuid16_create(&svclass, HEADSET_SVCLASS_ID);
+ search = sdp_list_append(NULL, &svclass);
+ attrid = sdp_list_append(NULL, &attr1);
+ attrid = sdp_list_append(attrid, &attr2);
+
+ err = sdp_service_search_attr_req(s, search,
+ SDP_ATTR_REQ_INDIVIDUAL, attrid, &rsp);
+
+ sdp_list_free(search, NULL);
+ sdp_list_free(attrid, NULL);
+
+ if (err <= 0) {
+ sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+ search = sdp_list_append(NULL, &svclass);
+ attrid = sdp_list_append(NULL, &attr1);
+ attrid = sdp_list_append(attrid, &attr2);
+
+ err = sdp_service_search_attr_req(s, search,
+ SDP_ATTR_REQ_INDIVIDUAL, attrid, &rsp);
+
+ sdp_list_free(search, NULL);
+ sdp_list_free(attrid, NULL);
+
+ if (err < 0) {
+ sdp_close(s);
+ return err;
+ }
+
+ if (uuid)
+ *uuid = SERIAL_PORT_SVCLASS_ID;
+ } else {
+ if (uuid)
+ *uuid = HEADSET_SVCLASS_ID;
+ }
+
+ sdp_close(s);
+
+ for (; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ sdp_get_service_name(rec, name, len);
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+ if (ch > 0) {
+ if (channel)
+ *channel = ch;
+ return 0;
+ }
+ }
+
+ sdp_record_free(rec);
+ }
+
+ return -EIO;
+}
+
+void bnep_sdp_unregister(void)
+{
+ if (record && sdp_record_unregister(session, record))
+ syslog(LOG_ERR, "Service record unregistration failed.");
+
+ sdp_close(session);
+}
+
+int bnep_sdp_register(bdaddr_t *device, uint16_t role)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, pan, l2cap, bnep;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ sdp_data_t *v, *p;
+ uint16_t psm = 15, version = 0x0100;
+ uint16_t security_desc = 0;
+ uint16_t net_access_type = 0xfffe;
+ uint32_t max_net_access_rate = 0;
+ char *name = "BlueZ PAN";
+ char *desc = "BlueZ PAN Service";
+ int status;
+
+ session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+ if (!session) {
+ syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ record = sdp_record_alloc();
+ if (!record) {
+ syslog(LOG_ERR, "Failed to allocate service record %s(%d)",
+ strerror(errno), errno);
+ sdp_close(session);
+ return -1;
+ }
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, 0);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ p = sdp_data_alloc(SDP_UINT16, &psm);
+ proto[0] = sdp_list_append(proto[0], p);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep);
+ v = sdp_data_alloc(SDP_UINT16, &version);
+ proto[1] = sdp_list_append(proto[1], v);
+
+ /* Supported protocols */
+ {
+ uint16_t ptype[4] = {
+ 0x0800, /* IPv4 */
+ 0x0806, /* ARP */
+ };
+ sdp_data_t *head, *pseq;
+ int p;
+
+ for (p = 0, head = NULL; p < 2; p++) {
+ sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+ if (head)
+ sdp_seq_append(head, data);
+ else
+ head = data;
+ }
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ }
+
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ add_lang_attr(record);
+
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_data_free(p);
+ sdp_data_free(v);
+ sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security_desc);
+
+ switch (role) {
+ case BNEP_SVC_NAP:
+ sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, "Network Access Point", name, desc);
+
+ sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &net_access_type);
+ sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, SDP_UINT32, &max_net_access_rate);
+ break;
+
+ case BNEP_SVC_GN:
+ sdp_uuid16_create(&pan, GN_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, "Group Network Service", name, desc);
+ break;
+
+ case BNEP_SVC_PANU:
+ sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+ sdp_list_free(svclass, 0);
+
+ sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ sdp_list_free(pfseq, 0);
+
+ sdp_set_info_attr(record, "PAN User", name, desc);
+ break;
+ }
+
+ status = sdp_device_record_register(session, device, record, 0);
+ if (status) {
+ syslog(LOG_ERR, "SDP registration failed.");
+ sdp_record_free(record); record = NULL;
+ sdp_close(session);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Search for PAN service.
+ * Returns 1 if service is found and 0 otherwise. */
+int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service)
+{
+ sdp_list_t *srch, *rsp = NULL;
+ sdp_session_t *s;
+ uuid_t svclass;
+ int err;
+
+ switch (service) {
+ case BNEP_SVC_PANU:
+ sdp_uuid16_create(&svclass, PANU_SVCLASS_ID);
+ break;
+ case BNEP_SVC_NAP:
+ sdp_uuid16_create(&svclass, NAP_SVCLASS_ID);
+ break;
+ case BNEP_SVC_GN:
+ sdp_uuid16_create(&svclass, GN_SVCLASS_ID);
+ break;
+ }
+
+ srch = sdp_list_append(NULL, &svclass);
+
+ s = sdp_connect(src, dst, 0);
+ if (!s) {
+ syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)",
+ strerror(errno), errno);
+ return 0;
+ }
+
+ err = sdp_service_search_req(s, srch, 1, &rsp);
+ sdp_close(s);
+
+ /* Assume that search is successeful
+ * if at least one record is found */
+ if (!err && sdp_list_len(rsp))
+ return 1;
+
+ return 0;
+}
+
+static unsigned char async_uuid[] = { 0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
+ 0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
+
+void dun_sdp_unregister(void)
+{
+ if (record && sdp_record_unregister(session, record))
+ syslog(LOG_ERR, "Service record unregistration failed.");
+ sdp_close(session);
+}
+
+int dun_sdp_register(bdaddr_t *device, uint8_t channel, int type)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, l2cap, rfcomm, dun;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ int status;
+
+ session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+ if (!session) {
+ syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ record = sdp_record_alloc();
+ if (!record) {
+ syslog(LOG_ERR, "Failed to alloc service record");
+ return -1;
+ }
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm);
+ proto[1] = sdp_list_append(proto[1], sdp_data_alloc(SDP_UINT8, &channel));
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ switch (type) {
+ case MROUTER:
+ sdp_uuid16_create(&dun, SERIAL_PORT_SVCLASS_ID);
+ break;
+ case ACTIVESYNC:
+ sdp_uuid128_create(&dun, (void *) async_uuid);
+ break;
+ case DIALUP:
+ sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+ break;
+ default:
+ sdp_uuid16_create(&dun, LAN_ACCESS_SVCLASS_ID);
+ break;
+ }
+
+ svclass = sdp_list_append(NULL, &dun);
+ sdp_set_service_classes(record, svclass);
+
+ switch (type) {
+ case LANACCESS:
+ sdp_uuid16_create(&profile[0].uuid, LAN_ACCESS_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ break;
+ case DIALUP:
+ sdp_uuid16_create(&profile[0].uuid, DIALUP_NET_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ break;
+ }
+
+ switch (type) {
+ case MROUTER:
+ sdp_set_info_attr(record, "mRouter", NULL, NULL);
+ break;
+ case ACTIVESYNC:
+ sdp_set_info_attr(record, "ActiveSync", NULL, NULL);
+ break;
+ case DIALUP:
+ sdp_set_info_attr(record, "Dialup Networking", NULL, NULL);
+ break;
+ default:
+ sdp_set_info_attr(record, "LAN Access Point", NULL, NULL);
+ break;
+ }
+
+ status = sdp_device_record_register(session, device, record, 0);
+ if (status) {
+ syslog(LOG_ERR, "SDP registration failed.");
+ sdp_record_free(record);
+ record = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+int dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel, int type)
+{
+ sdp_session_t *s;
+ sdp_list_t *srch, *attrs, *rsp;
+ uuid_t svclass;
+ uint16_t attr;
+ int err;
+
+ s = sdp_connect(src, dst, 0);
+ if (!s) {
+ syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ switch (type) {
+ case MROUTER:
+ sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+ break;
+ case ACTIVESYNC:
+ sdp_uuid128_create(&svclass, (void *) async_uuid);
+ break;
+ case DIALUP:
+ sdp_uuid16_create(&svclass, DIALUP_NET_SVCLASS_ID);
+ break;
+ default:
+ sdp_uuid16_create(&svclass, LAN_ACCESS_SVCLASS_ID);
+ break;
+ }
+
+ srch = sdp_list_append(NULL, &svclass);
+
+ attr = SDP_ATTR_PROTO_DESC_LIST;
+ attrs = sdp_list_append(NULL, &attr);
+
+ err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+
+ sdp_close(s);
+
+ if (err)
+ return 0;
+
+ for(; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ int ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+ if (ch > 0) {
+ *channel = ch;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/compat/sdp.h b/compat/sdp.h
new file mode 100644
index 00000000..7ff75d68
--- /dev/null
+++ b/compat/sdp.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#define LANACCESS 0
+#define MROUTER 1
+#define ACTIVESYNC 2
+#define DIALUP 3
+
+int get_stored_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req);
+int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req);
+int get_alternate_device_info(const bdaddr_t *src, const bdaddr_t *dst, uint16_t *uuid, uint8_t *channel, char *name, size_t len);
+
+int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service);
+int bnep_sdp_register(bdaddr_t *device, uint16_t role);
+void bnep_sdp_unregister(void);
+
+int dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel, int type);
+int dun_sdp_register(bdaddr_t *device, uint8_t channel, int type);
+void dun_sdp_unregister(void);