Merge BlueZ 5.20 into master

Change-Id: I2d69df1b233a4e0135fd5137bc792a7040897f2a
diff --git a/Makefile.plugins b/Makefile.plugins
index 7a02d9f..dbcb491 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -5,6 +5,9 @@
 builtin_modules += wiimote
 builtin_sources += plugins/wiimote.c
 
+builtin_modules += gfrm
+builtin_sources += plugins/gfrm.c
+
 builtin_modules += autopair
 builtin_sources += plugins/autopair.c
 
diff --git a/Makefile.tools b/Makefile.tools
index c24bdf7..966dd64 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -176,7 +176,8 @@
 if TOOLS
 bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
 			tools/rfcomm tools/rctest tools/l2test tools/l2ping \
-			tools/sdptool tools/ciptool tools/bccmd tools/bluemoon
+			tools/sdptool tools/ciptool tools/bccmd tools/bluemoon \
+			tools/btmgmt
 
 tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
 						tools/hciattach_st.c \
@@ -248,6 +249,14 @@
 				src/shared/queue.h src/shared/queue.c \
 				src/shared/ringbuf.h src/shared/ringbuf.c
 
+tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/io.h src/shared/io-mainloop.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/mgmt.h src/shared/mgmt.c
+tools_btmgmt_LDADD = lib/libbluetooth-internal.la
+
 dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
 			tools/hcitool.1 tools/hcidump.1 \
 			tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
@@ -421,4 +430,4 @@
 		test/service-ftp.xml test/simple-player test/test-nap \
 		test/test-heartrate test/test-alert test/test-hfp \
 		test/test-cyclingspeed test/opp-client test/ftp-client \
-		test/pbap-client test/map-client
+		test/pbap-client test/map-client test/gfiber-agent
diff --git a/lib/hci.c b/lib/hci.c
index 005578a..89ec869 100644
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -2185,6 +2185,57 @@
 	return 0;
 }
 
