| 
									
										
										
										
											2008-10-05 11:16:16 -07: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 01:18:31 -08:00
										 |  |  | 	struct net_device	*dev; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-06 23:10:50 -08:00
										 |  |  | static __be16 gprs_type_trans(struct sk_buff *skb) | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	const u8 *pvfc; | 
					
						
							|  |  |  | 	u8 buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pvfc = skb_header_pointer(skb, 0, 1, &buf); | 
					
						
							|  |  |  | 	if (!pvfc) | 
					
						
							| 
									
										
										
										
											2008-11-06 23:10:50 -08:00
										 |  |  | 		return htons(0); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07: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-06 23:10:50 -08:00
										 |  |  | 	return htons(0); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 15:48:50 -08: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 11:16:16 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Socket callbacks | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void gprs_state_change(struct sock *sk) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	struct gprs_dev *gp = sk->sk_user_data; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (sk->sk_state == TCP_CLOSE_WAIT) { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 		struct net_device *dev = gp->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		netif_stop_queue(dev); | 
					
						
							|  |  |  | 		netif_carrier_off(dev); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | static int gprs_recv(struct gprs_dev *gp, struct sk_buff *skb) | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	struct net_device *dev = gp->dev; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	int err = 0; | 
					
						
							| 
									
										
										
										
											2008-11-06 23:10:50 -08:00
										 |  |  | 	__be16 protocol = gprs_type_trans(skb); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!protocol) { | 
					
						
							|  |  |  | 		err = -EINVAL; | 
					
						
							|  |  |  | 		goto drop; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-04 02:02:47 +00:00
										 |  |  | 	if (skb_headroom(skb) & 3) { | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 		struct sk_buff *rskb, *fs; | 
					
						
							|  |  |  | 		int flen = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-04 02:02:47 +00:00
										 |  |  | 		/* Phonet Pipe data header may be misaligned (3 bytes),
 | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07: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 01:18:31 -08:00
										 |  |  | 		rskb = netdev_alloc_skb(dev, 0); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07: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 00:21:58 -07:00
										 |  |  | 		skb_walk_frags(skb, fs) | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 			flen += fs->len; | 
					
						
							|  |  |  | 		skb->next = skb_shinfo(skb)->frag_list; | 
					
						
							| 
									
										
										
										
											2009-06-09 00:21:58 -07:00
										 |  |  | 		skb_frag_list_init(skb); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 		skb->len -= flen; | 
					
						
							|  |  |  | 		skb->data_len -= flen; | 
					
						
							|  |  |  | 		skb->truesize -= flen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		skb = rskb; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb->protocol = protocol; | 
					
						
							|  |  |  | 	skb_reset_mac_header(skb); | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	skb->dev = dev; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	if (likely(dev->flags & IFF_UP)) { | 
					
						
							|  |  |  | 		dev->stats.rx_packets++; | 
					
						
							|  |  |  | 		dev->stats.rx_bytes += skb->len; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 		netif_rx(skb); | 
					
						
							|  |  |  | 		skb = NULL; | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		err = -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | drop: | 
					
						
							|  |  |  | 	if (skb) { | 
					
						
							|  |  |  | 		dev_kfree_skb(skb); | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 		dev->stats.rx_dropped++; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void gprs_data_ready(struct sock *sk, int len) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	struct gprs_dev *gp = sk->sk_user_data; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	struct sk_buff *skb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((skb = pep_read(sk)) != NULL) { | 
					
						
							|  |  |  | 		skb_orphan(skb); | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 		gprs_recv(gp, skb); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void gprs_write_space(struct sock *sk) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	struct gprs_dev *gp = sk->sk_user_data; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 15:48:50 -08:00
										 |  |  | 	if (netif_running(gp->dev)) | 
					
						
							|  |  |  | 		gprs_writeable(gp); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Network device callbacks | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-15 00:53:57 -08:00
										 |  |  | static int gprs_open(struct net_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct gprs_dev *gp = netdev_priv(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 15:48:50 -08:00
										 |  |  | 	gprs_writeable(gp); | 
					
						
							| 
									
										
										
										
											2008-12-15 00:53:57 -08:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int gprs_close(struct net_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	netif_stop_queue(dev); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-31 19:50:51 +00:00
										 |  |  | static netdev_tx_t gprs_xmit(struct sk_buff *skb, struct net_device *dev) | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	struct gprs_dev *gp = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2008-12-17 15:48:50 -08:00
										 |  |  | 	struct sock *sk = gp->sk; | 
					
						
							|  |  |  | 	int len, err; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (skb->protocol) { | 
					
						
							|  |  |  | 	case  htons(ETH_P_IP): | 
					
						
							|  |  |  | 	case  htons(ETH_P_IPV6): | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		dev_kfree_skb(skb); | 
					
						
							| 
									
										
										
										
											2009-06-23 06:03:08 +00:00
										 |  |  | 		return NETDEV_TX_OK; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 15:48:50 -08: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 01:18:31 -08:00
										 |  |  | 		dev->stats.tx_packets++; | 
					
						
							| 
									
										
										
										
											2008-12-17 15:48:50 -08:00
										 |  |  | 		dev->stats.tx_bytes += len; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-01 00:35:16 +00:00
										 |  |  | 	netif_stop_queue(dev); | 
					
						
							|  |  |  | 	if (pep_writeable(sk)) | 
					
						
							|  |  |  | 		netif_wake_queue(dev); | 
					
						
							| 
									
										
										
										
											2009-06-23 06:03:08 +00:00
										 |  |  | 	return NETDEV_TX_OK; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | static int gprs_set_mtu(struct net_device *dev, int new_mtu) | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11))) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	dev->mtu = new_mtu; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-07 17:24:34 -08: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 01:18:31 -08:00
										 |  |  | static void gprs_setup(struct net_device *dev) | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	dev->features		= NETIF_F_FRAGLIST; | 
					
						
							| 
									
										
										
										
											2008-12-17 15:47:48 -08:00
										 |  |  | 	dev->type		= ARPHRD_PHONET_PIPE; | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08: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-07 17:24:34 -08:00
										 |  |  | 	dev->netdev_ops		= &gprs_netdev_ops; | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	dev->destructor		= free_netdev; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07: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 01:18:31 -08:00
										 |  |  | 	struct gprs_dev *gp; | 
					
						
							|  |  |  | 	struct net_device *dev; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (unlikely(sk->sk_type == SOCK_STREAM)) | 
					
						
							|  |  |  | 		return -EINVAL; /* need packet boundaries */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Create net device */ | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	dev = alloc_netdev(sizeof(*gp), ifname, gprs_setup); | 
					
						
							|  |  |  | 	if (!dev) | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	gp = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2008-12-17 15:48:50 -08:00
										 |  |  | 	gp->sk = sk; | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	gp->dev = dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	netif_stop_queue(dev); | 
					
						
							|  |  |  | 	err = register_netdev(dev); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	if (err) { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 		free_netdev(dev); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07: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 01:18:31 -08: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 11:16:16 -07: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 01:18:31 -08:00
										 |  |  | 	printk(KERN_DEBUG"%s: attached\n", dev->name); | 
					
						
							|  |  |  | 	return dev->ifindex; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | out_rel: | 
					
						
							|  |  |  | 	release_sock(sk); | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	unregister_netdev(dev); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void gprs_detach(struct sock *sk) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	struct gprs_dev *gp = sk->sk_user_data; | 
					
						
							|  |  |  | 	struct net_device *dev = gp->dev; | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	lock_sock(sk); | 
					
						
							|  |  |  | 	sk->sk_user_data	= NULL; | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08: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 11:16:16 -07:00
										 |  |  | 	release_sock(sk); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-16 01:18:31 -08:00
										 |  |  | 	printk(KERN_DEBUG"%s: detached\n", dev->name); | 
					
						
							|  |  |  | 	unregister_netdev(dev); | 
					
						
							| 
									
										
										
										
											2008-10-05 11:16:16 -07:00
										 |  |  | 	sock_put(sk); | 
					
						
							|  |  |  | } |