diff options
Diffstat (limited to 'drivers/misc/modem_if/sipc4_io_device.c')
-rw-r--r-- | drivers/misc/modem_if/sipc4_io_device.c | 252 |
1 files changed, 190 insertions, 62 deletions
diff --git a/drivers/misc/modem_if/sipc4_io_device.c b/drivers/misc/modem_if/sipc4_io_device.c index 94cd85b..28f95f7 100644 --- a/drivers/misc/modem_if/sipc4_io_device.c +++ b/drivers/misc/modem_if/sipc4_io_device.c @@ -85,6 +85,35 @@ 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 ssize_t show_loopback(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; + unsigned char *ip = (unsigned char *)&msd->loopback_ipaddr; + char *p = buf; + + 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 int get_header_size(struct io_device *iod) { switch (iod->format) { @@ -300,7 +329,7 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld, * make skb for header info first */ if (iod->format == IPC_RFS && !hdr->frag_len) { - skb = rx_alloc_skb(head_size, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(head_size, iod, ld); if (unlikely(!skb)) return -ENOMEM; memcpy(skb_put(skb, head_size), hdr->hdr, head_size); @@ -311,17 +340,11 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld, * MAX_RXDATA_SIZE, this packet will split to * multiple packets */ - if (iod->use_handover) - alloc_size += sizeof(struct ethhdr); - - skb = rx_alloc_skb(alloc_size, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(alloc_size, iod, ld); if (unlikely(!skb)) { fragdata(iod, ld)->realloc_offset = continue_len; return -ENOMEM; } - - if (iod->use_handover) - skb_reserve(skb, sizeof(struct ethhdr)); fragdata(iod, ld)->skb_recv = skb; } @@ -357,7 +380,7 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld, alloc_size = min(rest_len, MAX_RXDATA_SIZE); - skb = rx_alloc_skb(alloc_size, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(alloc_size, iod, ld); if (unlikely(!skb)) { fragdata(iod, ld)->realloc_offset = done_len; return -ENOMEM; @@ -399,8 +422,7 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) return 0; } else { struct fmt_hdr *fh = NULL; - skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, GFP_ATOMIC, - iod, ld); + skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, iod, ld); if (!skb) { mif_err("<%d> alloc_skb fail\n", __LINE__); @@ -482,8 +504,7 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) return 0; } else { struct fmt_hdr *fh = NULL; - skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, GFP_ATOMIC, - real_iod, ld); + skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, real_iod, ld); if (!skb) { mif_err("alloc_skb fail\n"); return -ENOMEM; @@ -631,6 +652,9 @@ static int rx_multipdp(struct sk_buff *skb) struct io_device *real_iod = NULL; ch = raw_header->channel; + if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr) + ch = RMNET0_CH_ID; + real_iod = link_get_iod_with_channel(ld, 0x20 | ch); if (!real_iod) { mif_err("wrong channel %d\n", ch); @@ -828,7 +852,7 @@ static int rx_rfs_packet(struct io_device *iod, struct link_device *ld, } } - skb = rx_alloc_skb(size, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(size, iod, ld); if (unlikely(!skb)) { mif_err("alloc_skb fail\n"); return -ENOMEM; @@ -850,6 +874,9 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, { struct sk_buff *skb; int err; + unsigned int alloc_size, rest_len; + char *cur; + /* check the iod(except IODEV_DUMMY) is open? * if the iod is MULTIPDP, check this data on rx_iodev_skb_raw() @@ -887,18 +914,39 @@ 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); - if (!skb) { - mif_err("fail alloc skb (%d)\n", __LINE__); - return -ENOMEM; - } + skb = rx_alloc_skb(len, iod, ld); + if (skb) { + mif_debug("boot len : %d\n", len); - mif_debug("boot len : %d\n", len); + memcpy(skb_put(skb, len), data, len); + skb_queue_tail(&iod->sk_rx_q, skb); + mif_debug("skb len : %d\n", skb->len); - memcpy(skb_put(skb, len), data, len); - skb_queue_tail(&iod->sk_rx_q, skb); - mif_debug("skb len : %d\n", skb->len); + wake_up(&iod->wq); + return len; + } + /* 32KB page alloc fail case, alloc 3.5K a page.. */ + mif_info("(%d)page fail, alloc fragment pages\n", len); + + rest_len = len; + cur = (char *)data; + while (rest_len) { + alloc_size = min_t(unsigned int, MAX_RXDATA_SIZE, + rest_len); + skb = rx_alloc_skb(alloc_size, iod, ld); + if (!skb) { + mif_err("fail alloc skb (%d)\n", __LINE__); + return -ENOMEM; + } + mif_debug("boot len : %d\n", alloc_size); + memcpy(skb_put(skb, alloc_size), cur, alloc_size); + skb_queue_tail(&iod->sk_rx_q, skb); + mif_debug("skb len : %d\n", skb->len); + + rest_len -= alloc_size; + cur += alloc_size; + } wake_up(&iod->wq); return len; @@ -950,7 +998,7 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) 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; @@ -958,7 +1006,7 @@ static int misc_open(struct inode *inode, struct file *filp) mif_err("iod = %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) { @@ -975,14 +1023,14 @@ static int misc_open(struct inode *inode, struct file *filp) 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_err("iod = %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); } @@ -1002,6 +1050,9 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) } else if ((iod->mc->phone_state == STATE_CRASH_RESET) || (iod->mc->phone_state == STATE_CRASH_EXIT) || (iod->mc->phone_state == STATE_NV_REBUILDING) || +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) + (iod->mc->phone_state == STATE_MODEM_SWITCH) || +#endif (iod->mc->sim_state.changed)) { if (iod->format == IPC_RAW) { msleep(20); @@ -1019,6 +1070,9 @@ 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; + char str[TASK_COMM_LEN]; mif_debug("cmd = 0x%x\n", cmd); @@ -1044,8 +1098,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return iod->mc->ops.modem_boot_off(iod->mc); /* TODO - will remove this command after ril updated */ - case IOCTL_MODEM_START: - mif_debug("misc_ioctl : IOCTL_MODEM_START\n"); + case IOCTL_MODEM_BOOT_DONE: + mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_DONE\n"); return 0; case IOCTL_MODEM_STATUS: @@ -1056,10 +1110,13 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) (p_state == STATE_CRASH_EXIT)) { mif_err("<%s> send err state : %d\n", iod->name, p_state); - } else if (iod->mc->sim_state.changed) { + } else if (iod->mc->sim_state.changed && + !strcmp(get_task_comm(str, get_current()), "rild")) { int s_state = iod->mc->sim_state.online ? STATE_SIM_ATTACH : STATE_SIM_DETACH; iod->mc->sim_state.changed = false; + + mif_info("SIM states (%d) to %s\n", s_state, str); return s_state; } else if (p_state == STATE_NV_REBUILDING) { mif_info("send nv rebuild state : %d\n", @@ -1074,7 +1131,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: @@ -1083,7 +1140,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: @@ -1113,6 +1170,38 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mif_err("misc_ioctl : IOCTL_MODEM_DUMP_RESET\n"); return iod->mc->ops.modem_dump_reset(iod->mc); +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) + case IOCTL_MODEM_SWITCH_MODEM: + mif_err("misc_ioctl : IOCTL_MODEM_SWITCH_MODEM\n"); + iod->mc->phone_state = STATE_MODEM_SWITCH; + wake_up(&iod->wq); + return 0; +#endif + + case IOCTL_MIF_LOG_DUMP: + 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 @@ -1246,6 +1335,8 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, struct io_device *iod = (struct io_device *)filp->private_data; struct sk_buff *skb = NULL; int pktsize = 0; + unsigned int rest_len, copy_len; + char *cur = buf; skb = skb_dequeue(&iod->sk_rx_q); if (!skb) { @@ -1254,43 +1345,67 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, } mif_debug("<%s> skb->len : %d\n", iod->name, skb->len); - if (skb->len > count) { - /* BOOT device receviced rx data as serial stream, return data - by User requested size */ - if (iod->format == IPC_BOOT) { - mif_err("skb->len %d > count %d\n", skb->len, - count); - pr_skb("BOOT-wRX", skb); - if (copy_to_user(buf, skb->data, count) != 0) { + if (iod->format == IPC_BOOT) { + pktsize = rest_len = count; + while (rest_len) { + if (skb->len > rest_len) { + /* BOOT device receviced rx data as serial + stream, return data by User requested size */ + mif_err("skb->len %d > count %d\n", skb->len, + rest_len); + pr_skb("BOOT-wRX", skb); + if (copy_to_user(cur, skb->data, rest_len) + != 0) { + dev_kfree_skb_any(skb); + return -EFAULT; + } + cur += rest_len; + skb_pull(skb, rest_len); + if (skb->len) { + mif_info("queue-head, skb->len = %d\n", + skb->len); + skb_queue_head(&iod->sk_rx_q, skb); + } + mif_debug("return %u\n", rest_len); + return rest_len; + } + + copy_len = min(rest_len, skb->len); + if (copy_to_user(cur, skb->data, copy_len) != 0) { dev_kfree_skb_any(skb); return -EFAULT; } - skb_pull(skb, count); - if (skb->len) { - mif_info("queue-head, skb->len = %d\n", - skb->len); - skb_queue_head(&iod->sk_rx_q, skb); - } + cur += skb->len; + dev_kfree_skb_any(skb); + rest_len -= copy_len; - return count; - } else { - mif_err("<%s> skb->len %d > count %d\n", - iod->name, skb->len, count); + if (!rest_len) + break; + + skb = skb_dequeue(&iod->sk_rx_q); + if (!skb) { + mif_err("<%s> %d / %d sk_rx_q\n", iod->name, + (count - rest_len), count); + return count - rest_len; + } + } + } else { + if (skb->len > count) { + mif_err("<%s> skb->len %d > count %d\n", iod->name, + skb->len, count); dev_kfree_skb_any(skb); return -EFAULT; } - } + pktsize = skb->len; + if (copy_to_user(buf, skb->data, pktsize) != 0) { + dev_kfree_skb_any(skb); + return -EFAULT; + } + if (iod->format == IPC_FMT) + mif_debug("copied %d bytes to user\n", pktsize); - pktsize = skb->len; - if (copy_to_user(buf, skb->data, pktsize) != 0) { dev_kfree_skb_any(skb); - return -EFAULT; } - if (iod->format == IPC_FMT) - mif_debug("copied %d bytes to user\n", pktsize); - - dev_kfree_skb_any(skb); - return pktsize; } @@ -1370,6 +1485,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) struct io_device *iod = vnet->iod; struct link_device *ld = get_current_link(iod); struct raw_hdr hd; + struct iphdr *ip_header = NULL; /* When use `handover' with Network Bridge, * user -> TCP/IP(kernel) -> bridge device -> TCP/IP(kernel) -> this. @@ -1383,9 +1499,17 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_pull(skb, sizeof(struct ethhdr)); } + /* 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); + hd.channel = DATA_LOOPBACK_CHANNEL; + } else { + hd.channel = iod->id & 0x1F; + } hd.len = skb->len + sizeof(hd); hd.control = 0; - hd.channel = iod->id & 0x1F; headroom = sizeof(hd) + sizeof(hdlc_start); tailroom = sizeof(hdlc_end); @@ -1523,9 +1647,13 @@ int sipc4_init_io_device(struct io_device *iod) ret = device_create_file(iod->miscdev.this_device, &attr_waketime); if (ret) - mif_err("failed to create sysfs file : %s\n", + mif_err("failed to create `waketime' file : %s\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); - break; default: |