⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 eth1394.c

📁 Ieee1394驱动实现
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem
 *
 * Copyright (C) 2001-2003 Ben Collins <bcollins@debian.org>
 *               2000 Bonin Franck <boninf@free.fr>
 *               2003 Steve Kinneberg <kinnebergsteve@acmsystems.com>
 *
 * Mainly based on work by Emanuel Pirker and Andreas E. Bombe
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * This driver intends to support RFC 2734, which describes a method for
 * transporting IPv4 datagrams over IEEE-1394 serial busses.
 *
 * TODO:
 * RFC 2734 related:
 * - Add MCAP. Limited Multicast exists only to 224.0.0.1 and 224.0.0.2.
 *
 * Non-RFC 2734 related:
 * - Handle fragmented skb's coming from the networking layer.
 * - Move generic GASP reception to core 1394 code
 * - Convert kmalloc/kfree for link fragments to use kmem_cache_* instead
 * - Stability improvements
 * - Performance enhancements
 * - Consider garbage collecting old partial datagrams after X amount of time
 */

#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/workqueue.h>

#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/bitops.h>
#include <linux/ethtool.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
#include <asm/unaligned.h>
#include <net/arp.h>

#include "config_roms.h"
#include "csr1212.h"
#include "eth1394.h"
#include "highlevel.h"
#include "ieee1394.h"
#include "ieee1394_core.h"
#include "ieee1394_hotplug.h"
#include "ieee1394_transactions.h"
#include "ieee1394_types.h"
#include "iso.h"
#include "nodemgr.h"

#define ETH1394_PRINT_G(level, fmt, args...) \
	printk(level "%s: " fmt, driver_name, ## args)

#define ETH1394_PRINT(level, dev_name, fmt, args...) \
	printk(level "%s: %s: " fmt, driver_name, dev_name, ## args)

struct fragment_info {
	struct list_head list;
	int offset;
	int len;
};

struct partial_datagram {
	struct list_head list;
	u16 dgl;
	u16 dg_size;
	u16 ether_type;
	struct sk_buff *skb;
	char *pbuf;
	struct list_head frag_info;
};

struct pdg_list {
	struct list_head list;	/* partial datagram list per node	*/
	unsigned int sz;	/* partial datagram list size per node	*/
	spinlock_t lock;	/* partial datagram lock		*/
};

struct eth1394_host_info {
	struct hpsb_host *host;
	struct net_device *dev;
};

struct eth1394_node_ref {
	struct unit_directory *ud;
	struct list_head list;
};

struct eth1394_node_info {
	u16 maxpayload;		/* max payload			*/
	u8 sspd;		/* max speed			*/
	u64 fifo;		/* FIFO address			*/
	struct pdg_list pdg;	/* partial RX datagram lists	*/
	int dgl;		/* outgoing datagram label	*/
};

static const char driver_name[] = "eth1394";

static struct kmem_cache *packet_task_cache;

static struct hpsb_highlevel eth1394_highlevel;

/* Use common.lf to determine header len */
static const int hdr_type_len[] = {
	sizeof(struct eth1394_uf_hdr),
	sizeof(struct eth1394_ff_hdr),
	sizeof(struct eth1394_sf_hdr),
	sizeof(struct eth1394_sf_hdr)
};

static const u16 eth1394_speedto_maxpayload[] = {
/*     S100, S200, S400, S800, S1600, S3200 */
	512, 1024, 2048, 4096,  4096,  4096
};

MODULE_AUTHOR("Ben Collins (bcollins@debian.org)");
MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)");
MODULE_LICENSE("GPL");

/*
 * The max_partial_datagrams parameter is the maximum number of fragmented
 * datagrams per node that eth1394 will keep in memory.  Providing an upper
 * bound allows us to limit the amount of memory that partial datagrams
 * consume in the event that some partial datagrams are never completed.
 */
static int max_partial_datagrams = 25;
module_param(max_partial_datagrams, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(max_partial_datagrams,
		 "Maximum number of partially received fragmented datagrams "
		 "(default = 25).");


static int ether1394_header(struct sk_buff *skb, struct net_device *dev,
			    unsigned short type, void *daddr, void *saddr,
			    unsigned len);
static int ether1394_rebuild_header(struct sk_buff *skb);
static int ether1394_header_parse(struct sk_buff *skb, unsigned char *haddr);
static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh);
static void ether1394_header_cache_update(struct hh_cache *hh,
					  struct net_device *dev,
					  unsigned char *haddr);
static int ether1394_tx(struct sk_buff *skb, struct net_device *dev);
static void ether1394_iso(struct hpsb_iso *iso);

static struct ethtool_ops ethtool_ops;

static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
			   quadlet_t *data, u64 addr, size_t len, u16 flags);
