Пример #1
0
static void pass_skb_to_net(struct mem_link_device *mld, struct sk_buff *skb)
{
	struct link_device *ld = &mld->link_dev;
	struct skbuff_private *priv;
	struct io_device *iod;
	int ret;

	priv = skbpriv(skb);
	if (unlikely(!priv)) {
		mif_err("%s: ERR! No PRIV in skb@%p\n", ld->name, skb);
		dev_kfree_skb_any(skb);
		modemctl_notify_event(MDM_CRASH_INVALID_SKBCB);
		return;
	}

	iod = priv->iod;
	if (unlikely(!iod)) {
		mif_err("%s: ERR! No IOD in skb@%p\n", ld->name, skb);
		dev_kfree_skb_any(skb);
		modemctl_notify_event(MDM_CRASH_INVALID_SKBIOD);
		return;
	}

	log_ipc_pkt(LNK_RX, iod->id, skb);

	ret = iod->recv_net_skb(iod, ld, skb);
	if (unlikely(ret < 0)) {
		struct modem_ctl *mc = ld->mc;
		mif_err_limited("%s: %s<-%s: ERR! %s->recv_net_skb fail (%d)\n",
				ld->name, iod->name, mc->name, iod->name, ret);
		dev_kfree_skb_any(skb);
	}
}
Пример #2
0
/**
@brief		extract all IPC link frames from an SBD RB

In a while loop,\n
1) receives each IPC link frame stored in the @b @@RB.\n
2) passes it to the DEMUX layer immediately.\n

@param rb	the pointer to a mem_ring_buffer instance

@retval "> 0"	if valid data received
@retval "= 0"	if no data received
@retval "< 0"	if ANY error
*/
static int rx_ipc_frames_from_rb(struct sbd_ring_buffer *rb)
{
	int rcvd = 0;
	struct link_device *ld = rb->ld;
	struct mem_link_device *mld = ld_to_mem_link_device(ld);
	unsigned int qlen = rb->len;
	unsigned int in = *rb->wp;
	unsigned int out = *rb->rp;
	unsigned int num_frames = circ_get_usage(qlen, in, out);

	while (rcvd < num_frames) {
		struct sk_buff *skb;

		skb = sbd_pio_rx(rb);
		if (!skb) {
#ifdef CONFIG_SEC_MODEM_DEBUG
			panic("skb alloc failed.");
#else
			modemctl_notify_event(MDM_CRASH_NO_MEM);
#endif
			break;
		}

		/* The $rcvd must be accumulated here, because $skb can be freed
		   in pass_skb_to_demux(). */
		rcvd++;

		if (skbpriv(skb)->lnk_hdr) {
			u8 ch = rb->ch;
			u8 fch = sipc5_get_ch(skb->data);
			if (fch != ch) {
				mif_err("frm.ch:%d != rb.ch:%d\n", fch, ch);
				dev_kfree_skb_any(skb);

				modemctl_notify_event(MDM_EVENT_CP_ABNORMAL_RX);
				continue;
			}
		}

		pass_skb_to_demux(mld, skb);
	}

	if (rcvd < num_frames) {
		struct io_device *iod = rb->iod;
		struct modem_ctl *mc = ld->mc;
		mif_err("%s: %s<-%s: WARN! rcvd %d < num_frames %d\n",
			ld->name, iod->name, mc->name, rcvd, num_frames);
	}

	return rcvd;
}
Пример #3
0
static bool forbid_cp_sleep_wait(struct mem_link_device *mld)
{
	long res;

	if (mld->forbid_cp_sleep)
		mld->forbid_cp_sleep(mld);

#ifndef CONFIG_SEC_MODEM_XMM7260_CAT6
	if (atomic_read(&mld->cp_boot_done)) {
#endif
		/* As of now, tx path of iosm message should always guarantee
		 * process context. We don't have to care rx path because cp
		 * might try to mount lli i/f before sending data. */
		res = wait_event_interruptible_timeout(mld->wq,
				mld->link_active(mld), msecs_to_jiffies(5000));
		switch (res) {
		case 0:
			mif_err("timeout for link active event\n");
#ifdef CONFIG_SEC_MODEM_XMM7260_CAT6
			modemctl_notify_event(MDM_CRASH_BY_IOSM);
#endif
			return false;
		case -ERESTARTSYS:
			mif_info("woken by a signal\n");
			return false;
		default:
			mif_info("got link active event\n");
		}
#ifndef CONFIG_SEC_MODEM_XMM7260_CAT6
	}
#endif

	return true;
}
Пример #4
0
static void pm_cp_fail_cb(struct modem_link_pm *pm)
{
	struct link_device *ld = pm_to_link_device(pm);
	struct mem_link_device *mld = ld_to_mem_link_device(ld);
	struct modem_ctl *mc = ld->mc;
	struct io_device *iod = mc->iod;

	unsigned long flags;

	spin_lock_irqsave(&mc->lock, flags);

	if (cp_online(mc)) {
		spin_unlock_irqrestore(&mc->lock, flags);

		if (mld->stop_pm)
			mld->stop_pm(mld);

		modemctl_notify_event(MDM_CRASH_PM_CP_FAIL);
		return;
	}

	if (cp_booting(mc)) {
		iod->modem_state_changed(iod, STATE_OFFLINE);
		ld->reload(ld);
		spin_unlock_irqrestore(&mc->lock, flags);
		return;
	}

	spin_unlock_irqrestore(&mc->lock, flags);
}
static inline bool lli_check_max_intr(void)
{
	if (++intr_cnt >= max_intr) {
		if (time_before(jiffies, expired)) {
			mif_info("cp_crash due to irq cnt %d\n", intr_cnt);
			intr_cnt = 0;
			modemctl_notify_event(MDM_EVENT_CP_FORCE_CRASH);
			return true;
		}
		lli_mark_last_busy();
	}
	return false;
}
Пример #6
0
static void cmd_crash_reset_handler(struct mem_link_device *mld)
{
	struct link_device *ld = &mld->link_dev;
	struct modem_ctl *mc = ld->mc;
	unsigned long flags;

	spin_lock_irqsave(&ld->lock, flags);
	ld->state = LINK_STATE_OFFLINE;
	spin_unlock_irqrestore(&ld->lock, flags);

	mif_err("%s<-%s: ERR! CP_CRASH_RESET\n", ld->name, mc->name);

	modemctl_notify_event(MDM_CRASH_CMD_RESET);
}
Пример #7
0
static void cmd_crash_exit_handler(struct mem_link_device *mld)
{
	struct link_device *ld = &mld->link_dev;
	struct modem_ctl *mc = ld->mc;
	unsigned long flags;

	spin_lock_irqsave(&ld->lock, flags);
	ld->state = LINK_STATE_CP_CRASH;
	spin_unlock_irqrestore(&ld->lock, flags);

	if (timer_pending(&mc->crash_ack_timer))
		del_timer(&mc->crash_ack_timer);

	mif_err("%s<-%s: ERR! CP_CRASH_EXIT\n", ld->name, mc->name);

	modemctl_notify_event(MDM_CRASH_CMD_EXIT);
}
Пример #8
0
/**
@brief		pass a socket buffer to the DEMUX layer

Invokes the recv_skb_single method in the io_device instance to perform
receiving IPC messages from each skb.

@param mld	the pointer to a mem_link_device instance
@param skb	the pointer to an sk_buff instance

@retval "> 0"	if succeeded to pass an @b @@skb to the DEMUX layer
@retval "< 0"	an error code
*/
static void pass_skb_to_demux(struct mem_link_device *mld, struct sk_buff *skb)
{
	struct link_device *ld = &mld->link_dev;
	struct io_device *iod = skbpriv(skb)->iod;
	int ret;
	u8 ch = skbpriv(skb)->sipc_ch;

	if (unlikely(!iod)) {
		mif_err("%s: ERR! No IOD for CH.%d\n", ld->name, ch);
		dev_kfree_skb_any(skb);
		modemctl_notify_event(MDM_CRASH_INVALID_IOD);
		return;
	}

	log_ipc_pkt(LNK_RX, ch, skb);

	ret = iod->recv_skb_single(iod, ld, skb);
	if (unlikely(ret < 0)) {
		struct modem_ctl *mc = ld->mc;
		mif_err_limited("%s: %s<-%s: ERR! %s->recv_skb fail (%d)\n",
				ld->name, iod->name, mc->name, iod->name, ret);
		dev_kfree_skb_any(skb);
	}
}
Пример #9
0
static void pm_fail_cb(struct modem_link_pm *pm)
{
	modemctl_notify_event(MDM_CRASH_PM_FAIL);
}
Пример #10
0
static void pm_fail_cb(struct modem_link_pm *pm)
{
    mipi_lli_debug_info();
    modemctl_notify_event(MDM_CRASH_PM_FAIL);
}
Пример #11
0
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;
}
Пример #12
0
void mem_forced_cp_crash(struct mem_link_device *mld)
{
	struct link_device *ld = &mld->link_dev;
	struct modem_ctl *mc = ld->mc;
	bool duplicated = false;
	unsigned long flags;

	/* Disable normal IPC */
	set_magic(mld, MEM_CRASH_MAGIC);
	set_access(mld, 0);

	spin_lock_irqsave(&mld->lock, flags);
	if (atomic_read(&mc->forced_cp_crash))
		duplicated = true;
	else
		atomic_set(&mc->forced_cp_crash, 1);
	spin_unlock_irqrestore(&mld->lock, flags);

	if (duplicated) {
		evt_log(0, "%s: %s: ALREADY in progress <%pf>\n",
			FUNC, ld->name, CALLER);
		return;
	}

	if (!cp_online(mc)) {
		evt_log(0, "%s: %s: %s.state %s != ONLINE <%pf>\n",
			FUNC, ld->name, mc->name, mc_state(mc), CALLER);
		return;
	}

	if (mc->wake_lock) {
		if (!wake_lock_active(mc->wake_lock)) {
			wake_lock(mc->wake_lock);
			mif_err("%s->wake_lock locked\n", mc->name);
		}
	}

	if (mld->attrs & LINK_ATTR(LINK_ATTR_MEM_DUMP)) {
		stop_net_ifaces(ld);

		if (mld->debug_info)
			mld->debug_info();

		/**
		 * If there is no CRASH_ACK from CP in a timeout,
		 * handle_no_cp_crash_ack() will be executed.
		 */
		mif_add_timer(&mc->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT,
			      handle_no_cp_crash_ack, (unsigned long)mld);

		/* Send CRASH_EXIT command to a CP */
		send_ipc_irq(mld, cmd2int(CMD_CRASH_EXIT));
	} else {
		modemctl_notify_event(MDM_EVENT_CP_FORCE_CRASH);
	}

	evt_log(0, "%s->%s: CP_CRASH_REQ <%pf>\n", ld->name, mc->name, CALLER);

#ifdef DEBUG_MODEM_IF
	if (in_interrupt())
		queue_work(system_nrt_wq, &mld->dump_work);
	else
		save_mem_dump(mld);
#endif
}