static int rx_frames_from_dev(struct mem_link_device *mld, struct mem_ipc_device *dev) { struct link_device *ld = &mld->link_dev; struct sk_buff_head *skb_rxq = dev->skb_rxq; unsigned int qsize = get_rxq_buff_size(dev); unsigned int in = get_rxq_head(dev); unsigned int out = get_rxq_tail(dev); unsigned int size = circ_get_usage(qsize, in, out); int rcvd = 0; if (unlikely(circ_empty(in, out))) return 0; while (rcvd < size) { struct sk_buff *skb; u8 ch; struct io_device *iod; skb = rxq_read(mld, dev, in); if (!skb) break; ch = sipc5_get_ch(skb->data); iod = link_get_iod_with_channel(ld, ch); if (!iod) { mif_err("%s: ERR! No IOD for CH.%d\n", ld->name, ch); dev_kfree_skb_any(skb); mem_forced_cp_crash(mld); break; } /* Record the IO device and the link device into the &skb->cb */ skbpriv(skb)->iod = iod; skbpriv(skb)->ld = ld; skbpriv(skb)->lnk_hdr = iod->link_header; skbpriv(skb)->sipc_ch = ch; /* The $rcvd must be accumulated here, because $skb can be freed in pass_skb_to_demux(). */ rcvd += skb->len; if (likely(sipc_ps_ch(ch))) skb_queue_tail(skb_rxq, skb); else pass_skb_to_demux(mld, skb); } if (rcvd < size) { struct link_device *ld = &mld->link_dev; mif_err("%s: WARN! rcvd %d < size %d\n", ld->name, rcvd, size); } return rcvd; }
/** @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; }
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) { /* TODO : Replace with panic() */ mem_forced_cp_crash(mld); 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); 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; }
static inline void link_to_demux(struct mem_link_device *mld) { int i; for (i = 0; i < MAX_SIPC5_DEVICES; i++) { struct mem_ipc_device *dev = mld->dev[i]; struct sk_buff_head *skb_rxq = dev->skb_rxq; while (1) { struct sk_buff *skb; skb = skb_dequeue(skb_rxq); if (!skb) break; pass_skb_to_demux(mld, skb); } } }
/** @brief extract all IPC link frames from a circular queue In a while loop,\n 1) Receives each IPC link frame stored in the @b @@dev RXQ.\n 2) If the frame is a PS (network) data frame, stores it to an skb_rxq and schedules a delayed work for PS data reception.\n 3) Otherwise, passes it to the DEMUX layer immediately.\n @param mld the pointer to a mem_link_device instance @param dev the pointer to a mem_ipc_device instance (IPC_FMT, etc.) @retval "> 0" if valid data received @retval "= 0" if no data received @retval "< 0" if ANY error */ static int rx_frames_from_dev(struct mem_link_device *mld, struct mem_ipc_device *dev) { struct sk_buff_head *skb_rxq = dev->skb_rxq; unsigned int qsize = get_rxq_buff_size(dev); unsigned int in = get_rxq_head(dev); unsigned int out = get_rxq_tail(dev); unsigned int size = circ_get_usage(qsize, in, out); int rcvd = 0; if (unlikely(circ_empty(in, out))) return 0; while (rcvd < size) { struct sk_buff *skb; skb = rxq_read(mld, dev, in); if (!skb) break; /* The $rcvd must be accumulated here, because $skb can be freed in pass_skb_to_demux(). */ rcvd += skb->len; if (likely(sipc_ps_ch(sipc5_get_ch_id(skb->data)))) skb_queue_tail(skb_rxq, skb); else pass_skb_to_demux(mld, skb); } #ifdef DEBUG_MODEM_IF if (rcvd < size) { struct link_device *ld = &mld->link_dev; mif_err("%s: WARN! rcvd %d < size %d\n", ld->name, rcvd, size); } #endif return rcvd; }
/** @brief pass socket buffers in every skb_rxq to the DEMUX layer @param ws the pointer to a work_struct instance @see schedule_link_to_demux() @see rx_frames_from_dev() @see mem_create_link_device() */ static void link_to_demux_work(struct work_struct *ws) { struct link_device *ld; struct mem_link_device *mld; int i; ld = container_of(ws, struct link_device, rx_delayed_work.work); mld = to_mem_link_device(ld); for (i = IPC_FMT; i < MAX_SIPC5_DEV; i++) { struct mem_ipc_device *dev = mld->dev[i]; struct sk_buff_head *skb_rxq = dev->skb_rxq; while (1) { struct sk_buff *skb; skb = skb_dequeue(skb_rxq); if (!skb) break; pass_skb_to_demux(mld, skb); } } }