162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IPv4 over IEEE 1394, per RFC 2734
462306a36Sopenharmony_ci * IPv6 over IEEE 1394, per RFC 3146
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * based on eth1394 by Ben Collins et al
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/bug.h>
1262306a36Sopenharmony_ci#include <linux/compiler.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/ethtool.h>
1662306a36Sopenharmony_ci#include <linux/firewire.h>
1762306a36Sopenharmony_ci#include <linux/firewire-constants.h>
1862306a36Sopenharmony_ci#include <linux/highmem.h>
1962306a36Sopenharmony_ci#include <linux/in.h>
2062306a36Sopenharmony_ci#include <linux/ip.h>
2162306a36Sopenharmony_ci#include <linux/jiffies.h>
2262306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <linux/moduleparam.h>
2562306a36Sopenharmony_ci#include <linux/mutex.h>
2662306a36Sopenharmony_ci#include <linux/netdevice.h>
2762306a36Sopenharmony_ci#include <linux/skbuff.h>
2862306a36Sopenharmony_ci#include <linux/slab.h>
2962306a36Sopenharmony_ci#include <linux/spinlock.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <asm/unaligned.h>
3262306a36Sopenharmony_ci#include <net/arp.h>
3362306a36Sopenharmony_ci#include <net/firewire.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* rx limits */
3662306a36Sopenharmony_ci#define FWNET_MAX_FRAGMENTS		30 /* arbitrary, > TX queue depth */
3762306a36Sopenharmony_ci#define FWNET_ISO_PAGE_COUNT		(PAGE_SIZE < 16*1024 ? 4 : 2)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* tx limits */
4062306a36Sopenharmony_ci#define FWNET_MAX_QUEUED_DATAGRAMS	20 /* < 64 = number of tlabels */
4162306a36Sopenharmony_ci#define FWNET_MIN_QUEUED_DATAGRAMS	10 /* should keep AT DMA busy enough */
4262306a36Sopenharmony_ci#define FWNET_TX_QUEUE_LEN		FWNET_MAX_QUEUED_DATAGRAMS /* ? */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define IEEE1394_BROADCAST_CHANNEL	31
4562306a36Sopenharmony_ci#define IEEE1394_ALL_NODES		(0xffc0 | 0x003f)
4662306a36Sopenharmony_ci#define IEEE1394_MAX_PAYLOAD_S100	512
4762306a36Sopenharmony_ci#define FWNET_NO_FIFO_ADDR		(~0ULL)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define IANA_SPECIFIER_ID		0x00005eU
5062306a36Sopenharmony_ci#define RFC2734_SW_VERSION		0x000001U
5162306a36Sopenharmony_ci#define RFC3146_SW_VERSION		0x000002U
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define IEEE1394_GASP_HDR_SIZE	8
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define RFC2374_UNFRAG_HDR_SIZE	4
5662306a36Sopenharmony_ci#define RFC2374_FRAG_HDR_SIZE	8
5762306a36Sopenharmony_ci#define RFC2374_FRAG_OVERHEAD	4
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define RFC2374_HDR_UNFRAG	0	/* unfragmented		*/
6062306a36Sopenharmony_ci#define RFC2374_HDR_FIRSTFRAG	1	/* first fragment	*/
6162306a36Sopenharmony_ci#define RFC2374_HDR_LASTFRAG	2	/* last fragment	*/
6262306a36Sopenharmony_ci#define RFC2374_HDR_INTFRAG	3	/* interior fragment	*/
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic bool fwnet_hwaddr_is_multicast(u8 *ha)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return !!(*ha & 1);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* IPv4 and IPv6 encapsulation header */
7062306a36Sopenharmony_cistruct rfc2734_header {
7162306a36Sopenharmony_ci	u32 w0;
7262306a36Sopenharmony_ci	u32 w1;
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define fwnet_get_hdr_lf(h)		(((h)->w0 & 0xc0000000) >> 30)
7662306a36Sopenharmony_ci#define fwnet_get_hdr_ether_type(h)	(((h)->w0 & 0x0000ffff))
7762306a36Sopenharmony_ci#define fwnet_get_hdr_dg_size(h)	((((h)->w0 & 0x0fff0000) >> 16) + 1)
7862306a36Sopenharmony_ci#define fwnet_get_hdr_fg_off(h)		(((h)->w0 & 0x00000fff))
7962306a36Sopenharmony_ci#define fwnet_get_hdr_dgl(h)		(((h)->w1 & 0xffff0000) >> 16)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define fwnet_set_hdr_lf(lf)		((lf) << 30)
8262306a36Sopenharmony_ci#define fwnet_set_hdr_ether_type(et)	(et)
8362306a36Sopenharmony_ci#define fwnet_set_hdr_dg_size(dgs)	(((dgs) - 1) << 16)
8462306a36Sopenharmony_ci#define fwnet_set_hdr_fg_off(fgo)	(fgo)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define fwnet_set_hdr_dgl(dgl)		((dgl) << 16)
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic inline void fwnet_make_uf_hdr(struct rfc2734_header *hdr,
8962306a36Sopenharmony_ci		unsigned ether_type)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_UNFRAG)
9262306a36Sopenharmony_ci		  | fwnet_set_hdr_ether_type(ether_type);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic inline void fwnet_make_ff_hdr(struct rfc2734_header *hdr,
9662306a36Sopenharmony_ci		unsigned ether_type, unsigned dg_size, unsigned dgl)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_FIRSTFRAG)
9962306a36Sopenharmony_ci		  | fwnet_set_hdr_dg_size(dg_size)
10062306a36Sopenharmony_ci		  | fwnet_set_hdr_ether_type(ether_type);
10162306a36Sopenharmony_ci	hdr->w1 = fwnet_set_hdr_dgl(dgl);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic inline void fwnet_make_sf_hdr(struct rfc2734_header *hdr,
10562306a36Sopenharmony_ci		unsigned lf, unsigned dg_size, unsigned fg_off, unsigned dgl)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	hdr->w0 = fwnet_set_hdr_lf(lf)
10862306a36Sopenharmony_ci		  | fwnet_set_hdr_dg_size(dg_size)
10962306a36Sopenharmony_ci		  | fwnet_set_hdr_fg_off(fg_off);
11062306a36Sopenharmony_ci	hdr->w1 = fwnet_set_hdr_dgl(dgl);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* This list keeps track of what parts of the datagram have been filled in */
11462306a36Sopenharmony_cistruct fwnet_fragment_info {
11562306a36Sopenharmony_ci	struct list_head fi_link;
11662306a36Sopenharmony_ci	u16 offset;
11762306a36Sopenharmony_ci	u16 len;
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistruct fwnet_partial_datagram {
12162306a36Sopenharmony_ci	struct list_head pd_link;
12262306a36Sopenharmony_ci	struct list_head fi_list;
12362306a36Sopenharmony_ci	struct sk_buff *skb;
12462306a36Sopenharmony_ci	/* FIXME Why not use skb->data? */
12562306a36Sopenharmony_ci	char *pbuf;
12662306a36Sopenharmony_ci	u16 datagram_label;
12762306a36Sopenharmony_ci	u16 ether_type;
12862306a36Sopenharmony_ci	u16 datagram_size;
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic DEFINE_MUTEX(fwnet_device_mutex);
13262306a36Sopenharmony_cistatic LIST_HEAD(fwnet_device_list);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistruct fwnet_device {
13562306a36Sopenharmony_ci	struct list_head dev_link;
13662306a36Sopenharmony_ci	spinlock_t lock;
13762306a36Sopenharmony_ci	enum {
13862306a36Sopenharmony_ci		FWNET_BROADCAST_ERROR,
13962306a36Sopenharmony_ci		FWNET_BROADCAST_RUNNING,
14062306a36Sopenharmony_ci		FWNET_BROADCAST_STOPPED,
14162306a36Sopenharmony_ci	} broadcast_state;
14262306a36Sopenharmony_ci	struct fw_iso_context *broadcast_rcv_context;
14362306a36Sopenharmony_ci	struct fw_iso_buffer broadcast_rcv_buffer;
14462306a36Sopenharmony_ci	void **broadcast_rcv_buffer_ptrs;
14562306a36Sopenharmony_ci	unsigned broadcast_rcv_next_ptr;
14662306a36Sopenharmony_ci	unsigned num_broadcast_rcv_ptrs;
14762306a36Sopenharmony_ci	unsigned rcv_buffer_size;
14862306a36Sopenharmony_ci	/*
14962306a36Sopenharmony_ci	 * This value is the maximum unfragmented datagram size that can be
15062306a36Sopenharmony_ci	 * sent by the hardware.  It already has the GASP overhead and the
15162306a36Sopenharmony_ci	 * unfragmented datagram header overhead calculated into it.
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	unsigned broadcast_xmt_max_payload;
15462306a36Sopenharmony_ci	u16 broadcast_xmt_datagramlabel;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * The CSR address that remote nodes must send datagrams to for us to
15862306a36Sopenharmony_ci	 * receive them.
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	struct fw_address_handler handler;
16162306a36Sopenharmony_ci	u64 local_fifo;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Number of tx datagrams that have been queued but not yet acked */
16462306a36Sopenharmony_ci	int queued_datagrams;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	int peer_count;
16762306a36Sopenharmony_ci	struct list_head peer_list;
16862306a36Sopenharmony_ci	struct fw_card *card;
16962306a36Sopenharmony_ci	struct net_device *netdev;
17062306a36Sopenharmony_ci};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistruct fwnet_peer {
17362306a36Sopenharmony_ci	struct list_head peer_link;
17462306a36Sopenharmony_ci	struct fwnet_device *dev;
17562306a36Sopenharmony_ci	u64 guid;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* guarded by dev->lock */
17862306a36Sopenharmony_ci	struct list_head pd_list; /* received partial datagrams */
17962306a36Sopenharmony_ci	unsigned pdg_size;        /* pd_list size */
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	u16 datagram_label;       /* outgoing datagram label */
18262306a36Sopenharmony_ci	u16 max_payload;          /* includes RFC2374_FRAG_HDR_SIZE overhead */
18362306a36Sopenharmony_ci	int node_id;
18462306a36Sopenharmony_ci	int generation;
18562306a36Sopenharmony_ci	unsigned speed;
18662306a36Sopenharmony_ci};
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/* This is our task struct. It's used for the packet complete callback.  */
18962306a36Sopenharmony_cistruct fwnet_packet_task {
19062306a36Sopenharmony_ci	struct fw_transaction transaction;
19162306a36Sopenharmony_ci	struct rfc2734_header hdr;
19262306a36Sopenharmony_ci	struct sk_buff *skb;
19362306a36Sopenharmony_ci	struct fwnet_device *dev;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	int outstanding_pkts;
19662306a36Sopenharmony_ci	u64 fifo_addr;
19762306a36Sopenharmony_ci	u16 dest_node;
19862306a36Sopenharmony_ci	u16 max_payload;
19962306a36Sopenharmony_ci	u8 generation;
20062306a36Sopenharmony_ci	u8 speed;
20162306a36Sopenharmony_ci	u8 enqueued;
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/*
20562306a36Sopenharmony_ci * saddr == NULL means use device source address.
20662306a36Sopenharmony_ci * daddr == NULL means leave destination address (eg unresolved arp).
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_cistatic int fwnet_header_create(struct sk_buff *skb, struct net_device *net,
20962306a36Sopenharmony_ci			unsigned short type, const void *daddr,
21062306a36Sopenharmony_ci			const void *saddr, unsigned len)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct fwnet_header *h;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	h = skb_push(skb, sizeof(*h));
21562306a36Sopenharmony_ci	put_unaligned_be16(type, &h->h_proto);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (net->flags & (IFF_LOOPBACK | IFF_NOARP)) {
21862306a36Sopenharmony_ci		memset(h->h_dest, 0, net->addr_len);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		return net->hard_header_len;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (daddr) {
22462306a36Sopenharmony_ci		memcpy(h->h_dest, daddr, net->addr_len);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		return net->hard_header_len;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return -net->hard_header_len;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int fwnet_header_cache(const struct neighbour *neigh,
23362306a36Sopenharmony_ci			      struct hh_cache *hh, __be16 type)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct net_device *net;
23662306a36Sopenharmony_ci	struct fwnet_header *h;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (type == cpu_to_be16(ETH_P_802_3))
23962306a36Sopenharmony_ci		return -1;
24062306a36Sopenharmony_ci	net = neigh->dev;
24162306a36Sopenharmony_ci	h = (struct fwnet_header *)((u8 *)hh->hh_data + HH_DATA_OFF(sizeof(*h)));
24262306a36Sopenharmony_ci	h->h_proto = type;
24362306a36Sopenharmony_ci	memcpy(h->h_dest, neigh->ha, net->addr_len);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Pairs with the READ_ONCE() in neigh_resolve_output(),
24662306a36Sopenharmony_ci	 * neigh_hh_output() and neigh_update_hhs().
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	smp_store_release(&hh->hh_len, FWNET_HLEN);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/* Called by Address Resolution module to notify changes in address. */
25462306a36Sopenharmony_cistatic void fwnet_header_cache_update(struct hh_cache *hh,
25562306a36Sopenharmony_ci		const struct net_device *net, const unsigned char *haddr)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	memcpy((u8 *)hh->hh_data + HH_DATA_OFF(FWNET_HLEN), haddr, net->addr_len);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	memcpy(haddr, skb->dev->dev_addr, FWNET_ALEN);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return FWNET_ALEN;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic const struct header_ops fwnet_header_ops = {
26862306a36Sopenharmony_ci	.create         = fwnet_header_create,
26962306a36Sopenharmony_ci	.cache		= fwnet_header_cache,
27062306a36Sopenharmony_ci	.cache_update	= fwnet_header_cache_update,
27162306a36Sopenharmony_ci	.parse          = fwnet_header_parse,
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/* FIXME: is this correct for all cases? */
27562306a36Sopenharmony_cistatic bool fwnet_frag_overlap(struct fwnet_partial_datagram *pd,
27662306a36Sopenharmony_ci			       unsigned offset, unsigned len)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct fwnet_fragment_info *fi;
27962306a36Sopenharmony_ci	unsigned end = offset + len;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	list_for_each_entry(fi, &pd->fi_list, fi_link)
28262306a36Sopenharmony_ci		if (offset < fi->offset + fi->len && end > fi->offset)
28362306a36Sopenharmony_ci			return true;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return false;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/* Assumes that new fragment does not overlap any existing fragments */
28962306a36Sopenharmony_cistatic struct fwnet_fragment_info *fwnet_frag_new(
29062306a36Sopenharmony_ci	struct fwnet_partial_datagram *pd, unsigned offset, unsigned len)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct fwnet_fragment_info *fi, *fi2, *new;
29362306a36Sopenharmony_ci	struct list_head *list;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	list = &pd->fi_list;
29662306a36Sopenharmony_ci	list_for_each_entry(fi, &pd->fi_list, fi_link) {
29762306a36Sopenharmony_ci		if (fi->offset + fi->len == offset) {
29862306a36Sopenharmony_ci			/* The new fragment can be tacked on to the end */
29962306a36Sopenharmony_ci			/* Did the new fragment plug a hole? */
30062306a36Sopenharmony_ci			fi2 = list_entry(fi->fi_link.next,
30162306a36Sopenharmony_ci					 struct fwnet_fragment_info, fi_link);
30262306a36Sopenharmony_ci			if (fi->offset + fi->len == fi2->offset) {
30362306a36Sopenharmony_ci				/* glue fragments together */
30462306a36Sopenharmony_ci				fi->len += len + fi2->len;
30562306a36Sopenharmony_ci				list_del(&fi2->fi_link);
30662306a36Sopenharmony_ci				kfree(fi2);
30762306a36Sopenharmony_ci			} else {
30862306a36Sopenharmony_ci				fi->len += len;
30962306a36Sopenharmony_ci			}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci			return fi;
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci		if (offset + len == fi->offset) {
31462306a36Sopenharmony_ci			/* The new fragment can be tacked on to the beginning */
31562306a36Sopenharmony_ci			/* Did the new fragment plug a hole? */
31662306a36Sopenharmony_ci			fi2 = list_entry(fi->fi_link.prev,
31762306a36Sopenharmony_ci					 struct fwnet_fragment_info, fi_link);
31862306a36Sopenharmony_ci			if (fi2->offset + fi2->len == fi->offset) {
31962306a36Sopenharmony_ci				/* glue fragments together */
32062306a36Sopenharmony_ci				fi2->len += fi->len + len;
32162306a36Sopenharmony_ci				list_del(&fi->fi_link);
32262306a36Sopenharmony_ci				kfree(fi);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci				return fi2;
32562306a36Sopenharmony_ci			}
32662306a36Sopenharmony_ci			fi->offset = offset;
32762306a36Sopenharmony_ci			fi->len += len;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci			return fi;
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci		if (offset > fi->offset + fi->len) {
33262306a36Sopenharmony_ci			list = &fi->fi_link;
33362306a36Sopenharmony_ci			break;
33462306a36Sopenharmony_ci		}
33562306a36Sopenharmony_ci		if (offset + len < fi->offset) {
33662306a36Sopenharmony_ci			list = fi->fi_link.prev;
33762306a36Sopenharmony_ci			break;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	new = kmalloc(sizeof(*new), GFP_ATOMIC);
34262306a36Sopenharmony_ci	if (!new)
34362306a36Sopenharmony_ci		return NULL;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	new->offset = offset;
34662306a36Sopenharmony_ci	new->len = len;
34762306a36Sopenharmony_ci	list_add(&new->fi_link, list);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return new;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic struct fwnet_partial_datagram *fwnet_pd_new(struct net_device *net,
35362306a36Sopenharmony_ci		struct fwnet_peer *peer, u16 datagram_label, unsigned dg_size,
35462306a36Sopenharmony_ci		void *frag_buf, unsigned frag_off, unsigned frag_len)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct fwnet_partial_datagram *new;
35762306a36Sopenharmony_ci	struct fwnet_fragment_info *fi;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	new = kmalloc(sizeof(*new), GFP_ATOMIC);
36062306a36Sopenharmony_ci	if (!new)
36162306a36Sopenharmony_ci		goto fail;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	INIT_LIST_HEAD(&new->fi_list);
36462306a36Sopenharmony_ci	fi = fwnet_frag_new(new, frag_off, frag_len);
36562306a36Sopenharmony_ci	if (fi == NULL)
36662306a36Sopenharmony_ci		goto fail_w_new;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	new->datagram_label = datagram_label;
36962306a36Sopenharmony_ci	new->datagram_size = dg_size;
37062306a36Sopenharmony_ci	new->skb = dev_alloc_skb(dg_size + LL_RESERVED_SPACE(net));
37162306a36Sopenharmony_ci	if (new->skb == NULL)
37262306a36Sopenharmony_ci		goto fail_w_fi;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	skb_reserve(new->skb, LL_RESERVED_SPACE(net));
37562306a36Sopenharmony_ci	new->pbuf = skb_put(new->skb, dg_size);
37662306a36Sopenharmony_ci	memcpy(new->pbuf + frag_off, frag_buf, frag_len);
37762306a36Sopenharmony_ci	list_add_tail(&new->pd_link, &peer->pd_list);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return new;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cifail_w_fi:
38262306a36Sopenharmony_ci	kfree(fi);
38362306a36Sopenharmony_cifail_w_new:
38462306a36Sopenharmony_ci	kfree(new);
38562306a36Sopenharmony_cifail:
38662306a36Sopenharmony_ci	return NULL;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic struct fwnet_partial_datagram *fwnet_pd_find(struct fwnet_peer *peer,
39062306a36Sopenharmony_ci						    u16 datagram_label)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	struct fwnet_partial_datagram *pd;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	list_for_each_entry(pd, &peer->pd_list, pd_link)
39562306a36Sopenharmony_ci		if (pd->datagram_label == datagram_label)
39662306a36Sopenharmony_ci			return pd;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return NULL;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic void fwnet_pd_delete(struct fwnet_partial_datagram *old)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct fwnet_fragment_info *fi, *n;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	list_for_each_entry_safe(fi, n, &old->fi_list, fi_link)
40762306a36Sopenharmony_ci		kfree(fi);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	list_del(&old->pd_link);
41062306a36Sopenharmony_ci	dev_kfree_skb_any(old->skb);
41162306a36Sopenharmony_ci	kfree(old);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic bool fwnet_pd_update(struct fwnet_peer *peer,
41562306a36Sopenharmony_ci		struct fwnet_partial_datagram *pd, void *frag_buf,
41662306a36Sopenharmony_ci		unsigned frag_off, unsigned frag_len)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	if (fwnet_frag_new(pd, frag_off, frag_len) == NULL)
41962306a36Sopenharmony_ci		return false;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	memcpy(pd->pbuf + frag_off, frag_buf, frag_len);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/*
42462306a36Sopenharmony_ci	 * Move list entry to beginning of list so that oldest partial
42562306a36Sopenharmony_ci	 * datagrams percolate to the end of the list
42662306a36Sopenharmony_ci	 */
42762306a36Sopenharmony_ci	list_move_tail(&pd->pd_link, &peer->pd_list);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return true;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct fwnet_fragment_info *fi;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	fi = list_entry(pd->fi_list.next, struct fwnet_fragment_info, fi_link);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return fi->len == pd->datagram_size;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/* caller must hold dev->lock */
44262306a36Sopenharmony_cistatic struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev,
44362306a36Sopenharmony_ci						  u64 guid)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct fwnet_peer *peer;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	list_for_each_entry(peer, &dev->peer_list, peer_link)
44862306a36Sopenharmony_ci		if (peer->guid == guid)
44962306a36Sopenharmony_ci			return peer;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return NULL;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/* caller must hold dev->lock */
45562306a36Sopenharmony_cistatic struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev,
45662306a36Sopenharmony_ci						int node_id, int generation)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct fwnet_peer *peer;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	list_for_each_entry(peer, &dev->peer_list, peer_link)
46162306a36Sopenharmony_ci		if (peer->node_id    == node_id &&
46262306a36Sopenharmony_ci		    peer->generation == generation)
46362306a36Sopenharmony_ci			return peer;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return NULL;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */
46962306a36Sopenharmony_cistatic unsigned fwnet_max_payload(unsigned max_rec, unsigned speed)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	max_rec = min(max_rec, speed + 8);
47262306a36Sopenharmony_ci	max_rec = clamp(max_rec, 8U, 11U); /* 512...4096 */
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic int fwnet_finish_incoming_packet(struct net_device *net,
47962306a36Sopenharmony_ci					struct sk_buff *skb, u16 source_node_id,
48062306a36Sopenharmony_ci					bool is_broadcast, u16 ether_type)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	int status, len;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	switch (ether_type) {
48562306a36Sopenharmony_ci	case ETH_P_ARP:
48662306a36Sopenharmony_ci	case ETH_P_IP:
48762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
48862306a36Sopenharmony_ci	case ETH_P_IPV6:
48962306a36Sopenharmony_ci#endif
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	default:
49262306a36Sopenharmony_ci		goto err;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* Write metadata, and then pass to the receive level */
49662306a36Sopenharmony_ci	skb->dev = net;
49762306a36Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/*
50062306a36Sopenharmony_ci	 * Parse the encapsulation header. This actually does the job of
50162306a36Sopenharmony_ci	 * converting to an ethernet-like pseudo frame header.
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	if (dev_hard_header(skb, net, ether_type,
50462306a36Sopenharmony_ci			   is_broadcast ? net->broadcast : net->dev_addr,
50562306a36Sopenharmony_ci			   NULL, skb->len) >= 0) {
50662306a36Sopenharmony_ci		struct fwnet_header *eth;
50762306a36Sopenharmony_ci		u16 *rawp;
50862306a36Sopenharmony_ci		__be16 protocol;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		skb_reset_mac_header(skb);
51162306a36Sopenharmony_ci		skb_pull(skb, sizeof(*eth));
51262306a36Sopenharmony_ci		eth = (struct fwnet_header *)skb_mac_header(skb);
51362306a36Sopenharmony_ci		if (fwnet_hwaddr_is_multicast(eth->h_dest)) {
51462306a36Sopenharmony_ci			if (memcmp(eth->h_dest, net->broadcast,
51562306a36Sopenharmony_ci				   net->addr_len) == 0)
51662306a36Sopenharmony_ci				skb->pkt_type = PACKET_BROADCAST;
51762306a36Sopenharmony_ci#if 0
51862306a36Sopenharmony_ci			else
51962306a36Sopenharmony_ci				skb->pkt_type = PACKET_MULTICAST;
52062306a36Sopenharmony_ci#endif
52162306a36Sopenharmony_ci		} else {
52262306a36Sopenharmony_ci			if (memcmp(eth->h_dest, net->dev_addr, net->addr_len))
52362306a36Sopenharmony_ci				skb->pkt_type = PACKET_OTHERHOST;
52462306a36Sopenharmony_ci		}
52562306a36Sopenharmony_ci		if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
52662306a36Sopenharmony_ci			protocol = eth->h_proto;
52762306a36Sopenharmony_ci		} else {
52862306a36Sopenharmony_ci			rawp = (u16 *)skb->data;
52962306a36Sopenharmony_ci			if (*rawp == 0xffff)
53062306a36Sopenharmony_ci				protocol = htons(ETH_P_802_3);
53162306a36Sopenharmony_ci			else
53262306a36Sopenharmony_ci				protocol = htons(ETH_P_802_2);
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci		skb->protocol = protocol;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	len = skb->len;
53862306a36Sopenharmony_ci	status = netif_rx(skb);
53962306a36Sopenharmony_ci	if (status == NET_RX_DROP) {
54062306a36Sopenharmony_ci		net->stats.rx_errors++;
54162306a36Sopenharmony_ci		net->stats.rx_dropped++;
54262306a36Sopenharmony_ci	} else {
54362306a36Sopenharmony_ci		net->stats.rx_packets++;
54462306a36Sopenharmony_ci		net->stats.rx_bytes += len;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci err:
55062306a36Sopenharmony_ci	net->stats.rx_errors++;
55162306a36Sopenharmony_ci	net->stats.rx_dropped++;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return -ENOENT;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
55962306a36Sopenharmony_ci				 int source_node_id, int generation,
56062306a36Sopenharmony_ci				 bool is_broadcast)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct sk_buff *skb;
56362306a36Sopenharmony_ci	struct net_device *net = dev->netdev;
56462306a36Sopenharmony_ci	struct rfc2734_header hdr;
56562306a36Sopenharmony_ci	unsigned lf;
56662306a36Sopenharmony_ci	unsigned long flags;
56762306a36Sopenharmony_ci	struct fwnet_peer *peer;
56862306a36Sopenharmony_ci	struct fwnet_partial_datagram *pd;
56962306a36Sopenharmony_ci	int fg_off;
57062306a36Sopenharmony_ci	int dg_size;
57162306a36Sopenharmony_ci	u16 datagram_label;
57262306a36Sopenharmony_ci	int retval;
57362306a36Sopenharmony_ci	u16 ether_type;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (len <= RFC2374_UNFRAG_HDR_SIZE)
57662306a36Sopenharmony_ci		return 0;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	hdr.w0 = be32_to_cpu(buf[0]);
57962306a36Sopenharmony_ci	lf = fwnet_get_hdr_lf(&hdr);
58062306a36Sopenharmony_ci	if (lf == RFC2374_HDR_UNFRAG) {
58162306a36Sopenharmony_ci		/*
58262306a36Sopenharmony_ci		 * An unfragmented datagram has been received by the ieee1394
58362306a36Sopenharmony_ci		 * bus. Build an skbuff around it so we can pass it to the
58462306a36Sopenharmony_ci		 * high level network layer.
58562306a36Sopenharmony_ci		 */
58662306a36Sopenharmony_ci		ether_type = fwnet_get_hdr_ether_type(&hdr);
58762306a36Sopenharmony_ci		buf++;
58862306a36Sopenharmony_ci		len -= RFC2374_UNFRAG_HDR_SIZE;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		skb = dev_alloc_skb(len + LL_RESERVED_SPACE(net));
59162306a36Sopenharmony_ci		if (unlikely(!skb)) {
59262306a36Sopenharmony_ci			net->stats.rx_dropped++;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci			return -ENOMEM;
59562306a36Sopenharmony_ci		}
59662306a36Sopenharmony_ci		skb_reserve(skb, LL_RESERVED_SPACE(net));
59762306a36Sopenharmony_ci		skb_put_data(skb, buf, len);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		return fwnet_finish_incoming_packet(net, skb, source_node_id,
60062306a36Sopenharmony_ci						    is_broadcast, ether_type);
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* A datagram fragment has been received, now the fun begins. */
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (len <= RFC2374_FRAG_HDR_SIZE)
60662306a36Sopenharmony_ci		return 0;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	hdr.w1 = ntohl(buf[1]);
60962306a36Sopenharmony_ci	buf += 2;
61062306a36Sopenharmony_ci	len -= RFC2374_FRAG_HDR_SIZE;
61162306a36Sopenharmony_ci	if (lf == RFC2374_HDR_FIRSTFRAG) {
61262306a36Sopenharmony_ci		ether_type = fwnet_get_hdr_ether_type(&hdr);
61362306a36Sopenharmony_ci		fg_off = 0;
61462306a36Sopenharmony_ci	} else {
61562306a36Sopenharmony_ci		ether_type = 0;
61662306a36Sopenharmony_ci		fg_off = fwnet_get_hdr_fg_off(&hdr);
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci	datagram_label = fwnet_get_hdr_dgl(&hdr);
61962306a36Sopenharmony_ci	dg_size = fwnet_get_hdr_dg_size(&hdr);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	if (fg_off + len > dg_size)
62262306a36Sopenharmony_ci		return 0;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation);
62762306a36Sopenharmony_ci	if (!peer) {
62862306a36Sopenharmony_ci		retval = -ENOENT;
62962306a36Sopenharmony_ci		goto fail;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	pd = fwnet_pd_find(peer, datagram_label);
63362306a36Sopenharmony_ci	if (pd == NULL) {
63462306a36Sopenharmony_ci		while (peer->pdg_size >= FWNET_MAX_FRAGMENTS) {
63562306a36Sopenharmony_ci			/* remove the oldest */
63662306a36Sopenharmony_ci			fwnet_pd_delete(list_first_entry(&peer->pd_list,
63762306a36Sopenharmony_ci				struct fwnet_partial_datagram, pd_link));
63862306a36Sopenharmony_ci			peer->pdg_size--;
63962306a36Sopenharmony_ci		}
64062306a36Sopenharmony_ci		pd = fwnet_pd_new(net, peer, datagram_label,
64162306a36Sopenharmony_ci				  dg_size, buf, fg_off, len);
64262306a36Sopenharmony_ci		if (pd == NULL) {
64362306a36Sopenharmony_ci			retval = -ENOMEM;
64462306a36Sopenharmony_ci			goto fail;
64562306a36Sopenharmony_ci		}
64662306a36Sopenharmony_ci		peer->pdg_size++;
64762306a36Sopenharmony_ci	} else {
64862306a36Sopenharmony_ci		if (fwnet_frag_overlap(pd, fg_off, len) ||
64962306a36Sopenharmony_ci		    pd->datagram_size != dg_size) {
65062306a36Sopenharmony_ci			/*
65162306a36Sopenharmony_ci			 * Differing datagram sizes or overlapping fragments,
65262306a36Sopenharmony_ci			 * discard old datagram and start a new one.
65362306a36Sopenharmony_ci			 */
65462306a36Sopenharmony_ci			fwnet_pd_delete(pd);
65562306a36Sopenharmony_ci			pd = fwnet_pd_new(net, peer, datagram_label,
65662306a36Sopenharmony_ci					  dg_size, buf, fg_off, len);
65762306a36Sopenharmony_ci			if (pd == NULL) {
65862306a36Sopenharmony_ci				peer->pdg_size--;
65962306a36Sopenharmony_ci				retval = -ENOMEM;
66062306a36Sopenharmony_ci				goto fail;
66162306a36Sopenharmony_ci			}
66262306a36Sopenharmony_ci		} else {
66362306a36Sopenharmony_ci			if (!fwnet_pd_update(peer, pd, buf, fg_off, len)) {
66462306a36Sopenharmony_ci				/*
66562306a36Sopenharmony_ci				 * Couldn't save off fragment anyway
66662306a36Sopenharmony_ci				 * so might as well obliterate the
66762306a36Sopenharmony_ci				 * datagram now.
66862306a36Sopenharmony_ci				 */
66962306a36Sopenharmony_ci				fwnet_pd_delete(pd);
67062306a36Sopenharmony_ci				peer->pdg_size--;
67162306a36Sopenharmony_ci				retval = -ENOMEM;
67262306a36Sopenharmony_ci				goto fail;
67362306a36Sopenharmony_ci			}
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci	} /* new datagram or add to existing one */
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (lf == RFC2374_HDR_FIRSTFRAG)
67862306a36Sopenharmony_ci		pd->ether_type = ether_type;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (fwnet_pd_is_complete(pd)) {
68162306a36Sopenharmony_ci		ether_type = pd->ether_type;
68262306a36Sopenharmony_ci		peer->pdg_size--;
68362306a36Sopenharmony_ci		skb = skb_get(pd->skb);
68462306a36Sopenharmony_ci		fwnet_pd_delete(pd);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		return fwnet_finish_incoming_packet(net, skb, source_node_id,
68962306a36Sopenharmony_ci						    false, ether_type);
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci	/*
69262306a36Sopenharmony_ci	 * Datagram is not complete, we're done for the
69362306a36Sopenharmony_ci	 * moment.
69462306a36Sopenharmony_ci	 */
69562306a36Sopenharmony_ci	retval = 0;
69662306a36Sopenharmony_ci fail:
69762306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return retval;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
70362306a36Sopenharmony_ci		int tcode, int destination, int source, int generation,
70462306a36Sopenharmony_ci		unsigned long long offset, void *payload, size_t length,
70562306a36Sopenharmony_ci		void *callback_data)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	struct fwnet_device *dev = callback_data;
70862306a36Sopenharmony_ci	int rcode;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (destination == IEEE1394_ALL_NODES) {
71162306a36Sopenharmony_ci		// Although the response to the broadcast packet is not necessarily required, the
71262306a36Sopenharmony_ci		// fw_send_response() function should still be called to maintain the reference
71362306a36Sopenharmony_ci		// counting of the object. In the case, the call of function just releases the
71462306a36Sopenharmony_ci		// object as a result to decrease the reference counting.
71562306a36Sopenharmony_ci		rcode = RCODE_COMPLETE;
71662306a36Sopenharmony_ci	} else if (offset != dev->handler.offset) {
71762306a36Sopenharmony_ci		rcode = RCODE_ADDRESS_ERROR;
71862306a36Sopenharmony_ci	} else if (tcode != TCODE_WRITE_BLOCK_REQUEST) {
71962306a36Sopenharmony_ci		rcode = RCODE_TYPE_ERROR;
72062306a36Sopenharmony_ci	} else if (fwnet_incoming_packet(dev, payload, length,
72162306a36Sopenharmony_ci					 source, generation, false) != 0) {
72262306a36Sopenharmony_ci		dev_err(&dev->netdev->dev, "incoming packet failure\n");
72362306a36Sopenharmony_ci		rcode = RCODE_CONFLICT_ERROR;
72462306a36Sopenharmony_ci	} else {
72562306a36Sopenharmony_ci		rcode = RCODE_COMPLETE;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	fw_send_response(card, r, rcode);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic int gasp_source_id(__be32 *p)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	return be32_to_cpu(p[0]) >> 16;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic u32 gasp_specifier_id(__be32 *p)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	return (be32_to_cpu(p[0]) & 0xffff) << 8 |
73962306a36Sopenharmony_ci	       (be32_to_cpu(p[1]) & 0xff000000) >> 24;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic u32 gasp_version(__be32 *p)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	return be32_to_cpu(p[1]) & 0xffffff;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic void fwnet_receive_broadcast(struct fw_iso_context *context,
74862306a36Sopenharmony_ci		u32 cycle, size_t header_length, void *header, void *data)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct fwnet_device *dev;
75162306a36Sopenharmony_ci	struct fw_iso_packet packet;
75262306a36Sopenharmony_ci	__be16 *hdr_ptr;
75362306a36Sopenharmony_ci	__be32 *buf_ptr;
75462306a36Sopenharmony_ci	int retval;
75562306a36Sopenharmony_ci	u32 length;
75662306a36Sopenharmony_ci	unsigned long offset;
75762306a36Sopenharmony_ci	unsigned long flags;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	dev = data;
76062306a36Sopenharmony_ci	hdr_ptr = header;
76162306a36Sopenharmony_ci	length = be16_to_cpup(hdr_ptr);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	offset = dev->rcv_buffer_size * dev->broadcast_rcv_next_ptr;
76662306a36Sopenharmony_ci	buf_ptr = dev->broadcast_rcv_buffer_ptrs[dev->broadcast_rcv_next_ptr++];
76762306a36Sopenharmony_ci	if (dev->broadcast_rcv_next_ptr == dev->num_broadcast_rcv_ptrs)
76862306a36Sopenharmony_ci		dev->broadcast_rcv_next_ptr = 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (length > IEEE1394_GASP_HDR_SIZE &&
77362306a36Sopenharmony_ci	    gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
77462306a36Sopenharmony_ci	    (gasp_version(buf_ptr) == RFC2734_SW_VERSION
77562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
77662306a36Sopenharmony_ci	     || gasp_version(buf_ptr) == RFC3146_SW_VERSION
77762306a36Sopenharmony_ci#endif
77862306a36Sopenharmony_ci	    ))
77962306a36Sopenharmony_ci		fwnet_incoming_packet(dev, buf_ptr + 2,
78062306a36Sopenharmony_ci				      length - IEEE1394_GASP_HDR_SIZE,
78162306a36Sopenharmony_ci				      gasp_source_id(buf_ptr),
78262306a36Sopenharmony_ci				      context->card->generation, true);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	packet.payload_length = dev->rcv_buffer_size;
78562306a36Sopenharmony_ci	packet.interrupt = 1;
78662306a36Sopenharmony_ci	packet.skip = 0;
78762306a36Sopenharmony_ci	packet.tag = 3;
78862306a36Sopenharmony_ci	packet.sy = 0;
78962306a36Sopenharmony_ci	packet.header_length = IEEE1394_GASP_HDR_SIZE;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	retval = fw_iso_context_queue(dev->broadcast_rcv_context, &packet,
79462306a36Sopenharmony_ci				      &dev->broadcast_rcv_buffer, offset);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (retval >= 0)
79962306a36Sopenharmony_ci		fw_iso_context_queue_flush(dev->broadcast_rcv_context);
80062306a36Sopenharmony_ci	else
80162306a36Sopenharmony_ci		dev_err(&dev->netdev->dev, "requeue failed\n");
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic struct kmem_cache *fwnet_packet_task_cache;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic void fwnet_free_ptask(struct fwnet_packet_task *ptask)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	dev_kfree_skb_any(ptask->skb);
80962306a36Sopenharmony_ci	kmem_cache_free(fwnet_packet_task_cache, ptask);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci/* Caller must hold dev->lock. */
81362306a36Sopenharmony_cistatic void dec_queued_datagrams(struct fwnet_device *dev)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	if (--dev->queued_datagrams == FWNET_MIN_QUEUED_DATAGRAMS)
81662306a36Sopenharmony_ci		netif_wake_queue(dev->netdev);
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic int fwnet_send_packet(struct fwnet_packet_task *ptask);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct fwnet_device *dev = ptask->dev;
82462306a36Sopenharmony_ci	struct sk_buff *skb = ptask->skb;
82562306a36Sopenharmony_ci	unsigned long flags;
82662306a36Sopenharmony_ci	bool free;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	ptask->outstanding_pkts--;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/* Check whether we or the networking TX soft-IRQ is last user. */
83362306a36Sopenharmony_ci	free = (ptask->outstanding_pkts == 0 && ptask->enqueued);
83462306a36Sopenharmony_ci	if (free)
83562306a36Sopenharmony_ci		dec_queued_datagrams(dev);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (ptask->outstanding_pkts == 0) {
83862306a36Sopenharmony_ci		dev->netdev->stats.tx_packets++;
83962306a36Sopenharmony_ci		dev->netdev->stats.tx_bytes += skb->len;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (ptask->outstanding_pkts > 0) {
84562306a36Sopenharmony_ci		u16 dg_size;
84662306a36Sopenharmony_ci		u16 fg_off;
84762306a36Sopenharmony_ci		u16 datagram_label;
84862306a36Sopenharmony_ci		u16 lf;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		/* Update the ptask to point to the next fragment and send it */
85162306a36Sopenharmony_ci		lf = fwnet_get_hdr_lf(&ptask->hdr);
85262306a36Sopenharmony_ci		switch (lf) {
85362306a36Sopenharmony_ci		case RFC2374_HDR_LASTFRAG:
85462306a36Sopenharmony_ci		case RFC2374_HDR_UNFRAG:
85562306a36Sopenharmony_ci		default:
85662306a36Sopenharmony_ci			dev_err(&dev->netdev->dev,
85762306a36Sopenharmony_ci				"outstanding packet %x lf %x, header %x,%x\n",
85862306a36Sopenharmony_ci				ptask->outstanding_pkts, lf, ptask->hdr.w0,
85962306a36Sopenharmony_ci				ptask->hdr.w1);
86062306a36Sopenharmony_ci			BUG();
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		case RFC2374_HDR_FIRSTFRAG:
86362306a36Sopenharmony_ci			/* Set frag type here for future interior fragments */
86462306a36Sopenharmony_ci			dg_size = fwnet_get_hdr_dg_size(&ptask->hdr);
86562306a36Sopenharmony_ci			fg_off = ptask->max_payload - RFC2374_FRAG_HDR_SIZE;
86662306a36Sopenharmony_ci			datagram_label = fwnet_get_hdr_dgl(&ptask->hdr);
86762306a36Sopenharmony_ci			break;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		case RFC2374_HDR_INTFRAG:
87062306a36Sopenharmony_ci			dg_size = fwnet_get_hdr_dg_size(&ptask->hdr);
87162306a36Sopenharmony_ci			fg_off = fwnet_get_hdr_fg_off(&ptask->hdr)
87262306a36Sopenharmony_ci				  + ptask->max_payload - RFC2374_FRAG_HDR_SIZE;
87362306a36Sopenharmony_ci			datagram_label = fwnet_get_hdr_dgl(&ptask->hdr);
87462306a36Sopenharmony_ci			break;
87562306a36Sopenharmony_ci		}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci		if (ptask->dest_node == IEEE1394_ALL_NODES) {
87862306a36Sopenharmony_ci			skb_pull(skb,
87962306a36Sopenharmony_ci				 ptask->max_payload + IEEE1394_GASP_HDR_SIZE);
88062306a36Sopenharmony_ci		} else {
88162306a36Sopenharmony_ci			skb_pull(skb, ptask->max_payload);
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci		if (ptask->outstanding_pkts > 1) {
88462306a36Sopenharmony_ci			fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_INTFRAG,
88562306a36Sopenharmony_ci					  dg_size, fg_off, datagram_label);
88662306a36Sopenharmony_ci		} else {
88762306a36Sopenharmony_ci			fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_LASTFRAG,
88862306a36Sopenharmony_ci					  dg_size, fg_off, datagram_label);
88962306a36Sopenharmony_ci			ptask->max_payload = skb->len + RFC2374_FRAG_HDR_SIZE;
89062306a36Sopenharmony_ci		}
89162306a36Sopenharmony_ci		fwnet_send_packet(ptask);
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (free)
89562306a36Sopenharmony_ci		fwnet_free_ptask(ptask);
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic void fwnet_transmit_packet_failed(struct fwnet_packet_task *ptask)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	struct fwnet_device *dev = ptask->dev;
90162306a36Sopenharmony_ci	unsigned long flags;
90262306a36Sopenharmony_ci	bool free;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* One fragment failed; don't try to send remaining fragments. */
90762306a36Sopenharmony_ci	ptask->outstanding_pkts = 0;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* Check whether we or the networking TX soft-IRQ is last user. */
91062306a36Sopenharmony_ci	free = ptask->enqueued;
91162306a36Sopenharmony_ci	if (free)
91262306a36Sopenharmony_ci		dec_queued_datagrams(dev);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	dev->netdev->stats.tx_dropped++;
91562306a36Sopenharmony_ci	dev->netdev->stats.tx_errors++;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (free)
92062306a36Sopenharmony_ci		fwnet_free_ptask(ptask);
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic void fwnet_write_complete(struct fw_card *card, int rcode,
92462306a36Sopenharmony_ci				 void *payload, size_t length, void *data)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	struct fwnet_packet_task *ptask = data;
92762306a36Sopenharmony_ci	static unsigned long j;
92862306a36Sopenharmony_ci	static int last_rcode, errors_skipped;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (rcode == RCODE_COMPLETE) {
93162306a36Sopenharmony_ci		fwnet_transmit_packet_done(ptask);
93262306a36Sopenharmony_ci	} else {
93362306a36Sopenharmony_ci		if (printk_timed_ratelimit(&j,  1000) || rcode != last_rcode) {
93462306a36Sopenharmony_ci			dev_err(&ptask->dev->netdev->dev,
93562306a36Sopenharmony_ci				"fwnet_write_complete failed: %x (skipped %d)\n",
93662306a36Sopenharmony_ci				rcode, errors_skipped);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci			errors_skipped = 0;
93962306a36Sopenharmony_ci			last_rcode = rcode;
94062306a36Sopenharmony_ci		} else {
94162306a36Sopenharmony_ci			errors_skipped++;
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci		fwnet_transmit_packet_failed(ptask);
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic int fwnet_send_packet(struct fwnet_packet_task *ptask)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct fwnet_device *dev;
95062306a36Sopenharmony_ci	unsigned tx_len;
95162306a36Sopenharmony_ci	struct rfc2734_header *bufhdr;
95262306a36Sopenharmony_ci	unsigned long flags;
95362306a36Sopenharmony_ci	bool free;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	dev = ptask->dev;
95662306a36Sopenharmony_ci	tx_len = ptask->max_payload;
95762306a36Sopenharmony_ci	switch (fwnet_get_hdr_lf(&ptask->hdr)) {
95862306a36Sopenharmony_ci	case RFC2374_HDR_UNFRAG:
95962306a36Sopenharmony_ci		bufhdr = skb_push(ptask->skb, RFC2374_UNFRAG_HDR_SIZE);
96062306a36Sopenharmony_ci		put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0);
96162306a36Sopenharmony_ci		break;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	case RFC2374_HDR_FIRSTFRAG:
96462306a36Sopenharmony_ci	case RFC2374_HDR_INTFRAG:
96562306a36Sopenharmony_ci	case RFC2374_HDR_LASTFRAG:
96662306a36Sopenharmony_ci		bufhdr = skb_push(ptask->skb, RFC2374_FRAG_HDR_SIZE);
96762306a36Sopenharmony_ci		put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0);
96862306a36Sopenharmony_ci		put_unaligned_be32(ptask->hdr.w1, &bufhdr->w1);
96962306a36Sopenharmony_ci		break;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	default:
97262306a36Sopenharmony_ci		BUG();
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci	if (ptask->dest_node == IEEE1394_ALL_NODES) {
97562306a36Sopenharmony_ci		u8 *p;
97662306a36Sopenharmony_ci		int generation;
97762306a36Sopenharmony_ci		int node_id;
97862306a36Sopenharmony_ci		unsigned int sw_version;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		/* ptask->generation may not have been set yet */
98162306a36Sopenharmony_ci		generation = dev->card->generation;
98262306a36Sopenharmony_ci		smp_rmb();
98362306a36Sopenharmony_ci		node_id = dev->card->node_id;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		switch (ptask->skb->protocol) {
98662306a36Sopenharmony_ci		default:
98762306a36Sopenharmony_ci			sw_version = RFC2734_SW_VERSION;
98862306a36Sopenharmony_ci			break;
98962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
99062306a36Sopenharmony_ci		case htons(ETH_P_IPV6):
99162306a36Sopenharmony_ci			sw_version = RFC3146_SW_VERSION;
99262306a36Sopenharmony_ci#endif
99362306a36Sopenharmony_ci		}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
99662306a36Sopenharmony_ci		put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
99762306a36Sopenharmony_ci		put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
99862306a36Sopenharmony_ci						| sw_version, &p[4]);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		/* We should not transmit if broadcast_channel.valid == 0. */
100162306a36Sopenharmony_ci		fw_send_request(dev->card, &ptask->transaction,
100262306a36Sopenharmony_ci				TCODE_STREAM_DATA,
100362306a36Sopenharmony_ci				fw_stream_packet_destination_id(3,
100462306a36Sopenharmony_ci						IEEE1394_BROADCAST_CHANNEL, 0),
100562306a36Sopenharmony_ci				generation, SCODE_100, 0ULL, ptask->skb->data,
100662306a36Sopenharmony_ci				tx_len + 8, fwnet_write_complete, ptask);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		spin_lock_irqsave(&dev->lock, flags);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		/* If the AT tasklet already ran, we may be last user. */
101162306a36Sopenharmony_ci		free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
101262306a36Sopenharmony_ci		if (!free)
101362306a36Sopenharmony_ci			ptask->enqueued = true;
101462306a36Sopenharmony_ci		else
101562306a36Sopenharmony_ci			dec_queued_datagrams(dev);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		goto out;
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	fw_send_request(dev->card, &ptask->transaction,
102362306a36Sopenharmony_ci			TCODE_WRITE_BLOCK_REQUEST, ptask->dest_node,
102462306a36Sopenharmony_ci			ptask->generation, ptask->speed, ptask->fifo_addr,
102562306a36Sopenharmony_ci			ptask->skb->data, tx_len, fwnet_write_complete, ptask);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	/* If the AT tasklet already ran, we may be last user. */
103062306a36Sopenharmony_ci	free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
103162306a36Sopenharmony_ci	if (!free)
103262306a36Sopenharmony_ci		ptask->enqueued = true;
103362306a36Sopenharmony_ci	else
103462306a36Sopenharmony_ci		dec_queued_datagrams(dev);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	netif_trans_update(dev->netdev);
103962306a36Sopenharmony_ci out:
104062306a36Sopenharmony_ci	if (free)
104162306a36Sopenharmony_ci		fwnet_free_ptask(ptask);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	return 0;
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cistatic void fwnet_fifo_stop(struct fwnet_device *dev)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	if (dev->local_fifo == FWNET_NO_FIFO_ADDR)
104962306a36Sopenharmony_ci		return;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	fw_core_remove_address_handler(&dev->handler);
105262306a36Sopenharmony_ci	dev->local_fifo = FWNET_NO_FIFO_ADDR;
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistatic int fwnet_fifo_start(struct fwnet_device *dev)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	int retval;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	if (dev->local_fifo != FWNET_NO_FIFO_ADDR)
106062306a36Sopenharmony_ci		return 0;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	dev->handler.length = 4096;
106362306a36Sopenharmony_ci	dev->handler.address_callback = fwnet_receive_packet;
106462306a36Sopenharmony_ci	dev->handler.callback_data = dev;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	retval = fw_core_add_address_handler(&dev->handler,
106762306a36Sopenharmony_ci					     &fw_high_memory_region);
106862306a36Sopenharmony_ci	if (retval < 0)
106962306a36Sopenharmony_ci		return retval;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	dev->local_fifo = dev->handler.offset;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	return 0;
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_cistatic void __fwnet_broadcast_stop(struct fwnet_device *dev)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	unsigned u;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	if (dev->broadcast_state != FWNET_BROADCAST_ERROR) {
108162306a36Sopenharmony_ci		for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++)
108262306a36Sopenharmony_ci			kunmap(dev->broadcast_rcv_buffer.pages[u]);
108362306a36Sopenharmony_ci		fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card);
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci	if (dev->broadcast_rcv_context) {
108662306a36Sopenharmony_ci		fw_iso_context_destroy(dev->broadcast_rcv_context);
108762306a36Sopenharmony_ci		dev->broadcast_rcv_context = NULL;
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci	kfree(dev->broadcast_rcv_buffer_ptrs);
109062306a36Sopenharmony_ci	dev->broadcast_rcv_buffer_ptrs = NULL;
109162306a36Sopenharmony_ci	dev->broadcast_state = FWNET_BROADCAST_ERROR;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic void fwnet_broadcast_stop(struct fwnet_device *dev)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	if (dev->broadcast_state == FWNET_BROADCAST_ERROR)
109762306a36Sopenharmony_ci		return;
109862306a36Sopenharmony_ci	fw_iso_context_stop(dev->broadcast_rcv_context);
109962306a36Sopenharmony_ci	__fwnet_broadcast_stop(dev);
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic int fwnet_broadcast_start(struct fwnet_device *dev)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	struct fw_iso_context *context;
110562306a36Sopenharmony_ci	int retval;
110662306a36Sopenharmony_ci	unsigned num_packets;
110762306a36Sopenharmony_ci	unsigned max_receive;
110862306a36Sopenharmony_ci	struct fw_iso_packet packet;
110962306a36Sopenharmony_ci	unsigned long offset;
111062306a36Sopenharmony_ci	void **ptrptr;
111162306a36Sopenharmony_ci	unsigned u;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (dev->broadcast_state != FWNET_BROADCAST_ERROR)
111462306a36Sopenharmony_ci		return 0;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	max_receive = 1U << (dev->card->max_receive + 1);
111762306a36Sopenharmony_ci	num_packets = (FWNET_ISO_PAGE_COUNT * PAGE_SIZE) / max_receive;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	ptrptr = kmalloc_array(num_packets, sizeof(void *), GFP_KERNEL);
112062306a36Sopenharmony_ci	if (!ptrptr) {
112162306a36Sopenharmony_ci		retval = -ENOMEM;
112262306a36Sopenharmony_ci		goto failed;
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci	dev->broadcast_rcv_buffer_ptrs = ptrptr;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	context = fw_iso_context_create(dev->card, FW_ISO_CONTEXT_RECEIVE,
112762306a36Sopenharmony_ci					IEEE1394_BROADCAST_CHANNEL,
112862306a36Sopenharmony_ci					dev->card->link_speed, 8,
112962306a36Sopenharmony_ci					fwnet_receive_broadcast, dev);
113062306a36Sopenharmony_ci	if (IS_ERR(context)) {
113162306a36Sopenharmony_ci		retval = PTR_ERR(context);
113262306a36Sopenharmony_ci		goto failed;
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer, dev->card,
113662306a36Sopenharmony_ci				    FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE);
113762306a36Sopenharmony_ci	if (retval < 0)
113862306a36Sopenharmony_ci		goto failed;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	dev->broadcast_state = FWNET_BROADCAST_STOPPED;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) {
114362306a36Sopenharmony_ci		void *ptr;
114462306a36Sopenharmony_ci		unsigned v;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		ptr = kmap(dev->broadcast_rcv_buffer.pages[u]);
114762306a36Sopenharmony_ci		for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++)
114862306a36Sopenharmony_ci			*ptrptr++ = (void *) ((char *)ptr + v * max_receive);
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci	dev->broadcast_rcv_context = context;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	packet.payload_length = max_receive;
115362306a36Sopenharmony_ci	packet.interrupt = 1;
115462306a36Sopenharmony_ci	packet.skip = 0;
115562306a36Sopenharmony_ci	packet.tag = 3;
115662306a36Sopenharmony_ci	packet.sy = 0;
115762306a36Sopenharmony_ci	packet.header_length = IEEE1394_GASP_HDR_SIZE;
115862306a36Sopenharmony_ci	offset = 0;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	for (u = 0; u < num_packets; u++) {
116162306a36Sopenharmony_ci		retval = fw_iso_context_queue(context, &packet,
116262306a36Sopenharmony_ci				&dev->broadcast_rcv_buffer, offset);
116362306a36Sopenharmony_ci		if (retval < 0)
116462306a36Sopenharmony_ci			goto failed;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci		offset += max_receive;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci	dev->num_broadcast_rcv_ptrs = num_packets;
116962306a36Sopenharmony_ci	dev->rcv_buffer_size = max_receive;
117062306a36Sopenharmony_ci	dev->broadcast_rcv_next_ptr = 0U;
117162306a36Sopenharmony_ci	retval = fw_iso_context_start(context, -1, 0,
117262306a36Sopenharmony_ci			FW_ISO_CONTEXT_MATCH_ALL_TAGS); /* ??? sync */
117362306a36Sopenharmony_ci	if (retval < 0)
117462306a36Sopenharmony_ci		goto failed;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	/* FIXME: adjust it according to the min. speed of all known peers? */
117762306a36Sopenharmony_ci	dev->broadcast_xmt_max_payload = IEEE1394_MAX_PAYLOAD_S100
117862306a36Sopenharmony_ci			- IEEE1394_GASP_HDR_SIZE - RFC2374_UNFRAG_HDR_SIZE;
117962306a36Sopenharmony_ci	dev->broadcast_state = FWNET_BROADCAST_RUNNING;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	return 0;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci failed:
118462306a36Sopenharmony_ci	__fwnet_broadcast_stop(dev);
118562306a36Sopenharmony_ci	return retval;
118662306a36Sopenharmony_ci}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic void set_carrier_state(struct fwnet_device *dev)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	if (dev->peer_count > 1)
119162306a36Sopenharmony_ci		netif_carrier_on(dev->netdev);
119262306a36Sopenharmony_ci	else
119362306a36Sopenharmony_ci		netif_carrier_off(dev->netdev);
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci/* ifup */
119762306a36Sopenharmony_cistatic int fwnet_open(struct net_device *net)
119862306a36Sopenharmony_ci{
119962306a36Sopenharmony_ci	struct fwnet_device *dev = netdev_priv(net);
120062306a36Sopenharmony_ci	int ret;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	ret = fwnet_broadcast_start(dev);
120362306a36Sopenharmony_ci	if (ret)
120462306a36Sopenharmony_ci		return ret;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	netif_start_queue(net);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	spin_lock_irq(&dev->lock);
120962306a36Sopenharmony_ci	set_carrier_state(dev);
121062306a36Sopenharmony_ci	spin_unlock_irq(&dev->lock);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return 0;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci/* ifdown */
121662306a36Sopenharmony_cistatic int fwnet_stop(struct net_device *net)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	struct fwnet_device *dev = netdev_priv(net);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	netif_stop_queue(net);
122162306a36Sopenharmony_ci	fwnet_broadcast_stop(dev);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	return 0;
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cistatic netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
122762306a36Sopenharmony_ci{
122862306a36Sopenharmony_ci	struct fwnet_header hdr_buf;
122962306a36Sopenharmony_ci	struct fwnet_device *dev = netdev_priv(net);
123062306a36Sopenharmony_ci	__be16 proto;
123162306a36Sopenharmony_ci	u16 dest_node;
123262306a36Sopenharmony_ci	unsigned max_payload;
123362306a36Sopenharmony_ci	u16 dg_size;
123462306a36Sopenharmony_ci	u16 *datagram_label_ptr;
123562306a36Sopenharmony_ci	struct fwnet_packet_task *ptask;
123662306a36Sopenharmony_ci	struct fwnet_peer *peer;
123762306a36Sopenharmony_ci	unsigned long flags;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	/* Can this happen? */
124262306a36Sopenharmony_ci	if (netif_queue_stopped(dev->netdev)) {
124362306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	ptask = kmem_cache_alloc(fwnet_packet_task_cache, GFP_ATOMIC);
124962306a36Sopenharmony_ci	if (ptask == NULL)
125062306a36Sopenharmony_ci		goto fail;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
125362306a36Sopenharmony_ci	if (!skb)
125462306a36Sopenharmony_ci		goto fail;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	/*
125762306a36Sopenharmony_ci	 * Make a copy of the driver-specific header.
125862306a36Sopenharmony_ci	 * We might need to rebuild the header on tx failure.
125962306a36Sopenharmony_ci	 */
126062306a36Sopenharmony_ci	memcpy(&hdr_buf, skb->data, sizeof(hdr_buf));
126162306a36Sopenharmony_ci	proto = hdr_buf.h_proto;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	switch (proto) {
126462306a36Sopenharmony_ci	case htons(ETH_P_ARP):
126562306a36Sopenharmony_ci	case htons(ETH_P_IP):
126662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
126762306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
126862306a36Sopenharmony_ci#endif
126962306a36Sopenharmony_ci		break;
127062306a36Sopenharmony_ci	default:
127162306a36Sopenharmony_ci		goto fail;
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	skb_pull(skb, sizeof(hdr_buf));
127562306a36Sopenharmony_ci	dg_size = skb->len;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	/*
127862306a36Sopenharmony_ci	 * Set the transmission type for the packet.  ARP packets and IP
127962306a36Sopenharmony_ci	 * broadcast packets are sent via GASP.
128062306a36Sopenharmony_ci	 */
128162306a36Sopenharmony_ci	if (fwnet_hwaddr_is_multicast(hdr_buf.h_dest)) {
128262306a36Sopenharmony_ci		max_payload        = dev->broadcast_xmt_max_payload;
128362306a36Sopenharmony_ci		datagram_label_ptr = &dev->broadcast_xmt_datagramlabel;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		ptask->fifo_addr   = FWNET_NO_FIFO_ADDR;
128662306a36Sopenharmony_ci		ptask->generation  = 0;
128762306a36Sopenharmony_ci		ptask->dest_node   = IEEE1394_ALL_NODES;
128862306a36Sopenharmony_ci		ptask->speed       = SCODE_100;
128962306a36Sopenharmony_ci	} else {
129062306a36Sopenharmony_ci		union fwnet_hwaddr *ha = (union fwnet_hwaddr *)hdr_buf.h_dest;
129162306a36Sopenharmony_ci		__be64 guid = get_unaligned(&ha->uc.uniq_id);
129262306a36Sopenharmony_ci		u8 generation;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci		peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid));
129562306a36Sopenharmony_ci		if (!peer)
129662306a36Sopenharmony_ci			goto fail;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci		generation         = peer->generation;
129962306a36Sopenharmony_ci		dest_node          = peer->node_id;
130062306a36Sopenharmony_ci		max_payload        = peer->max_payload;
130162306a36Sopenharmony_ci		datagram_label_ptr = &peer->datagram_label;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci		ptask->fifo_addr   = get_unaligned_be48(ha->uc.fifo);
130462306a36Sopenharmony_ci		ptask->generation  = generation;
130562306a36Sopenharmony_ci		ptask->dest_node   = dest_node;
130662306a36Sopenharmony_ci		ptask->speed       = peer->speed;
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	ptask->hdr.w0 = 0;
131062306a36Sopenharmony_ci	ptask->hdr.w1 = 0;
131162306a36Sopenharmony_ci	ptask->skb = skb;
131262306a36Sopenharmony_ci	ptask->dev = dev;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/* Does it all fit in one packet? */
131562306a36Sopenharmony_ci	if (dg_size <= max_payload) {
131662306a36Sopenharmony_ci		fwnet_make_uf_hdr(&ptask->hdr, ntohs(proto));
131762306a36Sopenharmony_ci		ptask->outstanding_pkts = 1;
131862306a36Sopenharmony_ci		max_payload = dg_size + RFC2374_UNFRAG_HDR_SIZE;
131962306a36Sopenharmony_ci	} else {
132062306a36Sopenharmony_ci		u16 datagram_label;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci		max_payload -= RFC2374_FRAG_OVERHEAD;
132362306a36Sopenharmony_ci		datagram_label = (*datagram_label_ptr)++;
132462306a36Sopenharmony_ci		fwnet_make_ff_hdr(&ptask->hdr, ntohs(proto), dg_size,
132562306a36Sopenharmony_ci				  datagram_label);
132662306a36Sopenharmony_ci		ptask->outstanding_pkts = DIV_ROUND_UP(dg_size, max_payload);
132762306a36Sopenharmony_ci		max_payload += RFC2374_FRAG_HDR_SIZE;
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	if (++dev->queued_datagrams == FWNET_MAX_QUEUED_DATAGRAMS)
133162306a36Sopenharmony_ci		netif_stop_queue(dev->netdev);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	ptask->max_payload = max_payload;
133662306a36Sopenharmony_ci	ptask->enqueued    = 0;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	fwnet_send_packet(ptask);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	return NETDEV_TX_OK;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci fail:
134362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	if (ptask)
134662306a36Sopenharmony_ci		kmem_cache_free(fwnet_packet_task_cache, ptask);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	if (skb != NULL)
134962306a36Sopenharmony_ci		dev_kfree_skb(skb);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	net->stats.tx_dropped++;
135262306a36Sopenharmony_ci	net->stats.tx_errors++;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/*
135562306a36Sopenharmony_ci	 * FIXME: According to a patch from 2003-02-26, "returning non-zero
135662306a36Sopenharmony_ci	 * causes serious problems" here, allegedly.  Before that patch,
135762306a36Sopenharmony_ci	 * -ERRNO was returned which is not appropriate under Linux 2.6.
135862306a36Sopenharmony_ci	 * Perhaps more needs to be done?  Stop the queue in serious
135962306a36Sopenharmony_ci	 * conditions and restart it elsewhere?
136062306a36Sopenharmony_ci	 */
136162306a36Sopenharmony_ci	return NETDEV_TX_OK;
136262306a36Sopenharmony_ci}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_cistatic const struct ethtool_ops fwnet_ethtool_ops = {
136562306a36Sopenharmony_ci	.get_link	= ethtool_op_get_link,
136662306a36Sopenharmony_ci};
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_cistatic const struct net_device_ops fwnet_netdev_ops = {
136962306a36Sopenharmony_ci	.ndo_open       = fwnet_open,
137062306a36Sopenharmony_ci	.ndo_stop	= fwnet_stop,
137162306a36Sopenharmony_ci	.ndo_start_xmit = fwnet_tx,
137262306a36Sopenharmony_ci};
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_cistatic void fwnet_init_dev(struct net_device *net)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	net->header_ops		= &fwnet_header_ops;
137762306a36Sopenharmony_ci	net->netdev_ops		= &fwnet_netdev_ops;
137862306a36Sopenharmony_ci	net->watchdog_timeo	= 2 * HZ;
137962306a36Sopenharmony_ci	net->flags		= IFF_BROADCAST | IFF_MULTICAST;
138062306a36Sopenharmony_ci	net->features		= NETIF_F_HIGHDMA;
138162306a36Sopenharmony_ci	net->addr_len		= FWNET_ALEN;
138262306a36Sopenharmony_ci	net->hard_header_len	= FWNET_HLEN;
138362306a36Sopenharmony_ci	net->type		= ARPHRD_IEEE1394;
138462306a36Sopenharmony_ci	net->tx_queue_len	= FWNET_TX_QUEUE_LEN;
138562306a36Sopenharmony_ci	net->ethtool_ops	= &fwnet_ethtool_ops;
138662306a36Sopenharmony_ci}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci/* caller must hold fwnet_device_mutex */
138962306a36Sopenharmony_cistatic struct fwnet_device *fwnet_dev_find(struct fw_card *card)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	struct fwnet_device *dev;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	list_for_each_entry(dev, &fwnet_device_list, dev_link)
139462306a36Sopenharmony_ci		if (dev->card == card)
139562306a36Sopenharmony_ci			return dev;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	return NULL;
139862306a36Sopenharmony_ci}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_cistatic int fwnet_add_peer(struct fwnet_device *dev,
140162306a36Sopenharmony_ci			  struct fw_unit *unit, struct fw_device *device)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	struct fwnet_peer *peer;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	peer = kmalloc(sizeof(*peer), GFP_KERNEL);
140662306a36Sopenharmony_ci	if (!peer)
140762306a36Sopenharmony_ci		return -ENOMEM;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	dev_set_drvdata(&unit->device, peer);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	peer->dev = dev;
141262306a36Sopenharmony_ci	peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
141362306a36Sopenharmony_ci	INIT_LIST_HEAD(&peer->pd_list);
141462306a36Sopenharmony_ci	peer->pdg_size = 0;
141562306a36Sopenharmony_ci	peer->datagram_label = 0;
141662306a36Sopenharmony_ci	peer->speed = device->max_speed;
141762306a36Sopenharmony_ci	peer->max_payload = fwnet_max_payload(device->max_rec, peer->speed);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	peer->generation = device->generation;
142062306a36Sopenharmony_ci	smp_rmb();
142162306a36Sopenharmony_ci	peer->node_id = device->node_id;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	spin_lock_irq(&dev->lock);
142462306a36Sopenharmony_ci	list_add_tail(&peer->peer_link, &dev->peer_list);
142562306a36Sopenharmony_ci	dev->peer_count++;
142662306a36Sopenharmony_ci	set_carrier_state(dev);
142762306a36Sopenharmony_ci	spin_unlock_irq(&dev->lock);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	return 0;
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_cistatic int fwnet_probe(struct fw_unit *unit,
143362306a36Sopenharmony_ci		       const struct ieee1394_device_id *id)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	struct fw_device *device = fw_parent_device(unit);
143662306a36Sopenharmony_ci	struct fw_card *card = device->card;
143762306a36Sopenharmony_ci	struct net_device *net;
143862306a36Sopenharmony_ci	bool allocated_netdev = false;
143962306a36Sopenharmony_ci	struct fwnet_device *dev;
144062306a36Sopenharmony_ci	union fwnet_hwaddr ha;
144162306a36Sopenharmony_ci	int ret;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	mutex_lock(&fwnet_device_mutex);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	dev = fwnet_dev_find(card);
144662306a36Sopenharmony_ci	if (dev) {
144762306a36Sopenharmony_ci		net = dev->netdev;
144862306a36Sopenharmony_ci		goto have_dev;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	net = alloc_netdev(sizeof(*dev), "firewire%d", NET_NAME_UNKNOWN,
145262306a36Sopenharmony_ci			   fwnet_init_dev);
145362306a36Sopenharmony_ci	if (net == NULL) {
145462306a36Sopenharmony_ci		mutex_unlock(&fwnet_device_mutex);
145562306a36Sopenharmony_ci		return -ENOMEM;
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	allocated_netdev = true;
145962306a36Sopenharmony_ci	SET_NETDEV_DEV(net, card->device);
146062306a36Sopenharmony_ci	dev = netdev_priv(net);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	spin_lock_init(&dev->lock);
146362306a36Sopenharmony_ci	dev->broadcast_state = FWNET_BROADCAST_ERROR;
146462306a36Sopenharmony_ci	dev->broadcast_rcv_context = NULL;
146562306a36Sopenharmony_ci	dev->broadcast_xmt_max_payload = 0;
146662306a36Sopenharmony_ci	dev->broadcast_xmt_datagramlabel = 0;
146762306a36Sopenharmony_ci	dev->local_fifo = FWNET_NO_FIFO_ADDR;
146862306a36Sopenharmony_ci	dev->queued_datagrams = 0;
146962306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->peer_list);
147062306a36Sopenharmony_ci	dev->card = card;
147162306a36Sopenharmony_ci	dev->netdev = net;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	ret = fwnet_fifo_start(dev);
147462306a36Sopenharmony_ci	if (ret < 0)
147562306a36Sopenharmony_ci		goto out;
147662306a36Sopenharmony_ci	dev->local_fifo = dev->handler.offset;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/*
147962306a36Sopenharmony_ci	 * default MTU: RFC 2734 cl. 4, RFC 3146 cl. 4
148062306a36Sopenharmony_ci	 * maximum MTU: RFC 2734 cl. 4.2, fragment encapsulation header's
148162306a36Sopenharmony_ci	 *              maximum possible datagram_size + 1 = 0xfff + 1
148262306a36Sopenharmony_ci	 */
148362306a36Sopenharmony_ci	net->mtu = 1500U;
148462306a36Sopenharmony_ci	net->min_mtu = ETH_MIN_MTU;
148562306a36Sopenharmony_ci	net->max_mtu = 4096U;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	/* Set our hardware address while we're at it */
148862306a36Sopenharmony_ci	ha.uc.uniq_id = cpu_to_be64(card->guid);
148962306a36Sopenharmony_ci	ha.uc.max_rec = dev->card->max_receive;
149062306a36Sopenharmony_ci	ha.uc.sspd = dev->card->link_speed;
149162306a36Sopenharmony_ci	put_unaligned_be48(dev->local_fifo, ha.uc.fifo);
149262306a36Sopenharmony_ci	dev_addr_set(net, ha.u);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	memset(net->broadcast, -1, net->addr_len);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	ret = register_netdev(net);
149762306a36Sopenharmony_ci	if (ret)
149862306a36Sopenharmony_ci		goto out;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	list_add_tail(&dev->dev_link, &fwnet_device_list);
150162306a36Sopenharmony_ci	dev_notice(&net->dev, "IP over IEEE 1394 on card %s\n",
150262306a36Sopenharmony_ci		   dev_name(card->device));
150362306a36Sopenharmony_ci have_dev:
150462306a36Sopenharmony_ci	ret = fwnet_add_peer(dev, unit, device);
150562306a36Sopenharmony_ci	if (ret && allocated_netdev) {
150662306a36Sopenharmony_ci		unregister_netdev(net);
150762306a36Sopenharmony_ci		list_del(&dev->dev_link);
150862306a36Sopenharmony_ci out:
150962306a36Sopenharmony_ci		fwnet_fifo_stop(dev);
151062306a36Sopenharmony_ci		free_netdev(net);
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	mutex_unlock(&fwnet_device_mutex);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	return ret;
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci/*
151962306a36Sopenharmony_ci * FIXME abort partially sent fragmented datagrams,
152062306a36Sopenharmony_ci * discard partially received fragmented datagrams
152162306a36Sopenharmony_ci */
152262306a36Sopenharmony_cistatic void fwnet_update(struct fw_unit *unit)
152362306a36Sopenharmony_ci{
152462306a36Sopenharmony_ci	struct fw_device *device = fw_parent_device(unit);
152562306a36Sopenharmony_ci	struct fwnet_peer *peer = dev_get_drvdata(&unit->device);
152662306a36Sopenharmony_ci	int generation;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	generation = device->generation;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	spin_lock_irq(&peer->dev->lock);
153162306a36Sopenharmony_ci	peer->node_id    = device->node_id;
153262306a36Sopenharmony_ci	peer->generation = generation;
153362306a36Sopenharmony_ci	spin_unlock_irq(&peer->dev->lock);
153462306a36Sopenharmony_ci}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_cistatic void fwnet_remove_peer(struct fwnet_peer *peer, struct fwnet_device *dev)
153762306a36Sopenharmony_ci{
153862306a36Sopenharmony_ci	struct fwnet_partial_datagram *pd, *pd_next;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	spin_lock_irq(&dev->lock);
154162306a36Sopenharmony_ci	list_del(&peer->peer_link);
154262306a36Sopenharmony_ci	dev->peer_count--;
154362306a36Sopenharmony_ci	set_carrier_state(dev);
154462306a36Sopenharmony_ci	spin_unlock_irq(&dev->lock);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	list_for_each_entry_safe(pd, pd_next, &peer->pd_list, pd_link)
154762306a36Sopenharmony_ci		fwnet_pd_delete(pd);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	kfree(peer);
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_cistatic void fwnet_remove(struct fw_unit *unit)
155362306a36Sopenharmony_ci{
155462306a36Sopenharmony_ci	struct fwnet_peer *peer = dev_get_drvdata(&unit->device);
155562306a36Sopenharmony_ci	struct fwnet_device *dev = peer->dev;
155662306a36Sopenharmony_ci	struct net_device *net;
155762306a36Sopenharmony_ci	int i;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	mutex_lock(&fwnet_device_mutex);
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	net = dev->netdev;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	fwnet_remove_peer(peer, dev);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	if (list_empty(&dev->peer_list)) {
156662306a36Sopenharmony_ci		unregister_netdev(net);
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci		fwnet_fifo_stop(dev);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		for (i = 0; dev->queued_datagrams && i < 5; i++)
157162306a36Sopenharmony_ci			ssleep(1);
157262306a36Sopenharmony_ci		WARN_ON(dev->queued_datagrams);
157362306a36Sopenharmony_ci		list_del(&dev->dev_link);
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci		free_netdev(net);
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	mutex_unlock(&fwnet_device_mutex);
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic const struct ieee1394_device_id fwnet_id_table[] = {
158262306a36Sopenharmony_ci	{
158362306a36Sopenharmony_ci		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
158462306a36Sopenharmony_ci				IEEE1394_MATCH_VERSION,
158562306a36Sopenharmony_ci		.specifier_id = IANA_SPECIFIER_ID,
158662306a36Sopenharmony_ci		.version      = RFC2734_SW_VERSION,
158762306a36Sopenharmony_ci	},
158862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
158962306a36Sopenharmony_ci	{
159062306a36Sopenharmony_ci		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
159162306a36Sopenharmony_ci				IEEE1394_MATCH_VERSION,
159262306a36Sopenharmony_ci		.specifier_id = IANA_SPECIFIER_ID,
159362306a36Sopenharmony_ci		.version      = RFC3146_SW_VERSION,
159462306a36Sopenharmony_ci	},
159562306a36Sopenharmony_ci#endif
159662306a36Sopenharmony_ci	{ }
159762306a36Sopenharmony_ci};
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_cistatic struct fw_driver fwnet_driver = {
160062306a36Sopenharmony_ci	.driver = {
160162306a36Sopenharmony_ci		.owner  = THIS_MODULE,
160262306a36Sopenharmony_ci		.name   = KBUILD_MODNAME,
160362306a36Sopenharmony_ci		.bus    = &fw_bus_type,
160462306a36Sopenharmony_ci	},
160562306a36Sopenharmony_ci	.probe    = fwnet_probe,
160662306a36Sopenharmony_ci	.update   = fwnet_update,
160762306a36Sopenharmony_ci	.remove   = fwnet_remove,
160862306a36Sopenharmony_ci	.id_table = fwnet_id_table,
160962306a36Sopenharmony_ci};
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_cistatic const u32 rfc2374_unit_directory_data[] = {
161262306a36Sopenharmony_ci	0x00040000,	/* directory_length		*/
161362306a36Sopenharmony_ci	0x1200005e,	/* unit_specifier_id: IANA	*/
161462306a36Sopenharmony_ci	0x81000003,	/* textual descriptor offset	*/
161562306a36Sopenharmony_ci	0x13000001,	/* unit_sw_version: RFC 2734	*/
161662306a36Sopenharmony_ci	0x81000005,	/* textual descriptor offset	*/
161762306a36Sopenharmony_ci	0x00030000,	/* descriptor_length		*/
161862306a36Sopenharmony_ci	0x00000000,	/* text				*/
161962306a36Sopenharmony_ci	0x00000000,	/* minimal ASCII, en		*/
162062306a36Sopenharmony_ci	0x49414e41,	/* I A N A			*/
162162306a36Sopenharmony_ci	0x00030000,	/* descriptor_length		*/
162262306a36Sopenharmony_ci	0x00000000,	/* text				*/
162362306a36Sopenharmony_ci	0x00000000,	/* minimal ASCII, en		*/
162462306a36Sopenharmony_ci	0x49507634,	/* I P v 4			*/
162562306a36Sopenharmony_ci};
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_cistatic struct fw_descriptor rfc2374_unit_directory = {
162862306a36Sopenharmony_ci	.length = ARRAY_SIZE(rfc2374_unit_directory_data),
162962306a36Sopenharmony_ci	.key    = (CSR_DIRECTORY | CSR_UNIT) << 24,
163062306a36Sopenharmony_ci	.data   = rfc2374_unit_directory_data
163162306a36Sopenharmony_ci};
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
163462306a36Sopenharmony_cistatic const u32 rfc3146_unit_directory_data[] = {
163562306a36Sopenharmony_ci	0x00040000,	/* directory_length		*/
163662306a36Sopenharmony_ci	0x1200005e,	/* unit_specifier_id: IANA	*/
163762306a36Sopenharmony_ci	0x81000003,	/* textual descriptor offset	*/
163862306a36Sopenharmony_ci	0x13000002,	/* unit_sw_version: RFC 3146	*/
163962306a36Sopenharmony_ci	0x81000005,	/* textual descriptor offset	*/
164062306a36Sopenharmony_ci	0x00030000,	/* descriptor_length		*/
164162306a36Sopenharmony_ci	0x00000000,	/* text				*/
164262306a36Sopenharmony_ci	0x00000000,	/* minimal ASCII, en		*/
164362306a36Sopenharmony_ci	0x49414e41,	/* I A N A			*/
164462306a36Sopenharmony_ci	0x00030000,	/* descriptor_length		*/
164562306a36Sopenharmony_ci	0x00000000,	/* text				*/
164662306a36Sopenharmony_ci	0x00000000,	/* minimal ASCII, en		*/
164762306a36Sopenharmony_ci	0x49507636,	/* I P v 6			*/
164862306a36Sopenharmony_ci};
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic struct fw_descriptor rfc3146_unit_directory = {
165162306a36Sopenharmony_ci	.length = ARRAY_SIZE(rfc3146_unit_directory_data),
165262306a36Sopenharmony_ci	.key    = (CSR_DIRECTORY | CSR_UNIT) << 24,
165362306a36Sopenharmony_ci	.data   = rfc3146_unit_directory_data
165462306a36Sopenharmony_ci};
165562306a36Sopenharmony_ci#endif
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_cistatic int __init fwnet_init(void)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	int err;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	err = fw_core_add_descriptor(&rfc2374_unit_directory);
166262306a36Sopenharmony_ci	if (err)
166362306a36Sopenharmony_ci		return err;
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
166662306a36Sopenharmony_ci	err = fw_core_add_descriptor(&rfc3146_unit_directory);
166762306a36Sopenharmony_ci	if (err)
166862306a36Sopenharmony_ci		goto out;
166962306a36Sopenharmony_ci#endif
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	fwnet_packet_task_cache = kmem_cache_create("packet_task",
167262306a36Sopenharmony_ci			sizeof(struct fwnet_packet_task), 0, 0, NULL);
167362306a36Sopenharmony_ci	if (!fwnet_packet_task_cache) {
167462306a36Sopenharmony_ci		err = -ENOMEM;
167562306a36Sopenharmony_ci		goto out2;
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	err = driver_register(&fwnet_driver.driver);
167962306a36Sopenharmony_ci	if (!err)
168062306a36Sopenharmony_ci		return 0;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	kmem_cache_destroy(fwnet_packet_task_cache);
168362306a36Sopenharmony_ciout2:
168462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
168562306a36Sopenharmony_ci	fw_core_remove_descriptor(&rfc3146_unit_directory);
168662306a36Sopenharmony_ciout:
168762306a36Sopenharmony_ci#endif
168862306a36Sopenharmony_ci	fw_core_remove_descriptor(&rfc2374_unit_directory);
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	return err;
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_cimodule_init(fwnet_init);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_cistatic void __exit fwnet_cleanup(void)
169562306a36Sopenharmony_ci{
169662306a36Sopenharmony_ci	driver_unregister(&fwnet_driver.driver);
169762306a36Sopenharmony_ci	kmem_cache_destroy(fwnet_packet_task_cache);
169862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
169962306a36Sopenharmony_ci	fw_core_remove_descriptor(&rfc3146_unit_directory);
170062306a36Sopenharmony_ci#endif
170162306a36Sopenharmony_ci	fw_core_remove_descriptor(&rfc2374_unit_directory);
170262306a36Sopenharmony_ci}
170362306a36Sopenharmony_cimodule_exit(fwnet_cleanup);
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ciMODULE_AUTHOR("Jay Fenlason <fenlason@redhat.com>");
170662306a36Sopenharmony_ciMODULE_DESCRIPTION("IP over IEEE1394 as per RFC 2734/3146");
170762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
170862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ieee1394, fwnet_id_table);
1709