d MC^2 V0 support for the pandaboard.
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/ieee1394/eth1394.c
blob: 93362eed94eda0be345958ab069f0f78935c6443 (plain) (tree)
1
2
  
                                                         





















                                                                          


                                                                        













                                                                             

                         





                         
                            


                             









                           
                          

                    
                        
                    


                      
                          
                             
                                  
                           

                    






                                                                    
















                                   


                                                                          












                                  




                                                                  

  
                                            
 
                                            




                                               



                                      

  








                                                                             

                                                                          


















                                                                                
                                                                






                                                                             


                                                          













                                                  
                                                         
 




                                                                      

                                                  
                                                                             




                                                                      
                                                                             










                                                                 
                                    
                                                 

                                                     
                
 
                                                   
                                                

                                   
         
                               



                                      
                                                 
 


                               
                              



                                     
                                                                       



                                                                   


                                                                              
 

                                                                        
 

                                                                        

 





                                                                              

                                                                    
                    
 
                         
                               
 







                                                                            





                                                         
                                    
                                 


                                                            

                                                   
                                                                







































                                                                               

                                            

                                                   



                    

                                                         
 



                                            
                                                          


                               
                                                            







                                             
                                                     





                                                            


                 












                                                                 

















                                                                 

                         
 

                                  
 
                                                                      
 





                                                                    
 

                                      







                                                    





                                                                 
                                                          

                         
 
                                        

 












                                                             
                                      


                                           




                                                 
                                                                     




                                                     
                                                                            

                                           
                                              
 
                                                        


                                   








                                                                         

                                                   
         

                                  
                      


                                                                              

                                                                

                                                          

         
                                                   

 
                                                      
 











                                                                
 

                                           






                                                                

                                                                              


  






























                                                                                



                                                                             
                                                      



                                            

                      

                                                                                
                       
         
 



                                                                                

                                                                         
                                                    

                       
 
                                                                       
                          
                                                             
                         
         

                              
                                           

                                
                                            


                                     

                                                     

                                                                         
                         
                                                             
                         
         
 
                                     
 

                                                                          


                         

                                                                                







                                                                             
                                  
               
    
                


                                                                
                                                                       
                                            


                                 
                                                         

                                     
                                  

                                                         



                                                                              
                                            



                                             

 

                                                        











                                                         
                


                       
                                
 


                                                          

                                                              
                                                         


                                                               
                                                               
                                                   






                                                                    
                              






                                                               






                                                                          

                                                                         


                                   
                                                      
                                                      
                                            


                    
                                                          



                                            

 









                                                                
 
                                            
                                                                    
 


                                                                





                                                                            
 



                                                   


                                                                               
                                            

                                                                             
 
                                       
                          












                                                                       
                                                                            

 




                                            
                                                                            



                               
                                  
                                    


                               
                                                                            







                                                                      
         
 
                                        



                                    

                                              
 
                                  



                                                                     


                                                                             




                                                     
                                                                                


                                                 
                                                                          




                                                                       
                                             
                                                                              


                                                                    
                                                                   





                                                         
                            





                                                                          

                                                                  
 
                                                          
                                                                  
                                                                 
                          
                                 
 

                                                                             

















                                                                                
                                                                         

                                                                              
                                                       
                                                        
                    
                                                                    


                                          

                                                                         




                                                     
                                                                             

                                 
                               
 

                                                                      
                                 
 


                 
                                                                               


                                    
                                           

                                         
 



                                                                       
                                                                         





                                                                
                                                     



                                                                               
                                                                  





                                                             
                                                        




                                                                                
                                                                   





                                                             
                                                           
                              
                                                       




                                      
                                                






                                 


                 


                                                                               


                                     
                                                



























                                                                           


                 

                                                                                
 

                                                                      
 
                                                                 
                               




                                                                     
                            


                 
                                                                  
 




                                                                        





















                                                                                
                                      










                                                                           
                                                                       











                                                                             
                                     
                                                                     



                                                                    

                                                                  



















































































                                                                                  
                                                        
                                                    

























                                                                        
                                              
 
































                                                                            
                            

                                                                         






















                                                                             
                            

                                                                         








                                                                                
                                                                         




                                           

                                                                         



                                                       

                                                                              


























                                                                              

                                                            

                                                     



                                                                      



                                                                  

                                                                 



                                                                      



                                                                        
 

                                                                              











                                                                                
                                                                 

 


                                                                   





                                                           
                        



























                                                                             


                                          
                                                                                











                                                          

                                                                             
                                                                        

                          
 

                               
 
                                

                               

                                                                                

                                                        

                                         
                       



                 


                                                                       



                                     

                                                                              
                              

                                                            
                                                                 
                                                                       
                                                         
 
                                      


                                                                       

 
                                                             














                                                                                
                               

                                             
                                                            






                                                                             
                               







                                                                           
                            




                 
                                                                       
                                                                      

                                         
                                                          
















                                                   














                                                                            
                                



                                                                              




                                                                              
                                                        
                 




                                                   
                                          
                                                                    
 
                                  
                                                     
                     


                                







                                                   
                                                                
                          
                          




                                                                        
                                                                           
                          

      
                                               
                 
                          
 


                                                                     

                                    
                                



                                                                        
                                                                        

                                        
                                                        


                                                                            
                                                                    



                                                                            
                                                                   
 
                                                                  
                                                                 
                          
                                  
 

                                                                             
                                                                  
                                  


                                                    
                                                                    







                                                                            
                                      
                                                   





















                                                           

                                                                          



                                                                      



                                                         







                                                                                






                                                                             
 
                            






                                                          
                                              

                                 
                                                   
 








                                                                           

 

                                                               
 

                                                                            





                                            
                                             
 

                

                                                                         
                                                                

                               
 
                                                    





                                                              

 
                                              







                                                        
/*
 * 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)
		return 0;

	return eth1394_new_node(hi, ud);
}

static struct ieee1394_device_id eth1394_id_table[] = {
	{
		.match_flags = (IEEE1394_MATCH_SPECIFIER_ID |
				IEEE1394_MATCH_VERSION),
		.specifier_id =	ETHER1394_GASP_SPECIFIER_ID,
		.version = ETHER1394_GASP_VERSION,
	},
	{}
};

MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table);

static struct hpsb_protocol_driver eth1394_proto_driver = {
	.name		= driver_name,
	.id_table	= eth1394_id_table,
	.update		= eth1394_update,
	.driver		= {
		.probe		= eth1394_probe,
		.remove		= eth1394_remove,
	},
};

static void ether1394_reset_priv(struct net_device *dev, int set_mtu)
{
	unsigned long flags;
	int i;
	struct eth1394_priv *priv = netdev_priv(dev);
	struct hpsb_host *host = priv->host;
	u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3]));
	int max_speed = IEEE1394_SPEED_MAX;

	spin_lock_irqsave(&priv->lock, flags);

	memset(priv->ud_list, 0, sizeof(priv->ud_list));
	priv->bc_maxpayload = 512;

	/* Determine speed limit */
	/* FIXME: This is broken for nodes with link speed < PHY speed,
	 * and it is suboptimal for S200B...S800B hardware.
	 * The result of nodemgr's speed probe should be used somehow. */
	for (i = 0; i < host->node_count; i++) {
		/* take care of S100B...S400B PHY ports */
		if (host->speed[i] == SELFID_SPEED_UNKNOWN) {
			max_speed = IEEE1394_SPEED_100;
			break;
		}
		if (max_speed > host->speed[i])
			max_speed = host->speed[i];
	}
	priv->bc_sspd = max_speed;

	if (set_mtu) {
		/* Use the RFC 2734 default 1500 octets or the maximum payload
		 * as initial MTU */
		dev->mtu = min(1500, ether1394_max_mtu(host));

		/* Set our hardware address while we're at it */
		memcpy(dev->dev_addr, &guid, sizeof(u64));
		memset(dev->broadcast, 0xff, sizeof(u64));
	}

	spin_unlock_irqrestore(&priv->lock, flags);
}