static void ether1394_add_host(struct hpsb_host *host);
static void ether1394_remove_host(struct hpsb_host *host);
static void ether1394_host_reset(struct hpsb_host *host);

/* Function for incoming 1394 packets */
static struct hpsb_address_ops addr_ops = {
	.write =	ether1394_write,
};

/* Ieee1394 highlevel driver functions */
static struct hpsb_highlevel eth1394_highlevel = {
	.name =		driver_name,
	.add_host =	ether1394_add_host,
	.remove_host =	ether1394_remove_host,
	.host_reset =	ether1394_host_reset,
};

static int ether1394_recv_init(struct eth1394_priv *priv)
{
	unsigned int iso_buf_size;

	/* FIXME: rawiso limits us to PAGE_SIZE */
	iso_buf_size = min((unsigned int)PAGE_SIZE,
			   2 * (1U << (priv->host->csr.max_rec + 1)));

	priv->iso = hpsb_iso_recv_init(priv->host,
				       ETHER1394_GASP_BUFFERS * iso_buf_size,
				       ETHER1394_GASP_BUFFERS,
				       priv->broadcast_channel,
				       HPSB_ISO_DMA_PACKET_PER_BUFFER,
				       1, ether1394_iso);
	if (priv->iso == NULL) {
		ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n");
		priv->bc_state = ETHER1394_BC_ERROR;
		return -EAGAIN;
	}

	if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0)
		priv->bc_state = ETHER1394_BC_STOPPED;
	else
		priv->bc_state = ETHER1394_BC_RUNNING;
	return 0;
}

/* This is called after an "ifup" */
static int ether1394_open(struct net_device *dev)
{
	struct eth1394_priv *priv = netdev_priv(dev);
	int ret;

	if (priv->bc_state == ETHER1394_BC_ERROR) {
		ret = ether1394_recv_init(priv);
		if (ret)
			return ret;
	}
	netif_start_queue(dev);
	return 0;
}

/* This is called after an "ifdown" */
static int ether1394_stop(struct net_device *dev)
{
	/* flush priv->wake */
	flush_scheduled_work();

	netif_stop_queue(dev);
	return 0;
}

/* Return statistics to the caller */
static struct net_device_stats *ether1394_stats(struct net_device *dev)
{
	return &(((struct eth1394_priv *)netdev_priv(dev))->stats);
}

/* FIXME: What to do if we timeout? I think a host reset is probably in order,
 * so that's what we do. Should we increment the stat counters too?  */
static void ether1394_tx_timeout(struct net_device *dev)
{
	struct hpsb_host *host =
			((struct eth1394_priv *)netdev_priv(dev))->host;

	ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n");
	ether1394_host_reset(host);
}

static inline int ether1394_max_mtu(struct hpsb_host* host)
{
	return (1 << (host->csr.max_rec + 1))
			- sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD;
}

static int ether1394_change_mtu(struct net_device *dev, int new_mtu)
{
	int max_mtu;

	if (new_mtu < 68)
		return -EINVAL;

	max_mtu = ether1394_max_mtu(
			((struct eth1394_priv *)netdev_priv(dev))->host);
	if (new_mtu > max_mtu) {
		ETH1394_PRINT(KERN_INFO, dev->name,
			      "Local node constrains MTU to %d\n", max_mtu);
		return -ERANGE;
	}

	dev->mtu = new_mtu;
	return 0;
}

