Add BT_POWER socket option.

BT_POWER socket option is used to control the power
characteristics of the underlying ACL link. Currently, this is
used to set the ACL link to active mode when the remote end has put
the ACL link in sniff mode and we are sending data. By default,
the ACL link will always be moved to active mode. However, this
causes problems with HID devices. And hence, for HID devices the
ACL link will not be moved to active mode when we are sending data.

This socket option is implemented for both rfcomm and l2cap sockets.

Change-Id: I0261a8daa2241d691670cc690cd27c39c67c1591
diff --git a/btio/btio.c b/btio/btio.c
index d8439e0..564aa4b 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -59,6 +59,7 @@
 	uint16_t omtu;
 	int master;
 	uint8_t mode;
+	uint8_t force_active;
 };
 
 struct connect {
@@ -374,6 +375,26 @@
 	return 0;
 }
 
+static gboolean set_force_active(int sock, BtIOType type, uint8_t force_active,
+					GError **err)
+{
+	struct bt_power pwr;
+
+	memset(&pwr, 0, sizeof(struct bt_power));
+	pwr.force_active = force_active;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_POWER, &pwr,
+							sizeof(pwr)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_POWER)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
 {
 	struct bt_security sec;
@@ -453,6 +474,27 @@
 	return 0;
 }
 
+static gboolean get_force_active(int sock, BtIOType type, uint8_t *force_active,
+							GError **err)
+{
+	struct bt_power pwr;
+	socklen_t len;
+
+	memset(&pwr, 0, sizeof(pwr));
+	len = sizeof(pwr);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_POWER, &pwr, &len) == 0) {
+		*force_active = pwr.force_active;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_POWER)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static gboolean get_sec_level(int sock, BtIOType type, int *level,
 								GError **err)
 {
@@ -486,7 +528,8 @@
 }
 
 static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
-					uint8_t mode, int master, GError **err)
+					uint8_t mode, int master, uint8_t force_active,
+					GError **err)
 {
 	if (imtu || omtu || mode) {
 		struct l2cap_options l2o;
@@ -522,6 +565,9 @@
 	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
 		return FALSE;
 
+	if (!set_force_active(sock, BT_IO_L2CAP, force_active, err))
+		return FALSE;
+
 	return TRUE;
 }
 
@@ -560,11 +606,15 @@
 	return 0;
 }
 
-static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+static gboolean rfcomm_set(int sock, int sec_level, int master,
+				uint8_t force_active, GError **err)
 {
 	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
 		return FALSE;
 
+	if (!set_force_active(sock, BT_IO_RFCOMM, force_active, err))
+		return FALSE;
+
 	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
 		ERROR_FAILED(err, "rfcomm_set_master", errno);
 		return FALSE;
@@ -643,6 +693,7 @@
 	opts->master = -1;
 	opts->sec_level = BT_IO_SEC_MEDIUM;
 	opts->mode = L2CAP_MODE_BASIC;
+	opts->force_active = 1;
 
 	while (opt != BT_IO_OPT_INVALID) {
 		switch (opt) {
@@ -698,6 +749,9 @@
 		case BT_IO_OPT_MODE:
 			opts->mode = va_arg(args, int);
 			break;
+		case BT_IO_OPT_POWER_ACTIVE:
+			opts->force_active = va_arg(args, int);
+			break;
 		default:
 			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
 					"Unknown option %d", opt);
@@ -842,6 +896,11 @@
 		case BT_IO_OPT_MODE:
 			*(va_arg(args, uint8_t *)) = l2o.mode;
 			break;
+		case BT_IO_OPT_POWER_ACTIVE:
+			if (!get_force_active(sock, BT_IO_L2CAP,
+						va_arg(args, uint8_t *), err))
+				return FALSE;
+			break;
 		default:
 			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
 					"Unknown option %d", opt);
@@ -949,6 +1008,11 @@
 			}
 			memcpy(va_arg(args, uint8_t *), dev_class, 3);
 			break;
