.. | .. |
---|
| 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); |
---|