📄 eth1394.c
字号:
/*
* 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(ð1394_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(ð1394_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(ð1394_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 + -