Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit deca2019 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "bluetooth: hci_ibs: Vote on/off UART clocks in non atomic context."

parents 1580b057 5cc098d6
Loading
Loading
Loading
Loading
+110 −27
Original line number Diff line number Diff line
@@ -107,6 +107,13 @@ struct ibs_struct {
	unsigned long rx_vote;		/* clock must be on for RX */
	struct	timer_list tx_idle_timer;
	struct	timer_list wake_retrans_timer;
	struct	workqueue_struct *workqueue;
	struct	work_struct ws_awake_rx;
	struct	work_struct ws_awake_device;
	struct	work_struct ws_rx_vote_off;
	struct	work_struct ws_tx_vote_off;
	void *ibs_hu; /* keeps the hci_uart pointer for reference */

	/* debug */
	unsigned long ibs_sent_wacks;
	unsigned long ibs_sent_slps;
@@ -237,12 +244,90 @@ out:
	return err;
}

static void ibs_wq_awake_device(struct work_struct *work)
{
	struct ibs_struct *ibs = container_of(work, struct ibs_struct,
					ws_awake_device);
	struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;

	BT_DBG(" %p ", hu);

	/* Vote for serial clock */
	ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);

	spin_lock(&ibs->hci_ibs_lock);

	/* send wake indication to device */
	if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0)
		BT_ERR("cannot send WAKE to device");

	ibs->ibs_sent_wakes++; /* debug */

	/* start retransmit timer */
	mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);

	spin_unlock(&ibs->hci_ibs_lock);
}

static void ibs_wq_awake_rx(struct work_struct *work)
{
	struct ibs_struct *ibs = container_of(work, struct ibs_struct,
					ws_awake_rx);
	struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;

	BT_DBG(" %p ", hu);

	ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);

	spin_lock(&ibs->hci_ibs_lock);
	ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
	/* Always acknowledge device wake up,
	 * sending IBS message doesn't count as TX ON
	 */
	if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0)
		BT_ERR("cannot acknowledge device wake up");

	ibs->ibs_sent_wacks++; /* debug */

	spin_unlock(&ibs->hci_ibs_lock);
	/* actually send the packets */
	hci_uart_tx_wakeup(hu);

}

static void ibs_wq_serial_rx_clock_vote_off(struct work_struct *work)
{
	struct ibs_struct *ibs = container_of(work, struct ibs_struct,
					ws_rx_vote_off);
	struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;

	BT_DBG(" %p ", hu);

	ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);

}

static void ibs_wq_serial_tx_clock_vote_off(struct work_struct *work)
{
	struct ibs_struct *ibs = container_of(work, struct ibs_struct,
					ws_tx_vote_off);
	struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;

	BT_DBG(" %p ", hu);

	hci_uart_tx_wakeup(hu);  /* run HCI tx handling unlocked */

	/* now that message queued to tty driver, vote for tty clocks off */
	/* It is up to the tty driver to pend the clocks off until tx done. */
	ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);

}

static void hci_ibs_tx_idle_timeout(unsigned long arg)
{
	struct hci_uart *hu = (struct hci_uart *) arg;
	struct ibs_struct *ibs = hu->priv;
	unsigned long flags;
	unsigned long vote_tx_sleep = 0;

	BT_DBG("hu %p idle timeout in %lu state", hu, ibs->tx_ibs_state);

@@ -262,22 +347,11 @@ static void hci_ibs_tx_idle_timeout(unsigned long arg)
		}
		ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
		ibs->ibs_sent_slps++; /* debug */
		vote_tx_sleep = 1;
		break;
	}

	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);

	hci_uart_tx_wakeup(hu);  /* run HCI tx handling unlocked */

	if (!vote_tx_sleep)
		return;
	/* now that message queued to tty driver, vote for tty clocks off */
	/* It is up to the tty driver to pend the clocks off until tx done. */
	queue_work(ibs->workqueue, &ibs->ws_tx_vote_off);

	spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
					flags, SINGLE_DEPTH_NESTING);
	ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
out:
	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
}
@@ -331,6 +405,19 @@ static int ibs_open(struct hci_uart *hu)
	skb_queue_head_init(&ibs->txq);
	skb_queue_head_init(&ibs->tx_wait_q);
	spin_lock_init(&ibs->hci_ibs_lock);
	ibs->workqueue = create_singlethread_workqueue("ibs_wq");
	if (!ibs->workqueue) {
		BT_ERR("IBS Workqueue not initialized properly");
		kfree(ibs);
		return -ENOMEM;
	}

	INIT_WORK(&ibs->ws_awake_rx, ibs_wq_awake_rx);
	INIT_WORK(&ibs->ws_awake_device, ibs_wq_awake_device);
	INIT_WORK(&ibs->ws_rx_vote_off, ibs_wq_serial_rx_clock_vote_off);
	INIT_WORK(&ibs->ws_tx_vote_off, ibs_wq_serial_tx_clock_vote_off);

	ibs->ibs_hu = (void *)hu;

	/* Assume we start with both sides asleep -- extra wakes OK */
	ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
@@ -427,6 +514,8 @@ static int ibs_close(struct hci_uart *hu)
	skb_queue_purge(&ibs->txq);
	del_timer(&ibs->tx_idle_timer);
	del_timer(&ibs->wake_retrans_timer);
	destroy_workqueue(ibs->workqueue);
	ibs->ibs_hu = NULL;

	kfree_skb(ibs->rx_skb);

@@ -458,9 +547,10 @@ static void ibs_device_want_to_wakeup(struct hci_uart *hu)
		/* Make sure clock is on - we may have turned clock off since
		 * receiving the wake up indicator
		 */
		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
		ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
		/* deliberate fall-through */
		/* awake rx clock */
		queue_work(ibs->workqueue, &ibs->ws_awake_rx);
		spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
		return;
	case HCI_IBS_RX_AWAKE:
		/* Always acknowledge device wake up,
		 * sending IBS message doesn't count as TX ON.
@@ -505,7 +595,8 @@ static void ibs_device_want_to_sleep(struct hci_uart *hu)
	case HCI_IBS_RX_AWAKE:
		/* update state */
		ibs->rx_ibs_state = HCI_IBS_RX_ASLEEP;
		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
		/* vote off rx clock under workqueue */
		queue_work(ibs->workqueue, &ibs->ws_rx_vote_off);
		break;
	case HCI_IBS_RX_ASLEEP:
		/* deliberate fall-through */
@@ -590,20 +681,12 @@ static int ibs_enqueue(struct hci_uart *hu, struct sk_buff *skb)

	case HCI_IBS_TX_ASLEEP:
		BT_DBG("device asleep, waking up and queueing packet");
		ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
		/* save packet for later */
		skb_queue_tail(&ibs->tx_wait_q, skb);
		/* awake device */
		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
			BT_ERR("cannot send WAKE to device");
			break;
		}
		ibs->ibs_sent_wakes++; /* debug */

		/* start retransmit timer */
		mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);

		ibs->tx_ibs_state = HCI_IBS_TX_WAKING;
		/* schedule a work queue to wake up device */
		queue_work(ibs->workqueue, &ibs->ws_awake_device);
		break;

	case HCI_IBS_TX_WAKING: