2005-04-16 15:20:36 -07:00
/*
* Things to sort out :
*
* o tbusy handling
* o allow users to set the parameters
* o sync / async switching ?
*
* Note : This does _not_ implement CCITT X .25 asynchronous framing
* recommendations . Its primarily for testing purposes . If you wanted
* to do CCITT then in theory all you need is to nick the HDLC async
* checksum routines from ppp . c
* Changes :
*
* 2000 - 10 - 29 Henner Eisen lapb_data_indication ( ) return status .
*/
# include <linux/module.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <linux/bitops.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/in.h>
# include <linux/tty.h>
# include <linux/errno.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/if_arp.h>
# include <linux/x25.h>
# include <linux/lapb.h>
# include <linux/init.h>
# include "x25_asy.h"
# include <net/x25device.h>
static struct net_device * * x25_asy_devs ;
static int x25_asy_maxdev = SL_NRUNIT ;
module_param ( x25_asy_maxdev , int , 0 ) ;
MODULE_LICENSE ( " GPL " ) ;
static int x25_asy_esc ( unsigned char * p , unsigned char * d , int len ) ;
static void x25_asy_unesc ( struct x25_asy * sl , unsigned char c ) ;
static void x25_asy_setup ( struct net_device * dev ) ;
/* Find a free X.25 channel, and link in this `tty' line. */
static struct x25_asy * x25_asy_alloc ( void )
{
struct net_device * dev = NULL ;
struct x25_asy * sl ;
int i ;
if ( x25_asy_devs = = NULL )
return NULL ; /* Master array missing ! */
for ( i = 0 ; i < x25_asy_maxdev ; i + + ) {
dev = x25_asy_devs [ i ] ;
/* Not allocated ? */
if ( dev = = NULL )
break ;
sl = dev - > priv ;
/* Not in use ? */
if ( ! test_and_set_bit ( SLF_INUSE , & sl - > flags ) )
return sl ;
}
/* Sorry, too many, all slots in use */
if ( i > = x25_asy_maxdev )
return NULL ;
/* If no channels are available, allocate one */
if ( ! dev ) {
char name [ IFNAMSIZ ] ;
sprintf ( name , " x25asy%d " , i ) ;
dev = alloc_netdev ( sizeof ( struct x25_asy ) ,
name , x25_asy_setup ) ;
if ( ! dev )
return NULL ;
/* Initialize channel control data */
sl = dev - > priv ;
dev - > base_addr = i ;
/* register device so that it can be ifconfig'ed */
if ( register_netdev ( dev ) = = 0 ) {
/* (Re-)Set the INUSE bit. Very Important! */
set_bit ( SLF_INUSE , & sl - > flags ) ;
x25_asy_devs [ i ] = dev ;
return sl ;
} else {
printk ( " x25_asy_alloc() - register_netdev() failure. \n " ) ;
free_netdev ( dev ) ;
}
}
return NULL ;
}
/* Free an X.25 channel. */
static void x25_asy_free ( struct x25_asy * sl )
{
/* Free all X.25 frame buffers. */
2005-05-03 14:33:27 -07:00
kfree ( sl - > rbuff ) ;
2005-04-16 15:20:36 -07:00
sl - > rbuff = NULL ;
2005-05-03 14:33:27 -07:00
kfree ( sl - > xbuff ) ;
2005-04-16 15:20:36 -07:00
sl - > xbuff = NULL ;
if ( ! test_and_clear_bit ( SLF_INUSE , & sl - > flags ) ) {
printk ( " %s: x25_asy_free for already free unit. \n " , sl - > dev - > name ) ;
}
}
static int x25_asy_change_mtu ( struct net_device * dev , int newmtu )
{
struct x25_asy * sl = dev - > priv ;
unsigned char * xbuff , * rbuff ;
int len = 2 * newmtu ;
xbuff = ( unsigned char * ) kmalloc ( len + 4 , GFP_ATOMIC ) ;
rbuff = ( unsigned char * ) kmalloc ( len + 4 , GFP_ATOMIC ) ;
if ( xbuff = = NULL | | rbuff = = NULL )
{
printk ( " %s: unable to grow X.25 buffers, MTU change cancelled. \n " ,
dev - > name ) ;
2005-05-03 14:33:27 -07:00
kfree ( xbuff ) ;
kfree ( rbuff ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
spin_lock_bh ( & sl - > lock ) ;
xbuff = xchg ( & sl - > xbuff , xbuff ) ;
if ( sl - > xleft ) {
if ( sl - > xleft < = len ) {
memcpy ( sl - > xbuff , sl - > xhead , sl - > xleft ) ;
} else {
sl - > xleft = 0 ;
sl - > stats . tx_dropped + + ;
}
}
sl - > xhead = sl - > xbuff ;
rbuff = xchg ( & sl - > rbuff , rbuff ) ;
if ( sl - > rcount ) {
if ( sl - > rcount < = len ) {
memcpy ( sl - > rbuff , rbuff , sl - > rcount ) ;
} else {
sl - > rcount = 0 ;
sl - > stats . rx_over_errors + + ;
set_bit ( SLF_ERROR , & sl - > flags ) ;
}
}
dev - > mtu = newmtu ;
sl - > buffsize = len ;
spin_unlock_bh ( & sl - > lock ) ;
2005-05-03 14:33:27 -07:00
kfree ( xbuff ) ;
kfree ( rbuff ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/* Set the "sending" flag. This must be atomic, hence the ASM. */
static inline void x25_asy_lock ( struct x25_asy * sl )
{
netif_stop_queue ( sl - > dev ) ;
}
/* Clear the "sending" flag. This must be atomic, hence the ASM. */
static inline void x25_asy_unlock ( struct x25_asy * sl )
{
netif_wake_queue ( sl - > dev ) ;
}
/* Send one completely decapsulated IP datagram to the IP layer. */
static void x25_asy_bump ( struct x25_asy * sl )
{
struct sk_buff * skb ;
int count ;
int err ;
count = sl - > rcount ;
sl - > stats . rx_bytes + = count ;
skb = dev_alloc_skb ( count + 1 ) ;
if ( skb = = NULL )
{
printk ( " %s: memory squeeze, dropping packet. \n " , sl - > dev - > name ) ;
sl - > stats . rx_dropped + + ;
return ;
}
skb_push ( skb , 1 ) ; /* LAPB internal control */
memcpy ( skb_put ( skb , count ) , sl - > rbuff , count ) ;
skb - > protocol = x25_type_trans ( skb , sl - > dev ) ;
if ( ( err = lapb_data_received ( skb - > dev , skb ) ) ! = LAPB_OK )
{
kfree_skb ( skb ) ;
printk ( KERN_DEBUG " x25_asy: data received err - %d \n " , err ) ;
}
else
{
netif_rx ( skb ) ;
sl - > dev - > last_rx = jiffies ;
sl - > stats . rx_packets + + ;
}
}
/* Encapsulate one IP datagram and stuff into a TTY queue. */
static void x25_asy_encaps ( struct x25_asy * sl , unsigned char * icp , int len )
{
unsigned char * p ;
int actual , count , mtu = sl - > dev - > mtu ;
if ( len > mtu )
{ /* Sigh, shouldn't occur BUT ... */
len = mtu ;
printk ( " %s: truncating oversized transmit packet! \n " , sl - > dev - > name ) ;
sl - > stats . tx_dropped + + ;
x25_asy_unlock ( sl ) ;
return ;
}
p = icp ;
count = x25_asy_esc ( p , ( unsigned char * ) sl - > xbuff , len ) ;
/* Order of next two lines is *very* important.
* When we are sending a little amount of data ,
* the transfer may be completed inside driver . write ( )
* routine , because it ' s running with interrupts enabled .
* In this case we * never * got WRITE_WAKEUP event ,
* if we did not request it before write operation .
* 14 Oct 1994 Dmitry Gorodchanin .
*/
sl - > tty - > flags | = ( 1 < < TTY_DO_WRITE_WAKEUP ) ;
actual = sl - > tty - > driver - > write ( sl - > tty , sl - > xbuff , count ) ;
sl - > xleft = count - actual ;
sl - > xhead = sl - > xbuff + actual ;
/* VSV */
clear_bit ( SLF_OUTWAIT , & sl - > flags ) ; /* reset outfill flag */
}
/*
* Called by the driver when there ' s room for more data . If we have
* more packets to send , we send them here .
*/
static void x25_asy_write_wakeup ( struct tty_struct * tty )
{
int actual ;
struct x25_asy * sl = ( struct x25_asy * ) tty - > disc_data ;
/* First make sure we're connected. */
if ( ! sl | | sl - > magic ! = X25_ASY_MAGIC | | ! netif_running ( sl - > dev ) )
return ;
if ( sl - > xleft < = 0 )
{
/* Now serial buffer is almost free & we can start
* transmission of another packet */
sl - > stats . tx_packets + + ;
tty - > flags & = ~ ( 1 < < TTY_DO_WRITE_WAKEUP ) ;
x25_asy_unlock ( sl ) ;
return ;
}
actual = tty - > driver - > write ( tty , sl - > xhead , sl - > xleft ) ;
sl - > xleft - = actual ;
sl - > xhead + = actual ;
}
static void x25_asy_timeout ( struct net_device * dev )
{
struct x25_asy * sl = ( struct x25_asy * ) ( dev - > priv ) ;
spin_lock ( & sl - > lock ) ;
if ( netif_queue_stopped ( dev ) ) {
/* May be we must check transmitter timeout here ?
* 14 Oct 1994 Dmitry Gorodchanin .
*/
printk ( KERN_WARNING " %s: transmit timed out, %s? \n " , dev - > name ,
( sl - > tty - > driver - > chars_in_buffer ( sl - > tty ) | | sl - > xleft ) ?
" bad line quality " : " driver error " ) ;
sl - > xleft = 0 ;
sl - > tty - > flags & = ~ ( 1 < < TTY_DO_WRITE_WAKEUP ) ;
x25_asy_unlock ( sl ) ;
}
spin_unlock ( & sl - > lock ) ;
}
/* Encapsulate an IP datagram and kick it into a TTY queue. */
static int x25_asy_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct x25_asy * sl = ( struct x25_asy * ) ( dev - > priv ) ;
int err ;
if ( ! netif_running ( sl - > dev ) ) {
printk ( " %s: xmit call when iface is down \n " , dev - > name ) ;
kfree_skb ( skb ) ;
return 0 ;
}
switch ( skb - > data [ 0 ] )
{
case 0x00 : break ;
case 0x01 : /* Connection request .. do nothing */
if ( ( err = lapb_connect_request ( dev ) ) ! = LAPB_OK )
printk ( KERN_ERR " x25_asy: lapb_connect_request error - %d \n " , err ) ;
kfree_skb ( skb ) ;
return 0 ;
case 0x02 : /* Disconnect request .. do nothing - hang up ?? */
if ( ( err = lapb_disconnect_request ( dev ) ) ! = LAPB_OK )
printk ( KERN_ERR " x25_asy: lapb_disconnect_request error - %d \n " , err ) ;
default :
kfree_skb ( skb ) ;
return 0 ;
}
skb_pull ( skb , 1 ) ; /* Remove control byte */
/*
* If we are busy already - too bad . We ought to be able
* to queue things at this point , to allow for a little
* frame buffer . Oh well . . .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* I hate queues in X .25 driver . May be it ' s efficient ,
* but for me latency is more important . ; )
* So , no queues !
* 14 Oct 1994 Dmitry Gorodchanin .
*/
if ( ( err = lapb_data_request ( dev , skb ) ) ! = LAPB_OK )
{
printk ( KERN_ERR " lapbeth: lapb_data_request error - %d \n " , err ) ;
kfree_skb ( skb ) ;
return 0 ;
}
return 0 ;
}
/*
* LAPB interface boilerplate
*/
/*
* Called when I frame data arrives . We did the work above - throw it
* at the net layer .
*/
static int x25_asy_data_indication ( struct net_device * dev , struct sk_buff * skb )
{
skb - > dev - > last_rx = jiffies ;
return netif_rx ( skb ) ;
}
/*
* Data has emerged from the LAPB protocol machine . We don ' t handle
* busy cases too well . Its tricky to see how to do this nicely -
* perhaps lapb should allow us to bounce this ?
*/
static void x25_asy_data_transmit ( struct net_device * dev , struct sk_buff * skb )
{
struct x25_asy * sl = dev - > priv ;
spin_lock ( & sl - > lock ) ;
if ( netif_queue_stopped ( sl - > dev ) | | sl - > tty = = NULL )
{
spin_unlock ( & sl - > lock ) ;
printk ( KERN_ERR " x25_asy: tbusy drop \n " ) ;
kfree_skb ( skb ) ;
return ;
}
/* We were not busy, so we are now... :-) */
if ( skb ! = NULL )
{
x25_asy_lock ( sl ) ;
sl - > stats . tx_bytes + = skb - > len ;
x25_asy_encaps ( sl , skb - > data , skb - > len ) ;
dev_kfree_skb ( skb ) ;
}
spin_unlock ( & sl - > lock ) ;
}
/*
* LAPB connection establish / down information .
*/
static void x25_asy_connected ( struct net_device * dev , int reason )
{
struct x25_asy * sl = dev - > priv ;
struct sk_buff * skb ;
unsigned char * ptr ;
if ( ( skb = dev_alloc_skb ( 1 ) ) = = NULL ) {
printk ( KERN_ERR " lapbeth: out of memory \n " ) ;
return ;
}
ptr = skb_put ( skb , 1 ) ;
* ptr = 0x01 ;
skb - > protocol = x25_type_trans ( skb , sl - > dev ) ;
netif_rx ( skb ) ;
sl - > dev - > last_rx = jiffies ;
}
static void x25_asy_disconnected ( struct net_device * dev , int reason )
{
struct x25_asy * sl = dev - > priv ;
struct sk_buff * skb ;
unsigned char * ptr ;
if ( ( skb = dev_alloc_skb ( 1 ) ) = = NULL ) {
printk ( KERN_ERR " x25_asy: out of memory \n " ) ;
return ;
}
ptr = skb_put ( skb , 1 ) ;
* ptr = 0x02 ;
skb - > protocol = x25_type_trans ( skb , sl - > dev ) ;
netif_rx ( skb ) ;
sl - > dev - > last_rx = jiffies ;
}
static struct lapb_register_struct x25_asy_callbacks = {
. connect_confirmation = x25_asy_connected ,
. connect_indication = x25_asy_connected ,
. disconnect_confirmation = x25_asy_disconnected ,
. disconnect_indication = x25_asy_disconnected ,
. data_indication = x25_asy_data_indication ,
. data_transmit = x25_asy_data_transmit ,
} ;
/* Open the low-level part of the X.25 channel. Easy! */
static int x25_asy_open ( struct net_device * dev )
{
struct x25_asy * sl = ( struct x25_asy * ) ( dev - > priv ) ;
unsigned long len ;
int err ;
if ( sl - > tty = = NULL )
return - ENODEV ;
/*
* Allocate the X .25 frame buffers :
*
* rbuff Receive buffer .
* xbuff Transmit buffer .
*/
len = dev - > mtu * 2 ;
sl - > rbuff = ( unsigned char * ) kmalloc ( len + 4 , GFP_KERNEL ) ;
if ( sl - > rbuff = = NULL ) {
goto norbuff ;
}
sl - > xbuff = ( unsigned char * ) kmalloc ( len + 4 , GFP_KERNEL ) ;
if ( sl - > xbuff = = NULL ) {
goto noxbuff ;
}
sl - > buffsize = len ;
sl - > rcount = 0 ;
sl - > xleft = 0 ;
sl - > flags & = ( 1 < < SLF_INUSE ) ; /* Clear ESCAPE & ERROR flags */
netif_start_queue ( dev ) ;
/*
* Now attach LAPB
*/
if ( ( err = lapb_register ( dev , & x25_asy_callbacks ) ) = = LAPB_OK )
return 0 ;
/* Cleanup */
kfree ( sl - > xbuff ) ;
noxbuff :
kfree ( sl - > rbuff ) ;
norbuff :
return - ENOMEM ;
}
/* Close the low-level part of the X.25 channel. Easy! */
static int x25_asy_close ( struct net_device * dev )
{
struct x25_asy * sl = ( struct x25_asy * ) ( dev - > priv ) ;
int err ;
spin_lock ( & sl - > lock ) ;
if ( sl - > tty )
sl - > tty - > flags & = ~ ( 1 < < TTY_DO_WRITE_WAKEUP ) ;
netif_stop_queue ( dev ) ;
sl - > rcount = 0 ;
sl - > xleft = 0 ;
if ( ( err = lapb_unregister ( dev ) ) ! = LAPB_OK )
printk ( KERN_ERR " x25_asy_close: lapb_unregister error -%d \n " , err ) ;
spin_unlock ( & sl - > lock ) ;
return 0 ;
}
static int x25_asy_receive_room ( struct tty_struct * tty )
{
return 65536 ; /* We can handle an infinite amount of data. :-) */
}
/*
* Handle the ' receiver data ready ' interrupt .
* This function is called by the ' tty_io ' module in the kernel when
* a block of X .25 data has been received , which can now be decapsulated
* and sent on to some IP layer for further processing .
*/
static void x25_asy_receive_buf ( struct tty_struct * tty , const unsigned char * cp , char * fp , int count )
{
struct x25_asy * sl = ( struct x25_asy * ) tty - > disc_data ;
if ( ! sl | | sl - > magic ! = X25_ASY_MAGIC | | ! netif_running ( sl - > dev ) )
return ;
/* Read the characters out of the buffer */
while ( count - - ) {
if ( fp & & * fp + + ) {
if ( ! test_and_set_bit ( SLF_ERROR , & sl - > flags ) ) {
sl - > stats . rx_errors + + ;
}
cp + + ;
continue ;
}
x25_asy_unesc ( sl , * cp + + ) ;
}
}
/*
* Open the high - level part of the X .25 channel .
* This function is called by the TTY module when the
* X .25 line discipline is called for . Because we are
* sure the tty line exists , we only have to link it to
* a free X .25 channel . . .
*/
static int x25_asy_open_tty ( struct tty_struct * tty )
{
struct x25_asy * sl = ( struct x25_asy * ) tty - > disc_data ;
int err ;
/* First make sure we're not already connected. */
if ( sl & & sl - > magic = = X25_ASY_MAGIC ) {
return - EEXIST ;
}
/* OK. Find a free X.25 channel to use. */
if ( ( sl = x25_asy_alloc ( ) ) = = NULL ) {
return - ENFILE ;
}
sl - > tty = tty ;
tty - > disc_data = sl ;
if ( tty - > driver - > flush_buffer ) {
tty - > driver - > flush_buffer ( tty ) ;
}
if ( tty - > ldisc . flush_buffer ) {
tty - > ldisc . flush_buffer ( tty ) ;
}
/* Restore default settings */
sl - > dev - > type = ARPHRD_X25 ;
/* Perform the low-level X.25 async init */
if ( ( err = x25_asy_open ( sl - > dev ) ) )
return err ;
/* Done. We have linked the TTY line to a channel. */
return sl - > dev - > base_addr ;
}
/*
* Close down an X .25 channel .
* This means flushing out any pending queues , and then restoring the
* TTY line discipline to what it was before it got hooked to X .25
* ( which usually is TTY again ) .
*/
static void x25_asy_close_tty ( struct tty_struct * tty )
{
struct x25_asy * sl = ( struct x25_asy * ) tty - > disc_data ;
/* First make sure we're connected. */
if ( ! sl | | sl - > magic ! = X25_ASY_MAGIC )
return ;
if ( sl - > dev - > flags & IFF_UP )
{
( void ) dev_close ( sl - > dev ) ;
}
tty - > disc_data = NULL ;
sl - > tty = NULL ;
x25_asy_free ( sl ) ;
}
static struct net_device_stats * x25_asy_get_stats ( struct net_device * dev )
{
struct x25_asy * sl = ( struct x25_asy * ) ( dev - > priv ) ;
return & sl - > stats ;
}
/************************************************************************
* STANDARD X .25 ENCAPSULATION *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int x25_asy_esc ( unsigned char * s , unsigned char * d , int len )
{
unsigned char * ptr = d ;
unsigned char c ;
/*
* Send an initial END character to flush out any
* data that may have accumulated in the receiver
* due to line noise .
*/
* ptr + + = X25_END ; /* Send 10111110 bit seq */
/*
* For each byte in the packet , send the appropriate
* character sequence , according to the X .25 protocol .
*/
while ( len - - > 0 )
{
switch ( c = * s + + )
{
case X25_END :
* ptr + + = X25_ESC ;
* ptr + + = X25_ESCAPE ( X25_END ) ;
break ;
case X25_ESC :
* ptr + + = X25_ESC ;
* ptr + + = X25_ESCAPE ( X25_ESC ) ;
break ;
default :
* ptr + + = c ;
break ;
}
}
* ptr + + = X25_END ;
return ( ptr - d ) ;
}
static void x25_asy_unesc ( struct x25_asy * sl , unsigned char s )
{
switch ( s )
{
case X25_END :
if ( ! test_and_clear_bit ( SLF_ERROR , & sl - > flags ) & & ( sl - > rcount > 2 ) )
{
x25_asy_bump ( sl ) ;
}
clear_bit ( SLF_ESCAPE , & sl - > flags ) ;
sl - > rcount = 0 ;
return ;
case X25_ESC :
set_bit ( SLF_ESCAPE , & sl - > flags ) ;
return ;
case X25_ESCAPE ( X25_ESC ) :
case X25_ESCAPE ( X25_END ) :
if ( test_and_clear_bit ( SLF_ESCAPE , & sl - > flags ) )
s = X25_UNESCAPE ( s ) ;
break ;
}
if ( ! test_bit ( SLF_ERROR , & sl - > flags ) )
{
if ( sl - > rcount < sl - > buffsize )
{
sl - > rbuff [ sl - > rcount + + ] = s ;
return ;
}
sl - > stats . rx_over_errors + + ;
set_bit ( SLF_ERROR , & sl - > flags ) ;
}
}
/* Perform I/O control on an active X.25 channel. */
static int x25_asy_ioctl ( struct tty_struct * tty , struct file * file ,
unsigned int cmd , unsigned long arg )
{
struct x25_asy * sl = ( struct x25_asy * ) tty - > disc_data ;
/* First make sure we're connected. */
if ( ! sl | | sl - > magic ! = X25_ASY_MAGIC )
return - EINVAL ;
switch ( cmd ) {
case SIOCGIFNAME :
if ( copy_to_user ( ( void __user * ) arg , sl - > dev - > name ,
strlen ( sl - > dev - > name ) + 1 ) )
return - EFAULT ;
return 0 ;
case SIOCSIFHWADDR :
return - EINVAL ;
/* Allow stty to read, but not set, the serial port */
case TCGETS :
case TCGETA :
return n_tty_ioctl ( tty , file , cmd , arg ) ;
default :
return - ENOIOCTLCMD ;
}
}
static int x25_asy_open_dev ( struct net_device * dev )
{
struct x25_asy * sl = ( struct x25_asy * ) ( dev - > priv ) ;
if ( sl - > tty = = NULL )
return - ENODEV ;
return 0 ;
}
/* Initialise the X.25 driver. Called by the device init code */
static void x25_asy_setup ( struct net_device * dev )
{
struct x25_asy * sl = dev - > priv ;
sl - > magic = X25_ASY_MAGIC ;
sl - > dev = dev ;
spin_lock_init ( & sl - > lock ) ;
set_bit ( SLF_INUSE , & sl - > flags ) ;
/*
* Finish setting up the DEVICE info .
*/
dev - > mtu = SL_MTU ;
dev - > hard_start_xmit = x25_asy_xmit ;
dev - > tx_timeout = x25_asy_timeout ;
dev - > watchdog_timeo = HZ * 20 ;
dev - > open = x25_asy_open_dev ;
dev - > stop = x25_asy_close ;
dev - > get_stats = x25_asy_get_stats ;
dev - > change_mtu = x25_asy_change_mtu ;
dev - > hard_header_len = 0 ;
dev - > addr_len = 0 ;
dev - > type = ARPHRD_X25 ;
dev - > tx_queue_len = 10 ;
/* New-style flags. */
dev - > flags = IFF_NOARP ;
}
static struct tty_ldisc x25_ldisc = {
. owner = THIS_MODULE ,
. magic = TTY_LDISC_MAGIC ,
. name = " X.25 " ,
. open = x25_asy_open_tty ,
. close = x25_asy_close_tty ,
. ioctl = x25_asy_ioctl ,
. receive_buf = x25_asy_receive_buf ,
. receive_room = x25_asy_receive_room ,
. write_wakeup = x25_asy_write_wakeup ,
} ;
static int __init init_x25_asy ( void )
{
if ( x25_asy_maxdev < 4 )
x25_asy_maxdev = 4 ; /* Sanity */
printk ( KERN_INFO " X.25 async: version 0.00 ALPHA "
" (dynamic channels, max=%d). \n " , x25_asy_maxdev ) ;
x25_asy_devs = kmalloc ( sizeof ( struct net_device * ) * x25_asy_maxdev ,
GFP_KERNEL ) ;
if ( ! x25_asy_devs ) {
printk ( KERN_WARNING " X25 async: Can't allocate x25_asy_ctrls[] "
" array! Uaargh! (-> No X.25 available) \n " ) ;
return - ENOMEM ;
}
memset ( x25_asy_devs , 0 , sizeof ( struct net_device * ) * x25_asy_maxdev ) ;
return tty_register_ldisc ( N_X25 , & x25_ldisc ) ;
}
static void __exit exit_x25_asy ( void )
{
struct net_device * dev ;
int i ;
for ( i = 0 ; i < x25_asy_maxdev ; i + + ) {
dev = x25_asy_devs [ i ] ;
if ( dev ) {
struct x25_asy * sl = dev - > priv ;
spin_lock_bh ( & sl - > lock ) ;
if ( sl - > tty )
tty_hangup ( sl - > tty ) ;
spin_unlock_bh ( & sl - > lock ) ;
/*
* VSV = if dev - > start = = 0 , then device
* unregistered while close proc .
*/
unregister_netdev ( dev ) ;
free_netdev ( dev ) ;
}
}
kfree ( x25_asy_devs ) ;
2005-06-23 00:10:33 -07:00
tty_unregister_ldisc ( N_X25 ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( init_x25_asy ) ;
module_exit ( exit_x25_asy ) ;