Bluetooth: Add delayed init sequence support for UART controllers
authorJohan Hedberg <johan.hedberg@intel.com>
Mon, 16 Jul 2012 13:12:11 +0000 (16:12 +0300)
committerGustavo Padovan <gustavo.padovan@collabora.co.uk>
Tue, 17 Jul 2012 17:48:29 +0000 (14:48 -0300)
This patch makes it possible to have UART drivers perform an internal
initialization before calling hci_register_dev. This allows moving a lot
of init code from user space (hciattach) to the kernel side, thereby
creating a more controlled/robust initialization process.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_uart.h

index b6d1f200401a4f04d0a449706e8b955c75c5fdf5..74e0966b3ead0bbcf3678ebea52ea6886a65baee 100644 (file)
@@ -156,6 +156,35 @@ restart:
        return 0;
 }
 
+static void hci_uart_init_work(struct work_struct *work)
+{
+       struct hci_uart *hu = container_of(work, struct hci_uart, init_ready);
+       int err;
+
+       if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
+               return;
+
+       err = hci_register_dev(hu->hdev);
+       if (err < 0) {
+               BT_ERR("Can't register HCI device");
+               hci_free_dev(hu->hdev);
+               hu->hdev = NULL;
+               hu->proto->close(hu);
+       }
+
+       set_bit(HCI_UART_REGISTERED, &hu->flags);
+}
+
+int hci_uart_init_ready(struct hci_uart *hu)
+{
+       if (!test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
+               return -EALREADY;
+
+       schedule_work(&hu->init_ready);
+
+       return 0;
+}
+
 /* ------- Interface to HCI layer ------ */
 /* Initialize device */
 static int hci_uart_open(struct hci_dev *hdev)
@@ -264,6 +293,8 @@ static int hci_uart_tty_open(struct tty_struct *tty)
        hu->tty = tty;
        tty->receive_room = 65536;
 
+       INIT_WORK(&hu->init_ready, hci_uart_init_work);
+
        spin_lock_init(&hu->rx_lock);
 
        /* Flush any pending characters in the driver and line discipline. */
@@ -302,7 +333,8 @@ static void hci_uart_tty_close(struct tty_struct *tty)
 
        if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
                if (hdev) {
-                       hci_unregister_dev(hdev);
+                       if (test_bit(HCI_UART_REGISTERED, &hu->flags))
+                               hci_unregister_dev(hdev);
                        hci_free_dev(hdev);
                }
                hu->proto->close(hu);
@@ -402,12 +434,17 @@ static int hci_uart_register_dev(struct hci_uart *hu)
        else
                hdev->dev_type = HCI_BREDR;
 
+       if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
+               return 0;
+
        if (hci_register_dev(hdev) < 0) {
                BT_ERR("Can't register HCI device");
                hci_free_dev(hdev);
                return -ENODEV;
        }
 
+       set_bit(HCI_UART_REGISTERED, &hu->flags);
+
        return 0;
 }
 
index aaf9d7de1b9f17573238a33ec1ffb4a71e91ec79..fffa61ff5cb14983baeba17a4d6234a58de1e806 100644 (file)
@@ -47,6 +47,7 @@
 #define HCI_UART_RAW_DEVICE    0
 #define HCI_UART_RESET_ON_INIT 1
 #define HCI_UART_CREATE_AMP    2
+#define HCI_UART_INIT_PENDING  3
 
 struct hci_uart;
 
@@ -66,6 +67,8 @@ struct hci_uart {
        unsigned long           flags;
        unsigned long           hdev_flags;
 
+       struct work_struct      init_ready;
+
        struct hci_uart_proto   *proto;
        void                    *priv;
 
@@ -76,6 +79,7 @@ struct hci_uart {
 
 /* HCI_UART proto flag bits */
 #define HCI_UART_PROTO_SET     0
+#define HCI_UART_REGISTERED    1
 
 /* TX states  */
 #define HCI_UART_SENDING       1
@@ -84,6 +88,7 @@ struct hci_uart {
 int hci_uart_register_proto(struct hci_uart_proto *p);
 int hci_uart_unregister_proto(struct hci_uart_proto *p);
 int hci_uart_tx_wakeup(struct hci_uart *hu);
+int hci_uart_init_ready(struct hci_uart *hu);
 
 #ifdef CONFIG_BT_HCIUART_H4
 int h4_init(void);