Files
openairinterface5g/openair2/NETWORK_DRIVER/LITE/device.c
T

657 lines
21 KiB
C
Executable File

/*******************************************************************************
OpenAirInterface
Copyright(c) 1999 - 2014 Eurecom
OpenAirInterface is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenAirInterface 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.
You should have received a copy of the GNU General Public License
along with OpenAirInterface.The full GNU General Public License is
included in this distribution in the file called "COPYING". If not,
see <http://www.gnu.org/licenses/>.
Contact Information
OpenAirInterface Admin: openair_admin@eurecom.fr
OpenAirInterface Tech : openair_tech@eurecom.fr
OpenAirInterface Dev : openair4g-devel@lists.eurecom.fr
Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
*******************************************************************************/
/*! \file device.c
* \brief Networking Device Driver for OpenAirInterface Ethernet
* \author navid.nikaein, Lionel Gauthier, raymond.knopp,
* \company Eurecom
* \email:navid.nikaein@eurecom.fr, lionel.gauthier@eurecom.fr, raymond.knopp@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/version.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
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; 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)
{
//---------------------------------------------------------------------------
uint8_t 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);
}
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,
};
/*.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);
//
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
//
// 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, (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<OAI_NW_DRV_NB_INSTANCES_MAX; inst++) {
printk("[OAI_IP_DRV][%s] begin init instance %d\n", __FUNCTION__,inst);
sprintf(devicename,"oai%d",inst);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
oai_nw_drv_dev[inst] = alloc_netdev(sizeof(struct oai_nw_drv_priv),devicename, oai_nw_drv_init);
#else
oai_nw_drv_dev[inst] = alloc_netdev(sizeof(struct oai_nw_drv_priv),devicename, NET_NAME_PREDICTABLE, oai_nw_drv_init);
#endif
//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);