Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 85 additions & 8 deletions net/bluetooth/l2cap_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -4670,13 +4670,78 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
return 0;
}

struct conn_param_update_data {
u16 handle;
bdaddr_t dst;
u8 dst_type;
u16 min;
u16 max;
u16 latency;
u16 to_multiplier;
};

static int l2cap_conn_param_update_sync(struct hci_dev *hdev, void *data)
{
struct conn_param_update_data *d = data;
struct hci_conn *conn;
struct hci_conn_params *params;
struct hci_cp_le_conn_update cp;
u8 store_hint = 0x00;

/* Verify the connection is still alive and matches the original
* request, since the handle could theoretically be reused after
* a disconnect/reconnect cycle.
*/
hci_dev_lock(hdev);

conn = hci_conn_hash_lookup_handle(hdev, d->handle);
if (!conn || bacmp(&conn->dst, &d->dst)) {
hci_dev_unlock(hdev);
return 0;
}

params = hci_conn_params_lookup(hdev, &d->dst, d->dst_type);
if (params) {
params->conn_min_interval = d->min;
params->conn_max_interval = d->max;
params->conn_latency = d->latency;
params->supervision_timeout = d->to_multiplier;
store_hint = 0x01;
}

hci_dev_unlock(hdev);

memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(d->handle);
cp.conn_interval_min = cpu_to_le16(d->min);
cp.conn_interval_max = cpu_to_le16(d->max);
cp.conn_latency = cpu_to_le16(d->latency);
cp.supervision_timeout = cpu_to_le16(d->to_multiplier);
cp.min_ce_len = cpu_to_le16(0x0000);
cp.max_ce_len = cpu_to_le16(0x0000);

hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);

mgmt_new_conn_param(hdev, &d->dst, d->dst_type, store_hint,
d->min, d->max, d->latency, d->to_multiplier);

return 0;
}

static void l2cap_conn_param_update_destroy(struct hci_dev *hdev, void *data,
int err)
{
kfree(data);
}

static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, u8 *data)
{
struct hci_conn *hcon = conn->hcon;
struct l2cap_conn_param_update_req *req;
struct l2cap_conn_param_update_rsp rsp;
struct conn_param_update_data *d;
u16 min, max, latency, to_multiplier;
int err;

Expand All @@ -4703,18 +4768,30 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
else
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED);

if (!err) {
d = kmalloc(sizeof(*d), GFP_KERNEL);
if (!d) {
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
err = -ENOMEM;
}
}

l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
sizeof(rsp), &rsp);

if (!err) {
u8 store_hint;

store_hint = hci_le_conn_update(hcon, min, max, latency,
to_multiplier);
mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type,
store_hint, min, max, latency,
to_multiplier);

d->handle = hcon->handle;
bacpy(&d->dst, &hcon->dst);
d->dst_type = hcon->dst_type;
d->min = min;
d->max = max;
d->latency = latency;
d->to_multiplier = to_multiplier;

if (hci_cmd_sync_queue(hcon->hdev,
l2cap_conn_param_update_sync, d,
l2cap_conn_param_update_destroy) < 0)
kfree(d);
}

return 0;
Expand Down
Loading