Hi,
Changes in v3:
- max TX power patches split as requested
- conn info age configurable by min/max values and actual value is randomized
- commands order changes to be RSSI, TX power and TX power max
- removed queueing of requests - we'll only have one pending request per
connection and reply from cache for others
- request can only fail if reading RSSI failed, otherwise it's considered ok
and HCI_TX_POWER_INVALID is returned as TX power values
Andrzej Kaczmarek (5):
Bluetooth: Add conn info lifetime parameters to debugfs
Bluetooth: Add support to get connection information
Bluetooth: Avoid polling TX power for LE links
Bluetooth: Store max TX power level for connection
Bluetooth: Add support for max_tx_power in Get Conn Info
include/net/bluetooth/hci_core.h | 9 ++
include/net/bluetooth/mgmt.h | 14 +++
net/bluetooth/hci_conn.c | 1 +
net/bluetooth/hci_core.c | 7 ++
net/bluetooth/hci_event.c | 12 ++-
net/bluetooth/mgmt.c | 213 +++++++++++++++++++++++++++++++++++++++
6 files changed, 255 insertions(+), 1 deletion(-)
--
1.9.3
Hi Johan,
>>>>> +static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
>>>>> + u16 len)
>>>>> +{
>>>>> + struct mgmt_cp_get_conn_info *cp = data;
>>>>> + struct mgmt_rp_get_conn_info rp;
>>>>> + struct hci_conn *conn;
>>>>> + unsigned long conn_info_age;
>>>>> + int err = 0;
>>>>> +
>>>>> + BT_DBG("%s", hdev->name);
>>>>> +
>>>>> + memset(&rp, 0, sizeof(rp));
>>>>> + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
>>>>> + rp.addr.type = cp->addr.type;
>>>>
>>>> Do we really want to just keep zeroed out values and not set the
>>>> values to invalid. What are we doing for other commands that
>>>> require the same return parameters set to match up with the remote
>>>> address.
>>>
>>> Actually there's no other command which returns address *and* some
>>> parameters so this will be first one. So in case of failure should we
>>> put proper 'invalid' values into response parameter and not just
>>> leaving them set to zero? There is always error code which means
>>> request failed and returned parameters are invalid anyway. And in case
>>> of success response we overwrite zeroes with proper values of course.
>>
>> good question. Johan, what do you think?
>
> I'm fine with the "zeroes in case of failure" approach. It's also
> consistent with the "sender sets to zero and receiver ignores" approach
> that's present in various protocol specs for reserved or invalid parts
> of PDUs. That said, I'm not sure if there is a simple example to be
> taken for reference from the HCI spec.
okay, then we go with the zero. However that means that mgmt-api.txt needs to mention that these fields are invalid in case of error response.
Regards
Marcel
Hi Marcel,
On Tue, May 13, 2014, Marcel Holtmann wrote:
> >>> +static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
> >>> + u16 len)
> >>> +{
> >>> + struct mgmt_cp_get_conn_info *cp = data;
> >>> + struct mgmt_rp_get_conn_info rp;
> >>> + struct hci_conn *conn;
> >>> + unsigned long conn_info_age;
> >>> + int err = 0;
> >>> +
> >>> + BT_DBG("%s", hdev->name);
> >>> +
> >>> + memset(&rp, 0, sizeof(rp));
> >>> + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
> >>> + rp.addr.type = cp->addr.type;
> >>
> >> Do we really want to just keep zeroed out values and not set the
> >> values to invalid. What are we doing for other commands that
> >> require the same return parameters set to match up with the remote
> >> address.
> >
> > Actually there's no other command which returns address *and* some
> > parameters so this will be first one. So in case of failure should we
> > put proper 'invalid' values into response parameter and not just
> > leaving them set to zero? There is always error code which means
> > request failed and returned parameters are invalid anyway. And in case
> > of success response we overwrite zeroes with proper values of course.
>
> good question. Johan, what do you think?
I'm fine with the "zeroes in case of failure" approach. It's also
consistent with the "sender sets to zero and receiver ignores" approach
that's present in various protocol specs for reserved or invalid parts
of PDUs. That said, I'm not sure if there is a simple example to be
taken for reference from the HCI spec.
Johan
Hi Andrzej,
>>> This patch adds support for Get Connection Information mgmt command
>>> which can be used to query for information about connection, i.e. RSSI
>>> and local TX power level.
>>>
>>> In general values cached in hci_conn are returned as long as they are
>>> considered valid, i.e. do not exceed age limit set in hdev. This limit
>>> is calculated as random value between min/max values to avoid client
>>> trying to guess when to poll for updated information.
>>>
>>> Signed-off-by: Andrzej Kaczmarek <[email protected]>
>>> ---
>>> include/net/bluetooth/hci_core.h | 2 +
>>> include/net/bluetooth/mgmt.h | 13 +++
>>> net/bluetooth/mgmt.c | 194 +++++++++++++++++++++++++++++++++++++++
>>> 3 files changed, 209 insertions(+)
>>>
>>> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
>>> index 4623f45..cbbab63 100644
>>> --- a/include/net/bluetooth/hci_core.h
>>> +++ b/include/net/bluetooth/hci_core.h
>>> @@ -384,6 +384,8 @@ struct hci_conn {
>>> __s8 tx_power;
>>> unsigned long flags;
>>>
>>> + unsigned long conn_info_timestamp;
>>> +
>>> __u8 remote_cap;
>>> __u8 remote_auth;
>>> __u8 remote_id;
>>> diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
>>> index d4b571c..9b60367 100644
>>> --- a/include/net/bluetooth/mgmt.h
>>> +++ b/include/net/bluetooth/mgmt.h
>>> @@ -409,6 +409,19 @@ struct mgmt_cp_load_irks {
>>> } __packed;
>>> #define MGMT_LOAD_IRKS_SIZE 2
>>>
>>> +#define MGMT_CONN_INFO_DATA_TX_POWER 0x00000001
>>> +
>>> +#define MGMT_OP_GET_CONN_INFO 0x0031
>>> +struct mgmt_cp_get_conn_info {
>>> + struct mgmt_addr_info addr;
>>> +} __packed;
>>> +#define MGMT_GET_CONN_INFO_SIZE MGMT_ADDR_INFO_SIZE
>>> +struct mgmt_rp_get_conn_info {
>>> + struct mgmt_addr_info addr;
>>> + __s8 rssi;
>>> + __s8 tx_power;
>>> +} __packed;
>>
>> add the max_tx_power here right from the start and initialize it to invalid. When we ever have to bisect it, we do not want an API change in between.
>>
>>> +
>>> #define MGMT_EV_CMD_COMPLETE 0x0001
>>> struct mgmt_ev_cmd_complete {
>>> __le16 opcode;
>>> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
>>> index f2a9422..c3e4382 100644
>>> --- a/net/bluetooth/mgmt.c
>>> +++ b/net/bluetooth/mgmt.c
>>> @@ -83,6 +83,7 @@ static const u16 mgmt_commands[] = {
>>> MGMT_OP_SET_DEBUG_KEYS,
>>> MGMT_OP_SET_PRIVACY,
>>> MGMT_OP_LOAD_IRKS,
>>> + MGMT_OP_GET_CONN_INFO,
>>> };
>>>
>>> static const u16 mgmt_events[] = {
>>> @@ -4557,6 +4558,198 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
>>> return err;
>>> }
>>>
>>> +struct cmd_conn_lookup {
>>> + struct hci_conn *conn;
>>> + bool valid_tx_power;
>>> + u8 mgmt_status;
>>> +};
>>> +
>>> +static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
>>> +{
>>> + struct cmd_conn_lookup *match = data;
>>> + struct mgmt_cp_get_conn_info *cp;
>>> + struct mgmt_rp_get_conn_info rp;
>>> + struct hci_conn *conn = cmd->user_data;
>>> +
>>> + if (conn != match->conn)
>>> + return;
>>> +
>>> + cp = (struct mgmt_cp_get_conn_info *) cmd->param;
>>> +
>>> + memset(&rp, 0, sizeof(rp));
>>> + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
>>> + rp.addr.type = cp->addr.type;
>>> +
>>> + if (!match->mgmt_status) {
>>> + rp.rssi = conn->rssi;
>>> +
>>> + if (match->valid_tx_power)
>>> + rp.tx_power = conn->tx_power;
>>> + else
>>> + rp.tx_power = HCI_TX_POWER_INVALID;
>>> + }
>>> +
>>> + cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
>>> + match->mgmt_status, &rp, sizeof(rp));
>>> +
>>> + hci_conn_drop(conn);
>>> +
>>> + mgmt_pending_remove(cmd);
>>> +}
>>> +
>>> +static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
>>> +{
>>> + struct hci_cp_read_rssi *cp;
>>> + struct hci_conn *conn;
>>> + struct cmd_conn_lookup match;
>>> + u16 handle;
>>> +
>>> + BT_DBG("status 0x%02x", status);
>>> +
>>> + hci_dev_lock(hdev);
>>> +
>>> + /* TX power data is valid in case request completed successfully,
>>> + * otherwise we assume it's not valid.
>>> + */
>>> + match.valid_tx_power = !status;
>>> +
>>> + /* Commands sent in request are either Read RSSI or Read Transmit Power
>>> + * Level so we check which one was last sent to retrieve connection
>>> + * handle. Both commands have handle as first parameter so it's safe to
>>> + * cast data on the same command struct.
>>> + *
>>> + * First command sent is always Read RSSI and we fail only if it fails.
>>> + * In other case we simply override error to indicate success as we
>>> + * already remembered if TX power value is actually valid.
>>> + */
>>> + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
>>> + if (!cp) {
>>> + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
>>> + status = 0;
>>> + }
>>> +
>>> + if (!cp) {
>>> + BT_ERR("invalid sent_cmd in response");
>>> + goto unlock;
>>> + }
>>> +
>>> + handle = __le16_to_cpu(cp->handle);
>>> + conn = hci_conn_hash_lookup_handle(hdev, handle);
>>> + if (!conn) {
>>> + BT_ERR("unknown handle (%d) in response", handle);
>>> + goto unlock;
>>> + }
>>> +
>>> + match.conn = conn;
>>> + match.mgmt_status = mgmt_status(status);
>>> +
>>> + /* Cache refresh is complete, now reply for mgmt request for given
>>> + * connection only.
>>> + */
>>> + mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
>>> + get_conn_info_complete, &match);
>>> +
>>> +unlock:
>>> + hci_dev_unlock(hdev);
>>> +}
>>> +
>>> +static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
>>> + u16 len)
>>> +{
>>> + struct mgmt_cp_get_conn_info *cp = data;
>>> + struct mgmt_rp_get_conn_info rp;
>>> + struct hci_conn *conn;
>>> + unsigned long conn_info_age;
>>> + int err = 0;
>>> +
>>> + BT_DBG("%s", hdev->name);
>>> +
>>> + memset(&rp, 0, sizeof(rp));
>>> + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
>>> + rp.addr.type = cp->addr.type;
>>
>> Do we really want to just keep zeroed out values and not set the values to invalid. What are we doing for other commands that require the same return parameters set to match up with the remote address.
>
> Actually there's no other command which returns address *and* some
> parameters so this will be first one. So in case of failure should we
> put proper 'invalid' values into response parameter and not just
> leaving them set to zero? There is always error code which means
> request failed and returned parameters are invalid anyway. And in case
> of success response we overwrite zeroes with proper values of course.
good question. Johan, what do you think?
Regards
Marcel
Hi Marcel,
On 13 May 2014 18:22, Marcel Holtmann <[email protected]> wrote:
> Hi Andrzej,
>
>> This patch adds support for Get Connection Information mgmt command
>> which can be used to query for information about connection, i.e. RSSI
>> and local TX power level.
>>
>> In general values cached in hci_conn are returned as long as they are
>> considered valid, i.e. do not exceed age limit set in hdev. This limit
>> is calculated as random value between min/max values to avoid client
>> trying to guess when to poll for updated information.
>>
>> Signed-off-by: Andrzej Kaczmarek <[email protected]>
>> ---
>> include/net/bluetooth/hci_core.h | 2 +
>> include/net/bluetooth/mgmt.h | 13 +++
>> net/bluetooth/mgmt.c | 194 +++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 209 insertions(+)
>>
>> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
>> index 4623f45..cbbab63 100644
>> --- a/include/net/bluetooth/hci_core.h
>> +++ b/include/net/bluetooth/hci_core.h
>> @@ -384,6 +384,8 @@ struct hci_conn {
>> __s8 tx_power;
>> unsigned long flags;
>>
>> + unsigned long conn_info_timestamp;
>> +
>> __u8 remote_cap;
>> __u8 remote_auth;
>> __u8 remote_id;
>> diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
>> index d4b571c..9b60367 100644
>> --- a/include/net/bluetooth/mgmt.h
>> +++ b/include/net/bluetooth/mgmt.h
>> @@ -409,6 +409,19 @@ struct mgmt_cp_load_irks {
>> } __packed;
>> #define MGMT_LOAD_IRKS_SIZE 2
>>
>> +#define MGMT_CONN_INFO_DATA_TX_POWER 0x00000001
>> +
>> +#define MGMT_OP_GET_CONN_INFO 0x0031
>> +struct mgmt_cp_get_conn_info {
>> + struct mgmt_addr_info addr;
>> +} __packed;
>> +#define MGMT_GET_CONN_INFO_SIZE MGMT_ADDR_INFO_SIZE
>> +struct mgmt_rp_get_conn_info {
>> + struct mgmt_addr_info addr;
>> + __s8 rssi;
>> + __s8 tx_power;
>> +} __packed;
>
> add the max_tx_power here right from the start and initialize it to invalid. When we ever have to bisect it, we do not want an API change in between.
>
>> +
>> #define MGMT_EV_CMD_COMPLETE 0x0001
>> struct mgmt_ev_cmd_complete {
>> __le16 opcode;
>> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
>> index f2a9422..c3e4382 100644
>> --- a/net/bluetooth/mgmt.c
>> +++ b/net/bluetooth/mgmt.c
>> @@ -83,6 +83,7 @@ static const u16 mgmt_commands[] = {
>> MGMT_OP_SET_DEBUG_KEYS,
>> MGMT_OP_SET_PRIVACY,
>> MGMT_OP_LOAD_IRKS,
>> + MGMT_OP_GET_CONN_INFO,
>> };
>>
>> static const u16 mgmt_events[] = {
>> @@ -4557,6 +4558,198 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
>> return err;
>> }
>>
>> +struct cmd_conn_lookup {
>> + struct hci_conn *conn;
>> + bool valid_tx_power;
>> + u8 mgmt_status;
>> +};
>> +
>> +static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
>> +{
>> + struct cmd_conn_lookup *match = data;
>> + struct mgmt_cp_get_conn_info *cp;
>> + struct mgmt_rp_get_conn_info rp;
>> + struct hci_conn *conn = cmd->user_data;
>> +
>> + if (conn != match->conn)
>> + return;
>> +
>> + cp = (struct mgmt_cp_get_conn_info *) cmd->param;
>> +
>> + memset(&rp, 0, sizeof(rp));
>> + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
>> + rp.addr.type = cp->addr.type;
>> +
>> + if (!match->mgmt_status) {
>> + rp.rssi = conn->rssi;
>> +
>> + if (match->valid_tx_power)
>> + rp.tx_power = conn->tx_power;
>> + else
>> + rp.tx_power = HCI_TX_POWER_INVALID;
>> + }
>> +
>> + cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
>> + match->mgmt_status, &rp, sizeof(rp));
>> +
>> + hci_conn_drop(conn);
>> +
>> + mgmt_pending_remove(cmd);
>> +}
>> +
>> +static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
>> +{
>> + struct hci_cp_read_rssi *cp;
>> + struct hci_conn *conn;
>> + struct cmd_conn_lookup match;
>> + u16 handle;
>> +
>> + BT_DBG("status 0x%02x", status);
>> +
>> + hci_dev_lock(hdev);
>> +
>> + /* TX power data is valid in case request completed successfully,
>> + * otherwise we assume it's not valid.
>> + */
>> + match.valid_tx_power = !status;
>> +
>> + /* Commands sent in request are either Read RSSI or Read Transmit Power
>> + * Level so we check which one was last sent to retrieve connection
>> + * handle. Both commands have handle as first parameter so it's safe to
>> + * cast data on the same command struct.
>> + *
>> + * First command sent is always Read RSSI and we fail only if it fails.
>> + * In other case we simply override error to indicate success as we
>> + * already remembered if TX power value is actually valid.
>> + */
>> + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
>> + if (!cp) {
>> + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
>> + status = 0;
>> + }
>> +
>> + if (!cp) {
>> + BT_ERR("invalid sent_cmd in response");
>> + goto unlock;
>> + }
>> +
>> + handle = __le16_to_cpu(cp->handle);
>> + conn = hci_conn_hash_lookup_handle(hdev, handle);
>> + if (!conn) {
>> + BT_ERR("unknown handle (%d) in response", handle);
>> + goto unlock;
>> + }
>> +
>> + match.conn = conn;
>> + match.mgmt_status = mgmt_status(status);
>> +
>> + /* Cache refresh is complete, now reply for mgmt request for given
>> + * connection only.
>> + */
>> + mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
>> + get_conn_info_complete, &match);
>> +
>> +unlock:
>> + hci_dev_unlock(hdev);
>> +}
>> +
>> +static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
>> + u16 len)
>> +{
>> + struct mgmt_cp_get_conn_info *cp = data;
>> + struct mgmt_rp_get_conn_info rp;
>> + struct hci_conn *conn;
>> + unsigned long conn_info_age;
>> + int err = 0;
>> +
>> + BT_DBG("%s", hdev->name);
>> +
>> + memset(&rp, 0, sizeof(rp));
>> + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
>> + rp.addr.type = cp->addr.type;
>
> Do we really want to just keep zeroed out values and not set the values to invalid. What are we doing for other commands that require the same return parameters set to match up with the remote address.
Actually there's no other command which returns address *and* some
parameters so this will be first one. So in case of failure should we
put proper 'invalid' values into response parameter and not just
leaving them set to zero? There is always error code which means
request failed and returned parameters are invalid anyway. And in case
of success response we overwrite zeroes with proper values of course.
BR,
Andrzej
Hi Andrzej,
> This patch adds support for Get Connection Information mgmt command
> which can be used to query for information about connection, i.e. RSSI
> and local TX power level.
>
> In general values cached in hci_conn are returned as long as they are
> considered valid, i.e. do not exceed age limit set in hdev. This limit
> is calculated as random value between min/max values to avoid client
> trying to guess when to poll for updated information.
>
> Signed-off-by: Andrzej Kaczmarek <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 2 +
> include/net/bluetooth/mgmt.h | 13 +++
> net/bluetooth/mgmt.c | 194 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 209 insertions(+)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 4623f45..cbbab63 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -384,6 +384,8 @@ struct hci_conn {
> __s8 tx_power;
> unsigned long flags;
>
> + unsigned long conn_info_timestamp;
> +
> __u8 remote_cap;
> __u8 remote_auth;
> __u8 remote_id;
> diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
> index d4b571c..9b60367 100644
> --- a/include/net/bluetooth/mgmt.h
> +++ b/include/net/bluetooth/mgmt.h
> @@ -409,6 +409,19 @@ struct mgmt_cp_load_irks {
> } __packed;
> #define MGMT_LOAD_IRKS_SIZE 2
>
> +#define MGMT_CONN_INFO_DATA_TX_POWER 0x00000001
> +
> +#define MGMT_OP_GET_CONN_INFO 0x0031
> +struct mgmt_cp_get_conn_info {
> + struct mgmt_addr_info addr;
> +} __packed;
> +#define MGMT_GET_CONN_INFO_SIZE MGMT_ADDR_INFO_SIZE
> +struct mgmt_rp_get_conn_info {
> + struct mgmt_addr_info addr;
> + __s8 rssi;
> + __s8 tx_power;
> +} __packed;
add the max_tx_power here right from the start and initialize it to invalid. When we ever have to bisect it, we do not want an API change in between.
> +
> #define MGMT_EV_CMD_COMPLETE 0x0001
> struct mgmt_ev_cmd_complete {
> __le16 opcode;
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index f2a9422..c3e4382 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -83,6 +83,7 @@ static const u16 mgmt_commands[] = {
> MGMT_OP_SET_DEBUG_KEYS,
> MGMT_OP_SET_PRIVACY,
> MGMT_OP_LOAD_IRKS,
> + MGMT_OP_GET_CONN_INFO,
> };
>
> static const u16 mgmt_events[] = {
> @@ -4557,6 +4558,198 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
> return err;
> }
>
> +struct cmd_conn_lookup {
> + struct hci_conn *conn;
> + bool valid_tx_power;
> + u8 mgmt_status;
> +};
> +
> +static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
> +{
> + struct cmd_conn_lookup *match = data;
> + struct mgmt_cp_get_conn_info *cp;
> + struct mgmt_rp_get_conn_info rp;
> + struct hci_conn *conn = cmd->user_data;
> +
> + if (conn != match->conn)
> + return;
> +
> + cp = (struct mgmt_cp_get_conn_info *) cmd->param;
> +
> + memset(&rp, 0, sizeof(rp));
> + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
> + rp.addr.type = cp->addr.type;
> +
> + if (!match->mgmt_status) {
> + rp.rssi = conn->rssi;
> +
> + if (match->valid_tx_power)
> + rp.tx_power = conn->tx_power;
> + else
> + rp.tx_power = HCI_TX_POWER_INVALID;
> + }
> +
> + cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
> + match->mgmt_status, &rp, sizeof(rp));
> +
> + hci_conn_drop(conn);
> +
> + mgmt_pending_remove(cmd);
> +}
> +
> +static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
> +{
> + struct hci_cp_read_rssi *cp;
> + struct hci_conn *conn;
> + struct cmd_conn_lookup match;
> + u16 handle;
> +
> + BT_DBG("status 0x%02x", status);
> +
> + hci_dev_lock(hdev);
> +
> + /* TX power data is valid in case request completed successfully,
> + * otherwise we assume it's not valid.
> + */
> + match.valid_tx_power = !status;
> +
> + /* Commands sent in request are either Read RSSI or Read Transmit Power
> + * Level so we check which one was last sent to retrieve connection
> + * handle. Both commands have handle as first parameter so it's safe to
> + * cast data on the same command struct.
> + *
> + * First command sent is always Read RSSI and we fail only if it fails.
> + * In other case we simply override error to indicate success as we
> + * already remembered if TX power value is actually valid.
> + */
> + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
> + if (!cp) {
> + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
> + status = 0;
> + }
> +
> + if (!cp) {
> + BT_ERR("invalid sent_cmd in response");
> + goto unlock;
> + }
> +
> + handle = __le16_to_cpu(cp->handle);
> + conn = hci_conn_hash_lookup_handle(hdev, handle);
> + if (!conn) {
> + BT_ERR("unknown handle (%d) in response", handle);
> + goto unlock;
> + }
> +
> + match.conn = conn;
> + match.mgmt_status = mgmt_status(status);
> +
> + /* Cache refresh is complete, now reply for mgmt request for given
> + * connection only.
> + */
> + mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
> + get_conn_info_complete, &match);
> +
> +unlock:
> + hci_dev_unlock(hdev);
> +}
> +
> +static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
> + u16 len)
> +{
> + struct mgmt_cp_get_conn_info *cp = data;
> + struct mgmt_rp_get_conn_info rp;
> + struct hci_conn *conn;
> + unsigned long conn_info_age;
> + int err = 0;
> +
> + BT_DBG("%s", hdev->name);
> +
> + memset(&rp, 0, sizeof(rp));
> + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
> + rp.addr.type = cp->addr.type;
Do we really want to just keep zeroed out values and not set the values to invalid. What are we doing for other commands that require the same return parameters set to match up with the remote address.
> +
> + if (!bdaddr_type_is_valid(cp->addr.type))
> + return cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
> + MGMT_STATUS_INVALID_PARAMS,
> + &rp, sizeof(rp));
> +
> + hci_dev_lock(hdev);
> +
> + if (!hdev_is_powered(hdev)) {
> + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
> + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
> + goto unlock;
> + }
> +
> + if (cp->addr.type == BDADDR_BREDR)
> + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
> + &cp->addr.bdaddr);
> + else
> + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
> +
> + if (!conn || conn->state != BT_CONNECTED) {
> + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
> + MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
> + goto unlock;
> + }
> +
> + /* To avoid client trying to guess when to poll again for information we
> + * calculate conn info age as random value between min/max set in hdev.
> + */
> + conn_info_age = hdev->conn_info_min_age +
> + prandom_u32_max(hdev->conn_info_max_age -
> + hdev->conn_info_min_age);
> +
> + /* Query controller to refresh cached values if they are too old or were
> + * never read.
> + */
> + if (time_after(jiffies, conn->conn_info_timestamp + conn_info_age) ||
> + !conn->conn_info_timestamp) {
> + struct hci_request req;
> + struct hci_cp_read_tx_power req_txp_cp;
> + struct hci_cp_read_rssi req_rssi_cp;
> + struct pending_cmd *cmd;
> +
> + hci_req_init(&req, hdev);
> + req_rssi_cp.handle = cpu_to_le16(conn->handle);
> + hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp),
> + &req_rssi_cp);
> +
> + req_txp_cp.handle = cpu_to_le16(conn->handle);
> + req_txp_cp.type = 0x00;
> + hci_req_add(&req, HCI_OP_READ_TX_POWER,
> + sizeof(req_txp_cp), &req_txp_cp);
> +
> + err = hci_req_run(&req, conn_info_refresh_complete);
> + if (err < 0)
> + goto unlock;
> +
> + cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev,
> + data, len);
> + if (!cmd) {
> + err = -ENOMEM;
> + goto unlock;
> + }
> +
> + hci_conn_hold(conn);
> + cmd->user_data = conn;
> +
> + conn->conn_info_timestamp = jiffies;
> + } else {
> + /* Cache is valid, just reply with values cached in hci_conn */
> + rp.rssi = conn->rssi;
> + rp.tx_power = conn->tx_power;
> + rp.max_tx_power = conn->max_tx_power;
> +
> + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
> + MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
> + }
> +
> +unlock:
> + hci_dev_unlock(hdev);
> + return err;
> +}
> +
> static const struct mgmt_handler {
> int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
> u16 data_len);
> @@ -4612,6 +4805,7 @@ static const struct mgmt_handler {
> { set_debug_keys, false, MGMT_SETTING_SIZE },
> { set_privacy, false, MGMT_SET_PRIVACY_SIZE },
> { load_irks, true, MGMT_LOAD_IRKS_SIZE },
> + { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE },
> };
>
Regards
Marcel
Hi Andrzej,
> This patch adds conn_info_min_age and conn_info_max_age parameters to
> debugfs which determine lifetime of connection information. Actual
> lifetime will be random value between min and max age.
>
> Default values for min and max age are 1000ms and 3000ms respectively.
>
> Signed-off-by: Andrzej Kaczmarek <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 6 ++++++
> net/bluetooth/hci_core.c | 7 +++++++
> 2 files changed, 13 insertions(+)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 211bad6..4623f45 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -145,6 +145,10 @@ struct oob_data {
> /* Default LE RPA expiry time, 15 minutes */
> #define HCI_DEFAULT_RPA_TIMEOUT (15 * 60)
>
> +/* Default min/max age of connection information (1s/3s) */
> +#define DEFAULT_CONN_INFO_MIN_AGE 1000
> +#define DEFAULT_CONN_INFO_MAX_AGE 3000
> +
> struct amp_assoc {
> __u16 len;
> __u16 offset;
> @@ -200,6 +204,8 @@ struct hci_dev {
> __u16 le_conn_min_interval;
> __u16 le_conn_max_interval;
> __u16 discov_interleaved_timeout;
> + __u16 conn_info_min_age;
> + __u16 conn_info_max_age;
> __u8 ssp_debug_mode;
>
> __u16 devid_source;
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index d31f144..62ebcea 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -1754,6 +1754,11 @@ static int __hci_init(struct hci_dev *hdev)
> &blacklist_fops);
> debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
>
> + debugfs_create_u16("conn_info_min_age", 0644, hdev->debugfs,
> + &hdev->conn_info_min_age);
> + debugfs_create_u16("conn_info_max_age", 0644, hdev->debugfs,
> + &hdev->conn_info_max_age);
> +
I know this is dead simple, but I rather have this done similar to how we do the sniff_max_interval and sniff_min_interval. So that you can not set wrong values or bad ranges.
Regards
Marcel
This patch adds support for Get Connection Information mgmt command
which can be used to query for information about connection, i.e. RSSI
and local TX power level.
In general values cached in hci_conn are returned as long as they are
considered valid, i.e. do not exceed age limit set in hdev. This limit
is calculated as random value between min/max values to avoid client
trying to guess when to poll for updated information.
Signed-off-by: Andrzej Kaczmarek <[email protected]>
---
include/net/bluetooth/hci_core.h | 2 +
include/net/bluetooth/mgmt.h | 13 +++
net/bluetooth/mgmt.c | 194 +++++++++++++++++++++++++++++++++++++++
3 files changed, 209 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4623f45..cbbab63 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -384,6 +384,8 @@ struct hci_conn {
__s8 tx_power;
unsigned long flags;
+ unsigned long conn_info_timestamp;
+
__u8 remote_cap;
__u8 remote_auth;
__u8 remote_id;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index d4b571c..9b60367 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -409,6 +409,19 @@ struct mgmt_cp_load_irks {
} __packed;
#define MGMT_LOAD_IRKS_SIZE 2
+#define MGMT_CONN_INFO_DATA_TX_POWER 0x00000001
+
+#define MGMT_OP_GET_CONN_INFO 0x0031
+struct mgmt_cp_get_conn_info {
+ struct mgmt_addr_info addr;
+} __packed;
+#define MGMT_GET_CONN_INFO_SIZE MGMT_ADDR_INFO_SIZE
+struct mgmt_rp_get_conn_info {
+ struct mgmt_addr_info addr;
+ __s8 rssi;
+ __s8 tx_power;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index f2a9422..c3e4382 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -83,6 +83,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_SET_DEBUG_KEYS,
MGMT_OP_SET_PRIVACY,
MGMT_OP_LOAD_IRKS,
+ MGMT_OP_GET_CONN_INFO,
};
static const u16 mgmt_events[] = {
@@ -4557,6 +4558,198 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
return err;
}
+struct cmd_conn_lookup {
+ struct hci_conn *conn;
+ bool valid_tx_power;
+ u8 mgmt_status;
+};
+
+static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
+{
+ struct cmd_conn_lookup *match = data;
+ struct mgmt_cp_get_conn_info *cp;
+ struct mgmt_rp_get_conn_info rp;
+ struct hci_conn *conn = cmd->user_data;
+
+ if (conn != match->conn)
+ return;
+
+ cp = (struct mgmt_cp_get_conn_info *) cmd->param;
+
+ memset(&rp, 0, sizeof(rp));
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
+
+ if (!match->mgmt_status) {
+ rp.rssi = conn->rssi;
+
+ if (match->valid_tx_power)
+ rp.tx_power = conn->tx_power;
+ else
+ rp.tx_power = HCI_TX_POWER_INVALID;
+ }
+
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
+ match->mgmt_status, &rp, sizeof(rp));
+
+ hci_conn_drop(conn);
+
+ mgmt_pending_remove(cmd);
+}
+
+static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_read_rssi *cp;
+ struct hci_conn *conn;
+ struct cmd_conn_lookup match;
+ u16 handle;
+
+ BT_DBG("status 0x%02x", status);
+
+ hci_dev_lock(hdev);
+
+ /* TX power data is valid in case request completed successfully,
+ * otherwise we assume it's not valid.
+ */
+ match.valid_tx_power = !status;
+
+ /* Commands sent in request are either Read RSSI or Read Transmit Power
+ * Level so we check which one was last sent to retrieve connection
+ * handle. Both commands have handle as first parameter so it's safe to
+ * cast data on the same command struct.
+ *
+ * First command sent is always Read RSSI and we fail only if it fails.
+ * In other case we simply override error to indicate success as we
+ * already remembered if TX power value is actually valid.
+ */
+ cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
+ if (!cp) {
+ cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
+ status = 0;
+ }
+
+ if (!cp) {
+ BT_ERR("invalid sent_cmd in response");
+ goto unlock;
+ }
+
+ handle = __le16_to_cpu(cp->handle);
+ conn = hci_conn_hash_lookup_handle(hdev, handle);
+ if (!conn) {
+ BT_ERR("unknown handle (%d) in response", handle);
+ goto unlock;
+ }
+
+ match.conn = conn;
+ match.mgmt_status = mgmt_status(status);
+
+ /* Cache refresh is complete, now reply for mgmt request for given
+ * connection only.
+ */
+ mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
+ get_conn_info_complete, &match);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_cp_get_conn_info *cp = data;
+ struct mgmt_rp_get_conn_info rp;
+ struct hci_conn *conn;
+ unsigned long conn_info_age;
+ int err = 0;
+
+ BT_DBG("%s", hdev->name);
+
+ memset(&rp, 0, sizeof(rp));
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
+
+ if (!bdaddr_type_is_valid(cp->addr.type))
+ return cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+ MGMT_STATUS_INVALID_PARAMS,
+ &rp, sizeof(rp));
+
+ hci_dev_lock(hdev);
+
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+ MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
+ goto unlock;
+ }
+
+ if (cp->addr.type == BDADDR_BREDR)
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+ &cp->addr.bdaddr);
+ else
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+
+ if (!conn || conn->state != BT_CONNECTED) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+ MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
+ goto unlock;
+ }
+
+ /* To avoid client trying to guess when to poll again for information we
+ * calculate conn info age as random value between min/max set in hdev.
+ */
+ conn_info_age = hdev->conn_info_min_age +
+ prandom_u32_max(hdev->conn_info_max_age -
+ hdev->conn_info_min_age);
+
+ /* Query controller to refresh cached values if they are too old or were
+ * never read.
+ */
+ if (time_after(jiffies, conn->conn_info_timestamp + conn_info_age) ||
+ !conn->conn_info_timestamp) {
+ struct hci_request req;
+ struct hci_cp_read_tx_power req_txp_cp;
+ struct hci_cp_read_rssi req_rssi_cp;
+ struct pending_cmd *cmd;
+
+ hci_req_init(&req, hdev);
+ req_rssi_cp.handle = cpu_to_le16(conn->handle);
+ hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp),
+ &req_rssi_cp);
+
+ req_txp_cp.handle = cpu_to_le16(conn->handle);
+ req_txp_cp.type = 0x00;
+ hci_req_add(&req, HCI_OP_READ_TX_POWER,
+ sizeof(req_txp_cp), &req_txp_cp);
+
+ err = hci_req_run(&req, conn_info_refresh_complete);
+ if (err < 0)
+ goto unlock;
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev,
+ data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ hci_conn_hold(conn);
+ cmd->user_data = conn;
+
+ conn->conn_info_timestamp = jiffies;
+ } else {
+ /* Cache is valid, just reply with values cached in hci_conn */
+ rp.rssi = conn->rssi;
+ rp.tx_power = conn->tx_power;
+ rp.max_tx_power = conn->max_tx_power;
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+ MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+ }
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
static const struct mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len);
@@ -4612,6 +4805,7 @@ static const struct mgmt_handler {
{ set_debug_keys, false, MGMT_SETTING_SIZE },
{ set_privacy, false, MGMT_SET_PRIVACY_SIZE },
{ load_irks, true, MGMT_LOAD_IRKS_SIZE },
+ { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE },
};
--
1.9.3
This patch adds support for max_tx_power in Get Connection Information
request. Value is read only once for given connection and then always
returned in response as parameter.
Signed-off-by: Andrzej Kaczmarek <[email protected]>
---
include/net/bluetooth/mgmt.h | 1 +
net/bluetooth/mgmt.c | 19 ++++++++++++++++---
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 9b60367..b74309a 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -420,6 +420,7 @@ struct mgmt_rp_get_conn_info {
struct mgmt_addr_info addr;
__s8 rssi;
__s8 tx_power;
+ __s8 max_tx_power;
} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index f3d90b9..17ebaa3 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -4583,10 +4583,13 @@ static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
if (!match->mgmt_status) {
rp.rssi = conn->rssi;
- if (match->valid_tx_power)
+ if (match->valid_tx_power) {
rp.tx_power = conn->tx_power;
- else
+ rp.max_tx_power = conn->max_tx_power;
+ } else {
rp.tx_power = HCI_TX_POWER_INVALID;
+ rp.max_tx_power = HCI_TX_POWER_INVALID;
+ }
}
cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
@@ -4609,7 +4612,9 @@ static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
hci_dev_lock(hdev);
/* TX power data is valid in case request completed successfully,
- * otherwise we assume it's not valid.
+ * otherwise we assume it's not valid. At the moment we assume that
+ * either both or none of current and max values are valid to keep code
+ * simple.
*/
match.valid_tx_power = !status;
@@ -4726,6 +4731,14 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
sizeof(req_txp_cp), &req_txp_cp);
}
+ /* Max TX power needs to be read only once per connection */
+ if (conn->max_tx_power == HCI_TX_POWER_INVALID) {
+ req_txp_cp.handle = cpu_to_le16(conn->handle);
+ req_txp_cp.type = 0x01;
+ hci_req_add(&req, HCI_OP_READ_TX_POWER,
+ sizeof(req_txp_cp), &req_txp_cp);
+ }
+
err = hci_req_run(&req, conn_info_refresh_complete);
if (err < 0)
goto unlock;
--
1.9.3
TX power for LE links is immutable thus we do not need to query for it
if already have value.
Signed-off-by: Andrzej Kaczmarek <[email protected]>
---
net/bluetooth/mgmt.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index c3e4382..f3d90b9 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -4715,10 +4715,16 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp),
&req_rssi_cp);
- req_txp_cp.handle = cpu_to_le16(conn->handle);
- req_txp_cp.type = 0x00;
- hci_req_add(&req, HCI_OP_READ_TX_POWER,
- sizeof(req_txp_cp), &req_txp_cp);
+ /* For LE links TX power does not change thus we don't need to
+ * query for it once value is known.
+ */
+ if (!bdaddr_type_is_le(cp->addr.type) ||
+ conn->tx_power == HCI_TX_POWER_INVALID) {
+ req_txp_cp.handle = cpu_to_le16(conn->handle);
+ req_txp_cp.type = 0x00;
+ hci_req_add(&req, HCI_OP_READ_TX_POWER,
+ sizeof(req_txp_cp), &req_txp_cp);
+ }
err = hci_req_run(&req, conn_info_refresh_complete);
if (err < 0)
--
1.9.3
This patch adds support to store local maximum TX power level for
connection when reply for HCI_Read_Transmit_Power_Level is received.
Signed-off-by: Andrzej Kaczmarek <[email protected]>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_conn.c | 1 +
net/bluetooth/hci_event.c | 12 +++++++++++-
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index cbbab63..b386bf1 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -382,6 +382,7 @@ struct hci_conn {
__u16 le_conn_max_interval;
__s8 rssi;
__s8 tx_power;
+ __s8 max_tx_power;
unsigned long flags;
unsigned long conn_info_timestamp;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 74b368b..a987e7d 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -408,6 +408,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
conn->remote_auth = 0xff;
conn->key_type = 0xff;
conn->tx_power = HCI_TX_POWER_INVALID;
+ conn->max_tx_power = HCI_TX_POWER_INVALID;
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index fa614e3..492d8d5 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1282,9 +1282,19 @@ static void hci_cc_read_tx_power(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
- if (conn && sent->type == 0x00)
+ if (!conn)
+ goto unlock;
+
+ switch (sent->type) {
+ case 0x00:
conn->tx_power = rp->tx_power;
+ break;
+ case 0x01:
+ conn->max_tx_power = rp->tx_power;
+ break;
+ }
+unlock:
hci_dev_unlock(hdev);
}
--
1.9.3
This patch adds conn_info_min_age and conn_info_max_age parameters to
debugfs which determine lifetime of connection information. Actual
lifetime will be random value between min and max age.
Default values for min and max age are 1000ms and 3000ms respectively.
Signed-off-by: Andrzej Kaczmarek <[email protected]>
---
include/net/bluetooth/hci_core.h | 6 ++++++
net/bluetooth/hci_core.c | 7 +++++++
2 files changed, 13 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 211bad6..4623f45 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -145,6 +145,10 @@ struct oob_data {
/* Default LE RPA expiry time, 15 minutes */
#define HCI_DEFAULT_RPA_TIMEOUT (15 * 60)
+/* Default min/max age of connection information (1s/3s) */
+#define DEFAULT_CONN_INFO_MIN_AGE 1000
+#define DEFAULT_CONN_INFO_MAX_AGE 3000
+
struct amp_assoc {
__u16 len;
__u16 offset;
@@ -200,6 +204,8 @@ struct hci_dev {
__u16 le_conn_min_interval;
__u16 le_conn_max_interval;
__u16 discov_interleaved_timeout;
+ __u16 conn_info_min_age;
+ __u16 conn_info_max_age;
__u8 ssp_debug_mode;
__u16 devid_source;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d31f144..62ebcea 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1754,6 +1754,11 @@ static int __hci_init(struct hci_dev *hdev)
&blacklist_fops);
debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+ debugfs_create_u16("conn_info_min_age", 0644, hdev->debugfs,
+ &hdev->conn_info_min_age);
+ debugfs_create_u16("conn_info_max_age", 0644, hdev->debugfs,
+ &hdev->conn_info_max_age);
+
if (lmp_bredr_capable(hdev)) {
debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
hdev, &inquiry_cache_fops);
@@ -3789,6 +3794,8 @@ struct hci_dev *hci_alloc_dev(void)
hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT;
+ hdev->conn_info_min_age = DEFAULT_CONN_INFO_MIN_AGE;
+ hdev->conn_info_max_age = DEFAULT_CONN_INFO_MAX_AGE;
mutex_init(&hdev->lock);
mutex_init(&hdev->req_lock);
--
1.9.3