From edad63886993d18ab800c49f6587a93432ef8b35 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 22 Aug 2009 14:22:15 -0700 Subject: Bluetooth: Let HIDP grab the device reference for connections 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 Signed-off-by: Marcel Holtmann --- net/bluetooth/hidp/core.c | 62 +++++++++++++++++++++++++++++++---------------- net/bluetooth/hidp/hidp.h | 2 ++ 2 files changed, 43 insertions(+), 21 deletions(-) (limited to 'net/bluetooth/hidp') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index f912d6537180..09bedeb5579c 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -93,10 +93,14 @@ static void __hidp_link_session(struct hidp_session *session) { __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); } @@ -577,7 +581,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 */ @@ -605,25 +611,27 @@ static struct device *hidp_get_device(struct hidp_session *session) { 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) @@ -670,7 +678,13 @@ static int hidp_setup_input(struct hidp_session *session, 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) @@ -752,13 +766,11 @@ static int hidp_setup_hid(struct hidp_session *session, { 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; @@ -780,16 +792,17 @@ static int hidp_setup_hid(struct hidp_session *session, 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) @@ -839,13 +852,13 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, 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); @@ -873,13 +886,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); diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index e503c89057ad..faf3d74c3586 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_conninfo *ci); struct hidp_session { struct list_head list; + struct hci_conn *conn; + struct socket *ctrl_sock; struct socket *intr_sock; -- cgit v1.2.2