/* * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The OpenAirInterface Software Alliance licenses this file to You under * the OAI Public License, Version 1.1 (the "License"); you may not use this file * except in compliance with the License. * You may obtain a copy of the License at * * http://www.openairinterface.org/?page_id=698 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *------------------------------------------------------------------------------- * For more information about the OpenAirInterface (OAI) Software Alliance: * contact@openairinterface.org */ /*! \file device.c * \brief Networking Device Driver for OpenAirInterface * \author navid nikaein, Lionel Gauthier, raymond knopp * \company Eurecom * \email: raymond.knopp@eurecom.fr, navid.nikaein@eurecom.fr, lionel.gauthier@eurecom.fr */ /*******************************************************************************/ #include "constant.h" #include "local.h" #include "proto_extern.h" #include <linux/kernel.h> #include <linux/version.h> #include <linux/init.h> #include <linux/spinlock.h> #include <linux/moduleparam.h> #include <asm/io.h> #include <asm/bitops.h> #include <asm/uaccess.h> #include <asm/segment.h> #include <asm/page.h> #include <asm/delay.h> #include <asm/unistd.h> #include <linux/netdevice.h> struct net_device *ue_ip_dev[UE_IP_NB_INSTANCES_MAX]; #ifdef OAI_NW_DRIVER_USE_NETLINK extern void ue_ip_netlink_release(void); extern int ue_ip_netlink_init(void); #endif //--------------------------------------------------------------------------- int ue_ip_find_inst(struct net_device *dev_pP) { //--------------------------------------------------------------------------- int i; for (i=0; i<UE_IP_NB_INSTANCES_MAX; i++) if (ue_ip_dev[i] == dev_pP) { return(i); } return(-1); } //--------------------------------------------------------------------------- #ifndef OAI_NW_DRIVER_USE_NETLINK void *ue_ip_interrupt(void) { //--------------------------------------------------------------------------- uint8_t cxi; // ue_ip_priv_t *priv_p=netdev_priv(dev_id); // unsigned int flags; // priv_p->lock = SPIN_LOCK_UNLOCKED; #ifdef OAI_DRV_DEBUG_INTERRUPT printk("INTERRUPT - begin\n"); #endif // spin_lock_irqsave(&priv_p->lock,flags); cxi=0; // mesh_GC_receive(); // mesh_DC_receive(naspriv->cx+cxi); #ifndef OAI_NW_DRIVER_USE_NETLINK ue_ip_common_wireless2ip(); #endif // spin_unlock_irqrestore(&priv_p->lock,flags); #ifdef OAI_DRV_DEBUG_INTERRUPT printk("INTERRUPT: end\n"); #endif // return 0; } #endif //NETLINK //--------------------------------------------------------------------------- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)) void ue_ip_timer(struct timer_list *t) #else void ue_ip_timer(unsigned long dataP) #endif { //--------------------------------------------------------------------------- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)) ue_ip_priv_t *priv_p = from_timer(priv_p, t, timer); #else ue_ip_priv_t *priv_p = (ue_ip_priv_t *)dataP; #endif spin_lock(&priv_p->lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)) mod_timer(&priv_p->timer, jiffies + UE_IP_TIMER_TICK); #else (priv_p->timer).function = ue_ip_timer; (priv_p->timer).expires = jiffies + UE_IP_TIMER_TICK; (priv_p->timer).data = dataP; add_timer(&priv_p->timer); #endif spin_unlock(&priv_p->lock); return; // add_timer(&gpriv->timer); // spin_unlock(&gpriv->lock); } //--------------------------------------------------------------------------- // Called by ifconfig when the device is activated by ifconfig int ue_ip_open(struct net_device *dev_pP) { //--------------------------------------------------------------------------- ue_ip_priv_t *priv_p=netdev_priv(dev_pP); // Address has already been set at init #ifndef OAI_NW_DRIVER_USE_NETLINK if (pdcp_2_ue_ip_irq==-EBUSY) { printk("[UE_IP_DRV][%s] : irq failure\n", __FUNCTION__); return -EBUSY; } #endif //OAI_NW_DRIVER_USE_NETLINK if(!netif_queue_stopped(dev_pP)) { netif_start_queue(dev_pP); } else { netif_wake_queue(dev_pP); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)) timer_setup(&(priv_p->timer), ue_ip_timer, 0); (priv_p->timer).expires = jiffies+UE_IP_TIMER_TICK; #else init_timer(&priv_p->timer); (priv_p->timer).expires = jiffies+UE_IP_TIMER_TICK; (priv_p->timer).data = (unsigned long)priv_p; (priv_p->timer).function = ue_ip_timer; #endif //add_timer(&priv_p->timer); printk("[UE_IP_DRV][%s] name = %s\n", __FUNCTION__, dev_pP->name); return 0; } //--------------------------------------------------------------------------- // Called by ifconfig when the device is desactivated int ue_ip_stop(struct net_device *dev_pP) { //--------------------------------------------------------------------------- ue_ip_priv_t *priv_p = netdev_priv(dev_pP); printk("[UE_IP_DRV][%s] Begin\n", __FUNCTION__); del_timer(&(priv_p->timer)); netif_stop_queue(dev_pP); // MOD_DEC_USE_COUNT; printk("[UE_IP_DRV][%s] End\n", __FUNCTION__); return 0; } //--------------------------------------------------------------------------- void ue_ip_teardown(struct net_device *dev_pP) { //--------------------------------------------------------------------------- ue_ip_priv_t *priv_p; int inst; printk("[UE_IP_DRV][%s] Begin\n", __FUNCTION__); if (dev_pP) { priv_p = netdev_priv(dev_pP); inst = ue_ip_find_inst(dev_pP); if ((inst<0) || (inst>=UE_IP_NB_INSTANCES_MAX)) { printk("[UE_IP_DRV][%s] ERROR, couldn't find instance\n", __FUNCTION__); return; } printk("[UE_IP_DRV][%s] End\n", __FUNCTION__); } // check dev_pP else { printk("[UE_IP_DRV][%s] Device is null\n", __FUNCTION__); } } //--------------------------------------------------------------------------- int ue_ip_set_config(struct net_device *dev_pP, struct ifmap *map_pP) { //--------------------------------------------------------------------------- printk("[UE_IP_DRV][%s] Begin\n", __FUNCTION__); if (dev_pP->flags & IFF_UP) { return -EBUSY; } if (map_pP->base_addr != dev_pP->base_addr) { printk(KERN_WARNING "[UE_IP_DRV][%s] Can't change I/O address\n", __FUNCTION__); return -EOPNOTSUPP; } if (map_pP->irq != dev_pP->irq) { dev_pP->irq = map_pP->irq; } printk("[UE_IP_DRV][%s] End\n", __FUNCTION__); return 0; } //--------------------------------------------------------------------------- // int ue_ip_hard_start_xmit(struct sk_buff *skb_pP, struct net_device *dev_pP) { //--------------------------------------------------------------------------- int inst; if (dev_pP) { inst = ue_ip_find_inst(dev_pP); } else { printk("[UE_IP_DRV][%s] ERROR, device is null\n", __FUNCTION__); return -1; } if ((inst>=0) && (inst<UE_IP_NB_INSTANCES_MAX)) { #ifdef OAI_DRV_OAI_DRV_DEBUG_DEVICE printk("[UE_IP_DRV][%s] inst %d, begin\n", __FUNCTION__,inst); #endif if (!skb_pP) { printk("[UE_IP_DRV][%s] input parameter skb is NULL\n", __FUNCTION__); return -1; } // End debug information netif_stop_queue(dev_pP); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) || (defined RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= 1796) netif_trans_update(dev_pP); #else dev_pP->trans_start = jiffies; #endif #ifdef OAI_DRV_DEBUG_DEVICE printk("[UE_IP_DRV][%s] step 1\n", __FUNCTION__); #endif ue_ip_common_ip2wireless(skb_pP,inst); #ifdef OAI_DRV_DEBUG_DEVICE printk("[UE_IP_DRV][%s] step 2\n", __FUNCTION__); #endif dev_kfree_skb(skb_pP); #ifdef OAI_DRV_DEBUG_DEVICE printk("[UE_IP_DRV][%s] step 3\n", __FUNCTION__); #endif netif_wake_queue(dev_pP); #ifdef OAI_DRV_DEBUG_DEVICE printk("[UE_IP_DRV][%s] end\n", __FUNCTION__); #endif } else { printk("[UE_IP_DRV][%s] ERROR, couldn't find instance\n", __FUNCTION__); return(-1); } return 0; } //--------------------------------------------------------------------------- struct net_device_stats *ue_ip_get_stats(struct net_device *dev_pP) { //--------------------------------------------------------------------------- ue_ip_priv_t *priv_p = netdev_priv(dev_pP); return &priv_p->stats; } //--------------------------------------------------------------------------- int ue_ip_set_mac_address(struct net_device *dev_pP, void *mac_pP) { //--------------------------------------------------------------------------- //struct sockaddr *addr = mac_pP; printk("[UE_IP_DRV][%s] CHANGE MAC ADDRESS UNSUPPORTED\n", __FUNCTION__); //memcpy(dev_pP->dev_addr, addr->sa_data, dev_pP->addr_len); return 0; } //--------------------------------------------------------------------------- int ue_ip_change_mtu(struct net_device *dev_pP, int mtuP) { //--------------------------------------------------------------------------- printk("[UE_IP_DRV][%s] CHANGE MTU %d bytes\n", __FUNCTION__, mtuP); if ((mtuP<50) || (mtuP>1500)) { return -EINVAL; } dev_pP->mtu = mtuP; return 0; } //--------------------------------------------------------------------------- void ue_ip_change_rx_flags(struct net_device *dev_pP, int flagsP) { //--------------------------------------------------------------------------- ue_ip_priv_t *priv_p = netdev_priv(dev_pP); printk("[UE_IP_DRV][%s] CHANGE RX FLAGS %08X\n", __FUNCTION__, flagsP); priv_p->rx_flags ^= flagsP; } //--------------------------------------------------------------------------- void ue_ip_tx_timeout(struct net_device *dev_pP) { //--------------------------------------------------------------------------- // Transmitter timeout, serious problems. ue_ip_priv_t *priv_p = netdev_priv(dev_pP); printk("[UE_IP_DRV][%s] begin\n", __FUNCTION__); // (ue_ip_priv_t *)(dev_pP->priv_p)->stats.tx_errors++; (priv_p->stats).tx_errors++; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) || (defined RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= 1796) netif_trans_update(dev_pP); #else dev_pP->trans_start = jiffies; #endif netif_wake_queue(dev_pP); printk("[UE_IP_DRV][%s] transmit timed out %s\n", __FUNCTION__,dev_pP->name); } static const struct net_device_ops ue_ip_netdev_ops = { .ndo_open = ue_ip_open, .ndo_stop = ue_ip_stop, .ndo_start_xmit = ue_ip_hard_start_xmit, .ndo_validate_addr = NULL, .ndo_get_stats = ue_ip_get_stats, .ndo_set_mac_address = ue_ip_set_mac_address, .ndo_set_config = ue_ip_set_config, .ndo_do_ioctl = NULL, #if (defined RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= 1797) .extended.ndo_change_mtu = ue_ip_change_mtu, #else .ndo_change_mtu = ue_ip_change_mtu, #endif .ndo_tx_timeout = ue_ip_tx_timeout, .ndo_change_rx_flags = ue_ip_change_rx_flags, }; /*.ndo_set_multicast_list = NULL,*/ //--------------------------------------------------------------------------- // Initialisation of the network device void ue_ip_init(struct net_device *dev_pP) { //--------------------------------------------------------------------------- ue_ip_priv_t *priv_p = NULL; if (dev_pP) { priv_p = netdev_priv(dev_pP); memset(priv_p, 0, sizeof(ue_ip_priv_t)); spin_lock_init(&priv_p->lock); dev_pP->netdev_ops = &ue_ip_netdev_ops; dev_pP->hard_header_len = 0; dev_pP->addr_len = UE_IP_ADDR_LEN; dev_pP->flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOARP; dev_pP->tx_queue_len = UE_IP_TX_QUEUE_LEN; dev_pP->mtu = UE_IP_MTU; } else { printk("[UE_IP_DRV][%s] ERROR, Device is NULL!!\n", __FUNCTION__); return; } } //--------------------------------------------------------------------------- int init_module (void) { //--------------------------------------------------------------------------- int err,inst; char devicename[100]; // Initialize parameters shared with RRC printk("[UE_IP_DRV][%s] Starting OAI IP driver", __FUNCTION__); for (inst=0; inst<UE_IP_NB_INSTANCES_MAX; inst++) { printk("[UE_IP_DRV][%s] begin init instance %d\n", __FUNCTION__,inst); sprintf(devicename,"oip%d",inst+1); #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) ue_ip_dev[inst] = alloc_netdev(sizeof(ue_ip_priv_t),devicename, ue_ip_init); #else ue_ip_dev[inst] = alloc_netdev(sizeof(ue_ip_priv_t),devicename, NET_NAME_PREDICTABLE,ue_ip_init); #endif //netif_stop_queue(ue_ip_dev[inst]); if (ue_ip_dev[inst] == NULL) { printk("[UE_IP_DRV][%s][INST %02d] alloc_netdev FAILED\n", __FUNCTION__,inst); } else { // linux/net/core/dev.c line 4767 err= register_netdev(ue_ip_dev[inst]); if (err) { printk("[UE_IP_DRV][%s] (inst %d): error %i registering device %s\n", __FUNCTION__, inst,err, ue_ip_dev[inst]->name); } else { printk("[UE_IP_DRV][%s] registering device %s, ifindex = %d\n\n", __FUNCTION__,ue_ip_dev[inst]->name, ue_ip_dev[inst]->ifindex); } } } printk("[UE_IP_DRV][%s] NETLINK INIT\n", __FUNCTION__); if ((err=ue_ip_netlink_init()) == -1) { printk("[UE_IP_DRV][%s] NETLINK failed\n", __FUNCTION__); } return err; } //--------------------------------------------------------------------------- void cleanup_module(void) { //--------------------------------------------------------------------------- int inst; printk("[UE_IP_DRV][CLEANUP] begin\n"); for (inst=0; inst<UE_IP_NB_INSTANCES_MAX; inst++) { #ifdef OAI_DRV_DEBUG_DEVICE printk("[UE_IP_DRV][CLEANUP] unregister and free net device instance %d\n",inst); #endif if (ue_ip_dev[inst]) { unregister_netdev(ue_ip_dev[inst]); ue_ip_teardown(ue_ip_dev[inst]); free_netdev(ue_ip_dev[inst]); } } ue_ip_netlink_release(); printk("[UE_IP_DRV][CLEANUP] end\n"); } #define DRV_NAME "ue_ip" #define DRV_VERSION "1.0"DRV_NAME #define DRV_DESCRIPTION "OPENAIR UE IP Device Driver" #define DRV_COPYRIGHT "-Copyright(c) GNU GPL Eurecom 2013" #define DRV_AUTHOR "Lionel GAUTHIER: <firstname.name@eurecom.fr>"DRV_COPYRIGHT