--- drivers/hid/hid-sony.c.orig	2014-11-05 20:32:47.387599486 -0500
+++ drivers/hid/hid-sony.c	2014-11-14 20:35:28.327053036 -0500
@@ -8,6 +8,7 @@
  *  Copyright (c) 2012 David Dillow <dave@thedillows.org>
  *  Copyright (c) 2006-2013 Jiri Kosina
  *  Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
+ *  Copyright (c) 2014 Brian S. Julin <bri@abrij.org>
  */
 
 /*
@@ -35,6 +36,9 @@
 #include <linux/list.h>
 #include <linux/idr.h>
 #include <linux/input/mt.h>
+#include <linux/ctype.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
 
 #include "hid-ids.h"
 
@@ -750,7 +754,7 @@ union sixaxis_output_report_01 {
 
 static spinlock_t sony_dev_list_lock;
 static LIST_HEAD(sony_device_list);
-static DEFINE_IDA(sony_device_id_allocator);
+static DEFINE_IDA(sony_gamepad_number_allocator);
 
 struct sony_sc {
 	spinlock_t lock;
@@ -760,7 +764,7 @@ struct sony_sc {
 	unsigned long quirks;
 	struct work_struct state_worker;
 	struct power_supply battery;
-	int device_id;
+	int gamepad_number;
 
 #ifdef CONFIG_SONY_FF
 	__u8 left;
@@ -1321,7 +1325,8 @@ static int sony_leds_init(struct sony_sc
 		if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
 			return -ENODEV;
 	} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
-		dualshock4_set_leds_from_id(sc->device_id, initial_values);
+		dualshock4_set_leds_from_id(sc->gamepad_number,
+					    initial_values);
 		initial_values[3] = 1;
 		sc->led_count = 4;
 		memset(max_brightness, 255, 3);
@@ -1330,7 +1335,7 @@ static int sony_leds_init(struct sony_sc
 		name_len = 0;
 		name_fmt = "%s:%s";
 	} else {
-		sixaxis_set_leds_from_id(sc->device_id, initial_values);
+		sixaxis_set_leds_from_id(sc->gamepad_number, initial_values);
 		sc->led_count = 4;
 		memset(use_hw_blink, 1, 4);
 		use_ds4_names = 0;
@@ -1758,38 +1763,209 @@ static int sony_check_add(struct sony_sc
 	return sony_check_add_dev_list(sc);
 }
 
-static int sony_set_device_id(struct sony_sc *sc)
+/* PS3 pads do not pair over bluetooth; Instead a USB handshake is used. */
+static ssize_t sony_master_bdaddr_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct sony_sc *sc = hid_get_drvdata(hdev);
+	size_t count;
+	unsigned char msg[8];
+	int ret;
+
+	if (!sc) {
+		hid_err(hdev, "No device data\n");
+		return 0;
+	}
+
+	if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+		struct device *parent;
+
+		parent = dev->parent;
+		if (!parent)
+			return 0;
+		parent = parent->parent;
+		if (!parent || !parent->type
+		    || strcmp(parent->type->name, "host"))
+			return 0;
+
+		count = scnprintf(buf, PAGE_SIZE, "%pMR\n",
+				  &(to_hci_dev(parent)->bdaddr));
+		return count;
+	}
+
+	if (!(sc->quirks & SIXAXIS_CONTROLLER_USB))
+		return 0;
+
+	ret = hid_hw_raw_request(hdev, 0xf5, msg, sizeof(msg),
+				 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+	if (ret < 0) {
+		/* This matches bluez error message */
+		hid_err(hdev, "failed to read master address\n");
+		return 0;
+	}
+
+	count = scnprintf(buf, PAGE_SIZE, "%pM\n", msg + 2);
+	return count;
+}
+
+/*
+ * This could probably be useful in lib/.
+ *
+ * Convert a string to a MAC address, accepting multiple formats.
+ *
+ * Will take most commonly cut-and-pasted formats without being too liberal.
+ * Accepts any hex with delims [-:.] between any octets, no mixing delims,
+ * no splitting octets.
+ *
+ * *buf should have at least 6 bytes of space and will not be altered on fail
+ * Will halt at null terminator or before reading more than lim chars from *c
+ * Return code is sscanf-style: number of non-null bytes read, or 0 on failure
+ */
+static int sony_kstrntomac(const char *c, u8 *buf, size_t lim)
+{
+	int cnt = 0;
+	int dcnt = 0;
+	char delim = '\0';
+	u8 octet[6];
+
+	while (cnt < 6) {
+		if (lim < 2 || !isxdigit(*c) || !isxdigit(*(c+1)))
+			return 0;
+
+		lim -= 2;
+
+		if (isdigit(*c))
+			octet[cnt] = (*c - '0') << 4;
+		else
+			octet[cnt] = (tolower(*c) - 'a' + 10) << 4;
+		c++;
+
+		if (isdigit(*c))
+			octet[cnt] |= *c - '0';
+		else
+			octet[cnt] |= tolower(*c) - 'a' + 10;
+		c++;
+
+		if (cnt < 5) {
+			if (!lim)
+				return 0;
+			if (!delim && (*c == '-' || *c == ':' || *c == '.'))
+				delim = *c;
+			if (delim && *c == delim) {
+				dcnt++;
+				c++;
+				lim--;
+			}
+		}
+		cnt++;
+	}
+
+	memcpy(buf, octet, 6);
+
+	return cnt * 2 + dcnt;
+}
+
+static ssize_t sony_master_bdaddr_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct sony_sc *sc = hid_get_drvdata(hdev);
+	u8 msg[8];
+	int ret;
+
+	if (!sc) {
+		hid_err(hdev, "No device data\n");
+		return -EBADFD;
+	}
+
+	if (!(sc->quirks & SIXAXIS_CONTROLLER_USB)) {
+		if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+			hid_err(hdev,
+				"To set master disconnect BT then use USB\n");
+			return -EOPNOTSUPP;
+		}
+		return -ENOTSUPP;
+	}
+
+	msg[0] = 0x01;
+	msg[1] = 0x00;
+
+	if (!sony_kstrntomac(buf, msg + 2, count))
+		return -EINVAL;
+
+	ret = hid_hw_raw_request(hdev, 0xf5, msg, sizeof(msg),
+				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+	if (ret < 0) {
+		/* This matches bluez error message */
+		hid_err(hdev, "failed to write master address\n");
+		return count;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(master_bdaddr, S_IRUGO | S_IWUSR,
+		   sony_master_bdaddr_show, sony_master_bdaddr_store);
+
+static int sony_set_gamepad_number(struct sony_sc *sc)
 {
 	int ret;
 
 	/*
-	 * Only DualShock 4 or Sixaxis controllers get an id.
+	 * Only DualShock 4 or Sixaxis controllers get a number.
 	 * All others are set to -1.
 	 */
 	if ((sc->quirks & SIXAXIS_CONTROLLER) ||
 	    (sc->quirks & DUALSHOCK4_CONTROLLER)) {
-		ret = ida_simple_get(&sony_device_id_allocator, 0, 0,
-					GFP_KERNEL);
+		ret = ida_simple_get(&sony_gamepad_number_allocator, 0, 0,
+				     GFP_KERNEL);
 		if (ret < 0) {
-			sc->device_id = -1;
+			sc->gamepad_number = -1;
 			return ret;
 		}
-		sc->device_id = ret;
+		sc->gamepad_number = ret;
 	} else {
-		sc->device_id = -1;
+		sc->gamepad_number = -1;
 	}
 
 	return 0;
 }
 
-static void sony_release_device_id(struct sony_sc *sc)
+static void sony_release_gamepad_number(struct sony_sc *sc)
 {
-	if (sc->device_id >= 0) {
-		ida_simple_remove(&sony_device_id_allocator, sc->device_id);
-		sc->device_id = -1;
+	if (sc->gamepad_number >= 0) {
+		ida_simple_remove(&sony_gamepad_number_allocator,
+				  sc->gamepad_number);
+		sc->gamepad_number = -1;
 	}
 }
 
+static ssize_t sony_gamepad_number_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct sony_sc *sc = hid_get_drvdata(hdev);
+	size_t count;
+
+	if (!sc) {
+		hid_err(hdev, "No device data\n");
+		return 0;
+	}
+
+	if (sc->gamepad_number < 0)
+		return 0;
+
+	/* Add one to match console user expectations */
+	count = scnprintf(buf, PAGE_SIZE, "%i\n", sc->gamepad_number + 1);
+	return count;
+}
+
+static DEVICE_ATTR(gamepad_number, S_IRUGO, sony_gamepad_number_show, NULL);
+
 static inline void sony_init_work(struct sony_sc *sc,
 					void (*worker)(struct work_struct *))
 {
@@ -1841,10 +2017,22 @@ static int sony_probe(struct hid_device
 		return ret;
 	}
 
-	ret = sony_set_device_id(sc);
+	ret = sony_set_gamepad_number(sc);
 	if (ret < 0) {
-		hid_err(hdev, "failed to allocate the device id\n");
-		goto err_stop;
+		hid_err(hdev, "failed to allocate the gamepad number\n");
+		goto err_nosysfs2;
+	}
+
+	ret = device_create_file(&hdev->dev, &dev_attr_gamepad_number);
+	if (ret) {
+		hid_err(hdev, "failed to create gamepad_number sysfs file\n");
+		goto err_nosysfs2;
+	}
+
+	ret = device_create_file(&hdev->dev, &dev_attr_master_bdaddr);
+	if (ret) {
+		hid_err(hdev, "failed to create master_bdaddr sysfs file\n");
+		goto err_nosysfs1;
 	}
 
 	if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
@@ -1932,13 +2120,17 @@ static int sony_probe(struct hid_device
 err_close:
 	hid_hw_close(hdev);
 err_stop:
+	device_remove_file(&hdev->dev, &dev_attr_master_bdaddr);
+err_nosysfs1:
+	device_remove_file(&hdev->dev, &dev_attr_gamepad_number);
+err_nosysfs2:
 	if (sc->quirks & SONY_LED_SUPPORT)
 		sony_leds_remove(sc);
 	if (sc->quirks & SONY_BATTERY_SUPPORT)
 		sony_battery_remove(sc);
 	sony_cancel_work_sync(sc);
 	sony_remove_dev_list(sc);
-	sony_release_device_id(sc);
+	sony_release_gamepad_number(sc);
 	hid_hw_stop(hdev);
 	return ret;
 }
@@ -1959,7 +2151,11 @@ static void sony_remove(struct hid_devic
 
 	sony_remove_dev_list(sc);
 
-	sony_release_device_id(sc);
+	device_remove_file(&hdev->dev, &dev_attr_master_bdaddr);
+
+	device_remove_file(&hdev->dev, &dev_attr_gamepad_number);
+
+	sony_release_gamepad_number(sc);
 
 	hid_hw_stop(hdev);
 }
@@ -2017,7 +2213,7 @@ static void __exit sony_exit(void)
 {
 	dbg_hid("Sony:%s\n", __func__);
 
-	ida_destroy(&sony_device_id_allocator);
+	ida_destroy(&sony_gamepad_number_allocator);
 	hid_unregister_driver(&sony_driver);
 }
 module_init(sony_init);
