2014-07-05 01:34:38 +04:00
/* Copyright 2011-2014 Autronica Fire and Security AS
2013-10-31 00:10:47 +04:00
*
* 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 .
*
* Author ( s ) :
2014-07-05 01:34:38 +04:00
* 2011 - 2014 Arvid Brodin , arvid . brodin @ alten . se
2013-10-31 00:10:47 +04:00
*
* This file contains device methods for creating , using and destroying
* virtual HSR devices .
*/
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/etherdevice.h>
# include <linux/rtnetlink.h>
# include <linux/pkt_sched.h>
# include "hsr_device.h"
2014-07-05 01:35:24 +04:00
# include "hsr_slave.h"
2013-10-31 00:10:47 +04:00
# include "hsr_framereg.h"
# include "hsr_main.h"
2014-07-05 01:41:03 +04:00
# include "hsr_forward.h"
2013-10-31 00:10:47 +04:00
static bool is_admin_up ( struct net_device * dev )
{
return dev & & ( dev - > flags & IFF_UP ) ;
}
static bool is_slave_up ( struct net_device * dev )
{
return dev & & is_admin_up ( dev ) & & netif_oper_up ( dev ) ;
}
static void __hsr_set_operstate ( struct net_device * dev , int transition )
{
write_lock_bh ( & dev_base_lock ) ;
if ( dev - > operstate ! = transition ) {
dev - > operstate = transition ;
write_unlock_bh ( & dev_base_lock ) ;
netdev_state_change ( dev ) ;
} else {
write_unlock_bh ( & dev_base_lock ) ;
}
}
2014-07-05 01:38:05 +04:00
static void hsr_set_operstate ( struct hsr_port * master , bool has_carrier )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:38:05 +04:00
if ( ! is_admin_up ( master - > dev ) ) {
__hsr_set_operstate ( master - > dev , IF_OPER_DOWN ) ;
2013-10-31 00:10:47 +04:00
return ;
}
2014-07-05 01:36:40 +04:00
if ( has_carrier )
2014-07-05 01:38:05 +04:00
__hsr_set_operstate ( master - > dev , IF_OPER_UP ) ;
2013-10-31 00:10:47 +04:00
else
2014-07-05 01:38:05 +04:00
__hsr_set_operstate ( master - > dev , IF_OPER_LOWERLAYERDOWN ) ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:38:05 +04:00
static bool hsr_check_carrier ( struct hsr_port * master )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:38:05 +04:00
struct hsr_port * port ;
2014-07-05 01:36:40 +04:00
bool has_carrier ;
2014-07-05 01:38:05 +04:00
has_carrier = false ;
rcu_read_lock ( ) ;
hsr_for_each_port ( master - > hsr , port )
if ( ( port - > type ! = HSR_PT_MASTER ) & & is_slave_up ( port - > dev ) ) {
has_carrier = true ;
break ;
}
rcu_read_unlock ( ) ;
2014-07-05 01:36:40 +04:00
if ( has_carrier )
2014-07-05 01:38:05 +04:00
netif_carrier_on ( master - > dev ) ;
2013-10-31 00:10:47 +04:00
else
2014-07-05 01:38:05 +04:00
netif_carrier_off ( master - > dev ) ;
2014-07-05 01:36:40 +04:00
return has_carrier ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:36:40 +04:00
static void hsr_check_announce ( struct net_device * hsr_dev ,
unsigned char old_operstate )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( hsr_dev ) ;
2013-10-31 00:10:47 +04:00
if ( ( hsr_dev - > operstate = = IF_OPER_UP ) & & ( old_operstate ! = IF_OPER_UP ) ) {
/* Went up */
2014-07-05 01:34:38 +04:00
hsr - > announce_count = 0 ;
hsr - > announce_timer . expires = jiffies +
2013-10-31 00:10:47 +04:00
msecs_to_jiffies ( HSR_ANNOUNCE_INTERVAL ) ;
2014-07-05 01:34:38 +04:00
add_timer ( & hsr - > announce_timer ) ;
2013-10-31 00:10:47 +04:00
}
if ( ( hsr_dev - > operstate ! = IF_OPER_UP ) & & ( old_operstate = = IF_OPER_UP ) )
/* Went down */
2014-07-05 01:34:38 +04:00
del_timer ( & hsr - > announce_timer ) ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:36:40 +04:00
void hsr_check_carrier_and_operstate ( struct hsr_priv * hsr )
{
2014-07-05 01:38:05 +04:00
struct hsr_port * master ;
2014-07-05 01:36:40 +04:00
unsigned char old_operstate ;
bool has_carrier ;
2014-07-05 01:38:05 +04:00
master = hsr_port_get_hsr ( hsr , HSR_PT_MASTER ) ;
2014-07-05 01:36:40 +04:00
/* netif_stacked_transfer_operstate() cannot be used here since
* it doesn ' t set IF_OPER_LOWERLAYERDOWN ( ? )
*/
2014-07-05 01:38:05 +04:00
old_operstate = master - > dev - > operstate ;
has_carrier = hsr_check_carrier ( master ) ;
hsr_set_operstate ( master , has_carrier ) ;
hsr_check_announce ( master - > dev , old_operstate ) ;
2014-07-05 01:36:40 +04:00
}
2014-07-05 01:34:38 +04:00
int hsr_get_max_mtu ( struct hsr_priv * hsr )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:37:27 +04:00
unsigned int mtu_max ;
2014-07-05 01:38:05 +04:00
struct hsr_port * port ;
2014-07-05 01:37:27 +04:00
mtu_max = ETH_DATA_LEN ;
rcu_read_lock ( ) ;
2014-07-05 01:38:05 +04:00
hsr_for_each_port ( hsr , port )
if ( port - > type ! = HSR_PT_MASTER )
mtu_max = min ( port - > dev - > mtu , mtu_max ) ;
2014-07-05 01:37:27 +04:00
rcu_read_unlock ( ) ;
if ( mtu_max < HSR_HLEN )
return 0 ;
2014-07-05 01:34:38 +04:00
return mtu_max - HSR_HLEN ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:37:27 +04:00
2013-10-31 00:10:47 +04:00
static int hsr_dev_change_mtu ( struct net_device * dev , int new_mtu )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2014-07-05 01:38:05 +04:00
struct hsr_port * master ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( dev ) ;
2014-07-05 01:38:05 +04:00
master = hsr_port_get_hsr ( hsr , HSR_PT_MASTER ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
if ( new_mtu > hsr_get_max_mtu ( hsr ) ) {
2014-07-05 01:38:05 +04:00
netdev_info ( master - > dev , " A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets). \n " ,
2014-07-05 01:34:38 +04:00
HSR_HLEN ) ;
2013-10-31 00:10:47 +04:00
return - EINVAL ;
}
dev - > mtu = new_mtu ;
return 0 ;
}
static int hsr_dev_open ( struct net_device * dev )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2014-07-05 01:38:05 +04:00
struct hsr_port * port ;
char designation ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( dev ) ;
2014-07-05 01:38:05 +04:00
designation = ' \0 ' ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:38:05 +04:00
rcu_read_lock ( ) ;
hsr_for_each_port ( hsr , port ) {
if ( port - > type = = HSR_PT_MASTER )
continue ;
switch ( port - > type ) {
case HSR_PT_SLAVE_A :
designation = ' A ' ;
break ;
case HSR_PT_SLAVE_B :
designation = ' B ' ;
break ;
default :
designation = ' ? ' ;
}
if ( ! is_slave_up ( port - > dev ) )
netdev_warn ( dev , " Slave %c (%s) is not up; please bring it up to get a fully working HSR network \n " ,
designation , port - > dev - > name ) ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:38:05 +04:00
rcu_read_unlock ( ) ;
if ( designation = = ' \0 ' )
netdev_warn ( dev , " No slave devices configured \n " ) ;
2013-10-31 00:10:47 +04:00
return 0 ;
}
2014-07-05 01:38:05 +04:00
2013-10-31 00:10:47 +04:00
static int hsr_dev_close ( struct net_device * dev )
{
2014-07-05 01:38:05 +04:00
/* Nothing to do here. */
2013-10-31 00:10:47 +04:00
return 0 ;
}
2014-07-05 01:38:57 +04:00
static netdev_features_t hsr_features_recompute ( struct hsr_priv * hsr ,
netdev_features_t features )
{
netdev_features_t mask ;
struct hsr_port * port ;
mask = features ;
/* Mask out all features that, if supported by one device, should be
* enabled for all devices ( see NETIF_F_ONE_FOR_ALL ) .
*
* Anything that ' s off in mask will not be enabled - so only things
* that were in features originally , and also is in NETIF_F_ONE_FOR_ALL ,
* may become enabled .
*/
features & = ~ NETIF_F_ONE_FOR_ALL ;
hsr_for_each_port ( hsr , port )
features = netdev_increment_features ( features ,
port - > dev - > features ,
mask ) ;
return features ;
}
static netdev_features_t hsr_fix_features ( struct net_device * dev ,
netdev_features_t features )
{
struct hsr_priv * hsr = netdev_priv ( dev ) ;
return hsr_features_recompute ( hsr , features ) ;
}
2013-10-31 00:10:47 +04:00
static int hsr_dev_xmit ( struct sk_buff * skb , struct net_device * dev )
{
2014-07-05 01:41:03 +04:00
struct hsr_priv * hsr = netdev_priv ( dev ) ;
2014-07-05 01:38:05 +04:00
struct hsr_port * master ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:38:05 +04:00
master = hsr_port_get_hsr ( hsr , HSR_PT_MASTER ) ;
2014-07-05 01:41:03 +04:00
skb - > dev = master - > dev ;
hsr_forward_skb ( skb , master ) ;
2013-10-31 00:10:47 +04:00
return NETDEV_TX_OK ;
}
static const struct header_ops hsr_header_ops = {
2014-07-05 01:41:03 +04:00
. create = eth_header ,
2013-10-31 00:10:47 +04:00
. parse = eth_header_parse ,
} ;
/* HSR:2010 supervision frames should be padded so that the whole frame,
* including headers and FCS , is 64 bytes ( without VLAN ) .
*/
static int hsr_pad ( int size )
{
2014-07-05 01:34:38 +04:00
const int min_size = ETH_ZLEN - HSR_HLEN - ETH_HLEN ;
2013-10-31 00:10:47 +04:00
if ( size > = min_size )
return size ;
return min_size ;
}
2014-07-05 01:41:03 +04:00
static void send_hsr_supervision_frame ( struct hsr_port * master , u8 type )
2013-10-31 00:10:47 +04:00
{
struct sk_buff * skb ;
int hlen , tlen ;
struct hsr_sup_tag * hsr_stag ;
struct hsr_sup_payload * hsr_sp ;
unsigned long irqflags ;
2014-07-05 01:38:05 +04:00
hlen = LL_RESERVED_SPACE ( master - > dev ) ;
tlen = master - > dev - > needed_tailroom ;
2013-10-31 00:10:47 +04:00
skb = alloc_skb ( hsr_pad ( sizeof ( struct hsr_sup_payload ) ) + hlen + tlen ,
GFP_ATOMIC ) ;
if ( skb = = NULL )
return ;
skb_reserve ( skb , hlen ) ;
2014-07-05 01:38:05 +04:00
skb - > dev = master - > dev ;
2013-10-31 00:10:47 +04:00
skb - > protocol = htons ( ETH_P_PRP ) ;
skb - > priority = TC_PRIO_CONTROL ;
2014-07-05 01:41:03 +04:00
if ( dev_hard_header ( skb , skb - > dev , ETH_P_PRP ,
master - > hsr - > sup_multicast_addr ,
skb - > dev - > dev_addr , skb - > len ) < = 0 )
2013-10-31 00:10:47 +04:00
goto out ;
2014-07-05 01:41:03 +04:00
skb_reset_mac_header ( skb ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
hsr_stag = ( typeof ( hsr_stag ) ) skb_put ( skb , sizeof ( * hsr_stag ) ) ;
2013-10-31 00:10:47 +04:00
set_hsr_stag_path ( hsr_stag , 0xf ) ;
set_hsr_stag_HSR_Ver ( hsr_stag , 0 ) ;
2014-07-05 01:41:03 +04:00
spin_lock_irqsave ( & master - > hsr - > seqnr_lock , irqflags ) ;
hsr_stag - > sequence_nr = htons ( master - > hsr - > sequence_nr ) ;
master - > hsr - > sequence_nr + + ;
spin_unlock_irqrestore ( & master - > hsr - > seqnr_lock , irqflags ) ;
2013-10-31 00:10:47 +04:00
hsr_stag - > HSR_TLV_Type = type ;
hsr_stag - > HSR_TLV_Length = 12 ;
/* Payload: MacAddressA */
hsr_sp = ( typeof ( hsr_sp ) ) skb_put ( skb , sizeof ( * hsr_sp ) ) ;
2014-07-05 01:38:05 +04:00
ether_addr_copy ( hsr_sp - > MacAddressA , master - > dev - > dev_addr ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
hsr_forward_skb ( skb , master ) ;
2013-10-31 00:10:47 +04:00
return ;
out :
2015-11-21 13:34:12 +03:00
WARN_ONCE ( 1 , " HSR: Could not send supervision frame \n " ) ;
2013-10-31 00:10:47 +04:00
kfree_skb ( skb ) ;
}
/* Announce (supervision frame) timer function
*/
static void hsr_announce ( unsigned long data )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2014-07-05 01:38:05 +04:00
struct hsr_port * master ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = ( struct hsr_priv * ) data ;
2014-07-05 01:41:03 +04:00
rcu_read_lock ( ) ;
2014-07-05 01:38:05 +04:00
master = hsr_port_get_hsr ( hsr , HSR_PT_MASTER ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
if ( hsr - > announce_count < 3 ) {
2014-07-05 01:41:03 +04:00
send_hsr_supervision_frame ( master , HSR_TLV_ANNOUNCE ) ;
2014-07-05 01:34:38 +04:00
hsr - > announce_count + + ;
2013-10-31 00:10:47 +04:00
} else {
2014-07-05 01:41:03 +04:00
send_hsr_supervision_frame ( master , HSR_TLV_LIFE_CHECK ) ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:34:38 +04:00
if ( hsr - > announce_count < 3 )
hsr - > announce_timer . expires = jiffies +
2013-10-31 00:10:47 +04:00
msecs_to_jiffies ( HSR_ANNOUNCE_INTERVAL ) ;
else
2014-07-05 01:34:38 +04:00
hsr - > announce_timer . expires = jiffies +
2013-10-31 00:10:47 +04:00
msecs_to_jiffies ( HSR_LIFE_CHECK_INTERVAL ) ;
2014-07-05 01:38:05 +04:00
if ( is_admin_up ( master - > dev ) )
2014-07-05 01:34:38 +04:00
add_timer ( & hsr - > announce_timer ) ;
2014-07-05 01:41:03 +04:00
rcu_read_unlock ( ) ;
2013-10-31 00:10:47 +04:00
}
/* According to comments in the declaration of struct net_device, this function
* is " Called from unregister, can be used to call free_netdev " . Ok then . . .
*/
static void hsr_dev_destroy ( struct net_device * hsr_dev )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2014-07-05 01:38:05 +04:00
struct hsr_port * port ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( hsr_dev ) ;
2015-02-27 23:26:03 +03:00
rtnl_lock ( ) ;
2014-07-05 01:38:05 +04:00
hsr_for_each_port ( hsr , port )
hsr_del_port ( port ) ;
2015-02-27 23:26:03 +03:00
rtnl_unlock ( ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:35:47 +04:00
del_timer_sync ( & hsr - > prune_timer ) ;
del_timer_sync ( & hsr - > announce_timer ) ;
2014-07-05 01:38:05 +04:00
synchronize_rcu ( ) ;
free_netdev ( hsr_dev ) ;
2013-10-31 00:10:47 +04:00
}
static const struct net_device_ops hsr_device_ops = {
. ndo_change_mtu = hsr_dev_change_mtu ,
. ndo_open = hsr_dev_open ,
. ndo_stop = hsr_dev_close ,
. ndo_start_xmit = hsr_dev_xmit ,
2014-07-05 01:38:57 +04:00
. ndo_fix_features = hsr_fix_features ,
2013-10-31 00:10:47 +04:00
} ;
2014-07-05 01:39:42 +04:00
static struct device_type hsr_type = {
. name = " hsr " ,
} ;
2013-10-31 00:10:47 +04:00
void hsr_dev_setup ( struct net_device * dev )
{
random_ether_addr ( dev - > dev_addr ) ;
ether_setup ( dev ) ;
2014-07-05 01:39:42 +04:00
dev - > header_ops = & hsr_header_ops ;
dev - > netdev_ops = & hsr_device_ops ;
SET_NETDEV_DEVTYPE ( dev , & hsr_type ) ;
2015-08-18 11:30:45 +03:00
dev - > priv_flags | = IFF_NO_QUEUE ;
2013-10-31 00:10:47 +04:00
dev - > destructor = hsr_dev_destroy ;
2014-07-05 01:38:57 +04:00
dev - > hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
NETIF_F_HW_VLAN_CTAG_TX ;
dev - > features = dev - > hw_features ;
/* Prevent recursive tx locking */
dev - > features | = NETIF_F_LLTX ;
/* VLAN on top of HSR needs testing and probably some work on
* hsr_header_create ( ) etc .
*/
dev - > features | = NETIF_F_VLAN_CHALLENGED ;
2014-07-05 01:39:42 +04:00
/* Not sure about this. Taken from bridge code. netdev_features.h says
* it means " Does not change network namespaces " .
*/
dev - > features | = NETIF_F_NETNS_LOCAL ;
2013-10-31 00:10:47 +04:00
}
/* Return true if dev is a HSR master; return false otherwise.
*/
2014-07-05 01:38:05 +04:00
inline bool is_hsr_master ( struct net_device * dev )
2013-10-31 00:10:47 +04:00
{
return ( dev - > netdev_ops - > ndo_start_xmit = = hsr_dev_xmit ) ;
}
/* Default multicast address for HSR Supervision frames */
2014-02-18 22:37:20 +04:00
static const unsigned char def_multicast_addr [ ETH_ALEN ] __aligned ( 2 ) = {
2013-10-31 00:10:47 +04:00
0x01 , 0x15 , 0x4e , 0x00 , 0x01 , 0x00
} ;
int hsr_dev_finalize ( struct net_device * hsr_dev , struct net_device * slave [ 2 ] ,
unsigned char multicast_spec )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2014-07-05 01:38:05 +04:00
struct hsr_port * port ;
2013-10-31 00:10:47 +04:00
int res ;
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( hsr_dev ) ;
2014-07-05 01:38:05 +04:00
INIT_LIST_HEAD ( & hsr - > ports ) ;
2014-07-05 01:34:38 +04:00
INIT_LIST_HEAD ( & hsr - > node_db ) ;
INIT_LIST_HEAD ( & hsr - > self_node_db ) ;
2014-07-05 01:37:27 +04:00
ether_addr_copy ( hsr_dev - > dev_addr , slave [ 0 ] - > dev_addr ) ;
/* Make sure we recognize frames from ourselves in hsr_rcv() */
res = hsr_create_self_node ( & hsr - > self_node_db , hsr_dev - > dev_addr ,
slave [ 1 ] - > dev_addr ) ;
if ( res < 0 )
return res ;
2014-07-05 01:34:38 +04:00
spin_lock_init ( & hsr - > seqnr_lock ) ;
2013-10-31 00:10:47 +04:00
/* Overflow soon to find bugs easier: */
2014-07-05 01:41:03 +04:00
hsr - > sequence_nr = HSR_SEQNR_START ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
init_timer ( & hsr - > announce_timer ) ;
hsr - > announce_timer . function = hsr_announce ;
hsr - > announce_timer . data = ( unsigned long ) hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:35:47 +04:00
init_timer ( & hsr - > prune_timer ) ;
hsr - > prune_timer . function = hsr_prune_nodes ;
hsr - > prune_timer . data = ( unsigned long ) hsr ;
2014-07-05 01:34:38 +04:00
ether_addr_copy ( hsr - > sup_multicast_addr , def_multicast_addr ) ;
hsr - > sup_multicast_addr [ ETH_ALEN - 1 ] = multicast_spec ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:38:05 +04:00
/* FIXME: should I modify the value of these?
*
* - hsr_dev - > flags - i . e .
* IFF_MASTER / SLAVE ?
* - hsr_dev - > priv_flags - i . e .
* IFF_EBRIDGE ?
* IFF_TX_SKB_SHARING ?
* IFF_HSR_MASTER / SLAVE ?
*/
2013-10-31 00:10:47 +04:00
/* Make sure the 1st call to netif_carrier_on() gets through */
netif_carrier_off ( hsr_dev ) ;
2014-07-05 01:38:05 +04:00
res = hsr_add_port ( hsr , hsr_dev , HSR_PT_MASTER ) ;
2013-10-31 00:10:47 +04:00
if ( res )
2014-07-05 01:37:27 +04:00
return res ;
2014-07-05 01:38:05 +04:00
res = register_netdevice ( hsr_dev ) ;
2014-07-05 01:37:27 +04:00
if ( res )
2014-07-05 01:38:05 +04:00
goto fail ;
res = hsr_add_port ( hsr , slave [ 0 ] , HSR_PT_SLAVE_A ) ;
if ( res )
goto fail ;
res = hsr_add_port ( hsr , slave [ 1 ] , HSR_PT_SLAVE_B ) ;
if ( res )
goto fail ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:35:47 +04:00
hsr - > prune_timer . expires = jiffies + msecs_to_jiffies ( PRUNE_PERIOD ) ;
add_timer ( & hsr - > prune_timer ) ;
2013-10-31 00:10:47 +04:00
return 0 ;
2014-07-05 01:38:05 +04:00
fail :
hsr_for_each_port ( hsr , port )
hsr_del_port ( port ) ;
return res ;
2013-10-31 00:10:47 +04:00
}