2005-04-17 02:20:36 +04:00
/*
* Generic HDLC support routines for Linux
* HDLC Ethernet emulation support
*
2006-09-27 01:23:45 +04:00
* Copyright ( C ) 2002 - 2006 Krzysztof Halasa < khc @ pm . waw . pl >
2005-04-17 02:20:36 +04:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/errno.h>
# include <linux/if_arp.h>
# include <linux/init.h>
# include <linux/skbuff.h>
# include <linux/pkt_sched.h>
# include <linux/random.h>
# include <linux/inetdevice.h>
# include <linux/lapb.h>
# include <linux/rtnetlink.h>
# include <linux/etherdevice.h>
# include <linux/hdlc.h>
2006-09-27 01:23:45 +04:00
static int raw_eth_ioctl ( struct net_device * dev , struct ifreq * ifr ) ;
2005-04-17 02:20:36 +04:00
static int eth_tx ( struct sk_buff * skb , struct net_device * dev )
{
int pad = ETH_ZLEN - skb - > len ;
if ( pad > 0 ) { /* Pad the frame with zeros */
int len = skb - > len ;
if ( skb_tailroom ( skb ) < pad )
if ( pskb_expand_head ( skb , 0 , pad , GFP_ATOMIC ) ) {
hdlc_stats ( dev ) - > tx_dropped + + ;
dev_kfree_skb ( skb ) ;
return 0 ;
}
skb_put ( skb , pad ) ;
memset ( skb - > data + len , 0 , pad ) ;
}
return dev_to_hdlc ( dev ) - > xmit ( skb , dev ) ;
}
2006-09-27 01:23:45 +04:00
static struct hdlc_proto proto = {
. type_trans = eth_type_trans ,
. ioctl = raw_eth_ioctl ,
. module = THIS_MODULE ,
} ;
static int raw_eth_ioctl ( struct net_device * dev , struct ifreq * ifr )
2005-04-17 02:20:36 +04:00
{
raw_hdlc_proto __user * raw_s = ifr - > ifr_settings . ifs_ifsu . raw_hdlc ;
const size_t size = sizeof ( raw_hdlc_proto ) ;
raw_hdlc_proto new_settings ;
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
int result ;
2007-12-22 20:52:52 +03:00
int ( * old_ch_mtu ) ( struct net_device * , int ) ;
2005-04-17 02:20:36 +04:00
int old_qlen ;
switch ( ifr - > ifr_settings . type ) {
case IF_GET_PROTO :
2006-09-27 01:23:45 +04:00
if ( dev_to_hdlc ( dev ) - > proto ! = & proto )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
ifr - > ifr_settings . type = IF_PROTO_HDLC_ETH ;
if ( ifr - > ifr_settings . size < size ) {
ifr - > ifr_settings . size = size ; /* data size wanted */
return - ENOBUFS ;
}
2006-09-27 01:23:45 +04:00
if ( copy_to_user ( raw_s , hdlc - > state , size ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
case IF_PROTO_HDLC_ETH :
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
if ( dev - > flags & IFF_UP )
return - EBUSY ;
if ( copy_from_user ( & new_settings , raw_s , size ) )
return - EFAULT ;
if ( new_settings . encoding = = ENCODING_DEFAULT )
new_settings . encoding = ENCODING_NRZ ;
if ( new_settings . parity = = PARITY_DEFAULT )
new_settings . parity = PARITY_CRC16_PR1_CCITT ;
result = hdlc - > attach ( dev , new_settings . encoding ,
new_settings . parity ) ;
if ( result )
return result ;
2008-02-02 00:37:12 +03:00
result = attach_hdlc_protocol ( dev , & proto ,
2006-09-27 01:23:45 +04:00
sizeof ( raw_hdlc_proto ) ) ;
if ( result )
return result ;
memcpy ( hdlc - > state , & new_settings , size ) ;
2005-04-17 02:20:36 +04:00
dev - > hard_start_xmit = eth_tx ;
old_ch_mtu = dev - > change_mtu ;
old_qlen = dev - > tx_queue_len ;
ether_setup ( dev ) ;
dev - > change_mtu = old_ch_mtu ;
dev - > tx_queue_len = old_qlen ;
memcpy ( dev - > dev_addr , " \x00 \x01 " , 2 ) ;
get_random_bytes ( dev - > dev_addr + 2 , ETH_ALEN - 2 ) ;
2006-07-22 01:41:01 +04:00
netif_dormant_off ( dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
return - EINVAL ;
}
2006-09-27 01:23:45 +04:00
static int __init mod_init ( void )
{
register_hdlc_protocol ( & proto ) ;
return 0 ;
}
static void __exit mod_exit ( void )
{
unregister_hdlc_protocol ( & proto ) ;
}
module_init ( mod_init ) ;
module_exit ( mod_exit ) ;
MODULE_AUTHOR ( " Krzysztof Halasa <khc@pm.waw.pl> " ) ;
MODULE_DESCRIPTION ( " Ethernet encapsulation support for generic HDLC " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;