/* Copyright (c) 2013 - 2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* * WWAN Network Interface. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WWAN_DEV_NAME "rmnet%d" #define WWAN_METADATA_SHFT 16 #define WWAN_METADATA_MASK 0x00FF0000 #define IPA_RM_INACTIVITY_TIMER 1000 #define WWAN_DEVICE_COUNT (8) #define WWAN_DATA_LEN 2000 #define HEADROOM_FOR_A2_MUX 8 /* for mux header */ #define TAILROOM 8 /* for padding by mux layer */ enum wwan_device_status { WWAN_DEVICE_INACTIVE = 0, WWAN_DEVICE_ACTIVE = 1 }; static enum ipa_rm_resource_name ipa_rm_resource_by_ch_id[WWAN_DEVICE_COUNT] = { IPA_RM_RESOURCE_WWAN_0_PROD, IPA_RM_RESOURCE_WWAN_1_PROD, IPA_RM_RESOURCE_WWAN_2_PROD, IPA_RM_RESOURCE_WWAN_3_PROD, IPA_RM_RESOURCE_WWAN_4_PROD, IPA_RM_RESOURCE_WWAN_5_PROD, IPA_RM_RESOURCE_WWAN_6_PROD, IPA_RM_RESOURCE_WWAN_7_PROD }; static enum a2_mux_logical_channel_id a2_mux_lcid_by_ch_id[WWAN_DEVICE_COUNT] = { A2_MUX_WWAN_0, A2_MUX_WWAN_1, A2_MUX_WWAN_2, A2_MUX_WWAN_3, A2_MUX_WWAN_4, A2_MUX_WWAN_5, A2_MUX_WWAN_6, A2_MUX_WWAN_7 }; /** * struct wwan_private - WWAN private data * @stats: iface statistics * @ch_id: channel id * @lock: spinlock for mutual exclusion * @device_status: holds device status * * WWAN private - holds all relevant info about WWAN driver */ struct wwan_private { struct net_device_stats stats; uint32_t ch_id; spinlock_t lock; struct completion resource_granted_completion; enum wwan_device_status device_status; }; static struct net_device *netdevs[WWAN_DEVICE_COUNT]; static __be16 wwan_ip_type_trans(struct sk_buff *skb) { __be16 protocol = 0; /* Determine L3 protocol */ switch (skb->data[0] & 0xf0) { case 0x40: protocol = htons(ETH_P_IP); break; case 0x60: protocol = htons(ETH_P_IPV6); break; default: pr_err("[%s] %s() L3 protocol decode error: 0x%02x", skb->dev->name, __func__, skb->data[0] & 0xf0); /* skb will be dropped in upper layer for unknown protocol */ break; } return protocol; } /** * a2_mux_recv_notify() - Deliver an RX packet to network stack * * @skb: skb to be delivered * @dev: network device * * Return codes: * None */ static void a2_mux_recv_notify(void *dev, struct sk_buff *skb) { struct wwan_private *wwan_ptr = netdev_priv(dev); skb->dev = dev; skb->protocol = wwan_ip_type_trans(skb); wwan_ptr->stats.rx_packets++; wwan_ptr->stats.rx_bytes += skb->len; pr_debug("[%s] Rx packet #%lu len=%d\n", skb->dev->name, wwan_ptr->stats.rx_packets, skb->len); netif_rx(skb); } /** * wwan_send_packet() - Deliver a TX packet to A2 MUX driver. * * @skb: skb to be delivered * @dev: network device * * Return codes: * 0: success * -EAGAIN: A2 MUX is not ready to send the skb. try later * -EFAULT: A2 MUX rejected the skb * -EPREM: Unknown error */ static int wwan_send_packet(struct sk_buff *skb, struct net_device *dev) { struct wwan_private *wwan_ptr = netdev_priv(dev); int ret; ret = a2_mux_write(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id], skb); if (ret != 0 && ret != -EAGAIN && ret != -EFAULT) { pr_err("[%s] %s: write returned error %d", dev->name, __func__, ret); return -EPERM; } return ret; } /** * a2_mux_write_done() - Update device statistics and start * network stack queue is was stop and A2 MUX queue is below low * watermark. * * @dev: network device * @skb: skb to be delivered * * Return codes: * None */ static void a2_mux_write_done(void *dev, struct sk_buff *skb) { struct wwan_private *wwan_ptr = netdev_priv(dev); unsigned long flags; pr_debug("%s: write complete\n", __func__); wwan_ptr->stats.tx_packets++; wwan_ptr->stats.tx_bytes += skb->len; pr_debug("[%s] Tx packet #%lu len=%d mark=0x%x\n", ((struct net_device *)(dev))->name, wwan_ptr->stats.tx_packets, skb->len, skb->mark); dev_kfree_skb_any(skb); spin_lock_irqsave(&wwan_ptr->lock, flags); if (netif_queue_stopped(dev) && a2_mux_is_ch_low(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id])) { pr_debug("%s: Low WM hit, waking queue=%p\n", __func__, skb); netif_wake_queue(dev); } if (a2_mux_is_ch_empty(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id])) { if (ipa_emb_ul_pipes_empty()) ipa_rm_inactivity_timer_release_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); else pr_err_ratelimited("%s: ch=%d empty but UL desc FIFOs not empty\n", __func__, wwan_ptr->ch_id); } spin_unlock_irqrestore(&wwan_ptr->lock, flags); } /** * a2_mux_notify() - Callback function for A2 MUX events Handles * A2_MUX_RECEIVE and A2_MUX_WRITE_DONE events. * * @dev: network device * @event: A2 MUX event * @data: Additional data provided by A2 MUX * * Return codes: * None */ static void a2_mux_notify(void *dev, enum a2_mux_event_type event, unsigned long data) { struct sk_buff *skb = (struct sk_buff *)data; switch (event) { case A2_MUX_RECEIVE: if (!skb) { pr_err("[%s] %s: No skb received", ((struct net_device *)dev)->name, __func__); return; } a2_mux_recv_notify(dev, skb); break; case A2_MUX_WRITE_DONE: a2_mux_write_done(dev, skb); break; default: pr_err("%s: unknown event %d\n", __func__, event); break; } } /** * ipa_rm_resource_granted() - Called upon * IPA_RM_RESOURCE_GRANTED event. Wakes up queue is was stopped. * * @work: work object supplied ny workqueue * * Return codes: * None */ static void ipa_rm_resource_granted(void *dev) { netif_wake_queue(dev); } /** * ipa_rm_notify() - Callback function for RM events. Handles * IPA_RM_RESOURCE_GRANTED and IPA_RM_RESOURCE_RELEASED events. * IPA_RM_RESOURCE_GRANTED is handled in the context of shared * workqueue. * * @dev: network device * @event: IPA RM event * @data: Additional data provided by IPA RM * * Return codes: * None */ static void ipa_rm_notify(void *dev, enum ipa_rm_event event, unsigned long data) { struct wwan_private *wwan_ptr = netdev_priv(dev); pr_debug("%s: event %d\n", __func__, event); switch (event) { case IPA_RM_RESOURCE_GRANTED: if (wwan_ptr->device_status == WWAN_DEVICE_INACTIVE) { complete_all(&wwan_ptr->resource_granted_completion); break; } ipa_rm_resource_granted(dev); break; case IPA_RM_RESOURCE_RELEASED: break; default: pr_err("%s: unknown event %d\n", __func__, event); break; } } static int wwan_register_to_ipa(struct net_device *dev) { struct wwan_private *wwan_ptr = netdev_priv(dev); struct ipa_tx_intf tx_properties = {0}; struct ipa_ioc_tx_intf_prop tx_ioc_properties[2] = { {0}, {0} }; struct ipa_ioc_tx_intf_prop *tx_ipv4_property; struct ipa_ioc_tx_intf_prop *tx_ipv6_property; struct ipa_rx_intf rx_properties = {0}; struct ipa_ioc_rx_intf_prop rx_ioc_properties[2] = { {0}, {0} }; struct ipa_ioc_rx_intf_prop *rx_ipv4_property; struct ipa_ioc_rx_intf_prop *rx_ipv6_property; int ret = 0; pr_debug("[%s] %s:\n", dev->name, __func__); tx_properties.prop = tx_ioc_properties; tx_ipv4_property = &tx_properties.prop[0]; tx_ipv4_property->ip = IPA_IP_v4; tx_ipv4_property->dst_pipe = IPA_CLIENT_A2_EMBEDDED_CONS; snprintf(tx_ipv4_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d", A2_MUX_HDR_NAME_V4_PREF, a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]); tx_ipv6_property = &tx_properties.prop[1]; tx_ipv6_property->ip = IPA_IP_v6; tx_ipv6_property->dst_pipe = IPA_CLIENT_A2_EMBEDDED_CONS; snprintf(tx_ipv6_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d", A2_MUX_HDR_NAME_V6_PREF, a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]); tx_properties.num_props = 2; rx_properties.prop = rx_ioc_properties; rx_ipv4_property = &rx_properties.prop[0]; rx_ipv4_property->ip = IPA_IP_v4; rx_ipv4_property->attrib.attrib_mask |= IPA_FLT_META_DATA; rx_ipv4_property->attrib.meta_data = wwan_ptr->ch_id << WWAN_METADATA_SHFT; rx_ipv4_property->attrib.meta_data_mask = WWAN_METADATA_MASK; rx_ipv4_property->src_pipe = IPA_CLIENT_A2_EMBEDDED_PROD; rx_ipv6_property = &rx_properties.prop[1]; rx_ipv6_property->ip = IPA_IP_v6; rx_ipv6_property->attrib.attrib_mask |= IPA_FLT_META_DATA; rx_ipv6_property->attrib.meta_data = wwan_ptr->ch_id << WWAN_METADATA_SHFT; rx_ipv6_property->attrib.meta_data_mask = WWAN_METADATA_MASK; rx_ipv6_property->src_pipe = IPA_CLIENT_A2_EMBEDDED_PROD; rx_properties.num_props = 2; ret = ipa_register_intf(dev->name, &tx_properties, &rx_properties); if (ret) { pr_err("[%s] %s: ipa_register_intf failed %d\n", dev->name, __func__, ret); return ret; } return 0; } static int __wwan_open(struct net_device *dev) { int r; struct wwan_private *wwan_ptr = netdev_priv(dev); pr_debug("[%s] __wwan_open()\n", dev->name); if (wwan_ptr->device_status != WWAN_DEVICE_ACTIVE) { INIT_COMPLETION(wwan_ptr->resource_granted_completion); r = ipa_rm_inactivity_timer_request_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); if (r < 0 && r != -EINPROGRESS) { pr_err("%s: ipa rm timer request resource failed %d\n", __func__, r); return -ENODEV; } if (r == -EINPROGRESS) { wait_for_completion( &wwan_ptr->resource_granted_completion); } r = a2_mux_open_channel(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id], dev, a2_mux_notify); if (r < 0) { pr_err("%s: ch=%d failed with rc %d\n", __func__, wwan_ptr->ch_id, r); ipa_rm_inactivity_timer_release_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); return -ENODEV; } ipa_rm_inactivity_timer_release_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); r = wwan_register_to_ipa(dev); if (r < 0) { pr_err("%s: ch=%d failed to register to IPA rc %d\n", __func__, wwan_ptr->ch_id, r); return -ENODEV; } } wwan_ptr->device_status = WWAN_DEVICE_ACTIVE; return 0; } /** * wwan_open() - Opens the wwan network interface. Opens logical * channel on A2 MUX driver and starts the network stack queue * * @dev: network device * * Return codes: * 0: success * -ENODEV: Error while opening logical channel on A2 MUX driver */ static int wwan_open(struct net_device *dev) { int rc = 0; pr_debug("[%s] wwan_open()\n", dev->name); rc = __wwan_open(dev); if (rc == 0) netif_start_queue(dev); return rc; } static int __wwan_close(struct net_device *dev) { struct wwan_private *wwan_ptr = netdev_priv(dev); int rc = 0; if (wwan_ptr->device_status == WWAN_DEVICE_ACTIVE) { wwan_ptr->device_status = WWAN_DEVICE_INACTIVE; /* do not close wwan port once up, this causes remote side to hang if tried to open again */ INIT_COMPLETION(wwan_ptr->resource_granted_completion); rc = ipa_rm_inactivity_timer_request_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); if (rc < 0 && rc != -EINPROGRESS) { pr_err("%s: ipa rm timer request resource failed %d\n", __func__, rc); return -ENODEV; } if (rc == -EINPROGRESS) { wait_for_completion( &wwan_ptr->resource_granted_completion); } rc = a2_mux_close_channel( a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]); if (rc) { pr_err("[%s] %s: a2_mux_close_channel failed %d\n", dev->name, __func__, rc); ipa_rm_inactivity_timer_release_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); return rc; } ipa_rm_inactivity_timer_release_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); rc = ipa_deregister_intf(dev->name); if (rc) { pr_err("[%s] %s: ipa_deregister_intf failed %d\n", dev->name, __func__, rc); return rc; } return rc; } else return -EBADF; } /** * wwan_stop() - Stops the wwan network interface. Closes * logical channel on A2 MUX driver and stops the network stack * queue * * @dev: network device * * Return codes: * 0: success * -ENODEV: Error while opening logical channel on A2 MUX driver */ static int wwan_stop(struct net_device *dev) { pr_debug("[%s] wwan_stop()\n", dev->name); __wwan_close(dev); netif_stop_queue(dev); return 0; } static int wwan_change_mtu(struct net_device *dev, int new_mtu) { if (0 > new_mtu || WWAN_DATA_LEN < new_mtu) return -EINVAL; pr_debug("[%s] MTU change: old=%d new=%d\n", dev->name, dev->mtu, new_mtu); dev->mtu = new_mtu; return 0; } /** * wwan_xmit() - Transmits an skb. In charge of asking IPA * RM needed resources. In case that IPA RM is not ready, then * the skb is saved for tranmitting as soon as IPA RM resources * are granted. * * @skb: skb to be transmitted * @dev: network device * * Return codes: * 0: success * NETDEV_TX_BUSY: Error while transmitting the skb. Try again * later * -EFAULT: Error while transmitting the skb */ static int wwan_xmit(struct sk_buff *skb, struct net_device *dev) { struct wwan_private *wwan_ptr = netdev_priv(dev); unsigned long flags; int ret = 0; if (netif_queue_stopped(dev)) { pr_err("[%s]fatal: wwan_xmit called when netif_queue stopped\n", dev->name); return 0; } dev->trans_start = jiffies; ret = ipa_rm_inactivity_timer_request_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); if (ret == -EINPROGRESS) { netif_stop_queue(dev); return NETDEV_TX_BUSY; } if (ret) { pr_err("[%s] fatal: ipa rm timer request resource failed %d\n", dev->name, ret); return -EFAULT; } ret = wwan_send_packet(skb, dev); if (ret == -EPERM) { ret = NETDEV_TX_BUSY; goto exit; } /* * detected SSR a bit early. shut some things down now, and leave * the rest to the main ssr handling code when that happens later */ if (ret == -EFAULT) { netif_carrier_off(dev); dev_kfree_skb_any(skb); ret = 0; goto exit; } if (ret == -EAGAIN) { /* * This should not happen * EAGAIN means we attempted to overflow the high watermark * Clearly the queue is not stopped like it should be, so * stop it and return BUSY to the TCP/IP framework. It will * retry this packet with the queue is restarted which happens * in the write_done callback when the low watermark is hit. */ netif_stop_queue(dev); ret = NETDEV_TX_BUSY; goto exit; } spin_lock_irqsave(&wwan_ptr->lock, flags); if (a2_mux_is_ch_full(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id])) { netif_stop_queue(dev); pr_debug("%s: High WM hit, stopping queue=%p\n", __func__, skb); } spin_unlock_irqrestore(&wwan_ptr->lock, flags); return ret; exit: ipa_rm_inactivity_timer_release_resource( ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]); return ret; } static struct net_device_stats *wwan_get_stats(struct net_device *dev) { struct wwan_private *wwan_ptr = netdev_priv(dev); return &wwan_ptr->stats; } static void wwan_tx_timeout(struct net_device *dev) { pr_warning("[%s] wwan_tx_timeout(), data stall in UL\n", dev->name); ipa_bam_reg_dump(); } /** * wwan_ioctl() - I/O control for wwan network driver. * * @dev: network device * @ifr: ignored * @cmd: cmd to be excecuded. can be one of the following: * WWAN_IOCTL_OPEN - Open the network interface * WWAN_IOCTL_CLOSE - Close the network interface * * Return codes: * 0: success * NETDEV_TX_BUSY: Error while transmitting the skb. Try again * later * -EFAULT: Error while transmitting the skb */ static int wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int rc = 0; switch (cmd) { case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */ break; case RMNET_IOCTL_GET_LLP: /* Get link protocol state */ ifr->ifr_ifru.ifru_data = (void *) RMNET_MODE_LLP_IP; break; case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */ break; case RMNET_IOCTL_FLOW_ENABLE: tc_qdisc_flow_control(dev, (u32)ifr->ifr_data, 1); pr_debug("[%s] %s: enabled flow", dev->name, __func__); break; case RMNET_IOCTL_FLOW_DISABLE: tc_qdisc_flow_control(dev, (u32)ifr->ifr_data, 0); pr_debug("[%s] %s: disabled flow", dev->name, __func__); break; case RMNET_IOCTL_GET_QOS: /* Get QoS header state */ /* QoS disabled */ ifr->ifr_ifru.ifru_data = (void *) 0; break; case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */ ifr->ifr_ifru.ifru_data = (void *) RMNET_MODE_LLP_IP; break; case RMNET_IOCTL_OPEN: /* Open transport port */ rc = __wwan_open(dev); pr_debug("[%s] wwan_ioctl(): open transport port\n", dev->name); break; case RMNET_IOCTL_CLOSE: /* Close transport port */ rc = __wwan_close(dev); pr_debug("[%s] wwan_ioctl(): close transport port\n", dev->name); break; default: pr_err("[%s] error: wwan_ioct called for unsupported cmd[%d]", dev->name, cmd); return -EINVAL; } return rc; } static const struct net_device_ops wwan_ops_ip = { .ndo_open = wwan_open, .ndo_stop = wwan_stop, .ndo_start_xmit = wwan_xmit, .ndo_get_stats = wwan_get_stats, .ndo_tx_timeout = wwan_tx_timeout, .ndo_do_ioctl = wwan_ioctl, .ndo_change_mtu = wwan_change_mtu, .ndo_set_mac_address = 0, .ndo_validate_addr = 0, }; /** * wwan_setup() - Setups the wwan network driver. * * @dev: network device * * Return codes: * None */ static void wwan_setup(struct net_device *dev) { dev->netdev_ops = &wwan_ops_ip; ether_setup(dev); /* set this after calling ether_setup */ dev->header_ops = 0; /* No header */ dev->type = ARPHRD_RAWIP; dev->hard_header_len = 0; dev->mtu = WWAN_DATA_LEN; dev->addr_len = 0; dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); dev->needed_headroom = HEADROOM_FOR_A2_MUX; dev->needed_tailroom = TAILROOM; dev->watchdog_timeo = 30 * HZ; } /** * wwan_init() - Initialized the module and registers as a * network interface to the network stack * * Return codes: * 0: success * -ENOMEM: No memory available * -EFAULT: Internal error */ static int __init wwan_init(void) { int ret; struct net_device *dev; struct wwan_private *wwan_ptr; unsigned n; struct ipa_rm_create_params ipa_rm_params; pr_info("%s: WWAN devices[%d]\n", __func__, WWAN_DEVICE_COUNT); for (n = 0; n < WWAN_DEVICE_COUNT; n++) { dev = alloc_netdev(sizeof(struct wwan_private), WWAN_DEV_NAME, wwan_setup); if (!dev) { pr_err("%s: no memory for netdev %d\n", __func__, n); ret = -ENOMEM; goto fail; } netdevs[n] = dev; wwan_ptr = netdev_priv(dev); wwan_ptr->ch_id = n; spin_lock_init(&wwan_ptr->lock); init_completion(&wwan_ptr->resource_granted_completion); memset(&ipa_rm_params, 0, sizeof(struct ipa_rm_create_params)); ipa_rm_params.name = ipa_rm_resource_by_ch_id[n]; ipa_rm_params.reg_params.user_data = dev; ipa_rm_params.reg_params.notify_cb = ipa_rm_notify; ret = ipa_rm_create_resource(&ipa_rm_params); if (ret) { pr_err("%s: unable to create resourse %d in IPA RM\n", __func__, ipa_rm_resource_by_ch_id[n]); goto fail; } ret = ipa_rm_inactivity_timer_init(ipa_rm_resource_by_ch_id[n], IPA_RM_INACTIVITY_TIMER); if (ret) { pr_err("%s: ipa rm timer init failed %d on ins %d\n", __func__, ret, n); goto fail; } ret = ipa_rm_add_dependency(ipa_rm_resource_by_ch_id[n], IPA_RM_RESOURCE_A2_CONS); if (ret) { pr_err("%s: unable to add dependency %d rc=%d\n", __func__, n, ret); goto fail; } ret = register_netdev(dev); if (ret) { pr_err("%s: unable to register netdev %d rc=%d\n", __func__, n, ret); goto fail; } } return 0; fail: for (n = 0; n < WWAN_DEVICE_COUNT; n++) { if (!netdevs[n]) break; unregister_netdev(netdevs[n]); ipa_rm_inactivity_timer_destroy(ipa_rm_resource_by_ch_id[n]); free_netdev(netdevs[n]); netdevs[n] = NULL; } return ret; } late_initcall(wwan_init); void wwan_cleanup(void) { unsigned n; pr_info("%s: WWAN devices[%d]\n", __func__, WWAN_DEVICE_COUNT); for (n = 0; n < WWAN_DEVICE_COUNT; n++) { unregister_netdev(netdevs[n]); ipa_rm_inactivity_timer_destroy(ipa_rm_resource_by_ch_id[n]); free_netdev(netdevs[n]); netdevs[n] = NULL; } } MODULE_DESCRIPTION("WWAN Network Interface"); MODULE_LICENSE("GPL v2");