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 <brian@xyzw.org>

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
Marcel Holtmann 2009-08-22 14:22:15 -07:00
parent 9eba32b86d
commit edad638869
2 changed files with 43 additions and 21 deletions

View File

@ -93,10 +93,14 @@ static void __hidp_link_session(struct hidp_session *session)
{ {
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
list_add(&session->list, &hidp_session_list); list_add(&session->list, &hidp_session_list);
hci_conn_hold_device(session->conn);
} }
static void __hidp_unlink_session(struct hidp_session *session) static void __hidp_unlink_session(struct hidp_session *session)
{ {
hci_conn_put_device(session->conn);
list_del(&session->list); list_del(&session->list);
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
@ -577,7 +581,9 @@ static int hidp_session(void *arg)
hidinput_disconnect(session->hid); hidinput_disconnect(session->hid);
if (session->hid->claimed & HID_CLAIMED_HIDRAW) if (session->hid->claimed & HID_CLAIMED_HIDRAW)
hidraw_disconnect(session->hid); hidraw_disconnect(session->hid);
hid_destroy_device(session->hid); hid_destroy_device(session->hid);
session->hid = NULL;
} }
/* Wakeup user-space polling for socket errors */ /* 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 *src = &bt_sk(session->ctrl_sock->sk)->src;
bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
struct device *device = NULL;
struct hci_dev *hdev; struct hci_dev *hdev;
struct hci_conn *conn;
hdev = hci_get_route(dst, src); hdev = hci_get_route(dst, src);
if (!hdev) if (!hdev)
return NULL; 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); hci_dev_put(hdev);
return conn ? &conn->dev : NULL; return device;
} }
static int hidp_setup_input(struct hidp_session *session, static int hidp_setup_input(struct hidp_session *session,
struct hidp_connadd_req *req) struct hidp_connadd_req *req)
{ {
struct input_dev *input; struct input_dev *input;
int i; int err, i;
input = input_allocate_device(); input = input_allocate_device();
if (!input) if (!input)
@ -670,7 +678,13 @@ static int hidp_setup_input(struct hidp_session *session,
input->event = hidp_input_event; 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) 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; struct hid_device *hid;
bdaddr_t src, dst; bdaddr_t src, dst;
int ret; int err;
hid = hid_allocate_device(); hid = hid_allocate_device();
if (IS_ERR(hid)) { if (IS_ERR(hid))
ret = PTR_ERR(session->hid); return PTR_ERR(session->hid);
goto err;
}
session->hid = hid; session->hid = hid;
session->req = req; session->req = req;
@ -780,16 +792,17 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->dev.parent = hidp_get_device(session); hid->dev.parent = hidp_get_device(session);
hid->ll_driver = &hidp_hid_driver; hid->ll_driver = &hidp_hid_driver;
ret = hid_add_device(hid); err = hid_add_device(hid);
if (ret) if (err < 0)
goto err_hid; goto failed;
return 0; return 0;
err_hid:
failed:
hid_destroy_device(hid); hid_destroy_device(hid);
session->hid = NULL; 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) 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) { if (req->rd_size > 0) {
err = hidp_setup_hid(session, req); err = hidp_setup_hid(session, req);
if (err && err != -ENODEV) if (err && err != -ENODEV)
goto err_skb; goto purge;
} }
if (!session->hid) { if (!session->hid) {
err = hidp_setup_input(session, req); err = hidp_setup_input(session, req);
if (err < 0) if (err < 0)
goto err_skb; goto purge;
} }
__hidp_link_session(session); __hidp_link_session(session);
@ -873,13 +886,20 @@ unlink:
__hidp_unlink_session(session); __hidp_unlink_session(session);
if (session->input) if (session->input) {
input_unregister_device(session->input); input_unregister_device(session->input);
if (session->hid) session->input = NULL;
}
if (session->hid) {
hid_destroy_device(session->hid); hid_destroy_device(session->hid);
err_skb: session->hid = NULL;
}
purge:
skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit); skb_queue_purge(&session->intr_transmit);
failed: failed:
up_write(&hidp_session_sem); up_write(&hidp_session_sem);

View File

@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_conninfo *ci);
struct hidp_session { struct hidp_session {
struct list_head list; struct list_head list;
struct hci_conn *conn;
struct socket *ctrl_sock; struct socket *ctrl_sock;
struct socket *intr_sock; struct socket *intr_sock;