diff options
Diffstat (limited to 'drivers/misc/modem_if/sipc5_io_device.c')
-rw-r--r-- | drivers/misc/modem_if/sipc5_io_device.c | 878 |
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: |