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.c878
1 files changed, 542 insertions, 336 deletions
diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c
index 05578f4..a9932c1 100644
--- a/drivers/misc/modem_if/sipc5_io_device.c
+++ b/drivers/misc/modem_if/sipc5_io_device.c
@@ -69,48 +69,129 @@ static ssize_t store_waketime(struct device *dev,
static struct device_attribute attr_waketime =
__ATTR(waketime, S_IRUGO | S_IWUSR, show_waketime, store_waketime);
-static inline int sipc5_check_frame_cfg(u8 *buff, struct sipc5_frame_data *frm)
+static ssize_t show_loopback(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- u8 config = buff[0];
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+ unsigned char *ip = (unsigned char *)&msd->loopback_ipaddr;
+ char *p = buf;
- if ((config & SIPC5_START_MASK) != SIPC5_START_MASK)
- return -EBADMSG;
+ p += sprintf(buf, "%u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
+
+ return p - buf;
+}
+
+static ssize_t store_loopback(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+
+ msd->loopback_ipaddr = ipv4str_to_be32(buf, count);
+
+ return count;
+}
+
+static struct device_attribute attr_loopback =
+ __ATTR(loopback, S_IRUGO | S_IWUSR, show_loopback, store_loopback);
+
+static void iodev_showtxlink(struct io_device *iod, void *args)
+{
+ char **p = (char **)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);
+}
+
+static ssize_t show_txlink(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+ char *p = buf;
+
+ iodevs_for_each(msd, iodev_showtxlink, &p);
+
+ return p - buf;
+}
+
+static ssize_t store_txlink(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ /* don't change without gpio dynamic switching */
+ return -EINVAL;
+}
- frm->config = config;
- frm->hdr_len = SIPC5_MIN_HEADER_SIZE;
+static struct device_attribute attr_txlink =
+ __ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink);
- if (config & SIPC5_PADDING_EXIST)
+/**
+ * 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)
+{
+ frm->config = cfg;
+
+ if (likely(cfg & SIPC5_PADDING_EXIST))
frm->padding = true;
- if (unlikely(config & SIPC5_EXT_FIELD_EXIST)) {
- frm->ext_fld = true;
- if (config & SIPC5_CTL_FIELD_EXIST) {
+ 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;
}
+ } else {
+ frm->hdr_len = SIPC5_MIN_HEADER_SIZE;
}
- return SIPC5_CONFIG_SIZE;
+ return frm->hdr_len;
}
-static inline void sipc5_build_rx_frame_data(struct link_device *ld,
+/**
+ * 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];
- frm->len = *sz16;
- if (unlikely(frm->ext_fld)) {
- if (frm->ctl_fld)
- frm->control = frm->hdr[SIPC5_CTL_OFFSET];
- else
- frm->len = *sz32;
- }
+ 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;
@@ -118,114 +199,83 @@ static inline void sipc5_build_rx_frame_data(struct link_device *ld,
ld->name, frm->ch_id, frm->len, frm->control, frm->data_len);
}
-static inline struct sk_buff *sipc5_prepare_rx_skb(struct io_device *iod,
- struct link_device *ld, unsigned len)
-{
- struct sk_buff *skb;
-
- if (iod->format == IPC_MULTI_RAW && iod->use_handover) {
- int alloc = len + sizeof(struct ethhdr);
- skb = rx_alloc_skb(alloc, GFP_ATOMIC, iod, ld);
- if (unlikely(!skb))
- return NULL;
- skb_reserve(skb, sizeof(struct ethhdr));
- } else {
- skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld);
- }
-
- return skb;
-}
-
-/* Check and store link layer header, then alloc an skb */
-static int sipc5_recv_header(struct io_device *iod, struct link_device *ld,
- u8 *buff, unsigned size, struct sipc5_frame_data *frm)
+/**
+ * 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)
{
- int len = 0;
-
- mif_debug("%s: size %d\n", ld->name, size);
+ u8 *buff = frm->hdr;
+ u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET);
+ u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET);
- if (likely(!frm->config)) {
- len = sipc5_check_frame_cfg(buff, frm);
- if (len < 0) {
- mif_info("%s: ERR! wrong start (0x%02x)\n",
- ld->name, buff[0]);
- return len;
- }
+ memset(frm, 0, sizeof(struct sipc5_frame_data));
- /* 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);
+ if (iod->format == IPC_CMD ||
+ iod->format == IPC_BOOT ||
+ iod->format == IPC_RAMDUMP) {
+ frm->len = count;
+ return 0;
}
- frm->hdr_rcvd += len;
+ frm->config = SIPC5_START_MASK;
- mif_debug("%s: FRM hdr.len:%d hdr.rcvd:%d\n",
- ld->name, frm->hdr_len, frm->hdr_rcvd);
+ if (iod->format == IPC_FMT && count > 2048) {
+ frm->ctl_fld = true;
+ frm->config |= SIPC5_EXT_FIELD_EXIST;
+ frm->config |= SIPC5_CTL_FIELD_EXIST;
+ }
- if (frm->hdr_rcvd >= frm->hdr_len) {
- struct sk_buff *skb;
- sipc5_build_rx_frame_data(ld, frm);
- skb = sipc5_prepare_rx_skb(iod, ld, frm->data_len);
- fragdata(iod, ld)->skb_recv = skb;
+ if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) {
+ frm->ext_len = true;
+ frm->config |= SIPC5_EXT_FIELD_EXIST;
}
- return len;
-}
+ if (ld->aligned)
+ frm->config |= SIPC5_PADDING_EXIST;
-/* copy data to skb */
-static int sipc5_recv_payload(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;
- unsigned rest = frm->data_len - frm->data_rcvd;
- unsigned len;
+ frm->ch_id = iod->id;
- /*
- ** rest == frm->data_len - frm->data_rcvd == tailroom of skb or mifb
- */
- rest = frm->data_len - frm->data_rcvd;
- mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n",
- ld->name, frm->data_len, frm->data_rcvd, rest, size);
+ frm->hdr_len = sipc5_get_hdr_len(frm->config);
+ frm->data_len = count;
+ frm->len = frm->hdr_len + frm->data_len;
- /* If there is no skb, data must be dropped. */
- len = min(rest, size);
- if (skb)
- memcpy(skb_put(skb, len), buff, len);
+ buff[SIPC5_CONFIG_OFFSET] = frm->config;
+ buff[SIPC5_CH_ID_OFFSET] = frm->ch_id;
- frm->data_rcvd += len;
+ if (unlikely(frm->ext_len))
+ *sz32 = (u32)frm->len;
+ else
+ *sz16 = (u16)frm->len;
- mif_debug("%s: FRM data.len:%d data.rcvd:%d\n",
- ld->name, frm->data_len, frm->data_rcvd);
+ if (unlikely(frm->ctl_fld))
+ buff[SIPC5_CTL_OFFSET] = frm->control;
- return len;
+ return frm->hdr_len;
}
-static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
+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;
+ struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sipc_fmt_hdr *fh;
struct sk_buff *rx_skb;
- unsigned id;
-
- rxq = &iod->sk_rx_q;
- if (!rxq) {
- mif_debug("%s: no sk_rx_q\n", iod->name);
- return -EINVAL;
- }
-
- id = frm->control & 0x7F;
+ u8 ctrl = skbpriv(skb)->control;
+ unsigned id = ctrl & 0x7F;
if (iod->skb[id] == NULL) {
/*
** There has been no multiple frame with this ID.
*/
- if ((frm->control & 0x80) == 0) {
+ if ((ctrl & 0x80) == 0) {
/*
** It is a single frame because the "more" bit is 0.
*/
@@ -252,7 +302,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
mif_debug("%s: start multi-frame (ID:%d len:%d)\n",
iod->name, id, fh->len);
- rx_skb = rx_alloc_skb(fh->len, GFP_ATOMIC, iod, ld);
+ rx_skb = rx_alloc_skb(fh->len, iod, ld);
if (!rx_skb) {
mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name);
return -ENOMEM;
@@ -269,7 +319,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
dev_kfree_skb_any(skb);
- if (frm->control & 0x80) {
+ 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);
@@ -295,7 +345,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
return 0;
}
-static int sipc5_recv_rfs(struct sk_buff *skb)
+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;
@@ -303,8 +353,7 @@ static int sipc5_recv_rfs(struct sk_buff *skb)
skb_queue_tail(rxq, skb);
if (unlikely(rxq->qlen > 2048)) {
struct sk_buff *victim;
- mif_debug("%s: ERR! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
+ mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen);
victim = skb_dequeue(rxq);
dev_kfree_skb_any(victim);
} else {
@@ -316,7 +365,42 @@ static int sipc5_recv_rfs(struct sk_buff *skb)
return 0;
}
-static int sipc5_recv_misc(struct sk_buff *skb)
+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;
@@ -324,8 +408,7 @@ static int sipc5_recv_misc(struct sk_buff *skb)
skb_queue_tail(rxq, skb);
if (unlikely(rxq->qlen > 2048)) {
struct sk_buff *victim;
- mif_debug("%s: ERR! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
+ mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen);
victim = skb_dequeue(rxq);
dev_kfree_skb_any(victim);
} else {
@@ -337,7 +420,7 @@ static int sipc5_recv_misc(struct sk_buff *skb)
return 0;
}
-static int sipc5_recv_pdp(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 net_device *ndev;
@@ -386,69 +469,24 @@ static int sipc5_recv_pdp(struct sk_buff *skb)
return ret;
}
-/** rx_iodev_work - rx workqueue for raw data
- *
- * @work: workqueue
- *
- * If you throw packets to Network layer directly in interrupt context,
- * sometimes, you'll meet backlog buffer full of Network layer.
- * Applications need some time to get packets from Network layer.
- * And, we need to retry logic when NET_RX_DROP occured. this work ensure
- * retry when netif_rx failed.
- */
-static void rx_iodev_work(struct work_struct *work)
-{
- int ret = 0;
- struct sk_buff *skb = NULL;
- struct io_device *iod = container_of(work, struct io_device,
- rx_work.work);
-
- while ((skb = skb_dequeue(&iod->sk_rx_q)) != NULL) {
- ret = sipc5_recv_pdp(skb);
- if (ret < 0) {
- mif_err("%s: sipc5_recv_pdp fail (err %d)",
- iod->name, ret);
- dev_kfree_skb_any(skb);
- } else if (ret == NET_RX_DROP) {
- mif_err("%s: ret == NET_RX_DROP. retry later.\n",
- iod->name);
- schedule_delayed_work(&iod->rx_work,
- msecs_to_jiffies(100));
- return;
- }
- }
-}
-
-static int rx_multipdp(struct sk_buff *skb)
-{
- /* in sipc5, this iod == real_iod. not MULTIPDP's iod */
- struct io_device *iod = skbpriv(skb)->iod;
-
- if (iod->io_typ != IODEV_NET) {
- mif_info("%s: ERR! wrong io_type %d\n", iod->name, iod->io_typ);
- return -EINVAL;
- }
-
- skb_queue_tail(&iod->sk_rx_q, skb);
- mif_debug("%s: sk_rx_qlen %d\n", iod->name, iod->sk_rx_q.qlen);
-
- schedule_delayed_work(&iod->rx_work, 0);
- return 0;
-}
-
-static int sipc5_recv_demux(struct link_device *ld, struct sk_buff *skb,
- struct sipc5_frame_data *frm)
+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;
- if (unlikely(frm->ch_id >= SIPC5_CH_ID_MAX || frm->ch_id == 0)) {
- mif_info("%s: ERR! invalid channel %d\n", ld->name, frm->ch_id);
+ if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) {
+ mif_info("%s: ERR! invalid ch# %d\n", link, ch);
return -ENODEV;
}
- iod = link_get_iod_with_channel(ld, frm->ch_id);
+ /* 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", ld->name, frm->ch_id);
+ mif_info("%s: ERR! no iod for ch# %d\n", link, ch);
return -ENODEV;
}
@@ -456,54 +494,127 @@ static int sipc5_recv_demux(struct link_device *ld, struct sk_buff *skb,
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))
+ return rx_loopback(skb);
+
if (atomic_read(&iod->opened) <= 0) {
- mif_info("%s: ERR! %s is not opened\n", ld->name, iod->name);
+ mif_info("%s: ERR! %s is not opened\n", link, iod->name);
return -ENODEV;
}
- if (frm->ch_id >= SIPC5_CH_ID_RFS_0)
- return sipc5_recv_rfs(skb);
- else if (frm->ch_id >= SIPC5_CH_ID_FMT_0)
- return sipc5_recv_fmt(skb, frm);
+ if (ch >= SIPC5_CH_ID_RFS_0)
+ return rx_rfs_frame(skb);
+ else if (ch >= SIPC5_CH_ID_FMT_0)
+ return rx_fmt_frame(skb);
else if (iod->io_typ == IODEV_MISC)
- return sipc5_recv_misc(skb);
+ return rx_raw_misc(skb);
else
- return rx_multipdp(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)
+{
+ 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);
+ }
+
+ 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;
+ }
+
+ 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->data_rcvd += len;
+
+ mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n",
+ link, frm->data_len, frm->data_rcvd);
+
+ return len;
}
-static int sipc5_recv_ipc_from_serial(struct io_device *iod,
- struct link_device *ld, const char *data, unsigned size)
+static int rx_frame_from_serial(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 err = 0;
int done = 0;
- mif_debug("%s: size = %d\n", ld->name, size);
+ mif_debug("%s: size = %d\n", link, size);
if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) {
/*
- There may be an skb or mifb (fragdata(iod, ld)->skb_recv) that
- is waiting for more IPC frame. In this case, sipc5_recv_header
- function must be skipped.
+ ** 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",
- ld->name, frm->data_len, frm->data_rcvd);
+ link, frm->data_len, frm->data_rcvd);
goto recv_data;
}
next_frame:
/* Receive and analyze header, then prepare an akb */
- err = done = sipc5_recv_header(iod, ld, buff, rest, frm);
+ err = done = rx_header_from_serial(iod, ld, buff, rest, frm);
if (err < 0)
goto err_exit;
buff += done;
rest -= done;
- mif_debug("%s: sipc5_recv_header() -> done:%d rest:%d\n",
- ld->name, done, rest);
+ mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest);
if (rest < 0)
goto err_range;
@@ -513,15 +624,13 @@ next_frame:
recv_data:
err = 0;
- mif_debug("%s: done:%d rest:%d -> recv_payload()\n",
- ld->name, done, rest);
+ mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest);
- done = sipc5_recv_payload(iod, ld, buff, rest, frm);
+ done = rx_payload_from_serial(iod, ld, buff, rest, frm);
buff += done;
rest -= done;
- mif_debug("%s: recv_payload() -> done:%d rest:%d\n",
- ld->name, done, rest);
+ mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest);
if (rest == 0 && frm->data_rcvd < frm->data_len) {
/*
@@ -538,14 +647,14 @@ recv_data:
done = sipc5_calc_padding_size(frm->len);
if (done > rest) {
mif_info("%s: ERR! padding %d > rest %d\n",
- ld->name, done, rest);
+ link, done, rest);
goto err_exit;
}
buff += done;
rest -= done;
- mif_debug("%s: padding:%d -> rest:%d\n", ld->name, done, rest);
+ mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest);
if (rest < 0)
goto err_range;
@@ -554,12 +663,12 @@ recv_data:
skb = fragdata(iod, ld)->skb_recv;
if (likely(skb)) {
- mif_debug("%s: len %d -> recv_demux()\n", ld->name, skb->len);
- err = sipc5_recv_demux(ld, skb, frm);
+ 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", ld->name, skb->len);
+ mif_debug("%s: len:%d -> drop\n", link, skb->len);
}
/* initialize the skb_recv and the frame_data buffer */
@@ -578,44 +687,61 @@ err_exit:
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", ld->name);
+ mif_info("%s: ERR! clear frag\n", link);
}
return err;
err_range:
- mif_info("%s: ERR! size:%d vs. rest:%d\n", ld->name, size, rest);
+ mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest);
return size;
}
-/* Check and store link layer header */
-static int sipc5_recv_header_from_dpram(struct link_device *ld, u8 *buff,
+/**
+ * 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
+ *
+ * 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
+ *
+ * Returns SIPC5 header length
+ */
+static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest,
struct sipc5_frame_data *frm)
{
- int len = sipc5_check_frame_cfg(buff, frm);
+ char *link = ld->name;
+ u8 cfg = buff[0];
- if (len < 0) {
- mif_info("%s: ERR! wrong start 0x%02x\n",
- ld->name, buff[0]);
- return len;
- } else if (len > SIPC5_MAX_HEADER_SIZE) {
- mif_info("%s: ERR! wrong header length %d\n",
- ld->name, len);
+ /* 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);
- /* Copy the link layer header to the header buffer */
- len = frm->hdr_len;
- memcpy(frm->hdr, buff, len);
+ /* Store the link layer header to the header buffer */
+ memcpy(frm->hdr, buff, frm->hdr_len);
frm->hdr_rcvd = frm->hdr_len;
- sipc5_build_rx_frame_data(ld, frm);
+ /* Build and store the meta data of this frame */
+ rx_build_meta_data(ld, frm);
- return len;
+ /* Verify frame length */
+ if (unlikely(frm->len > rest)) {
+ mif_info("%s: ERR! frame length %d > rest %d\n",
+ link, frm->len, rest);
+ return -EBADMSG;
+ }
+
+ return frm->hdr_rcvd;
}
/* copy data to skb */
-static int sipc5_recv_payload_from_dpram(struct sk_buff *skb, u8 *buff,
- unsigned len)
+static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len)
{
/* If there is no skb, data must be dropped. */
if (skb)
@@ -623,60 +749,81 @@ static int sipc5_recv_payload_from_dpram(struct sk_buff *skb, u8 *buff,
return len;
}
-static int sipc5_recv_ipc_from_dpram(struct io_device *iod,
- struct link_device *ld, const char *data, unsigned size)
+static int rx_frame_from_mem(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;
- mif_debug("%s: size = %d\n", ld->name, size);
+ mif_debug("%s: size = %d\n", link, size);
while (rest > 0) {
/* Initialize the frame data buffer */
memset(frm, 0, sizeof(struct sipc5_frame_data));
+ skb = NULL;
/* Receive and analyze link layer header */
- done = sipc5_recv_header_from_dpram(ld, buff, frm);
+ done = rx_header_from_mem(ld, buff, rest, frm);
if (unlikely(done < 0))
return -EBADMSG;
+ /* Verify rest size */
rest -= done;
- if (rest < 0)
+ if (rest < 0) {
+ mif_info("%s: ERR! rx_header -> rest %d\n", link, rest);
return -ERANGE;
+ }
+
+ /* Move buff pointer to the payload */
buff += done;
/* Prepare an akb */
len = frm->data_len;
- skb = sipc5_prepare_rx_skb(iod, ld, len);
+ skb = rx_alloc_skb(len, iod, ld);
+
+ /* Store channel ID and control fields to the CB of the skb */
+ skbpriv(skb)->ch_id = frm->ch_id;
+ skbpriv(skb)->control = frm->control;
/* Receive payload */
- mif_debug("%s: done:%d rest:%d len:%d -> recv_payload()\n",
- ld->name, done, rest, len);
- done = sipc5_recv_payload_from_dpram(skb, buff, len);
+ 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)
+ 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;
/* A padding size is applied to access the next IPC frame. */
if (frm->padding) {
done = sipc5_calc_padding_size(frm->len);
- rest -= done;
- if (rest < 0)
+ if (done > rest) {
+ mif_info("%s: ERR! padding %d > rest %d\n",
+ link, done, rest);
+ if (skb)
+ dev_kfree_skb_any(skb);
return -ERANGE;
+ }
buff += done;
+ rest -= done;
}
if (likely(skb)) {
- mif_debug("%s: len:%d -> demux\n", ld->name, skb->len);
- if (sipc5_recv_demux(ld, skb, frm) < 0)
+ 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", ld->name, skb->len);
+ mif_debug("%s: len:%d -> drop\n", link, skb->len);
}
}
@@ -689,26 +836,16 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
{
struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sk_buff *skb;
+ char *link = ld->name;
int err;
- if (!ld) {
- mif_info("ERR: !ld\n");
- return -EINVAL;
- }
-
- if (!iod) {
- mif_info("%s: ERR! !iod\n", ld->name);
- return -EINVAL;
- }
-
if (!data) {
- mif_info("%s: ERR! !data\n", ld->name);
+ mif_info("%s: ERR! !data\n", link);
return -EINVAL;
}
if (len <= 0) {
- mif_info("%s: ERR! len = %d <= 0\n",
- ld->name, len);
+ mif_info("%s: ERR! len %d <= 0\n", link, len);
return -EINVAL;
}
@@ -721,13 +858,13 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
wake_lock_timeout(&iod->wakelock, iod->waketime);
if (ld->link_type == LINKDEV_DPRAM && ld->aligned)
- err = sipc5_recv_ipc_from_dpram(iod, ld, data, len);
+ err = rx_frame_from_mem(iod, ld, data, len);
else
- err = sipc5_recv_ipc_from_serial(iod, ld, data, len);
+ err = rx_frame_from_serial(iod, ld, data, len);
if (err < 0)
- mif_info("%s: ERR! sipc5_recv_ipc_from_link fail "
- "(err %d)\n", ld->name, err);
+ mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n",
+ link, err);
return err;
@@ -735,13 +872,13 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
case IPC_BOOT:
case IPC_RAMDUMP:
/* save packet to sk_buff */
- skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(len, iod, ld);
if (!skb) {
- mif_info("%s: ERR! rx_alloc_skb fail\n", ld->name);
+ mif_info("%s: ERR! rx_alloc_skb fail\n", link);
return -ENOMEM;
}
- mif_debug("%s: len:%d -> iod:%s\n", ld->name, len, iod->name);
+ mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name);
memcpy(skb_put(skb, len), data, len);
skb_queue_tail(rxq, skb);
@@ -757,68 +894,84 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
return len;
default:
- mif_info("%s: ERR! unknown format %d\n", ld->name, iod->format);
+ mif_info("%s: ERR! unknown format %d\n", link, iod->format);
return -EINVAL;
}
}
-static unsigned sipc5_build_tx_link_header(struct sipc5_frame_data *frm,
- struct io_device *iod, struct link_device *ld, ssize_t count)
+static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld,
+ struct sk_buff *skb)
{
- u8 *buff = frm->hdr;
- u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET);
- u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET);
+ struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
+ u8 cfg = skb->data[0];
+ /* Initialize the frame data buffer */
memset(frm, 0, sizeof(struct sipc5_frame_data));
- if (iod->format == IPC_CMD ||
- iod->format == IPC_BOOT ||
- iod->format == IPC_RAMDUMP) {
- frm->len = count;
- return 0;
- }
+ /*
+ ** The start of a link layer header has already been checked in the
+ ** link device.
+ */
- frm->config = SIPC5_START_MASK;
+ /* Analyze the configuration of the link layer header */
+ rx_check_frame_cfg(cfg, frm);
- if (iod->format == IPC_FMT && count > 2048) {
- frm->ext_fld = true;
- frm->ctl_fld = true;
+ /* Store the link layer header to the header buffer */
+ memcpy(frm->hdr, skb->data, frm->hdr_len);
+ frm->hdr_rcvd = frm->hdr_len;
- frm->config |= SIPC5_EXT_FIELD_EXIST;
- frm->config |= SIPC5_CTL_FIELD_EXIST;
- }
+ /* Build and store the meta data of this frame */
+ rx_build_meta_data(ld, frm);
- if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) {
- frm->ext_fld = true;
- frm->ext_len = true;
+ /*
+ ** The length of the frame has already been checked in the link device.
+ */
- frm->config |= SIPC5_EXT_FIELD_EXIST;
- }
+ /* Trim the link layer header off the frame */
+ skb_pull(skb, frm->hdr_len);
- if (ld->aligned)
- frm->config |= SIPC5_PADDING_EXIST;
+ /* Store channel ID and control fields to the CB of the skb */
+ skbpriv(skb)->ch_id = frm->ch_id;
+ skbpriv(skb)->control = frm->control;
- frm->ch_id = iod->id;
+ /* Demux the frame */
+ if (rx_demux(ld, skb) < 0) {
+ mif_err("%s: ERR! rx_demux fail\n", ld->name);
+ return -EINVAL;
+ }
- frm->hdr_len = sipc5_get_hdr_size(frm->config);
- frm->data_len = count;
- frm->len = frm->hdr_len + frm->data_len;
+ return 0;
+}
- buff[SIPC5_CONFIG_OFFSET] = frm->config;
- buff[SIPC5_CH_ID_OFFSET] = frm->ch_id;
+/* 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;
- if (frm->ext_fld) {
- if (frm->ctl_fld) {
- *sz16 = (u16)frm->len;
- buff[SIPC5_CTL_OFFSET] = frm->control;
- } else {
- *sz32 = (u32)frm->len;
+ switch (dev) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ case IPC_MULTI_RAW:
+ if (iod->waketime)
+ wake_lock_timeout(&iod->wakelock, iod->waketime);
+
+ err = rx_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);
}
- } else {
- *sz16 = (u16)frm->len;
- }
- return frm->hdr_len;
+ return err;
+
+ default:
+ mif_info("%s: ERR! unknown device %d\n", link, dev);
+ return -EINVAL;
+ }
}
/* inform the IO device that the modem is now online or offline or
@@ -858,18 +1011,25 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online)
}
}
+static void iodev_dump_status(struct io_device *iod, void *args)
+{
+ if (iod->format == IPC_RAW && iod->io_typ == IODEV_NET) {
+ struct link_device *ld = get_current_link(iod);
+ mif_com_log(iod->mc->msd, "%s: %s\n", iod->name, ld->name);
+ }
+}
+
static int misc_open(struct inode *inode, struct file *filp)
{
struct io_device *iod = to_io_device(filp->private_data);
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
int ret;
filp->private_data = (void *)iod;
- mif_info("%s\n", iod->name);
atomic_inc(&iod->opened);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ 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) {
@@ -880,24 +1040,27 @@ static int misc_open(struct inode *inode, struct file *filp)
}
}
+ mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened));
+
return 0;
}
static int misc_release(struct inode *inode, struct file *filp)
{
struct io_device *iod = (struct io_device *)filp->private_data;
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
- mif_info("%s\n", iod->name);
atomic_dec(&iod->opened);
skb_queue_purge(&iod->sk_rx_q);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->terminate_comm)
ld->terminate_comm(ld, iod);
}
+ mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened));
+
return 0;
}
@@ -930,6 +1093,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
char cpinfo_buf[530] = "CP Crash ";
+ unsigned long size;
+ int ret;
switch (cmd) {
case IOCTL_MODEM_ON:
@@ -952,9 +1117,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name);
return iod->mc->ops.modem_boot_off(iod->mc);
- case IOCTL_MODEM_START:
- mif_info("%s: IOCTL_MODEM_START\n", iod->name);
- return 0;
+ 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;
case IOCTL_MODEM_STATUS:
mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name);
@@ -983,7 +1151,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0);
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
return 0;
case IOCTL_MODEM_PROTOCOL_RESUME:
@@ -993,7 +1161,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_wake, 0);
+ iodevs_for_each(iod->msd, iodev_netif_wake, 0);
return 0;
case IOCTL_MODEM_DUMP_START:
@@ -1022,6 +1190,31 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name);
return iod->mc->ops.modem_dump_reset(iod->mc);
+ case IOCTL_MIF_LOG_DUMP:
+ iodevs_for_each(iod->msd, iodev_dump_status, 0);
+ size = MAX_MIF_BUFF_SIZE;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+
+ mif_dump_log(iod->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
@@ -1040,18 +1233,18 @@ 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 sipc5_frame_data *frm = &iod->meta_frame;
struct sk_buff *skb;
- u8 *buff;
- unsigned headroom;
+ int ret;
+ unsigned headroom = 0;
unsigned tailroom = 0;
size_t tx_size;
- int ret;
+ struct sipc5_frame_data frm;
+ struct timespec epoch;
if (iod->format <= IPC_RFS && iod->id == 0)
return -EINVAL;
- headroom = sipc5_build_tx_link_header(frm, iod, ld, count);
+ headroom = tx_build_link_header(&frm, iod, ld, count);
if (ld->aligned)
tailroom = sipc5_calc_padding_size(headroom + count);
@@ -1066,20 +1259,19 @@ static ssize_t misc_write(struct file *filp, const char __user *data,
}
/* store IPC link header*/
- buff = skb_put(skb, headroom);
- memcpy(buff, frm->hdr, headroom);
+ memcpy(skb_put(skb, headroom), frm.hdr, headroom);
/* store IPC message */
- buff = skb_put(skb, count);
- if (copy_from_user(buff, data, count) != 0) {
+ if (copy_from_user(skb_put(skb, count), data, count) != 0) {
if (skb)
dev_kfree_skb_any(skb);
return -EFAULT;
}
if (iod->id == SIPC5_CH_ID_FMT_0) {
- mif_ipc_log(iod->mc, MIF_IOD_TX_EVT, iod, ld, buff, count);
- mif_flush_logs(ld->mc);
+ getnstimeofday(&epoch);
+ mif_time_log(iod->mc->msd, epoch, NULL, 0);
+ mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len);
}
/* store padding */
@@ -1112,6 +1304,7 @@ 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) {
@@ -1120,9 +1313,9 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
}
if (iod->id == SIPC5_CH_ID_FMT_0) {
- mif_ipc_log(iod->mc, MIF_IOD_RX_EVT, iod, NULL, skb->data,
- skb->len);
- mif_flush_logs(iod->mc);
+ getnstimeofday(&epoch);
+ mif_time_log(iod->mc->msd, epoch, NULL, 0);
+ mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len);
}
copied = skb->len > count ? count : skb->len;
@@ -1200,7 +1393,7 @@ static int vnet_open(struct net_device *ndev)
{
struct vnet *vnet = netdev_priv(ndev);
- mif_info("%s\n", vnet->iod->name);
+ mif_err("%s\n", vnet->iod->name);
netif_start_queue(ndev);
atomic_inc(&vnet->iod->opened);
@@ -1211,10 +1404,11 @@ static int vnet_stop(struct net_device *ndev)
{
struct vnet *vnet = netdev_priv(ndev);
- mif_info("%s\n", vnet->iod->name);
+ mif_err("%s\n", vnet->iod->name);
atomic_dec(&vnet->iod->opened);
netif_stop_queue(ndev);
+ skb_queue_purge(&vnet->iod->sk_rx_q);
return 0;
}
@@ -1223,32 +1417,38 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
struct vnet *vnet = netdev_priv(ndev);
struct io_device *iod = vnet->iod;
struct link_device *ld = get_current_link(iod);
- struct sipc5_frame_data *frm = &iod->meta_frame;
struct sk_buff *skb_new;
- unsigned headroom;
- unsigned tailroom = 0;
int ret;
-
-#if 0
- mif_ipc_log(iod->mc, MIF_IOD_TX_EVT, iod, ld, skb->data, skb->len);
- mif_flush_logs(ld->mc);
-#endif
+ unsigned headroom = 0;
+ unsigned tailroom = 0;
+ unsigned long tx_bytes = skb->len;
+ struct iphdr *ip_header = NULL;
+ struct sipc5_frame_data frm;
/* When use `handover' with Network Bridge,
- * user -> TCP/IP(kernel) -> bridge device -> TCP/IP(kernel) -> this.
- *
- * We remove the one ethernet header of skb before using skb->len,
- * because the skb has two ethernet headers.
+ * user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here.
+ * bridge device is ethernet device unlike xxxx_rmnet(net device).
+ * We remove the an ethernet header of skb before using skb->len,
+ * because bridge device added an ethernet header to skb.
*/
if (iod->use_handover) {
if (iod->id >= PS_DATA_CH_0 && iod->id <= PS_DATA_CH_LAST)
skb_pull(skb, sizeof(struct ethhdr));
}
- headroom = sipc5_build_tx_link_header(frm, iod, ld, skb->len);
+ headroom = tx_build_link_header(&frm, iod, ld, 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;
+ }
if (ld->aligned)
- tailroom = sipc5_calc_padding_size(frm->len);
+ tailroom = sipc5_calc_padding_size(frm.len);
if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) {
mif_debug("%s: skb_copy_expand needed\n", iod->name);
@@ -1263,7 +1463,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_new = skb;
}
- memcpy(skb_push(skb_new, headroom), frm->hdr, headroom);
+ memcpy(skb_push(skb_new, headroom), frm.hdr, headroom);
if (tailroom)
skb_put(skb_new, tailroom);
@@ -1278,7 +1478,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
}
ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += skb->len;
+ ndev->stats.tx_bytes += tx_bytes;
return NETDEV_TX_OK;
}
@@ -1327,13 +1527,13 @@ int sipc5_init_io_device(struct io_device *iod)
/* Get data from link device */
mif_debug("%s: SIPC version = %d\n", iod->name, iod->ipc_version);
iod->recv = io_dev_recv_data_from_link_dev;
+ iod->recv_skb = io_dev_recv_skb_from_link_dev;
/* Register misc or net device */
switch (iod->io_typ) {
case IODEV_MISC:
init_waitqueue_head(&iod->wq);
skb_queue_head_init(&iod->sk_rx_q);
- INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
iod->miscdev.minor = MISC_DYNAMIC_MINOR;
iod->miscdev.name = iod->name;
@@ -1347,8 +1547,6 @@ int sipc5_init_io_device(struct io_device *iod)
case IODEV_NET:
skb_queue_head_init(&iod->sk_rx_q);
- INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
-
if (iod->use_handover)
iod->ndev = alloc_netdev(0, iod->name,
vnet_setup_ether);
@@ -1375,7 +1573,6 @@ int sipc5_init_io_device(struct io_device *iod)
case IODEV_DUMMY:
skb_queue_head_init(&iod->sk_rx_q);
- /* in sipc5, does not need rx_iodev_work on DUMMY */
iod->miscdev.minor = MISC_DYNAMIC_MINOR;
iod->miscdev.name = iod->name;
@@ -1389,7 +1586,16 @@ int sipc5_init_io_device(struct io_device *iod)
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)
+ mif_err("failed to create `txlink file' : %s\n",
+ iod->name);
break;
default: