Loading drivers/bluetooth/hci_ibs.c +110 −27 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -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 */ Loading Loading @@ -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: Loading Loading
drivers/bluetooth/hci_ibs.c +110 −27 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -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 */ Loading Loading @@ -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: Loading