2008-07-27 03:54:58 +04:00
/*
*
* Author Karsten Keil < kkeil @ novell . com >
*
* Copyright 2008 by Karsten Keil < kkeil @ novell . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
# include <linux/mISDNif.h>
# include "core.h"
2008-12-13 08:13:45 +03:00
static u_int * debug ;
2008-07-27 03:54:58 +04:00
static struct proto mISDN_proto = {
. name = " misdn " ,
. owner = THIS_MODULE ,
. obj_size = sizeof ( struct mISDN_sock )
} ;
# define _pms(sk) ((struct mISDN_sock *)sk)
static struct mISDN_sock_list data_sockets = {
. lock = __RW_LOCK_UNLOCKED ( data_sockets . lock )
} ;
static struct mISDN_sock_list base_sockets = {
. lock = __RW_LOCK_UNLOCKED ( base_sockets . lock )
} ;
# define L2_HEADER_LEN 4
static inline struct sk_buff *
_l2_alloc_skb ( unsigned int len , gfp_t gfp_mask )
{
struct sk_buff * skb ;
skb = alloc_skb ( len + L2_HEADER_LEN , gfp_mask ) ;
if ( likely ( skb ) )
skb_reserve ( skb , L2_HEADER_LEN ) ;
return skb ;
}
static void
mISDN_sock_link ( struct mISDN_sock_list * l , struct sock * sk )
{
write_lock_bh ( & l - > lock ) ;
sk_add_node ( sk , & l - > head ) ;
write_unlock_bh ( & l - > lock ) ;
}
static void mISDN_sock_unlink ( struct mISDN_sock_list * l , struct sock * sk )
{
write_lock_bh ( & l - > lock ) ;
sk_del_node_init ( sk ) ;
write_unlock_bh ( & l - > lock ) ;
}
static int
mISDN_send ( struct mISDNchannel * ch , struct sk_buff * skb )
{
struct mISDN_sock * msk ;
int err ;
msk = container_of ( ch , struct mISDN_sock , ch ) ;
if ( * debug & DEBUG_SOCKET )
printk ( KERN_DEBUG " %s len %d %p \n " , __func__ , skb - > len , skb ) ;
if ( msk - > sk . sk_state = = MISDN_CLOSED )
return - EUNATCH ;
__net_timestamp ( skb ) ;
err = sock_queue_rcv_skb ( & msk - > sk , skb ) ;
if ( err )
printk ( KERN_WARNING " %s: error %d \n " , __func__ , err ) ;
return err ;
}
static int
mISDN_ctrl ( struct mISDNchannel * ch , u_int cmd , void * arg )
{
struct mISDN_sock * msk ;
msk = container_of ( ch , struct mISDN_sock , ch ) ;
if ( * debug & DEBUG_SOCKET )
printk ( KERN_DEBUG " %s(%p, %x, %p) \n " , __func__ , ch , cmd , arg ) ;
switch ( cmd ) {
case CLOSE_CHANNEL :
msk - > sk . sk_state = MISDN_CLOSED ;
break ;
}
return 0 ;
}
static inline void
mISDN_sock_cmsg ( struct sock * sk , struct msghdr * msg , struct sk_buff * skb )
{
struct timeval tv ;
if ( _pms ( sk ) - > cmask & MISDN_TIME_STAMP ) {
skb_get_timestamp ( skb , & tv ) ;
put_cmsg ( msg , SOL_MISDN , MISDN_TIME_STAMP , sizeof ( tv ) , & tv ) ;
}
}
static int
mISDN_sock_recvmsg ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * msg , size_t len , int flags )
{
struct sk_buff * skb ;
struct sock * sk = sock - > sk ;
struct sockaddr_mISDN * maddr ;
int copied , err ;
if ( * debug & DEBUG_SOCKET )
printk ( KERN_DEBUG " %s: len %d, flags %x ch.nr %d, proto %x \n " ,
__func__ , ( int ) len , flags , _pms ( sk ) - > ch . nr ,
sk - > sk_protocol ) ;
if ( flags & ( MSG_OOB ) )
return - EOPNOTSUPP ;
if ( sk - > sk_state = = MISDN_CLOSED )
return 0 ;
skb = skb_recv_datagram ( sk , flags , flags & MSG_DONTWAIT , & err ) ;
if ( ! skb )
return err ;
if ( msg - > msg_namelen > = sizeof ( struct sockaddr_mISDN ) ) {
msg - > msg_namelen = sizeof ( struct sockaddr_mISDN ) ;
maddr = ( struct sockaddr_mISDN * ) msg - > msg_name ;
maddr - > family = AF_ISDN ;
maddr - > dev = _pms ( sk ) - > dev - > id ;
if ( ( sk - > sk_protocol = = ISDN_P_LAPD_TE ) | |
( sk - > sk_protocol = = ISDN_P_LAPD_NT ) ) {
maddr - > channel = ( mISDN_HEAD_ID ( skb ) > > 16 ) & 0xff ;
maddr - > tei = ( mISDN_HEAD_ID ( skb ) > > 8 ) & 0xff ;
maddr - > sapi = mISDN_HEAD_ID ( skb ) & 0xff ;
} else {
maddr - > channel = _pms ( sk ) - > ch . nr ;
maddr - > sapi = _pms ( sk ) - > ch . addr & 0xFF ;
maddr - > tei = ( _pms ( sk ) - > ch . addr > > 8 ) & 0xFF ;
}
} else {
if ( msg - > msg_namelen )
printk ( KERN_WARNING " %s: too small namelen %d \n " ,
__func__ , msg - > msg_namelen ) ;
msg - > msg_namelen = 0 ;
}
copied = skb - > len + MISDN_HEADER_LEN ;
if ( len < copied ) {
if ( flags & MSG_PEEK )
atomic_dec ( & skb - > users ) ;
else
skb_queue_head ( & sk - > sk_receive_queue , skb ) ;
return - ENOSPC ;
}
memcpy ( skb_push ( skb , MISDN_HEADER_LEN ) , mISDN_HEAD_P ( skb ) ,
MISDN_HEADER_LEN ) ;
err = skb_copy_datagram_iovec ( skb , 0 , msg - > msg_iov , copied ) ;
mISDN_sock_cmsg ( sk , msg , skb ) ;
skb_free_datagram ( sk , skb ) ;
return err ? : copied ;
}
static int
mISDN_sock_sendmsg ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * msg , size_t len )
{
struct sock * sk = sock - > sk ;
struct sk_buff * skb ;
int err = - ENOMEM ;
struct sockaddr_mISDN * maddr ;
if ( * debug & DEBUG_SOCKET )
printk ( KERN_DEBUG " %s: len %d flags %x ch %d proto %x \n " ,
__func__ , ( int ) len , msg - > msg_flags , _pms ( sk ) - > ch . nr ,
sk - > sk_protocol ) ;
if ( msg - > msg_flags & MSG_OOB )
return - EOPNOTSUPP ;
if ( msg - > msg_flags & ~ ( MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE ) )
return - EINVAL ;
if ( len < MISDN_HEADER_LEN )
return - EINVAL ;
if ( sk - > sk_state ! = MISDN_BOUND )
return - EBADFD ;
lock_sock ( sk ) ;
skb = _l2_alloc_skb ( len , GFP_KERNEL ) ;
if ( ! skb )
goto done ;
if ( memcpy_fromiovec ( skb_put ( skb , len ) , msg - > msg_iov , len ) ) {
err = - EFAULT ;
goto drop ;
}
memcpy ( mISDN_HEAD_P ( skb ) , skb - > data , MISDN_HEADER_LEN ) ;
skb_pull ( skb , MISDN_HEADER_LEN ) ;
if ( msg - > msg_namelen > = sizeof ( struct sockaddr_mISDN ) ) {
/* if we have a address, we use it */
maddr = ( struct sockaddr_mISDN * ) msg - > msg_name ;
mISDN_HEAD_ID ( skb ) = maddr - > channel ;
} else { /* use default for L2 messages */
if ( ( sk - > sk_protocol = = ISDN_P_LAPD_TE ) | |
( sk - > sk_protocol = = ISDN_P_LAPD_NT ) )
mISDN_HEAD_ID ( skb ) = _pms ( sk ) - > ch . nr ;
}
if ( * debug & DEBUG_SOCKET )
printk ( KERN_DEBUG " %s: ID:%x \n " ,
__func__ , mISDN_HEAD_ID ( skb ) ) ;
err = - ENODEV ;
if ( ! _pms ( sk ) - > ch . peer | |
( err = _pms ( sk ) - > ch . recv ( _pms ( sk ) - > ch . peer , skb ) ) )
goto drop ;
err = len ;
done :
release_sock ( sk ) ;
return err ;
drop :
kfree_skb ( skb ) ;
goto done ;
}
static int
data_sock_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
if ( * debug & DEBUG_SOCKET )
printk ( KERN_DEBUG " %s(%p) sk=%p \n " , __func__ , sock , sk ) ;
if ( ! sk )
return 0 ;
switch ( sk - > sk_protocol ) {
case ISDN_P_TE_S0 :
case ISDN_P_NT_S0 :
case ISDN_P_TE_E1 :
case ISDN_P_NT_E1 :
if ( sk - > sk_state = = MISDN_BOUND )
delete_channel ( & _pms ( sk ) - > ch ) ;
else
mISDN_sock_unlink ( & data_sockets , sk ) ;
break ;
case ISDN_P_LAPD_TE :
case ISDN_P_LAPD_NT :
case ISDN_P_B_RAW :
case ISDN_P_B_HDLC :
case ISDN_P_B_X75SLP :
case ISDN_P_B_L2DTMF :
case ISDN_P_B_L2DSP :
case ISDN_P_B_L2DSPHDLC :
delete_channel ( & _pms ( sk ) - > ch ) ;
mISDN_sock_unlink ( & data_sockets , sk ) ;
break ;
}
lock_sock ( sk ) ;
sock_orphan ( sk ) ;
skb_queue_purge ( & sk - > sk_receive_queue ) ;
release_sock ( sk ) ;
sock_put ( sk ) ;
return 0 ;
}
static int
data_sock_ioctl_bound ( struct sock * sk , unsigned int cmd , void __user * p )
{
struct mISDN_ctrl_req cq ;
int err = - EINVAL , val ;
struct mISDNchannel * bchan , * next ;
lock_sock ( sk ) ;
if ( ! _pms ( sk ) - > dev ) {
err = - ENODEV ;
goto done ;
}
switch ( cmd ) {
case IMCTRLREQ :
if ( copy_from_user ( & cq , p , sizeof ( cq ) ) ) {
err = - EFAULT ;
break ;
}
if ( ( sk - > sk_protocol & ~ ISDN_P_B_MASK ) = = ISDN_P_B_START ) {
list_for_each_entry_safe ( bchan , next ,
& _pms ( sk ) - > dev - > bchannels , list ) {
if ( bchan - > nr = = cq . channel ) {
err = bchan - > ctrl ( bchan ,
CONTROL_CHANNEL , & cq ) ;
break ;
}
}
} else
err = _pms ( sk ) - > dev - > D . ctrl ( & _pms ( sk ) - > dev - > D ,
CONTROL_CHANNEL , & cq ) ;
if ( err )
break ;
if ( copy_to_user ( p , & cq , sizeof ( cq ) ) )
err = - EFAULT ;
break ;
case IMCLEAR_L2 :
if ( sk - > sk_protocol ! = ISDN_P_LAPD_NT ) {
err = - EINVAL ;
break ;
}
if ( get_user ( val , ( int __user * ) p ) ) {
err = - EFAULT ;
break ;
}
err = _pms ( sk ) - > dev - > teimgr - > ctrl ( _pms ( sk ) - > dev - > teimgr ,
CONTROL_CHANNEL , & val ) ;
break ;
default :
err = - EINVAL ;
break ;
}
done :
release_sock ( sk ) ;
return err ;
}
static int
data_sock_ioctl ( struct socket * sock , unsigned int cmd , unsigned long arg )
{
int err = 0 , id ;
struct sock * sk = sock - > sk ;
struct mISDNdevice * dev ;
struct mISDNversion ver ;
switch ( cmd ) {
case IMGETVERSION :
ver . major = MISDN_MAJOR_VERSION ;
ver . minor = MISDN_MINOR_VERSION ;
ver . release = MISDN_RELEASE ;
if ( copy_to_user ( ( void __user * ) arg , & ver , sizeof ( ver ) ) )
err = - EFAULT ;
break ;
case IMGETCOUNT :
id = get_mdevice_count ( ) ;
if ( put_user ( id , ( int __user * ) arg ) )
err = - EFAULT ;
break ;
case IMGETDEVINFO :
if ( get_user ( id , ( int __user * ) arg ) ) {
err = - EFAULT ;
break ;
}
dev = get_mdevice ( id ) ;
if ( dev ) {
struct mISDN_devinfo di ;
di . id = dev - > id ;
di . Dprotocols = dev - > Dprotocols ;
di . Bprotocols = dev - > Bprotocols | get_all_Bprotocols ( ) ;
di . protocol = dev - > D . protocol ;
memcpy ( di . channelmap , dev - > channelmap ,
2008-07-30 20:26:58 +04:00
sizeof ( di . channelmap ) ) ;
2008-07-27 03:54:58 +04:00
di . nrbchan = dev - > nrbchan ;
2008-08-16 02:04:33 +04:00
strcpy ( di . name , dev_name ( & dev - > dev ) ) ;
2008-07-27 03:54:58 +04:00
if ( copy_to_user ( ( void __user * ) arg , & di , sizeof ( di ) ) )
err = - EFAULT ;
} else
err = - ENODEV ;
break ;
default :
if ( sk - > sk_state = = MISDN_BOUND )
err = data_sock_ioctl_bound ( sk , cmd ,
( void __user * ) arg ) ;
else
err = - ENOTCONN ;
}
return err ;
}
static int data_sock_setsockopt ( struct socket * sock , int level , int optname ,
char __user * optval , int len )
{
struct sock * sk = sock - > sk ;
int err = 0 , opt = 0 ;
if ( * debug & DEBUG_SOCKET )
printk ( KERN_DEBUG " %s(%p, %d, %x, %p, %d) \n " , __func__ , sock ,
level , optname , optval , len ) ;
lock_sock ( sk ) ;
switch ( optname ) {
case MISDN_TIME_STAMP :
if ( get_user ( opt , ( int __user * ) optval ) ) {
err = - EFAULT ;
break ;
}
if ( opt )
_pms ( sk ) - > cmask | = MISDN_TIME_STAMP ;
else
_pms ( sk ) - > cmask & = ~ MISDN_TIME_STAMP ;
break ;
default :
err = - ENOPROTOOPT ;
break ;
}
release_sock ( sk ) ;
return err ;
}
static int data_sock_getsockopt ( struct socket * sock , int level , int optname ,
char __user * optval , int __user * optlen )
{
struct sock * sk = sock - > sk ;
int len , opt ;
if ( get_user ( len , optlen ) )
return - EFAULT ;
switch ( optname ) {
case MISDN_TIME_STAMP :
if ( _pms ( sk ) - > cmask & MISDN_TIME_STAMP )
opt = 1 ;
else
opt = 0 ;
if ( put_user ( opt , optval ) )
return - EFAULT ;
break ;
default :
return - ENOPROTOOPT ;
}
return 0 ;
}
static int
data_sock_bind ( struct socket * sock , struct sockaddr * addr , int addr_len )
{
struct sockaddr_mISDN * maddr = ( struct sockaddr_mISDN * ) addr ;
struct sock * sk = sock - > sk ;
int err = 0 ;
if ( * debug & DEBUG_SOCKET )
printk ( KERN_DEBUG " %s(%p) sk=%p \n " , __func__ , sock , sk ) ;
if ( addr_len ! = sizeof ( struct sockaddr_mISDN ) )
return - EINVAL ;
if ( ! maddr | | maddr - > family ! = AF_ISDN )
return - EINVAL ;
lock_sock ( sk ) ;
if ( _pms ( sk ) - > dev ) {
err = - EALREADY ;
goto done ;
}
_pms ( sk ) - > dev = get_mdevice ( maddr - > dev ) ;
if ( ! _pms ( sk ) - > dev ) {
err = - ENODEV ;
goto done ;
}
_pms ( sk ) - > ch . send = mISDN_send ;
_pms ( sk ) - > ch . ctrl = mISDN_ctrl ;
switch ( sk - > sk_protocol ) {
case ISDN_P_TE_S0 :
case ISDN_P_NT_S0 :
case ISDN_P_TE_E1 :
case ISDN_P_NT_E1 :
mISDN_sock_unlink ( & data_sockets , sk ) ;
err = connect_layer1 ( _pms ( sk ) - > dev , & _pms ( sk ) - > ch ,
sk - > sk_protocol , maddr ) ;
if ( err )
mISDN_sock_link ( & data_sockets , sk ) ;
break ;
case ISDN_P_LAPD_TE :
case ISDN_P_LAPD_NT :
err = create_l2entity ( _pms ( sk ) - > dev , & _pms ( sk ) - > ch ,
sk - > sk_protocol , maddr ) ;
break ;
case ISDN_P_B_RAW :
case ISDN_P_B_HDLC :
case ISDN_P_B_X75SLP :
case ISDN_P_B_L2DTMF :
case ISDN_P_B_L2DSP :
case ISDN_P_B_L2DSPHDLC :
err = connect_Bstack ( _pms ( sk ) - > dev , & _pms ( sk ) - > ch ,
sk - > sk_protocol , maddr ) ;
break ;
default :
err = - EPROTONOSUPPORT ;
}
if ( err )
goto done ;
sk - > sk_state = MISDN_BOUND ;
_pms ( sk ) - > ch . protocol = sk - > sk_protocol ;
done :
release_sock ( sk ) ;
return err ;
}
static int
data_sock_getname ( struct socket * sock , struct sockaddr * addr ,
int * addr_len , int peer )
{
struct sockaddr_mISDN * maddr = ( struct sockaddr_mISDN * ) addr ;
struct sock * sk = sock - > sk ;
if ( ! _pms ( sk ) - > dev )
return - EBADFD ;
lock_sock ( sk ) ;
* addr_len = sizeof ( * maddr ) ;
maddr - > dev = _pms ( sk ) - > dev - > id ;
maddr - > channel = _pms ( sk ) - > ch . nr ;
maddr - > sapi = _pms ( sk ) - > ch . addr & 0xff ;
maddr - > tei = ( _pms ( sk ) - > ch . addr > > 8 ) & 0xff ;
release_sock ( sk ) ;
return 0 ;
}
static const struct proto_ops data_sock_ops = {
. family = PF_ISDN ,
. owner = THIS_MODULE ,
. release = data_sock_release ,
. ioctl = data_sock_ioctl ,
. bind = data_sock_bind ,
. getname = data_sock_getname ,
. sendmsg = mISDN_sock_sendmsg ,
. recvmsg = mISDN_sock_recvmsg ,
. poll = datagram_poll ,
. listen = sock_no_listen ,
. shutdown = sock_no_shutdown ,
. setsockopt = data_sock_setsockopt ,
. getsockopt = data_sock_getsockopt ,
. connect = sock_no_connect ,
. socketpair = sock_no_socketpair ,
. accept = sock_no_accept ,
. mmap = sock_no_mmap
} ;
static int
data_sock_create ( struct net * net , struct socket * sock , int protocol )
{
struct sock * sk ;
if ( sock - > type ! = SOCK_DGRAM )
return - ESOCKTNOSUPPORT ;
sk = sk_alloc ( net , PF_ISDN , GFP_KERNEL , & mISDN_proto ) ;
if ( ! sk )
return - ENOMEM ;
sock_init_data ( sock , sk ) ;
sock - > ops = & data_sock_ops ;
sock - > state = SS_UNCONNECTED ;
sock_reset_flag ( sk , SOCK_ZAPPED ) ;
sk - > sk_protocol = protocol ;
sk - > sk_state = MISDN_OPEN ;
mISDN_sock_link ( & data_sockets , sk ) ;
return 0 ;
}
static int
base_sock_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
printk ( KERN_DEBUG " %s(%p) sk=%p \n " , __func__ , sock , sk ) ;
if ( ! sk )
return 0 ;
mISDN_sock_unlink ( & base_sockets , sk ) ;
sock_orphan ( sk ) ;
sock_put ( sk ) ;
return 0 ;
}
static int
base_sock_ioctl ( struct socket * sock , unsigned int cmd , unsigned long arg )
{
int err = 0 , id ;
struct mISDNdevice * dev ;
struct mISDNversion ver ;
switch ( cmd ) {
case IMGETVERSION :
ver . major = MISDN_MAJOR_VERSION ;
ver . minor = MISDN_MINOR_VERSION ;
ver . release = MISDN_RELEASE ;
if ( copy_to_user ( ( void __user * ) arg , & ver , sizeof ( ver ) ) )
err = - EFAULT ;
break ;
case IMGETCOUNT :
id = get_mdevice_count ( ) ;
if ( put_user ( id , ( int __user * ) arg ) )
err = - EFAULT ;
break ;
case IMGETDEVINFO :
if ( get_user ( id , ( int __user * ) arg ) ) {
err = - EFAULT ;
break ;
}
dev = get_mdevice ( id ) ;
if ( dev ) {
struct mISDN_devinfo di ;
di . id = dev - > id ;
di . Dprotocols = dev - > Dprotocols ;
di . Bprotocols = dev - > Bprotocols | get_all_Bprotocols ( ) ;
di . protocol = dev - > D . protocol ;
memcpy ( di . channelmap , dev - > channelmap ,
2008-07-30 20:26:58 +04:00
sizeof ( di . channelmap ) ) ;
2008-07-27 03:54:58 +04:00
di . nrbchan = dev - > nrbchan ;
2008-08-16 02:04:33 +04:00
strcpy ( di . name , dev_name ( & dev - > dev ) ) ;
2008-07-27 03:54:58 +04:00
if ( copy_to_user ( ( void __user * ) arg , & di , sizeof ( di ) ) )
err = - EFAULT ;
} else
err = - ENODEV ;
break ;
2008-08-12 12:12:09 +04:00
case IMSETDEVNAME :
{
struct mISDN_devrename dn ;
if ( copy_from_user ( & dn , ( void __user * ) arg ,
sizeof ( dn ) ) ) {
err = - EFAULT ;
break ;
}
dev = get_mdevice ( dn . id ) ;
if ( dev )
2008-08-16 02:04:33 +04:00
err = device_rename ( & dev - > dev , dn . name ) ;
2008-08-12 12:12:09 +04:00
else
err = - ENODEV ;
}
break ;
2008-07-27 03:54:58 +04:00
default :
err = - EINVAL ;
}
return err ;
}
static int
base_sock_bind ( struct socket * sock , struct sockaddr * addr , int addr_len )
{
struct sockaddr_mISDN * maddr = ( struct sockaddr_mISDN * ) addr ;
struct sock * sk = sock - > sk ;
int err = 0 ;
if ( ! maddr | | maddr - > family ! = AF_ISDN )
return - EINVAL ;
lock_sock ( sk ) ;
if ( _pms ( sk ) - > dev ) {
err = - EALREADY ;
goto done ;
}
_pms ( sk ) - > dev = get_mdevice ( maddr - > dev ) ;
if ( ! _pms ( sk ) - > dev ) {
err = - ENODEV ;
goto done ;
}
sk - > sk_state = MISDN_BOUND ;
done :
release_sock ( sk ) ;
return err ;
}
static const struct proto_ops base_sock_ops = {
. family = PF_ISDN ,
. owner = THIS_MODULE ,
. release = base_sock_release ,
. ioctl = base_sock_ioctl ,
. bind = base_sock_bind ,
. getname = sock_no_getname ,
. sendmsg = sock_no_sendmsg ,
. recvmsg = sock_no_recvmsg ,
. poll = sock_no_poll ,
. listen = sock_no_listen ,
. shutdown = sock_no_shutdown ,
. setsockopt = sock_no_setsockopt ,
. getsockopt = sock_no_getsockopt ,
. connect = sock_no_connect ,
. socketpair = sock_no_socketpair ,
. accept = sock_no_accept ,
. mmap = sock_no_mmap
} ;
static int
base_sock_create ( struct net * net , struct socket * sock , int protocol )
{
struct sock * sk ;
if ( sock - > type ! = SOCK_RAW )
return - ESOCKTNOSUPPORT ;
sk = sk_alloc ( net , PF_ISDN , GFP_KERNEL , & mISDN_proto ) ;
if ( ! sk )
return - ENOMEM ;
sock_init_data ( sock , sk ) ;
sock - > ops = & base_sock_ops ;
sock - > state = SS_UNCONNECTED ;
sock_reset_flag ( sk , SOCK_ZAPPED ) ;
sk - > sk_protocol = protocol ;
sk - > sk_state = MISDN_OPEN ;
mISDN_sock_link ( & base_sockets , sk ) ;
return 0 ;
}
static int
mISDN_sock_create ( struct net * net , struct socket * sock , int proto )
{
int err = - EPROTONOSUPPORT ;
switch ( proto ) {
case ISDN_P_BASE :
err = base_sock_create ( net , sock , proto ) ;
break ;
case ISDN_P_TE_S0 :
case ISDN_P_NT_S0 :
case ISDN_P_TE_E1 :
case ISDN_P_NT_E1 :
case ISDN_P_LAPD_TE :
case ISDN_P_LAPD_NT :
case ISDN_P_B_RAW :
case ISDN_P_B_HDLC :
case ISDN_P_B_X75SLP :
case ISDN_P_B_L2DTMF :
case ISDN_P_B_L2DSP :
case ISDN_P_B_L2DSPHDLC :
err = data_sock_create ( net , sock , proto ) ;
break ;
default :
return err ;
}
return err ;
}
static struct
net_proto_family mISDN_sock_family_ops = {
. owner = THIS_MODULE ,
. family = PF_ISDN ,
. create = mISDN_sock_create ,
} ;
int
misdn_sock_init ( u_int * deb )
{
int err ;
debug = deb ;
err = sock_register ( & mISDN_sock_family_ops ) ;
if ( err )
printk ( KERN_ERR " %s: error(%d) \n " , __func__ , err ) ;
return err ;
}
void
misdn_sock_cleanup ( void )
{
sock_unregister ( PF_ISDN ) ;
}