102a47617SRémi Denis-Courmont /* 202a47617SRémi Denis-Courmont * File: pep-gprs.c 302a47617SRémi Denis-Courmont * 402a47617SRémi Denis-Courmont * GPRS over Phonet pipe end point socket 502a47617SRémi Denis-Courmont * 602a47617SRémi Denis-Courmont * Copyright (C) 2008 Nokia Corporation. 702a47617SRémi Denis-Courmont * 802a47617SRémi Denis-Courmont * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> 902a47617SRémi Denis-Courmont * 1002a47617SRémi Denis-Courmont * This program is free software; you can redistribute it and/or 1102a47617SRémi Denis-Courmont * modify it under the terms of the GNU General Public License 1202a47617SRémi Denis-Courmont * version 2 as published by the Free Software Foundation. 1302a47617SRémi Denis-Courmont * 1402a47617SRémi Denis-Courmont * This program is distributed in the hope that it will be useful, but 1502a47617SRémi Denis-Courmont * WITHOUT ANY WARRANTY; without even the implied warranty of 1602a47617SRémi Denis-Courmont * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1702a47617SRémi Denis-Courmont * General Public License for more details. 1802a47617SRémi Denis-Courmont * 1902a47617SRémi Denis-Courmont * You should have received a copy of the GNU General Public License 2002a47617SRémi Denis-Courmont * along with this program; if not, write to the Free Software 2102a47617SRémi Denis-Courmont * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 2202a47617SRémi Denis-Courmont * 02110-1301 USA 2302a47617SRémi Denis-Courmont */ 2402a47617SRémi Denis-Courmont 2502a47617SRémi Denis-Courmont #include <linux/kernel.h> 2602a47617SRémi Denis-Courmont #include <linux/netdevice.h> 2702a47617SRémi Denis-Courmont #include <linux/if_ether.h> 2802a47617SRémi Denis-Courmont #include <linux/if_arp.h> 2902a47617SRémi Denis-Courmont #include <net/sock.h> 3002a47617SRémi Denis-Courmont 3102a47617SRémi Denis-Courmont #include <linux/if_phonet.h> 3202a47617SRémi Denis-Courmont #include <net/tcp_states.h> 3302a47617SRémi Denis-Courmont #include <net/phonet/gprs.h> 3402a47617SRémi Denis-Courmont 3502a47617SRémi Denis-Courmont #define GPRS_DEFAULT_MTU 1400 3602a47617SRémi Denis-Courmont 3702a47617SRémi Denis-Courmont struct gprs_dev { 3802a47617SRémi Denis-Courmont struct sock *sk; 3902a47617SRémi Denis-Courmont void (*old_state_change)(struct sock *); 4002a47617SRémi Denis-Courmont void (*old_data_ready)(struct sock *, int); 4102a47617SRémi Denis-Courmont void (*old_write_space)(struct sock *); 4202a47617SRémi Denis-Courmont 43*09a2c3c0SRémi Denis-Courmont struct net_device *dev; 4402a47617SRémi Denis-Courmont 4502a47617SRémi Denis-Courmont struct sk_buff_head tx_queue; 4602a47617SRémi Denis-Courmont struct work_struct tx_work; 4702a47617SRémi Denis-Courmont spinlock_t tx_lock; 4802a47617SRémi Denis-Courmont unsigned tx_max; 4902a47617SRémi Denis-Courmont }; 5002a47617SRémi Denis-Courmont 515c7f0333SHarvey Harrison static __be16 gprs_type_trans(struct sk_buff *skb) 5202a47617SRémi Denis-Courmont { 5302a47617SRémi Denis-Courmont const u8 *pvfc; 5402a47617SRémi Denis-Courmont u8 buf; 5502a47617SRémi Denis-Courmont 5602a47617SRémi Denis-Courmont pvfc = skb_header_pointer(skb, 0, 1, &buf); 5702a47617SRémi Denis-Courmont if (!pvfc) 585c7f0333SHarvey Harrison return htons(0); 5902a47617SRémi Denis-Courmont /* Look at IP version field */ 6002a47617SRémi Denis-Courmont switch (*pvfc >> 4) { 6102a47617SRémi Denis-Courmont case 4: 6202a47617SRémi Denis-Courmont return htons(ETH_P_IP); 6302a47617SRémi Denis-Courmont case 6: 6402a47617SRémi Denis-Courmont return htons(ETH_P_IPV6); 6502a47617SRémi Denis-Courmont } 665c7f0333SHarvey Harrison return htons(0); 6702a47617SRémi Denis-Courmont } 6802a47617SRémi Denis-Courmont 6902a47617SRémi Denis-Courmont /* 7002a47617SRémi Denis-Courmont * Socket callbacks 7102a47617SRémi Denis-Courmont */ 7202a47617SRémi Denis-Courmont 7302a47617SRémi Denis-Courmont static void gprs_state_change(struct sock *sk) 7402a47617SRémi Denis-Courmont { 75*09a2c3c0SRémi Denis-Courmont struct gprs_dev *gp = sk->sk_user_data; 7602a47617SRémi Denis-Courmont 7702a47617SRémi Denis-Courmont if (sk->sk_state == TCP_CLOSE_WAIT) { 78*09a2c3c0SRémi Denis-Courmont struct net_device *dev = gp->dev; 79*09a2c3c0SRémi Denis-Courmont 80*09a2c3c0SRémi Denis-Courmont netif_stop_queue(dev); 81*09a2c3c0SRémi Denis-Courmont netif_carrier_off(dev); 8202a47617SRémi Denis-Courmont } 8302a47617SRémi Denis-Courmont } 8402a47617SRémi Denis-Courmont 85*09a2c3c0SRémi Denis-Courmont static int gprs_recv(struct gprs_dev *gp, struct sk_buff *skb) 8602a47617SRémi Denis-Courmont { 87*09a2c3c0SRémi Denis-Courmont struct net_device *dev = gp->dev; 8802a47617SRémi Denis-Courmont int err = 0; 895c7f0333SHarvey Harrison __be16 protocol = gprs_type_trans(skb); 9002a47617SRémi Denis-Courmont 9102a47617SRémi Denis-Courmont if (!protocol) { 9202a47617SRémi Denis-Courmont err = -EINVAL; 9302a47617SRémi Denis-Courmont goto drop; 9402a47617SRémi Denis-Courmont } 9502a47617SRémi Denis-Courmont 9602a47617SRémi Denis-Courmont if (likely(skb_headroom(skb) & 3)) { 9702a47617SRémi Denis-Courmont struct sk_buff *rskb, *fs; 9802a47617SRémi Denis-Courmont int flen = 0; 9902a47617SRémi Denis-Courmont 10002a47617SRémi Denis-Courmont /* Phonet Pipe data header is misaligned (3 bytes), 10102a47617SRémi Denis-Courmont * so wrap the IP packet as a single fragment of an head-less 10202a47617SRémi Denis-Courmont * socket buffer. The network stack will pull what it needs, 10302a47617SRémi Denis-Courmont * but at least, the whole IP payload is not memcpy'd. */ 104*09a2c3c0SRémi Denis-Courmont rskb = netdev_alloc_skb(dev, 0); 10502a47617SRémi Denis-Courmont if (!rskb) { 10602a47617SRémi Denis-Courmont err = -ENOBUFS; 10702a47617SRémi Denis-Courmont goto drop; 10802a47617SRémi Denis-Courmont } 10902a47617SRémi Denis-Courmont skb_shinfo(rskb)->frag_list = skb; 11002a47617SRémi Denis-Courmont rskb->len += skb->len; 11102a47617SRémi Denis-Courmont rskb->data_len += rskb->len; 11202a47617SRémi Denis-Courmont rskb->truesize += rskb->len; 11302a47617SRémi Denis-Courmont 11402a47617SRémi Denis-Courmont /* Avoid nested fragments */ 11502a47617SRémi Denis-Courmont for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next) 11602a47617SRémi Denis-Courmont flen += fs->len; 11702a47617SRémi Denis-Courmont skb->next = skb_shinfo(skb)->frag_list; 11802a47617SRémi Denis-Courmont skb_shinfo(skb)->frag_list = NULL; 11902a47617SRémi Denis-Courmont skb->len -= flen; 12002a47617SRémi Denis-Courmont skb->data_len -= flen; 12102a47617SRémi Denis-Courmont skb->truesize -= flen; 12202a47617SRémi Denis-Courmont 12302a47617SRémi Denis-Courmont skb = rskb; 12402a47617SRémi Denis-Courmont } 12502a47617SRémi Denis-Courmont 12602a47617SRémi Denis-Courmont skb->protocol = protocol; 12702a47617SRémi Denis-Courmont skb_reset_mac_header(skb); 128*09a2c3c0SRémi Denis-Courmont skb->dev = dev; 12902a47617SRémi Denis-Courmont 130*09a2c3c0SRémi Denis-Courmont if (likely(dev->flags & IFF_UP)) { 131*09a2c3c0SRémi Denis-Courmont dev->stats.rx_packets++; 132*09a2c3c0SRémi Denis-Courmont dev->stats.rx_bytes += skb->len; 13302a47617SRémi Denis-Courmont netif_rx(skb); 13402a47617SRémi Denis-Courmont skb = NULL; 13502a47617SRémi Denis-Courmont } else 13602a47617SRémi Denis-Courmont err = -ENODEV; 13702a47617SRémi Denis-Courmont 13802a47617SRémi Denis-Courmont drop: 13902a47617SRémi Denis-Courmont if (skb) { 14002a47617SRémi Denis-Courmont dev_kfree_skb(skb); 141*09a2c3c0SRémi Denis-Courmont dev->stats.rx_dropped++; 14202a47617SRémi Denis-Courmont } 14302a47617SRémi Denis-Courmont return err; 14402a47617SRémi Denis-Courmont } 14502a47617SRémi Denis-Courmont 14602a47617SRémi Denis-Courmont static void gprs_data_ready(struct sock *sk, int len) 14702a47617SRémi Denis-Courmont { 148*09a2c3c0SRémi Denis-Courmont struct gprs_dev *gp = sk->sk_user_data; 14902a47617SRémi Denis-Courmont struct sk_buff *skb; 15002a47617SRémi Denis-Courmont 15102a47617SRémi Denis-Courmont while ((skb = pep_read(sk)) != NULL) { 15202a47617SRémi Denis-Courmont skb_orphan(skb); 153*09a2c3c0SRémi Denis-Courmont gprs_recv(gp, skb); 15402a47617SRémi Denis-Courmont } 15502a47617SRémi Denis-Courmont } 15602a47617SRémi Denis-Courmont 15702a47617SRémi Denis-Courmont static void gprs_write_space(struct sock *sk) 15802a47617SRémi Denis-Courmont { 159*09a2c3c0SRémi Denis-Courmont struct gprs_dev *gp = sk->sk_user_data; 160*09a2c3c0SRémi Denis-Courmont struct net_device *dev = gp->dev; 16102a47617SRémi Denis-Courmont unsigned credits = pep_writeable(sk); 16202a47617SRémi Denis-Courmont 163*09a2c3c0SRémi Denis-Courmont spin_lock_bh(&gp->tx_lock); 164*09a2c3c0SRémi Denis-Courmont gp->tx_max = credits; 165*09a2c3c0SRémi Denis-Courmont if (credits > skb_queue_len(&gp->tx_queue) && netif_running(dev)) 166*09a2c3c0SRémi Denis-Courmont netif_wake_queue(dev); 167*09a2c3c0SRémi Denis-Courmont spin_unlock_bh(&gp->tx_lock); 16802a47617SRémi Denis-Courmont } 16902a47617SRémi Denis-Courmont 17002a47617SRémi Denis-Courmont /* 17102a47617SRémi Denis-Courmont * Network device callbacks 17202a47617SRémi Denis-Courmont */ 17302a47617SRémi Denis-Courmont 1744798a2b8SRémi Denis-Courmont static int gprs_open(struct net_device *dev) 1754798a2b8SRémi Denis-Courmont { 1764798a2b8SRémi Denis-Courmont struct gprs_dev *gp = netdev_priv(dev); 1774798a2b8SRémi Denis-Courmont 1784798a2b8SRémi Denis-Courmont gprs_write_space(gp->sk); 1794798a2b8SRémi Denis-Courmont return 0; 1804798a2b8SRémi Denis-Courmont } 1814798a2b8SRémi Denis-Courmont 1824798a2b8SRémi Denis-Courmont static int gprs_close(struct net_device *dev) 1834798a2b8SRémi Denis-Courmont { 1844798a2b8SRémi Denis-Courmont struct gprs_dev *gp = netdev_priv(dev); 1854798a2b8SRémi Denis-Courmont 1864798a2b8SRémi Denis-Courmont netif_stop_queue(dev); 1874798a2b8SRémi Denis-Courmont flush_work(&gp->tx_work); 1884798a2b8SRémi Denis-Courmont return 0; 1894798a2b8SRémi Denis-Courmont } 1904798a2b8SRémi Denis-Courmont 191*09a2c3c0SRémi Denis-Courmont static int gprs_xmit(struct sk_buff *skb, struct net_device *dev) 19202a47617SRémi Denis-Courmont { 193*09a2c3c0SRémi Denis-Courmont struct gprs_dev *gp = netdev_priv(dev); 19402a47617SRémi Denis-Courmont 19502a47617SRémi Denis-Courmont switch (skb->protocol) { 19602a47617SRémi Denis-Courmont case htons(ETH_P_IP): 19702a47617SRémi Denis-Courmont case htons(ETH_P_IPV6): 19802a47617SRémi Denis-Courmont break; 19902a47617SRémi Denis-Courmont default: 20002a47617SRémi Denis-Courmont dev_kfree_skb(skb); 20102a47617SRémi Denis-Courmont return 0; 20202a47617SRémi Denis-Courmont } 20302a47617SRémi Denis-Courmont 204*09a2c3c0SRémi Denis-Courmont spin_lock(&gp->tx_lock); 205*09a2c3c0SRémi Denis-Courmont if (likely(skb_queue_len(&gp->tx_queue) < gp->tx_max)) { 206*09a2c3c0SRémi Denis-Courmont skb_queue_tail(&gp->tx_queue, skb); 20702a47617SRémi Denis-Courmont skb = NULL; 20802a47617SRémi Denis-Courmont } 209*09a2c3c0SRémi Denis-Courmont if (skb_queue_len(&gp->tx_queue) >= gp->tx_max) 210*09a2c3c0SRémi Denis-Courmont netif_stop_queue(dev); 211*09a2c3c0SRémi Denis-Courmont spin_unlock(&gp->tx_lock); 21202a47617SRémi Denis-Courmont 213*09a2c3c0SRémi Denis-Courmont schedule_work(&gp->tx_work); 21402a47617SRémi Denis-Courmont if (unlikely(skb)) 21502a47617SRémi Denis-Courmont dev_kfree_skb(skb); 21602a47617SRémi Denis-Courmont return 0; 21702a47617SRémi Denis-Courmont } 21802a47617SRémi Denis-Courmont 21902a47617SRémi Denis-Courmont static void gprs_tx(struct work_struct *work) 22002a47617SRémi Denis-Courmont { 221*09a2c3c0SRémi Denis-Courmont struct gprs_dev *gp = container_of(work, struct gprs_dev, tx_work); 222*09a2c3c0SRémi Denis-Courmont struct net_device *dev = gp->dev; 223*09a2c3c0SRémi Denis-Courmont struct sock *sk = gp->sk; 22402a47617SRémi Denis-Courmont struct sk_buff *skb; 22502a47617SRémi Denis-Courmont 226*09a2c3c0SRémi Denis-Courmont while ((skb = skb_dequeue(&gp->tx_queue)) != NULL) { 22702a47617SRémi Denis-Courmont int err; 22802a47617SRémi Denis-Courmont 229*09a2c3c0SRémi Denis-Courmont dev->stats.tx_bytes += skb->len; 230*09a2c3c0SRémi Denis-Courmont dev->stats.tx_packets++; 23102a47617SRémi Denis-Courmont 23202a47617SRémi Denis-Courmont skb_orphan(skb); 23302a47617SRémi Denis-Courmont skb_set_owner_w(skb, sk); 23402a47617SRémi Denis-Courmont 23502a47617SRémi Denis-Courmont lock_sock(sk); 23602a47617SRémi Denis-Courmont err = pep_write(sk, skb); 23702a47617SRémi Denis-Courmont if (err) { 23802a47617SRémi Denis-Courmont LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n", 239*09a2c3c0SRémi Denis-Courmont dev->name, err); 240*09a2c3c0SRémi Denis-Courmont dev->stats.tx_aborted_errors++; 241*09a2c3c0SRémi Denis-Courmont dev->stats.tx_errors++; 24202a47617SRémi Denis-Courmont } 24302a47617SRémi Denis-Courmont release_sock(sk); 24402a47617SRémi Denis-Courmont } 24502a47617SRémi Denis-Courmont 24602a47617SRémi Denis-Courmont lock_sock(sk); 24702a47617SRémi Denis-Courmont gprs_write_space(sk); 24802a47617SRémi Denis-Courmont release_sock(sk); 24902a47617SRémi Denis-Courmont } 25002a47617SRémi Denis-Courmont 251*09a2c3c0SRémi Denis-Courmont static int gprs_set_mtu(struct net_device *dev, int new_mtu) 25202a47617SRémi Denis-Courmont { 25302a47617SRémi Denis-Courmont if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11))) 25402a47617SRémi Denis-Courmont return -EINVAL; 25502a47617SRémi Denis-Courmont 256*09a2c3c0SRémi Denis-Courmont dev->mtu = new_mtu; 25702a47617SRémi Denis-Courmont return 0; 25802a47617SRémi Denis-Courmont } 25902a47617SRémi Denis-Courmont 260*09a2c3c0SRémi Denis-Courmont static void gprs_setup(struct net_device *dev) 26102a47617SRémi Denis-Courmont { 262*09a2c3c0SRémi Denis-Courmont dev->features = NETIF_F_FRAGLIST; 263*09a2c3c0SRémi Denis-Courmont dev->type = ARPHRD_NONE; 264*09a2c3c0SRémi Denis-Courmont dev->flags = IFF_POINTOPOINT | IFF_NOARP; 265*09a2c3c0SRémi Denis-Courmont dev->mtu = GPRS_DEFAULT_MTU; 266*09a2c3c0SRémi Denis-Courmont dev->hard_header_len = 0; 267*09a2c3c0SRémi Denis-Courmont dev->addr_len = 0; 268*09a2c3c0SRémi Denis-Courmont dev->tx_queue_len = 10; 26902a47617SRémi Denis-Courmont 270*09a2c3c0SRémi Denis-Courmont dev->destructor = free_netdev; 271*09a2c3c0SRémi Denis-Courmont dev->open = gprs_open; 272*09a2c3c0SRémi Denis-Courmont dev->stop = gprs_close; 273*09a2c3c0SRémi Denis-Courmont dev->hard_start_xmit = gprs_xmit; /* mandatory */ 274*09a2c3c0SRémi Denis-Courmont dev->change_mtu = gprs_set_mtu; 27502a47617SRémi Denis-Courmont } 27602a47617SRémi Denis-Courmont 27702a47617SRémi Denis-Courmont /* 27802a47617SRémi Denis-Courmont * External interface 27902a47617SRémi Denis-Courmont */ 28002a47617SRémi Denis-Courmont 28102a47617SRémi Denis-Courmont /* 28202a47617SRémi Denis-Courmont * Attach a GPRS interface to a datagram socket. 28302a47617SRémi Denis-Courmont * Returns the interface index on success, negative error code on error. 28402a47617SRémi Denis-Courmont */ 28502a47617SRémi Denis-Courmont int gprs_attach(struct sock *sk) 28602a47617SRémi Denis-Courmont { 28702a47617SRémi Denis-Courmont static const char ifname[] = "gprs%d"; 288*09a2c3c0SRémi Denis-Courmont struct gprs_dev *gp; 289*09a2c3c0SRémi Denis-Courmont struct net_device *dev; 29002a47617SRémi Denis-Courmont int err; 29102a47617SRémi Denis-Courmont 29202a47617SRémi Denis-Courmont if (unlikely(sk->sk_type == SOCK_STREAM)) 29302a47617SRémi Denis-Courmont return -EINVAL; /* need packet boundaries */ 29402a47617SRémi Denis-Courmont 29502a47617SRémi Denis-Courmont /* Create net device */ 296*09a2c3c0SRémi Denis-Courmont dev = alloc_netdev(sizeof(*gp), ifname, gprs_setup); 297*09a2c3c0SRémi Denis-Courmont if (!dev) 29802a47617SRémi Denis-Courmont return -ENOMEM; 299*09a2c3c0SRémi Denis-Courmont gp = netdev_priv(dev); 300*09a2c3c0SRémi Denis-Courmont gp->dev = dev; 301*09a2c3c0SRémi Denis-Courmont gp->tx_max = 0; 302*09a2c3c0SRémi Denis-Courmont spin_lock_init(&gp->tx_lock); 303*09a2c3c0SRémi Denis-Courmont skb_queue_head_init(&gp->tx_queue); 304*09a2c3c0SRémi Denis-Courmont INIT_WORK(&gp->tx_work, gprs_tx); 30502a47617SRémi Denis-Courmont 306*09a2c3c0SRémi Denis-Courmont netif_stop_queue(dev); 307*09a2c3c0SRémi Denis-Courmont err = register_netdev(dev); 30802a47617SRémi Denis-Courmont if (err) { 309*09a2c3c0SRémi Denis-Courmont free_netdev(dev); 31002a47617SRémi Denis-Courmont return err; 31102a47617SRémi Denis-Courmont } 31202a47617SRémi Denis-Courmont 31302a47617SRémi Denis-Courmont lock_sock(sk); 31402a47617SRémi Denis-Courmont if (unlikely(sk->sk_user_data)) { 31502a47617SRémi Denis-Courmont err = -EBUSY; 31602a47617SRémi Denis-Courmont goto out_rel; 31702a47617SRémi Denis-Courmont } 31802a47617SRémi Denis-Courmont if (unlikely((1 << sk->sk_state & (TCPF_CLOSE|TCPF_LISTEN)) || 31902a47617SRémi Denis-Courmont sock_flag(sk, SOCK_DEAD))) { 32002a47617SRémi Denis-Courmont err = -EINVAL; 32102a47617SRémi Denis-Courmont goto out_rel; 32202a47617SRémi Denis-Courmont } 323*09a2c3c0SRémi Denis-Courmont sk->sk_user_data = gp; 324*09a2c3c0SRémi Denis-Courmont gp->old_state_change = sk->sk_state_change; 325*09a2c3c0SRémi Denis-Courmont gp->old_data_ready = sk->sk_data_ready; 326*09a2c3c0SRémi Denis-Courmont gp->old_write_space = sk->sk_write_space; 32702a47617SRémi Denis-Courmont sk->sk_state_change = gprs_state_change; 32802a47617SRémi Denis-Courmont sk->sk_data_ready = gprs_data_ready; 32902a47617SRémi Denis-Courmont sk->sk_write_space = gprs_write_space; 33002a47617SRémi Denis-Courmont release_sock(sk); 33102a47617SRémi Denis-Courmont 33202a47617SRémi Denis-Courmont sock_hold(sk); 333*09a2c3c0SRémi Denis-Courmont gp->sk = sk; 33402a47617SRémi Denis-Courmont 335*09a2c3c0SRémi Denis-Courmont printk(KERN_DEBUG"%s: attached\n", dev->name); 336*09a2c3c0SRémi Denis-Courmont return dev->ifindex; 33702a47617SRémi Denis-Courmont 33802a47617SRémi Denis-Courmont out_rel: 33902a47617SRémi Denis-Courmont release_sock(sk); 340*09a2c3c0SRémi Denis-Courmont unregister_netdev(dev); 34102a47617SRémi Denis-Courmont return err; 34202a47617SRémi Denis-Courmont } 34302a47617SRémi Denis-Courmont 34402a47617SRémi Denis-Courmont void gprs_detach(struct sock *sk) 34502a47617SRémi Denis-Courmont { 346*09a2c3c0SRémi Denis-Courmont struct gprs_dev *gp = sk->sk_user_data; 347*09a2c3c0SRémi Denis-Courmont struct net_device *dev = gp->dev; 34802a47617SRémi Denis-Courmont 34902a47617SRémi Denis-Courmont lock_sock(sk); 35002a47617SRémi Denis-Courmont sk->sk_user_data = NULL; 351*09a2c3c0SRémi Denis-Courmont sk->sk_state_change = gp->old_state_change; 352*09a2c3c0SRémi Denis-Courmont sk->sk_data_ready = gp->old_data_ready; 353*09a2c3c0SRémi Denis-Courmont sk->sk_write_space = gp->old_write_space; 35402a47617SRémi Denis-Courmont release_sock(sk); 35502a47617SRémi Denis-Courmont 356*09a2c3c0SRémi Denis-Courmont printk(KERN_DEBUG"%s: detached\n", dev->name); 357*09a2c3c0SRémi Denis-Courmont unregister_netdev(dev); 35802a47617SRémi Denis-Courmont sock_put(sk); 35902a47617SRémi Denis-Courmont } 360