// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instruments' Bluetooth HCILL UART protocol * * HCILL (HCI Low Level) is a Texas Instruments' power management * protocol extension to H4. * * Copyright (C) 2007 Texas Instruments, Inc. * * Written by Ohad Ben-Cohen <ohad@bencohen.org> * * Acknowledgements: * This file is based on hci_h4.c, which was written * by Maxim Krasnyansky and Marcel Holtmann.
*/
if (hu->serdev) { struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev);
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
clk_disable_unprepare(lldev->ext_clk);
}
hu->priv = NULL;
kfree(ll);
return 0;
}
/* * internal function, which does common work of the device wake up process: * 1. places all pending packets (waiting in tx_wait_q list) in txq list. * 2. changes internal state to HCILL_AWAKE. * Note: assumes that hcill_lock spinlock is taken, * shouldn't be called otherwise!
*/ staticvoid __ll_do_awake(struct ll_struct *ll)
{ struct sk_buff *skb = NULL;
while ((skb = skb_dequeue(&ll->tx_wait_q)))
skb_queue_tail(&ll->txq, skb);
ll->hcill_state = HCILL_AWAKE;
}
/* * Called upon a wake-up-indication from the device
*/ staticvoid ll_device_want_to_wakeup(struct hci_uart *hu)
{ unsignedlong flags; struct ll_struct *ll = hu->priv;
BT_DBG("hu %p", hu);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
switch (ll->hcill_state) { case HCILL_ASLEEP_TO_AWAKE: /* * This state means that both the host and the BRF chip * have simultaneously sent a wake-up-indication packet. * Traditionally, in this case, receiving a wake-up-indication * was enough and an additional wake-up-ack wasn't needed. * This has changed with the BRF6350, which does require an * explicit wake-up-ack. Other BRF versions, which do not * require an explicit ack here, do accept it, thus it is * perfectly safe to always send one.
*/
BT_DBG("dual wake-up-indication");
fallthrough; case HCILL_ASLEEP: /* acknowledge device wake up */ if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) {
BT_ERR("cannot acknowledge device wake up"); goto out;
} break; default: /* any other state is illegal */
BT_ERR("received HCILL_WAKE_UP_IND in state %ld",
ll->hcill_state); break;
}
/* send pending packets and change state to HCILL_AWAKE */
__ll_do_awake(ll);
/* actually send the packets */
hci_uart_tx_wakeup(hu);
}
/* * Called upon a sleep-indication from the device
*/ staticvoid ll_device_want_to_sleep(struct hci_uart *hu)
{ unsignedlong flags; struct ll_struct *ll = hu->priv;
BT_DBG("hu %p", hu);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
/* sanity check */ if (ll->hcill_state != HCILL_AWAKE)
BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld",
ll->hcill_state);
/* actually send the sleep ack packet */
hci_uart_tx_wakeup(hu);
}
/* * Called upon wake-up-acknowledgement from the device
*/ staticvoid ll_device_woke_up(struct hci_uart *hu)
{ unsignedlong flags; struct ll_struct *ll = hu->priv;
BT_DBG("hu %p", hu);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
/* sanity check */ if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE)
BT_ERR("received HCILL_WAKE_UP_ACK in state %ld",
ll->hcill_state);
/* send pending packets and change state to HCILL_AWAKE */
__ll_do_awake(ll);
spin_unlock_irqrestore(&ll->hcill_lock, flags);
/* actually send the packets */
hci_uart_tx_wakeup(hu);
}
/* Enqueue frame for transmission (padding, crc, etc) */ /* may be called from two simultaneous tasklets */ staticint ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{ unsignedlong flags = 0; struct ll_struct *ll = hu->priv;
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
/* act according to current state */ switch (ll->hcill_state) { case HCILL_AWAKE:
BT_DBG("device awake, sending normally");
skb_queue_tail(&ll->txq, skb); break; case HCILL_ASLEEP:
BT_DBG("device asleep, waking up and queueing packet"); /* save packet for later */
skb_queue_tail(&ll->tx_wait_q, skb); /* awake device */ if (send_hcill_cmd(HCILL_WAKE_UP_IND, hu) < 0) {
BT_ERR("cannot wake up device"); break;
}
ll->hcill_state = HCILL_ASLEEP_TO_AWAKE; break; case HCILL_ASLEEP_TO_AWAKE:
BT_DBG("device waking up, queueing packet"); /* transient state; just keep packet for later */
skb_queue_tail(&ll->tx_wait_q, skb); break; default:
BT_ERR("illegal hcill state: %ld (losing packet)",
ll->hcill_state);
dev_kfree_skb_irq(skb); break;
}
/* HCI_VS_WRITE_BD_ADDR (at least on a CC2560A chip) expects the BD * address to be MSB first, but bdaddr_t has the convention of being * LSB first.
*/
baswap(&bdaddr_swapped, bdaddr);
skb = __hci_cmd_sync(hdev, HCI_VS_WRITE_BD_ADDR, sizeof(bdaddr_t),
&bdaddr_swapped, HCI_INIT_TIMEOUT); if (!IS_ERR(skb))
kfree_skb(skb);
do { /* Reset the Bluetooth device */
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
msleep(5);
gpiod_set_value_cansleep(lldev->enable_gpio, 1);
mdelay(100);
err = serdev_device_wait_for_cts(serdev, true, 200); if (err) {
bt_dev_err(hu->hdev, "Failed to get CTS"); return err;
}
err = download_firmware(lldev); if (!err) break;
/* Toggle BT_EN and retry */
bt_dev_err(hu->hdev, "download firmware failed, retrying...");
} while (retry--);
if (err) return err;
/* Set BD address if one was specified at probe */ if (!bacmp(&lldev->bdaddr, BDADDR_NONE)) { /* This means that there was an error getting the BD address * during probe, so mark the device as having a bad address.
*/
hci_set_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR);
} elseif (bacmp(&lldev->bdaddr, BDADDR_ANY)) {
err = ll_set_bdaddr(hu->hdev, &lldev->bdaddr); if (err)
hci_set_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR);
}
/* Operational speed if any */ if (hu->oper_speed)
speed = hu->oper_speed; elseif (hu->proto->oper_speed)
speed = hu->proto->oper_speed; else
speed = 0;
if (speed) {
__le32 speed_le = cpu_to_le32(speed); struct sk_buff *skb;
/* optional BD address from nvram */
bdaddr_cell = nvmem_cell_get(&serdev->dev, "bd-address"); if (IS_ERR(bdaddr_cell)) { int err = PTR_ERR(bdaddr_cell);
if (err == -EPROBE_DEFER) return err;
/* ENOENT means there is no matching nvmem cell and ENOSYS * means that nvmem is not enabled in the kernel configuration.
*/ if (err != -ENOENT && err != -ENOSYS) { /* If there was some other error, give userspace a * chance to fix the problem instead of failing to load * the driver. Using BDADDR_NONE as a flag that is * tested later in the setup function.
*/
dev_warn(&serdev->dev, "Failed to get \"bd-address\" nvmem cell (%d)\n",
err);
bacpy(&lldev->bdaddr, BDADDR_NONE);
}
} else {
bdaddr_t *bdaddr;
size_t len;
/* As per the device tree bindings, the value from nvmem is * expected to be MSB first, but in the kernel it is expected * that bdaddr_t is LSB first.
*/
baswap(&lldev->bdaddr, bdaddr);
kfree(bdaddr);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.