aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/modem_if/sipc5_io_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/modem_if/sipc5_io_device.c')
-rw-r--r--drivers/misc/modem_if/sipc5_io_device.c1561
1 files changed, 807 insertions, 754 deletions
diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c
index a9932c1..71596ae 100644
--- a/drivers/misc/modem_if/sipc5_io_device.c
+++ b/drivers/misc/modem_if/sipc5_io_device.c
@@ -1,5 +1,4 @@
-/* /linux/drivers/misc/modem_if/sipc5_io_device.c
- *
+/*
* Copyright (C) 2010 Samsung Electronics.
*
* This software is licensed under the terms of the GNU General Public
@@ -24,11 +23,9 @@
#include <linux/if_ether.h>
#include <linux/etherdevice.h>
#include <linux/device.h>
+#include <linux/module.h>
-#include <linux/platform_data/modem.h>
-#ifdef CONFIG_LINK_DEVICE_C2C
-#include <linux/platform_data/c2c.h>
-#endif
+#include "modem.h"
#include "modem_prj.h"
#include "modem_utils.h"
@@ -104,7 +101,7 @@ static void iodev_showtxlink(struct io_device *iod, void *args)
struct link_device *ld = get_current_link(iod);
if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld))
- *p += sprintf(*p, "%s: %s\n", iod->name, ld->name);
+ *p += sprintf(*p, "%s<->%s\n", iod->name, ld->name);
}
static ssize_t show_txlink(struct device *dev,
@@ -130,181 +127,107 @@ static ssize_t store_txlink(struct device *dev,
static struct device_attribute attr_txlink =
__ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink);
-/**
- * rx_check_frame_cfg
- * @cfg: configuration field of a link layer header
- * @frm: pointer to the sipc5_frame_data buffer
- *
- * 1) Checks whether or not an extended field exists
- * 2) Calculates the length of a link layer header
- *
- * Returns the size of a link layer header
- *
- * Must be invoked only when the configuration field of the link layer header
- * is validated with sipc5_start_valid() function
- */
-static int rx_check_frame_cfg(u8 cfg, struct sipc5_frame_data *frm)
+static int netif_flow_ctrl(struct link_device *ld, struct sk_buff *skb)
{
- frm->config = cfg;
-
- if (likely(cfg & SIPC5_PADDING_EXIST))
- frm->padding = true;
-
- if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) {
- if (cfg & SIPC5_CTL_FIELD_EXIST) {
- frm->ctl_fld = true;
- frm->hdr_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD;
- } else {
- frm->ext_len = true;
- frm->hdr_len = SIPC5_HEADER_SIZE_WITH_EXT_LEN;
- }
+ u8 cmd = skb->data[0];
+
+ if (cmd == FLOW_CTRL_SUSPEND) {
+ if (ld->suspend_netif_tx)
+ goto exit;
+ ld->suspend_netif_tx = true;
+ mif_netif_stop(ld);
+ mif_info("%s: FLOW_CTRL_SUSPEND\n", ld->name);
+ } else if (cmd == FLOW_CTRL_RESUME) {
+ if (!ld->suspend_netif_tx)
+ goto exit;
+ ld->suspend_netif_tx = false;
+ mif_netif_wake(ld);
+ mif_info("%s: FLOW_CTRL_RESUME\n", ld->name);
} else {
- frm->hdr_len = SIPC5_MIN_HEADER_SIZE;
+ mif_info("%s: ERR! invalid command %02X\n", ld->name, cmd);
}
- return frm->hdr_len;
-}
-
-/**
- * rx_build_meta_data
- * @ld: pointer to the link device
- * @frm: pointer to the sipc5_frame_data buffer
- *
- * Fills each field of sipc5_frame_data from a link layer header
- * 1) Extracts the channel ID
- * 2) Calculates the length of a link layer frame
- * 3) Extracts a control field if exists
- * 4) Calculates the length of an IPC message packet in the link layer frame
- *
- */
-static void rx_build_meta_data(struct link_device *ld,
- struct sipc5_frame_data *frm)
-{
- u16 *sz16 = (u16 *)(frm->hdr + SIPC5_LEN_OFFSET);
- u32 *sz32 = (u32 *)(frm->hdr + SIPC5_LEN_OFFSET);
-
- frm->ch_id = frm->hdr[SIPC5_CH_ID_OFFSET];
-
- if (unlikely(frm->ext_len))
- frm->len = *sz32;
- else
- frm->len = *sz16;
-
- if (unlikely(frm->ctl_fld))
- frm->control = frm->hdr[SIPC5_CTL_OFFSET];
-
- frm->data_len = frm->len - frm->hdr_len;
-
- mif_debug("%s: FRM ch:%d len:%d ctl:%02X data.len:%d\n",
- ld->name, frm->ch_id, frm->len, frm->control, frm->data_len);
+exit:
+ dev_kfree_skb_any(skb);
+ return 0;
}
-/**
- * tx_build_link_header
- * @frm: pointer to the sipc5_frame_data buffer
- * @iod: pointer to the IO device
- * @ld: pointer to the link device
- * @count: length of the data to be transmitted
- *
- * Builds the meta data for an SIPC5 frame and the link layer header of it
- * Returns the link layer header length for an SIPC5 frame or 0 for other frame
- */
-static unsigned tx_build_link_header(struct sipc5_frame_data *frm,
- struct io_device *iod, struct link_device *ld, ssize_t count)
+static inline int queue_skb_to_iod(struct sk_buff *skb, struct io_device *iod)
{
- u8 *buff = frm->hdr;
- u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET);
- u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET);
+ struct sk_buff_head *rxq = &iod->sk_rx_q;
- memset(frm, 0, sizeof(struct sipc5_frame_data));
+ skb_queue_tail(rxq, skb);
- if (iod->format == IPC_CMD ||
- iod->format == IPC_BOOT ||
- iod->format == IPC_RAMDUMP) {
- frm->len = count;
+ if (iod->format < IPC_MULTI_RAW && rxq->qlen > MAX_IOD_RXQ_LEN) {
+ struct sk_buff *victim = skb_dequeue(rxq);
+ mif_err("%s: %s application may be dead (rxq->qlen %d > %d)\n",
+ iod->name, iod->app ? iod->app : "corresponding",
+ rxq->qlen, MAX_IOD_RXQ_LEN);
+ if (victim)
+ dev_kfree_skb_any(victim);
+ return -ENOSPC;
+ } else {
+ mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen);
return 0;
}
+}
- frm->config = SIPC5_START_MASK;
+static int rx_drain(struct sk_buff *skb)
+{
+ dev_kfree_skb_any(skb);
+ return 0;
+}
- if (iod->format == IPC_FMT && count > 2048) {
- frm->ctl_fld = true;
- frm->config |= SIPC5_EXT_FIELD_EXIST;
- frm->config |= SIPC5_CTL_FIELD_EXIST;
- }
+static int rx_loopback(struct sk_buff *skb)
+{
+ struct io_device *iod = skbpriv(skb)->iod;
+ struct link_device *ld = skbpriv(skb)->ld;
+ int ret;
- if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) {
- frm->ext_len = true;
- frm->config |= SIPC5_EXT_FIELD_EXIST;
+ ret = ld->send(ld, iod, skb);
+ if (ret < 0) {
+ mif_err("%s->%s: ERR! ld->send fail (err %d)\n",
+ iod->name, ld->name, ret);
}
- if (ld->aligned)
- frm->config |= SIPC5_PADDING_EXIST;
-
- frm->ch_id = iod->id;
-
- frm->hdr_len = sipc5_get_hdr_len(frm->config);
- frm->data_len = count;
- frm->len = frm->hdr_len + frm->data_len;
-
- buff[SIPC5_CONFIG_OFFSET] = frm->config;
- buff[SIPC5_CH_ID_OFFSET] = frm->ch_id;
-
- if (unlikely(frm->ext_len))
- *sz32 = (u32)frm->len;
- else
- *sz16 = (u16)frm->len;
-
- if (unlikely(frm->ctl_fld))
- buff[SIPC5_CTL_OFFSET] = frm->control;
-
- return frm->hdr_len;
+ return ret;
}
static int rx_fmt_frame(struct sk_buff *skb)
{
- struct io_device *iod = skbpriv(skb)->iod;
struct link_device *ld = skbpriv(skb)->ld;
- struct sk_buff_head *rxq = &iod->sk_rx_q;
- struct sipc_fmt_hdr *fh;
+ struct io_device *iod = skbpriv(skb)->iod;
struct sk_buff *rx_skb;
- u8 ctrl = skbpriv(skb)->control;
- unsigned id = ctrl & 0x7F;
+ int hdr_len = sipc5_get_hdr_len(skb->data);
+ u8 ctrl;
+ u8 id;
- if (iod->skb[id] == NULL) {
- /*
- ** There has been no multiple frame with this ID.
- */
- if ((ctrl & 0x80) == 0) {
- /*
- ** It is a single frame because the "more" bit is 0.
- */
- skb_queue_tail(rxq, skb);
- if (unlikely(rxq->qlen > 2048)) {
- struct sk_buff *victim;
- mif_info("%s: WARNING! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
- victim = skb_dequeue(rxq);
- dev_kfree_skb_any(victim);
- } else {
- mif_debug("%s: rxq->qlen = %d\n",
- iod->name, rxq->qlen);
- }
+ if (!sipc5_multi_frame(skb->data)) {
+ skb_pull(skb, hdr_len);
+ queue_skb_to_iod(skb, iod);
+ wake_up(&iod->wq);
+ return 0;
+ }
- wake_up(&iod->wq);
- return 0;
- }
+ /* Get the control field */
+ ctrl = sipc5_get_ctrl_field(skb->data);
- /*
- ** The start of multiple frames
- */
- fh = (struct sipc_fmt_hdr *)skb->data;
- mif_debug("%s: start multi-frame (ID:%d len:%d)\n",
- iod->name, id, fh->len);
+ /* Extract the control ID from the control field */
+ id = ctrl & 0x7F;
+
+ /* Remove SIPC5 link header */
+ skb_pull(skb, hdr_len);
+
+ /* If there has been no multiple frame with this ID, ... */
+ if (iod->skb[id] == NULL) {
+ struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)skb->data;
+
+ mif_err("%s->%s: start of multi-frame (ID:%d len:%d)\n",
+ ld->name, iod->name, id, fh->len);
rx_skb = rx_alloc_skb(fh->len, iod, ld);
if (!rx_skb) {
- mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name);
+ mif_err("%s: ERR! rx_alloc_skb fail\n", iod->name);
return -ENOMEM;
}
@@ -313,31 +236,19 @@ static int rx_fmt_frame(struct sk_buff *skb)
rx_skb = iod->skb[id];
}
- /*
- ** Start multi-frame processing
- */
+ /* Perform multi-frame processing */
memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
dev_kfree_skb_any(skb);
if (ctrl & 0x80) {
/* The last frame has not arrived yet. */
- mif_debug("%s: recv multi-frame (ID:%d rcvd:%d)\n",
- iod->name, id, rx_skb->len);
+ mif_info("%s->%s: recv multi-frame (ID:%d rcvd:%d)\n",
+ ld->name, iod->name, id, rx_skb->len);
} else {
/* It is the last frame because the "more" bit is 0. */
- mif_debug("%s: end multi-frame (ID:%d rcvd:%d)\n",
- iod->name, id, rx_skb->len);
- skb_queue_tail(rxq, rx_skb);
- if (unlikely(rxq->qlen > 2048)) {
- struct sk_buff *victim;
- mif_info("%s: WARNING! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
- victim = skb_dequeue(rxq);
- dev_kfree_skb_any(victim);
- } else {
- mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen);
- }
-
+ mif_err("%s->%s: end of multi-frame (ID:%d rcvd:%d)\n",
+ ld->name, iod->name, id, rx_skb->len);
+ queue_skb_to_iod(rx_skb, iod);
iod->skb[id] = NULL;
wake_up(&iod->wq);
}
@@ -345,76 +256,14 @@ static int rx_fmt_frame(struct sk_buff *skb)
return 0;
}
-static int rx_rfs_frame(struct sk_buff *skb)
-{
- struct io_device *iod = skbpriv(skb)->iod;
- struct sk_buff_head *rxq = &iod->sk_rx_q;
-
- skb_queue_tail(rxq, skb);
- if (unlikely(rxq->qlen > 2048)) {
- struct sk_buff *victim;
- mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen);
- victim = skb_dequeue(rxq);
- dev_kfree_skb_any(victim);
- } else {
- mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen);
- }
-
- wake_up(&iod->wq);
-
- return 0;
-}
-
-static int rx_loopback(struct sk_buff *skb)
-{
- struct io_device *iod = skbpriv(skb)->iod;
- struct link_device *ld = get_current_link(iod);
- struct sipc5_frame_data frm;
- unsigned headroom;
- unsigned tailroom = 0;
- int ret;
-
- headroom = tx_build_link_header(&frm, iod, ld, skb->len);
-
- if (ld->aligned)
- tailroom = sipc5_calc_padding_size(headroom + skb->len);
-
- /* We need not to expand skb in here. dev_alloc_skb (in rx_alloc_skb)
- * already alloc 32bytes padding in headroom. 32bytes are enough.
- */
-
- /* store IPC link header to start of skb
- * this is skb_push not skb_put. different with misc_write.
- */
- memcpy(skb_push(skb, headroom), frm.hdr, headroom);
-
- /* store padding */
- if (tailroom)
- skb_put(skb, tailroom);
-
- /* forward */
- ret = ld->send(ld, iod, skb);
- if (ret < 0)
- mif_err("%s->%s: ld->send fail: %d\n", iod->name,
- ld->name, ret);
- return ret;
-}
-
static int rx_raw_misc(struct sk_buff *skb)
{
- struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */
- struct sk_buff_head *rxq = &iod->sk_rx_q;
+ struct io_device *iod = skbpriv(skb)->iod;
- skb_queue_tail(rxq, skb);
- if (unlikely(rxq->qlen > 2048)) {
- struct sk_buff *victim;
- mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen);
- victim = skb_dequeue(rxq);
- dev_kfree_skb_any(victim);
- } else {
- mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen);
- }
+ /* Remove the SIPC5 link header */
+ skb_pull(skb, sipc5_get_hdr_len(skb->data));
+ queue_skb_to_iod(skb, iod);
wake_up(&iod->wq);
return 0;
@@ -422,12 +271,11 @@ static int rx_raw_misc(struct sk_buff *skb)
static int rx_multi_pdp(struct sk_buff *skb)
{
- struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */
+ struct link_device *ld = skbpriv(skb)->ld;
+ struct io_device *iod = skbpriv(skb)->iod;
struct net_device *ndev;
struct iphdr *iphdr;
- struct ethhdr *ehdr;
int ret;
- const char source[ETH_ALEN] = SOURCE_MAC_ADDR;
ndev = iod->ndev;
if (!ndev) {
@@ -435,6 +283,9 @@ static int rx_multi_pdp(struct sk_buff *skb)
return -ENODEV;
}
+ /* Remove the SIPC5 link header */
+ skb_pull(skb, sipc5_get_hdr_len(skb->data));
+
skb->dev = ndev;
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += skb->len;
@@ -447,14 +298,15 @@ static int rx_multi_pdp(struct sk_buff *skb)
skb->protocol = htons(ETH_P_IP);
if (iod->use_handover) {
- skb_push(skb, sizeof(struct ethhdr));
- ehdr = (void *)skb->data;
+ struct ethhdr *ehdr;
+ const char source[ETH_ALEN] = SOURCE_MAC_ADDR;
+
+ ehdr = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr));
memcpy(ehdr->h_dest, ndev->dev_addr, ETH_ALEN);
memcpy(ehdr->h_source, source, ETH_ALEN);
ehdr->h_proto = skb->protocol;
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_reset_mac_header(skb);
-
skb_pull(skb, sizeof(struct ethhdr));
}
@@ -463,48 +315,82 @@ static int rx_multi_pdp(struct sk_buff *skb)
else
ret = netif_rx_ni(skb);
- if (ret != NET_RX_SUCCESS)
- mif_info("%s: ERR! netif_rx fail (err %d)\n", iod->name, ret);
+ if (ret != NET_RX_SUCCESS) {
+ mif_err("%s->%s: ERR! netif_rx fail (err %d)\n",
+ ld->name, iod->name, ret);
+ }
return ret;
}
static int rx_demux(struct link_device *ld, struct sk_buff *skb)
{
- struct io_device *iod = NULL;
- char *link = ld->name;
- u8 ch = skbpriv(skb)->ch_id;
+ struct io_device *iod;
+ u8 ch = sipc5_get_ch_id(skb->data);
+#ifdef DEBUG_MODEM_IF
+ struct modem_ctl *mc = ld->mc;
+ size_t len = (skb->len > 20) ? 20 : skb->len;
+ char tag[MIF_MAX_STR_LEN];
+#endif
- if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) {
- mif_info("%s: ERR! invalid ch# %d\n", link, ch);
+ if (unlikely(ch == 0)) {
+ mif_err("%s: ERR! invalid ch# %d\n", ld->name, ch);
return -ENODEV;
}
+ if (unlikely(ch == SIPC5_CH_ID_FLOW_CTRL))
+ return netif_flow_ctrl(ld, skb);
+
/* IP loopback */
if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr)
ch = RMNET0_CH_ID;
iod = link_get_iod_with_channel(ld, ch);
if (unlikely(!iod)) {
- mif_info("%s: ERR! no iod for ch# %d\n", link, ch);
+ mif_err("%s: ERR! no iod with ch# %d\n", ld->name, ch);
return -ENODEV;
}
skbpriv(skb)->ld = ld;
skbpriv(skb)->iod = iod;
- skbpriv(skb)->real_iod = iod;
- /* don't care about CP2AP_LOOPBACK_CHANNEL is opened */
- if (unlikely(iod->id == CP2AP_LOOPBACK_CHANNEL))
+ /* Don't care whether or not DATA_DRAIN_CHANNEL is opened */
+ if (iod->id == DATA_DRAIN_CHANNEL)
+ return rx_drain(skb);
+
+ /* Don't care whether or not DATA_LOOPBACK_CHANNEL is opened */
+ if (iod->id == DATA_LOOPBACK_CHANNEL)
return rx_loopback(skb);
+#ifdef DEBUG_MODEM_IF
+ snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", mc->name, iod->name);
+ if (unlikely(iod->format == IPC_FMT))
+ pr_ipc(1, tag, skb->data, len);
+#if 0
+ if (iod->format == IPC_RAW)
+ pr_ipc(0, tag, skb->data, len);
+#endif
+#if 0
+ if (iod->format == IPC_BOOT)
+ pr_ipc(0, tag, skb->data, len);
+#endif
+#if 0
+ if (iod->format == IPC_RAMDUMP)
+ pr_ipc(0, tag, skb->data, len);
+#endif
+#if 0
+ if (ch == 28)
+ pr_ipc(0, tag, skb->data, len);
+#endif
+#endif /*DEBUG_MODEM_IF*/
+
if (atomic_read(&iod->opened) <= 0) {
- mif_info("%s: ERR! %s is not opened\n", link, iod->name);
+ mif_err("%s: ERR! %s is not opened\n", ld->name, iod->name);
return -ENODEV;
}
if (ch >= SIPC5_CH_ID_RFS_0)
- return rx_rfs_frame(skb);
+ return rx_raw_misc(skb);
else if (ch >= SIPC5_CH_ID_FMT_0)
return rx_fmt_frame(skb);
else if (iod->io_typ == IODEV_MISC)
@@ -513,342 +399,337 @@ static int rx_demux(struct link_device *ld, struct sk_buff *skb)
return rx_multi_pdp(skb);
}
-/* Check and store link layer header, then alloc an skb */
-static int rx_header_from_serial(struct io_device *iod, struct link_device *ld,
- u8 *buff, unsigned size, struct sipc5_frame_data *frm)
+/**
+ * rx_frame_config
+ * @iod: pointer to an instance of io_device structure
+ * @ld: pointer to an instance of link_device structure
+ * @buff: pointer to a buffer in which incoming data is stored
+ * @size: size of data in the buffer
+ * @frm: pointer to an instance of sipc5_frame_data structure
+ *
+ * 1) Checks a config field
+ * 2) Calculates the length of link layer header in an incoming frame and stores
+ * the value to "frm->hdr_len"
+ * 3) Stores the config field to "frm->hdr" and add the size of config field to
+ * "frm->hdr_rcvd"
+ *
+ * Returns the length of a config field that was copied to "frm"
+ */
+static int rx_frame_config(struct io_device *iod, struct link_device *ld,
+ u8 *buff, int size, struct sipc5_frame_data *frm)
{
- char *link = ld->name;
- struct sk_buff *skb;
- int len;
- u8 cfg = buff[0];
-
- mif_debug("%s: size %d\n", link, size);
-
- if (!frm->config) {
- if (unlikely(!sipc5_start_valid(cfg))) {
- mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg);
- return -EBADMSG;
- }
- rx_check_frame_cfg(cfg, frm);
-
- /* Copy the link layer header to the header buffer */
- len = min(frm->hdr_len, size);
- memcpy(frm->hdr, buff, len);
- } else {
- /* Copy the link layer header to the header buffer */
- len = min((frm->hdr_len - frm->hdr_rcvd), size);
- memcpy((frm->hdr + frm->hdr_rcvd), buff, len);
- }
+ int rest;
+ int rcvd;
- frm->hdr_rcvd += len;
-
- mif_debug("%s: FRM hdr_len:%d, hdr_rcvd:%d\n",
- link, frm->hdr_len, frm->hdr_rcvd);
-
- if (frm->hdr_rcvd >= frm->hdr_len) {
- rx_build_meta_data(ld, frm);
- skb = rx_alloc_skb(frm->data_len, iod, ld);
- fragdata(iod, ld)->skb_recv = skb;
- skbpriv(skb)->ch_id = frm->ch_id;
- skbpriv(skb)->control = frm->control;
+ if (unlikely(!sipc5_start_valid(buff))) {
+ mif_err("%s->%s: ERR! INVALID config 0x%02x\n",
+ ld->name, iod->name, buff[0]);
+ return -EBADMSG;
}
- return len;
-}
-
-/* copy data to skb */
-static int rx_payload_from_serial(struct io_device *iod, struct link_device *ld,
- u8 *buff, unsigned size, struct sipc5_frame_data *frm)
-{
- struct sk_buff *skb = fragdata(iod, ld)->skb_recv;
- char *link = ld->name;
- unsigned rest = frm->data_len - frm->data_rcvd;
- unsigned len;
-
- /* rest == (frm->data_len - frm->data_rcvd) == tailroom of skb */
- rest = frm->data_len - frm->data_rcvd;
- mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n",
- link, frm->data_len, frm->data_rcvd, rest, size);
-
- /* If there is no skb, data must be dropped. */
- len = min(rest, size);
- if (skb)
- memcpy(skb_put(skb, len), buff, len);
+ frm->hdr_len = sipc5_get_hdr_len(buff);
- frm->data_rcvd += len;
+ /* Calculate the size of a segment that will be copied */
+ rest = frm->hdr_len;
+ rcvd = SIPC5_CONFIG_SIZE;
+ mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n",
+ ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size,
+ rcvd);
- mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n",
- link, frm->data_len, frm->data_rcvd);
+ /* Copy the config field of an SIPC5 link header to the header buffer */
+ memcpy(frm->hdr, buff, rcvd);
+ frm->hdr_rcvd += rcvd;
- return len;
+ return rcvd;
}
-static int rx_frame_from_serial(struct io_device *iod, struct link_device *ld,
- const char *data, unsigned size)
+/**
+ * rx_frame_prepare_skb
+ * @iod: pointer to an instance of io_device structure
+ * @ld: pointer to an instance of link_device structure
+ * @frm: pointer to an instance of sipc5_frame_data structure
+ *
+ * 1) Extracts the length of a link frame from the link header in "frm->hdr"
+ * 2) Allocates an skb
+ * 3) Calculates the payload size in the link frame
+ * 4) Calculates the padding size in the link frame
+ *
+ * Returns the pointer to an skb
+ */
+static struct sk_buff *rx_frame_prepare_skb(struct io_device *iod,
+ struct link_device *ld, struct sipc5_frame_data *frm)
{
- struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
struct sk_buff *skb;
- char *link = ld->name;
- u8 *buff = (u8 *)data;
- int rest = (int)size;
- int err = 0;
- int done = 0;
- mif_debug("%s: size = %d\n", link, size);
+ /* Get the frame length */
+ frm->len = sipc5_get_frame_len(frm->hdr);
- if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) {
- /*
- ** There is an skb that is waiting for more SIPC5 data.
- ** In this case, rx_header_from_serial() must be skipped.
- */
- mif_debug("%s: FRM data.len:%d data.rcvd:%d -> recv_data\n",
- link, frm->data_len, frm->data_rcvd);
- goto recv_data;
+ /* Allocate an skb */
+ skb = rx_alloc_skb(frm->len, iod, ld);
+ if (!skb) {
+ mif_err("%s->%s: ERR! rx_alloc_skb fail (size %d)\n",
+ ld->name, iod->name, frm->len);
+ return NULL;
}
-next_frame:
- /* Receive and analyze header, then prepare an akb */
- err = done = rx_header_from_serial(iod, ld, buff, rest, frm);
- if (err < 0)
- goto err_exit;
-
- buff += done;
- rest -= done;
- mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest);
- if (rest < 0)
- goto err_range;
+ /* Calculates the payload size */
+ frm->pay_len = frm->len - frm->hdr_len;
- if (rest == 0)
- return size;
+ /* Calculates the padding size */
+ if (sipc5_padding_exist(frm->hdr))
+ frm->pad_len = sipc5_calc_padding_size(frm->len);
-recv_data:
- err = 0;
+ mif_debug("%s->%s: size %d (header:%d payload:%d padding:%d)\n",
+ ld->name, iod->name, frm->len, frm->hdr_len, frm->pay_len,
+ frm->pad_len);
- mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest);
-
- done = rx_payload_from_serial(iod, ld, buff, rest, frm);
- buff += done;
- rest -= done;
+ return skb;
+}
- mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest);
+/**
+ * rx_frame_header
+ * @iod: pointer to an instance of io_device structure
+ * @ld: pointer to an instance of link_device structure
+ * @buff: pointer to a buffer in which incoming data is stored
+ * @size: size of data in the buffer
+ * @frm: pointer to an instance of sipc5_frame_data structure
+ *
+ * 1) Stores a link layer header to "frm->hdr" temporarily while "frm->hdr_rcvd"
+ * is less than "frm->hdr_len"
+ * 2) Then,
+ * Allocates an skb
+ * Copies the link header from "frm" to "skb"
+ * Register the skb to receive payload
+ *
+ * Returns the size of a segment that was copied to "frm"
+ */
+static int rx_frame_header(struct io_device *iod, struct link_device *ld,
+ u8 *buff, int size, struct sipc5_frame_data *frm)
+{
+ struct sk_buff *skb;
+ int rest;
+ int rcvd;
- if (rest == 0 && frm->data_rcvd < frm->data_len) {
- /*
- Data is being received and more data will come within the next
- frame from the link device.
- */
- return size;
- }
+ /* Calculate the size of a segment that will be copied */
+ rest = frm->hdr_len - frm->hdr_rcvd;
+ rcvd = min(rest, size);
+ mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n",
+ ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size,
+ rcvd);
- /* At this point, one complete link layer frame has been received. */
+ /* Copy a segment of an SIPC5 link header to "frm" */
+ memcpy((frm->hdr + frm->hdr_rcvd), buff, rcvd);
+ frm->hdr_rcvd += rcvd;
- /* A padding size is applied to access the next IPC frame. */
- if (frm->padding) {
- done = sipc5_calc_padding_size(frm->len);
- if (done > rest) {
- mif_info("%s: ERR! padding %d > rest %d\n",
- link, done, rest);
- goto err_exit;
+ if (frm->hdr_rcvd >= frm->hdr_len) {
+ /* Prepare an skb with the information in {iod, ld, frm} */
+ skb = rx_frame_prepare_skb(iod, ld, frm);
+ if (!skb) {
+ mif_err("%s->%s: ERR! rx_frame_prepare_skb fail\n",
+ ld->name, iod->name);
+ return -ENOMEM;
}
- buff += done;
- rest -= done;
-
- mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest);
-
- if (rest < 0)
- goto err_range;
-
- }
-
- skb = fragdata(iod, ld)->skb_recv;
- if (likely(skb)) {
- mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len);
- err = rx_demux(ld, skb);
- if (err < 0)
- dev_kfree_skb_any(skb);
- } else {
- mif_debug("%s: len:%d -> drop\n", link, skb->len);
- }
-
- /* initialize the skb_recv and the frame_data buffer */
- fragdata(iod, ld)->skb_recv = NULL;
- memset(frm, 0, sizeof(struct sipc5_frame_data));
+ /* Copy an SIPC5 link header from "frm" to "skb" */
+ memcpy(skb_put(skb, frm->hdr_len), frm->hdr, frm->hdr_len);
- if (rest > 0)
- goto next_frame;
-
- if (rest <= 0)
- return size;
-
-err_exit:
- if (fragdata(iod, ld)->skb_recv &&
- frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd >= frm->data_len) {
- dev_kfree_skb_any(fragdata(iod, ld)->skb_recv);
- memset(frm, 0, sizeof(struct sipc5_frame_data));
- fragdata(iod, ld)->skb_recv = NULL;
- mif_info("%s: ERR! clear frag\n", link);
+ /* Register the skb to receive payload */
+ fragdata(iod, ld)->skb_recv = skb;
}
- return err;
-err_range:
- mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest);
- return size;
+ return rcvd;
}
/**
- * rx_header_from_mem
- * @ld: pointer to the link device
- * @buff: pointer to the frame
- * @rest: size of the frame
- * @frm: pointer to the sipc5_frame_data buffer
+ * rx_frame_payload
+ * @iod: pointer to an instance of io_device structure
+ * @ld: pointer to an instance of link_device structure
+ * @buff: pointer to a buffer in which incoming data is stored
+ * @size: size of data in the buffer
+ * @frm: pointer to an instance of sipc5_frame_data structure
*
- * 1) Verifies a link layer header configuration of a frame
- * 2) Stores the link layer header to the header buffer
- * 3) Builds and stores the meta data of the frame into a meta data buffer
- * 4) Verifies the length of the frame
+ * Stores a link layer payload to "skb"
*
- * Returns SIPC5 header length
+ * Returns the size of a segment that was copied to "skb"
*/
-static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest,
- struct sipc5_frame_data *frm)
+static int rx_frame_payload(struct io_device *iod, struct link_device *ld,
+ u8 *buff, int size, struct sipc5_frame_data *frm)
{
- char *link = ld->name;
- u8 cfg = buff[0];
+ struct sk_buff *skb = fragdata(iod, ld)->skb_recv;
+ int rest;
+ int rcvd;
- /* Verify link layer header configuration */
- if (unlikely(!sipc5_start_valid(cfg))) {
- mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg);
- return -EBADMSG;
- }
- rx_check_frame_cfg(cfg, frm);
+ /* Calculate the size of a segment that will be copied */
+ rest = frm->pay_len - frm->pay_rcvd;
+ rcvd = min(rest, size);
+ mif_debug("%s->%s: pay_len:%d pay_rcvd:%d rest:%d size:%d rcvd:%d\n",
+ ld->name, iod->name, frm->pay_len, frm->pay_rcvd, rest, size,
+ rcvd);
- /* Store the link layer header to the header buffer */
- memcpy(frm->hdr, buff, frm->hdr_len);
- frm->hdr_rcvd = frm->hdr_len;
+ /* Copy an SIPC5 link payload to "skb" */
+ memcpy(skb_put(skb, rcvd), buff, rcvd);
+ frm->pay_rcvd += rcvd;
- /* Build and store the meta data of this frame */
- rx_build_meta_data(ld, frm);
+ return rcvd;
+}
- /* Verify frame length */
- if (unlikely(frm->len > rest)) {
- mif_info("%s: ERR! frame length %d > rest %d\n",
- link, frm->len, rest);
- return -EBADMSG;
- }
+static int rx_frame_padding(struct io_device *iod, struct link_device *ld,
+ u8 *buff, int size, struct sipc5_frame_data *frm)
+{
+ struct sk_buff *skb = fragdata(iod, ld)->skb_recv;
+ int rest;
+ int rcvd;
+
+ /* Calculate the size of a segment that will be dropped as padding */
+ rest = frm->pad_len - frm->pad_rcvd;
+ rcvd = min(rest, size);
+ mif_debug("%s->%s: pad_len:%d pad_rcvd:%d rest:%d size:%d rcvd:%d\n",
+ ld->name, iod->name, frm->pad_len, frm->pad_rcvd, rest, size,
+ rcvd);
- return frm->hdr_rcvd;
+ /* Copy an SIPC5 link padding to "skb" */
+ memcpy(skb_put(skb, rcvd), buff, rcvd);
+ frm->pad_rcvd += rcvd;
+
+ return rcvd;
}
-/* copy data to skb */
-static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len)
+static int rx_frame_done(struct io_device *iod, struct link_device *ld,
+ struct sk_buff *skb)
{
- /* If there is no skb, data must be dropped. */
- if (skb)
- memcpy(skb_put(skb, len), buff, len);
- return len;
+ /* Cut off the padding of the current frame */
+ skb_trim(skb, sipc5_get_frame_len(skb->data));
+ mif_debug("%s->%s: frame length = %d\n", ld->name, iod->name, skb->len);
+
+ return rx_demux(ld, skb);
}
-static int rx_frame_from_mem(struct io_device *iod, struct link_device *ld,
+static int recv_frame_from_buff(struct io_device *iod, struct link_device *ld,
const char *data, unsigned size)
{
struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
struct sk_buff *skb;
- char *link = ld->name;
u8 *buff = (u8 *)data;
int rest = (int)size;
- int len;
- int done;
+ int done = 0;
+ int err = 0;
- mif_debug("%s: size = %d\n", link, size);
+ mif_debug("%s->%s: size %d (RX state = %s)\n", ld->name, iod->name,
+ size, get_rx_state_str(iod->curr_rx_state));
while (rest > 0) {
- /* Initialize the frame data buffer */
- memset(frm, 0, sizeof(struct sipc5_frame_data));
- skb = NULL;
+ switch (iod->curr_rx_state) {
+ case IOD_RX_ON_STANDBY:
+ fragdata(iod, ld)->skb_recv = NULL;
+ memset(frm, 0, sizeof(struct sipc5_frame_data));
+
+ done = rx_frame_config(iod, ld, buff, rest, frm);
+ if (done < 0) {
+ err = done;
+ goto err_exit;
+ }
- /* Receive and analyze link layer header */
- done = rx_header_from_mem(ld, buff, rest, frm);
- if (unlikely(done < 0))
- return -EBADMSG;
+ iod->next_rx_state = IOD_RX_HEADER;
- /* Verify rest size */
- rest -= done;
- if (rest < 0) {
- mif_info("%s: ERR! rx_header -> rest %d\n", link, rest);
- return -ERANGE;
- }
+ break;
- /* Move buff pointer to the payload */
- buff += done;
+ case IOD_RX_HEADER:
+ done = rx_frame_header(iod, ld, buff, rest, frm);
+ if (done < 0) {
+ err = done;
+ goto err_exit;
+ }
- /* Prepare an akb */
- len = frm->data_len;
- skb = rx_alloc_skb(len, iod, ld);
+ if (frm->hdr_rcvd >= frm->hdr_len)
+ iod->next_rx_state = IOD_RX_PAYLOAD;
+ else
+ iod->next_rx_state = IOD_RX_HEADER;
- /* Store channel ID and control fields to the CB of the skb */
- skbpriv(skb)->ch_id = frm->ch_id;
- skbpriv(skb)->control = frm->control;
+ break;
- /* Receive payload */
- mif_debug("%s: done:%d rest:%d len:%d -> rx_payload()\n",
- link, done, rest, len);
- done = rx_payload_from_mem(skb, buff, len);
- rest -= done;
- if (rest < 0) {
- mif_info("%s: ERR! rx_payload() -> rest %d\n",
- link, rest);
- if (skb)
- dev_kfree_skb_any(skb);
- return -ERANGE;
- }
- buff += done;
+ case IOD_RX_PAYLOAD:
+ done = rx_frame_payload(iod, ld, buff, rest, frm);
+ if (done < 0) {
+ err = done;
+ goto err_exit;
+ }
- /* A padding size is applied to access the next IPC frame. */
- if (frm->padding) {
- done = sipc5_calc_padding_size(frm->len);
- if (done > rest) {
- mif_info("%s: ERR! padding %d > rest %d\n",
- link, done, rest);
- if (skb)
- dev_kfree_skb_any(skb);
- return -ERANGE;
+ if (frm->pay_rcvd >= frm->pay_len) {
+ if (frm->pad_len > 0)
+ iod->next_rx_state = IOD_RX_PADDING;
+ else
+ iod->next_rx_state = IOD_RX_ON_STANDBY;
+ } else {
+ iod->next_rx_state = IOD_RX_PAYLOAD;
}
- buff += done;
- rest -= done;
+
+ break;
+
+ case IOD_RX_PADDING:
+ done = rx_frame_padding(iod, ld, buff, rest, frm);
+ if (done < 0) {
+ err = done;
+ goto err_exit;
+ }
+
+ if (frm->pad_rcvd >= frm->pad_len)
+ iod->next_rx_state = IOD_RX_ON_STANDBY;
+ else
+ iod->next_rx_state = IOD_RX_PADDING;
+
+ break;
+
+ default:
+ mif_err("%s->%s: ERR! INVALID RX state %d\n",
+ ld->name, iod->name, iod->curr_rx_state);
+ err = -EINVAL;
+ goto err_exit;
}
- if (likely(skb)) {
- mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len);
- if (rx_demux(ld, skb) < 0)
- dev_kfree_skb_any(skb);
- } else {
- mif_debug("%s: len:%d -> drop\n", link, skb->len);
+ if (iod->next_rx_state == IOD_RX_ON_STANDBY) {
+ /*
+ ** A complete frame is in fragdata(iod, ld)->skb_recv.
+ */
+ skb = fragdata(iod, ld)->skb_recv;
+ err = rx_frame_done(iod, ld, skb);
+ if (err < 0)
+ goto err_exit;
}
+
+ buff += done;
+ rest -= done;
+ if (rest < 0)
+ goto err_range;
+
+ iod->curr_rx_state = iod->next_rx_state;
}
- return 0;
+ return size;
+
+err_exit:
+ if (fragdata(iod, ld)->skb_recv) {
+ mif_err("%s->%s: ERR! clear frag (size:%d done:%d rest:%d)\n",
+ ld->name, iod->name, size, done, rest);
+ dev_kfree_skb_any(fragdata(iod, ld)->skb_recv);
+ fragdata(iod, ld)->skb_recv = NULL;
+ }
+ iod->curr_rx_state = IOD_RX_ON_STANDBY;
+ return err;
+
+err_range:
+ mif_err("%s->%s: ERR! size:%d done:%d rest:%d\n",
+ ld->name, iod->name, size, done, rest);
+ iod->curr_rx_state = IOD_RX_ON_STANDBY;
+ return size;
}
/* called from link device when a packet arrives for this io device */
static int io_dev_recv_data_from_link_dev(struct io_device *iod,
struct link_device *ld, const char *data, unsigned int len)
{
- struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sk_buff *skb;
- char *link = ld->name;
int err;
- if (!data) {
- mif_info("%s: ERR! !data\n", link);
- return -EINVAL;
- }
-
- if (len <= 0) {
- mif_info("%s: ERR! len %d <= 0\n", link, len);
- return -EINVAL;
- }
-
switch (iod->format) {
case IPC_FMT:
case IPC_RAW:
@@ -857,97 +738,151 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
if (iod->waketime)
wake_lock_timeout(&iod->wakelock, iod->waketime);
- if (ld->link_type == LINKDEV_DPRAM && ld->aligned)
- err = rx_frame_from_mem(iod, ld, data, len);
- else
- err = rx_frame_from_serial(iod, ld, data, len);
-
- if (err < 0)
- mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n",
- link, err);
+ err = recv_frame_from_buff(iod, ld, data, len);
+ if (err < 0) {
+ mif_err("%s->%s: ERR! recv_frame_from_buff fail "
+ "(err %d)\n", ld->name, iod->name, err);
+ }
return err;
- case IPC_CMD:
- case IPC_BOOT:
- case IPC_RAMDUMP:
+ default:
+ mif_debug("%s->%s: len %d\n", ld->name, iod->name, len);
+
/* save packet to sk_buff */
skb = rx_alloc_skb(len, iod, ld);
if (!skb) {
- mif_info("%s: ERR! rx_alloc_skb fail\n", link);
+ mif_info("%s->%s: ERR! rx_alloc_skb fail\n",
+ ld->name, iod->name);
return -ENOMEM;
}
- mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name);
-
memcpy(skb_put(skb, len), data, len);
- skb_queue_tail(rxq, skb);
- if (unlikely(rxq->qlen > 2048)) {
- struct sk_buff *victim;
- mif_info("%s: ERR! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
- victim = skb_dequeue(rxq);
- dev_kfree_skb_any(victim);
- }
+
+ queue_skb_to_iod(skb, iod);
+
wake_up(&iod->wq);
return len;
-
- default:
- mif_info("%s: ERR! unknown format %d\n", link, iod->format);
- return -EINVAL;
}
}
-static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld,
+static int recv_frame_from_skb(struct io_device *iod, struct link_device *ld,
struct sk_buff *skb)
{
- struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
- u8 cfg = skb->data[0];
+ struct sk_buff *clone;
+ unsigned int rest;
+ unsigned int rcvd;
+ int tot; /* total length including padding */
+ int err = 0;
- /* Initialize the frame data buffer */
- memset(frm, 0, sizeof(struct sipc5_frame_data));
+ /*
+ ** If there is only one SIPC5 frame in @skb, receive the SIPC5 frame and
+ ** return immediately. In this case, the frame verification must already
+ ** have been done at the link device.
+ */
+ if (skbpriv(skb)->single_frame) {
+ err = rx_frame_done(iod, ld, skb);
+ if (err < 0)
+ goto exit;
+ return 0;
+ }
/*
- ** The start of a link layer header has already been checked in the
- ** link device.
+ ** The routine from here is used only if there may be multiple SIPC5
+ ** frames in @skb.
*/
- /* Analyze the configuration of the link layer header */
- rx_check_frame_cfg(cfg, frm);
+ /* Check the config field of the first frame in @skb */
+ if (!sipc5_start_valid(skb->data)) {
+ mif_err("%s->%s: ERR! INVALID config 0x%02X\n",
+ ld->name, iod->name, skb->data[0]);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ /* Get the total length of the frame with a padding */
+ tot = sipc5_get_total_len(skb->data);
- /* Store the link layer header to the header buffer */
- memcpy(frm->hdr, skb->data, frm->hdr_len);
- frm->hdr_rcvd = frm->hdr_len;
+ /* Verify the total length of the first frame */
+ rest = skb->len;
+ if (unlikely(tot > rest)) {
+ mif_err("%s->%s: ERR! tot %d > skb->len %d)\n",
+ ld->name, iod->name, tot, rest);
+ err = -EINVAL;
+ goto exit;
+ }
- /* Build and store the meta data of this frame */
- rx_build_meta_data(ld, frm);
+ /* If there is only one SIPC5 frame in @skb, */
+ if (likely(tot == rest)) {
+ /* Receive the SIPC5 frame and return immediately */
+ err = rx_frame_done(iod, ld, skb);
+ if (err < 0)
+ goto exit;
+ return 0;
+ }
/*
- ** The length of the frame has already been checked in the link device.
+ ** This routine is used only if there are multiple SIPC5 frames in @skb.
*/
+ rcvd = 0;
+ while (rest > 0) {
+ clone = skb_clone(skb, GFP_ATOMIC);
+ if (unlikely(!clone)) {
+ mif_err("%s->%s: ERR! skb_clone fail\n",
+ ld->name, iod->name);
+ err = -ENOMEM;
+ goto exit;
+ }
- /* Trim the link layer header off the frame */
- skb_pull(skb, frm->hdr_len);
+ /* Get the start of an SIPC5 frame */
+ skb_pull(clone, rcvd);
+ if (!sipc5_start_valid(clone->data)) {
+ mif_err("%s->%s: ERR! INVALID config 0x%02X\n",
+ ld->name, iod->name, clone->data[0]);
+ dev_kfree_skb_any(clone);
+ err = -EINVAL;
+ goto exit;
+ }
- /* Store channel ID and control fields to the CB of the skb */
- skbpriv(skb)->ch_id = frm->ch_id;
- skbpriv(skb)->control = frm->control;
+ /* Get the total length of the current frame with a padding */
+ tot = sipc5_get_total_len(clone->data);
+ if (unlikely(tot > rest)) {
+ mif_err("%s->%s: ERR! dirty frame (tot %d > rest %d)\n",
+ ld->name, iod->name, tot, rest);
+ dev_kfree_skb_any(clone);
+ err = -EINVAL;
+ goto exit;
+ }
- /* Demux the frame */
- if (rx_demux(ld, skb) < 0) {
- mif_err("%s: ERR! rx_demux fail\n", ld->name);
- return -EINVAL;
+ /* Cut off the padding of the current frame */
+ skb_trim(clone, sipc5_get_frame_len(clone->data));
+
+ /* Demux the frame */
+ err = rx_demux(ld, clone);
+ if (err < 0) {
+ mif_err("%s->%s: ERR! rx_demux fail (err %d)\n",
+ ld->name, iod->name, err);
+ dev_kfree_skb_any(clone);
+ goto exit;
+ }
+
+ /* Calculate the start of the next frame */
+ rcvd += tot;
+
+ /* Calculate the rest size of data in @skb */
+ rest -= tot;
}
- return 0;
+exit:
+ dev_kfree_skb_any(skb);
+ return err;
}
/* called from link device when a packet arrives for this io device */
static int io_dev_recv_skb_from_link_dev(struct io_device *iod,
struct link_device *ld, struct sk_buff *skb)
{
- char *link = ld->name;
enum dev_format dev = iod->format;
int err;
@@ -959,17 +894,35 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod,
if (iod->waketime)
wake_lock_timeout(&iod->wakelock, iod->waketime);
- err = rx_frame_from_skb(iod, ld, skb);
+ err = recv_frame_from_skb(iod, ld, skb);
if (err < 0) {
- dev_kfree_skb_any(skb);
- mif_info("%s: ERR! rx_frame_from_skb fail (err %d)\n",
- link, err);
+ mif_err("%s->%s: ERR! recv_frame_from_skb fail "
+ "(err %d)\n", ld->name, iod->name, err);
+ }
+
+ return err;
+
+ case IPC_BOOT:
+ case IPC_RAMDUMP:
+ if (!iod->id) {
+ mif_err("%s->%s: ERR! invalid iod\n",
+ ld->name, iod->name);
+ return -ENODEV;
+ }
+
+ if (iod->waketime)
+ wake_lock_timeout(&iod->wakelock, iod->waketime);
+
+ err = recv_frame_from_skb(iod, ld, skb);
+ if (err < 0) {
+ mif_err("%s->%s: ERR! recv_frame_from_skb fail "
+ "(err %d)\n", ld->name, iod->name, err);
}
return err;
default:
- mif_info("%s: ERR! unknown device %d\n", link, dev);
+ mif_err("%s->%s: ERR! invalid iod\n", ld->name, iod->name);
return -EINVAL;
}
}
@@ -980,10 +933,14 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod,
static void io_dev_modem_state_changed(struct io_device *iod,
enum modem_state state)
{
- mif_info("%s: %s state changed (state %d)\n",
- iod->name, iod->mc->name, state);
+ struct modem_ctl *mc = iod->mc;
+ int old_state = mc->phone_state;
- iod->mc->phone_state = state;
+ if (old_state != state) {
+ mc->phone_state = state;
+ mif_err("%s state changed (%s -> %s)\n", mc->name,
+ get_cp_state_str(old_state), get_cp_state_str(state));
+ }
if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT ||
state == STATE_NV_REBUILDING)
@@ -1024,23 +981,27 @@ static int misc_open(struct inode *inode, struct file *filp)
struct io_device *iod = to_io_device(filp->private_data);
struct modem_shared *msd = iod->msd;
struct link_device *ld;
+ int ref_cnt;
int ret;
filp->private_data = (void *)iod;
- atomic_inc(&iod->opened);
-
list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->init_comm) {
ret = ld->init_comm(ld, iod);
if (ret < 0) {
- mif_info("%s: init_comm fail(%d)\n",
- ld->name, ret);
+ mif_err("%s<->%s: ERR! init_comm fail(%d)\n",
+ iod->name, ld->name, ret);
return ret;
}
}
}
- mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened));
+ ref_cnt = atomic_inc_return(&iod->opened);
+
+ if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP)
+ mif_err("%s (opened %d)\n", iod->name, ref_cnt);
+ else
+ mif_info("%s (opened %d)\n", iod->name, ref_cnt);
return 0;
}
@@ -1050,8 +1011,8 @@ static int misc_release(struct inode *inode, struct file *filp)
struct io_device *iod = (struct io_device *)filp->private_data;
struct modem_shared *msd = iod->msd;
struct link_device *ld;
+ int ref_cnt;
- atomic_dec(&iod->opened);
skb_queue_purge(&iod->sk_rx_q);
list_for_each_entry(ld, &msd->link_dev_list, list) {
@@ -1059,7 +1020,12 @@ static int misc_release(struct inode *inode, struct file *filp)
ld->terminate_comm(ld, iod);
}
- mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened));
+ ref_cnt = atomic_dec_return(&iod->opened);
+
+ if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP)
+ mif_err("%s (opened %d)\n", iod->name, ref_cnt);
+ else
+ mif_info("%s (opened %d)\n", iod->name, ref_cnt);
return 0;
}
@@ -1067,20 +1033,23 @@ static int misc_release(struct inode *inode, struct file *filp)
static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait)
{
struct io_device *iod = (struct io_device *)filp->private_data;
+ struct modem_ctl *mc = iod->mc;
poll_wait(filp, &iod->wq, wait);
- if (!skb_queue_empty(&iod->sk_rx_q) &&
- iod->mc->phone_state != STATE_OFFLINE) {
+ if (!skb_queue_empty(&iod->sk_rx_q) && mc->phone_state != STATE_OFFLINE)
return POLLIN | POLLRDNORM;
- } else if ((iod->mc->phone_state == STATE_CRASH_RESET) ||
- (iod->mc->phone_state == STATE_CRASH_EXIT) ||
- (iod->mc->phone_state == STATE_NV_REBUILDING) ||
- (iod->mc->sim_state.changed)) {
+
+ if (mc->phone_state == STATE_CRASH_RESET
+ || mc->phone_state == STATE_CRASH_EXIT
+ || mc->phone_state == STATE_NV_REBUILDING
+ || mc->sim_state.changed) {
if (iod->format == IPC_RAW) {
msleep(20);
return 0;
}
+ if (iod->format == IPC_RAMDUMP)
+ return 0;
return POLLHUP;
} else {
return 0;
@@ -1089,132 +1058,191 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait)
static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
- int p_state;
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
- char cpinfo_buf[530] = "CP Crash ";
+ struct modem_ctl *mc = iod->mc;
+ int p_state;
+ char *buff;
+ void __user *user_buff;
unsigned long size;
- int ret;
switch (cmd) {
case IOCTL_MODEM_ON:
- mif_info("%s: IOCTL_MODEM_ON\n", iod->name);
- return iod->mc->ops.modem_on(iod->mc);
+ if (mc->ops.modem_on) {
+ mif_err("%s: IOCTL_MODEM_ON\n", iod->name);
+ return mc->ops.modem_on(mc);
+ }
+ mif_err("%s: !mc->ops.modem_on\n", iod->name);
+ return -EINVAL;
case IOCTL_MODEM_OFF:
- mif_info("%s: IOCTL_MODEM_OFF\n", iod->name);
- return iod->mc->ops.modem_off(iod->mc);
+ if (mc->ops.modem_off) {
+ mif_err("%s: IOCTL_MODEM_OFF\n", iod->name);
+ return mc->ops.modem_off(mc);
+ }
+ mif_err("%s: !mc->ops.modem_off\n", iod->name);
+ return -EINVAL;
case IOCTL_MODEM_RESET:
- mif_info("%s: IOCTL_MODEM_RESET\n", iod->name);
- return iod->mc->ops.modem_reset(iod->mc);
+ if (mc->ops.modem_reset) {
+ mif_err("%s: IOCTL_MODEM_RESET\n", iod->name);
+ return mc->ops.modem_reset(mc);
+ }
+ mif_err("%s: !mc->ops.modem_reset\n", iod->name);
+ return -EINVAL;
case IOCTL_MODEM_BOOT_ON:
- mif_info("%s: IOCTL_MODEM_BOOT_ON\n", iod->name);
- return iod->mc->ops.modem_boot_on(iod->mc);
+ if (mc->ops.modem_boot_on) {
+ mif_err("%s: IOCTL_MODEM_BOOT_ON\n", iod->name);
+ return mc->ops.modem_boot_on(mc);
+ }
+ mif_err("%s: !mc->ops.modem_boot_on\n", iod->name);
+ return -EINVAL;
case IOCTL_MODEM_BOOT_OFF:
- mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name);
- return iod->mc->ops.modem_boot_off(iod->mc);
+ if (mc->ops.modem_boot_off) {
+ mif_err("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name);
+ return mc->ops.modem_boot_off(mc);
+ }
+ mif_err("%s: !mc->ops.modem_boot_off\n", iod->name);
+ return -EINVAL;
case IOCTL_MODEM_BOOT_DONE:
mif_err("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name);
- if (iod->mc->ops.modem_boot_done)
- return iod->mc->ops.modem_boot_done(iod->mc);
- else
- return 0;
+ if (mc->ops.modem_boot_done)
+ return mc->ops.modem_boot_done(mc);
+ return 0;
case IOCTL_MODEM_STATUS:
mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name);
- p_state = iod->mc->phone_state;
+ p_state = mc->phone_state;
if ((p_state == STATE_CRASH_RESET) ||
(p_state == STATE_CRASH_EXIT)) {
- mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n",
- iod->name, p_state);
- } else if (iod->mc->sim_state.changed) {
- int s_state = iod->mc->sim_state.online ?
+ mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n",
+ iod->name, get_cp_state_str(p_state));
+ } else if (mc->sim_state.changed) {
+ int s_state = mc->sim_state.online ?
STATE_SIM_ATTACH : STATE_SIM_DETACH;
- iod->mc->sim_state.changed = false;
+ mc->sim_state.changed = false;
return s_state;
} else if (p_state == STATE_NV_REBUILDING) {
- mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n",
- iod->name, p_state);
- iod->mc->phone_state = STATE_ONLINE;
+ mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n",
+ iod->name, get_cp_state_str(p_state));
+ mc->phone_state = STATE_ONLINE;
}
return p_state;
- case IOCTL_MODEM_PROTOCOL_SUSPEND:
- mif_debug("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n",
- iod->name);
-
- if (iod->format != IPC_MULTI_RAW)
- return -EINVAL;
+ case IOCTL_MODEM_XMIT_BOOT:
+ if (ld->xmit_boot) {
+ mif_info("%s: IOCTL_MODEM_XMIT_BOOT\n", iod->name);
+ return ld->xmit_boot(ld, iod, arg);
+ }
+ mif_err("%s: !ld->xmit_boot\n", iod->name);
+ return -EINVAL;
- iodevs_for_each(iod->msd, iodev_netif_stop, 0);
- return 0;
+ case IOCTL_MODEM_DL_START:
+ if (ld->dload_start) {
+ mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name);
+ return ld->dload_start(ld, iod);
+ }
+ mif_err("%s: !ld->dload_start\n", iod->name);
+ return -EINVAL;
- case IOCTL_MODEM_PROTOCOL_RESUME:
- mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n",
- iod->name);
+ case IOCTL_MODEM_FW_UPDATE:
+ if (ld->firm_update) {
+ mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name);
+ return ld->firm_update(ld, iod, arg);
+ }
+ mif_err("%s: !ld->firm_update\n", iod->name);
+ return -EINVAL;
- if (iod->format != IPC_MULTI_RAW)
- return -EINVAL;
+ case IOCTL_MODEM_FORCE_CRASH_EXIT:
+ if (mc->ops.modem_force_crash_exit) {
+ mif_err("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n",
+ iod->name);
+ return mc->ops.modem_force_crash_exit(mc);
+ }
+ mif_err("%s: !mc->ops.modem_force_crash_exit\n", iod->name);
+ return -EINVAL;
- iodevs_for_each(iod->msd, iodev_netif_wake, 0);
- return 0;
+ case IOCTL_MODEM_DUMP_RESET:
+ if (mc->ops.modem_dump_reset) {
+ mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name);
+ return mc->ops.modem_dump_reset(mc);
+ }
+ mif_err("%s: !mc->ops.modem_dump_reset\n", iod->name);
+ return -EINVAL;
case IOCTL_MODEM_DUMP_START:
- mif_info("%s: IOCTL_MODEM_DUMP_START\n", iod->name);
- return ld->dump_start(ld, iod);
+ if (ld->dump_start) {
+ mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name);
+ return ld->dump_start(ld, iod);
+ }
+ mif_err("%s: !ld->dump_start\n", iod->name);
+ return -EINVAL;
+
+ case IOCTL_MODEM_RAMDUMP_START:
+ if (ld->dump_start) {
+ mif_info("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name);
+ return ld->dump_start(ld, iod);
+ }
+ mif_err("%s: !ld->dump_start\n", iod->name);
+ return -EINVAL;
case IOCTL_MODEM_DUMP_UPDATE:
- mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name);
- return ld->dump_update(ld, iod, arg);
+ if (ld->dump_update) {
+ mif_info("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name);
+ return ld->dump_update(ld, iod, arg);
+ }
+ mif_err("%s: !ld->dump_update\n", iod->name);
+ return -EINVAL;
- case IOCTL_MODEM_FORCE_CRASH_EXIT:
- mif_info("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name);
- if (iod->mc->ops.modem_force_crash_exit)
- return iod->mc->ops.modem_force_crash_exit(iod->mc);
+ case IOCTL_MODEM_RAMDUMP_STOP:
+ if (ld->dump_finish) {
+ mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name);
+ return ld->dump_finish(ld, iod, arg);
+ }
+ mif_err("%s: !ld->dump_finish\n", iod->name);
return -EINVAL;
case IOCTL_MODEM_CP_UPLOAD:
mif_info("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name);
- if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf),
- (void __user *)arg, MAX_CPINFO_SIZE) != 0)
- return -EFAULT;
- panic(cpinfo_buf);
+ strcpy(iod->msd->cp_crash_info, CP_CRASH_TAG);
+ if (arg) {
+ buff = iod->msd->cp_crash_info + strlen(CP_CRASH_TAG);
+ user_buff = (void __user *)arg;
+ if (copy_from_user(buff, user_buff, MAX_CPINFO_SIZE))
+ return -EFAULT;
+ }
+ panic(iod->msd->cp_crash_info);
return 0;
- case IOCTL_MODEM_DUMP_RESET:
- mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name);
- return iod->mc->ops.modem_dump_reset(iod->mc);
+ case IOCTL_MODEM_PROTOCOL_SUSPEND:
+ mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name);
+ if (iod->format == IPC_MULTI_RAW) {
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
+ return 0;
+ }
+ return -EINVAL;
+
+ case IOCTL_MODEM_PROTOCOL_RESUME:
+ mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name);
+ if (iod->format != IPC_MULTI_RAW) {
+ iodevs_for_each(iod->msd, iodev_netif_wake, 0);
+ return 0;
+ }
+ return -EINVAL;
case IOCTL_MIF_LOG_DUMP:
iodevs_for_each(iod->msd, iodev_dump_status, 0);
+ user_buff = (void __user *)arg;
size = MAX_MIF_BUFF_SIZE;
- ret = copy_to_user((void __user *)arg, &size,
- sizeof(unsigned long));
- if (ret < 0)
+ if (copy_to_user(user_buff, &size, sizeof(unsigned long)))
return -EFAULT;
-
- mif_dump_log(iod->mc->msd, iod);
+ mif_dump_log(mc->msd, iod);
return 0;
- case IOCTL_MIF_DPRAM_DUMP:
-#ifdef CONFIG_LINK_DEVICE_DPRAM
- if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) {
- size = iod->mc->mdm_data->dpram_ctl->dp_size;
- ret = copy_to_user((void __user *)arg, &size,
- sizeof(unsigned long));
- if (ret < 0)
- return -EFAULT;
- mif_dump_dpram(iod);
- return 0;
- }
-#endif
- return -EINVAL;
-
default:
/* If you need to handle the ioctl for specific link device,
* then assign the link ioctl handler to ld->ioctl
@@ -1222,9 +1250,10 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (ld->ioctl)
return ld->ioctl(ld, iod, cmd, arg);
- mif_info("%s: ERR! cmd 0x%X not defined.\n", iod->name, cmd);
+ mif_info("%s: ERR! undefined cmd 0x%X\n", iod->name, cmd);
return -EINVAL;
}
+
return 0;
}
@@ -1234,49 +1263,76 @@ static ssize_t misc_write(struct file *filp, const char __user *data,
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
struct sk_buff *skb;
+ u8 *buff;
int ret;
- unsigned headroom = 0;
- unsigned tailroom = 0;
- size_t tx_size;
- struct sipc5_frame_data frm;
- struct timespec epoch;
+ size_t headroom;
+ size_t tailroom;
+ size_t tx_bytes;
+ u8 cfg;
if (iod->format <= IPC_RFS && iod->id == 0)
return -EINVAL;
- headroom = tx_build_link_header(&frm, iod, ld, count);
+ cfg = sipc5_build_config(iod, ld, count);
+
+ if (cfg)
+ headroom = sipc5_get_hdr_len(&cfg);
+ else
+ headroom = 0;
if (ld->aligned)
tailroom = sipc5_calc_padding_size(headroom + count);
+ else
+ tailroom = 0;
- tx_size = headroom + count + tailroom;
+ tx_bytes = headroom + count + tailroom;
- skb = alloc_skb(tx_size, GFP_KERNEL);
+ skb = alloc_skb(tx_bytes, GFP_KERNEL);
if (!skb) {
- mif_info("%s: ERR! alloc_skb fail (tx_size:%d)\n",
- iod->name, tx_size);
+ mif_info("%s: ERR! alloc_skb fail (tx_bytes:%d)\n",
+ iod->name, tx_bytes);
return -ENOMEM;
}
- /* store IPC link header*/
- memcpy(skb_put(skb, headroom), frm.hdr, headroom);
+ /* Build SIPC5 link header*/
+ if (cfg) {
+ buff = skb_put(skb, headroom);
+ sipc5_build_header(iod, ld, buff, cfg, 0, count);
+ }
- /* store IPC message */
- if (copy_from_user(skb_put(skb, count), data, count) != 0) {
- if (skb)
- dev_kfree_skb_any(skb);
+ /* Store IPC message */
+ buff = skb_put(skb, count);
+ if (copy_from_user(buff, data, count)) {
+ mif_err("%s->%s: ERR! copy_from_user fail (count %d)\n",
+ iod->name, ld->name, count);
+ dev_kfree_skb_any(skb);
return -EFAULT;
}
- if (iod->id == SIPC5_CH_ID_FMT_0) {
+ /* Apply padding */
+ if (tailroom)
+ skb_put(skb, tailroom);
+
+ if (iod->format == IPC_FMT) {
+ struct timespec epoch;
+ u8 *msg = (skb->data + headroom);
+#if 0
+ char tag[MIF_MAX_STR_LEN];
+ snprintf(tag, MIF_MAX_STR_LEN, "%s: RIL2MIF", iod->mc->name);
+ pr_ipc(1, tag, msg, (count > 20 ? 20 : count));
+#endif
getnstimeofday(&epoch);
mif_time_log(iod->mc->msd, epoch, NULL, 0);
- mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len);
+ mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, msg, count);
}
- /* store padding */
- if (tailroom)
- skb_put(skb, tailroom);
+#if 0
+ if (iod->format == IPC_RAMDUMP) {
+ char tag[MIF_MAX_STR_LEN];
+ snprintf(tag, MIF_MAX_STR_LEN, "%s: DUMP2MIF", iod->name);
+ pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len));
+ }
+#endif
/* send data with sk_buff, link device will put sk_buff
* into the specific sk_buff_q and run work-q to send data
@@ -1286,13 +1342,15 @@ static ssize_t misc_write(struct file *filp, const char __user *data,
ret = ld->send(ld, iod, skb);
if (ret < 0) {
- mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret);
+ mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n",
+ iod->name, ld->name, ret, tx_bytes);
return ret;
}
- if (ret != tx_size)
- mif_info("%s: wrong tx size (count:%d tx_size:%d ret:%d)\n",
- iod->name, count, tx_size, ret);
+ if (ret != tx_bytes) {
+ mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n",
+ iod->name, ld->name, ret, tx_bytes, count);
+ }
return count;
}
@@ -1304,24 +1362,38 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sk_buff *skb;
int copied = 0;
- struct timespec epoch;
- skb = skb_dequeue(rxq);
- if (!skb) {
+ if (skb_queue_empty(rxq)) {
mif_info("%s: ERR! no data in rxq\n", iod->name);
return 0;
}
- if (iod->id == SIPC5_CH_ID_FMT_0) {
+ skb = skb_dequeue(rxq);
+
+ if (iod->format == IPC_FMT) {
+ struct timespec epoch;
+#if 0
+ char tag[MIF_MAX_STR_LEN];
+ snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2RIL", iod->mc->name);
+ pr_ipc(0, tag, skb->data, (skb->len > 20 ? 20 : skb->len));
+#endif
getnstimeofday(&epoch);
mif_time_log(iod->mc->msd, epoch, NULL, 0);
mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len);
}
+#if 0
+ if (iod->format == IPC_RAMDUMP) {
+ char tag[MIF_MAX_STR_LEN];
+ snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2DUMP", iod->name);
+ pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len));
+ }
+#endif
+
copied = skb->len > count ? count : skb->len;
if (copy_to_user(buf, skb->data, copied)) {
- mif_info("%s: ERR! copy_to_user fail\n", iod->name);
+ mif_err("%s: ERR! copy_to_user fail\n", iod->name);
dev_kfree_skb_any(skb);
return -EFAULT;
}
@@ -1339,43 +1411,6 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
return copied;
}
-#ifdef CONFIG_LINK_DEVICE_C2C
-static int misc_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- int r = 0;
- unsigned long size = 0;
- unsigned long pfn = 0;
- unsigned long offset = 0;
- struct io_device *iod = (struct io_device *)filp->private_data;
-
- if (!vma)
- return -EFAULT;
-
- size = vma->vm_end - vma->vm_start;
- offset = vma->vm_pgoff << PAGE_SHIFT;
- if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) {
- mif_info("ERR: offset + size > C2C_CP_RGN_SIZE\n");
- return -EINVAL;
- }
-
- /* Set the noncacheable property to the region */
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- vma->vm_flags |= VM_RESERVED | VM_IO;
-
- pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset);
- r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
- if (r) {
- mif_info("ERR: Failed in remap_pfn_range()!!!\n");
- return -EAGAIN;
- }
-
- mif_info("%s: VA = 0x%08lx, offset = 0x%lx, size = %lu\n",
- iod->name, vma->vm_start, offset, size);
-
- return 0;
-}
-#endif
-
static const struct file_operations misc_io_fops = {
.owner = THIS_MODULE,
.open = misc_open,
@@ -1384,9 +1419,6 @@ static const struct file_operations misc_io_fops = {
.unlocked_ioctl = misc_ioctl,
.write = misc_write,
.read = misc_read,
-#ifdef CONFIG_LINK_DEVICE_C2C
- .mmap = misc_mmap,
-#endif
};
static int vnet_open(struct net_device *ndev)
@@ -1419,11 +1451,13 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
struct link_device *ld = get_current_link(iod);
struct sk_buff *skb_new;
int ret;
- unsigned headroom = 0;
- unsigned tailroom = 0;
- unsigned long tx_bytes = skb->len;
+ unsigned headroom;
+ unsigned tailroom;
+ size_t count;
+ size_t tx_bytes;
struct iphdr *ip_header = NULL;
- struct sipc5_frame_data frm;
+ u8 *buff;
+ u8 cfg;
/* When use `handover' with Network Bridge,
* user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here.
@@ -1436,19 +1470,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_pull(skb, sizeof(struct ethhdr));
}
- headroom = tx_build_link_header(&frm, iod, ld, skb->len);
+ count = skb->len;
- /* ip loop-back */
- ip_header = (struct iphdr *)skb->data;
- if (iod->msd->loopback_ipaddr &&
- ip_header->daddr == iod->msd->loopback_ipaddr) {
- swap(ip_header->saddr, ip_header->daddr);
- frm.ch_id = DATA_LOOPBACK_CHANNEL;
- frm.hdr[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL;
- }
+ cfg = sipc5_build_config(iod, ld, count);
+
+ headroom = sipc5_get_hdr_len(&cfg);
if (ld->aligned)
- tailroom = sipc5_calc_padding_size(frm.len);
+ tailroom = sipc5_calc_padding_size(headroom + count);
+ else
+ tailroom = 0;
+
+ tx_bytes = headroom + count + tailroom;
if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) {
mif_debug("%s: skb_copy_expand needed\n", iod->name);
@@ -1463,7 +1496,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_new = skb;
}
- memcpy(skb_push(skb_new, headroom), frm.hdr, headroom);
+ /* Build SIPC5 link header*/
+ buff = skb_push(skb_new, headroom);
+ sipc5_build_header(iod, ld, buff, cfg, 0, count);
+
+ /* IP loop-back */
+ ip_header = (struct iphdr *)skb->data;
+ if (iod->msd->loopback_ipaddr &&
+ ip_header->daddr == iod->msd->loopback_ipaddr) {
+ swap(ip_header->saddr, ip_header->daddr);
+ buff[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL;
+ }
+
if (tailroom)
skb_put(skb_new, tailroom);
@@ -1473,12 +1517,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
ret = ld->send(ld, iod, skb_new);
if (ret < 0) {
netif_stop_queue(ndev);
- mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret);
+ mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n",
+ iod->name, ld->name, ret, tx_bytes);
return NETDEV_TX_BUSY;
}
+ if (ret != tx_bytes) {
+ mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n",
+ iod->name, ld->name, ret, tx_bytes, count);
+ }
+
ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += tx_bytes;
+ ndev->stats.tx_bytes += count;
return NETDEV_TX_OK;
}
@@ -1581,16 +1631,19 @@ int sipc5_init_io_device(struct io_device *iod)
ret = misc_register(&iod->miscdev);
if (ret)
mif_info("%s: ERR! misc_register fail\n", iod->name);
+
ret = device_create_file(iod->miscdev.this_device,
&attr_waketime);
if (ret)
mif_info("%s: ERR! device_create_file fail\n",
iod->name);
+
ret = device_create_file(iod->miscdev.this_device,
&attr_loopback);
if (ret)
mif_err("failed to create `loopback file' : %s\n",
iod->name);
+
ret = device_create_file(iod->miscdev.this_device,
&attr_txlink);
if (ret)