Skip to content

Commit

Permalink
Bluetooth: Introduce user channel flag for HCI devices
Browse files Browse the repository at this point in the history
This patch introduces a new user channel flag that allows to give full
control of a HCI device to a user application. The kernel will stay away
from the device and does not allow any further modifications of the
device states.

The existing raw flag is not used since it has a bit of unclear meaning
due to its legacy. Using a new flag makes the code clearer.

A device with the user channel flag set can still be enumerate using the
legacy API, but it does not longer enumerate using the new management
interface used by BlueZ 5 and beyond. This is intentional to not confuse
users of modern systems.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
  • Loading branch information
Marcel Holtmann authored and Gustavo Padovan committed Sep 16, 2013
1 parent c1c4f95 commit 0736cfa
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 9 deletions.
1 change: 1 addition & 0 deletions include/net/bluetooth/hci.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ enum {
HCI_SERVICE_CACHE,
HCI_DEBUG_KEYS,
HCI_UNREGISTER,
HCI_USER_CHANNEL,

HCI_LE_SCAN,
HCI_SSP_ENABLED,
Expand Down
39 changes: 36 additions & 3 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,11 @@ int hci_inquiry(void __user *arg)
if (!hdev)
return -ENODEV;

if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
err = -EBUSY;
goto done;
}

hci_dev_lock(hdev);
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
Expand Down Expand Up @@ -1177,7 +1182,8 @@ int hci_dev_open(__u16 dev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
set_bit(HCI_RAW, &hdev->flags);

if (!test_bit(HCI_RAW, &hdev->flags))
if (!test_bit(HCI_RAW, &hdev->flags) &&
!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
ret = __hci_init(hdev);
}

Expand All @@ -1188,6 +1194,7 @@ int hci_dev_open(__u16 dev)
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
mgmt_valid_hdev(hdev)) {
hci_dev_lock(hdev);
mgmt_powered(hdev, 1);
Expand Down Expand Up @@ -1324,11 +1331,17 @@ int hci_dev_close(__u16 dev)
if (!hdev)
return -ENODEV;

if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
err = -EBUSY;
goto done;
}

if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
cancel_delayed_work(&hdev->power_off);

err = hci_dev_do_close(hdev);

done:
hci_dev_put(hdev);
return err;
}
Expand All @@ -1349,6 +1362,11 @@ int hci_dev_reset(__u16 dev)
goto done;
}

if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
ret = -EBUSY;
goto done;
}

/* Drop queues */
skb_queue_purge(&hdev->rx_q);
skb_queue_purge(&hdev->cmd_q);
Expand Down Expand Up @@ -1382,10 +1400,15 @@ int hci_dev_reset_stat(__u16 dev)
if (!hdev)
return -ENODEV;

if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
ret = -EBUSY;
goto done;
}

memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));

done:
hci_dev_put(hdev);

return ret;
}

Expand All @@ -1402,6 +1425,11 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
if (!hdev)
return -ENODEV;

if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
err = -EBUSY;
goto done;
}

switch (cmd) {
case HCISETAUTH:
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
Expand Down Expand Up @@ -1460,6 +1488,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
break;
}

done:
hci_dev_put(hdev);
return err;
}
Expand Down Expand Up @@ -1568,6 +1597,9 @@ static int hci_rfkill_set_block(void *data, bool blocked)

BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);

if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
return -EBUSY;

if (!blocked)
return 0;

Expand Down Expand Up @@ -3459,7 +3491,8 @@ static void hci_rx_work(struct work_struct *work)
hci_send_to_sock(hdev, skb);
}

if (test_bit(HCI_RAW, &hdev->flags)) {
if (test_bit(HCI_RAW, &hdev->flags) ||
test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
kfree_skb(skb);
continue;
}
Expand Down
15 changes: 9 additions & 6 deletions net/bluetooth/hci_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,9 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
if (!hdev)
return -EBADFD;

if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
return -EBUSY;

switch (cmd) {
case HCISETRAW:
if (!capable(CAP_NET_ADMIN))
Expand Down Expand Up @@ -530,19 +533,19 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
if (!capable(CAP_NET_ADMIN))
return -EPERM;
return hci_sock_blacklist_del(hdev, (void __user *) arg);

default:
if (hdev->ioctl)
return hdev->ioctl(hdev, cmd, arg);
return -EINVAL;
}

if (hdev->ioctl)
return hdev->ioctl(hdev, cmd, arg);

return -EINVAL;
}

static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
struct sock *sk = sock->sk;
void __user *argp = (void __user *) arg;
struct sock *sk = sock->sk;
int err;

BT_DBG("cmd %x arg %lx", cmd, arg);
Expand Down
9 changes: 9 additions & 0 deletions net/bluetooth/mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
if (test_bit(HCI_SETUP, &d->dev_flags))
continue;

if (test_bit(HCI_USER_CHANNEL, &d->dev_flags))
continue;

if (!mgmt_valid_hdev(d))
continue;

Expand Down Expand Up @@ -3320,6 +3323,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
MGMT_STATUS_INVALID_INDEX);
goto done;
}

if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
err = cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
}

if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
Expand Down

0 comments on commit 0736cfa

Please sign in to comment.