612 lines
14 KiB
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;
|
|
}
|
|
|
|
|