18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IPv4 over IEEE 1394, per RFC 2734
48c2ecf20Sopenharmony_ci * IPv6 over IEEE 1394, per RFC 3146
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * based on eth1394 by Ben Collins et al
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/bug.h>
128c2ecf20Sopenharmony_ci#include <linux/compiler.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/device.h>
158c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
168c2ecf20Sopenharmony_ci#include <linux/firewire.h>
178c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h>
188c2ecf20Sopenharmony_ci#include <linux/highmem.h>
198c2ecf20Sopenharmony_ci#include <linux/in.h>
208c2ecf20Sopenharmony_ci#include <linux/ip.h>
218c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
228c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
258c2ecf20Sopenharmony_ci#include <linux/mutex.h>
268c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
278c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
288c2ecf20Sopenharmony_ci#include <linux/slab.h>
298c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
328c2ecf20Sopenharmony_ci#include <net/arp.h>
338c2ecf20Sopenharmony_ci#include <net/firewire.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* rx limits */
368c2ecf20Sopenharmony_ci#define FWNET_MAX_FRAGMENTS		30 /* arbitrary, > TX queue depth */
378c2ecf20Sopenharmony_ci#define FWNET_ISO_PAGE_COUNT		(PAGE_SIZE < 16*1024 ? 4 : 2)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* tx limits */
408c2ecf20Sopenharmony_ci#define FWNET_MAX_QUEUED_DATAGRAMS	20 /* < 64 = number of tlabels */
418c2ecf20Sopenharmony_ci#define FWNET_MIN_QUEUED_DATAGRAMS	10 /* should keep AT DMA busy enough */
428c2ecf20Sopenharmony_ci#define FWNET_TX_QUEUE_LEN		FWNET_MAX_QUEUED_DATAGRAMS /* ? */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define IEEE1394_BROADCAST_CHANNEL	31
458c2ecf20Sopenharmony_ci#define IEEE1394_ALL_NODES		(0xffc0 | 0x003f)
468c2ecf20Sopenharmony_ci#define IEEE1394_MAX_PAYLOAD_S100	512
478c2ecf20Sopenharmony_ci#define FWNET_NO_FIFO_ADDR		(~0ULL)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define IANA_SPECIFIER_ID		0x00005eU
508c2ecf20Sopenharmony_ci#define RFC2734_SW_VERSION		0x000001U
518c2ecf20Sopenharmony_ci#define RFC3146_SW_VERSION		0x000002U
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define IEEE1394_GASP_HDR_SIZE	8
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define RFC2374_UNFRAG_HDR_SIZE	4
568c2ecf20Sopenharmony_ci#define RFC2374_FRAG_HDR_SIZE	8
578c2ecf20Sopenharmony_ci#define RFC2374_FRAG_OVERHEAD	4
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define RFC2374_HDR_UNFRAG	0	/* unfragmented		*/
608c2ecf20Sopenharmony_ci#define RFC2374_HDR_FIRSTFRAG	1	/* first fragment	*/
618c2ecf20Sopenharmony_ci#define RFC2374_HDR_LASTFRAG	2	/* last fragment	*/
628c2ecf20Sopenharmony_ci#define RFC2374_HDR_INTFRAG	3	/* interior fragment	*/
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic bool fwnet_hwaddr_is_multicast(u8 *ha)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	return !!(*ha & 1);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* IPv4 and IPv6 encapsulation header */
708c2ecf20Sopenharmony_cistruct rfc2734_header {
718c2ecf20Sopenharmony_ci	u32 w0;
728c2ecf20Sopenharmony_ci	u32 w1;
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define fwnet_get_hdr_lf(h)		(((h)->w0 & 0xc0000000) >> 30)
768c2ecf20Sopenharmony_ci#define fwnet_get_hdr_ether_type(h)	(((h)->w0 & 0x0000ffff))
778c2ecf20Sopenharmony_ci#define fwnet_get_hdr_dg_size(h)	((((h)->w0 & 0x0fff0000) >> 16) + 1)
788c2ecf20Sopenharmony_ci#define fwnet_get_hdr_fg_off(h)		(((h)->w0 & 0x00000fff))
798c2ecf20Sopenharmony_ci#define fwnet_get_hdr_dgl(h)		(((h)->w1 & 0xffff0000) >> 16)
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define fwnet_set_hdr_lf(lf)		((lf) << 30)
828c2ecf20Sopenharmony_ci#define fwnet_set_hdr_ether_type(et)	(et)
838c2ecf20Sopenharmony_ci#define fwnet_set_hdr_dg_size(dgs)	(((dgs) - 1) << 16)
848c2ecf20Sopenharmony_ci#define fwnet_set_hdr_fg_off(fgo)	(fgo)
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define fwnet_set_hdr_dgl(dgl)		((dgl) << 16)
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic inline void fwnet_make_uf_hdr(struct rfc2734_header *hdr,
898c2ecf20Sopenharmony_ci		unsigned ether_type)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_UNFRAG)
928c2ecf20Sopenharmony_ci		  | fwnet_set_hdr_ether_type(ether_type);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic inline void fwnet_make_ff_hdr(struct rfc2734_header *hdr,
968c2ecf20Sopenharmony_ci		unsigned ether_type, unsigned dg_size, unsigned dgl)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_FIRSTFRAG)
998c2ecf20Sopenharmony_ci		  | fwnet_set_hdr_dg_size(dg_size)
1008c2ecf20Sopenharmony_ci		  | fwnet_set_hdr_ether_type(ether_type);
1018c2ecf20Sopenharmony_ci	hdr->w1 = fwnet_set_hdr_dgl(dgl);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic inline void fwnet_make_sf_hdr(struct rfc2734_header *hdr,
1058c2ecf20Sopenharmony_ci		unsigned lf, unsigned dg_size, unsigned fg_off, unsigned dgl)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	hdr->w0 = fwnet_set_hdr_lf(lf)
1088c2ecf20Sopenharmony_ci		  | fwnet_set_hdr_dg_size(dg_size)
1098c2ecf20Sopenharmony_ci		  | fwnet_set_hdr_fg_off(fg_off);
1108c2ecf20Sopenharmony_ci	hdr->w1 = fwnet_set_hdr_dgl(dgl);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* This list keeps track of what parts of the datagram have been filled in */
1148c2ecf20Sopenharmony_cistruct fwnet_fragment_info {
1158c2ecf20Sopenharmony_ci	struct list_head fi_link;
1168c2ecf20Sopenharmony_ci	u16 offset;
1178c2ecf20Sopenharmony_ci	u16 len;
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistruct fwnet_partial_datagram {
1218c2ecf20Sopenharmony_ci	struct list_head pd_link;
1228c2ecf20Sopenharmony_ci	struct list_head fi_list;
1238c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1248c2ecf20Sopenharmony_ci	/* FIXME Why not use skb->data? */
1258c2ecf20Sopenharmony_ci	char *pbuf;
1268c2ecf20Sopenharmony_ci	u16 datagram_label;
1278c2ecf20Sopenharmony_ci	u16 ether_type;
1288c2ecf20Sopenharmony_ci	u16 datagram_size;
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(fwnet_device_mutex);
1328c2ecf20Sopenharmony_cistatic LIST_HEAD(fwnet_device_list);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistruct fwnet_device {
1358c2ecf20Sopenharmony_ci	struct list_head dev_link;
1368c2ecf20Sopenharmony_ci	spinlock_t lock;
1378c2ecf20Sopenharmony_ci	enum {
1388c2ecf20Sopenharmony_ci		FWNET_BROADCAST_ERROR,
1398c2ecf20Sopenharmony_ci		FWNET_BROADCAST_RUNNING,
1408c2ecf20Sopenharmony_ci		FWNET_BROADCAST_STOPPED,
1418c2ecf20Sopenharmony_ci	} broadcast_state;
1428c2ecf20Sopenharmony_ci	struct fw_iso_context *broadcast_rcv_context;
1438c2ecf20Sopenharmony_ci	struct fw_iso_buffer broadcast_rcv_buffer;
1448c2ecf20Sopenharmony_ci	void **broadcast_rcv_buffer_ptrs;
1458c2ecf20Sopenharmony_ci	unsigned broadcast_rcv_next_ptr;
1468c2ecf20Sopenharmony_ci	unsigned num_broadcast_rcv_ptrs;
1478c2ecf20Sopenharmony_ci	unsigned rcv_buffer_size;
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * This value is the maximum unfragmented datagram size that can be
1508c2ecf20Sopenharmony_ci	 * sent by the hardware.  It already has the GASP overhead and the
1518c2ecf20Sopenharmony_ci	 * unfragmented datagram header overhead calculated into it.
1528c2ecf20Sopenharmony_ci	 */
1538c2ecf20Sopenharmony_ci	unsigned broadcast_xmt_max_payload;
1548c2ecf20Sopenharmony_ci	u16 broadcast_xmt_datagramlabel;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/*
1578c2ecf20Sopenharmony_ci	 * The CSR address that remote nodes must send datagrams to for us to
1588c2ecf20Sopenharmony_ci	 * receive them.
1598c2ecf20Sopenharmony_ci	 */
1608c2ecf20Sopenharmony_ci	struct fw_address_handler handler;
1618c2ecf20Sopenharmony_ci	u64 local_fifo;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Number of tx datagrams that have been queued but not yet acked */
1648c2ecf20Sopenharmony_ci	int queued_datagrams;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	int peer_count;
1678c2ecf20Sopenharmony_ci	struct list_head peer_list;
1688c2ecf20Sopenharmony_ci	struct fw_card *card;
1698c2ecf20Sopenharmony_ci	struct net_device *netdev;
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistruct fwnet_peer {
1738c2ecf20Sopenharmony_ci	struct list_head peer_link;
1748c2ecf20Sopenharmony_ci	struct fwnet_device *dev;
1758c2ecf20Sopenharmony_ci	u64 guid;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* guarded by dev->lock */
1788c2ecf20Sopenharmony_ci	struct list_head pd_list; /* received partial datagrams */
1798c2ecf20Sopenharmony_ci	unsigned pdg_size;        /* pd_list size */
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	u16 datagram_label;       /* outgoing datagram label */
1828c2ecf20Sopenharmony_ci	u16 max_payload;          /* includes RFC2374_FRAG_HDR_SIZE overhead */
1838c2ecf20Sopenharmony_ci	int node_id;
1848c2ecf20Sopenharmony_ci	int generation;
1858c2ecf20Sopenharmony_ci	unsigned speed;
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/* This is our task struct. It's used for the packet complete callback.  */
1898c2ecf20Sopenharmony_cistruct fwnet_packet_task {
1908c2ecf20Sopenharmony_ci	struct fw_transaction transaction;
1918c2ecf20Sopenharmony_ci	struct rfc2734_header hdr;
1928c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1938c2ecf20Sopenharmony_ci	struct fwnet_device *dev;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	int outstanding_pkts;
1968c2ecf20Sopenharmony_ci	u64 fifo_addr;
1978c2ecf20Sopenharmony_ci	u16 dest_node;
1988c2ecf20Sopenharmony_ci	u16 max_payload;
1998c2ecf20Sopenharmony_ci	u8 generation;
2008c2ecf20Sopenharmony_ci	u8 speed;
2018c2ecf20Sopenharmony_ci	u8 enqueued;
2028c2ecf20Sopenharmony_ci};
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/*
2058c2ecf20Sopenharmony_ci * Get fifo address embedded in hwaddr
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_cistatic __u64 fwnet_hwaddr_fifo(union fwnet_hwaddr *ha)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	return (u64)get_unaligned_be16(&ha->uc.fifo_hi) << 32
2108c2ecf20Sopenharmony_ci	       | get_unaligned_be32(&ha->uc.fifo_lo);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/*
2148c2ecf20Sopenharmony_ci * saddr == NULL means use device source address.
2158c2ecf20Sopenharmony_ci * daddr == NULL means leave destination address (eg unresolved arp).
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_cistatic int fwnet_header_create(struct sk_buff *skb, struct net_device *net,
2188c2ecf20Sopenharmony_ci			unsigned short type, const void *daddr,
2198c2ecf20Sopenharmony_ci			const void *saddr, unsigned len)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct fwnet_header *h;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	h = skb_push(skb, sizeof(*h));
2248c2ecf20Sopenharmony_ci	put_unaligned_be16(type, &h->h_proto);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (net->flags & (IFF_LOOPBACK | IFF_NOARP)) {
2278c2ecf20Sopenharmony_ci		memset(h->h_dest, 0, net->addr_len);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		return net->hard_header_len;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (daddr) {
2338c2ecf20Sopenharmony_ci		memcpy(h->h_dest, daddr, net->addr_len);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		return net->hard_header_len;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return -net->hard_header_len;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int fwnet_header_cache(const struct neighbour *neigh,
2428c2ecf20Sopenharmony_ci			      struct hh_cache *hh, __be16 type)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct net_device *net;
2458c2ecf20Sopenharmony_ci	struct fwnet_header *h;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (type == cpu_to_be16(ETH_P_802_3))
2488c2ecf20Sopenharmony_ci		return -1;
2498c2ecf20Sopenharmony_ci	net = neigh->dev;
2508c2ecf20Sopenharmony_ci	h = (struct fwnet_header *)((u8 *)hh->hh_data + HH_DATA_OFF(sizeof(*h)));
2518c2ecf20Sopenharmony_ci	h->h_proto = type;
2528c2ecf20Sopenharmony_ci	memcpy(h->h_dest, neigh->ha, net->addr_len);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* Pairs with the READ_ONCE() in neigh_resolve_output(),
2558c2ecf20Sopenharmony_ci	 * neigh_hh_output() and neigh_update_hhs().
2568c2ecf20Sopenharmony_ci	 */
2578c2ecf20Sopenharmony_ci	smp_store_release(&hh->hh_len, FWNET_HLEN);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return 0;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/* Called by Address Resolution module to notify changes in address. */
2638c2ecf20Sopenharmony_cistatic void fwnet_header_cache_update(struct hh_cache *hh,
2648c2ecf20Sopenharmony_ci		const struct net_device *net, const unsigned char *haddr)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	memcpy((u8 *)hh->hh_data + HH_DATA_OFF(FWNET_HLEN), haddr, net->addr_len);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	memcpy(haddr, skb->dev->dev_addr, FWNET_ALEN);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return FWNET_ALEN;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic const struct header_ops fwnet_header_ops = {
2778c2ecf20Sopenharmony_ci	.create         = fwnet_header_create,
2788c2ecf20Sopenharmony_ci	.cache		= fwnet_header_cache,
2798c2ecf20Sopenharmony_ci	.cache_update	= fwnet_header_cache_update,
2808c2ecf20Sopenharmony_ci	.parse          = fwnet_header_parse,
2818c2ecf20Sopenharmony_ci};
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci/* FIXME: is this correct for all cases? */
2848c2ecf20Sopenharmony_cistatic bool fwnet_frag_overlap(struct fwnet_partial_datagram *pd,
2858c2ecf20Sopenharmony_ci			       unsigned offset, unsigned len)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct fwnet_fragment_info *fi;
2888c2ecf20Sopenharmony_ci	unsigned end = offset + len;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	list_for_each_entry(fi, &pd->fi_list, fi_link)
2918c2ecf20Sopenharmony_ci		if (offset < fi->offset + fi->len && end > fi->offset)
2928c2ecf20Sopenharmony_ci			return true;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return false;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci/* Assumes that new fragment does not overlap any existing fragments */
2988c2ecf20Sopenharmony_cistatic struct fwnet_fragment_info *fwnet_frag_new(
2998c2ecf20Sopenharmony_ci	struct fwnet_partial_datagram *pd, unsigned offset, unsigned len)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct fwnet_fragment_info *fi, *fi2, *new;
3028c2ecf20Sopenharmony_ci	struct list_head *list;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	list = &pd->fi_list;
3058c2ecf20Sopenharmony_ci	list_for_each_entry(fi, &pd->fi_list, fi_link) {
3068c2ecf20Sopenharmony_ci		if (fi->offset + fi->len == offset) {
3078c2ecf20Sopenharmony_ci			/* The new fragment can be tacked on to the end */
3088c2ecf20Sopenharmony_ci			/* Did the new fragment plug a hole? */
3098c2ecf20Sopenharmony_ci			fi2 = list_entry(fi->fi_link.next,
3108c2ecf20Sopenharmony_ci					 struct fwnet_fragment_info, fi_link);
3118c2ecf20Sopenharmony_ci			if (fi->offset + fi->len == fi2->offset) {
3128c2ecf20Sopenharmony_ci				/* glue fragments together */
3138c2ecf20Sopenharmony_ci				fi->len += len + fi2->len;
3148c2ecf20Sopenharmony_ci				list_del(&fi2->fi_link);
3158c2ecf20Sopenharmony_ci				kfree(fi2);
3168c2ecf20Sopenharmony_ci			} else {
3178c2ecf20Sopenharmony_ci				fi->len += len;
3188c2ecf20Sopenharmony_ci			}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci			return fi;
3218c2ecf20Sopenharmony_ci		}
3228c2ecf20Sopenharmony_ci		if (offset + len == fi->offset) {
3238c2ecf20Sopenharmony_ci			/* The new fragment can be tacked on to the beginning */
3248c2ecf20Sopenharmony_ci			/* Did the new fragment plug a hole? */
3258c2ecf20Sopenharmony_ci			fi2 = list_entry(fi->fi_link.prev,
3268c2ecf20Sopenharmony_ci					 struct fwnet_fragment_info, fi_link);
3278c2ecf20Sopenharmony_ci			if (fi2->offset + fi2->len == fi->offset) {
3288c2ecf20Sopenharmony_ci				/* glue fragments together */
3298c2ecf20Sopenharmony_ci				fi2->len += fi->len + len;
3308c2ecf20Sopenharmony_ci				list_del(&fi->fi_link);
3318c2ecf20Sopenharmony_ci				kfree(fi);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci				return fi2;
3348c2ecf20Sopenharmony_ci			}
3358c2ecf20Sopenharmony_ci			fi->offset = offset;
3368c2ecf20Sopenharmony_ci			fi->len += len;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci			return fi;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci		if (offset > fi->offset + fi->len) {
3418c2ecf20Sopenharmony_ci			list = &fi->fi_link;
3428c2ecf20Sopenharmony_ci			break;
3438c2ecf20Sopenharmony_ci		}
3448c2ecf20Sopenharmony_ci		if (offset + len < fi->offset) {
3458c2ecf20Sopenharmony_ci			list = fi->fi_link.prev;
3468c2ecf20Sopenharmony_ci			break;
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	new = kmalloc(sizeof(*new), GFP_ATOMIC);
3518c2ecf20Sopenharmony_ci	if (!new)
3528c2ecf20Sopenharmony_ci		return NULL;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	new->offset = offset;
3558c2ecf20Sopenharmony_ci	new->len = len;
3568c2ecf20Sopenharmony_ci	list_add(&new->fi_link, list);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return new;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic struct fwnet_partial_datagram *fwnet_pd_new(struct net_device *net,
3628c2ecf20Sopenharmony_ci		struct fwnet_peer *peer, u16 datagram_label, unsigned dg_size,
3638c2ecf20Sopenharmony_ci		void *frag_buf, unsigned frag_off, unsigned frag_len)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct fwnet_partial_datagram *new;
3668c2ecf20Sopenharmony_ci	struct fwnet_fragment_info *fi;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	new = kmalloc(sizeof(*new), GFP_ATOMIC);
3698c2ecf20Sopenharmony_ci	if (!new)
3708c2ecf20Sopenharmony_ci		goto fail;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&new->fi_list);
3738c2ecf20Sopenharmony_ci	fi = fwnet_frag_new(new, frag_off, frag_len);
3748c2ecf20Sopenharmony_ci	if (fi == NULL)
3758c2ecf20Sopenharmony_ci		goto fail_w_new;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	new->datagram_label = datagram_label;
3788c2ecf20Sopenharmony_ci	new->datagram_size = dg_size;
3798c2ecf20Sopenharmony_ci	new->skb = dev_alloc_skb(dg_size + LL_RESERVED_SPACE(net));
3808c2ecf20Sopenharmony_ci	if (new->skb == NULL)
3818c2ecf20Sopenharmony_ci		goto fail_w_fi;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	skb_reserve(new->skb, LL_RESERVED_SPACE(net));
3848c2ecf20Sopenharmony_ci	new->pbuf = skb_put(new->skb, dg_size);
3858c2ecf20Sopenharmony_ci	memcpy(new->pbuf + frag_off, frag_buf, frag_len);
3868c2ecf20Sopenharmony_ci	list_add_tail(&new->pd_link, &peer->pd_list);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return new;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cifail_w_fi:
3918c2ecf20Sopenharmony_ci	kfree(fi);
3928c2ecf20Sopenharmony_cifail_w_new:
3938c2ecf20Sopenharmony_ci	kfree(new);
3948c2ecf20Sopenharmony_cifail:
3958c2ecf20Sopenharmony_ci	return NULL;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic struct fwnet_partial_datagram *fwnet_pd_find(struct fwnet_peer *peer,
3998c2ecf20Sopenharmony_ci						    u16 datagram_label)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct fwnet_partial_datagram *pd;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	list_for_each_entry(pd, &peer->pd_list, pd_link)
4048c2ecf20Sopenharmony_ci		if (pd->datagram_label == datagram_label)
4058c2ecf20Sopenharmony_ci			return pd;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return NULL;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic void fwnet_pd_delete(struct fwnet_partial_datagram *old)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct fwnet_fragment_info *fi, *n;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	list_for_each_entry_safe(fi, n, &old->fi_list, fi_link)
4168c2ecf20Sopenharmony_ci		kfree(fi);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	list_del(&old->pd_link);
4198c2ecf20Sopenharmony_ci	dev_kfree_skb_any(old->skb);
4208c2ecf20Sopenharmony_ci	kfree(old);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic bool fwnet_pd_update(struct fwnet_peer *peer,
4248c2ecf20Sopenharmony_ci		struct fwnet_partial_datagram *pd, void *frag_buf,
4258c2ecf20Sopenharmony_ci		unsigned frag_off, unsigned frag_len)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	if (fwnet_frag_new(pd, frag_off, frag_len) == NULL)
4288c2ecf20Sopenharmony_ci		return false;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	memcpy(pd->pbuf + frag_off, frag_buf, frag_len);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/*
4338c2ecf20Sopenharmony_ci	 * Move list entry to beginning of list so that oldest partial
4348c2ecf20Sopenharmony_ci	 * datagrams percolate to the end of the list
4358c2ecf20Sopenharmony_ci	 */
4368c2ecf20Sopenharmony_ci	list_move_tail(&pd->pd_link, &peer->pd_list);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return true;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	struct fwnet_fragment_info *fi;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	fi = list_entry(pd->fi_list.next, struct fwnet_fragment_info, fi_link);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	return fi->len == pd->datagram_size;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/* caller must hold dev->lock */
4518c2ecf20Sopenharmony_cistatic struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev,
4528c2ecf20Sopenharmony_ci						  u64 guid)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct fwnet_peer *peer;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	list_for_each_entry(peer, &dev->peer_list, peer_link)
4578c2ecf20Sopenharmony_ci		if (peer->guid == guid)
4588c2ecf20Sopenharmony_ci			return peer;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return NULL;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci/* caller must hold dev->lock */
4648c2ecf20Sopenharmony_cistatic struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev,
4658c2ecf20Sopenharmony_ci						int node_id, int generation)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct fwnet_peer *peer;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	list_for_each_entry(peer, &dev->peer_list, peer_link)
4708c2ecf20Sopenharmony_ci		if (peer->node_id    == node_id &&
4718c2ecf20Sopenharmony_ci		    peer->generation == generation)
4728c2ecf20Sopenharmony_ci			return peer;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	return NULL;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */
4788c2ecf20Sopenharmony_cistatic unsigned fwnet_max_payload(unsigned max_rec, unsigned speed)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	max_rec = min(max_rec, speed + 8);
4818c2ecf20Sopenharmony_ci	max_rec = clamp(max_rec, 8U, 11U); /* 512...4096 */
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic int fwnet_finish_incoming_packet(struct net_device *net,
4888c2ecf20Sopenharmony_ci					struct sk_buff *skb, u16 source_node_id,
4898c2ecf20Sopenharmony_ci					bool is_broadcast, u16 ether_type)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct fwnet_device *dev;
4928c2ecf20Sopenharmony_ci	int status;
4938c2ecf20Sopenharmony_ci	__be64 guid;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	switch (ether_type) {
4968c2ecf20Sopenharmony_ci	case ETH_P_ARP:
4978c2ecf20Sopenharmony_ci	case ETH_P_IP:
4988c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
4998c2ecf20Sopenharmony_ci	case ETH_P_IPV6:
5008c2ecf20Sopenharmony_ci#endif
5018c2ecf20Sopenharmony_ci		break;
5028c2ecf20Sopenharmony_ci	default:
5038c2ecf20Sopenharmony_ci		goto err;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	dev = netdev_priv(net);
5078c2ecf20Sopenharmony_ci	/* Write metadata, and then pass to the receive level */
5088c2ecf20Sopenharmony_ci	skb->dev = net;
5098c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/*
5128c2ecf20Sopenharmony_ci	 * Parse the encapsulation header. This actually does the job of
5138c2ecf20Sopenharmony_ci	 * converting to an ethernet-like pseudo frame header.
5148c2ecf20Sopenharmony_ci	 */
5158c2ecf20Sopenharmony_ci	guid = cpu_to_be64(dev->card->guid);
5168c2ecf20Sopenharmony_ci	if (dev_hard_header(skb, net, ether_type,
5178c2ecf20Sopenharmony_ci			   is_broadcast ? net->broadcast : net->dev_addr,
5188c2ecf20Sopenharmony_ci			   NULL, skb->len) >= 0) {
5198c2ecf20Sopenharmony_ci		struct fwnet_header *eth;
5208c2ecf20Sopenharmony_ci		u16 *rawp;
5218c2ecf20Sopenharmony_ci		__be16 protocol;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		skb_reset_mac_header(skb);
5248c2ecf20Sopenharmony_ci		skb_pull(skb, sizeof(*eth));
5258c2ecf20Sopenharmony_ci		eth = (struct fwnet_header *)skb_mac_header(skb);
5268c2ecf20Sopenharmony_ci		if (fwnet_hwaddr_is_multicast(eth->h_dest)) {
5278c2ecf20Sopenharmony_ci			if (memcmp(eth->h_dest, net->broadcast,
5288c2ecf20Sopenharmony_ci				   net->addr_len) == 0)
5298c2ecf20Sopenharmony_ci				skb->pkt_type = PACKET_BROADCAST;
5308c2ecf20Sopenharmony_ci#if 0
5318c2ecf20Sopenharmony_ci			else
5328c2ecf20Sopenharmony_ci				skb->pkt_type = PACKET_MULTICAST;
5338c2ecf20Sopenharmony_ci#endif
5348c2ecf20Sopenharmony_ci		} else {
5358c2ecf20Sopenharmony_ci			if (memcmp(eth->h_dest, net->dev_addr, net->addr_len))
5368c2ecf20Sopenharmony_ci				skb->pkt_type = PACKET_OTHERHOST;
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci		if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
5398c2ecf20Sopenharmony_ci			protocol = eth->h_proto;
5408c2ecf20Sopenharmony_ci		} else {
5418c2ecf20Sopenharmony_ci			rawp = (u16 *)skb->data;
5428c2ecf20Sopenharmony_ci			if (*rawp == 0xffff)
5438c2ecf20Sopenharmony_ci				protocol = htons(ETH_P_802_3);
5448c2ecf20Sopenharmony_ci			else
5458c2ecf20Sopenharmony_ci				protocol = htons(ETH_P_802_2);
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci		skb->protocol = protocol;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci	status = netif_rx(skb);
5508c2ecf20Sopenharmony_ci	if (status == NET_RX_DROP) {
5518c2ecf20Sopenharmony_ci		net->stats.rx_errors++;
5528c2ecf20Sopenharmony_ci		net->stats.rx_dropped++;
5538c2ecf20Sopenharmony_ci	} else {
5548c2ecf20Sopenharmony_ci		net->stats.rx_packets++;
5558c2ecf20Sopenharmony_ci		net->stats.rx_bytes += skb->len;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	return 0;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci err:
5618c2ecf20Sopenharmony_ci	net->stats.rx_errors++;
5628c2ecf20Sopenharmony_ci	net->stats.rx_dropped++;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	return -ENOENT;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
5708c2ecf20Sopenharmony_ci				 int source_node_id, int generation,
5718c2ecf20Sopenharmony_ci				 bool is_broadcast)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5748c2ecf20Sopenharmony_ci	struct net_device *net = dev->netdev;
5758c2ecf20Sopenharmony_ci	struct rfc2734_header hdr;
5768c2ecf20Sopenharmony_ci	unsigned lf;
5778c2ecf20Sopenharmony_ci	unsigned long flags;
5788c2ecf20Sopenharmony_ci	struct fwnet_peer *peer;
5798c2ecf20Sopenharmony_ci	struct fwnet_partial_datagram *pd;
5808c2ecf20Sopenharmony_ci	int fg_off;
5818c2ecf20Sopenharmony_ci	int dg_size;
5828c2ecf20Sopenharmony_ci	u16 datagram_label;
5838c2ecf20Sopenharmony_ci	int retval;
5848c2ecf20Sopenharmony_ci	u16 ether_type;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (len <= RFC2374_UNFRAG_HDR_SIZE)
5878c2ecf20Sopenharmony_ci		return 0;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	hdr.w0 = be32_to_cpu(buf[0]);
5908c2ecf20Sopenharmony_ci	lf = fwnet_get_hdr_lf(&hdr);
5918c2ecf20Sopenharmony_ci	if (lf == RFC2374_HDR_UNFRAG) {
5928c2ecf20Sopenharmony_ci		/*
5938c2ecf20Sopenharmony_ci		 * An unfragmented datagram has been received by the ieee1394
5948c2ecf20Sopenharmony_ci		 * bus. Build an skbuff around it so we can pass it to the
5958c2ecf20Sopenharmony_ci		 * high level network layer.
5968c2ecf20Sopenharmony_ci		 */
5978c2ecf20Sopenharmony_ci		ether_type = fwnet_get_hdr_ether_type(&hdr);
5988c2ecf20Sopenharmony_ci		buf++;
5998c2ecf20Sopenharmony_ci		len -= RFC2374_UNFRAG_HDR_SIZE;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		skb = dev_alloc_skb(len + LL_RESERVED_SPACE(net));
6028c2ecf20Sopenharmony_ci		if (unlikely(!skb)) {
6038c2ecf20Sopenharmony_ci			net->stats.rx_dropped++;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci			return -ENOMEM;
6068c2ecf20Sopenharmony_ci		}
6078c2ecf20Sopenharmony_ci		skb_reserve(skb, LL_RESERVED_SPACE(net));
6088c2ecf20Sopenharmony_ci		skb_put_data(skb, buf, len);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci		return fwnet_finish_incoming_packet(net, skb, source_node_id,
6118c2ecf20Sopenharmony_ci						    is_broadcast, ether_type);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* A datagram fragment has been received, now the fun begins. */
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (len <= RFC2374_FRAG_HDR_SIZE)
6178c2ecf20Sopenharmony_ci		return 0;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	hdr.w1 = ntohl(buf[1]);
6208c2ecf20Sopenharmony_ci	buf += 2;
6218c2ecf20Sopenharmony_ci	len -= RFC2374_FRAG_HDR_SIZE;
6228c2ecf20Sopenharmony_ci	if (lf == RFC2374_HDR_FIRSTFRAG) {
6238c2ecf20Sopenharmony_ci		ether_type = fwnet_get_hdr_ether_type(&hdr);
6248c2ecf20Sopenharmony_ci		fg_off = 0;
6258c2ecf20Sopenharmony_ci	} else {
6268c2ecf20Sopenharmony_ci		ether_type = 0;
6278c2ecf20Sopenharmony_ci		fg_off = fwnet_get_hdr_fg_off(&hdr);
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci	datagram_label = fwnet_get_hdr_dgl(&hdr);
6308c2ecf20Sopenharmony_ci	dg_size = fwnet_get_hdr_dg_size(&hdr);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (fg_off + len > dg_size)
6338c2ecf20Sopenharmony_ci		return 0;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation);
6388c2ecf20Sopenharmony_ci	if (!peer) {
6398c2ecf20Sopenharmony_ci		retval = -ENOENT;
6408c2ecf20Sopenharmony_ci		goto fail;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	pd = fwnet_pd_find(peer, datagram_label);
6448c2ecf20Sopenharmony_ci	if (pd == NULL) {
6458c2ecf20Sopenharmony_ci		while (peer->pdg_size >= FWNET_MAX_FRAGMENTS) {
6468c2ecf20Sopenharmony_ci			/* remove the oldest */
6478c2ecf20Sopenharmony_ci			fwnet_pd_delete(list_first_entry(&peer->pd_list,
6488c2ecf20Sopenharmony_ci				struct fwnet_partial_datagram, pd_link));
6498c2ecf20Sopenharmony_ci			peer->pdg_size--;
6508c2ecf20Sopenharmony_ci		}
6518c2ecf20Sopenharmony_ci		pd = fwnet_pd_new(net, peer, datagram_label,
6528c2ecf20Sopenharmony_ci				  dg_size, buf, fg_off, len);
6538c2ecf20Sopenharmony_ci		if (pd == NULL) {
6548c2ecf20Sopenharmony_ci			retval = -ENOMEM;
6558c2ecf20Sopenharmony_ci			goto fail;
6568c2ecf20Sopenharmony_ci		}
6578c2ecf20Sopenharmony_ci		peer->pdg_size++;
6588c2ecf20Sopenharmony_ci	} else {
6598c2ecf20Sopenharmony_ci		if (fwnet_frag_overlap(pd, fg_off, len) ||
6608c2ecf20Sopenharmony_ci		    pd->datagram_size != dg_size) {
6618c2ecf20Sopenharmony_ci			/*
6628c2ecf20Sopenharmony_ci			 * Differing datagram sizes or overlapping fragments,
6638c2ecf20Sopenharmony_ci			 * discard old datagram and start a new one.
6648c2ecf20Sopenharmony_ci			 */
6658c2ecf20Sopenharmony_ci			fwnet_pd_delete(pd);
6668c2ecf20Sopenharmony_ci			pd = fwnet_pd_new(net, peer, datagram_label,
6678c2ecf20Sopenharmony_ci					  dg_size, buf, fg_off, len);
6688c2ecf20Sopenharmony_ci			if (pd == NULL) {
6698c2ecf20Sopenharmony_ci				peer->pdg_size--;
6708c2ecf20Sopenharmony_ci				retval = -ENOMEM;
6718c2ecf20Sopenharmony_ci				goto fail;
6728c2ecf20Sopenharmony_ci			}
6738c2ecf20Sopenharmony_ci		} else {
6748c2ecf20Sopenharmony_ci			if (!fwnet_pd_update(peer, pd, buf, fg_off, len)) {
6758c2ecf20Sopenharmony_ci				/*
6768c2ecf20Sopenharmony_ci				 * Couldn't save off fragment anyway
6778c2ecf20Sopenharmony_ci				 * so might as well obliterate the
6788c2ecf20Sopenharmony_ci				 * datagram now.
6798c2ecf20Sopenharmony_ci				 */
6808c2ecf20Sopenharmony_ci				fwnet_pd_delete(pd);
6818c2ecf20Sopenharmony_ci				peer->pdg_size--;
6828c2ecf20Sopenharmony_ci				retval = -ENOMEM;
6838c2ecf20Sopenharmony_ci				goto fail;
6848c2ecf20Sopenharmony_ci			}
6858c2ecf20Sopenharmony_ci		}
6868c2ecf20Sopenharmony_ci	} /* new datagram or add to existing one */
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if (lf == RFC2374_HDR_FIRSTFRAG)
6898c2ecf20Sopenharmony_ci		pd->ether_type = ether_type;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (fwnet_pd_is_complete(pd)) {
6928c2ecf20Sopenharmony_ci		ether_type = pd->ether_type;
6938c2ecf20Sopenharmony_ci		peer->pdg_size--;
6948c2ecf20Sopenharmony_ci		skb = skb_get(pd->skb);
6958c2ecf20Sopenharmony_ci		fwnet_pd_delete(pd);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci		return fwnet_finish_incoming_packet(net, skb, source_node_id,
7008c2ecf20Sopenharmony_ci						    false, ether_type);
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci	/*
7038c2ecf20Sopenharmony_ci	 * Datagram is not complete, we're done for the
7048c2ecf20Sopenharmony_ci	 * moment.
7058c2ecf20Sopenharmony_ci	 */
7068c2ecf20Sopenharmony_ci	retval = 0;
7078c2ecf20Sopenharmony_ci fail:
7088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return retval;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
7148c2ecf20Sopenharmony_ci		int tcode, int destination, int source, int generation,
7158c2ecf20Sopenharmony_ci		unsigned long long offset, void *payload, size_t length,
7168c2ecf20Sopenharmony_ci		void *callback_data)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	struct fwnet_device *dev = callback_data;
7198c2ecf20Sopenharmony_ci	int rcode;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	if (destination == IEEE1394_ALL_NODES) {
7228c2ecf20Sopenharmony_ci		kfree(r);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		return;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if (offset != dev->handler.offset)
7288c2ecf20Sopenharmony_ci		rcode = RCODE_ADDRESS_ERROR;
7298c2ecf20Sopenharmony_ci	else if (tcode != TCODE_WRITE_BLOCK_REQUEST)
7308c2ecf20Sopenharmony_ci		rcode = RCODE_TYPE_ERROR;
7318c2ecf20Sopenharmony_ci	else if (fwnet_incoming_packet(dev, payload, length,
7328c2ecf20Sopenharmony_ci				       source, generation, false) != 0) {
7338c2ecf20Sopenharmony_ci		dev_err(&dev->netdev->dev, "incoming packet failure\n");
7348c2ecf20Sopenharmony_ci		rcode = RCODE_CONFLICT_ERROR;
7358c2ecf20Sopenharmony_ci	} else
7368c2ecf20Sopenharmony_ci		rcode = RCODE_COMPLETE;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	fw_send_response(card, r, rcode);
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_cistatic int gasp_source_id(__be32 *p)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	return be32_to_cpu(p[0]) >> 16;
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic u32 gasp_specifier_id(__be32 *p)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	return (be32_to_cpu(p[0]) & 0xffff) << 8 |
7498c2ecf20Sopenharmony_ci	       (be32_to_cpu(p[1]) & 0xff000000) >> 24;
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic u32 gasp_version(__be32 *p)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	return be32_to_cpu(p[1]) & 0xffffff;
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic void fwnet_receive_broadcast(struct fw_iso_context *context,
7588c2ecf20Sopenharmony_ci		u32 cycle, size_t header_length, void *header, void *data)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	struct fwnet_device *dev;
7618c2ecf20Sopenharmony_ci	struct fw_iso_packet packet;
7628c2ecf20Sopenharmony_ci	__be16 *hdr_ptr;
7638c2ecf20Sopenharmony_ci	__be32 *buf_ptr;
7648c2ecf20Sopenharmony_ci	int retval;
7658c2ecf20Sopenharmony_ci	u32 length;
7668c2ecf20Sopenharmony_ci	unsigned long offset;
7678c2ecf20Sopenharmony_ci	unsigned long flags;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	dev = data;
7708c2ecf20Sopenharmony_ci	hdr_ptr = header;
7718c2ecf20Sopenharmony_ci	length = be16_to_cpup(hdr_ptr);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	offset = dev->rcv_buffer_size * dev->broadcast_rcv_next_ptr;
7768c2ecf20Sopenharmony_ci	buf_ptr = dev->broadcast_rcv_buffer_ptrs[dev->broadcast_rcv_next_ptr++];
7778c2ecf20Sopenharmony_ci	if (dev->broadcast_rcv_next_ptr == dev->num_broadcast_rcv_ptrs)
7788c2ecf20Sopenharmony_ci		dev->broadcast_rcv_next_ptr = 0;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (length > IEEE1394_GASP_HDR_SIZE &&
7838c2ecf20Sopenharmony_ci	    gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
7848c2ecf20Sopenharmony_ci	    (gasp_version(buf_ptr) == RFC2734_SW_VERSION
7858c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
7868c2ecf20Sopenharmony_ci	     || gasp_version(buf_ptr) == RFC3146_SW_VERSION
7878c2ecf20Sopenharmony_ci#endif
7888c2ecf20Sopenharmony_ci	    ))
7898c2ecf20Sopenharmony_ci		fwnet_incoming_packet(dev, buf_ptr + 2,
7908c2ecf20Sopenharmony_ci				      length - IEEE1394_GASP_HDR_SIZE,
7918c2ecf20Sopenharmony_ci				      gasp_source_id(buf_ptr),
7928c2ecf20Sopenharmony_ci				      context->card->generation, true);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	packet.payload_length = dev->rcv_buffer_size;
7958c2ecf20Sopenharmony_ci	packet.interrupt = 1;
7968c2ecf20Sopenharmony_ci	packet.skip = 0;
7978c2ecf20Sopenharmony_ci	packet.tag = 3;
7988c2ecf20Sopenharmony_ci	packet.sy = 0;
7998c2ecf20Sopenharmony_ci	packet.header_length = IEEE1394_GASP_HDR_SIZE;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	retval = fw_iso_context_queue(dev->broadcast_rcv_context, &packet,
8048c2ecf20Sopenharmony_ci				      &dev->broadcast_rcv_buffer, offset);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (retval >= 0)
8098c2ecf20Sopenharmony_ci		fw_iso_context_queue_flush(dev->broadcast_rcv_context);
8108c2ecf20Sopenharmony_ci	else
8118c2ecf20Sopenharmony_ci		dev_err(&dev->netdev->dev, "requeue failed\n");
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cistatic struct kmem_cache *fwnet_packet_task_cache;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_cistatic void fwnet_free_ptask(struct fwnet_packet_task *ptask)
8178c2ecf20Sopenharmony_ci{
8188c2ecf20Sopenharmony_ci	dev_kfree_skb_any(ptask->skb);
8198c2ecf20Sopenharmony_ci	kmem_cache_free(fwnet_packet_task_cache, ptask);
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci/* Caller must hold dev->lock. */
8238c2ecf20Sopenharmony_cistatic void dec_queued_datagrams(struct fwnet_device *dev)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	if (--dev->queued_datagrams == FWNET_MIN_QUEUED_DATAGRAMS)
8268c2ecf20Sopenharmony_ci		netif_wake_queue(dev->netdev);
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic int fwnet_send_packet(struct fwnet_packet_task *ptask);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	struct fwnet_device *dev = ptask->dev;
8348c2ecf20Sopenharmony_ci	struct sk_buff *skb = ptask->skb;
8358c2ecf20Sopenharmony_ci	unsigned long flags;
8368c2ecf20Sopenharmony_ci	bool free;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	ptask->outstanding_pkts--;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	/* Check whether we or the networking TX soft-IRQ is last user. */
8438c2ecf20Sopenharmony_ci	free = (ptask->outstanding_pkts == 0 && ptask->enqueued);
8448c2ecf20Sopenharmony_ci	if (free)
8458c2ecf20Sopenharmony_ci		dec_queued_datagrams(dev);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (ptask->outstanding_pkts == 0) {
8488c2ecf20Sopenharmony_ci		dev->netdev->stats.tx_packets++;
8498c2ecf20Sopenharmony_ci		dev->netdev->stats.tx_bytes += skb->len;
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (ptask->outstanding_pkts > 0) {
8558c2ecf20Sopenharmony_ci		u16 dg_size;
8568c2ecf20Sopenharmony_ci		u16 fg_off;
8578c2ecf20Sopenharmony_ci		u16 datagram_label;
8588c2ecf20Sopenharmony_ci		u16 lf;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		/* Update the ptask to point to the next fragment and send it */
8618c2ecf20Sopenharmony_ci		lf = fwnet_get_hdr_lf(&ptask->hdr);
8628c2ecf20Sopenharmony_ci		switch (lf) {
8638c2ecf20Sopenharmony_ci		case RFC2374_HDR_LASTFRAG:
8648c2ecf20Sopenharmony_ci		case RFC2374_HDR_UNFRAG:
8658c2ecf20Sopenharmony_ci		default:
8668c2ecf20Sopenharmony_ci			dev_err(&dev->netdev->dev,
8678c2ecf20Sopenharmony_ci				"outstanding packet %x lf %x, header %x,%x\n",
8688c2ecf20Sopenharmony_ci				ptask->outstanding_pkts, lf, ptask->hdr.w0,
8698c2ecf20Sopenharmony_ci				ptask->hdr.w1);
8708c2ecf20Sopenharmony_ci			BUG();
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci		case RFC2374_HDR_FIRSTFRAG:
8738c2ecf20Sopenharmony_ci			/* Set frag type here for future interior fragments */
8748c2ecf20Sopenharmony_ci			dg_size = fwnet_get_hdr_dg_size(&ptask->hdr);
8758c2ecf20Sopenharmony_ci			fg_off = ptask->max_payload - RFC2374_FRAG_HDR_SIZE;
8768c2ecf20Sopenharmony_ci			datagram_label = fwnet_get_hdr_dgl(&ptask->hdr);
8778c2ecf20Sopenharmony_ci			break;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci		case RFC2374_HDR_INTFRAG:
8808c2ecf20Sopenharmony_ci			dg_size = fwnet_get_hdr_dg_size(&ptask->hdr);
8818c2ecf20Sopenharmony_ci			fg_off = fwnet_get_hdr_fg_off(&ptask->hdr)
8828c2ecf20Sopenharmony_ci				  + ptask->max_payload - RFC2374_FRAG_HDR_SIZE;
8838c2ecf20Sopenharmony_ci			datagram_label = fwnet_get_hdr_dgl(&ptask->hdr);
8848c2ecf20Sopenharmony_ci			break;
8858c2ecf20Sopenharmony_ci		}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci		if (ptask->dest_node == IEEE1394_ALL_NODES) {
8888c2ecf20Sopenharmony_ci			skb_pull(skb,
8898c2ecf20Sopenharmony_ci				 ptask->max_payload + IEEE1394_GASP_HDR_SIZE);
8908c2ecf20Sopenharmony_ci		} else {
8918c2ecf20Sopenharmony_ci			skb_pull(skb, ptask->max_payload);
8928c2ecf20Sopenharmony_ci		}
8938c2ecf20Sopenharmony_ci		if (ptask->outstanding_pkts > 1) {
8948c2ecf20Sopenharmony_ci			fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_INTFRAG,
8958c2ecf20Sopenharmony_ci					  dg_size, fg_off, datagram_label);
8968c2ecf20Sopenharmony_ci		} else {
8978c2ecf20Sopenharmony_ci			fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_LASTFRAG,
8988c2ecf20Sopenharmony_ci					  dg_size, fg_off, datagram_label);
8998c2ecf20Sopenharmony_ci			ptask->max_payload = skb->len + RFC2374_FRAG_HDR_SIZE;
9008c2ecf20Sopenharmony_ci		}
9018c2ecf20Sopenharmony_ci		fwnet_send_packet(ptask);
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	if (free)
9058c2ecf20Sopenharmony_ci		fwnet_free_ptask(ptask);
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic void fwnet_transmit_packet_failed(struct fwnet_packet_task *ptask)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct fwnet_device *dev = ptask->dev;
9118c2ecf20Sopenharmony_ci	unsigned long flags;
9128c2ecf20Sopenharmony_ci	bool free;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	/* One fragment failed; don't try to send remaining fragments. */
9178c2ecf20Sopenharmony_ci	ptask->outstanding_pkts = 0;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* Check whether we or the networking TX soft-IRQ is last user. */
9208c2ecf20Sopenharmony_ci	free = ptask->enqueued;
9218c2ecf20Sopenharmony_ci	if (free)
9228c2ecf20Sopenharmony_ci		dec_queued_datagrams(dev);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	dev->netdev->stats.tx_dropped++;
9258c2ecf20Sopenharmony_ci	dev->netdev->stats.tx_errors++;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	if (free)
9308c2ecf20Sopenharmony_ci		fwnet_free_ptask(ptask);
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_cistatic void fwnet_write_complete(struct fw_card *card, int rcode,
9348c2ecf20Sopenharmony_ci				 void *payload, size_t length, void *data)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	struct fwnet_packet_task *ptask = data;
9378c2ecf20Sopenharmony_ci	static unsigned long j;
9388c2ecf20Sopenharmony_ci	static int last_rcode, errors_skipped;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	if (rcode == RCODE_COMPLETE) {
9418c2ecf20Sopenharmony_ci		fwnet_transmit_packet_done(ptask);
9428c2ecf20Sopenharmony_ci	} else {
9438c2ecf20Sopenharmony_ci		if (printk_timed_ratelimit(&j,  1000) || rcode != last_rcode) {
9448c2ecf20Sopenharmony_ci			dev_err(&ptask->dev->netdev->dev,
9458c2ecf20Sopenharmony_ci				"fwnet_write_complete failed: %x (skipped %d)\n",
9468c2ecf20Sopenharmony_ci				rcode, errors_skipped);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci			errors_skipped = 0;
9498c2ecf20Sopenharmony_ci			last_rcode = rcode;
9508c2ecf20Sopenharmony_ci		} else {
9518c2ecf20Sopenharmony_ci			errors_skipped++;
9528c2ecf20Sopenharmony_ci		}
9538c2ecf20Sopenharmony_ci		fwnet_transmit_packet_failed(ptask);
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_cistatic int fwnet_send_packet(struct fwnet_packet_task *ptask)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	struct fwnet_device *dev;
9608c2ecf20Sopenharmony_ci	unsigned tx_len;
9618c2ecf20Sopenharmony_ci	struct rfc2734_header *bufhdr;
9628c2ecf20Sopenharmony_ci	unsigned long flags;
9638c2ecf20Sopenharmony_ci	bool free;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	dev = ptask->dev;
9668c2ecf20Sopenharmony_ci	tx_len = ptask->max_payload;
9678c2ecf20Sopenharmony_ci	switch (fwnet_get_hdr_lf(&ptask->hdr)) {
9688c2ecf20Sopenharmony_ci	case RFC2374_HDR_UNFRAG:
9698c2ecf20Sopenharmony_ci		bufhdr = skb_push(ptask->skb, RFC2374_UNFRAG_HDR_SIZE);
9708c2ecf20Sopenharmony_ci		put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0);
9718c2ecf20Sopenharmony_ci		break;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	case RFC2374_HDR_FIRSTFRAG:
9748c2ecf20Sopenharmony_ci	case RFC2374_HDR_INTFRAG:
9758c2ecf20Sopenharmony_ci	case RFC2374_HDR_LASTFRAG:
9768c2ecf20Sopenharmony_ci		bufhdr = skb_push(ptask->skb, RFC2374_FRAG_HDR_SIZE);
9778c2ecf20Sopenharmony_ci		put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0);
9788c2ecf20Sopenharmony_ci		put_unaligned_be32(ptask->hdr.w1, &bufhdr->w1);
9798c2ecf20Sopenharmony_ci		break;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	default:
9828c2ecf20Sopenharmony_ci		BUG();
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci	if (ptask->dest_node == IEEE1394_ALL_NODES) {
9858c2ecf20Sopenharmony_ci		u8 *p;
9868c2ecf20Sopenharmony_ci		int generation;
9878c2ecf20Sopenharmony_ci		int node_id;
9888c2ecf20Sopenharmony_ci		unsigned int sw_version;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci		/* ptask->generation may not have been set yet */
9918c2ecf20Sopenharmony_ci		generation = dev->card->generation;
9928c2ecf20Sopenharmony_ci		smp_rmb();
9938c2ecf20Sopenharmony_ci		node_id = dev->card->node_id;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci		switch (ptask->skb->protocol) {
9968c2ecf20Sopenharmony_ci		default:
9978c2ecf20Sopenharmony_ci			sw_version = RFC2734_SW_VERSION;
9988c2ecf20Sopenharmony_ci			break;
9998c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
10008c2ecf20Sopenharmony_ci		case htons(ETH_P_IPV6):
10018c2ecf20Sopenharmony_ci			sw_version = RFC3146_SW_VERSION;
10028c2ecf20Sopenharmony_ci#endif
10038c2ecf20Sopenharmony_ci		}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci		p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
10068c2ecf20Sopenharmony_ci		put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
10078c2ecf20Sopenharmony_ci		put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
10088c2ecf20Sopenharmony_ci						| sw_version, &p[4]);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci		/* We should not transmit if broadcast_channel.valid == 0. */
10118c2ecf20Sopenharmony_ci		fw_send_request(dev->card, &ptask->transaction,
10128c2ecf20Sopenharmony_ci				TCODE_STREAM_DATA,
10138c2ecf20Sopenharmony_ci				fw_stream_packet_destination_id(3,
10148c2ecf20Sopenharmony_ci						IEEE1394_BROADCAST_CHANNEL, 0),
10158c2ecf20Sopenharmony_ci				generation, SCODE_100, 0ULL, ptask->skb->data,
10168c2ecf20Sopenharmony_ci				tx_len + 8, fwnet_write_complete, ptask);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci		spin_lock_irqsave(&dev->lock, flags);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		/* If the AT tasklet already ran, we may be last user. */
10218c2ecf20Sopenharmony_ci		free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
10228c2ecf20Sopenharmony_ci		if (!free)
10238c2ecf20Sopenharmony_ci			ptask->enqueued = true;
10248c2ecf20Sopenharmony_ci		else
10258c2ecf20Sopenharmony_ci			dec_queued_datagrams(dev);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci		goto out;
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	fw_send_request(dev->card, &ptask->transaction,
10338c2ecf20Sopenharmony_ci			TCODE_WRITE_BLOCK_REQUEST, ptask->dest_node,
10348c2ecf20Sopenharmony_ci			ptask->generation, ptask->speed, ptask->fifo_addr,
10358c2ecf20Sopenharmony_ci			ptask->skb->data, tx_len, fwnet_write_complete, ptask);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	/* If the AT tasklet already ran, we may be last user. */
10408c2ecf20Sopenharmony_ci	free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
10418c2ecf20Sopenharmony_ci	if (!free)
10428c2ecf20Sopenharmony_ci		ptask->enqueued = true;
10438c2ecf20Sopenharmony_ci	else
10448c2ecf20Sopenharmony_ci		dec_queued_datagrams(dev);
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	netif_trans_update(dev->netdev);
10498c2ecf20Sopenharmony_ci out:
10508c2ecf20Sopenharmony_ci	if (free)
10518c2ecf20Sopenharmony_ci		fwnet_free_ptask(ptask);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	return 0;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cistatic void fwnet_fifo_stop(struct fwnet_device *dev)
10578c2ecf20Sopenharmony_ci{
10588c2ecf20Sopenharmony_ci	if (dev->local_fifo == FWNET_NO_FIFO_ADDR)
10598c2ecf20Sopenharmony_ci		return;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	fw_core_remove_address_handler(&dev->handler);
10628c2ecf20Sopenharmony_ci	dev->local_fifo = FWNET_NO_FIFO_ADDR;
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic int fwnet_fifo_start(struct fwnet_device *dev)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	int retval;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	if (dev->local_fifo != FWNET_NO_FIFO_ADDR)
10708c2ecf20Sopenharmony_ci		return 0;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	dev->handler.length = 4096;
10738c2ecf20Sopenharmony_ci	dev->handler.address_callback = fwnet_receive_packet;
10748c2ecf20Sopenharmony_ci	dev->handler.callback_data = dev;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	retval = fw_core_add_address_handler(&dev->handler,
10778c2ecf20Sopenharmony_ci					     &fw_high_memory_region);
10788c2ecf20Sopenharmony_ci	if (retval < 0)
10798c2ecf20Sopenharmony_ci		return retval;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	dev->local_fifo = dev->handler.offset;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	return 0;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic void __fwnet_broadcast_stop(struct fwnet_device *dev)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	unsigned u;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	if (dev->broadcast_state != FWNET_BROADCAST_ERROR) {
10918c2ecf20Sopenharmony_ci		for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++)
10928c2ecf20Sopenharmony_ci			kunmap(dev->broadcast_rcv_buffer.pages[u]);
10938c2ecf20Sopenharmony_ci		fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card);
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci	if (dev->broadcast_rcv_context) {
10968c2ecf20Sopenharmony_ci		fw_iso_context_destroy(dev->broadcast_rcv_context);
10978c2ecf20Sopenharmony_ci		dev->broadcast_rcv_context = NULL;
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci	kfree(dev->broadcast_rcv_buffer_ptrs);
11008c2ecf20Sopenharmony_ci	dev->broadcast_rcv_buffer_ptrs = NULL;
11018c2ecf20Sopenharmony_ci	dev->broadcast_state = FWNET_BROADCAST_ERROR;
11028c2ecf20Sopenharmony_ci}
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_cistatic void fwnet_broadcast_stop(struct fwnet_device *dev)
11058c2ecf20Sopenharmony_ci{
11068c2ecf20Sopenharmony_ci	if (dev->broadcast_state == FWNET_BROADCAST_ERROR)
11078c2ecf20Sopenharmony_ci		return;
11088c2ecf20Sopenharmony_ci	fw_iso_context_stop(dev->broadcast_rcv_context);
11098c2ecf20Sopenharmony_ci	__fwnet_broadcast_stop(dev);
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic int fwnet_broadcast_start(struct fwnet_device *dev)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	struct fw_iso_context *context;
11158c2ecf20Sopenharmony_ci	int retval;
11168c2ecf20Sopenharmony_ci	unsigned num_packets;
11178c2ecf20Sopenharmony_ci	unsigned max_receive;
11188c2ecf20Sopenharmony_ci	struct fw_iso_packet packet;
11198c2ecf20Sopenharmony_ci	unsigned long offset;
11208c2ecf20Sopenharmony_ci	void **ptrptr;
11218c2ecf20Sopenharmony_ci	unsigned u;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	if (dev->broadcast_state != FWNET_BROADCAST_ERROR)
11248c2ecf20Sopenharmony_ci		return 0;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	max_receive = 1U << (dev->card->max_receive + 1);
11278c2ecf20Sopenharmony_ci	num_packets = (FWNET_ISO_PAGE_COUNT * PAGE_SIZE) / max_receive;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	ptrptr = kmalloc_array(num_packets, sizeof(void *), GFP_KERNEL);
11308c2ecf20Sopenharmony_ci	if (!ptrptr) {
11318c2ecf20Sopenharmony_ci		retval = -ENOMEM;
11328c2ecf20Sopenharmony_ci		goto failed;
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci	dev->broadcast_rcv_buffer_ptrs = ptrptr;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	context = fw_iso_context_create(dev->card, FW_ISO_CONTEXT_RECEIVE,
11378c2ecf20Sopenharmony_ci					IEEE1394_BROADCAST_CHANNEL,
11388c2ecf20Sopenharmony_ci					dev->card->link_speed, 8,
11398c2ecf20Sopenharmony_ci					fwnet_receive_broadcast, dev);
11408c2ecf20Sopenharmony_ci	if (IS_ERR(context)) {
11418c2ecf20Sopenharmony_ci		retval = PTR_ERR(context);
11428c2ecf20Sopenharmony_ci		goto failed;
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer, dev->card,
11468c2ecf20Sopenharmony_ci				    FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE);
11478c2ecf20Sopenharmony_ci	if (retval < 0)
11488c2ecf20Sopenharmony_ci		goto failed;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	dev->broadcast_state = FWNET_BROADCAST_STOPPED;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) {
11538c2ecf20Sopenharmony_ci		void *ptr;
11548c2ecf20Sopenharmony_ci		unsigned v;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci		ptr = kmap(dev->broadcast_rcv_buffer.pages[u]);
11578c2ecf20Sopenharmony_ci		for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++)
11588c2ecf20Sopenharmony_ci			*ptrptr++ = (void *) ((char *)ptr + v * max_receive);
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci	dev->broadcast_rcv_context = context;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	packet.payload_length = max_receive;
11638c2ecf20Sopenharmony_ci	packet.interrupt = 1;
11648c2ecf20Sopenharmony_ci	packet.skip = 0;
11658c2ecf20Sopenharmony_ci	packet.tag = 3;
11668c2ecf20Sopenharmony_ci	packet.sy = 0;
11678c2ecf20Sopenharmony_ci	packet.header_length = IEEE1394_GASP_HDR_SIZE;
11688c2ecf20Sopenharmony_ci	offset = 0;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	for (u = 0; u < num_packets; u++) {
11718c2ecf20Sopenharmony_ci		retval = fw_iso_context_queue(context, &packet,
11728c2ecf20Sopenharmony_ci				&dev->broadcast_rcv_buffer, offset);
11738c2ecf20Sopenharmony_ci		if (retval < 0)
11748c2ecf20Sopenharmony_ci			goto failed;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci		offset += max_receive;
11778c2ecf20Sopenharmony_ci	}
11788c2ecf20Sopenharmony_ci	dev->num_broadcast_rcv_ptrs = num_packets;
11798c2ecf20Sopenharmony_ci	dev->rcv_buffer_size = max_receive;
11808c2ecf20Sopenharmony_ci	dev->broadcast_rcv_next_ptr = 0U;
11818c2ecf20Sopenharmony_ci	retval = fw_iso_context_start(context, -1, 0,
11828c2ecf20Sopenharmony_ci			FW_ISO_CONTEXT_MATCH_ALL_TAGS); /* ??? sync */
11838c2ecf20Sopenharmony_ci	if (retval < 0)
11848c2ecf20Sopenharmony_ci		goto failed;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	/* FIXME: adjust it according to the min. speed of all known peers? */
11878c2ecf20Sopenharmony_ci	dev->broadcast_xmt_max_payload = IEEE1394_MAX_PAYLOAD_S100
11888c2ecf20Sopenharmony_ci			- IEEE1394_GASP_HDR_SIZE - RFC2374_UNFRAG_HDR_SIZE;
11898c2ecf20Sopenharmony_ci	dev->broadcast_state = FWNET_BROADCAST_RUNNING;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	return 0;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci failed:
11948c2ecf20Sopenharmony_ci	__fwnet_broadcast_stop(dev);
11958c2ecf20Sopenharmony_ci	return retval;
11968c2ecf20Sopenharmony_ci}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic void set_carrier_state(struct fwnet_device *dev)
11998c2ecf20Sopenharmony_ci{
12008c2ecf20Sopenharmony_ci	if (dev->peer_count > 1)
12018c2ecf20Sopenharmony_ci		netif_carrier_on(dev->netdev);
12028c2ecf20Sopenharmony_ci	else
12038c2ecf20Sopenharmony_ci		netif_carrier_off(dev->netdev);
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci/* ifup */
12078c2ecf20Sopenharmony_cistatic int fwnet_open(struct net_device *net)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	struct fwnet_device *dev = netdev_priv(net);
12108c2ecf20Sopenharmony_ci	int ret;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	ret = fwnet_broadcast_start(dev);
12138c2ecf20Sopenharmony_ci	if (ret)
12148c2ecf20Sopenharmony_ci		return ret;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	netif_start_queue(net);
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->lock);
12198c2ecf20Sopenharmony_ci	set_carrier_state(dev);
12208c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->lock);
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	return 0;
12238c2ecf20Sopenharmony_ci}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci/* ifdown */
12268c2ecf20Sopenharmony_cistatic int fwnet_stop(struct net_device *net)
12278c2ecf20Sopenharmony_ci{
12288c2ecf20Sopenharmony_ci	struct fwnet_device *dev = netdev_priv(net);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	netif_stop_queue(net);
12318c2ecf20Sopenharmony_ci	fwnet_broadcast_stop(dev);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	return 0;
12348c2ecf20Sopenharmony_ci}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cistatic netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	struct fwnet_header hdr_buf;
12398c2ecf20Sopenharmony_ci	struct fwnet_device *dev = netdev_priv(net);
12408c2ecf20Sopenharmony_ci	__be16 proto;
12418c2ecf20Sopenharmony_ci	u16 dest_node;
12428c2ecf20Sopenharmony_ci	unsigned max_payload;
12438c2ecf20Sopenharmony_ci	u16 dg_size;
12448c2ecf20Sopenharmony_ci	u16 *datagram_label_ptr;
12458c2ecf20Sopenharmony_ci	struct fwnet_packet_task *ptask;
12468c2ecf20Sopenharmony_ci	struct fwnet_peer *peer;
12478c2ecf20Sopenharmony_ci	unsigned long flags;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	/* Can this happen? */
12528c2ecf20Sopenharmony_ci	if (netif_queue_stopped(dev->netdev)) {
12538c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dev->lock, flags);
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	ptask = kmem_cache_alloc(fwnet_packet_task_cache, GFP_ATOMIC);
12598c2ecf20Sopenharmony_ci	if (ptask == NULL)
12608c2ecf20Sopenharmony_ci		goto fail;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
12638c2ecf20Sopenharmony_ci	if (!skb)
12648c2ecf20Sopenharmony_ci		goto fail;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	/*
12678c2ecf20Sopenharmony_ci	 * Make a copy of the driver-specific header.
12688c2ecf20Sopenharmony_ci	 * We might need to rebuild the header on tx failure.
12698c2ecf20Sopenharmony_ci	 */
12708c2ecf20Sopenharmony_ci	memcpy(&hdr_buf, skb->data, sizeof(hdr_buf));
12718c2ecf20Sopenharmony_ci	proto = hdr_buf.h_proto;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	switch (proto) {
12748c2ecf20Sopenharmony_ci	case htons(ETH_P_ARP):
12758c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
12768c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
12778c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
12788c2ecf20Sopenharmony_ci#endif
12798c2ecf20Sopenharmony_ci		break;
12808c2ecf20Sopenharmony_ci	default:
12818c2ecf20Sopenharmony_ci		goto fail;
12828c2ecf20Sopenharmony_ci	}
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(hdr_buf));
12858c2ecf20Sopenharmony_ci	dg_size = skb->len;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	/*
12888c2ecf20Sopenharmony_ci	 * Set the transmission type for the packet.  ARP packets and IP
12898c2ecf20Sopenharmony_ci	 * broadcast packets are sent via GASP.
12908c2ecf20Sopenharmony_ci	 */
12918c2ecf20Sopenharmony_ci	if (fwnet_hwaddr_is_multicast(hdr_buf.h_dest)) {
12928c2ecf20Sopenharmony_ci		max_payload        = dev->broadcast_xmt_max_payload;
12938c2ecf20Sopenharmony_ci		datagram_label_ptr = &dev->broadcast_xmt_datagramlabel;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci		ptask->fifo_addr   = FWNET_NO_FIFO_ADDR;
12968c2ecf20Sopenharmony_ci		ptask->generation  = 0;
12978c2ecf20Sopenharmony_ci		ptask->dest_node   = IEEE1394_ALL_NODES;
12988c2ecf20Sopenharmony_ci		ptask->speed       = SCODE_100;
12998c2ecf20Sopenharmony_ci	} else {
13008c2ecf20Sopenharmony_ci		union fwnet_hwaddr *ha = (union fwnet_hwaddr *)hdr_buf.h_dest;
13018c2ecf20Sopenharmony_ci		__be64 guid = get_unaligned(&ha->uc.uniq_id);
13028c2ecf20Sopenharmony_ci		u8 generation;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci		peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid));
13058c2ecf20Sopenharmony_ci		if (!peer)
13068c2ecf20Sopenharmony_ci			goto fail;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		generation         = peer->generation;
13098c2ecf20Sopenharmony_ci		dest_node          = peer->node_id;
13108c2ecf20Sopenharmony_ci		max_payload        = peer->max_payload;
13118c2ecf20Sopenharmony_ci		datagram_label_ptr = &peer->datagram_label;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		ptask->fifo_addr   = fwnet_hwaddr_fifo(ha);
13148c2ecf20Sopenharmony_ci		ptask->generation  = generation;
13158c2ecf20Sopenharmony_ci		ptask->dest_node   = dest_node;
13168c2ecf20Sopenharmony_ci		ptask->speed       = peer->speed;
13178c2ecf20Sopenharmony_ci	}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	ptask->hdr.w0 = 0;
13208c2ecf20Sopenharmony_ci	ptask->hdr.w1 = 0;
13218c2ecf20Sopenharmony_ci	ptask->skb = skb;
13228c2ecf20Sopenharmony_ci	ptask->dev = dev;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	/* Does it all fit in one packet? */
13258c2ecf20Sopenharmony_ci	if (dg_size <= max_payload) {
13268c2ecf20Sopenharmony_ci		fwnet_make_uf_hdr(&ptask->hdr, ntohs(proto));
13278c2ecf20Sopenharmony_ci		ptask->outstanding_pkts = 1;
13288c2ecf20Sopenharmony_ci		max_payload = dg_size + RFC2374_UNFRAG_HDR_SIZE;
13298c2ecf20Sopenharmony_ci	} else {
13308c2ecf20Sopenharmony_ci		u16 datagram_label;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci		max_payload -= RFC2374_FRAG_OVERHEAD;
13338c2ecf20Sopenharmony_ci		datagram_label = (*datagram_label_ptr)++;
13348c2ecf20Sopenharmony_ci		fwnet_make_ff_hdr(&ptask->hdr, ntohs(proto), dg_size,
13358c2ecf20Sopenharmony_ci				  datagram_label);
13368c2ecf20Sopenharmony_ci		ptask->outstanding_pkts = DIV_ROUND_UP(dg_size, max_payload);
13378c2ecf20Sopenharmony_ci		max_payload += RFC2374_FRAG_HDR_SIZE;
13388c2ecf20Sopenharmony_ci	}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	if (++dev->queued_datagrams == FWNET_MAX_QUEUED_DATAGRAMS)
13418c2ecf20Sopenharmony_ci		netif_stop_queue(dev->netdev);
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	ptask->max_payload = max_payload;
13468c2ecf20Sopenharmony_ci	ptask->enqueued    = 0;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	fwnet_send_packet(ptask);
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci fail:
13538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	if (ptask)
13568c2ecf20Sopenharmony_ci		kmem_cache_free(fwnet_packet_task_cache, ptask);
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	if (skb != NULL)
13598c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	net->stats.tx_dropped++;
13628c2ecf20Sopenharmony_ci	net->stats.tx_errors++;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	/*
13658c2ecf20Sopenharmony_ci	 * FIXME: According to a patch from 2003-02-26, "returning non-zero
13668c2ecf20Sopenharmony_ci	 * causes serious problems" here, allegedly.  Before that patch,
13678c2ecf20Sopenharmony_ci	 * -ERRNO was returned which is not appropriate under Linux 2.6.
13688c2ecf20Sopenharmony_ci	 * Perhaps more needs to be done?  Stop the queue in serious
13698c2ecf20Sopenharmony_ci	 * conditions and restart it elsewhere?
13708c2ecf20Sopenharmony_ci	 */
13718c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic const struct ethtool_ops fwnet_ethtool_ops = {
13758c2ecf20Sopenharmony_ci	.get_link	= ethtool_op_get_link,
13768c2ecf20Sopenharmony_ci};
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_cistatic const struct net_device_ops fwnet_netdev_ops = {
13798c2ecf20Sopenharmony_ci	.ndo_open       = fwnet_open,
13808c2ecf20Sopenharmony_ci	.ndo_stop	= fwnet_stop,
13818c2ecf20Sopenharmony_ci	.ndo_start_xmit = fwnet_tx,
13828c2ecf20Sopenharmony_ci};
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_cistatic void fwnet_init_dev(struct net_device *net)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	net->header_ops		= &fwnet_header_ops;
13878c2ecf20Sopenharmony_ci	net->netdev_ops		= &fwnet_netdev_ops;
13888c2ecf20Sopenharmony_ci	net->watchdog_timeo	= 2 * HZ;
13898c2ecf20Sopenharmony_ci	net->flags		= IFF_BROADCAST | IFF_MULTICAST;
13908c2ecf20Sopenharmony_ci	net->features		= NETIF_F_HIGHDMA;
13918c2ecf20Sopenharmony_ci	net->addr_len		= FWNET_ALEN;
13928c2ecf20Sopenharmony_ci	net->hard_header_len	= FWNET_HLEN;
13938c2ecf20Sopenharmony_ci	net->type		= ARPHRD_IEEE1394;
13948c2ecf20Sopenharmony_ci	net->tx_queue_len	= FWNET_TX_QUEUE_LEN;
13958c2ecf20Sopenharmony_ci	net->ethtool_ops	= &fwnet_ethtool_ops;
13968c2ecf20Sopenharmony_ci}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci/* caller must hold fwnet_device_mutex */
13998c2ecf20Sopenharmony_cistatic struct fwnet_device *fwnet_dev_find(struct fw_card *card)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	struct fwnet_device *dev;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	list_for_each_entry(dev, &fwnet_device_list, dev_link)
14048c2ecf20Sopenharmony_ci		if (dev->card == card)
14058c2ecf20Sopenharmony_ci			return dev;
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	return NULL;
14088c2ecf20Sopenharmony_ci}
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_cistatic int fwnet_add_peer(struct fwnet_device *dev,
14118c2ecf20Sopenharmony_ci			  struct fw_unit *unit, struct fw_device *device)
14128c2ecf20Sopenharmony_ci{
14138c2ecf20Sopenharmony_ci	struct fwnet_peer *peer;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	peer = kmalloc(sizeof(*peer), GFP_KERNEL);
14168c2ecf20Sopenharmony_ci	if (!peer)
14178c2ecf20Sopenharmony_ci		return -ENOMEM;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	dev_set_drvdata(&unit->device, peer);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	peer->dev = dev;
14228c2ecf20Sopenharmony_ci	peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
14238c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&peer->pd_list);
14248c2ecf20Sopenharmony_ci	peer->pdg_size = 0;
14258c2ecf20Sopenharmony_ci	peer->datagram_label = 0;
14268c2ecf20Sopenharmony_ci	peer->speed = device->max_speed;
14278c2ecf20Sopenharmony_ci	peer->max_payload = fwnet_max_payload(device->max_rec, peer->speed);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	peer->generation = device->generation;
14308c2ecf20Sopenharmony_ci	smp_rmb();
14318c2ecf20Sopenharmony_ci	peer->node_id = device->node_id;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->lock);
14348c2ecf20Sopenharmony_ci	list_add_tail(&peer->peer_link, &dev->peer_list);
14358c2ecf20Sopenharmony_ci	dev->peer_count++;
14368c2ecf20Sopenharmony_ci	set_carrier_state(dev);
14378c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->lock);
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	return 0;
14408c2ecf20Sopenharmony_ci}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_cistatic int fwnet_probe(struct fw_unit *unit,
14438c2ecf20Sopenharmony_ci		       const struct ieee1394_device_id *id)
14448c2ecf20Sopenharmony_ci{
14458c2ecf20Sopenharmony_ci	struct fw_device *device = fw_parent_device(unit);
14468c2ecf20Sopenharmony_ci	struct fw_card *card = device->card;
14478c2ecf20Sopenharmony_ci	struct net_device *net;
14488c2ecf20Sopenharmony_ci	bool allocated_netdev = false;
14498c2ecf20Sopenharmony_ci	struct fwnet_device *dev;
14508c2ecf20Sopenharmony_ci	int ret;
14518c2ecf20Sopenharmony_ci	union fwnet_hwaddr *ha;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	mutex_lock(&fwnet_device_mutex);
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	dev = fwnet_dev_find(card);
14568c2ecf20Sopenharmony_ci	if (dev) {
14578c2ecf20Sopenharmony_ci		net = dev->netdev;
14588c2ecf20Sopenharmony_ci		goto have_dev;
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	net = alloc_netdev(sizeof(*dev), "firewire%d", NET_NAME_UNKNOWN,
14628c2ecf20Sopenharmony_ci			   fwnet_init_dev);
14638c2ecf20Sopenharmony_ci	if (net == NULL) {
14648c2ecf20Sopenharmony_ci		mutex_unlock(&fwnet_device_mutex);
14658c2ecf20Sopenharmony_ci		return -ENOMEM;
14668c2ecf20Sopenharmony_ci	}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	allocated_netdev = true;
14698c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(net, card->device);
14708c2ecf20Sopenharmony_ci	dev = netdev_priv(net);
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	spin_lock_init(&dev->lock);
14738c2ecf20Sopenharmony_ci	dev->broadcast_state = FWNET_BROADCAST_ERROR;
14748c2ecf20Sopenharmony_ci	dev->broadcast_rcv_context = NULL;
14758c2ecf20Sopenharmony_ci	dev->broadcast_xmt_max_payload = 0;
14768c2ecf20Sopenharmony_ci	dev->broadcast_xmt_datagramlabel = 0;
14778c2ecf20Sopenharmony_ci	dev->local_fifo = FWNET_NO_FIFO_ADDR;
14788c2ecf20Sopenharmony_ci	dev->queued_datagrams = 0;
14798c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->peer_list);
14808c2ecf20Sopenharmony_ci	dev->card = card;
14818c2ecf20Sopenharmony_ci	dev->netdev = net;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	ret = fwnet_fifo_start(dev);
14848c2ecf20Sopenharmony_ci	if (ret < 0)
14858c2ecf20Sopenharmony_ci		goto out;
14868c2ecf20Sopenharmony_ci	dev->local_fifo = dev->handler.offset;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	/*
14898c2ecf20Sopenharmony_ci	 * default MTU: RFC 2734 cl. 4, RFC 3146 cl. 4
14908c2ecf20Sopenharmony_ci	 * maximum MTU: RFC 2734 cl. 4.2, fragment encapsulation header's
14918c2ecf20Sopenharmony_ci	 *              maximum possible datagram_size + 1 = 0xfff + 1
14928c2ecf20Sopenharmony_ci	 */
14938c2ecf20Sopenharmony_ci	net->mtu = 1500U;
14948c2ecf20Sopenharmony_ci	net->min_mtu = ETH_MIN_MTU;
14958c2ecf20Sopenharmony_ci	net->max_mtu = 4096U;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	/* Set our hardware address while we're at it */
14988c2ecf20Sopenharmony_ci	ha = (union fwnet_hwaddr *)net->dev_addr;
14998c2ecf20Sopenharmony_ci	put_unaligned_be64(card->guid, &ha->uc.uniq_id);
15008c2ecf20Sopenharmony_ci	ha->uc.max_rec = dev->card->max_receive;
15018c2ecf20Sopenharmony_ci	ha->uc.sspd = dev->card->link_speed;
15028c2ecf20Sopenharmony_ci	put_unaligned_be16(dev->local_fifo >> 32, &ha->uc.fifo_hi);
15038c2ecf20Sopenharmony_ci	put_unaligned_be32(dev->local_fifo & 0xffffffff, &ha->uc.fifo_lo);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	memset(net->broadcast, -1, net->addr_len);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	ret = register_netdev(net);
15088c2ecf20Sopenharmony_ci	if (ret)
15098c2ecf20Sopenharmony_ci		goto out;
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	list_add_tail(&dev->dev_link, &fwnet_device_list);
15128c2ecf20Sopenharmony_ci	dev_notice(&net->dev, "IP over IEEE 1394 on card %s\n",
15138c2ecf20Sopenharmony_ci		   dev_name(card->device));
15148c2ecf20Sopenharmony_ci have_dev:
15158c2ecf20Sopenharmony_ci	ret = fwnet_add_peer(dev, unit, device);
15168c2ecf20Sopenharmony_ci	if (ret && allocated_netdev) {
15178c2ecf20Sopenharmony_ci		unregister_netdev(net);
15188c2ecf20Sopenharmony_ci		list_del(&dev->dev_link);
15198c2ecf20Sopenharmony_ci out:
15208c2ecf20Sopenharmony_ci		fwnet_fifo_stop(dev);
15218c2ecf20Sopenharmony_ci		free_netdev(net);
15228c2ecf20Sopenharmony_ci	}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	mutex_unlock(&fwnet_device_mutex);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	return ret;
15278c2ecf20Sopenharmony_ci}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci/*
15308c2ecf20Sopenharmony_ci * FIXME abort partially sent fragmented datagrams,
15318c2ecf20Sopenharmony_ci * discard partially received fragmented datagrams
15328c2ecf20Sopenharmony_ci */
15338c2ecf20Sopenharmony_cistatic void fwnet_update(struct fw_unit *unit)
15348c2ecf20Sopenharmony_ci{
15358c2ecf20Sopenharmony_ci	struct fw_device *device = fw_parent_device(unit);
15368c2ecf20Sopenharmony_ci	struct fwnet_peer *peer = dev_get_drvdata(&unit->device);
15378c2ecf20Sopenharmony_ci	int generation;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	generation = device->generation;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	spin_lock_irq(&peer->dev->lock);
15428c2ecf20Sopenharmony_ci	peer->node_id    = device->node_id;
15438c2ecf20Sopenharmony_ci	peer->generation = generation;
15448c2ecf20Sopenharmony_ci	spin_unlock_irq(&peer->dev->lock);
15458c2ecf20Sopenharmony_ci}
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_cistatic void fwnet_remove_peer(struct fwnet_peer *peer, struct fwnet_device *dev)
15488c2ecf20Sopenharmony_ci{
15498c2ecf20Sopenharmony_ci	struct fwnet_partial_datagram *pd, *pd_next;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->lock);
15528c2ecf20Sopenharmony_ci	list_del(&peer->peer_link);
15538c2ecf20Sopenharmony_ci	dev->peer_count--;
15548c2ecf20Sopenharmony_ci	set_carrier_state(dev);
15558c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->lock);
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pd, pd_next, &peer->pd_list, pd_link)
15588c2ecf20Sopenharmony_ci		fwnet_pd_delete(pd);
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	kfree(peer);
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic void fwnet_remove(struct fw_unit *unit)
15648c2ecf20Sopenharmony_ci{
15658c2ecf20Sopenharmony_ci	struct fwnet_peer *peer = dev_get_drvdata(&unit->device);
15668c2ecf20Sopenharmony_ci	struct fwnet_device *dev = peer->dev;
15678c2ecf20Sopenharmony_ci	struct net_device *net;
15688c2ecf20Sopenharmony_ci	int i;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	mutex_lock(&fwnet_device_mutex);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	net = dev->netdev;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	fwnet_remove_peer(peer, dev);
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	if (list_empty(&dev->peer_list)) {
15778c2ecf20Sopenharmony_ci		unregister_netdev(net);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci		fwnet_fifo_stop(dev);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci		for (i = 0; dev->queued_datagrams && i < 5; i++)
15828c2ecf20Sopenharmony_ci			ssleep(1);
15838c2ecf20Sopenharmony_ci		WARN_ON(dev->queued_datagrams);
15848c2ecf20Sopenharmony_ci		list_del(&dev->dev_link);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci		free_netdev(net);
15878c2ecf20Sopenharmony_ci	}
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	mutex_unlock(&fwnet_device_mutex);
15908c2ecf20Sopenharmony_ci}
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_cistatic const struct ieee1394_device_id fwnet_id_table[] = {
15938c2ecf20Sopenharmony_ci	{
15948c2ecf20Sopenharmony_ci		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
15958c2ecf20Sopenharmony_ci				IEEE1394_MATCH_VERSION,
15968c2ecf20Sopenharmony_ci		.specifier_id = IANA_SPECIFIER_ID,
15978c2ecf20Sopenharmony_ci		.version      = RFC2734_SW_VERSION,
15988c2ecf20Sopenharmony_ci	},
15998c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
16008c2ecf20Sopenharmony_ci	{
16018c2ecf20Sopenharmony_ci		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
16028c2ecf20Sopenharmony_ci				IEEE1394_MATCH_VERSION,
16038c2ecf20Sopenharmony_ci		.specifier_id = IANA_SPECIFIER_ID,
16048c2ecf20Sopenharmony_ci		.version      = RFC3146_SW_VERSION,
16058c2ecf20Sopenharmony_ci	},
16068c2ecf20Sopenharmony_ci#endif
16078c2ecf20Sopenharmony_ci	{ }
16088c2ecf20Sopenharmony_ci};
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic struct fw_driver fwnet_driver = {
16118c2ecf20Sopenharmony_ci	.driver = {
16128c2ecf20Sopenharmony_ci		.owner  = THIS_MODULE,
16138c2ecf20Sopenharmony_ci		.name   = KBUILD_MODNAME,
16148c2ecf20Sopenharmony_ci		.bus    = &fw_bus_type,
16158c2ecf20Sopenharmony_ci	},
16168c2ecf20Sopenharmony_ci	.probe    = fwnet_probe,
16178c2ecf20Sopenharmony_ci	.update   = fwnet_update,
16188c2ecf20Sopenharmony_ci	.remove   = fwnet_remove,
16198c2ecf20Sopenharmony_ci	.id_table = fwnet_id_table,
16208c2ecf20Sopenharmony_ci};
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_cistatic const u32 rfc2374_unit_directory_data[] = {
16238c2ecf20Sopenharmony_ci	0x00040000,	/* directory_length		*/
16248c2ecf20Sopenharmony_ci	0x1200005e,	/* unit_specifier_id: IANA	*/
16258c2ecf20Sopenharmony_ci	0x81000003,	/* textual descriptor offset	*/
16268c2ecf20Sopenharmony_ci	0x13000001,	/* unit_sw_version: RFC 2734	*/
16278c2ecf20Sopenharmony_ci	0x81000005,	/* textual descriptor offset	*/
16288c2ecf20Sopenharmony_ci	0x00030000,	/* descriptor_length		*/
16298c2ecf20Sopenharmony_ci	0x00000000,	/* text				*/
16308c2ecf20Sopenharmony_ci	0x00000000,	/* minimal ASCII, en		*/
16318c2ecf20Sopenharmony_ci	0x49414e41,	/* I A N A			*/
16328c2ecf20Sopenharmony_ci	0x00030000,	/* descriptor_length		*/
16338c2ecf20Sopenharmony_ci	0x00000000,	/* text				*/
16348c2ecf20Sopenharmony_ci	0x00000000,	/* minimal ASCII, en		*/
16358c2ecf20Sopenharmony_ci	0x49507634,	/* I P v 4			*/
16368c2ecf20Sopenharmony_ci};
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_cistatic struct fw_descriptor rfc2374_unit_directory = {
16398c2ecf20Sopenharmony_ci	.length = ARRAY_SIZE(rfc2374_unit_directory_data),
16408c2ecf20Sopenharmony_ci	.key    = (CSR_DIRECTORY | CSR_UNIT) << 24,
16418c2ecf20Sopenharmony_ci	.data   = rfc2374_unit_directory_data
16428c2ecf20Sopenharmony_ci};
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
16458c2ecf20Sopenharmony_cistatic const u32 rfc3146_unit_directory_data[] = {
16468c2ecf20Sopenharmony_ci	0x00040000,	/* directory_length		*/
16478c2ecf20Sopenharmony_ci	0x1200005e,	/* unit_specifier_id: IANA	*/
16488c2ecf20Sopenharmony_ci	0x81000003,	/* textual descriptor offset	*/
16498c2ecf20Sopenharmony_ci	0x13000002,	/* unit_sw_version: RFC 3146	*/
16508c2ecf20Sopenharmony_ci	0x81000005,	/* textual descriptor offset	*/
16518c2ecf20Sopenharmony_ci	0x00030000,	/* descriptor_length		*/
16528c2ecf20Sopenharmony_ci	0x00000000,	/* text				*/
16538c2ecf20Sopenharmony_ci	0x00000000,	/* minimal ASCII, en		*/
16548c2ecf20Sopenharmony_ci	0x49414e41,	/* I A N A			*/
16558c2ecf20Sopenharmony_ci	0x00030000,	/* descriptor_length		*/
16568c2ecf20Sopenharmony_ci	0x00000000,	/* text				*/
16578c2ecf20Sopenharmony_ci	0x00000000,	/* minimal ASCII, en		*/
16588c2ecf20Sopenharmony_ci	0x49507636,	/* I P v 6			*/
16598c2ecf20Sopenharmony_ci};
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_cistatic struct fw_descriptor rfc3146_unit_directory = {
16628c2ecf20Sopenharmony_ci	.length = ARRAY_SIZE(rfc3146_unit_directory_data),
16638c2ecf20Sopenharmony_ci	.key    = (CSR_DIRECTORY | CSR_UNIT) << 24,
16648c2ecf20Sopenharmony_ci	.data   = rfc3146_unit_directory_data
16658c2ecf20Sopenharmony_ci};
16668c2ecf20Sopenharmony_ci#endif
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_cistatic int __init fwnet_init(void)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	int err;
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	err = fw_core_add_descriptor(&rfc2374_unit_directory);
16738c2ecf20Sopenharmony_ci	if (err)
16748c2ecf20Sopenharmony_ci		return err;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
16778c2ecf20Sopenharmony_ci	err = fw_core_add_descriptor(&rfc3146_unit_directory);
16788c2ecf20Sopenharmony_ci	if (err)
16798c2ecf20Sopenharmony_ci		goto out;
16808c2ecf20Sopenharmony_ci#endif
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	fwnet_packet_task_cache = kmem_cache_create("packet_task",
16838c2ecf20Sopenharmony_ci			sizeof(struct fwnet_packet_task), 0, 0, NULL);
16848c2ecf20Sopenharmony_ci	if (!fwnet_packet_task_cache) {
16858c2ecf20Sopenharmony_ci		err = -ENOMEM;
16868c2ecf20Sopenharmony_ci		goto out2;
16878c2ecf20Sopenharmony_ci	}
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	err = driver_register(&fwnet_driver.driver);
16908c2ecf20Sopenharmony_ci	if (!err)
16918c2ecf20Sopenharmony_ci		return 0;
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	kmem_cache_destroy(fwnet_packet_task_cache);
16948c2ecf20Sopenharmony_ciout2:
16958c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
16968c2ecf20Sopenharmony_ci	fw_core_remove_descriptor(&rfc3146_unit_directory);
16978c2ecf20Sopenharmony_ciout:
16988c2ecf20Sopenharmony_ci#endif
16998c2ecf20Sopenharmony_ci	fw_core_remove_descriptor(&rfc2374_unit_directory);
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	return err;
17028c2ecf20Sopenharmony_ci}
17038c2ecf20Sopenharmony_cimodule_init(fwnet_init);
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_cistatic void __exit fwnet_cleanup(void)
17068c2ecf20Sopenharmony_ci{
17078c2ecf20Sopenharmony_ci	driver_unregister(&fwnet_driver.driver);
17088c2ecf20Sopenharmony_ci	kmem_cache_destroy(fwnet_packet_task_cache);
17098c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
17108c2ecf20Sopenharmony_ci	fw_core_remove_descriptor(&rfc3146_unit_directory);
17118c2ecf20Sopenharmony_ci#endif
17128c2ecf20Sopenharmony_ci	fw_core_remove_descriptor(&rfc2374_unit_directory);
17138c2ecf20Sopenharmony_ci}
17148c2ecf20Sopenharmony_cimodule_exit(fwnet_cleanup);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jay Fenlason <fenlason@redhat.com>");
17178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IP over IEEE1394 as per RFC 2734/3146");
17188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
17198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ieee1394, fwnet_id_table);
1720