/** @brief check whether or not TX is possible via the link @param mld the pointer to a mem_link_device instance @param dev the pointer to a mem_ipc_device instance (IPC_FMT, etc.) @param skb the pointer to an skb that will be transmitted @retval "> 0" the size of the data in @b @@skb if there is NO ERROR @retval "< 0" an error code (-EIO or -EBUSY) */ static inline int check_tx_link(struct mem_link_device *mld, struct mem_ipc_device *dev, struct sk_buff *skb) { struct link_device *ld = &mld->link_dev; struct modem_ctl *mc = ld->mc; struct sk_buff_head *skb_txq = dev->skb_txq; int ret = skb->len; if (unlikely(cp_online(mc) && !ipc_active(mld))) return -EIO; if (unlikely(skb_txq->qlen >= MAX_SKB_TXQ_DEPTH)) { #ifdef DEBUG_MODEM_IF struct io_device *iod = skbpriv(skb)->iod; mif_debug("%s: %s->%s: ERR! %s "\ "SKB_TXQ qlen %d >= limit %d\n", ld->name, iod->name, mc->name, dev->name, skb_txq->qlen, MAX_SKB_TXQ_DEPTH); #endif return -EBUSY; } return ret; }
/** @brief function for the @b send method in a link_device instance @param ld the pointer to a link_device instance @param iod the pointer to an io_device instance @param skb the pointer to an skb that will be transmitted @retval "> 0" the length of data transmitted if there is NO ERROR @retval "< 0" an error code */ static int mem_send(struct link_device *ld, struct io_device *iod, struct sk_buff *skb) { struct mem_link_device *mld = to_mem_link_device(ld); struct modem_ctl *mc = ld->mc; enum dev_format id = iod->format; u8 ch = iod->id; switch (id) { case IPC_FMT: case IPC_RAW: case IPC_RFS: if (likely(sipc5_ipc_ch(ch))) { if (unlikely(!ipc_active(mld))) return -EIO; if (iod->sbd_ipc) { if (likely(sbd_active(&mld->sbd_link_dev))) return xmit_ipc_to_rb(mld, ch, skb); else return -ENODEV; } else { BUG_ON(1); } } else { return xmit_udl(mld, iod, ch, skb); } break; case IPC_BOOT: case IPC_DUMP: if (sipc5_udl_ch(ch)) return xmit_udl(mld, iod, ch, skb); break; default: break; } mif_err("%s:%s->%s: ERR! Invalid IO device (format:%s id:%d)\n", ld->name, iod->name, mc->name, dev_str(id), ch); return -ENODEV; }
static int xmit_ipc(struct mem_link_device *mld, struct io_device *iod, enum sipc_ch_id ch, struct sk_buff *skb) { if (unlikely(!ipc_active(mld))) return -EIO; #ifdef CONFIG_LINK_DEVICE_WITH_SBD_ARCH if (iod->sbd_ipc) { if (likely(sbd_active(&mld->sbd_link_dev))) return xmit_ipc_to_rb(mld, ch, skb); else return -ENODEV; } else { return xmit_ipc_to_dev(mld, ch, skb); } #else return xmit_ipc_to_dev(mld, ch, skb); #endif }
static enum hrtimer_restart tx_timer_func(struct hrtimer *timer) { struct mem_link_device *mld; struct link_device *ld; struct modem_ctl *mc; int i; bool need_schedule; u16 mask; mld = container_of(timer, struct mem_link_device, tx_timer); ld = &mld->link_dev; mc = ld->mc; if (unlikely(cp_online(mc) && !ipc_active(mld))) goto exit; need_schedule = false; mask = 0; for (i = IPC_FMT; i < MAX_SIPC5_DEV; i++) { struct mem_ipc_device *dev = mld->dev[i]; int ret; if (unlikely(under_tx_flow_ctrl(mld, dev))) { ret = check_tx_flow_ctrl(mld, dev); if (ret < 0) { if (ret == -EBUSY || ret == -ETIME) { need_schedule = true; continue; } else { mem_forced_cp_crash(mld); goto exit; } } } ret = tx_frames_to_dev(mld, dev); if (unlikely(ret < 0)) { if (ret == -EBUSY || ret == -ENOSPC) { need_schedule = true; start_tx_flow_ctrl(mld, dev); continue; } else { mem_forced_cp_crash(mld); goto exit; } } mask |= msg_mask(dev); if (!skb_queue_empty(dev->skb_txq)) need_schedule = true; } if (mask) send_ipc_irq(mld, mask2int(mask)); if (need_schedule) { ktime_t ktime = ns_to_ktime(ms2ns(TX_PERIOD_MS)); hrtimer_forward_now(timer, ktime); return HRTIMER_RESTART; } exit: #ifdef CONFIG_LINK_POWER_MANAGEMENT mld->permit_cp_sleep(mld); #endif return HRTIMER_NORESTART; }
static enum hrtimer_restart sbd_tx_timer_func(struct hrtimer *timer) { struct mem_link_device *mld; struct link_device *ld; struct modem_ctl *mc; struct sbd_link_device *sl; int i; bool need_schedule; u16 mask; unsigned long flags = 0; mld = container_of(timer, struct mem_link_device, sbd_tx_timer); ld = &mld->link_dev; mc = ld->mc; sl = &mld->sbd_link_dev; need_schedule = false; mask = 0; spin_lock_irqsave(&mc->lock, flags); if (unlikely(!ipc_active(mld))) { spin_unlock_irqrestore(&mc->lock, flags); goto exit; } spin_unlock_irqrestore(&mc->lock, flags); if (mld->link_active) { if (!mld->link_active(mld)) { need_schedule = true; goto exit; } } for (i = 0; i < sl->num_channels; i++) { struct sbd_ring_buffer *rb = sbd_id2rb(sl, i, TX); int ret; ret = tx_frames_to_rb(rb); if (unlikely(ret < 0)) { if (ret == -EBUSY || ret == -ENOSPC) { need_schedule = true; mask = MASK_SEND_DATA; continue; } else { modemctl_notify_event(MDM_CRASH_INVALID_RB); need_schedule = false; goto exit; } } if (ret > 0) mask = MASK_SEND_DATA; if (!skb_queue_empty(&rb->skb_q)) need_schedule = true; } if (!need_schedule) { for (i = 0; i < sl->num_channels; i++) { struct sbd_ring_buffer *rb; rb = sbd_id2rb(sl, i, TX); if (!rb_empty(rb)) { need_schedule = true; break; } } } if (mask) { spin_lock_irqsave(&mc->lock, flags); if (unlikely(!ipc_active(mld))) { spin_unlock_irqrestore(&mc->lock, flags); need_schedule = false; goto exit; } send_ipc_irq(mld, mask2int(mask)); spin_unlock_irqrestore(&mc->lock, flags); } exit: if (need_schedule) { ktime_t ktime = ktime_set(0, ms2ns(TX_PERIOD_MS)); hrtimer_start(timer, ktime, HRTIMER_MODE_REL); } return HRTIMER_NORESTART; }
static enum hrtimer_restart tx_timer_func(struct hrtimer *timer) { struct mem_link_device *mld; struct link_device *ld; struct modem_ctl *mc; int i; bool need_schedule; u16 mask; unsigned long flags; mld = container_of(timer, struct mem_link_device, tx_timer); ld = &mld->link_dev; mc = ld->mc; need_schedule = false; mask = 0; spin_lock_irqsave(&mc->lock, flags); if (unlikely(!ipc_active(mld))) goto exit; #ifdef CONFIG_LINK_POWER_MANAGEMENT_WITH_FSM if (mld->link_active) { if (!mld->link_active(mld)) { need_schedule = true; goto exit; } } #endif for (i = 0; i < MAX_SIPC5_DEVICES; i++) { struct mem_ipc_device *dev = mld->dev[i]; int ret; if (unlikely(under_tx_flow_ctrl(mld, dev))) { ret = check_tx_flow_ctrl(mld, dev); if (ret < 0) { if (ret == -EBUSY || ret == -ETIME) { need_schedule = true; continue; } else { mem_forced_cp_crash(mld); need_schedule = false; goto exit; } } } ret = tx_frames_to_dev(mld, dev); if (unlikely(ret < 0)) { if (ret == -EBUSY || ret == -ENOSPC) { need_schedule = true; start_tx_flow_ctrl(mld, dev); continue; } else { mem_forced_cp_crash(mld); need_schedule = false; goto exit; } } if (ret > 0) mask |= msg_mask(dev); if (!skb_queue_empty(dev->skb_txq)) need_schedule = true; } if (!need_schedule) { for (i = 0; i < MAX_SIPC5_DEVICES; i++) { if (!txq_empty(mld->dev[i])) { need_schedule = true; break; } } } if (mask) send_ipc_irq(mld, mask2int(mask)); exit: if (need_schedule) { ktime_t ktime = ktime_set(0, ms2ns(TX_PERIOD_MS)); hrtimer_start(timer, ktime, HRTIMER_MODE_REL); } spin_unlock_irqrestore(&mc->lock, flags); return HRTIMER_NORESTART; }