xref: /openbmc/linux/net/phonet/pep-gprs.c (revision 2b27bdcc20958d644d04f9f12d683e52b37a5427)
1*2b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
202a47617SRémi Denis-Courmont /*
302a47617SRémi Denis-Courmont  * File: pep-gprs.c
402a47617SRémi Denis-Courmont  *
502a47617SRémi Denis-Courmont  * GPRS over Phonet pipe end point socket
602a47617SRémi Denis-Courmont  *
702a47617SRémi Denis-Courmont  * Copyright (C) 2008 Nokia Corporation.
802a47617SRémi Denis-Courmont  *
931fdc555SRémi Denis-Courmont  * Author: Rémi Denis-Courmont
1002a47617SRémi Denis-Courmont  */
1102a47617SRémi Denis-Courmont 
1202a47617SRémi Denis-Courmont #include <linux/kernel.h>
1302a47617SRémi Denis-Courmont #include <linux/netdevice.h>
1402a47617SRémi Denis-Courmont #include <linux/if_ether.h>
1502a47617SRémi Denis-Courmont #include <linux/if_arp.h>
1602a47617SRémi Denis-Courmont #include <net/sock.h>
1702a47617SRémi Denis-Courmont 
1802a47617SRémi Denis-Courmont #include <linux/if_phonet.h>
1902a47617SRémi Denis-Courmont #include <net/tcp_states.h>
2002a47617SRémi Denis-Courmont #include <net/phonet/gprs.h>
2102a47617SRémi Denis-Courmont 
2202a47617SRémi Denis-Courmont #define GPRS_DEFAULT_MTU 1400
2302a47617SRémi Denis-Courmont 
2402a47617SRémi Denis-Courmont struct gprs_dev {
2502a47617SRémi Denis-Courmont 	struct sock		*sk;
2602a47617SRémi Denis-Courmont 	void			(*old_state_change)(struct sock *);
27676d2369SDavid S. Miller 	void			(*old_data_ready)(struct sock *);
2802a47617SRémi Denis-Courmont 	void			(*old_write_space)(struct sock *);
2902a47617SRémi Denis-Courmont 
3009a2c3c0SRémi Denis-Courmont 	struct net_device	*dev;
3102a47617SRémi Denis-Courmont };
3202a47617SRémi Denis-Courmont 
335c7f0333SHarvey Harrison static __be16 gprs_type_trans(struct sk_buff *skb)
3402a47617SRémi Denis-Courmont {
3502a47617SRémi Denis-Courmont 	const u8 *pvfc;
3602a47617SRémi Denis-Courmont 	u8 buf;
3702a47617SRémi Denis-Courmont 
3802a47617SRémi Denis-Courmont 	pvfc = skb_header_pointer(skb, 0, 1, &buf);
3902a47617SRémi Denis-Courmont 	if (!pvfc)
405c7f0333SHarvey Harrison 		return htons(0);
4102a47617SRémi Denis-Courmont 	/* Look at IP version field */
4202a47617SRémi Denis-Courmont 	switch (*pvfc >> 4) {
4302a47617SRémi Denis-Courmont 	case 4:
4402a47617SRémi Denis-Courmont 		return htons(ETH_P_IP);
4502a47617SRémi Denis-Courmont 	case 6:
4602a47617SRémi Denis-Courmont 		return htons(ETH_P_IPV6);
4702a47617SRémi Denis-Courmont 	}
485c7f0333SHarvey Harrison 	return htons(0);
4902a47617SRémi Denis-Courmont }
5002a47617SRémi Denis-Courmont 
51893873f3SRémi Denis-Courmont static void gprs_writeable(struct gprs_dev *gp)
52893873f3SRémi Denis-Courmont {
53893873f3SRémi Denis-Courmont 	struct net_device *dev = gp->dev;
54893873f3SRémi Denis-Courmont 
55893873f3SRémi Denis-Courmont 	if (pep_writeable(gp->sk))
56893873f3SRémi Denis-Courmont 		netif_wake_queue(dev);
57893873f3SRémi Denis-Courmont }
58893873f3SRémi Denis-Courmont 
5902a47617SRémi Denis-Courmont /*
6002a47617SRémi Denis-Courmont  * Socket callbacks
6102a47617SRémi Denis-Courmont  */
6202a47617SRémi Denis-Courmont 
6302a47617SRémi Denis-Courmont static void gprs_state_change(struct sock *sk)
6402a47617SRémi Denis-Courmont {
6509a2c3c0SRémi Denis-Courmont 	struct gprs_dev *gp = sk->sk_user_data;
6602a47617SRémi Denis-Courmont 
6702a47617SRémi Denis-Courmont 	if (sk->sk_state == TCP_CLOSE_WAIT) {
6809a2c3c0SRémi Denis-Courmont 		struct net_device *dev = gp->dev;
6909a2c3c0SRémi Denis-Courmont 
7009a2c3c0SRémi Denis-Courmont 		netif_stop_queue(dev);
7109a2c3c0SRémi Denis-Courmont 		netif_carrier_off(dev);
7202a47617SRémi Denis-Courmont 	}
7302a47617SRémi Denis-Courmont }
7402a47617SRémi Denis-Courmont 
7509a2c3c0SRémi Denis-Courmont static int gprs_recv(struct gprs_dev *gp, struct sk_buff *skb)
7602a47617SRémi Denis-Courmont {
7709a2c3c0SRémi Denis-Courmont 	struct net_device *dev = gp->dev;
7802a47617SRémi Denis-Courmont 	int err = 0;
795c7f0333SHarvey Harrison 	__be16 protocol = gprs_type_trans(skb);
8002a47617SRémi Denis-Courmont 
8102a47617SRémi Denis-Courmont 	if (!protocol) {
8202a47617SRémi Denis-Courmont 		err = -EINVAL;
8302a47617SRémi Denis-Courmont 		goto drop;
8402a47617SRémi Denis-Courmont 	}
8502a47617SRémi Denis-Courmont 
86fc6a1107SRémi Denis-Courmont 	if (skb_headroom(skb) & 3) {
8702a47617SRémi Denis-Courmont 		struct sk_buff *rskb, *fs;
8802a47617SRémi Denis-Courmont 		int flen = 0;
8902a47617SRémi Denis-Courmont 
90fc6a1107SRémi Denis-Courmont 		/* Phonet Pipe data header may be misaligned (3 bytes),
9102a47617SRémi Denis-Courmont 		 * so wrap the IP packet as a single fragment of an head-less
9202a47617SRémi Denis-Courmont 		 * socket buffer. The network stack will pull what it needs,
9302a47617SRémi Denis-Courmont 		 * but at least, the whole IP payload is not memcpy'd. */
9409a2c3c0SRémi Denis-Courmont 		rskb = netdev_alloc_skb(dev, 0);
9502a47617SRémi Denis-Courmont 		if (!rskb) {
9602a47617SRémi Denis-Courmont 			err = -ENOBUFS;
9702a47617SRémi Denis-Courmont 			goto drop;
9802a47617SRémi Denis-Courmont 		}
9902a47617SRémi Denis-Courmont 		skb_shinfo(rskb)->frag_list = skb;
10002a47617SRémi Denis-Courmont 		rskb->len += skb->len;
10102a47617SRémi Denis-Courmont 		rskb->data_len += rskb->len;
10202a47617SRémi Denis-Courmont 		rskb->truesize += rskb->len;
10302a47617SRémi Denis-Courmont 
10402a47617SRémi Denis-Courmont 		/* Avoid nested fragments */
1055c313e9aSDavid S. Miller 		skb_walk_frags(skb, fs)
10602a47617SRémi Denis-Courmont 			flen += fs->len;
10702a47617SRémi Denis-Courmont 		skb->next = skb_shinfo(skb)->frag_list;
1085c313e9aSDavid S. Miller 		skb_frag_list_init(skb);
10902a47617SRémi Denis-Courmont 		skb->len -= flen;
11002a47617SRémi Denis-Courmont 		skb->data_len -= flen;
11102a47617SRémi Denis-Courmont 		skb->truesize -= flen;
11202a47617SRémi Denis-Courmont 
11302a47617SRémi Denis-Courmont 		skb = rskb;
11402a47617SRémi Denis-Courmont 	}
11502a47617SRémi Denis-Courmont 
11602a47617SRémi Denis-Courmont 	skb->protocol = protocol;
11702a47617SRémi Denis-Courmont 	skb_reset_mac_header(skb);
11809a2c3c0SRémi Denis-Courmont 	skb->dev = dev;
11902a47617SRémi Denis-Courmont 
12009a2c3c0SRémi Denis-Courmont 	if (likely(dev->flags & IFF_UP)) {
12109a2c3c0SRémi Denis-Courmont 		dev->stats.rx_packets++;
12209a2c3c0SRémi Denis-Courmont 		dev->stats.rx_bytes += skb->len;
12302a47617SRémi Denis-Courmont 		netif_rx(skb);
12402a47617SRémi Denis-Courmont 		skb = NULL;
12502a47617SRémi Denis-Courmont 	} else
12602a47617SRémi Denis-Courmont 		err = -ENODEV;
12702a47617SRémi Denis-Courmont 
12802a47617SRémi Denis-Courmont drop:
12902a47617SRémi Denis-Courmont 	if (skb) {
13002a47617SRémi Denis-Courmont 		dev_kfree_skb(skb);
13109a2c3c0SRémi Denis-Courmont 		dev->stats.rx_dropped++;
13202a47617SRémi Denis-Courmont 	}
13302a47617SRémi Denis-Courmont 	return err;
13402a47617SRémi Denis-Courmont }
13502a47617SRémi Denis-Courmont 
136676d2369SDavid S. Miller static void gprs_data_ready(struct sock *sk)
13702a47617SRémi Denis-Courmont {
13809a2c3c0SRémi Denis-Courmont 	struct gprs_dev *gp = sk->sk_user_data;
13902a47617SRémi Denis-Courmont 	struct sk_buff *skb;
14002a47617SRémi Denis-Courmont 
14102a47617SRémi Denis-Courmont 	while ((skb = pep_read(sk)) != NULL) {
14202a47617SRémi Denis-Courmont 		skb_orphan(skb);
14309a2c3c0SRémi Denis-Courmont 		gprs_recv(gp, skb);
14402a47617SRémi Denis-Courmont 	}
14502a47617SRémi Denis-Courmont }
14602a47617SRémi Denis-Courmont 
14702a47617SRémi Denis-Courmont static void gprs_write_space(struct sock *sk)
14802a47617SRémi Denis-Courmont {
14909a2c3c0SRémi Denis-Courmont 	struct gprs_dev *gp = sk->sk_user_data;
15002a47617SRémi Denis-Courmont 
151893873f3SRémi Denis-Courmont 	if (netif_running(gp->dev))
152893873f3SRémi Denis-Courmont 		gprs_writeable(gp);
15302a47617SRémi Denis-Courmont }
15402a47617SRémi Denis-Courmont 
15502a47617SRémi Denis-Courmont /*
15602a47617SRémi Denis-Courmont  * Network device callbacks
15702a47617SRémi Denis-Courmont  */
15802a47617SRémi Denis-Courmont 
1594798a2b8SRémi Denis-Courmont static int gprs_open(struct net_device *dev)
1604798a2b8SRémi Denis-Courmont {
1614798a2b8SRémi Denis-Courmont 	struct gprs_dev *gp = netdev_priv(dev);
1624798a2b8SRémi Denis-Courmont 
163893873f3SRémi Denis-Courmont 	gprs_writeable(gp);
1644798a2b8SRémi Denis-Courmont 	return 0;
1654798a2b8SRémi Denis-Courmont }
1664798a2b8SRémi Denis-Courmont 
1674798a2b8SRémi Denis-Courmont static int gprs_close(struct net_device *dev)
1684798a2b8SRémi Denis-Courmont {
1694798a2b8SRémi Denis-Courmont 	netif_stop_queue(dev);
1704798a2b8SRémi Denis-Courmont 	return 0;
1714798a2b8SRémi Denis-Courmont }
1724798a2b8SRémi Denis-Courmont 
173424efe9cSStephen Hemminger static netdev_tx_t gprs_xmit(struct sk_buff *skb, struct net_device *dev)
17402a47617SRémi Denis-Courmont {
17509a2c3c0SRémi Denis-Courmont 	struct gprs_dev *gp = netdev_priv(dev);
176893873f3SRémi Denis-Courmont 	struct sock *sk = gp->sk;
177893873f3SRémi Denis-Courmont 	int len, err;
17802a47617SRémi Denis-Courmont 
17902a47617SRémi Denis-Courmont 	switch (skb->protocol) {
18002a47617SRémi Denis-Courmont 	case  htons(ETH_P_IP):
18102a47617SRémi Denis-Courmont 	case  htons(ETH_P_IPV6):
18202a47617SRémi Denis-Courmont 		break;
18302a47617SRémi Denis-Courmont 	default:
18402a47617SRémi Denis-Courmont 		dev_kfree_skb(skb);
1856ed10654SPatrick McHardy 		return NETDEV_TX_OK;
18602a47617SRémi Denis-Courmont 	}
18702a47617SRémi Denis-Courmont 
18802a47617SRémi Denis-Courmont 	skb_orphan(skb);
18902a47617SRémi Denis-Courmont 	skb_set_owner_w(skb, sk);
190893873f3SRémi Denis-Courmont 	len = skb->len;
19102a47617SRémi Denis-Courmont 	err = pep_write(sk, skb);
19202a47617SRémi Denis-Courmont 	if (err) {
193ba7a46f1SJoe Perches 		net_dbg_ratelimited("%s: TX error (%d)\n", dev->name, err);
19409a2c3c0SRémi Denis-Courmont 		dev->stats.tx_aborted_errors++;
19509a2c3c0SRémi Denis-Courmont 		dev->stats.tx_errors++;
196893873f3SRémi Denis-Courmont 	} else {
197893873f3SRémi Denis-Courmont 		dev->stats.tx_packets++;
198893873f3SRémi Denis-Courmont 		dev->stats.tx_bytes += len;
19902a47617SRémi Denis-Courmont 	}
20002a47617SRémi Denis-Courmont 
201893873f3SRémi Denis-Courmont 	netif_stop_queue(dev);
202bbd5898dSRémi Denis-Courmont 	if (pep_writeable(sk))
203bbd5898dSRémi Denis-Courmont 		netif_wake_queue(dev);
2046ed10654SPatrick McHardy 	return NETDEV_TX_OK;
20502a47617SRémi Denis-Courmont }
20602a47617SRémi Denis-Courmont 
207ab638e69SStephen Hemminger static const struct net_device_ops gprs_netdev_ops = {
208ab638e69SStephen Hemminger 	.ndo_open	= gprs_open,
209ab638e69SStephen Hemminger 	.ndo_stop	= gprs_close,
210ab638e69SStephen Hemminger 	.ndo_start_xmit	= gprs_xmit,
211ab638e69SStephen Hemminger };
212ab638e69SStephen Hemminger 
21309a2c3c0SRémi Denis-Courmont static void gprs_setup(struct net_device *dev)
21402a47617SRémi Denis-Courmont {
21509a2c3c0SRémi Denis-Courmont 	dev->features		= NETIF_F_FRAGLIST;
21657c81fffSRémi Denis-Courmont 	dev->type		= ARPHRD_PHONET_PIPE;
21709a2c3c0SRémi Denis-Courmont 	dev->flags		= IFF_POINTOPOINT | IFF_NOARP;
21809a2c3c0SRémi Denis-Courmont 	dev->mtu		= GPRS_DEFAULT_MTU;
219b3e3893eSJarod Wilson 	dev->min_mtu		= 576;
220b3e3893eSJarod Wilson 	dev->max_mtu		= (PHONET_MAX_MTU - 11);
22109a2c3c0SRémi Denis-Courmont 	dev->hard_header_len	= 0;
22209a2c3c0SRémi Denis-Courmont 	dev->addr_len		= 0;
22309a2c3c0SRémi Denis-Courmont 	dev->tx_queue_len	= 10;
22402a47617SRémi Denis-Courmont 
225ab638e69SStephen Hemminger 	dev->netdev_ops		= &gprs_netdev_ops;
226cf124db5SDavid S. Miller 	dev->needs_free_netdev	= true;
22702a47617SRémi Denis-Courmont }
22802a47617SRémi Denis-Courmont 
22902a47617SRémi Denis-Courmont /*
23002a47617SRémi Denis-Courmont  * External interface
23102a47617SRémi Denis-Courmont  */
23202a47617SRémi Denis-Courmont 
23302a47617SRémi Denis-Courmont /*
23402a47617SRémi Denis-Courmont  * Attach a GPRS interface to a datagram socket.
23502a47617SRémi Denis-Courmont  * Returns the interface index on success, negative error code on error.
23602a47617SRémi Denis-Courmont  */
23702a47617SRémi Denis-Courmont int gprs_attach(struct sock *sk)
23802a47617SRémi Denis-Courmont {
23902a47617SRémi Denis-Courmont 	static const char ifname[] = "gprs%d";
24009a2c3c0SRémi Denis-Courmont 	struct gprs_dev *gp;
24109a2c3c0SRémi Denis-Courmont 	struct net_device *dev;
24202a47617SRémi Denis-Courmont 	int err;
24302a47617SRémi Denis-Courmont 
24402a47617SRémi Denis-Courmont 	if (unlikely(sk->sk_type == SOCK_STREAM))
24502a47617SRémi Denis-Courmont 		return -EINVAL; /* need packet boundaries */
24602a47617SRémi Denis-Courmont 
24702a47617SRémi Denis-Courmont 	/* Create net device */
248c835a677STom Gundersen 	dev = alloc_netdev(sizeof(*gp), ifname, NET_NAME_UNKNOWN, gprs_setup);
24909a2c3c0SRémi Denis-Courmont 	if (!dev)
25002a47617SRémi Denis-Courmont 		return -ENOMEM;
25109a2c3c0SRémi Denis-Courmont 	gp = netdev_priv(dev);
252893873f3SRémi Denis-Courmont 	gp->sk = sk;
25309a2c3c0SRémi Denis-Courmont 	gp->dev = dev;
25402a47617SRémi Denis-Courmont 
25509a2c3c0SRémi Denis-Courmont 	netif_stop_queue(dev);
25609a2c3c0SRémi Denis-Courmont 	err = register_netdev(dev);
25702a47617SRémi Denis-Courmont 	if (err) {
25809a2c3c0SRémi Denis-Courmont 		free_netdev(dev);
25902a47617SRémi Denis-Courmont 		return err;
26002a47617SRémi Denis-Courmont 	}
26102a47617SRémi Denis-Courmont 
26202a47617SRémi Denis-Courmont 	lock_sock(sk);
26302a47617SRémi Denis-Courmont 	if (unlikely(sk->sk_user_data)) {
26402a47617SRémi Denis-Courmont 		err = -EBUSY;
26502a47617SRémi Denis-Courmont 		goto out_rel;
26602a47617SRémi Denis-Courmont 	}
26702a47617SRémi Denis-Courmont 	if (unlikely((1 << sk->sk_state & (TCPF_CLOSE|TCPF_LISTEN)) ||
26802a47617SRémi Denis-Courmont 			sock_flag(sk, SOCK_DEAD))) {
26902a47617SRémi Denis-Courmont 		err = -EINVAL;
27002a47617SRémi Denis-Courmont 		goto out_rel;
27102a47617SRémi Denis-Courmont 	}
27209a2c3c0SRémi Denis-Courmont 	sk->sk_user_data	= gp;
27309a2c3c0SRémi Denis-Courmont 	gp->old_state_change	= sk->sk_state_change;
27409a2c3c0SRémi Denis-Courmont 	gp->old_data_ready	= sk->sk_data_ready;
27509a2c3c0SRémi Denis-Courmont 	gp->old_write_space	= sk->sk_write_space;
27602a47617SRémi Denis-Courmont 	sk->sk_state_change	= gprs_state_change;
27702a47617SRémi Denis-Courmont 	sk->sk_data_ready	= gprs_data_ready;
27802a47617SRémi Denis-Courmont 	sk->sk_write_space	= gprs_write_space;
27902a47617SRémi Denis-Courmont 	release_sock(sk);
28002a47617SRémi Denis-Courmont 	sock_hold(sk);
28102a47617SRémi Denis-Courmont 
28209a2c3c0SRémi Denis-Courmont 	printk(KERN_DEBUG"%s: attached\n", dev->name);
28309a2c3c0SRémi Denis-Courmont 	return dev->ifindex;
28402a47617SRémi Denis-Courmont 
28502a47617SRémi Denis-Courmont out_rel:
28602a47617SRémi Denis-Courmont 	release_sock(sk);
28709a2c3c0SRémi Denis-Courmont 	unregister_netdev(dev);
28802a47617SRémi Denis-Courmont 	return err;
28902a47617SRémi Denis-Courmont }
29002a47617SRémi Denis-Courmont 
29102a47617SRémi Denis-Courmont void gprs_detach(struct sock *sk)
29202a47617SRémi Denis-Courmont {
29309a2c3c0SRémi Denis-Courmont 	struct gprs_dev *gp = sk->sk_user_data;
29409a2c3c0SRémi Denis-Courmont 	struct net_device *dev = gp->dev;
29502a47617SRémi Denis-Courmont 
29602a47617SRémi Denis-Courmont 	lock_sock(sk);
29702a47617SRémi Denis-Courmont 	sk->sk_user_data	= NULL;
29809a2c3c0SRémi Denis-Courmont 	sk->sk_state_change	= gp->old_state_change;
29909a2c3c0SRémi Denis-Courmont 	sk->sk_data_ready	= gp->old_data_ready;
30009a2c3c0SRémi Denis-Courmont 	sk->sk_write_space	= gp->old_write_space;
30102a47617SRémi Denis-Courmont 	release_sock(sk);
30202a47617SRémi Denis-Courmont 
30309a2c3c0SRémi Denis-Courmont 	printk(KERN_DEBUG"%s: detached\n", dev->name);
30409a2c3c0SRémi Denis-Courmont 	unregister_netdev(dev);
30502a47617SRémi Denis-Courmont 	sock_put(sk);
30602a47617SRémi Denis-Courmont }
307