2008-10-05 22:16:16 +04:00
/*
* File : pep - gprs . c
*
* GPRS over Phonet pipe end point socket
*
* Copyright ( C ) 2008 Nokia Corporation .
*
* Author : Rémi Denis - Courmont < remi . denis - courmont @ nokia . 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*/
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/if_ether.h>
# include <linux/if_arp.h>
# include <net/sock.h>
# include <linux/if_phonet.h>
# include <net/tcp_states.h>
# include <net/phonet/gprs.h>
# define GPRS_DEFAULT_MTU 1400
struct gprs_dev {
struct sock * sk ;
void ( * old_state_change ) ( struct sock * ) ;
void ( * old_data_ready ) ( struct sock * , int ) ;
void ( * old_write_space ) ( struct sock * ) ;
2008-12-16 12:18:31 +03:00
struct net_device * dev ;
2008-10-05 22:16:16 +04:00
} ;
2008-11-07 10:10:50 +03:00
static __be16 gprs_type_trans ( struct sk_buff * skb )
2008-10-05 22:16:16 +04:00
{
const u8 * pvfc ;
u8 buf ;
pvfc = skb_header_pointer ( skb , 0 , 1 , & buf ) ;
if ( ! pvfc )
2008-11-07 10:10:50 +03:00
return htons ( 0 ) ;
2008-10-05 22:16:16 +04:00
/* Look at IP version field */
switch ( * pvfc > > 4 ) {
case 4 :
return htons ( ETH_P_IP ) ;
case 6 :
return htons ( ETH_P_IPV6 ) ;
}
2008-11-07 10:10:50 +03:00
return htons ( 0 ) ;
2008-10-05 22:16:16 +04:00
}
2008-12-18 02:48:50 +03:00
static void gprs_writeable ( struct gprs_dev * gp )
{
struct net_device * dev = gp - > dev ;
if ( pep_writeable ( gp - > sk ) )
netif_wake_queue ( dev ) ;
}
2008-10-05 22:16:16 +04:00
/*
* Socket callbacks
*/
static void gprs_state_change ( struct sock * sk )
{
2008-12-16 12:18:31 +03:00
struct gprs_dev * gp = sk - > sk_user_data ;
2008-10-05 22:16:16 +04:00
if ( sk - > sk_state = = TCP_CLOSE_WAIT ) {
2008-12-16 12:18:31 +03:00
struct net_device * dev = gp - > dev ;
netif_stop_queue ( dev ) ;
netif_carrier_off ( dev ) ;
2008-10-05 22:16:16 +04:00
}
}
2008-12-16 12:18:31 +03:00
static int gprs_recv ( struct gprs_dev * gp , struct sk_buff * skb )
2008-10-05 22:16:16 +04:00
{
2008-12-16 12:18:31 +03:00
struct net_device * dev = gp - > dev ;
2008-10-05 22:16:16 +04:00
int err = 0 ;
2008-11-07 10:10:50 +03:00
__be16 protocol = gprs_type_trans ( skb ) ;
2008-10-05 22:16:16 +04:00
if ( ! protocol ) {
err = - EINVAL ;
goto drop ;
}
2010-01-04 05:02:47 +03:00
if ( skb_headroom ( skb ) & 3 ) {
2008-10-05 22:16:16 +04:00
struct sk_buff * rskb , * fs ;
int flen = 0 ;
2010-01-04 05:02:47 +03:00
/* Phonet Pipe data header may be misaligned (3 bytes),
2008-10-05 22:16:16 +04:00
* so wrap the IP packet as a single fragment of an head - less
* socket buffer . The network stack will pull what it needs ,
* but at least , the whole IP payload is not memcpy ' d . */
2008-12-16 12:18:31 +03:00
rskb = netdev_alloc_skb ( dev , 0 ) ;
2008-10-05 22:16:16 +04:00
if ( ! rskb ) {
err = - ENOBUFS ;
goto drop ;
}
skb_shinfo ( rskb ) - > frag_list = skb ;
rskb - > len + = skb - > len ;
rskb - > data_len + = rskb - > len ;
rskb - > truesize + = rskb - > len ;
/* Avoid nested fragments */
2009-06-09 11:21:58 +04:00
skb_walk_frags ( skb , fs )
2008-10-05 22:16:16 +04:00
flen + = fs - > len ;
skb - > next = skb_shinfo ( skb ) - > frag_list ;
2009-06-09 11:21:58 +04:00
skb_frag_list_init ( skb ) ;
2008-10-05 22:16:16 +04:00
skb - > len - = flen ;
skb - > data_len - = flen ;
skb - > truesize - = flen ;
skb = rskb ;
}
skb - > protocol = protocol ;
skb_reset_mac_header ( skb ) ;
2008-12-16 12:18:31 +03:00
skb - > dev = dev ;
2008-10-05 22:16:16 +04:00
2008-12-16 12:18:31 +03:00
if ( likely ( dev - > flags & IFF_UP ) ) {
dev - > stats . rx_packets + + ;
dev - > stats . rx_bytes + = skb - > len ;
2008-10-05 22:16:16 +04:00
netif_rx ( skb ) ;
skb = NULL ;
} else
err = - ENODEV ;
drop :
if ( skb ) {
dev_kfree_skb ( skb ) ;
2008-12-16 12:18:31 +03:00
dev - > stats . rx_dropped + + ;
2008-10-05 22:16:16 +04:00
}
return err ;
}
static void gprs_data_ready ( struct sock * sk , int len )
{
2008-12-16 12:18:31 +03:00
struct gprs_dev * gp = sk - > sk_user_data ;
2008-10-05 22:16:16 +04:00
struct sk_buff * skb ;
while ( ( skb = pep_read ( sk ) ) ! = NULL ) {
skb_orphan ( skb ) ;
2008-12-16 12:18:31 +03:00
gprs_recv ( gp , skb ) ;
2008-10-05 22:16:16 +04:00
}
}
static void gprs_write_space ( struct sock * sk )
{
2008-12-16 12:18:31 +03:00
struct gprs_dev * gp = sk - > sk_user_data ;
2008-10-05 22:16:16 +04:00
2008-12-18 02:48:50 +03:00
if ( netif_running ( gp - > dev ) )
gprs_writeable ( gp ) ;
2008-10-05 22:16:16 +04:00
}
/*
* Network device callbacks
*/
2008-12-15 11:53:57 +03:00
static int gprs_open ( struct net_device * dev )
{
struct gprs_dev * gp = netdev_priv ( dev ) ;
2008-12-18 02:48:50 +03:00
gprs_writeable ( gp ) ;
2008-12-15 11:53:57 +03:00
return 0 ;
}
static int gprs_close ( struct net_device * dev )
{
netif_stop_queue ( dev ) ;
return 0 ;
}
2009-08-31 23:50:51 +04:00
static netdev_tx_t gprs_xmit ( struct sk_buff * skb , struct net_device * dev )
2008-10-05 22:16:16 +04:00
{
2008-12-16 12:18:31 +03:00
struct gprs_dev * gp = netdev_priv ( dev ) ;
2008-12-18 02:48:50 +03:00
struct sock * sk = gp - > sk ;
int len , err ;
2008-10-05 22:16:16 +04:00
switch ( skb - > protocol ) {
case htons ( ETH_P_IP ) :
case htons ( ETH_P_IPV6 ) :
break ;
default :
dev_kfree_skb ( skb ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2008-10-05 22:16:16 +04:00
}
2008-12-18 02:48:50 +03:00
skb_orphan ( skb ) ;
skb_set_owner_w ( skb , sk ) ;
len = skb - > len ;
err = pep_write ( sk , skb ) ;
if ( err ) {
LIMIT_NETDEBUG ( KERN_WARNING " %s: TX error (%d) \n " ,
dev - > name , err ) ;
dev - > stats . tx_aborted_errors + + ;
dev - > stats . tx_errors + + ;
} else {
2008-12-16 12:18:31 +03:00
dev - > stats . tx_packets + + ;
2008-12-18 02:48:50 +03:00
dev - > stats . tx_bytes + = len ;
2008-10-05 22:16:16 +04:00
}
2009-06-01 04:35:16 +04:00
netif_stop_queue ( dev ) ;
if ( pep_writeable ( sk ) )
netif_wake_queue ( dev ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2008-10-05 22:16:16 +04:00
}
2008-12-16 12:18:31 +03:00
static int gprs_set_mtu ( struct net_device * dev , int new_mtu )
2008-10-05 22:16:16 +04:00
{
if ( ( new_mtu < 576 ) | | ( new_mtu > ( PHONET_MAX_MTU - 11 ) ) )
return - EINVAL ;
2008-12-16 12:18:31 +03:00
dev - > mtu = new_mtu ;
2008-10-05 22:16:16 +04:00
return 0 ;
}
2009-01-08 04:24:34 +03:00
static const struct net_device_ops gprs_netdev_ops = {
. ndo_open = gprs_open ,
. ndo_stop = gprs_close ,
. ndo_start_xmit = gprs_xmit ,
. ndo_change_mtu = gprs_set_mtu ,
} ;
2008-12-16 12:18:31 +03:00
static void gprs_setup ( struct net_device * dev )
2008-10-05 22:16:16 +04:00
{
2008-12-16 12:18:31 +03:00
dev - > features = NETIF_F_FRAGLIST ;
2008-12-18 02:47:48 +03:00
dev - > type = ARPHRD_PHONET_PIPE ;
2008-12-16 12:18:31 +03:00
dev - > flags = IFF_POINTOPOINT | IFF_NOARP ;
dev - > mtu = GPRS_DEFAULT_MTU ;
dev - > hard_header_len = 0 ;
dev - > addr_len = 0 ;
dev - > tx_queue_len = 10 ;
2009-01-08 04:24:34 +03:00
dev - > netdev_ops = & gprs_netdev_ops ;
2008-12-16 12:18:31 +03:00
dev - > destructor = free_netdev ;
2008-10-05 22:16:16 +04:00
}
/*
* External interface
*/
/*
* Attach a GPRS interface to a datagram socket .
* Returns the interface index on success , negative error code on error .
*/
int gprs_attach ( struct sock * sk )
{
static const char ifname [ ] = " gprs%d " ;
2008-12-16 12:18:31 +03:00
struct gprs_dev * gp ;
struct net_device * dev ;
2008-10-05 22:16:16 +04:00
int err ;
if ( unlikely ( sk - > sk_type = = SOCK_STREAM ) )
return - EINVAL ; /* need packet boundaries */
/* Create net device */
2008-12-16 12:18:31 +03:00
dev = alloc_netdev ( sizeof ( * gp ) , ifname , gprs_setup ) ;
if ( ! dev )
2008-10-05 22:16:16 +04:00
return - ENOMEM ;
2008-12-16 12:18:31 +03:00
gp = netdev_priv ( dev ) ;
2008-12-18 02:48:50 +03:00
gp - > sk = sk ;
2008-12-16 12:18:31 +03:00
gp - > dev = dev ;
netif_stop_queue ( dev ) ;
err = register_netdev ( dev ) ;
2008-10-05 22:16:16 +04:00
if ( err ) {
2008-12-16 12:18:31 +03:00
free_netdev ( dev ) ;
2008-10-05 22:16:16 +04:00
return err ;
}
lock_sock ( sk ) ;
if ( unlikely ( sk - > sk_user_data ) ) {
err = - EBUSY ;
goto out_rel ;
}
if ( unlikely ( ( 1 < < sk - > sk_state & ( TCPF_CLOSE | TCPF_LISTEN ) ) | |
sock_flag ( sk , SOCK_DEAD ) ) ) {
err = - EINVAL ;
goto out_rel ;
}
2008-12-16 12:18:31 +03:00
sk - > sk_user_data = gp ;
gp - > old_state_change = sk - > sk_state_change ;
gp - > old_data_ready = sk - > sk_data_ready ;
gp - > old_write_space = sk - > sk_write_space ;
2008-10-05 22:16:16 +04:00
sk - > sk_state_change = gprs_state_change ;
sk - > sk_data_ready = gprs_data_ready ;
sk - > sk_write_space = gprs_write_space ;
release_sock ( sk ) ;
sock_hold ( sk ) ;
2008-12-16 12:18:31 +03:00
printk ( KERN_DEBUG " %s: attached \n " , dev - > name ) ;
return dev - > ifindex ;
2008-10-05 22:16:16 +04:00
out_rel :
release_sock ( sk ) ;
2008-12-16 12:18:31 +03:00
unregister_netdev ( dev ) ;
2008-10-05 22:16:16 +04:00
return err ;
}
void gprs_detach ( struct sock * sk )
{
2008-12-16 12:18:31 +03:00
struct gprs_dev * gp = sk - > sk_user_data ;
struct net_device * dev = gp - > dev ;
2008-10-05 22:16:16 +04:00
lock_sock ( sk ) ;
sk - > sk_user_data = NULL ;
2008-12-16 12:18:31 +03:00
sk - > sk_state_change = gp - > old_state_change ;
sk - > sk_data_ready = gp - > old_data_ready ;
sk - > sk_write_space = gp - > old_write_space ;
2008-10-05 22:16:16 +04:00
release_sock ( sk ) ;
2008-12-16 12:18:31 +03:00
printk ( KERN_DEBUG " %s: detached \n " , dev - > name ) ;
unregister_netdev ( dev ) ;
2008-10-05 22:16:16 +04:00
sock_put ( sk ) ;
}