+int hci_read_page_scan_type(int dd, uint8_t *type, int to)
+{
+	read_page_scan_type_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_PAGE_SCAN_TYPE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_PAGE_SCAN_TYPE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*type = rp.type;
+	return 0;
+}
+
+int hci_write_page_scan_type(int dd, uint8_t type, int to)
+{
+	write_page_scan_type_cp cp;
+	write_page_scan_type_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.type = type;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_PAGE_SCAN_TYPE;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_PAGE_SCAN_TYPE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_PAGE_SCAN_TYPE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
 int hci_read_inquiry_mode(int dd, uint8_t *mode, int to)
 {
 	read_inquiry_mode_rp rp;
diff --git a/lib/hci.h b/lib/hci.h
index 0c94829..760b3fa 100644
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -1070,10 +1070,23 @@
 #define WRITE_INQUIRY_MODE_RP_SIZE 1
 
 #define OCF_READ_PAGE_SCAN_TYPE		0x0046
+typedef struct {
+	uint8_t		status;
+	uint8_t		type;
+} __attribute__ ((packed)) read_page_scan_type_rp;
+#define READ_PAGE_SCAN_TYPE_RP_SIZE 2
 
 #define OCF_WRITE_PAGE_SCAN_TYPE	0x0047
-	#define PAGE_SCAN_TYPE_STANDARD		0x00
-	#define PAGE_SCAN_TYPE_INTERLACED	0x01
+typedef struct {
+	uint8_t		type;
+} __attribute__ ((packed)) write_page_scan_type_cp;
+#define WRITE_PAGE_SCAN_TYPE_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_page_scan_type_rp;
+#define WRITE_PAGE_SCAN_TYPE_RP_SIZE 1
+#define PAGE_SCAN_TYPE_STANDARD		0x00
+#define PAGE_SCAN_TYPE_INTERLACED	0x01
 
 #define OCF_READ_AFH_MODE		0x0048
 typedef struct {
diff --git a/lib/hci_lib.h b/lib/hci_lib.h
index 50744c3..5e7e11d 100644
--- a/lib/hci_lib.h
+++ b/lib/hci_lib.h
@@ -92,6 +92,8 @@
 int hci_exit_park_mode(int dd, uint16_t handle, int to);
 int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to);
 int hci_write_inquiry_scan_type(int dd, uint8_t type, int to);
+int hci_read_page_scan_type(int dd, uint8_t *type, int to);
+int hci_write_page_scan_type(int dd, uint8_t type, int to);
 int hci_read_inquiry_mode(int dd, uint8_t *mode, int to);
 int hci_write_inquiry_mode(int dd, uint8_t mode, int to);
 int hci_read_afh_mode(int dd, uint8_t *mode, int to);
diff --git a/plugins/gfrm.c b/plugins/gfrm.c
new file mode 100644
index 0000000..4588ef4
--- /dev/null
+++ b/plugins/gfrm.c
@@ -0,0 +1,252 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014 Google Inc.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <glib.h>
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/log.h"
+
+#define BD_ADDR_FIFO "/tmp/bd-addr-fifo"
+
+struct gfrm_device {
+	char *name;
+	uint8_t oui[3];
+	uint8_t bdaddr_type;
+	uint16_t vid;
+	uint16_t pid;
+};
+
+static struct gfrm_device gfrm_devs[] = {
+	{ "GFRM100", {0x00, 0x24, 0x1C}, BDADDR_BREDR, 0x58, 0x2000 },
+	{ "GFRM200", {0x20, 0xCD, 0x39}, BDADDR_LE_PUBLIC, 0x471, 0x2210 },
+	{ "HID AdvRemote", {0x90, 0x59, 0xAF}, BDADDR_LE_PUBLIC, 0xD, 0x0 },
+};
+
+static struct gfrm_device *gfrm_find_dev_by_vid_pid(uint16_t vid, uint16_t pid)
+{
+	uint16_t i;
+
+	for (i = 0; i < G_N_ELEMENTS(gfrm_devs); ++i) {
+		if (vid == gfrm_devs[i].vid && pid == gfrm_devs[i].pid) {
+			return &gfrm_devs[i];
+		}
+	}
+
+	return NULL;
+}
+
+static struct gfrm_device *gfrm_find_dev_by_oui(uint8_t oui0, uint8_t oui1,
+								uint8_t oui2)
+{
+	uint16_t i;
+
+	for (i = 0; i < G_N_ELEMENTS(gfrm_devs); ++i) {
+		if (oui0 == gfrm_devs[i].oui[0] &&
+		    oui1 == gfrm_devs[i].oui[1] &&
+		    oui2 == gfrm_devs[i].oui[2]) {
+			return &gfrm_devs[i];
+		}
+	}
+
+	return NULL;
+}
+
+static ssize_t gfrm_pincb(struct btd_adapter *adapter, struct btd_device *device,
+			  char *pinbuf, bool *display, unsigned int attempt)
+{
+	uint16_t vid, pid;
+	char addr[18], name[25];
+
+	/*
+	 * Only try the pin code once per device.
+	 * If it's not correct then it's an unknown device.
+	 */
+	if (attempt > 1)
+		return 0;
+
+	ba2str(device_get_address(device), addr);
+
+	vid = btd_device_get_vendor(device);
+	pid = btd_device_get_product(device);
+	DBG("vendor 0x%x product 0x%x", vid, pid);
+
+	device_get_name(device, name, sizeof(name));
+	name[sizeof(name) - 1] = 0;
+
+	if (gfrm_find_dev_by_vid_pid(vid, pid)) {
+		DBG("Forcing PIN 0000 on %s at %s", name, addr);
+		memcpy(pinbuf, "0000", 4);
+		return 4;
+	}
+
+	return 0;
+}
+
+static void gfrm_start_bd_addr_fifo_watch(
+		gboolean (*callback)(GIOChannel *, GIOCondition, gpointer))
+{
+	int fd;
+	GIOChannel *io;
+
+	if ((fd = open(BD_ADDR_FIFO, O_RDONLY | O_NONBLOCK)) < 0) {
+		DBG("open failed");
+		return;
+	}
+
+	if ((io = g_io_channel_unix_new(fd)) == NULL) {
+		DBG("g_io_channel_unix_new failed");
+		close(fd);
+		return;
+	}
+
+	if (!g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+			    callback, NULL)) {
+		DBG("g_io_add_watch failed");
+	}
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_unref(io);
+}
+
+static gboolean gfrm_add_device(GIOChannel *io, GIOCondition condition,
+				gpointer data)
+{
+	int fd;
+	bdaddr_t bdaddr_be;
+	bdaddr_t bdaddr;
+	char bdaddr_str[18];
+	struct gfrm_device *gfrm_dev;
+	struct btd_adapter *adapter;
+	struct btd_device *device;
+
+	if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		DBG("io error: condition %u", condition);
+	}
+
+	if (!(condition & (G_IO_IN))) {
+		DBG("pipe has no data to read");
+		goto reopen;
+	}
+
+	/* Read BD address from FIFO */
+	fd = g_io_channel_unix_get_fd(io);
+
+	if (read(fd, &bdaddr_be, sizeof(bdaddr_be)) != 6) {
+		DBG("read failed");
+		goto reopen;
+	}
+
+	baswap(&bdaddr, &bdaddr_be);
+	ba2str(&bdaddr, bdaddr_str);
+
+	gfrm_dev = gfrm_find_dev_by_oui(bdaddr.b[5], bdaddr.b[4], bdaddr.b[3]);
+	if (!gfrm_dev)
+		goto reopen;
+
+	DBG("Discovered %s at %s", gfrm_dev->name, bdaddr_str);
+
+	/* Add device to BlueZ stack */
+	adapter = btd_adapter_get_default();
+	if (!adapter) {
+		DBG("btd_adapter_get_default failed");
+		goto reopen;
+	}
+
+	device = btd_adapter_find_device(adapter, &bdaddr, gfrm_dev->bdaddr_type);
+	if (device) {
+		btd_device_set_temporary(device, TRUE);
+		btd_adapter_remove_device(adapter, device);
+	}
+
+	device = btd_adapter_get_device(adapter, &bdaddr, gfrm_dev->bdaddr_type);
+	btd_device_device_set_name(device, gfrm_dev->name);
+	btd_device_set_pnpid(device, 0x1, gfrm_dev->vid, gfrm_dev->pid, 0x0);
+	btd_device_set_temporary(device, FALSE);
+
+	/*
+	 * Pairing the remote is handled in Python script:
+	 * test/gfiber-agent
+	 */
+
+reopen:
+	/* Reopen the FIFO (new fd, io channel, and watch) */
+	gfrm_start_bd_addr_fifo_watch(gfrm_add_device);
+
+	/*
+	 * Return FALSE, so that the watch on the old io channel is removed.
+	 * That, in turn, triggers closing of io channel and file descriptor.
+	 */
+	return FALSE;
+}
+
+static int gfrm_probe(struct btd_adapter *adapter)
+{
+	btd_adapter_register_pin_cb(adapter, gfrm_pincb);
+}
+
+static void gfrm_remove(struct btd_adapter *adapter)
+{
+	btd_adapter_unregister_pin_cb(adapter, gfrm_pincb);
+}
+
+static struct btd_adapter_driver gfrm_driver = {
+	.name	= "gfrm",
+	.probe	= gfrm_probe,
+	.remove	= gfrm_remove,
+};
+
+static int gfrm_init(void)
+{
+	btd_register_adapter_driver(&gfrm_driver);
+
+	/* Create FIFO for IR-assisted pairing */
+	if (mkfifo(BD_ADDR_FIFO, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
+		   S_IROTH | S_IWOTH) < 0 && errno != EEXIST) {
+		DBG("mkfifo failed");
+		return 0;
+	}
+
+	/* Start watching the FIFO */
+	gfrm_start_bd_addr_fifo_watch(gfrm_add_device);
+
+	return 0;
+}
+
+static void gfrm_exit(void)
+{
+	btd_unregister_adapter_driver(&gfrm_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(gfrm, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_HIGH, gfrm_init, gfrm_exit)
diff --git a/profiles/input/device.c b/profiles/input/device.c
index a61b2c7..e37e7b9 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -92,6 +92,7 @@
 
 static int idle_timeout = 0;
 static bool uhid_enabled = false;
+static bool encryption_enabled = true;
 
 void input_set_idle_timeout(int timeout)
 {
@@ -103,6 +104,11 @@
 	uhid_enabled = state;
 }
 
+void input_enable_encryption(bool state)
+{
+	encryption_enabled = state;
+}
+
 static void input_device_enter_reconnect_mode(struct input_device *idev);
 static int connection_disconnect(struct input_device *idev, uint32_t flags);
 
@@ -968,7 +974,7 @@
 		device_get_name(idev->device, req->name, sizeof(req->name));
 
 	/* Encryption is mandatory for keyboards */
-	if (req->subclass & 0x40) {
+	if (encryption_enabled && (req->subclass & 0x40)) {
 		if (!bt_io_set(idev->intr_io, &gerr,
 					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
 					BT_IO_OPT_INVALID)) {
diff --git a/profiles/input/device.h b/profiles/input/device.h
index 51a9aee..4edc965 100644
--- a/profiles/input/device.h
+++ b/profiles/input/device.h
@@ -29,6 +29,7 @@
 
 void input_set_idle_timeout(int timeout);
 void input_enable_userspace_hid(bool state);
+void input_enable_encryption(bool state);
 
 int input_device_register(struct btd_service *service);
 void input_device_unregister(struct btd_service *service);
diff --git a/profiles/input/manager.c b/profiles/input/manager.c
index 9712d2c..2302f95 100644
--- a/profiles/input/manager.c
+++ b/profiles/input/manager.c
@@ -98,6 +98,7 @@
 	if (config) {
 		int idle_timeout;
 		gboolean uhid_enabled;
+		gboolean encryption;
 
 		idle_timeout = g_key_file_get_integer(config, "General",
 							"IdleTimeout", &err);
@@ -115,6 +116,15 @@
 			input_enable_userspace_hid(uhid_enabled);
 		} else
 			g_clear_error(&err);
+
+		encryption = g_key_file_get_boolean(config, "General",
+							"Encryption", &err);
+		if (!err) {
+			DBG("input.conf: Encryption=%s", encryption ?
+							"true" : "false");
+			input_enable_encryption(encryption);
+		} else
+			g_clear_error(&err);
 	}
 
 	btd_profile_register(&input_profile);
diff --git a/profiles/input/uhid_copy.h b/profiles/input/uhid_copy.h
index 23a6287..9eb11fd 100644
--- a/profiles/input/uhid_copy.h
+++ b/profiles/input/uhid_copy.h
@@ -21,6 +21,7 @@
 
 #include <linux/input.h>
 #include <linux/types.h>
+#include <linux/hid.h>
 
 enum uhid_event_type {
 	UHID_CREATE,
@@ -34,6 +35,8 @@
 	UHID_INPUT,
 	UHID_FEATURE,
 	UHID_FEATURE_ANSWER,
+	UHID_CREATE2,
+	UHID_INPUT2,
 };
 
 struct uhid_create_req {
@@ -50,6 +53,19 @@
 	__u32 country;
 } __attribute__((__packed__));
 
+struct uhid_create2_req {
+	__u8 name[128];
+	__u8 phys[64];
+	__u8 uniq[64];
+	__u16 rd_size;
+	__u16 bus;
+	__u32 vendor;
+	__u32 product;
+	__u32 version;
+	__u32 country;
+	__u8 rd_data[HID_MAX_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
 #define UHID_DATA_MAX 4096
 
 enum uhid_report_type {
@@ -63,6 +79,11 @@
 	__u16 size;
 } __attribute__((__packed__));
 
+struct uhid_input2_req {
+	__u16 size;
+	__u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
 struct uhid_output_req {
 	__u8 data[UHID_DATA_MAX];
 	__u16 size;
@@ -100,6 +121,8 @@
 		struct uhid_output_ev_req output_ev;
 		struct uhid_feature_req feature;
 		struct uhid_feature_answer_req feature_answer;
+		struct uhid_create2_req create2;
+		struct uhid_input2_req input2;
 	} u;
 } __attribute__((__packed__));
 
diff --git a/src/adapter.c b/src/adapter.c
index f5f8c8c..b4878a3 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -6573,6 +6573,10 @@
 
 	if (adapter->current_settings & MGMT_SETTING_POWERED)
 		adapter_start(adapter);
+	else
+		set_mode(adapter, MGMT_OP_SET_POWERED, 0x01);
+
+	set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01);
 
 	return;
 
diff --git a/src/shared/crypto.c b/src/shared/crypto.c
index c438ab3..e2b0e7a 100644
--- a/src/shared/crypto.c
+++ b/src/shared/crypto.c
@@ -33,6 +33,10 @@
 #include "src/shared/util.h"
 #include "src/shared/crypto.h"
 
+#ifdef PF_ALG
+#undef PF_ALG
+#endif
+
 #ifndef PF_ALG
 #include <linux/types.h>
 
diff --git a/test/gfiber-agent b/test/gfiber-agent
new file mode 100755
index 0000000..2e66cbe
--- /dev/null
+++ b/test/gfiber-agent
@@ -0,0 +1,162 @@
+#!/usr/bin/python -u
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import dbus
+import dbus.exceptions
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BLUEZ_BUS = 'org.bluez'
+BLUEZ_ROOT_OBJ = '/org/bluez'
+AGENT_INTF = 'org.bluez.Agent1'
+AGENT_MGR_INTF = 'org.bluez.AgentManager1'
+DEVICE_INTF = 'org.bluez.Device1'
+OBJECT_MGR_INTF = 'org.freedesktop.DBus.ObjectManager'
+PROPERTY_INTF = 'org.freedesktop.DBus.Properties'
+AGENT_PATH = '/com/google/gfiber/agent'
+AGENT_CAPABILITY = 'NoInputNoOutput'
+DEV_NAME_GFRM100 = 'GFRM100'
+DEV_NAME_GFRM200 = 'GFRM200'
+DEV_NAME_TIARC = 'HID AdvRemote'
+DEV_OUI_GFRM100 = '00:24:1C'
+DEV_OUI_GFRM200 = '20:CD:39'
+DEV_OUI_TIARC = '90:59:AF'
+
+bus = None
+agent = None
+
+def dev_trusted(path):
+	obj = bus.get_object(BLUEZ_BUS, path)
+	props = dbus.Interface(obj, PROPERTY_INTF)
+	props.Set(DEVICE_INTF, "Trusted", True)
+
+class Rejected(dbus.DBusException):
+	_dbus_error_name = "org.bluez.Error.Rejected"
+
+class Agent(dbus.service.Object):
+	exit_on_release = True
+
+	def set_exit_on_release(self, exit_on_release):
+		self.exit_on_release = exit_on_release
+
+	@dbus.service.method(AGENT_INTF, in_signature="", out_signature="")
+	def Release(self):
+		print("Release")
+		if self.exit_on_release:
+			mainloop.quit()
+
+	@dbus.service.method(AGENT_INTF, in_signature="os", out_signature="")
+	def AuthorizeService(self, device, uuid):
+		print("AuthorizeService (%s, %s)" % (device, uuid))
+		raise Rejected("Not implemented")
+
+	@dbus.service.method(AGENT_INTF, in_signature="o", out_signature="s")
+	def RequestPinCode(self, device):
+		print("RequestPinCode (%s)" % (device))
+		dev_trusted(device)
+		return '0000'
+
+	@dbus.service.method(AGENT_INTF, in_signature="o", out_signature="u")
+	def RequestPasskey(self, device):
+		print("RequestPasskey (%s)" % (device))
+		dev_trusted(device)
+		return dbus.UInt32('0000')
+
+	@dbus.service.method(AGENT_INTF, in_signature="ouq", out_signature="")
+	def DisplayPasskey(self, device, passkey, entered):
+		print("DisplayPasskey (%s, %06u entered %u)" %
+						(device, passkey, entered))
+
+	@dbus.service.method(AGENT_INTF, in_signature="os", out_signature="")
+	def DisplayPinCode(self, device, pincode):
+		print("DisplayPinCode (%s, %s)" % (device, pincode))
+
+	@dbus.service.method(AGENT_INTF, in_signature="ou", out_signature="")
+	def RequestConfirmation(self, device, passkey):
+		print("RequestConfirmation (%s, %06d)" % (device, passkey))
+		raise Rejected("Not implemented")
+
+	@dbus.service.method(AGENT_INTF, in_signature="o", out_signature="")
+	def RequestAuthorization(self, device):
+		print("RequestAuthorization (%s)" % (device))
+		raise Rejected("Not implemented")
+
+	@dbus.service.method(AGENT_INTF, in_signature="", out_signature="")
+	def Cancel(self):
+		print("Cancel")
+
+def register_agent():
+	obj = bus.get_object(BLUEZ_BUS, BLUEZ_ROOT_OBJ)
+	mgr = dbus.Interface(obj, AGENT_MGR_INTF)
+	mgr.RegisterAgent(AGENT_PATH, AGENT_CAPABILITY)
+	mgr.RequestDefaultAgent(AGENT_PATH)
+	print("Agent registered")
+
+def unregister_agent():
+	obj = bus.get_object(BLUEZ_BUS, BLUEZ_ROOT_OBJ)
+	mgr = dbus.Interface(obj, AGENT_MGR_INTF)
+	mgr.UnregisterAgent(AGENT_PATH)
+	print("Agent unregistered")
+
+def dev_pair_and_connect(path):
+	obj = bus.get_object(BLUEZ_BUS, path)
+	dev = dbus.Interface(obj, DEVICE_INTF)
+	props = dbus.Interface(obj, PROPERTY_INTF)
+	paired = props.Get(DEVICE_INTF, "Paired")
+	if paired == True:
+		print("%s is already paired" % (path))
+		return
+	dev.Pair()
+	props.Set(DEVICE_INTF, "Trusted", True)
+	dev.Connect()
+
+def interfaces_added(path, interfaces):
+	if not DEVICE_INTF in interfaces:
+		return
+	obj = bus.get_object(BLUEZ_BUS, path)
+	props = dbus.Interface(obj, PROPERTY_INTF)
+	addr = props.Get(DEVICE_INTF, "Address")
+	try:
+		name = props.Get(DEVICE_INTF, "Name")
+	except dbus.exceptions.DBusException:
+		name = ''
+	print("Discovered %s [%s] [%s]" % (path, addr, name))
+
+	if (name == DEV_NAME_GFRM100 or
+	    name == DEV_NAME_GFRM200 or
+	    name == DEV_NAME_TIARC or
+	    addr.startswith(DEV_OUI_GFRM100) or
+	    addr.startswith(DEV_OUI_GFRM200) or
+	    addr.startswith(DEV_OUI_TIARC)):
+		print("Pair with  %s [%s] [%s]" % (path, addr, name))
+		dev_pair_and_connect(path)
+
+def main():
+	global bus
+	global agent
+
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+	agent = Agent(bus, AGENT_PATH)
+
+	register_agent()
+	bus.add_signal_receiver(interfaces_added, bus_name=BLUEZ_BUS,
+				dbus_interface=OBJECT_MGR_INTF,
+				signal_name="InterfacesAdded")
+
+	mainloop = GObject.MainLoop()
+	mainloop.run()
+
+	bus.remove_signal_receiver(interfaces_added, bus_name=BLUEZ_BUS,
+				   dbus_interface=OBJECT_MGR_INTF,
+				   signal_name="InterfacesAdded")
+	unregister_agent()
+
+if __name__ == '__main__':
+	main()
diff --git a/tools/hciconfig.c b/tools/hciconfig.c
index 765d980..1a2df42 100644
--- a/tools/hciconfig.c
+++ b/tools/hciconfig.c
@@ -1463,6 +1463,43 @@
 	}
 }
 
+static void cmd_page_type(int ctl, int hdev, char *opt)
+{
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (opt) {
+		uint8_t type = atoi(opt);
+
+		if (hci_write_page_scan_type(dd, type, 2000) < 0) {
+			fprintf(stderr, "Can't set page scan type on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint8_t type;
+
+		if (hci_read_page_scan_type(dd, &type, 1000) < 0) {
+			fprintf(stderr, "Can't read page scan type on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+
+		print_dev_hdr(&di);
+		printf("\tPage scan type: %s\n",
+			type == PAGE_SCAN_TYPE_INTERLACED ?
+			"Interlaced Page Scan" : "Standard Page Scan");
+	}
+
+	hci_close_dev(dd);
+}
+
 static void cmd_page_parms(int ctl, int hdev, char *opt)
 {
 	struct hci_request rq;
@@ -1932,6 +1969,7 @@
 	{ "inqdata",	cmd_inq_data,	"[data]",	"Get/Set inquiry data" },
 	{ "inqtype",	cmd_inq_type,	"[type]",	"Get/Set inquiry scan type" },
 	{ "inqparms",	cmd_inq_parms,	"[win:int]",	"Get/Set inquiry scan window and interval" },
+	{ "pagetype",	cmd_page_type,	"[type]",	"Get/Set page scan type" },
 	{ "pageparms",	cmd_page_parms,	"[win:int]",	"Get/Set page scan window and interval" },
 	{ "pageto",	cmd_page_to,	"[to]",		"Get/Set page timeout" },
 	{ "afhmode",	cmd_afh_mode,	"[mode]",	"Get/Set AFH mode" },