/******************************************************************************* Eurecom OpenAirInterface 2 Copyright(c) 1999 - 2010 Eurecom This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information Openair Admin: openair_admin@eurecom.fr Openair Tech : openair_tech@eurecom.fr Forums : http://forums.eurecom.fsr/openairinterface Address : Eurecom, 2229, route des crĂȘtes, 06560 Valbonne Sophia Antipolis, France *******************************************************************************/ /*! \file device.c * \brief Networking Device Driver for OpenAirInterface Ethernet * \author navid.nikaein, yan.moret(no longer valid), michelle.wetterwald, raymond.knopp, Lionel Gauthier * \company Eurecom * \email: raymond.knopp@eurecom.fr, navid.nikaein@eurecom.fr, michelle.wetterwald@eurecom.fr, lionel.gauthier@eurecom.fr */ /*******************************************************************************/ #ifndef OAI_NW_DRIVER_USE_NETLINK #ifdef RTAI #include "rtai_posix.h" #define RTAI_IRQ 30 //try to get this irq with RTAI #endif // RTAI #endif // OAI_NW_DRIVER_USE_NETLINK #include "constant.h" #include "local.h" #include "proto_extern.h" //#include #include #include #include #include #ifdef OAI_NW_DRIVER_TYPE_ETHERNET #include #endif #include #include #include #include #include #include #include #include #ifdef OAI_NW_DRIVER_TYPE_ETHERNET #include #endif struct net_device *oai_nw_drv_dev[OAI_NW_DRV_NB_INSTANCES_MAX]; #ifdef OAI_NW_DRIVER_USE_NETLINK extern void oai_nw_drv_netlink_release(void); extern int oai_nw_drv_netlink_init(void); #endif uint8_t NULL_IMEI[14]={0x05, 0x04, 0x03, 0x01, 0x02 ,0x00, 0x00, 0x00, 0x05, 0x04, 0x03 ,0x00, 0x01, 0x08}; static unsigned char oai_nw_drv_IMEI[14]; static int m_arg=0; static unsigned int oai_nw_drv_is_clusterhead=0; //--------------------------------------------------------------------------- int oai_nw_drv_find_inst(struct net_device *dev) { //--------------------------------------------------------------------------- int i; for (i=0;ilock = SPIN_LOCK_UNLOCKED; #ifdef OAI_DRV_DEBUG_INTERRUPT printk("INTERRUPT - begin\n"); #endif // spin_lock_irqsave(&priv->lock,flags); cxi=0; // mesh_GC_receive(); // mesh_DC_receive(naspriv->cx+cxi); #ifndef OAI_NW_DRIVER_USE_NETLINK oai_nw_drv_common_wireless2ip(); #endif // spin_unlock_irqrestore(&priv->lock,flags); #ifdef OAI_DRV_DEBUG_INTERRUPT printk("INTERRUPT: end\n"); #endif // return 0; } #endif //NETLINK //--------------------------------------------------------------------------- void oai_nw_drv_timer(unsigned long data){ //--------------------------------------------------------------------------- struct oai_nw_drv_priv *priv=(struct oai_nw_drv_priv *)data; spin_lock(&priv->lock); (priv->timer).function=oai_nw_drv_timer; (priv->timer).expires=jiffies+OAI_NW_DRV_TIMER_TICK; (priv->timer).data=data; add_timer(&priv->timer); spin_unlock(&priv->lock); return; // add_timer(&gpriv->timer); // spin_unlock(&gpriv->lock); } //--------------------------------------------------------------------------- // Called by ifconfig when the device is activated by ifconfig int oai_nw_drv_open(struct net_device *dev){ //--------------------------------------------------------------------------- struct oai_nw_drv_priv *priv=netdev_priv(dev); // Address has already been set at init #ifndef OAI_NW_DRIVER_USE_NETLINK if (pdcp_2_oai_nw_drv_irq==-EBUSY){ printk("[OAI_IP_DRV][%s] : irq failure\n", __FUNCTION__); return -EBUSY; } #endif //OAI_NW_DRIVER_USE_NETLINK if(!netif_queue_stopped(dev)) netif_start_queue(dev); else netif_wake_queue(dev); init_timer(&priv->timer); (priv->timer).expires = jiffies+OAI_NW_DRV_TIMER_TICK; (priv->timer).data = (unsigned long)priv; (priv->timer).function = oai_nw_drv_timer; //add_timer(&priv->timer); printk("[OAI_IP_DRV][%s] name = %s\n", __FUNCTION__, dev->name); return 0; } //--------------------------------------------------------------------------- // Called by ifconfig when the device is desactivated int oai_nw_drv_stop(struct net_device *dev){ //--------------------------------------------------------------------------- struct oai_nw_drv_priv *priv = netdev_priv(dev); printk("[OAI_IP_DRV][%s] Begin\n", __FUNCTION__); del_timer(&(priv->timer)); netif_stop_queue(dev); // MOD_DEC_USE_COUNT; printk("[OAI_IP_DRV][%s] End\n", __FUNCTION__); return 0; } //--------------------------------------------------------------------------- void oai_nw_drv_teardown(struct net_device *dev){ //--------------------------------------------------------------------------- struct oai_nw_drv_priv *priv; int inst; printk("[OAI_IP_DRV][%s] Begin\n", __FUNCTION__); if (dev) { priv = netdev_priv(dev); inst = oai_nw_drv_find_inst(dev); if ((inst<0) || (inst>=OAI_NW_DRV_NB_INSTANCES_MAX)) { printk("[OAI_IP_DRV][%s] ERROR, couldn't find instance\n", __FUNCTION__); return; } /*oai_nw_drv_class_flush_recv_classifier(priv); for (cxi=0;cxicx+cxi); oai_nw_drv_class_flush_send_classifier(priv->cx+cxi); }*/ printk("[OAI_IP_DRV][%s] End\n", __FUNCTION__); } // check dev else { printk("[OAI_IP_DRV][%s] Device is null\n", __FUNCTION__); } } //--------------------------------------------------------------------------- int oai_nw_drv_set_config(struct net_device *dev, struct ifmap *map){ //--------------------------------------------------------------------------- printk("[OAI_IP_DRV][%s] Begin\n", __FUNCTION__); if (dev->flags & IFF_UP) return -EBUSY; if (map->base_addr != dev->base_addr) { printk(KERN_WARNING "[OAI_IP_DRV][%s] Can't change I/O address\n", __FUNCTION__); return -EOPNOTSUPP; } if (map->irq != dev->irq) dev->irq = map->irq; printk("[OAI_IP_DRV][%s] End\n", __FUNCTION__); return 0; } //--------------------------------------------------------------------------- // int oai_nw_drv_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){ //--------------------------------------------------------------------------- int inst; if (dev) { inst = oai_nw_drv_find_inst(dev); } else { printk("[OAI_IP_DRV][%s] ERROR, device is null\n", __FUNCTION__); return -1; } if ((inst>=0) && (insttrans_start = jiffies; #ifdef OAI_DRV_DEBUG_DEVICE printk("[OAI_IP_DRV][%s] step 1\n", __FUNCTION__); #endif oai_nw_drv_common_ip2wireless(skb,inst); #ifdef OAI_DRV_DEBUG_DEVICE printk("[OAI_IP_DRV][%s] step 2\n", __FUNCTION__); #endif dev_kfree_skb(skb); #ifdef OAI_DRV_DEBUG_DEVICE printk("[OAI_IP_DRV][%s] step 3\n", __FUNCTION__); #endif netif_wake_queue(dev); #ifdef OAI_DRV_DEBUG_DEVICE printk("[OAI_IP_DRV][%s] end\n", __FUNCTION__); #endif } else { printk("[OAI_IP_DRV][%s] ERROR, couldn't find instance\n", __FUNCTION__); return(-1); } return 0; } //--------------------------------------------------------------------------- struct net_device_stats *oai_nw_drv_get_stats(struct net_device *dev){ //--------------------------------------------------------------------------- // return &((struct oai_nw_drv_priv *)dev->priv)->stats; struct oai_nw_drv_priv *priv = netdev_priv(dev); //printk("[OAI_IP_DRV][%s]\n", __FUNCTION__); return &priv->stats; } //--------------------------------------------------------------------------- int oai_nw_drv_set_mac_address(struct net_device *dev, void *mac) { //--------------------------------------------------------------------------- struct sockaddr *addr = mac; printk("[OAI_IP_DRV][%s] CHANGE MAC ADDRESS\n", __FUNCTION__); memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); return 0; } //--------------------------------------------------------------------------- int oai_nw_drv_change_mtu(struct net_device *dev, int mtu){ //--------------------------------------------------------------------------- printk("[OAI_IP_DRV][%s] CHANGE MTU %d bytes\n", __FUNCTION__, mtu); if ((mtu<50) || (mtu>1500)) return -EINVAL; dev->mtu = mtu; return 0; } //--------------------------------------------------------------------------- void oai_nw_drv_change_rx_flags(struct net_device *dev, int flags){ //--------------------------------------------------------------------------- struct oai_nw_drv_priv *priv = netdev_priv(dev); printk("[OAI_IP_DRV][%s] CHANGE RX FLAGS %08X\n", __FUNCTION__, flags); priv->rx_flags ^= flags; } //--------------------------------------------------------------------------- void oai_nw_drv_tx_timeout(struct net_device *dev){ //--------------------------------------------------------------------------- // Transmitter timeout, serious problems. struct oai_nw_drv_priv *priv = netdev_priv(dev); printk("[OAI_IP_DRV][%s] begin\n", __FUNCTION__); // (struct oai_nw_drv_priv *)(dev->priv)->stats.tx_errors++; (priv->stats).tx_errors++; dev->trans_start = jiffies; netif_wake_queue(dev); printk("[OAI_IP_DRV][%s] transmit timed out %s\n", __FUNCTION__,dev->name); } #ifdef KERNEL_VERSION_GREATER_THAN_2629 static const struct net_device_ops nasmesh_netdev_ops = { .ndo_open = oai_nw_drv_open, .ndo_stop = oai_nw_drv_stop, .ndo_start_xmit = oai_nw_drv_hard_start_xmit, .ndo_validate_addr = NULL, .ndo_get_stats = oai_nw_drv_get_stats, .ndo_set_mac_address = oai_nw_drv_set_mac_address, .ndo_set_config = oai_nw_drv_set_config, .ndo_do_ioctl = oai_nw_drv_CTL_ioctl, .ndo_change_mtu = oai_nw_drv_change_mtu, .ndo_tx_timeout = oai_nw_drv_tx_timeout, .ndo_change_rx_flags = oai_nw_drv_change_rx_flags, }; #endif /*.ndo_set_multicast_list = NULL,*/ //--------------------------------------------------------------------------- // Initialisation of the network device void oai_nw_drv_init(struct net_device *dev){ //--------------------------------------------------------------------------- uint8_t cxi; struct oai_nw_drv_priv *priv; int index; if (dev) { priv = netdev_priv(dev); memset(priv, 0, sizeof(struct oai_nw_drv_priv)); #ifndef OAI_NW_DRIVER_TYPE_ETHERNET dev->type = ARPHRD_EURECOM_LTE; dev->features = NETIF_F_NO_CSUM; dev->hard_header_len = 0; dev->addr_len = ETH_ALEN; // 6 dev->flags = IFF_BROADCAST | IFF_MULTICAST | IFF_NOARP; dev->tx_queue_len = NAS_TX_QUEUE_LEN; dev->mtu = NAS_MTU; #endif // Can be one of the following enum defined in include/linux/netdevice.h: // enum netdev_state_t { // __LINK_STATE_START, // __LINK_STATE_PRESENT, // __LINK_STATE_NOCARRIER, // __LINK_STATE_LINKWATCH_PENDING, // __LINK_STATE_DORMANT, // }; set_bit(__LINK_STATE_PRESENT, &dev->state); // #ifdef KERNEL_VERSION_GREATER_THAN_2629 printk("[OAI_IP_DRV][%s] KERNEL_VERSION_GREATER_THAN_2629\n", __FUNCTION__); #ifdef KERNEL_VERSION_GREATER_THAN_32 printk("[OAI_IP_DRV][%s] KERNEL_VERSION_GREATER_THAN_32\n", __FUNCTION__); #endif dev->netdev_ops = &nasmesh_netdev_ops; #ifdef OAI_NW_DRIVER_TYPE_ETHERNET printk("[OAI_IP_DRV][%s] Driver type ETHERNET\n", __FUNCTION__); // Memo: linux-source-3.2.0/net/ethernet/eth.c: //334 void ether_setup(struct net_device *dev) //335 { //336 dev->header_ops = ð_header_ops; //337 dev->type = ARPHRD_ETHER; //338 dev->hard_header_len = ETH_HLEN; //339 dev->mtu = ETH_DATA_LEN; //340 dev->addr_len = ETH_ALEN; //341 dev->tx_queue_len = 1000; /* Ethernet wants good queues */ //342 dev->flags = IFF_BROADCAST|IFF_MULTICAST; //343 dev->priv_flags |= IFF_TX_SKB_SHARING; //344 //345 memset(dev->broadcast, 0xFF, ETH_ALEN); //346 } ether_setup(dev); #endif #else printk("[OAI_IP_DRV][%s] KERNEL_VERSION_LOWER_THAN_2629\n", __FUNCTION__); dev->open = oai_nw_drv_open; dev->stop = oai_nw_drv_stop; dev->set_config = oai_nw_drv_set_config; dev->hard_start_xmit = oai_nw_drv_hard_start_xmit; dev->do_ioctl = oai_nw_drv_CTL_ioctl; dev->get_stats = oai_nw_drv_get_stats; // dev->rebuild_header = NULL; // dev->hard_header = NULL; dev->change_mtu = oai_nw_drv_change_mtu; // dev->hard_header_cache = NULL; // dev->header_cache_update = NULL; dev->tx_timeout = oai_nw_drv_tx_timeout; dev->change_rx_flags = oai_nw_drv_change_rx_flags; #endif // // Initialize private structure priv->rx_flags = OAI_NW_DRV_RESET_RX_FLAGS; priv->sap[OAI_NW_DRV_RAB_INPUT_SAPI] = PDCP2IP_FIFO;//QOS_DEVICE_CONVERSATIONAL_INPUT; priv->sap[OAI_NW_DRV_RAB_OUTPUT_SAPI] = IP2PDCP_FIFO;//QOS_DEVICE_STREAMING_INPUT; // priv->retry_limit=RETRY_LIMIT_DEFAULT; // priv->timer_establishment=TIMER_ESTABLISHMENT_DEFAULT; // priv->timer_release=TIMER_RELEASE_DEFAULT; /*for (dscpi=0; dscpirclassifier[dscpi]=NULL; } priv->nrclassifier=0;*/ // for (cxi=0;cxicx[cxi].sap[NAS_DC_INPUT_SAPI] = RRC_DEVICE_DC_INPUT0; // priv->cx[cxi].sap[NAS_DC_OUTPUT_SAPI] = RRC_DEVICE_DC_OUTPUT0; priv->cx[cxi].state = OAI_NW_DRV_IDLE; priv->cx[cxi].countimer = OAI_NW_DRV_TIMER_IDLE; priv->cx[cxi].retry = 0; priv->cx[cxi].lcr = cxi; /*priv->cx[cxi].rb = NULL; priv->cx[cxi].num_rb = 0; // initialisation of the classifier for (dscpi=0; dscpi<65; ++dscpi) { priv->cx[cxi].sclassifier[dscpi] = NULL; priv->cx[cxi].fclassifier[dscpi] = NULL; } priv->cx[cxi].nsclassifier=0; priv->cx[cxi].nfclassifier=0; */ // initialisation of the IP address oai_nw_drv_TOOL_eNB_imei2iid(oai_nw_drv_IMEI, (uint8_t *)priv->cx[cxi].iid6, dev->addr_len); priv->cx[cxi].iid4=0; // } spin_lock_init(&priv->lock); //this requires kernel patch for OAI driver: typically for RF/hard realtime emu experimentation #define ADDRCONF #ifdef ADDRCONF #ifdef OAI_NW_DRIVER_USE_NETLINK oai_nw_drv_TOOL_eNB_imei2iid(oai_nw_drv_IMEI, dev->dev_addr, dev->addr_len);// IMEI to device address (for stateless autoconfiguration address) oai_nw_drv_TOOL_eNB_imei2iid(oai_nw_drv_IMEI, (uint8_t *)priv->cx[0].iid6, dev->addr_len); #else oai_nw_drv_TOOL_imei2iid(oai_nw_drv_IMEI, dev->dev_addr);// IMEI to device address (for stateless autoconfiguration address) oai_nw_drv_TOOL_imei2iid(oai_nw_drv_IMEI, (uint8_t *)priv->cx[0].iid6); #endif // this is more appropriate for user space soft realtime emulation #else // LG: strange for (index = 0; index < dev->addr_len; index++) { dev->dev_addr[index] = 16*oai_nw_drv_IMEI[index]+oai_nw_drv_IMEI[index+1]); } memcpy((uint8_t *)priv->cx[0].iid6,&oai_nw_drv_IMEI[0],dev->addr_len); printk("INIT: init IMEI to IID\n"); #endif printk("[OAI_IP_DRV][%s] Setting HW addr to : ", __FUNCTION__); for (index = 0; index < dev->addr_len; index++) { printk("%02X", dev->dev_addr[index]); } printk("\n[OAI_IP_DRV][%s] Setting priv->cx to : ", __FUNCTION__); for (index = 0; index < 8; index++) { printk("%02X", ((uint8_t *)(priv->cx[0].iid6))[index]); } printk("\n"); printk("[OAI_IP_DRV][%s] INIT: end\n", __FUNCTION__); return; } else { printk("[OAI_IP_DRV][%s] ERROR, Device is NULL!!\n", __FUNCTION__); return; } } //--------------------------------------------------------------------------- int init_module (void) { //--------------------------------------------------------------------------- int err,inst, index; struct oai_nw_drv_priv *priv; char devicename[100]; // Initialize parameters shared with RRC printk("[OAI_IP_DRV][%s] Starting NASMESH, number of IMEI parameters %d, IMEI ", __FUNCTION__, m_arg); for (index = 0; index < m_arg; index++) { printk("%02X", oai_nw_drv_IMEI[index]); } printk("\n"); #ifndef OAI_NW_DRIVER_USE_NETLINK #ifdef RTAI //with RTAI you have to indicate which irq# you want pdcp_2_oai_nw_drv_irq=rt_request_srq(0, oai_nw_drv_interrupt, NULL); #endif if (pdcp_2_oai_nw_drv_irq == -EBUSY || pdcp_2_oai_nw_drv_irq == -EINVAL){ printk("[OAI_IP_DRV][%s] No interrupt resource available\n", __FUNCTION__); return -EBUSY; } else printk("[OAI_IP_DRV][%s] Interrupt %d\n", __FUNCTION__, pdcp_2_oai_nw_drv_irq); //rt_startup_irq(RTAI_IRQ); //rt_enable_irq(RTAI_IRQ); #endif //NETLINK for (inst=0;inst 0x0F) { oai_nw_drv_IMEI[8] = oai_nw_drv_IMEI[9] >> 4; oai_nw_drv_IMEI[9] = oai_nw_drv_IMEI[9] & 0x0F; } } // linux/net/core/dev.c line 4767 err= register_netdev(oai_nw_drv_dev[inst]); if (err){ printk("[OAI_IP_DRV][%s] (inst %d): error %i registering device %s\n", __FUNCTION__, inst,err, oai_nw_drv_dev[inst]->name); }else{ printk("[OAI_IP_DRV][%s] registering device %s, ifindex = %d\n\n", __FUNCTION__,oai_nw_drv_dev[inst]->name, oai_nw_drv_dev[inst]->ifindex); } } } #ifdef OAI_NW_DRIVER_USE_NETLINK printk("[OAI_IP_DRV][%s] NETLINK INIT\n", __FUNCTION__); if ((err=oai_nw_drv_netlink_init()) == -1) printk("[OAI_IP_DRV][%s] NETLINK failed\n", __FUNCTION__); #endif //NETLINK return err; } //--------------------------------------------------------------------------- void cleanup_module(void){ //--------------------------------------------------------------------------- int inst; printk("[OAI_IP_DRV][CLEANUP]nasmesh_cleanup_module: begin\n"); #ifndef OAI_NW_DRIVER_USE_NETLINK if (pdcp_2_oai_nw_drv_irq!=-EBUSY) { pdcp_2_oai_nw_drv_irq=0; #ifdef RTAI // V1 // rt_free_linux_irq(priv->irq, NULL); // END V1 rt_free_srq(pdcp_2_oai_nw_drv_irq); #endif // Start IRQ linux //free_irq(priv->irq, NULL); // End IRQ linux } #else // NETLINK #endif //NETLINK for (inst=0;inst"DRV_COPYRIGHT module_param_array_named(oai_nw_drv_IMEI,oai_nw_drv_IMEI,byte,&m_arg,0); //module_param_array(oai_nw_drv_IMEI,byte,&m_arg,0444); MODULE_PARM_DESC(oai_nw_drv_IMEI,"The IMEI Hardware address (64-bit, decimal nibbles)"); //module_param(oai_nw_drv_is_clusterhead,uint,0444); module_param_named(oai_nw_drv_is_clusterhead,oai_nw_drv_is_clusterhead,uint,0444); MODULE_PARM_DESC(oai_nw_drv_is_clusterhead,"The Clusterhead Indicator"); //MODULE_AUTHOR(DRV_AUTHOR); //MODULE_DESCRIPTION(DRV_DESCRIPTION); //MODULE_LICENSE("GPL"); //MODULE_VERSION(DRV_VERSION); /*#endif*/