[27/46] Bluetooth: Let HIDP grab the device reference for connections

From: Greg KH
Date: Fri Oct 16 2009 - 13:24:54 EST


2.6.31-stable review patch. If anyone has any objections, please let us know.

------------------
From: Marcel Holtmann <marcel@xxxxxxxxxxxx>

commit edad63886993d18ab800c49f6587a93432ef8b35 upstream.

The core exports the hci_conn_hold_device() and hci_conn_put_device()
functions for device reference of connections. Use this to ensure that
the uevents from the parent are send after the child ones.

Based on a report by Brian Rogers <brian@xxxxxxxx>

Signed-off-by: Marcel Holtmann <marcel@xxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>

---
net/bluetooth/hidp/core.c | 62 ++++++++++++++++++++++++++++++----------------
net/bluetooth/hidp/hidp.h | 2 +
2 files changed, 43 insertions(+), 21 deletions(-)

--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -93,10 +93,14 @@ static void __hidp_link_session(struct h
{
__module_get(THIS_MODULE);
list_add(&session->list, &hidp_session_list);
+
+ hci_conn_hold_device(session->conn);
}

static void __hidp_unlink_session(struct hidp_session *session)
{
+ hci_conn_put_device(session->conn);
+
list_del(&session->list);
module_put(THIS_MODULE);
}
@@ -576,7 +580,9 @@ static int hidp_session(void *arg)
hidinput_disconnect(session->hid);
if (session->hid->claimed & HID_CLAIMED_HIDRAW)
hidraw_disconnect(session->hid);
+
hid_destroy_device(session->hid);
+ session->hid = NULL;
}

/* Wakeup user-space polling for socket errors */
@@ -604,25 +610,27 @@ static struct device *hidp_get_device(st
{
bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
+ struct device *device = NULL;
struct hci_dev *hdev;
- struct hci_conn *conn;

hdev = hci_get_route(dst, src);
if (!hdev)
return NULL;

- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
+ session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
+ if (session->conn)
+ device = &session->conn->dev;

hci_dev_put(hdev);

- return conn ? &conn->dev : NULL;
+ return device;
}

static int hidp_setup_input(struct hidp_session *session,
struct hidp_connadd_req *req)
{
struct input_dev *input;
- int i;
+ int err, i;

input = input_allocate_device();
if (!input)
@@ -669,7 +677,13 @@ static int hidp_setup_input(struct hidp_

input->event = hidp_input_event;

- return input_register_device(input);
+ err = input_register_device(input);
+ if (err < 0) {
+ hci_conn_put_device(session->conn);
+ return err;
+ }
+
+ return 0;
}

static int hidp_open(struct hid_device *hid)
@@ -751,13 +765,11 @@ static int hidp_setup_hid(struct hidp_se
{
struct hid_device *hid;
bdaddr_t src, dst;
- int ret;
+ int err;

hid = hid_allocate_device();
- if (IS_ERR(hid)) {
- ret = PTR_ERR(session->hid);
- goto err;
- }
+ if (IS_ERR(hid))
+ return PTR_ERR(session->hid);

session->hid = hid;
session->req = req;
@@ -779,16 +791,17 @@ static int hidp_setup_hid(struct hidp_se
hid->dev.parent = hidp_get_device(session);
hid->ll_driver = &hidp_hid_driver;

- ret = hid_add_device(hid);
- if (ret)
- goto err_hid;
+ err = hid_add_device(hid);
+ if (err < 0)
+ goto failed;

return 0;
-err_hid:
+
+failed:
hid_destroy_device(hid);
session->hid = NULL;
-err:
- return ret;
+
+ return err;
}

int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
@@ -838,13 +851,13 @@ int hidp_add_connection(struct hidp_conn
if (req->rd_size > 0) {
err = hidp_setup_hid(session, req);
if (err && err != -ENODEV)
- goto err_skb;
+ goto purge;
}

if (!session->hid) {
err = hidp_setup_input(session, req);
if (err < 0)
- goto err_skb;
+ goto purge;
}

__hidp_link_session(session);
@@ -872,13 +885,20 @@ unlink:

__hidp_unlink_session(session);

- if (session->input)
+ if (session->input) {
input_unregister_device(session->input);
- if (session->hid)
+ session->input = NULL;
+ }
+
+ if (session->hid) {
hid_destroy_device(session->hid);
-err_skb:
+ session->hid = NULL;
+ }
+
+purge:
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
+
failed:
up_write(&hidp_session_sem);

--- a/net/bluetooth/hidp/hidp.h
+++ b/net/bluetooth/hidp/hidp.h
@@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_connin
struct hidp_session {
struct list_head list;

+ struct hci_conn *conn;
+
struct socket *ctrl_sock;
struct socket *intr_sock;



--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/