patch-2.4.19 linux-2.4.19/drivers/net/wan/8253x/8253xnet.c

Next file: linux-2.4.19/drivers/net/wan/8253x/8253xplx.c
Previous file: linux-2.4.19/drivers/net/wan/8253x/8253xmcs.h
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/net/wan/8253x/8253xnet.c linux-2.4.19/drivers/net/wan/8253x/8253xnet.c
@@ -0,0 +1,702 @@
+/* -*- linux-c -*- */
+/* 
+ * Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.
+ *
+ * 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.
+ *
+ **/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/pgtable.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <linux/version.h>
+#include <linux/etherdevice.h>
+#include "Reg9050.h"
+#include "8253xctl.h"
+#include "ring.h"
+#include "8253x.h"
+#include "crc32dcl.h"
+
+				/* turns network packet into a pseudoethernet */
+				/* frame -- does ethernet stuff that 8253x does */
+				/* not do -- makes minimum  64 bytes add crc, etc*/
+int 
+sab8253xn_write2(struct sk_buff *skb, struct net_device *dev)
+{
+	size_t cnt;
+	unsigned int flags;
+	SAB_PORT *priv = (SAB_PORT*) dev->priv;
+	struct sk_buff *substitute;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+	if(dev->tbusy != 0)		/* something of an error */
+	{
+		++(priv->Counters.tx_drops);
+		dev_kfree_skb_any(skb);
+		return -EBUSY;		/* only during release */
+	}
+#endif
+
+	if(priv->active2.transmit == NULL)
+	{
+		return -ENOMEM;
+	}
+	
+	DEBUGPRINT((KERN_ALERT "sab8253x: sending IP packet(bytes):\n"));
+
+	DEBUGPRINT((KERN_ALERT "sab8253x: start address is %p.\n", skb->data));
+	
+	cnt = skb->tail - skb->data;
+	cnt = MIN(cnt, sab8253xn_rbufsize);
+	if(cnt < ETH_ZLEN)
+	{
+		if((skb->end - skb->data) >= ETH_ZLEN)
+		{
+			skb->tail = (skb->data + ETH_ZLEN);
+			cnt = ETH_ZLEN;
+		}
+		else
+		{
+			substitute = dev_alloc_skb(ETH_ZLEN);
+			if(substitute == NULL)
+			{
+				dev_kfree_skb_any(skb);
+				return 0;
+			}
+			substitute->tail = (substitute->data + ETH_ZLEN);
+			memcpy(substitute->data, skb->data, cnt);
+			cnt = ETH_ZLEN;
+			dev_kfree_skb_any(skb);
+			skb = substitute;
+		}
+	}
+	
+	save_flags(flags); cli();
+	if((priv->active2.transmit->Count & OWNER) == OWN_SAB)
+	{
+		++(priv->Counters.tx_drops);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+		dev->tbusy = 1;
+#else
+		netif_stop_queue (dev);
+#endif
+		priv->tx_full = 1;
+		restore_flags(flags);
+		return 1;
+	}
+	restore_flags(flags);
+#ifndef FREEINTERRUPT
+	if(priv->active2.transmit->HostVaddr != NULL)
+	{
+		register RING_DESCRIPTOR *freeme;
+		
+		freeme = priv->active2.transmit;
+		do
+		{
+			skb_unlink((struct sk_buff*)freeme->HostVaddr);
+			dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr);
+			freeme->HostVaddr = NULL;
+			freeme = (RING_DESCRIPTOR*) freeme->VNext;
+		}
+		while(((freeme->Count & OWNER) != OWN_SAB) &&
+		      (freeme->HostVaddr != NULL));
+	}
+#endif
+	dev->trans_start = jiffies;
+	skb_queue_head(priv->sab8253xbuflist, skb);
+	priv->active2.transmit->HostVaddr = skb;
+	priv->active2.transmit->sendcrc = 1;
+	priv->active2.transmit->crcindex = 0;
+	priv->active2.transmit->crc = fn_calc_memory_crc32(skb->data, cnt);
+	priv->active2.transmit->Count = (OWN_SAB|cnt); /* must be this order */
+	priv->active2.transmit = 
+		(RING_DESCRIPTOR*) priv->active2.transmit->VNext;
+	priv->Counters.transmitbytes += cnt;
+	sab8253x_start_txS(priv);
+	return 0;
+}
+
+	/* packetizes the received character */
+	/* stream */
+static void sab8253x_receive_charsN(struct sab_port *port,
+				    union sab8253x_irq_status *stat)
+{
+	unsigned char buf[32];
+	int free_fifo = 0;
+	int reset_fifo = 0;
+	int msg_done = 0;
+	int msg_bad = 0;
+	int count = 0;
+	int total_size = 0;
+	int rstatus = 0;
+	struct sk_buff *skb;
+	
+	/* Read number of BYTES (Character + Status) available. */
+	
+	if((stat->images[ISR1_IDX] & SAB82532_ISR1_RDO) || (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO) )
+	{
+		++msg_bad;
+		++free_fifo;
+		++reset_fifo;
+	}
+	else
+	{
+		if (stat->images[ISR0_IDX] & SAB82532_ISR0_RPF) 
+		{
+			count = port->recv_fifo_size;
+			++free_fifo;
+		}
+		
+		if (stat->images[ISR0_IDX] & SAB82532_ISR0_RME) 
+		{
+			count = READB(port,rbcl);
+			count &= (port->recv_fifo_size - 1);
+			++msg_done;
+			++free_fifo;
+			
+			total_size = READB(port, rbch);
+			if(total_size & SAB82532_RBCH_OV)
+			{
+				msg_bad++;
+			}
+			
+			rstatus = READB(port, rsta);
+			if((rstatus & SAB82532_RSTA_VFR) == 0)
+			{
+				msg_bad++;
+			}
+			if(rstatus & SAB82532_RSTA_RDO)
+			{
+				msg_bad++;
+			}
+			if((rstatus & SAB82532_RSTA_CRC) == 0)
+			{
+				msg_bad++;
+			}
+			if(rstatus & SAB82532_RSTA_RAB)
+			{
+				msg_bad++;
+			}
+		}
+	}
+	
+	/* Read the FIFO. */
+	(*port->readfifo)(port, buf, count);
+	
+	/* Issue Receive Message Complete command. */
+	
+	if (free_fifo) 
+	{
+		sab8253x_cec_wait(port);
+		WRITEB(port, cmdr, SAB82532_CMDR_RMC);
+	}
+	
+	if(reset_fifo)
+	{
+		sab8253x_cec_wait(port);
+		WRITEB(port, cmdr, SAB82532_CMDR_RHR);
+	}
+	
+	if(port->active2.receive == NULL)
+	{
+		return;
+	}
+	
+	if(msg_bad)
+	{
+		++(port->Counters.rx_drops);
+		port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; /* clear the buffer */
+		port->active2.receive->Count = sab8253xn_rbufsize|OWN_SAB;
+		return;
+	}
+	
+	memcpy(port->active2.receive->HostVaddr->tail, buf, count);
+	port->active2.receive->HostVaddr->tail += count;
+	
+	if(msg_done)
+	{
+		port->active2.receive->Count = 
+			(port->active2.receive->HostVaddr->tail - port->active2.receive->HostVaddr->data);
+		if((port->active2.receive->Count < (ETH_ZLEN+4+3)) || /* 4 is the CRC32 size 3 bytes from the SAB part */
+		   (skb = dev_alloc_skb(sab8253xn_rbufsize), skb == NULL))
+		{
+			++(port->Counters.rx_drops);
+			port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; 
+				/* clear the buffer */
+			port->active2.receive->Count = sab8253xn_rbufsize|OWN_SAB;
+		}
+		else
+		{
+			port->active2.receive->Count -= 3;
+			port->active2.receive->HostVaddr->len = port->active2.receive->Count;
+			port->active2.receive->HostVaddr->pkt_type = PACKET_HOST;
+			port->active2.receive->HostVaddr->dev = port->dev;
+			port->active2.receive->HostVaddr->protocol = 
+				eth_type_trans(port->active2.receive->HostVaddr, port->dev);
+			port->active2.receive->HostVaddr->tail -= 3;
+			++(port->Counters.receivepacket);
+			port->Counters.receivebytes += port->active2.receive->Count;
+			skb_unlink(port->active2.receive->HostVaddr);
+			
+			netif_rx(port->active2.receive->HostVaddr);
+			
+			skb_queue_head(port->sab8253xbuflist, skb);
+			port->active2.receive->HostVaddr = skb;
+			port->active2.receive->Count = sab8253xn_rbufsize|OWN_SAB;
+		}
+	}
+}
+
+static void sab8253x_check_statusN(struct sab_port *port,
+				   union sab8253x_irq_status *stat)
+{
+	int modem_change = 0;
+	mctlsig_t         *sig;
+	
+	
+	if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO) 
+	{
+		port->icount.buf_overrun++;
+	}
+	
+	/* Checking DCD */
+	sig = &port->dcd;
+	if (stat->images[sig->irq] & sig->irqmask) 
+	{
+		sig->val = ISON(port,dcd);
+		port->icount.dcd++;
+		modem_change++;
+	}
+	/* Checking CTS */
+	sig = &port->cts;
+	if (stat->images[sig->irq] & sig->irqmask) 
+	{
+		sig->val = ISON(port,cts);
+		port->icount.cts++;
+		modem_change++;
+	}
+	/* Checking DSR */
+	sig = &port->dsr;
+	if (stat->images[sig->irq] & sig->irqmask) 
+	{
+		sig->val = ISON(port,dsr);
+		port->icount.dsr++;
+		modem_change++;
+	}
+	if (modem_change)
+	{
+		wake_up_interruptible(&port->delta_msr_wait);
+	}
+	
+	sig = &port->dcd;
+	if ((port->flags & FLAG8253X_CHECK_CD) &&
+	    (stat->images[sig->irq] & sig->irqmask)) 
+	{
+		
+		if (sig->val)
+		{
+			netif_carrier_on(port->dev);
+		}
+		else if (!((port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
+			   (port->flags & FLAG8253X_CALLOUT_NOHUP))) 
+		{
+			netif_carrier_off(port->dev);
+		}
+	}
+#if 0				/* need to think about CTS/RTS stuff for a network driver */
+	sig = &port->cts;
+	if (port->flags & FLAG8253X_CTS_FLOW) 
+	{				/* not setting this yet */
+		if (port->tty->hw_stopped) 
+		{
+			if (sig->val) 
+			{
+				
+				port->tty->hw_stopped = 0;
+				sab8253x_sched_event(port, RS_EVENT_WRITE_WAKEUP);
+				port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+				WRITEB(port, imr1, port->interrupt_mask1);
+				sab8253x_start_txS(port);
+			}
+		} 
+		else 
+		{
+			if (!(sig->val)) 
+			{
+				port->tty->hw_stopped = 1;
+			}
+		}
+	}
+#endif
+}
+
+static void Sab8253xCollectStats(struct net_device *dev)
+{
+	
+	struct net_device_stats *statsp = 
+		&((SAB_PORT*) dev->priv)->stats;
+	
+	memset(statsp, 0, sizeof(struct net_device_stats));
+	
+	statsp->rx_packets +=
+		((SAB_PORT*)dev->priv)->Counters.receivepacket;
+	statsp->tx_packets +=
+		((SAB_PORT*)dev->priv)->Counters.transmitpacket;
+	statsp->tx_dropped += 
+		((SAB_PORT*)dev->priv)->Counters.tx_drops;
+	statsp->rx_dropped += 
+		((SAB_PORT*)dev->priv)->Counters.rx_drops;
+}
+
+struct net_device_stats *sab8253xn_stats(struct net_device *dev)
+{
+	SAB_PORT *priv = (SAB_PORT*) dev->priv;
+	
+	Sab8253xCollectStats(dev);
+	return &priv->stats;
+}
+
+/* minimal ioctls -- more to be added later */
+int sab8253xn_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	
+	SAB_PORT *priv = (SAB_PORT*) dev->priv;
+	
+	switch(cmd)
+	{
+	case SAB8253XCLEARCOUNTERS:
+		memset(&priv->Counters, 0, sizeof(struct counters));
+		break;
+		
+	default:
+		break;
+	}
+	return 0;
+}
+
+#if 0
+static int sab8253x_block_til_readyN(SAB_PORT *port)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int retval;
+	int do_clocal = 0;
+	unsigned long	flags;
+	
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (port->flags & FLAG8253X_CLOSING)
+	{
+		if (port->flags & FLAG8253X_CLOSING)
+		{
+			interruptible_sleep_on(&port->close_wait);
+		}
+#ifdef SERIAL_DO_RESTART
+		if (port->flags & FLAG8253X_HUP_NOTIFY)
+		{
+			return -EAGAIN;
+		}
+		else
+		{
+			return -ERESTARTSYS;
+		}
+#else
+		return -EAGAIN;
+#endif
+	}
+	
+	/*
+	 * this is not a callout device
+	 */
+	
+	/* suppose callout active */
+	if (port->flags & FLAG8253X_CALLOUT_ACTIVE) 
+	{
+		if (port->normal_termios.c_cflag & CLOCAL)
+		{
+			do_clocal = 1;
+		}
+	} 
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, port->count is dropped by one, so that
+	 * sab8253x_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&port->open_wait, &wait);
+	port->blocked_open++;
+	while (1) 
+	{
+		save_flags(flags); cli();
+		if (!(port->flags & FLAG8253X_CALLOUT_ACTIVE))
+		{
+			RAISE(port,dtr);
+			RAISE(port,rts);	/* maybe not correct for sync */
+			/*
+			 * ??? Why changing the mode here? 
+			 *  port->regs->rw.mode |= SAB82532_MODE_FRTS;
+			 *  port->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+			 */
+		}
+		restore_flags(flags);
+		current->state = TASK_INTERRUPTIBLE;
+		if (!(port->flags & FLAG8253X_INITIALIZED)) 
+		{
+#ifdef SERIAL_DO_RESTART
+			if (port->flags & FLAG8253X_HUP_NOTIFY)
+			{
+				retval = -EAGAIN;
+			}
+			else
+			{
+				retval = -ERESTARTSYS;	
+			}
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
+		    !(port->flags & FLAG8253X_CLOSING) &&
+		    (do_clocal || ISON(port,dcd))) 
+		{
+			break;
+		}
+#ifdef DEBUG_OPEN
+		printk("block_til_readyN:2 flags = 0x%x\n",port->flags);
+#endif
+		if (signal_pending(current)) 
+		{
+			retval = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&port->open_wait, &wait);
+	port->blocked_open--;
+	if (retval)
+	{
+		return retval;
+	}
+	port->flags |= FLAG8253X_NORMAL_ACTIVE; /* is this a good flag? */
+	return 0;
+}
+#endif
+
+int sab8253x_startupN(struct sab_port *port)
+{
+	unsigned long flags;
+	int retval = 0;
+	
+	save_flags(flags); cli();
+	
+	if (port->flags & FLAG8253X_INITIALIZED) 
+	{
+		goto errout;
+	}
+	
+	if (!port->regs) 
+	{
+		retval = -ENODEV;
+		goto errout;
+	}
+	/*
+	 * Initialize the Hardware
+	 */
+	sab8253x_init_lineS(port);	/* nothing in this function
+					 * refers to tty structure */
+	
+	/* Activate RTS */
+	RAISE(port,rts);
+	/* Activate DTR */
+	RAISE(port,dtr);
+	/*
+	 * Initialize the modem signals values
+	 */
+	port->dcd.val=ISON(port,dcd);
+	port->cts.val=ISON(port,cts);
+	port->dsr.val=ISON(port,dsr);
+	/*
+	 * Finally, enable interrupts
+	 */
+	
+	port->interrupt_mask0 = SAB82532_IMR0_RFS | SAB82532_IMR0_PCE |
+		SAB82532_IMR0_PLLA | SAB82532_IMR0_RSC | SAB82532_IMR0_CDSC;
+	/*((port->ccontrol.ccr2 & SAB82532_CCR2_TOE) ? SAB82532_IMR0_CDSC : 0); */
+	
+	WRITEB(port,imr0,port->interrupt_mask0);
+	port->interrupt_mask1 = SAB82532_IMR1_EOP | SAB82532_IMR1_XMR |
+		SAB82532_IMR1_TIN | SAB82532_IMR1_XPR;
+	WRITEB(port, imr1, port->interrupt_mask1);
+	port->all_sent = 1;
+	
+	
+	/*
+	 * and set the speed of the serial port
+	 */
+	sab8253x_change_speedN(port);
+	
+	port->flags |= FLAG8253X_INITIALIZED; /* bad name for indicating to other functionalities status */
+	port->receive_chars = sab8253x_receive_charsN;
+	port->transmit_chars = sab8253x_transmit_charsS;
+	port->check_status = sab8253x_check_statusN;
+	port->receive_test = (SAB82532_ISR0_RME | SAB82532_ISR0_RFO | SAB82532_ISR0_RPF);
+	port->transmit_test = (SAB82532_ISR1_ALLS | SAB82532_ISR1_RDO | SAB82532_ISR1_XPR |
+			       SAB82532_ISR1_XDU | SAB82532_ISR1_CSC);
+	port->check_status_test = (SAB82532_ISR1_CSC);
+	
+	/*((port->ccontrol.ccr2 & SAB82532_CCR2_TOE) ? 0 : SAB82532_ISR0_CDSC));*/
+	
+	restore_flags(flags);
+	return 0;
+	
+ errout:
+	restore_flags(flags);
+	return retval;
+}
+
+int sab8253xn_open(struct net_device *dev)
+{
+	unsigned int retval;
+	SAB_PORT *priv = (SAB_PORT*) dev->priv;
+	
+	if(priv->function != FUNCTION_NR)
+	{
+		return -ENODEV;		/* only allowed if there are no restrictions on the port */
+	}
+	
+	
+	if(priv->flags & FLAG8253X_CLOSING) /* try again after the TTY close finishes */
+	{	
+#ifdef SERIAL_DO_RESTART
+		return ((priv->flags & FLAG8253X_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS); /* The ifconfig UP will just fail */
+#else
+		return -EAGAIN;
+#endif
+	}
+	
+	/*
+	 * Maybe start up serial port -- may already be running a TTY
+	 */
+	if(priv->flags & FLAG8253X_NORMAL_ACTIVE) /* probably should be a test open at all */
+	{
+		return -EBUSY;	/* can't reopen in NET */
+	}
+	
+	if(Sab8253xSetUpLists(priv))
+	{
+		return -ENODEV;
+	}
+	
+	if(Sab8253xInitDescriptors2(priv, sab8253xn_listsize, sab8253xn_rbufsize))
+	{
+		Sab8253xCleanUpTransceiveN(priv);
+		return -ENODEV;
+	}
+	netif_carrier_off(dev);
+	
+	priv->open_type = OPEN_SYNC_NET;
+	priv->tty = 0;
+	
+	retval = sab8253x_startupN(priv);
+	if (retval)
+	{
+		Sab8253xCleanUpTransceiveN(priv);
+		return retval;		
+	}
+	
+	priv->flags |= FLAG8253X_NETWORK; /* flag the call out driver that it has to reinitialize the port */
+	priv->tx_full = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+	dev->start = 1;
+	dev->tbusy = 0;
+#else
+	netif_start_queue(dev);
+#endif
+
+	priv->flags |= FLAG8253X_NORMAL_ACTIVE; /* is this a good flag? */
+	MOD_INC_USE_COUNT;
+	return 0;			/* success */
+}
+/* stop the PPC, free all skbuffers */
+int sab8253xn_release(struct net_device *dev)	/* stop */
+{
+	SAB_PORT *priv = (SAB_PORT*) dev->priv;
+	unsigned long flags;
+	
+	printk(KERN_ALERT "sab8253xn: network interface going down.\n");
+	save_flags(flags); cli();
+	
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+	dev->start = 0;
+	dev->tbusy = 1;
+#else
+	netif_stop_queue (dev);
+#endif
+	
+	sab8253x_shutdownN(priv);
+	Sab8253xCleanUpTransceiveN(priv);
+	netif_carrier_off(dev);
+	priv->flags &= ~FLAG8253X_NETWORK; 
+	priv->flags &= ~(FLAG8253X_NORMAL_ACTIVE|/*FLAG8253X_CALLOUT_ACTIVE|*/
+			 FLAG8253X_CLOSING);
+	priv->open_type = OPEN_NOT;
+	MOD_DEC_USE_COUNT;
+	restore_flags(flags);
+	return 0;
+}
+
+SAB_PORT *current_sab_port = NULL;
+
+int sab8253xn_init(struct net_device *dev)
+{
+	
+	SAB_PORT *priv;
+	
+	printk(KERN_ALERT "sab8253xn: initializing SAB8253X network driver instance.\n");
+	
+	priv = current_sab_port;
+	dev->priv = priv;
+	
+	if(dev->priv == NULL)
+	{
+		printk(KERN_ALERT "sab8253xn: could not find active port!\n");
+		return -ENOMEM;
+	}
+	priv->dev = dev;
+	
+	ether_setup(dev);
+	
+	dev->irq = priv->irq;
+	dev->hard_start_xmit = sab8253xn_write2;
+	dev->do_ioctl = sab8253xn_ioctl;
+	dev->open = sab8253xn_open;
+	dev->stop = sab8253xn_release;
+	dev->get_stats = sab8253xn_stats;
+	dev->base_addr = (unsigned) priv->regs;
+	/* should I do a request region here */
+	priv->next_dev = Sab8253xRoot;
+	Sab8253xRoot = dev;
+	return 0;
+}
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)