static void purge_partial_datagram(struct list_head *old)
{
	struct partial_datagram *pd;
	struct list_head *lh, *n;
	struct fragment_info *fi;

	pd = list_entry(old, struct partial_datagram, list);

	list_for_each_safe(lh, n, &pd->frag_info) {
		fi = list_entry(lh, struct fragment_info, list);
		list_del(lh);
		kfree(fi);
	}
	list_del(old);
	kfree_skb(pd->skb);
	kfree(pd);
}

/******************************************
 * 1394 bus activity functions
 ******************************************/

static struct eth1394_node_ref *eth1394_find_node(struct list_head *inl,
						  struct unit_directory *ud)
{
	struct eth1394_node_ref *node;

	list_for_each_entry(node, inl, list)
		if (node->ud == ud)
			return node;

	return NULL;
}

static struct eth1394_node_ref *eth1394_find_node_guid(struct list_head *inl,
						       u64 guid)
{
	struct eth1394_node_ref *node;

	list_for_each_entry(node, inl, list)
		if (node->ud->ne->guid == guid)
			return node;

	return NULL;
}

static struct eth1394_node_ref *eth1394_find_node_nodeid(struct list_head *inl,
							 nodeid_t nodeid)
{
	struct eth1394_node_ref *node;

	list_for_each_entry(node, inl, list)
		if (node->ud->ne->nodeid == nodeid)
			return node;

	return NULL;
}

static int eth1394_new_node(struct eth1394_host_info *hi,
			    struct unit_directory *ud)
{
	struct eth1394_priv *priv;
	struct eth1394_node_ref *new_node;
	struct eth1394_node_info *node_info;

	new_node = kmalloc(sizeof(*new_node), GFP_KERNEL);
	if (!new_node)
		return -ENOMEM;

	node_info = kmalloc(sizeof(*node_info), GFP_KERNEL);
	if (!node_info) {
		kfree(new_node);
		return -ENOMEM;
	}

	spin_lock_init(&node_info->pdg.lock);
	INIT_LIST_HEAD(&node_info->pdg.list);
	node_info->pdg.sz = 0;
	node_info->fifo = CSR1212_INVALID_ADDR_SPACE;

	ud->device.driver_data = node_info;
	new_node->ud = ud;

	priv = netdev_priv(hi->dev);
	list_add_tail(&new_node->list, &priv->ip_node_list);
	return 0;
}

static int eth1394_probe(struct device *dev)
{
	struct unit_directory *ud;
	struct eth1394_host_info *hi;

	ud = container_of(dev, struct unit_directory, device);
	hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
	if (!hi)
		return -ENOENT;

	return eth1394_new_node(hi, ud);
}

static int eth1394_remove(struct device *dev)
{
	struct unit_directory *ud;
	struct eth1394_host_info *hi;
	struct eth1394_priv *priv;
	struct eth1394_node_ref *old_node;
	struct eth1394_node_info *node_info;
	struct list_head *lh, *n;
	unsigned long flags;

	ud = container_of(dev, struct unit_directory, device);
	hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
	if (!hi)
		return -ENOENT;

	priv = netdev_priv(hi->dev);

	old_node = eth1394_find_node(&priv->ip_node_list, ud);
	if (!old_node)
		return 0;

	list_del(&old_node->list);
	kfree(old_node);

	node_info = (struct eth1394_node_info*)ud->device.driver_data;

	spin_lock_irqsave(&node_info->pdg.lock, flags);
	/* The partial datagram list should be empty, but we'll just
	 * make sure anyway... */
	list_for_each_safe(lh, n, &node_info->pdg.list)
		purge_partial_datagram(lh);
	spin_unlock_irqrestore(&node_info->pdg.lock, flags);

	kfree(node_info);
	ud->device.driver_data = NULL;
	return 0;
}

static int eth1394_update(struct unit_directory *ud)
{
	struct eth1394_host_info *hi;
	struct eth1394_priv *priv;
	struct eth1394_node_ref *node;

	hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
	if (!hi)
		return -ENOENT;

	priv = netdev_priv(hi->dev);
	node = eth1394_find_node(&priv->ip_node_list, ud);
	if (node)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -