wanpipe/patches/kdrivers/src/wanrouter/wandev.c

612 lines
14 KiB
C

/*****************************************************************************
* wandev.c WAN Multiprotocol Interface Module. Main code.
*
* Author: Nenad Corbic
*
* Copyright: (c) 2008 Sangoma Technologies Inc.
*
* This program 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
* 2 of the License, or (at your option) any later version.
* ============================================================================
*/
#include "wanpipe_includes.h"
#include "wanpipe_defines.h"
#include "wanpipe_debug.h"
#include "wanpipe_common.h"
#include "wanpipe_iface.h"
#include "wanpipe.h"
#include "wanpipe_api.h"
#include "if_wanpipe.h"
#include "wanpipe_cdev_iface.h"
#include "sdladrv.h"
/*=================================================
* Type Defines
*================================================*/
#define DEBUG_WANDEV DEBUG_TEST
typedef struct wanpipe_wandev
{
int init;
int used;
wanpipe_cdev_t *cdev;
wan_mutexlock_t lock;
}wanpipe_wandev_t;
/*=================================================
* Prototypes
*================================================*/
static int wp_wandev_open(void *obj);
static int wp_wandev_close(void *obj);
static int wp_wandev_ioctl(void *obj, int cmd, void *data);
static int wanpipe_port_cfg(wanpipe_wandev_t *wdev, void *data);
static int wanpipe_port_management(wanpipe_wandev_t *wdev, void *data);
static int wanpipe_mgmnt_get_hardware_info(wanpipe_wandev_t *wdev, wan_device_t *wandev, port_management_struct_t *usr_port_mgmnt);
static int wanpipe_mgmnt_stop_port(wanpipe_wandev_t *wdev, wan_device_t *wandev, port_management_struct_t *usr_port_mgmnt);
static int wanpipe_mgmnt_start_port(wanpipe_wandev_t *wdev, wan_device_t *wandev, port_management_struct_t *usr_port_mgmnt);
static int wanpipe_mgmnt_get_driver_version(wanpipe_wandev_t *wdev, wan_device_t *wandev, port_management_struct_t *usr_port_mgmnt);
static int wanpipe_mgmnt_start_port_if(wanpipe_wandev_t *wdev, wan_device_t *wandev, unsigned short if_no);
static int wanpipe_mgmnt_stop_port_if(wanpipe_wandev_t *wdev, wan_device_t *wandev, char* if_name);
/*=================================================
* Global Defines
*================================================*/
static wanpipe_wandev_t wandev;
static wanpipe_cdev_ops_t wandev_fops = {
open: wp_wandev_open,
close: wp_wandev_close,
ioctl: wp_wandev_ioctl,
};
/*=================================================
* Functions
*================================================*/
int wanpipe_wandev_create(void)
{
int err=-EINVAL;
wanpipe_cdev_t *cdev = wan_kmalloc(sizeof(wanpipe_cdev_t));
if (!cdev) {
return -ENOMEM;
}
memset(cdev,0,sizeof(wanpipe_cdev_t));
memset(&wandev,0,sizeof(wanpipe_wandev_t));
wan_mutex_lock_init(&wandev.lock, "wandev_mutex_lock");
cdev->dev_ptr=&wandev;
wandev.cdev=cdev;
memcpy(&cdev->ops,&wandev_fops,sizeof(wanpipe_cdev_ops_t));
err=wanpipe_cdev_cfg_ctrl_create(cdev);
if (err) {
wan_free(cdev);
memset(&wandev,0,sizeof(wanpipe_wandev_t));
}
DEBUG_WANDEV("%s: WANDEV CREATE \n",__FUNCTION__);
return err;
}
int wanpipe_wandev_free()
{
if (wandev.cdev) {
wanpipe_cdev_free(wandev.cdev);
wan_free(wandev.cdev);
wandev.cdev=NULL;
}
DEBUG_WANDEV("%s: WANDEV FREE \n",__FUNCTION__);
return 0;
}
static int wp_wandev_open(void *obj)
{
#if 0
wanpipe_wandev_t *wdev=(wanpipe_wandev_t*)obj;
if (wan_test_and_set_bit(0,&wdev->used)) {
return -EBUSY;
}
#endif
DEBUG_WANDEV("%s: WANDEV OPEN \n",__FUNCTION__);
return 0;
}
static int wp_wandev_close(void *obj)
{
#if 0
wanpipe_wandev_t *wdev=(wanpipe_wandev_t*)obj;
wan_clear_bit(0,&wdev->used);
#endif
DEBUG_WANDEV("%s: WANDEV CLOSE \n",__FUNCTION__);
return 0;
}
static int wp_wandev_ioctl(void *obj, int cmd, void *data)
{
wanpipe_wandev_t *wdev=(wanpipe_wandev_t*)obj;
wan_smp_flag_t flag;
int err=-EINVAL;
wan_mutex_lock(&wdev->lock,&flag);
switch (cmd) {
case WANPIPE_IOCTL_WRITE:
case WANPIPE_IOCTL_READ:
break;
case WANPIPE_IOCTL_MGMT:
case WANPIPE_IOCTL_SET_IDLE_TX_BUFFER:
case WANPIPE_IOCTL_API_POLL:
case WANPIPE_IOCTL_SET_SHARED_EVENT:
break;
case WANPIPE_IOCTL_PORT_MGMT:
err=wanpipe_port_management(wdev,data);
break;
case WANPIPE_IOCTL_PORT_CONFIG:
err=wanpipe_port_cfg(wdev,data);
break;
}
wan_mutex_unlock(&wdev->lock,&flag);
return err;
}
static int wanpipe_port_management(wanpipe_wandev_t *wdev, void *data)
{
port_management_struct_t *usr_port_mgmnt=NULL;
int err=-EINVAL;
char card_name[100];
wan_device_t *wandev;
int cnt;
DEBUG_WANDEV("%s: WPCTRL PORT MANAGMENT IOCTL \n",__FUNCTION__);
usr_port_mgmnt = wan_kmalloc(sizeof(port_management_struct_t));
if (!usr_port_mgmnt) {
err=-ENOMEM;
goto wanpipe_port_management_exit;
}
err=WAN_COPY_FROM_USER(usr_port_mgmnt, data, sizeof(port_management_struct_t));
if (err) {
goto wanpipe_port_management_exit;
}
usr_port_mgmnt->operation_status = SANG_STATUS_INVALID_DEVICE;
if (!usr_port_mgmnt->port_no) {
err=-ENODEV;
goto wanpipe_port_management_exit;
}
sprintf(card_name,"wanpipe%d",usr_port_mgmnt->port_no);
wandev=wan_find_wandev_device(card_name);
if (!wandev) {
err=-ENODEV;
goto wanpipe_port_management_exit;
}
switch (usr_port_mgmnt->command_code) {
case GET_HARDWARE_INFO:
/* Fill in "hardware_info_t" structure */
err=wanpipe_mgmnt_get_hardware_info(wdev, wandev, usr_port_mgmnt);
if (err != 0) {
err=-ENODEV;
}
break;
case STOP_PORT:
/* Stop Port. Prior to calling this command all 'handles'
to communication interfaces such as WANPIPE1_IF0 must
be closed using CloseHandle() system call. */
err=wanpipe_mgmnt_stop_port(wdev, wandev, usr_port_mgmnt);
break;
case START_PORT_VOLATILE_CONFIG:
/* Start Port. Use configuration stored in a Port's Driver memory buffer.
This command runs faster than START_PORT_REGISTRY_CFG.
Recommended for use if Port is restarted *a lot* */
err=wanpipe_mgmnt_start_port(wdev, wandev, usr_port_mgmnt);
break;
case START_PORT_REGISTRY_CONFIG:
/* Start Port. Use configuration stored in the Port's Registry key.
This command runs slower than START_PORT_VOLATILE_CFG.
Recommended for use if Port is *not* restarted often (most cases) */
/* Not supported */
break;
case START_PORT_IF_CONFIG:
/* Start specific port/interface */
err=wanpipe_mgmnt_start_port_if(wdev, wandev, (unsigned short)usr_port_mgmnt->data[0]);
break;
case STOP_PORT_IF:
/* Stop specific port/interface */
err=wanpipe_mgmnt_stop_port_if(wdev, wandev, usr_port_mgmnt->data);
break;
case GET_DRIVER_VERSION:
/* Fill in "DRIVER_VERSION" structure. */
err = wanpipe_mgmnt_get_driver_version(wdev, wandev, usr_port_mgmnt);
break;
case GET_PORT_OPERATIONAL_STATS:
/* Fill in "port_stats_t" structure. */
break;
case FLUSH_PORT_OPERATIONAL_STATS:
/* Reset port's statistics counters in API driver */
break;
case WANPIPE_HARDWARE_RESCAN:
{
int new_cards=0;
/* Detect only new cards */
new_cards = sdla_hw_probe();
#if defined(CONFIG_PRODUCT_WANPIPE_USB)
new_cards += sdla_get_hw_usb_adptr_cnt();
#endif
/* Free all so that we erase any hw that is not there
any more. And rescan from begining. */
sdla_hw_probe_free();
cnt = sdla_hw_probe();
#if defined(CONFIG_PRODUCT_WANPIPE_USB)
cnt += sdla_get_hw_usb_adptr_cnt();
#endif
DEBUG_EVENT("wandev: hardware rescan: total=%i new=%i\n",cnt,new_cards);
#if defined(WANPIPE_DEVICE_ALLOC_CNT)
if (WANPIPE_DEVICE_ALLOC_CNT > cnt) {
/* The pre defined number of cards still exceed
* the newly detected cards, thus no need to
* allocate more */
new_cards=0;
}
#endif
/* Only add new cards */
if (new_cards) {
int i;
for (i = 0; i < new_cards; i++){
if (sdladrv_callback.add_device){
sdladrv_callback.add_device();
}
}
}
usr_port_mgmnt->port_no=cnt;
err=0;
}
break;
}
wanpipe_port_management_exit:
if (err==0) {
usr_port_mgmnt->operation_status = SANG_STATUS_SUCCESS;
}
if (usr_port_mgmnt) {
int res=WAN_COPY_TO_USER(data, usr_port_mgmnt, sizeof(port_management_struct_t));
if (err==0 && res) {
err=res;
}
wan_free(usr_port_mgmnt);
usr_port_mgmnt=NULL;
}
return err;
}
static int wanpipe_port_cfg(wanpipe_wandev_t *wdev, void *data)
{
wanpipe_port_cfg_t *usr_port_cfg=NULL;
int err=-EINVAL;
wan_device_t *wandev;
char card_name[100];
if (!wdev || !data) {
DEBUG_ERROR("%s: Error: Invalid wdev or data ptrs \n",__FUNCTION__);
return -EFAULT;
}
usr_port_cfg = wan_kmalloc(sizeof(wanpipe_port_cfg_t));
if (!usr_port_cfg) {
err = -ENOMEM;
goto wanpipe_port_cfg_exit;
}
err=WAN_COPY_FROM_USER(usr_port_cfg,
data,
sizeof(wanpipe_port_cfg_t));
if (err) {
goto wanpipe_port_cfg_exit;
}
usr_port_cfg->operation_status = SANG_STATUS_INVALID_DEVICE;
if (!usr_port_cfg->port_no) {
goto wanpipe_port_cfg_exit;
}
sprintf(card_name,"wanpipe%d",usr_port_cfg->port_no);
wandev=wan_find_wandev_device(card_name);
if (!wandev) {
err=-ENODEV;
goto wanpipe_port_cfg_exit;
}
switch (usr_port_cfg->command_code) {
case GET_PORT_VOLATILE_CONFIG:
if (!wandev->port_cfg) {
err = -ENODEV;
goto wanpipe_port_cfg_exit;
}
err=0;
memcpy(usr_port_cfg,wandev->port_cfg, sizeof(wanpipe_port_cfg_t));
/* Get current Port configuration stored in a Port's Driver memory buffer */
break;
case SET_PORT_VOLATILE_CONFIG:
/* Set new Port configuration in a Port's Driver memory buffer.
Prior to using this command Port must be stopped with STOP_PORT command.
This command will not update Port's Property Pages in the Device Manager.
Recommended for use if Port is restarted *a lot*. */
if (!wandev->port_cfg) {
wandev->port_cfg = wan_kmalloc(sizeof(wanpipe_port_cfg_t));
if (!wandev->port_cfg) {
err = -ENOMEM;
goto wanpipe_port_cfg_exit;
}
}
err=0;
memcpy(wandev->port_cfg,usr_port_cfg,sizeof(wanpipe_port_cfg_t));
break;
}
if (err == 0) {
usr_port_cfg->operation_status = SANG_STATUS_SUCCESS;
}
wanpipe_port_cfg_exit:
if (usr_port_cfg) {
err= WAN_COPY_TO_USER(data,
usr_port_cfg,
sizeof(wanpipe_port_cfg_t));
wan_free(usr_port_cfg);
usr_port_cfg=NULL;
}
return err;
}
static int wanpipe_mgmnt_get_driver_version(wanpipe_wandev_t *wdev, wan_device_t *wandev, port_management_struct_t *usr_port_mgmnt)
{
wan_driver_version_t *drv_ver = (wan_driver_version_t*)usr_port_mgmnt->data;
drv_ver->major=WANPIPE_VERSION_MAJOR;
drv_ver->minor=WANPIPE_VERSION_MINOR;
drv_ver->minor1=WANPIPE_VERSION_MINOR1;
drv_ver->minor2=WANPIPE_VERSION_MINOR2;
usr_port_mgmnt->operation_status = 0;
return 0;
}
static int wanpipe_mgmnt_get_hardware_info(wanpipe_wandev_t *wdev, wan_device_t *wandev, port_management_struct_t *usr_port_mgmnt)
{
hardware_info_t *hw_info = (hardware_info_t*)usr_port_mgmnt->data;
sdla_t *card=(sdla_t*)wandev->priv;
if (!card) {
DEBUG_EVENT("%s: Error: Card pointer not associated with Device %s!\n",
__FUNCTION__,wandev->name);
return -EINVAL;
}
return sdla_get_hwinfo(hw_info,usr_port_mgmnt->port_no);
}
static int wanpipe_mgmnt_stop_port(wanpipe_wandev_t *wdev, wan_device_t *wandev, port_management_struct_t *usr_port_mgmnt)
{
/* Bring down all network interfaces */
return wan_device_shutdown(wandev, NULL);
}
static int wanpipe_mgmnt_start_port(wanpipe_wandev_t *wdev, wan_device_t *wandev, port_management_struct_t *usr_port_mgmnt)
{
int err=-EINVAL;
int i;
wanpipe_port_cfg_t *port_cfg=wandev->port_cfg;
if (!wandev->port_cfg) {
/* No configuration present */
return -EINVAL;
}
err=wan_device_setup(wandev, &port_cfg->wandev_conf, 0);
if (err) {
DEBUG_EVENT("%s: Error: Failed to configure device\n",
__FUNCTION__);
return err;
}
for (i = 0; i < port_cfg->num_of_ifs; i++) {
err=wan_device_new_if (wandev, &port_cfg->if_cfg[i], 0);
if (err) {
DEBUG_EVENT("%s: Error: Failed to configure interface %i\n",
__FUNCTION__,i);
return err;
}
}
err=-ENODEV;
for (i = 0; i < port_cfg->num_of_ifs; i++) {
netdevice_t *dev = wan_dev_get_by_name(port_cfg->if_cfg[i].name);
if (dev) {
dev_put(dev);
rtnl_lock();
#if defined(KERN_DEV_CHG_FLAG_UPDATE) && KERN_DEV_CHG_FLAG_UPDATE > 0
err=dev_change_flags(dev,(dev->flags|IFF_UP),NULL);
#else
err=dev_change_flags(dev,(dev->flags|IFF_UP));
#endif
rtnl_unlock();
} else {
err=-ENODEV;
}
if (err) {
break;
}
}
/* Bring up all network interfaces */
return err;
}
static int wanpipe_mgmnt_stop_port_if(wanpipe_wandev_t *wdev, wan_device_t *wandev, char * if_name)
{
return wan_device_del_if (wandev, if_name, 0);
}
static int wanpipe_mgmnt_start_port_if(wanpipe_wandev_t *wdev, wan_device_t *wandev, unsigned short if_no)
{
int err=-EINVAL;
int i;
wanpipe_port_cfg_t *port_cfg=wandev->port_cfg;
if (!wandev->port_cfg) {
/* No configuration present */
return -EINVAL;
}
#if 0
err=wan_device_setup(wandev, &port_cfg->wandev_conf, 0);
if (err) {
DEBUG_EVENT("%s: Error: Failed to configure device\n",
__FUNCTION__);
return err;
}
#endif
for (i = 0; i < port_cfg->num_of_ifs; i++) {
if (i==if_no-1) {
err=wan_device_new_if (wandev, &port_cfg->if_cfg[i], 0);
if (err) {
DEBUG_EVENT("%s: Error: Failed to configure interface %i\n",
__FUNCTION__,i);
return err;
}
}
}
err=-ENODEV;
for (i = 0; i < port_cfg->num_of_ifs; i++) {
if (i==if_no-1) {
netdevice_t *dev = wan_dev_get_by_name(port_cfg->if_cfg[i].name);
if (dev) {
dev_put(dev);
rtnl_lock();
#if defined(KERN_DEV_CHG_FLAG_UPDATE) && KERN_DEV_CHG_FLAG_UPDATE > 0
err=dev_change_flags(dev,(dev->flags|IFF_UP),NULL);
#else
err=dev_change_flags(dev,(dev->flags|IFF_UP));
#endif
rtnl_unlock();
} else {
err=-ENODEV;
}
if (err) {
break;
}
}
}
return err;
}