diff options
Diffstat (limited to 'drivers/bluetooth/hci_ldisc.c')
| -rw-r--r-- | drivers/bluetooth/hci_ldisc.c | 162 |
1 files changed, 103 insertions, 59 deletions
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 720148294e6..e00f8f5b5c8 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -48,8 +48,6 @@ #define VERSION "2.2" -static int reset = 0; - static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO]; int hci_uart_register_proto(struct hci_uart_proto *p) @@ -120,10 +118,6 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) int hci_uart_tx_wakeup(struct hci_uart *hu) { - struct tty_struct *tty = hu->tty; - struct hci_dev *hdev = hu->hdev; - struct sk_buff *skb; - if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); return 0; @@ -131,6 +125,22 @@ int hci_uart_tx_wakeup(struct hci_uart *hu) BT_DBG(""); + schedule_work(&hu->write_work); + + return 0; +} + +static void hci_uart_write_work(struct work_struct *work) +{ + struct hci_uart *hu = container_of(work, struct hci_uart, write_work); + struct tty_struct *tty = hu->tty; + struct hci_dev *hdev = hu->hdev; + struct sk_buff *skb; + + /* REVISIT: should we cope with bad skbs or ->write() returning + * and error value ? + */ + restart: clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); @@ -155,6 +165,34 @@ restart: goto restart; clear_bit(HCI_UART_SENDING, &hu->tx_state); +} + +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; } @@ -174,7 +212,7 @@ static int hci_uart_open(struct hci_dev *hdev) /* Reset device */ static int hci_uart_flush(struct hci_dev *hdev) { - struct hci_uart *hu = (struct hci_uart *) hdev->driver_data; + struct hci_uart *hu = hci_get_drvdata(hdev); struct tty_struct *tty = hu->tty; BT_DBG("hdev %p tty %p", hdev, tty); @@ -207,21 +245,13 @@ static int hci_uart_close(struct hci_dev *hdev) } /* Send frames from HCI layer */ -static int hci_uart_send_frame(struct sk_buff *skb) +static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev* hdev = (struct hci_dev *) skb->dev; - struct hci_uart *hu; - - if (!hdev) { - BT_ERR("Frame for unknown device (hdev=NULL)"); - return -ENODEV; - } + struct hci_uart *hu = hci_get_drvdata(hdev); if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - hu = (struct hci_uart *) hdev->driver_data; - BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); hu->proto->enqueue(hu, skb); @@ -231,42 +261,29 @@ static int hci_uart_send_frame(struct sk_buff *skb) return 0; } -static void hci_uart_destruct(struct hci_dev *hdev) -{ - if (!hdev) - return; - - BT_DBG("%s", hdev->name); - kfree(hdev->driver_data); -} - /* ------ LDISC part ------ */ /* hci_uart_tty_open - * + * * Called when line discipline changed to HCI_UART. * * Arguments: * tty pointer to tty info structure - * Return Value: + * Return Value: * 0 if success, otherwise error code */ static int hci_uart_tty_open(struct tty_struct *tty) { - struct hci_uart *hu = (void *) tty->disc_data; + struct hci_uart *hu; BT_DBG("tty %p", tty); - /* FIXME: This btw is bogus, nothing requires the old ldisc to clear - the pointer */ - if (hu) - return -EEXIST; - /* Error if the tty has no write op instead of leaving an exploitable hole */ if (tty->ops->write == NULL) return -EOPNOTSUPP; - if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) { + hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL); + if (!hu) { BT_ERR("Can't allocate control structure"); return -ENFILE; } @@ -275,6 +292,9 @@ 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); + INIT_WORK(&hu->write_work, hci_uart_write_work); + spin_lock_init(&hu->rx_lock); /* Flush any pending characters in the driver and line discipline. */ @@ -297,24 +317,32 @@ static int hci_uart_tty_open(struct tty_struct *tty) static void hci_uart_tty_close(struct tty_struct *tty) { struct hci_uart *hu = (void *)tty->disc_data; + struct hci_dev *hdev; BT_DBG("tty %p", tty); /* Detach from the tty */ tty->disc_data = NULL; - if (hu) { - struct hci_dev *hdev = hu->hdev; + if (!hu) + return; - if (hdev) - hci_uart_close(hdev); + hdev = hu->hdev; + if (hdev) + hci_uart_close(hdev); - if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { - hu->proto->close(hu); - hci_unregister_dev(hdev); + cancel_work_sync(&hu->write_work); + + if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { + if (hdev) { + if (test_bit(HCI_UART_REGISTERED, &hu->flags)) + hci_unregister_dev(hdev); hci_free_dev(hdev); } + hu->proto->close(hu); } + + kfree(hu); } /* hci_uart_tty_wakeup() @@ -344,15 +372,15 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) } /* hci_uart_tty_receive() - * + * * Called by tty low level driver when receive data is * available. - * + * * Arguments: tty pointer to tty isntance data * data pointer to received data * flags pointer to flags for data * count count of received data in bytes - * + * * Return Value: None */ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) @@ -367,7 +395,10 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *f spin_lock(&hu->rx_lock); hu->proto->recv(hu, (void *) data, count); - hu->hdev->stat.byte_rx += count; + + if (hu->hdev) + hu->hdev->stat.byte_rx += count; + spin_unlock(&hu->rx_lock); tty_unthrottle(tty); @@ -389,28 +420,36 @@ static int hci_uart_register_dev(struct hci_uart *hu) hu->hdev = hdev; hdev->bus = HCI_UART; - hdev->driver_data = hu; + hci_set_drvdata(hdev, hu); hdev->open = hci_uart_open; hdev->close = hci_uart_close; hdev->flush = hci_uart_flush; hdev->send = hci_uart_send_frame; - hdev->destruct = hci_uart_destruct; - - hdev->owner = THIS_MODULE; - - if (!reset) - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + SET_HCIDEV_DEV(hdev, hu->tty->dev); if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); + + if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) + hdev->dev_type = HCI_AMP; + 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; } @@ -497,7 +536,7 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, default: err = n_tty_ioctl_helper(tty, file, cmd, arg); break; - }; + } return err; } @@ -545,7 +584,8 @@ static int __init hci_uart_init(void) hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup; hci_uart_ldisc.owner = THIS_MODULE; - if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) { + err = tty_register_ldisc(N_HCI, &hci_uart_ldisc); + if (err) { BT_ERR("HCI line discipline registration failed. (%d)", err); return err; } @@ -562,6 +602,9 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_ATH3K ath_init(); #endif +#ifdef CONFIG_BT_HCIUART_3WIRE + h5_init(); +#endif return 0; } @@ -582,18 +625,19 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_ATH3K ath_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_3WIRE + h5_deinit(); +#endif /* Release tty registration of line discipline */ - if ((err = tty_unregister_ldisc(N_HCI))) + err = tty_unregister_ldisc(N_HCI); + if (err) BT_ERR("Can't unregister HCI line discipline (%d)", err); } module_init(hci_uart_init); module_exit(hci_uart_exit); -module_param(reset, bool, 0644); -MODULE_PARM_DESC(reset, "Send HCI reset command on initialization"); - MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION); MODULE_VERSION(VERSION); |