static void ether1394_init_dev(struct net_device *dev)
{
	dev->open		= ether1394_open;
	dev->stop		= ether1394_stop;
	dev->hard_start_xmit	= ether1394_tx;
	dev->get_stats		= ether1394_stats;
	dev->tx_timeout		= ether1394_tx_timeout;
	dev->change_mtu		= ether1394_change_mtu;

	dev->hard_header	= ether1394_header;
	dev->rebuild_header	= ether1394_rebuild_header;
	dev->hard_header_cache	= ether1394_header_cache;
	dev->header_cache_update= ether1394_header_cache_update;
	dev->hard_header_parse	= ether1394_header_parse;

	SET_ETHTOOL_OPS(dev, &ethtool_ops);

	dev->watchdog_timeo	= ETHER1394_TIMEOUT;
	dev->flags		= IFF_BROADCAST | IFF_MULTICAST;
	dev->features		= NETIF_F_HIGHDMA;
	dev->addr_len		= ETH1394_ALEN;
	dev->hard_header_len 	= ETH1394_HLEN;
	dev->type		= ARPHRD_IEEE1394;

	/* FIXME: This value was copied from ether_setup(). Is it too much? */
	dev->tx_queue_len	= 1000;
}

/*
 * Wake the queue up after commonly encountered transmit failure conditions are
 * hopefully over.  Currently only tlabel exhaustion is accounted for.
 */
static void ether1394_wake_queue(struct work_struct *work)
{
	struct eth1394_priv *priv;
	struct hpsb_packet *packet;

	priv = container_of(work, struct eth1394_priv, wake);
	packet = hpsb_alloc_packet(0);

	/* This is really bad, but unjam the queue anyway. */
	if (!packet)
		goto out;

	packet->host = priv->host;
	packet->node_id = priv->wake_node;
	/*
	 * A transaction label is all we really want.  If we get one, it almost
	 * always means we can get a lot more because the ieee1394 core recycled
	 * a whole batch of tlabels, at last.
	 */
	if (hpsb_get_tlabel(packet) == 0)
		hpsb_free_tlabel(packet);

	hpsb_free_packet(packet);
out:
	netif_wake_queue(priv->wake_dev);
}

/*
 * This function is called every time a card is found. It is generally called
 * when the module is installed. This is where we add all of our ethernet
 * devices. One for each host.
 */
static void ether1394_add_host(struct hpsb_host *host)
{
	struct eth1394_host_info *hi = NULL;