+		case BT_IO_OPT_POWER_ACTIVE:
+			if (!get_force_active(sock, BT_IO_RFCOMM,
+						va_arg(args, uint8_t *), err))
+				return FALSE;
+			break;
 		default:
 			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
 					"Unknown option %d", opt);
@@ -1115,9 +1179,11 @@
 	case BT_IO_L2RAW:
 	case BT_IO_L2CAP:
 		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
+						opts.mode, opts.master, opts.force_active,
+						err);
 	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+		return rfcomm_set(sock, opts.sec_level, opts.master, opts.force_active,
+				err);
 	case BT_IO_SCO:
 		return sco_set(sock, opts.mtu, err);
 	}
@@ -1156,7 +1222,8 @@
 		if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
 							opts->cid, err) < 0)
 			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, opts->force_active,
+					err))
 			goto failed;
 		break;
 	case BT_IO_L2CAP:
@@ -1169,7 +1236,8 @@
 							opts->cid, err) < 0)
 			goto failed;
 		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
-						opts->mode, opts->master, err))
+						opts->mode, opts->master,
+						opts->force_active, err))
 			goto failed;
 		break;
 	case BT_IO_RFCOMM:
@@ -1181,7 +1249,8 @@
 		if (rfcomm_bind(sock, &opts->src,
 					server ? opts->channel : 0, err) < 0)
 			goto failed;
-		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+		if (!rfcomm_set(sock, opts->sec_level, opts->master,
+					opts->force_active, err))
 			goto failed;
 		break;
 	case BT_IO_SCO:
diff --git a/btio/btio.h b/btio/btio.h
index 53e8eaa..a57360f 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -64,6 +64,7 @@
 	BT_IO_OPT_HANDLE,
 	BT_IO_OPT_CLASS,
 	BT_IO_OPT_MODE,
+	BT_IO_OPT_POWER_ACTIVE,
 } BtIOOption;
 
 typedef enum {
diff --git a/input/device.c b/input/device.c
index 554f5ac..6c2e0f3 100644
--- a/input/device.c
+++ b/input/device.c
@@ -374,6 +374,7 @@
 				NULL, err,
 				BT_IO_OPT_SOURCE_BDADDR, &idev->src,
 				BT_IO_OPT_DEST_BDADDR, &idev->dst,
+				BT_IO_OPT_POWER_ACTIVE, 0,
 				BT_IO_OPT_INVALID);
 	if (!io)
 		return FALSE;
@@ -868,6 +869,7 @@
 				BT_IO_OPT_DEST_BDADDR, &idev->dst,
 				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
 				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_POWER_ACTIVE, 0,
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("%s", err->message);
@@ -948,6 +950,7 @@
 					BT_IO_OPT_DEST_BDADDR, &idev->dst,
 					BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
 					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_POWER_ACTIVE, 0,
 					BT_IO_OPT_INVALID);
 		iconn->ctrl_io = io;
 	}
diff --git a/input/server.c b/input/server.c
index d98018b..67e4f8b 100644
--- a/input/server.c
+++ b/input/server.c
@@ -188,6 +188,7 @@
 				BT_IO_OPT_SOURCE_BDADDR, src,
 				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
 				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_POWER_ACTIVE, 0,
 				BT_IO_OPT_INVALID);
 	if (!server->ctrl) {
 		error("Failed to listen on control channel");
@@ -201,6 +202,7 @@
 				BT_IO_OPT_SOURCE_BDADDR, src,
 				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
 				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_POWER_ACTIVE, 0,
 				BT_IO_OPT_INVALID);
 	if (!server->intr) {
 		error("Failed to listen on interrupt channel");
diff --git a/lib/bluetooth/bluetooth.h b/lib/bluetooth/bluetooth.h
index bc0921e..997d3fd 100644
--- a/lib/bluetooth/bluetooth.h
+++ b/lib/bluetooth/bluetooth.h
@@ -75,6 +75,11 @@
 #define BT_FLUSHABLE_OFF	0
 #define BT_FLUSHABLE_ON		1
 
+#define BT_POWER	9
+struct bt_power {
+	uint8_t force_active;
+};
+
 /* Connection and socket states */
 enum {
 	BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */