Merge changes I29417600,Ic3b53ed5 into msm-3.0
* changes:
msm: acpuclock-8960: Use VDD dig corner regulator for 8930 and 8627
msm: board-8930: Add new VDD_dig voltage corner regulator
diff --git a/Documentation/devicetree/bindings/resource-names.txt b/Documentation/devicetree/bindings/resource-names.txt
new file mode 100644
index 0000000..e280fef
--- /dev/null
+++ b/Documentation/devicetree/bindings/resource-names.txt
@@ -0,0 +1,54 @@
+Some properties contain an ordered list of 1 or more datum which are
+normally accessed by index. However, some devices will have multiple
+values which are more naturally accessed by name. Device nodes can
+include a supplemental property for assigning names to each of the list
+items. The names property consists of a list of strings in the same
+order as the data in the resource property.
+
+The following supplemental names properties are defined.
+
+Resource Property Supplemental Names Property
+----------------- ---------------------------
+reg reg-names
+clocks clock-names
+interrupts interrupt-names
+
+Usage:
+
+The -names property must be used in conjunction with the normal resource
+property. If not it will be ignored.
+
+Examples:
+
+l4-abe {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0x48000000 0x00001000>, /* MPU path */
+ <1 0 0x49000000 0x00001000>; /* L3 path */
+ mcasp {
+ compatible = "ti,mcasp";
+ reg = <0 0x10 0x10>, <0 0x20 0x10>,
+ <1 0x10 0x10>, <1 0x20 0x10>;
+ reg-names = "mpu", "dat",
+ "dma", "dma_dat";
+ interrupts = <11>, <12>;
+ interrupt-names = "rx", "tx";
+ };
+
+ timer {
+ compatible = "ti,timer";
+ reg = <0 0x40 0x10>, <1 0x40 0x10>;
+ reg-names = "mpu", "dma";
+ };
+};
+
+
+usb {
+ compatible = "ti,usb-host";
+ reg = <0x4a064000 0x800>, <0x4a064800 0x200>,
+ <0x4a064c00 0x200>;
+ reg-names = "config", "ohci", "ehci";
+ interrupts = <14>, <15>;
+ interrupt-names = "ohci", "ehci";
+};
diff --git a/arch/arm/configs/msm-copper_defconfig b/arch/arm/configs/msm-copper_defconfig
index e9479c6..f1fe039 100644
--- a/arch/arm/configs/msm-copper_defconfig
+++ b/arch/arm/configs/msm-copper_defconfig
@@ -94,6 +94,7 @@
CONFIG_INPUT_UINPUT=y
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_DCC_TTY=y
CONFIG_SPMI=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index b6c56b6..2a9f4a0 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -293,6 +293,9 @@
CONFIG_SND_SOC=y
CONFIG_SND_MSM7KV2_SOC=y
CONFIG_SND_MVS_SOC=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index d12df73..ebbfb0b 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -299,6 +299,8 @@
CONFIG_SENSORS_PM8XXX_ADC=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8960=y
+CONFIG_THERMAL_PM8XXX=y
+CONFIG_THERMAL_MONITOR=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
diff --git a/arch/arm/mach-msm/board-8064-camera.c b/arch/arm/mach-msm/board-8064-camera.c
index c714bc8..d335978 100644
--- a/arch/arm/mach-msm/board-8064-camera.c
+++ b/arch/arm/mach-msm/board-8064-camera.c
@@ -530,8 +530,10 @@
msm_gpiomux_install(apq8064_cam_common_configs,
ARRAY_SIZE(apq8064_cam_common_configs));
- if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid())
+ if (machine_is_apq8064_cdp())
sensor_board_info_imx074.mount_angle = 0;
+ else if (machine_is_apq8064_liquid())
+ sensor_board_info_imx074.mount_angle = 180;
platform_device_register(&msm8960_device_i2c_mux_gsbi4);
platform_device_register(&msm8960_device_csiphy0);
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index 75160ef..4b4f32a 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -125,7 +125,6 @@
PM8921_GPIO_INPUT(38, PM_GPIO_PULL_UP_1P5),
/* TABLA CODEC RESET */
PM8921_GPIO_OUTPUT(34, 1, MED),
- PM8921_GPIO_INPUT(17, PM_GPIO_PULL_UP_1P5), /* SD_WP */
};
static struct pm8xxx_gpio_init pm8921_mtp_kp_gpios[] __initdata = {
@@ -136,6 +135,7 @@
static struct pm8xxx_gpio_init pm8921_cdp_kp_gpios[] __initdata = {
PM8921_GPIO_INPUT(37, PM_GPIO_PULL_UP_1P5),
PM8921_GPIO_INPUT(42, PM_GPIO_PULL_UP_1P5),
+ PM8921_GPIO_INPUT(17, PM_GPIO_PULL_UP_1P5), /* SD_WP */
};
/* Initial PM8XXX MPP configurations */
diff --git a/arch/arm/mach-msm/board-8064-storage.c b/arch/arm/mach-msm/board-8064-storage.c
index b8cae49..b52004a 100644
--- a/arch/arm/mach-msm/board-8064-storage.c
+++ b/arch/arm/mach-msm/board-8064-storage.c
@@ -273,6 +273,10 @@
if (apq8064_sdc3_pdata) {
apq8064_sdc3_pdata->swfi_latency =
apq8064_rpm_get_swfi_latency();
+ if (!machine_is_apq8064_cdp()) {
+ apq8064_sdc3_pdata->wpswitch_gpio = 0;
+ apq8064_sdc3_pdata->wpswitch_polarity = 0;
+ }
apq8064_add_sdcc(3, apq8064_sdc3_pdata);
}
}
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index 793f063..c3331f8 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -1879,6 +1879,7 @@
switch (opcode) {
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ):
+ case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
hci_cc_fm_enable_rsp(hdev, skb);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ):
@@ -1886,6 +1887,7 @@
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ):
+ case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
hci_cc_fm_disable_rsp(hdev, skb);
break;
@@ -1900,8 +1902,6 @@
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_CH_DET_THRESHOLD):
- case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
- case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_RT_REQ):
case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_PS_REQ):
case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
diff --git a/drivers/media/video/msm/wfd/wfd-ioctl.c b/drivers/media/video/msm/wfd/wfd-ioctl.c
index e38bb80..fd6e6e7 100644
--- a/drivers/media/video/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm/wfd/wfd-ioctl.c
@@ -388,6 +388,8 @@
struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
int rc = 0;
+ WFD_MSG_ERR("Stream on called\n");
+ WFD_MSG_DBG("enc start\n");
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
ENCODE_START, (void *)inst->venc_inst);
if (rc) {
@@ -395,6 +397,7 @@
goto subdev_start_fail;
}
+ WFD_MSG_DBG("vsg start\n");
rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
VSG_START, NULL);
if (rc) {
@@ -408,6 +411,7 @@
rc = PTR_ERR(inst->mdp_task);
goto subdev_start_fail;
}
+ WFD_MSG_DBG("mdp start\n");
rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
MDP_START, (void *)inst->mdp_inst);
if (rc)
@@ -423,17 +427,20 @@
(struct wfd_device *)video_drvdata(priv_data);
struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
int rc = 0;
+ WFD_MSG_DBG("mdp stop\n");
rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
MDP_STOP, (void *)inst->mdp_inst);
if (rc)
WFD_MSG_ERR("Failed to stop MDP\n");
+ WFD_MSG_DBG("vsg stop\n");
rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
VSG_STOP, NULL);
if (rc)
WFD_MSG_ERR("Failed to stop VSG\n");
kthread_stop(inst->mdp_task);
+ WFD_MSG_DBG("enc stop\n");
rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
ENCODE_STOP, (void *)inst->venc_inst);
if (rc)
diff --git a/drivers/media/video/msm/wfd/wfd-util.h b/drivers/media/video/msm/wfd/wfd-util.h
index 3fe03e6..06dbffa 100644
--- a/drivers/media/video/msm/wfd/wfd-util.h
+++ b/drivers/media/video/msm/wfd/wfd-util.h
@@ -20,12 +20,11 @@
#ifdef DEBUG_WFD
#define WFD_MSG_INFO(fmt...) pr_info(WFD_TAG fmt)
#define WFD_MSG_WARN(fmt...) pr_warning(WFD_TAG fmt)
- #define WFD_MSG_DBG(fmt...) pr_debug(WFD_TAG fmt)
#else
#define WFD_MSG_INFO(fmt...)
#define WFD_MSG_WARN(fmt...)
- #define WFD_MSG_DBG(fmt...)
#endif
#define WFD_MSG_ERR(fmt...) pr_err(KERN_ERR WFD_TAG fmt)
#define WFD_MSG_CRIT(fmt...) pr_crit(KERN_CRIT WFD_TAG fmt)
+ #define WFD_MSG_DBG(fmt...) pr_debug(WFD_TAG fmt)
#endif
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 495a986..ba00d5e 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -486,23 +486,6 @@
if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
return -EFAULT;
- if (qseecom.qseos_version == QSEOS_VERSION_13) {
- long pil_error;
- mutex_lock(&pil_access_lock);
- if (pil_ref_cnt == 0) {
- pil = pil_get("tzapps");
- if (IS_ERR(pil)) {
- pr_err("Playready PIL image load failed\n");
- pil_error = PTR_ERR(pil);
- pil = NULL;
- pr_debug("tzapps image load FAILED\n");
- mutex_unlock(&pil_access_lock);
- return pil_error;
- }
- }
- pil_ref_cnt++;
- mutex_unlock(&pil_access_lock);
- }
/* Get the handle of the shared fd */
data->client.ihandle = ion_import_fd(qseecom.ion_clnt, req.ifd_data_fd);
if (IS_ERR_OR_NULL(data->client.ihandle)) {
@@ -789,11 +772,6 @@
break;
}
}
- mutex_lock(&pil_access_lock);
- if (pil_ref_cnt == 1)
- pil_put(pil);
- pil_ref_cnt--;
- mutex_unlock(&pil_access_lock);
}
data->released = true;
return ret;
@@ -1330,6 +1308,23 @@
data->released = false;
init_waitqueue_head(&data->abort_wq);
atomic_set(&data->ioctl_count, 0);
+ if (qseecom.qseos_version == QSEOS_VERSION_13) {
+ int pil_error;
+ mutex_lock(&pil_access_lock);
+ if (pil_ref_cnt == 0) {
+ pil = pil_get("tzapps");
+ if (IS_ERR(pil)) {
+ pr_err("Playready PIL image load failed\n");
+ pil_error = PTR_ERR(pil);
+ pil = NULL;
+ pr_debug("tzapps image load FAILED\n");
+ mutex_unlock(&pil_access_lock);
+ return pil_error;
+ }
+ }
+ pil_ref_cnt++;
+ mutex_unlock(&pil_access_lock);
+ }
return ret;
}
@@ -1349,6 +1344,13 @@
return ret;
}
}
+ if (qseecom.qseos_version == QSEOS_VERSION_13) {
+ mutex_lock(&pil_access_lock);
+ if (pil_ref_cnt == 1)
+ pil_put(pil);
+ pil_ref_cnt--;
+ mutex_unlock(&pil_access_lock);
+ }
kfree(data);
qsee_disable_clock_vote();
diff --git a/drivers/of/address.c b/drivers/of/address.c
index da1f4b9..e29a5cf 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -14,7 +14,7 @@
static struct of_bus *of_match_bus(struct device_node *np);
static int __of_address_to_resource(struct device_node *dev,
const __be32 *addrp, u64 size, unsigned int flags,
- struct resource *r);
+ const char *name, struct resource *r);
/* Debug utility */
#ifdef DEBUG
@@ -215,7 +215,7 @@
addrp = of_get_pci_address(dev, bar, &size, &flags);
if (addrp == NULL)
return -EINVAL;
- return __of_address_to_resource(dev, addrp, size, flags, r);
+ return __of_address_to_resource(dev, addrp, size, flags, NULL, r);
}
EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
#endif /* CONFIG_PCI */
@@ -529,7 +529,7 @@
static int __of_address_to_resource(struct device_node *dev,
const __be32 *addrp, u64 size, unsigned int flags,
- struct resource *r)
+ const char *name, struct resource *r)
{
u64 taddr;
@@ -551,7 +551,8 @@
r->end = taddr + size - 1;
}
r->flags = flags;
- r->name = dev->full_name;
+ r->name = name ? name : dev->full_name;
+
return 0;
}
@@ -569,11 +570,16 @@
const __be32 *addrp;
u64 size;
unsigned int flags;
+ const char *name = NULL;
addrp = of_get_address(dev, index, &size, &flags);
if (addrp == NULL)
return -EINVAL;
- return __of_address_to_resource(dev, addrp, size, flags, r);
+
+ /* Get optional "reg-names" property to add a name to a resource */
+ of_property_read_string_index(dev, "reg-names", index, &name);
+
+ return __of_address_to_resource(dev, addrp, size, flags, name, r);
}
EXPORT_SYMBOL_GPL(of_address_to_resource);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 02ed367..39bcf16 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -661,6 +661,90 @@
EXPORT_SYMBOL_GPL(of_property_read_string);
/**
+ * of_property_read_string_index - Find and read a string from a multiple
+ * strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @index: index of the string in the list of strings
+ * @out_string: pointer to null terminated return string, modified only if
+ * return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy) in the list of strings
+ * contained in that property.
+ * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
+ * property does not have a value, and -EILSEQ if the string is not
+ * null-terminated within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+int of_property_read_string_index(struct device_node *np, const char *propname,
+ int index, const char **output)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+ int i = 0;
+ size_t l = 0, total = 0;
+ const char *p;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ if (strnlen(prop->value, prop->length) >= prop->length)
+ return -EILSEQ;
+
+ p = prop->value;
+
+ for (i = 0; total < prop->length; total += l, p += l) {
+ l = strlen(p) + 1;
+ if ((*p != 0) && (i++ == index)) {
+ *output = p;
+ return 0;
+ }
+ }
+ return -ENODATA;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string_index);
+
+
+/**
+ * of_property_count_strings - Find and return the number of strings from a
+ * multiple strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ *
+ * Search for a property in a device tree node and retrieve the number of null
+ * terminated string contain in it. Returns the number of strings on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ */
+int of_property_count_strings(struct device_node *np, const char *propname)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+ int i = 0;
+ size_t l = 0, total = 0;
+ const char *p;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ if (strnlen(prop->value, prop->length) >= prop->length)
+ return -EILSEQ;
+
+ p = prop->value;
+
+ for (i = 0; total < prop->length; total += l, p += l) {
+ l = strlen(p) + 1;
+ if (*p != 0)
+ i++;
+ }
+ return i;
+}
+EXPORT_SYMBOL_GPL(of_property_count_strings);
+
+/**
* of_parse_phandle - Resolve a phandle property to a device_node pointer
* @np: Pointer to device node holding phandle property
* @phandle_name: Name of property holding a phandle value
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 19c0115..8a31313 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -346,9 +346,18 @@
/* Only dereference the resource if both the
* resource and the irq are valid. */
if (r && irq != NO_IRQ) {
+ const char *name = NULL;
+
+ /*
+ * Get optional "interrupts-names" property to add a name
+ * to the resource.
+ */
+ of_property_read_string_index(dev, "interrupt-names", index,
+ &name);
+
r->start = r->end = irq;
r->flags = IORESOURCE_IRQ;
- r->name = dev->full_name;
+ r->name = name ? name : dev->full_name;
}
return irq;
diff --git a/include/linux/of.h b/include/linux/of.h
index f904bf4..b904f7e 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -202,6 +202,11 @@
extern int of_property_read_string(struct device_node *np, char *propname,
const char **out_string);
+extern int of_property_read_string_index(struct device_node *np,
+ const char *propname,
+ int index, const char **output);
+extern int of_property_count_strings(struct device_node *np,
+ const char *propname);
extern int of_device_is_compatible(const struct device_node *device,
const char *);
extern int of_device_is_available(const struct device_node *device);
@@ -263,6 +268,19 @@
return -ENOSYS;
}
+static inline int of_property_read_string_index(struct device_node *np,
+ const char *propname, int index,
+ const char **out_string)
+{
+ return -ENOSYS;
+}
+
+static inline int of_property_count_strings(struct device_node *np,
+ const char *propname)
+{
+ return -ENOSYS;
+}
+
static inline const void *of_get_property(const struct device_node *node,
const char *name,
int *lenp)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index cd85855..eb89f4b 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -869,6 +869,17 @@
__le16 num_blocks;
} __packed;
+#define HCI_OP_READ_RSSI 0x1405
+struct hci_cp_read_rssi {
+ __le16 handle;
+} __packed;
+
+struct hci_rp_read_rssi {
+ __u8 status;
+ __le16 handle;
+ __s8 rssi;
+} __packed;
+
#define HCI_OP_READ_LOCAL_AMP_INFO 0x1409
struct hci_rp_read_local_amp_info {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 3ca14ff..4c86f24 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -310,12 +310,17 @@
__u8 remote_oob;
__u8 remote_auth;
+ __s8 rssi_threshold;
+ __u16 rssi_update_interval;
+ __u8 rssi_update_thresh_exceed;
+
unsigned int sent;
struct sk_buff_head data_q;
struct timer_list disc_timer;
struct timer_list idle_timer;
+ struct delayed_work rssi_update_work;
struct work_struct work_add;
struct work_struct work_del;
@@ -607,6 +612,10 @@
void hci_conn_hold_device(struct hci_conn *conn);
void hci_conn_put_device(struct hci_conn *conn);
+void hci_conn_set_rssi_reporter(struct hci_conn *conn,
+ s8 rssi_threshold, u16 interval, u8 updateOnThreshExceed);
+void hci_conn_unset_rssi_reporter(struct hci_conn *conn);
+
static inline void hci_conn_hold(struct hci_conn *conn)
{
atomic_inc(&conn->refcnt);
@@ -1039,6 +1048,8 @@
u8 status);
int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir);
+void mgmt_read_rssi_complete(u16 index, s8 rssi, bdaddr_t *bdaddr,
+ u16 handle, u8 status);
int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name);
void mgmt_inquiry_started(u16 index);
void mgmt_inquiry_complete_evt(u16 index, u8 status);
@@ -1090,4 +1101,6 @@
void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
void hci_le_ltk_neg_reply(struct hci_conn *conn);
+void hci_read_rssi(struct hci_conn *conn);
+
#endif /* __HCI_CORE_H */
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 9c089e0..e34c425 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -233,6 +233,19 @@
__u8 enable;
} __packed;
+#define MGMT_OP_SET_RSSI_REPORTER 0x0022
+struct mgmt_cp_set_rssi_reporter {
+ bdaddr_t bdaddr;
+ __s8 rssi_threshold;
+ __le16 interval;
+ __u8 updateOnThreshExceed;
+} __packed;
+
+#define MGMT_OP_UNSET_RSSI_REPORTER 0x0023
+struct mgmt_cp_unset_rssi_reporter {
+ bdaddr_t bdaddr;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
@@ -360,3 +373,9 @@
bdaddr_t bdaddr;
uint8_t features[8];
} __packed;
+
+#define MGMT_EV_RSSI_UPDATE 0x0020
+struct mgmt_ev_rssi_update {
+ bdaddr_t bdaddr;
+ __s8 rssi;
+} __packed;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index a8e5af9..2854395 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -225,6 +225,18 @@
}
EXPORT_SYMBOL(hci_le_conn_update);
+void hci_read_rssi(struct hci_conn *conn)
+{
+ struct hci_cp_read_rssi cp;
+ struct hci_dev *hdev = conn->hdev;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cpu_to_le16(conn->handle);
+
+ hci_send_cmd(hdev, HCI_OP_READ_RSSI, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_read_rssi);
+
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
__u8 ltk[16])
{
@@ -340,6 +352,18 @@
hci_conn_enter_sniff_mode(conn);
}
+static void hci_conn_rssi_update(struct work_struct *work)
+{
+ struct delayed_work *delayed =
+ container_of(work, struct delayed_work, work);
+ struct hci_conn *conn =
+ container_of(delayed, struct hci_conn, rssi_update_work);
+
+ BT_DBG("conn %p mode %d", conn, conn->mode);
+
+ hci_read_rssi(conn);
+}
+
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type,
__u16 pkt_type, bdaddr_t *dst)
{
@@ -392,6 +416,7 @@
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
+ INIT_DELAYED_WORK(&conn->rssi_update_work, hci_conn_rssi_update);
atomic_set(&conn->refcnt, 0);
@@ -434,6 +459,7 @@
del_timer(&conn->idle_timer);
del_timer(&conn->disc_timer);
del_timer(&conn->smp_timer);
+ __cancel_delayed_work(&conn->rssi_update_work);
if (conn->type == ACL_LINK) {
struct hci_conn *sco = conn->link;
@@ -900,6 +926,43 @@
jiffies + msecs_to_jiffies(hdev->idle_timeout));
}
+static inline void hci_conn_stop_rssi_timer(struct hci_conn *conn)
+{
+ BT_DBG("conn %p", conn);
+ cancel_delayed_work(&conn->rssi_update_work);
+}
+
+static inline void hci_conn_start_rssi_timer(struct hci_conn *conn,
+ u16 interval)
+{
+ struct hci_dev *hdev = conn->hdev;
+ BT_DBG("conn %p, pending %d", conn,
+ delayed_work_pending(&conn->rssi_update_work));
+ if (!delayed_work_pending(&conn->rssi_update_work)) {
+ queue_delayed_work(hdev->workqueue, &conn->rssi_update_work,
+ msecs_to_jiffies(interval));
+ }
+}
+
+void hci_conn_set_rssi_reporter(struct hci_conn *conn,
+ s8 rssi_threshold, u16 interval, u8 updateOnThreshExceed)
+{
+ if (conn) {
+ conn->rssi_threshold = rssi_threshold;
+ conn->rssi_update_interval = interval;
+ conn->rssi_update_thresh_exceed = updateOnThreshExceed;
+ hci_conn_start_rssi_timer(conn, interval);
+ }
+}
+
+void hci_conn_unset_rssi_reporter(struct hci_conn *conn)
+{
+ if (conn) {
+ BT_DBG("Deleting the rssi_update_timer");
+ hci_conn_stop_rssi_timer(conn);
+ }
+}
+
/* Enter sniff mode */
void hci_conn_enter_sniff_mode(struct hci_conn *conn)
{
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index aac934a..1d76674 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -940,6 +940,21 @@
hci_dev_unlock(hdev);
}
+static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_conn *conn;
+ struct hci_rp_read_rssi *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ BT_DBG("%s rssi : %d handle : %d", hdev->name, rp->rssi, rp->handle);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+ if (conn)
+ mgmt_read_rssi_complete(hdev->id, rp->rssi, &conn->dst,
+ __le16_to_cpu(rp->handle), rp->status);
+}
+
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -2177,6 +2192,10 @@
hci_cc_le_read_buffer_size(hdev, skb);
break;
+ case HCI_OP_READ_RSSI:
+ hci_cc_read_rssi(hdev, skb);
+ break;
+
case HCI_OP_USER_CONFIRM_REPLY:
hci_cc_user_confirm_reply(hdev, skb);
break;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index bc68c9f..ddb650b 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1810,6 +1810,81 @@
return err;
}
+static int set_rssi_reporter(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct mgmt_cp_set_rssi_reporter *cp = (void *) data;
+ struct hci_dev *hdev;
+ struct hci_conn *conn;
+ int err = 0;
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+
+ if (!conn) {
+ err = cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER,
+ ENOTCONN);
+ goto failed;
+ }
+
+ BT_DBG("updateOnThreshExceed %d ", cp->updateOnThreshExceed);
+ hci_conn_set_rssi_reporter(conn, cp->rssi_threshold,
+ __le16_to_cpu(cp->interval), cp->updateOnThreshExceed);
+
+failed:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int unset_rssi_reporter(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct mgmt_cp_unset_rssi_reporter *cp = (void *) data;
+ struct hci_dev *hdev;
+ struct hci_conn *conn;
+ int err = 0;
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+
+ if (!conn) {
+ err = cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER,
+ ENOTCONN);
+ goto failed;
+ }
+
+ hci_conn_unset_rssi_reporter(conn);
+
+failed:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
@@ -2365,6 +2440,12 @@
case MGMT_OP_SET_CONNECTION_PARAMS:
err = set_connection_params(sk, index, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_SET_RSSI_REPORTER:
+ err = set_rssi_reporter(sk, index, buf + sizeof(*hdr), len);
+ break;
+ case MGMT_OP_UNSET_RSSI_REPORTER:
+ err = unset_rssi_reporter(sk, index, buf + sizeof(*hdr), len);
+ break;
case MGMT_OP_READ_LOCAL_OOB_DATA:
err = read_local_oob_data(sk, index);
break;
@@ -2817,6 +2898,57 @@
return err;
}
+void mgmt_read_rssi_complete(u16 index, s8 rssi, bdaddr_t *bdaddr,
+ u16 handle, u8 status)
+{
+ struct mgmt_ev_rssi_update ev;
+ struct hci_conn *conn;
+ struct hci_dev *hdev;
+
+ if (status)
+ return;
+
+ hdev = hci_dev_get(index);
+ conn = hci_conn_hash_lookup_handle(hdev, handle);
+
+ if (!conn)
+ return;
+
+ BT_DBG("rssi_update_thresh_exceed : %d ",
+ conn->rssi_update_thresh_exceed);
+ BT_DBG("RSSI Threshold : %d , recvd RSSI : %d ",
+ conn->rssi_threshold, rssi);
+
+ if (conn->rssi_update_thresh_exceed == 1) {
+ BT_DBG("rssi_update_thresh_exceed == 1");
+ if (rssi >= conn->rssi_threshold) {
+ memset(&ev, 0, sizeof(ev));
+ bacpy(&ev.bdaddr, bdaddr);
+ ev.rssi = rssi;
+ mgmt_event(MGMT_EV_RSSI_UPDATE, index, &ev,
+ sizeof(ev), NULL);
+ } else {
+ hci_conn_set_rssi_reporter(conn, conn->rssi_threshold,
+ conn->rssi_update_interval,
+ conn->rssi_update_thresh_exceed);
+ }
+ } else {
+ BT_DBG("rssi_update_thresh_exceed == 0");
+ if (rssi <= conn->rssi_threshold) {
+ memset(&ev, 0, sizeof(ev));
+ bacpy(&ev.bdaddr, bdaddr);
+ ev.rssi = rssi;
+ mgmt_event(MGMT_EV_RSSI_UPDATE, index, &ev,
+ sizeof(ev), NULL);
+ } else {
+ hci_conn_set_rssi_reporter(conn, conn->rssi_threshold,
+ conn->rssi_update_interval,
+ conn->rssi_update_thresh_exceed);
+ }
+ }
+}
+
+
int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir)
{