Files
openairinterface5g/openair2/NAS/DRIVER/LITE/device.c
T

631 lines
22 KiB
C
Executable File

/*******************************************************************************
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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#ifdef OAI_NW_DRIVER_TYPE_ETHERNET
#include <linux/if_ether.h>
#endif
#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>
#ifdef OAI_NW_DRIVER_TYPE_ETHERNET
#include <linux/etherdevice.h>
#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
u8 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;i<OAI_NW_DRV_NB_INSTANCES_MAX;i++)
if (oai_nw_drv_dev[i] == dev)
return(i);
return(-1);
}
//---------------------------------------------------------------------------
#ifndef OAI_NW_DRIVER_USE_NETLINK
void *oai_nw_drv_interrupt(void){
//---------------------------------------------------------------------------
u8 cxi;
// struct oai_nw_drv_priv *priv=netdev_priv(dev_id);
// unsigned int flags;
// priv->lock = 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;cxi<OAI_NW_DRV_CX_MAX;cxi++) {
oai_nw_drv_common_flush_rb(priv->cx+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) && (inst<OAI_NW_DRV_NB_INSTANCES_MAX)) {
#ifdef OAI_DRV_OAI_DRV_DEBUG_DEVICE
printk("[OAI_IP_DRV][%s] inst %d, begin\n", __FUNCTION__,inst);
#endif
if (!skb){
printk("[OAI_IP_DRV][%s] input parameter skb is NULL\n", __FUNCTION__);
return -1;
}
// End debug information
netif_stop_queue(dev);
dev->trans_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){
//---------------------------------------------------------------------------
u8 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 = &eth_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; dscpi<OAI_NW_DRV_DSCP_MAX; ++dscpi) {
priv->rclassifier[dscpi]=NULL;
}
priv->nrclassifier=0;*/
//
for (cxi=0;cxi<OAI_NW_DRV_CX_MAX;cxi++) {
#ifdef OAI_DRV_DEBUG_DEVICE
printk("[OAI_IP_DRV][%s] init classifiers, state and timer for MT %u\n", __FUNCTION__, cxi);
#endif
// priv->cx[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, (u8 *)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, (u8 *)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, (u8 *)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((u8 *)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", ((u8 *)(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<OAI_NW_DRV_NB_INSTANCES_MAX;inst++) {
printk("[OAI_IP_DRV][%s] begin init instance %d\n", __FUNCTION__,inst);
sprintf(devicename,"oai%d",inst);
oai_nw_drv_dev[inst] = alloc_netdev(sizeof(struct oai_nw_drv_priv),devicename, oai_nw_drv_init);
//netif_stop_queue(oai_nw_drv_dev[inst]);
if (oai_nw_drv_dev[inst] == NULL) {
printk("[OAI_IP_DRV][%s][INST %02d] alloc_netdev FAILED\n", __FUNCTION__,inst);
} else {
priv = netdev_priv(oai_nw_drv_dev[inst]);
if (oai_nw_drv_dev[inst]){
oai_nw_drv_IMEI[9] += 1;
if (oai_nw_drv_IMEI[9] > 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<OAI_NW_DRV_NB_INSTANCES_MAX;inst++) {
#ifdef OAI_DRV_DEBUG_DEVICE
printk("nasmesh_cleanup_module: unregister and free net device instance %d\n",inst);
#endif
if (oai_nw_drv_dev[inst]) {
unregister_netdev(oai_nw_drv_dev[inst]);
oai_nw_drv_teardown(oai_nw_drv_dev[inst]);
free_netdev(oai_nw_drv_dev[inst]);
}
}
#ifdef OAI_NW_DRIVER_USE_NETLINK
oai_nw_drv_netlink_release();
#endif //OAI_NW_DRIVER_USE_NETLINK
printk("nasmesh_cleanup_module: end\n");
}
#define DRV_NAME "NASMESH"
#define DRV_VERSION "3.0.2"DRV_NAME
#define DRV_DESCRIPTION "OPENAIR MESH Device Driver"
#define DRV_COPYRIGHT "-Copyright(c) GNU GPL Eurecom 2009"
#define DRV_AUTHOR "Raymond Knopp, and Navid Nikaein: <firstname.name@eurecom.fr>"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*/