| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Bluetooth HCI serdev driver lib |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 8 | 9 | * Copyright (C) 2000-2001 Qualcomm Incorporated |
|---|
| 9 | 10 | * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> |
|---|
| 10 | 11 | * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 13 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 14 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 15 | | - * (at your option) any later version. |
|---|
| 16 | | - * |
|---|
| 17 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 18 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 19 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 20 | | - * GNU General Public License for more details. |
|---|
| 21 | | - * |
|---|
| 22 | 12 | */ |
|---|
| 23 | 13 | |
|---|
| 24 | 14 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 30 | 20 | #include <net/bluetooth/hci_core.h> |
|---|
| 31 | 21 | |
|---|
| 32 | 22 | #include "hci_uart.h" |
|---|
| 33 | | - |
|---|
| 34 | | -static struct serdev_device_ops hci_serdev_client_ops; |
|---|
| 35 | 23 | |
|---|
| 36 | 24 | static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type) |
|---|
| 37 | 25 | { |
|---|
| .. | .. |
|---|
| 57 | 45 | { |
|---|
| 58 | 46 | struct sk_buff *skb = hu->tx_skb; |
|---|
| 59 | 47 | |
|---|
| 60 | | - if (!skb) |
|---|
| 61 | | - skb = hu->proto->dequeue(hu); |
|---|
| 62 | | - else |
|---|
| 48 | + if (!skb) { |
|---|
| 49 | + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) |
|---|
| 50 | + skb = hu->proto->dequeue(hu); |
|---|
| 51 | + } else |
|---|
| 63 | 52 | hu->tx_skb = NULL; |
|---|
| 64 | 53 | |
|---|
| 65 | 54 | return skb; |
|---|
| .. | .. |
|---|
| 94 | 83 | hci_uart_tx_complete(hu, hci_skb_pkt_type(skb)); |
|---|
| 95 | 84 | kfree_skb(skb); |
|---|
| 96 | 85 | } |
|---|
| 97 | | - } while(test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)); |
|---|
| 98 | 86 | |
|---|
| 99 | | - clear_bit(HCI_UART_SENDING, &hu->tx_state); |
|---|
| 87 | + clear_bit(HCI_UART_SENDING, &hu->tx_state); |
|---|
| 88 | + } while (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)); |
|---|
| 100 | 89 | } |
|---|
| 101 | 90 | |
|---|
| 102 | 91 | /* ------- Interface to HCI layer ------ */ |
|---|
| .. | .. |
|---|
| 124 | 113 | /* Initialize device */ |
|---|
| 125 | 114 | static int hci_uart_open(struct hci_dev *hdev) |
|---|
| 126 | 115 | { |
|---|
| 116 | + struct hci_uart *hu = hci_get_drvdata(hdev); |
|---|
| 117 | + int err; |
|---|
| 118 | + |
|---|
| 127 | 119 | BT_DBG("%s %p", hdev->name, hdev); |
|---|
| 120 | + |
|---|
| 121 | + /* When Quirk HCI_QUIRK_NON_PERSISTENT_SETUP is set by |
|---|
| 122 | + * driver, BT SoC is completely turned OFF during |
|---|
| 123 | + * BT OFF. Upon next BT ON UART port should be opened. |
|---|
| 124 | + */ |
|---|
| 125 | + if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) { |
|---|
| 126 | + err = serdev_device_open(hu->serdev); |
|---|
| 127 | + if (err) |
|---|
| 128 | + return err; |
|---|
| 129 | + set_bit(HCI_UART_PROTO_READY, &hu->flags); |
|---|
| 130 | + } |
|---|
| 128 | 131 | |
|---|
| 129 | 132 | /* Undo clearing this from hci_uart_close() */ |
|---|
| 130 | 133 | hdev->flush = hci_uart_flush; |
|---|
| .. | .. |
|---|
| 135 | 138 | /* Close device */ |
|---|
| 136 | 139 | static int hci_uart_close(struct hci_dev *hdev) |
|---|
| 137 | 140 | { |
|---|
| 141 | + struct hci_uart *hu = hci_get_drvdata(hdev); |
|---|
| 142 | + |
|---|
| 138 | 143 | BT_DBG("hdev %p", hdev); |
|---|
| 144 | + |
|---|
| 145 | + if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) |
|---|
| 146 | + return 0; |
|---|
| 139 | 147 | |
|---|
| 140 | 148 | hci_uart_flush(hdev); |
|---|
| 141 | 149 | hdev->flush = NULL; |
|---|
| 150 | + |
|---|
| 151 | + /* When QUIRK HCI_QUIRK_NON_PERSISTENT_SETUP is set by driver, |
|---|
| 152 | + * BT SOC is completely powered OFF during BT OFF, holding port |
|---|
| 153 | + * open may drain the battery. |
|---|
| 154 | + */ |
|---|
| 155 | + if (test_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks)) { |
|---|
| 156 | + clear_bit(HCI_UART_PROTO_READY, &hu->flags); |
|---|
| 157 | + serdev_device_close(hu->serdev); |
|---|
| 158 | + } |
|---|
| 142 | 159 | |
|---|
| 143 | 160 | return 0; |
|---|
| 144 | 161 | } |
|---|
| .. | .. |
|---|
| 269 | 286 | return count; |
|---|
| 270 | 287 | } |
|---|
| 271 | 288 | |
|---|
| 272 | | -static struct serdev_device_ops hci_serdev_client_ops = { |
|---|
| 289 | +static const struct serdev_device_ops hci_serdev_client_ops = { |
|---|
| 273 | 290 | .receive_buf = hci_uart_receive_buf, |
|---|
| 274 | 291 | .write_wakeup = hci_uart_write_wakeup, |
|---|
| 275 | 292 | }; |
|---|
| .. | .. |
|---|
| 284 | 301 | |
|---|
| 285 | 302 | serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops); |
|---|
| 286 | 303 | |
|---|
| 304 | + if (percpu_init_rwsem(&hu->proto_lock)) |
|---|
| 305 | + return -ENOMEM; |
|---|
| 306 | + |
|---|
| 287 | 307 | err = serdev_device_open(hu->serdev); |
|---|
| 288 | 308 | if (err) |
|---|
| 289 | | - return err; |
|---|
| 309 | + goto err_rwsem; |
|---|
| 290 | 310 | |
|---|
| 291 | 311 | err = p->open(hu); |
|---|
| 292 | 312 | if (err) |
|---|
| .. | .. |
|---|
| 310 | 330 | |
|---|
| 311 | 331 | INIT_WORK(&hu->init_ready, hci_uart_init_work); |
|---|
| 312 | 332 | INIT_WORK(&hu->write_work, hci_uart_write_work); |
|---|
| 313 | | - percpu_init_rwsem(&hu->proto_lock); |
|---|
| 314 | 333 | |
|---|
| 315 | 334 | /* Only when vendor specific setup callback is provided, consider |
|---|
| 316 | 335 | * the manufacturer information valid. This avoids filling in the |
|---|
| .. | .. |
|---|
| 331 | 350 | |
|---|
| 332 | 351 | if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags)) |
|---|
| 333 | 352 | set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); |
|---|
| 334 | | - |
|---|
| 335 | | - if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) |
|---|
| 336 | | - set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); |
|---|
| 337 | 353 | |
|---|
| 338 | 354 | if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) |
|---|
| 339 | 355 | hdev->dev_type = HCI_AMP; |
|---|
| .. | .. |
|---|
| 360 | 376 | p->close(hu); |
|---|
| 361 | 377 | err_open: |
|---|
| 362 | 378 | serdev_device_close(hu->serdev); |
|---|
| 379 | +err_rwsem: |
|---|
| 380 | + percpu_free_rwsem(&hu->proto_lock); |
|---|
| 363 | 381 | return err; |
|---|
| 364 | 382 | } |
|---|
| 365 | 383 | EXPORT_SYMBOL_GPL(hci_uart_register_device); |
|---|
| .. | .. |
|---|
| 367 | 385 | void hci_uart_unregister_device(struct hci_uart *hu) |
|---|
| 368 | 386 | { |
|---|
| 369 | 387 | struct hci_dev *hdev = hu->hdev; |
|---|
| 370 | | - |
|---|
| 371 | | - clear_bit(HCI_UART_PROTO_READY, &hu->flags); |
|---|
| 372 | 388 | |
|---|
| 373 | 389 | cancel_work_sync(&hu->init_ready); |
|---|
| 374 | 390 | if (test_bit(HCI_UART_REGISTERED, &hu->flags)) |
|---|
| .. | .. |
|---|
| 378 | 394 | cancel_work_sync(&hu->write_work); |
|---|
| 379 | 395 | |
|---|
| 380 | 396 | hu->proto->close(hu); |
|---|
| 381 | | - serdev_device_close(hu->serdev); |
|---|
| 397 | + |
|---|
| 398 | + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { |
|---|
| 399 | + clear_bit(HCI_UART_PROTO_READY, &hu->flags); |
|---|
| 400 | + serdev_device_close(hu->serdev); |
|---|
| 401 | + } |
|---|
| 402 | + percpu_free_rwsem(&hu->proto_lock); |
|---|
| 382 | 403 | } |
|---|
| 383 | 404 | EXPORT_SYMBOL_GPL(hci_uart_unregister_device); |
|---|