summaryrefslogtreecommitdiff
path: root/drivers/bluetooth/hci_ldisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth/hci_ldisc.c')
-rw-r--r--drivers/bluetooth/hci_ldisc.c47
1 files changed, 40 insertions, 7 deletions
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 96bcec5598c2..85bb31b6052d 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -53,6 +53,16 @@
static const struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+static inline void hci_uart_proto_lock(struct hci_uart *hu)
+{
+ mutex_lock(&hu->proto_lock);
+}
+
+static inline void hci_uart_proto_unlock(struct hci_uart *hu)
+{
+ mutex_unlock(&hu->proto_lock);
+}
+
int hci_uart_register_proto(const struct hci_uart_proto *p)
{
if (p->id >= HCI_UART_MAX_PROTO)
@@ -113,8 +123,15 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
{
struct sk_buff *skb = hu->tx_skb;
- if (!skb)
+ if (!skb) {
+ hci_uart_proto_lock(hu);
+ if (!hu->proto) {
+ hci_uart_proto_unlock(hu);
+ return NULL;
+ }
skb = hu->proto->dequeue(hu);
+ hci_uart_proto_unlock(hu);
+ }
else
hu->tx_skb = NULL;
@@ -142,6 +159,8 @@ static void hci_uart_write_work(struct work_struct *work)
struct hci_dev *hdev = hu->hdev;
struct sk_buff *skb;
+ BT_DBG("hu %pK hdev %pK tty %pK", hu, hdev, tty);
+
/* REVISIT: should we cope with bad skbs or ->write() returning
* and error value ?
*/
@@ -205,7 +224,7 @@ int hci_uart_init_ready(struct hci_uart *hu)
/* Initialize device */
static int hci_uart_open(struct hci_dev *hdev)
{
- BT_DBG("%s %p", hdev->name, hdev);
+ BT_DBG("%s %pK", hdev->name, hdev);
/* Nothing to do for UART driver */
return 0;
@@ -217,7 +236,7 @@ static int hci_uart_flush(struct hci_dev *hdev)
struct hci_uart *hu = hci_get_drvdata(hdev);
struct tty_struct *tty = hu->tty;
- BT_DBG("hdev %p tty %p", hdev, tty);
+ BT_DBG("hdev %pK tty %pK", hdev, tty);
if (hu->tx_skb) {
kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
@@ -236,7 +255,7 @@ static int hci_uart_flush(struct hci_dev *hdev)
/* Close device */
static int hci_uart_close(struct hci_dev *hdev)
{
- BT_DBG("hdev %p", hdev);
+ BT_DBG("hdev %pK", hdev);
hci_uart_flush(hdev);
hdev->flush = NULL;
@@ -441,7 +460,7 @@ static int hci_uart_tty_open(struct tty_struct *tty)
{
struct hci_uart *hu;
- BT_DBG("tty %p", tty);
+ BT_DBG("tty %pK", tty);
/* Error if the tty has no write op instead of leaving an exploitable
hole */
@@ -461,6 +480,9 @@ static int hci_uart_tty_open(struct tty_struct *tty)
INIT_WORK(&hu->init_ready, hci_uart_init_work);
INIT_WORK(&hu->write_work, hci_uart_write_work);
+ spin_lock_init(&hu->rx_lock);
+ mutex_init(&hu->proto_lock);
+
/* Flush any pending characters in the driver and line discipline. */
/* FIXME: why is this needed. Note don't use ldisc_ref here as the
@@ -483,7 +505,7 @@ static void hci_uart_tty_close(struct tty_struct *tty)
struct hci_uart *hu = tty->disc_data;
struct hci_dev *hdev;
- BT_DBG("tty %p", tty);
+ BT_DBG("tty %pK", tty);
/* Detach from the tty */
tty->disc_data = NULL;
@@ -492,8 +514,11 @@ static void hci_uart_tty_close(struct tty_struct *tty)
return;
hdev = hu->hdev;
- if (hdev)
+ if (hdev) {
hci_uart_close(hdev);
+ if (test_bit(HCI_UART_REGISTERED, &hu->flags))
+ hci_unregister_dev(hdev);
+ }
cancel_work_sync(&hu->write_work);
@@ -503,9 +528,17 @@ static void hci_uart_tty_close(struct tty_struct *tty)
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
+ hci_uart_proto_lock(hu);
hu->proto->close(hu);
+ hu->proto = NULL;
+ hci_uart_proto_unlock(hu);
}
+ cancel_work_sync(&hu->write_work);
+
+ if (hdev)
+ hci_free_dev(hdev);
+ mutex_destroy(&hu->proto_lock);
kfree(hu);
}