162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/proc_fs.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/fs.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/sysctl.h>
1362306a36Sopenharmony_ci#include <linux/string.h>
1462306a36Sopenharmony_ci#include <linux/socket.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/fcntl.h>
1762306a36Sopenharmony_ci#include <linux/in.h>
1862306a36Sopenharmony_ci#include <linux/if_ether.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <asm/io.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/inet.h>
2462306a36Sopenharmony_ci#include <linux/netdevice.h>
2562306a36Sopenharmony_ci#include <linux/etherdevice.h>
2662306a36Sopenharmony_ci#include <linux/if_arp.h>
2762306a36Sopenharmony_ci#include <linux/skbuff.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <net/ip.h>
3062306a36Sopenharmony_ci#include <net/arp.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <net/ax25.h>
3362306a36Sopenharmony_ci#include <net/rose.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int rose_header(struct sk_buff *skb, struct net_device *dev,
3662306a36Sopenharmony_ci		       unsigned short type,
3762306a36Sopenharmony_ci		       const void *daddr, const void *saddr, unsigned int len)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	unsigned char *buff = skb_push(skb, ROSE_MIN_LEN + 2);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (daddr)
4262306a36Sopenharmony_ci		memcpy(buff + 7, daddr, dev->addr_len);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	*buff++ = ROSE_GFI | ROSE_Q_BIT;
4562306a36Sopenharmony_ci	*buff++ = 0x00;
4662306a36Sopenharmony_ci	*buff++ = ROSE_DATA;
4762306a36Sopenharmony_ci	*buff++ = 0x7F;
4862306a36Sopenharmony_ci	*buff++ = AX25_P_IP;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (daddr != NULL)
5162306a36Sopenharmony_ci		return 37;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return -37;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int rose_set_mac_address(struct net_device *dev, void *addr)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct sockaddr *sa = addr;
5962306a36Sopenharmony_ci	int err;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!memcmp(dev->dev_addr, sa->sa_data, dev->addr_len))
6262306a36Sopenharmony_ci		return 0;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (dev->flags & IFF_UP) {
6562306a36Sopenharmony_ci		err = rose_add_loopback_node((rose_address *)sa->sa_data);
6662306a36Sopenharmony_ci		if (err)
6762306a36Sopenharmony_ci			return err;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		rose_del_loopback_node((const rose_address *)dev->dev_addr);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	dev_addr_set(dev, sa->sa_data);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int rose_open(struct net_device *dev)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	int err;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	err = rose_add_loopback_node((const rose_address *)dev->dev_addr);
8262306a36Sopenharmony_ci	if (err)
8362306a36Sopenharmony_ci		return err;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	netif_start_queue(dev);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int rose_close(struct net_device *dev)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	netif_stop_queue(dev);
9362306a36Sopenharmony_ci	rose_del_loopback_node((const rose_address *)dev->dev_addr);
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic netdev_tx_t rose_xmit(struct sk_buff *skb, struct net_device *dev)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
10062306a36Sopenharmony_ci	unsigned int len = skb->len;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (!netif_running(dev)) {
10362306a36Sopenharmony_ci		printk(KERN_ERR "ROSE: rose_xmit - called when iface is down\n");
10462306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!rose_route_frame(skb, NULL)) {
10862306a36Sopenharmony_ci		dev_kfree_skb(skb);
10962306a36Sopenharmony_ci		stats->tx_errors++;
11062306a36Sopenharmony_ci		return NETDEV_TX_OK;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	stats->tx_packets++;
11462306a36Sopenharmony_ci	stats->tx_bytes += len;
11562306a36Sopenharmony_ci	return NETDEV_TX_OK;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic const struct header_ops rose_header_ops = {
11962306a36Sopenharmony_ci	.create	= rose_header,
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic const struct net_device_ops rose_netdev_ops = {
12362306a36Sopenharmony_ci	.ndo_open		= rose_open,
12462306a36Sopenharmony_ci	.ndo_stop		= rose_close,
12562306a36Sopenharmony_ci	.ndo_start_xmit		= rose_xmit,
12662306a36Sopenharmony_ci	.ndo_set_mac_address    = rose_set_mac_address,
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_civoid rose_setup(struct net_device *dev)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	dev->mtu		= ROSE_MAX_PACKET_SIZE - 2;
13262306a36Sopenharmony_ci	dev->netdev_ops		= &rose_netdev_ops;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	dev->header_ops		= &rose_header_ops;
13562306a36Sopenharmony_ci	dev->hard_header_len	= AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
13662306a36Sopenharmony_ci	dev->addr_len		= ROSE_ADDR_LEN;
13762306a36Sopenharmony_ci	dev->type		= ARPHRD_ROSE;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* New-style flags. */
14062306a36Sopenharmony_ci	dev->flags		= IFF_NOARP;
14162306a36Sopenharmony_ci}
142