/* The vendor specific WMT commands are all answered by a vendor * specific event and will not have the Command Status or Command * Complete as with usual HCI command flow control. * * After sending the command, wait for BTMTKUART_TX_WAIT_VND_EVT * state to be cleared. The driver specific event receive routine * will clear that state and with that indicate completion of the * WMT command.
*/
err = wait_on_bit_timeout(&bdev->tx_state, BTMTKUART_TX_WAIT_VND_EVT,
TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT); if (err == -EINTR) {
bt_dev_err(hdev, "Execution of wmt command interrupted");
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state); goto err_free_wc;
}
if (err) {
bt_dev_err(hdev, "Execution of wmt command timed out");
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
err = -ETIMEDOUT; goto err_free_wc;
}
/* Parse and handle the return WMT event */
wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data; if (wmt_evt->whdr.op != hdr->op) {
bt_dev_err(hdev, "Wrong op received %d expected %d",
wmt_evt->whdr.op, hdr->op);
err = -EIO; goto err_free_wc;
}
switch (wmt_evt->whdr.op) { case BTMTK_WMT_SEMAPHORE: if (wmt_evt->whdr.flag == 2)
status = BTMTK_WMT_PATCH_UNDONE; else
status = BTMTK_WMT_PATCH_DONE; break; case BTMTK_WMT_FUNC_CTRL:
wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
status = BTMTK_WMT_ON_DONE; elseif (be16_to_cpu(wmt_evt_funcc->status) == 0x420)
status = BTMTK_WMT_ON_PROGRESS; else
status = BTMTK_WMT_ON_UNDONE; break;
}
if (wmt_params->status)
*wmt_params->status = status;
/* When someone waits for the WMT event, the skb is being cloned * and being processed the events from there then.
*/ if (test_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state)) {
bdev->evt_skb = skb_clone(skb, GFP_KERNEL); if (!bdev->evt_skb) {
err = -ENOMEM; goto err_out;
}
}
err = hci_recv_frame(hdev, skb); if (err < 0) goto err_free_skb;
if (hdr->evt == HCI_EV_WMT) { if (test_and_clear_bit(BTMTKUART_TX_WAIT_VND_EVT,
&bdev->tx_state)) { /* Barrier to sync with other CPUs */
smp_mb__after_atomic();
wake_up_bit(&bdev->tx_state, BTMTKUART_TX_WAIT_VND_EVT);
}
}
staticvoid btmtkuart_tx_wakeup(struct btmtkuart_dev *bdev)
{ if (test_and_set_bit(BTMTKUART_TX_STATE_ACTIVE, &bdev->tx_state))
set_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state);
schedule_work(&bdev->tx_work);
}
staticconstunsignedchar *
mtk_stp_split(struct btmtkuart_dev *bdev, constunsignedchar *data, int count, int *sz_h4)
{ struct mtk_stp_hdr *shdr;
/* The cursor is reset when all the data of STP is consumed out */ if (!bdev->stp_dlen && bdev->stp_cursor >= 6)
bdev->stp_cursor = 0;
/* Filling pad until all STP info is obtained */ while (bdev->stp_cursor < 6 && count > 0) {
bdev->stp_pad[bdev->stp_cursor] = *data;
bdev->stp_cursor++;
data++;
count--;
}
/* Retrieve STP info and have a sanity check */ if (!bdev->stp_dlen && bdev->stp_cursor >= 6) {
shdr = (struct mtk_stp_hdr *)&bdev->stp_pad[2];
bdev->stp_dlen = be16_to_cpu(shdr->dlen) & 0x0fff;
/* Resync STP when unexpected data is being read */ if (shdr->prefix != 0x80 || bdev->stp_dlen > 2048) {
bt_dev_err(bdev->hdev, "stp format unexpected (%d, %d)",
shdr->prefix, bdev->stp_dlen);
bdev->stp_cursor = 2;
bdev->stp_dlen = 0;
}
}
/* Directly quit when there's no data found for H4 can process */ if (count <= 0) return NULL;
/* Translate to how much the size of data H4 can handle so far */
*sz_h4 = min_t(int, count, bdev->stp_dlen);
/* Update the remaining size of STP packet */
bdev->stp_dlen -= *sz_h4;
/* Data points to STP payload which can be handled by H4 */ return data;
}
while (sz_left > 0) { /* The serial data received from MT7622 BT controller is * at all time padded around with the STP header and tailer. * * A full STP packet is looking like * ----------------------------------- * | STP header | H:4 | STP tailer | * ----------------------------------- * but it doesn't guarantee to contain a full H:4 packet which * means that it's possible for multiple STP packets forms a * full H:4 packet that means extra STP header + length doesn't * indicate a full H:4 frame, things can fragment. Whose length * recorded in STP header just shows up the most length the * H:4 engine can handle currently.
*/
p_h4 = mtk_stp_split(bdev, p_left, sz_left, &sz_h4); if (!p_h4) break;
/* Enable the power domain and clock the device requires */
pm_runtime_enable(dev);
err = pm_runtime_resume_and_get(dev); if (err < 0) goto err_disable_rpm;
err = clk_prepare_enable(bdev->clk); if (err < 0) goto err_put_rpm;
/* Indicate the device to enter the probe state the host is * ready to change a new baudrate.
*/
baudrate = cpu_to_le32(bdev->desired_speed);
wmt_params.op = BTMTK_WMT_HIF;
wmt_params.flag = 1;
wmt_params.dlen = 4;
wmt_params.data = &baudrate;
wmt_params.status = NULL;
err = mtk_hci_wmt_sync(hdev, &wmt_params); if (err < 0) {
bt_dev_err(hdev, "Failed to device baudrate (%d)", err); return err;
}
err = serdev_device_set_baudrate(bdev->serdev,
bdev->desired_speed); if (err < 0) {
bt_dev_err(hdev, "Failed to set up host baudrate (%d)",
err); return err;
}
/* Wakeup MCUSYS is required for certain devices before we start to * do any setups.
*/ if (test_bit(BTMTKUART_REQUIRED_WAKEUP, &bdev->tx_state)) {
wmt_params.op = BTMTK_WMT_WAKEUP;
wmt_params.flag = 3;
wmt_params.dlen = 0;
wmt_params.data = NULL;
wmt_params.status = NULL;
err = mtk_hci_wmt_sync(hdev, &wmt_params); if (err < 0) {
bt_dev_err(hdev, "Failed to wakeup the chip (%d)", err); return err;
}
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* Make sure that there is enough rooms for STP header and trailer */ if (unlikely(skb_headroom(skb) < sizeof(*shdr)) ||
(skb_tailroom(skb) < MTK_STP_TLR_SIZE)) {
err = pskb_expand_head(skb, sizeof(*shdr), MTK_STP_TLR_SIZE,
GFP_ATOMIC); if (err < 0) return err;
}
/* Add the STP header */
dlen = skb->len;
shdr = skb_push(skb, sizeof(*shdr));
shdr->prefix = 0x80;
shdr->dlen = cpu_to_be16((dlen & 0x0fff) | (type << 12));
shdr->cs = 0; /* MT7622 doesn't care about checksum value */
/* Add the STP trailer */
skb_put_zero(skb, MTK_STP_TLR_SIZE);
if (btmtkuart_is_standalone(bdev)) {
err = clk_prepare_enable(bdev->osc); if (err < 0) goto err_hci_free_dev;
if (bdev->boot) {
gpiod_set_value_cansleep(bdev->boot, 1);
} else { /* Switch to the specific pin state for the booting * requires.
*/
pinctrl_select_state(bdev->pinctrl, bdev->pins_boot);
}
/* Power on */
err = regulator_enable(bdev->vcc); if (err < 0) goto err_clk_disable_unprepare;
/* Reset if the reset-gpios is available otherwise the board * -level design should be guaranteed.
*/ if (bdev->reset) {
gpiod_set_value_cansleep(bdev->reset, 1);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(bdev->reset, 0);
}
/* Wait some time until device got ready and switch to the pin * mode the device requires for UART transfers.
*/
msleep(50);
if (bdev->boot)
devm_gpiod_put(&serdev->dev, bdev->boot);
err_regulator_disable: if (btmtkuart_is_standalone(bdev))
regulator_disable(bdev->vcc);
err_clk_disable_unprepare: if (btmtkuart_is_standalone(bdev))
clk_disable_unprepare(bdev->osc);
err_hci_free_dev:
hci_free_dev(hdev);
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.